mirror of
https://github.com/denizsafak/abogen.git
synced 2026-07-18 13:40:27 +02:00
Merge pull request #69 from denizsafak/test
Add word-by-word karaoke highlighting option
This commit is contained in:
+73
-3
@@ -566,6 +566,11 @@ class ConversionThread(QThread):
|
|||||||
merged_subtitle_file.write("[Script Info]\n")
|
merged_subtitle_file.write("[Script Info]\n")
|
||||||
merged_subtitle_file.write("Title: Generated by Abogen\n")
|
merged_subtitle_file.write("Title: Generated by Abogen\n")
|
||||||
merged_subtitle_file.write("ScriptType: v4.00+\n\n")
|
merged_subtitle_file.write("ScriptType: v4.00+\n\n")
|
||||||
|
# Add style definitions for karaoke highlighting
|
||||||
|
if self.subtitle_mode == "Sentence + Highlighting":
|
||||||
|
merged_subtitle_file.write("[V4+ Styles]\n")
|
||||||
|
merged_subtitle_file.write("Format: Name, Fontname, Fontsize, PrimaryColour, SecondaryColour, OutlineColour, BackColour, Bold, Italic, Underline, StrikeOut, ScaleX, ScaleY, Spacing, Angle, BorderStyle, Outline, Shadow, Alignment, MarginL, MarginR, MarginV, Encoding\n")
|
||||||
|
merged_subtitle_file.write("Style: Default,Arial,24,&H00FFFFFF,&H00808080,&H00000000,&H00404040,0,0,0,0,100,100,0,0,3,2,0,5,10,10,10,1\n\n")
|
||||||
merged_subtitle_file.write("[Events]\n")
|
merged_subtitle_file.write("[Events]\n")
|
||||||
merged_subtitle_file.write(
|
merged_subtitle_file.write(
|
||||||
"Format: Layer, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text\n"
|
"Format: Layer, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text\n"
|
||||||
@@ -708,6 +713,13 @@ class ConversionThread(QThread):
|
|||||||
chapter_subtitle_file.write("[Script Info]\n")
|
chapter_subtitle_file.write("[Script Info]\n")
|
||||||
chapter_subtitle_file.write("Title: Generated by Abogen\n")
|
chapter_subtitle_file.write("Title: Generated by Abogen\n")
|
||||||
chapter_subtitle_file.write("ScriptType: v4.00+\n\n")
|
chapter_subtitle_file.write("ScriptType: v4.00+\n\n")
|
||||||
|
|
||||||
|
# Add style definitions for karaoke highlighting
|
||||||
|
if self.subtitle_mode == "Sentence + Highlighting":
|
||||||
|
chapter_subtitle_file.write("[V4+ Styles]\n")
|
||||||
|
chapter_subtitle_file.write("Format: Name, Fontname, Fontsize, PrimaryColour, SecondaryColour, OutlineColour, BackColour, Bold, Italic, Underline, StrikeOut, ScaleX, ScaleY, Spacing, Angle, BorderStyle, Outline, Shadow, Alignment, MarginL, MarginR, MarginV, Encoding\n")
|
||||||
|
chapter_subtitle_file.write("Style: Default,Arial,24,&H00FFFFFF,&H00808080,&H00000000,&H00404040,0,0,0,0,100,100,0,0,3,2,0,5,10,10,10,1\n\n")
|
||||||
|
|
||||||
chapter_subtitle_file.write("[Events]\n")
|
chapter_subtitle_file.write("[Events]\n")
|
||||||
chapter_subtitle_file.write(
|
chapter_subtitle_file.write(
|
||||||
"Format: Layer, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text\n"
|
"Format: Layer, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text\n"
|
||||||
@@ -831,8 +843,10 @@ class ConversionThread(QThread):
|
|||||||
for start, end, text in new_entries:
|
for start, end, text in new_entries:
|
||||||
start_time = self._ass_time(start)
|
start_time = self._ass_time(start)
|
||||||
end_time = self._ass_time(end)
|
end_time = self._ass_time(end)
|
||||||
|
# Use karaoke effect for highlighting mode
|
||||||
|
effect = "karaoke" if self.subtitle_mode == "Sentence + Highlighting" else ""
|
||||||
merged_subtitle_file.write(
|
merged_subtitle_file.write(
|
||||||
f"Dialogue: 0,{start_time},{end_time},Default,,{merged_subtitle_margin},{merged_subtitle_margin},0,,{merged_subtitle_alignment_tag}{text}\n"
|
f"Dialogue: 0,{start_time},{end_time},Default,,{merged_subtitle_margin},{merged_subtitle_margin},0,{effect},{merged_subtitle_alignment_tag}{text}\n"
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
for entry in new_entries:
|
for entry in new_entries:
|
||||||
@@ -858,8 +872,10 @@ class ConversionThread(QThread):
|
|||||||
for start, end, text in new_chapter_entries:
|
for start, end, text in new_chapter_entries:
|
||||||
start_time = self._ass_time(start)
|
start_time = self._ass_time(start)
|
||||||
end_time = self._ass_time(end)
|
end_time = self._ass_time(end)
|
||||||
|
# Use karaoke effect for highlighting mode
|
||||||
|
effect = "karaoke" if self.subtitle_mode == "Sentence + Highlighting" else ""
|
||||||
chapter_subtitle_file.write(
|
chapter_subtitle_file.write(
|
||||||
f"Dialogue: 0,{start_time},{end_time},Default,,{chapter_subtitle_margin},{chapter_subtitle_margin},0,,{chapter_subtitle_alignment_tag}{text}\n"
|
f"Dialogue: 0,{start_time},{end_time},Default,,{chapter_subtitle_margin},{chapter_subtitle_margin},0,{effect},{chapter_subtitle_alignment_tag}{text}\n"
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
for entry in new_chapter_entries:
|
for entry in new_chapter_entries:
|
||||||
@@ -1160,7 +1176,61 @@ class ConversionThread(QThread):
|
|||||||
if not tokens_with_timestamps:
|
if not tokens_with_timestamps:
|
||||||
return
|
return
|
||||||
|
|
||||||
if self.subtitle_mode == "Sentence" or self.subtitle_mode == "Sentence + Comma":
|
if self.subtitle_mode == "Sentence + Highlighting":
|
||||||
|
# Sentence-based processing with karaoke highlighting
|
||||||
|
separator = r"[.!?]"
|
||||||
|
current_sentence = []
|
||||||
|
word_count = 0
|
||||||
|
|
||||||
|
for token in tokens_with_timestamps:
|
||||||
|
current_sentence.append(token)
|
||||||
|
word_count += 1
|
||||||
|
|
||||||
|
# Split sentences based on separator or word count
|
||||||
|
if (
|
||||||
|
re.search(separator, token["text"]) and token["whitespace"] == " "
|
||||||
|
) or word_count >= max_subtitle_words:
|
||||||
|
if current_sentence:
|
||||||
|
# Create karaoke subtitle entry for this sentence
|
||||||
|
start_time = current_sentence[0]["start"]
|
||||||
|
end_time = current_sentence[-1]["end"]
|
||||||
|
|
||||||
|
# Generate karaoke text with background highlighting
|
||||||
|
karaoke_text = ""
|
||||||
|
for t in current_sentence:
|
||||||
|
# Calculate duration in centiseconds
|
||||||
|
duration = t["end"] - t["start"] if t["end"] and t["start"] else 0.5
|
||||||
|
duration_cs = int(duration * 100)
|
||||||
|
# Add karaoke effect - relies on style's SecondaryColour for highlighting
|
||||||
|
karaoke_text += f"{{\\kf{duration_cs}}}{t['text']}{t.get('whitespace', '') or ''}"
|
||||||
|
|
||||||
|
subtitle_entries.append(
|
||||||
|
(start_time, end_time, karaoke_text.strip())
|
||||||
|
)
|
||||||
|
current_sentence = []
|
||||||
|
word_count = 0
|
||||||
|
|
||||||
|
# Add any remaining tokens as a sentence
|
||||||
|
if current_sentence:
|
||||||
|
start_time = current_sentence[0]["start"]
|
||||||
|
end_time = current_sentence[-1]["end"]
|
||||||
|
|
||||||
|
# Generate karaoke text for remaining tokens
|
||||||
|
karaoke_text = ""
|
||||||
|
for t in current_sentence:
|
||||||
|
duration = t["end"] - t["start"] if t["end"] and t["start"] else 0.5
|
||||||
|
duration_cs = int(duration * 100)
|
||||||
|
karaoke_text += f"{{\\kf{duration_cs}}}{t['text']}{t.get('whitespace', '') or ''}"
|
||||||
|
subtitle_entries.append((start_time, end_time, karaoke_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)
|
||||||
|
|
||||||
|
elif self.subtitle_mode == "Sentence" or self.subtitle_mode == "Sentence + Comma":
|
||||||
# Define separator pattern based on mode
|
# Define separator pattern based on mode
|
||||||
separator = r"[.!?]" if self.subtitle_mode == "Sentence" else r"[.!?,]"
|
separator = r"[.!?]" if self.subtitle_mode == "Sentence" else r"[.!?,]"
|
||||||
current_sentence = []
|
current_sentence = []
|
||||||
|
|||||||
+2
-1
@@ -895,6 +895,7 @@ class abogen(QWidget):
|
|||||||
"Disabled: No subtitles will be generated.\n"
|
"Disabled: No subtitles will be generated.\n"
|
||||||
"Sentence: Subtitles will be generated for each sentence.\n"
|
"Sentence: Subtitles will be generated for each sentence.\n"
|
||||||
"Sentence + Comma: Subtitles will be generated for each sentence and comma.\n"
|
"Sentence + Comma: Subtitles will be generated for each sentence and comma.\n"
|
||||||
|
"Sentence + Highlighting: Subtitles with word-by-word karaoke highlighting.\n"
|
||||||
"1+ word: Subtitles will be generated for each word(s).\n\n"
|
"1+ word: Subtitles will be generated for each word(s).\n\n"
|
||||||
"Supported languages for subtitle generation:\n"
|
"Supported languages for subtitle generation:\n"
|
||||||
+ "\n".join(
|
+ "\n".join(
|
||||||
@@ -902,7 +903,7 @@ class abogen(QWidget):
|
|||||||
for lang in SUPPORTED_LANGUAGES_FOR_SUBTITLE_GENERATION
|
for lang in SUPPORTED_LANGUAGES_FOR_SUBTITLE_GENERATION
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
subtitle_options = ["Disabled", "Sentence", "Sentence + Comma"] + [
|
subtitle_options = ["Disabled", "Sentence", "Sentence + Comma", "Sentence + Highlighting"] + [
|
||||||
f"{i} word" if i == 1 else f"{i} words" for i in range(1, 11)
|
f"{i} word" if i == 1 else f"{i} words" for i in range(1, 11)
|
||||||
]
|
]
|
||||||
self.subtitle_combo.addItems(subtitle_options)
|
self.subtitle_combo.addItems(subtitle_options)
|
||||||
|
|||||||
Reference in New Issue
Block a user