[multiplex] Correction multiple + première utilisation réelle

This commit is contained in:
lionel
2025-06-26 15:15:14 +02:00
parent 5d7ad7ebb0
commit 4bb36fe4c8
2 changed files with 83 additions and 38 deletions

View File

@ -1,9 +1,9 @@
import os import os
import sys import sys
import glob import glob
from pymkv import MKVFile, MKVTrack import re
from pathlib import Path from pathlib import Path
from pymkv import MKVFile, MKVTrack
#: Dictionnaire de correspondance entre les codes de langue et leur nom complet en français #: Dictionnaire de correspondance entre les codes de langue et leur nom complet en français
LANG_MAP = { LANG_MAP = {
@ -19,23 +19,42 @@ LANG_MAP = {
'nld': 'Néerlandais', 'nld': 'Néerlandais',
} }
TRADUCTIONS = {
"Forced": "Forcé",
"Full": "Complet",
"French": "Français",
"English": "Anglais",
"Japan": "Japonais",
"subtitle": "Sous-titre",
}
def traduire(texte):
for ancien, nouveau in TRADUCTIONS.items():
texte = re.sub(re.escape(ancien), nouveau, texte, flags=re.IGNORECASE)
return texte
def ligne_bleu(): def ligne_bleu():
"""Affiche une ligne bleue pleine largeur dans le terminal.""" """Affiche une ligne bleue pleine largeur dans le terminal."""
try:
cols = os.get_terminal_size().columns cols = os.get_terminal_size().columns
except OSError:
cols = 100
print("\033[0;34m" + "" * cols + "\033[0m") print("\033[0;34m" + "" * cols + "\033[0m")
def find_episode_file(source_dir, saison, episode): def find_episode_file(source_dir, saison, episode):
""" """
Recherche un fichier d'épisode dans un répertoire donné. Recherche un fichier d'épisode dans un répertoire donné (insensible à la casse).
:param source_dir: Répertoire source :param source_dir: Répertoire source
:param saison: Numéro de la saison :param saison: Numéro de la saison
:param episode: Numéro de l'épisode :param episode: Numéro de l'épisode
:return: Chemin du fichier trouvé ou None :return: Chemin du fichier trouvé ou None
""" """
pattern = f"*S0{saison}E{episode}*.mkv" target = f"s0{saison}e{episode}".lower()
files = glob.glob(os.path.join(source_dir, pattern)) for file in glob.glob(os.path.join(source_dir, "*.mkv")):
return files[0] if files else None if target in os.path.basename(file).lower():
return file
return None
def find_first_video_track_index(mkv_file): def find_first_video_track_index(mkv_file):
""" """
@ -61,20 +80,26 @@ def process_episode(episode, source_dir_1, source_dir_2, saison, serie_name, des
""" """
Traite un épisode en combinant les pistes vidéo et audio/sous-titres de deux sources. Traite un épisode en combinant les pistes vidéo et audio/sous-titres de deux sources.
La vidéo provient de source_1_file.
Les pistes audio et sous-titres sont priorisées depuis source_2_file.
Les pistes audio/sous-titres en français sont réorganisées, annotées et une seule est définie par défaut.
:param episode: Numéro de l'épisode (format chaîne "01", "02", etc.) :param episode: Numéro de l'épisode (format chaîne "01", "02", etc.)
:param source_dir_1: Répertoire contenant la source vidéo :param source_dir_1: Répertoire contenant la source vidéo
:param source_dir_2: Répertoire contenant la source audio/sous-titres :param source_dir_2: Répertoire contenant la source audio/sous-titres
:param saison: Numéro de saison :param saison: Numéro de saison
:param serie_name: Nom de la série :param serie_name: Nom de la série
:param dest_dir: Répertoire de sortie :param dest_dir: Répertoire de sortie
:return: None
""" """
# Affiche une ligne bleue pour la lisibilité dans le terminal # Affiche une ligne bleue pour la lisibilité dans le terminal
ligne_bleu() ligne_bleu()
# Crée le chemin complet pour le fichier de sortie
output_filename = f"{serie_name} S0{saison} E{episode}.mkv"
output_path = os.path.join(dest_dir, output_filename)
# Vérifie si le fichier final existe déjà
if os.path.exists(output_path):
print(f'\033[91m{output_filename} existe déjà.\033[0m')
return
# Recherche les fichiers source correspondants à l'épisode # Recherche les fichiers source correspondants à l'épisode
source_1_file = find_episode_file(source_dir_1, saison, episode) source_1_file = find_episode_file(source_dir_1, saison, episode)
source_2_file = find_episode_file(source_dir_2, saison, episode) source_2_file = find_episode_file(source_dir_2, saison, episode)
@ -89,12 +114,8 @@ def process_episode(episode, source_dir_1, source_dir_2, saison, serie_name, des
if not source_1_file and not source_2_file: if not source_1_file and not source_2_file:
return return
# Crée le chemin complet pour le fichier de sortie
output_filename = f"{serie_name} S0{saison} E{episode}.mkv"
output_path = os.path.join(dest_dir, output_filename)
# Crée un objet MKVFile pour le fichier final # Crée un objet MKVFile pour le fichier final
mkv = MKVFile() final_mkv = MKVFile()
# Charge les fichiers source # Charge les fichiers source
source_1_mkv = MKVFile(source_1_file) source_1_mkv = MKVFile(source_1_file)
@ -103,7 +124,7 @@ def process_episode(episode, source_dir_1, source_dir_2, saison, serie_name, des
# Ajout de la piste vidéo principale # Ajout de la piste vidéo principale
video_track_index = find_first_video_track_index(source_1_mkv) video_track_index = find_first_video_track_index(source_1_mkv)
if video_track_index is not None: if video_track_index is not None:
mkv.add_track(MKVTrack(source_1_file, track_id=video_track_index)) final_mkv.add_track(source_1_mkv.tracks[video_track_index])
# Sélection des pistes audio (FR prioritaires) # Sélection des pistes audio (FR prioritaires)
audio_tracks_fr = [t for t in source_2_mkv.tracks if t.track_type == 'audio' and t.language == 'fre'] audio_tracks_fr = [t for t in source_2_mkv.tracks if t.track_type == 'audio' and t.language == 'fre']
@ -123,35 +144,59 @@ def process_episode(episode, source_dir_1, source_dir_2, saison, serie_name, des
subtitle_tracks_fr = [t for t in source_2_mkv.tracks if t.track_type == 'subtitles' and t.language == 'fre'] subtitle_tracks_fr = [t for t in source_2_mkv.tracks if t.track_type == 'subtitles' and t.language == 'fre']
subtitle_tracks_other = [t for t in source_2_mkv.tracks if t.track_type == 'subtitles' and t.language != 'fre'] subtitle_tracks_other = [t for t in source_2_mkv.tracks if t.track_type == 'subtitles' and t.language != 'fre']
for track in subtitle_tracks_fr + subtitle_tracks_other: for idx, track in enumerate(subtitle_tracks_fr):
track.default_track = False track.default_track = (idx == 0)
if not track.track_name:
# Renommage des pistes FR si exactement deux codec = track._track_codec
if len(subtitle_tracks_fr) == 2:
for track in subtitle_tracks_fr:
codec = track.codec_id.upper()
if track.forced_track: if track.forced_track:
track.track_name = f"FR Forcé [{codec}]" track.track_name = f"FR Forcé [{codec}]"
else: else:
track.track_name = f"FR Complet [{codec}]" track.track_name = f"FR Complet [{codec}]"
for track in subtitle_tracks_other:
track.default_track = False
# Ajout de toutes les pistes audio et sous-titres # Ajout de toutes les pistes audio et sous-titres
for track in audio_tracks_fr + audio_tracks_other + subtitle_tracks_fr + subtitle_tracks_other: for track in audio_tracks_fr + audio_tracks_other + subtitle_tracks_fr + subtitle_tracks_other:
mkv.add_track(track) # Traduction des noms des pistes
track.track_name = traduire(track.track_name)
final_mkv.add_track(track)
# Traitement des chapitres : choix du fichier avec le plus de chapitres valides # Traitement des chapitres
chapters_1 = source_1_mkv.chapters or [] try:
chapters_2 = source_2_mkv.chapters or [] chapters_1 = (source_1_mkv._info_json.get('chapters'))[0].get('num_entries')
if len(chapters_1) >= 4 and has_named_chapters(chapters_1) and len(chapters_1) >= len(chapters_2): except (AttributeError, IndexError):
mkv.chapters = chapters_1 chapters_1 = 0
elif len(chapters_2) >= 4 and has_named_chapters(chapters_2): try:
mkv.chapters = chapters_2 chapters_2 = (source_2_mkv._info_json.get('chapters'))[0].get('num_entries')
except (AttributeError, IndexError):
chapters_2 = 0
valid_1 = chapters_1 >= 4
valid_2 = chapters_2 >= 4
if valid_1 and len(chapters_1) >= len(chapters_2):
source_2_mkv.no_chapters()
elif valid_2:
source_1_mkv.no_chapters()
else: else:
mkv.chapters = None source_1_mkv.no_chapters()
source_2_mkv.no_chapters()
# Supprime le titre et génère le fichier final
mkv.title = "" # Éffacer tous les tags
mkv.mux(output_path) source_1_mkv.no_global_tags()
source_2_mkv.no_global_tags()
final_mkv.no_global_tags()
source_1_mkv.no_track_tags()
source_2_mkv.no_track_tags()
final_mkv.no_track_tags()
final_mkv.title = ""
#print(final_mkv.command(output_path))
final_mkv.mux(output_path)
print(f"✔ Fichier généré : {output_path}") print(f"✔ Fichier généré : {output_path}")
def main(): def main():
@ -162,7 +207,7 @@ def main():
et lance le traitement de chaque épisode (de 01 à 30). et lance le traitement de chaque épisode (de 01 à 30).
""" """
if len(sys.argv) != 5: if len(sys.argv) != 5:
print("Usage: script.py <SOURCE_DIR_1> <SOURCE_DIR_2> <SERIE_NAME> <SAISON>") print(f'Usage: {sys.argv[0]} <SOURCE_DIR_1> <SOURCE_DIR_2> <SERIE_NAME> <SAISON>')
sys.exit(1) sys.exit(1)
source_dir_1 = sys.argv[1] source_dir_1 = sys.argv[1]

View File

@ -13,5 +13,5 @@ setup(
'multiplex=multiplex:main', 'multiplex=multiplex:main',
], ],
}, },
python_requires='>=3.6', python_requires='>=3.9',
) )