@cueframe/kernel 0.2.44 → 0.2.46
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/_members/composition/ops/compositionOps.shared.d.ts +1 -1
- package/dist/_members/composition/projection/cropProjection.d.ts +2 -1
- package/dist/_members/composition-ops/index.d.ts +4 -0
- package/dist/_members/composition-ops/materializeCaptions.d.ts +19 -0
- package/dist/_members/core/render/compositionRender.d.ts +1 -1
- package/dist/_members/core/render/index.d.ts +1 -1
- package/dist/_members/core/render/trackProjection.d.ts +24 -0
- package/dist/_members/core/render/types.d.ts +1 -0
- package/dist/_members/errors/registry.d.ts +32 -0
- package/dist/_members/mcp/executor.d.ts +1 -1
- package/dist/_members/mcp/generated/harness.d.ts +1 -1
- package/dist/_members/mcp/server.d.ts +2 -0
- package/dist/_members/plane/contract.d.ts +12 -5
- package/dist/authoring/build-tools.js +3 -3
- package/dist/authoring/context.js +2 -2
- package/dist/authoring/tools.js +2 -2
- package/dist/authoring.js +6 -6
- package/dist/{chunk-S3HHEJMZ.js → chunk-4FKNPTLX.js} +1 -1
- package/dist/{chunk-4GLMARCJ.js → chunk-ACURZCPN.js} +1 -1
- package/dist/{chunk-JENEKKAM.js → chunk-AJ3YRZ7A.js} +44 -5
- package/dist/{chunk-46PFSJZE.js → chunk-AOIY7NGF.js} +3059 -3120
- package/dist/{chunk-MJWRC7G7.js → chunk-BBNF4QLD.js} +2 -2
- package/dist/{chunk-5YO4LAB7.js → chunk-CFIQY4OE.js} +1 -1
- package/dist/{chunk-GOJKL5FM.js → chunk-DV7ZFVL4.js} +1 -1
- package/dist/{chunk-XWOV6KI6.js → chunk-E63UEO5C.js} +8 -2
- package/dist/{chunk-ZXWSZUSU.js → chunk-FEG264CF.js} +55 -32
- package/dist/{chunk-OASSDEWY.js → chunk-FGJ7GKWZ.js} +6 -6
- package/dist/{chunk-SPF3AXTB.js → chunk-FYS2XUKJ.js} +1 -1
- package/dist/{chunk-QISFYLBB.js → chunk-IMIUHG2E.js} +1 -1
- package/dist/{chunk-V7LQ7ZHV.js → chunk-JPMVNMF3.js} +1 -1
- package/dist/chunk-LELDNLLA.js +1060 -0
- package/dist/{chunk-KKHI5NLK.js → chunk-N54EGUVM.js} +1 -1
- package/dist/{chunk-WYRKF2OX.js → chunk-Q5P5UFOP.js} +1 -1
- package/dist/{chunk-ZUJCWFS7.js → chunk-SLUTOUWI.js} +1 -1
- package/dist/{chunk-DENF4TPI.js → chunk-T56BK7NS.js} +6 -4
- package/dist/{chunk-B4VMU2IJ.js → chunk-TBHSOEYK.js} +1 -1
- package/dist/{chunk-POTCIQTA.js → chunk-VEQTURKN.js} +90 -11
- package/dist/{chunk-YIJZBLQE.js → chunk-W5AFGFVZ.js} +24 -3
- package/dist/{chunk-WKT7SEVP.js → chunk-WWXTJZ67.js} +4 -2
- package/dist/{chunk-OZNULDQE.js → chunk-XSBVOXSB.js} +1 -1
- package/dist/{chunk-FRNBTODO.js → chunk-Z4G3VM3H.js} +1 -1
- package/dist/compose.js +1 -1
- package/dist/composition/invariants.js +2 -2
- package/dist/composition/ops.js +1 -1
- package/dist/composition/projection.js +2 -2
- package/dist/composition/timeline.js +2 -2
- package/dist/composition-ops.js +36 -894
- package/dist/composition.js +4 -4
- package/dist/{context-KNFL62QR.js → context-YNR73FNI.js} +2 -2
- package/dist/core/render.js +7 -5
- package/dist/core/select.js +2 -2
- package/dist/core.js +5 -5
- package/dist/director/ai-sdk.js +3 -3
- package/dist/director/domain.js +2 -2
- package/dist/director.js +3 -3
- package/dist/errors.js +3 -1
- package/dist/mcp/adapter.js +3 -3
- package/dist/mcp/core.js +1 -1
- package/dist/mcp/curation.js +1 -1
- package/dist/mcp/executor.js +2 -2
- package/dist/mcp/scene-shot.js +2 -2
- package/dist/mcp/server.js +26 -5
- package/dist/mcp.js +26 -7
- package/dist/plane/conformance.js +1 -1
- package/dist/plane/contract.js +5 -1
- package/dist/plane.js +1 -1
- package/dist/render-harness/cardAssetAdmission.js +2 -2
- package/dist/render-harness/cardFonts.js +2 -2
- package/dist/render-harness.js +7 -7
- package/package.json +11 -11
|
@@ -7,7 +7,7 @@ import {
|
|
|
7
7
|
} from "./chunk-7AZLFN26.js";
|
|
8
8
|
import {
|
|
9
9
|
selectAllOverlays
|
|
10
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-VEQTURKN.js";
|
|
11
11
|
import {
|
|
12
12
|
ComposePlan
|
|
13
13
|
} from "./chunk-AG7HKF2U.js";
|
|
@@ -63,7 +63,7 @@ var COMPOSE_ROLE_TOOLS = Object.freeze({
|
|
|
63
63
|
schema: z.object({}),
|
|
64
64
|
metadata: { label: "Get Context" },
|
|
65
65
|
handler: async (_args, capabilities) => {
|
|
66
|
-
const { buildDirectorContext } = await import("./context-
|
|
66
|
+
const { buildDirectorContext } = await import("./context-YNR73FNI.js");
|
|
67
67
|
return buildDirectorContext(capabilities);
|
|
68
68
|
}
|
|
69
69
|
},
|
|
@@ -312,7 +312,7 @@ Call wait_job(kind="compose", id="${j.composeJobId}", projectId="${a.projectId}"
|
|
|
312
312
|
name: "apply_composition",
|
|
313
313
|
operationId: "applyComposition",
|
|
314
314
|
pathArgs: { projectId: "id" },
|
|
315
|
-
description: 'Atomically apply an array of CompositionOps to a project composition. All ops succeed or none do (transactional). Pass `if_match` for optimistic-concurrency control (409 stale_etag on mismatch) and `dry_run: true` to validate the batch without persisting. Ops land on the project\'s ACTIVE composition unless you pass `composition_id` (mutually exclusive with `session_id`), which targets a specific one \u2014 that is how you author a second composition minted with new_composition and run an A/B. Use this to seed a composition (add media clips) or make targeted edits (set a crop intent, reorder clips, set the format). CAPTIONS:
|
|
315
|
+
description: 'Atomically apply an array of CompositionOps to a project composition. All ops succeed or none do (transactional). Call describe_composition_ops without type for the compact operation list, then with a type for its exact JSON Schema. Pass `if_match` for optimistic-concurrency control (409 stale_etag on mismatch) and `dry_run: true` to validate the batch without persisting. Ops land on the project\'s ACTIVE composition unless you pass `composition_id` (mutually exclusive with `session_id`), which targets a specific one \u2014 that is how you author a second composition minted with new_composition and run an A/B. Use this to seed a composition (add media clips) or make targeted edits (set a crop intent, reorder clips, set the format). CAPTIONS FOR EDITED TIMELINES: after saving all clip edits, call again with {type:"captions.fromComposition", trackId} to build captions from every audible clip on that track, honoring trims, exclusions and placement. Clips must have duration matching playable source duration divided by playbackRate; unsupported timing fails explicitly. Re-run it after later timeline edits; captions do not update automatically. {type:"captions.fromTranscript", mediaId, window?} instead replaces all captions from one media transcript, rebased to zero; it does not follow a montage. Style with {type:"setCaptionStyle", style}; or set explicit words with {type:"setCaptions", segments, style?} (e.g. translations or your own VO timings). Brand is NOT set here \u2014 it lives on the project (create_project / update_project with brandKitId \u2014 or PATCH /projects/:id over REST). CARDS: a {kind:"card", html, tokens} clip projects each `tokens` KEY `x` to the CSS custom property `--cf-x`, so the html must read it as `var(--cf-x)` \u2014 write `tokens:{"plate":"#0af"}` + `background:var(--cf-plate)`. Do NOT put dashes in the key: `"--plate"` becomes `--cf---plate` and your `var(--plate)` then resolves to NOTHING, which CSS silently degrades to a transparent background and black text. Responses carry `warnings[]` when a card reads a var nothing defines. SCENE SHOTS: for a 3D product shot \u2014 a tilted page screenshot, a lit laptop/phone showing a real screenshot, two-colour type, a colour-field backdrop, focus/grain/vignette and a camera move \u2014 use {type:"scene.add", clipId, newTrack|trackId, startTime, duration, scene, assets} and revise it with {type:"scene.update", clipId, scene?, assets?, startTime?, duration?}. You author a scene SPEC, never wrapper TSX; the server registers the component for you. There is NO built-in scene primitive in list_catalog \u2014 READ cueframe://scene-shot first for the JSON schema, the canonical V2 world-objects recipe with nested groups, slabs, pages, phones, laptops, world/screen text, admitted surfaces, camera motion, lighting, and the current resource limits.',
|
|
316
316
|
okFormat: (j, a) => {
|
|
317
317
|
const warns = formatWarnings(j);
|
|
318
318
|
if (j && typeof j.valid === "boolean") {
|
|
@@ -229,8 +229,10 @@ var ERROR_CODES = {
|
|
|
229
229
|
// for. None of that is the author's doing and none of it is visible to them,
|
|
230
230
|
// so it stays a platform fault we see in Sentry.
|
|
231
231
|
component_output_unverifiable: { stage: "render", category: "internal", retryable: false, httpStatus: 500 },
|
|
232
|
-
// Two ways a bake dies without a diagnostic of its own.
|
|
233
|
-
//
|
|
232
|
+
// Two ways a bake dies without a diagnostic of its own. Both are raised as
|
|
233
|
+
// workflow failures by the host, never as a ComponentRuntimeError, so they are
|
|
234
|
+
// deliberately absent from COMPONENT_RUNTIME_ERROR_CODES. The sandbox ran out
|
|
235
|
+
// of memory: the same bake on a larger box can succeed, so it is worth one retry.
|
|
234
236
|
sandbox_oom: { stage: "render", category: "internal", retryable: true, httpStatus: 503 },
|
|
235
237
|
// The bake process died leaving nothing to read. Retrying reproduces it, so it
|
|
236
238
|
// is ours to diagnose from the run, not the caller's to wait out.
|
|
@@ -239,6 +241,9 @@ var ERROR_CODES = {
|
|
|
239
241
|
component_scene_invalid: { stage: "validate", category: "authoring", retryable: false, httpStatus: 422 },
|
|
240
242
|
motion_preset_unknown: { stage: "validate", category: "authoring", retryable: false, httpStatus: 422 },
|
|
241
243
|
motion_preset_target_invalid: { stage: "validate", category: "authoring", retryable: false, httpStatus: 422 },
|
|
244
|
+
caption_track_invalid: { stage: "validate", category: "authoring", retryable: false, httpStatus: 422 },
|
|
245
|
+
caption_clip_unsupported: { stage: "validate", category: "authoring", retryable: false, httpStatus: 422 },
|
|
246
|
+
caption_clips_overlap: { stage: "validate", category: "authoring", retryable: false, httpStatus: 422 },
|
|
242
247
|
grid_target_invalid: { stage: "validate", category: "authoring", retryable: false, httpStatus: 422 },
|
|
243
248
|
grid_unavailable: { stage: "validate", category: "authoring", retryable: false, httpStatus: 422 },
|
|
244
249
|
shot_grid_unavailable: { stage: "validate", category: "authoring", retryable: false, httpStatus: 422 },
|
|
@@ -512,6 +517,7 @@ export {
|
|
|
512
517
|
encodeError,
|
|
513
518
|
decodeError,
|
|
514
519
|
scrubSensitive,
|
|
520
|
+
DEFAULT_RETRY_AFTER_SECONDS,
|
|
515
521
|
retryAfterSecondsFor,
|
|
516
522
|
toWireEnvelope
|
|
517
523
|
};
|
|
@@ -20,11 +20,11 @@ import {
|
|
|
20
20
|
scoreAndRank,
|
|
21
21
|
segmentShots,
|
|
22
22
|
stabilizeSelectionHysteresis
|
|
23
|
-
} from "./chunk-
|
|
23
|
+
} from "./chunk-JPMVNMF3.js";
|
|
24
24
|
import {
|
|
25
25
|
flattenCompoundClips,
|
|
26
26
|
hasCompoundClips
|
|
27
|
-
} from "./chunk-
|
|
27
|
+
} from "./chunk-IMIUHG2E.js";
|
|
28
28
|
import {
|
|
29
29
|
compositionHasVo,
|
|
30
30
|
roleDefaultGainDb
|
|
@@ -36,7 +36,7 @@ import {
|
|
|
36
36
|
import {
|
|
37
37
|
CueframeError,
|
|
38
38
|
encodeError
|
|
39
|
-
} from "./chunk-
|
|
39
|
+
} from "./chunk-E63UEO5C.js";
|
|
40
40
|
import {
|
|
41
41
|
SCENE_CAPTURE_IDENTITY_FIELDS_V1,
|
|
42
42
|
SCENE_PRESET_TRIANGLES,
|
|
@@ -1342,6 +1342,38 @@ function playableSpans(trimStart, trimEnd, excluded) {
|
|
|
1342
1342
|
if (cursor < trimEnd) out.push({ start: cursor, end: trimEnd });
|
|
1343
1343
|
return out;
|
|
1344
1344
|
}
|
|
1345
|
+
function projectMediaPlaybackSpans({
|
|
1346
|
+
startTime,
|
|
1347
|
+
duration,
|
|
1348
|
+
trim,
|
|
1349
|
+
excludedRanges,
|
|
1350
|
+
playbackRate = 1,
|
|
1351
|
+
fps
|
|
1352
|
+
}) {
|
|
1353
|
+
const trimStart = trim?.start ?? 0;
|
|
1354
|
+
const trimEnd = trim?.end ?? trimStart + duration * playbackRate;
|
|
1355
|
+
let playedSourceSeconds = 0;
|
|
1356
|
+
return playableSpans(trimStart, trimEnd, excludedRanges).map((span) => {
|
|
1357
|
+
const timelineStart = startTime + playedSourceSeconds / playbackRate;
|
|
1358
|
+
playedSourceSeconds += span.end - span.start;
|
|
1359
|
+
const timelineEnd = startTime + playedSourceSeconds / playbackRate;
|
|
1360
|
+
const { startFrame: timelineStartFrames, durationInFrames } = timelineSpanFrames(
|
|
1361
|
+
timelineStart,
|
|
1362
|
+
timelineEnd,
|
|
1363
|
+
fps
|
|
1364
|
+
);
|
|
1365
|
+
return {
|
|
1366
|
+
sourceStart: span.start,
|
|
1367
|
+
sourceEnd: span.end,
|
|
1368
|
+
timelineStart,
|
|
1369
|
+
timelineEnd,
|
|
1370
|
+
sourceInFrames: frames(span.start, fps),
|
|
1371
|
+
sourceOutFrames: frames(span.end, fps),
|
|
1372
|
+
timelineStartFrames,
|
|
1373
|
+
durationInFrames
|
|
1374
|
+
};
|
|
1375
|
+
});
|
|
1376
|
+
}
|
|
1345
1377
|
function projectMediaTrack(track, trackIndex, trackCount, fps, srcUrls, trajectoryUrls, coViewUrls, trajectories) {
|
|
1346
1378
|
const clips = [];
|
|
1347
1379
|
const audio = [];
|
|
@@ -1365,7 +1397,6 @@ function projectMediaTrack(track, trackIndex, trackCount, fps, srcUrls, trajecto
|
|
|
1365
1397
|
const packedUrl = coViewUrls?.[c.id];
|
|
1366
1398
|
const packedEnvelopeFields = packedUrl === void 0 ? [] : [
|
|
1367
1399
|
c.region !== void 0 && !(c.region.x === 0 && c.region.y === 0 && c.region.w === 1 && c.region.h === 1) ? "region" : null,
|
|
1368
|
-
c.fit !== void 0 && c.fit !== "cover" ? "fit" : null,
|
|
1369
1400
|
c.opacity !== void 0 && c.opacity !== 1 ? "opacity" : null,
|
|
1370
1401
|
c.scale !== void 0 && c.scale !== 1 ? "scale" : null,
|
|
1371
1402
|
c.rotation !== void 0 && c.rotation !== 0 ? "rotation" : null,
|
|
@@ -1384,33 +1415,31 @@ function projectMediaTrack(track, trackIndex, trackCount, fps, srcUrls, trajecto
|
|
|
1384
1415
|
}
|
|
1385
1416
|
);
|
|
1386
1417
|
}
|
|
1418
|
+
const spans = projectMediaPlaybackSpans({
|
|
1419
|
+
startTime: c.startTime,
|
|
1420
|
+
duration: c.duration,
|
|
1421
|
+
trim: c.source.trim,
|
|
1422
|
+
excludedRanges: c.source.excludedRanges,
|
|
1423
|
+
playbackRate: c.source.playbackRate,
|
|
1424
|
+
fps
|
|
1425
|
+
});
|
|
1387
1426
|
if (track.kind === "audio") {
|
|
1388
|
-
const trimStart2 = c.source.trim?.start ?? 0;
|
|
1389
|
-
const trimEnd2 = c.source.trim?.end ?? trimStart2 + c.duration;
|
|
1390
|
-
const spans2 = playableSpans(trimStart2, trimEnd2, c.source.excludedRanges);
|
|
1391
1427
|
const resolvedVolume = (c.source.volumeDb !== void 0 ? Math.pow(10, c.source.volumeDb / 20) : c.source.volume ?? 1) * (track.volume ?? 1);
|
|
1392
1428
|
const fadeInFrames = c.source.fadeInMs !== void 0 ? Math.max(0, Math.round(c.source.fadeInMs / 1e3 * fps)) : void 0;
|
|
1393
1429
|
const fadeOutFrames = c.source.fadeOutMs !== void 0 ? Math.max(0, Math.round(c.source.fadeOutMs / 1e3 * fps)) : void 0;
|
|
1394
|
-
let
|
|
1395
|
-
|
|
1396
|
-
const
|
|
1397
|
-
const spanStartSec = c.startTime + playedSec2;
|
|
1398
|
-
playedSec2 += span.end - span.start;
|
|
1399
|
-
const { startFrame, durationInFrames: subDurationFrames } = timelineSpanFrames(
|
|
1400
|
-
spanStartSec,
|
|
1401
|
-
c.startTime + playedSec2,
|
|
1402
|
-
fps
|
|
1403
|
-
);
|
|
1430
|
+
for (let s = 0; s < spans.length; s++) {
|
|
1431
|
+
const span = spans[s];
|
|
1432
|
+
const { timelineStartFrames: startFrame, durationInFrames: subDurationFrames } = span;
|
|
1404
1433
|
if (subDurationFrames === 0) continue;
|
|
1405
|
-
const sourceInFrames = frames(span.start, fps);
|
|
1406
1434
|
audio.push({
|
|
1407
|
-
id:
|
|
1435
|
+
id: spans.length === 1 ? c.id : `${c.id}#${s}`,
|
|
1408
1436
|
src,
|
|
1409
|
-
sourceInFrames,
|
|
1410
|
-
sourceOutFrames:
|
|
1437
|
+
sourceInFrames: span.sourceInFrames,
|
|
1438
|
+
sourceOutFrames: span.sourceOutFrames,
|
|
1411
1439
|
startFrame,
|
|
1412
1440
|
durationInFrames: subDurationFrames,
|
|
1413
1441
|
volume: resolvedVolume,
|
|
1442
|
+
...c.source.playbackRate !== void 0 ? { playbackRate: c.source.playbackRate } : {},
|
|
1414
1443
|
...fadeInFrames !== void 0 ? { fadeInFrames } : {},
|
|
1415
1444
|
...fadeOutFrames !== void 0 ? { fadeOutFrames } : {},
|
|
1416
1445
|
...c.source.loop !== void 0 ? { loop: c.source.loop } : {}
|
|
@@ -1418,18 +1447,11 @@ function projectMediaTrack(track, trackIndex, trackCount, fps, srcUrls, trajecto
|
|
|
1418
1447
|
}
|
|
1419
1448
|
continue;
|
|
1420
1449
|
}
|
|
1421
|
-
const trimStart = c.source.trim?.start ?? 0;
|
|
1422
|
-
const trimEnd = c.source.trim?.end ?? trimStart + c.duration;
|
|
1423
|
-
const spans = playableSpans(trimStart, trimEnd, c.source.excludedRanges);
|
|
1424
1450
|
let firstEmittedSubId;
|
|
1425
|
-
let playedSec = 0;
|
|
1426
1451
|
for (let s = 0; s < spans.length; s++) {
|
|
1427
1452
|
const span = spans[s];
|
|
1428
|
-
const
|
|
1429
|
-
playedSec += span.end - span.start;
|
|
1430
|
-
const { startFrame: timelineCursorFrames, durationInFrames: subDurationFrames } = timelineSpanFrames(spanStartSec, c.startTime + playedSec, fps);
|
|
1453
|
+
const { timelineStartFrames: timelineCursorFrames, durationInFrames: subDurationFrames } = span;
|
|
1431
1454
|
if (subDurationFrames === 0) continue;
|
|
1432
|
-
const sourceInFrames = frames(span.start, fps);
|
|
1433
1455
|
const subId = spans.length === 1 ? c.id : `${c.id}#${s}`;
|
|
1434
1456
|
if (firstEmittedSubId === void 0) firstEmittedSubId = subId;
|
|
1435
1457
|
clips.push({
|
|
@@ -1437,11 +1459,11 @@ function projectMediaTrack(track, trackIndex, trackCount, fps, srcUrls, trajecto
|
|
|
1437
1459
|
id: subId,
|
|
1438
1460
|
...spans.length > 1 ? { parentId: c.id } : {},
|
|
1439
1461
|
src,
|
|
1440
|
-
sourceInFrames,
|
|
1441
|
-
sourceOutFrames:
|
|
1462
|
+
sourceInFrames: span.sourceInFrames,
|
|
1463
|
+
sourceOutFrames: span.sourceOutFrames,
|
|
1442
1464
|
timelineStartFrames: timelineCursorFrames,
|
|
1443
1465
|
durationInFrames: subDurationFrames,
|
|
1444
|
-
trimStartFrames: frames(
|
|
1466
|
+
trimStartFrames: frames(c.source.trim?.start ?? 0, fps),
|
|
1445
1467
|
...c.source.playbackRate !== void 0 ? { playbackRate: c.source.playbackRate } : {},
|
|
1446
1468
|
...c.source.volume !== void 0 ? { volume: c.source.volume } : {},
|
|
1447
1469
|
...c.source.muted !== void 0 ? { muted: c.source.muted } : {},
|
|
@@ -2952,6 +2974,7 @@ export {
|
|
|
2952
2974
|
validateComposition,
|
|
2953
2975
|
assertBehindSubjectCaptionCoverage,
|
|
2954
2976
|
playableSpans,
|
|
2977
|
+
projectMediaPlaybackSpans,
|
|
2955
2978
|
compositionFormatDimensions,
|
|
2956
2979
|
referencedMediaIds,
|
|
2957
2980
|
compositionToRenderInput,
|
|
@@ -6,7 +6,7 @@ import {
|
|
|
6
6
|
import {
|
|
7
7
|
CueframeError,
|
|
8
8
|
encodeError
|
|
9
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-E63UEO5C.js";
|
|
10
10
|
|
|
11
11
|
// ../composition/src/projection/cardTokens.ts
|
|
12
12
|
var CARD_MOTION_VAR_NAMES = [
|
|
@@ -454,17 +454,17 @@ function resolveSegmentAt(traj, srcFrame) {
|
|
|
454
454
|
}
|
|
455
455
|
return { seg: best, inSegIdx: Math.max(0, Math.min(bestInIdx, best.centers.length - 1)) };
|
|
456
456
|
}
|
|
457
|
-
function outputToTrajectoryFrame(sections, trimStartFrames, outFrame, srcFps, fps) {
|
|
457
|
+
function outputToTrajectoryFrame(sections, trimStartFrames, outFrame, srcFps, fps, playbackRate = 1) {
|
|
458
458
|
let cum = 0;
|
|
459
459
|
for (const s of sections) {
|
|
460
460
|
if (outFrame < cum + s.durationInFrames) {
|
|
461
|
-
const clipFramesAtFps2 = s.sourceInFrames - trimStartFrames + (outFrame - cum);
|
|
461
|
+
const clipFramesAtFps2 = s.sourceInFrames - trimStartFrames + (outFrame - cum) * playbackRate;
|
|
462
462
|
return Math.round(clipFramesAtFps2 * srcFps / fps);
|
|
463
463
|
}
|
|
464
464
|
cum += s.durationInFrames;
|
|
465
465
|
}
|
|
466
466
|
const last = sections[sections.length - 1];
|
|
467
|
-
const clipFramesAtFps = last.sourceInFrames - trimStartFrames + (last.durationInFrames - 1);
|
|
467
|
+
const clipFramesAtFps = last.sourceInFrames - trimStartFrames + (last.durationInFrames - 1) * playbackRate;
|
|
468
468
|
return Math.round(clipFramesAtFps * srcFps / fps);
|
|
469
469
|
}
|
|
470
470
|
function computeCropTransform(input) {
|
|
@@ -519,13 +519,13 @@ function sourceRectToOutput(src, t, outW, outH) {
|
|
|
519
519
|
};
|
|
520
520
|
}
|
|
521
521
|
function buildSubjectsOutput(args) {
|
|
522
|
-
const { traj, sections, trimStartFrames, durationInFrames, outW, outH, fps } = args;
|
|
522
|
+
const { traj, sections, trimStartFrames, durationInFrames, outW, outH, fps, playbackRate = 1 } = args;
|
|
523
523
|
const series = /* @__PURE__ */ new Map();
|
|
524
524
|
const activeBySubject = /* @__PURE__ */ new Map();
|
|
525
525
|
const order = [];
|
|
526
526
|
const emptySeries = () => Array(durationInFrames).fill(null);
|
|
527
527
|
for (let f = 0; f < durationInFrames; f++) {
|
|
528
|
-
const srcFrame = outputToTrajectoryFrame(sections, trimStartFrames, f, traj.src_fps, fps);
|
|
528
|
+
const srcFrame = outputToTrajectoryFrame(sections, trimStartFrames, f, traj.src_fps, fps, playbackRate);
|
|
529
529
|
const { seg, inSegIdx } = resolveSegmentAt(traj, srcFrame);
|
|
530
530
|
const [cx, cy] = seg.centers[inSegIdx];
|
|
531
531
|
const transform = computeCropTransform({
|