@koda-sl/baker-cli 0.185.2 → 0.186.0
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/README.md +27 -6
- package/dist/{chunk-3TXZKSAR.js → chunk-OWDTSPY4.js} +133 -9
- package/dist/chunk-OWDTSPY4.js.map +1 -0
- package/dist/cli.js +130 -25
- package/dist/cli.js.map +1 -1
- package/dist/engine/index.js +1 -1
- package/package.json +1 -1
- package/dist/chunk-3TXZKSAR.js.map +0 -1
package/dist/cli.js
CHANGED
|
@@ -29,6 +29,7 @@ import {
|
|
|
29
29
|
isPersistedAssetRef,
|
|
30
30
|
looksLikeHttpUrl,
|
|
31
31
|
maxInputReferences,
|
|
32
|
+
maxInputSlot,
|
|
32
33
|
nearestClipDuration,
|
|
33
34
|
nearestSupportedAspectRatio,
|
|
34
35
|
nearestSupportedImageSize,
|
|
@@ -46,7 +47,7 @@ import {
|
|
|
46
47
|
toModelSafeImage,
|
|
47
48
|
ulid,
|
|
48
49
|
validateCanvasDeep
|
|
49
|
-
} from "./chunk-
|
|
50
|
+
} from "./chunk-OWDTSPY4.js";
|
|
50
51
|
import {
|
|
51
52
|
csvOrJson,
|
|
52
53
|
daysAgoIso,
|
|
@@ -3487,9 +3488,10 @@ var imagesIngestResponseSchema = z11.object({
|
|
|
3487
3488
|
import { z as z12 } from "zod";
|
|
3488
3489
|
var studioGenerationStatusSchema = z12.enum(["running", "completed", "partial", "failed"]);
|
|
3489
3490
|
var studioVariantStatusSchema = z12.enum(["pending", "ready", "failed"]);
|
|
3490
|
-
var studioClipModeSchema = z12.enum(["first_frame", "references"]);
|
|
3491
|
+
var studioClipModeSchema = z12.enum(["first_frame", "references", "text"]);
|
|
3491
3492
|
var videoGenerateModelSchema = z12.enum([
|
|
3492
3493
|
"bytedance/seedance-2.0",
|
|
3494
|
+
"bytedance/seedance-2.5",
|
|
3493
3495
|
"google/gemini-omni-flash",
|
|
3494
3496
|
"google/veo-3.1",
|
|
3495
3497
|
"google/veo-3.1-fast",
|
|
@@ -3504,7 +3506,9 @@ var MAX_STUDIO_IMAGES = 8;
|
|
|
3504
3506
|
var MAX_STUDIO_CLIPS = 3;
|
|
3505
3507
|
var MAX_STUDIO_PROMPT_CHARS = 32e3;
|
|
3506
3508
|
var MAX_STUDIO_IMAGE_REFERENCES = 16;
|
|
3507
|
-
var MAX_STUDIO_CLIP_IMAGES =
|
|
3509
|
+
var MAX_STUDIO_CLIP_IMAGES = 30;
|
|
3510
|
+
var MAX_STUDIO_CLIP_REFS = 10;
|
|
3511
|
+
var MAX_STUDIO_CLIP_SECONDS = 30;
|
|
3508
3512
|
var aspectRatioSchema = z12.string().regex(/^[1-9]\d*:[1-9]\d*$/, "Write the shape as width:height, like 1:1, 4:5 or 16:9");
|
|
3509
3513
|
var seedSchema = z12.coerce.number().int().min(0).max(4294967295);
|
|
3510
3514
|
var studioGenerateRequestSchema = z12.object({
|
|
@@ -3537,15 +3541,39 @@ var studioGenerateRequestSchema = z12.object({
|
|
|
3537
3541
|
backgroundRgbColor: rgbTriple.optional()
|
|
3538
3542
|
});
|
|
3539
3543
|
var studioAnimateRequestSchema = z12.object({
|
|
3540
|
-
/**
|
|
3544
|
+
/**
|
|
3545
|
+
* What MOVES, when a still fixes the look. With no still it is the whole shot,
|
|
3546
|
+
* because the clip is then generated from the prompt alone.
|
|
3547
|
+
*/
|
|
3541
3548
|
prompt: z12.string().min(1).max(MAX_STUDIO_PROMPT_CHARS),
|
|
3542
|
-
|
|
3549
|
+
/**
|
|
3550
|
+
* The still(s) the clip is built from. MAY BE EMPTY — every curated video model
|
|
3551
|
+
* also does text-to-video, so a prompt on its own is a complete request. The
|
|
3552
|
+
* CLI still requires one unless the caller asks for text-to-video explicitly,
|
|
3553
|
+
* because "I forgot the image" and "I meant no image" look identical here.
|
|
3554
|
+
*/
|
|
3555
|
+
imageIds: z12.array(z12.string()).max(MAX_STUDIO_CLIP_IMAGES),
|
|
3556
|
+
/**
|
|
3557
|
+
* Clips this render builds on — extend, edit, or carry the motion of. Addressed
|
|
3558
|
+
* by the batch and slot they were rendered in, because a studio take is a plain
|
|
3559
|
+
* public file the provider can fetch while a library video is a Mux asset that
|
|
3560
|
+
* is not. Seedance 2.5 only; the backend refuses them on any other model.
|
|
3561
|
+
*/
|
|
3562
|
+
clipRefs: z12.array(z12.object({ generationId: z12.string(), slot: z12.coerce.number().int().min(0) })).max(MAX_STUDIO_CLIP_REFS).optional(),
|
|
3563
|
+
/**
|
|
3564
|
+
* The same thing from the VIDEO LIBRARY — footage the company uploaded or
|
|
3565
|
+
* synced rather than rendered here. Ids, not URLs: a library video is a Mux
|
|
3566
|
+
* asset with no downloadable file until one is encoded for it, which the
|
|
3567
|
+
* backend does on first use.
|
|
3568
|
+
*/
|
|
3569
|
+
videoIds: z12.array(z12.string()).max(MAX_STUDIO_CLIP_REFS).optional(),
|
|
3543
3570
|
clipMode: studioClipModeSchema.optional(),
|
|
3544
3571
|
model: videoGenerateModelSchema.optional(),
|
|
3545
3572
|
aspectRatio: aspectRatioSchema.optional(),
|
|
3546
|
-
//
|
|
3547
|
-
// one actually offers is a per-model enum settled
|
|
3548
|
-
|
|
3573
|
+
// 30s is the longest clip any curated model renders (Seedance 2.5); which
|
|
3574
|
+
// lengths the chosen one actually offers is a per-model enum settled
|
|
3575
|
+
// server-side, and most of the roster stops well short of this.
|
|
3576
|
+
duration: z12.coerce.number().int().min(1).max(MAX_STUDIO_CLIP_SECONDS).optional(),
|
|
3549
3577
|
resolution: z12.string().optional(),
|
|
3550
3578
|
count: z12.coerce.number().int().min(1).max(MAX_STUDIO_CLIPS).optional(),
|
|
3551
3579
|
generateAudio: z12.boolean().optional(),
|
|
@@ -3554,7 +3582,9 @@ var studioAnimateRequestSchema = z12.object({
|
|
|
3554
3582
|
personGeneration: z12.enum(["allow_all", "allow_adult"]).optional(),
|
|
3555
3583
|
enhancePrompt: z12.boolean().optional(),
|
|
3556
3584
|
conditioningScale: z12.coerce.number().optional(),
|
|
3557
|
-
cfgScale: z12.coerce.number().min(0).max(1).optional()
|
|
3585
|
+
cfgScale: z12.coerce.number().min(0).max(1).optional(),
|
|
3586
|
+
/** Seedance 2.5 only. Left unset the lane sends `false` — see `resolveClipRequest`. */
|
|
3587
|
+
watermark: z12.boolean().optional()
|
|
3558
3588
|
});
|
|
3559
3589
|
var studioVariantSchema = z12.object({
|
|
3560
3590
|
slot: z12.number(),
|
|
@@ -3592,7 +3622,8 @@ var studioClipSettingsSchema = z12.object({
|
|
|
3592
3622
|
personGeneration: z12.string().optional(),
|
|
3593
3623
|
enhancePrompt: z12.boolean().optional(),
|
|
3594
3624
|
conditioningScale: z12.number().optional(),
|
|
3595
|
-
cfgScale: z12.number().optional()
|
|
3625
|
+
cfgScale: z12.number().optional(),
|
|
3626
|
+
watermark: z12.boolean().optional()
|
|
3596
3627
|
});
|
|
3597
3628
|
var studioGenerationSchema = z12.object({
|
|
3598
3629
|
generationId: z12.string(),
|
|
@@ -34862,23 +34893,33 @@ var MODEL_LIST = VIDEO_MODEL_IDS;
|
|
|
34862
34893
|
var DEFAULT_MAX_WAIT_MS = 6e5;
|
|
34863
34894
|
registerSchema({
|
|
34864
34895
|
command: "studio.animate",
|
|
34865
|
-
description: "
|
|
34896
|
+
description: "Render a clip. With --image the look is already fixed, so the prompt describes MOVEMENT \u2014 camera, subject, timing; with --from text there is no image and the prompt is the whole shot. --image takes library image ids, local file paths and/or image URLs (paths and URLs are added to the media library first). A rendered clip is NOT in the video library until `baker studio keep` approves it.",
|
|
34866
34897
|
args: {
|
|
34867
34898
|
prompt: {
|
|
34868
34899
|
type: "string",
|
|
34869
|
-
description: "What MOVES \u2014 camera, subject, timing. Kling reads 2,500 characters and Seedance 4,000; over the cap the request is refused rather than trimmed.",
|
|
34900
|
+
description: "What MOVES \u2014 camera, subject, timing. Kling reads 2,500 characters and Seedance 2.0 4,000; over the cap the request is refused rather than trimmed. Seedance 2.5 publishes no cap, which is why it is the one to reach for when the brief is a whole multi-shot sequence.",
|
|
34870
34901
|
required: true
|
|
34871
34902
|
},
|
|
34872
34903
|
image: {
|
|
34873
34904
|
type: "string",
|
|
34874
|
-
description: "Comma-separated source images: library image ids, local file paths, and/or image URLs. With the default --from frame this must be exactly ONE image (the opening frame) \u2014 several are refused rather than silently reduced to the first. With --from references, up to 9 on Seedance.",
|
|
34875
|
-
required:
|
|
34905
|
+
description: "Comma-separated source images: library image ids, local file paths, and/or image URLs. Required unless --from text. With the default --from frame this must be exactly ONE image (the opening frame) \u2014 several are refused rather than silently reduced to the first. With --from references, up to 9 on Seedance 2.0 and 30 on Seedance 2.5.",
|
|
34906
|
+
required: false
|
|
34876
34907
|
},
|
|
34877
34908
|
from: {
|
|
34878
34909
|
type: "string",
|
|
34879
|
-
description: "frame (default) animates a single image as the opening frame; references uses several
|
|
34910
|
+
description: "What the clip is built FROM. frame (default) animates a single image as the opening frame; references uses several assets as visual guidance; text renders the prompt with no image at all. frame and references are mutually exclusive at every provider, and reference mode is Seedance/Gemini Omni only \u2014 Veo and Kling are frame-only. Every model does text.",
|
|
34880
34911
|
required: false,
|
|
34881
|
-
enum: ["frame", "references"]
|
|
34912
|
+
enum: ["frame", "references", "text"]
|
|
34913
|
+
},
|
|
34914
|
+
"from-clip": {
|
|
34915
|
+
type: "string",
|
|
34916
|
+
description: "Comma-separated Studio takes to BUILD ON, each written batchId:slot (e.g. j57abc\u2026:0) \u2014 this is how you extend a clip, edit one, or carry its motion into a new shot. Seedance 2.5 only, up to 10 source clips in total, and requires --from references.",
|
|
34917
|
+
required: false
|
|
34918
|
+
},
|
|
34919
|
+
"from-video": {
|
|
34920
|
+
type: "string",
|
|
34921
|
+
description: "The same, but from the VIDEO LIBRARY: comma-separated video ids (see `baker videos library`) to build on. Use this for footage the client uploaded or synced rather than something the Studio rendered. Counts against the same 10 source clips as --from-clip. The first use of a given video spends a minute encoding a reusable copy and is refused with a 'try again shortly' message; every use after that is instant.",
|
|
34922
|
+
required: false
|
|
34882
34923
|
},
|
|
34883
34924
|
model: {
|
|
34884
34925
|
type: "string",
|
|
@@ -34896,7 +34937,7 @@ registerSchema({
|
|
|
34896
34937
|
// Left unset this SNAPS onto the model's set rather than failing: the lane
|
|
34897
34938
|
// default is 5s and Veo renders only 4/6/8, so an unstated length becomes 4
|
|
34898
34939
|
// there. An explicit length the model cannot render is refused by name.
|
|
34899
|
-
description: `Clip length in seconds \u2014 per-model. Unset it and the lane default (5s) snaps onto what the model renders. Across the roster: ${CLIP_DURATIONS.join(", ")}`,
|
|
34940
|
+
description: `Clip length in seconds \u2014 per-model. Unset it and the lane default (5s) snaps onto what the model renders. Past 15s only bytedance/seedance-2.5 will do it, out to 30. Across the roster: ${CLIP_DURATIONS.join(", ")}`,
|
|
34900
34941
|
required: false
|
|
34901
34942
|
},
|
|
34902
34943
|
quality: {
|
|
@@ -34949,6 +34990,11 @@ registerSchema({
|
|
|
34949
34990
|
description: "How closely the clip follows the prompt, 0-1 (Kling only; higher = closer to the prompt)",
|
|
34950
34991
|
required: false
|
|
34951
34992
|
},
|
|
34993
|
+
watermark: {
|
|
34994
|
+
type: "boolean",
|
|
34995
|
+
description: "Burn the provider's watermark into the picture. Seedance 2.5 only, and OFF by default because it cannot be removed after the render \u2014 pass it only if a client actually wants the mark.",
|
|
34996
|
+
required: false
|
|
34997
|
+
},
|
|
34952
34998
|
wait: {
|
|
34953
34999
|
type: "string",
|
|
34954
35000
|
description: "Pass false to get the batch id back immediately instead of waiting for the takes",
|
|
@@ -34975,8 +35021,31 @@ function clipKnobs(args) {
|
|
|
34975
35021
|
if (typeof args["enhance-prompt"] === "boolean") knobs.enhancePrompt = args["enhance-prompt"];
|
|
34976
35022
|
knobs.conditioningScale = num2(args["conditioning-scale"]);
|
|
34977
35023
|
knobs.cfgScale = num2(args["cfg-scale"]);
|
|
35024
|
+
if (args.watermark === true) knobs.watermark = true;
|
|
34978
35025
|
return knobs;
|
|
34979
35026
|
}
|
|
35027
|
+
function parseVideoIds(value) {
|
|
35028
|
+
if (typeof value !== "string") return void 0;
|
|
35029
|
+
const ids = value.split(",").map((part) => part.trim()).filter(Boolean);
|
|
35030
|
+
return ids.length > 0 ? ids : void 0;
|
|
35031
|
+
}
|
|
35032
|
+
function parseClipRefs(value) {
|
|
35033
|
+
if (typeof value !== "string" || value.trim() === "") return void 0;
|
|
35034
|
+
const refs = [];
|
|
35035
|
+
for (const entry of value.split(",").map((part) => part.trim())) {
|
|
35036
|
+
if (!entry) continue;
|
|
35037
|
+
const separator = entry.lastIndexOf(":");
|
|
35038
|
+
const generationId = separator === -1 ? "" : entry.slice(0, separator);
|
|
35039
|
+
const slot = Number(entry.slice(separator + 1));
|
|
35040
|
+
if (!generationId || !Number.isInteger(slot) || slot < 0) {
|
|
35041
|
+
throw new Error(
|
|
35042
|
+
`--from-clip takes batchId:slot pairs \u2014 "${entry}" is not one. Read a batch with \`baker studio get <id>\` to see its slots.`
|
|
35043
|
+
);
|
|
35044
|
+
}
|
|
35045
|
+
refs.push({ generationId, slot });
|
|
35046
|
+
}
|
|
35047
|
+
return refs.length > 0 ? refs : void 0;
|
|
35048
|
+
}
|
|
34980
35049
|
function clipImageLimitFor(args) {
|
|
34981
35050
|
const model = args.model ?? DEFAULT_VIDEO_GENERATE_MODEL;
|
|
34982
35051
|
const label = MODEL_REGISTRY.video_generate[model]?.label ?? model;
|
|
@@ -34999,9 +35068,18 @@ function clipImageLimitFor(args) {
|
|
|
34999
35068
|
refuse: (count) => `${label} takes up to ${max} reference images \u2014 you passed ${count}. Drop the weakest ones.`
|
|
35000
35069
|
};
|
|
35001
35070
|
}
|
|
35071
|
+
function clipModeFor(from) {
|
|
35072
|
+
if (from === "references") return "references";
|
|
35073
|
+
if (from === "text") return "text";
|
|
35074
|
+
return "first_frame";
|
|
35075
|
+
}
|
|
35002
35076
|
function buildAnimateBody(args, prompt, imageIds) {
|
|
35003
35077
|
const body = { prompt, imageIds, ...clipKnobs(args) };
|
|
35004
|
-
|
|
35078
|
+
const clipRefs = parseClipRefs(args["from-clip"]);
|
|
35079
|
+
if (clipRefs) body.clipRefs = clipRefs;
|
|
35080
|
+
const videoIds = parseVideoIds(args["from-video"]);
|
|
35081
|
+
if (videoIds) body.videoIds = videoIds;
|
|
35082
|
+
if (args.from) body.clipMode = clipModeFor(args.from);
|
|
35005
35083
|
if (args.model) body.model = args.model;
|
|
35006
35084
|
if (args["aspect-ratio"]) body.aspectRatio = args["aspect-ratio"];
|
|
35007
35085
|
body.duration = num2(args.duration);
|
|
@@ -35021,7 +35099,12 @@ function costHintsFor(body) {
|
|
|
35021
35099
|
// ceiling against an actual 5s — exactly double.
|
|
35022
35100
|
duration: body.duration ?? nearestClipDuration(model, DEFAULT_CLIP_DURATION_S),
|
|
35023
35101
|
resolution: body.resolution,
|
|
35024
|
-
generateAudio: body.generateAudio
|
|
35102
|
+
generateAudio: body.generateAudio,
|
|
35103
|
+
buildsOnSourceClip: (body.clipRefs?.length ?? 0) + (body.videoIds?.length ?? 0) > 0,
|
|
35104
|
+
// Reference images might carry a face, and a face sends the render to the
|
|
35105
|
+
// dearer provider. Scoped to reference mode because that is the only shape
|
|
35106
|
+
// that gets re-routed — a frame-started clip never does.
|
|
35107
|
+
mayRouteToFal: body.clipMode === "references" && body.imageIds.length > 0
|
|
35025
35108
|
});
|
|
35026
35109
|
const total = perTake * count;
|
|
35027
35110
|
const hints = [
|
|
@@ -35037,7 +35120,7 @@ function costHintsFor(body) {
|
|
|
35037
35120
|
var animateCommand = defineCommand163({
|
|
35038
35121
|
meta: {
|
|
35039
35122
|
name: "animate",
|
|
35040
|
-
description: "
|
|
35123
|
+
description: "Render a clip. With an image the look is already fixed, so the prompt describes MOVEMENT \u2014 what the camera does, what the subject does, in what order. With --from text there is no image and the prompt is the whole shot.\n\nA rendered clip is NOT usable anywhere until you keep it: `baker studio keep <id> --slot N` is what puts it in the video library. Takes nobody keeps are never ingested, which is what makes a rejected batch cheap.\n\nFor anything longer than 15 seconds, or to build on footage that already exists, use --model bytedance/seedance-2.5: it renders 4-30s and is the only model that reads an existing clip or an existing soundtrack.\n\nExamples:\n baker studio animate 'slow push in, model turns to camera and smiles' --image j57abc123def456ghi789\n baker studio animate 'handheld drift right, steam rising from the cup' --image './out/hero.png' --duration 6 --quality 1080p\n baker studio animate 'product rotates once on a turntable' --image j57abc\u2026,j57def\u2026 --from references\n baker studio animate 'she keeps walking, camera stays with her, then she stops and looks up' --image j57abc\u2026 --from references --from-clip j57batch\u2026:0 --model bytedance/seedance-2.5 --duration 20\n baker studio animate 'hold on the product, then a slow push-in' --from references --from-video j57vid\u2026 --model bytedance/seedance-2.5\n baker studio animate 'slow drone pull-back over a solar farm at golden hour, no people' --from text --model bytedance/seedance-2.5 --duration 12"
|
|
35041
35124
|
},
|
|
35042
35125
|
args: {
|
|
35043
35126
|
prompt: { type: "positional", description: "What MOVES", required: false },
|
|
@@ -35046,7 +35129,17 @@ var animateCommand = defineCommand163({
|
|
|
35046
35129
|
description: "Comma-separated library image ids, local file paths, and/or image URLs",
|
|
35047
35130
|
required: false
|
|
35048
35131
|
},
|
|
35049
|
-
from: { type: "string", description: "frame (default) | references", required: false },
|
|
35132
|
+
from: { type: "string", description: "frame (default) | references | text", required: false },
|
|
35133
|
+
"from-clip": {
|
|
35134
|
+
type: "string",
|
|
35135
|
+
description: "Studio takes to build on, batchId:slot (Seedance 2.5, needs --from references)",
|
|
35136
|
+
required: false
|
|
35137
|
+
},
|
|
35138
|
+
"from-video": {
|
|
35139
|
+
type: "string",
|
|
35140
|
+
description: "Library video ids to build on (Seedance 2.5, needs --from references)",
|
|
35141
|
+
required: false
|
|
35142
|
+
},
|
|
35050
35143
|
model: {
|
|
35051
35144
|
type: "string",
|
|
35052
35145
|
description: `Model id (default ${DEFAULT_VIDEO_GENERATE_MODEL})`,
|
|
@@ -35079,6 +35172,11 @@ var animateCommand = defineCommand163({
|
|
|
35079
35172
|
},
|
|
35080
35173
|
"conditioning-scale": { type: "string", description: "Frame adherence", required: false },
|
|
35081
35174
|
"cfg-scale": { type: "string", description: "Prompt adherence 0-1", required: false },
|
|
35175
|
+
watermark: {
|
|
35176
|
+
type: "boolean",
|
|
35177
|
+
description: "Burn in the provider watermark (Seedance 2.5, off by default)",
|
|
35178
|
+
required: false
|
|
35179
|
+
},
|
|
35082
35180
|
wait: { type: "string", description: "false to return the batch id immediately", required: false },
|
|
35083
35181
|
"max-wait": { type: "string", description: "Max ms to wait (default 600000, 0 = no wait)", required: false },
|
|
35084
35182
|
full: { type: "boolean", description: "Include settings, references and attribution", required: false }
|
|
@@ -35090,17 +35188,18 @@ var animateCommand = defineCommand163({
|
|
|
35090
35188
|
writeJson({ ok: false, error: { code: "VALIDATION_ERROR", message: "A prompt is required" } });
|
|
35091
35189
|
process.exit(1);
|
|
35092
35190
|
}
|
|
35093
|
-
if (!args.image) {
|
|
35191
|
+
if (!args.image && args.from !== "text") {
|
|
35094
35192
|
writeJson({
|
|
35095
35193
|
ok: false,
|
|
35096
35194
|
error: {
|
|
35097
35195
|
code: "VALIDATION_ERROR",
|
|
35098
|
-
message: "--image is required: a clip animates a still, so it needs the image to start from"
|
|
35196
|
+
message: "--image is required unless you pass --from text: a clip normally animates a still, so it needs the image to start from",
|
|
35197
|
+
fix: "Pass --image <id|path|url>, or --from text to render from the prompt alone."
|
|
35099
35198
|
}
|
|
35100
35199
|
});
|
|
35101
35200
|
process.exit(1);
|
|
35102
35201
|
}
|
|
35103
|
-
const { imageIds } = await resolveLibraryImageIds(args.image, clipImageLimitFor(args));
|
|
35202
|
+
const { imageIds } = args.image ? await resolveLibraryImageIds(args.image, clipImageLimitFor(args)) : { imageIds: [] };
|
|
35104
35203
|
const body = buildAnimateBody(args, prompt, imageIds);
|
|
35105
35204
|
const costHints = costHintsFor(body);
|
|
35106
35205
|
const start = await apiPost("/api/studio/animate", body);
|
|
@@ -35619,6 +35718,7 @@ function imageCard(model) {
|
|
|
35619
35718
|
};
|
|
35620
35719
|
}
|
|
35621
35720
|
function videoCard(model) {
|
|
35721
|
+
const sourceClips = maxInputSlot("video_generate", model, "video");
|
|
35622
35722
|
return {
|
|
35623
35723
|
id: model,
|
|
35624
35724
|
label: MODEL_REGISTRY.video_generate[model]?.label,
|
|
@@ -35630,6 +35730,11 @@ function videoCard(model) {
|
|
|
35630
35730
|
// Only meaningful in reference mode; a first-frame clip always takes exactly
|
|
35631
35731
|
// one image, whatever the model's reference slot allows.
|
|
35632
35732
|
referenceMode: supportsReferenceToVideo(model) ? `up to ${maxInputReferences("video_generate", model) ?? "several"} reference images` : "first frame only \u2014 this model cannot take reference images",
|
|
35733
|
+
// Stated on EVERY card, not just the one model that has it: "can this build
|
|
35734
|
+
// on a clip I already made" is the question that decides whether extend and
|
|
35735
|
+
// edit are possible at all, and a field that appears only when the answer is
|
|
35736
|
+
// yes reads as "unknown" everywhere else.
|
|
35737
|
+
buildsOnClips: sourceClips ? `up to ${sourceClips} source clips \u2014 extend, edit, or carry their motion (pass with --from-clip)` : "no \u2014 this model cannot build on an existing clip",
|
|
35633
35738
|
knobs: knobsFor("video_generate", model)
|
|
35634
35739
|
};
|
|
35635
35740
|
}
|
|
@@ -35679,7 +35784,7 @@ Examples:
|
|
|
35679
35784
|
data: cards,
|
|
35680
35785
|
meta: { count: cards.length },
|
|
35681
35786
|
hints: [
|
|
35682
|
-
kind === "image" ? "Shapes snap to the nearest the model renders; every other setting is refused if the model does not have it." : "A clip starting from one exact frame uses ONE image. Only Seedance and Gemini Omni take several as references."
|
|
35787
|
+
kind === "image" ? "Shapes snap to the nearest the model renders; every other setting is refused if the model does not have it." : "A clip starting from one exact frame uses ONE image. Only Seedance and Gemini Omni take several as references, and only Seedance 2.5 goes past 15s or builds on an existing clip."
|
|
35683
35788
|
]
|
|
35684
35789
|
});
|
|
35685
35790
|
}
|