Files
video-converter/gui.py

187 lines
7.5 KiB
Python

import customtkinter
import tkinter as tk
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()