208 lines
8.3 KiB
Python
208 lines
8.3 KiB
Python
|
|
import logging
|
|
import tkinter as tk
|
|
from tkinter import ttk, filedialog, messagebox
|
|
from tkinter.scrolledtext import ScrolledText
|
|
from typing import Optional, List, Dict, Any
|
|
|
|
from downloaderyoutube.core.core import Downloader, VideoFormat
|
|
from downloaderyoutube.utils.logger import add_tkinter_handler, get_logger
|
|
|
|
# Get a logger instance for this module
|
|
logger = get_logger(__name__)
|
|
|
|
# A basic logging config dictionary for the Tkinter handler
|
|
LOGGING_CONFIG = {
|
|
"colors": {
|
|
logging.DEBUG: "gray",
|
|
logging.INFO: "black",
|
|
logging.WARNING: "orange",
|
|
logging.ERROR: "red",
|
|
logging.CRITICAL: "red",
|
|
},
|
|
"queue_poll_interval_ms": 100,
|
|
}
|
|
|
|
|
|
class App(tk.Frame):
|
|
"""
|
|
The main graphical user interface for the Downloader application.
|
|
"""
|
|
|
|
def __init__(self, master: tk.Tk):
|
|
super().__init__(master)
|
|
self.master = master
|
|
self.downloader = Downloader()
|
|
self.download_path: Optional[str] = None
|
|
self.available_formats: List[VideoFormat] = []
|
|
|
|
# StringVars for video info labels
|
|
self.title_var = tk.StringVar(value="Titolo: N/A")
|
|
self.uploader_var = tk.StringVar(value="Autore: N/A")
|
|
self.duration_var = tk.StringVar(value="Durata: N/A")
|
|
|
|
self.master.title("YouTube Downloader")
|
|
self.master.geometry("800x650") # Increased height for new info panel
|
|
|
|
self.pack(fill=tk.BOTH, expand=True, padx=10, pady=10)
|
|
|
|
self._create_widgets()
|
|
self._setup_logging_handler()
|
|
|
|
def _create_widgets(self):
|
|
"""Creates and lays out the widgets in the application window."""
|
|
# --- Top Frame for URL Input ---
|
|
url_frame = ttk.Frame(self)
|
|
url_frame.pack(fill=tk.X, pady=(0, 5))
|
|
|
|
ttk.Label(url_frame, text="Video URL:").pack(side=tk.LEFT, padx=(0, 5))
|
|
self.url_entry = ttk.Entry(url_frame)
|
|
self.url_entry.pack(side=tk.LEFT, fill=tk.X, expand=True, padx=5)
|
|
self.analyze_button = ttk.Button(
|
|
url_frame, text="Analizza URL", command=self._analyze_url
|
|
)
|
|
self.analyze_button.pack(side=tk.LEFT, padx=(5, 0))
|
|
|
|
# --- Video Info Panel ---
|
|
info_frame = ttk.LabelFrame(self, text="Informazioni Video")
|
|
info_frame.pack(fill=tk.X, pady=5, padx=2)
|
|
|
|
ttk.Label(info_frame, textvariable=self.title_var).pack(anchor="w", padx=5)
|
|
ttk.Label(info_frame, textvariable=self.uploader_var).pack(anchor="w", padx=5)
|
|
ttk.Label(info_frame, textvariable=self.duration_var).pack(anchor="w", padx=5)
|
|
|
|
# --- Path Frame ---
|
|
path_frame = ttk.Frame(self)
|
|
path_frame.pack(fill=tk.X, pady=5)
|
|
|
|
self.path_button = ttk.Button(
|
|
path_frame, text="Choose Folder...", command=self.select_download_path
|
|
)
|
|
self.path_button.pack(side=tk.LEFT)
|
|
self.path_label = ttk.Label(path_frame, text="No download folder selected.")
|
|
self.path_label.pack(side=tk.LEFT, fill=tk.X, expand=True, padx=10)
|
|
|
|
# --- Format Selection Frame ---
|
|
format_frame = ttk.Frame(self)
|
|
format_frame.pack(fill=tk.X, pady=5)
|
|
|
|
ttk.Label(format_frame, text="Formato:").pack(side=tk.LEFT, padx=(0, 5))
|
|
self.format_combobox = ttk.Combobox(format_frame, state="readonly")
|
|
self.format_combobox.pack(side=tk.LEFT, fill=tk.X, expand=True, padx=5)
|
|
self.download_button = ttk.Button(
|
|
format_frame, text="Download", command=self.start_download, state="disabled"
|
|
)
|
|
self.download_button.pack(side=tk.LEFT, padx=(5, 0))
|
|
|
|
# --- Progress Bar ---
|
|
self.progress_bar = ttk.Progressbar(
|
|
self, orient="horizontal", length=100, mode="determinate"
|
|
)
|
|
self.progress_bar.pack(fill=tk.X, pady=(10, 5))
|
|
|
|
# --- Log Viewer ---
|
|
log_frame = ttk.LabelFrame(self, text="Log")
|
|
log_frame.pack(fill=tk.BOTH, expand=True)
|
|
self.log_widget = ScrolledText(
|
|
log_frame, state=tk.DISABLED, wrap=tk.WORD, font=("Courier New", 9)
|
|
)
|
|
self.log_widget.pack(fill=tk.BOTH, expand=True, padx=5, pady=5)
|
|
|
|
def _setup_logging_handler(self):
|
|
logger.info("Setting up GUI logging handler.")
|
|
add_tkinter_handler(
|
|
gui_log_widget=self.log_widget,
|
|
root_tk_instance_for_gui_handler=self.master,
|
|
logging_config_dict=LOGGING_CONFIG,
|
|
)
|
|
logger.info("GUI logging handler configured.")
|
|
|
|
def select_download_path(self):
|
|
path = filedialog.askdirectory()
|
|
if path:
|
|
self.download_path = path
|
|
self.path_label.config(text=f"Saving to: {self.download_path}")
|
|
logger.info(f"Download path set to: {self.download_path}")
|
|
|
|
def _analyze_url(self):
|
|
url = self.url_entry.get()
|
|
if not url:
|
|
messagebox.showerror("Error", "Please enter a YouTube URL.")
|
|
return
|
|
|
|
logger.info(f"Analyze button clicked. Fetching formats for {url}")
|
|
self.analyze_button.config(state="disabled")
|
|
self.download_button.config(state="disabled")
|
|
self.format_combobox.set("Analisi in corso...")
|
|
self.title_var.set("Titolo: Analisi in corso...")
|
|
self.uploader_var.set("Autore: Analisi in corso...")
|
|
self.duration_var.set("Durata: Analisi in corso...")
|
|
self.downloader.get_video_formats(url, self._on_formats_received)
|
|
|
|
def _on_formats_received(self, video_info: Optional[Dict[str, Any]], formats: Optional[List[VideoFormat]]):
|
|
self.master.after(0, self._update_ui_after_analysis, video_info, formats)
|
|
|
|
def _update_ui_after_analysis(self, video_info: Optional[Dict[str, Any]], formats: Optional[List[VideoFormat]]):
|
|
self.analyze_button.config(state="normal")
|
|
if video_info and formats:
|
|
logger.info("Successfully fetched video info and formats.")
|
|
self.title_var.set(f"Titolo: {video_info['title']}")
|
|
self.uploader_var.set(f"Autore: {video_info['uploader']}")
|
|
self.duration_var.set(f"Durata: {video_info['duration_string']}")
|
|
|
|
self.available_formats = formats
|
|
self.format_combobox["values"] = [str(f) for f in formats]
|
|
self.format_combobox.current(0)
|
|
self.format_combobox.config(state="readonly")
|
|
self.download_button.config(state="normal")
|
|
else:
|
|
logger.error("Failed to fetch video info or formats.")
|
|
messagebox.showerror("Error", "Could not retrieve video information. Check the URL and logs.")
|
|
self.format_combobox.set("Analisi fallita.")
|
|
self.format_combobox["values"] = []
|
|
self.title_var.set("Titolo: N/A")
|
|
self.uploader_var.set("Autore: N/A")
|
|
self.duration_var.set("Durata: N/A")
|
|
|
|
def start_download(self):
|
|
url = self.url_entry.get()
|
|
if not self.download_path:
|
|
messagebox.showerror("Error", "Please select a download folder.")
|
|
return
|
|
|
|
selected_index = self.format_combobox.current()
|
|
if selected_index < 0:
|
|
messagebox.showerror("Error", "Please select a format to download.")
|
|
return
|
|
|
|
selected_format = self.available_formats[selected_index]
|
|
logger.info(f"Download button clicked for format: {selected_format.format_id}")
|
|
|
|
self.download_button.config(state="disabled")
|
|
self.analyze_button.config(state="disabled")
|
|
self.progress_bar["value"] = 0
|
|
|
|
self.downloader.download_video(
|
|
url=url,
|
|
download_path=self.download_path,
|
|
format_id=selected_format.format_id,
|
|
progress_callback=self._on_download_progress,
|
|
completion_callback=self._on_download_complete,
|
|
)
|
|
|
|
def _on_download_progress(self, percentage: int):
|
|
self.master.after(0, self.progress_bar.config, {"value": percentage})
|
|
|
|
def _on_download_complete(self, error_message: Optional[str]):
|
|
self.master.after(0, self._finalize_download, error_message)
|
|
|
|
def _finalize_download(self, error_message: Optional[str]):
|
|
self.progress_bar["value"] = 0
|
|
self.download_button.config(state="normal")
|
|
self.analyze_button.config(state="normal")
|
|
|
|
if error_message:
|
|
messagebox.showerror("Download Failed", error_message)
|
|
else:
|
|
messagebox.showinfo("Success", "Video downloaded successfully!")
|