feat: Refactor code structure to move web-related components to the webui module and update references accordingly

This commit is contained in:
JB
2025-12-21 08:12:48 -08:00
parent 5303dcf681
commit 938e122166
40 changed files with 161 additions and 93 deletions
+1 -1
View File
@@ -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.
+1 -1
View File
@@ -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")
+1 -1
View File
@@ -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,
+2 -2
View File
@@ -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)
+7 -7
View File
@@ -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",
+6 -6
View File
@@ -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
+2 -2
View File
@@ -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__)
+4 -4
View File
@@ -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,
+7 -7
View File
@@ -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__)
+5 -5
View File
@@ -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,
+3 -3
View File
@@ -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
+2 -2
View File
@@ -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]]:
+1 -1
View File
@@ -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):
+7 -7
View File
@@ -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
+1 -1
View File
@@ -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):
+1 -1
View File
@@ -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"]
+1 -1
View File
@@ -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",
+3 -3
View File
@@ -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] = {}
+3 -3
View File
@@ -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,
+58
View File
@@ -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
+11 -1
View File
@@ -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:-}
+5 -5
View File
@@ -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]
+2 -2
View File
@@ -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:
+1 -1
View File
@@ -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]:
+1 -1
View File
@@ -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:
+1 -1
View File
@@ -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():
+1 -1
View File
@@ -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,
+1 -1
View File
@@ -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:
+2 -2
View File
@@ -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:
+4 -4
View File
@@ -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))
+1 -1
View File
@@ -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):
+1 -1
View File
@@ -1,4 +1,4 @@
from abogen.web import conversion_runner
from abogen.webui import conversion_runner
class DummyJob:
+1 -1
View File
@@ -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:
+4 -4
View File
@@ -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",
)
+3 -3
View File
@@ -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:
@@ -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):
+1 -1
View File
@@ -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):
+1 -1
View File
@@ -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):
+2 -2
View File
@@ -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)
+1 -1
View File
@@ -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