feat: editor-v2 assemble lane — multi-clip/card timeline rendering in the worker

Per the adopted qwen38 plan §2 (+ glm53 amendments): video_processing_jobs
kind 'assemble' (claim widened via 044), EdlPlan mirror validation
(lenient-fail), the filter_complex builder (interleaved concat pairs, fit
cover/contain chains, absolute-timeline PNG overlays with enable windows,
VO adelay/sidechain duck, hard-breaking word wrap for card PNGs), and the
runner's assemble path with pre-spawn trim gates, attempt backoff and 15-min
cap. Sources open by magic (SPENC1 kek / SPENC2 direct key) so direct-to-R2
uploads assemble without the api ever seeing bytes. Tests: builder 18,
edl-plan 12, overlay-png 3, runner fakes extended.
This commit is contained in:
glass beadian 2026-10-16 21:12:55 +00:00
parent df62aa88d4
commit 10dfda3b7c
12 changed files with 1749 additions and 8 deletions

View file

@ -88,6 +88,11 @@ function doubles() {
return storageKey === undefined ? null : { storageKey, mimeType: "audio/mp4" };
},
async completeProbe() { /* probe completions are asserted in the pg-store suite */ },
async assembleAsset(_workspaceId, assetId) {
const storageKey = assetKeys.get(assetId);
return storageKey === undefined ? null
: { id: assetId, storageKey, mimeType: "video/mp4", sizeBytes: 1, width: 1280, height: 720, durationMs: 30_000, frameRate: 30, hasAudio: true };
},
};
const storage: ProduceObjectStorage = {
async getSealedObject(k) {
@ -111,6 +116,7 @@ function job(input: string, kind: "compose" | "mix", request: unknown, source: P
workspaceId: WS,
kind,
request: parseProduceRequest(request, kind),
assemble: null,
source: {
id: UUID(1), storageKey: input, mimeType: "video/mp4", sizeBytes: 1,
width: 320, height: 180, durationMs: 2000, frameRate: 30, hasAudio: true, ...source,