@echo off setlocal EnableExtensions EnableDelayedExpansion :: Force UTF-8 for CMD chcp 65001 >nul :: Prefer PowerShell 7, fallback to Windows PowerShell 5.1 set "PS_EXE=pwsh" where /Q pwsh >nul 2>&1 || set "PS_EXE=powershell" :: One canonical set of flags for every PowerShell call in this script set "PS_ARGS=-NoLogo -NoProfile -NonInteractive -ExecutionPolicy Bypass" :: Detect Constrained Language Mode (corporate lockdown) "%PS_EXE%" %PS_ARGS% -Command "if ($ExecutionContext.SessionState.LanguageMode -ne 'FullLanguage') { exit 99 }" if errorlevel 99 ( echo ERROR: PowerShell Constrained Language Mode detected. This environment is not supported. goto :failed ) :: Ensure PS output encoding is UTF-8 for this session (non-persistent) "%PS_EXE%" %PS_ARGS% -Command "[Console]::OutputEncoding=[System.Text.Encoding]::UTF8" >nul 2>&1 :: Enable ANSI VT mode reg query HKCU\Console /v VirtualTerminalLevel >nul 2>&1 if errorlevel 1 ( reg add HKCU\Console /v VirtualTerminalLevel /t REG_DWORD /d 1 /f >nul ) :: Real ESC byte via PowerShell (RELIABLE) for /f "delims=" %%e in (' cmd /c ""%PS_EXE%" %PS_ARGS% -Command "[char]27"" ') do set "ESC=%%e" :: Capture all arguments into ARGS set "ARGS=%*" set "NATIVE=native" set "BUILD_DOCKER=build_docker" set "SCRIPT_MODE=%NATIVE%" set "SCRIPT_DIR=%~dp0" if "%SCRIPT_DIR:~-1%"=="\" set "SCRIPT_DIR=%SCRIPT_DIR:~0,-1%" set "APP_NAME=ebook2audiobook" set /p APP_VERSION=<"%SCRIPT_DIR%\VERSION.txt" set "APP_FILE=%APP_NAME%.cmd" set "OS_LANG=%LANG%"& if "!OS_LANG!"=="" set "OS_LANG=en"& set "OS_LANG=!OS_LANG:~0,2!" set "TEST_HOST=127.0.0.1" set "TEST_PORT=7860" set "ICON_PATH=%SCRIPT_DIR%\tools\icons\windows\appIcon.ico" set "STARTMENU_DIR=%APPDATA%\Microsoft\Windows\Start Menu\Programs\%APP_NAME%" set "STARTMENU_LNK=%STARTMENU_DIR%\%APP_NAME%.lnk" set "DESKTOP_LNK=%USERPROFILE%\Desktop\%APP_NAME%.lnk" set "ARCH=%PROCESSOR_ARCHITECTURE%" set "PYTHON_VERSION=3.12" set "PYTHON_SCOOP=python%PYTHON_VERSION:.=%" set "PYTHON_ENV=python_env" set "PYTHONUTF8=1" set "PYTHONIOENCODING=utf-8" set "CURRENT_ENV=" set "HOST_PROGRAMS=cmake rustup python calibre-normal ffmpeg nodejs espeak-ng sox tesseract" set "DOCKER_PROGRAMS=ffmpeg nodejs espeak-ng sox tesseract-ocr" # tesseract-ocr-[lang] and calibre are hardcoded in Dockerfile set "DOCKER_CALIBRE_INSTALLER_URL=https://download.calibre-ebook.com/linux-installer.sh" set "DOCKER_DEVICE_STR=" set "DOCKER_IMG_NAME=ebook2audiobook" set "TMP=%SCRIPT_DIR%\tmp" set "TEMP=%SCRIPT_DIR%\tmp" set "ESPEAK_DATA_PATH=%USERPROFILE%\scoop\apps\espeak-ng\current\eSpeak NG\espeak-ng-data" set "SCOOP_HOME=%USERPROFILE%\scoop" set "SCOOP_SHIMS=%SCOOP_HOME%\shims" set "SCOOP_APPS=%SCOOP_HOME%\apps" set "CONDA_URL=https://github.com/conda-forge/miniforge/releases/latest/download/Miniforge3-Windows-x86_64.exe" set "CONDA_HOME=%USERPROFILE%\Miniforge3" set "CONDA_INSTALLER=Miniforge3-Windows-x86_64.exe" set "CONDA_ENV=%CONDA_HOME%\condabin\conda.bat" set "CONDA_PATH=%CONDA_HOME%\condabin" set "TESSDATA_PREFIX=%SCRIPT_DIR%\models\tessdata" set "NODE_PATH=%SCOOP_HOME%\apps\nodejs\current" set "PATH=%SCOOP_SHIMS%;%SCOOP_APPS%;%CONDA_PATH%;%NODE_PATH%;%PATH%" set "INSTALLED_LOG=%SCRIPT_DIR%\.installed" set "UNINSTALLER=%SCRIPT_DIR%\uninstall.cmd" set "BROWSER_HELPER=%SCRIPT_DIR%\.bh.ps1" set "HELP_FOUND=%ARGS:--help=%" set "HEADLESS_FOUND=%ARGS:--headless=%" IF NOT DEFINED DEVICE_TAG SET "DEVICE_TAG=" set "OK_SCOOP=0" set "OK_CONDA=0" set "OK_PROGRAMS=0" set "OK_DOCKER=0" :: Refresh environment variables (append registry Path to current PATH) for /f "tokens=2,*" %%A in ('reg query "HKLM\SYSTEM\CurrentControlSet\Control\Session Manager\Environment" /v Path') do ( set "PATH=%%B;%PATH%" ) if "%ARCH%"=="x86" ( echo %ESC%[31m=============== Error: 32-bit architecture is not supported.%ESC%[0m goto :failed ) if not exist "%INSTALLED_LOG%" if /I not "%SCRIPT_MODE%"=="BUILD_DOCKER" ( type nul > "%INSTALLED_LOG%" ) cd /d "%SCRIPT_DIR%" :: Clear previous associative values for /f "tokens=1* delims==" %%A in ('set arguments. 2^>nul') do set "%%A=" set "FORWARD_ARGS=" ::::::::::::::::::::::::::::::: CORE FUNCTIONS :parse_args if "%~1"=="" goto :parse_args_done set "arg=%~1" :: ALWAYS forward args set "FORWARD_ARGS=!FORWARD_ARGS! !arg!" :: Flag or key-value argument if "!arg:~0,2!"=="--" ( set "key=!arg:~2!" :: Check for a value (next arg exists AND does not start with --) if not "%~2"=="" ( echo %~2 | findstr "^--" >nul if errorlevel 1 ( set "arguments.!key!=%~2" shift & shift goto parse_args ) ) :: Boolean flag set "arguments.!key!=true" shift goto parse_args ) shift goto parse_args :parse_args_done if defined arguments.script_mode ( if /I "!arguments.script_mode!"=="%BUILD_DOCKER%" ( set "SCRIPT_MODE=!arguments.script_mode!" ) else ( echo Error: Invalid script mode argument: !arguments.script_mode! goto :failed ) ) if defined arguments.docker_device ( set "DOCKER_DEVICE_STR=!arguments.docker_device!" if /i "!arguments.docker_device!"=="true" ( echo Error: --docker_device has no value! goto :failed ) ) if defined arguments.script_mode ( if /I "!arguments.script_mode!"=="true" ( echo Error: --script_mode requires a value goto :failed ) for /f "tokens=1,2 delims==" %%A in ('set arguments. 2^>nul') do ( set "argname=%%A" set "argname=!argname:arguments.=!" if /I not "!argname!"=="script_mode" if /I not "!argname!"=="docker_device" ( echo Error: when --script_mode is used, only --docker_device is allowed as additional option. Invalid option: --!argname! goto :failed ) ) ) if defined arguments.docker_device ( if /I "!arguments.docker_device!"=="true" ( echo Error: --docker_device requires a value goto :failed ) ) goto :check_scoop ::::::::::::::: DESKTOP APP :make_shortcut set "shortcut=%~1" "%PS_EXE%" %PS_ARGS% -Command "$s=New-Object -ComObject WScript.Shell; $sc=$s.CreateShortcut('%shortcut%'); $sc.TargetPath='cmd.exe'; $sc.Arguments='/k ""cd /d """"%SCRIPT_DIR%"""" && """"%APP_FILE%""""""'; $sc.WorkingDirectory='%SCRIPT_DIR%'; $sc.IconLocation='%ICON_PATH%'; $sc.Save()" exit /b :build_gui if /I not "%HEADLESS_FOUND%"=="%ARGS%" ( if not exist "%STARTMENU_DIR%" mkdir "%STARTMENU_DIR%" if not exist "%STARTMENU_LNK%" ( call :make_shortcut "%STARTMENU_LNK%" call :make_shortcut "%DESKTOP_LNK%" ) for /f "skip=1 delims=" %%L in ('tasklist /v /fo csv /fi "imagename eq powershell.exe" 2^>nul') do ( echo %%L | findstr /I "%APP_NAME%" >nul && ( for /f "tokens=2 delims=," %%A in ("%%L") do ( taskkill /PID %%~A /F >nul 2>&1 ) ) ) reg add "HKCU\Software\Microsoft\Windows\CurrentVersion\Uninstall\%APP_NAME%" /v "DisplayName" /d "%APP_NAME%" /f >nul 2>&1 reg add "HKCU\Software\Microsoft\Windows\CurrentVersion\Uninstall\%APP_NAME%" /v "DisplayVersion" /d "%APP_VERSION%" /f >nul 2>&1 reg add "HKCU\Software\Microsoft\Windows\CurrentVersion\Uninstall\%APP_NAME%" /v "Publisher" /d "ebook2audiobook Team" /f >nul 2>&1 reg add "HKCU\Software\Microsoft\Windows\CurrentVersion\Uninstall\%APP_NAME%" /v "InstallLocation" /d "%SCRIPT_DIR%" /f >nul 2>&1 reg add "HKCU\Software\Microsoft\Windows\CurrentVersion\Uninstall\%APP_NAME%" /v "UninstallString" /d "\"%UNINSTALLER%\"" /f >nul 2>&1 reg add "HKCU\Software\Microsoft\Windows\CurrentVersion\Uninstall\%APP_NAME%" /v "DisplayIcon" /d "%ICON_PATH%" /f >nul 2>&1 reg add "HKCU\Software\Microsoft\Windows\CurrentVersion\Uninstall\%APP_NAME%" /v "NoModify" /t REG_DWORD /d 1 /f >nul 2>&1 reg add "HKCU\Software\Microsoft\Windows\CurrentVersion\Uninstall\%APP_NAME%" /v "NoRepair" /t REG_DWORD /d 1 /f >nul 2>&1 start "%APP_NAME%" /min "%PS_EXE%" %PS_ARGS% -File "%BROWSER_HELPER%" -HostName "%TEST_HOST%" -Port %TEST_PORT% ) exit /b :::::: END OF DESKTOP APP :get_iso3_lang set "arg=%~1" if /i "%arg%"=="en" echo eng& goto :eof if /i "%arg%"=="fr" echo fra& goto :eof if /i "%arg%"=="de" echo deu& goto :eof if /i "%arg%"=="it" echo ita& goto :eof if /i "%arg%"=="es" echo spa& goto :eof if /i "%arg%"=="pt" echo por& goto :eof if /i "%arg%"=="ar" echo ara& goto :eof if /i "%arg%"=="tr" echo tur& goto :eof if /i "%arg%"=="ru" echo rus& goto :eof if /i "%arg%"=="bn" echo ben& goto :eof if /i "%arg%"=="zh" echo chi_sim& goto :eof if /i "%arg%"=="fa" echo fas& goto :eof if /i "%arg%"=="hi" echo hin& goto :eof if /i "%arg%"=="hu" echo hun& goto :eof if /i "%arg%"=="id" echo ind& goto :eof if /i "%arg%"=="jv" echo jav& goto :eof if /i "%arg%"=="ja" echo jpn& goto :eof if /i "%arg%"=="ko" echo kor& goto :eof if /i "%arg%"=="pl" echo pol& goto :eof if /i "%arg%"=="ta" echo tam& goto :eof if /i "%arg%"=="te" echo tel& goto :eof if /i "%arg%"=="yo" echo yor& goto :eof echo eng exit /b :check_scoop where /Q scoop if errorlevel 1 ( echo Scoop is not installed. set "OK_SCOOP=1" goto :install_programs ) if "%SCRIPT_MODE%"=="%BUILD_DOCKER%" ( goto :check_required_programs ) else ( goto :check_conda ) exit /b :check_required_programs set "missing_prog_array=" for %%p in (%HOST_PROGRAMS%) do ( set "prog=%%p" if "%%p"=="nodejs" set "prog=node" if "%%p"=="calibre-normal" set "prog=calibre" where /Q !prog! if errorlevel 1 ( echo %%p is not installed. set "missing_prog_array=!missing_prog_array! %%p" ) ) if not "%missing_prog_array%"=="" ( set "OK_PROGRAMS=1" goto :install_programs ) goto :dispatch exit /b :install_programs if not "%OK_SCOOP%"=="0" ( echo Installing Scoop... call "%PS_EXE%" %PS_ARGS% -Command "Set-ExecutionPolicy RemoteSigned -Scope CurrentUser -Force" call "%PS_EXE%" %PS_ARGS% -Command "iwr -useb get.scoop.sh | iex" call "%PS_EXE%" %PS_ARGS% -Command "scoop --version" >nul 2>&1 if not errorlevel 1 ( call "%PS_EXE%" %PS_ARGS% -Command "scoop install git" call "%PS_EXE%" %PS_ARGS% -Command "scoop bucket add muggle https://github.com/hu3rror/scoop-muggle.git" call "%PS_EXE%" %PS_ARGS% -Command "scoop bucket add extras" call "%PS_EXE%" %PS_ARGS% -Command "scoop bucket add versions" if "%OK_PROGRAMS%"=="0" ( echo %ESC%[32m=============== Scoop is installed! ===============%ESC%[0m set "OK_SCOOP=0" ) findstr /i /x "scoop" "%INSTALLED_LOG%" >nul 2>&1 if errorlevel 1 ( echo scoop>>"%INSTALLED_LOG%" ) start "" cmd /k cd /d "%SCRIPT_DIR%" ^& call "%~f0" ) else ( echo %ESC%[31m=============== Scoop installation failed.%ESC%[0m goto :failed ) exit ) if not "%OK_CONDA%"=="0" ( echo Installing Miniforge... call "%PS_EXE%" %PS_ARGS% -Command "Invoke-WebRequest -Uri %CONDA_URL% -OutFile '%CONDA_INSTALLER%'" call start /wait "" "%CONDA_INSTALLER%" /InstallationType=JustMe /RegisterPython=0 /S /D=%UserProfile%\Miniforge3 where /Q conda if not errorlevel 1 ( echo %ESC%[32m=============== Miniforge3 is installed! ===============%ESC%[0m findstr /i /x "Miniforge3" "%INSTALLED_LOG%" >nul 2>&1 if errorlevel 1 ( echo Miniforge3>>"%INSTALLED_LOG%" ) ) else ( echo %ESC%[31m=============== Miniforge3 installation failed.%ESC%[0m goto :failed ) if not exist "%USERPROFILE%\.condarc" ( call conda config --set auto_activate false ) call conda update --all -y call conda clean --index-cache -y call conda clean --packages --tarballs -y del "%CONDA_INSTALLER%" set "OK_CONDA=0" start "" cmd /k cd /d "%CD%" ^& call "%~f0" exit ) if not "%OK_PROGRAMS%"=="0" ( echo Installing missing programs... if "%OK_SCOOP%"=="0" ( call "%PS_EXE%" %PS_ARGS% -Command "scoop bucket add muggle https://github.com/hu3rror/scoop-muggle.git" call "%PS_EXE%" %PS_ARGS% -Command "scoop bucket add extras" call "%PS_EXE%" %PS_ARGS% -Command "scoop bucket add versions" ) for %%p in (%missing_prog_array%) do ( set "prog=%%p" call "%PS_EXE%" %PS_ARGS% -Command "scoop install %%p" if "%%p"=="tesseract" ( where /Q !prog! if not errorlevel 1 ( for /f %%i in ('call :get_iso3_lang %OS_LANG%') do set "ISO3_LANG=%%i" echo Detected system language: !OS_LANG! → downloading OCR language: %ISO3_LANG% set "tessdata=%SCOOP_APPS%\tesseract\current\tessdata" if not exist "!tessdata!\%ISO3_LANG%.traineddata" ( call "%PS_EXE%" %PS_ARGS% -Command "Invoke-WebRequest -Uri https://github.com/tesseract-ocr/tessdata_best/raw/main/%ISO3_LANG%.traineddata -OutFile '!tessdata!\%ISO3_LANG%.traineddata'" ) if exist "!tessdata!\%ISO3_LANG%.traineddata" ( echo Tesseract OCR language %ISO3_LANG% installed in !tessdata! ) else ( echo Failed to install OCR language %ISO3_LANG% ) ) ) if "%%p"=="python" ( set "PY_FOUND=" where /Q python && set PY_FOUND=1 where /Q python3 && set PY_FOUND=1 where /Q py && set PY_FOUND=1 if not defined PY_FOUND ( echo %ESC%[31m=============== %%p installation failed.%ESC%[0m goto :failed ) ) if "%%p"=="nodejs" ( set "prog=node" ) if "%%p"=="calibre-normal" ( set "prog=calibre" ) if "%%p"=="rustup" ( if exist "%USERPROFILE%\scoop\apps\rustup\current\.cargo\bin\rustup.exe" ( set "PATH=%USERPROFILE%\scoop\apps\rustup\current\.cargo\bin;%PATH%" ) ) where /Q !prog! if not errorlevel 1 ( echo %ESC%[32m=============== %%p is installed! ===============%ESC%[0m findstr /i /x "%%p" "%INSTALLED_LOG%" >nul 2>&1 if errorlevel 1 ( echo %%p>>"%INSTALLED_LOG%" ) ) else ( echo %ESC%[31m=============== %%p installation failed.%ESC%[0m goto :failed ) ) call "%PS_EXE%" %PS_ARGS% -Command "[System.Environment]::SetEnvironmentVariable('Path', [System.Environment]::GetEnvironmentVariable('Path', 'User') + ';%SCOOP_SHIMS%;%SCOOP_APPS%;%CONDA_PATH%;%NODE_PATH%', 'User')" set "OK_SCOOP=0" set "OK_PROGRAMS=0" set "missing_prog_array=" ) goto :dispatch exit /b :check_conda where /Q conda if errorlevel 1 ( echo Miniforge3 is not installed. set "OK_CONDA=1" goto :install_programs ) :: Check if running in a Conda environment if defined CONDA_DEFAULT_ENV ( set "CURRENT_ENV=%CONDA_PREFIX%" ) :: Check if running in a Python virtual environment if defined VIRTUAL_ENV ( set "CURRENT_ENV=%VIRTUAL_ENV%" ) for /f "delims=" %%i in ('where python') do ( if defined CONDA_PREFIX ( if /i "%%i"=="%CONDA_PREFIX%\Scripts\python.exe" ( set "CURRENT_ENV=%CONDA_PREFIX%" break ) ) else if defined VIRTUAL_ENV ( if /i "%%i"=="%VIRTUAL_ENV%\Scripts\python.exe" ( set "CURRENT_ENV=%VIRTUAL_ENV%" break ) ) ) if "%CURRENT_ENV%"=="" ( if not exist "%SCRIPT_DIR%\%PYTHON_ENV%" ( echo Creating ./python_env version %PYTHON_VERSION%... call "%CONDA_HOME%\Scripts\activate.bat" call conda update -n base -c conda-forge conda -y call conda update --all -y call conda clean --index-cache -y call conda clean --packages --tarballs -y call conda create --prefix "%SCRIPT_DIR%\%PYTHON_ENV%" python=%PYTHON_VERSION% -y call conda activate base call conda activate "%SCRIPT_DIR%\%PYTHON_ENV%" call :install_python_packages if errorlevel 1 goto :failed call conda deactivate call conda deactivate ) ) else ( echo Current python virtual environment detected: %CURRENT_ENV%. echo =============== This script runs with its own virtual env and must be out of any other virtual environment when it's launched. goto :failed ) goto :check_required_programs exit /b 0 :check_docker where /Q docker if errorlevel 1 ( echo %ESC%[31m=============== Docker is not installed or not running. Please install or run Docker manually.%ESC%[0m exit /b 1 ) exit /b 0 :install_python_packages echo [ebook2audiobook] Installing dependencies... python -m pip cache purge >nul 2>&1 python -m pip install --upgrade pip pip setuptools wheel >nul 2>&1 python -m pip install --upgrade llvmlite numba --only-binary=:all: set count_pkg=0 for /f "usebackq delims=" %%P in ("%SCRIPT_DIR%\requirements.txt") do ( if not "%%P"=="" if not "%%P:~0,1%"=="#" ( set /a count_pkg+=1 echo [!count_pkg!] Installing %%P python -m pip install --upgrade --no-cache-dir "%%P" || exit /b 1 ) ) 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" ) python -m unidic download if errorlevel 1 goto :failed echo [ebook2audiobook] Installation completed. exit /b 0 :check_device_info set "arg=%~1" "%PS_EXE%" %PS_ARGS% -Command ^ @" %PYTHON_SCOOP% - << 'EOF' from lib.classes.device_installer import DeviceInstaller device = DeviceInstaller() result = device.check_device_info(r"%arg%") if result: print(result) raise SystemExit(0) raise SystemExit(1) EOF "@ exit /b %errorlevel% :install_device_packages set "arg=%~1" "%PS_EXE%" %PS_ARGS% -Command ^ @" python - << 'EOF' import sys from lib.classes.device_installer import DeviceInstaller device = DeviceInstaller() exit_code = device.install_device_packages(r"%arg%") sys.exit(exit_code) EOF "@ exit /b %errorlevel% :check_sitecustomized set "src_pyfile=%SCRIPT_DIR%\components\sitecustomize.py" for /f "delims=" %%a in ('python -c "import sysconfig;print(sysconfig.get_paths()[\"purelib\"])"') do ( set "site_packages_path=%%a" ) if "%site_packages_path%"=="" ( echo [WARN] Could not detect Python site-packages exit /b 0 ) set "dst_pyfile=%site_packages_path%\sitecustomize.py" if not exist "%dst_pyfile%" ( copy /y "%src_pyfile%" "%dst_pyfile%" >nul if errorlevel 1 ( echo %ESC%[31m=============== sitecustomize.py hook installation error: copy failed.%ESC%[0m exit /b 1 ) exit /b 0 ) for %%I in ("%src_pyfile%") do set "src_time=%%~tI" for %%I in ("%dst_pyfile%") do set "dst_time=%%~tI" if "%src_time%" GTR "%dst_time%" ( copy /y "%src_pyfile%" "%dst_pyfile%" >nul if errorlevel 1 ( echo %ESC%[31m=============== sitecustomize.py hook update failed.%ESC%[0m exit /b 1 ) ) exit /b 0 :build_docker_image set "ARG=%~1" "%PS_EXE%" %PS_ARGS% -command "if (!(Get-Command docker -ErrorAction SilentlyContinue)) { Write-Host '=============== Error: Docker must be installed and running!' -ForegroundColor Red; exit 1 }" if errorlevel 1 exit /b 1 "%PS_EXE%" %PS_ARGS% -command "if (docker compose version > $null 2>&1) { exit 0 } else { exit 1 }" set "cmd_options=" set "cmd_extra=" set "py_vers=%PYTHON_VERSION% " if /i "%TAG:~0,2%"=="cu" ( set "cmd_options=--gpus all" ) else if /i "%TAG:~0,6%"=="jetson" ( set "cmd_options=--runtime nvidia --gpus all" set "py_vers=3.10 " ) else if /i "%TAG:~0,8%"=="rocm" ( set "cmd_options=--device=/dev/kfd --device=/dev/dri" ) else if /i "%TAG%"=="xpu" ( set "cmd_options=--device=/dev/dri" ) else if /i "%TAG%"=="mps" ( set "cmd_options=" ) else if /i "%TAG%"=="cpu" ( set "cmd_options=" ) set "HAS_COMPOSE=%errorlevel%" set "DOCKER_IMG_NAME=%DOCKER_IMG_NAME%:%TAG%" if %HAS_COMPOSE%==0 ( BUILD_NAME="%DOCKER_IMG_NAME%" docker compose --progress=plain build --no-cache ^ --build-arg PYTHON_VERSION="%py_vers%" ^ --build-arg APP_VERSION="%APP_VERSION%" ^ --build-arg DEVICE_TAG="%TAG%" ^ --build-arg DOCKER_DEVICE_STR="%ARG%" ^ --build-arg DOCKER_PROGRAMS_STR="%DOCKER_PROGRAMS%" ^ --build-arg CALIBRE_INSTALLER_URL="%DOCKER_CALIBRE_INSTALLER_URL%" ^ --build-arg ISO3_LANG="%ISO3_LANG%" if errorlevel 1 exit /b 1 ) else ( docker build --no-cache --progress plain ^ --build-arg PYTHON_VERSION="%py_vers%" ^ --build-arg APP_VERSION="%APP_VERSION%" ^ --build-arg DEVICE_TAG="%TAG%" ^ --build-arg DOCKER_DEVICE_STR="%ARG%" ^ --build-arg DOCKER_PROGRAMS_STR="%DOCKER_PROGRAMS%" ^ --build-arg CALIBRE_INSTALLER_URL="%DOCKER_CALIBRE_INSTALLER_URL%" ^ --build-arg ISO3_LANG="%ISO3_LANG%" ^ -t "%DOCKER_IMG_NAME%" . if errorlevel 1 exit /b 1 ) if defined cmd_options set "cmd_extra=%cmd_options% " echo Docker image ready! to run your docker:" echo GUI mode: echo docker run %cmd_extra%--rm -it -p 7860:7860 %DOCKER_IMG_NAME% echo Headless mode: echo docker run %cmd_extra%--rm -it -v "/my/real/ebooks/folder/absolute/path:/app/ebooks" -v "/my/real/output/folder/absolute/path:/app/audiobooks" -p 7860:7860 %DOCKER_IMG_NAME% --headless --ebook "/app/ebooks/myfile.pdf" [--voice /app/my/voicepath/voice.mp3 etc..] echo Docker Compose: echo DEVICE_TAG=%TAG% docker compose up -d echo Podman Compose: echo DEVICE_TAG=%TAG% podman-compose up -d exit /b 0 :::::::::::: END CORE FUNCTIONS :dispatch if "%OK_SCOOP%"=="0" ( if "%OK_PROGRAMS%"=="0" ( if "%OK_CONDA%"=="0" ( if "%OK_DOCKER%"=="0" ( goto :main ) else ( goto :failed ) ) ) ) echo OK_PROGRAMS: %OK_PROGRAMS% echo OK_CONDA: %OK_CONDA% echo OK_DOCKER: %OK_DOCKER% goto :install_programs exit /b :main if defined arguments.help ( if /I "!arguments.help!"=="true" ( where /Q conda if errorlevel 0 ( call conda activate "%SCRIPT_DIR%\%PYTHON_ENV%" call python "%SCRIPT_DIR%\app.py" %FORWARD_ARGS% call conda deactivate ) else ( echo Ebook2Audiobook must be installed before to run --help. ) goto :eof ) ) else ( if "%SCRIPT_MODE%"=="%BUILD_DOCKER%" ( if "!DOCKER_DEVICE_STR!"=="" ( call %PYTHON_SCOOP% --version >null 2>&1 || call scoop install %PYTHON_SCOOP% 2>null where /Q %PYTHON_SCOOP% if errorlevel 1 ( echo %ESC%[31m=============== %PYTHON_SCOOP% installation failed.%ESC%[0m goto :failed ) call :check_docker if errorlevel 1 goto :failed set "device_info=" for /f "usebackq delims=" %%A in (`call :check_device_info "%SCRIPT_MODE%"`) do ( set "device_info=%%A" ) if not defined device_info ( echo Device info check failed goto :failed ) if "%DEVICE_TAG%"=="" ( for /f "usebackq delims=" %%A in (`powershell -NoLogo -Command "(ConvertFrom-Json '%device_info%').tag"`) do ( set "TAG=%%A" ) ) else ( set "TAG=%DEVICE_TAG%" ) call docker image inspect "%DOCKER_IMG_NAME%:%TAG%" >nul 2>&1 if not errorlevel 1 ( echo [STOP] Docker image '%DOCKER_IMG_NAME%:%TAG%' already exists. Aborting build. echo Delete it using: docker rmi %DOCKER_IMG_NAME%:%TAG% --force goto :failed ) call :build_docker_image "%device_info%" if errorlevel 1 goto :failed ) else ( call :install_python_packages if errorlevel 1 goto :failed call :install_device_packages "%DOCKER_DEVICE_STR%" if errorlevel 1 goto :failed call :check_sitecustomized if errorlevel 1 goto :failed ) ) else ( call "%CONDA_HOME%\Scripts\activate.bat" call conda activate base call conda activate "%SCRIPT_DIR%\%PYTHON_ENV%" call :check_sitecustomized if errorlevel 1 goto :failed call :build_gui call python "%SCRIPT_DIR%\app.py" --script_mode %SCRIPT_MODE% %ARGS% call conda deactivate >nul && call conda deactivate >nul ) ) exit /b 0 :failed echo =============== ebook2audiobook is not correctly installed. where conda >nul 2>&1 && call conda deactivate >nul 2>&1 && call conda deactivate >nul exit /b 1 endlocal pause