"""The base-clip + variant + annotation manifest the simulator renders. Replaces simulator/fixtures.py (the selection-era synthetic catalog). Each base clip carries: the raw base file, a map of pre-baked Right-strength variant files (strength 0 is always the raw base), an authored Left annotation track (box + label key + the minimum Left level at which it appears), and per-language string tables. See the reconciled-simulator-alteration-slice design ยง3.2. """ from __future__ import annotations import json from dataclasses import dataclass from pathlib import Path from typing import Any @dataclass(frozen=True) class Clip: id: str title: str base_file: str license: str source: str right_variants: dict # {"1": {"file": ...}, "4": {...}} (no "0") annotations: list # [{"key", "box":[x,y,w,h], "min_level"}, ...] strings: dict # {"en": {key: text}} def variant_file(self, strength: int) -> str: """The video file for a Right strength; 0 and any unauthored strength fall back to the raw base file.""" entry = self.right_variants.get(str(strength)) return entry["file"] if entry else self.base_file def to_dict(self) -> dict: variants = {"0": {"file": self.base_file, "raw": True}} for k, v in self.right_variants.items(): variants[k] = v return { "id": self.id, "title": self.title, "base_file": self.base_file, "license": self.license, "source": self.source, "right_variants": variants, "annotations": self.annotations, "strings": self.strings, } def _clip_from_dict(d: dict[str, Any]) -> Clip: return Clip( id=d["id"], title=d["title"], base_file=d["base_file"], license=d.get("license", ""), source=d.get("source", ""), right_variants=d.get("right_variants", {}), annotations=d.get("annotations", []), strings=d.get("strings", {}), ) def load_manifest(path: str | Path) -> list[Clip]: """Load the base-clip manifest. Raises FileNotFoundError if missing.""" path = Path(path) data = json.loads(path.read_text()) return [_clip_from_dict(c) for c in data["clips"]]