refactor: word substitution in planner, add planner tests

This commit is contained in:
Artem Akymenko
2026-07-24 19:17:37 +03:00
parent 0ee5bb0496
commit a1241ee9ca
2 changed files with 76 additions and 5 deletions
+21 -4
View File
@@ -82,16 +82,33 @@ def _extract_source_text(request: ConversionRequest) -> Optional[str]:
from abogen.subtitle_utils import clean_text from abogen.subtitle_utils import clean_text
if request.direct_text: if request.direct_text:
return clean_text(request.direct_text) text = clean_text(request.direct_text)
if request.source_path and request.source_path.exists(): elif request.source_path and request.source_path.exists():
encoding = "utf-8" encoding = "utf-8"
try: try:
with open(request.source_path, "r", encoding=encoding, errors="replace") as f: with open(request.source_path, "r", encoding=encoding, errors="replace") as f:
text = f.read() text = f.read()
except Exception: except Exception:
return None return None
return clean_text(text) text = clean_text(text)
return None else:
return None
# Apply word substitutions if configured
if request.word_substitution:
from abogen.word_substitution import apply_word_substitutions
ws = request.word_substitution
text = apply_word_substitutions(
text,
ws.substitutions_list,
ws.case_sensitive,
ws.replace_caps,
ws.replace_numerals,
ws.fix_punctuation,
)
return text
def _extract_metadata(request: ConversionRequest) -> Dict[str, Any]: def _extract_metadata(request: ConversionRequest) -> Dict[str, Any]:
+55 -1
View File
@@ -25,7 +25,7 @@ from abogen.application.conversion_models import (
OutputLayout, OutputLayout,
SegmentPlan, SegmentPlan,
) )
from abogen.application.conversion_config import ChapterChunkConfig from abogen.application.conversion_config import ChapterChunkConfig, WordSubstitutionConfig
from abogen.application.conversion_planner import build_conversion_plan from abogen.application.conversion_planner import build_conversion_plan
from abogen.application.conversion_request import ConversionRequest from abogen.application.conversion_request import ConversionRequest
@@ -203,6 +203,60 @@ class TestBuildConversionPlan:
assert plan.chapters[0].segments[0].kind == "body" assert plan.chapters[0].segments[0].kind == "body"
class TestWordSubstitution:
"""Tests for word substitution in the planner."""
def test_basic_substitution(self):
"""Single word substitution is applied."""
req = ConversionRequest(
direct_text="The quick brown fox",
voice="M1",
word_substitution=WordSubstitutionConfig(
substitutions_list="fox|cat",
),
)
plan = build_conversion_plan(req)
assert "cat" in plan.chapters[0].body_text
assert "fox" not in plan.chapters[0].body_text
def test_multiple_substitutions(self):
"""Multiple word substitutions are applied."""
req = ConversionRequest(
direct_text="The quick brown fox jumps",
voice="M1",
word_substitution=WordSubstitutionConfig(
substitutions_list="fox|cat\nquick|slow",
),
)
plan = build_conversion_plan(req)
text = plan.chapters[0].body_text
assert "cat" in text
assert "slow" in text
def test_substitution_preserves_chapter_markers(self):
"""Chapter markers are preserved during substitution."""
req = ConversionRequest(
direct_text="<<CHAPTER_MARKER:Ch1>>\nThe quick brown fox",
voice="M1",
word_substitution=WordSubstitutionConfig(
substitutions_list="fox|cat",
),
)
plan = build_conversion_plan(req)
assert len(plan.chapters) >= 1
assert "cat" in plan.chapters[0].body_text
def test_no_substitution_when_disabled(self):
"""No substitution when word_substitution is None."""
req = ConversionRequest(
direct_text="The quick brown fox",
voice="M1",
word_substitution=None,
)
plan = build_conversion_plan(req)
assert "fox" in plan.chapters[0].body_text
class TestPlannerWithFileSource: class TestPlannerWithFileSource:
"""Tests using actual file sources (not direct_text).""" """Tests using actual file sources (not direct_text)."""