This commit is contained in:
Morgan 2024-02-17 08:40:45 +09:00
parent 368c26d491
commit 826aca5c9f
No known key found for this signature in database
6 changed files with 1306 additions and 1056 deletions

View File

@ -1,11 +1,12 @@
import json import json
import re import re
def parse_vtt(vtt_filename): def parse_vtt(vtt_filename):
with open(vtt_filename, 'r', encoding='utf-8') as file: with open(vtt_filename, "r", encoding="utf-8") as file:
lines = file.readlines() lines = file.readlines()
time_pattern = re.compile(r'(\d+\.\d{3}) --> (\d+\.\d{3})') time_pattern = re.compile(r"(\d+\.\d{3}) --> (\d+\.\d{3})")
subtitles = [] subtitles = []
current_subtitle = {} current_subtitle = {}
@ -13,43 +14,47 @@ def parse_vtt(vtt_filename):
for line in lines[1:]: for line in lines[1:]:
match = time_pattern.match(line) match = time_pattern.match(line)
if match: if match:
current_subtitle['start'] = float(match.group(1)) current_subtitle["start"] = float(match.group(1))
current_subtitle['end'] = float(match.group(2)) current_subtitle["end"] = float(match.group(2))
current_subtitle['content'] = "" current_subtitle["content"] = ""
elif line.strip() == '': elif line.strip() == "":
if current_subtitle: if current_subtitle:
if current_subtitle['content'][-1] == "\n": if current_subtitle["content"][-1] == "\n":
current_subtitle['content'] = current_subtitle['content'][:-1] current_subtitle["content"] = current_subtitle["content"][:-1]
subtitles.append(current_subtitle) subtitles.append(current_subtitle)
current_subtitle = {} current_subtitle = {}
else: else:
current_subtitle['content'] += line.strip() + "\n" # Space to separate lines current_subtitle["content"] += (
line.strip() + "\n"
) # Space to separate lines
if current_subtitle: if current_subtitle:
if current_subtitle['content'][-1] == "\n": if current_subtitle["content"][-1] == "\n":
current_subtitle['content'] = current_subtitle['content'][:-1] current_subtitle["content"] = current_subtitle["content"][:-1]
subtitles.append(current_subtitle) subtitles.append(current_subtitle)
return subtitles return subtitles
def subtitles_to_backup(subtitles):
def subtitles_to_backup(subtitles):
backup_data = { backup_data = {
"subtitles": subtitles, "subtitles": subtitles,
"script_lines": [], "script_lines": [],
"line_index": len(subtitles), "line_index": len(subtitles),
"current_subtitle": {}, "current_subtitle": {},
"play": 0 "play": 0,
} }
return backup_data return backup_data
def main(vtt_filename, output_filename): def main(vtt_filename, output_filename):
subtitles = parse_vtt(vtt_filename) subtitles = parse_vtt(vtt_filename)
backup_data = subtitles_to_backup(subtitles) backup_data = subtitles_to_backup(subtitles)
with open(output_filename, 'w', encoding='utf-8') as json_file: with open(output_filename, "w", encoding="utf-8") as json_file:
json.dump(backup_data, json_file, indent=2) json.dump(backup_data, json_file, indent=2)
vtt_filename = 'audio.vtt'
output_filename = 'backup2.json' vtt_filename = "audio.vtt"
output_filename = "backup2.json"
main(vtt_filename, output_filename) main(vtt_filename, output_filename)

697
snusub.py
View File

@ -6,410 +6,479 @@ from datetime import timedelta
### ###
def from_vtt(vtt_string): def from_vtt(vtt_string):
VTT_TIMECODE_PATTERN = r"((?:\d{2}:)?\d{2}:\d{2}\.\d{3}) --> ((?:\d{2}:)?\d{2}:\d{2}\.\d{3})" VTT_TIMECODE_PATTERN = (
VTT_LINE_NUMBER_PATTERN = r"^\d+$" r"((?:\d{2}:)?\d{2}:\d{2}\.\d{3}) --> ((?:\d{2}:)?\d{2}:\d{2}\.\d{3})"
parts = re.split(r'\n\n+', vtt_string.strip()) )
if parts[0].startswith('WEBVTT'): VTT_LINE_NUMBER_PATTERN = r"^\d+$"
parts.pop(0) parts = re.split(r"\n\n+", vtt_string.strip())
if parts[0].startswith("WEBVTT"):
parts.pop(0)
subtitles = [] subtitles = []
for part in parts: for part in parts:
lines = part.split('\n') lines = part.split("\n")
match = re.match(VTT_TIMECODE_PATTERN, lines[0]) match = re.match(VTT_TIMECODE_PATTERN, lines[0])
if not match: if not match:
if re.match(VTT_LINE_NUMBER_PATTERN, lines[0]): if re.match(VTT_LINE_NUMBER_PATTERN, lines[0]):
lines.pop(0) lines.pop(0)
match = re.match(VTT_TIMECODE_PATTERN, lines[0]) match = re.match(VTT_TIMECODE_PATTERN, lines[0])
if not match: if not match:
continue continue
start, end = match.groups() start, end = match.groups()
content = '\n'.join(lines[1:]) + "\n" content = "\n".join(lines[1:]) + "\n"
# if start == end: # if start == end:
# continue # continue
subtitles.append({ subtitles.append(
'start': start, {
'end': end, "start": start,
'content': (content.replace("-\n", "\n").replace("</u>-\n", "</u>\n").replace("-", " ").replace("%", " ").replace("<u> "," <u>").replace(" </u>","</u> ").replace("<u> </u>","").replace("<u></u>","").replace(" \n", "\n"))[:-1] "end": end,
}) "content": (
content.replace("-\n", "\n")
.replace("</u>-\n", "</u>\n")
.replace("-", " ")
.replace("%", " ")
.replace("<u> ", " <u>")
.replace(" </u>", "</u> ")
.replace("<u> </u>", "")
.replace("<u></u>", "")
.replace(" \n", "\n")
)[:-1],
}
)
return subtitles
return subtitles
def to_vtt(subtitles): def to_vtt(subtitles):
vtt_content = "WEBVTT\n\n\n" vtt_content = "WEBVTT\n\n\n"
for idx, subtitle in enumerate(subtitles): for idx, subtitle in enumerate(subtitles):
content = subtitle['content'] content = subtitle["content"]
if not subtitle.get("split", False): if not subtitle.get("split", False):
start = subtitle['start'] start = subtitle["start"]
end = subtitle['end'] end = subtitle["end"]
if not start or not end or start == end: if not start or not end or start == end:
raise Exception(f"VTT timestamp parse error from #{idx}.") raise Exception(f"VTT timestamp parse error from #{idx}.")
vtt_content += f"{start} --> {end}\n{content}\n\n\n" vtt_content += f"{start} --> {end}\n{content}\n\n\n"
else: else:
vtt_content += f"NOTE {content}\n\n\n" vtt_content += f"NOTE {content}\n\n\n"
return vtt_content.strip() return vtt_content.strip()
def to_stacked_vtt(subtitles, continous = True):
vtt_content = "WEBVTT\n\n\n"
buffer = ""
for n, subtitle in enumerate(subtitles):
if subtitle.get("split", False):
buffer = ""
continue
if len(buffer) != 0: def to_stacked_vtt(subtitles, continous=True):
if str(subtitle['content'].strip())[-1] == ".": vtt_content = "WEBVTT\n\n\n"
buffer += "\n" buffer = ""
else: for n, subtitle in enumerate(subtitles):
buffer += " " if subtitle.get("split", False):
buffer = ""
continue
buffer += subtitle['content'].strip() if len(buffer) != 0:
if str(subtitle["content"].strip())[-1] == ".":
buffer += "\n"
else:
buffer += " "
if n < len(subtitles) - 1: buffer += subtitle["content"].strip()
end_time = subtitles[n+1]['start'] if continous and not subtitles[n+1].get("split", False) else subtitle['end']
else:
end_time = subtitle['end']
if not subtitle['start'] or not end_time: if n < len(subtitles) - 1:
raise Exception(f"VTT timestamp parse error from #{idx}.") end_time = (
if subtitle['start'] == end_time: subtitles[n + 1]["start"]
raise Exception(f"Error, subtitle timestamp overlaps.\n{subtitle['start']} --> {end_time} {subtitle['content'].strip()}") if continous and not subtitles[n + 1].get("split", False)
vtt_content += f"{subtitle['start']} --> {end_time}\n" else subtitle["end"]
vtt_content += buffer )
vtt_content += "\n\n\n" else:
end_time = subtitle["end"]
print(f"{subtitle['start']} --> {end_time}\n{buffer}\n\n") if not subtitle["start"] or not end_time:
raise Exception(f"VTT timestamp parse error from #{idx}.")
if subtitle["start"] == end_time:
raise Exception(
f"Error, subtitle timestamp overlaps.\n{subtitle['start']} --> {end_time} {subtitle['content'].strip()}"
)
vtt_content += f"{subtitle['start']} --> {end_time}\n"
vtt_content += buffer
vtt_content += "\n\n\n"
print(f"{subtitle['start']} --> {end_time}\n{buffer}\n\n")
return vtt_content
return vtt_content
### ###
def script_from_word_vtt(wordvtt): def script_from_word_vtt(wordvtt):
subtitles = from_vtt(wordvtt) subtitles = from_vtt(wordvtt)
print(f"Generating script file from VTT...") print(f"Generating script file from VTT...")
sentences = [] sentences = []
ADD_NEXT_SENTENCE = 0 ADD_NEXT_SENTENCE = 0
for n, subtitle in enumerate(subtitles): for n, subtitle in enumerate(subtitles):
sentence = subtitle["content"].replace("<u>", "").replace("</u>", "") sentence = subtitle["content"].replace("<u>", "").replace("</u>", "")
if ((sentences[-1] if sentences else None) != sentence) or ADD_NEXT_SENTENCE: if ((sentences[-1] if sentences else None) != sentence) or ADD_NEXT_SENTENCE:
sentences.append(sentence) sentences.append(sentence)
ADD_NEXT_SENTENCE = 0 ADD_NEXT_SENTENCE = 0
if subtitle["content"][-4:] == "</u>": if subtitle["content"][-4:] == "</u>":
ADD_NEXT_SENTENCE = 1 ADD_NEXT_SENTENCE = 1
if n + 2 < len(subtitles): if n + 2 < len(subtitles):
if subtitles[n+2]["content"].replace("<u>", "").replace("</u>", "") != sentence: if (
ADD_NEXT_SENTENCE = 0 subtitles[n + 2]["content"].replace("<u>", "").replace("</u>", "")
return sentences != sentence
):
ADD_NEXT_SENTENCE = 0
return sentences
def create_word_scenes(raw_vtt, raw_script): def create_word_scenes(raw_vtt, raw_script):
subtitles = from_vtt(raw_vtt) subtitles = from_vtt(raw_vtt)
scripts = [i for i in raw_script.split("\n") if i] scripts = [i for i in raw_script.split("\n") if i]
print(f"Found {len(subtitles)} subtitles, {len(scripts)} scenes.\nTimestamping each words...") print(
f"Found {len(subtitles)} subtitles, {len(scripts)} scenes.\nTimestamping each words..."
)
scenes = [] scenes = []
for n, script in enumerate(scripts): for n, script in enumerate(scripts):
if len(script.split(" ")) == 1: if len(script.split(" ")) == 1:
continue continue
scenes.append({"scene": script, "timestamp": []}) scenes.append({"scene": script, "timestamp": []})
scenes_cur = 0 scenes_cur = 0
for n, subtitle in enumerate(subtitles): for n, subtitle in enumerate(subtitles):
sentence = subtitle["content"].replace("<u>", "").replace("</u>", "") sentence = subtitle["content"].replace("<u>", "").replace("</u>", "")
if len(sentence.split(" ")) == 1: if len(sentence.split(" ")) == 1:
continue continue
if sentence != scenes[scenes_cur].get("scene"): if sentence != scenes[scenes_cur].get("scene"):
if sentence == scenes[scenes_cur+1].get("scene"): if sentence == scenes[scenes_cur + 1].get("scene"):
scenes_cur += 1 scenes_cur += 1
else: else:
raise Exception(f"Error, Failed to match sentence with scene.\n\"{scenes[scenes_cur].get("scene")}\" or \"[{scenes_cur+1}] {scenes[scenes_cur+1].get("scene")}\" != \"{sentence}\"") raise Exception(
f"Error, Failed to match sentence with scene.\n\"{scenes[scenes_cur].get("scene")}\" or \"[{scenes_cur+1}] {scenes[scenes_cur+1].get("scene")}\" != \"{sentence}\""
)
current_scene = scenes[scenes_cur] current_scene = scenes[scenes_cur]
if current_scene["timestamp"]: if current_scene["timestamp"]:
word_idx = current_scene["timestamp"][-1]["index"] + 1 word_idx = current_scene["timestamp"][-1]["index"] + 1
else: else:
word_idx = 0 word_idx = 0
if ("<u>" not in subtitle["content"]) and word_idx >= len(sentence.split(" ")): if ("<u>" not in subtitle["content"]) and word_idx >= len(sentence.split(" ")):
# Ignore trailing dummy subtitle after last word indexed. # Ignore trailing dummy subtitle after last word indexed.
pass pass
if ("<u>" in subtitle["content"]) and word_idx >= len(sentence.split(" ")): if ("<u>" in subtitle["content"]) and word_idx >= len(sentence.split(" ")):
# If there is trailing non-dummy timestamped subtitle, Reset word_idx and step to next scene. (Repeating sentence doesnt increment cur.) # If there is trailing non-dummy timestamped subtitle, Reset word_idx and step to next scene. (Repeating sentence doesnt increment cur.)
print(f"Error, Index wrong. {scenes_cur}, word: {word_idx}, total words: {len(sentence.split(" "))}\n{subtitle}") print(
word_idx = 0 f"Error, Index wrong. {scenes_cur}, word: {word_idx}, total words: {len(sentence.split(" "))}\n{subtitle}"
scenes_cur += 1 )
current_scene = scenes[scenes_cur] word_idx = 0
if current_scene["timestamp"]: scenes_cur += 1
word_idx = current_scene["timestamp"][-1]["index"] + 1 current_scene = scenes[scenes_cur]
else: if current_scene["timestamp"]:
word_idx = 0 word_idx = current_scene["timestamp"][-1]["index"] + 1
print(f"Changed to {word_idx}, {scenes_cur}") else:
word_idx = 0
print(f"Changed to {word_idx}, {scenes_cur}")
# Start matching words. # Start matching words.
if "<u>" in subtitle["content"]: if "<u>" in subtitle["content"]:
word = subtitle["content"].split("<u>")[1].split("</u>")[0] word = subtitle["content"].split("<u>")[1].split("</u>")[0]
if word not in sentence.split(" "): if word not in sentence.split(" "):
raise Exception(f"Error, Mismatch\n=> \"{word}\" not in \"{sentence}\"") raise Exception(f'Error, Mismatch\n=> "{word}" not in "{sentence}"')
return return
try: try:
assert sentence.split(" ")[word_idx] == word assert sentence.split(" ")[word_idx] == word
except: except:
raise Exception(f"Error, Mismatch\n=> \"{word}\" != [{word_idx}] of \"{sentence}\"") raise Exception(
f'Error, Mismatch\n=> "{word}" != [{word_idx}] of "{sentence}"'
)
word_time = {"start": subtitle["start"], "end": subtitle["end"], "index": word_idx, "word": word} word_time = {
current_scene["timestamp"].append(word_time) "start": subtitle["start"],
"end": subtitle["end"],
"index": word_idx,
"word": word,
}
current_scene["timestamp"].append(word_time)
for scene in scenes: for scene in scenes:
if len(scene["scene"].split(" ")) != len(scene["timestamp"]): if len(scene["scene"].split(" ")) != len(scene["timestamp"]):
raise Exception("Error, Scene length and timestamp length doesnt match.") raise Exception("Error, Scene length and timestamp length doesnt match.")
if "" in scene["scene"].split(" "): if "" in scene["scene"].split(" "):
print(repr(scene["scene"])) print(repr(scene["scene"]))
full_script, full_scenes = [], [] full_script, full_scenes = [], []
for scene in scenes: for scene in scenes:
full_script += scene["scene"].split(" ")[:-1] full_script += scene["scene"].split(" ")[:-1]
full_script.append(scene["scene"].split(" ")[-1]+"##") full_script.append(scene["scene"].split(" ")[-1] + "##")
full_scenes += scene["timestamp"] full_scenes += scene["timestamp"]
for i, j in zip(full_script, full_scenes): for i, j in zip(full_script, full_scenes):
if i.replace("##", "") != j["word"]: if i.replace("##", "") != j["word"]:
raise Exception("Error, Mismatch") raise Exception("Error, Mismatch")
return return
assert len(full_scenes) == len(full_script) assert len(full_scenes) == len(full_script)
return full_script, full_scenes
return full_script, full_scenes
def scene_from_new_script(raw_script, full_script, full_scenes): def scene_from_new_script(raw_script, full_script, full_scenes):
mod_script = raw_script.replace("\n", " \n ").split(" ") mod_script = raw_script.replace("\n", " \n ").split(" ")
mod_script = [i for i in mod_script if i] mod_script = [i for i in mod_script if i]
n = 0 n = 0
while True: while True:
if mod_script[n] == "\n": if mod_script[n] == "\n":
mod_script[n-1] += "\n" mod_script[n - 1] += "\n"
del(mod_script[n]) del mod_script[n]
n -= 1 n -= 1
n += 1 n += 1
if n == len(mod_script): if n == len(mod_script):
break break
print(f"Original: {len(full_script)}, Modded: {len(mod_script)}") print(f"Original: {len(full_script)}, Modded: {len(mod_script)}")
allowed_list = [".", "\n", "\n\n", ",", "?", "##"] allowed_list = [".", "\n", "\n\n", ",", "?", "##"]
def normalized(x): def normalized(x):
for i in allowed_list: for i in allowed_list:
x = x.replace(i, "") x = x.replace(i, "")
return x.upper() return x.upper()
same = lambda a, b: normalized(a) == normalized(b)
new_script, new_timestamp, orig_index, n = [], [], 0, 0
fail = 0
while n < len(mod_script):
print(f"{repr(mod_script[n]):>20} ? {repr(full_script[orig_index])}")
word = mod_script[n]
if same(word, full_script[orig_index].replace("##", "")):
cur = full_scenes[orig_index]
new_script.append(word.replace("##", ""))
new_timestamp.append({"start": cur["start"], "end": cur["end"]})
fail = 0
else:
if fail > 10:
raise Exception("Error: Failed to match words,")
return
fail += 1
n -= 1
n, orig_index = n + 1, orig_index + 1
assert len(new_script) == len(new_timestamp)
return new_script, new_timestamp
same = lambda a, b: normalized(a) == normalized(b)
new_script, new_timestamp, orig_index, n = [], [], 0, 0
fail = 0
while n < len(mod_script):
print(f"{repr(mod_script[n]):>20} ? {repr(full_script[orig_index])}")
word = mod_script[n]
if same(word, full_script[orig_index].replace("##", "")):
cur = full_scenes[orig_index]
new_script.append(word.replace("##", ""))
new_timestamp.append({"start": cur["start"], "end": cur["end"]})
fail = 0
else:
if fail > 10:
raise Exception("Error: Failed to match words,")
return
fail += 1
n -= 1
n, orig_index = n+1, orig_index+1
assert len(new_script) == len(new_timestamp)
return new_script, new_timestamp
def build_new_subtitle(new_script, new_timestamp): def build_new_subtitle(new_script, new_timestamp):
buffer, new_scenes, start, end = [], [], None, None buffer, new_scenes, start, end = [], [], None, None
current_scene = [] current_scene = []
for i, j in zip(new_script, new_timestamp): for i, j in zip(new_script, new_timestamp):
buffer.append(i.replace("\n", "")) buffer.append(i.replace("\n", ""))
if not start: if not start:
start = j["start"] start = j["start"]
if "\n" in i: if "\n" in i:
current_scene.append({"content": " ".join(buffer).replace("##", ""), "start": start, "end": j["end"]}) current_scene.append(
buffer, start = [], None {
"content": " ".join(buffer).replace("##", ""),
"start": start,
"end": j["end"],
}
)
buffer, start = [], None
if "\n\n" in i: if "\n\n" in i:
print(f"Section break at line #{len(current_scene):<3}| \"{current_scene[-1]["content"]}\"") print(
new_scenes.append(current_scene) f"Section break at line #{len(current_scene):<3}| \"{current_scene[-1]["content"]}\""
current_scene = [] )
new_scenes.append(current_scene)
current_scene = []
if start: if start:
buffer.append(i.replace("\n", "")) buffer.append(i.replace("\n", ""))
current_scene.append({"content": " ".join(buffer), "start": start, "end": j["end"]}) current_scene.append(
{"content": " ".join(buffer), "start": start, "end": j["end"]}
)
if current_scene != (new_scenes[-1] if new_scenes else None): if current_scene != (new_scenes[-1] if new_scenes else None):
new_scenes.append(current_scene) new_scenes.append(current_scene)
newsub = [] newsub = []
for n, i in enumerate(new_scenes): for n, i in enumerate(new_scenes):
newsub += i newsub += i
if n < len(new_scenes) - 1: if n < len(new_scenes) - 1:
newsub.append({"content": "Break", "start": None, "end": None, "split": True}) newsub.append(
{"content": "Break", "start": None, "end": None, "split": True}
)
return newsub
return newsub
### ###
def autobreak(lines, times): def autobreak(lines, times):
from datetime import timedelta from datetime import timedelta
def parsetime(time_str): def parsetime(time_str):
minutes, seconds = time_str.split(':') minutes, seconds = time_str.split(":")
seconds, milliseconds = seconds.split('.') seconds, milliseconds = seconds.split(".")
td = timedelta(minutes=int(minutes), seconds=int(seconds), milliseconds=int(milliseconds)) td = timedelta(
return td minutes=int(minutes), seconds=int(seconds), milliseconds=int(milliseconds)
)
return td
script = [] script = []
long_breaks = [] long_breaks = []
tmark = parsetime("0:0.0") tmark = parsetime("0:0.0")
for i, j in zip(lines, times): for i, j in zip(lines, times):
tdiff = parsetime(j["start"]) - tmark tdiff = parsetime(j["start"]) - tmark
tmark = parsetime(j["end"]) tmark = parsetime(j["end"])
if tdiff > parsetime("0:0.0"): if tdiff > parsetime("0:0.0"):
long_breaks.append(tdiff) long_breaks.append(tdiff)
mean_break = parsetime("0:0.0") mean_break = parsetime("0:0.0")
for i in long_breaks: for i in long_breaks:
mean_break += i/len(long_breaks) mean_break += i / len(long_breaks)
print(mean_break) print(mean_break)
script = "" script = ""
tmark = parsetime("0:0.0") tmark = parsetime("0:0.0")
tmp = " " tmp = " "
continous_line = 0 continous_line = 0
for i, j in zip(lines, times): for i, j in zip(lines, times):
tdiff = parsetime(j["start"]) - tmark tdiff = parsetime(j["start"]) - tmark
tmark = parsetime(j["end"]) tmark = parsetime(j["end"])
if tdiff > mean_break and tmp[-1] != ".": if tdiff > mean_break and tmp[-1] != ".":
script += "\n" script += "\n"
if (tdiff >= mean_break and tmp[-1] == "."): if tdiff >= mean_break and tmp[-1] == ".":
script += "\n" script += "\n"
continous_line = 0 continous_line = 0
else: else:
continous_line += 1 continous_line += 1
script += i.replace("##", "") script += i.replace("##", "")
if i[-1] == ".": if i[-1] == ".":
script += "\n" script += "\n"
elif "##" in i: elif "##" in i:
script += "\n" script += "\n"
else: else:
script += " " script += " "
tmp = i tmp = i
return script
return script
############################################ ############################################
def saveFile(filename, data, override = False):
if os.path.exists(filename) and not override: def saveFile(filename, data, override=False):
raise Exception(f"File {filename} already exists.") if os.path.exists(filename) and not override:
return raise Exception(f"File {filename} already exists.")
with open(filename, "w") as f: return
f.write(data) with open(filename, "w") as f:
f.write(data)
def openFile(filename): def openFile(filename):
if not os.path.exists(filename): if not os.path.exists(filename):
raise Exception(f"File {filename} doesnt exists.") raise Exception(f"File {filename} doesnt exists.")
return return
with open(filename, "r") as f: with open(filename, "r") as f:
data = f.read() data = f.read()
if not data: if not data:
raise Exception("Data empty.") raise Exception("Data empty.")
return return
return data return data
############################################ ############################################
if __name__=="__main__": if __name__ == "__main__":
PROG = sys.argv[0].split("/")[-1] PROG = sys.argv[0].split("/")[-1]
if len(sys.argv) not in (3, 4): if len(sys.argv) not in (3, 4):
print( \ print(
f"""Usage: {PROG} [COMMAND] [FILES]... f"""Usage: {PROG} [COMMAND] [FILES]...
Commands: Commands:
- script <VTT file> Generates script file from vtt file. - script <VTT file> Generates script file from vtt file.
- apply <VTT file> <script file> Applies new scripted file to create JSON file. - apply <VTT file> <script file> Applies new scripted file to create JSON file.
- create <JSON file> Creates new vtt from given JSON. - create <JSON file> Creates new vtt from given JSON.
""") """
sys.exit() )
sys.exit()
COMMAND = sys.argv[1] COMMAND = sys.argv[1]
if COMMAND not in ["script", "apply", "create"]: if COMMAND not in ["script", "apply", "create"]:
print("Error. Command not found.") print("Error. Command not found.")
sys.exit() sys.exit()
print(f"-> {sys.argv}") print(f"-> {sys.argv}")
if COMMAND == "script": if COMMAND == "script":
FILE = sys.argv[2] FILE = sys.argv[2]
if (not os.path.exists(FILE)): if not os.path.exists(FILE):
print(f"Input file doesnt exists.") print(f"Input file doesnt exists.")
sys.exit(-1) sys.exit(-1)
modfile = ".".join(scriptfile.split(".")[:-1]) + ".script" modfile = ".".join(scriptfile.split(".")[:-1]) + ".script"
x = create_word_scenes(openFile(FILE), "\n".join(script_from_word_vtt(openFile(FILE)))) x = create_word_scenes(
if not x: openFile(FILE), "\n".join(script_from_word_vtt(openFile(FILE)))
sys.exit(-1) )
if not x:
sys.exit(-1)
full_script, full_scenes = x full_script, full_scenes = x
genscript = autobreak(full_script, full_scenes) genscript = autobreak(full_script, full_scenes)
saveFile(modfile, genscript) saveFile(modfile, genscript)
print(f"Saved script file {modfile}.") print(f"Saved script file {modfile}.")
elif COMMAND == "apply": elif COMMAND == "apply":
if len(sys.argv) != 4: if len(sys.argv) != 4:
print(f"Not sufficient input.") print(f"Not sufficient input.")
sys.exit() sys.exit()
FILE1, FILE2 = sys.argv[2], sys.argv[3] FILE1, FILE2 = sys.argv[2], sys.argv[3]
if (not os.path.exists(FILE1)) or (not os.path.exists(FILE2)): if (not os.path.exists(FILE1)) or (not os.path.exists(FILE2)):
print(f"Input file doesnt exists.") print(f"Input file doesnt exists.")
sys.exit(-1) sys.exit(-1)
x = create_word_scenes(openFile(FILE1), "\n".join(script_from_word_vtt(openFile(FILE1)))) x = create_word_scenes(
if not x: openFile(FILE1), "\n".join(script_from_word_vtt(openFile(FILE1)))
sys.exit(-1) )
full_script, full_scenes = x if not x:
sys.exit(-1)
full_script, full_scenes = x
x = scene_from_new_script(openFile(FILE2), full_script, full_scenes) x = scene_from_new_script(openFile(FILE2), full_script, full_scenes)
if not x: if not x:
sys.exit(-1) sys.exit(-1)
a, b = x a, b = x
final_sub = build_new_subtitle(a, b) final_sub = build_new_subtitle(a, b)
jsonfile = ".".join(FILE1.split(".")[:-1]) + ".json" jsonfile = ".".join(FILE1.split(".")[:-1]) + ".json"
saveFile(jsonfile, json.dumps(final_sub, indent=2), True) saveFile(jsonfile, json.dumps(final_sub, indent=2), True)
print(f"Saved JSON file {jsonfile}.") print(f"Saved JSON file {jsonfile}.")
sys.exit(0) sys.exit(0)
elif COMMAND == "create": elif COMMAND == "create":
FILE = sys.argv[2] FILE = sys.argv[2]
if (not os.path.exists(FILE)): if not os.path.exists(FILE):
print(f"Input file doesnt exists.") print(f"Input file doesnt exists.")
sys.exit(-1) sys.exit(-1)
final_vtt = json.loads(openFile(FILE)) final_vtt = json.loads(openFile(FILE))
orgf = ".".join(FILE.split(".")[:-1]) orgf = ".".join(FILE.split(".")[:-1])
print(f"Saved VTT file as {orgf}.final.vtt.") print(f"Saved VTT file as {orgf}.final.vtt.")
if os.path.exists(orgf + ".vtt"): if os.path.exists(orgf + ".vtt"):
saveFile(orgf + ".stacked.vtt", to_stacked_vtt(final_vtt), True) saveFile(orgf + ".stacked.vtt", to_stacked_vtt(final_vtt), True)
else: else:
saveFile(orgf + ".vtt", to_stacked_vtt(final_vtt), True) saveFile(orgf + ".vtt", to_stacked_vtt(final_vtt), True)
sys.exit(0) sys.exit(0)

View File

@ -1,18 +1,21 @@
import re import re
from datetime import timedelta from datetime import timedelta
VTT_TIMECODE_PATTERN = r"((?:\d{2}:)?\d{2}:\d{2}\.\d{3}) --> ((?:\d{2}:)?\d{2}:\d{2}\.\d{3})" VTT_TIMECODE_PATTERN = (
r"((?:\d{2}:)?\d{2}:\d{2}\.\d{3}) --> ((?:\d{2}:)?\d{2}:\d{2}\.\d{3})"
)
VTT_LINE_NUMBER_PATTERN = r"^\d+$" VTT_LINE_NUMBER_PATTERN = r"^\d+$"
def from_vtt(vtt_string):
parts = re.split(r'\n\n+', vtt_string.strip())
if parts[0].startswith('WEBVTT'): def from_vtt(vtt_string):
parts = re.split(r"\n\n+", vtt_string.strip())
if parts[0].startswith("WEBVTT"):
parts.pop(0) parts.pop(0)
subtitles = [] subtitles = []
for part in parts: for part in parts:
lines = part.split('\n') lines = part.split("\n")
match = re.match(VTT_TIMECODE_PATTERN, lines[0]) match = re.match(VTT_TIMECODE_PATTERN, lines[0])
if not match: if not match:
if re.match(VTT_LINE_NUMBER_PATTERN, lines[0]): if re.match(VTT_LINE_NUMBER_PATTERN, lines[0]):
@ -22,25 +25,23 @@ def from_vtt(vtt_string):
continue continue
start, end = match.groups() start, end = match.groups()
content = '\n'.join(lines[1:]) content = "\n".join(lines[1:])
subtitles.append({ subtitles.append({"start": start, "end": end, "content": content})
'start': start,
'end': end,
'content': content
})
return subtitles return subtitles
def to_vtt(subtitles): def to_vtt(subtitles):
vtt_content = "WEBVTT\n\n" vtt_content = "WEBVTT\n\n"
for idx, subtitle in enumerate(subtitles): for idx, subtitle in enumerate(subtitles):
start = subtitle['start'] start = subtitle["start"]
end = subtitle['end'] end = subtitle["end"]
content = subtitle['content'] content = subtitle["content"]
vtt_content += f"{start} --> {end}\n{content}\n\n" vtt_content += f"{start} --> {end}\n{content}\n\n"
return vtt_content.strip() return vtt_content.strip()
def stack_subtitle(): def stack_subtitle():
buffer = [] buffer = []
linebuf = [] linebuf = []
@ -62,10 +63,11 @@ def stack_subtitle():
# if scene["content"][-1] == ".": # if scene["content"][-1] == ".":
strbuf += "\n" strbuf += "\n"
# else: # else:
# strbuf += " " # strbuf += " "
scene["content"] = strbuf scene["content"] = strbuf
sub.append(scene) sub.append(scene)
with open("example.vtt", "r") as f: with open("example.vtt", "r") as f:
vtt_content = f.read() vtt_content = f.read()

View File

@ -1,79 +1,88 @@
import json import json
import os, sys import os, sys
def readFile(file):
if not os.path.exists(file):
raise Exception(f"File {file} doesn't exists.")
with open(file, "r") as f:
data = f.read()
return data
def writeFile(file, data, overwrite = False): def readFile(file):
if (not overwrite) and os.path.exists(file): if not os.path.exists(file):
raise Exception(f"File {file} already exists.") raise Exception(f"File {file} doesn't exists.")
if not len(data): with open(file, "r") as f:
raise Exception(f"Tried to write empty data.") data = f.read()
with open(file, "w") as f: return data
ret = f.write(data)
return ret
def writeFile(file, data, overwrite=False):
if (not overwrite) and os.path.exists(file):
raise Exception(f"File {file} already exists.")
if not len(data):
raise Exception(f"Tried to write empty data.")
with open(file, "w") as f:
ret = f.write(data)
return ret
file = sys.argv[1] file = sys.argv[1]
if ".json" in file: if ".json" in file:
subtitles = json.loads(readFile(file)) subtitles = json.loads(readFile(file))
output = "" output = ""
index = 0 index = 0
for subtitle in subtitles: for subtitle in subtitles:
if subtitle.get("split", False): if subtitle.get("split", False):
output += "\n" output += "\n"
else: else:
index += 1 index += 1
start = subtitle["start"] start = subtitle["start"]
end = subtitle["end"] end = subtitle["end"]
content = subtitle["content"] content = subtitle["content"]
"| {start:>10} --> {end:>10} |" "| {start:>10} --> {end:>10} |"
output += f"{index:03} | {content.strip()}\n" output += f"{index:03} | {content.strip()}\n"
output += "############ TIMESTAMPS ############\n\n" output += "############ TIMESTAMPS ############\n\n"
index = 0 index = 0
for subtitle in subtitles: for subtitle in subtitles:
if not subtitle.get("split", False): if not subtitle.get("split", False):
index += 1 index += 1
start = subtitle["start"] start = subtitle["start"]
end = subtitle["end"] end = subtitle["end"]
output += f"{index:03} | {start} --> {end} \n" output += f"{index:03} | {start} --> {end} \n"
writeFile(os.path.splitext(file)[0]+".edit", output) writeFile(os.path.splitext(file)[0] + ".edit", output)
elif ".edit" in file: elif ".edit" in file:
subtitles = json.loads(readFile(os.path.splitext(file)[0]+".json")) subtitles = json.loads(readFile(os.path.splitext(file)[0] + ".json"))
lines = readFile(file) lines = readFile(file)
idx, sub = 0, {} idx, sub = 0, {}
for subtitle in subtitles: for subtitle in subtitles:
if not subtitle.get("split", False): if not subtitle.get("split", False):
sub[idx] = subtitle sub[idx] = subtitle
idx += 1 idx += 1
new_brk, new_sub = [], {} new_brk, new_sub = [], {}
for line in lines.split("\n"): for line in lines.split("\n"):
if "\n############ TIMESTAMPS ############" == line: if "\n############ TIMESTAMPS ############" == line:
break break
if line: if line:
idx, content = line.split(" | ") idx, content = line.split(" | ")
idx = int(idx) - 1 idx = int(idx) - 1
if sub[idx]["content"] != content: if sub[idx]["content"] != content:
print(f"{idx} {sub[idx]["content"]} -> {content}") print(f"{idx} {sub[idx]["content"]} -> {content}")
new_sub[idx] = {"content": content, "start": sub[idx]["start"], "end": sub[idx]["end"]} new_sub[idx] = {
else: "content": content,
new_brk.append(idx) "start": sub[idx]["start"],
"end": sub[idx]["end"],
}
else:
new_brk.append(idx)
output = [] output = []
for n in sorted(new_sub): for n in sorted(new_sub):
subtitle = new_sub[n] subtitle = new_sub[n]
output.append(subtitle) output.append(subtitle)
if n in new_brk: if n in new_brk:
output.append({"content": "Break", "start": None, "end": None, "split": True}) output.append(
{"content": "Break", "start": None, "end": None, "split": True}
)
writeFile(os.path.splitext(file)[0]+".json.1", json.dumps(output, indent=2)) writeFile(os.path.splitext(file)[0] + ".json.1", json.dumps(output, indent=2))

View File

@ -7,7 +7,7 @@ from tkinter import messagebox
import vlc import vlc
import time, sys import time, sys
vlc_instance = vlc.Instance('--verbose=0') vlc_instance = vlc.Instance("--verbose=0")
player = vlc_instance.media_player_new() player = vlc_instance.media_player_new()
audio_started = False audio_started = False
@ -20,6 +20,7 @@ MediaTotalLength = 0
stdout_buf = [] stdout_buf = []
class TextRedirector(object): class TextRedirector(object):
def __init__(self, buf, origin): def __init__(self, buf, origin):
self.buffer = buf self.buffer = buf
@ -32,122 +33,143 @@ class TextRedirector(object):
def flush(self): def flush(self):
self.origin.flush() self.origin.flush()
def toggle_audio(event = None):
global audio_started
if not audio_started:
print("Audio Play.")
player.play()
audio_started = True
elif player.is_playing():
print("Audio Pause.")
player.pause()
else:
print("Audio Play.")
player.play()
def rewind_audio(event = None): def toggle_audio(event=None):
new_time = max(player.get_time() - 5000, 0) global audio_started
player.set_time(new_time) if not audio_started:
print("Audio Play.")
player.play()
audio_started = True
elif player.is_playing():
print("Audio Pause.")
player.pause()
else:
print("Audio Play.")
player.play()
def rewind_audio(event=None):
new_time = max(player.get_time() - 5000, 0)
player.set_time(new_time)
def fastforward_audio(event=None):
new_time = player.get_time() + 5000
player.set_time(new_time)
def fastforward_audio(event = None):
new_time = player.get_time() + 5000
player.set_time(new_time)
def mark_start(): def mark_start():
print(line_index) print(line_index)
timestamp = player.get_time() / 1000.0 timestamp = player.get_time() / 1000.0
current_subtitle["start"] = timestamp current_subtitle["start"] = timestamp
if line_index > len(script_lines) or not len(script_lines): if line_index > len(script_lines) or not len(script_lines):
print("Please load first..") print("Please load first..")
return return
if line_index > len(script_lines) - 1: if line_index > len(script_lines) - 1:
messagebox.showerror("Error", "No more subtitle.") messagebox.showerror("Error", "No more subtitle.")
current_subtitle["start"] = None current_subtitle["start"] = None
return return
current_subtitle["content"] = script_lines[line_index] current_subtitle["content"] = script_lines[line_index]
current_subtitle["index"] = line_index current_subtitle["index"] = line_index
update_display() update_display()
print(f"\n{timestamp} --> ", end="") print(f"\n{timestamp} --> ", end="")
def mark_end(): def mark_end():
if current_subtitle.get("content") == None: if current_subtitle.get("content") == None:
print("Please load first..") print("Please load first..")
return return
timestamp = player.get_time() / 1000.0 timestamp = player.get_time() / 1000.0
current_subtitle["end"] = timestamp current_subtitle["end"] = timestamp
subtitles.append(current_subtitle.copy()) subtitles.append(current_subtitle.copy())
print(f"{current_subtitle["end"]}\n{current_subtitle["content"]}") print(f"{current_subtitle["end"]}\n{current_subtitle["content"]}")
current_subtitle["content"] = ""
current_subtitle["start"] = None
load_next_line()
update_display()
def on_next_press(event = None):
if current_subtitle.get("start") is None:
mark_start()
else:
mark_end()
def on_autonext_press(event = None):
if current_subtitle.get("start") is None:
mark_start()
else:
mark_end()
mark_start()
def on_done_press(event = None):
if line_index < 1:
return
if current_subtitle["start"] != None:
mark_end()
subtitles.append({"start": None,"end": None,"content": None,"split": True})
scene_breaks.append(line_index)
update_display()
def on_back(event = None):
global subtitles
if len(subtitles) == 0:
messagebox.showerror("Error", f"No subtitle to remove.")
return
if current_subtitle["start"]:
if not messagebox.askokcancel("Warning", f"\nDeleting current #{current_subtitle.get("index")}.\n You need to go back and mark start again."):
return
current_subtitle["content"] = "" current_subtitle["content"] = ""
if len(subtitles) > 1:
current_subtitle["content"] = subtitles[-1]["content"] + "\n"
current_subtitle["start"] = None current_subtitle["start"] = None
else: load_next_line()
if not messagebox.askokcancel("Warning", f"\nDeleting previous #{subtitles[-1].get("index")}.\n You need to go back and mark start again."): update_display()
return
del(subtitles[-1])
current_subtitle["content"] = "" def on_next_press(event=None):
if len(subtitles) > 2: if current_subtitle.get("start") is None:
current_subtitle["content"] = subtitles[-1]["content"] + "\n" mark_start()
current_subtitle["start"] = None else:
print(f"\nCurrent: #{len(subtitles)+1} {current_subtitle}") mark_end()
load_next_line(diff = -1)
def on_autonext_press(event=None):
if current_subtitle.get("start") is None:
mark_start()
else:
mark_end()
mark_start()
def on_done_press(event=None):
if line_index < 1:
return
if current_subtitle["start"] != None:
mark_end()
subtitles.append({"start": None, "end": None, "content": None, "split": True})
scene_breaks.append(line_index)
update_display()
def on_back(event=None):
global subtitles
if len(subtitles) == 0:
messagebox.showerror("Error", f"No subtitle to remove.")
return
if current_subtitle["start"]:
if not messagebox.askokcancel(
"Warning",
f"\nDeleting current #{current_subtitle.get("index")}.\n You need to go back and mark start again.",
):
return
current_subtitle["content"] = ""
if len(subtitles) > 1:
current_subtitle["content"] = subtitles[-1]["content"] + "\n"
current_subtitle["start"] = None
else:
if not messagebox.askokcancel(
"Warning",
f"\nDeleting previous #{subtitles[-1].get("index")}.\n You need to go back and mark start again.",
):
return
del subtitles[-1]
current_subtitle["content"] = ""
if len(subtitles) > 2:
current_subtitle["content"] = subtitles[-1]["content"] + "\n"
current_subtitle["start"] = None
print(f"\nCurrent: #{len(subtitles)+1} {current_subtitle}")
load_next_line(diff=-1)
def update_timestamp(): def update_timestamp():
current_pos = max(player.get_time() / 1000, 0) current_pos = max(player.get_time() / 1000, 0)
timestamp_label.config(text=f"{to_time(current_pos, True)} / {to_time(MediaTotalLength, True)}") timestamp_label.config(
root.after(200, update_timestamp) text=f"{to_time(current_pos, True)} / {to_time(MediaTotalLength, True)}"
)
root.after(200, update_timestamp)
def multiline(content): def multiline(content):
return content.strip() return content.strip()
if content: if content:
return "- " + content.strip().replace("\n", "\n- ") return "- " + content.strip().replace("\n", "\n- ")
def to_time(seconds, hide=False):
minutes = int(seconds // 60)
second = seconds % 60
hours = int(minutes // 60)
minute = minutes % 60
if hide:
if minutes > 60:
return f"{hours}:{minute:02}:{second:06.3f}"
else:
return f"{minute:02}:{second:06.3f}"
return f"{hours}:{minute:02}:{second:06.3f}"
def to_time(seconds, hide = False):
minutes = int(seconds // 60)
second = seconds % 60
hours = int(minutes // 60)
minute = minutes % 60
if hide:
if minutes > 60:
return f"{hours}:{minute:02}:{second:06.3f}"
else:
return f"{minute:02}:{second:06.3f}"
return f"{hours}:{minute:02}:{second:06.3f}"
def update_display(): def update_display():
listbox_scroll_pos = script_listbox.yview() listbox_scroll_pos = script_listbox.yview()
@ -166,61 +188,65 @@ def update_display():
lstart = subtitles[n]["start"] lstart = subtitles[n]["start"]
lend = subtitles[n]["end"] lend = subtitles[n]["end"]
if lstart: if lstart:
subtitle_display = f"{subtitles[n]["index"]}\n{to_time(lstart)} --> {to_time(lend)}\n{multiline(subtitles[n]['content']).replace("\n", "\n")} \n\n" subtitle_display = f"{subtitles[n]["index"]}\n{to_time(lstart)} --> {to_time(lend)}\n{multiline(subtitles[n]['content']).replace("\n", "\n")} \n\n"
else: else:
scene_breaks.append(subtitles[n-1]["index"]) scene_breaks.append(subtitles[n - 1]["index"])
print(f"End section at idx {subtitles[n-1]["index"]}, {to_time(subtitles[n-1]["end"])}") print(
subtitle_display = f"----------\n\n" f"End section at idx {subtitles[n-1]["index"]}, {to_time(subtitles[n-1]["end"])}"
)
subtitle_display = f"----------\n\n"
subtitle_text.insert(tk.END, subtitle_display) subtitle_text.insert(tk.END, subtitle_display)
elif n == len(subtitles) : elif n == len(subtitles):
lstart = current_subtitle.get("start", "") lstart = current_subtitle.get("start", "")
if lstart: if lstart:
subtitle_display = f"{subtitles[n]["index"]}\n{to_time(lstart)} -> \n{multiline(current_subtitle.get('content').replace("\n", "\n"))} \n\n" subtitle_display = f"{subtitles[n]["index"]}\n{to_time(lstart)} -> \n{multiline(current_subtitle.get('content').replace("\n", "\n"))} \n\n"
subtitle_text.insert(tk.END, subtitle_display) subtitle_text.insert(tk.END, subtitle_display)
list_color = 0 list_color = 0
for n, line in enumerate(script_lines): for n, line in enumerate(script_lines):
display_line = " " + line display_line = " " + line
script_listbox.insert(tk.END, f"{n:03} " + display_line) script_listbox.insert(tk.END, f"{n:03} " + display_line)
if n-1 in scene_breaks: if n - 1 in scene_breaks:
print(f"New section from {n}") print(f"New section from {n}")
list_color = ~list_color list_color = ~list_color
if n == line_index: if n == line_index:
script_listbox.itemconfig(n, {'bg': 'lightgrey'}) script_listbox.itemconfig(n, {"bg": "lightgrey"})
if n < line_index: if n < line_index:
if list_color: if list_color:
script_listbox.itemconfig(n, {'bg': '#FFEFFF'}) script_listbox.itemconfig(n, {"bg": "#FFEFFF"})
else: else:
script_listbox.itemconfig(n, {'bg': '#FFFFEF'}) script_listbox.itemconfig(n, {"bg": "#FFFFEF"})
if player.is_playing() or not audio_started: if player.is_playing() or not audio_started:
script_listbox.see(min(line_index + 5, len(script_lines))) script_listbox.see(min(line_index + 5, len(script_lines)))
subtitle_text.see(tk.END) subtitle_text.see(tk.END)
else: else:
script_listbox.yview_moveto(listbox_scroll_pos[0]) script_listbox.yview_moveto(listbox_scroll_pos[0])
subtitle_text.see(text_scroll_index) subtitle_text.see(text_scroll_index)
info_label.config(text="[ Current stack ]\n" + current_subtitle.get('content', "")) info_label.config(text="[ Current stack ]\n" + current_subtitle.get("content", ""))
subtitle_text.config(state=tk.DISABLED) subtitle_text.config(state=tk.DISABLED)
def load_next_line(diff = 1):
global line_index
print(line_index, len(script_lines))
if (line_index < len(script_lines) - 1) or (diff < 0): def load_next_line(diff=1):
line_index += diff global line_index
update_display() print(line_index, len(script_lines))
return
if (line_index < len(script_lines) - 1) or (diff < 0):
line_index += diff
update_display()
return
elif line_index == len(script_lines) - 1:
line_index += 1
update_display()
if player.is_playing():
player.pause()
messagebox.showerror("Error", f"No more subtitle.")
elif line_index == len(script_lines) - 1:
line_index += 1
update_display()
if player.is_playing():
player.pause()
messagebox.showerror("Error", f"No more subtitle.")
def load_file(label): def load_file(label):
file_path = filedialog.askopenfilename() file_path = filedialog.askopenfilename()
@ -229,6 +255,7 @@ def load_file(label):
return file_path return file_path
return None return None
def choose_audio(): def choose_audio():
global player, MediaTotalLength global player, MediaTotalLength
file_path = load_file(audio_label) file_path = load_file(audio_label)
@ -236,82 +263,114 @@ def choose_audio():
player.set_media(media := vlc.Media(file_path)) player.set_media(media := vlc.Media(file_path))
media.parse_with_options(vlc.MediaParseFlag.fetch_network, 0) media.parse_with_options(vlc.MediaParseFlag.fetch_network, 0)
while not media.is_parsed(): while not media.is_parsed():
time.sleep(0.1) time.sleep(0.1)
MediaTotalLength = (media.get_duration() // 1000) MediaTotalLength = media.get_duration() // 1000
print(f"Media loaded {file_path} length {MediaTotalLength}") print(f"Media loaded {file_path} length {MediaTotalLength}")
def choose_script(): def choose_script():
global script_lines, line_index, current_subtitle global script_lines, line_index, current_subtitle
file_path = load_file(script_label) file_path = load_file(script_label)
if file_path: if file_path:
script_lines = open(file_path, 'r').read().splitlines() script_lines = open(file_path, "r").read().splitlines()
subtitles.clear() subtitles.clear()
line_index, current_subtitle = 0, {} line_index, current_subtitle = 0, {}
print(f"Script loaded {file_path}") print(f"Script loaded {file_path}")
update_display() update_display()
def save_subtitles(): def save_subtitles():
vtt_path = filedialog.asksaveasfilename(defaultextension=".vtt", filetypes=[("VTT files", "*.vtt"), ("All files", "*.*")]) vtt_path = filedialog.asksaveasfilename(
defaultextension=".vtt",
filetypes=[("VTT files", "*.vtt"), ("All files", "*.*")],
)
if vtt_path: if vtt_path:
with open(vtt_path, 'w') as f: with open(vtt_path, "w") as f:
f.write('WEBVTT\n\n') f.write("WEBVTT\n\n")
for subtitle in subtitles: for subtitle in subtitles:
f.write(f"{to_time(subtitle['start'])} --> {to_time(subtitle['end'])}\n") f.write(
f.write(multiline(subtitle['content'])) f"{to_time(subtitle['start'])} --> {to_time(subtitle['end'])}\n"
)
f.write(multiline(subtitle["content"]))
f.write("\n\n") f.write("\n\n")
print(f"Done saving {len(subtitles)} subtitles") print(f"Done saving {len(subtitles)} subtitles")
def save_stacked_subtitles(): def save_stacked_subtitles():
vtt_path = filedialog.asksaveasfilename(defaultextension=".vtt", filetypes=[("VTT files", "*.vtt"), ("All files", "*.*")]) vtt_path = filedialog.asksaveasfilename(
defaultextension=".vtt",
filetypes=[("VTT files", "*.vtt"), ("All files", "*.*")],
)
if vtt_path: if vtt_path:
with open(vtt_path, 'w') as f: with open(vtt_path, "w") as f:
f.write('WEBVTT\n\n\n') f.write("WEBVTT\n\n\n")
buffer = "" buffer = ""
for subtitle in subtitles: for subtitle in subtitles:
if subtitle.get("split", False): if subtitle.get("split", False):
buffer = "" buffer = ""
continue continue
if len(buffer) != 0: if len(buffer) != 0:
if str(subtitle['content'].strip())[-1] == ".": if str(subtitle["content"].strip())[-1] == ".":
buffer += "\n" buffer += "\n"
else: else:
buffer += " " buffer += " "
buffer += multiline(subtitle['content']) buffer += multiline(subtitle["content"])
f.write(f"{to_time(subtitle['start'])} --> {to_time(subtitle['end'])}\n") f.write(
f"{to_time(subtitle['start'])} --> {to_time(subtitle['end'])}\n"
)
f.write(buffer) f.write(buffer)
f.write("\n\n\n") f.write("\n\n\n")
print(f"Done saving {len(subtitles)} subtitles (stacking)") print(f"Done saving {len(subtitles)} subtitles (stacking)")
def save_prog(): def save_prog():
import json import json
fpath = filedialog.asksaveasfilename(defaultextension=".json", filetypes=[("JSON files", "*.json"), ("All files", "*.*")])
if fpath: fpath = filedialog.asksaveasfilename(
with open(fpath, 'w') as f: defaultextension=".json",
f.write(json.dumps({"subtitles": subtitles, "script_lines": script_lines,"line_index": line_index,"current_subtitle": current_subtitle, "play": player.get_time()}, indent=2)) filetypes=[("JSON files", "*.json"), ("All files", "*.*")],
)
if fpath:
with open(fpath, "w") as f:
f.write(
json.dumps(
{
"subtitles": subtitles,
"script_lines": script_lines,
"line_index": line_index,
"current_subtitle": current_subtitle,
"play": player.get_time(),
},
indent=2,
)
)
def load_prog(): def load_prog():
global subtitles, player, script_lines, line_index, current_subtitle global subtitles, player, script_lines, line_index, current_subtitle
import json import json
fpath = filedialog.askopenfilename()
if fpath: fpath = filedialog.askopenfilename()
with open(fpath, 'r') as f: if fpath:
back = json.loads(f.read()) with open(fpath, "r") as f:
subtitles = back["subtitles"] back = json.loads(f.read())
script_lines = back["script_lines"] subtitles = back["subtitles"]
line_index = back["line_index"] script_lines = back["script_lines"]
current_subtitle = back["current_subtitle"] line_index = back["line_index"]
curpos = int(back["play"]) current_subtitle = back["current_subtitle"]
messagebox.showinfo("Loaded", f"Loaded {len(subtitles)} subtitles.") curpos = int(back["play"])
player.play() messagebox.showinfo("Loaded", f"Loaded {len(subtitles)} subtitles.")
player.set_time(curpos) player.play()
time.sleep(0.3) player.set_time(curpos)
player.pause() time.sleep(0.3)
update_display() player.pause()
update_display()
def skip_to_time(): def skip_to_time():
try: try:
@ -324,56 +383,63 @@ def skip_to_time():
except Exception as e: except Exception as e:
print(e) print(e)
def on_list_right_click(event): def on_list_right_click(event):
try: try:
context_menu.tk_popup(event.x_root, event.y_root) context_menu.tk_popup(event.x_root, event.y_root)
finally: finally:
context_menu.grab_release() context_menu.grab_release()
def on_left_click(event): def on_left_click(event):
context_menu.unpost() context_menu.unpost()
def on_list_left_click(event): def on_list_left_click(event):
if player.is_playing(): if player.is_playing():
player.pause() player.pause()
on_left_click(event) on_left_click(event)
def merge_selected(event = None):
def merge_selected(event=None):
selected_indices = script_listbox.curselection() selected_indices = script_listbox.curselection()
if not selected_indices: if not selected_indices:
messagebox.showinfo("No selection", "Please select items to merge.") messagebox.showinfo("No selection", "Please select items to merge.")
return return
if len(selected_indices) == 1: if len(selected_indices) == 1:
messagebox.showinfo("Not enough selection", "Please select multiple item to merge.") messagebox.showinfo(
"Not enough selection", "Please select multiple item to merge."
)
return return
selected_lines = [script_lines[i] for i in selected_indices] selected_lines = [script_lines[i] for i in selected_indices]
print("Merge", selected_lines) print("Merge", selected_lines)
merged_line = ' '.join(selected_lines) merged_line = " ".join(selected_lines)
script_lines[selected_indices[0]] = merged_line script_lines[selected_indices[0]] = merged_line
for i in selected_indices[1:]: for i in selected_indices[1:]:
del(script_lines[i]) del script_lines[i]
update_display() update_display()
def edit_selected(event = None, selected_idx = None):
def edit_selected(event=None, selected_idx=None):
if not selected_idx: if not selected_idx:
selected_indices = script_listbox.curselection() selected_indices = script_listbox.curselection()
if not selected_indices: if not selected_indices:
messagebox.showinfo("No selection", "Please select item to split.") messagebox.showinfo("No selection", "Please select item to split.")
return return
if len(selected_indices) > 1: if len(selected_indices) > 1:
messagebox.showinfo("Too many selection", "Please select one item.") messagebox.showinfo("Too many selection", "Please select one item.")
return return
selected_idx = selected_indices[0] selected_idx = selected_indices[0]
selected_line = script_lines[selected_idx] selected_line = script_lines[selected_idx]
print("Edit", selected_line) print("Edit", selected_line)
top = tk.Toplevel(root) top = tk.Toplevel(root)
top.title("Edit and Split Line") top.title("Edit and Split Line")
@ -382,16 +448,17 @@ def edit_selected(event = None, selected_idx = None):
text.insert(tk.END, selected_line) text.insert(tk.END, selected_line)
editted_lines = [] editted_lines = []
def edit_and_close(event = None):
def edit_and_close(event=None):
global splitted_lines global splitted_lines
edited_line = text.get('1.0', tk.END).strip() edited_line = text.get("1.0", tk.END).strip()
editted_lines = edited_line.split('\n') editted_lines = edited_line.split("\n")
top.destroy() top.destroy()
del(script_lines[selected_idx]) del script_lines[selected_idx]
print(editted_lines) print(editted_lines)
for i in reversed(editted_lines): for i in reversed(editted_lines):
script_lines.insert(selected_idx, i) script_lines.insert(selected_idx, i)
update_display() update_display()
@ -403,46 +470,53 @@ def edit_selected(event = None, selected_idx = None):
button.pack(pady=5) button.pack(pady=5)
def remove_selected(event = None): def remove_selected(event=None):
selected_indices = script_listbox.curselection() selected_indices = script_listbox.curselection()
if not selected_indices: if not selected_indices:
messagebox.showinfo("No selection", "Please select items to remove.") messagebox.showinfo("No selection", "Please select items to remove.")
return return
print("Delete", selected_lines) print("Delete", selected_lines)
for i in selected_indices[1:]: for i in selected_indices[1:]:
del(script_lines[i]) del script_lines[i]
update_display() update_display()
def on_list_double_click(event):
index = script_listbox.nearest(event.y)
if not script_listbox.selection_includes(index):
return
text = script_listbox.get(index)
entry = tk.Entry(root, bd=1, highlightthickness=1, )
entry.insert(0, text)
entry.select_range(0, tk.END)
def save_edit(event=None):
script_listbox.delete(index)
script_listbox.insert(index, entry.get())
entry.destroy()
bbox = script_listbox.bbox(index)
entry.place(x=bbox[0], y=bbox[1], width=bbox[2], height=bbox[3])
entry.bind("<Return>", save_edit)
entry.bind("<FocusOut>", save_edit)
entry.bind("<Escape>", lambda e: entry.destroy())
entry.focus_set()
def on_list_double_click(event): def on_list_double_click(event):
index = script_listbox.nearest(event.y) index = script_listbox.nearest(event.y)
if not script_listbox.selection_includes(index): if not script_listbox.selection_includes(index):
return return
edit_selected(selected_idx = index)
text = script_listbox.get(index)
entry = tk.Entry(
root,
bd=1,
highlightthickness=1,
)
entry.insert(0, text)
entry.select_range(0, tk.END)
def save_edit(event=None):
script_listbox.delete(index)
script_listbox.insert(index, entry.get())
entry.destroy()
bbox = script_listbox.bbox(index)
entry.place(x=bbox[0], y=bbox[1], width=bbox[2], height=bbox[3])
entry.bind("<Return>", save_edit)
entry.bind("<FocusOut>", save_edit)
entry.bind("<Escape>", lambda e: entry.destroy())
entry.focus_set()
def on_list_double_click(event):
index = script_listbox.nearest(event.y)
if not script_listbox.selection_includes(index):
return
edit_selected(selected_idx=index)
def update_stdout(): def update_stdout():
stdoutext.config(state=tk.NORMAL) stdoutext.config(state=tk.NORMAL)
@ -451,47 +525,68 @@ def update_stdout():
stdoutext.config(state=tk.DISABLED) stdoutext.config(state=tk.DISABLED)
debugwindow.after(100, update_stdout) debugwindow.after(100, update_stdout)
def show_console_output_screen(): def show_console_output_screen():
global stdoutext, debugwindow global stdoutext, debugwindow
debugwindow = tk.Toplevel(root) debugwindow = tk.Toplevel(root)
debugwindow.geometry("700x400") debugwindow.geometry("700x400")
stdoutext = tk.Text(debugwindow, state=tk.DISABLED) stdoutext = tk.Text(debugwindow, state=tk.DISABLED)
stdoutext.pack(padx=15, pady=15, fill=tk.BOTH) stdoutext.pack(padx=15, pady=15, fill=tk.BOTH)
update_stdout() update_stdout()
def on_prev_line(): def on_prev_line():
load_next_line(diff=-1) load_next_line(diff=-1)
speedex = 1 speedex = 1
def inc_playrate(): def inc_playrate():
global speedex global speedex
speedex = round(speedex * 1.2, 2) speedex = round(speedex * 1.2, 2)
playrate() playrate()
def dec_playrate(): def dec_playrate():
global speedex global speedex
speedex = round(speedex / 1.2, 2) speedex = round(speedex / 1.2, 2)
playrate() playrate()
def playrate(): def playrate():
speed_label.config(text=f"x{speedex:.2f}") speed_label.config(text=f"x{speedex:.2f}")
print(f"Playback rate x{speedex:.2f}") print(f"Playback rate x{speedex:.2f}")
player.set_rate(speedex) player.set_rate(speedex)
root = tk.Tk() root = tk.Tk()
root.title("Subtitle Editor") root.title("Subtitle Editor")
root.geometry('1000x800') root.geometry("1000x800")
root.resizable(False, False) root.resizable(False, False)
content_frame = tk.Frame(root) content_frame = tk.Frame(root)
content_frame.pack(side=tk.LEFT, padx=5, pady=5) content_frame.pack(side=tk.LEFT, padx=5, pady=5)
subtitle_text = tk.Text(content_frame, width=100, height=20, borderwidth=1, relief="solid", state=tk.DISABLED) subtitle_text = tk.Text(
subtitle_text.pack(side=tk.BOTTOM, fill='both', expand=True) content_frame,
width=100,
height=20,
borderwidth=1,
relief="solid",
state=tk.DISABLED,
)
subtitle_text.pack(side=tk.BOTTOM, fill="both", expand=True)
script_listbox = tk.Listbox(content_frame, width=100, height=20, borderwidth=1, relief="solid", selectmode=tk.EXTENDED) script_listbox = tk.Listbox(
script_listbox.pack(side=tk.BOTTOM, fill='both', expand=True) content_frame,
width=100,
height=20,
borderwidth=1,
relief="solid",
selectmode=tk.EXTENDED,
)
script_listbox.pack(side=tk.BOTTOM, fill="both", expand=True)
context_menu = tk.Menu(root, tearoff=0) context_menu = tk.Menu(root, tearoff=0)
context_menu.add_command(label="Merge (M)", command=merge_selected) context_menu.add_command(label="Merge (M)", command=merge_selected)
@ -509,15 +604,17 @@ script_listbox.bind("<Double-1>", on_list_double_click)
button_frame = tk.Frame(root) button_frame = tk.Frame(root)
button_frame.pack(side=tk.BOTTOM, pady=(0,15)) button_frame.pack(side=tk.BOTTOM, pady=(0, 15))
rewind_button = tk.Button(button_frame, text='-5s', width=2, command=rewind_audio) rewind_button = tk.Button(button_frame, text="-5s", width=2, command=rewind_audio)
rewind_button.pack(side=tk.LEFT, padx=5, pady=5) rewind_button.pack(side=tk.LEFT, padx=5, pady=5)
play_button = tk.Button(button_frame, text='P/P', width=2, command=toggle_audio) play_button = tk.Button(button_frame, text="P/P", width=2, command=toggle_audio)
play_button.pack(side=tk.LEFT, padx=5, pady=5) play_button.pack(side=tk.LEFT, padx=5, pady=5)
fastforward_button = tk.Button(button_frame, text='+5s', width=2, command=fastforward_audio) fastforward_button = tk.Button(
button_frame, text="+5s", width=2, command=fastforward_audio
)
fastforward_button.pack(side=tk.LEFT, padx=5, pady=5) fastforward_button.pack(side=tk.LEFT, padx=5, pady=5)
timestamp_label = tk.Label(root, text="0.00s / 0.00s") timestamp_label = tk.Label(root, text="0.00s / 0.00s")
@ -527,74 +624,75 @@ timestamp_label.pack(side=tk.BOTTOM, pady=5)
ctrl_frame = tk.Frame(root, borderwidth=0, relief="solid") ctrl_frame = tk.Frame(root, borderwidth=0, relief="solid")
ctrl_frame.pack(side=tk.BOTTOM, padx=5) ctrl_frame.pack(side=tk.BOTTOM, padx=5)
one_button = tk.Button(ctrl_frame, text='-', borderwidth=0, command=dec_playrate) one_button = tk.Button(ctrl_frame, text="-", borderwidth=0, command=dec_playrate)
one_button.pack(side=tk.LEFT, padx=(5,0), pady=5) one_button.pack(side=tk.LEFT, padx=(5, 0), pady=5)
speed_label = tk.Label(ctrl_frame, text="x1") speed_label = tk.Label(ctrl_frame, text="x1")
speed_label.pack(side=tk.LEFT, padx=0, pady=5) speed_label.pack(side=tk.LEFT, padx=0, pady=5)
two_button = tk.Button(ctrl_frame, text='+', borderwidth=0, command=inc_playrate) two_button = tk.Button(ctrl_frame, text="+", borderwidth=0, command=inc_playrate)
two_button.pack(side=tk.LEFT, padx=(0,5), pady=5) two_button.pack(side=tk.LEFT, padx=(0, 5), pady=5)
btn_frame = tk.Frame(root, borderwidth=0, relief="solid") btn_frame = tk.Frame(root, borderwidth=0, relief="solid")
btn_frame.pack(side=tk.BOTTOM, padx=5) btn_frame.pack(side=tk.BOTTOM, padx=5)
next_button = tk.Button(btn_frame, text='Mark', width=2, command=on_next_press) next_button = tk.Button(btn_frame, text="Mark", width=2, command=on_next_press)
next_button.pack(side=tk.LEFT, padx=5, pady=5) next_button.pack(side=tk.LEFT, padx=5, pady=5)
autonext_button = tk.Button(btn_frame, text='Next', width=2, command=on_autonext_press) autonext_button = tk.Button(btn_frame, text="Next", width=2, command=on_autonext_press)
autonext_button.pack(side=tk.LEFT, padx=5, pady=5) autonext_button.pack(side=tk.LEFT, padx=5, pady=5)
done_button = tk.Button(btn_frame, text='Done', width=2, command=on_done_press) done_button = tk.Button(btn_frame, text="Done", width=2, command=on_done_press)
done_button.pack(side=tk.LEFT, padx=5, pady=5) done_button.pack(side=tk.LEFT, padx=5, pady=5)
btn_frame2 = tk.Frame(root, borderwidth=0, relief="solid") btn_frame2 = tk.Frame(root, borderwidth=0, relief="solid")
btn_frame2.pack(side=tk.BOTTOM, padx=5) btn_frame2.pack(side=tk.BOTTOM, padx=5)
prev_button = tk.Button(btn_frame2, text='Back', width=2, command=on_prev_line) prev_button = tk.Button(btn_frame2, text="Back", width=2, command=on_prev_line)
prev_button.pack(side=tk.LEFT, padx=5, pady=5) prev_button.pack(side=tk.LEFT, padx=5, pady=5)
del_button = tk.Button(btn_frame2, text='Del', width=2, command=on_back) del_button = tk.Button(btn_frame2, text="Del", width=2, command=on_back)
del_button.pack(side=tk.LEFT, padx=5, pady=5) del_button.pack(side=tk.LEFT, padx=5, pady=5)
skip_button = tk.Button(btn_frame2, text='Skip', width=2, command=load_next_line) skip_button = tk.Button(btn_frame2, text="Skip", width=2, command=load_next_line)
skip_button.pack(side=tk.LEFT, padx=5, pady=5) skip_button.pack(side=tk.LEFT, padx=5, pady=5)
file_frame = tk.Frame(root, padx=10, pady=5, borderwidth=0, relief="solid") file_frame = tk.Frame(root, padx=10, pady=5, borderwidth=0, relief="solid")
file_frame.pack(side=tk.TOP, padx=(0,5), pady=5, fill=tk.BOTH) file_frame.pack(side=tk.TOP, padx=(0, 5), pady=5, fill=tk.BOTH)
audio_button = tk.Button(file_frame, text='Choose Audio', command=choose_audio) audio_button = tk.Button(file_frame, text="Choose Audio", command=choose_audio)
audio_button.pack(side=tk.TOP, fill=tk.X) audio_button.pack(side=tk.TOP, fill=tk.X)
audio_label = tk.Label(file_frame, text='No audio file selected') audio_label = tk.Label(file_frame, text="No audio file selected")
audio_label.pack(side=tk.TOP, pady=5) audio_label.pack(side=tk.TOP, pady=5)
script_button = tk.Button(file_frame, text='Choose Script', command=choose_script) script_button = tk.Button(file_frame, text="Choose Script", command=choose_script)
script_button.pack(side=tk.TOP, fill=tk.X) script_button.pack(side=tk.TOP, fill=tk.X)
script_label = tk.Label(file_frame, text='No script file selected') script_label = tk.Label(file_frame, text="No script file selected")
script_label.pack(side=tk.TOP, pady=5) script_label.pack(side=tk.TOP, pady=5)
saveprog_button = tk.Button(file_frame, text='Save progress', command=save_prog) saveprog_button = tk.Button(file_frame, text="Save progress", command=save_prog)
saveprog_button.pack(side=tk.TOP, pady=5, fill=tk.X) saveprog_button.pack(side=tk.TOP, pady=5, fill=tk.X)
loadprog_button = tk.Button(file_frame, text='Load progress', command=load_prog) loadprog_button = tk.Button(file_frame, text="Load progress", command=load_prog)
loadprog_button.pack(side=tk.TOP, fill=tk.X) loadprog_button.pack(side=tk.TOP, fill=tk.X)
save_button = tk.Button(file_frame, text='Save Subtitles', command=save_subtitles) save_button = tk.Button(file_frame, text="Save Subtitles", command=save_subtitles)
save_button.pack(side=tk.TOP, pady=5, fill=tk.X) save_button.pack(side=tk.TOP, pady=5, fill=tk.X)
stack_button = tk.Button(file_frame, text='Save Stacked', command=save_stacked_subtitles) stack_button = tk.Button(
stack_button.pack(side=tk.TOP, pady=(0,10), fill=tk.X) file_frame, text="Save Stacked", command=save_stacked_subtitles
)
stack_button.pack(side=tk.TOP, pady=(0, 10), fill=tk.X)
skip_time_frame = tk.Frame(file_frame) skip_time_frame = tk.Frame(file_frame)
skip_time_frame.pack(side=tk.TOP, fill=tk.X) skip_time_frame.pack(side=tk.TOP, fill=tk.X)
skip_time_button = tk.Button(skip_time_frame, text='Skip To', command=skip_to_time) skip_time_button = tk.Button(skip_time_frame, text="Skip To", command=skip_to_time)
skip_time_button.pack(side=tk.LEFT) skip_time_button.pack(side=tk.LEFT)
skip_time_entry = tk.Entry(skip_time_frame) skip_time_entry = tk.Entry(skip_time_frame)
@ -602,9 +700,16 @@ skip_time_entry.pack(side=tk.LEFT, padx=5, fill=tk.X)
skip_time_entry.insert(0, "0") skip_time_entry.insert(0, "0")
info_frame = tk.Frame(root, borderwidth=0, relief="solid", width=10, height=10, padx=10) info_frame = tk.Frame(root, borderwidth=0, relief="solid", width=10, height=10, padx=10)
info_frame.pack(side=tk.TOP, expand=True, anchor="nw", padx=(5,15), pady=(10,15)) info_frame.pack(side=tk.TOP, expand=True, anchor="nw", padx=(5, 15), pady=(10, 15))
info_label = tk.Label(info_frame, text='[ Current stack ]\n', width=20, font=("monospace", 8), anchor="nw", justify=tk.LEFT) info_label = tk.Label(
info_frame,
text="[ Current stack ]\n",
width=20,
font=("monospace", 8),
anchor="nw",
justify=tk.LEFT,
)
# info_label = tk.Label(info_frame, text='' \ # info_label = tk.Label(info_frame, text='' \
# # 'VTT Maker by @morgan9e\n\n' \ # # 'VTT Maker by @morgan9e\n\n' \
@ -615,32 +720,39 @@ info_label = tk.Label(info_frame, text='[ Current stack ]\n', width=20, font=("m
info_label.pack(side=tk.TOP, anchor="nw") info_label.pack(side=tk.TOP, anchor="nw")
debug_button = tk.Button(root, text='Show stdout', command=show_console_output_screen, borderwidth=0) debug_button = tk.Button(
debug_button.pack(side=tk.TOP, pady=(0,5)) root, text="Show stdout", command=show_console_output_screen, borderwidth=0
)
debug_button.pack(side=tk.TOP, pady=(0, 5))
def presskey(btn, func): def presskey(btn, func):
def wrapper(event): def wrapper(event):
btn.config(relief=tk.SUNKEN) btn.config(relief=tk.SUNKEN)
root.after(100, lambda: btn.config(relief=tk.RAISED)) root.after(100, lambda: btn.config(relief=tk.RAISED))
return func() return func()
return wrapper
root.bind('\'', presskey(next_button,on_next_press)) return wrapper
root.bind(';', presskey(autonext_button,on_autonext_press))
root.bind('<Return>', presskey(skip_button,on_done_press))
root.bind('\\', on_back)
root.bind('<space>', presskey(play_button,toggle_audio))
root.bind('<Left>', presskey(rewind_button,rewind_audio))
root.bind('<Right>', presskey(fastforward_button,fastforward_audio)) root.bind("'", presskey(next_button, on_next_press))
root.bind(";", presskey(autonext_button, on_autonext_press))
root.bind("<Return>", presskey(skip_button, on_done_press))
root.bind("\\", on_back)
root.bind("<space>", presskey(play_button, toggle_audio))
root.bind("<Left>", presskey(rewind_button, rewind_audio))
root.bind("<Right>", presskey(fastforward_button, fastforward_audio))
root.bind("<Button-1>", on_left_click) root.bind("<Button-1>", on_left_click)
def on_closing(): def on_closing():
if messagebox.askokcancel("Quit", "Do you want to quit?"): if messagebox.askokcancel("Quit", "Do you want to quit?"):
player.stop() player.stop()
root.destroy() root.destroy()
root.protocol("WM_DELETE_WINDOW", on_closing) root.protocol("WM_DELETE_WINDOW", on_closing)
update_timestamp() update_timestamp()

View File

@ -4,50 +4,64 @@ import re, json
import os import os
from datetime import timedelta from datetime import timedelta
def from_vtt(vtt_string): def from_vtt(vtt_string):
VTT_TIMECODE_PATTERN = r"((?:\d{2}:)?\d{2}:\d{2}\.\d{3}) --> ((?:\d{2}:)?\d{2}:\d{2}\.\d{3})" VTT_TIMECODE_PATTERN = (
VTT_LINE_NUMBER_PATTERN = r"^\d+$" r"((?:\d{2}:)?\d{2}:\d{2}\.\d{3}) --> ((?:\d{2}:)?\d{2}:\d{2}\.\d{3})"
parts = re.split(r'\n\n+', vtt_string.strip()) )
if parts[0].startswith('WEBVTT'): VTT_LINE_NUMBER_PATTERN = r"^\d+$"
parts.pop(0) parts = re.split(r"\n\n+", vtt_string.strip())
if parts[0].startswith("WEBVTT"):
parts.pop(0)
subtitles = [] subtitles = []
for part in parts: for part in parts:
lines = part.split('\n') lines = part.split("\n")
match = re.match(VTT_TIMECODE_PATTERN, lines[0]) match = re.match(VTT_TIMECODE_PATTERN, lines[0])
if not match: if not match:
if re.match(VTT_LINE_NUMBER_PATTERN, lines[0]): if re.match(VTT_LINE_NUMBER_PATTERN, lines[0]):
lines.pop(0) lines.pop(0)
match = re.match(VTT_TIMECODE_PATTERN, lines[0]) match = re.match(VTT_TIMECODE_PATTERN, lines[0])
if not match: if not match:
continue continue
start, end = match.groups() start, end = match.groups()
content = '\n'.join(lines[1:]) + "\n" content = "\n".join(lines[1:]) + "\n"
subtitles.append({ subtitles.append(
'start': start, {
'end': end, "start": start,
'content': (content.replace("-\n", "\n").replace("</u>-\n", "</u>\n").replace("-", " ").replace("%", " ").replace("<u> "," <u>").replace(" </u>","</u> ").replace("<u> </u>","").replace("<u></u>","").replace(" \n", "\n"))[:-1] "end": end,
}) "content": (
# def sanitizevttwordlevel(subtitles): content.replace("-\n", "\n")
# errorwords = [] .replace("</u>-\n", "</u>\n")
# newords = {} .replace("-", " ")
# for subtitle in subtitles: .replace("%", " ")
# for word in subtitle["content"].split(" "): .replace("<u> ", " <u>")
# if ("<u>" in word): .replace(" </u>", "</u> ")
# newword = None .replace("<u> </u>", "")
# if (len(word.split("<u>")) > 1): .replace("<u></u>", "")
# newword = word.replace("<u>", " <u>") .replace(" \n", "\n")
# if (len(word.split("</u>")) > 1): )[:-1],
# newword = word.replace("</u>", "</u> ") }
# if newword: )
# original = word.split("<u>")[1].split("</u>")[0] # def sanitizevttwordlevel(subtitles):
# if original in errorwords: # errorwords = []
# for i in errorwords[original]: # newords = {}
# for subtitle in subtitles:
# for word in subtitle["content"].split(" "):
# if ("<u>" in word):
# newword = None
# if (len(word.split("<u>")) > 1):
# newword = word.replace("<u>", " <u>")
# if (len(word.split("</u>")) > 1):
# newword = word.replace("</u>", "</u> ")
# if newword:
# original = word.split("<u>")[1].split("</u>")[0]
# if original in errorwords:
# for i in errorwords[original]:
# else:
# else: # errorwords[orig].append(word)
# errorwords[orig].append(word)
# error = False # error = False
# if "<u>" in subtitle["content"]: # if "<u>" in subtitle["content"]:
@ -63,347 +77,386 @@ def from_vtt(vtt_string):
# for word in subtitle["content"].split(" "): # for word in subtitle["content"].split(" "):
# if word.replace("<u>") # if word.replace("<u>")
# for subtitle in subtitles: # for subtitle in subtitles:
# for words in subtitle["content"].split(" "): # for words in subtitle["content"].split(" "):
# if word in errorwords: # if word in errorwords:
# subtitle["content"] # subtitle["content"]
# sanitizevttwordlevel(subtitles)
return subtitles
# sanitizevttwordlevel(subtitles)
return subtitles
def to_vtt(subtitles): def to_vtt(subtitles):
vtt_content = "WEBVTT\n\n\n" vtt_content = "WEBVTT\n\n\n"
for idx, subtitle in enumerate(subtitles): for idx, subtitle in enumerate(subtitles):
content = subtitle['content'] content = subtitle["content"]
if not subtitle.get("split", False): if not subtitle.get("split", False):
start = subtitle['start'] start = subtitle["start"]
end = subtitle['end'] end = subtitle["end"]
vtt_content += f"{start} --> {end}\n{content}\n\n\n" vtt_content += f"{start} --> {end}\n{content}\n\n\n"
else: else:
vtt_content += f"NOTE {content}\n\n\n" vtt_content += f"NOTE {content}\n\n\n"
return vtt_content.strip() return vtt_content.strip()
def to_stacked_vtt(subtitles): def to_stacked_vtt(subtitles):
vtt_content = "WEBVTT\n\n\n" vtt_content = "WEBVTT\n\n\n"
buffer = "" buffer = ""
for subtitle in subtitles: for subtitle in subtitles:
if subtitle.get("split", False): if subtitle.get("split", False):
buffer = "" buffer = ""
continue continue
if len(buffer) != 0: if len(buffer) != 0:
if str(subtitle['content'].strip())[-1] == ".": if str(subtitle["content"].strip())[-1] == ".":
buffer += "\n" buffer += "\n"
else: else:
buffer += " " buffer += " "
buffer += subtitle['content'].strip() buffer += subtitle["content"].strip()
vtt_content += f"{subtitle['start']} --> {subtitle['end']}\n" vtt_content += f"{subtitle['start']} --> {subtitle['end']}\n"
vtt_content += buffer vtt_content += buffer
vtt_content += "\n\n\n" vtt_content += "\n\n\n"
return vtt_content return vtt_content
def script_from_word_vtt(wordvtt): def script_from_word_vtt(wordvtt):
subtitles = from_vtt(wordvtt) subtitles = from_vtt(wordvtt)
print(f"VTT {len(subtitles)} lines. Generating script file from VTT.") print(f"VTT {len(subtitles)} lines. Generating script file from VTT.")
sentences = [] sentences = []
EXCEPTION_FLAG, ADD_NEXT_SENTENCE = "", 0 EXCEPTION_FLAG, ADD_NEXT_SENTENCE = "", 0
for n, subtitle in enumerate(subtitles): for n, subtitle in enumerate(subtitles):
sentence = subtitle["content"].replace("<u>", "").replace("</u>", "") sentence = subtitle["content"].replace("<u>", "").replace("</u>", "")
if ((sentences[-1] if sentences else None) != sentence) or ADD_NEXT_SENTENCE: if ((sentences[-1] if sentences else None) != sentence) or ADD_NEXT_SENTENCE:
sentences.append(sentence) sentences.append(sentence)
ADD_NEXT_SENTENCE = 0 ADD_NEXT_SENTENCE = 0
if subtitle["content"][-4:] == "</u>": if subtitle["content"][-4:] == "</u>":
# print(f"{len(sentences)} END {subtitle["content"]}") # print(f"{len(sentences)} END {subtitle["content"]}")
ADD_NEXT_SENTENCE = 1 ADD_NEXT_SENTENCE = 1
if n + 2 < len(subtitles): if n + 2 < len(subtitles):
if subtitles[n+2]["content"].replace("<u>", "").replace("</u>", "") != sentence: if (
ADD_NEXT_SENTENCE = 0 subtitles[n + 2]["content"].replace("<u>", "").replace("</u>", "")
return sentences != sentence
):
ADD_NEXT_SENTENCE = 0
return sentences
def create_word_scenes(wordvtt, scriptraw): def create_word_scenes(wordvtt, scriptraw):
subtitles = from_vtt(wordvtt) subtitles = from_vtt(wordvtt)
scripts = [i for i in scriptraw.split("\n") if i] scripts = [i for i in scriptraw.split("\n") if i]
print(f"VTT {len(subtitles)} lines, Script {len(scripts)} lines") print(f"VTT {len(subtitles)} lines, Script {len(scripts)} lines")
scenes = [] scenes = []
for n, script in enumerate(scripts): for n, script in enumerate(scripts):
if len(script.split(" ")) == 1: if len(script.split(" ")) == 1:
continue continue
scenes.append({"scene": script, "timestamp": []}) scenes.append({"scene": script, "timestamp": []})
scenes_cur = 0 scenes_cur = 0
for n, subtitle in enumerate(subtitles): for n, subtitle in enumerate(subtitles):
sentence = subtitle["content"].replace("<u>", "").replace("</u>", "") sentence = subtitle["content"].replace("<u>", "").replace("</u>", "")
if len(sentence.split(" ")) == 1: if len(sentence.split(" ")) == 1:
continue continue
if sentence != scenes[scenes_cur].get("scene"): if sentence != scenes[scenes_cur].get("scene"):
if sentence == scenes[scenes_cur+1].get("scene"): if sentence == scenes[scenes_cur + 1].get("scene"):
scenes_cur += 1 scenes_cur += 1
else: else:
print(f"Error, Mismatch in scenes\n=>\"[{scenes_cur}] {scenes[scenes_cur].get("scene")}\" or \"[{scenes_cur+1}] {scenes[scenes_cur+1].get("scene")}\" != \"{sentence}\"") print(
return f"Error, Mismatch in scenes\n=>\"[{scenes_cur}] {scenes[scenes_cur].get("scene")}\" or \"[{scenes_cur+1}] {scenes[scenes_cur+1].get("scene")}\" != \"{sentence}\""
)
return
current_scene = scenes[scenes_cur] current_scene = scenes[scenes_cur]
if current_scene["timestamp"]: if current_scene["timestamp"]:
word_idx = current_scene["timestamp"][-1]["index"] + 1 word_idx = current_scene["timestamp"][-1]["index"] + 1
else: else:
word_idx = 0 word_idx = 0
# print(scenes_cur, subtitle, word_idx, sentence) # print(scenes_cur, subtitle, word_idx, sentence)
if ("<u>" not in subtitle["content"]) and word_idx >= len(sentence.split(" ")): if ("<u>" not in subtitle["content"]) and word_idx >= len(sentence.split(" ")):
pass pass
if ("<u>" in subtitle["content"]) and word_idx >= len(sentence.split(" ")): if ("<u>" in subtitle["content"]) and word_idx >= len(sentence.split(" ")):
print(f"Error, index wrong. {scenes_cur}, word: {word_idx}, total words: {len(sentence.split(" "))}\n{subtitle}") print(
word_idx = 0 f"Error, index wrong. {scenes_cur}, word: {word_idx}, total words: {len(sentence.split(" "))}\n{subtitle}"
scenes_cur += 1 )
current_scene = scenes[scenes_cur] word_idx = 0
if current_scene["timestamp"]: scenes_cur += 1
word_idx = current_scene["timestamp"][-1]["index"] + 1 current_scene = scenes[scenes_cur]
else: if current_scene["timestamp"]:
word_idx = 0 word_idx = current_scene["timestamp"][-1]["index"] + 1
print(f"Changed to {word_idx}, {scenes_cur}") else:
word_idx = 0
print(f"Changed to {word_idx}, {scenes_cur}")
if "<u>" in subtitle["content"]: if "<u>" in subtitle["content"]:
# print(subtitle["content"]) # print(subtitle["content"])
word = subtitle["content"].split("<u>")[1].split("</u>")[0] word = subtitle["content"].split("<u>")[1].split("</u>")[0]
if word not in sentence.split(" "): if word not in sentence.split(" "):
print(f"Error, Mismatch\n=> \"{word}\" not in \"{sentence}\"") print(f'Error, Mismatch\n=> "{word}" not in "{sentence}"')
return return
try: try:
assert sentence.split(" ")[word_idx] == word assert sentence.split(" ")[word_idx] == word
except: except:
print(f"Error, Mismatch\n=> \"{word}\" != [{word_idx}] of \"{sentence}\"") print(f'Error, Mismatch\n=> "{word}" != [{word_idx}] of "{sentence}"')
return return
word_time = {"start": subtitle["start"], "end": subtitle["end"], "index": word_idx, "word": word} word_time = {
current_scene["timestamp"].append(word_time) "start": subtitle["start"],
"end": subtitle["end"],
"index": word_idx,
"word": word,
}
current_scene["timestamp"].append(word_time)
# print(json.dumps(scenes, indent=2)) # print(json.dumps(scenes, indent=2))
for scene in scenes: for scene in scenes:
if len(scene["scene"].split(" ")) != len(scene["timestamp"]): if len(scene["scene"].split(" ")) != len(scene["timestamp"]):
print("Error, Mismatch length") print("Error, Mismatch length")
return return
if "" in scene["scene"].split(" "): if "" in scene["scene"].split(" "):
print(repr(scene["scene"])) print(repr(scene["scene"]))
full_script, full_scenes = [], [] full_script, full_scenes = [], []
for scene in scenes: for scene in scenes:
full_script += scene["scene"].split(" ")[:-1] full_script += scene["scene"].split(" ")[:-1]
full_script.append(scene["scene"].split(" ")[-1]+"##") full_script.append(scene["scene"].split(" ")[-1] + "##")
full_scenes += scene["timestamp"] full_scenes += scene["timestamp"]
for i, j in zip(full_script, full_scenes): for i, j in zip(full_script, full_scenes):
if i.replace("##", "") != j["word"]: if i.replace("##", "") != j["word"]:
print("Error, Mismatch") print("Error, Mismatch")
return return
assert len(full_scenes) == len(full_script)
assert len(full_scenes) == len(full_script) return full_script, full_scenes
return full_script, full_scenes
# Detect long break or change in context, inserts section break into script. # Detect long break or change in context, inserts section break into script.
def autobreak(lines, times): def autobreak(lines, times):
from datetime import timedelta from datetime import timedelta
def parsetime(time_str): def parsetime(time_str):
minutes, seconds = time_str.split(':') minutes, seconds = time_str.split(":")
seconds, milliseconds = seconds.split('.') seconds, milliseconds = seconds.split(".")
td = timedelta(minutes=int(minutes), seconds=int(seconds), milliseconds=int(milliseconds)) td = timedelta(
return td minutes=int(minutes), seconds=int(seconds), milliseconds=int(milliseconds)
)
return td
script = [] script = []
long_breaks = [] long_breaks = []
tmark = parsetime("0:0.0") tmark = parsetime("0:0.0")
for i, j in zip(lines, times): for i, j in zip(lines, times):
tdiff = parsetime(j["start"]) - tmark tdiff = parsetime(j["start"]) - tmark
tmark = parsetime(j["end"]) tmark = parsetime(j["end"])
if tdiff > parsetime("0:0.0"): if tdiff > parsetime("0:0.0"):
long_breaks.append(tdiff) long_breaks.append(tdiff)
# print() # print()
# print(i, end=" ") # print(i, end=" ")
# print() # print()
mean_break = parsetime("0:0.0") mean_break = parsetime("0:0.0")
for i in long_breaks: for i in long_breaks:
mean_break += i/len(long_breaks) mean_break += i / len(long_breaks)
print(mean_break) print(mean_break)
script = "" script = ""
tmark = parsetime("0:0.0") tmark = parsetime("0:0.0")
tmp = " " tmp = " "
continous_line = 0 continous_line = 0
for i, j in zip(lines, times): for i, j in zip(lines, times):
tdiff = parsetime(j["start"]) - tmark tdiff = parsetime(j["start"]) - tmark
tmark = parsetime(j["end"]) tmark = parsetime(j["end"])
if tdiff > mean_break and tmp[-1] != ".": if tdiff > mean_break and tmp[-1] != ".":
script += "\n" script += "\n"
if (tdiff >= mean_break and tmp[-1] == "."): if tdiff >= mean_break and tmp[-1] == ".":
script += "\n" script += "\n"
continous_line = 0 continous_line = 0
else: else:
continous_line += 1 continous_line += 1
script += i.replace("##", "") script += i.replace("##", "")
if i[-1] == ".": if i[-1] == ".":
script += "\n" script += "\n"
elif "##" in i: elif "##" in i:
script += "\n" script += "\n"
else: else:
script += " " script += " "
tmp = i tmp = i
return script
return script
def scene_from_new_script(raw_script, full_script, full_scenes): def scene_from_new_script(raw_script, full_script, full_scenes):
mod_script = raw_script.replace("\n", " \n ").split(" ") mod_script = raw_script.replace("\n", " \n ").split(" ")
mod_script = [i for i in mod_script if i] mod_script = [i for i in mod_script if i]
n = 0 n = 0
while True: while True:
if mod_script[n] == "\n": if mod_script[n] == "\n":
mod_script[n-1] += "\n" mod_script[n - 1] += "\n"
del(mod_script[n]) del mod_script[n]
n -= 1 n -= 1
n += 1 n += 1
if n == len(mod_script): if n == len(mod_script):
break break
# print(mod_script) # print(mod_script)
print(f"Original: {len(full_script)}, Modded: {len(mod_script)}") print(f"Original: {len(full_script)}, Modded: {len(mod_script)}")
allowed_list = [".", "\n", "\n\n", ",", "?", "##"] allowed_list = [".", "\n", "\n\n", ",", "?", "##"]
def normalized(x): def normalized(x):
for i in allowed_list: for i in allowed_list:
x = x.replace(i, "") x = x.replace(i, "")
return x.upper() return x.upper()
same = lambda a, b: normalized(a) == normalized(b)
new_script, new_timestamp, orig_index, n = [], [], 0, 0
fail = 0
while n < len(mod_script):
print(f"{repr(mod_script[n]):>20} ? {repr(full_script[orig_index])}")
word = mod_script[n]
if same(word, full_script[orig_index].replace("##", "")):
cur = full_scenes[orig_index]
new_script.append(word.replace("##", ""))
new_timestamp.append({"start": cur["start"], "end": cur["end"]})
fail = 0
else:
if fail > 10:
print("Error: Failed to match words,")
return
# print("Back")
fail += 1
n -= 1
n, orig_index = n + 1, orig_index + 1
assert len(new_script) == len(new_timestamp)
return new_script, new_timestamp
same = lambda a, b: normalized(a) == normalized(b)
new_script, new_timestamp, orig_index, n = [], [], 0, 0
fail = 0
while n < len(mod_script):
print(f"{repr(mod_script[n]):>20} ? {repr(full_script[orig_index])}")
word = mod_script[n]
if same(word, full_script[orig_index].replace("##", "")):
cur = full_scenes[orig_index]
new_script.append(word.replace("##", ""))
new_timestamp.append({"start": cur["start"], "end": cur["end"]})
fail = 0
else:
if fail > 10:
print("Error: Failed to match words,")
return
# print("Back")
fail += 1
n -= 1
n, orig_index = n+1, orig_index+1
assert len(new_script) == len(new_timestamp)
return new_script, new_timestamp
def build_new_subtitle(new_script, new_timestamp): def build_new_subtitle(new_script, new_timestamp):
buffer, new_scenes, start, end = [], [], None, None buffer, new_scenes, start, end = [], [], None, None
current_scene = [] current_scene = []
# print(" ".join(new_script).split("\n")) # print(" ".join(new_script).split("\n"))
for i, j in zip(new_script, new_timestamp): for i, j in zip(new_script, new_timestamp):
if "\n" in i: if "\n" in i:
buffer.append(i.replace("\n", "")) buffer.append(i.replace("\n", ""))
current_scene.append({"content": " ".join(buffer).replace("##", ""), "start": start, "end": j["end"]}) current_scene.append(
buffer, start = [], None {
if "\n\n" in i: "content": " ".join(buffer).replace("##", ""),
print(f"Section break at line #{len(current_scene):<3}| \"{current_scene[-1]["content"]}\"") "start": start,
"end": j["end"],
}
)
buffer, start = [], None
if "\n\n" in i:
print(
f"Section break at line #{len(current_scene):<3}| \"{current_scene[-1]["content"]}\""
)
new_scenes.append(current_scene)
current_scene = []
else:
buffer.append(i)
if not start:
start = j["start"]
if start:
buffer.append(i.replace("\n", ""))
current_scene.append(
{"content": " ".join(buffer), "start": start, "end": j["end"]}
)
if current_scene != (new_scenes[-1] if new_scenes else None):
new_scenes.append(current_scene) new_scenes.append(current_scene)
current_scene = []
else:
buffer.append(i)
if not start:
start = j["start"]
if start: # print("\n\n".join(["\n".join([j["content"] for j in i]) for i in new_scenes]))
buffer.append(i.replace("\n", "")) newsub = []
current_scene.append({"content": " ".join(buffer), "start": start, "end": j["end"]}) for n, i in enumerate(new_scenes):
newsub += i
if n < len(new_scenes) - 1:
newsub.append(
{"content": "Break", "start": None, "end": None, "split": True}
)
if current_scene != (new_scenes[-1] if new_scenes else None): return newsub
new_scenes.append(current_scene)
# print("\n\n".join(["\n".join([j["content"] for j in i]) for i in new_scenes]))
newsub = []
for n, i in enumerate(new_scenes):
newsub += i
if n < len(new_scenes) - 1:
newsub.append({"content": "Break", "start": None, "end": None, "split": True})
return newsub def saveFile(filename, data, override=False):
if os.path.exists(filename) and not override:
print(f"File {filename} already exists.")
return -1
with open(filename, "w") as f:
f.write(data)
def saveFile(filename, data, override = False):
if os.path.exists(filename) and not override:
print(f"File {filename} already exists.")
return -1
with open(filename, "w") as f:
f.write(data)
def openFile(filename): def openFile(filename):
with open(filename, "r") as f: with open(filename, "r") as f:
data = f.read() data = f.read()
if not data: if not data:
return -1 return -1
return data return data
def main(vttfile, scriptfile): def main(vttfile, scriptfile):
modfile = ".".join(scriptfile.split(".")[:-1]) + ".script" modfile = ".".join(scriptfile.split(".")[:-1]) + ".script"
x = create_word_scenes(openFile(vttfile), openFile(scriptfile)) x = create_word_scenes(openFile(vttfile), openFile(scriptfile))
if not x:
sys.exit(-1)
full_script, full_scenes = x
if not os.path.exists(modfile):
genscript = autobreak(full_script,full_scenes)
saveFile(modfile, genscript)
print(f"Saved modification file as {modfile}. Modify it and return back.")
else:
x = scene_from_new_script(openFile(modfile), full_script, full_scenes)
if not x: if not x:
sys.exit(-1) sys.exit(-1)
a, b = x full_script, full_scenes = x
final_vtt = build_new_subtitle(a, b) if not os.path.exists(modfile):
jsonfile = ".".join(vttfile.split(".")[:-1]) + ".json" genscript = autobreak(full_script, full_scenes)
saveFile(jsonfile, json.dumps(final_vtt, indent=2), True) saveFile(modfile, genscript)
print(f"Saved JSON file as {jsonfile}. Fix it, and convert it to VTT.") print(f"Saved modification file as {modfile}. Modify it and return back.")
else:
x = scene_from_new_script(openFile(modfile), full_script, full_scenes)
if not x:
sys.exit(-1)
a, b = x
if __name__=="__main__": final_vtt = build_new_subtitle(a, b)
import sys jsonfile = ".".join(vttfile.split(".")[:-1]) + ".json"
if len(sys.argv) not in (2, 3): saveFile(jsonfile, json.dumps(final_vtt, indent=2), True)
print(f"Usage: {sys.argv[0].split("/")[-1]} [vtt file] (txt file)\n" \ print(f"Saved JSON file as {jsonfile}. Fix it, and convert it to VTT.")
f" {sys.argv[0].split("/")[-1]} [JSON file]\n" \
"** Only output from openai-whisper with '--word-timestamp true' is accepted.)\n" \
"** You have to run this for first time, and then fix .script file, and then re-run this script.\n" \
"** Adding newline/period/commas are onlt permitted. Fix else in JSON file.")
sys.exit()
vtt = sys.argv[1]
print(f"\n[{vtt}]") if __name__ == "__main__":
if len(sys.argv) == 3: import sys
script = sys.argv[2]
if (not os.path.exists(vtt)) or (not os.path.exists(script)): if len(sys.argv) not in (2, 3):
print(f"Input file doesnt exists.") print(
sys.exit(-1) f"Usage: {sys.argv[0].split("/")[-1]} [vtt file] (txt file)\n"
main(vtt, script) f" {sys.argv[0].split("/")[-1]} [JSON file]\n"
else: "** Only output from openai-whisper with '--word-timestamp true' is accepted.)\n"
if ".json" in vtt: "** You have to run this for first time, and then fix .script file, and then re-run this script.\n"
final_vtt = json.loads(openFile(vtt)) "** Adding newline/period/commas are onlt permitted. Fix else in JSON file."
orgf = ".".join(vtt.split(".")[:-1]) )
print(f"Saved VTT file as {orgf}.final.vtt.") sys.exit()
saveFile(orgf + ".final.vtt", to_vtt(final_vtt), True)
saveFile(orgf + ".stacked.vtt", to_stacked_vtt(final_vtt), True) vtt = sys.argv[1]
sys.exit(0) print(f"\n[{vtt}]")
if (not os.path.exists(vtt)): if len(sys.argv) == 3:
print(f"Input file doesnt exists.") script = sys.argv[2]
sys.exit(-1) if (not os.path.exists(vtt)) or (not os.path.exists(script)):
script = ".".join(vtt.split(".")[:-1]) + ".txt" print(f"Input file doesnt exists.")
saveFile(script, "\n".join(script_from_word_vtt(openFile(vtt)))) sys.exit(-1)
main(vtt, script) main(vtt, script)
else:
if ".json" in vtt:
final_vtt = json.loads(openFile(vtt))
orgf = ".".join(vtt.split(".")[:-1])
print(f"Saved VTT file as {orgf}.final.vtt.")
saveFile(orgf + ".final.vtt", to_vtt(final_vtt), True)
saveFile(orgf + ".stacked.vtt", to_stacked_vtt(final_vtt), True)
sys.exit(0)
if not os.path.exists(vtt):
print(f"Input file doesnt exists.")
sys.exit(-1)
script = ".".join(vtt.split(".")[:-1]) + ".txt"
saveFile(script, "\n".join(script_from_word_vtt(openFile(vtt))))
main(vtt, script)