Fix
This commit is contained in:
parent
368c26d491
commit
826aca5c9f
37
backup.py
37
backup.py
|
@ -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)
|
||||||
|
|
153
snusub.py
153
snusub.py
|
@ -6,16 +6,19 @@ 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 = (
|
||||||
|
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+$"
|
||||||
parts = re.split(r'\n\n+', vtt_string.strip())
|
parts = re.split(r"\n\n+", vtt_string.strip())
|
||||||
if parts[0].startswith('WEBVTT'):
|
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]):
|
||||||
|
@ -25,25 +28,38 @@ def from_vtt(vtt_string):
|
||||||
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"
|
||||||
|
@ -52,6 +68,7 @@ def to_vtt(subtitles):
|
||||||
|
|
||||||
return vtt_content.strip()
|
return vtt_content.strip()
|
||||||
|
|
||||||
|
|
||||||
def to_stacked_vtt(subtitles, continous=True):
|
def to_stacked_vtt(subtitles, continous=True):
|
||||||
vtt_content = "WEBVTT\n\n\n"
|
vtt_content = "WEBVTT\n\n\n"
|
||||||
buffer = ""
|
buffer = ""
|
||||||
|
@ -61,22 +78,28 @@ def to_stacked_vtt(subtitles, continous = True):
|
||||||
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()
|
||||||
|
|
||||||
if n < len(subtitles) - 1:
|
if n < len(subtitles) - 1:
|
||||||
end_time = subtitles[n+1]['start'] if continous and not subtitles[n+1].get("split", False) else subtitle['end']
|
end_time = (
|
||||||
|
subtitles[n + 1]["start"]
|
||||||
|
if continous and not subtitles[n + 1].get("split", False)
|
||||||
|
else subtitle["end"]
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
end_time = subtitle['end']
|
end_time = subtitle["end"]
|
||||||
|
|
||||||
if not subtitle['start'] or not end_time:
|
if not subtitle["start"] or not end_time:
|
||||||
raise Exception(f"VTT timestamp parse error from #{idx}.")
|
raise Exception(f"VTT timestamp parse error from #{idx}.")
|
||||||
if subtitle['start'] == end_time:
|
if subtitle["start"] == end_time:
|
||||||
raise Exception(f"Error, subtitle timestamp overlaps.\n{subtitle['start']} --> {end_time} {subtitle['content'].strip()}")
|
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 += f"{subtitle['start']} --> {end_time}\n"
|
||||||
vtt_content += buffer
|
vtt_content += buffer
|
||||||
vtt_content += "\n\n\n"
|
vtt_content += "\n\n\n"
|
||||||
|
@ -85,8 +108,10 @@ def to_stacked_vtt(subtitles, continous = True):
|
||||||
|
|
||||||
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...")
|
||||||
|
@ -100,14 +125,20 @@ def script_from_word_vtt(wordvtt):
|
||||||
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 (
|
||||||
|
subtitles[n + 2]["content"].replace("<u>", "").replace("</u>", "")
|
||||||
|
!= sentence
|
||||||
|
):
|
||||||
ADD_NEXT_SENTENCE = 0
|
ADD_NEXT_SENTENCE = 0
|
||||||
return sentences
|
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):
|
||||||
|
@ -125,7 +156,9 @@ def create_word_scenes(raw_vtt, raw_script):
|
||||||
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"]:
|
||||||
|
@ -139,7 +172,9 @@ def create_word_scenes(raw_vtt, raw_script):
|
||||||
|
|
||||||
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(
|
||||||
|
f"Error, Index wrong. {scenes_cur}, word: {word_idx}, total words: {len(sentence.split(" "))}\n{subtitle}"
|
||||||
|
)
|
||||||
word_idx = 0
|
word_idx = 0
|
||||||
scenes_cur += 1
|
scenes_cur += 1
|
||||||
current_scene = scenes[scenes_cur]
|
current_scene = scenes[scenes_cur]
|
||||||
|
@ -154,15 +189,22 @@ def create_word_scenes(raw_vtt, raw_script):
|
||||||
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 = {
|
||||||
|
"start": subtitle["start"],
|
||||||
|
"end": subtitle["end"],
|
||||||
|
"index": word_idx,
|
||||||
|
"word": word,
|
||||||
|
}
|
||||||
current_scene["timestamp"].append(word_time)
|
current_scene["timestamp"].append(word_time)
|
||||||
|
|
||||||
for scene in scenes:
|
for scene in scenes:
|
||||||
|
@ -186,6 +228,7 @@ def create_word_scenes(raw_vtt, raw_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]
|
||||||
|
@ -193,7 +236,7 @@ def scene_from_new_script(raw_script, full_script, full_scenes):
|
||||||
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):
|
||||||
|
@ -228,6 +271,7 @@ def scene_from_new_script(raw_script, full_script, full_scenes):
|
||||||
assert len(new_script) == len(new_timestamp)
|
assert len(new_script) == len(new_timestamp)
|
||||||
return new_script, 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 = []
|
||||||
|
@ -238,17 +282,27 @@ def build_new_subtitle(new_script, new_timestamp):
|
||||||
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(
|
||||||
|
{
|
||||||
|
"content": " ".join(buffer).replace("##", ""),
|
||||||
|
"start": start,
|
||||||
|
"end": j["end"],
|
||||||
|
}
|
||||||
|
)
|
||||||
buffer, start = [], None
|
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(
|
||||||
|
f"Section break at line #{len(current_scene):<3}| \"{current_scene[-1]["content"]}\""
|
||||||
|
)
|
||||||
new_scenes.append(current_scene)
|
new_scenes.append(current_scene)
|
||||||
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)
|
||||||
|
@ -257,19 +311,25 @@ def build_new_subtitle(new_script, new_timestamp):
|
||||||
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(
|
||||||
|
minutes=int(minutes), seconds=int(seconds), milliseconds=int(milliseconds)
|
||||||
|
)
|
||||||
return td
|
return td
|
||||||
|
|
||||||
script = []
|
script = []
|
||||||
|
@ -297,7 +357,7 @@ def autobreak(lines, times):
|
||||||
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:
|
||||||
|
@ -315,8 +375,10 @@ def autobreak(lines, times):
|
||||||
|
|
||||||
return script
|
return script
|
||||||
|
|
||||||
|
|
||||||
############################################
|
############################################
|
||||||
|
|
||||||
|
|
||||||
def saveFile(filename, data, override=False):
|
def saveFile(filename, data, override=False):
|
||||||
if os.path.exists(filename) and not override:
|
if os.path.exists(filename) and not override:
|
||||||
raise Exception(f"File {filename} already exists.")
|
raise Exception(f"File {filename} already exists.")
|
||||||
|
@ -324,6 +386,7 @@ def saveFile(filename, data, override = False):
|
||||||
with open(filename, "w") as f:
|
with open(filename, "w") as f:
|
||||||
f.write(data)
|
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.")
|
||||||
|
@ -335,19 +398,21 @@ def openFile(filename):
|
||||||
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]
|
||||||
|
@ -358,12 +423,14 @@ Commands:
|
||||||
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(
|
||||||
|
openFile(FILE), "\n".join(script_from_word_vtt(openFile(FILE)))
|
||||||
|
)
|
||||||
if not x:
|
if not x:
|
||||||
sys.exit(-1)
|
sys.exit(-1)
|
||||||
|
|
||||||
|
@ -382,7 +449,9 @@ Commands:
|
||||||
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(
|
||||||
|
openFile(FILE1), "\n".join(script_from_word_vtt(openFile(FILE1)))
|
||||||
|
)
|
||||||
if not x:
|
if not x:
|
||||||
sys.exit(-1)
|
sys.exit(-1)
|
||||||
full_script, full_scenes = x
|
full_script, full_scenes = x
|
||||||
|
@ -400,7 +469,7 @@ Commands:
|
||||||
|
|
||||||
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)
|
||||||
|
|
||||||
|
|
30
stackvtt.py
30
stackvtt.py
|
@ -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 = []
|
||||||
|
@ -66,6 +67,7 @@ def stack_subtitle():
|
||||||
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()
|
||||||
|
|
||||||
|
|
13
subedit.py
13
subedit.py
|
@ -1,6 +1,7 @@
|
||||||
import json
|
import json
|
||||||
import os, sys
|
import os, sys
|
||||||
|
|
||||||
|
|
||||||
def readFile(file):
|
def readFile(file):
|
||||||
if not os.path.exists(file):
|
if not os.path.exists(file):
|
||||||
raise Exception(f"File {file} doesn't exists.")
|
raise Exception(f"File {file} doesn't exists.")
|
||||||
|
@ -8,6 +9,7 @@ def readFile(file):
|
||||||
data = f.read()
|
data = f.read()
|
||||||
return data
|
return data
|
||||||
|
|
||||||
|
|
||||||
def writeFile(file, data, overwrite=False):
|
def writeFile(file, data, overwrite=False):
|
||||||
if (not overwrite) and os.path.exists(file):
|
if (not overwrite) and os.path.exists(file):
|
||||||
raise Exception(f"File {file} already exists.")
|
raise Exception(f"File {file} already exists.")
|
||||||
|
@ -17,6 +19,7 @@ def writeFile(file, data, overwrite = False):
|
||||||
ret = f.write(data)
|
ret = f.write(data)
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
|
|
||||||
file = sys.argv[1]
|
file = sys.argv[1]
|
||||||
|
|
||||||
if ".json" in file:
|
if ".json" in file:
|
||||||
|
@ -65,7 +68,11 @@ elif ".edit" in file:
|
||||||
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] = {
|
||||||
|
"content": content,
|
||||||
|
"start": sub[idx]["start"],
|
||||||
|
"end": sub[idx]["end"],
|
||||||
|
}
|
||||||
else:
|
else:
|
||||||
new_brk.append(idx)
|
new_brk.append(idx)
|
||||||
|
|
||||||
|
@ -74,6 +81,8 @@ elif ".edit" in file:
|
||||||
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))
|
252
vttmaker.py
252
vttmaker.py
|
@ -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,6 +33,7 @@ class TextRedirector(object):
|
||||||
def flush(self):
|
def flush(self):
|
||||||
self.origin.flush()
|
self.origin.flush()
|
||||||
|
|
||||||
|
|
||||||
def toggle_audio(event=None):
|
def toggle_audio(event=None):
|
||||||
global audio_started
|
global audio_started
|
||||||
if not audio_started:
|
if not audio_started:
|
||||||
|
@ -45,14 +47,17 @@ def toggle_audio(event = None):
|
||||||
print("Audio Play.")
|
print("Audio Play.")
|
||||||
player.play()
|
player.play()
|
||||||
|
|
||||||
|
|
||||||
def rewind_audio(event=None):
|
def rewind_audio(event=None):
|
||||||
new_time = max(player.get_time() - 5000, 0)
|
new_time = max(player.get_time() - 5000, 0)
|
||||||
player.set_time(new_time)
|
player.set_time(new_time)
|
||||||
|
|
||||||
|
|
||||||
def fastforward_audio(event=None):
|
def fastforward_audio(event=None):
|
||||||
new_time = player.get_time() + 5000
|
new_time = player.get_time() + 5000
|
||||||
player.set_time(new_time)
|
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
|
||||||
|
@ -69,6 +74,7 @@ def mark_start():
|
||||||
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..")
|
||||||
|
@ -82,12 +88,14 @@ def mark_end():
|
||||||
load_next_line()
|
load_next_line()
|
||||||
update_display()
|
update_display()
|
||||||
|
|
||||||
|
|
||||||
def on_next_press(event=None):
|
def on_next_press(event=None):
|
||||||
if current_subtitle.get("start") is None:
|
if current_subtitle.get("start") is None:
|
||||||
mark_start()
|
mark_start()
|
||||||
else:
|
else:
|
||||||
mark_end()
|
mark_end()
|
||||||
|
|
||||||
|
|
||||||
def on_autonext_press(event=None):
|
def on_autonext_press(event=None):
|
||||||
if current_subtitle.get("start") is None:
|
if current_subtitle.get("start") is None:
|
||||||
mark_start()
|
mark_start()
|
||||||
|
@ -95,6 +103,7 @@ def on_autonext_press(event = None):
|
||||||
mark_end()
|
mark_end()
|
||||||
mark_start()
|
mark_start()
|
||||||
|
|
||||||
|
|
||||||
def on_done_press(event=None):
|
def on_done_press(event=None):
|
||||||
if line_index < 1:
|
if line_index < 1:
|
||||||
return
|
return
|
||||||
|
@ -104,22 +113,29 @@ def on_done_press(event = None):
|
||||||
scene_breaks.append(line_index)
|
scene_breaks.append(line_index)
|
||||||
update_display()
|
update_display()
|
||||||
|
|
||||||
|
|
||||||
def on_back(event=None):
|
def on_back(event=None):
|
||||||
global subtitles
|
global subtitles
|
||||||
if len(subtitles) == 0:
|
if len(subtitles) == 0:
|
||||||
messagebox.showerror("Error", f"No subtitle to remove.")
|
messagebox.showerror("Error", f"No subtitle to remove.")
|
||||||
return
|
return
|
||||||
if current_subtitle["start"]:
|
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."):
|
if not messagebox.askokcancel(
|
||||||
|
"Warning",
|
||||||
|
f"\nDeleting current #{current_subtitle.get("index")}.\n You need to go back and mark start again.",
|
||||||
|
):
|
||||||
return
|
return
|
||||||
current_subtitle["content"] = ""
|
current_subtitle["content"] = ""
|
||||||
if len(subtitles) > 1:
|
if len(subtitles) > 1:
|
||||||
current_subtitle["content"] = subtitles[-1]["content"] + "\n"
|
current_subtitle["content"] = subtitles[-1]["content"] + "\n"
|
||||||
current_subtitle["start"] = None
|
current_subtitle["start"] = None
|
||||||
else:
|
else:
|
||||||
if not messagebox.askokcancel("Warning", f"\nDeleting previous #{subtitles[-1].get("index")}.\n You need to go back and mark start again."):
|
if not messagebox.askokcancel(
|
||||||
|
"Warning",
|
||||||
|
f"\nDeleting previous #{subtitles[-1].get("index")}.\n You need to go back and mark start again.",
|
||||||
|
):
|
||||||
return
|
return
|
||||||
del(subtitles[-1])
|
del subtitles[-1]
|
||||||
current_subtitle["content"] = ""
|
current_subtitle["content"] = ""
|
||||||
if len(subtitles) > 2:
|
if len(subtitles) > 2:
|
||||||
current_subtitle["content"] = subtitles[-1]["content"] + "\n"
|
current_subtitle["content"] = subtitles[-1]["content"] + "\n"
|
||||||
|
@ -127,16 +143,21 @@ def on_back(event = None):
|
||||||
print(f"\nCurrent: #{len(subtitles)+1} {current_subtitle}")
|
print(f"\nCurrent: #{len(subtitles)+1} {current_subtitle}")
|
||||||
load_next_line(diff=-1)
|
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(
|
||||||
|
text=f"{to_time(current_pos, True)} / {to_time(MediaTotalLength, True)}"
|
||||||
|
)
|
||||||
root.after(200, update_timestamp)
|
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):
|
def to_time(seconds, hide=False):
|
||||||
minutes = int(seconds // 60)
|
minutes = int(seconds // 60)
|
||||||
second = seconds % 60
|
second = seconds % 60
|
||||||
|
@ -149,6 +170,7 @@ def to_time(seconds, hide = False):
|
||||||
return f"{minute:02}:{second:06.3f}"
|
return f"{minute:02}:{second:06.3f}"
|
||||||
return f"{hours}:{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()
|
||||||
text_scroll_index = subtitle_text.index(tk.INSERT)
|
text_scroll_index = subtitle_text.index(tk.INSERT)
|
||||||
|
@ -169,7 +191,9 @@ def update_display():
|
||||||
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(
|
||||||
|
f"End section at idx {subtitles[n-1]["index"]}, {to_time(subtitles[n-1]["end"])}"
|
||||||
|
)
|
||||||
subtitle_display = f"----------\n\n"
|
subtitle_display = f"----------\n\n"
|
||||||
subtitle_text.insert(tk.END, subtitle_display)
|
subtitle_text.insert(tk.END, subtitle_display)
|
||||||
|
|
||||||
|
@ -187,13 +211,13 @@ def update_display():
|
||||||
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)))
|
||||||
|
@ -202,10 +226,11 @@ def update_display():
|
||||||
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):
|
def load_next_line(diff=1):
|
||||||
global line_index
|
global line_index
|
||||||
print(line_index, len(script_lines))
|
print(line_index, len(script_lines))
|
||||||
|
@ -222,6 +247,7 @@ def load_next_line(diff = 1):
|
||||||
player.pause()
|
player.pause()
|
||||||
messagebox.showerror("Error", f"No more subtitle.")
|
messagebox.showerror("Error", f"No more subtitle.")
|
||||||
|
|
||||||
|
|
||||||
def load_file(label):
|
def load_file(label):
|
||||||
file_path = filedialog.askopenfilename()
|
file_path = filedialog.askopenfilename()
|
||||||
if file_path:
|
if file_path:
|
||||||
|
@ -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)
|
||||||
|
@ -237,36 +264,47 @@ def choose_audio():
|
||||||
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):
|
||||||
|
@ -274,32 +312,52 @@ def save_stacked_subtitles():
|
||||||
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", "*.*")])
|
|
||||||
|
fpath = filedialog.asksaveasfilename(
|
||||||
|
defaultextension=".json",
|
||||||
|
filetypes=[("JSON files", "*.json"), ("All files", "*.*")],
|
||||||
|
)
|
||||||
if fpath:
|
if fpath:
|
||||||
with open(fpath, 'w') as f:
|
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))
|
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()
|
fpath = filedialog.askopenfilename()
|
||||||
if fpath:
|
if fpath:
|
||||||
with open(fpath, 'r') as f:
|
with open(fpath, "r") as f:
|
||||||
back = json.loads(f.read())
|
back = json.loads(f.read())
|
||||||
subtitles = back["subtitles"]
|
subtitles = back["subtitles"]
|
||||||
script_lines = back["script_lines"]
|
script_lines = back["script_lines"]
|
||||||
|
@ -313,6 +371,7 @@ def load_prog():
|
||||||
player.pause()
|
player.pause()
|
||||||
update_display()
|
update_display()
|
||||||
|
|
||||||
|
|
||||||
def skip_to_time():
|
def skip_to_time():
|
||||||
try:
|
try:
|
||||||
time_seconds = float(skip_time_entry.get())
|
time_seconds = float(skip_time_entry.get())
|
||||||
|
@ -324,20 +383,24 @@ 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:
|
||||||
|
@ -345,19 +408,22 @@ def merge_selected(event = None):
|
||||||
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()
|
||||||
|
@ -382,13 +448,14 @@ 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)
|
||||||
|
@ -410,9 +477,10 @@ def remove_selected(event = None):
|
||||||
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):
|
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):
|
||||||
|
@ -420,7 +488,11 @@ def on_list_double_click(event):
|
||||||
|
|
||||||
text = script_listbox.get(index)
|
text = script_listbox.get(index)
|
||||||
|
|
||||||
entry = tk.Entry(root, bd=1, highlightthickness=1, )
|
entry = tk.Entry(
|
||||||
|
root,
|
||||||
|
bd=1,
|
||||||
|
highlightthickness=1,
|
||||||
|
)
|
||||||
entry.insert(0, text)
|
entry.insert(0, text)
|
||||||
entry.select_range(0, tk.END)
|
entry.select_range(0, tk.END)
|
||||||
|
|
||||||
|
@ -438,12 +510,14 @@ def on_list_double_click(event):
|
||||||
|
|
||||||
entry.focus_set()
|
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)
|
edit_selected(selected_idx=index)
|
||||||
|
|
||||||
|
|
||||||
def update_stdout():
|
def update_stdout():
|
||||||
stdoutext.config(state=tk.NORMAL)
|
stdoutext.config(state=tk.NORMAL)
|
||||||
stdoutext.delete("1.0", tk.END)
|
stdoutext.delete("1.0", tk.END)
|
||||||
|
@ -451,6 +525,7 @@ 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)
|
||||||
|
@ -459,39 +534,59 @@ def show_console_output_screen():
|
||||||
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)
|
||||||
|
@ -511,13 +606,15 @@ 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(
|
||||||
|
file_frame, text="Save Stacked", command=save_stacked_subtitles
|
||||||
|
)
|
||||||
stack_button.pack(side=tk.TOP, pady=(0, 10), fill=tk.X)
|
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)
|
||||||
|
@ -604,7 +702,14 @@ 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(
|
||||||
|
root, text="Show stdout", command=show_console_output_screen, borderwidth=0
|
||||||
|
)
|
||||||
debug_button.pack(side=tk.TOP, pady=(0, 5))
|
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
|
return wrapper
|
||||||
|
|
||||||
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("'", presskey(next_button, on_next_press))
|
||||||
root.bind('<Right>', presskey(fastforward_button,fastforward_audio))
|
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()
|
||||||
|
|
129
wordvtt.py
129
wordvtt.py
|
@ -4,16 +4,19 @@ 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 = (
|
||||||
|
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+$"
|
||||||
parts = re.split(r'\n\n+', vtt_string.strip())
|
parts = re.split(r"\n\n+", vtt_string.strip())
|
||||||
if parts[0].startswith('WEBVTT'):
|
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]):
|
||||||
|
@ -23,12 +26,24 @@ def from_vtt(vtt_string):
|
||||||
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": (
|
||||||
|
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],
|
||||||
|
}
|
||||||
|
)
|
||||||
# def sanitizevttwordlevel(subtitles):
|
# def sanitizevttwordlevel(subtitles):
|
||||||
# errorwords = []
|
# errorwords = []
|
||||||
# newords = {}
|
# newords = {}
|
||||||
|
@ -45,7 +60,6 @@ def from_vtt(vtt_string):
|
||||||
# if original in errorwords:
|
# if original in errorwords:
|
||||||
# for i in errorwords[original]:
|
# for i in errorwords[original]:
|
||||||
|
|
||||||
|
|
||||||
# else:
|
# else:
|
||||||
# errorwords[orig].append(word)
|
# errorwords[orig].append(word)
|
||||||
|
|
||||||
|
@ -71,19 +85,21 @@ def from_vtt(vtt_string):
|
||||||
# sanitizevttwordlevel(subtitles)
|
# sanitizevttwordlevel(subtitles)
|
||||||
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"]
|
||||||
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 = ""
|
||||||
|
@ -92,16 +108,17 @@ def to_stacked_vtt(subtitles):
|
||||||
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.")
|
||||||
|
@ -116,10 +133,14 @@ def script_from_word_vtt(wordvtt):
|
||||||
# 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 (
|
||||||
|
subtitles[n + 2]["content"].replace("<u>", "").replace("</u>", "")
|
||||||
|
!= sentence
|
||||||
|
):
|
||||||
ADD_NEXT_SENTENCE = 0
|
ADD_NEXT_SENTENCE = 0
|
||||||
return sentences
|
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]
|
||||||
|
@ -140,7 +161,9 @@ def create_word_scenes(wordvtt, scriptraw):
|
||||||
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(
|
||||||
|
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
|
return
|
||||||
|
|
||||||
current_scene = scenes[scenes_cur]
|
current_scene = scenes[scenes_cur]
|
||||||
|
@ -153,7 +176,9 @@ def create_word_scenes(wordvtt, scriptraw):
|
||||||
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(
|
||||||
|
f"Error, index wrong. {scenes_cur}, word: {word_idx}, total words: {len(sentence.split(" "))}\n{subtitle}"
|
||||||
|
)
|
||||||
word_idx = 0
|
word_idx = 0
|
||||||
scenes_cur += 1
|
scenes_cur += 1
|
||||||
current_scene = scenes[scenes_cur]
|
current_scene = scenes[scenes_cur]
|
||||||
|
@ -168,16 +193,21 @@ def create_word_scenes(wordvtt, scriptraw):
|
||||||
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 = {
|
||||||
|
"start": subtitle["start"],
|
||||||
|
"end": subtitle["end"],
|
||||||
|
"index": word_idx,
|
||||||
|
"word": word,
|
||||||
|
}
|
||||||
current_scene["timestamp"].append(word_time)
|
current_scene["timestamp"].append(word_time)
|
||||||
|
|
||||||
# print(json.dumps(scenes, indent=2))
|
# print(json.dumps(scenes, indent=2))
|
||||||
|
@ -200,19 +230,21 @@ def create_word_scenes(wordvtt, scriptraw):
|
||||||
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(
|
||||||
|
minutes=int(minutes), seconds=int(seconds), milliseconds=int(milliseconds)
|
||||||
|
)
|
||||||
return td
|
return td
|
||||||
|
|
||||||
script = []
|
script = []
|
||||||
|
@ -243,7 +275,7 @@ def autobreak(lines, times):
|
||||||
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:
|
||||||
|
@ -261,6 +293,7 @@ def autobreak(lines, times):
|
||||||
|
|
||||||
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]
|
||||||
|
@ -268,7 +301,7 @@ def scene_from_new_script(raw_script, full_script, full_scenes):
|
||||||
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):
|
||||||
|
@ -304,6 +337,7 @@ def scene_from_new_script(raw_script, full_script, full_scenes):
|
||||||
assert len(new_script) == len(new_timestamp)
|
assert len(new_script) == len(new_timestamp)
|
||||||
return new_script, 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 = []
|
||||||
|
@ -312,10 +346,18 @@ def build_new_subtitle(new_script, new_timestamp):
|
||||||
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(
|
||||||
|
{
|
||||||
|
"content": " ".join(buffer).replace("##", ""),
|
||||||
|
"start": start,
|
||||||
|
"end": j["end"],
|
||||||
|
}
|
||||||
|
)
|
||||||
buffer, start = [], None
|
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(
|
||||||
|
f"Section break at line #{len(current_scene):<3}| \"{current_scene[-1]["content"]}\""
|
||||||
|
)
|
||||||
new_scenes.append(current_scene)
|
new_scenes.append(current_scene)
|
||||||
current_scene = []
|
current_scene = []
|
||||||
else:
|
else:
|
||||||
|
@ -325,7 +367,9 @@ def build_new_subtitle(new_script, new_timestamp):
|
||||||
|
|
||||||
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)
|
||||||
|
@ -335,10 +379,13 @@ def build_new_subtitle(new_script, new_timestamp):
|
||||||
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 saveFile(filename, data, override=False):
|
def saveFile(filename, data, override=False):
|
||||||
if os.path.exists(filename) and not override:
|
if os.path.exists(filename) and not override:
|
||||||
print(f"File {filename} already exists.")
|
print(f"File {filename} already exists.")
|
||||||
|
@ -346,6 +393,7 @@ def saveFile(filename, data, override = False):
|
||||||
with open(filename, "w") as f:
|
with open(filename, "w") as f:
|
||||||
f.write(data)
|
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()
|
||||||
|
@ -353,6 +401,7 @@ def openFile(filename):
|
||||||
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))
|
||||||
|
@ -375,14 +424,18 @@ def main(vttfile, scriptfile):
|
||||||
saveFile(jsonfile, json.dumps(final_vtt, indent=2), True)
|
saveFile(jsonfile, json.dumps(final_vtt, indent=2), True)
|
||||||
print(f"Saved JSON file as {jsonfile}. Fix it, and convert it to VTT.")
|
print(f"Saved JSON file as {jsonfile}. Fix it, and convert it to VTT.")
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
if len(sys.argv) not in (2, 3):
|
if len(sys.argv) not in (2, 3):
|
||||||
print(f"Usage: {sys.argv[0].split("/")[-1]} [vtt file] (txt file)\n" \
|
print(
|
||||||
f" {sys.argv[0].split("/")[-1]} [JSON file]\n" \
|
f"Usage: {sys.argv[0].split("/")[-1]} [vtt file] (txt file)\n"
|
||||||
"** Only output from openai-whisper with '--word-timestamp true' is accepted.)\n" \
|
f" {sys.argv[0].split("/")[-1]} [JSON file]\n"
|
||||||
"** You have to run this for first time, and then fix .script file, and then re-run this script.\n" \
|
"** Only output from openai-whisper with '--word-timestamp true' is accepted.)\n"
|
||||||
"** Adding newline/period/commas are onlt permitted. Fix else in JSON file.")
|
"** 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()
|
sys.exit()
|
||||||
|
|
||||||
vtt = sys.argv[1]
|
vtt = sys.argv[1]
|
||||||
|
@ -401,7 +454,7 @@ if __name__=="__main__":
|
||||||
saveFile(orgf + ".final.vtt", to_vtt(final_vtt), True)
|
saveFile(orgf + ".final.vtt", to_vtt(final_vtt), True)
|
||||||
saveFile(orgf + ".stacked.vtt", to_stacked_vtt(final_vtt), True)
|
saveFile(orgf + ".stacked.vtt", to_stacked_vtt(final_vtt), True)
|
||||||
sys.exit(0)
|
sys.exit(0)
|
||||||
if (not os.path.exists(vtt)):
|
if not os.path.exists(vtt):
|
||||||
print(f"Input file doesnt exists.")
|
print(f"Input file doesnt exists.")
|
||||||
sys.exit(-1)
|
sys.exit(-1)
|
||||||
script = ".".join(vtt.split(".")[:-1]) + ".txt"
|
script = ".".join(vtt.split(".")[:-1]) + ".txt"
|
||||||
|
|
Loading…
Reference in New Issue