diff --git a/main.py b/main.py index ff0fc38..2e98491 100644 --- a/main.py +++ b/main.py @@ -4,69 +4,137 @@ import requests from io import BytesIO from tkinter import * from PIL import Image, ImageTk +from tkinter.ttk import Progressbar +import threading +import queue def add_url(): + + #Ajoute un classe pour intercepter les logs + class YTDLLogger: + """ + Logger personnalisé pour capturer et afficher les messages de yt-dlp. + """ + def debug(self, msg): + print(msg) # Affiche les messages de debug + ProgresseLabel.config(text=msg) + + def warning(self, msg): + print(f"WARNING: {msg}") # Affiche les avertissements + + def error(self, msg): + print(f"ERROR: {msg}") # Affiche les erreurs + #Récuperer l'url url = EntryURL.get() - - ydl_opts = {} - - with yt_dlp.YoutubeDL(ydl_opts) as ydl: - info = ydl.extract_info(url, download=False) - - title = info.get('title', 'Titre indisponible') - subtitlte = ( - info.get('artist', info.get('uploader', 'Artiste indisponible')) - + ' · ' - + '{:,}'.format(info.get('view_count', 'Nombre de vue indisponible')).replace(',', ' ') - + ' vues' - ) - thumbnail_url = info.get('thumbnail') - - response = requests.get(thumbnail_url) - image = Image.open(BytesIO(response.content)) - width, height = image.size - - # Découper l'image en un carré centré - square_size = min(width, height) - left = (width - square_size) // 2 - top = (height - square_size) // 2 - - # Calculer les coordonnées du coin inférieur droit du carré - right = left + square_size - bottom = top + square_size - - # Découper l'image carrée - image = image.crop((left, top, right, bottom)) - image.thumbnail((50, 50)) - - # Création d'un cadre pour chaque vidéo - cadre = Frame(scrollable_frame) - cadre.pack(fill=X, padx=10, pady=5) - - # Charger l'image et l'afficher - image = ImageTk.PhotoImage(image) - image_label = Label(cadre, image=image) - image_label.image = image # Préserver la référence pour éviter le garbage collection - image_label.pack(side=LEFT, padx=5) - - # Ajouter les titres - texte_frame = Frame(cadre) - texte_frame.pack(side=LEFT, padx=10, fill=BOTH, expand=True) - - label_texte1 = Label(texte_frame, text=title, font=("Arial", 12, "bold")) - label_texte1.pack(anchor="w") - - label_texte2 = Label(texte_frame, text=subtitlte, font=("Arial", 10)) - label_texte2.pack(anchor="w") - - # Ajouter un bouton "Supprimer" - menu_button = Button(cadre, text="Supprimer", relief=FLAT, command=cadre.destroy) - menu_button.pack(side=RIGHT) - # Effacer l'entrée URL EntryURL.delete(0, END) + ydl_opts = { + 'quiet': True, # Supprime la sortie standard (redirigée vers le logger) + 'logger': YTDLLogger(), # Utilise le logger personnalisé + } + + # Queue pour recuperer les donnes + q = queue.Queue() + + # Fonction qui exécute le téléchargement dans un thread séparé + def process_download(): + try: + with yt_dlp.YoutubeDL(ydl_opts) as ydl: + info = ydl.extract_info(url, download=False) # Extraire les métadonnées sans télécharger + q.put(info) # Mettre l'info dans la queue pour être récupérée par le thread principal + except Exception as e: + q.put(f"Erreur : {str(e)}") # Mettre l'erreur dans la queue + finally: + progress_windows.after(2000, progress_windows.destroy) # Fermer la fenêtre de progression après 2s + + # Fonction pour vérifier la queue et mettre à jour l'interface + def check_queue(): + try: + # Essayer de récupérer l'info sans bloquer + info = q.get_nowait() + + if isinstance(info, dict): # Si l'info est un dictionnaire (métadonnées) + ProgresseLabel.config(text=f"Dowloading complet") + title = info.get('title', 'Titre indisponible') + subtitlte = ( + info.get('artist', info.get('uploader', 'Artiste indisponible')) + + ' · ' + + '{:,}'.format(info.get('view_count', 'Nombre de vue indisponible')).replace(',', ' ') + + ' vues' + ) + thumbnail_url = info.get('thumbnail') + + response = requests.get(thumbnail_url) + image = Image.open(BytesIO(response.content)) + width, height = image.size + + # Découper l'image en un carré centré + square_size = min(width, height) + left = (width - square_size) // 2 + top = (height - square_size) // 2 + + # Calculer les coordonnées du coin inférieur droit du carré + right = left + square_size + bottom = top + square_size + + # Découper l'image carrée + image = image.crop((left, top, right, bottom)) + image.thumbnail((50, 50)) + + # Création d'un cadre pour chaque vidéo + cadre = Frame(scrollable_frame) + cadre.pack(fill=X, padx=10, pady=5) + + # Charger l'image et l'afficher + image = ImageTk.PhotoImage(image) + image_label = Label(cadre, image=image) + image_label.image = image # Préserver la référence pour éviter le garbage collection + image_label.pack(side=LEFT, padx=5) + + # Ajouter les titres + texte_frame = Frame(cadre) + texte_frame.pack(side=LEFT, padx=10, fill=BOTH, expand=True) + + label_texte1 = Label(texte_frame, text=title, font=("Arial", 12, "bold")) + label_texte1.pack(anchor="w") + + label_texte2 = Label(texte_frame, text=subtitlte, font=("Arial", 10)) + label_texte2.pack(anchor="w") + + # Ajouter un bouton "Supprimer" + menu_button = Button(cadre, text="Supprimer", relief=FLAT, command=cadre.destroy) + menu_button.pack(side=RIGHT) + else: # Si c'est une erreur + ProgresseLabel.config(text=info) + + progress_windows.after(2000, progress_windows.destroy) # Fermer après 2s + + except queue.Empty: # Si la queue est vide, vérifier à nouveau + progress_windows.after(100, check_queue) + + # Création de la fenêtre de progression + progress_windows = Toplevel(root) + + # Barre de progression et label + progressBarMetahdonne = Progressbar(progress_windows, orient=HORIZONTAL, length=400, mode='indeterminate') + progressBarMetahdonne.pack(side=TOP) + progressBarMetahdonne.start() + + ProgresseLabel = Label(progress_windows, text="Téléchargement") + ProgresseLabel.pack(side=BOTTOM) + + # Démarrer le téléchargement dans un thread séparé + thread = threading.Thread(target=process_download, daemon=True) + thread.start() + + # Lancer la vérification périodique de la queue + check_queue() + + # Mainloop Tkinter + root.mainloop() + # Fenêtre principale root = Tk() root.title("Téléchargeur YouTube")