From 593df4b028a458121104a77132fec2ac50fee496 Mon Sep 17 00:00:00 2001 From: lionel <.> Date: Mon, 9 Jun 2025 09:38:33 +0200 Subject: [PATCH] =?UTF-8?q?[tree=5Fstream]=20Ajout=20du=20titre=20du=20m?= =?UTF-8?q?=C3=A9dia=20et=20du=20d=C3=A9bit=20binaire?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tree_stream/tree_stream/main.py | 83 ++++++++++++++++++++++++++++++++- 1 file changed, 81 insertions(+), 2 deletions(-) diff --git a/tree_stream/tree_stream/main.py b/tree_stream/tree_stream/main.py index 8f97e03..146512a 100644 --- a/tree_stream/tree_stream/main.py +++ b/tree_stream/tree_stream/main.py @@ -40,6 +40,58 @@ def couleur(nom_fichier, est_dossier): else: return nom_fichier +def nettoyer_tags(tags): + """ + Crée un dictionnaire normalisé à partir des tags, + en supprimant les suffixes comme '-eng', '-fre', etc. + Si plusieurs clés mènent à la même base, la première rencontrée est conservée. + """ + tags_sans_suffixes = {} + for cle, valeur in tags.items(): + base = cle.split('-')[0].strip().upper() + if base not in tags_sans_suffixes: + tags_sans_suffixes[base] = valeur + return tags_sans_suffixes + +def estimer_kbps(stream, probe): + """ + Estime le débit binaire (kb/s) d’un flux multimédia. + Priorité : + 1. stream['bit_rate'] + 2. stream['stream_size'] + probe['format']['duration'] + 3. stream['tags']['BPS'] + 4. stream['tags']['NUMBER_OF_BYTES'] + stream['tags']['DURATION'] + """ + tags = nettoyer_tags(stream.get('tags', {})) + + try: + # Méthode 1 : bit_rate direct + if 'bit_rate' in stream: + return int(stream['bit_rate']) // 1000 + + # Méthode 2 : stream_size + durée globale + if 'stream_size' in stream and 'format' in probe and 'duration' in probe['format']: + taille = int(stream['stream_size']) + duree = float(probe['format']['duration']) + return int((taille * 8) / duree / 1000) + + # Méthode 3 : BPS dans les tags + if 'BPS' in tags: + return int(tags['BPS']) // 1000 + + # Méthode 4 : NUMBER_OF_BYTES + DURATION dans les tags + if 'NUMBER_OF_BYTES' in tags and 'DURATION' in tags: + total_octets = int(tags['NUMBER_OF_BYTES']) + duree_texte = tags['DURATION'] # Format HH:MM:SS.microsec + h, m, s = duree_texte.split(':') + secondes = int(h) * 3600 + int(m) * 60 + float(s) + return int((total_octets * 8) / secondes / 1000) + + except (ValueError, TypeError, ZeroDivisionError): + pass + + return -1 # Si toutes les méthodes échouent + def analyser_fichier_video(chemin): try: probe = ffmpeg.probe(chemin, show_chapters=None) # Contrairement à ce que l'on pourrait croire, on demande les chapitres, c'est l'option show_chapters qui ne prend pas de paramètre. @@ -69,7 +121,8 @@ def analyser_fichier_video(chemin): f"{ROUGE}{i}{NORMAL}" f"{BLANC}:{NORMAL} " f"{BLEU}{codec_type:<8}{NORMAL} " - f"{BLANC}({codec_name}, {langue}){NORMAL}" + f"{BLANC}({codec_name:<6}, {langue:<3}){NORMAL} " + f"{ORANGE}{estimer_kbps(stream, probe):>5.0f} kb/s{NORMAL}" ) # Ajouter les flags défaut/forcé s'ils existent @@ -99,7 +152,32 @@ def analyser_fichier_video(chemin): except Exception as e: return [f"{ROUGE}Erreur : {e}{NORMAL}"] +def recuperer_titre_media(chemin): + """ + @brief Récupère le titre d'un média via ffmpeg.probe. + + @param chemin Chemin vers le fichier média. + @return Le titre formaté avec couleurs et guillemets si disponible, sinon chaîne vide. + """ + try: + probe = ffmpeg.probe(chemin) + titre = probe.get('format', {}).get('tags', {}).get('title', None) + if titre is not None: + return f" {VERT}\"{titre}\"{NORMAL}" + except Exception: + pass + return "" + def afficher_arborescence(dossier, prefixe="", tout_afficher=False, niveau_max=None, niveau_actuel=0): + """ + @brief Affiche l'arborescence d'un dossier avec détails sur les fichiers vidéo. + + @param dossier Dossier à parcourir. + @param prefixe Préfixe pour l'affichage en mode arborescence. + @param tout_afficher Booléen pour afficher ou non les fichiers cachés. + @param niveau_max Profondeur maximale d'affichage (None pour illimité). + @param niveau_actuel Niveau courant de récursion (interne). + """ if niveau_max is not None and niveau_actuel > niveau_max: return @@ -120,7 +198,8 @@ def afficher_arborescence(dossier, prefixe="", tout_afficher=False, niveau_max=N branche = "└── " if est_dernier else "├── " sous_prefixe = " " if est_dernier else "│ " est_dossier = os.path.isdir(chemin_complet) - print(prefixe + branche + couleur(nom, est_dossier)) + titre = recuperer_titre_media(chemin_complet) + print(prefixe + branche + couleur(nom, est_dossier) + titre) if est_dossier: afficher_arborescence(