videobook 0.2.0 → 0.2.2
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/CHANGELOG.md +25 -0
- package/dist/chat-router.js +1 -1
- package/dist/guides/seedance_2_prompting_guide.md +1 -1
- package/dist/image-edit-models.js +1 -0
- package/dist/jobs/generate-media.js +18 -6
- package/dist/jobs/run-notebook.js +23 -4
- package/dist/media/adapters/fal.js +7 -0
- package/dist/media/catalog.js +4 -2
- package/dist/media/cost.js +2 -2
- package/dist/media/refs.js +13 -0
- package/dist/media/request.js +7 -0
- package/dist/media/seedance-edit.js +9 -0
- package/dist/provider-usage.js +3 -1
- package/dist/tools/estimate-cost.js +4 -1
- package/dist/tools/generate-media.js +2 -1
- package/dist/tools/reconcile-project-work.js +2 -1
- package/package.json +14 -14
- package/ui/assets/{alive-captions-DPEr1nKM.js → alive-captions-C48o6dfv.js} +1 -1
- package/ui/assets/confirm-bodies-aq6Az8Md.js +1 -0
- package/ui/assets/{fullscreen-B7nw4YbE.js → fullscreen-4m0qtZ7w.js} +1 -1
- package/ui/assets/{goal-ui-DK-xTCK0.js → goal-ui-2pUWP_B2.js} +2 -2
- package/ui/assets/{index-kS1905s5.js → index-DmDH4gYr.js} +2 -2
- package/ui/assets/{inspector-MlzQbTVo.js → inspector-CnKySYc5.js} +1 -1
- package/ui/assets/{jobs-ui-CoinOWx0.js → jobs-ui-lRoS1MbL.js} +1 -1
- package/ui/assets/{music-video-Cbe80kmQ.js → music-video-BQnnPp-X.js} +1 -1
- package/ui/assets/ui-OHFUngrc.js +2 -0
- package/ui/assets/{vendor-flow-BXbeWxW7.js → vendor-flow-zBavaNEW.js} +1 -1
- package/ui/assets/{vendor-mcp-BTlEJcHV.js → vendor-mcp-DQ7EusT9.js} +1 -1
- package/ui/assets/vendor-yctIZ9Dz.js +19 -0
- package/ui/assets/{videobook-Qlp9HQFu.js → videobook-DZ58pKW5.js} +12 -12
- package/ui/index.html +11 -11
- package/ui/assets/confirm-bodies-D8ouHXKy.js +0 -1
- package/ui/assets/ui-xo_Y1yQg.js +0 -2
- package/ui/assets/vendor-DfTCWgKm.js +0 -19
package/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,31 @@ and the package uses [Semantic Versioning](https://semver.org/).
|
|
|
6
6
|
|
|
7
7
|
## [Unreleased]
|
|
8
8
|
|
|
9
|
+
## [0.2.2] - 2026-10-21
|
|
10
|
+
|
|
11
|
+
### Added
|
|
12
|
+
|
|
13
|
+
- KIE FLUX 3 Klein text-to-image and image editing, with 1K defaults,
|
|
14
|
+
up to ten edit references, and resolution-aware per-image cost estimates.
|
|
15
|
+
- FLUX 3 Klein Edit in the image editor for whole-image and region edits.
|
|
16
|
+
|
|
17
|
+
### Changed
|
|
18
|
+
|
|
19
|
+
- Upgrade all application `@apicity/*` dependencies to 0.16.2.
|
|
20
|
+
|
|
21
|
+
### Fixed
|
|
22
|
+
|
|
23
|
+
- Accept npm 12 workspace pack results in the installed-package smoke gate.
|
|
24
|
+
|
|
25
|
+
## [0.2.1] - 2026-10-20
|
|
26
|
+
|
|
27
|
+
### Changed
|
|
28
|
+
|
|
29
|
+
- Upgrade to videobook-engine 5.7.1 with official DoltLite 0.51.1 for
|
|
30
|
+
concurrency, reopened-catalog allocation, schema-conflict recovery, and
|
|
31
|
+
ignored AUTOINCREMENT sequence fixes. Existing 0.50.9 books retain their
|
|
32
|
+
format and engine schema.
|
|
33
|
+
|
|
9
34
|
## [0.2.0] - 2026-10-19
|
|
10
35
|
|
|
11
36
|
### Added
|
package/dist/chat-router.js
CHANGED
|
@@ -576,7 +576,7 @@ Parameter guidelines:
|
|
|
576
576
|
- Preserve every consumed notebook @address in the generation prompt at the semantic position where it belongs. Do not strip, rename, or expand notebook references into prose
|
|
577
577
|
- For generate_video: put the compact generation description in "prompt" and preserve every consumed @ reference
|
|
578
578
|
- For generate_image: put the compact image description in "prompt" and preserve every consumed image @address there; use refs.image ONLY for the corresponding resolved asset tags whose asset context type is "image", in the same order (include @ prefix). Never put a video or audio asset tag in refs.image — if the user wants an image inspired by a video, describe that source without connecting it as an image
|
|
579
|
-
- ByteDance naming: Seedance is video, Seedream is image. Users often say Seedance for both. When the query names seedance, bytedance, or seedream: if the edit/replace target is an image, use generate_image with model kie:seedream/5-lite-image-to-image and put the edit-target image first in refs.image, followed by replacement image refs. If the target is a video, use generate_video with model kie:bytedance/seedance-2-5,
|
|
579
|
+
- ByteDance naming: Seedance is video, Seedream is image. Users often say Seedance for both. When the query names seedance, bytedance, or seedream: if the edit/replace target is an image, use generate_image with model kie:seedream/5-lite-image-to-image and put the edit-target image first in refs.image, followed by replacement image refs. If the target is a video, use generate_video with model kie:bytedance/seedance-2-5, mode v2v, one 4–30 second clip in refs.video and replacement stills in refs.image. Edit mode follows the source aspect ratio and duration. Never route an image-target Seedance ask to a video model or to Nano Banana Pro
|
|
580
580
|
- Treat "Seedance 5.0 edit", "Seedance 5", and "Seedream 5" as Seedream 5. Generic chat uses Seedream 5 Lite image-to-image; Seedream 5 Pro edit lives in the subject/region pipeline. When Seedream is used for an image ask that said Seedance, the status message must note that substitution
|
|
581
581
|
- For create_watermark_card (QR code / watermark card): use this tool for real scannable QR codes — never generate_image. Pass url from the referenced asset's source_url/webpage_url when the user says "from the url in @vid-…" or similar; optional handle is an X or TikTok alias without @; set platform to "tiktok" for TikTok or "x" for X. apply_watermark uses the same platform field for its alias, icon, and default QR destination
|
|
582
582
|
- For create_qart (artistic / picture QR): use when turning an image asset into scan-safe color and monochrome QR mosaics; pass image_ref and value (URL/text). Prefer over create_watermark_card when the user wants a photo integrated into the QR modules
|
|
@@ -85,7 +85,7 @@ Use Seedance 2.0 reference-to-video when replacing people in a clip. Pass the so
|
|
|
85
85
|
Replace the on-screen person matching @Image1 with @Image1 as character reference. Replace the on-screen person matching @Image2 with @Image2 as character reference. Replace the on-screen person matching @Image3 with @Image3 as character reference. Keep camera movement, pacing, and framing of @Video1.
|
|
86
86
|
```
|
|
87
87
|
|
|
88
|
-
Notebook wiring: connect the source video plus the identity stills to a Seedance generation cell (model `kie:bytedance/seedance-2-5`, mode `
|
|
88
|
+
Notebook wiring: connect the source video plus the identity stills to a Seedance generation cell (model `kie:bytedance/seedance-2-5`, mode `v2v` / Edit video). Use one source video with a known duration of 4–30 seconds; import URL sources into the book first. Edit mode sets `aspect_ratio=adaptive` and `duration=-1` to preserve the source shape and length. Ordinary reference generation remains `r2v` with its chosen output settings. The runner fills `refs.video` from connected video cells and `refs.image` from connected image cells. The result is a normal generated video artifact (`original.mp4` / `proxy.mp4`), not an orientation render.
|
|
89
89
|
|
|
90
90
|
## Camera Movement Vocabulary
|
|
91
91
|
|
|
@@ -9,6 +9,7 @@ const STANDARD_IMAGE_EDIT_CAPABILITIES = {
|
|
|
9
9
|
};
|
|
10
10
|
const EDIT_MODELS = [
|
|
11
11
|
{ catalogModel: `kie:${DEFAULT_IMAGE_EDIT_MODEL_ID}`, label: "Nano Banana 2.1 Edit" },
|
|
12
|
+
{ catalogModel: "kie:flux-3/klein-edit-image", label: "FLUX 3 Klein Edit" },
|
|
12
13
|
{ catalogModel: "kie:seedream/5-pro-image-to-image", label: "Seedream 5 Pro" },
|
|
13
14
|
{ catalogModel: "kie:gpt-image-2-5-flare-image-to-image", label: "GPT Image 2.5 Flare" },
|
|
14
15
|
{ catalogModel: "kie:gpt-image-2-5-sunburst-image-to-image", label: "GPT Image 2.5 Sunburst" },
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { isSeedanceEdit } from "../media/seedance-edit.js";
|
|
1
2
|
import { registerJobHandler } from "../queue.js";
|
|
2
3
|
import { assertProviderPayload } from "../provider-prompt-transform.js";
|
|
3
4
|
import { requireApiKey } from "../api-key.js";
|
|
@@ -5,7 +6,7 @@ import { referenceVideoDuration } from "../reference-video-duration.js";
|
|
|
5
6
|
import { resolveModel } from "../media/descriptor.js";
|
|
6
7
|
import { buildWireRequest } from "../media/request.js";
|
|
7
8
|
import { applyPromptDialect } from "../media/prompt-dialects.js";
|
|
8
|
-
import { checkInsertVideoTiming, checkRefDurations, resolveRefInput, uploadRefs } from "../media/refs.js";
|
|
9
|
+
import { checkSeedanceEditSource, checkInsertVideoTiming, checkRefDurations, resolveRefInput, uploadRefs } from "../media/refs.js";
|
|
9
10
|
import { adapterFor } from "../media/adapters/index.js";
|
|
10
11
|
import { completeMediaGeneration } from "../media/complete.js";
|
|
11
12
|
function storedPayload(model, request) {
|
|
@@ -17,11 +18,21 @@ async function replaceAudioMeta(engine, ref) {
|
|
|
17
18
|
const input = await resolveRefInput(engine, "audio", ref);
|
|
18
19
|
return { replaceAudioWith: { assetId: input.assetId, filename: input.filename } };
|
|
19
20
|
}
|
|
20
|
-
async function
|
|
21
|
-
|
|
21
|
+
export async function generationSourceCostHints(engine, refs, job) {
|
|
22
|
+
const videoSeconds = await referenceVideoDuration(engine, refs.sourceVideoIds);
|
|
23
|
+
if (videoSeconds !== undefined) {
|
|
24
|
+
return { inputDurationSeconds: videoSeconds, ...(isSeedanceEdit(job.model, job.mode) ? { durationSeconds: videoSeconds } : {}) };
|
|
25
|
+
}
|
|
26
|
+
if (resolveModel(job.model).provider !== "fal")
|
|
22
27
|
return {};
|
|
23
|
-
const
|
|
24
|
-
|
|
28
|
+
for (const assetId of refs.sourceAudioIds) {
|
|
29
|
+
const metadata = await engine.metadata.artifacts.read(assetId, "original");
|
|
30
|
+
const seconds = metadata.ok ? metadata.value.duration : undefined;
|
|
31
|
+
if (typeof seconds === "number" && Number.isFinite(seconds) && seconds > 0) {
|
|
32
|
+
return { inputDurationSeconds: seconds };
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
return {};
|
|
25
36
|
}
|
|
26
37
|
function generationMeta(job, model, refs) {
|
|
27
38
|
return {
|
|
@@ -40,6 +51,7 @@ export async function runGenerateMedia(engine, job, ctx) {
|
|
|
40
51
|
await engine.metadata.artifacts.write(job.assetId, "generation-request", job);
|
|
41
52
|
const prompt = job.prompt === undefined ? undefined : applyPromptDialect(model.entry.dialect, job.prompt, job.refs);
|
|
42
53
|
const durationError = (await checkInsertVideoTiming(engine, model, job.refs, job.options))
|
|
54
|
+
?? (await checkSeedanceEditSource(engine, model, job.refs, job.mode))
|
|
43
55
|
?? await checkRefDurations(engine, model, job.refs);
|
|
44
56
|
if (durationError)
|
|
45
57
|
throw new Error(durationError);
|
|
@@ -58,7 +70,7 @@ export async function runGenerateMedia(engine, job, ctx) {
|
|
|
58
70
|
assetId: job.assetId,
|
|
59
71
|
assetDir: job.assetDir,
|
|
60
72
|
meta,
|
|
61
|
-
costHints: await
|
|
73
|
+
costHints: await generationSourceCostHints(engine, refs, job),
|
|
62
74
|
});
|
|
63
75
|
if (result.kind === "done") {
|
|
64
76
|
await completeMediaGeneration({ engine, assetId: job.assetId, assetDir: job.assetDir, bookId: job.bookId, meta, outputs: result.outputs });
|
|
@@ -14,7 +14,7 @@ import { smoothSceneConcatDef } from "../tools/smooth-scene-concat.js";
|
|
|
14
14
|
import { spliceVideoDef } from "../tools/splice-video.js";
|
|
15
15
|
import { tryResolveModel } from "../media/descriptor.js";
|
|
16
16
|
import { previewGeneration } from "../media/preview.js";
|
|
17
|
-
import { checkInsertVideoTiming, normalizeRefs } from "../media/refs.js";
|
|
17
|
+
import { checkSeedanceEditSource, checkInsertVideoTiming, normalizeRefs } from "../media/refs.js";
|
|
18
18
|
import { REF_ROLES } from "../media/types.js";
|
|
19
19
|
import { captureFrame, concatSourceRanges, exportSequenceMaster, extractSourceAudio, MINIMUM_SPLIT_PIECE_SECONDS, normalizeSceneVideo, splitSourceRange, splitSourceRanges, } from "../notebook-media-ops.js";
|
|
20
20
|
import { analyzeNotebookSource } from "../notebook-live-analysis.js";
|
|
@@ -830,6 +830,9 @@ async function prepareMediaGeneration(model, document, cell, bookId, engine, opt
|
|
|
830
830
|
const timingError = await checkInsertVideoTiming(engine, model, normalizeRefs(prepared.input.refs), preview.preview.payload.input ?? {});
|
|
831
831
|
if (timingError)
|
|
832
832
|
throw new Error(timingError);
|
|
833
|
+
const sourceError = await checkSeedanceEditSource(engine, model, normalizeRefs(prepared.input.refs), prepared.input.mode);
|
|
834
|
+
if (sourceError)
|
|
835
|
+
throw new Error(sourceError);
|
|
833
836
|
}
|
|
834
837
|
return {
|
|
835
838
|
tool: `generate_${model.media}`,
|
|
@@ -2357,7 +2360,7 @@ export async function failQueuedNotebookCells(engine, notebookId, options) {
|
|
|
2357
2360
|
function notebookRunPlanRecords(document) {
|
|
2358
2361
|
return document.notebookRunPlans ?? [];
|
|
2359
2362
|
}
|
|
2360
|
-
async function updateNotebookRunPlanState(engine, notebookId, planId, status, details = {}) {
|
|
2363
|
+
async function updateNotebookRunPlanState(engine, notebookId, planId, status, details = {}, recordedRun) {
|
|
2361
2364
|
const result = await mutateNotebook(engine, notebookId, "update_notebook_run_plan", (document) => ({
|
|
2362
2365
|
...document,
|
|
2363
2366
|
notebookRunPlans: notebookRunPlanRecords(document).map((record) => record.planId === planId
|
|
@@ -2367,6 +2370,17 @@ async function updateNotebookRunPlanState(engine, notebookId, planId, status, de
|
|
|
2367
2370
|
...record,
|
|
2368
2371
|
status,
|
|
2369
2372
|
...details,
|
|
2373
|
+
...(recordedRun ? {
|
|
2374
|
+
plan: {
|
|
2375
|
+
...record.plan,
|
|
2376
|
+
recordedRunId: recordedRun.recordedRunId,
|
|
2377
|
+
recordedRuns: [
|
|
2378
|
+
...(record.plan.recordedRuns ?? []),
|
|
2379
|
+
recordedRun,
|
|
2380
|
+
],
|
|
2381
|
+
},
|
|
2382
|
+
error: recordedRun.error,
|
|
2383
|
+
} : {}),
|
|
2370
2384
|
updatedAt: new Date().toISOString(),
|
|
2371
2385
|
}
|
|
2372
2386
|
: record),
|
|
@@ -2922,7 +2936,7 @@ export function registerRunNotebookJob(engine) {
|
|
|
2922
2936
|
}
|
|
2923
2937
|
const now = new Date().toISOString();
|
|
2924
2938
|
const run = {
|
|
2925
|
-
id:
|
|
2939
|
+
id: uuidv7(),
|
|
2926
2940
|
notebookId: input.notebookId,
|
|
2927
2941
|
status: errors.length === 0 ? "completed" : "failed",
|
|
2928
2942
|
startedAt: now,
|
|
@@ -2939,11 +2953,16 @@ export function registerRunNotebookJob(engine) {
|
|
|
2939
2953
|
runId,
|
|
2940
2954
|
outputs,
|
|
2941
2955
|
...(errors.length > 0 ? { error: errors.join("\n") } : {}),
|
|
2956
|
+
}, {
|
|
2957
|
+
recordedRunId: run.id,
|
|
2958
|
+
status: run.status,
|
|
2959
|
+
completedAt: now,
|
|
2960
|
+
...(run.error ? { error: run.error } : {}),
|
|
2942
2961
|
});
|
|
2943
2962
|
}
|
|
2944
2963
|
if (errors.length > 0)
|
|
2945
2964
|
throw new Error(errors.join("\n"));
|
|
2946
|
-
return run;
|
|
2965
|
+
return { ...run, id: runId, runId, recordedRunId: run.id };
|
|
2947
2966
|
};
|
|
2948
2967
|
registerJobHandler("run_notebook", async (payload) => {
|
|
2949
2968
|
const input = payload;
|
|
@@ -3,6 +3,7 @@ import { requireApiKey } from "../../api-key.js";
|
|
|
3
3
|
import { uploadAssetToFal } from "../../jobs/_fal-upload.js";
|
|
4
4
|
import { describeFalError } from "../../jobs/_fal-shared.js";
|
|
5
5
|
import { withProviderHandoff } from "../../jobs/_lease.js";
|
|
6
|
+
import { generationCostHints } from "../cost.js";
|
|
6
7
|
import { providerEndpoints } from "../endpoints.js";
|
|
7
8
|
import { writePendingTask } from "./pending.js";
|
|
8
9
|
import { MEDIA_TASK_TYPES } from "./types.js";
|
|
@@ -30,6 +31,12 @@ async function submitQueued(request, context, ownerId) {
|
|
|
30
31
|
return submitted.request_id;
|
|
31
32
|
}
|
|
32
33
|
async function submit(request, context) {
|
|
34
|
+
const hints = generationCostHints(request, context.costHints);
|
|
35
|
+
if (Object.keys(hints).length > 0) {
|
|
36
|
+
const written = await context.engine.metadata.artifacts.write(context.assetId, "generation-cost-hints", hints);
|
|
37
|
+
if (!written.ok)
|
|
38
|
+
throw new Error(written.error.message);
|
|
39
|
+
}
|
|
33
40
|
const taskId = await withProviderHandoff(context.engine, context.assetId, context.ctx, "generate", async (ownerId) => {
|
|
34
41
|
const id = await submitQueued(request, context, ownerId);
|
|
35
42
|
return { taskId: id, result: id };
|
package/dist/media/catalog.js
CHANGED
|
@@ -60,6 +60,8 @@ function h3MaxStyle(style) {
|
|
|
60
60
|
}
|
|
61
61
|
export const MODEL_ENTRIES = [
|
|
62
62
|
{ id: "kie:nano-banana-2-1", media: "image", operations: ["text_to_image", "edit_image"], dialect: "natural_numbered", defaults: { ...LIGHTNING_SCENE_OPTIONS } },
|
|
63
|
+
{ id: "kie:flux-3/klein-text-to-image", media: "image", operations: ["text_to_image"], dialect: "natural", defaults: { resolution: "1K" } },
|
|
64
|
+
{ id: "kie:flux-3/klein-edit-image", media: "image", operations: ["edit_image"], dialect: "natural", defaults: { resolution: "1K" } },
|
|
63
65
|
{ id: "kie:nano-banana-pro", media: "image", operations: ["text_to_image", "edit_image"], guide: "nano_banana", dialect: "natural" },
|
|
64
66
|
{ id: "kie:gpt-image-2-5-flare-text-to-image", media: "image", operations: ["text_to_image"], guide: "gpt_image", dialect: "natural", defaults: { resolution: "2K" } },
|
|
65
67
|
{ id: "kie:gpt-image-2-5-flare-image-to-image", media: "image", operations: ["edit_image"], guide: "gpt_image", dialect: "natural", defaults: { resolution: "2K" } },
|
|
@@ -115,10 +117,10 @@ export const MODEL_ENTRIES = [
|
|
|
115
117
|
{
|
|
116
118
|
id: "kie:bytedance/seedance-2-5",
|
|
117
119
|
media: "video",
|
|
118
|
-
operations: ["text_to_video", "image_to_video", "reference_to_video"],
|
|
120
|
+
operations: ["text_to_video", "image_to_video", "reference_to_video", "edit_video"],
|
|
119
121
|
guide: "seedance25",
|
|
120
122
|
dialect: "seedance",
|
|
121
|
-
modes: SEEDANCE_MODES,
|
|
123
|
+
modes: [...SEEDANCE_MODES, { id: "v2v", label: "Edit video", roles: ["video", "image", "audio"], requireAny: ["video"] }],
|
|
122
124
|
defaults: { ...LIGHTNING_VIDEO_OPTIONS },
|
|
123
125
|
frameOptions: { aspect_ratio: "adaptive" },
|
|
124
126
|
},
|
package/dist/media/cost.js
CHANGED
|
@@ -18,7 +18,7 @@ function durationHint(input) {
|
|
|
18
18
|
?? (numeric(input.music_length_ms) !== undefined ? numeric(input.music_length_ms) / 1000 : undefined);
|
|
19
19
|
return seconds !== undefined && seconds > 0 ? { durationSeconds: seconds } : {};
|
|
20
20
|
}
|
|
21
|
-
function
|
|
21
|
+
export function generationCostHints(input, hints) {
|
|
22
22
|
const combined = { ...durationHint(input), ...hints };
|
|
23
23
|
return combined.durationSeconds === undefined && combined.inputDurationSeconds !== undefined
|
|
24
24
|
? { ...combined, durationSeconds: combined.inputDurationSeconds }
|
|
@@ -29,7 +29,7 @@ function estimateWireRequest(model, request, input, hints = {}) {
|
|
|
29
29
|
provider: costProvider(model),
|
|
30
30
|
payload: costPayload(model, request, input),
|
|
31
31
|
endpoint: model.apiId,
|
|
32
|
-
costHints:
|
|
32
|
+
costHints: generationCostHints(input, hints),
|
|
33
33
|
});
|
|
34
34
|
}
|
|
35
35
|
export function estimateGeneration(model, call, hints = {}) {
|
package/dist/media/refs.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { isSeedanceEdit } from "./seedance-edit.js";
|
|
1
2
|
import { referenceVideoDuration } from "../reference-video-duration.js";
|
|
2
3
|
import { insertVideoTimingError, isInsertVideoModel } from "./insert-video-timing.js";
|
|
3
4
|
import { resolveAudioRef, resolveImageRef, resolveVideoRef } from "../tools/_media-refs.js";
|
|
@@ -129,3 +130,15 @@ export async function checkInsertVideoTiming(engine, model, refs, options) {
|
|
|
129
130
|
const duration = source ? await referenceVideoDuration(engine, [source]) : undefined;
|
|
130
131
|
return insertVideoTimingError(options, duration);
|
|
131
132
|
}
|
|
133
|
+
export async function checkSeedanceEditSource(engine, model, refs, mode) {
|
|
134
|
+
if (!isSeedanceEdit(model.id, mode))
|
|
135
|
+
return null;
|
|
136
|
+
if (refs.video?.length !== 1)
|
|
137
|
+
return "Seedance Edit video requires exactly one source video (4–30 seconds).";
|
|
138
|
+
const duration = await referenceVideoDuration(engine, refs.video);
|
|
139
|
+
if (duration === undefined)
|
|
140
|
+
return "Seedance Edit video requires a source with known duration. Import the video into the book and use its artifact reference (4–30 seconds).";
|
|
141
|
+
return duration < 4 || duration > 30
|
|
142
|
+
? `Seedance Edit video source is ${duration} seconds; use a source between 4 and 30 seconds.`
|
|
143
|
+
: null;
|
|
144
|
+
}
|
package/dist/media/request.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { isSeedanceEdit } from "./seedance-edit.js";
|
|
1
2
|
import { insertVideoTimingError, isInsertVideoModel } from "./insert-video-timing.js";
|
|
2
3
|
function clone(value) {
|
|
3
4
|
return value === undefined ? value : structuredClone(value);
|
|
@@ -162,6 +163,8 @@ export function buildWireRequest(model, call) {
|
|
|
162
163
|
const refError = checkRefs(model, refs, call.mode);
|
|
163
164
|
if (refError)
|
|
164
165
|
return { ok: false, error: refError };
|
|
166
|
+
if (isSeedanceEdit(model.id, call.mode) && refs.video?.length !== 1)
|
|
167
|
+
return { ok: false, error: "Seedance Edit video requires exactly one source video (4–30 seconds)." };
|
|
165
168
|
const promptError = checkPrompt(model, call.prompt);
|
|
166
169
|
if (promptError)
|
|
167
170
|
return { ok: false, error: promptError };
|
|
@@ -172,6 +175,10 @@ export function buildWireRequest(model, call) {
|
|
|
172
175
|
if (optionError)
|
|
173
176
|
return { ok: false, error: optionError };
|
|
174
177
|
applyFrameOptions(model, value, refs, call.mode);
|
|
178
|
+
if (isSeedanceEdit(model.id, call.mode)) {
|
|
179
|
+
value.aspect_ratio = "adaptive";
|
|
180
|
+
value.duration = -1;
|
|
181
|
+
}
|
|
175
182
|
const prompt = call.prompt?.trim();
|
|
176
183
|
if (model.promptField && prompt)
|
|
177
184
|
setPath(value, model.promptField, prompt);
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export const SEEDANCE_EDIT_DESCRIPTION = "Edit video follows the source aspect ratio and duration. Connect one 4–30 second video plus optional image or audio references.";
|
|
2
|
+
export function isSeedanceEdit(model, mode) {
|
|
3
|
+
return model === "kie:bytedance/seedance-2-5" && mode === "v2v";
|
|
4
|
+
}
|
|
5
|
+
export function seedanceEditFailure(model, message) {
|
|
6
|
+
if (model !== "kie:bytedance/seedance-2-5" || !/video edit/i.test(message))
|
|
7
|
+
return message;
|
|
8
|
+
return `${message} Select Edit video (mode v2v) with one 4–30 second source video; this sets aspect_ratio=adaptive and duration=-1. Review the preview before retrying.`;
|
|
9
|
+
}
|
package/dist/provider-usage.js
CHANGED
|
@@ -3,6 +3,7 @@ import { readFileSync } from "node:fs";
|
|
|
3
3
|
import { dirname, join } from "node:path";
|
|
4
4
|
import { fileURLToPath } from "node:url";
|
|
5
5
|
import { createCost, PRICING, PRICING_AS_OF } from "@apicity/cost";
|
|
6
|
+
import { modelEntry } from "./media/catalog.js";
|
|
6
7
|
import { currentBookContext } from "./book-context.js";
|
|
7
8
|
function installedPackageVersion(name) {
|
|
8
9
|
let dir = dirname(fileURLToPath(import.meta.resolve(name)));
|
|
@@ -127,7 +128,8 @@ function isBillable(provider, path, method) {
|
|
|
127
128
|
if (provider === "kie")
|
|
128
129
|
return /\/(createTask|veo\/(generate|extend))$/u.test(path);
|
|
129
130
|
if (provider === "fal")
|
|
130
|
-
return
|
|
131
|
+
return !/\/requests\//u.test(path)
|
|
132
|
+
&& (/^\/(fal-ai|alibaba)\//u.test(path) || modelEntry(`fal:${path.slice(1)}`) !== undefined);
|
|
131
133
|
if (provider === "alibaba")
|
|
132
134
|
return path.includes("/services/aigc/");
|
|
133
135
|
if (provider === "elevenlabs")
|
|
@@ -5,7 +5,7 @@ import { previewPlaceholderUrl } from "../preview-payload.js";
|
|
|
5
5
|
import { DEFAULT_IMAGE_EDIT_MODEL_ID, findImageEditModel } from "../image-edit-models.js";
|
|
6
6
|
import { tryResolveModel } from "../media/descriptor.js";
|
|
7
7
|
import { estimateGeneration } from "../media/cost.js";
|
|
8
|
-
import { normalizeRefs } from "../media/refs.js";
|
|
8
|
+
import { checkSeedanceEditSource, normalizeRefs } from "../media/refs.js";
|
|
9
9
|
import { REF_ROLES } from "../media/types.js";
|
|
10
10
|
const GENERATION_TOOLS = new Set(["generate_image", "generate_video", "generate_audio"]);
|
|
11
11
|
const REWRITE_MODEL = "gpt-5.6-sol";
|
|
@@ -67,6 +67,9 @@ async function estimateMedia(engine, input) {
|
|
|
67
67
|
const options = input.options !== null && typeof input.options === "object" && !Array.isArray(input.options)
|
|
68
68
|
? input.options
|
|
69
69
|
: {};
|
|
70
|
+
const sourceError = await checkSeedanceEditSource(engine, model, refs, input.mode);
|
|
71
|
+
if (sourceError)
|
|
72
|
+
return unavailable(sourceError);
|
|
70
73
|
const result = estimateGeneration(model, {
|
|
71
74
|
prompt: text(input.prompt),
|
|
72
75
|
refUrls: placeholderRefs(refs),
|
|
@@ -4,7 +4,7 @@ import { generationLabel } from "../artifact-label.js";
|
|
|
4
4
|
import { previewPlaceholderUrl } from "../preview-payload.js";
|
|
5
5
|
import { resolveModel, UnknownModelError } from "../media/descriptor.js";
|
|
6
6
|
import { buildWireRequest } from "../media/request.js";
|
|
7
|
-
import { checkInsertVideoTiming, checkRefDurations, checkRefs, normalizeRefs } from "../media/refs.js";
|
|
7
|
+
import { checkSeedanceEditSource, checkInsertVideoTiming, checkRefDurations, checkRefs, normalizeRefs } from "../media/refs.js";
|
|
8
8
|
import { PROVIDER_ENV_KEYS } from "../media/adapters/index.js";
|
|
9
9
|
import { listModels, modelListing } from "../media/listing.js";
|
|
10
10
|
import { OPERATIONS_BY_MEDIA, REF_ROLES } from "../media/types.js";
|
|
@@ -80,6 +80,7 @@ async function validate(engine, model, args, refs) {
|
|
|
80
80
|
return built.error;
|
|
81
81
|
return (await checkRefs(engine, refs))
|
|
82
82
|
?? (await checkInsertVideoTiming(engine, model, refs, optionsOf(args.options)))
|
|
83
|
+
?? (await checkSeedanceEditSource(engine, model, refs, args.mode))
|
|
83
84
|
?? checkRefDurations(engine, model, refs);
|
|
84
85
|
}
|
|
85
86
|
async function enqueueGeneration(engine, model, args, refs) {
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { seedanceEditFailure } from "../media/seedance-edit.js";
|
|
1
2
|
import { createKie, createXai, createAlibaba, createFal } from "../usage-providers.js";
|
|
2
3
|
import { describeFalError } from "../jobs/_fal-shared.js";
|
|
3
4
|
import { currentBookContext, withBookContext } from "../book-context.js";
|
|
@@ -1276,7 +1277,7 @@ async function processKieTaskResult(engine, task, taskInfo, result, bookId) {
|
|
|
1276
1277
|
result.completed.push({ assetId: task.artifactId, taskType: task.taskType, jobId });
|
|
1277
1278
|
}
|
|
1278
1279
|
else if (data?.state === "fail") {
|
|
1279
|
-
const failMsg = data.failMsg ?? "unknown error";
|
|
1280
|
+
const failMsg = seedanceEditFailure(task.meta?.model, data.failMsg ?? "unknown error");
|
|
1280
1281
|
// A kie 500 is only retryable while the task is still in flight; once it
|
|
1281
1282
|
// has terminally completed, surface the failure instead of stranding the
|
|
1282
1283
|
// asset as "working" until the 30m reconciliation timeout. See isTransientKieFailure.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "videobook",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.2",
|
|
4
4
|
"description": "Local-first MCP server and web UI for AI video creation: notebooks, generation, editing, rendering and publishing.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"mcp",
|
|
@@ -36,18 +36,18 @@
|
|
|
36
36
|
"start": "node dist/index.js"
|
|
37
37
|
},
|
|
38
38
|
"dependencies": {
|
|
39
|
-
"@apicity/alibaba": "^0.16.
|
|
40
|
-
"@apicity/b2": "^0.16.
|
|
41
|
-
"@apicity/cost": "^0.16.
|
|
42
|
-
"@apicity/dropbox": "^0.16.
|
|
43
|
-
"@apicity/elevenlabs": "^0.16.
|
|
44
|
-
"@apicity/fal": "^0.16.
|
|
45
|
-
"@apicity/free-media-upload": "^0.16.
|
|
46
|
-
"@apicity/kie": "^0.16.
|
|
47
|
-
"@apicity/kimicoding": "^0.16.
|
|
48
|
-
"@apicity/openai": "^0.16.
|
|
49
|
-
"@apicity/x": "^0.16.
|
|
50
|
-
"@apicity/xai": "^0.16.
|
|
39
|
+
"@apicity/alibaba": "^0.16.2",
|
|
40
|
+
"@apicity/b2": "^0.16.2",
|
|
41
|
+
"@apicity/cost": "^0.16.2",
|
|
42
|
+
"@apicity/dropbox": "^0.16.2",
|
|
43
|
+
"@apicity/elevenlabs": "^0.16.2",
|
|
44
|
+
"@apicity/fal": "^0.16.2",
|
|
45
|
+
"@apicity/free-media-upload": "^0.16.2",
|
|
46
|
+
"@apicity/kie": "^0.16.2",
|
|
47
|
+
"@apicity/kimicoding": "^0.16.2",
|
|
48
|
+
"@apicity/openai": "^0.16.2",
|
|
49
|
+
"@apicity/x": "^0.16.2",
|
|
50
|
+
"@apicity/xai": "^0.16.2",
|
|
51
51
|
"@devmehq/open-graph-extractor": "^1.1.1",
|
|
52
52
|
"@ffprobe-installer/ffprobe": "2.1.2",
|
|
53
53
|
"@modelcontextprotocol/client": "^2.2.0",
|
|
@@ -69,7 +69,7 @@
|
|
|
69
69
|
"remotion": "4.0.494",
|
|
70
70
|
"sharp": "0.35.5",
|
|
71
71
|
"uuid": "^14.0.0",
|
|
72
|
-
"videobook-engine": "5.7.
|
|
72
|
+
"videobook-engine": "5.7.1",
|
|
73
73
|
"ws": "^8.21.0",
|
|
74
74
|
"ytdlp-nodejs": "^3.4.4",
|
|
75
75
|
"zod": "^4.4.3"
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{c as F,j as t,a as A}from"./vendor-react-Dj7D1fD4.js";import{u as be,a as xe,V as Se,A as pe,P as Ne}from"./vendor-remotion-CH1HXrlc.js";import{ea as Ce,eb as ke,ec as we,ed as $e,ee as Pe,ef as de,eg as Te,eh as me,ei as Re,ej as Ae,ek as Me,el as _e,em as Ee,en as Oe,eo as fe,bu as We}from"./videobook-Qlp9HQFu.js";import{g as Le,a6 as ze,_ as Fe,h as He,X as Ie}from"./vendor-icons-CGqhCIXV.js";import"./vendor-DfTCWgKm.js";import"./vendor-mcp-BTlEJcHV.js";const Be={face:"#63c7d8",text:"#f0a860",graphic:"#f0a860"};function De(a){const e=F.c(25),{phrase:s,cue:n,settings:o,sourceWidth:c,sourceHeight:l}=a,{width:i,height:h}=xe();let r,f,m,d;if(e[0]!==h||e[1]!==s.id||e[2]!==s.regions||e[3]!==o||e[4]!==l||e[5]!==c||e[6]!==i){const j={sourceWidth:c,sourceHeight:l,targetWidth:i,targetHeight:h};let v;e[11]!==s.regions||e[12]!==o?(v=new Set($e(s.regions,o)),e[11]=s.regions,e[12]=o,e[13]=v):v=e[13];const C=v;let y;e[14]!==i?(y=Math.round(i/480),e[14]=i,e[15]=y):y=e[15],f=Math.max(2,y),r=pe,e[16]===Symbol.for("react.memo_cache_sentinel")?(m={pointerEvents:"none"},e[16]=m):m=e[16],d=s.regions.map((w,$)=>{const b=Pe(w,j);return t.jsx("div",{style:{position:"absolute",left:b.x*i,top:b.y*h,width:b.width*i,height:b.height*h,border:`${f}px dashed ${Be[w.kind]}`,opacity:C.has(w)?1:.4,borderRadius:f*2}},`${s.id}-${$}`)}),e[0]=h,e[1]=s.id,e[2]=s.regions,e[3]=o,e[4]=l,e[5]=c,e[6]=i,e[7]=r,e[8]=f,e[9]=m,e[10]=d}else r=e[7],f=e[8],m=e[9],d=e[10];let g;e[17]!==n.column||e[18]!==f?(g=t.jsx(Ue,{column:n.column,stroke:f}),e[17]=n.column,e[18]=f,e[19]=g):g=e[19];let u;return e[20]!==r||e[21]!==m||e[22]!==d||e[23]!==g?(u=t.jsxs(r,{style:m,children:[d,g]}),e[20]=r,e[21]=m,e[22]=d,e[23]=g,e[24]=u):u=e[24],u}function Ue(a){const e=F.c(7),{column:s,stroke:n}=a,o=`${Math.max(1,n/2)}px dashed rgba(114, 231, 170, 0.6)`,c=n*2;let l;return e[0]!==s.height||e[1]!==s.width||e[2]!==s.x||e[3]!==s.y||e[4]!==o||e[5]!==c?(l=t.jsx("div",{style:{position:"absolute",left:s.x,top:s.y,width:s.width,height:s.height,border:o,borderRadius:c}}),e[0]=s.height,e[1]=s.width,e[2]=s.x,e[3]=s.y,e[4]=o,e[5]=c,e[6]=l):l=e[6],l}function Ke(a){const e=F.c(34),{mediaUrl:s,sourceWidth:n,sourceHeight:o,cues:c,phrases:l,settings:i,showRegions:h}=a,r=be(),{fps:f,width:m,height:d}=xe();let g;e[0]!==d||e[1]!==o||e[2]!==n||e[3]!==m?(g=ke({sourceWidth:n,sourceHeight:o,targetWidth:m,targetHeight:d}),e[0]=d,e[1]=o,e[2]=n,e[3]=m,e[4]=g):g=e[4];const u=g,j=r/f;let v;e[5]!==c||e[6]!==j?(v=Ce(c,j),e[5]=c,e[6]=j,e[7]=v):v=e[7];const C=v;let y;e[8]!==C||e[9]!==l?(y=C?l.find(M=>M.id===C.id):void 0,e[8]=C,e[9]=l,e[10]=y):y=e[10];const w=y;let $;e[11]===Symbol.for("react.memo_cache_sentinel")?($={background:"#000000"},e[11]=$):$=e[11];let b;e[12]!==u.height||e[13]!==u.width||e[14]!==u.x||e[15]!==u.y?(b={position:"absolute",left:u.x,top:u.y,width:u.width,height:u.height},e[12]=u.height,e[13]=u.width,e[14]=u.x,e[15]=u.y,e[16]=b):b=e[16];let N;e[17]!==s||e[18]!==b?(N=t.jsx(Se,{src:s,style:b}),e[17]=s,e[18]=b,e[19]=N):N=e[19];let P;e[20]!==C||e[21]!==w||e[22]!==i||e[23]!==h||e[24]!==o||e[25]!==n?(P=h&&C&&w&&t.jsx(De,{phrase:w,cue:C,settings:i,sourceWidth:n,sourceHeight:o}),e[20]=C,e[21]=w,e[22]=i,e[23]=h,e[24]=o,e[25]=n,e[26]=P):P=e[26];let x;e[27]!==c||e[28]!==i?(x=t.jsx(we,{cues:c,settings:i,strokeWidth:6,strokeColor:"#000000",shadowBlur:6,shadowOpacity:60}),e[27]=c,e[28]=i,e[29]=x):x=e[29];let k;return e[30]!==N||e[31]!==P||e[32]!==x?(k=t.jsxs(pe,{style:$,children:[N,P,x]}),e[30]=N,e[31]=P,e[32]=x,e[33]=k):k=e[33],k}const ue=[{id:"stack",label:"Stack",hint:"Words land one by one and build a stacked block beside the subject, each line sized to fill the column."},{id:"punch",label:"Punch",hint:"One word at a time, as big as the free space allows. Best on fast verses."},{id:"karaoke",label:"Karaoke",hint:"The whole stacked phrase shows dimmed and lights up word by word."}],Ve=[{id:"pop",label:"Pop"},{id:"slam",label:"Slam"},{id:"rise",label:"Rise"}],Ge=[{name:"Green",color:"#72E7AA"},{name:"Pink",color:"#F0B9D0"},{name:"Cyan",color:"#63C7D8"},{name:"Amber",color:"#E7C372"}];function Xe(a,e){if(!a.drifted)return de(a.start);const s=a.driftMs>0?"+":"";return e?`${de(a.start)} · snapped`:`${de(a.start)} · ${s}${a.driftMs}ms`}function qe(a){const e=F.c(21),{rows:s,phraseLabels:n,activePhrase:o,time:c,snapped:l,onSeek:i}=a;let h;e[0]===Symbol.for("react.memo_cache_sentinel")?(h=t.jsx("h3",{children:"Transcript"}),e[0]=h):h=e[0];let r;e[1]!==s.length?(r=t.jsxs("header",{className:"alive-panel-header",children:[h,t.jsxs("p",{children:[s.length," words · timed to the vocal"]})]}),e[1]=s.length,e[2]=r):r=e[2];let f;if(e[3]!==o||e[4]!==i||e[5]!==n||e[6]!==s||e[7]!==l||e[8]!==c){let g;e[10]!==o||e[11]!==i||e[12]!==s||e[13]!==l||e[14]!==c?(g=(u,j)=>t.jsxs("div",{className:"alive-phrase","data-active":j===o||void 0,children:[t.jsxs("div",{className:"alive-phrase-head",children:[t.jsxs("span",{children:["Phrase ",j+1]}),t.jsx("span",{children:u})]}),t.jsx("div",{className:"alive-phrase-words",children:s.filter(v=>v.phraseIndex===j).map(v=>t.jsxs("button",{type:"button",className:"alive-word","data-active":c>=v.start&&c<v.end||void 0,"data-drifted":v.drifted&&!l||void 0,onClick:()=>i(v.start+.03),children:[t.jsx("span",{children:v.text}),t.jsx("small",{children:Xe(v,l)})]},v.key))})]},`${j}-${u}`),e[10]=o,e[11]=i,e[12]=s,e[13]=l,e[14]=c,e[15]=g):g=e[15],f=n.map(g),e[3]=o,e[4]=i,e[5]=n,e[6]=s,e[7]=l,e[8]=c,e[9]=f}else f=e[9];let m;e[16]!==f?(m=t.jsx("div",{className:"alive-transcript-list",children:f}),e[16]=f,e[17]=m):m=e[17];let d;return e[18]!==r||e[19]!==m?(d=t.jsxs("section",{className:"alive-panel alive-transcript","aria-label":"Transcript",children:[r,m]}),e[18]=r,e[19]=m,e[20]=d):d=e[20],d}function ve(a){const e=F.c(10),{label:s,options:n,value:o,onChange:c}=a;let l;if(e[0]!==c||e[1]!==n||e[2]!==o){let h;e[4]!==c||e[5]!==o?(h=r=>t.jsx("button",{type:"button","aria-pressed":r.id===o,onClick:()=>c(r.id),children:r.label},r.id),e[4]=c,e[5]=o,e[6]=h):h=e[6],l=n.map(h),e[0]=c,e[1]=n,e[2]=o,e[3]=l}else l=e[3];let i;return e[7]!==s||e[8]!==l?(i=t.jsx("div",{className:"alive-segmented",role:"group","aria-label":s,children:l}),e[7]=s,e[8]=l,e[9]=i):i=e[9],i}function re(a){const e=F.c(8),{checked:s,onChange:n,children:o}=a;let c;e[0]!==n?(c=h=>n(h.target.checked),e[0]=n,e[1]=c):c=e[1];let l;e[2]!==s||e[3]!==c?(l=t.jsx("input",{type:"checkbox",checked:s,onChange:c}),e[2]=s,e[3]=c,e[4]=l):l=e[4];let i;return e[5]!==o||e[6]!==l?(i=t.jsxs("label",{className:"alive-toggle",children:[l,o]}),e[5]=o,e[6]=l,e[7]=i):i=e[7],i}function Ye(a){const e=F.c(69),{settings:s,showRegions:n,regionsAnalyzed:o,working:c,onChange:l,onShowRegions:i,onReanalyze:h}=a;let r;e[0]!==s.style?(r=ue.find(S=>S.id===s.style)?.hint,e[0]=s.style,e[1]=r):r=e[1];const f=r;let m;e[2]===Symbol.for("react.memo_cache_sentinel")?(m=t.jsx("h4",{children:"Style"}),e[2]=m):m=e[2];let d;e[3]!==l?(d=S=>l({style:S}),e[3]=l,e[4]=d):d=e[4];let g;e[5]!==s.style||e[6]!==d?(g=t.jsx(ve,{label:"Caption style",options:ue,value:s.style,onChange:d}),e[5]=s.style,e[6]=d,e[7]=g):g=e[7];let u;e[8]!==f?(u=t.jsx("p",{className:"alive-hint",children:f}),e[8]=f,e[9]=u):u=e[9];let j;e[10]!==g||e[11]!==u?(j=t.jsxs("div",{className:"alive-field",children:[m,g,u]}),e[10]=g,e[11]=u,e[12]=j):j=e[12];let v;e[13]===Symbol.for("react.memo_cache_sentinel")?(v=t.jsx("h4",{children:"Word entrance"}),e[13]=v):v=e[13];let C;e[14]!==l?(C=S=>l({motion:S}),e[14]=l,e[15]=C):C=e[15];let y;e[16]!==s.motion||e[17]!==C?(y=t.jsxs("div",{className:"alive-field",children:[v,t.jsx(ve,{label:"Word entrance",options:Ve,value:s.motion,onChange:C})]}),e[16]=s.motion,e[17]=C,e[18]=y):y=e[18];let w;e[19]===Symbol.for("react.memo_cache_sentinel")?(w=t.jsx("h4",{children:"Frame awareness"}),e[19]=w):w=e[19];let $;e[20]!==l?($=S=>l({avoidFaces:S}),e[20]=l,e[21]=$):$=e[21];let b;e[22]!==s.avoidFaces||e[23]!==$?(b=t.jsx(re,{checked:s.avoidFaces,onChange:$,children:"Keep words off faces"}),e[22]=s.avoidFaces,e[23]=$,e[24]=b):b=e[24];let N;e[25]!==l?(N=S=>l({avoidText:S}),e[25]=l,e[26]=N):N=e[26];let P;e[27]!==s.avoidText||e[28]!==N?(P=t.jsx(re,{checked:s.avoidText,onChange:N,children:"Keep words off on-screen text"}),e[27]=s.avoidText,e[28]=N,e[29]=P):P=e[29];let x;e[30]!==i||e[31]!==n?(x=t.jsx(re,{checked:n,onChange:i,children:"Show detected regions"}),e[30]=i,e[31]=n,e[32]=x):x=e[32];let k;e[33]!==o?(k=!o&&t.jsx("p",{className:"alive-hint alive-warning",children:"Face and text detection did not run, so stacks use the default columns. Set OPENAI_API_KEY and re-run the analysis."}),e[33]=o,e[34]=k):k=e[34];let M;e[35]!==b||e[36]!==P||e[37]!==x||e[38]!==k?(M=t.jsxs("div",{className:"alive-field",children:[w,b,P,x,k]}),e[35]=b,e[36]=P,e[37]=x,e[38]=k,e[39]=M):M=e[39];let D;e[40]===Symbol.for("react.memo_cache_sentinel")?(D=t.jsx("h4",{children:"Audio"}),e[40]=D):D=e[40];let O;e[41]!==l?(O=S=>l({energySizing:S}),e[41]=l,e[42]=O):O=e[42];let _;e[43]!==s.energySizing||e[44]!==O?(_=t.jsxs("div",{className:"alive-field",children:[D,t.jsx(re,{checked:s.energySizing,onChange:O,children:"Size words by vocal energy"})]}),e[43]=s.energySizing,e[44]=O,e[45]=_):_=e[45];let H;e[46]===Symbol.for("react.memo_cache_sentinel")?(H=t.jsx("h4",{children:"Hit color"}),e[46]=H):H=e[46];let W;e[47]!==l||e[48]!==s.hitColor?(W=Ge.map(S=>t.jsx("button",{type:"button","aria-label":S.name,"aria-pressed":s.hitColor.toUpperCase()===S.color,onClick:()=>l({hitColor:S.color}),children:t.jsx("span",{style:{background:S.color}})},S.color)),e[47]=l,e[48]=s.hitColor,e[49]=W):W=e[49];let R;e[50]===Symbol.for("react.memo_cache_sentinel")?(R=t.jsx("span",{children:"Text"}),e[50]=R):R=e[50];let p;e[51]!==l?(p=S=>l({textColor:S.target.value.toUpperCase()}),e[51]=l,e[52]=p):p=e[52];let L;e[53]!==s.textColor||e[54]!==p?(L=t.jsxs("label",{className:"alive-color-input",children:[R,t.jsx("input",{type:"color",value:s.textColor,onChange:p})]}),e[53]=s.textColor,e[54]=p,e[55]=L):L=e[55];let I;e[56]!==W||e[57]!==L?(I=t.jsxs("div",{className:"alive-field",children:[H,t.jsxs("div",{className:"alive-swatches",children:[W,L]})]}),e[56]=W,e[57]=L,e[58]=I):I=e[58];let z;e[59]!==h||e[60]!==c?(z=t.jsx("div",{className:"alive-field",children:t.jsx("button",{type:"button",className:"alive-secondary",disabled:c,onClick:h,children:"Re-run analysis"})}),e[59]=h,e[60]=c,e[61]=z):z=e[61];let B;return e[62]!==M||e[63]!==_||e[64]!==I||e[65]!==z||e[66]!==j||e[67]!==y?(B=t.jsxs("section",{className:"alive-panel alive-settings","aria-label":"Caption settings",children:[j,y,M,_,I,z]}),e[62]=M,e[63]=_,e[64]=I,e[65]=z,e[66]=j,e[67]=y,e[68]=B):B=e[68],B}const Je=90;function U(a,e){return`${Math.max(0,a)/e*100}%`}function Qe(a,e,s){if(e.count===0)return`All ${s} words already sit on their vocal onsets`;if(a)return`${e.count} words snapped to their vocal onsets`;const n=e.minMs===e.maxMs?`${e.maxMs}`:`${e.minMs}–${e.maxMs}`;return`${e.count} words start ${n} ms off the vocal`}function Ze(a){const e=a>120?10:a>30?5:1,s=[];for(let n=0;n<=a;n+=e)s.push(n);return s}function et(a,e,s){const n=F.c(5),o=A.useRef(null);let c,l;return n[0]!==e||n[1]!==s||n[2]!==a?(c=()=>{const i=o.current;if(!i||!s)return;const h=a/e*i.scrollWidth;(h<i.scrollLeft||h>i.scrollLeft+i.clientWidth*.85)&&(i.scrollLeft=Math.max(0,h-i.clientWidth*.2))},l=[a,e,s],n[0]=e,n[1]=s,n[2]=a,n[3]=c,n[4]=l):(c=n[3],l=n[4]),A.useEffect(c,l),o}function tt(a){const e=F.c(105),{duration:s,fps:n,time:o,playing:c,envelope:l,rows:i,cues:h,activeCue:r,snapped:f,drift:m}=a,d=Math.max(s,.1),g=et(o,d,c);let u;e[0]!==a||e[1]!==d?(u=T=>{const E=T.currentTarget.getBoundingClientRect();a.onSeek((T.clientX-E.left)/E.width*d)},e[0]=a,e[1]=d,e[2]=u):u=e[2];const j=u;let v;e[3]!==a?(v=()=>a.onStep(-1),e[3]=a,e[4]=v):v=e[4];let C;e[5]===Symbol.for("react.memo_cache_sentinel")?(C=t.jsx(Le,{size:16}),e[5]=C):C=e[5];let y;e[6]!==v?(y=t.jsx("button",{type:"button","aria-label":"Previous frame",onClick:v,children:C}),e[6]=v,e[7]=y):y=e[7];const w=c?"Pause":"Play";let $;e[8]!==c?($=c?t.jsx(ze,{size:16}):t.jsx(Fe,{size:16}),e[8]=c,e[9]=$):$=e[9];let b;e[10]!==a.onTogglePlay||e[11]!==w||e[12]!==$?(b=t.jsx("button",{type:"button",className:"alive-play","aria-label":w,onClick:a.onTogglePlay,children:$}),e[10]=a.onTogglePlay,e[11]=w,e[12]=$,e[13]=b):b=e[13];let N;e[14]!==a?(N=()=>a.onStep(1),e[14]=a,e[15]=N):N=e[15];let P;e[16]===Symbol.for("react.memo_cache_sentinel")?(P=t.jsx(He,{size:16}),e[16]=P):P=e[16];let x;e[17]!==N?(x=t.jsx("button",{type:"button","aria-label":"Next frame",onClick:N,children:P}),e[17]=N,e[18]=x):x=e[18];let k;e[19]!==o?(k=de(o),e[19]=o,e[20]=k):k=e[20];let M;e[21]!==k?(M=t.jsx("strong",{className:"alive-clock",children:k}),e[21]=k,e[22]=M):M=e[22];const D=Math.floor(o*n),O=Math.floor(d*n);let _;e[23]!==D||e[24]!==O?(_=t.jsxs("span",{className:"alive-muted",children:["frame ",D," / ",O]}),e[23]=D,e[24]=O,e[25]=_):_=e[25];let H;e[26]===Symbol.for("react.memo_cache_sentinel")?(H=t.jsx("span",{className:"alive-spacer"}),e[26]=H):H=e[26];const W=m.count>0&&!f||void 0;let R;e[27]!==m||e[28]!==i.length||e[29]!==f?(R=Qe(f,m,i.length),e[27]=m,e[28]=i.length,e[29]=f,e[30]=R):R=e[30];let p;e[31]!==W||e[32]!==R?(p=t.jsx("span",{className:"alive-drift","data-drifted":W,children:R}),e[31]=W,e[32]=R,e[33]=p):p=e[33];const L=f?"alive-secondary":"alive-primary alive-warn",I=m.count===0,z=f?"Use transcript timing":"Snap words to audio";let B;e[34]!==a.onToggleSnap||e[35]!==L||e[36]!==I||e[37]!==z?(B=t.jsx("button",{type:"button",className:L,disabled:I,onClick:a.onToggleSnap,children:z}),e[34]=a.onToggleSnap,e[35]=L,e[36]=I,e[37]=z,e[38]=B):B=e[38];let S;e[39]!==M||e[40]!==_||e[41]!==p||e[42]!==B||e[43]!==y||e[44]!==b||e[45]!==x?(S=t.jsxs("div",{className:"alive-transport",children:[y,b,x,M,_,H,p,B]}),e[39]=M,e[40]=_,e[41]=p,e[42]=B,e[43]=y,e[44]=b,e[45]=x,e[46]=S):S=e[46];let ne;e[47]===Symbol.for("react.memo_cache_sentinel")?(ne=t.jsxs("div",{className:"alive-lane-labels","aria-hidden":"true",children:[t.jsx("span",{className:"alive-lane-ruler"}),t.jsx("span",{className:"alive-lane-audio",children:"Audio"}),t.jsx("span",{className:"alive-lane-words",children:"Words"}),t.jsx("span",{className:"alive-lane-stacks",children:"Stacks"})]}),e[47]=ne):ne=e[47];const he=d*Je;let K;e[48]!==he?(K={minWidth:he},e[48]=he,e[49]=K):K=e[49];let V;e[50]!==d?(V=Ze(d).map(T=>t.jsxs("span",{style:{left:U(T,d)},children:[T,"s"]},T)),e[50]=d,e[51]=V):V=e[51];let G;e[52]!==V?(G=t.jsx("div",{className:"alive-lane-ruler",children:V}),e[52]=V,e[53]=G):G=e[53];let X;e[54]!==l?(X=Te(l),e[54]=l,e[55]=X):X=e[55];let q;e[56]!==X?(q=t.jsx("svg",{viewBox:"0 0 1000 100",preserveAspectRatio:"none","aria-hidden":"true",children:t.jsx("path",{d:X})}),e[56]=X,e[57]=q):q=e[57];let Y;e[58]!==i||e[59]!==d||e[60]!==f?(Y=!f&&i.filter(st).map(T=>t.jsx("span",{className:"alive-onset",style:{left:U(T.onset,d)}},T.key)),e[58]=i,e[59]=d,e[60]=f,e[61]=Y):Y=e[61];let J;e[62]!==q||e[63]!==Y?(J=t.jsxs("div",{className:"alive-lane-audio",children:[q,Y]}),e[62]=q,e[63]=Y,e[64]=J):J=e[64];let Q;if(e[65]!==i||e[66]!==d||e[67]!==f||e[68]!==o){let T;e[70]!==d||e[71]!==f||e[72]!==o?(T=E=>t.jsx("span",{className:"alive-chip","data-active":o>=E.start&&o<E.end||void 0,"data-drifted":E.drifted&&!f||void 0,style:{left:U(E.start,d),width:U(E.end-E.start,d)},children:E.text},E.key),e[70]=d,e[71]=f,e[72]=o,e[73]=T):T=e[73],Q=i.map(T),e[65]=i,e[66]=d,e[67]=f,e[68]=o,e[69]=Q}else Q=e[69];let Z;e[74]!==Q?(Z=t.jsx("div",{className:"alive-lane-words",children:Q}),e[74]=Q,e[75]=Z):Z=e[75];let ee;if(e[76]!==r||e[77]!==h||e[78]!==d){let T;e[80]!==r||e[81]!==d?(T=(E,ye)=>t.jsx("span",{className:"alive-stack-bar","data-active":ye===r||void 0,style:{left:U(E.start,d),width:U(E.end-E.start,d)},children:me(E)},E.id),e[80]=r,e[81]=d,e[82]=T):T=e[82],ee=h.map(T),e[76]=r,e[77]=h,e[78]=d,e[79]=ee}else ee=e[79];let te;e[83]!==ee?(te=t.jsx("div",{className:"alive-lane-stacks",children:ee}),e[83]=ee,e[84]=te):te=e[84];let se;e[85]!==d||e[86]!==o?(se=U(o,d),e[85]=d,e[86]=o,e[87]=se):se=e[87];let le;e[88]!==se?(le=t.jsx("span",{className:"alive-playhead",style:{left:se}}),e[88]=se,e[89]=le):le=e[89];let ae;e[90]!==j||e[91]!==K||e[92]!==G||e[93]!==J||e[94]!==Z||e[95]!==te||e[96]!==le?(ae=t.jsxs("div",{className:"alive-lane-tracks",style:K,onClick:j,role:"presentation",children:[G,J,Z,te,le]}),e[90]=j,e[91]=K,e[92]=G,e[93]=J,e[94]=Z,e[95]=te,e[96]=le,e[97]=ae):ae=e[97];let ie;e[98]!==g||e[99]!==ae?(ie=t.jsxs("div",{className:"alive-lanes",children:[ne,t.jsx("div",{className:"alive-lane-scroll",ref:g,children:ae})]}),e[98]=g,e[99]=ae,e[100]=ie):ie=e[100];let oe;e[101]===Symbol.for("react.memo_cache_sentinel")?(oe=t.jsxs("div",{className:"alive-legend","aria-hidden":"true",children:[t.jsxs("span",{children:[t.jsx("i",{className:"alive-legend-chip"}),"Transcript word"]}),t.jsxs("span",{children:[t.jsx("i",{className:"alive-legend-drift"}),"Word off its vocal onset"]}),t.jsxs("span",{children:[t.jsx("i",{className:"alive-legend-onset"}),"Detected vocal onset"]})]}),e[101]=oe):oe=e[101];let ce;return e[102]!==S||e[103]!==ie?(ce=t.jsxs("section",{className:"alive-panel alive-timeline","aria-label":"Caption timeline",children:[S,ie,oe]}),e[102]=S,e[103]=ie,e[104]=ce):ce=e[104],ce}function st(a){return a.drifted}const ge=30;function lt(a){const e=F.c(3),s=A.useRef(null);let n,o;return e[0]!==a?(n=()=>{const c=s.current;return c?.showModal(),c?.querySelector(".alive-close")?.focus(),()=>{c?.close(),We(a)}},o=[a],e[0]=a,e[1]=n,e[2]=o):(n=e[1],o=e[2]),A.useEffect(n,o),s}function at(a){const e=F.c(6),[s,n]=A.useState(0),[o,c]=A.useState(!1);let l,i;e[0]!==a?(l=()=>{const r=a.current;if(!r)return;const f=g=>n(g.detail.frame),m=()=>c(!0),d=()=>c(!1);return r.addEventListener("frameupdate",f),r.addEventListener("play",m),r.addEventListener("pause",d),()=>{r.removeEventListener("frameupdate",f),r.removeEventListener("play",m),r.removeEventListener("pause",d)}},i=[a],e[0]=a,e[1]=l,e[2]=i):(l=e[1],i=e[2]),A.useEffect(l,i);let h;return e[3]!==s||e[4]!==o?(h={frame:s,playing:o},e[3]=s,e[4]=o,e[5]=h):h=e[5],h}function ft(a){const e=F.c(14),{asset:s,state:n,returnFocus:o,onClose:c}=a,l=lt(o),i=`Alive captions for ${s.label}`;let h;e[0]!==c?(h=m=>{m.preventDefault(),c()},e[0]=c,e[1]=h):h=e[1];let r;e[2]!==s.label||e[3]!==c||e[4]!==a||e[5]!==n.data||e[6]!==n.message||e[7]!==n.status?(r=n.status==="ready"?t.jsx(it,{...a,data:n.data}):t.jsxs("div",{className:"alive-studio-message",children:[t.jsx(je,{label:s.label,detail:null,onClose:c}),t.jsx("p",{role:n.status==="error"?"alert":"status",children:n.status==="error"?n.message:"Analysing vocals, phrases and faces…"})]}),e[2]=s.label,e[3]=c,e[4]=a,e[5]=n.data,e[6]=n.message,e[7]=n.status,e[8]=r):r=e[8];let f;return e[9]!==l||e[10]!==i||e[11]!==h||e[12]!==r?(f=t.jsx("dialog",{className:"alive-studio","aria-label":i,onCancel:h,ref:l,children:r}),e[9]=l,e[10]=i,e[11]=h,e[12]=r,e[13]=f):f=e[13],f}function je(a){const e=F.c(12),{label:s,detail:n,onClose:o,children:c}=a;let l;e[0]===Symbol.for("react.memo_cache_sentinel")?(l=t.jsx("h2",{children:"Alive captions"}),e[0]=l):l=e[0];const i=n?` · ${n}`:"";let h;e[1]!==s||e[2]!==i?(h=t.jsxs("span",{className:"alive-muted",children:[s,i]}),e[1]=s,e[2]=i,e[3]=h):h=e[3];let r;e[4]===Symbol.for("react.memo_cache_sentinel")?(r=t.jsx("span",{className:"alive-spacer"}),e[4]=r):r=e[4];let f;e[5]===Symbol.for("react.memo_cache_sentinel")?(f=t.jsx(Ie,{size:16}),e[5]=f):f=e[5];let m;e[6]!==o?(m=t.jsx("button",{type:"button",className:"alive-close","aria-label":"Close alive captions",onClick:o,children:f}),e[6]=o,e[7]=m):m=e[7];let d;return e[8]!==c||e[9]!==h||e[10]!==m?(d=t.jsxs("header",{className:"alive-header",children:[l,h,r,c,m]}),e[8]=c,e[9]=h,e[10]=m,e[11]=d):d=e[11],d}function it({asset:a,data:e,onClose:s,onSave:n,onRender:o,onReanalyze:c}){const{analysis:l}=e,i=Math.round(a.fps??ge)||ge,h=Math.max(1,Math.round(l.durationSeconds*i)),[r,f]=A.useState(e.settings),[m,d]=A.useState(()=>Re(a.width,a.height)),[g,u]=A.useState(!0),[j,v]=A.useState(null),[C,y]=A.useState(null),w=A.useRef(null),{frame:$,playing:b}=at(w),N=$/i,P=A.useMemo(()=>Ae(l,m),[l,m]),x=A.useMemo(()=>Me(l.phrases,r,P),[l.phrases,r,P]),k=A.useMemo(()=>_e(l,r),[l,r]),M=A.useMemo(()=>Ee(k),[k]),D=A.useMemo(()=>({mediaUrl:a.mediaUrl,sourceWidth:l.sourceWidth,sourceHeight:l.sourceHeight,cues:x,phrases:l.phrases,settings:r,showRegions:g}),[a.mediaUrl,l,x,r,g]),O=Oe(x,N),_=p=>w.current?.seekTo(Math.max(0,Math.min(h-1,Math.round(p*i)))),H=p=>f(L=>({...L,...p})),W=async(p,L,I)=>{v(p),y(null);try{await L(),y(I)}catch(z){y(z instanceof Error?z.message:String(z))}finally{v(null)}},R=fe.find(p=>p.id===m)??fe[0];return t.jsxs("div",{className:"alive-studio-body",children:[t.jsxs(je,{label:a.label,detail:`${l.sourceWidth}×${l.sourceHeight} · ${i} fps`,onClose:s,children:[t.jsx("div",{className:"alive-segmented",role:"group","aria-label":"Preview orientation",children:fe.map(p=>t.jsx("button",{type:"button","aria-pressed":p.id===m,onClick:()=>d(p.id),children:p.label},p.id))}),t.jsx("button",{type:"button",className:"alive-secondary",disabled:j!==null,onClick:()=>{W("Saving",()=>n(r),"Saved. Renders of this video now use alive captions.")},children:"Save"}),t.jsxs("button",{type:"button",className:"alive-primary",disabled:j!==null,onClick:()=>{W("Rendering",()=>o(r,m),`Saved and queued a ${R.label} render.`)},children:["Render ",R.label]})]}),t.jsxs("div",{className:"alive-main",children:[t.jsx(qe,{rows:k,phraseLabels:x.map(me),activePhrase:O,time:N,snapped:r.snapToOnsets,onSeek:_}),t.jsxs("div",{className:"alive-stage",children:[t.jsx("div",{className:"alive-player",style:{aspectRatio:`${R.width} / ${R.height}`},children:t.jsx(Ne,{ref:w,component:Ke,inputProps:D,durationInFrames:h,fps:i,compositionWidth:R.width,compositionHeight:R.height,style:{width:"100%",height:"100%"},clickToPlay:!0,spaceKeyToPlayOrPause:!0})}),t.jsx("p",{className:"alive-status",role:"status",children:j?`${j}…`:C??(O>=0?`${me(x[O])} · ${x[O].lines.length} lines`:"Between phrases")})]}),t.jsx(Ye,{settings:r,showRegions:g,regionsAnalyzed:l.regionsAnalyzed,working:j!==null,onChange:H,onShowRegions:u,onReanalyze:()=>{W("Re-running analysis",c,"Analysis refreshed.")}})]}),t.jsx(tt,{duration:l.durationSeconds,fps:i,time:N,playing:b,envelope:l.envelope,rows:k,cues:x,activeCue:O,snapped:r.snapToOnsets,drift:M,onSeek:_,onStep:p=>_(N+p/i),onTogglePlay:()=>w.current?.toggle(),onToggleSnap:()=>H({snapToOnsets:!r.snapToOnsets})})]})}export{ft as AliveCaptionsStudio};
|
|
1
|
+
import{c as F,j as t,a as A}from"./vendor-react-Dj7D1fD4.js";import{u as be,a as pe,V as Se,A as xe,P as Ne}from"./vendor-remotion-CH1HXrlc.js";import{ec as Ce,ed as ke,ee as we,ef as $e,eg as Pe,eh as de,ei as Te,ej as me,ek as Re,el as Ae,em as Me,en as _e,eo as Ee,ep as Oe,eq as fe,bw as We}from"./videobook-DZ58pKW5.js";import{g as Le,a6 as ze,_ as Fe,h as He,X as Ie}from"./vendor-icons-CGqhCIXV.js";import"./vendor-yctIZ9Dz.js";import"./vendor-mcp-DQ7EusT9.js";const Be={face:"#63c7d8",text:"#f0a860",graphic:"#f0a860"};function De(a){const e=F.c(25),{phrase:s,cue:n,settings:o,sourceWidth:c,sourceHeight:l}=a,{width:i,height:h}=pe();let r,f,m,d;if(e[0]!==h||e[1]!==s.id||e[2]!==s.regions||e[3]!==o||e[4]!==l||e[5]!==c||e[6]!==i){const j={sourceWidth:c,sourceHeight:l,targetWidth:i,targetHeight:h};let v;e[11]!==s.regions||e[12]!==o?(v=new Set($e(s.regions,o)),e[11]=s.regions,e[12]=o,e[13]=v):v=e[13];const C=v;let y;e[14]!==i?(y=Math.round(i/480),e[14]=i,e[15]=y):y=e[15],f=Math.max(2,y),r=xe,e[16]===Symbol.for("react.memo_cache_sentinel")?(m={pointerEvents:"none"},e[16]=m):m=e[16],d=s.regions.map((w,$)=>{const b=Pe(w,j);return t.jsx("div",{style:{position:"absolute",left:b.x*i,top:b.y*h,width:b.width*i,height:b.height*h,border:`${f}px dashed ${Be[w.kind]}`,opacity:C.has(w)?1:.4,borderRadius:f*2}},`${s.id}-${$}`)}),e[0]=h,e[1]=s.id,e[2]=s.regions,e[3]=o,e[4]=l,e[5]=c,e[6]=i,e[7]=r,e[8]=f,e[9]=m,e[10]=d}else r=e[7],f=e[8],m=e[9],d=e[10];let g;e[17]!==n.column||e[18]!==f?(g=t.jsx(Ue,{column:n.column,stroke:f}),e[17]=n.column,e[18]=f,e[19]=g):g=e[19];let u;return e[20]!==r||e[21]!==m||e[22]!==d||e[23]!==g?(u=t.jsxs(r,{style:m,children:[d,g]}),e[20]=r,e[21]=m,e[22]=d,e[23]=g,e[24]=u):u=e[24],u}function Ue(a){const e=F.c(7),{column:s,stroke:n}=a,o=`${Math.max(1,n/2)}px dashed rgba(114, 231, 170, 0.6)`,c=n*2;let l;return e[0]!==s.height||e[1]!==s.width||e[2]!==s.x||e[3]!==s.y||e[4]!==o||e[5]!==c?(l=t.jsx("div",{style:{position:"absolute",left:s.x,top:s.y,width:s.width,height:s.height,border:o,borderRadius:c}}),e[0]=s.height,e[1]=s.width,e[2]=s.x,e[3]=s.y,e[4]=o,e[5]=c,e[6]=l):l=e[6],l}function Ke(a){const e=F.c(34),{mediaUrl:s,sourceWidth:n,sourceHeight:o,cues:c,phrases:l,settings:i,showRegions:h}=a,r=be(),{fps:f,width:m,height:d}=pe();let g;e[0]!==d||e[1]!==o||e[2]!==n||e[3]!==m?(g=ke({sourceWidth:n,sourceHeight:o,targetWidth:m,targetHeight:d}),e[0]=d,e[1]=o,e[2]=n,e[3]=m,e[4]=g):g=e[4];const u=g,j=r/f;let v;e[5]!==c||e[6]!==j?(v=Ce(c,j),e[5]=c,e[6]=j,e[7]=v):v=e[7];const C=v;let y;e[8]!==C||e[9]!==l?(y=C?l.find(M=>M.id===C.id):void 0,e[8]=C,e[9]=l,e[10]=y):y=e[10];const w=y;let $;e[11]===Symbol.for("react.memo_cache_sentinel")?($={background:"#000000"},e[11]=$):$=e[11];let b;e[12]!==u.height||e[13]!==u.width||e[14]!==u.x||e[15]!==u.y?(b={position:"absolute",left:u.x,top:u.y,width:u.width,height:u.height},e[12]=u.height,e[13]=u.width,e[14]=u.x,e[15]=u.y,e[16]=b):b=e[16];let N;e[17]!==s||e[18]!==b?(N=t.jsx(Se,{src:s,style:b}),e[17]=s,e[18]=b,e[19]=N):N=e[19];let P;e[20]!==C||e[21]!==w||e[22]!==i||e[23]!==h||e[24]!==o||e[25]!==n?(P=h&&C&&w&&t.jsx(De,{phrase:w,cue:C,settings:i,sourceWidth:n,sourceHeight:o}),e[20]=C,e[21]=w,e[22]=i,e[23]=h,e[24]=o,e[25]=n,e[26]=P):P=e[26];let p;e[27]!==c||e[28]!==i?(p=t.jsx(we,{cues:c,settings:i,strokeWidth:6,strokeColor:"#000000",shadowBlur:6,shadowOpacity:60}),e[27]=c,e[28]=i,e[29]=p):p=e[29];let k;return e[30]!==N||e[31]!==P||e[32]!==p?(k=t.jsxs(xe,{style:$,children:[N,P,p]}),e[30]=N,e[31]=P,e[32]=p,e[33]=k):k=e[33],k}const ue=[{id:"stack",label:"Stack",hint:"Words land one by one and build a stacked block beside the subject, each line sized to fill the column."},{id:"punch",label:"Punch",hint:"One word at a time, as big as the free space allows. Best on fast verses."},{id:"karaoke",label:"Karaoke",hint:"The whole stacked phrase shows dimmed and lights up word by word."}],Ve=[{id:"pop",label:"Pop"},{id:"slam",label:"Slam"},{id:"rise",label:"Rise"}],qe=[{name:"Green",color:"#72E7AA"},{name:"Pink",color:"#F0B9D0"},{name:"Cyan",color:"#63C7D8"},{name:"Amber",color:"#E7C372"}];function Ge(a,e){if(!a.drifted)return de(a.start);const s=a.driftMs>0?"+":"";return e?`${de(a.start)} · snapped`:`${de(a.start)} · ${s}${a.driftMs}ms`}function Xe(a){const e=F.c(21),{rows:s,phraseLabels:n,activePhrase:o,time:c,snapped:l,onSeek:i}=a;let h;e[0]===Symbol.for("react.memo_cache_sentinel")?(h=t.jsx("h3",{children:"Transcript"}),e[0]=h):h=e[0];let r;e[1]!==s.length?(r=t.jsxs("header",{className:"alive-panel-header",children:[h,t.jsxs("p",{children:[s.length," words · timed to the vocal"]})]}),e[1]=s.length,e[2]=r):r=e[2];let f;if(e[3]!==o||e[4]!==i||e[5]!==n||e[6]!==s||e[7]!==l||e[8]!==c){let g;e[10]!==o||e[11]!==i||e[12]!==s||e[13]!==l||e[14]!==c?(g=(u,j)=>t.jsxs("div",{className:"alive-phrase","data-active":j===o||void 0,children:[t.jsxs("div",{className:"alive-phrase-head",children:[t.jsxs("span",{children:["Phrase ",j+1]}),t.jsx("span",{children:u})]}),t.jsx("div",{className:"alive-phrase-words",children:s.filter(v=>v.phraseIndex===j).map(v=>t.jsxs("button",{type:"button",className:"alive-word","data-active":c>=v.start&&c<v.end||void 0,"data-drifted":v.drifted&&!l||void 0,onClick:()=>i(v.start+.03),children:[t.jsx("span",{children:v.text}),t.jsx("small",{children:Ge(v,l)})]},v.key))})]},`${j}-${u}`),e[10]=o,e[11]=i,e[12]=s,e[13]=l,e[14]=c,e[15]=g):g=e[15],f=n.map(g),e[3]=o,e[4]=i,e[5]=n,e[6]=s,e[7]=l,e[8]=c,e[9]=f}else f=e[9];let m;e[16]!==f?(m=t.jsx("div",{className:"alive-transcript-list",children:f}),e[16]=f,e[17]=m):m=e[17];let d;return e[18]!==r||e[19]!==m?(d=t.jsxs("section",{className:"alive-panel alive-transcript","aria-label":"Transcript",children:[r,m]}),e[18]=r,e[19]=m,e[20]=d):d=e[20],d}function ve(a){const e=F.c(10),{label:s,options:n,value:o,onChange:c}=a;let l;if(e[0]!==c||e[1]!==n||e[2]!==o){let h;e[4]!==c||e[5]!==o?(h=r=>t.jsx("button",{type:"button","aria-pressed":r.id===o,onClick:()=>c(r.id),children:r.label},r.id),e[4]=c,e[5]=o,e[6]=h):h=e[6],l=n.map(h),e[0]=c,e[1]=n,e[2]=o,e[3]=l}else l=e[3];let i;return e[7]!==s||e[8]!==l?(i=t.jsx("div",{className:"alive-segmented",role:"group","aria-label":s,children:l}),e[7]=s,e[8]=l,e[9]=i):i=e[9],i}function re(a){const e=F.c(8),{checked:s,onChange:n,children:o}=a;let c;e[0]!==n?(c=h=>n(h.target.checked),e[0]=n,e[1]=c):c=e[1];let l;e[2]!==s||e[3]!==c?(l=t.jsx("input",{type:"checkbox",checked:s,onChange:c}),e[2]=s,e[3]=c,e[4]=l):l=e[4];let i;return e[5]!==o||e[6]!==l?(i=t.jsxs("label",{className:"alive-toggle",children:[l,o]}),e[5]=o,e[6]=l,e[7]=i):i=e[7],i}function Ye(a){const e=F.c(69),{settings:s,showRegions:n,regionsAnalyzed:o,working:c,onChange:l,onShowRegions:i,onReanalyze:h}=a;let r;e[0]!==s.style?(r=ue.find(S=>S.id===s.style)?.hint,e[0]=s.style,e[1]=r):r=e[1];const f=r;let m;e[2]===Symbol.for("react.memo_cache_sentinel")?(m=t.jsx("h4",{children:"Style"}),e[2]=m):m=e[2];let d;e[3]!==l?(d=S=>l({style:S}),e[3]=l,e[4]=d):d=e[4];let g;e[5]!==s.style||e[6]!==d?(g=t.jsx(ve,{label:"Caption style",options:ue,value:s.style,onChange:d}),e[5]=s.style,e[6]=d,e[7]=g):g=e[7];let u;e[8]!==f?(u=t.jsx("p",{className:"alive-hint",children:f}),e[8]=f,e[9]=u):u=e[9];let j;e[10]!==g||e[11]!==u?(j=t.jsxs("div",{className:"alive-field",children:[m,g,u]}),e[10]=g,e[11]=u,e[12]=j):j=e[12];let v;e[13]===Symbol.for("react.memo_cache_sentinel")?(v=t.jsx("h4",{children:"Word entrance"}),e[13]=v):v=e[13];let C;e[14]!==l?(C=S=>l({motion:S}),e[14]=l,e[15]=C):C=e[15];let y;e[16]!==s.motion||e[17]!==C?(y=t.jsxs("div",{className:"alive-field",children:[v,t.jsx(ve,{label:"Word entrance",options:Ve,value:s.motion,onChange:C})]}),e[16]=s.motion,e[17]=C,e[18]=y):y=e[18];let w;e[19]===Symbol.for("react.memo_cache_sentinel")?(w=t.jsx("h4",{children:"Frame awareness"}),e[19]=w):w=e[19];let $;e[20]!==l?($=S=>l({avoidFaces:S}),e[20]=l,e[21]=$):$=e[21];let b;e[22]!==s.avoidFaces||e[23]!==$?(b=t.jsx(re,{checked:s.avoidFaces,onChange:$,children:"Keep words off faces"}),e[22]=s.avoidFaces,e[23]=$,e[24]=b):b=e[24];let N;e[25]!==l?(N=S=>l({avoidText:S}),e[25]=l,e[26]=N):N=e[26];let P;e[27]!==s.avoidText||e[28]!==N?(P=t.jsx(re,{checked:s.avoidText,onChange:N,children:"Keep words off on-screen text"}),e[27]=s.avoidText,e[28]=N,e[29]=P):P=e[29];let p;e[30]!==i||e[31]!==n?(p=t.jsx(re,{checked:n,onChange:i,children:"Show detected regions"}),e[30]=i,e[31]=n,e[32]=p):p=e[32];let k;e[33]!==o?(k=!o&&t.jsx("p",{className:"alive-hint alive-warning",children:"Face and text detection did not run, so stacks use the default columns. Set OPENAI_API_KEY and re-run the analysis."}),e[33]=o,e[34]=k):k=e[34];let M;e[35]!==b||e[36]!==P||e[37]!==p||e[38]!==k?(M=t.jsxs("div",{className:"alive-field",children:[w,b,P,p,k]}),e[35]=b,e[36]=P,e[37]=p,e[38]=k,e[39]=M):M=e[39];let D;e[40]===Symbol.for("react.memo_cache_sentinel")?(D=t.jsx("h4",{children:"Audio"}),e[40]=D):D=e[40];let O;e[41]!==l?(O=S=>l({energySizing:S}),e[41]=l,e[42]=O):O=e[42];let _;e[43]!==s.energySizing||e[44]!==O?(_=t.jsxs("div",{className:"alive-field",children:[D,t.jsx(re,{checked:s.energySizing,onChange:O,children:"Size words by vocal energy"})]}),e[43]=s.energySizing,e[44]=O,e[45]=_):_=e[45];let H;e[46]===Symbol.for("react.memo_cache_sentinel")?(H=t.jsx("h4",{children:"Hit color"}),e[46]=H):H=e[46];let W;e[47]!==l||e[48]!==s.hitColor?(W=qe.map(S=>t.jsx("button",{type:"button","aria-label":S.name,"aria-pressed":s.hitColor.toUpperCase()===S.color,onClick:()=>l({hitColor:S.color}),children:t.jsx("span",{style:{background:S.color}})},S.color)),e[47]=l,e[48]=s.hitColor,e[49]=W):W=e[49];let R;e[50]===Symbol.for("react.memo_cache_sentinel")?(R=t.jsx("span",{children:"Text"}),e[50]=R):R=e[50];let x;e[51]!==l?(x=S=>l({textColor:S.target.value.toUpperCase()}),e[51]=l,e[52]=x):x=e[52];let L;e[53]!==s.textColor||e[54]!==x?(L=t.jsxs("label",{className:"alive-color-input",children:[R,t.jsx("input",{type:"color",value:s.textColor,onChange:x})]}),e[53]=s.textColor,e[54]=x,e[55]=L):L=e[55];let I;e[56]!==W||e[57]!==L?(I=t.jsxs("div",{className:"alive-field",children:[H,t.jsxs("div",{className:"alive-swatches",children:[W,L]})]}),e[56]=W,e[57]=L,e[58]=I):I=e[58];let z;e[59]!==h||e[60]!==c?(z=t.jsx("div",{className:"alive-field",children:t.jsx("button",{type:"button",className:"alive-secondary",disabled:c,onClick:h,children:"Re-run analysis"})}),e[59]=h,e[60]=c,e[61]=z):z=e[61];let B;return e[62]!==M||e[63]!==_||e[64]!==I||e[65]!==z||e[66]!==j||e[67]!==y?(B=t.jsxs("section",{className:"alive-panel alive-settings","aria-label":"Caption settings",children:[j,y,M,_,I,z]}),e[62]=M,e[63]=_,e[64]=I,e[65]=z,e[66]=j,e[67]=y,e[68]=B):B=e[68],B}const Je=90;function U(a,e){return`${Math.max(0,a)/e*100}%`}function Qe(a,e,s){if(e.count===0)return`All ${s} words already sit on their vocal onsets`;if(a)return`${e.count} words snapped to their vocal onsets`;const n=e.minMs===e.maxMs?`${e.maxMs}`:`${e.minMs}–${e.maxMs}`;return`${e.count} words start ${n} ms off the vocal`}function Ze(a){const e=a>120?10:a>30?5:1,s=[];for(let n=0;n<=a;n+=e)s.push(n);return s}function et(a,e,s){const n=F.c(5),o=A.useRef(null);let c,l;return n[0]!==e||n[1]!==s||n[2]!==a?(c=()=>{const i=o.current;if(!i||!s)return;const h=a/e*i.scrollWidth;(h<i.scrollLeft||h>i.scrollLeft+i.clientWidth*.85)&&(i.scrollLeft=Math.max(0,h-i.clientWidth*.2))},l=[a,e,s],n[0]=e,n[1]=s,n[2]=a,n[3]=c,n[4]=l):(c=n[3],l=n[4]),A.useEffect(c,l),o}function tt(a){const e=F.c(105),{duration:s,fps:n,time:o,playing:c,envelope:l,rows:i,cues:h,activeCue:r,snapped:f,drift:m}=a,d=Math.max(s,.1),g=et(o,d,c);let u;e[0]!==a||e[1]!==d?(u=T=>{const E=T.currentTarget.getBoundingClientRect();a.onSeek((T.clientX-E.left)/E.width*d)},e[0]=a,e[1]=d,e[2]=u):u=e[2];const j=u;let v;e[3]!==a?(v=()=>a.onStep(-1),e[3]=a,e[4]=v):v=e[4];let C;e[5]===Symbol.for("react.memo_cache_sentinel")?(C=t.jsx(Le,{size:16}),e[5]=C):C=e[5];let y;e[6]!==v?(y=t.jsx("button",{type:"button","aria-label":"Previous frame",onClick:v,children:C}),e[6]=v,e[7]=y):y=e[7];const w=c?"Pause":"Play";let $;e[8]!==c?($=c?t.jsx(ze,{size:16}):t.jsx(Fe,{size:16}),e[8]=c,e[9]=$):$=e[9];let b;e[10]!==a.onTogglePlay||e[11]!==w||e[12]!==$?(b=t.jsx("button",{type:"button",className:"alive-play","aria-label":w,onClick:a.onTogglePlay,children:$}),e[10]=a.onTogglePlay,e[11]=w,e[12]=$,e[13]=b):b=e[13];let N;e[14]!==a?(N=()=>a.onStep(1),e[14]=a,e[15]=N):N=e[15];let P;e[16]===Symbol.for("react.memo_cache_sentinel")?(P=t.jsx(He,{size:16}),e[16]=P):P=e[16];let p;e[17]!==N?(p=t.jsx("button",{type:"button","aria-label":"Next frame",onClick:N,children:P}),e[17]=N,e[18]=p):p=e[18];let k;e[19]!==o?(k=de(o),e[19]=o,e[20]=k):k=e[20];let M;e[21]!==k?(M=t.jsx("strong",{className:"alive-clock",children:k}),e[21]=k,e[22]=M):M=e[22];const D=Math.floor(o*n),O=Math.floor(d*n);let _;e[23]!==D||e[24]!==O?(_=t.jsxs("span",{className:"alive-muted",children:["frame ",D," / ",O]}),e[23]=D,e[24]=O,e[25]=_):_=e[25];let H;e[26]===Symbol.for("react.memo_cache_sentinel")?(H=t.jsx("span",{className:"alive-spacer"}),e[26]=H):H=e[26];const W=m.count>0&&!f||void 0;let R;e[27]!==m||e[28]!==i.length||e[29]!==f?(R=Qe(f,m,i.length),e[27]=m,e[28]=i.length,e[29]=f,e[30]=R):R=e[30];let x;e[31]!==W||e[32]!==R?(x=t.jsx("span",{className:"alive-drift","data-drifted":W,children:R}),e[31]=W,e[32]=R,e[33]=x):x=e[33];const L=f?"alive-secondary":"alive-primary alive-warn",I=m.count===0,z=f?"Use transcript timing":"Snap words to audio";let B;e[34]!==a.onToggleSnap||e[35]!==L||e[36]!==I||e[37]!==z?(B=t.jsx("button",{type:"button",className:L,disabled:I,onClick:a.onToggleSnap,children:z}),e[34]=a.onToggleSnap,e[35]=L,e[36]=I,e[37]=z,e[38]=B):B=e[38];let S;e[39]!==M||e[40]!==_||e[41]!==x||e[42]!==B||e[43]!==y||e[44]!==b||e[45]!==p?(S=t.jsxs("div",{className:"alive-transport",children:[y,b,p,M,_,H,x,B]}),e[39]=M,e[40]=_,e[41]=x,e[42]=B,e[43]=y,e[44]=b,e[45]=p,e[46]=S):S=e[46];let ne;e[47]===Symbol.for("react.memo_cache_sentinel")?(ne=t.jsxs("div",{className:"alive-lane-labels","aria-hidden":"true",children:[t.jsx("span",{className:"alive-lane-ruler"}),t.jsx("span",{className:"alive-lane-audio",children:"Audio"}),t.jsx("span",{className:"alive-lane-words",children:"Words"}),t.jsx("span",{className:"alive-lane-stacks",children:"Stacks"})]}),e[47]=ne):ne=e[47];const he=d*Je;let K;e[48]!==he?(K={minWidth:he},e[48]=he,e[49]=K):K=e[49];let V;e[50]!==d?(V=Ze(d).map(T=>t.jsxs("span",{style:{left:U(T,d)},children:[T,"s"]},T)),e[50]=d,e[51]=V):V=e[51];let q;e[52]!==V?(q=t.jsx("div",{className:"alive-lane-ruler",children:V}),e[52]=V,e[53]=q):q=e[53];let G;e[54]!==l?(G=Te(l),e[54]=l,e[55]=G):G=e[55];let X;e[56]!==G?(X=t.jsx("svg",{viewBox:"0 0 1000 100",preserveAspectRatio:"none","aria-hidden":"true",children:t.jsx("path",{d:G})}),e[56]=G,e[57]=X):X=e[57];let Y;e[58]!==i||e[59]!==d||e[60]!==f?(Y=!f&&i.filter(st).map(T=>t.jsx("span",{className:"alive-onset",style:{left:U(T.onset,d)}},T.key)),e[58]=i,e[59]=d,e[60]=f,e[61]=Y):Y=e[61];let J;e[62]!==X||e[63]!==Y?(J=t.jsxs("div",{className:"alive-lane-audio",children:[X,Y]}),e[62]=X,e[63]=Y,e[64]=J):J=e[64];let Q;if(e[65]!==i||e[66]!==d||e[67]!==f||e[68]!==o){let T;e[70]!==d||e[71]!==f||e[72]!==o?(T=E=>t.jsx("span",{className:"alive-chip","data-active":o>=E.start&&o<E.end||void 0,"data-drifted":E.drifted&&!f||void 0,style:{left:U(E.start,d),width:U(E.end-E.start,d)},children:E.text},E.key),e[70]=d,e[71]=f,e[72]=o,e[73]=T):T=e[73],Q=i.map(T),e[65]=i,e[66]=d,e[67]=f,e[68]=o,e[69]=Q}else Q=e[69];let Z;e[74]!==Q?(Z=t.jsx("div",{className:"alive-lane-words",children:Q}),e[74]=Q,e[75]=Z):Z=e[75];let ee;if(e[76]!==r||e[77]!==h||e[78]!==d){let T;e[80]!==r||e[81]!==d?(T=(E,ye)=>t.jsx("span",{className:"alive-stack-bar","data-active":ye===r||void 0,style:{left:U(E.start,d),width:U(E.end-E.start,d)},children:me(E)},E.id),e[80]=r,e[81]=d,e[82]=T):T=e[82],ee=h.map(T),e[76]=r,e[77]=h,e[78]=d,e[79]=ee}else ee=e[79];let te;e[83]!==ee?(te=t.jsx("div",{className:"alive-lane-stacks",children:ee}),e[83]=ee,e[84]=te):te=e[84];let se;e[85]!==d||e[86]!==o?(se=U(o,d),e[85]=d,e[86]=o,e[87]=se):se=e[87];let le;e[88]!==se?(le=t.jsx("span",{className:"alive-playhead",style:{left:se}}),e[88]=se,e[89]=le):le=e[89];let ae;e[90]!==j||e[91]!==K||e[92]!==q||e[93]!==J||e[94]!==Z||e[95]!==te||e[96]!==le?(ae=t.jsxs("div",{className:"alive-lane-tracks",style:K,onClick:j,role:"presentation",children:[q,J,Z,te,le]}),e[90]=j,e[91]=K,e[92]=q,e[93]=J,e[94]=Z,e[95]=te,e[96]=le,e[97]=ae):ae=e[97];let ie;e[98]!==g||e[99]!==ae?(ie=t.jsxs("div",{className:"alive-lanes",children:[ne,t.jsx("div",{className:"alive-lane-scroll",ref:g,children:ae})]}),e[98]=g,e[99]=ae,e[100]=ie):ie=e[100];let oe;e[101]===Symbol.for("react.memo_cache_sentinel")?(oe=t.jsxs("div",{className:"alive-legend","aria-hidden":"true",children:[t.jsxs("span",{children:[t.jsx("i",{className:"alive-legend-chip"}),"Transcript word"]}),t.jsxs("span",{children:[t.jsx("i",{className:"alive-legend-drift"}),"Word off its vocal onset"]}),t.jsxs("span",{children:[t.jsx("i",{className:"alive-legend-onset"}),"Detected vocal onset"]})]}),e[101]=oe):oe=e[101];let ce;return e[102]!==S||e[103]!==ie?(ce=t.jsxs("section",{className:"alive-panel alive-timeline","aria-label":"Caption timeline",children:[S,ie,oe]}),e[102]=S,e[103]=ie,e[104]=ce):ce=e[104],ce}function st(a){return a.drifted}const ge=30;function lt(a){const e=F.c(3),s=A.useRef(null);let n,o;return e[0]!==a?(n=()=>{const c=s.current;return c?.showModal(),c?.querySelector(".alive-close")?.focus(),()=>{c?.close(),We(a)}},o=[a],e[0]=a,e[1]=n,e[2]=o):(n=e[1],o=e[2]),A.useEffect(n,o),s}function at(a){const e=F.c(6),[s,n]=A.useState(0),[o,c]=A.useState(!1);let l,i;e[0]!==a?(l=()=>{const r=a.current;if(!r)return;const f=g=>n(g.detail.frame),m=()=>c(!0),d=()=>c(!1);return r.addEventListener("frameupdate",f),r.addEventListener("play",m),r.addEventListener("pause",d),()=>{r.removeEventListener("frameupdate",f),r.removeEventListener("play",m),r.removeEventListener("pause",d)}},i=[a],e[0]=a,e[1]=l,e[2]=i):(l=e[1],i=e[2]),A.useEffect(l,i);let h;return e[3]!==s||e[4]!==o?(h={frame:s,playing:o},e[3]=s,e[4]=o,e[5]=h):h=e[5],h}function ft(a){const e=F.c(14),{asset:s,state:n,returnFocus:o,onClose:c}=a,l=lt(o),i=`Alive captions for ${s.label}`;let h;e[0]!==c?(h=m=>{m.preventDefault(),c()},e[0]=c,e[1]=h):h=e[1];let r;e[2]!==s.label||e[3]!==c||e[4]!==a||e[5]!==n.data||e[6]!==n.message||e[7]!==n.status?(r=n.status==="ready"?t.jsx(it,{...a,data:n.data}):t.jsxs("div",{className:"alive-studio-message",children:[t.jsx(je,{label:s.label,detail:null,onClose:c}),t.jsx("p",{role:n.status==="error"?"alert":"status",children:n.status==="error"?n.message:"Analysing vocals, phrases and faces…"})]}),e[2]=s.label,e[3]=c,e[4]=a,e[5]=n.data,e[6]=n.message,e[7]=n.status,e[8]=r):r=e[8];let f;return e[9]!==l||e[10]!==i||e[11]!==h||e[12]!==r?(f=t.jsx("dialog",{className:"alive-studio","aria-label":i,onCancel:h,ref:l,children:r}),e[9]=l,e[10]=i,e[11]=h,e[12]=r,e[13]=f):f=e[13],f}function je(a){const e=F.c(12),{label:s,detail:n,onClose:o,children:c}=a;let l;e[0]===Symbol.for("react.memo_cache_sentinel")?(l=t.jsx("h2",{children:"Alive captions"}),e[0]=l):l=e[0];const i=n?` · ${n}`:"";let h;e[1]!==s||e[2]!==i?(h=t.jsxs("span",{className:"alive-muted",children:[s,i]}),e[1]=s,e[2]=i,e[3]=h):h=e[3];let r;e[4]===Symbol.for("react.memo_cache_sentinel")?(r=t.jsx("span",{className:"alive-spacer"}),e[4]=r):r=e[4];let f;e[5]===Symbol.for("react.memo_cache_sentinel")?(f=t.jsx(Ie,{size:16}),e[5]=f):f=e[5];let m;e[6]!==o?(m=t.jsx("button",{type:"button",className:"alive-close","aria-label":"Close alive captions",onClick:o,children:f}),e[6]=o,e[7]=m):m=e[7];let d;return e[8]!==c||e[9]!==h||e[10]!==m?(d=t.jsxs("header",{className:"alive-header",children:[l,h,r,c,m]}),e[8]=c,e[9]=h,e[10]=m,e[11]=d):d=e[11],d}function it({asset:a,data:e,onClose:s,onSave:n,onRender:o,onReanalyze:c}){const{analysis:l}=e,i=Math.round(a.fps??ge)||ge,h=Math.max(1,Math.round(l.durationSeconds*i)),[r,f]=A.useState(e.settings),[m,d]=A.useState(()=>Re(a.width,a.height)),[g,u]=A.useState(!0),[j,v]=A.useState(null),[C,y]=A.useState(null),w=A.useRef(null),{frame:$,playing:b}=at(w),N=$/i,P=A.useMemo(()=>Ae(l,m),[l,m]),p=A.useMemo(()=>Me(l.phrases,r,P),[l.phrases,r,P]),k=A.useMemo(()=>_e(l,r),[l,r]),M=A.useMemo(()=>Ee(k),[k]),D=A.useMemo(()=>({mediaUrl:a.mediaUrl,sourceWidth:l.sourceWidth,sourceHeight:l.sourceHeight,cues:p,phrases:l.phrases,settings:r,showRegions:g}),[a.mediaUrl,l,p,r,g]),O=Oe(p,N),_=x=>w.current?.seekTo(Math.max(0,Math.min(h-1,Math.round(x*i)))),H=x=>f(L=>({...L,...x})),W=async(x,L,I)=>{v(x),y(null);try{await L(),y(I)}catch(z){y(z instanceof Error?z.message:String(z))}finally{v(null)}},R=fe.find(x=>x.id===m)??fe[0];return t.jsxs("div",{className:"alive-studio-body",children:[t.jsxs(je,{label:a.label,detail:`${l.sourceWidth}×${l.sourceHeight} · ${i} fps`,onClose:s,children:[t.jsx("div",{className:"alive-segmented",role:"group","aria-label":"Preview orientation",children:fe.map(x=>t.jsx("button",{type:"button","aria-pressed":x.id===m,onClick:()=>d(x.id),children:x.label},x.id))}),t.jsx("button",{type:"button",className:"alive-secondary",disabled:j!==null,onClick:()=>{W("Saving",()=>n(r),"Saved. Renders of this video now use alive captions.")},children:"Save"}),t.jsxs("button",{type:"button",className:"alive-primary",disabled:j!==null,onClick:()=>{W("Rendering",()=>o(r,m),`Saved and queued a ${R.label} render.`)},children:["Render ",R.label]})]}),t.jsxs("div",{className:"alive-main",children:[t.jsx(Xe,{rows:k,phraseLabels:p.map(me),activePhrase:O,time:N,snapped:r.snapToOnsets,onSeek:_}),t.jsxs("div",{className:"alive-stage",children:[t.jsx("div",{className:"alive-player",style:{aspectRatio:`${R.width} / ${R.height}`},children:t.jsx(Ne,{ref:w,component:Ke,inputProps:D,durationInFrames:h,fps:i,compositionWidth:R.width,compositionHeight:R.height,style:{width:"100%",height:"100%"},clickToPlay:!0,spaceKeyToPlayOrPause:!0})}),t.jsx("p",{className:"alive-status",role:"status",children:j?`${j}…`:C??(O>=0?`${me(p[O])} · ${p[O].lines.length} lines`:"Between phrases")})]}),t.jsx(Ye,{settings:r,showRegions:g,regionsAnalyzed:l.regionsAnalyzed,working:j!==null,onChange:H,onShowRegions:u,onReanalyze:()=>{W("Re-running analysis",c,"Analysis refreshed.")}})]}),t.jsx(tt,{duration:l.durationSeconds,fps:i,time:N,playing:b,envelope:l.envelope,rows:k,cues:p,activeCue:O,snapped:r.snapToOnsets,drift:M,onSeek:_,onStep:x=>_(N+x/i),onTogglePlay:()=>w.current?.toggle(),onToggleSnap:()=>H({snapToOnsets:!r.snapToOnsets})})]})}export{ft as AliveCaptionsStudio};
|