From 728be287f40b0d281168479e5a287b582340598c Mon Sep 17 00:00:00 2001 From: Phytacode Date: Sat, 16 Nov 2024 00:00:01 +0100 Subject: [PATCH] =?UTF-8?q?Ajout=20de=20la=20gestion=20des=20playlist,=20d?= =?UTF-8?q?es=20vid=C3=A9o=20n'ayant=20pas=20de=20jaquette=20et=20ajout=20?= =?UTF-8?q?du=20racourci=20clavier=20"entrer"=20dans=20la=20zone=20de=20te?= =?UTF-8?q?xte=20pour=20ajouter=20l'URL?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- main.py | 156 +++++++++++++++++++++++------------ res/img/jaquette_default.png | Bin 0 -> 1583 bytes 2 files changed, 102 insertions(+), 54 deletions(-) create mode 100644 res/img/jaquette_default.png diff --git a/main.py b/main.py index 2e98491..ed383c5 100644 --- a/main.py +++ b/main.py @@ -7,8 +7,12 @@ from PIL import Image, ImageTk from tkinter.ttk import Progressbar import threading import queue +from tkinter.messagebox import askyesno +import os -def add_url(): +res_directory = 'res' + +def add_url(url=False, dowload_playlist=False): #Ajoute un classe pour intercepter les logs class YTDLLogger: @@ -16,24 +20,104 @@ def add_url(): Logger personnalisé pour capturer et afficher les messages de yt-dlp. """ def debug(self, msg): - print(msg) # Affiche les messages de debug + # Affiche les messages de debug ProgresseLabel.config(text=msg) def warning(self, msg): - print(f"WARNING: {msg}") # Affiche les avertissements + # Affiche les avertissements + ProgresseLabel.config(text=f"WARNING: {msg}") def error(self, msg): - print(f"ERROR: {msg}") # Affiche les erreurs + # 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 - url = EntryURL.get() + #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(), # Utilise le logger personnalisé + '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() @@ -57,55 +141,18 @@ def add_url(): 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 + 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) - # 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) @@ -146,6 +193,7 @@ 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("", lambda event=None: add_url()) EntryButton = Button(EntryFrame, text="Ajouter", command=add_url) EntryButton.pack(side=RIGHT, padx=3.5) diff --git a/res/img/jaquette_default.png b/res/img/jaquette_default.png new file mode 100644 index 0000000000000000000000000000000000000000..e950f23d102da597b4deca4ddb03ea6b5710ad7e GIT binary patch literal 1583 zcmV+~2GIG5P)Px#1ZP1_K>z@;j|==^1poj532;bRa{vGi!~g&e!~vBn4jTXf1;j~2K~!i%?O9n! zmQ55sez{emn7LHqk`$rch$tpT7?z5NqM(6x5p-kZl?p*31u5u-7b!w$Q9;mhBNF0< zXpo8^O3;@#p_YmZiWaGecK$i@&$l1_cGKvAhnYEZ-kCY`&UR)z000FKJv1sj^!R7U zLBn5Th+8N7v17*|ARs`-*VfixeSKZV-2s;wa^%PnNJ~qD%*;%S;lqayadvhV?Zx-+-?6&78Uq6Z z*|P1S!+*rn(-X?d%HY9+2jK7TucRP{&(P2ieEReWrlzLg$B!RUPC8LhQIL?30Evl- zaP;U=DJQJ1uF7(nnwnr|XGclxhlO?4c=qgB93CE4{fa0m$F{aM%*)F|UteGL8)K4A zettfR;e}#osZtO>e*B1$k&)~-HjowW$}3l{;L6I1sv5DQqXXmO;#gf<7#|;xJv}|D zG{nWlMNCOaVRfyL6|&6A%EFC}4Ot->bK}MhR?iXMym=GFFqCEg`t=Lb)6-eKJ;(|z zn4Fv}DK@^YgNT@$qpydGaKy z=L()WbxL*=fmXt>urOASAuH5VqtRerU!N?PddrT29cDyC1kTLN$U3{ay3oVJgVi%5 zE7bGOojbBX8o#lzv8;|eNW)4j<+ASEw{NpLW@LquVrSN`P$eZLtd2WbT3RaWURqj0 zanxaTjK~TlX~!ne9>L|in>3_)dwXRYs;a739V4sK7RJQHusZue8qV9> z+c-BjhvGEB>L{{8%*n}-J@M?>Gd6!e`R2_V*_Moq3^rep72=a8Pvpl*Nl9$}ev(cT zgvXB`v-ygw5Wjr+f|HYzY~Fs8wg4JhV`F1%K9ThheSCc2+_`hm-QBGue-`Akh)EA( zzDU1^18Yclc)0vvWMo81{yeBHVgUlN^DBudSVM%PBSZ4$oCUeNe(qTXE~w zEjhoefm~kUf2fpgptgt=7{p;xNld{SLRU9LdwWuBShA7UAbc6PcI}!<*#1qjCu zrl+S>4i9#NdH?>s$;AL&-5fuDoYmFw_3PJ|nwpBD_w<`-U0od(7Z=MoIZ6~>y?Pa2 zzI>_wjw3l(tE;Q3c9d;-_wF65OWgI3&{YogtPV8x=?|HM;q$hb9#j#V)O#2~uSQygU+6tAGl`6%9450~YFO2^%2V2S%J`yxH zH>;EZ7KTJeN2}Pu#rgB+Rm^~0=g47$Lx#{;7sq^+aviiJzb?7 z!}j(z)YR0dlmYZ~;0(yY%FfP)ii!#)F@POf0|Nu{tYr>*$FK!d1})>}XlZFtG3!`a zSy3?q=*6p}qhr@sF8wxd%+se&>+gFB^mZY90ajN>I*QOev9Zkfh@vB1i53+VnaIp3 z+-)2k9aXg>e*XL!{rvn`UE-cERv{rFP*_+fU!C5*eG8kLn<~e42t(xS)vH&pl*D$> zNg_8l7c?3Tw70ig^^Yv9b0r@=dW51g*gRL#Wv{xsfqWWz7ZjsIN!$as8?s+;XlFl& h_-DvL!(U002ovPDHLkV1nKT_=Nxf literal 0 HcmV?d00001