Compare commits

...

2 Commits

2 changed files with 87 additions and 5 deletions

View File

@ -96,7 +96,7 @@ def set_column_width(ws, column_range, width=15):
for col in column_range:
ws.column_dimensions[col].width = width
def export_xlsx(lignes, fichier_sortie="résumé.xlsx"):
def export_xlsx(lignes, output_directory, fichier_sortie="résumé.xlsx"):
# Charger le classeur existant ou en créer un nouveau
if os.path.exists(fichier_sortie):
wb = openpyxl.load_workbook(fichier_sortie)
@ -109,6 +109,9 @@ def export_xlsx(lignes, fichier_sortie="résumé.xlsx"):
# Créer une nouvelle feuille
ws = wb.create_sheet(title=date)
# Définir la nouvelle feuille comme active
wb.active = ws
# En-têtes
headers = ["Fichier", "Codec", "Résolution", "Action", "Durée", "Taille SRC", "Bitrate SRC",
"Taille DES", "Bitrate DES", "Gain %", "Résultat"]
@ -143,7 +146,7 @@ def export_xlsx(lignes, fichier_sortie="résumé.xlsx"):
gain_percent = calc_gain_percent(input_size, output_size, raw=True)
row = [
file, # string
f'=HYPERLINK("{os.path.abspath(output_directory)}/{file}","{file}")', # string
codec, # string
resolution, # string
action, # string
@ -550,7 +553,7 @@ def main():
))
xlsx_file = "résumé.xlsx"
export_xlsx(lignes, fichier_sortie=xlsx_file)
export_xlsx(lignes, output_directory, fichier_sortie=xlsx_file)
print(f"📝 Classeur généré : {xlsx_file}")
lever_inhibit_arret(inhibiteurs)

View File

@ -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) dun 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(