Telechargeur_youtube/main.py

228 lines
8.5 KiB
Python

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
from tkinter.messagebox import askyesno
import os
res_directory = 'res'
def add_url(url=False, dowload_playlist=False):
#Ajoute un classe pour intercepter les logs
class YTDLLogger:
"""
Logger personnalisé pour capturer et afficher les messages de yt-dlp.
"""
def debug(self, msg):
# Affiche les messages de debug
ProgresseLabel.config(text=msg)
def warning(self, msg):
# Affiche les avertissements
ProgresseLabel.config(text=f"WARNING: {msg}")
def error(self, msg):
# Affiche les erreurs
ProgresseLabel.config(text=f"ERROR: {msg}")
#Ajoute une fonction pour ajouter une vidéo a la liste
def add_movies(info, is_playlist=False) :
title = info.get('title', 'Titre indisponible')
view_count = info.get('view_count', None)
subtitlte = (
info.get('artist', info.get('uploader', 'Artiste indisponible'))
+ ' · '
+ 'Nombre de vues indisponible' if view_count is None else '{:,}'.format(view_count).replace(',', ' ')
+ ' vues'
)
thumbnail_url = info.get('thumbnail')
if is_playlist :
title = '[Playlist] ' + title
subtitlte += ' · ' + str(info.get('playlist_count', 'Nombre de vidéo indisponible')) + ' vidéos'
try:
thumbnail_url = info.get('thumbnails')[0].get('url')
except TypeError :
thumbnail_url = False
# Création d'un cadre pour chaque vidéo
cadre = Frame(scrollable_frame)
cadre.pack(fill=X, padx=10, pady=5)
if thumbnail_url :
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))
else :
image = Image.open(os.path.join(os.path.join(res_directory, "img"), 'jaquette_default.png'))
image.thumbnail((50, 50))
# 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)
#Récuperer l'url si non fournie comme argument
if not url:
url = EntryURL.get()
if '&list=' in url :
if askyesno('Playlist', 'Attention, vous vous apretez à ajouter une playlist générer automatiquement par youtube, Pour garder uniquement la vidéo que vous regardiez, appuyer sur oui, si vous voulez télecharger la playlist, appuyer sur non.') :
url = url.split('&list=')[0]
# Effacer l'entrée URL
EntryURL.delete(0, END)
ydl_opts = {
'quiet': True, # Supprime la sortie standard (redirigée vers le logger)
'logger': YTDLLogger(),
'extract_flat': True, # Utilise le logger personnalisé
}
if dowload_playlist :
ydl_opts['extract_flat'] = False
# 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")
if info.get('_type', None) == 'playlist' : #Si c'est une playlist
if dowload_playlist : # Vérifie si l'utilisateur a déja répondus a la question suivante
for video in info.get('entries') :
add_movies(video)
elif askyesno('Playlist', f"L'URL que vous avez fourni est une playlist, voulez-vous l'ajouter en tant que playlist ou en plusieurs vidéos ?\nAttention, en fonction du nombre de vidéos (ici {str(info.get('playlist_count', 'Nombre de vidéo indisponible'))}), cette opération peut prendre du temps\n\n(Oui -> playliste / Non -> Vidéos)") : #Demande a l'utilisateur si on ajoute chaque vidéo séparément
add_movies(info, True)
else :
add_url(url, True)
else :
add_movies(info)
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)
EntryURL.bind("<Return>", lambda event=None: add_url())
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("<Configure>", on_frame_configure)
# Ajuster la largeur du Canvas au redimensionnement
MoviesCanva.bind("<Configure>", lambda e: MoviesCanva.itemconfig(canvas_frame, width=e.width))
root.mainloop()