feat(pipeline): crossfade seamless-loop arg builder (content pipeline §3.3)

This commit is contained in:
BenStullsBets
2026-06-24 07:54:01 -07:00
parent 642451925f
commit 7217daeb44
2 changed files with 42 additions and 1 deletions
+23
View File
@@ -28,3 +28,26 @@ def frame_args(src, dst, *, start: float, duration: float,
"-c:v", "libx264", "-preset", "medium", "-crf", "16",
"-pix_fmt", "yuv420p", "-movflags", "+faststart", str(dst),
]
def crossfade_loop_args(src, dst, *, duration: float, overlap: float,
ff: str = "ffmpeg") -> list[str]:
"""Stage 3 — make `src` loop seamlessly by crossfading its tail over its head.
Output length = duration overlap. Requires overlap < duration/2 so a non-empty
middle remains."""
if overlap <= 0 or overlap >= duration / 2:
raise ValueError(f"overlap {overlap} must be in (0, duration/2={duration/2})")
d, o = duration, overlap
fc = (
f"[0:v]trim=0:{o},setpts=PTS-STARTPTS[head];"
f"[0:v]trim={o}:{d - o},setpts=PTS-STARTPTS[mid];"
f"[0:v]trim={d - o}:{d},setpts=PTS-STARTPTS[tail];"
f"[tail][head]xfade=transition=fade:duration={o}:offset=0[xf];"
f"[xf][mid]concat=n=2:v=1,format=yuv420p[out]"
)
return [
ff, "-y", "-i", str(src), "-filter_complex", fc,
"-map", "[out]", "-an",
"-c:v", "libx264", "-preset", "medium", "-crf", "16",
"-pix_fmt", "yuv420p", "-movflags", "+faststart", str(dst),
]