import customtkinter from tkinter import filedialog, messagebox # Keep these for standard dialogs import os import sys import threading import time import converter import downloader import utils import config class VideoConverterGUI: def __init__(self, master): self.master = master master.title("Video Converter") customtkinter.set_appearance_mode("System") # Modes: "System" (default), "Dark", "Light" customtkinter.set_default_color_theme("blue") # Themes: "blue" (default), "green", "dark-blue") self.label = customtkinter.CTkLabel(master, text="Select video file or enter URL:") self.label.pack() self.filepath_label = customtkinter.CTkLabel(master, text="") self.filepath_label.pack() self.browse_button = customtkinter.CTkButton(master, text="Browse", command=self.browse_file) self.browse_button.pack() self.url_label = customtkinter.CTkLabel(master, text="URL:") self.url_label.pack() self.url_entry = customtkinter.CTkEntry(master, width=50) self.url_entry.pack() self.quality_label = customtkinter.CTkLabel(master, text="Quality:") self.quality_label.pack() self.quality_var = customtkinter.StringVar(master) self.quality_var.set("medium") # default value self.quality_menu = customtkinter.CTkOptionMenu(master, variable=self.quality_var, values=["low", "medium", "high", "archive"]) self.quality_menu.pack() self.cookies_label = customtkinter.CTkLabel(master, text="Cookies from Browser:") self.cookies_label.pack() self.cookies_var = customtkinter.StringVar(master) self.cookies_var.set("none") # default value self.cookies_menu = customtkinter.CTkOptionMenu(master, variable=self.cookies_var, values=["none", "brave", "chrome", "chromium", "edge", "firefox", "opera", "safari", "vivaldi", "whale"]) self.cookies_menu.pack() self.output_dir_label = customtkinter.CTkLabel(master, text="Output Directory:") self.output_dir_label.pack() self.output_dir_button = customtkinter.CTkButton(master, text="Select Directory", command=self.select_output_dir) self.output_dir_button.pack() self.output_dir_path_label = customtkinter.CTkLabel(master, text="") self.output_dir_path_label.pack() self.convert_button = customtkinter.CTkButton(master, text="Convert", command=self.convert) self.convert_button.pack() self.cancel_button = customtkinter.CTkButton(master, text="Cancel", command=self.cancel_conversion, state=customtkinter.DISABLED) self.cancel_button.pack() self.status_var = customtkinter.StringVar() self.status_label = customtkinter.CTkLabel(master, textvariable=self.status_var) self.status_label.pack(side=tk.BOTTOM, fill=tk.X) self.progress = customtkinter.CTkProgressBar(master, orientation="horizontal") self.progress.pack(side=tk.BOTTOM, fill=tk.X) def browse_file(self): filepath = filedialog.askopenfilename() self.filepath_label.config(text=filepath) def select_output_dir(self): output_dir = filedialog.askdirectory() self.output_dir_path_label.config(text=output_dir) def convert(self): input_path = self.filepath_label.cget("text") url = self.url_entry.get() output_dir = self.output_dir_path_label.cget("text") quality = self.quality_var.get() if not (input_path or url): messagebox.showerror("Error", "Please select a file or enter a URL.") return if not output_dir: messagebox.showerror("Error", "Please select an output directory.") return self.convert_button.config(state=tk.DISABLED) self.cancel_button.config(state=tk.NORMAL) self.status_var.set("Starting conversion...") self.conversion_thread = threading.Thread( target=self._run_conversion, args=(input_path, url, output_dir, quality, self.cookies_var.get()), daemon=True ) self.conversion_thread.start() def _run_conversion(self, input_path, url, output_dir, quality, cookies_from_browser): try: if url: self.status_var.set("Downloading video...") # Since we don't have progress for downloads, we'll use indeterminate mode self.progress['mode'] = 'indeterminate' self.progress.start() input_path = downloader.download_video(url, output_dir, cookies_from_browser) self.progress.stop() self.progress['mode'] = 'determinate' if not input_path: raise Exception("Download failed.") self.status_var.set("Converting video...") duration = utils.get_video_duration(input_path) if not duration: # If we can't get the duration, use indeterminate mode self.progress['mode'] = 'indeterminate' self.progress.start() else: self.progress['maximum'] = duration self.progress_thread = threading.Thread(target=self._update_progress, daemon=True) self.progress_thread.start() success = converter.convert_video(input_path, output_dir, quality) if success: self.status_var.set("Conversion successful!") messagebox.showinfo("Success", "Conversion completed successfully!") else: raise Exception("Conversion failed. Check logs for details.") except Exception as e: self.status_var.set(f"Error: {e}") try: with open(config.FFMPEG_LOG_FILE_PATH, "r") as f: log_lines = f.readlines() last_10_lines = "".join(log_lines[-10:]) messagebox.showerror("Error", f"An error occurred: {e}\n\nLast 10 lines of log:\n{last_10_lines}") except FileNotFoundError: messagebox.showerror("Error", f"An error occurred: {e}\n\nCould not find log file.") except Exception as log_e: messagebox.showerror("Error", f"An error occurred: {e}\n\nCould not read log file: {log_e}") finally: self.convert_button.config(state=tk.NORMAL) self.cancel_button.config(state=tk.DISABLED) self.progress.stop() self.progress['value'] = 0 def _update_progress(self): progress_file = "ffmpeg_progress.log" while self.conversion_thread.is_alive(): try: with open(progress_file, "r") as f: lines = f.readlines() if lines: last_line = lines[-1] if "out_time_ms" in last_line: time_ms = int(last_line.split("=")[1]) self.progress['value'] = time_ms / 1000000 except (FileNotFoundError, IndexError, ValueError): pass time.sleep(0.1) def cancel_conversion(self): # This is a bit tricky, as we can't easily kill the thread. # For now, we'll just show a message. # A more robust solution would involve a more complex mechanism # to signal the thread to stop. messagebox.showinfo("Cancel", "Cancellation requested. The current operation will continue until it is finished.") if __name__ == '__main__': root = customtkinter.CTk() gui = VideoConverterGUI(root) root.mainloop()