feat(pipeline): per-clip-pair morph manifest + member-pair baking (154 clips)

Tasks 1-2 of the altitude clip-pair morph plan. Manifest emits a morph
entry for every video pair in adjacent altitudes, both directions
(<src>__<dst>.mp4 zoom-in + .rev zoom-out); generate_media() bakes them
all via the existing xfade=zoomin recipe keyed by clip member.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
BenStullsBets
2026-06-27 07:04:29 -07:00
parent 3b3d30e8e0
commit cc469b5298
2 changed files with 87 additions and 21 deletions
+56
View File
@@ -0,0 +1,56 @@
"""Tests for the hand-authored rotating-pool manifest builder
(simulator/build_pool_manifest.py) — the per-clip-pair morph model."""
import simulator.build_pool_manifest as b
def _expected_pairs():
"""Every adjacent (higher H, lower L) edge, every member pair, BOTH directions."""
order = b.RING_ORDER
n = len(order)
expected = set()
for i in range(n):
H, L = order[i], order[(i + 1) % n]
for h in b.POOLS[H]:
for l in b.POOLS[L]:
expected.add((h, l, f"transitions/{h}__{l}.mp4")) # zoom in
expected.add((l, h, f"transitions/{h}__{l}.rev.mp4")) # zoom out
return expected
def test_transitions_are_per_clip_pair_both_directions():
m = b.build_manifest()
trans = m["ring"]["transitions"]
expected = _expected_pairs()
got = {(t["from"], t["to"], t["file"]) for t in trans}
assert got == expected
assert len(trans) == len(expected) == 154
assert all(t["model"] == "placeholder-zoom" for t in trans)
def test_generate_media_bakes_every_member_pair(monkeypatch, tmp_path):
calls = []
def fake_run(args, **kw):
out = args[-1]
from pathlib import Path
Path(out).parent.mkdir(parents=True, exist_ok=True)
Path(out).write_bytes(b"x")
calls.append(str(out))
class R:
returncode = 0
return R()
monkeypatch.setattr(b, "MEDIA", tmp_path)
monkeypatch.setattr(b.subprocess, "run", fake_run)
monkeypatch.setattr(b, "_ffmpeg", lambda: "ffmpeg")
b.generate_media()
outs = {c.split("transitions/")[-1] for c in calls}
fwd = {f"{h}__{l}.mp4" for H, L in b._adjacent_edges()
for h in b.POOLS[H] for l in b.POOLS[L]}
rev = {f"{h}__{l}.rev.mp4" for H, L in b._adjacent_edges()
for h in b.POOLS[H] for l in b.POOLS[L]}
assert fwd <= outs and rev <= outs
assert len(fwd) + len(rev) == 154