youtube_downloader.py
· 14 KiB · Python
Surowy
import tkinter as tk
from tkinter import ttk
from tkinter import filedialog
from tkinter import messagebox
import yt_dlp
import os
import re
from urllib.parse import urlparse, parse_qs
import webbrowser
from PIL import Image, ImageTk
import sys
from ttkthemes import ThemedTk
import logging
import traceback
# Set up logging
logging.basicConfig(
level=logging.DEBUG,
format='%(asctime)s - %(levelname)s - %(message)s',
handlers=[
logging.FileHandler('youtube_downloader.log'),
logging.StreamHandler()
]
)
class YouTubeDownloader:
def __init__(self, root):
try:
self.root = root
self.root.title("fedya-mp3")
self.root.geometry("650x400")
self.root.resizable(True, True)
# Icon setup with better error handling
try:
icon_paths = [
'icon.ico',
'C:/Users/fedia/Desktop/mp3/icon.ico',
os.path.join(os.path.dirname(__file__), 'icon.ico')
]
for icon_path in icon_paths:
if os.path.exists(icon_path):
self.root.iconbitmap(icon_path)
logging.info(f"Successfully loaded icon from: {icon_path}")
break
else:
logging.warning("No icon file found in any of the searched locations")
except Exception as e:
logging.error(f"Error loading icon: {str(e)}")
self.notebook = ttk.Notebook(self.root)
self.notebook.pack(fill=tk.BOTH, expand=True, padx=5, pady=5)
self.download_frame = ttk.Frame(self.notebook)
self.notebook.add(self.download_frame, text="Сваляне")
self.about_frame = ttk.Frame(self.notebook)
self.notebook.add(self.about_frame, text="За програмата")
self.main_frame = ttk.Frame(self.download_frame, padding="10")
self.main_frame.pack(fill=tk.BOTH, expand=True)
self.create_download_tab()
self.create_about_tab()
except Exception as e:
logging.error(f"Error in initialization: {str(e)}\n{traceback.format_exc()}")
messagebox.showerror("Error", f"Error initializing application: {str(e)}")
def create_download_tab(self):
try:
# URL секция
self.create_url_section()
# Дестинация секция
self.create_destination_section()
# Прогрес секция
self.create_progress_section()
# Бутони за действие
self.create_action_buttons()
# Статус
self.status_label = ttk.Label(self.main_frame,
text="Готов за сваляне",
font=('Helvetica', 10))
self.status_label.pack(pady=5)
except Exception as e:
logging.error(f"Error creating download tab: {str(e)}")
raise
def create_url_section(self):
url_frame = ttk.LabelFrame(self.main_frame, text="URL адрес", padding="5")
url_frame.pack(fill=tk.X, pady=5)
self.url_entry = ttk.Entry(url_frame, width=70)
self.url_entry.pack(side=tk.LEFT, fill=tk.X, expand=True, padx=5)
paste_button = tk.Button(url_frame,
text="Постави URL",
command=self.paste_url,
bg='#2196F3',
fg='white',
relief=tk.RAISED,
borderwidth=1,
cursor='hand2')
paste_button.pack(side=tk.RIGHT, padx=5)
def create_destination_section(self):
dest_frame = ttk.LabelFrame(self.main_frame, text="Дестинация", padding="5")
dest_frame.pack(fill=tk.X, pady=5)
self.destination_var = tk.StringVar(value=os.getcwd())
self.destination_entry = ttk.Entry(dest_frame, textvariable=self.destination_var)
self.destination_entry.pack(side=tk.LEFT, fill=tk.X, expand=True, padx=5)
dest_button = tk.Button(dest_frame,
text="Избери папка",
command=self.choose_destination,
bg='#FF9800',
fg='white',
relief=tk.RAISED,
borderwidth=1,
cursor='hand2')
dest_button.pack(side=tk.RIGHT, padx=5)
def create_progress_section(self):
progress_frame = ttk.Frame(self.main_frame)
progress_frame.pack(fill=tk.X, pady=10)
self.progress_var = tk.DoubleVar()
self.progress_bar = ttk.Progressbar(
progress_frame,
variable=self.progress_var,
maximum=100,
mode='determinate',
length=300
)
self.progress_bar.pack(fill=tk.X)
def create_action_buttons(self):
button_frame = ttk.Frame(self.main_frame)
button_frame.pack(fill=tk.X, pady=5)
download_button = tk.Button(
button_frame,
text="Изтегли",
command=self.start_download,
bg='#4CAF50',
fg='white',
relief=tk.RAISED,
borderwidth=1,
width=15,
cursor='hand2',
font=('Helvetica', 10, 'bold')
)
download_button.pack(side=tk.LEFT, padx=5)
cancel_button = tk.Button(
button_frame,
text="Отказ",
command=self.cancel_download,
bg='#f44336',
fg='white',
relief=tk.RAISED,
borderwidth=1,
width=15,
cursor='hand2',
font=('Helvetica', 10)
)
cancel_button.pack(side=tk.LEFT, padx=5)
for button in (download_button, cancel_button):
button.bind('<Enter>', lambda e, b=button: self.on_hover(e, b))
button.bind('<Leave>', lambda e, b=button: self.on_leave(e, b))
def create_about_tab(self):
center_frame = ttk.Frame(self.about_frame)
center_frame.pack(expand=True, fill=tk.BOTH, padx=20, pady=20)
title_label = ttk.Label(center_frame,
text="YouTube MP3 Downloader",
font=('Helvetica', 16, 'bold'))
title_label.pack(pady=(0,10))
version_label = ttk.Label(center_frame,
text="Версия 1.2.0",
font=('Helvetica', 10))
version_label.pack()
description = """YouTube MP3 Downloader е безплатна програма за сваляне на аудио от YouTube.
Програмата позволява лесно и бързо конвертиране на видеа в MP3 формат.
Характеристики:
- Лесен и интуитивен интерфейс
- Бързо сваляне и конвертиране
- Високо качество на звука (192kbps)
- Поддръжка на плейлисти
Важно предупреждение:
Този софтуер е създаден само с образователна цел.
"""
desc_text = tk.Text(center_frame, wrap=tk.WORD, height=10, width=50)
desc_text.insert('1.0', description)
desc_text.configure(state='disabled', border=0, font=('Helvetica', 10))
desc_text.pack(pady=20, padx=20)
website_label = ttk.Label(center_frame,
text="Посетете нашия сайт",
font=('Helvetica', 10, 'underline'),
foreground='blue',
cursor='hand2')
website_label.pack(pady=10)
website_label.bind('<Button-1>', lambda e: webbrowser.open('https://urocibg.eu/'))
copyright_label = ttk.Label(center_frame,
text="© 2024 Всички права запазени",
font=('Helvetica', 8))
copyright_label.pack(side=tk.BOTTOM, pady=5)
def validate_youtube_url(self, url):
if not url:
return False
patterns = [
r'^https?:\/\/(?:www\.)?youtube\.com\/watch\?v=[\w-]+',
r'^https?:\/\/(?:www\.)?youtu\.be\/[\w-]+',
r'^https?:\/\/(?:www\.)?youtube\.com\/playlist\?list=[\w-]+'
]
return any(re.match(pattern, url) for pattern in patterns)
def progress_hook(self, d):
if d['status'] == 'downloading':
try:
progress = (d['downloaded_bytes'] / d['total_bytes']) * 100
self.progress_var.set(progress)
self.status_label.config(text=f"Сваляне: {progress:.1f}%")
self.root.update()
except:
pass
elif d['status'] == 'finished':
self.status_label.config(text="Конвертиране в MP3...")
self.root.update()
def paste_url(self):
try:
clipboard_text = self.root.clipboard_get()
self.url_entry.delete(0, tk.END)
self.url_entry.insert(0, clipboard_text)
except tk.TclError:
messagebox.showwarning("Предупреждение", "Няма текст в клипборда")
def choose_destination(self):
chosen_folder = filedialog.askdirectory()
if chosen_folder:
self.destination_var.set(chosen_folder)
def on_hover(self, event, button):
if button['bg'] == '#4CAF50':
button.configure(bg='#45a049')
elif button['bg'] == '#f44336':
button.configure(bg='#da190b')
elif button['bg'] == '#2196F3':
button.configure(bg='#1976D2')
elif button['bg'] == '#FF9800':
button.configure(bg='#F57C00')
def on_leave(self, event, button):
if button['bg'] in ['#45a049']:
button.configure(bg='#4CAF50')
elif button['bg'] in ['#da190b']:
button.configure(bg='#f44336')
elif button['bg'] in ['#1976D2']:
button.configure(bg='#2196F3')
elif button['bg'] in ['#F57C00']:
button.configure(bg='#FF9800')
def start_download(self):
try:
url = self.url_entry.get().strip()
logging.info(f"Starting download for URL: {url}")
if not self.validate_youtube_url(url):
logging.warning(f"Invalid URL attempted: {url}")
messagebox.showerror("Грешка", "Невалиден YouTube URL адрес")
return
destination = self.destination_var.get()
if not os.path.exists(destination):
logging.error(f"Invalid destination path: {destination}")
messagebox.showerror("Грешка", "Невалидна дестинация")
return
self.progress_var.set(0)
self.status_label.config(text="Започва сваляне...")
ydl_opts = {
'format': 'bestaudio/best',
'postprocessors': [{
'key': 'FFmpegExtractAudio',
'preferredcodec': 'mp3',
'preferredquality': '192',
}],
'outtmpl': os.path.join(destination, '%(title)s.%(ext)s'),
'progress_hooks': [self.progress_hook],
'no_warnings': False,
'quiet': False,
'verbose': True
}
logging.info("Starting download with yt-dlp")
with yt_dlp.YoutubeDL(ydl_opts) as ydl:
ydl.download([url])
self.progress_var.set(100)
self.status_label.config(text="Готово!")
logging.info("Download completed successfully")
messagebox.showinfo("Успех", "Файлът беше успешно свален и конвертиран в MP3 формат.")
except yt_dlp.utils.DownloadError as e:
logging.error(f"yt-dlp download error: {str(e)}\n{traceback.format_exc()}")
self.status_label.config(text="Грешка при сваляне")
messagebox.showerror("Грешка", f"Грешка при свалянето: {str(e)}")
except Exception as e:
logging.error(f"Unexpected error: {str(e)}\n{traceback.format_exc()}")
self.status_label.config(text="Грешка при сваляне")
messagebox.showerror("Грешка", f"Неочаквана грешка: {str(e)}")
finally:
self.progress_var.set(0)
def cancel_download(self):
self.status_label.config(text="Свалянето е отказано")
self.progress_var.set(0)
def run(self):
window_width = 650
window_height = 400
screen_width = self.root.winfo_screenwidth()
screen_height = self.root.winfo_screenheight()
center_x = int(screen_width/2 - window_width/2)
center_y = int(screen_height/2 - window_height/2)
self.root.geometry(f'{window_width}x{window_height}+{center_x}+{center_y}')
self.root.mainloop()
if __name__ == "__main__":
root = ThemedTk(theme="breeze")
app = YouTubeDownloader(root)
app.run()
| 1 | import tkinter as tk |
| 2 | from tkinter import ttk |
| 3 | from tkinter import filedialog |
| 4 | from tkinter import messagebox |
| 5 | import yt_dlp |
| 6 | import os |
| 7 | import re |
| 8 | from urllib.parse import urlparse, parse_qs |
| 9 | import webbrowser |
| 10 | from PIL import Image, ImageTk |
| 11 | import sys |
| 12 | from ttkthemes import ThemedTk |
| 13 | import logging |
| 14 | import traceback |
| 15 | |
| 16 | # Set up logging |
| 17 | logging.basicConfig( |
| 18 | level=logging.DEBUG, |
| 19 | format='%(asctime)s - %(levelname)s - %(message)s', |
| 20 | handlers=[ |
| 21 | logging.FileHandler('youtube_downloader.log'), |
| 22 | logging.StreamHandler() |
| 23 | ] |
| 24 | ) |
| 25 | |
| 26 | class YouTubeDownloader: |
| 27 | def __init__(self, root): |
| 28 | try: |
| 29 | self.root = root |
| 30 | self.root.title("fedya-mp3") |
| 31 | self.root.geometry("650x400") |
| 32 | self.root.resizable(True, True) |
| 33 | |
| 34 | # Icon setup with better error handling |
| 35 | try: |
| 36 | icon_paths = [ |
| 37 | 'icon.ico', |
| 38 | 'C:/Users/fedia/Desktop/mp3/icon.ico', |
| 39 | os.path.join(os.path.dirname(__file__), 'icon.ico') |
| 40 | ] |
| 41 | |
| 42 | for icon_path in icon_paths: |
| 43 | if os.path.exists(icon_path): |
| 44 | self.root.iconbitmap(icon_path) |
| 45 | logging.info(f"Successfully loaded icon from: {icon_path}") |
| 46 | break |
| 47 | else: |
| 48 | logging.warning("No icon file found in any of the searched locations") |
| 49 | except Exception as e: |
| 50 | logging.error(f"Error loading icon: {str(e)}") |
| 51 | |
| 52 | self.notebook = ttk.Notebook(self.root) |
| 53 | self.notebook.pack(fill=tk.BOTH, expand=True, padx=5, pady=5) |
| 54 | |
| 55 | self.download_frame = ttk.Frame(self.notebook) |
| 56 | self.notebook.add(self.download_frame, text="Сваляне") |
| 57 | |
| 58 | self.about_frame = ttk.Frame(self.notebook) |
| 59 | self.notebook.add(self.about_frame, text="За програмата") |
| 60 | |
| 61 | self.main_frame = ttk.Frame(self.download_frame, padding="10") |
| 62 | self.main_frame.pack(fill=tk.BOTH, expand=True) |
| 63 | |
| 64 | self.create_download_tab() |
| 65 | self.create_about_tab() |
| 66 | |
| 67 | except Exception as e: |
| 68 | logging.error(f"Error in initialization: {str(e)}\n{traceback.format_exc()}") |
| 69 | messagebox.showerror("Error", f"Error initializing application: {str(e)}") |
| 70 | |
| 71 | def create_download_tab(self): |
| 72 | try: |
| 73 | # URL секция |
| 74 | self.create_url_section() |
| 75 | |
| 76 | # Дестинация секция |
| 77 | self.create_destination_section() |
| 78 | |
| 79 | # Прогрес секция |
| 80 | self.create_progress_section() |
| 81 | |
| 82 | # Бутони за действие |
| 83 | self.create_action_buttons() |
| 84 | |
| 85 | # Статус |
| 86 | self.status_label = ttk.Label(self.main_frame, |
| 87 | text="Готов за сваляне", |
| 88 | font=('Helvetica', 10)) |
| 89 | self.status_label.pack(pady=5) |
| 90 | except Exception as e: |
| 91 | logging.error(f"Error creating download tab: {str(e)}") |
| 92 | raise |
| 93 | |
| 94 | def create_url_section(self): |
| 95 | url_frame = ttk.LabelFrame(self.main_frame, text="URL адрес", padding="5") |
| 96 | url_frame.pack(fill=tk.X, pady=5) |
| 97 | |
| 98 | self.url_entry = ttk.Entry(url_frame, width=70) |
| 99 | self.url_entry.pack(side=tk.LEFT, fill=tk.X, expand=True, padx=5) |
| 100 | |
| 101 | paste_button = tk.Button(url_frame, |
| 102 | text="Постави URL", |
| 103 | command=self.paste_url, |
| 104 | bg='#2196F3', |
| 105 | fg='white', |
| 106 | relief=tk.RAISED, |
| 107 | borderwidth=1, |
| 108 | cursor='hand2') |
| 109 | paste_button.pack(side=tk.RIGHT, padx=5) |
| 110 | |
| 111 | def create_destination_section(self): |
| 112 | dest_frame = ttk.LabelFrame(self.main_frame, text="Дестинация", padding="5") |
| 113 | dest_frame.pack(fill=tk.X, pady=5) |
| 114 | |
| 115 | self.destination_var = tk.StringVar(value=os.getcwd()) |
| 116 | self.destination_entry = ttk.Entry(dest_frame, textvariable=self.destination_var) |
| 117 | self.destination_entry.pack(side=tk.LEFT, fill=tk.X, expand=True, padx=5) |
| 118 | |
| 119 | dest_button = tk.Button(dest_frame, |
| 120 | text="Избери папка", |
| 121 | command=self.choose_destination, |
| 122 | bg='#FF9800', |
| 123 | fg='white', |
| 124 | relief=tk.RAISED, |
| 125 | borderwidth=1, |
| 126 | cursor='hand2') |
| 127 | dest_button.pack(side=tk.RIGHT, padx=5) |
| 128 | |
| 129 | def create_progress_section(self): |
| 130 | progress_frame = ttk.Frame(self.main_frame) |
| 131 | progress_frame.pack(fill=tk.X, pady=10) |
| 132 | |
| 133 | self.progress_var = tk.DoubleVar() |
| 134 | self.progress_bar = ttk.Progressbar( |
| 135 | progress_frame, |
| 136 | variable=self.progress_var, |
| 137 | maximum=100, |
| 138 | mode='determinate', |
| 139 | length=300 |
| 140 | ) |
| 141 | self.progress_bar.pack(fill=tk.X) |
| 142 | |
| 143 | def create_action_buttons(self): |
| 144 | button_frame = ttk.Frame(self.main_frame) |
| 145 | button_frame.pack(fill=tk.X, pady=5) |
| 146 | |
| 147 | download_button = tk.Button( |
| 148 | button_frame, |
| 149 | text="Изтегли", |
| 150 | command=self.start_download, |
| 151 | bg='#4CAF50', |
| 152 | fg='white', |
| 153 | relief=tk.RAISED, |
| 154 | borderwidth=1, |
| 155 | width=15, |
| 156 | cursor='hand2', |
| 157 | font=('Helvetica', 10, 'bold') |
| 158 | ) |
| 159 | download_button.pack(side=tk.LEFT, padx=5) |
| 160 | |
| 161 | cancel_button = tk.Button( |
| 162 | button_frame, |
| 163 | text="Отказ", |
| 164 | command=self.cancel_download, |
| 165 | bg='#f44336', |
| 166 | fg='white', |
| 167 | relief=tk.RAISED, |
| 168 | borderwidth=1, |
| 169 | width=15, |
| 170 | cursor='hand2', |
| 171 | font=('Helvetica', 10) |
| 172 | ) |
| 173 | cancel_button.pack(side=tk.LEFT, padx=5) |
| 174 | |
| 175 | for button in (download_button, cancel_button): |
| 176 | button.bind('<Enter>', lambda e, b=button: self.on_hover(e, b)) |
| 177 | button.bind('<Leave>', lambda e, b=button: self.on_leave(e, b)) |
| 178 | |
| 179 | def create_about_tab(self): |
| 180 | center_frame = ttk.Frame(self.about_frame) |
| 181 | center_frame.pack(expand=True, fill=tk.BOTH, padx=20, pady=20) |
| 182 | |
| 183 | title_label = ttk.Label(center_frame, |
| 184 | text="YouTube MP3 Downloader", |
| 185 | font=('Helvetica', 16, 'bold')) |
| 186 | title_label.pack(pady=(0,10)) |
| 187 | |
| 188 | version_label = ttk.Label(center_frame, |
| 189 | text="Версия 1.2.0", |
| 190 | font=('Helvetica', 10)) |
| 191 | version_label.pack() |
| 192 | |
| 193 | description = """YouTube MP3 Downloader е безплатна програма за сваляне на аудио от YouTube. |
| 194 | |
| 195 | Програмата позволява лесно и бързо конвертиране на видеа в MP3 формат. |
| 196 | |
| 197 | Характеристики: |
| 198 | - Лесен и интуитивен интерфейс |
| 199 | - Бързо сваляне и конвертиране |
| 200 | - Високо качество на звука (192kbps) |
| 201 | - Поддръжка на плейлисти |
| 202 | |
| 203 | Важно предупреждение: |
| 204 | Този софтуер е създаден само с образователна цел. |
| 205 | """ |
| 206 | |
| 207 | desc_text = tk.Text(center_frame, wrap=tk.WORD, height=10, width=50) |
| 208 | desc_text.insert('1.0', description) |
| 209 | desc_text.configure(state='disabled', border=0, font=('Helvetica', 10)) |
| 210 | desc_text.pack(pady=20, padx=20) |
| 211 | |
| 212 | website_label = ttk.Label(center_frame, |
| 213 | text="Посетете нашия сайт", |
| 214 | font=('Helvetica', 10, 'underline'), |
| 215 | foreground='blue', |
| 216 | cursor='hand2') |
| 217 | website_label.pack(pady=10) |
| 218 | website_label.bind('<Button-1>', lambda e: webbrowser.open('https://urocibg.eu/')) |
| 219 | |
| 220 | copyright_label = ttk.Label(center_frame, |
| 221 | text="© 2024 Всички права запазени", |
| 222 | font=('Helvetica', 8)) |
| 223 | copyright_label.pack(side=tk.BOTTOM, pady=5) |
| 224 | |
| 225 | def validate_youtube_url(self, url): |
| 226 | if not url: |
| 227 | return False |
| 228 | |
| 229 | patterns = [ |
| 230 | r'^https?:\/\/(?:www\.)?youtube\.com\/watch\?v=[\w-]+', |
| 231 | r'^https?:\/\/(?:www\.)?youtu\.be\/[\w-]+', |
| 232 | r'^https?:\/\/(?:www\.)?youtube\.com\/playlist\?list=[\w-]+' |
| 233 | ] |
| 234 | |
| 235 | return any(re.match(pattern, url) for pattern in patterns) |
| 236 | |
| 237 | def progress_hook(self, d): |
| 238 | if d['status'] == 'downloading': |
| 239 | try: |
| 240 | progress = (d['downloaded_bytes'] / d['total_bytes']) * 100 |
| 241 | self.progress_var.set(progress) |
| 242 | self.status_label.config(text=f"Сваляне: {progress:.1f}%") |
| 243 | self.root.update() |
| 244 | except: |
| 245 | pass |
| 246 | elif d['status'] == 'finished': |
| 247 | self.status_label.config(text="Конвертиране в MP3...") |
| 248 | self.root.update() |
| 249 | |
| 250 | def paste_url(self): |
| 251 | try: |
| 252 | clipboard_text = self.root.clipboard_get() |
| 253 | self.url_entry.delete(0, tk.END) |
| 254 | self.url_entry.insert(0, clipboard_text) |
| 255 | except tk.TclError: |
| 256 | messagebox.showwarning("Предупреждение", "Няма текст в клипборда") |
| 257 | |
| 258 | def choose_destination(self): |
| 259 | chosen_folder = filedialog.askdirectory() |
| 260 | if chosen_folder: |
| 261 | self.destination_var.set(chosen_folder) |
| 262 | |
| 263 | def on_hover(self, event, button): |
| 264 | if button['bg'] == '#4CAF50': |
| 265 | button.configure(bg='#45a049') |
| 266 | elif button['bg'] == '#f44336': |
| 267 | button.configure(bg='#da190b') |
| 268 | elif button['bg'] == '#2196F3': |
| 269 | button.configure(bg='#1976D2') |
| 270 | elif button['bg'] == '#FF9800': |
| 271 | button.configure(bg='#F57C00') |
| 272 | |
| 273 | def on_leave(self, event, button): |
| 274 | if button['bg'] in ['#45a049']: |
| 275 | button.configure(bg='#4CAF50') |
| 276 | elif button['bg'] in ['#da190b']: |
| 277 | button.configure(bg='#f44336') |
| 278 | elif button['bg'] in ['#1976D2']: |
| 279 | button.configure(bg='#2196F3') |
| 280 | elif button['bg'] in ['#F57C00']: |
| 281 | button.configure(bg='#FF9800') |
| 282 | |
| 283 | def start_download(self): |
| 284 | try: |
| 285 | url = self.url_entry.get().strip() |
| 286 | logging.info(f"Starting download for URL: {url}") |
| 287 | |
| 288 | if not self.validate_youtube_url(url): |
| 289 | logging.warning(f"Invalid URL attempted: {url}") |
| 290 | messagebox.showerror("Грешка", "Невалиден YouTube URL адрес") |
| 291 | return |
| 292 | |
| 293 | destination = self.destination_var.get() |
| 294 | if not os.path.exists(destination): |
| 295 | logging.error(f"Invalid destination path: {destination}") |
| 296 | messagebox.showerror("Грешка", "Невалидна дестинация") |
| 297 | return |
| 298 | |
| 299 | self.progress_var.set(0) |
| 300 | self.status_label.config(text="Започва сваляне...") |
| 301 | |
| 302 | ydl_opts = { |
| 303 | 'format': 'bestaudio/best', |
| 304 | 'postprocessors': [{ |
| 305 | 'key': 'FFmpegExtractAudio', |
| 306 | 'preferredcodec': 'mp3', |
| 307 | 'preferredquality': '192', |
| 308 | }], |
| 309 | 'outtmpl': os.path.join(destination, '%(title)s.%(ext)s'), |
| 310 | 'progress_hooks': [self.progress_hook], |
| 311 | 'no_warnings': False, |
| 312 | 'quiet': False, |
| 313 | 'verbose': True |
| 314 | } |
| 315 | |
| 316 | logging.info("Starting download with yt-dlp") |
| 317 | with yt_dlp.YoutubeDL(ydl_opts) as ydl: |
| 318 | ydl.download([url]) |
| 319 | |
| 320 | self.progress_var.set(100) |
| 321 | self.status_label.config(text="Готово!") |
| 322 | logging.info("Download completed successfully") |
| 323 | messagebox.showinfo("Успех", "Файлът беше успешно свален и конвертиран в MP3 формат.") |
| 324 | |
| 325 | except yt_dlp.utils.DownloadError as e: |
| 326 | logging.error(f"yt-dlp download error: {str(e)}\n{traceback.format_exc()}") |
| 327 | self.status_label.config(text="Грешка при сваляне") |
| 328 | messagebox.showerror("Грешка", f"Грешка при свалянето: {str(e)}") |
| 329 | except Exception as e: |
| 330 | logging.error(f"Unexpected error: {str(e)}\n{traceback.format_exc()}") |
| 331 | self.status_label.config(text="Грешка при сваляне") |
| 332 | messagebox.showerror("Грешка", f"Неочаквана грешка: {str(e)}") |
| 333 | finally: |
| 334 | self.progress_var.set(0) |
| 335 | |
| 336 | def cancel_download(self): |
| 337 | self.status_label.config(text="Свалянето е отказано") |
| 338 | self.progress_var.set(0) |
| 339 | |
| 340 | def run(self): |
| 341 | window_width = 650 |
| 342 | window_height = 400 |
| 343 | screen_width = self.root.winfo_screenwidth() |
| 344 | screen_height = self.root.winfo_screenheight() |
| 345 | center_x = int(screen_width/2 - window_width/2) |
| 346 | center_y = int(screen_height/2 - window_height/2) |
| 347 | self.root.geometry(f'{window_width}x{window_height}+{center_x}+{center_y}') |
| 348 | self.root.mainloop() |
| 349 | |
| 350 | if __name__ == "__main__": |
| 351 | root = ThemedTk(theme="breeze") |
| 352 | app = YouTubeDownloader(root) |
| 353 | app.run() |