#!/usr/bin/env python3 """Convert TMPlayer subtitles (HH:MM:SS:text, '|' = line break) to SRT. TMPlayer stores only a start time per line, so end times are inferred: each cue ends when the next one starts (minus a small gap), or earlier if its text is short enough to be read sooner. python3 tmp2srt.py input/film.txt [output.srt] """ import re import sys from pathlib import Path # A separator after the timestamp is optional in the wild: some lines use # ':', some a bare space, some nothing at all. LINE_RE = re.compile(r"^\s*(\d{1,2}):(\d{2}):(\d{2}):?\s*(.*)$") CPS = 16.0 # reading speed used to size a cue when there is room MIN_DUR = 1.2 # s MAX_DUR = 7.0 # s GAP = 0.08 # s left between consecutive cues def _fmt(t: float) -> str: ms = int(round(t * 1000)) h, ms = divmod(ms, 3600000) m, ms = divmod(ms, 60000) s, ms = divmod(ms, 1000) return f"{h:02d}:{m:02d}:{s:02d},{ms:03d}" def parse(path: Path): cues, skipped = [], [] for n, raw in enumerate(path.read_text(encoding="utf-8-sig").splitlines(), 1): if not raw.strip(): continue m = LINE_RE.match(raw) if not m: skipped.append((n, raw)) continue h, mi, s, text = m.groups() text = text.replace("|", "\n").strip() if not text: continue cues.append((int(h) * 3600 + int(mi) * 60 + int(s), text)) cues.sort(key=lambda c: c[0]) return cues, skipped def to_srt(cues) -> str: out = [] for i, (start, text) in enumerate(cues): wanted = len(text.replace("\n", " ")) / CPS end = start + max(MIN_DUR, min(MAX_DUR, wanted)) if i + 1 < len(cues): end = min(end, cues[i + 1][0] - GAP) if end <= start: # cues closer together than MIN_DUR end = (cues[i + 1][0] if i + 1 < len(cues) else start + MIN_DUR) - 0.01 if end <= start: end = start + 0.1 out.append(f"{i + 1}\n{_fmt(start)} --> {_fmt(end)}\n{text}\n") return "\n".join(out) def main(): if len(sys.argv) < 2: sys.exit(__doc__) src = Path(sys.argv[1]) dst = Path(sys.argv[2]) if len(sys.argv) > 2 else src.with_suffix(".srt") cues, skipped = parse(src) dst.write_text(to_srt(cues), encoding="utf-8") print(f"{src} -> {dst}: {len(cues)} cues") for n, raw in skipped: print(f" skipped line {n}: {raw[:70]!r}") if __name__ == "__main__": main()