import os
import sys
import threading
import time
import tkinter as tk
from tkinter import ttk, filedialog, messagebox, font
import yt_dlp
import subprocess
import webbrowser
from ttkthemes import ThemedTk
import platform

class YouTubeDownloader:
    @staticmethod
    def resource_path(relative_path):
        """ Get absolute path to resource, works for dev and for PyInstaller """
        try:
            # PyInstaller creates a temp folder and stores path in _MEIPASS
            base_path = sys._MEIPASS
        except Exception:
            base_path = os.path.abspath(".")
        return os.path.join(base_path, relative_path)
    
    def __init__(self, root):
        self.root = root
        self.root.title("YouTube to MP3 Downloader")
        self.root.geometry("800x600")
        self.root.minsize(700, 500)
        
        # Système d'exploitation
        self.system = platform.system()  # 'Windows', 'Darwin' (Mac), 'Linux'
        
        # Variables
        self.url_var = tk.StringVar()
        self.destination_var = tk.StringVar()
        self.status_var = tk.StringVar(value="Prêt")
        self.progress_var = tk.DoubleVar()
        self.global_progress_var = tk.DoubleVar()  # Pour la progression globale de la playlist
        self.download_type = tk.StringVar(value="single")
        self.downloading = False
        self.audio_quality = tk.StringVar(value="192")
        
        # Thèmes étendus
        self.available_themes = ["breeze", "arc", "equilux", "yaru", "adapta", "aquativo", 
                                "black", "blue", "clearlooks", "elegance", "keramik", 
                                "kroc", "plastik", "radiance", "smog", "winxpblue"]
        self.theme_var = tk.StringVar(value="breeze")
        
        self.use_cookies = tk.BooleanVar(value=False)
        self.browser_var = tk.StringVar(value="chrome")
        
        # Variables pour la gestion des polices
        self.default_font_size = tk.IntVar(value=10)
        self.console_font_size = tk.IntVar(value=9)
        
        # Définition des polices selon le système d'exploitation
        self.set_system_fonts()
        
        # Variables pour la gestion des playlists
        self.playlist_info = {
            "current": 0,
            "total": 0,
            "videos": []
        }
        
        # Appliquer un thème
        self.root.set_theme(self.theme_var.get())
        
        # Créer les widgets
        self.create_widgets()
        
        # Configurer la sortie de la console
        self.configure_redirect_output()
        
        # Appliquer les styles de polices initiaux
        self.apply_font_styles()
        
        # Afficher les infos de démarrage
        self.log_message("Application démarrée avec succès", "info")
        self.log_message("Prêt à télécharger", "info")
        
        # Mettre à jour yt-dlp au démarrage
        self.update_ytdlp()
    
    def set_system_fonts(self):
        """Définit les polices adaptées au système d'exploitation"""
        if self.system == "Windows":
            self.default_font_family = "Segoe UI"
            self.console_font_family = "Consolas"
        elif self.system == "Darwin":  # macOS
            self.default_font_family = "SF Pro"
            self.console_font_family = "Menlo"
        else:  # Linux et autres
            self.default_font_family = "DejaVu Sans"
            self.console_font_family = "DejaVu Sans Mono"
    
    def apply_font_styles(self):
        """Applique les styles de police à l'interface"""
        # Police par défaut pour l'interface
        default_font = font.nametofont("TkDefaultFont")
        default_font.configure(family=self.default_font_family, size=self.default_font_size.get())
        
        # Police pour les widgets de texte
        text_font = font.nametofont("TkTextFont")
        text_font.configure(family=self.default_font_family, size=self.default_font_size.get())
        
        # Police pour les widgets d'entrée
        entry_font = font.nametofont("TkFixedFont")
        entry_font.configure(family=self.console_font_family, size=self.default_font_size.get())
        
        # Police pour la console
        console_font = font.Font(family=self.console_font_family, size=self.console_font_size.get())
        self.console.configure(font=console_font)
        
        # Reconfigurer tous les widgets pour refléter les changements de police
        self.root.update_idletasks()
    
    def update_ytdlp(self):
        self.log_message("Vérification des mises à jour de yt-dlp...", "info")
        try:
            subprocess.run([sys.executable, "-m", "pip", "install", "--upgrade", "yt-dlp"], 
                          stdout=subprocess.PIPE, stderr=subprocess.PIPE)
            self.log_message("yt-dlp mis à jour avec succès", "info")
        except Exception as e:
            self.log_message(f"Impossible de mettre à jour yt-dlp: {e}", "warning")
    
    def create_widgets(self):
        main_frame = ttk.Frame(self.root)
        main_frame.pack(fill=tk.BOTH, expand=True, padx=10, pady=10)
        
        top_frame = ttk.Frame(main_frame)
        top_frame.pack(fill=tk.X, pady=5)
        
        # URL Input
        ttk.Label(top_frame, text="URL YouTube :").pack(side=tk.LEFT, padx=5)
        url_entry = ttk.Entry(top_frame, textvariable=self.url_var, width=50)
        url_entry.pack(side=tk.LEFT, padx=5, fill=tk.X, expand=True)
        url_entry.focus()  # Mettre le focus sur l'entrée d'URL
        
        # Type de téléchargement (vidéo unique ou playlist)
        type_frame = ttk.Frame(main_frame)
        type_frame.pack(fill=tk.X, pady=5)
        
        ttk.Label(type_frame, text="Type :").pack(side=tk.LEFT, padx=5)
        ttk.Radiobutton(type_frame, text="Vidéo unique", variable=self.download_type, value="single").pack(side=tk.LEFT, padx=5)
        ttk.Radiobutton(type_frame, text="Playlist", variable=self.download_type, value="playlist").pack(side=tk.LEFT, padx=5)
        
        # Destination folder
        dest_frame = ttk.Frame(main_frame)
        dest_frame.pack(fill=tk.X, pady=5)
        
        ttk.Label(dest_frame, text="Destination :").pack(side=tk.LEFT, padx=5)
        ttk.Entry(dest_frame, textvariable=self.destination_var, width=50).pack(side=tk.LEFT, padx=5, fill=tk.X, expand=True)
        ttk.Button(dest_frame, text="Parcourir", command=self.browse_destination).pack(side=tk.LEFT, padx=5)
        
        # Options frame
        self.options_frame = ttk.LabelFrame(main_frame, text="Options")
        self.options_frame.pack(fill=tk.X, pady=5)
        
        quality_frame = ttk.Frame(self.options_frame)
        quality_frame.pack(fill=tk.X, pady=5)
        
        ttk.Label(quality_frame, text="Qualité audio :").pack(side=tk.LEFT, padx=5)
        ttk.Combobox(quality_frame, textvariable=self.audio_quality, values=["64", "128", "192", "256", "320"], width=5).pack(side=tk.LEFT, padx=5)
        ttk.Label(quality_frame, text="kbps").pack(side=tk.LEFT)
        
        # Thème et police
        appearance_frame = ttk.Frame(self.options_frame)
        appearance_frame.pack(fill=tk.X, pady=5)
        
        ttk.Label(appearance_frame, text="Thème :").pack(side=tk.LEFT, padx=5)
        themes_combo = ttk.Combobox(appearance_frame, textvariable=self.theme_var, 
                                    values=self.available_themes, width=15)
        themes_combo.pack(side=tk.LEFT, padx=5)
        themes_combo.bind("<<ComboboxSelected>>", self.change_theme)
        
        # Taille de police
        ttk.Label(appearance_frame, text="Taille de police :").pack(side=tk.LEFT, padx=(15,5))
        font_size_spinbox = ttk.Spinbox(appearance_frame, from_=8, to=16, width=3, 
                                       textvariable=self.default_font_size, 
                                       command=self.apply_font_styles)
        font_size_spinbox.pack(side=tk.LEFT)
        
        # Taille de police de console
        ttk.Label(appearance_frame, text="Police console :").pack(side=tk.LEFT, padx=(15,5))
        console_font_size_spinbox = ttk.Spinbox(appearance_frame, from_=7, to=14, width=3, 
                                              textvariable=self.console_font_size, 
                                              command=self.apply_font_styles)
        console_font_size_spinbox.pack(side=tk.LEFT)
        
        # Cookies options
        cookies_frame = ttk.Frame(self.options_frame)
        cookies_frame.pack(fill='x', pady=5)
        
        cookies_check = ttk.Checkbutton(cookies_frame, text="Utiliser les cookies du navigateur", 
                                    variable=self.use_cookies,
                                    command=self.toggle_cookies_options)
        cookies_check.pack(side='left', padx=5)
        
        self.browser_dropdown = ttk.Combobox(cookies_frame, textvariable=self.browser_var, 
                                        values=["chrome", "firefox", "edge", "safari", "opera", "brave"],
                                        width=10, state="disabled")
        self.browser_dropdown.pack(side='left', padx=5)
        
        # Actions frame
        actions_frame = ttk.Frame(main_frame)
        actions_frame.pack(fill=tk.X, pady=10)
        
        self.download_btn = ttk.Button(actions_frame, text="Télécharger", command=self.start_download)
        self.download_btn.pack(side=tk.LEFT, padx=5)
        
        self.cancel_btn = ttk.Button(actions_frame, text="Annuler", command=self.cancel_download, state=tk.DISABLED)
        self.cancel_btn.pack(side=tk.LEFT, padx=5)
        
        ttk.Button(actions_frame, text="À propos", command=self.show_about).pack(side=tk.RIGHT, padx=5)
        
        # Status and Progress
        status_frame = ttk.Frame(main_frame)
        status_frame.pack(fill=tk.X, pady=5)
        
        ttk.Label(status_frame, text="Statut:").pack(side=tk.LEFT, padx=5)
        ttk.Label(status_frame, textvariable=self.status_var).pack(side=tk.LEFT, padx=5)
        
        # Progress Bar
        progress_frame = ttk.Frame(main_frame)
        progress_frame.pack(fill=tk.X, pady=5)
        
        ttk.Label(progress_frame, text="Progression:").pack(side=tk.LEFT, padx=5)
        ttk.Progressbar(progress_frame, orient="horizontal", length=100, mode="determinate", 
                      variable=self.progress_var).pack(side=tk.LEFT, padx=5, fill=tk.X, expand=True)
        
        # Global Progress Bar for playlists
        global_progress_frame = ttk.Frame(main_frame)
        global_progress_frame.pack(fill=tk.X, pady=5)
        
        ttk.Label(global_progress_frame, text="Progression globale:").pack(side=tk.LEFT, padx=5)
        ttk.Progressbar(global_progress_frame, orient="horizontal", length=100, mode="determinate", 
                      variable=self.global_progress_var).pack(side=tk.LEFT, padx=5, fill=tk.X, expand=True)
        
        # Console (log area)
        console_frame = ttk.LabelFrame(main_frame, text="Console")
        console_frame.pack(fill=tk.BOTH, expand=True, pady=5)
        
        # Scrolled text with custom tag configuration
        self.console = tk.Text(console_frame, wrap=tk.WORD, height=10, bg='#f0f0f0', fg='#000000')
        self.console.pack(side=tk.LEFT, fill=tk.BOTH, expand=True, padx=5, pady=5)
        
        # Configure tags for different message types
        self.console.tag_configure("error", foreground="red")
        self.console.tag_configure("warning", foreground="orange")
        self.console.tag_configure("info", foreground="blue")
        self.console.tag_configure("success", foreground="green")
        
        self.console.config(state=tk.DISABLED)  # Make it read-only
        
        # Scrollbar for console
        scrollbar = ttk.Scrollbar(console_frame, orient="vertical", command=self.console.yview)
        scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
        self.console.config(yscrollcommand=scrollbar.set)
    
    def toggle_cookies_options(self):
        """Active ou désactive les options de cookies"""
        if self.use_cookies.get():
            self.browser_dropdown.config(state="readonly")
        else:
            self.browser_dropdown.config(state="disabled")
    
    def change_theme(self, event):
        theme = self.theme_var.get()
        try:
            self.root.set_theme(theme)
            
            # Ajuster les couleurs de la console selon le thème
            self.adjust_theme_colors(theme)
                
            self.log_message(f"Thème changé pour: {theme}", "info")
        except Exception as e:
            self.log_message(f"Erreur lors du changement de thème: {e}", "error")
    
    def adjust_theme_colors(self, theme):
        """Ajuste les couleurs de l'interface selon le thème choisi"""
        # Thèmes sombres
        dark_themes = ["equilux", "black", "arc"]
        
        # Couleurs pour la console
        if theme in dark_themes:
            # Thèmes sombres
            self.console.config(bg='#2b2b2b', fg='#f8f8f2')
            self.console.tag_configure("error", foreground="#ff5555")
            self.console.tag_configure("warning", foreground="#ffb86c")
            self.console.tag_configure("info", foreground="#8be9fd")
            self.console.tag_configure("success", foreground="#50fa7b")
        else:
            # Thèmes clairs
            self.console.config(bg='#f0f0f0', fg='#000000')
            self.console.tag_configure("error", foreground="#cc0000")
            self.console.tag_configure("warning", foreground="#cc7000")
            self.console.tag_configure("info", foreground="#0066cc")
            self.console.tag_configure("success", foreground="#008800")
    
    def browse_destination(self):
        folder = filedialog.askdirectory()
        if folder:
            self.destination_var.set(folder)
    
    def start_download(self):
        # Vérifier URL
        url = self.url_var.get().strip()
        if not url:
            messagebox.showerror("Erreur", "Veuillez entrer une URL valide")
            return
        
        # Vérifier destination
        dest_dir = self.destination_var.get()
        if not dest_dir:
            messagebox.showerror("Erreur", "Veuillez sélectionner un dossier de destination")
            return
        
        # Vérifier si le dossier existe
        if not os.path.exists(dest_dir):
            try:
                os.makedirs(dest_dir)
                self.log_message(f"Création du dossier de destination: {dest_dir}", "info")
            except Exception as e:
                messagebox.showerror("Erreur", f"Impossible de créer le dossier de destination: {e}")
                return
        
        # Vérifier les droits d'écriture
        test_file = os.path.join(dest_dir, "test_write.tmp")
        try:
            with open(test_file, 'w') as f:
                f.write("test")
            os.remove(test_file)
        except Exception as e:
            messagebox.showerror("Erreur", f"Pas de permission d'écriture dans le dossier de destination: {e}")
            return
        
        # Configuration
        self.downloading = True
        self.progress_var.set(0)
        self.global_progress_var.set(0)
        self.download_btn.config(state=tk.DISABLED)
        self.cancel_btn.config(state=tk.NORMAL)
        
        # Options pour yt-dlp
        self.ydl_opts = {
            'format': 'bestaudio/best',
            'postprocessors': [{
                'key': 'FFmpegExtractAudio',
                'preferredcodec': 'mp3',
                'preferredquality': self.audio_quality.get(),
            }],
            'outtmpl': os.path.join(dest_dir, '%(title)s.%(ext)s'),
            'progress_hooks': [self.update_progress],
            'ffmpeg_location': YouTubeDownloader.resource_path('ffmpeg/bin'),
            'verbose': True,
        }
        
        # User Agent moderne
        modern_user_agent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.110 Safari/537.36"
        
        # Ajouter les options de cookies si activées
        if self.use_cookies.get():
            self.ydl_opts['cookiesfrombrowser'] = [self.browser_var.get()]
            self.ydl_opts['user_agent'] = modern_user_agent
        
        # Options spécifiques pour les playlists
        if self.download_type.get() == "playlist":
            self.ydl_opts['ignoreerrors'] = True  # Continuer en cas d'erreur sur une vidéo
            self.ydl_opts['playlistend'] = 80  # Limiter à 80 vidéos par mesure de sécurité
            
            # Réinitialiser les informations de la playlist
            self.playlist_info = {
                "current": 0,
                "total": 0,
                "videos": []
            }
        
        # Démarrer le téléchargement dans un thread séparé
        download_thread = threading.Thread(target=self.download_worker, args=(url,))
        download_thread.daemon = True
        download_thread.start()
        
        self.log_message(f"Téléchargement démarré: {url}", "info")
        self.log_message(f"Destination: {dest_dir}", "info")
        self.log_message(f"Qualité audio: {self.audio_quality.get()} kbps", "info")
    
    def download_worker(self, url):
        try:
            self.root.after(0, lambda: self.status_var.set("🔍 Recherche d'informations..."))
            
            # Si c'est une playlist, récupérer d'abord les informations
            if self.download_type.get() == "playlist":
                self.root.after(0, lambda: self.log_message("Récupération des informations de la playlist...", "info"))
                
                # Configurer des options temporaires pour juste obtenir les infos
                info_opts = dict(self.ydl_opts)
                info_opts['extract_flat'] = True
                info_opts['dump_single_json'] = True
                info_opts['quiet'] = True
                
                with yt_dlp.YoutubeDL(info_opts) as ydl:
                    try:
                        info = ydl.extract_info(url, download=False)
                        if 'entries' in info:
                            # C'est une playlist
                            entries = list(info['entries'])
                            total_videos = len(entries)
                            
                            if total_videos == 0:
                                self.root.after(0, lambda: self.download_error("Playlist vide ou inaccessible"))
                                return
                            
                            self.playlist_info["total"] = total_videos
                            for entry in entries:
                                self.playlist_info["videos"].append({
                                    "id": entry.get('id', 'unknown'),
                                    "title": entry.get('title', 'Vidéo sans titre'),
                                    "status": "pending"
                                })
                            
                            # Afficher les infos de la playlist
                            self.root.after(0, lambda: self.log_message(
                                f"Playlist détectée: {info.get('title', 'Sans titre')} - {total_videos} vidéo(s)", "info"))
                            
                            # Démarrer le téléchargement de la playlist
                            success_count = 0
                            for i, video in enumerate(self.playlist_info["videos"]):
                                if not self.downloading:  # Si l'utilisateur a annulé
                                    break
                                
                                # Mettre à jour les informations de progression
                                self.playlist_info["current"] = i + 1
                                
                                # Mettre à jour la barre de progression globale
                                global_percent = (i / total_videos) * 100
                                self
                                self.root.after(0, lambda p=global_percent: self.global_progress_var.set(p))
                                
                                # Statut actuel
                                status = f"⬇️ Téléchargement vidéo {i+1}/{total_videos}: {video['title']}"
                                self.root.after(0, lambda s=status: self.status_var.set(s))
                                self.root.after(0, lambda s=status: self.log_message(s, "info"))
                                
                                # Télécharger la vidéo
                                try:
                                    with yt_dlp.YoutubeDL(self.ydl_opts) as ydl2:
                                        ydl2.download([f"https://www.youtube.com/watch?v={video['id']}"])
                                    
                                    # Marquer comme complété
                                    video["status"] = "completed"
                                    success_count += 1
                                except Exception as e:
                                    video["status"] = "failed"
                                    error_msg = f"Erreur lors du téléchargement de {video['title']}: {str(e)}"
                                    self.root.after(0, lambda msg=error_msg: self.log_message(msg, "error"))
                            
                            # Mise à jour de la progression globale
                            self.root.after(0, lambda: self.global_progress_var.set(100))
                            self.root.after(0, self.download_complete)
                        else:
                            # Ce n'est pas une playlist, télécharger comme une vidéo simple
                            with yt_dlp.YoutubeDL(self.ydl_opts) as ydl3:
                                ydl3.download([url])
                            self.root.after(0, self.download_complete)
                    except Exception as e:
                        self.root.after(0, lambda: self.download_error(f"Erreur lors de la récupération de la playlist: {str(e)}"))
            else:
                # Téléchargement d'une vidéo simple
                with yt_dlp.YoutubeDL(self.ydl_opts) as ydl:
                    # Visuel: simuler un petit délai pour montrer l'activité
                    self.root.after(0, lambda: self.log_message("Connexion à YouTube...", "info"))
                    self.root.after(500, lambda: self.log_message("Récupération des métadonnées...", "info"))
                    
                    # Télécharger la vidéo
                    ydl.download([url])
                
                self.root.after(0, self.download_complete)
            
        except yt_dlp.utils.DownloadError as e:
            error_msg = str(e)
            if "Video unavailable" in error_msg or "account has been terminated" in error_msg:
                # Message d'erreur plus convivial pour les vidéos indisponibles
                user_friendly_msg = "Cette vidéo n'est plus disponible sur YouTube. Veuillez essayer une autre vidéo."
                self.root.after(0, lambda: self.log_message(user_friendly_msg, "error"))
                self.root.after(0, lambda: self.download_error(user_friendly_msg))
            elif "Private video" in error_msg:
                user_friendly_msg = "Cette vidéo est privée et nécessite une authentification. Essayez d'activer l'option 'Utiliser les cookies du navigateur'."
                self.root.after(0, lambda: self.log_message(user_friendly_msg, "error"))
                self.root.after(0, lambda: self.download_error(user_friendly_msg))
            else:
                # Autres erreurs de téléchargement
                self.root.after(0, lambda: self.download_error(error_msg))
        except Exception as e:
            error_msg = f"Erreur détaillée: {str(e)}\nType d'erreur: {type(e).__name__}"
            import traceback
            traceback_str = traceback.format_exc()
            self.root.after(0, lambda: self.log_message(f"Traceback: {traceback_str}", "error"))
            self.root.after(0, lambda: self.download_error(error_msg))
    
    def update_progress(self, d):
        if d['status'] == 'downloading':
            # Calculer le pourcentage si disponible
            if 'total_bytes' in d and d['total_bytes'] > 0:
                percent = (d['downloaded_bytes'] / d['total_bytes']) * 100
                self.root.after(0, lambda: self.progress_var.set(percent))
                # Statut avec vitesse de téléchargement
                if 'speed' in d and d['speed']:
                    speed = d['speed'] / 1024  # Convert to KB/s
                    speed_str = f"{speed:.1f} KB/s" if speed < 1024 else f"{speed/1024:.1f} MB/s"
                    
                    # Format différent pour playlist vs vidéo simple
                    if self.download_type.get() == "playlist":
                        status = f"⬇️ Vidéo {self.playlist_info['current']}/{self.playlist_info['total']} - {speed_str}"
                    else:
                        status = f"⬇️ Téléchargement en cours... ({speed_str})"
                        
                    self.root.after(0, lambda: self.status_var.set(status))
            else:
                # Si taille inconnue, au moins montrer une activité
                if self.download_type.get() == "playlist":
                    status = f"⬇️ Vidéo {self.playlist_info['current']}/{self.playlist_info['total']} - en cours..."
                else:
                    status = "⬇️ Téléchargement en cours..."
                self.root.after(0, lambda: self.status_var.set(status))
        
        elif d['status'] == 'finished':
            if self.download_type.get() == "playlist":
                self.root.after(0, lambda: self.status_var.set(
                    f"🔄 Conversion de la vidéo en MP3..."))
            else:
                self.root.after(0, lambda: self.status_var.set("🔄 Conversion en MP3..."))
            
            self.root.after(0, lambda: self.log_message(f"Téléchargement terminé, conversion en cours...", "info"))
            self.root.after(0, lambda: self.progress_var.set(100))
    
    def download_complete(self):
        self.downloading = False
        self.download_btn.config(state=tk.NORMAL)
        self.cancel_btn.config(state=tk.DISABLED)
        
        # Message différent selon le type de téléchargement
        if self.download_type.get() == "playlist":
            # Calculer le nombre de vidéos téléchargées avec succès
            success_count = 0
            for video in self.playlist_info["videos"]:
                if video["status"] == "completed":
                    success_count += 1
            
            status_msg = f"✅ Playlist terminée: vidéos téléchargées"
            self.status_var.set(status_msg)
            self.log_message(status_msg, "success")
            
            # Afficher un message différent si certaines vidéos ont échoué
            if success_count < self.playlist_info["total"]:
                error_count = self.playlist_info["total"] - success_count
                error_msg = f"{error_count} vidéos n'ont pas pu être téléchargées. Vérifiez la console pour plus de détails."
                self.log_message(error_msg, "warning")
                messagebox.showinfo("Téléchargement terminé", 
                                   f"La playlist a été téléchargée avec {success_count} vidéos sur {self.playlist_info['total']}.\n\n{error_msg}")
            else:
                messagebox.showinfo("Succès", f"Toutes les vidéos ont été téléchargées avec succès!")
        else:
            self.status_var.set("✅ Téléchargement terminé")
            self.log_message("Conversion en MP3 terminée avec succès!", "success")
            messagebox.showinfo("Succès", "Le téléchargement est terminé avec succès!")
    
    def download_error(self, error_message):
            self.downloading = False
            self.download_btn.config(state=tk.NORMAL)
            self.cancel_btn.config(state=tk.DISABLED)
            self.status_var.set("❌ Erreur lors du téléchargement")
            self.log_message(f"Erreur lors du téléchargement: {error_message}", "error")
            messagebox.showerror("Erreur lors du téléchargement", error_message)
    
    def cancel_download(self):
        if self.downloading:
            self.downloading = False
            
            if self.download_type.get() == "playlist":
                status = f"Téléchargement de playlist annulé après {self.playlist_info['current']}/{self.playlist_info['total']} vidéos"
            else:
                status = "Téléchargement annulé par l'utilisateur"
                
            self.status_var.set(status)
            self.download_btn.config(state=tk.NORMAL)
            self.cancel_btn.config(state=tk.DISABLED)
            self.log_message(status, "warning")
            
            # Notification pour l'utilisateur
            messagebox.showinfo("Téléchargement annulé", 
                               f"Le téléchargement a été annulé.\n\n{status}")
    
    def log_message(self, message, msg_type="info"):
        """Ajouter un message à la console avec coloration"""
        self.console.config(state=tk.NORMAL)
        timestamp = time.strftime("%H:%M:%S")
        self.console.insert(tk.END, f"[{timestamp}] ", "info")
        self.console.insert(tk.END, f"{message}\n", msg_type)
        self.console.see(tk.END)  # Défiler vers le bas
        self.console.config(state=tk.DISABLED)
    
    def show_about(self):
        about_text = """
YouTube to MP3 Downloader
Version 1.2

Un convertisseur simple mais puissant pour télécharger et convertir des vidéos YouTube en MP3.

Caractéristiques:
- Téléchargement de vidéos uniques et de playlists
- Sélection de la qualité audio
- Interface utilisateur moderne et personnalisable
- Utilisation des cookies du navigateur pour accéder à plus de contenu
- Suivi de progression globale pour playlists
- Polices adaptatives selon le thème

Créé avec Python, tkinter et yt-dlp.

© 2023 - Tous droits réservés
        """
        messagebox.showinfo("À propos", about_text)
    
    def configure_redirect_output(self):
        """Rediriger la sortie standard et d'erreur vers la console"""
        class TextRedirector:
            def __init__(self, text_widget, tag):
                self.text_widget = text_widget
                self.tag = tag
            
            def write(self, string):
                self.text_widget.after(0, self.update_text, string)
            
            def update_text(self, string):
                self.text_widget.config(state=tk.NORMAL)
                self.text_widget.insert(tk.END, string, self.tag)
                self.text_widget.see(tk.END)
                self.text_widget.config(state=tk.DISABLED)
            
            def flush(self):
                pass
        
        # Ne pas rediriger pendant le développement pour voir les erreurs dans la console
        # sys.stdout = TextRedirector(self.console, "info")
        # sys.stderr = TextRedirector(self.console, "error")


if __name__ == "__main__":
    try:
        root = ThemedTk(theme="breeze")
        app = YouTubeDownloader(root)
        root.mainloop()
    except Exception as e:
        print(f"Error starting application: {e}")
        messagebox.showerror("Error", f"Failed to start application: {e}")
        raise
