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('', lambda e, b=button: self.on_hover(e, b)) button.bind('', 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('', 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()