add version, change into english

This commit is contained in:
VALLONGOL 2025-09-15 14:20:35 +02:00
parent ee123b93d4
commit ea7968da4a
2 changed files with 149 additions and 32 deletions

View File

@ -0,0 +1,74 @@
# -*- coding: utf-8 -*-
# File generated by PyInstaller GUI Wrapper. DO NOT EDIT MANUALLY.
# Contains build-time information scraped from Git (if available)
# and a helper function to format version strings.
import re
# --- Version Data (Generated) ---
__version__ = "v.0.0.0.2-0-gd985bc9-dirty"
GIT_COMMIT_HASH = "d985bc91cd936473742143d3532f603a59d2e395"
GIT_BRANCH = "master"
BUILD_TIMESTAMP = "2025-09-15T11:47:35.974283+00:00"
IS_GIT_REPO = True
# --- Default Values (for comparison or fallback) ---
DEFAULT_VERSION = "0.0.0+unknown"
DEFAULT_COMMIT = "Unknown"
DEFAULT_BRANCH = "Unknown"
# --- Helper Function ---
def get_version_string(format_string=None):
"""
Returns a formatted string based on the build version information.
Args:
format_string (str, optional): A format string using placeholders.
Defaults to "{version} ({branch}/{commit_short})" if None.
Placeholders:
{{version}}: Full version string (e.g., 'v1.0.0-5-gabcdef-dirty')
{{tag}}: Clean tag part if exists (e.g., 'v1.0.0'), else DEFAULT_VERSION.
{{commit}}: Full Git commit hash.
{{commit_short}}: Short Git commit hash (7 chars).
{{branch}}: Git branch name.
{{dirty}}: '-dirty' if the repo was dirty, empty otherwise.
{{timestamp}}: Full build timestamp (ISO 8601 UTC).
{{timestamp_short}}: Build date only (YYYY-MM-DD).
{{is_git}}: 'Git' if IS_GIT_REPO is True, 'Unknown' otherwise.
Returns:
str: The formatted version string, or an error message if formatting fails.
"""
if format_string is None:
format_string = "{version} ({branch}/{commit_short})" # Default format
replacements = {}
try:
replacements['version'] = __version__ if __version__ else DEFAULT_VERSION
replacements['commit'] = GIT_COMMIT_HASH if GIT_COMMIT_HASH else DEFAULT_COMMIT
replacements['commit_short'] = GIT_COMMIT_HASH[:7] if GIT_COMMIT_HASH and len(GIT_COMMIT_HASH) >= 7 else DEFAULT_COMMIT
replacements['branch'] = GIT_BRANCH if GIT_BRANCH else DEFAULT_BRANCH
replacements['timestamp'] = BUILD_TIMESTAMP if BUILD_TIMESTAMP else "Unknown"
replacements['timestamp_short'] = BUILD_TIMESTAMP.split('T')[0] if BUILD_TIMESTAMP and 'T' in BUILD_TIMESTAMP else "Unknown"
replacements['is_git'] = "Git" if IS_GIT_REPO else "Unknown"
replacements['dirty'] = "-dirty" if __version__ and __version__.endswith('-dirty') else ""
tag = DEFAULT_VERSION
if __version__ and IS_GIT_REPO:
match = re.match(r'^(v?([0-9]+(?:\.[0-9]+)*))', __version__)
if match:
tag = match.group(1)
replacements['tag'] = tag
output_string = format_string
for placeholder, value in replacements.items():
pattern = re.compile(r'{{\s*' + re.escape(placeholder) + r'\s*}}')
output_string = pattern.sub(str(value), output_string)
if re.search(r'{\s*\w+\s*}', output_string):
pass # Or log a warning: print(f"Warning: Unreplaced placeholders found: {output_string}")
return output_string
except Exception as e:
return f"[Formatting Error: {e}]"

View File

@ -1,10 +1,11 @@
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, Dict, Any from typing import Optional, List, Dict, Any
import os import os
import subprocess
import sys
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
@ -20,6 +21,27 @@ LOGGING_CONFIG = {
"queue_poll_interval_ms": 100, "queue_poll_interval_ms": 100,
} }
# --- Import Version Info FOR THE WRAPPER ITSELF ---
try:
# Use absolute import based on package name
from downloaderyoutube import _version as wrapper_version
WRAPPER_APP_VERSION_STRING = f"{wrapper_version.__version__} ({wrapper_version.GIT_BRANCH}/{wrapper_version.GIT_COMMIT_HASH[:7]})"
WRAPPER_BUILD_INFO = f"Wrapper Built: {wrapper_version.BUILD_TIMESTAMP}"
except ImportError:
# This might happen if you run the wrapper directly from source
# without generating its _version.py first (if you use that approach for the wrapper itself)
WRAPPER_APP_VERSION_STRING = "(Dev Wrapper)"
WRAPPER_BUILD_INFO = "Wrapper build time unknown"
# --- End Import Version Info ---
# --- Constants for Version Generation ---
DEFAULT_VERSION = "0.0.0+unknown"
DEFAULT_COMMIT = "Unknown"
DEFAULT_BRANCH = "Unknown"
# --- End Constants ---
class App(tk.Frame): class App(tk.Frame):
def __init__(self, master: tk.Tk): def __init__(self, master: tk.Tk):
super().__init__(master) super().__init__(master)
@ -29,11 +51,11 @@ class App(tk.Frame):
self.available_formats: List[VideoFormat] = [] self.available_formats: List[VideoFormat] = []
self.url_to_tree_item: Dict[str, str] = {} self.url_to_tree_item: Dict[str, str] = {}
self.title_var = tk.StringVar(value="Titolo: N/A") self.title_var = tk.StringVar(value="Title: N/A")
self.uploader_var = tk.StringVar(value="Autore: N/A") self.uploader_var = tk.StringVar(value="Author: N/A")
self.duration_var = tk.StringVar(value="Durata: N/A") self.duration_var = tk.StringVar(value="Duration: N/A")
self.master.title("YouTube Downloader") self.master.title(f"YouTube Downloader- {WRAPPER_APP_VERSION_STRING}")
self.master.geometry("800x750") self.master.geometry("800x750")
self.pack(fill=tk.BOTH, expand=True, padx=5, pady=5) self.pack(fill=tk.BOTH, expand=True, padx=5, pady=5)
@ -47,8 +69,8 @@ class App(tk.Frame):
single_tab = ttk.Frame(self.notebook, padding=10) single_tab = ttk.Frame(self.notebook, padding=10)
batch_tab = ttk.Frame(self.notebook, padding=10) batch_tab = ttk.Frame(self.notebook, padding=10)
self.notebook.add(single_tab, text="Download Singolo") self.notebook.add(single_tab, text="Single Download")
self.notebook.add(batch_tab, text="Download Multiplo") self.notebook.add(batch_tab, text="Batch Download")
self._create_single_download_tab(single_tab) self._create_single_download_tab(single_tab)
self._create_batch_download_tab(batch_tab) self._create_batch_download_tab(batch_tab)
@ -60,6 +82,10 @@ class App(tk.Frame):
path_frame.pack(fill=tk.X, pady=5) 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 = ttk.Button(path_frame, text="Choose Folder...", command=self.select_download_path)
self.path_button.pack(side=tk.LEFT) self.path_button.pack(side=tk.LEFT)
self.open_folder_button = ttk.Button(path_frame, text="Open", command=self.open_download_folder, state="disabled")
self.open_folder_button.pack(side=tk.RIGHT)
self.path_label = ttk.Label(path_frame, text="No download folder selected.") 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) self.path_label.pack(side=tk.LEFT, fill=tk.X, expand=True, padx=10)
@ -77,10 +103,10 @@ class App(tk.Frame):
ttk.Label(url_frame, text="Video URL:").pack(side=tk.LEFT) ttk.Label(url_frame, text="Video URL:").pack(side=tk.LEFT)
self.url_entry = ttk.Entry(url_frame) self.url_entry = ttk.Entry(url_frame)
self.url_entry.pack(side=tk.LEFT, fill=tk.X, expand=True, padx=5) 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 = ttk.Button(url_frame, text="Analyze URL", command=self._analyze_url)
self.analyze_button.pack(side=tk.LEFT) self.analyze_button.pack(side=tk.LEFT)
info_frame = ttk.LabelFrame(tab, text="Informazioni Video") info_frame = ttk.LabelFrame(tab, text="Video Information")
info_frame.pack(fill=tk.X, pady=5, expand=True) info_frame.pack(fill=tk.X, pady=5, expand=True)
ttk.Label(info_frame, textvariable=self.title_var).pack(anchor="w", padx=5) 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.uploader_var).pack(anchor="w", padx=5)
@ -88,7 +114,7 @@ class App(tk.Frame):
format_frame = ttk.Frame(tab) format_frame = ttk.Frame(tab)
format_frame.pack(fill=tk.X, pady=5) format_frame.pack(fill=tk.X, pady=5)
ttk.Label(format_frame, text="Formato:").pack(side=tk.LEFT) ttk.Label(format_frame, text="Format:").pack(side=tk.LEFT)
self.format_combobox = ttk.Combobox(format_frame, state="disabled") self.format_combobox = ttk.Combobox(format_frame, state="disabled")
self.format_combobox.pack(side=tk.LEFT, fill=tk.X, expand=True, padx=5) 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_single_download, state="disabled") self.download_button = ttk.Button(format_frame, text="Download", command=self.start_single_download, state="disabled")
@ -100,16 +126,16 @@ class App(tk.Frame):
ttk.Label(add_url_frame, text="URL:").pack(side=tk.LEFT) ttk.Label(add_url_frame, text="URL:").pack(side=tk.LEFT)
self.batch_url_entry = ttk.Entry(add_url_frame) self.batch_url_entry = ttk.Entry(add_url_frame)
self.batch_url_entry.pack(side=tk.LEFT, fill=tk.X, expand=True, padx=5) self.batch_url_entry.pack(side=tk.LEFT, fill=tk.X, expand=True, padx=5)
self.add_url_button = ttk.Button(add_url_frame, text="Aggiungi", command=self._add_url_to_tree) self.add_url_button = ttk.Button(add_url_frame, text="Add", command=self._add_url_to_tree)
self.add_url_button.pack(side=tk.LEFT) self.add_url_button.pack(side=tk.LEFT)
tree_frame = ttk.Frame(tab) tree_frame = ttk.Frame(tab)
tree_frame.pack(fill=tk.BOTH, expand=True, pady=5) tree_frame.pack(fill=tk.BOTH, expand=True, pady=5)
self.batch_tree = ttk.Treeview(tree_frame, columns=("URL", "Stato"), show="headings") self.batch_tree = ttk.Treeview(tree_frame, columns=("URL", "Status"), show="headings")
self.batch_tree.heading("URL", text="URL") self.batch_tree.heading("URL", text="URL")
self.batch_tree.heading("Stato", text="Stato") self.batch_tree.heading("Status", text="Status")
self.batch_tree.column("URL", width=400) self.batch_tree.column("URL", width=400)
self.batch_tree.column("Stato", width=100, anchor="center") self.batch_tree.column("Status", width=100, anchor="center")
self.batch_tree.pack(side=tk.LEFT, fill=tk.BOTH, expand=True) self.batch_tree.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
tree_scrollbar = ttk.Scrollbar(tree_frame, orient="vertical", command=self.batch_tree.yview) tree_scrollbar = ttk.Scrollbar(tree_frame, orient="vertical", command=self.batch_tree.yview)
tree_scrollbar.pack(side=tk.RIGHT, fill="y") tree_scrollbar.pack(side=tk.RIGHT, fill="y")
@ -121,18 +147,18 @@ class App(tk.Frame):
controls_frame = ttk.Frame(tab) controls_frame = ttk.Frame(tab)
controls_frame.pack(fill=tk.X, pady=5) controls_frame.pack(fill=tk.X, pady=5)
ttk.Label(controls_frame, text="Profilo Qualità:").pack(side=tk.LEFT) ttk.Label(controls_frame, text="Quality Profile:").pack(side=tk.LEFT)
self.quality_profile_combobox = ttk.Combobox(controls_frame, state="readonly", values=["Qualità Massima (1080p+, richiede FFmpeg)", "Alta Qualità (720p)", "Qualità Media (480p)"]) self.quality_profile_combobox = ttk.Combobox(controls_frame, state="readonly", values=['''Maximum Quality (1080p+, requires FFmpeg)[''', '''High Quality (720p)[''', '''Medium Quality (480p)['''])
self.quality_profile_combobox.current(1) self.quality_profile_combobox.current(1)
self.quality_profile_combobox.pack(side=tk.LEFT, fill=tk.X, expand=True, padx=5) self.quality_profile_combobox.pack(side=tk.LEFT, fill=tk.X, expand=True, padx=5)
self.batch_download_button = ttk.Button(controls_frame, text="Scarica Tutto", command=self.start_batch_download) self.batch_download_button = ttk.Button(controls_frame, text="Download All", command=self.start_batch_download)
self.batch_download_button.pack(side=tk.LEFT) self.batch_download_button.pack(side=tk.LEFT)
def _add_url_to_tree(self): def _add_url_to_tree(self):
url = self.batch_url_entry.get().strip() url = self.batch_url_entry.get().strip()
if not url: return if not url: return
if url not in self.url_to_tree_item: if url not in self.url_to_tree_item:
item_id = self.batch_tree.insert("", tk.END, values=(url, "In attesa")) item_id = self.batch_tree.insert("", tk.END, values=(url, "Waiting"))
self.url_to_tree_item[url] = item_id self.url_to_tree_item[url] = item_id
self.batch_url_entry.delete(0, tk.END) self.batch_url_entry.delete(0, tk.END)
@ -146,6 +172,7 @@ class App(tk.Frame):
if os.path.isdir(path): if os.path.isdir(path):
self.download_path = path self.download_path = path
self.path_label.config(text=f"Saving to: {self.download_path}") self.path_label.config(text=f"Saving to: {self.download_path}")
self.open_folder_button.config(state="normal")
def save_last_path(self): def save_last_path(self):
if self.download_path: if self.download_path:
@ -156,6 +183,21 @@ class App(tk.Frame):
if path: if path:
self.download_path = path self.download_path = path
self.path_label.config(text=f"Saving to: {self.download_path}") self.path_label.config(text=f"Saving to: {self.download_path}")
self.open_folder_button.config(state="normal")
def open_download_folder(self):
if not self.download_path:
return
try:
if sys.platform == "win32":
os.startfile(self.download_path)
elif sys.platform == "darwin": # macOS
subprocess.run(["open", self.download_path])
else: # Linux and other UNIX-like
subprocess.run(["xdg-open", self.download_path])
except Exception as e:
logger.error(f"Failed to open folder: {e}")
messagebox.showerror("Error", f"Could not open the folder: {self.download_path}")
def _set_ui_state(self, enabled: bool): def _set_ui_state(self, enabled: bool):
state = "normal" if enabled else "disabled" state = "normal" if enabled else "disabled"
@ -164,6 +206,7 @@ class App(tk.Frame):
self.batch_download_button.config(state=state) self.batch_download_button.config(state=state)
self.add_url_button.config(state=state) self.add_url_button.config(state=state)
self.path_button.config(state=state) self.path_button.config(state=state)
self.open_folder_button.config(state=state if self.download_path and enabled else "disabled")
for entry in [self.url_entry, self.batch_url_entry]: for entry in [self.url_entry, self.batch_url_entry]:
entry.config(state="normal" if enabled else "disabled") entry.config(state="normal" if enabled else "disabled")
self.quality_profile_combobox.config(state="readonly" if enabled else "disabled") self.quality_profile_combobox.config(state="readonly" if enabled else "disabled")
@ -172,9 +215,9 @@ class App(tk.Frame):
logger.info("Resetting forms to initial state.") logger.info("Resetting forms to initial state.")
self.progress_bar["value"] = 0 self.progress_bar["value"] = 0
self.url_entry.delete(0, tk.END) self.url_entry.delete(0, tk.END)
self.title_var.set("Titolo: N/A") self.title_var.set("Title: N/A")
self.uploader_var.set("Autore: N/A") self.uploader_var.set("Author: N/A")
self.duration_var.set("Durata: N/A") self.duration_var.set("Duration: N/A")
self.available_formats = [] self.available_formats = []
self.format_combobox["values"] = [] self.format_combobox["values"] = []
self.format_combobox.set("") self.format_combobox.set("")
@ -186,7 +229,7 @@ class App(tk.Frame):
url = self.url_entry.get().strip() url = self.url_entry.get().strip()
if not url: return if not url: return
self._set_ui_state(False) self._set_ui_state(False)
self.format_combobox.set("Analisi in corso...") self.format_combobox.set("Analyzing...")
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, video_info, formats): def _on_formats_received(self, video_info, formats):
@ -194,16 +237,16 @@ class App(tk.Frame):
def _update_ui_after_analysis(self, video_info, formats): def _update_ui_after_analysis(self, video_info, formats):
if video_info and formats: if video_info and formats:
self.title_var.set(f"Titolo: {video_info['title']}") self.title_var.set(f"Title: {video_info['title']}")
self.uploader_var.set(f"Autore: {video_info['uploader']}") self.uploader_var.set(f"Author: {video_info['uploader']}")
self.duration_var.set(f"Durata: {video_info['duration_string']}") self.duration_var.set(f"Duration: {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.format_combobox.config(state="readonly")
else: else:
messagebox.showerror("Error", "Could not retrieve video information.") messagebox.showerror("Error", "Could not retrieve video information.")
self.format_combobox.set("Analisi fallita.") self.format_combobox.set("Analysis failed.")
# ALWAYS update UI state at the end # ALWAYS update UI state at the end
self._set_ui_state(True) self._set_ui_state(True)
@ -226,28 +269,28 @@ class App(tk.Frame):
self.downloader.download_batch(urls, self.download_path, quality, self._on_download_progress, self._on_batch_complete, self._on_video_started, self._on_video_completed, self._on_video_error) self.downloader.download_batch(urls, self.download_path, quality, self._on_download_progress, self._on_batch_complete, self._on_video_started, self._on_video_completed, self._on_video_error)
def _on_video_started(self, url): def _on_video_started(self, url):
self.master.after(0, self._update_batch_item, url, "In corso...", "downloading") self.master.after(0, self._update_batch_item, url, "Downloading...", "downloading")
def _on_video_completed(self, url): def _on_video_completed(self, url):
self.master.after(0, self._update_batch_item, url, "Completato", "completed") self.master.after(0, self._update_batch_item, url, "Completed", "completed")
def _on_video_error(self, url, error): def _on_video_error(self, url, error):
self.master.after(0, self._update_batch_item, url, "Errore", "error") self.master.after(0, self._update_batch_item, url, "Error", "error")
def _update_batch_item(self, url, status, tag): def _update_batch_item(self, url, status, tag):
if url in self.url_to_tree_item: if url in self.url_to_tree_item:
item_id = self.url_to_tree_item[url] item_id = self.url_to_tree_item[url]
self.batch_tree.set(item_id, "Stato", status) self.batch_tree.set(item_id, "Status", status)
self.batch_tree.item(item_id, tags=(tag,)) self.batch_tree.item(item_id, tags=(tag,))
def _on_download_progress(self, percentage: int): def _on_download_progress(self, percentage: int):
self.master.after(0, self.progress_bar.config, {"value": percentage}) self.master.after(0, self.progress_bar.config, {"value": percentage})
def _on_download_complete(self, error_message: Optional[str]): def _on_download_complete(self, error_message: Optional[str]):
self.master.after(0, self._finalize_download, error_message, "Download completato con successo!") self.master.after(0, self._finalize_download, error_message, "Download completed successfully!")
def _on_batch_complete(self, error_message: Optional[str]): def _on_batch_complete(self, error_message: Optional[str]):
self.master.after(0, self._finalize_download, error_message, "Download multiplo completato!") self.master.after(0, self._finalize_download, error_message, "Batch download completed!")
def _finalize_download(self, error_message: Optional[str], success_message: str): def _finalize_download(self, error_message: Optional[str], success_message: str):
self._reset_forms() self._reset_forms()