import os import sys import glob from pymkv import MKVFile, MKVTrack from pathlib import Path #: Dictionnaire de correspondance entre les codes de langue et leur nom complet en français LANG_MAP = { 'fre': 'Français', 'eng': 'Anglais', 'ita': 'Italien', 'spa': 'Espagnol', 'por': 'Portugais', 'rus': 'Russe', 'jpn': 'Japonais', 'chi': 'Chinois', 'kor': 'Coréen', 'nld': 'Néerlandais', } def ligne_bleu(): """Affiche une ligne bleue pleine largeur dans le terminal.""" cols = os.get_terminal_size().columns print("\033[0;34m" + "█" * cols + "\033[0m") def find_episode_file(source_dir, saison, episode): """ Recherche un fichier d'épisode dans un répertoire donné. :param source_dir: Répertoire source :param saison: Numéro de la saison :param episode: Numéro de l'épisode :return: Chemin du fichier trouvé ou None """ pattern = f"*S0{saison}E{episode}*.mkv" files = glob.glob(os.path.join(source_dir, pattern)) return files[0] if files else None def find_first_video_track_index(mkv_file): """ Recherche l'index de la première piste vidéo dans un fichier MKV. :param mkv_file: Objet MKVFile :return: Index de la piste vidéo ou None """ for idx, track in enumerate(mkv_file.tracks): if track.track_type == 'video': return idx return None def has_named_chapters(chapters): """ Détermine si une liste de chapitres contient des noms réels (autres que des génériques). """ import re pattern = re.compile(r'(?i)^\s*(chapter|chapitre)\s*\d+\s*$') return any(not pattern.match(c.name or '') for c in chapters) def process_episode(episode, source_dir_1, source_dir_2, saison, serie_name, dest_dir): """ 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 source_dir_1: Répertoire contenant la source vidéo :param source_dir_2: Répertoire contenant la source audio/sous-titres :param saison: Numéro de saison :param serie_name: Nom de la série :param dest_dir: Répertoire de sortie """ # Affiche une ligne bleue pour la lisibilité dans le terminal ligne_bleu() # Recherche les fichiers source correspondants à l'épisode source_1_file = find_episode_file(source_dir_1, saison, episode) source_2_file = find_episode_file(source_dir_2, saison, episode) # Gère les cas où un fichier source est manquant if not source_1_file and source_2_file: print(f"Fichier source 1 manquant pour l'épisode {episode}") return if source_1_file and not source_2_file: print(f"Fichier source 2 manquant pour l'épisode {episode}") return if not source_1_file and not source_2_file: 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 mkv = MKVFile() # Charge les fichiers source source_1_mkv = MKVFile(source_1_file) source_2_mkv = MKVFile(source_2_file) # Ajout de la piste vidéo principale video_track_index = find_first_video_track_index(source_1_mkv) if video_track_index is not None: mkv.add_track(MKVTrack(source_1_file, track_id=video_track_index)) # 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_other = [t for t in source_2_mkv.tracks if t.track_type == 'audio' and t.language != 'fre'] for idx, track in enumerate(audio_tracks_fr): track.default_track = (idx == 0) if not track.track_name: track.track_name = LANG_MAP.get(track.language, track.language.title()) for track in audio_tracks_other: track.default_track = False if not track.track_name: track.track_name = LANG_MAP.get(track.language, track.language.title()) # Sélection des pistes sous-titres (FR prioritaires) 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'] for track in subtitle_tracks_fr + subtitle_tracks_other: track.default_track = False # Renommage des pistes FR si exactement deux if len(subtitle_tracks_fr) == 2: for track in subtitle_tracks_fr: codec = track.codec_id.upper() if track.forced_track: track.track_name = f"FR Forcé [{codec}]" else: track.track_name = f"FR Complet [{codec}]" # Ajout de toutes les pistes audio et sous-titres for track in audio_tracks_fr + audio_tracks_other + subtitle_tracks_fr + subtitle_tracks_other: mkv.add_track(track) # Traitement des chapitres : choix du fichier avec le plus de chapitres valides chapters_1 = source_1_mkv.chapters or [] chapters_2 = source_2_mkv.chapters or [] if len(chapters_1) >= 4 and has_named_chapters(chapters_1) and len(chapters_1) >= len(chapters_2): mkv.chapters = chapters_1 elif len(chapters_2) >= 4 and has_named_chapters(chapters_2): mkv.chapters = chapters_2 else: mkv.chapters = None # Supprime le titre et génère le fichier final mkv.title = "" mkv.mux(output_path) print(f"✔ Fichier généré : {output_path}") def main(): """ Point d'entrée principal du script. Lit les arguments de ligne de commande, prépare les répertoires, et lance le traitement de chaque épisode (de 01 à 30). """ if len(sys.argv) != 5: print("Usage: script.py ") sys.exit(1) source_dir_1 = sys.argv[1] source_dir_2 = sys.argv[2] serie_name = sys.argv[3] saison = sys.argv[4] dest_dir = f"/media/data/reencoded/{serie_name}/Saison {saison}" Path(dest_dir).mkdir(parents=True, exist_ok=True) for i in range(1, 31): episode = f"{i:02}" process_episode(episode, source_dir_1, source_dir_2, saison, serie_name, dest_dir) if __name__ == '__main__': main()