[multiplex] Correction multiple + première utilisation réelle
This commit is contained in:
@ -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]
|
||||||
|
@ -13,5 +13,5 @@ setup(
|
|||||||
'multiplex=multiplex:main',
|
'multiplex=multiplex:main',
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
python_requires='>=3.6',
|
python_requires='>=3.9',
|
||||||
)
|
)
|
||||||
|
Reference in New Issue
Block a user