fix(subtitles): enable "N words" and karaoke modes in PyQt

Three related fixes:

- make_subtitle_writer: accept word-count modes ("N words") in
  create_subtitle_writer. SubtitleMode("5 words") raised ValueError,
  which make_subtitle_writer swallowed and returned None, so PyQt never
  created or wrote the subtitle file when an "N words" mode was selected.
  Unknown modes now fall back to SubtitleMode.SENTENCE (writers only
  branch on SENTENCE_HIGHLIGHT). Regression test added.

- gui.py: refresh subtitle combo item availability after initial
  voice/profile selection. update_subtitle_options_availability() ran
  during initUI with selected_lang=None for a profile, taking the
  non-English branch and disabling Highlighting/N-words items; the fix-up
  never re-ran because setCurrentIndex on an already-current index emits
  no signal. Now called once more after the profile/voice language is
  resolved.

- AssWriter: stop discarding per-word karaoke timing. _add_karaoke_tags
  unconditionally replaced entry text with uniform {\\k100} tags,
  destroying the real per-word {\\kf} timings from
  _process_karaoke_highlighting. Only synthesize simplified tags when the
  text has no karaoke tags. Regression test added.
This commit is contained in:
Deniz Şafak
2026-08-20 23:27:49 +03:00
parent 5432de7ac5
commit ffac4a4da9
3 changed files with 38 additions and 3 deletions
+18 -3
View File
@@ -220,8 +220,10 @@ class AssWriter(SubtitleWriter):
style = "Default"
if self.config.mode == SubtitleMode.SENTENCE_HIGHLIGHT:
# Add karaoke tags for highlighting
text = self._add_karaoke_tags(text)
# Entries from process_subtitle_tokens already carry per-word
# {\kf...} timing; only synthesize simplified tags when absent.
if "{\\k" not in text:
text = self._add_karaoke_tags(text)
style = "Highlight"
alignment_tag = r"{\an5}" if self._is_centered else ""
@@ -248,6 +250,19 @@ class AssWriter(SubtitleWriter):
return f"{hours}:{minutes:02d}:{secs:05.2f}"
def _coerce_mode(mode: str) -> SubtitleMode:
"""Parse a subtitle mode, tolerating word-count strings like "5 words".
Word-count modes are grouped upstream (subtitle_generation) and the writer
only branches on SubtitleMode.SENTENCE_HIGHLIGHT, so any non-highlight
fallback is behaviorally equivalent for the writers.
"""
try:
return SubtitleMode(mode)
except ValueError:
return SubtitleMode.SENTENCE
def create_subtitle_writer(
path: Path,
format: str,
@@ -257,7 +272,7 @@ def create_subtitle_writer(
) -> SubtitleWriter:
"""Factory function to create subtitle writer."""
fmt = SubtitleFormat(format.lower())
mode = SubtitleMode(mode)
mode = _coerce_mode(mode)
align = SubtitleAlignment(alignment.lower())
config = SubtitleConfig(
+1
View File
@@ -1019,6 +1019,7 @@ class abogen(QWidget):
self.selected_lang = (
language_for_voice_id(entry[0]) if entry and entry[0] else Language.EN_US
)
self.update_subtitle_options_availability()
if self.save_option == "Choose output folder" and self.selected_output_folder:
self.save_path_label.setText(self.selected_output_folder)
self.save_path_row_widget.show()
+19
View File
@@ -177,6 +177,19 @@ class TestAssWriter:
assert "Highlight" in content
assert r"{\k100}" in content
def test_highlight_mode_preserves_existing_karaoke_tags(self, tmp_path):
path = tmp_path / "test.ass"
config = SubtitleConfig(
format=SubtitleFormat.ASS,
mode=SubtitleMode.SENTENCE_HIGHLIGHT,
)
writer = AssWriter(path, config)
writer.write_entry(start=0.0, end=1.0, text=r"{\kf20}Hello {\kf20}world.")
writer.close()
content = path.read_text()
assert r"{\kf20}Hello {\kf20}world." in content
assert r"{\k100}" not in content
def test_centered_alignment(self, tmp_path):
path = tmp_path / "test.ass"
config = SubtitleConfig(
@@ -243,6 +256,12 @@ class TestCreateSubtitleWriter:
with pytest.raises(ValueError):
create_subtitle_writer(path, "xyz", "Line")
def test_word_count_mode(self, tmp_path):
path = tmp_path / "test.srt"
writer = create_subtitle_writer(path, "srt", "5 words", max_words=5)
assert isinstance(writer, SrtWriter)
writer.close()
# ===================================================================
# Context manager