This commit is contained in:
unknown
2025-12-11 12:05:44 -08:00
parent d8c6a62d3b
commit a41a6b0d0d
6 changed files with 100 additions and 172 deletions

View File

@@ -3,11 +3,11 @@ FROM python:${PYTHON_VERSION}-slim-bookworm
ARG APP_VERSION=25.12.12
LABEL org.opencontainers.image.title="ebook2audiobook" \
org.opencontainers.image.description="Generate audiobooks from e-books, voice cloning & 1158 languages!" \
org.opencontainers.image.version="${APP_VERSION}" \
org.opencontainers.image.authors="Drew Thomasson / Rob McDowell" \
org.opencontainers.image.licenses="MIT" \
org.opencontainers.image.source="https://github.com/DrewThomasson/ebook2audiobook"
org.opencontainers.image.description="Generate audiobooks from e-books, voice cloning & 1158 languages!" \
org.opencontainers.image.version="${APP_VERSION}" \
org.opencontainers.image.authors="Drew Thomasson / Rob McDowell" \
org.opencontainers.image.licenses="MIT" \
org.opencontainers.image.source="https://github.com/DrewThomasson/ebook2audiobook"
ARG DEVICE_TAG=cpu
ARG DOCKER_DEVICE_STR='{"name": "cpu", "os": "linux", "arch": "x86_64", "pyvenv": [3, 12], "tag": "cpu", "note": ""}'
@@ -16,73 +16,73 @@ ARG CALIBRE_INSTALLER_URL="https://download.calibre-ebook.com/linux-installer.sh
ARG ISO3_LANG=eng
ENV DEBIAN_FRONTEND=noninteractive \
PYTHONDONTWRITEBYTECODE=1 \
PYTHONUNBUFFERED=1 \
PYTHONWARNINGS="ignore::SyntaxWarning" \
PIP_NO_CACHE_DIR=1
PYTHONDONTWRITEBYTECODE=1 \
PYTHONUNBUFFERED=1 \
PYTHONWARNINGS="ignore::SyntaxWarning" \
PIP_NO_CACHE_DIR=1
WORKDIR /app
COPY . .
RUN apt-get update && \
apt-get install -y --no-install-recommends --allow-change-held-packages \
gcc g++ make python3-dev pkg-config git wget bash xz-utils \
libegl1 libopengl0 libgl1 \
libxcb1 libx11-6 libxcb-cursor0 libxcb-render0 libxcb-shm0 libxcb-xfixes0 \
${DOCKER_PROGRAMS_STR} \
tesseract-ocr-${ISO3_LANG} || true && \
rm -rf /var/lib/apt/lists/*
apt-get install -y --no-install-recommends --allow-change-held-packages \
gcc g++ make python3-dev pkg-config git wget bash xz-utils \
libegl1 libopengl0 libgl1 \
libxcb1 libx11-6 libxcb-cursor0 libxcb-render0 libxcb-shm0 libxcb-xfixes0 \
${DOCKER_PROGRAMS_STR} \
tesseract-ocr-${ISO3_LANG} || true && \
rm -rf /var/lib/apt/lists/*
RUN set -eux; \
if command -v curl >/dev/null 2>&1; then \
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y; \
elif command -v wget >/dev/null 2>&1; then \
wget -qO- https://sh.rustup.rs | sh -s -- -y; \
else \
echo "ERROR: curl or wget required to install rustup"; exit 1; \
fi && \
. "$HOME/.cargo/env" && \
chmod +x ebook2audiobook.sh && \
./ebook2audiobook.sh --script_mode build_docker --docker_device "${DOCKER_DEVICE_STR}"
if command -v curl >/dev/null 2>&1; then \
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y; \
elif command -v wget >/dev/null 2>&1; then \
wget -qO- https://sh.rustup.rs | sh -s -- -y; \
else \
echo "ERROR: curl or wget required to install rustup"; exit 1; \
fi && \
. "$HOME/.cargo/env" && \
chmod +x ebook2audiobook.sh && \
./ebook2audiobook.sh --script_mode build_docker --docker_device "${DOCKER_DEVICE_STR}"
RUN case "${DEVICE_TAG}" in \
jetson51*) \
echo "JetPack 5.1.x → copying CUDA 11.4 libs" && \
mkdir -p /usr/local/cuda-11.4/lib64 && \
( cp -P /usr/lib/aarch64-linux-gnu/libcuda* /usr/local/cuda-11.4/lib64/ 2>/dev/null || true ) && \
( cp -P /usr/lib/aarch64-linux-gnu/libcudart.so.11.0 /usr/local/cuda-11.4/lib64/ 2>/dev/null || true ) && \
( cp -P /usr/lib/aarch64-linux-gnu/libcublas* /usr/local/cuda-11.4/lib64/ 2>/dev/null || true ) && \
( cp -P /usr/lib/aarch64-linux-gnu/libcufft* /usr/local/cuda-11.4/lib64/ 2>/dev/null || true ) && \
( cp -P /usr/lib/aarch64-linux-gnu/libcurand* /usr/local/cuda-11.4/lib64/ 2>/dev/null || true ) && \
( cp -P /usr/lib/aarch64-linux-gnu/libcusparse* /usr/local/cuda-11.4/lib64/ 2>/dev/null || true ) ;; \
jetson60*|jetson61*) \
echo "JetPack 6.x → no extra CUDA lib copy needed" ;; \
xpu*) \
echo "Intel XPU detected — using IPEX" ;; \
rocm*) \
echo "AMD ROCm detected — using ROCm PyTorch" ;; \
*) ;; \
jetson51) \
echo "JetPack 5.1.x → copying CUDA 11.4 libs" && \
mkdir -p /usr/local/cuda-11.4/lib64 && \
( cp -P /usr/lib/aarch64-linux-gnu/libcuda* /usr/local/cuda-11.4/lib64/ 2>/dev/null || true ) && \
( cp -P /usr/lib/aarch64-linux-gnu/libcudart.so.11.0 /usr/local/cuda-11.4/lib64/ 2>/dev/null || true ) && \
( cp -P /usr/lib/aarch64-linux-gnu/libcublas* /usr/local/cuda-11.4/lib64/ 2>/dev/null || true ) && \
( cp -P /usr/lib/aarch64-linux-gnu/libcufft* /usr/local/cuda-11.4/lib64/ 2>/dev/null || true ) && \
( cp -P /usr/lib/aarch64-linux-gnu/libcurand* /usr/local/cuda-11.4/lib64/ 2>/dev/null || true ) && \
( cp -P /usr/lib/aarch64-linux-gnu/libcusparse* /usr/local/cuda-11.4/lib64/ 2>/dev/null || true ) ;; \
jetson60|jetson61) \
echo "JetPack 6.x → no extra CUDA lib copy needed" ;; \
xpu) \
echo "Intel XPU detected — using IPEX" ;; \
rocm*) \
echo "AMD ROCm detected — using ROCm PyTorch" ;; \
*) ;; \
esac
RUN if [ "${DEVICE_TAG}" = jetson51* ]; then \
echo "LD_LIBRARY_PATH=/usr/local/cuda-11.4/lib64" >> /etc/environment; \
else \
echo "LD_LIBRARY_PATH=" >> /etc/environment; \
fi
RUN if [ "${DEVICE_TAG}" = "jetson51" ]; then \
echo "LD_LIBRARY_PATH=/usr/local/cuda-11.4/lib64" >> /etc/environment; \
else \
echo "LD_LIBRARY_PATH=" >> /etc/environment; \
fi
ENV LD_LIBRARY_PATH=/usr/local/cuda-11.4/lib64
RUN wget -nv "$CALIBRE_INSTALLER_URL" -O /tmp/calibre.sh && \
bash /tmp/calibre.sh && rm -f /tmp/calibre.sh
bash /tmp/calibre.sh && rm -f /tmp/calibre.sh
RUN set -eux; \
find /usr /app -type d -name "__pycache__" -exec rm -rf {} +; \
rm -rf /usr/share/doc/* /usr/share/man/* /usr/share/locale/* \
/usr/share/icons/* /usr/share/fonts/* /var/cache/fontconfig/* \
/opt/calibre/*.txt /opt/calibre/*.md /opt/calibre/resources/man-pages \
/root/.cache /tmp/* $HOME/.rustup $HOME/.cargo || true; \
apt-get purge -y --auto-remove gcc g++ make python3-dev pkg-config git; \
apt-get clean; \
rm -rf /var/lib/apt/lists/*
find /usr /app -type d -name "__pycache__" -exec rm -rf {} +; \
rm -rf /usr/share/doc/* /usr/share/man/* /usr/share/locale/* \
/usr/share/icons/* /usr/share/fonts/* /var/cache/fontconfig/* \
/opt/calibre/*.txt /opt/calibre/*.md /opt/calibre/resources/man-pages \
/root/.cache /tmp/* $HOME/.rustup $HOME/.cargo || true; \
apt-get purge -y --auto-remove gcc g++ make python3-dev pkg-config git; \
apt-get clean; \
rm -rf /var/lib/apt/lists/*
RUN mkdir -p /app/audiobooks && chmod 777 /app/audiobooks
VOLUME /app/audiobooks

20
app.py
View File

@@ -100,26 +100,26 @@ Docker build image:
Docker run image:
Gradio/GUI:
CPU:
docker run --rm -it -v "$(pwd)/audiobooks:/app/audiobooks" -p 7860:7860 ebook2audiobook:cpu
docker run --rm -it -v "$(pwd)":/app:rw -p 7860:7860 ebook2audiobook:cpu
CUDA:
docker run --gpus all --rm -it -v "$(pwd)/audiobooks:/app/audiobooks" -p 7860:7860 ebook2audiobook:cu[118/121/128 etc..]
docker run --gpus all --rm -it -v "$(pwd)":/app:rw -p 7860:7860 ebook2audiobook:cu[118/121/128 etc..]
ROCM:
docker run --device=/dev/kfd --device=/dev/dri --rm -it -v "$(pwd)/audiobooks:/app/audiobooks" -p 7860:7860 ebook2audiobook:rocm[5.5/6.1/6.4 etc..]
docker run --device=/dev/kfd --device=/dev/dri --rm -it -v "$(pwd)":/app:rw -p 7860:7860 ebook2audiobook:rocm[5.5/6.1/6.4 etc..]
XPU:
docker run --device=/dev/dri --rm -it -v "$(pwd)/audiobooks:/app/audiobooks" -p 7860:7860 ebook2audiobook:xpu
docker run --device=/dev/dri --rm -it -v "$(pwd)":/app:rw -p 7860:7860 ebook2audiobook:xpu
JETSON:
docker run --runtime nvidia --rm -it -v "$(pwd)/audiobooks:/app/audiobooks" -p 7860:7860 ebook2audiobook:jetson[51/60/61 etc...]
docker run --runtime nvidia --rm -it -v "$(pwd)":/app:rw -p 7860:7860 ebook2audiobook:jetson[51/60/61 etc...]
Headless mode:
CPU:
docker run --rm -it -v "/my/real/ebooks/folder/absolute/path:/app/ebooks" -v "/my/real/output/folder/absolute/path:/app/audiobooks" -p 7860:7860 ebook2audiobook:cpu --headless --ebook "/app/ebooks/myfile.pdf" [--language etc..]
docker run --rm -it -v "/my/real/ebooks/folder/absolute/path:/app/ebooks" -v "/my/real/output/folder/absolute/path:/app/audiobooks" -p 7860:7860 ebook2audiobook:cpu --headless --ebook "/app/ebooks/myfile.pdf" [--voice /app/my/voicepath/voice.mp3 etc..]
CUDA:
docker run --gpus all --rm -it -v "/my/real/ebooks/folder/absolute/path:/app/ebooks" -v "/my/real/output/folder/absolute/path:/app/audiobooks" -p 7860:7860 ebook2audiobook::cu[118/121/128 etc..] --headless --ebook "/app/ebooks/myfile.pdf" [--language etc..]
docker run --gpus all --rm -it -v "/my/real/ebooks/folder/absolute/path:/app/ebooks" -v "/my/real/output/folder/absolute/path:/app/audiobooks" -p 7860:7860 ebook2audiobook:cu[118/121/128 etc..] --headless --ebook "/app/ebooks/myfile.pdf" [--voice /app/my/voicepath/voice.mp3 etc..]
ROCM:
docker run --device=/dev/kfd --device=/dev/dri --rm -it -v "/my/real/ebooks/folder/absolute/path:/app/ebooks" -v "/my/real/output/folder/absolute/path:/app/audiobooks" -p 7860:7860 ebook2audiobook:rocm[5.5/6.1/6.4 etc..] --headless --ebook "/app/ebooks/myfile.pdf" [--language etc..]
docker run --device=/dev/kfd --device=/dev/dri --rm -it -v "/my/real/ebooks/folder/absolute/path:/app/ebooks" -v "/my/real/output/folder/absolute/path:/app/audiobooks" -p 7860:7860 ebook2audiobook:rocm[5.5/6.1/6.4 etc..] --headless --ebook "/app/ebooks/myfile.pdf" [--voice /app/my/voicepath/voice.mp3 etc..]
XPU:
docker run --device=/dev/dri --rm -it -v "/my/real/ebooks/folder/absolute/path:/app/ebooks" -v "/my/real/output/folder/absolute/path:/app/audiobooks" -p 7860:7860 ebook2audiobook:xpu --headless --ebook "/app/ebooks/myfile.pdf" [--language etc..]
docker run --device=/dev/dri --rm -it -v "/my/real/ebooks/folder/absolute/path:/app/ebooks" -v "/my/real/output/folder/absolute/path:/app/audiobooks" -p 7860:7860 ebook2audiobook:xpu --headless --ebook "/app/ebooks/myfile.pdf" [--voice /app/my/voicepath/voice.mp3 etc..]
JETSON:
docker run --runtime nvidia --rm -it -v "/my/real/ebooks/folder/absolute/path:/app/ebooks" -v "/my/real/output/folder/absolute/path:/app/audiobooks" -p 7860:7860 ebook2audiobook:jetson[51/60/61 etc...] --headless --ebook "/app/ebooks/myfile.pdf" [--language etc..]
docker run --runtime nvidia --rm -it -v "/my/real/ebooks/folder/absolute/path:/app/ebooks" -v "/my/real/output/folder/absolute/path:/app/audiobooks" -p 7860:7860 ebook2audiobook:jetson[51/60/61 etc...] --headless --ebook "/app/ebooks/myfile.pdf" [--voice /app/my/voicepath/voice.mp3 etc..]
* MPS is not exposed in docker so CPU must be used.

View File

@@ -1,63 +1,38 @@
version: "3.9"
services:
ebook2audiobook:
#image: docker.io/athomasson2/ebook2audiobook:latest # <-- Use prebuilt image instead of building
image: ${BUILD_NAME}
# Uncomment to use pre-built image instead of building locally
# image: docker.io/athomasson2/ebook2audiobook:latest
image: ${BUILD_NAME:-ebook2audiobook:latest}
build:
context: .
dockerfile: Dockerfile
args:
APP_VERSION: "25.12.12"
DEVICE_TAG: cpu # override with environment variable if needed
container_name: ebook2audiobook
working_dir: /app
# To update ebook2audiobook to the latest you may have to rebuild
entrypoint: ["python", "app.py", "--script_mode", "full_docker"]
command: [] # <- Extra ebook2audiobook parameters can be added here
# The dir containing this docker-compose.yml file will be used as the base directory for any commands.
# Example Headless || command: ["--headless", "--ebook", "The-Tell-Tale-Heart.epub", "--tts_engine", "yourtts"] # "The-Tell-Tale-Heart.epub" file is located in the same dir as this docker-compose.yml. # More compose headless info can be found at wiki "https://github.com/DrewThomasson/ebook2audiobook/wiki/Docker-Compose-Headless-guide"
command: [] # Add extra app parameters here if needed
tty: true
stdin_open: true
ports:
- 7860:7860 # Maps container's port 7860 to the host's port 7860.
# ---------------------------------------------------
# UNIVERSAL GPU SUPPORT (NVIDIA + ROCm + Intel XPU)
# ---------------------------------------------------
# NVIDIA (WSL2 + Linux)
# NOTE:
# - Docker: we do NOT use "runtime: nvidia" or "gpus: all" here, because those will fail
# when nvidia-container-toolkit is not installed.
# - Podman: does not support "runtime: nvidia" either.
# Instead we run the container in privileged mode so that any existing GPU devices
# (/dev/nvidia*, /dev/kfd, /dev/dri, etc.) are visible automatically.
# On systems without those devices, the container still starts and just runs on CPU.
privileged: true
# Needed for ROCm + Intel iGPU (ignored if groups do not exist inside the container)
- "7860:7860"
privileged: false
group_add:
- video
- render
# Additional optional NVIDIA request (ignored on non-NVIDIA)
# NOTE:
# - Docker Swarm understands `deploy.resources.*` with "driver: nvidia".
# - Regular docker-compose and Podman ignore `deploy` completely.
# We keep this block only for symmetry with Swarm / future use.
deploy:
resources:
reservations:
devices:
- driver: nvidia
count: all
capabilities: ["gpu"]
limits: {} # Keeps limits as an empty mapping to avoid errors. Uncomment and configure below.
capabilities: [gpu]
volumes:
- ./:/app # Maps the local directory to the container.
- .:/app:rw # Full project bind — all folders (ebooks, audiobooks, etc.) synced instantly
restart: unless-stopped
# Common Issues: ----
# --> `python: can't open file '/home/user/app/app.py': [Errno 2] No such file or directory`
# Removed all post arguments as CMD was replaced with ENTRYPOINT in the Dockerfile
# Example correction:
# Before: command: ["python", "app.py", "--script_mode", "full_docker"] or -> `docker run athomasson2/ebook2audiobook python app.py --script_mode full_docker`
# After: nothing needed or just -> `docker run athomasson2/ebook2audiobook`
# Extra arguments after app.py can still be added to the -> command: []
# Example adding extra arguments -> command: ["--share"] or -> command: ["--help"]
volumes:
audiobooks: # Optional: use named volume for persistence if you prefer

View File

@@ -419,22 +419,6 @@ if errorlevel 1 (
)
exit /b 0
:compare_versions
setlocal EnableDelayedExpansion
set "v1=%~1"
set "v2=%~2"
:: Remove dots
set "v1_n=%v1:.=%"
set "v2_n=%v2:.=%"
:: Pad with zeros
set "v1_n=000000%v1_n%"
set "v2_n=000000%v2_n%"
set "v1_n=!v1_n:~-6!"
set "v2_n=!v2_n:~-6!"
if !v1_n! LSS !v2_n! (endlocal & set cmp_result=LEQ & exit /b)
if !v1_n! EQU !v2_n! (endlocal & set cmp_result=LEQ & exit /b)
endlocal & set cmp_result=GTR & exit /b
:install_python_packages
echo [ebook2audiobook] Installing dependencies...
python -m pip cache purge >nul 2>&1
@@ -444,11 +428,6 @@ if errorlevel 1 goto :failed
for /f "tokens=2 delims=: " %%A in ('pip show torch 2^>nul ^| findstr /b /c:"Version"') do (
set "torch_ver=%%A"
)
::call :compare_versions "%torch_ver%" "2.2.2"
::if /I "%cmp_result%"=="LEQ" (
:: python -m pip install --upgrade --no-cache-dir --use-pep517 "numpy<2"
:: if errorlevel 1 goto :failed
::)
python -m unidic download
if errorlevel 1 goto :failed
echo [ebook2audiobook] Installation completed.

View File

@@ -606,10 +606,6 @@ function install_python_packages {
python3 -m pip cache purge > /dev/null 2>&1
python3 -m pip install --upgrade pip > /dev/null 2>&1
python3 -m pip install --upgrade --no-cache-dir --progress-bar on --disable-pip-version-check --use-pep517 -r "$SCRIPT_DIR/requirements.txt" || exit 1
#torch_ver=$(python3 -m pip show torch 2>/dev/null | awk '/^Version:/{print $2}')
#if [[ "$(printf '%s\n%s\n' "$torch_ver" "2.2.2" | sort -V | head -n1)" == "$torch_ver" ]]; then
# python3 -m pip install --upgrade --no-cache-dir --use-pep517 "numpy<2" || exit 1
#fi
python3 -m unidic download || exit 1
echo "[ebook2audiobook] Installation completed."
return 0

View File

@@ -1,57 +1,35 @@
version: "3.9"
services:
ebook2audiobook:
#image: docker.io/athomasson2/ebook2audiobook:latest # <-- Use prebuilt image instead of building
# Uncomment to use pre-built image instead of building locally
#image: docker.io/athomasson2/ebook2audiobook:latest
image: ${BUILD_NAME}
build:
context: .
dockerfile: Dockerfile
container_name: ebook2audiobook
working_dir: /app
# To update ebook2audiobook to the latest you may have to rebuild
entrypoint: ["python", "app.py", "--script_mode", "full_docker"]
command: [] # <- Extra ebook2audiobook parameters can be added here
# The dir containing this docker-compose.yml file will be used as the base directory for any commands.
# Example Headless || command: ["--headless", "--ebook", "The-Tell-Tale-Heart.epub", "--tts_engine", "yourtts"] # "The-Tell-Tale-Heart.epub" file is located in the same dir as this docker-compose.yml. # More compose headless info can be found at wiki "https://github.com/DrewThomasson/ebook2audiobook/wiki/Docker-Compose-Headless-guide"
command: [] # Extra ebook2audiobook parameters can be added here
tty: true
stdin_open: true
ports:
- 7860:7860 # Maps container's port 7860 to the host's port 7860.
# ---------------------------------------------------
# UNIVERSAL GPU SUPPORT (NVIDIA + ROCm + Intel XPU)
# ---------------------------------------------------
# NVIDIA (WSL2 + Linux)
# NOTE: Podman DOES NOT support "runtime: nvidia"
# Instead of explicit /dev/nvidia* mappings (which fail when devices are absent),
# we run the container in privileged mode so that existing GPU devices on the host
# are visible inside the container, and on non-GPU systems it still runs fine.
- 7860:7860
privileged: true
# Needed for ROCm + Intel iGPU (ignored if groups do not exist inside the container)
# Needed for ROCm + Intel iGPU
group_add:
- video
- render
# Additional optional NVIDIA request (ignored on non-NVIDIA)
# NOTE: Podman ignores `deploy` entirely, but we preserve it for symmetry
deploy:
resources:
reservations:
devices:
- driver: nvidia
count: all
capabilities: ["gpu"]
limits: {} # Keeps limits as an empty mapping to avoid errors. Uncomment and configure below.
devices:
- /dev/dri:/dev/dri
- /dev/kfd:/dev/kfd
security_opt:
- label=disable
annotation:
io.containers.nvidia: "true"
environment:
- NVIDIA_VISIBLE_DEVICES=all
- NVIDIA_DRIVER_CAPABILITIES=compute,utility
volumes:
- ./:/app # Maps the local directory to the container.
# Common Issues: ----
# --> `python: can't open file '/home/user/app/app.py': [Errno 2] No such file or directory`
# Removed all post arguments as CMD was replaced with ENTRYPOINT in the Dockerfile
# Example correction:
# Before: command: ["python", "app.py", "--script_mode", "full_docker"] or -> `docker run athomasson2/ebook2audiobook python app.py --script_mode full_docker`
# After: nothing needed or just -> `docker run athomasson2/ebook2audiobook`
# Extra arguments after app.py can still be added to the -> command: []
# Example adding extra arguments -> command: ["--share"] or -> command: ["--help"]
- ./:/app