From 4735837d1f195f8f2b2f521e42f5f917084041cc Mon Sep 17 00:00:00 2001 From: Mariano Gabriel Date: Tue, 9 Jun 2026 11:09:37 -0300 Subject: [PATCH] mitus helper --- interleave_cht_frames.py | 120 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 120 insertions(+) create mode 100644 interleave_cht_frames.py diff --git a/interleave_cht_frames.py b/interleave_cht_frames.py new file mode 100644 index 0000000..d750d66 --- /dev/null +++ b/interleave_cht_frames.py @@ -0,0 +1,120 @@ +#!/usr/bin/env python3 +""" +Build an enhanced transcript that interleaves audio segments (from a +whisperx JSON) with screen frames sourced from a cht session's +frames/index.json. Frames are placed at their real timestamps rather +than appended at the end. + +Usage: + python interleave_cht_frames.py \\ + [output.txt] +""" +import json +import sys +from pathlib import Path + + +def fmt_ts(seconds: float) -> str: + seconds = int(seconds) + return f"{seconds // 60:02d}:{seconds % 60:02d}" + + +def load_audio(transcript_json: Path): + data = json.loads(transcript_json.read_text()) + out = [] + for s in data.get("segments", []): + out.append({ + "type": "audio", + "ts": float(s.get("start", 0)), + "speaker": s.get("speaker"), + "text": (s.get("text") or "").strip(), + }) + return out + + +def load_frames(index_json: Path): + data = json.loads(index_json.read_text()) + out = [] + for f in data: + out.append({ + "type": "frame", + "ts": float(f["timestamp"]), + "path": f["path"], + "id": f.get("id", ""), + }) + return out + + +def merge(audio, frames): + events = sorted(audio + frames, key=lambda e: e["ts"]) + grouped = [] + cur = None + for e in events: + if e["type"] == "frame": + if cur: + grouped.append(cur) + cur = None + grouped.append(e) + else: + if cur and cur.get("speaker") == e["speaker"]: + cur["text"] += " " + e["text"] + else: + if cur: + grouped.append(cur) + cur = { + "type": "audio", + "ts": e["ts"], + "speaker": e["speaker"], + "text": e["text"], + } + if cur: + grouped.append(cur) + return grouped + + +def render(grouped): + lines = [ + "=" * 80, + "ENHANCED MEETING TRANSCRIPT", + "Audio transcript + Screen frames (interleaved by timestamp)", + "=" * 80, + "", + ] + for e in grouped: + ts = fmt_ts(e["ts"]) + if e["type"] == "frame": + lines.append(f"[{ts}] SCREEN CONTENT:") + lines.append(f" Frame: {e['path']}") + else: + spk = e["speaker"] or "UNKNOWN" + lines.append(f"[{ts}] {spk}:") + lines.append(f" {e['text']}") + lines.append("") + return "\n".join(lines) + + +def main(): + if len(sys.argv) < 3: + print(__doc__, file=sys.stderr) + sys.exit(2) + transcript = Path(sys.argv[1]) + frames_index = Path(sys.argv[2]) + output = Path(sys.argv[3]) if len(sys.argv) >= 4 else None + + audio = load_audio(transcript) + frames = load_frames(frames_index) + grouped = merge(audio, frames) + + text = render(grouped) + if output: + output.write_text(text) + print(f"Wrote {output}") + print(f" audio segments in : {len(audio)}") + print(f" frames in : {len(frames)}") + print(f" output blocks : {len(grouped)}") + else: + sys.stdout.write(text) + + +if __name__ == "__main__": + main()