56 lines
2.1 KiB
Python
56 lines
2.1 KiB
Python
"""Stage 6 — assemble manifest entries (spec §3.6). Pure dict transforms over a
|
|
loaded manifest dict; the caller does the json read/write. Idempotent by id so
|
|
re-running earlier stages never clobbers authored labels."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import copy
|
|
|
|
|
|
def upsert_clip(manifest: dict, clip: dict) -> dict:
|
|
"""Replace the clip with the same id, or append it. Returns a new manifest."""
|
|
m = copy.deepcopy(manifest)
|
|
clips = m.setdefault("clips", [])
|
|
for i, c in enumerate(clips):
|
|
if c["id"] == clip["id"]:
|
|
clips[i] = clip
|
|
return m
|
|
clips.append(clip)
|
|
return m
|
|
|
|
|
|
def add_ring_scale(manifest: dict, scale_id: str, clip_id: str,
|
|
after: str | None = None) -> dict:
|
|
"""Insert a scale into ring.scales after the named scale (or append if `after`
|
|
is None / not found). No-op if scale_id already present. Returns a new manifest."""
|
|
m = copy.deepcopy(manifest)
|
|
ring = m.setdefault("ring", {"scales": [], "transitions": []})
|
|
scales = ring.setdefault("scales", [])
|
|
if any(s["id"] == scale_id for s in scales):
|
|
return m
|
|
entry = {"id": scale_id, "clip_id": clip_id}
|
|
idx = next((i for i, s in enumerate(scales) if s["id"] == after), None)
|
|
if idx is None:
|
|
scales.append(entry)
|
|
else:
|
|
scales.insert(idx + 1, entry)
|
|
return m
|
|
|
|
|
|
def rebuild_ring_edges(manifest: dict) -> dict:
|
|
"""Rebuild ring.transitions to one placeholder entry per ring edge (N scales ->
|
|
N edges incl. the wrap), preserving any existing model string for an edge.
|
|
Returns a new manifest."""
|
|
m = copy.deepcopy(manifest)
|
|
ring = m.setdefault("ring", {"scales": [], "transitions": []})
|
|
scales = ring.get("scales", [])
|
|
existing = {t["file"]: t.get("model", "") for t in ring.get("transitions", [])}
|
|
edges = []
|
|
n = len(scales)
|
|
for i in range(n):
|
|
a, b = scales[i]["id"], scales[(i + 1) % n]["id"]
|
|
file = f"transitions/{a}-{b}.mp4"
|
|
edges.append({"file": file, "model": existing.get(file, "placeholder-zoom")})
|
|
ring["transitions"] = edges
|
|
return m
|