From 7283966b2e4633e05efd1fb9629583e53fbc5202 Mon Sep 17 00:00:00 2001 From: Ramforth Date: Mon, 3 Nov 2025 01:18:38 +0100 Subject: [PATCH] Feat: Implement main content frame and dynamic mode switch text --- gui.py | 116 +++++++++++++++++++++++++-------------------------------- 1 file changed, 50 insertions(+), 66 deletions(-) diff --git a/gui.py b/gui.py index 3a8a2c9..e637c60 100644 --- a/gui.py +++ b/gui.py @@ -15,17 +15,19 @@ class VideoConverterGUI: def __init__(self, master): self.master = master master.title("Video Converter") - # Set a default size to prevent the narrow window - # You can change the "widthxheight" and add "+x+y" for position. master.geometry("600x750+100+100") # --- Set Dark Mode by Default --- - customtkinter.set_appearance_mode("Dark") # Modes: "System", "Dark", "Light" - customtkinter.set_default_color_theme("blue") + customtkinter.set_appearance_mode("Dark") + customtkinter.set_default_color_theme("blue") # Temporarily using built-in theme for build stability - # --- Footer Frame --- - self.footer_frame = customtkinter.CTkFrame(master, fg_color="transparent") - self.footer_frame.pack(side="bottom", fill="x", padx=10, pady=(5, 10)) + # Create the main content frame + self.main_frame = customtkinter.CTkFrame(master, fg_color="transparent") # Make it transparent to root + self.main_frame.pack(fill="both", expand=True, padx=10, pady=10) # Add some padding to the main frame + + # --- Footer Frame (packed into main_frame) --- + self.footer_frame = customtkinter.CTkFrame(self.main_frame, fg_color="transparent") + self.footer_frame.pack(side="bottom", fill="x", pady=(5, 10)) self.nickname_label = customtkinter.CTkLabel(self.footer_frame, text="by ramforth") self.nickname_label.pack(side="left", padx=(10, 0)) @@ -33,37 +35,35 @@ class VideoConverterGUI: self.gitea_link = customtkinter.CTkLabel(self.footer_frame, text="https://gitea.ramforth.net/ramforth", text_color="#4077d1", cursor="hand2") self.gitea_link.pack(side="right", padx=(0, 10)) self.gitea_link.bind("", lambda e: self.open_link("https://gitea.ramforth.net/ramforth")) - # Add underline font underline_font = customtkinter.CTkFont(family="sans-serif", size=12, underline=True) self.gitea_link.configure(font=underline_font) - # --- Pack bottom elements first --- - # This makes them stick to the bottom - self.status_var = customtkinter.StringVar(value="Ready") # Give it a default value - self.status_label = customtkinter.CTkLabel(master, textvariable=self.status_var) - # Add padding (x, y) + # --- Status and Progress (packed into main_frame) --- + self.status_var = customtkinter.StringVar(value="Ready") + self.status_label = customtkinter.CTkLabel(self.main_frame, textvariable=self.status_var) self.status_label.pack(side="bottom", fill="x", padx=10, pady=(5, 0)) - self.progress = customtkinter.CTkProgressBar(master, orientation="horizontal") + self.progress = customtkinter.CTkProgressBar(self.main_frame, orientation="horizontal") self.progress.pack(side="bottom", fill="x", padx=10, pady=(0, 10)) - self.progress.set(0) # Default to 0 + self.progress.set(0) - # --- Mode Switch --- - self.mode_switch = customtkinter.CTkSwitch(master, text="Dark Mode", command=self.toggle_mode) - # anchor="w" (west) aligns it to the left + # --- Mode Switch (packed into main_frame) --- + self.mode_switch = customtkinter.CTkSwitch(self.main_frame, command=self.toggle_mode) self.mode_switch.pack(pady=10, padx=20, anchor="w") - self.mode_switch.select() # Start in the "on" (Dark) state + self.mode_switch.select() + + self.mode_label = customtkinter.CTkLabel(self.main_frame, text="Dark Mode") + self.mode_label.pack(pady=0, padx=20, anchor="w") + self.toggle_mode() # Ensure initial mode is set based on switch state - # --- Main Widgets --- - # Create a frame to hold the labels for flexible packing - self.input_label_frame = customtkinter.CTkFrame(master, fg_color="transparent") + # --- Main Widgets (packed into main_frame) --- + self.input_label_frame = customtkinter.CTkFrame(self.main_frame, fg_color="transparent") self.input_label_frame.pack(pady=(10, 5)) self.label_part1 = customtkinter.CTkLabel(self.input_label_frame, text="Select video file ") self.label_part1.pack(side="left") - # Create a bold and underlined font for 'or' or_font = customtkinter.CTkFont(family="sans-serif", weight="bold", underline=True) self.label_or = customtkinter.CTkLabel(self.input_label_frame, text="or", font=or_font) self.label_or.pack(side="left") @@ -71,47 +71,45 @@ class VideoConverterGUI: self.label_part2 = customtkinter.CTkLabel(self.input_label_frame, text=" enter URL:") self.label_part2.pack(side="left") - # Use a fg_color to make the label background visible - self.filepath_label = customtkinter.CTkLabel(master, text="No file selected", fg_color=("gray75", "gray25")) + self.filepath_label = customtkinter.CTkLabel(self.main_frame, text="No file selected", fg_color=("gray75", "gray25")) self.filepath_label.pack(pady=5, padx=20, fill="x") - self.browse_button = customtkinter.CTkButton(master, text="Browse", command=self.browse_file) + self.browse_button = customtkinter.CTkButton(self.main_frame, text="Browse", command=self.browse_file) self.browse_button.pack(pady=5) - self.url_label = customtkinter.CTkLabel(master, text="URL:") - self.url_label.pack(pady=(15, 5)) # Add extra padding on top to create a group + self.url_label = customtkinter.CTkLabel(self.main_frame, text="URL:") + self.url_label.pack(pady=(15, 5)) - # Removed width=50, will use fill="x" in pack() instead - self.url_entry = customtkinter.CTkEntry(master, placeholder_text="https://...") + self.url_entry = customtkinter.CTkEntry(self.main_frame, placeholder_text="https://...") self.url_entry.pack(pady=5, padx=20, fill="x") - self.quality_label = customtkinter.CTkLabel(master, text="Quality:", fg_color="transparent") + self.quality_label = customtkinter.CTkLabel(self.main_frame, text="Quality:", fg_color="transparent") self.quality_label.pack(pady=(15, 5)) self.quality_var = customtkinter.StringVar(master, value="medium") - self.quality_menu = customtkinter.CTkOptionMenu(master, variable=self.quality_var, values=["low", "medium", "high", "archive"]) + self.quality_menu = customtkinter.CTkOptionMenu(self.main_frame, variable=self.quality_var, values=["low", "medium", "high", "archive"]) self.quality_menu.pack(pady=5) - self.cookies_label = customtkinter.CTkLabel(master, text="Cookies from Browser:", fg_color="transparent") + self.cookies_label = customtkinter.CTkLabel(self.main_frame, text="Cookies from Browser:", fg_color="transparent") self.cookies_label.pack(pady=(15, 5)) self.cookies_var = customtkinter.StringVar(master, value="none") - self.cookies_menu = customtkinter.CTkOptionMenu(master, variable=self.cookies_var, values=["none", "brave", "chrome", "chromium", "edge", "firefox", "opera", "safari", "vivaldi", "whale"]) + self.cookies_menu = customtkinter.CTkOptionMenu(self.main_frame, variable=self.cookies_var, values=["none", "brave", "chrome", "chromium", "edge", "firefox", "opera", "safari", "vivaldi", "whale"]) self.cookies_menu.pack(pady=5) - self.output_dir_label = customtkinter.CTkLabel(master, text="Output Directory:", fg_color="transparent") + self.output_dir_label = customtkinter.CTkLabel(self.main_frame, text="Output Directory:", fg_color="transparent") self.output_dir_label.pack(pady=(15, 5)) - self.output_dir_button = customtkinter.CTkButton(master, text="Select Directory", command=self.select_output_dir) + self.output_dir_button = customtkinter.CTkButton(self.main_frame, text="Select Directory", command=self.select_output_dir) self.output_dir_button.pack(pady=5) - self.output_dir_path_label = customtkinter.CTkLabel(master, text="No directory selected", fg_color=("gray75", "gray25")) + self.output_dir_path_label = customtkinter.CTkLabel(self.main_frame, text="No directory selected", fg_color=("gray75", "gray25")) self.output_dir_path_label.pack(pady=5, padx=20, fill="x") - self.convert_button = customtkinter.CTkButton(master, text="Convert", command=self.convert) - self.convert_button.pack(pady=(20, 5)) # Extra top padding + self.convert_button = customtkinter.CTkButton(self.main_frame, text="Convert", command=self.convert) + self.convert_button.pack(pady=(20, 5)) - self.cancel_button = customtkinter.CTkButton(master, text="Cancel", command=self.cancel_conversion, state="disabled") + self.cancel_button = customtkinter.CTkButton(self.main_frame, text="Cancel", command=self.cancel_conversion, state="disabled") self.cancel_button.pack(pady=5) def open_link(self, url): @@ -122,19 +120,23 @@ class VideoConverterGUI: # Check if the switch is on (1) or off (0) if self.mode_switch.get() == 1: customtkinter.set_appearance_mode("Dark") + customtkinter.set_default_color_theme("blue") # Temporarily using built-in theme + self.main_frame.configure(fg_color="#121212") # Set main frame background + self.mode_label.configure(text="Dark Mode") else: customtkinter.set_appearance_mode("Light") + customtkinter.set_default_color_theme("blue") # Temporarily using built-in theme + self.main_frame.configure(fg_color="#ecd7b2") # Set main frame background + self.mode_label.configure(text="Light Mode") def browse_file(self): filepath = filedialog.askopenfilename() if filepath: - # --- FIX: Use .configure() --- self.filepath_label.configure(text=filepath) def select_output_dir(self): output_dir = filedialog.askdirectory() if output_dir: - # --- FIX: Use .configure() --- self.output_dir_path_label.configure(text=output_dir) def convert(self): @@ -143,12 +145,11 @@ class VideoConverterGUI: output_dir = self.output_dir_path_label.cget("text") quality = self.quality_var.get() - # Clear file path if URL is being used if url: input_path = "" self.filepath_label.configure(text="Using URL") elif input_path == "No file selected": - input_path = "" # Ensure it's empty + input_path = "" if not (input_path or url): messagebox.showerror("Error", "Please select a file or enter a URL.") @@ -158,7 +159,6 @@ class VideoConverterGUI: messagebox.showerror("Error", "Please select an output directory.") return - # --- FIX: Use .configure() and string states --- self.convert_button.configure(state="disabled") self.cancel_button.configure(state="normal") self.status_var.set("Starting...") @@ -174,12 +174,10 @@ class VideoConverterGUI: try: if url: self.status_var.set("Downloading video...") - # --- FIX: Use .configure() --- self.progress.configure(mode='indeterminate') self.progress.start() input_path = downloader.download_video(url, output_dir, cookies_from_browser) self.progress.stop() - # --- FIX: Use .configure() --- self.progress.configure(mode='determinate') if not input_path: raise Exception("Download failed.") @@ -188,15 +186,12 @@ class VideoConverterGUI: duration = utils.get_video_duration(input_path) if not duration: - # If we can't get the duration, use indeterminate mode self.progress.configure(mode='indeterminate') self.progress.start() else: - # --- FIX: Set mode to determinate and clear value --- self.progress.configure(mode='determinate') self.progress.set(0) - # --- FIX: Pass duration to the progress updater --- self.progress_thread = threading.Thread(target=self._update_progress, args=(duration,), daemon=True) self.progress_thread.start() @@ -204,7 +199,7 @@ class VideoConverterGUI: if success: self.status_var.set("Conversion successful!") - self.progress.set(1) # Fill the bar on success + self.progress.set(1) messagebox.showinfo("Success", "Conversion completed successfully!") else: raise Exception("Conversion failed. Check logs for details.") @@ -222,19 +217,16 @@ class VideoConverterGUI: messagebox.showerror("Error", f"An error occurred: {e}\n\nCould not read log file: {log_e}") finally: - # --- FIX: Use .configure() and string states --- self.convert_button.configure(state="normal") self.cancel_button.configure(state="disabled") self.progress.stop() - # --- FIX: Use .set() to reset progress bar --- if "Error" not in self.status_var.get(): - self.progress.set(1) # Keep it full on success + self.progress.set(1) else: - self.progress.set(0) # Reset on error - if self.status_var.get() == "Starting...": # Handle cancellation before start + self.progress.set(0) + if self.status_var.get() == "Starting...": self.status_var.set("Ready") - # --- FIX: Accept duration as an argument --- def _update_progress(self, duration): progress_file = "ffmpeg_progress.log" while self.conversion_thread.is_alive(): @@ -246,7 +238,6 @@ class VideoConverterGUI: if "out_time_ms" in last_line: time_ms = int(last_line.split("=")[1]) current_time_sec = time_ms / 1000000 - # --- FIX: Calculate progress as 0.0-1.0 --- if duration and duration > 0: progress_value = max(0, min(1, current_time_sec / duration)) self.progress.set(progress_value) @@ -255,15 +246,8 @@ class VideoConverterGUI: time.sleep(0.1) def cancel_conversion(self): - # This is still a difficult operation. - # A more robust solution involves inter-thread communication - # or sending a 'q' to the ffmpeg process. self.status_var.set("Cancellation requested...") messagebox.showinfo("Cancel", "Cancellation requested. The current operation will try to stop.") - # A simple way to try and stop (if converter supports it): - # You would need to build a way to signal the converter thread - # to find and kill the ffmpeg subprocess. - # For now, we just update the UI. self.convert_button.configure(state="normal") self.cancel_button.configure(state="disabled") @@ -271,4 +255,4 @@ class VideoConverterGUI: if __name__ == '__main__': root = customtkinter.CTk() gui = VideoConverterGUI(root) - root.mainloop() + root.mainloop() \ No newline at end of file