diff --git a/README.md b/README.md index abd9b86..99cc48e 100644 --- a/README.md +++ b/README.md @@ -229,7 +229,7 @@ python -m pytest Unit tests cover the queue service, web routes, and conversion pipeline helpers. Contributions that add features should include new tests whenever practical. ## Upgrading from the desktop GUI -The legacy PyQt5 interface is no longer packaged. Existing scripts that call `abogen.main` should switch to the new web entry point (`abogen.web.app:main`). The new experience works headlessly, plays nicely in Docker, and exposes JSON APIs for automation. +The legacy PyQt5 interface is no longer packaged. Existing scripts that call `abogen.main` should switch to the new web entry point (`abogen.webui.app:main`). The new experience works headlessly, plays nicely in Docker, and exposes JSON APIs for automation. ## Troubleshooting - Conversion jobs stay pending → ensure the background worker has write access to the upload/output directories. diff --git a/abogen/main.py b/abogen/main.py index 0dadfab..90a55e1 100644 --- a/abogen/main.py +++ b/abogen/main.py @@ -9,7 +9,7 @@ import signal import sys from abogen.utils import load_config, prevent_sleep_end -from abogen.web.app import main as _run_web_ui +from abogen.webui.app import main as _run_web_ui # Configure Hugging Face Hub behaviour (mirrors legacy GUI defaults). os.environ.setdefault("HF_HUB_DISABLE_TELEMETRY", "1") diff --git a/abogen/webui/app.py b/abogen/webui/app.py index 4e913a2..211bfcb 100644 --- a/abogen/webui/app.py +++ b/abogen/webui/app.py @@ -95,7 +95,7 @@ def create_app(config: Optional[dict[str, Any]] = None) -> Flask: ) app.extensions["conversion_service"] = service - from abogen.web.routes import ( + from abogen.webui.routes import ( main_bp, jobs_bp, settings_bp, diff --git a/abogen/webui/debug_tts_runner.py b/abogen/webui/debug_tts_runner.py index a2e0e70..abfab4c 100644 --- a/abogen/webui/debug_tts_runner.py +++ b/abogen/webui/debug_tts_runner.py @@ -14,7 +14,7 @@ from abogen.kokoro_text_normalization import normalize_for_pipeline from abogen.normalization_settings import build_apostrophe_config from abogen.text_extractor import extract_from_path from abogen.voice_cache import ensure_voice_assets -from abogen.web.conversion_runner import SAMPLE_RATE, SPLIT_PATTERN, _select_device, _to_float32, _resolve_voice, _spec_to_voice_ids +from abogen.webui.conversion_runner import SAMPLE_RATE, SPLIT_PATTERN, _select_device, _to_float32, _resolve_voice, _spec_to_voice_ids from abogen.utils import load_numpy_kpipeline @@ -36,7 +36,7 @@ def _resolve_voice_setting(value: str) -> tuple[str, Optional[str], Optional[str Returns (resolved_voice_spec, profile_name, profile_language). """ - from abogen.web.routes.utils.voice import resolve_voice_setting + from abogen.webui.routes.utils.voice import resolve_voice_setting return resolve_voice_setting(value) diff --git a/abogen/webui/routes/__init__.py b/abogen/webui/routes/__init__.py index 827a911..6cf27f0 100644 --- a/abogen/webui/routes/__init__.py +++ b/abogen/webui/routes/__init__.py @@ -1,10 +1,10 @@ -from abogen.web.routes.main import main_bp -from abogen.web.routes.jobs import jobs_bp -from abogen.web.routes.settings import settings_bp -from abogen.web.routes.voices import voices_bp -from abogen.web.routes.entities import entities_bp -from abogen.web.routes.books import books_bp -from abogen.web.routes.api import api_bp +from abogen.webui.routes.main import main_bp +from abogen.webui.routes.jobs import jobs_bp +from abogen.webui.routes.settings import settings_bp +from abogen.webui.routes.voices import voices_bp +from abogen.webui.routes.entities import entities_bp +from abogen.webui.routes.books import books_bp +from abogen.webui.routes.api import api_bp __all__ = [ "main_bp", diff --git a/abogen/webui/routes/api.py b/abogen/webui/routes/api.py index 57b1579..98e06d7 100644 --- a/abogen/webui/routes/api.py +++ b/abogen/webui/routes/api.py @@ -6,7 +6,7 @@ from pathlib import Path from flask import Blueprint, request, jsonify, send_file, url_for, current_app from flask.typing import ResponseReturnValue -from abogen.web.routes.utils.settings import ( +from abogen.webui.routes.utils.settings import ( load_settings, load_integration_settings, coerce_float, @@ -22,9 +22,9 @@ from abogen.voice_profiles import ( export_profiles_payload, normalize_profile_entry, ) -from abogen.web.routes.utils.common import split_profile_spec -from abogen.web.routes.utils.preview import synthesize_preview, generate_preview_audio -from abogen.web.routes.utils.voice import formula_from_profile +from abogen.webui.routes.utils.common import split_profile_spec +from abogen.webui.routes.utils.preview import synthesize_preview, generate_preview_audio +from abogen.webui.routes.utils.voice import formula_from_profile from abogen.normalization_settings import ( build_llm_configuration, build_apostrophe_config, @@ -37,8 +37,8 @@ from abogen.integrations.calibre_opds import ( CalibreOPDSClient, CalibreOPDSError, ) -from abogen.web.routes.utils.service import get_service -from abogen.web.routes.utils.form import build_pending_job_from_extraction +from abogen.webui.routes.utils.service import get_service +from abogen.webui.routes.utils.form import build_pending_job_from_extraction from abogen.text_extractor import extract_from_path from werkzeug.utils import secure_filename diff --git a/abogen/webui/routes/books.py b/abogen/webui/routes/books.py index f4b862f..e0bfa51 100644 --- a/abogen/webui/routes/books.py +++ b/abogen/webui/routes/books.py @@ -3,11 +3,11 @@ from typing import Any, Dict from flask import Blueprint, render_template from flask.typing import ResponseReturnValue -from abogen.web.routes.utils.settings import ( +from abogen.webui.routes.utils.settings import ( load_settings, load_integration_settings, ) -from abogen.web.routes.utils.voice import template_options +from abogen.webui.routes.utils.voice import template_options books_bp = Blueprint("books", __name__) diff --git a/abogen/webui/routes/entities.py b/abogen/webui/routes/entities.py index f1c7242..44bd420 100644 --- a/abogen/webui/routes/entities.py +++ b/abogen/webui/routes/entities.py @@ -2,16 +2,16 @@ from typing import Mapping from flask import Blueprint, request, jsonify, abort, render_template, redirect, url_for from flask.typing import ResponseReturnValue -from abogen.web.routes.utils.service import require_pending_job, get_service -from abogen.web.routes.utils.entity import ( +from abogen.webui.routes.utils.service import require_pending_job, get_service +from abogen.webui.routes.utils.entity import ( refresh_entity_summary, pending_entities_payload, upsert_manual_override, delete_manual_override, search_manual_override_candidates, ) -from abogen.web.routes.utils.settings import coerce_int, load_settings -from abogen.web.routes.utils.voice import template_options +from abogen.webui.routes.utils.settings import coerce_int, load_settings +from abogen.webui.routes.utils.voice import template_options from abogen.pronunciation_store import ( delete_override as delete_pronunciation_override, save_override as save_pronunciation_override, diff --git a/abogen/webui/routes/jobs.py b/abogen/webui/routes/jobs.py index b546f5e..86d6ac8 100644 --- a/abogen/webui/routes/jobs.py +++ b/abogen/webui/routes/jobs.py @@ -6,25 +6,25 @@ from typing import Any, Dict, Optional from flask import Blueprint, Response, abort, redirect, render_template, request, url_for, send_file from flask.typing import ResponseReturnValue -from abogen.web.service import ( +from abogen.webui.service import ( JobStatus, load_audiobookshelf_chapters, build_audiobookshelf_metadata, ) -from abogen.web.routes.utils.service import get_service -from abogen.web.routes.utils.form import render_jobs_panel -from abogen.web.routes.utils.voice import template_options -from abogen.web.routes.utils.epub import ( +from abogen.webui.routes.utils.service import get_service +from abogen.webui.routes.utils.form import render_jobs_panel +from abogen.webui.routes.utils.voice import template_options +from abogen.webui.routes.utils.epub import ( job_download_flags, locate_job_epub, locate_job_audio, ) -from abogen.web.routes.utils.settings import ( +from abogen.webui.routes.utils.settings import ( stored_integration_config, build_audiobookshelf_config, coerce_bool, ) -from abogen.web.routes.utils.common import existing_paths +from abogen.webui.routes.utils.common import existing_paths from abogen.integrations.audiobookshelf import AudiobookshelfClient, AudiobookshelfUploadError logger = logging.getLogger(__name__) diff --git a/abogen/webui/routes/main.py b/abogen/webui/routes/main.py index 29a25c2..f01b752 100644 --- a/abogen/webui/routes/main.py +++ b/abogen/webui/routes/main.py @@ -7,11 +7,11 @@ from typing import Any, Dict, Optional, cast from flask import Blueprint, redirect, render_template, request, url_for, jsonify, current_app from werkzeug.utils import secure_filename -from abogen.web.service import PendingJob, JobStatus -from abogen.web.routes.utils.service import get_service, remove_pending_job, submit_job -from abogen.web.routes.utils.settings import load_settings -from abogen.web.routes.utils.voice import template_options -from abogen.web.routes.utils.form import ( +from abogen.webui.service import PendingJob, JobStatus +from abogen.webui.routes.utils.service import get_service, remove_pending_job, submit_job +from abogen.webui.routes.utils.settings import load_settings +from abogen.webui.routes.utils.voice import template_options +from abogen.webui.routes.utils.form import ( normalize_wizard_step, wants_wizard_json, render_wizard_partial, diff --git a/abogen/webui/routes/settings.py b/abogen/webui/routes/settings.py index c6c745c..d0bb991 100644 --- a/abogen/webui/routes/settings.py +++ b/abogen/webui/routes/settings.py @@ -6,7 +6,7 @@ from typing import Any from flask import Blueprint, current_app, render_template, request, redirect, url_for, flash, send_file, abort from flask.typing import ResponseReturnValue -from abogen.web.routes.utils.settings import ( +from abogen.webui.routes.utils.settings import ( load_settings, load_integration_settings, save_settings, @@ -19,8 +19,8 @@ from abogen.web.routes.utils.settings import ( _NORMALIZATION_STRING_KEYS, _DEFAULT_ANALYSIS_THRESHOLD, ) -from abogen.web.routes.utils.voice import template_options -from abogen.web.debug_tts_runner import run_debug_tts_wavs +from abogen.webui.routes.utils.voice import template_options +from abogen.webui.debug_tts_runner import run_debug_tts_wavs from abogen.debug_tts_samples import DEBUG_TTS_SAMPLES from abogen.utils import get_user_output_path, load_config diff --git a/abogen/webui/routes/utils/entity.py b/abogen/webui/routes/utils/entity.py index 9af88ef..f1acfea 100644 --- a/abogen/webui/routes/utils/entity.py +++ b/abogen/webui/routes/utils/entity.py @@ -2,7 +2,7 @@ import time import uuid from typing import Any, Dict, Iterable, List, Mapping, Optional -from abogen.web.service import PendingJob +from abogen.webui.service import PendingJob from abogen.entity_analysis import ( extract_entities, merge_override, @@ -16,7 +16,7 @@ from abogen.pronunciation_store import ( save_override as save_pronunciation_override, search_overrides as search_pronunciation_overrides, ) -from abogen.web.routes.utils.settings import load_settings +from abogen.webui.routes.utils.settings import load_settings from abogen.heteronym_overrides import extract_heteronym_overrides def collect_pronunciation_overrides(pending: PendingJob) -> List[Dict[str, Any]]: diff --git a/abogen/webui/routes/utils/epub.py b/abogen/webui/routes/utils/epub.py index 302032a..00a00c6 100644 --- a/abogen/webui/routes/utils/epub.py +++ b/abogen/webui/routes/utils/epub.py @@ -7,7 +7,7 @@ from pathlib import Path from typing import Any, Dict, Iterable, List, Mapping, Optional, Set, Tuple from xml.etree import ElementTree as ET -from abogen.web.service import Job, JobStatus +from abogen.webui.service import Job, JobStatus def _coerce_path(value: Any) -> Optional[Path]: if isinstance(value, Path): diff --git a/abogen/webui/routes/utils/form.py b/abogen/webui/routes/utils/form.py index 7fd33cc..41d605e 100644 --- a/abogen/webui/routes/utils/form.py +++ b/abogen/webui/routes/utils/form.py @@ -5,9 +5,9 @@ from typing import Any, Dict, Iterable, List, Mapping, Optional, Tuple, cast from flask import request, render_template, jsonify from flask.typing import ResponseReturnValue -from abogen.web.service import PendingJob, JobStatus -from abogen.web.routes.utils.service import get_service -from abogen.web.routes.utils.settings import ( +from abogen.webui.service import PendingJob, JobStatus +from abogen.webui.routes.utils.service import get_service +from abogen.webui.routes.utils.settings import ( load_settings, coerce_bool, coerce_int, @@ -18,7 +18,7 @@ from abogen.web.routes.utils.settings import ( SAVE_MODE_LABELS, audiobookshelf_manual_available, ) -from abogen.web.routes.utils.voice import ( +from abogen.webui.routes.utils.voice import ( parse_voice_formula, formula_from_profile, resolve_voice_setting, @@ -26,9 +26,9 @@ from abogen.web.routes.utils.voice import ( prepare_speaker_metadata, template_options, ) -from abogen.web.routes.utils.entity import sync_pronunciation_overrides -from abogen.web.routes.utils.epub import job_download_flags -from abogen.web.routes.utils.common import split_profile_spec +from abogen.webui.routes.utils.entity import sync_pronunciation_overrides +from abogen.webui.routes.utils.epub import job_download_flags +from abogen.webui.routes.utils.common import split_profile_spec from abogen.utils import calculate_text_length from abogen.voice_profiles import serialize_profiles, normalize_profile_entry from abogen.chunking import ChunkLevel, build_chunks_for_chapters diff --git a/abogen/webui/routes/utils/preview.py b/abogen/webui/routes/utils/preview.py index 95bf6ba..95c3040 100644 --- a/abogen/webui/routes/utils/preview.py +++ b/abogen/webui/routes/utils/preview.py @@ -75,7 +75,7 @@ def generate_preview_audio( source_text = text if pronunciation_overrides or manual_overrides or speakers: try: - from abogen.web import conversion_runner as runner + from abogen.webui import conversion_runner as runner class _PreviewJob: def __init__(self): diff --git a/abogen/webui/routes/utils/service.py b/abogen/webui/routes/utils/service.py index 1cae32a..b48f94a 100644 --- a/abogen/webui/routes/utils/service.py +++ b/abogen/webui/routes/utils/service.py @@ -1,6 +1,6 @@ from typing import cast from flask import current_app, abort -from abogen.web.service import ConversionService, PendingJob +from abogen.webui.service import ConversionService, PendingJob def get_service() -> ConversionService: return current_app.extensions["conversion_service"] diff --git a/abogen/webui/routes/utils/settings.py b/abogen/webui/routes/utils/settings.py index 9b15845..c96a66c 100644 --- a/abogen/webui/routes/utils/settings.py +++ b/abogen/webui/routes/utils/settings.py @@ -15,7 +15,7 @@ from abogen.normalization_settings import ( from abogen.utils import load_config, save_config from abogen.integrations.calibre_opds import CalibreOPDSClient from abogen.integrations.audiobookshelf import AudiobookshelfConfig -from abogen.web.routes.utils.common import split_profile_spec +from abogen.webui.routes.utils.common import split_profile_spec SAVE_MODE_LABELS = { "save_next_to_input": "Save next to input file", diff --git a/abogen/webui/routes/utils/voice.py b/abogen/webui/routes/utils/voice.py index 110a7c9..3d9081a 100644 --- a/abogen/webui/routes/utils/voice.py +++ b/abogen/webui/routes/utils/voice.py @@ -4,8 +4,8 @@ import numpy as np from abogen.speaker_configs import slugify_label from abogen.speaker_analysis import analyze_speakers -from abogen.web.routes.utils.settings import load_settings, settings_defaults, _DEFAULT_ANALYSIS_THRESHOLD, _CHUNK_LEVEL_OPTIONS, _APOSTROPHE_MODE_OPTIONS, _NORMALIZATION_GROUPS -from abogen.web.routes.utils.common import split_profile_spec +from abogen.webui.routes.utils.settings import load_settings, settings_defaults, _DEFAULT_ANALYSIS_THRESHOLD, _CHUNK_LEVEL_OPTIONS, _APOSTROPHE_MODE_OPTIONS, _NORMALIZATION_GROUPS +from abogen.webui.routes.utils.common import split_profile_spec from abogen.voice_profiles import ( load_profiles, serialize_profiles, @@ -21,7 +21,7 @@ from abogen.constants import ( ) from abogen.speaker_configs import list_configs from abogen.utils import load_numpy_kpipeline -from abogen.web.conversion_runner import _select_device, _to_float32, SAMPLE_RATE, SPLIT_PATTERN +from abogen.webui.conversion_runner import _select_device, _to_float32, SAMPLE_RATE, SPLIT_PATTERN _preview_pipeline_lock = threading.RLock() _preview_pipelines: Dict[Tuple[str, str], Any] = {} diff --git a/abogen/webui/routes/voices.py b/abogen/webui/routes/voices.py index c66d1bc..a7e9ce7 100644 --- a/abogen/webui/routes/voices.py +++ b/abogen/webui/routes/voices.py @@ -2,14 +2,14 @@ from typing import Any, Dict, List, Optional from flask import Blueprint, render_template, request, jsonify, abort, flash, redirect, url_for from flask.typing import ResponseReturnValue -from abogen.web.routes.utils.voice import ( +from abogen.webui.routes.utils.voice import ( template_options, resolve_voice_setting, resolve_voice_choice, parse_voice_formula, ) -from abogen.web.routes.utils.settings import load_settings, coerce_bool -from abogen.web.routes.utils.preview import synthesize_preview +from abogen.webui.routes.utils.settings import load_settings, coerce_bool +from abogen.webui.routes.utils.preview import synthesize_preview from abogen.speaker_configs import ( list_configs, get_config, diff --git a/docker-compose.webui.yml b/docker-compose.webui.yml new file mode 100644 index 0000000..fa35865 --- /dev/null +++ b/docker-compose.webui.yml @@ -0,0 +1,58 @@ +# Docker Compose for Abogen Web UI (Flask-based interface) +# +# This configuration runs the web-based Flask UI for Abogen. +# For the Qt desktop UI, see the upstream project's docker configuration. +# +# Usage: +# docker compose -f docker-compose.webui.yml up --build +# +# Or set as default: +# docker compose up --build +# +services: + abogen-webui: + build: + context: . + dockerfile: abogen/webui/Dockerfile + args: + TORCH_INDEX_URL: ${TORCH_INDEX_URL:-https://download.pytorch.org/whl/cu124} + TORCH_VERSION: ${TORCH_VERSION:-} + image: abogen-webui:latest + user: "${ABOGEN_UID:-1000}:${ABOGEN_GID:-1000}" + ports: + - "${ABOGEN_PORT:-8808}:8808" + volumes: + - ${ABOGEN_DATA:-./data}:/data + - ${ABOGEN_SETTINGS_DIR:-./config}:/config + - ${ABOGEN_OUTPUT_DIR:-./storage/output}:/data/outputs + - ${ABOGEN_TEMP_DIR:-./storage/tmp}:/data/cache + environment: + ABOGEN_HOST: 0.0.0.0 + ABOGEN_PORT: 8808 + ABOGEN_SETTINGS_DIR: "/config" + ABOGEN_UPLOAD_ROOT: /data/uploads + ABOGEN_OUTPUT_DIR: "/data/outputs" + ABOGEN_OUTPUT_ROOT: "/data/outputs" + ABOGEN_TEMP_DIR: "/data/cache" + ABOGEN_VOICE_CACHE_DIR: "/data/voice-cache" + HF_HOME: "/data/huggingface" + HUGGINGFACE_HUB_CACHE: "/data/huggingface/hub" + HOME: "/tmp/abogen-home" + # --- GPU support ----------------------------------------------------- + # These settings assume the NVIDIA Container Toolkit is installed. + # Leave them in place for GPU acceleration; comment out the entire block + # below if you are deploying to a CPU-only host. + deploy: + resources: + limits: + cpus: '4.0' + memory: 8G + reservations: + devices: + - capabilities: [gpu] + # driver: nvidia + # count: all + # Runtime flag is only honored by legacy docker-compose (v1) CLI. + # Uncomment if you're still using it: + # runtime: nvidia + restart: unless-stopped diff --git a/docker-compose.yaml b/docker-compose.yaml index ff39617..924ff10 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -1,8 +1,18 @@ +# Docker Compose for Abogen +# +# This configuration runs the Flask-based Web UI for Abogen. +# The Web UI provides a browser-based interface for audiobook generation. +# +# Usage: +# docker compose up --build +# +# Access the web interface at http://localhost:8808 +# services: abogen: build: context: . - dockerfile: abogen/Dockerfile + dockerfile: abogen/webui/Dockerfile args: TORCH_INDEX_URL: ${TORCH_INDEX_URL:-https://download.pytorch.org/whl/cu124} TORCH_VERSION: ${TORCH_VERSION:-} diff --git a/pyproject.toml b/pyproject.toml index f61385b..e9dc4af 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -62,11 +62,11 @@ allow-direct-references = true [project.gui-scripts] -abogen = "abogen.web.app:main" +abogen = "abogen.webui.app:main" [project.scripts] -abogen-cli = "abogen.web.app:main" -abogen-web = "abogen.web.app:main" +abogen-cli = "abogen.webui.app:main" +abogen-web = "abogen.webui.app:main" [tool.hatch.build.targets.sdist] exclude = [ @@ -82,8 +82,8 @@ packages = ["abogen"] [tool.hatch.build] include = [ - "abogen/web/templates/**", - "abogen/web/static/**", + "abogen/webui/templates/**", + "abogen/webui/static/**", ] [tool.hatch.version] diff --git a/tests/test_book_step_speaker_provider.py b/tests/test_book_step_speaker_provider.py index f137b66..6591c81 100644 --- a/tests/test_book_step_speaker_provider.py +++ b/tests/test_book_step_speaker_provider.py @@ -2,8 +2,8 @@ from pathlib import Path from werkzeug.datastructures import MultiDict -from abogen.web.routes.utils.form import apply_book_step_form -from abogen.web.service import PendingJob +from abogen.webui.routes.utils.form import apply_book_step_form +from abogen.webui.service import PendingJob def _make_pending_job() -> PendingJob: diff --git a/tests/test_chapter_overrides.py b/tests/test_chapter_overrides.py index 814d44b..27f942d 100644 --- a/tests/test_chapter_overrides.py +++ b/tests/test_chapter_overrides.py @@ -100,7 +100,7 @@ def _install_dependency_stubs() -> None: _install_dependency_stubs() from abogen.text_extractor import ExtractedChapter -from abogen.web.conversion_runner import _apply_chapter_overrides, _merge_metadata +from abogen.webui.conversion_runner import _apply_chapter_overrides, _merge_metadata def _sample_chapters() -> list[ExtractedChapter]: diff --git a/tests/test_chunk_helpers.py b/tests/test_chunk_helpers.py index d37730d..b937358 100644 --- a/tests/test_chunk_helpers.py +++ b/tests/test_chunk_helpers.py @@ -3,7 +3,7 @@ from __future__ import annotations from types import SimpleNamespace from abogen.chunking import chunk_text -from abogen.web.conversion_runner import _chunk_voice_spec, _group_chunks_by_chapter +from abogen.webui.conversion_runner import _chunk_voice_spec, _group_chunks_by_chapter def test_group_chunks_by_chapter_orders_and_groups() -> None: diff --git a/tests/test_chunk_text_for_tts_prefers_raw.py b/tests/test_chunk_text_for_tts_prefers_raw.py index 402bc2f..faf3c67 100644 --- a/tests/test_chunk_text_for_tts_prefers_raw.py +++ b/tests/test_chunk_text_for_tts_prefers_raw.py @@ -1,4 +1,4 @@ -from abogen.web.conversion_runner import _chunk_text_for_tts +from abogen.webui.conversion_runner import _chunk_text_for_tts def test_chunk_text_for_tts_prefers_text_over_normalized_text(): diff --git a/tests/test_conversion_chapter_titles.py b/tests/test_conversion_chapter_titles.py index af2fd8b..76805c6 100644 --- a/tests/test_conversion_chapter_titles.py +++ b/tests/test_conversion_chapter_titles.py @@ -61,7 +61,7 @@ if "bs4" not in sys.modules: sys.modules["bs4"] = bs4_stub -from abogen.web.conversion_runner import ( +from abogen.webui.conversion_runner import ( _format_spoken_chapter_title, _headings_equivalent, _normalize_chapter_opening_caps, diff --git a/tests/test_conversion_series.py b/tests/test_conversion_series.py index 554b94d..fb85138 100644 --- a/tests/test_conversion_series.py +++ b/tests/test_conversion_series.py @@ -61,7 +61,7 @@ if "bs4" not in sys.modules: sys.modules["bs4"] = bs4_stub -from abogen.web.conversion_runner import _build_outro_text, _build_title_intro_text +from abogen.webui.conversion_runner import _build_outro_text, _build_title_intro_text def test_title_intro_includes_series_sentence() -> None: diff --git a/tests/test_conversion_voice_resolution.py b/tests/test_conversion_voice_resolution.py index 61c4bfa..4987c5d 100644 --- a/tests/test_conversion_voice_resolution.py +++ b/tests/test_conversion_voice_resolution.py @@ -2,12 +2,12 @@ from types import SimpleNamespace from typing import cast from abogen.constants import VOICES_INTERNAL -from abogen.web.conversion_runner import ( +from abogen.webui.conversion_runner import ( _chapter_voice_spec, _chunk_voice_spec, _collect_required_voice_ids, ) -from abogen.web.service import Job +from abogen.webui.service import Job def _sample_job(formula: str) -> Job: diff --git a/tests/test_debug_tts_samples.py b/tests/test_debug_tts_samples.py index cf9c3b9..6f0a088 100644 --- a/tests/test_debug_tts_samples.py +++ b/tests/test_debug_tts_samples.py @@ -8,7 +8,7 @@ from abogen.debug_tts_samples import DEBUG_TTS_SAMPLES, MARKER_PREFIX, MARKER_SU from abogen.kokoro_text_normalization import HAS_NUM2WORDS, normalize_for_pipeline from abogen.normalization_settings import build_apostrophe_config from abogen.text_extractor import extract_from_path -from abogen.web.app import create_app +from abogen.webui.app import create_app def test_debug_epub_contains_all_codes(): @@ -25,7 +25,7 @@ def test_debug_epub_contains_all_codes(): def test_debug_samples_normalize_smoke(): # Use the same defaults as the web UI. - from abogen.web.routes.utils.settings import settings_defaults + from abogen.webui.routes.utils.settings import settings_defaults settings = settings_defaults() apostrophe = build_apostrophe_config(settings=settings) @@ -56,7 +56,7 @@ def test_debug_samples_normalize_smoke(): def test_settings_debug_route_writes_manifest(tmp_path, monkeypatch): # Avoid pulling Kokoro models in tests: stub the pipeline. - from abogen.web import debug_tts_runner as runner + from abogen.webui import debug_tts_runner as runner class _Seg: def __init__(self, audio): @@ -122,7 +122,7 @@ def test_debug_samples_have_minimum_per_category(): def test_debug_runner_resolves_profile_voice_before_pipeline(tmp_path, monkeypatch): - from abogen.web import debug_tts_runner as runner + from abogen.webui import debug_tts_runner as runner # Stub voice setting resolution so we don't depend on the user's profile file. monkeypatch.setattr(runner, "_resolve_voice_setting", lambda value: ("af_heart", "AM HQ Alt", None)) diff --git a/tests/test_ffmetadata.py b/tests/test_ffmetadata.py index 82d05cc..cc58cc8 100644 --- a/tests/test_ffmetadata.py +++ b/tests/test_ffmetadata.py @@ -2,7 +2,7 @@ from __future__ import annotations from pathlib import Path -from abogen.web.conversion_runner import _render_ffmetadata, _write_ffmetadata_file +from abogen.webui.conversion_runner import _render_ffmetadata, _write_ffmetadata_file def test_render_ffmetadata_includes_chapters(tmp_path): diff --git a/tests/test_manual_overrides_applied_first.py b/tests/test_manual_overrides_applied_first.py index efae6f4..d21de5e 100644 --- a/tests/test_manual_overrides_applied_first.py +++ b/tests/test_manual_overrides_applied_first.py @@ -1,4 +1,4 @@ -from abogen.web import conversion_runner +from abogen.webui import conversion_runner class DummyJob: diff --git a/tests/test_opds_import_metadata_mapping.py b/tests/test_opds_import_metadata_mapping.py index 63e20b5..1e7db36 100644 --- a/tests/test_opds_import_metadata_mapping.py +++ b/tests/test_opds_import_metadata_mapping.py @@ -1,6 +1,6 @@ from __future__ import annotations -from abogen.web.routes.api import _opds_metadata_overrides +from abogen.webui.routes.api import _opds_metadata_overrides def test_opds_metadata_overrides_maps_author_and_subtitle() -> None: diff --git a/tests/test_output_paths.py b/tests/test_output_paths.py index 698e1fe..a1d14a8 100644 --- a/tests/test_output_paths.py +++ b/tests/test_output_paths.py @@ -5,8 +5,8 @@ from pathlib import Path import pytest -from abogen.web.conversion_runner import _build_output_path, _prepare_project_layout -from abogen.web.service import Job +from abogen.webui.conversion_runner import _build_output_path, _prepare_project_layout +from abogen.webui.service import Job def _sample_job(tmp_path: Path) -> Job: @@ -33,7 +33,7 @@ def _sample_job(tmp_path: Path) -> Job: def test_prepare_project_layout_uses_timestamped_folder(monkeypatch: pytest.MonkeyPatch, tmp_path: Path) -> None: job = _sample_job(tmp_path) monkeypatch.setattr( - "abogen.web.conversion_runner._output_timestamp_token", + "abogen.webui.conversion_runner._output_timestamp_token", lambda: "20250101-120000", ) @@ -52,7 +52,7 @@ def test_prepare_project_layout_creates_project_subdirs(monkeypatch: pytest.Monk job = _sample_job(tmp_path) job.save_as_project = True monkeypatch.setattr( - "abogen.web.conversion_runner._output_timestamp_token", + "abogen.webui.conversion_runner._output_timestamp_token", lambda: "20250101-120500", ) diff --git a/tests/test_prepare_form.py b/tests/test_prepare_form.py index 7412dbb..4ea05eb 100644 --- a/tests/test_prepare_form.py +++ b/tests/test_prepare_form.py @@ -2,9 +2,9 @@ from pathlib import Path from werkzeug.datastructures import MultiDict -from abogen.web.routes.utils.form import apply_prepare_form -from abogen.web.routes.utils.voice import resolve_voice_setting -from abogen.web.service import PendingJob +from abogen.webui.routes.utils.form import apply_prepare_form +from abogen.webui.routes.utils.voice import resolve_voice_setting +from abogen.webui.service import PendingJob def _make_pending_job() -> PendingJob: diff --git a/tests/test_preview_applies_manual_overrides.py b/tests/test_preview_applies_manual_overrides.py index 5044c51..76998c4 100644 --- a/tests/test_preview_applies_manual_overrides.py +++ b/tests/test_preview_applies_manual_overrides.py @@ -1,4 +1,4 @@ -from abogen.web.routes.utils import preview +from abogen.webui.routes.utils import preview def test_preview_applies_manual_override_before_normalization(monkeypatch): diff --git a/tests/test_service.py b/tests/test_service.py index a83f4ab..07c61f1 100644 --- a/tests/test_service.py +++ b/tests/test_service.py @@ -2,7 +2,7 @@ from __future__ import annotations import io import time -from abogen.web.service import Job, JobStatus, build_service, _JOB_LOGGER, build_audiobookshelf_metadata +from abogen.webui.service import Job, JobStatus, build_service, _JOB_LOGGER, build_audiobookshelf_metadata def test_service_processes_job(tmp_path): diff --git a/tests/test_settings_integrations_secrets.py b/tests/test_settings_integrations_secrets.py index 0b77227..96896fa 100644 --- a/tests/test_settings_integrations_secrets.py +++ b/tests/test_settings_integrations_secrets.py @@ -3,7 +3,7 @@ from __future__ import annotations from pathlib import Path from abogen.utils import load_config, save_config -from abogen.web.app import create_app +from abogen.webui.app import create_app def test_settings_update_preserves_abs_api_token_when_blank(tmp_path): diff --git a/tests/test_voice_cache.py b/tests/test_voice_cache.py index ea89f3f..b0aa2ca 100644 --- a/tests/test_voice_cache.py +++ b/tests/test_voice_cache.py @@ -5,8 +5,8 @@ import pytest from abogen.constants import VOICES_INTERNAL from abogen.voice_cache import LocalEntryNotFoundError, _CACHED_VOICES, ensure_voice_assets -from abogen.web.conversion_runner import _collect_required_voice_ids -from abogen.web.service import Job +from abogen.webui.conversion_runner import _collect_required_voice_ids +from abogen.webui.service import Job @pytest.fixture(autouse=True) diff --git a/tests/test_voice_formula_resolution.py b/tests/test_voice_formula_resolution.py index 907dac9..2bc3430 100644 --- a/tests/test_voice_formula_resolution.py +++ b/tests/test_voice_formula_resolution.py @@ -1,6 +1,6 @@ from __future__ import annotations -from abogen.web.conversion_runner import _resolve_voice, _supertonic_voice_from_spec +from abogen.webui.conversion_runner import _resolve_voice, _supertonic_voice_from_spec from abogen.tts_supertonic import DEFAULT_SUPERTONIC_VOICES