mirror of
https://github.com/DrewThomasson/ebook2audiobook.git
synced 2026-01-09 13:58:14 -05:00
v25.12.28
This commit is contained in:
13
.github/workflows/Docker-Build.yml
vendored
13
.github/workflows/Docker-Build.yml
vendored
@@ -35,6 +35,7 @@ jobs:
|
||||
BASE_REF="${{ github.event.pull_request.base.ref }}"
|
||||
HEAD_REF="${{ github.event.pull_request.head.ref }}"
|
||||
HEAD_SHA="${{ github.event.pull_request.head.sha }}"
|
||||
HEAD_REPO_URL="${{ github.event.pull_request.head.repo.clone_url }}"
|
||||
TRIGGER_SHA="${{ github.sha }}"
|
||||
FRESH_CLONE=0
|
||||
|
||||
@@ -66,9 +67,17 @@ jobs:
|
||||
if [ "$IS_PR" = "true" ]; then
|
||||
echo "==> PR detected: simulating GitHub merge (base: $BASE_REF ← head: $HEAD_REF)"
|
||||
|
||||
# Fetch both branches
|
||||
# Fetch both branches. For the head, we must fetch from the actual source repo (fork)
|
||||
# because 'origin' might not have the fork's branch.
|
||||
git fetch origin "$BASE_REF":"origin/$BASE_REF"
|
||||
git fetch origin "$HEAD_REF":"origin/$HEAD_REF"
|
||||
|
||||
if [ -n "$HEAD_REPO_URL" ]; then
|
||||
echo "==> Fetching head from fork: $HEAD_REPO_URL"
|
||||
git fetch "$HEAD_REPO_URL" "$HEAD_REF":refs/remotes/origin/$HEAD_REF
|
||||
else
|
||||
# Fallback (shouldn't happen for PRs)
|
||||
git fetch origin "$HEAD_REF":"origin/$HEAD_REF"
|
||||
fi
|
||||
|
||||
# Reset to base branch
|
||||
git checkout -B "$BASE_REF" "remotes/origin/$BASE_REF"
|
||||
|
||||
21
.github/workflows/E2A-Test.yml
vendored
21
.github/workflows/E2A-Test.yml
vendored
@@ -95,6 +95,7 @@ jobs:
|
||||
BASE_REF="${{ github.event.pull_request.base.ref }}"
|
||||
HEAD_REF="${{ github.event.pull_request.head.ref }}"
|
||||
HEAD_SHA="${{ github.event.pull_request.head.sha }}"
|
||||
HEAD_REPO_URL="${{ github.event.pull_request.head.repo.clone_url }}"
|
||||
TRIGGER_SHA="${{ github.sha }}"
|
||||
FRESH_CLONE=0
|
||||
|
||||
@@ -126,9 +127,17 @@ jobs:
|
||||
if [ "$IS_PR" = "true" ]; then
|
||||
echo "==> PR detected: simulating GitHub merge (base: $BASE_REF ← head: $HEAD_REF)"
|
||||
|
||||
# Fetch both branches
|
||||
# Fetch both branches. For the head, we must fetch from the actual source repo (fork)
|
||||
# because 'origin' might not have the fork's branch.
|
||||
git fetch origin "$BASE_REF":"origin/$BASE_REF"
|
||||
git fetch origin "$HEAD_REF":"origin/$HEAD_REF"
|
||||
|
||||
if [ -n "$HEAD_REPO_URL" ]; then
|
||||
echo "==> Fetching head from fork: $HEAD_REPO_URL"
|
||||
git fetch "$HEAD_REPO_URL" "$HEAD_REF":refs/remotes/origin/$HEAD_REF
|
||||
else
|
||||
# Fallback (shouldn't happen for PRs)
|
||||
git fetch origin "$HEAD_REF":"origin/$HEAD_REF"
|
||||
fi
|
||||
|
||||
# Reset to base branch
|
||||
git checkout -B "$BASE_REF" "remotes/origin/$BASE_REF"
|
||||
@@ -238,6 +247,14 @@ jobs:
|
||||
|
||||
$RUN_CMD --headless --language eng --ebook "tools/workflow-testing/test1.txt" --tts_engine VITS --voice "voices/eng/elder/male/DavidAttenborough.wav" --output_dir ~/ebook2audiobook/audiobooks/VITS
|
||||
|
||||
- name: Hungarian VITS Custom-Voice headless single test
|
||||
shell: bash
|
||||
run: |
|
||||
cd ~/ebook2audiobook
|
||||
if [ "$IS_WINDOWS" != "true" ]; then conda deactivate 2>/dev/null || true; fi
|
||||
|
||||
$RUN_CMD --headless --language hun --ebook "tools/workflow-testing/hun-test.txt" --tts_engine VITS --voice "voices/eng/elder/male/DavidAttenborough.wav" --output_dir ~/ebook2audiobook/audiobooks/VITS
|
||||
|
||||
- name: English YOURTTS Custom-Voice headless batch test
|
||||
shell: bash
|
||||
run: |
|
||||
|
||||
@@ -3,7 +3,7 @@ FROM python:${PYTHON_VERSION}-slim-bookworm
|
||||
|
||||
SHELL ["/bin/bash", "-o", "pipefail", "-c"]
|
||||
|
||||
ARG APP_VERSION=25.25.25
|
||||
ARG APP_VERSION=25.12.28
|
||||
ARG DEVICE_TAG=cpu
|
||||
ARG DOCKER_DEVICE_STR='{"name": "cpu", "os": "manylinux_2_28", "arch": "x86_64", "pyvenv": [3, 12], "tag": "cpu", "note": "default device"}'
|
||||
ARG DOCKER_PROGRAMS_STR="curl ffmpeg nodejs npm espeak-ng sox tesseract-ocr"
|
||||
|
||||
@@ -142,7 +142,7 @@ class XTTSv2(TTSUtils, TTSRegistry, name='xtts'):
|
||||
audio_sentence = result.get('wav')
|
||||
if is_audio_data_valid(audio_sentence):
|
||||
if isinstance(audio_sentence, torch.Tensor):
|
||||
audio_tensor = audio_sentence.detach().cpu().unsqueeze(0)
|
||||
audio_tensor = audio_sentence.detach().unsqueeze(0)
|
||||
elif isinstance(audio_sentence, np.ndarray):
|
||||
audio_tensor = torch.from_numpy(audio_sentence).unsqueeze(0)
|
||||
elif isinstance(audio_sentence, (list, tuple)):
|
||||
@@ -151,6 +151,7 @@ class XTTSv2(TTSUtils, TTSRegistry, name='xtts'):
|
||||
error = f"Unsupported XTTSv2 wav type: {type(audio_sentence)}"
|
||||
print(error)
|
||||
return False
|
||||
audio_tensor = audio_tensor.cpu()
|
||||
if sentence[-1].isalnum() or sentence[-1] == '—':
|
||||
audio_tensor = trim_audio(audio_tensor.squeeze(), self.params['samplerate'], 0.001, trim_audio_buffer).unsqueeze(0)
|
||||
if audio_tensor is not None and audio_tensor.numel() > 0:
|
||||
|
||||
@@ -1072,8 +1072,9 @@ def build_interface(args:dict)->gr.Blocks:
|
||||
selected_name = os.path.basename(custom_model)
|
||||
shutil.rmtree(custom_model, ignore_errors=True)
|
||||
msg = f'Custom model {selected_name} deleted!'
|
||||
if session['custom_model'] in session['voice']:
|
||||
session['voice'] = models[session['fine_tuned']]['voice']
|
||||
if session['custom_model'] is not None and session['voice'] is not None:
|
||||
if session['custom_model'] in session['voice']:
|
||||
session['voice'] = models[session['fine_tuned']]['voice']
|
||||
session['custom_model'] = None
|
||||
show_alert({"type": "warning", "msg": msg})
|
||||
return update_gr_custom_model_list(id), gr.update(), gr.update(value='', visible=False), gr.update()
|
||||
@@ -1161,24 +1162,24 @@ def build_interface(args:dict)->gr.Blocks:
|
||||
voice_options = [('Default', None)] + sorted(voice_options, key=lambda x: x[0].lower())
|
||||
else:
|
||||
voice_options = sorted(voice_options, key=lambda x: x[0].lower())
|
||||
if session['voice'] is None and voice_options and voice_options[0][1] is not None:
|
||||
session['voice'] = models[session['fine_tuned']]['voice']
|
||||
if session['voice'] is not None:
|
||||
if not any(v[1] == session['voice'] for v in voice_options):
|
||||
new_voice_path = session['voice'].replace('/eng/', f"/{session['language']}/")
|
||||
if os.path.exists(new_voice_path):
|
||||
session['voice'] = new_voice_path
|
||||
else:
|
||||
fallback_voice = None
|
||||
try:
|
||||
if isinstance(models, dict):
|
||||
fine_tuned_cfg = models.get(session.get('fine_tuned'))
|
||||
if isinstance(fine_tuned_cfg, dict):
|
||||
fallback_voice = fine_tuned_cfg.get('voice')
|
||||
except Exception as e:
|
||||
error = f"update_gr_voice_list() - failed to resolve fallback_voice: {e}!"
|
||||
alert_exception(error, id)
|
||||
session['voice'] = fallback_voice
|
||||
if session['voice_dir'] not in session['voice']:
|
||||
if not any(v[1] == session['voice'] for v in voice_options):
|
||||
voice_path = Path(session['voice'])
|
||||
parts = list(voice_path.parts)
|
||||
if "voices" in parts:
|
||||
idx = parts.index("voices")
|
||||
if idx + 1 < len(parts):
|
||||
parts[idx + 1] = session['language']
|
||||
new_voice_path = str(Path(*parts))
|
||||
if os.path.exists(new_voice_path):
|
||||
session['voice'] = new_voice_path
|
||||
else:
|
||||
parts[idx + 1] = 'eng'
|
||||
session['voice'] = str(Path(*parts))
|
||||
else:
|
||||
if voice_options and voice_options[0][1] is not None:
|
||||
session['voice'] = models[session['fine_tuned']]['voice']
|
||||
return gr.update(choices=voice_options, value=session['voice'])
|
||||
except Exception as e:
|
||||
error = f'update_gr_voice_list(): {e}!'
|
||||
@@ -1254,9 +1255,6 @@ def build_interface(args:dict)->gr.Blocks:
|
||||
if session:
|
||||
prev = session['language']
|
||||
session['language'] = selected
|
||||
if session['voice'] is not None:
|
||||
if voice_options:
|
||||
session['voice'] = voice_options[0][1]
|
||||
return (
|
||||
gr.update(value=session['language']),
|
||||
update_gr_tts_engine_list(id),
|
||||
|
||||
312
uninstall.cmd
312
uninstall.cmd
@@ -1,157 +1,157 @@
|
||||
@echo off
|
||||
setlocal enabledelayedexpansion
|
||||
|
||||
:: ---------------------------------------
|
||||
:: CONFIG
|
||||
:: ---------------------------------------
|
||||
set "APP_NAME=ebook2audiobook"
|
||||
set "SCRIPT_DIR=%~dp0"
|
||||
set "STARTMENU_DIR=%APPDATA%\Microsoft\Windows\Start Menu\Programs\%APP_NAME%"
|
||||
set "DESKTOP_LNK=%USERPROFILE%\Desktop\%APP_NAME%.lnk"
|
||||
set "INSTALLED_LOG=%SCRIPT_DIR%.installed"
|
||||
set "MINIFORGE_PATH=%USERPROFILE%\Miniforge3"
|
||||
set "SELF=%~f0"
|
||||
set "TEMP_UNINSTALL=%TEMP%\%APP_NAME%_uninstall.cmd"
|
||||
:: ---------------------------------------
|
||||
|
||||
echo.
|
||||
echo ========================================================
|
||||
echo %APP_NAME% — Uninstaller (Secure & Verified Execution)
|
||||
echo ========================================================
|
||||
echo Running from: %SELF%
|
||||
echo.
|
||||
|
||||
:: ---------------------------------------
|
||||
:: SELF-RELAUNCH FROM TEMP (SAFER DESIGN)
|
||||
:: ---------------------------------------
|
||||
if /i not "%SELF%"=="%TEMP_UNINSTALL%" (
|
||||
echo Preparing safe removal environment...
|
||||
echo Copying uninstaller to TEMP: %TEMP_UNINSTALL%
|
||||
copy "%SELF%" "%TEMP_UNINSTALL%" >nul
|
||||
|
||||
echo Relaunching installer from safe location...
|
||||
start "" "%TEMP_UNINSTALL%" "%SCRIPT_DIR%"
|
||||
echo Cleaning handoff...
|
||||
exit /b
|
||||
)
|
||||
|
||||
:: At this point, the script runs safely from temp
|
||||
if "%~1"=="" (
|
||||
echo [ERROR] Install directory missing — cannot continue.
|
||||
pause
|
||||
exit /b 1
|
||||
)
|
||||
set "REAL_INSTALL_DIR=%~1"
|
||||
|
||||
echo.
|
||||
echo ========================================
|
||||
echo Uninstalling %APP_NAME%
|
||||
echo ========================================
|
||||
echo.
|
||||
|
||||
choice /M "Proceed with uninstall?" /C YN
|
||||
if errorlevel 2 exit /b
|
||||
|
||||
cd /d "%REAL_INSTALL_DIR%\.."
|
||||
|
||||
:: ---------------------------------------
|
||||
:: KILL PROCESSES (POLITE FIRST)
|
||||
:: ---------------------------------------
|
||||
echo.
|
||||
echo Checking for running program instances...
|
||||
|
||||
tasklist | find /i "%APP_NAME%.exe" >nul && (
|
||||
echo %APP_NAME%.exe is currently running.
|
||||
choice /M "Terminate it to continue?" /C YN
|
||||
if errorlevel 1 taskkill /IM "%APP_NAME%.exe" /F >nul 2>&1
|
||||
)
|
||||
|
||||
tasklist | find /i "python.exe" >nul && (
|
||||
echo Python is active and may be linked to the app.
|
||||
choice /M "Close python.exe automatically?" /C YN
|
||||
if errorlevel 1 taskkill /IM "python.exe" /F >nul 2>&1
|
||||
)
|
||||
|
||||
:: ---------------------------------------
|
||||
:: PROCESS .installed PACKAGES
|
||||
:: ---------------------------------------
|
||||
set "REMOVE_MINIFORGE="
|
||||
set "SCOOP_PRESENT="
|
||||
|
||||
if exist "%INSTALLED_LOG%" (
|
||||
echo.
|
||||
echo Reading .installed packages list...
|
||||
|
||||
for /f "usebackq delims=" %%A in ("%INSTALLED_LOG%") do (
|
||||
set "ITEM=%%A"
|
||||
if "!ITEM!"=="" (continue)
|
||||
|
||||
if /i "!ITEM!"=="Miniforge3" (
|
||||
set "REMOVE_MINIFORGE=1"
|
||||
echo Marked Miniforge3 for removal...
|
||||
continue
|
||||
)
|
||||
|
||||
if /i "!ITEM!"=="scoop" (
|
||||
set "SCOOP_PRESENT=1"
|
||||
echo Scoop presence detected — will remove at end...
|
||||
continue
|
||||
)
|
||||
|
||||
echo Uninstalling package using Scoop: !ITEM!
|
||||
scoop uninstall "!ITEM!" >nul 2>&1
|
||||
)
|
||||
)
|
||||
|
||||
:: ---------------------------------------
|
||||
:: REMOVE MINIFORGE3
|
||||
:: ---------------------------------------
|
||||
if defined REMOVE_MINIFORGE (
|
||||
if exist "%MINIFORGE_PATH%" (
|
||||
echo.
|
||||
echo Removing Miniforge3: %MINIFORGE_PATH%
|
||||
rd /s /q "%MINIFORGE_PATH%" >nul 2>&1
|
||||
)
|
||||
)
|
||||
|
||||
:: ---------------------------------------
|
||||
:: DEFERRED SCOOP UNINSTALL
|
||||
:: ---------------------------------------
|
||||
if defined SCOOP_PRESENT (
|
||||
echo.
|
||||
echo Removing Scoop and cleanup...
|
||||
start "" cmd /c "ping 127.0.0.1 -n 3 >nul & scoop uninstall scoop >nul 2>&1 & rd /s /q "%USERPROFILE%\scoop" >nul 2>&1"
|
||||
)
|
||||
|
||||
:: ---------------------------------------
|
||||
:: REMOVE SHORTCUTS AND REGISTRY
|
||||
:: ---------------------------------------
|
||||
echo.
|
||||
echo Removing Menu entries & Desktop shortcuts...
|
||||
|
||||
if exist "%STARTMENU_DIR%" rd /s /q "%STARTMENU_DIR%" >nul 2>&1
|
||||
if exist "%DESKTOP_LNK%" del /q "%DESKTOP_LNK%" >nul 2>&1
|
||||
|
||||
reg delete "HKCU\Software\Microsoft\Windows\CurrentVersion\Uninstall\ebook2audiobook" /f >nul 2>&1
|
||||
|
||||
:: ---------------------------------------
|
||||
:: DELETE THE ACTUAL APP FOLDER
|
||||
:: ---------------------------------------
|
||||
echo.
|
||||
echo Removing application directory:
|
||||
echo %REAL_INSTALL_DIR%
|
||||
rd /s /q "%REAL_INSTALL_DIR%" >nul 2>&1
|
||||
|
||||
:: ---------------------------------------
|
||||
:: DELETE SELF COPY & EXIT
|
||||
:: ---------------------------------------
|
||||
echo.
|
||||
echo ============================
|
||||
echo Uninstallation complete.
|
||||
echo ============================
|
||||
echo Cleaning temporary uninstaller...
|
||||
|
||||
del "%TEMP_UNINSTALL%" >nul 2>&1
|
||||
|
||||
timeout /t 2 >nul
|
||||
@echo off
|
||||
setlocal enabledelayedexpansion
|
||||
|
||||
:: ---------------------------------------
|
||||
:: CONFIG
|
||||
:: ---------------------------------------
|
||||
set "APP_NAME=ebook2audiobook"
|
||||
set "SCRIPT_DIR=%~dp0"
|
||||
set "STARTMENU_DIR=%APPDATA%\Microsoft\Windows\Start Menu\Programs\%APP_NAME%"
|
||||
set "DESKTOP_LNK=%USERPROFILE%\Desktop\%APP_NAME%.lnk"
|
||||
set "INSTALLED_LOG=%SCRIPT_DIR%.installed"
|
||||
set "MINIFORGE_PATH=%USERPROFILE%\Miniforge3"
|
||||
set "SELF=%~f0"
|
||||
set "TEMP_UNINSTALL=%TEMP%\%APP_NAME%_uninstall.cmd"
|
||||
:: ---------------------------------------
|
||||
|
||||
echo.
|
||||
echo ========================================================
|
||||
echo %APP_NAME% — Uninstaller (Secure & Verified Execution)
|
||||
echo ========================================================
|
||||
echo Running from: %SELF%
|
||||
echo.
|
||||
|
||||
:: ---------------------------------------
|
||||
:: SELF-RELAUNCH FROM TEMP (SAFER DESIGN)
|
||||
:: ---------------------------------------
|
||||
if /i not "%SELF%"=="%TEMP_UNINSTALL%" (
|
||||
echo Preparing safe removal environment...
|
||||
echo Copying uninstaller to TEMP: %TEMP_UNINSTALL%
|
||||
copy "%SELF%" "%TEMP_UNINSTALL%" >nul
|
||||
|
||||
echo Relaunching uninstaller from safe location...
|
||||
start "" cmd /c "%TEMP_UNINSTALL%" "%SCRIPT_DIR%"
|
||||
echo Cleaning handoff...
|
||||
exit /b
|
||||
)
|
||||
|
||||
:: At this point, the script runs safely from temp
|
||||
if "%~1"=="" (
|
||||
echo [ERROR] Install directory missing — cannot continue.
|
||||
pause
|
||||
exit /b 1
|
||||
)
|
||||
set "REAL_INSTALL_DIR=%~1"
|
||||
|
||||
echo.
|
||||
echo ========================================
|
||||
echo Uninstalling %APP_NAME%
|
||||
echo ========================================
|
||||
echo.
|
||||
|
||||
choice /M "Proceed with uninstall?" /C YN
|
||||
if errorlevel 2 exit /b
|
||||
|
||||
cd /d "%REAL_INSTALL_DIR%\.."
|
||||
|
||||
:: ---------------------------------------
|
||||
:: KILL PROCESSES (POLITE FIRST)
|
||||
:: ---------------------------------------
|
||||
echo.
|
||||
echo Checking for running program instances...
|
||||
|
||||
tasklist | find /i "%APP_NAME%.exe" >nul && (
|
||||
echo %APP_NAME%.exe is currently running.
|
||||
choice /M "Terminate it to continue?" /C YN
|
||||
if errorlevel 1 taskkill /IM "%APP_NAME%.exe" /F >nul 2>&1
|
||||
)
|
||||
|
||||
tasklist | find /i "python.exe" >nul && (
|
||||
echo Python is active and may be linked to the app.
|
||||
choice /M "Close python.exe automatically?" /C YN
|
||||
if errorlevel 1 taskkill /IM "python.exe" /F >nul 2>&1
|
||||
)
|
||||
|
||||
:: ---------------------------------------
|
||||
:: PROCESS .installed PACKAGES
|
||||
:: ---------------------------------------
|
||||
set "REMOVE_MINIFORGE="
|
||||
set "SCOOP_PRESENT="
|
||||
|
||||
if exist "%INSTALLED_LOG%" (
|
||||
echo.
|
||||
echo Reading .installed packages list...
|
||||
|
||||
for /f "usebackq delims=" %%A in ("%INSTALLED_LOG%") do (
|
||||
set "ITEM=%%A"
|
||||
if "!ITEM!"=="" (continue)
|
||||
|
||||
if /i "!ITEM!"=="Miniforge3" (
|
||||
set "REMOVE_MINIFORGE=1"
|
||||
echo Marked Miniforge3 for removal...
|
||||
continue
|
||||
)
|
||||
|
||||
if /i "!ITEM!"=="scoop" (
|
||||
set "SCOOP_PRESENT=1"
|
||||
echo Scoop presence detected — will remove at end...
|
||||
continue
|
||||
)
|
||||
|
||||
echo Uninstalling package using Scoop: !ITEM!
|
||||
scoop uninstall "!ITEM!" >nul 2>&1
|
||||
)
|
||||
)
|
||||
|
||||
:: ---------------------------------------
|
||||
:: REMOVE MINIFORGE3
|
||||
:: ---------------------------------------
|
||||
if defined REMOVE_MINIFORGE (
|
||||
if exist "%MINIFORGE_PATH%" (
|
||||
echo.
|
||||
echo Removing Miniforge3: %MINIFORGE_PATH%
|
||||
rd /s /q "%MINIFORGE_PATH%" >nul 2>&1
|
||||
)
|
||||
)
|
||||
|
||||
:: ---------------------------------------
|
||||
:: DEFERRED SCOOP UNINSTALL
|
||||
:: ---------------------------------------
|
||||
if defined SCOOP_PRESENT (
|
||||
echo.
|
||||
echo Removing Scoop and cleanup...
|
||||
start "" cmd /c "ping 127.0.0.1 -n 3 >nul & scoop uninstall scoop >nul 2>&1 & rd /s /q "%USERPROFILE%\scoop" >nul 2>&1"
|
||||
)
|
||||
|
||||
:: ---------------------------------------
|
||||
:: REMOVE SHORTCUTS AND REGISTRY
|
||||
:: ---------------------------------------
|
||||
echo.
|
||||
echo Removing Menu entries & Desktop shortcuts...
|
||||
|
||||
if exist "%STARTMENU_DIR%" rd /s /q "%STARTMENU_DIR%" >nul 2>&1
|
||||
if exist "%DESKTOP_LNK%" del /q "%DESKTOP_LNK%" >nul 2>&1
|
||||
|
||||
reg delete "HKCU\Software\Microsoft\Windows\CurrentVersion\Uninstall\ebook2audiobook" /f >nul 2>&1
|
||||
|
||||
:: ---------------------------------------
|
||||
:: DELETE THE ACTUAL APP FOLDER
|
||||
:: ---------------------------------------
|
||||
echo.
|
||||
echo Removing application directory:
|
||||
echo %REAL_INSTALL_DIR%
|
||||
rd /s /q "%REAL_INSTALL_DIR%" >nul 2>&1
|
||||
|
||||
:: ---------------------------------------
|
||||
:: DELETE SELF COPY & EXIT
|
||||
:: ---------------------------------------
|
||||
echo.
|
||||
echo ============================
|
||||
echo Uninstallation complete.
|
||||
echo ============================
|
||||
echo Cleaning temporary uninstaller...
|
||||
|
||||
del "%TEMP_UNINSTALL%" >nul 2>&1
|
||||
|
||||
timeout /t 2 >nul
|
||||
exit /b
|
||||
Reference in New Issue
Block a user