Fixed last sentence/subtitle entry timing in generated subtitles

This commit is contained in:
Deniz Şafak
2025-07-16 01:00:27 +03:00
parent 5d9409d391
commit d2f1c12de9
2 changed files with 20 additions and 33 deletions
+4
View File
@@ -1,3 +1,7 @@
# 1.1.3
- Fixed issue where the app would not restart properly on Windows.
- Fixed last sentence/subtitle entry timing in generated subtitles, the end time of the final subtitle entry now correctly matches the end of the audio chunk, preventing zero or invalid timings at the end.
# v1.1.2 # v1.1.2
- Now you can play the audio files while they are processing. - Now you can play the audio files while they are processing.
- Audio and subtitle files are now written directly to disk during generation, which significantly reduces memory usage. - Audio and subtitle files are now written directly to disk during generation, which significantly reduces memory usage.
+16 -33
View File
@@ -793,6 +793,7 @@ class ConversionThread(QThread):
tokens_with_timestamps, tokens_with_timestamps,
new_entries, new_entries,
self.max_subtitle_words, self.max_subtitle_words,
fallback_end_time=chunk_start + chunk_dur
) )
if merged_subtitle_file: if merged_subtitle_file:
subtitle_format = getattr( subtitle_format = getattr(
@@ -819,6 +820,7 @@ class ConversionThread(QThread):
chapter_tokens_with_timestamps, chapter_tokens_with_timestamps,
new_chapter_entries, new_chapter_entries,
self.max_subtitle_words, self.max_subtitle_words,
fallback_end_time=chapter_current_time + chunk_dur,
) )
if chapter_subtitle_file: if chapter_subtitle_file:
subtitle_format = getattr( subtitle_format = getattr(
@@ -1146,7 +1148,7 @@ class ConversionThread(QThread):
return f"{h}:{m:02}:{s:02}.{cs:02}" return f"{h}:{m:02}:{s:02}.{cs:02}"
def _process_subtitle_tokens( def _process_subtitle_tokens(
self, tokens_with_timestamps, subtitle_entries, max_subtitle_words self, tokens_with_timestamps, subtitle_entries, max_subtitle_words, fallback_end_time=None
): ):
"""Helper function to process subtitle tokens according to the subtitle mode""" """Helper function to process subtitle tokens according to the subtitle mode"""
if not tokens_with_timestamps: if not tokens_with_timestamps:
@@ -1191,9 +1193,15 @@ class ConversionThread(QThread):
sentence_text = "" sentence_text = ""
for t in current_sentence: for t in current_sentence:
sentence_text += t["text"] + (t.get("whitespace", "") or "") sentence_text += t["text"] + (t.get("whitespace", "") or "")
subtitle_entries.append((start_time, end_time, sentence_text.strip())) subtitle_entries.append((start_time, end_time, sentence_text.strip()))
# Fallback for last entry
if subtitle_entries and fallback_end_time is not None:
last_entry = subtitle_entries[-1]
start, end, text = last_entry
if end is None or end <= start or end <= 0:
subtitle_entries[-1] = (start, fallback_end_time, text)
else: else:
# Word count-based grouping # Word count-based grouping
try: try:
@@ -1230,37 +1238,12 @@ class ConversionThread(QThread):
subtitle_entries.append( subtitle_entries.append(
(group[0]["start"], group[-1]["end"], text.strip()) (group[0]["start"], group[-1]["end"], text.strip())
) )
# Fallback for last entry
def _write_ass_subtitle( if subtitle_entries and fallback_end_time is not None:
self, file_path, subtitle_entries, is_centered=False, is_narrow=False last_entry = subtitle_entries[-1]
): start, end, text = last_entry
with open(file_path, "w", encoding="utf-8", errors="replace") as f: if end is None or end <= start or end <= 0:
# Minimal ASS header subtitle_entries[-1] = (start, fallback_end_time, text)
f.write("[Script Info]\n")
f.write("Title: Generated by Abogen\n")
f.write("ScriptType: v4.00+\n\n")
# Only events section, use override tags for positioning
f.write("[Events]\n")
f.write(
"Format: Layer, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text\n"
)
# Set margin based on is_narrow parameter
margin = "90" if is_narrow else ""
alignment_tag = ""
if is_centered:
alignment = 5
alignment_tag = f"{{\\an{alignment}}}"
# Write each subtitle with override tag for alignment and margins
for i, (start, end, text) in enumerate(subtitle_entries, 1):
start_time = self._ass_time(start)
end_time = self._ass_time(end)
f.write(
f"Dialogue: 0,{start_time},{end_time},Default,,{margin},{margin},0,,{alignment_tag}{text}\n"
)
def cancel(self): def cancel(self):
self.cancel_requested = True self.cancel_requested = True
self.should_cancel = True self.should_cancel = True