#!/usr/bin/env python3 """Apply flashback-prologue reorder to Riddick Unjinxed.kdenlive and regen shifted subs.""" from __future__ import annotations import re import sys from pathlib import Path ROOT = Path(__file__).resolve().parent sys.path.insert(0, str(ROOT.parent.parent / "scripts")) from kdenlive_from_subs import Cue, build_ass, build_shifted_srt, parse_srt # noqa: E402 KDENLIVE = ROOT / "Riddick (2013) - Unjinxed.kdenlive" SRT = ROOT / "Riddick (2013) - Unjinxed.srt" # Kept segments (source seconds) — prologue + main + tail from existing playlist2 KEPT_NEW_HEAD: list[tuple[float, float]] = [ (10 * 60 + 54.487, 13 * 60 + 25.067), (15 * 60 + 50.717, 22 * 60 + 51.536), (1 * 60 + 13.573, 10 * 60 + 54.487), (22 * 60 + 51.536, 24 * 60 + 3.733), (24 * 60 + 3.733, 40 * 60 + 50.342), ] TAIL_ENTRIES = """ 4 1 1 brightness fade_from_black 00:00:00.000=0;00:00:06.965=1 0 4 4 1 1 brightness fade_from_black 00:00:00.000=0;00:00:03.086=1 0 4 1 1 brightness fade_from_black 00:00:00.000=0;00:00:05.172=1 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 1 1 brightness fade_to_black 00:00:00.000=1;00:00:02.920=0 0 4 1 1 brightness fade_from_black 00:00:00.000=0;00:00:06.298=1 0 4 """ def tc_to_sec(tc: str) -> float: h, m, rest = tc.split(":") s, ms = rest.split(".") return int(h) * 3600 + int(m) * 60 + int(s) + int(ms) / 1000.0 def sec_to_tc(sec: float) -> str: h = int(sec // 3600) sec -= h * 3600 m = int(sec // 60) sec -= m * 60 s = int(sec) ms = int(round((sec - s) * 1000)) if ms >= 1000: ms = 0 s += 1 return f"{h:02d}:{m:02d}:{s:02d}.{ms:03d}" TAIL_KEPT: list[tuple[float, float]] = [ (44 * 60 + 11.094, 55 * 60 + 17.893), (55 * 60 + 37.955, 64 * 60 + 22.147), (66 * 60 + 42.0, 68 * 60 + 2.669), (68 * 60 + 59.643, 70 * 60 + 30.113), (74 * 60 + 19.853, 82 * 60 + 34.493), (82 * 60 + 34.534, 82 * 60 + 44.294), (84 * 60 + 23.273, 84 * 60 + 36.787), (84 * 60 + 36.828, 84 * 60 + 38.830), (84 * 60 + 40.874, 84 * 60 + 43.168), (84 * 60 + 43.210, 84 * 60 + 48.632), (84 * 60 + 53.720, 85 * 60 + 25.275), (85 * 60 + 27.319, 85 * 60 + 45.337), (86 * 60 + 16.391, 86 * 60 + 27.569), (86 * 60 + 27.611, 88 * 60 + 14.514), (88 * 60 + 36.578, 89 * 60 + 25.150), (89 * 60 + 58.684, 90 * 60 + 23.357), (91 * 60 + 27.819, 96 * 60 + 4.046), (97 * 60 + 7.591, 98 * 60 + 36.995), (98 * 60 + 37.037, 98 * 60 + 55.764), (99 * 60 + 6.840, 99 * 60 + 13.305), (99 * 60 + 32.783, 99 * 60 + 40.916), (99 * 60 + 40.958, 100 * 60 + 14.223), (100 * 60 + 32.783, 101 * 60 + 27.444), (101 * 60 + 38.497, 105 * 60 + 10.969), (105 * 60 + 21.563, 119 * 60 + 2.711), (119 * 60 + 9.426, 119 * 60 + 31.198), (119 * 60 + 31.240, 126 * 60 + 47.631), ] def build_playlist2() -> str: head = """ 4 4 1 1 brightness fade_to_black 00:00:00.000=1;00:00:02.460=0 4 1 1 brightness fade_from_black 00:00:00.000=0;00:00:03.086=1 0 4 4 1 1 brightness fade_to_black 00:00:00.000=1;00:00:01.460=0 """ return head + TAIL_ENTRIES + " \n" def total_out_sec(kept: list[tuple[float, float]]) -> float: return sum(b - a for a, b in kept) def main() -> None: text = KDENLIVE.read_text(encoding="utf-8") new_pl = build_playlist2() text, n = re.subn( r" .*?\n", new_pl, text, count=1, flags=re.DOTALL, ) if n != 1: raise SystemExit("playlist2 replace failed") all_kept = KEPT_NEW_HEAD + TAIL_KEPT out_dur = total_out_sec(all_kept) out_tc = sec_to_tc(out_dur - 1 / 24) # frame-ish trim for MLT out text = re.sub( r'({out_tc}\2", text, count=1, ) text = re.sub( r'({out_tc}\2", text, count=1, ) dur_prop = out_tc.replace(".", ":") if len(dur_prop.split(":")[-1]) > 3: parts = dur_prop.split(":") dur_prop = f"{parts[0]}:{parts[1]}:{parts[2]}.{parts[3][:3]}" if len(parts) > 3 else dur_prop # kdenlive:duration uses HH:MM:SS.mmm style in file dur_kd = out_tc text = re.sub( r'(\s*)[^<]+', rf"\g<1>{dur_kd}", text, count=1, ) text = re.sub( r'({out_tc}\2", text, count=1, ) track_pat = r'()' text = re.sub(track_pat, rf"\g<1>{out_tc}\2", text, count=1) KDENLIVE.write_text(text, encoding="utf-8") print(f"Updated {KDENLIVE.name} duration ~{out_tc}") cues = parse_srt(SRT) all_kept_full = KEPT_NEW_HEAD + TAIL_KEPT shifted = build_shifted_srt(cues, all_kept_full) ass = build_ass(cues, all_kept_full) (ROOT / "Riddick (2013) - Unjinxed.kdenlive.shifted.srt").write_text( shifted, encoding="utf-8" ) (ROOT / "Riddick (2013) - Unjinxed.kdenlive.ass").write_text(ass, encoding="utf-8") print("Wrote shifted SRT and ASS") if __name__ == "__main__": main()