mirror of
https://github.com/DrewThomasson/ebook2audiobook.git
synced 2026-01-08 05:23:55 -05:00
681 lines
23 KiB
Batchfile
Executable File
681 lines
23 KiB
Batchfile
Executable File
@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 |