diff --git a/restack.py b/restack.py new file mode 100644 index 0000000..451bda8 --- /dev/null +++ b/restack.py @@ -0,0 +1,81 @@ +import re +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_LINE_NUMBER_PATTERN = r"^\d+$" + +def parse_vtt(vtt_string): + parts = re.split(r'\n\n+', vtt_string.strip()) + + if parts[0].startswith('WEBVTT'): + parts.pop(0) + + subtitles = [] + for part in parts: + lines = part.split('\n') + match = re.match(VTT_TIMECODE_PATTERN, lines[0]) + if not match: + if re.match(VTT_LINE_NUMBER_PATTERN, lines[0]): + lines.pop(0) + match = re.match(VTT_TIMECODE_PATTERN, lines[0]) + if not match: + continue + + start, end = match.groups() + content = '\n'.join(lines[1:]) + + subtitles.append({ + 'start': start, + 'end': end, + 'content': content + }) + + return subtitles + +def to_vtt(subtitles): + vtt_content = "WEBVTT\n\n" + for idx, subtitle in enumerate(subtitles): + # print(subtitle, idx) + start = subtitle['start'] + end = subtitle['end'] + content = subtitle['content'] + vtt_content += f"{start} --> {end}\n{content}\n\n" + return vtt_content.strip() + + +with open("example.vtt", "r") as f: + vtt_content = f.read() + +parsed_vtt = parse_vtt(vtt_content) +#print(len(parsed_vtt)) + +buffer = [] +linebuf = [] + +for line in parsed_vtt: +# print(line["content"].strip()) + content = line["content"].strip() + if "".join([i["content"] for i in linebuf]).count(".") < 4 or len(linebuf) < 5: + linebuf.append(line) + else: + linebuf.append(line) + buffer.append(linebuf) + linebuf = [] + +# print(buffer) + +sub = [] +for section in buffer: + strbuf = "" + for scene in section: + strbuf += scene["content"] + # if scene["content"][-1] == ".": + strbuf += "\n" + # else: + # strbuf += " " + scene["content"] = strbuf + sub.append(scene) + +# print(buffer[0]) + +print(to_vtt(sub)) \ No newline at end of file diff --git a/vttmaker.py b/vttmaker.py index 8b258e3..1941efc 100644 --- a/vttmaker.py +++ b/vttmaker.py @@ -60,6 +60,8 @@ def mark_start(): print("Please load first..") return current_subtitle["content"] = current_subtitle.get("content","") + script_lines[line_index] + # current_subtitle["content"] = script_lines[line_index] + current_subtitle["index"] = line_index update_display() print(f"\n{timestamp} --> ", end="") @@ -94,8 +96,6 @@ def on_done_press(event = None): return if current_subtitle["start"] == None: current_subtitle["content"] = "" - current_subtitle["start"] = None - load_next_line() update_display() return timestamp = player.get_time() / 1000.0 @@ -113,16 +113,20 @@ def on_back(event = None): messagebox.showerror("Error", f"No subtitle to remove.") return if current_subtitle["start"]: - if not messagebox.askokcancel("Warning", f"\nDeleting \"{current_subtitle.get("content")}\" \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 - if len(subtitles) > 2: - current_subtitle["content"] = subtitles[-2]["content"] + "\n" 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 \"{subtitles[-1].get("content")}\" \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 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) @@ -144,8 +148,12 @@ def to_time(seconds): return f"{hours}:{minute:02}:{second:06.3f}" def update_display(): + listbox_scroll_pos = script_listbox.yview() + text_scroll_index = subtitle_text.index(tk.INSERT) + script_listbox.delete(0, tk.END) script_listbox.selection_clear(0, tk.END) + subtitle_text.config(state=tk.NORMAL) subtitle_text.delete("1.0", tk.END) @@ -172,6 +180,11 @@ def update_display(): if player.is_playing() or not audio_started: script_listbox.see(min(line_index + 5, len(script_lines))) subtitle_text.see(tk.END) + else: + script_listbox.yview_moveto(listbox_scroll_pos[0]) + subtitle_text.see(text_scroll_index) + + info_label.config(text="[ Current stack ]\n" + current_subtitle.get('content', "")) subtitle_text.config(state=tk.DISABLED) @@ -396,6 +409,24 @@ def show_console_output_screen(): def on_prev_line(): load_next_line(diff=-1) +speedex = 1 + +def inc_playrate(): + global speedex + speedex = round(speedex * 1.2, 2) + playrate() + +def dec_playrate(): + global speedex + speedex = round(speedex / 1.2, 2) + playrate() + +def playrate(): + speed_label.config(text=f"x{speedex:.2f}") + print(f"Playback rate x{speedex:.2f}") + player.set_rate(speedex) + + root = tk.Tk() root.title("Subtitle Timing Editor") root.geometry('1000x800') @@ -441,6 +472,20 @@ timestamp_label = tk.Label(root, text="0.00s / 0.00s") timestamp_label.pack(side=tk.BOTTOM, pady=5) +ctrl_frame = tk.Frame(root, borderwidth=0, relief="solid") +ctrl_frame.pack(side=tk.BOTTOM, padx=5) + +one_button = tk.Button(ctrl_frame, text='-', borderwidth=0, command=dec_playrate) +one_button.pack(side=tk.LEFT, padx=(5,0), pady=5) + +speed_label = tk.Label(ctrl_frame, text="x1") +speed_label.pack(side=tk.LEFT, padx=0, pady=5) + +two_button = tk.Button(ctrl_frame, text='+', borderwidth=0, command=inc_playrate) +two_button.pack(side=tk.LEFT, padx=(0,5), pady=5) + + + btn_frame = tk.Frame(root, borderwidth=0, relief="solid") btn_frame.pack(side=tk.BOTTOM, padx=5) @@ -504,12 +549,14 @@ skip_time_entry.insert(0, "0") 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_label = tk.Label(info_frame, text='' \ - # 'VTT Maker by @morgan9e\n\n' \ - 'Usage:\n Mark <\'>\n Next <;>\n Done \n' \ - '\n- Creates \"stacked\" subtitles easily.\n- Stacks subtitle from previous scene.' \ - '\n- Load audio before restoring progress.\n- You can Edit, Merge, Delete script with left click.' \ - , font=("monospace", 8), wraplength=140, 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='' \ +# # 'VTT Maker by @morgan9e\n\n' \ +# 'Usage:\n Mark <\'>\n Next <;>\n Done \n' \ +# '\n- Creates \"stacked\" subtitles easily.\n- Stacks subtitle from previous scene.' \ +# '\n- Load audio before restoring progress.\n- You can Edit, Merge, Delete script with left click.' \ +# , font=("monospace", 8), wraplength=140, justify=tk.LEFT) info_label.pack(side=tk.TOP, anchor="nw")