import tkinter as tk from tkinter import filedialog from moviepy import VideoFileClip, TextClip, CompositeVideoClip import threading import json # Global settings with defaults settings = { "subtitle_y_px": 1550, "highlight_offset": -8, "font_size_subtitle": 65, "font_size_highlight": 68, "highlight_x_offset": 0, "video_path": None, "selected_font": "Arial" # Default font } # Compatible fonts that work across different systems COMPATIBLE_FONTS = [ "Arial", "Times-Roman", "Helvetica", "Courier", "Comic-Sans-MS", "Impact", "Verdana", "Tahoma", "Georgia", "Trebuchet-MS" ] preset_file = "subtitle_gui_presets.json" def save_presets(): with open(preset_file, "w") as f: json.dump(settings, f) print("💾 Presets saved!") def load_presets(): global settings try: with open(preset_file, "r") as f: loaded = json.load(f) settings.update(loaded) print("✅ Presets loaded!") sync_gui() except FileNotFoundError: print("⚠️ No presets found.") def sync_gui(): sub_y_slider.set(settings["subtitle_y_px"]) highlight_slider.set(settings["highlight_offset"]) highlight_x_slider.set(settings["highlight_x_offset"]) sub_font_slider.set(settings["font_size_subtitle"]) highlight_font_slider.set(settings["font_size_highlight"]) font_var.set(settings["selected_font"]) def render_preview(): if not settings["video_path"]: print("⚠️ No video selected.") return clip = VideoFileClip(settings["video_path"]).subclipped(0, 3) # Use first 3 seconds vertical_clip = clip.resized(height=1920).cropped(width=1080, x_center=clip.w / 2) subtitle_text = "THIS IS A TEST SUBTITLE" highlight_word = "SUBTITLE" base_subtitle = TextClip( text=subtitle_text, font_size=settings["font_size_subtitle"], font=settings["selected_font"], color='white', stroke_color='black', stroke_width=5 ).with_duration(3).with_position(('center', settings["subtitle_y_px"])) # Compute highlight word position full_text = subtitle_text.upper() words = full_text.split() highlight_index = words.index(highlight_word.upper()) chars_before = sum(len(w) + 1 for w in words[:highlight_index]) char_width = 35 total_width = len(full_text) * char_width x_offset = (chars_before * char_width) - (total_width // 2) + settings["highlight_x_offset"] highlighted_word = TextClip( text=highlight_word, font_size=settings["font_size_highlight"], font=settings["selected_font"], color='#FFD700', stroke_color='#FF6B35', stroke_width=5 ).with_duration(1.5).with_start(0.75).with_position((540 + x_offset, settings["subtitle_y_px"] + settings["highlight_offset"])) final = CompositeVideoClip([vertical_clip, base_subtitle, highlighted_word], size=(1080, 1920)) # Scale down the preview to fit 1080p monitor (max height ~900px to leave room for taskbar) preview_scale = 900 / 1920 # Scale factor to fit height preview_width = int(1080 * preview_scale) preview_height = int(1920 * preview_scale) preview_clip = final.resized((preview_width, preview_height)) preview_clip.preview(fps=24, audio=False) clip.close() final.close() preview_clip.close() def update_setting(var_name, value): settings[var_name] = int(value) def update_font(font_name): settings["selected_font"] = font_name def open_video(): file_path = filedialog.askopenfilename(filetypes=[("MP4 files", "*.mp4")]) if file_path: settings["video_path"] = file_path print(f"📂 Loaded video: {file_path}") def start_preview_thread(): threading.Thread(target=render_preview).start() # GUI Setup root = tk.Tk() root.title("Subtitle Positioning Tool") root.geometry("400x600") load_btn = tk.Button(root, text="🎥 Load Video", command=open_video) load_btn.pack(pady=10) tk.Label(root, text="Font Family").pack() font_var = tk.StringVar(value=settings["selected_font"]) font_dropdown = tk.OptionMenu(root, font_var, *COMPATIBLE_FONTS, command=update_font) font_dropdown.pack(pady=5) tk.Label(root, text="Subtitle Y Position").pack() sub_y_slider = tk.Scale(root, from_=1000, to=1800, orient="horizontal", command=lambda v: update_setting("subtitle_y_px", v)) sub_y_slider.set(settings["subtitle_y_px"]) sub_y_slider.pack() tk.Label(root, text="Highlight Y Offset").pack() highlight_slider = tk.Scale(root, from_=-100, to=100, orient="horizontal", command=lambda v: update_setting("highlight_offset", v)) highlight_slider.set(settings["highlight_offset"]) highlight_slider.pack() tk.Label(root, text="Highlight X Offset").pack() highlight_x_slider = tk.Scale(root, from_=-300, to=300, orient="horizontal", command=lambda v: update_setting("highlight_x_offset", v)) highlight_x_slider.set(settings["highlight_x_offset"]) highlight_x_slider.pack() tk.Label(root, text="Subtitle Font Size").pack() sub_font_slider = tk.Scale(root, from_=30, to=100, orient="horizontal", command=lambda v: update_setting("font_size_subtitle", v)) sub_font_slider.set(settings["font_size_subtitle"]) sub_font_slider.pack() tk.Label(root, text="Highlight Font Size").pack() highlight_font_slider = tk.Scale(root, from_=30, to=100, orient="horizontal", command=lambda v: update_setting("font_size_highlight", v)) highlight_font_slider.set(settings["font_size_highlight"]) highlight_font_slider.pack() preview_btn = tk.Button(root, text="▶️ Preview Clip", command=start_preview_thread) preview_btn.pack(pady=10) save_btn = tk.Button(root, text="💾 Save Preset", command=save_presets) save_btn.pack(pady=5) load_preset_btn = tk.Button(root, text="📂 Load Preset", command=load_presets) load_preset_btn.pack(pady=5) root.mainloop()