mirror of
https://github.com/denizsafak/abogen.git
synced 2026-07-22 07:10:28 +02:00
- domain/intro_outro.py: resolve_intro(), resolve_outro() return IntroOutroSpec - Both UIs call domain for text building + voice spec resolution - PyQt uses resolve_intro/resolve_outro instead of direct calls - infrastructure/subtitle_writer.py: resolve_subtitle_format(), make_subtitle_writer() - Deleted duplicate _create_subtitle_writer() from WebUI - Deleted duplicate _subtitle_alignment_from_format() from PyQt - domain/conversion_engine.py: run_tts_segment_loop() for TTS iteration - VoiceCache class in domain/voice_loader.py used by both UIs - Tests: 1253 passed
84 lines
2.6 KiB
Python
84 lines
2.6 KiB
Python
"""Intro/outro text building and voice resolution for audiobook conversion.
|
|
|
|
Both UIs (WebUI and Desktop) need to:
|
|
1. Build intro/outro text from book metadata
|
|
2. Resolve which voice to use for intro/outro synthesis
|
|
|
|
This module provides the shared domain logic. The actual TTS synthesis
|
|
and audio writing remain UI-specific.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from dataclasses import dataclass
|
|
from typing import Any, Dict, Optional
|
|
|
|
from abogen.domain.title_builder import build_title_intro_text, build_outro_text
|
|
from abogen.domain.voice_resolution import resolve_fallback_voice_spec
|
|
|
|
|
|
@dataclass
|
|
class IntroOutroSpec:
|
|
"""Resolved intro or outro specification ready for TTS synthesis."""
|
|
text: str
|
|
voice_spec: str
|
|
enabled: bool
|
|
|
|
|
|
def resolve_intro(
|
|
metadata: Optional[Dict[str, Any]],
|
|
original_filename: str,
|
|
read_title_intro: bool,
|
|
base_voice_spec: str,
|
|
job_voice: str,
|
|
voice_cache_keys: list[str],
|
|
) -> IntroOutroSpec:
|
|
"""Resolve the intro specification from job settings and metadata.
|
|
|
|
Returns an IntroOutroSpec with text and voice_spec populated,
|
|
or enabled=False if intro is disabled or text cannot be built.
|
|
"""
|
|
if not read_title_intro:
|
|
return IntroOutroSpec(text="", voice_spec="", enabled=False)
|
|
|
|
text = build_title_intro_text(metadata, original_filename)
|
|
if not text:
|
|
return IntroOutroSpec(text="", voice_spec="", enabled=False)
|
|
|
|
voice_spec = resolve_fallback_voice_spec(
|
|
base_voice_spec, job_voice, voice_cache_keys
|
|
)
|
|
if not voice_spec:
|
|
return IntroOutroSpec(text=text, voice_spec="", enabled=False)
|
|
|
|
return IntroOutroSpec(text=text, voice_spec=voice_spec, enabled=True)
|
|
|
|
|
|
def resolve_outro(
|
|
metadata: Optional[Dict[str, Any]],
|
|
original_filename: str,
|
|
read_closing_outro: bool,
|
|
base_voice_spec: str,
|
|
job_voice: str,
|
|
voice_cache_keys: list[str],
|
|
) -> IntroOutroSpec:
|
|
"""Resolve the outro specification from job settings and metadata.
|
|
|
|
Returns an IntroOutroSpec with text and voice_spec populated,
|
|
or enabled=False if outro is disabled or text cannot be built.
|
|
"""
|
|
if not read_closing_outro:
|
|
return IntroOutroSpec(text="", voice_spec="", enabled=False)
|
|
|
|
text = build_outro_text(metadata, original_filename)
|
|
if not text:
|
|
return IntroOutroSpec(text="", voice_spec="", enabled=False)
|
|
|
|
voice_spec = resolve_fallback_voice_spec(
|
|
base_voice_spec, job_voice, voice_cache_keys
|
|
)
|
|
if not voice_spec:
|
|
return IntroOutroSpec(text=text, voice_spec="", enabled=False)
|
|
|
|
return IntroOutroSpec(text=text, voice_spec=voice_spec, enabled=True)
|