add format, title
This commit is contained in:
parent
e3fbb24ef4
commit
6f0ca29327
@ -1,4 +1,3 @@
|
|||||||
|
|
||||||
import logging
|
import logging
|
||||||
import traceback
|
import traceback
|
||||||
from threading import Thread
|
from threading import Thread
|
||||||
@ -36,7 +35,7 @@ class Downloader:
|
|||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.progress_callback: Optional[Callable[[int], None]] = None
|
self.progress_callback: Optional[Callable[[int], None]] = None
|
||||||
self.completion_callback: Optional[Callable[[Optional[str]], None]] = None
|
self.completion_callback: Optional[Callable[[Optional[str]], None]] = None
|
||||||
self.formats_callback: Optional[Callable[[Optional[List[VideoFormat]]], None]] = None
|
self.formats_callback: Optional[Callable[[Optional[Dict[str, Any]], Optional[List[VideoFormat]]], None]] = None
|
||||||
|
|
||||||
def _progress_hook(self, d: Dict[str, Any]):
|
def _progress_hook(self, d: Dict[str, Any]):
|
||||||
if d["status"] == "downloading":
|
if d["status"] == "downloading":
|
||||||
@ -48,16 +47,21 @@ class Downloader:
|
|||||||
logger.info("yt-dlp finished downloading.")
|
logger.info("yt-dlp finished downloading.")
|
||||||
|
|
||||||
def _get_formats_task(self, url: str):
|
def _get_formats_task(self, url: str):
|
||||||
"""Task to fetch video formats in a thread."""
|
"""Task to fetch video formats and info in a thread."""
|
||||||
try:
|
try:
|
||||||
logger.info(f"Fetching formats for URL: {url}")
|
logger.info(f"Fetching formats for URL: {url}")
|
||||||
ydl_opts = {"noplaylist": True}
|
ydl_opts = {"noplaylist": True}
|
||||||
with yt_dlp.YoutubeDL(ydl_opts) as ydl:
|
with yt_dlp.YoutubeDL(ydl_opts) as ydl:
|
||||||
info = ydl.extract_info(url, download=False)
|
info = ydl.extract_info(url, download=False)
|
||||||
|
|
||||||
|
video_info = {
|
||||||
|
"title": info.get("title", "N/A"),
|
||||||
|
"uploader": info.get("uploader", "N/A"),
|
||||||
|
"duration_string": info.get("duration_string", "N/A"),
|
||||||
|
}
|
||||||
|
|
||||||
formats = []
|
formats = []
|
||||||
for f in info.get("formats", []):
|
for f in info.get("formats", []):
|
||||||
# We are interested in mp4 files that have video
|
|
||||||
if f.get("vcodec", "none") != "none" and f.get("ext") == "mp4":
|
if f.get("vcodec", "none") != "none" and f.get("ext") == "mp4":
|
||||||
note = "Progressive" if f.get("acodec", "none") != "none" else "Video Only"
|
note = "Progressive" if f.get("acodec", "none") != "none" else "Video Only"
|
||||||
formats.append(VideoFormat(
|
formats.append(VideoFormat(
|
||||||
@ -68,21 +72,20 @@ class Downloader:
|
|||||||
note=note
|
note=note
|
||||||
))
|
))
|
||||||
|
|
||||||
# Sort formats: progressive first, then by resolution
|
|
||||||
formats.sort(key=lambda x: (x.note != "Progressive", -int(x.resolution.replace("p", "")) if x.resolution.replace("p", "").isdigit() else 0))
|
formats.sort(key=lambda x: (x.note != "Progressive", -int(x.resolution.replace("p", "")) if x.resolution.replace("p", "").isdigit() else 0))
|
||||||
|
|
||||||
logger.info(f"Found {len(formats)} suitable formats.")
|
logger.info(f"Found {len(formats)} suitable formats.")
|
||||||
if self.formats_callback:
|
if self.formats_callback:
|
||||||
self.formats_callback(formats)
|
self.formats_callback(video_info, formats)
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
error_message = f"Failed to fetch formats: {e}"
|
error_message = f"Failed to fetch formats: {e}"
|
||||||
logger.error(error_message)
|
logger.error(error_message)
|
||||||
logger.debug(traceback.format_exc())
|
logger.debug(traceback.format_exc())
|
||||||
if self.formats_callback:
|
if self.formats_callback:
|
||||||
self.formats_callback(None) # Indicate failure
|
self.formats_callback(None, None) # Indicate failure
|
||||||
|
|
||||||
def get_video_formats(self, url: str, formats_callback: Callable[[Optional[List[VideoFormat]]], None]):
|
def get_video_formats(self, url: str, formats_callback: Callable[[Optional[Dict[str, Any]], Optional[List[VideoFormat]]], None]):
|
||||||
"""Starts the format fetching process in a new thread."""
|
"""Starts the format fetching process in a new thread."""
|
||||||
self.formats_callback = formats_callback
|
self.formats_callback = formats_callback
|
||||||
thread = Thread(target=self._get_formats_task, args=(url,), daemon=True)
|
thread = Thread(target=self._get_formats_task, args=(url,), daemon=True)
|
||||||
@ -128,4 +131,4 @@ class Downloader:
|
|||||||
thread = Thread(
|
thread = Thread(
|
||||||
target=self._download_task, args=(url, download_path, format_id), daemon=True
|
target=self._download_task, args=(url, download_path, format_id), daemon=True
|
||||||
)
|
)
|
||||||
thread.start()
|
thread.start()
|
||||||
@ -1,8 +1,9 @@
|
|||||||
|
|
||||||
import logging
|
import logging
|
||||||
import tkinter as tk
|
import tkinter as tk
|
||||||
from tkinter import ttk, filedialog, messagebox
|
from tkinter import ttk, filedialog, messagebox
|
||||||
from tkinter.scrolledtext import ScrolledText
|
from tkinter.scrolledtext import ScrolledText
|
||||||
from typing import Optional, List
|
from typing import Optional, List, Dict, Any
|
||||||
|
|
||||||
from downloaderyoutube.core.core import Downloader, VideoFormat
|
from downloaderyoutube.core.core import Downloader, VideoFormat
|
||||||
from downloaderyoutube.utils.logger import add_tkinter_handler, get_logger
|
from downloaderyoutube.utils.logger import add_tkinter_handler, get_logger
|
||||||
@ -35,8 +36,13 @@ class App(tk.Frame):
|
|||||||
self.download_path: Optional[str] = None
|
self.download_path: Optional[str] = None
|
||||||
self.available_formats: List[VideoFormat] = []
|
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.title("YouTube Downloader")
|
||||||
self.master.geometry("800x600")
|
self.master.geometry("800x650") # Increased height for new info panel
|
||||||
|
|
||||||
self.pack(fill=tk.BOTH, expand=True, padx=10, pady=10)
|
self.pack(fill=tk.BOTH, expand=True, padx=10, pady=10)
|
||||||
|
|
||||||
@ -57,6 +63,14 @@ class App(tk.Frame):
|
|||||||
)
|
)
|
||||||
self.analyze_button.pack(side=tk.LEFT, padx=(5, 0))
|
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 ---
|
||||||
path_frame = ttk.Frame(self)
|
path_frame = ttk.Frame(self)
|
||||||
path_frame.pack(fill=tk.X, pady=5)
|
path_frame.pack(fill=tk.X, pady=5)
|
||||||
@ -120,25 +134,35 @@ class App(tk.Frame):
|
|||||||
self.analyze_button.config(state="disabled")
|
self.analyze_button.config(state="disabled")
|
||||||
self.download_button.config(state="disabled")
|
self.download_button.config(state="disabled")
|
||||||
self.format_combobox.set("Analisi in corso...")
|
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)
|
self.downloader.get_video_formats(url, self._on_formats_received)
|
||||||
|
|
||||||
def _on_formats_received(self, formats: Optional[List[VideoFormat]]):
|
def _on_formats_received(self, video_info: Optional[Dict[str, Any]], formats: Optional[List[VideoFormat]]):
|
||||||
# This is called from a different thread, so we schedule GUI updates
|
self.master.after(0, self._update_ui_after_analysis, video_info, formats)
|
||||||
self.master.after(0, self._update_formats_combobox, formats)
|
|
||||||
|
|
||||||
def _update_formats_combobox(self, formats: Optional[List[VideoFormat]]):
|
def _update_ui_after_analysis(self, video_info: Optional[Dict[str, Any]], formats: Optional[List[VideoFormat]]):
|
||||||
self.analyze_button.config(state="normal")
|
self.analyze_button.config(state="normal")
|
||||||
if formats:
|
if video_info and formats:
|
||||||
logger.info("Successfully fetched formats. Populating combobox.")
|
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.available_formats = formats
|
||||||
self.format_combobox["values"] = [str(f) for f in formats]
|
self.format_combobox["values"] = [str(f) for f in formats]
|
||||||
self.format_combobox.current(0)
|
self.format_combobox.current(0)
|
||||||
|
self.format_combobox.config(state="readonly")
|
||||||
self.download_button.config(state="normal")
|
self.download_button.config(state="normal")
|
||||||
else:
|
else:
|
||||||
logger.error("Failed to fetch video formats.")
|
logger.error("Failed to fetch video info or formats.")
|
||||||
messagebox.showerror("Error", "Could not retrieve video formats. Check the URL and logs.")
|
messagebox.showerror("Error", "Could not retrieve video information. Check the URL and logs.")
|
||||||
self.format_combobox.set("Analisi fallita.")
|
self.format_combobox.set("Analisi fallita.")
|
||||||
self.format_combobox["values"] = []
|
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):
|
def start_download(self):
|
||||||
url = self.url_entry.get()
|
url = self.url_entry.get()
|
||||||
@ -180,4 +204,4 @@ class App(tk.Frame):
|
|||||||
if error_message:
|
if error_message:
|
||||||
messagebox.showerror("Download Failed", error_message)
|
messagebox.showerror("Download Failed", error_message)
|
||||||
else:
|
else:
|
||||||
messagebox.showinfo("Success", "Video downloaded successfully!")
|
messagebox.showinfo("Success", "Video downloaded successfully!")
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user