import yt_dlp from PIL import Image 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() # 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") root.geometry("1000x500") # Cadre pour la barre d'URL EntryFrame = Frame(root) EntryFrame.pack(fill="x", padx=5, pady=2.5) EntryURL = Entry(EntryFrame) EntryURL.pack(side=LEFT, fill="x", expand=True, padx=2.5) EntryButton = Button(EntryFrame, text="Ajouter", command=add_url) EntryButton.pack(side=RIGHT, padx=3.5) # LabelFrame pour lister les vidéos MoviesList = LabelFrame(root, text="Vidéos à télécharger", relief=GROOVE) MoviesList.pack(fill="both", expand=True, padx=5, pady=2.5) # Canvas pour le défilement MoviesCanva = Canvas(MoviesList, highlightthickness=0) MoviesCanva.pack(side="left", fill="both", expand=True) # Barre de défilement verticale scrollbar = Scrollbar(MoviesList, orient="vertical", command=MoviesCanva.yview) scrollbar.pack(side="right", fill="y") MoviesCanva.configure(yscrollcommand=scrollbar.set) # Frame pour les vidéos à l'intérieur du Canvas scrollable_frame = Frame(MoviesCanva) canvas_frame = MoviesCanva.create_window((0, 0), window=scrollable_frame, anchor="nw") # Ajuster la taille du Canvas en fonction du contenu def on_frame_configure(event): MoviesCanva.configure(scrollregion=MoviesCanva.bbox("all")) scrollable_frame.bind("", on_frame_configure) # Ajuster la largeur du Canvas au redimensionnement MoviesCanva.bind("", lambda e: MoviesCanva.itemconfig(canvas_frame, width=e.width)) root.mainloop()