diff --git a/README.md b/README.md index 3840ab7..c9dc7d6 100644 --- a/README.md +++ b/README.md @@ -195,17 +195,19 @@ preferred_dirs = F:\VOD, H:\VOD Damit wird `F:\VOD` bevorzugt, wenn vorhanden. Sonst wird `H:\VOD` verwendet, wenn vorhanden. -Dateinamen: +Dateinamen (Beispiel fuer Quelldatei `EinCoolerFilm_FTR_1080p2398_DEU20_ENG51_ENG20.mov`): ```text -TheDarkKnight_DEU20_PVD.mp4 -TheDarkKnight_DEU_AUDIO_PCM.mov -TheDarkKnight_OV_AUDIO_PCM.mov +EinCoolerFilm_FTR_1080p2398_DEU20_ENG51_ENG20_PVD.mp4 +EinCoolerFilm_FTR_DEU20_AUDIO.mov +EinCoolerFilm_FTR_ENG51_ENG20_AUDIO.mov ``` -Der Titel-Teil (vor dem ersten Unterstrich) enthaelt keine Unterstriche – Woerter werden direkt zusammengefuegt. Unterstriche stehen nur im Audio-Suffix (`_DEU20_PVD`, `_DEU_AUDIO_PCM`, `_OV_AUDIO_PCM`). +- Das MP4 erhaelt einfach `_PVD` als Suffix – der Quelldateiname bleibt unveraendert. +- Die Audio-MOVs verwenden den Stem ohne Aufloesung/FPS-Token, gefolgt von den Sprach-Tokens der enthaltenen Spuren und `_AUDIO`. +- Bei mehreren Spuren in einer MOV werden die Tokens mit Unterstrich verbunden (`ENG51_ENG20`). -Wenn nur deutsche Tonspuren vorhanden sind, wird keine `..._OV_AUDIO_PCM.mov` erzeugt. +Wenn nur deutsche Tonspuren vorhanden sind, wird keine OV-MOV erzeugt. ## ProRes-Audio im Dateinamen diff --git a/pvd_mezzanine.py b/pvd_mezzanine.py index 3379bf4..154b59b 100644 --- a/pvd_mezzanine.py +++ b/pvd_mezzanine.py @@ -267,33 +267,56 @@ def strip_audio_tokens(stem: str) -> str: return cleaned or stem +def strip_resolution_token(stem: str) -> str: + """Entfernt Aufloesung/FPS-Token wie _1080p2398 oder _720p25 aus dem Stem.""" + cleaned = re.sub(r"_(?:\d{3,4}|4[Kk])[pPiI]\d{2,5}(?=_|$)", "", stem) + cleaned = re.sub(r"^(?:\d{3,4}|4[Kk])[pPiI]\d{2,5}_?", "", cleaned) + return cleaned.strip("_") + + def extract_title(input_path: str) -> str: - """Extrahiert den Titel einheitlich für alle Ausgabedateien (Blu-ray / ProRes / DVD).""" + """Titel aus Blu-ray-Ordnername, bereinigt und ohne Sonderzeichen.""" path = Path(input_path) path_parts = [p.upper() for p in path.parts] - - if "BDMV" in path_parts: - bdmv_index = path_parts.index("BDMV") - try: - project_folder = path_parts[bdmv_index - 4] - except IndexError: - project_folder = path_parts[bdmv_index - 1] if bdmv_index > 0 else "UNKNOWN" - clean_name = re.sub(r"^BEST_", "", project_folder, flags=re.IGNORECASE) - words = [w.capitalize() for w in clean_name.split("_") if w.strip()] - title = "".join(words) - else: - title = strip_audio_tokens(path.stem) - - title = re.sub(r"[^A-Za-z0-9]", "", title) + bdmv_index = path_parts.index("BDMV") + try: + project_folder = path_parts[bdmv_index - 4] + except IndexError: + project_folder = path_parts[bdmv_index - 1] if bdmv_index > 0 else "UNKNOWN" + clean_name = re.sub(r"^BEST_", "", project_folder, flags=re.IGNORECASE) + words = [w.capitalize() for w in clean_name.split("_") if w.strip()] + title = re.sub(r"[^A-Za-z0-9]", "", "".join(words)) return title or "UNKNOWN_TITLE" def get_pvd_filename(input_path: str) -> str: - return f"{extract_title(input_path)}_DEU20_PVD.mp4" + """MP4-Ausgabename: Quell-Stem + _PVD; bei Blu-ray: konstruierter Titel + _PVD.""" + path = Path(input_path) + if "BDMV" in [p.upper() for p in path.parts]: + return f"{extract_title(input_path)}_PVD.mp4" + return f"{path.stem}_PVD.mp4" -def safe_output_stem(input_path: str) -> str: - return extract_title(input_path) +def get_mov_stem(input_path: str) -> str: + """MOV-Basis ohne Aufloesung/FPS und ohne Audio-Token; bei Blu-ray: konstruierter Titel.""" + path = Path(input_path) + if "BDMV" in [p.upper() for p in path.parts]: + return extract_title(input_path) + return strip_resolution_token(strip_audio_tokens(path.stem)) + + +def role_token(role: AudioRole) -> str: + """Rekonstruiert den Dateinamen-Token einer Audiospur, z.B. DEU51 oder ENG20.""" + if role.layout_from_name: + return f"{role.language}{role.layout_from_name.replace('.', '')}" + ch = role.effective_channels + if ch == 2: + return f"{role.language}20" + if ch == 6: + return f"{role.language}51" + if ch == 8: + return f"{role.language}71" + return f"{role.language}{ch}0" def probe_metadata(filepath: str) -> dict: @@ -462,11 +485,13 @@ def build_plan(input_file: str, output_dir: str = OUTPUT_BASE_DIR) -> JobPlan: output_base = Path(output_dir) pvd_mp4 = str(output_base / get_pvd_filename(input_file)) - output_stem = safe_output_stem(input_file) + mov_stem = get_mov_stem(input_file) german_roles = [role for role in audio_roles if role.is_german] original_roles = [role for role in audio_roles if not role.is_german] - german_mov = str(output_base / f"{output_stem}_DEU_AUDIO_PCM.mov") if german_roles else None - original_mov = str(output_base / f"{output_stem}_OV_AUDIO_PCM.mov") if original_roles else None + german_tok = "_".join(role_token(r) for r in german_roles) + original_tok = "_".join(role_token(r) for r in original_roles) + german_mov = str(output_base / f"{mov_stem}_{german_tok}_AUDIO.mov") if german_roles else None + original_mov = str(output_base / f"{mov_stem}_{original_tok}_AUDIO.mov") if original_roles else None forced_srt_path = os.path.splitext(input_file)[0] + "_forced.srt" forced_srt = forced_srt_path if os.path.exists(forced_srt_path) else None