@nodaro/shared 1.20.0 → 1.22.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/dist/index.cjs +261 -50
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +350 -15
- package/dist/index.d.ts +350 -15
- package/dist/index.js +250 -51
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/__tests__/llm-models.test.ts +138 -0
- package/src/__tests__/video-analysis-catalog-sync.test.ts +63 -0
- package/src/__tests__/video-analysis-pricing.test.ts +5 -4
- package/src/__tests__/video-analysis.test.ts +201 -1
- package/src/index.ts +3 -0
- package/src/llm-models.ts +141 -11
- package/src/model-catalog.ts +20 -20
- package/src/video-analysis-pricing.ts +21 -18
- package/src/video-analysis.ts +256 -1
package/dist/index.cjs
CHANGED
|
@@ -1667,11 +1667,11 @@ var VIDEO_MODELS = {
|
|
|
1667
1667
|
description: "Legacy fast-tier analysis model (pre-2026-07). Kept so stored raw-model configs keep running and keep pricing under their own identifier; new fast-tier runs use the current fast model.",
|
|
1668
1668
|
useCases: ["video-analysis", "shot-list", "fast"],
|
|
1669
1669
|
pricing: [
|
|
1670
|
-
{ identifier: "video-analysis:gemini-3-flash", credits:
|
|
1671
|
-
{ identifier: "video-analysis:gemini-3-flash:60s", credits:
|
|
1672
|
-
{ identifier: "video-analysis:gemini-3-flash:180s", credits:
|
|
1673
|
-
{ identifier: "video-analysis:gemini-3-flash:360s", credits:
|
|
1674
|
-
{ identifier: "video-analysis:gemini-3-flash:600s", credits:
|
|
1670
|
+
{ identifier: "video-analysis:gemini-3-flash", credits: 30, note: "10-min ceiling (no duration given)" },
|
|
1671
|
+
{ identifier: "video-analysis:gemini-3-flash:60s", credits: 6 },
|
|
1672
|
+
{ identifier: "video-analysis:gemini-3-flash:180s", credits: 7 },
|
|
1673
|
+
{ identifier: "video-analysis:gemini-3-flash:360s", credits: 18 },
|
|
1674
|
+
{ identifier: "video-analysis:gemini-3-flash:600s", credits: 30, note: "10-min ceiling" }
|
|
1675
1675
|
]
|
|
1676
1676
|
},
|
|
1677
1677
|
"gemini-3.6-flash-video-analysis": {
|
|
@@ -1684,11 +1684,11 @@ var VIDEO_MODELS = {
|
|
|
1684
1684
|
description: "Analyze a video into a structured shot list (scenes, camera, audio) \u2014 fast, economy tier. Billed per duration bucket.",
|
|
1685
1685
|
useCases: ["video-analysis", "shot-list", "fast"],
|
|
1686
1686
|
pricing: [
|
|
1687
|
-
{ identifier: "video-analysis:gemini-3.6-flash", credits:
|
|
1688
|
-
{ identifier: "video-analysis:gemini-3.6-flash:60s", credits:
|
|
1689
|
-
{ identifier: "video-analysis:gemini-3.6-flash:180s", credits:
|
|
1690
|
-
{ identifier: "video-analysis:gemini-3.6-flash:360s", credits:
|
|
1691
|
-
{ identifier: "video-analysis:gemini-3.6-flash:600s", credits:
|
|
1687
|
+
{ identifier: "video-analysis:gemini-3.6-flash", credits: 81, note: "10-min ceiling (no duration given)" },
|
|
1688
|
+
{ identifier: "video-analysis:gemini-3.6-flash:60s", credits: 14 },
|
|
1689
|
+
{ identifier: "video-analysis:gemini-3.6-flash:180s", credits: 19 },
|
|
1690
|
+
{ identifier: "video-analysis:gemini-3.6-flash:360s", credits: 49 },
|
|
1691
|
+
{ identifier: "video-analysis:gemini-3.6-flash:600s", credits: 81, note: "10-min ceiling" }
|
|
1692
1692
|
]
|
|
1693
1693
|
},
|
|
1694
1694
|
"gemini-3.1-pro-video-analysis": {
|
|
@@ -1701,11 +1701,11 @@ var VIDEO_MODELS = {
|
|
|
1701
1701
|
description: "Analyze a video into a structured shot list (scenes, camera, audio) \u2014 higher-fidelity, default tier. Billed per duration bucket.",
|
|
1702
1702
|
useCases: ["video-analysis", "shot-list", "cinematic"],
|
|
1703
1703
|
pricing: [
|
|
1704
|
-
{ identifier: "video-analysis:gemini-3.1-pro", credits:
|
|
1705
|
-
{ identifier: "video-analysis:gemini-3.1-pro:60s", credits:
|
|
1706
|
-
{ identifier: "video-analysis:gemini-3.1-pro:180s", credits:
|
|
1707
|
-
{ identifier: "video-analysis:gemini-3.1-pro:360s", credits:
|
|
1708
|
-
{ identifier: "video-analysis:gemini-3.1-pro:600s", credits:
|
|
1704
|
+
{ identifier: "video-analysis:gemini-3.1-pro", credits: 120, note: "10-min ceiling (no duration given)" },
|
|
1705
|
+
{ identifier: "video-analysis:gemini-3.1-pro:60s", credits: 21 },
|
|
1706
|
+
{ identifier: "video-analysis:gemini-3.1-pro:180s", credits: 27 },
|
|
1707
|
+
{ identifier: "video-analysis:gemini-3.1-pro:360s", credits: 72 },
|
|
1708
|
+
{ identifier: "video-analysis:gemini-3.1-pro:600s", credits: 120, note: "10-min ceiling" }
|
|
1709
1709
|
]
|
|
1710
1710
|
},
|
|
1711
1711
|
// Both mixed tiers are variants of the same advanced multi-engine analysis
|
|
@@ -1721,11 +1721,11 @@ var VIDEO_MODELS = {
|
|
|
1721
1721
|
description: "Our most advanced analysis tier \u2014 multiple analysis engines combined into one result for maximum completeness and accuracy. Billed per duration bucket.",
|
|
1722
1722
|
useCases: ["video-analysis", "shot-list", "premium", "most-complete"],
|
|
1723
1723
|
pricing: [
|
|
1724
|
-
{ identifier: "video-analysis:mixed", credits:
|
|
1725
|
-
{ identifier: "video-analysis:mixed:60s", credits:
|
|
1726
|
-
{ identifier: "video-analysis:mixed:180s", credits:
|
|
1727
|
-
{ identifier: "video-analysis:mixed:360s", credits:
|
|
1728
|
-
{ identifier: "video-analysis:mixed:600s", credits:
|
|
1724
|
+
{ identifier: "video-analysis:mixed", credits: 200, note: "10-min ceiling (no duration given)" },
|
|
1725
|
+
{ identifier: "video-analysis:mixed:60s", credits: 34 },
|
|
1726
|
+
{ identifier: "video-analysis:mixed:180s", credits: 46 },
|
|
1727
|
+
{ identifier: "video-analysis:mixed:360s", credits: 120 },
|
|
1728
|
+
{ identifier: "video-analysis:mixed:600s", credits: 200, note: "10-min ceiling" }
|
|
1729
1729
|
]
|
|
1730
1730
|
}
|
|
1731
1731
|
};
|
|
@@ -4281,7 +4281,14 @@ var LLM_MODELS = [
|
|
|
4281
4281
|
vendor: "google",
|
|
4282
4282
|
structuredOutputMode: "kie-response-format",
|
|
4283
4283
|
supportsImages: true,
|
|
4284
|
-
maxOutputTokens: 8192
|
|
4284
|
+
maxOutputTokens: 8192,
|
|
4285
|
+
// KIE-first: no `preferDirect` — direct is the reliability fallback only.
|
|
4286
|
+
// (Per-lane rates are deliberately NOT in this published package; see
|
|
4287
|
+
// backend/src/lib/pricing/llm-cost.ts.)
|
|
4288
|
+
directGeminiModel: "gemini-3-flash-preview",
|
|
4289
|
+
// No `reasoningEfforts` at all on the KIE lane, but the vendor API accepts
|
|
4290
|
+
// the full minimal→high ladder (`none` maps to Google's `minimal`).
|
|
4291
|
+
directReasoningEfforts: ["none", "low", "medium", "high"]
|
|
4285
4292
|
},
|
|
4286
4293
|
{
|
|
4287
4294
|
id: "gemini-3.6-flash",
|
|
@@ -4299,7 +4306,19 @@ var LLM_MODELS = [
|
|
|
4299
4306
|
maxOutputTokens: 8192,
|
|
4300
4307
|
// KIE's 3.6 endpoint accepts `reasoning_effort: low | high` (thinking
|
|
4301
4308
|
// level) — exactly the chat-completions wire mapping deriveParams sends.
|
|
4302
|
-
|
|
4309
|
+
// Google's own API additionally accepts `minimal` and `medium` on this
|
|
4310
|
+
// model; the set stays at the KIE-safe intersection because ONE field
|
|
4311
|
+
// feeds both lanes and this model is KIE-first. Widen it only if/when
|
|
4312
|
+
// `preferDirect` is set here.
|
|
4313
|
+
reasoningEfforts: ["low", "high"],
|
|
4314
|
+
// Google's own API additionally accepts `minimal` and `medium` here —
|
|
4315
|
+
// live-verified 2026-07-28. Advanced mode unlocks them.
|
|
4316
|
+
directReasoningEfforts: ["none", "low", "medium", "high"],
|
|
4317
|
+
// KIE-first: this model backs 5 of the LLM_FEATURE_DEFAULTS plus the
|
|
4318
|
+
// video-analysis fast tier, so it carries the highest call volume of any
|
|
4319
|
+
// Gemini entry — the lane with the lower unit cost wins by default and
|
|
4320
|
+
// direct is the reliability fallback only.
|
|
4321
|
+
directGeminiModel: "gemini-3.6-flash"
|
|
4303
4322
|
},
|
|
4304
4323
|
{
|
|
4305
4324
|
id: "claude-haiku-4.5",
|
|
@@ -4349,7 +4368,19 @@ var LLM_MODELS = [
|
|
|
4349
4368
|
vendor: "google",
|
|
4350
4369
|
structuredOutputMode: "kie-response-format",
|
|
4351
4370
|
supportsImages: true,
|
|
4352
|
-
maxOutputTokens: 16384
|
|
4371
|
+
maxOutputTokens: 16384,
|
|
4372
|
+
directGeminiModel: "gemini-3.1-pro-preview",
|
|
4373
|
+
// Google documents low/medium/high for 3.1 Pro — no `minimal` tier, so
|
|
4374
|
+
// this ladder is deliberately shorter than the flash models'.
|
|
4375
|
+
directReasoningEfforts: ["low", "medium", "high"],
|
|
4376
|
+
// The ONE Gemini model routed direct-first. It is the premium/low-volume
|
|
4377
|
+
// tier (video-analysis `pro`, no LLM_FEATURE_DEFAULTS entry), so the ~4×
|
|
4378
|
+
// list-price premium lands on the smallest call volume — and it is where
|
|
4379
|
+
// the direct lane's capability wins actually matter: real `thinkingLevel`
|
|
4380
|
+
// control, native media ingestion, and a `responseJsonSchema` that honours
|
|
4381
|
+
// `additionalProperties` (KIE's `response_format` silently DROPS
|
|
4382
|
+
// record/map-shaped fields — see the z.record rule in backend/CLAUDE.md).
|
|
4383
|
+
preferDirect: true
|
|
4353
4384
|
},
|
|
4354
4385
|
{
|
|
4355
4386
|
id: "claude-opus-4.7",
|
|
@@ -4569,14 +4600,24 @@ function getLlmModel(id) {
|
|
|
4569
4600
|
const aliased = LLM_MODELS.find((m) => m.id === canonical);
|
|
4570
4601
|
if (aliased) return aliased;
|
|
4571
4602
|
}
|
|
4572
|
-
return LLM_MODELS.find(
|
|
4603
|
+
return LLM_MODELS.find(
|
|
4604
|
+
(m) => m.kieSlugOrModel === id || m.directFallbackModel === id || m.directGeminiModel === id
|
|
4605
|
+
);
|
|
4573
4606
|
}
|
|
4574
4607
|
function getLlmTier(id) {
|
|
4575
4608
|
return getLlmModel(id)?.tier ?? "standard";
|
|
4576
4609
|
}
|
|
4577
|
-
function
|
|
4610
|
+
function availableReasoningEfforts(modelId, advanced = false) {
|
|
4611
|
+
const model = getLlmModel(modelId ?? "");
|
|
4612
|
+
if (!model) return [];
|
|
4613
|
+
if (advanced && supportsAdvancedMode(modelId)) {
|
|
4614
|
+
return model.directReasoningEfforts ?? model.reasoningEfforts ?? [];
|
|
4615
|
+
}
|
|
4616
|
+
return model.reasoningEfforts ?? [];
|
|
4617
|
+
}
|
|
4618
|
+
function effectiveReasoningEffort(modelId, requested, advanced = false) {
|
|
4578
4619
|
if (!requested || !(requested in EFFORT_RANK)) return void 0;
|
|
4579
|
-
const levels =
|
|
4620
|
+
const levels = availableReasoningEfforts(modelId, advanced);
|
|
4580
4621
|
if (!levels || levels.length === 0) return void 0;
|
|
4581
4622
|
const req = requested;
|
|
4582
4623
|
let best;
|
|
@@ -4585,20 +4626,32 @@ function effectiveReasoningEffort(modelId, requested) {
|
|
|
4585
4626
|
}
|
|
4586
4627
|
return best;
|
|
4587
4628
|
}
|
|
4588
|
-
function
|
|
4629
|
+
function bumpTier(tier) {
|
|
4630
|
+
if (tier === "economy") return "standard";
|
|
4631
|
+
if (tier === "standard") return "premium";
|
|
4632
|
+
return tier;
|
|
4633
|
+
}
|
|
4634
|
+
function supportsAdvancedMode(modelId) {
|
|
4635
|
+
return Boolean(modelId && getLlmModel(modelId)?.directGeminiModel);
|
|
4636
|
+
}
|
|
4637
|
+
var ADVANCED_MODE_UNAVAILABLE_REASON = "Advanced mode is available on Gemini models \u2014 switch the model to enable it.";
|
|
4638
|
+
function buildLlmCreditIdentifier(feature, modelId, reasoningEffort, advancedMode) {
|
|
4589
4639
|
if (!modelId) return feature;
|
|
4590
4640
|
let tier = getLlmTier(modelId);
|
|
4591
4641
|
const eff = effectiveReasoningEffort(modelId, reasoningEffort);
|
|
4592
|
-
if (eff !== void 0 && EFFORT_TIER_BUMP.has(eff))
|
|
4593
|
-
|
|
4594
|
-
else if (tier === "standard") tier = "premium";
|
|
4595
|
-
}
|
|
4642
|
+
if (eff !== void 0 && EFFORT_TIER_BUMP.has(eff)) tier = bumpTier(tier);
|
|
4643
|
+
if (advancedMode && supportsAdvancedMode(modelId)) tier = bumpTier(tier);
|
|
4596
4644
|
if (tier === "standard") return feature;
|
|
4597
4645
|
return `${feature}:${tier}`;
|
|
4598
4646
|
}
|
|
4599
4647
|
function resolveLlmCreditId(feature, body) {
|
|
4600
4648
|
const b = body;
|
|
4601
|
-
return buildLlmCreditIdentifier(
|
|
4649
|
+
return buildLlmCreditIdentifier(
|
|
4650
|
+
feature,
|
|
4651
|
+
b?.llmModel,
|
|
4652
|
+
b?.reasoningEffort,
|
|
4653
|
+
b?.advancedMode === true
|
|
4654
|
+
);
|
|
4602
4655
|
}
|
|
4603
4656
|
var VIDEO_ANALYSIS_LLM_MODELS = LLM_MODELS.filter((m) => getLlmModalityCaps(m.id).video && getLlmModalityCaps(m.id).audio).map((m) => m.id);
|
|
4604
4657
|
var VIDEO_ANALYSIS_TIERS = { fast: "gemini-3.6-flash", pro: "gemini-3.1-pro" };
|
|
@@ -12157,6 +12210,78 @@ function sanitizeRole(raw) {
|
|
|
12157
12210
|
return raw.trim().toLowerCase().replace(/\s+/g, "-").replace(/[^a-z0-9-]/g, "").replace(/^[^a-z]+/, "").slice(0, 32).replace(/-+/g, "-").replace(/-$/, "");
|
|
12158
12211
|
}
|
|
12159
12212
|
var VIDEO_ANALYSIS_MAX_SCENE_SEC = 8;
|
|
12213
|
+
var VIDEO_ANALYSIS_SHOT_ANGLES = [
|
|
12214
|
+
// Vertical placement and roll — the classical "angles".
|
|
12215
|
+
"eye-level",
|
|
12216
|
+
"low",
|
|
12217
|
+
"high",
|
|
12218
|
+
"overhead",
|
|
12219
|
+
"worms-eye",
|
|
12220
|
+
"dutch",
|
|
12221
|
+
// Relational viewpoints — where the camera sits with respect to the subject.
|
|
12222
|
+
"over-the-shoulder",
|
|
12223
|
+
"pov",
|
|
12224
|
+
"profile",
|
|
12225
|
+
"from-behind"
|
|
12226
|
+
];
|
|
12227
|
+
var VIDEO_ANALYSIS_FACELESS_ANGLES = /* @__PURE__ */ new Set(["over-the-shoulder", "from-behind"]);
|
|
12228
|
+
var VIDEO_ANALYSIS_VISUAL_EFFECTS = [
|
|
12229
|
+
"blur",
|
|
12230
|
+
"pixelate",
|
|
12231
|
+
"glitch",
|
|
12232
|
+
"grain",
|
|
12233
|
+
"vignette",
|
|
12234
|
+
"flash",
|
|
12235
|
+
"distortion",
|
|
12236
|
+
"double-exposure"
|
|
12237
|
+
];
|
|
12238
|
+
var VIDEO_ANALYSIS_TRANSITIONS = ["cut", "fade", "dissolve", "wipe", "whip"];
|
|
12239
|
+
var VIDEO_ANALYSIS_SPEED_EFFECTS = ["slow-motion", "ramp-in", "ramp-out", "timelapse", "freeze", "reverse"];
|
|
12240
|
+
var clipLookSchema = zod.z.object({
|
|
12241
|
+
/**
|
|
12242
|
+
* The rendering MEDIUM — "live-action photoreal", "2D anime", "stop-motion
|
|
12243
|
+
* claymation", "3D render", "oil painting", "pixel art".
|
|
12244
|
+
*
|
|
12245
|
+
* The most consequential field in this object, and orthogonal to every other
|
|
12246
|
+
* one: two shots with identical grade, lens, lighting and framing still look
|
|
12247
|
+
* nothing alike when one is live action and the other is an oil painting. Get
|
|
12248
|
+
* this wrong and a recreation renders the whole piece in the wrong medium,
|
|
12249
|
+
* which no amount of correct grade or lighting can rescue.
|
|
12250
|
+
*
|
|
12251
|
+
* Mirrors the product's Style picker, whose catalog defines exactly this axis
|
|
12252
|
+
* and states its independence from lighting, colour-look, atmosphere and lens.
|
|
12253
|
+
* A clip that genuinely changes medium partway (live action with an animated
|
|
12254
|
+
* insert) states the deviation in that scene's `visual`, as with `lighting`.
|
|
12255
|
+
*/
|
|
12256
|
+
style: zod.z.string().optional(),
|
|
12257
|
+
/** Colour grade / palette — "muted teal-and-orange, crushed blacks". */
|
|
12258
|
+
grade: zod.z.string().optional(),
|
|
12259
|
+
/** Camera or film FORMAT and stock — "anamorphic digital", "16mm film grain". */
|
|
12260
|
+
format: zod.z.string().optional(),
|
|
12261
|
+
/** Lens character — "wide-angle, shallow depth of field throughout". */
|
|
12262
|
+
lens: zod.z.string().optional(),
|
|
12263
|
+
/** Overall lighting style — "hard single-source daylight, deep shadow". */
|
|
12264
|
+
lighting: zod.z.string().optional(),
|
|
12265
|
+
/** What KIND of piece this is — "cinematic trailer", "talking-head vlog". */
|
|
12266
|
+
genre: zod.z.string().optional(),
|
|
12267
|
+
/**
|
|
12268
|
+
* The visual INFLUENCE the piece clearly evokes — a cinematographer, director,
|
|
12269
|
+
* photographer or named aesthetic ("shot like Deakins", "Wes Anderson
|
|
12270
|
+
* symmetry", "80s Kodachrome editorial").
|
|
12271
|
+
*
|
|
12272
|
+
* The highest-leverage field here by a distance: a couple of words transfer a
|
|
12273
|
+
* whole aesthetic that would otherwise take a paragraph of grade, lens and
|
|
12274
|
+
* lighting prose to approximate — which is exactly why the product already
|
|
12275
|
+
* exposes it as a curated picker ("Photographer / Artist") whose catalog ships
|
|
12276
|
+
* `in the style of …` prompt hints. The analyzer had no way to read it back.
|
|
12277
|
+
*
|
|
12278
|
+
* Deliberately conservative: OMIT unless the footage genuinely evokes a
|
|
12279
|
+
* well-known, nameable style. A confident misattribution is worse than silence,
|
|
12280
|
+
* because it drags an entire wrong aesthetic into every regenerated shot — so
|
|
12281
|
+
* describing the look in `grade`/`lighting` always beats guessing a name.
|
|
12282
|
+
*/
|
|
12283
|
+
influence: zod.z.string().optional()
|
|
12284
|
+
});
|
|
12160
12285
|
var VIDEO_ANALYSIS_ENTITY_SOURCES = ["wired-character", "wired-object", "wired-location", "wired-creature"];
|
|
12161
12286
|
var SLOT_TOKEN_RE = /\{slot:([a-z0-9-]+)\}/g;
|
|
12162
12287
|
var VIDEO_ANALYSIS_VARIATION_SLUGS = ["dream", "flashback", "disguise", "costume", "transformation", "era", "alt-1", "alt-2"];
|
|
@@ -12189,7 +12314,28 @@ var entitySlotSchema = zod.z.object({
|
|
|
12189
12314
|
var audioLayerSchema = zod.z.object({
|
|
12190
12315
|
mode: zod.z.enum(["speech", "music", "sfx"]),
|
|
12191
12316
|
content: zod.z.string().min(1),
|
|
12192
|
-
voice: zod.z.string().optional()
|
|
12317
|
+
voice: zod.z.string().optional(),
|
|
12318
|
+
/**
|
|
12319
|
+
* SPEECH ONLY — `slotId` of the on-screen speaker saying these words.
|
|
12320
|
+
*
|
|
12321
|
+
* `voice` casts a voice ("male, proud triumphant shouting"); this says WHO it
|
|
12322
|
+
* belongs to, so a recreation can route the line to the right character
|
|
12323
|
+
* instead of guessing. Usually one person speaks per scene and the guess is
|
|
12324
|
+
* right, which is exactly why the cases with two speakers over one cut fail
|
|
12325
|
+
* silently without this field.
|
|
12326
|
+
*
|
|
12327
|
+
* Optional by design and deliberately NOT refined against `mode` here: the
|
|
12328
|
+
* window schema is the enforced decode grammar, and rejecting a whole roll
|
|
12329
|
+
* because the model tagged a music layer would be a hair-trigger failure. A
|
|
12330
|
+
* speaker on a non-speech layer, or one naming a slot that no longer exists,
|
|
12331
|
+
* is stripped structurally by `dropUnknownSpeakers` — the same
|
|
12332
|
+
* unwrap/drop/sweep philosophy the slot-token and binding channels use.
|
|
12333
|
+
*
|
|
12334
|
+
* An unseen narrator gets NO speaker: a voice with no body is never a slot
|
|
12335
|
+
* (doctrine §5), so attribution here would resurrect the phantom-entity
|
|
12336
|
+
* defect that `stripOrphanSlots` exists to kill.
|
|
12337
|
+
*/
|
|
12338
|
+
speakerSlot: zod.z.string().optional()
|
|
12193
12339
|
});
|
|
12194
12340
|
var windowSceneBase = zod.z.object({
|
|
12195
12341
|
startSec: zod.z.number().min(0),
|
|
@@ -12197,8 +12343,27 @@ var windowSceneBase = zod.z.object({
|
|
|
12197
12343
|
label: zod.z.string().min(1),
|
|
12198
12344
|
shotType: zod.z.string().min(1),
|
|
12199
12345
|
camera: zod.z.string(),
|
|
12346
|
+
/** Camera VIEWPOINT. Absent ⇒ eye-level. Keeps `camera` to pure MOVEMENT and
|
|
12347
|
+
* frees `shotType` to state the SIZE even on an over-the-shoulder or POV. */
|
|
12348
|
+
angle: zod.z.enum(VIDEO_ANALYSIS_SHOT_ANGLES).optional(),
|
|
12349
|
+
/** Time manipulation. Absent ⇒ normal speed. */
|
|
12350
|
+
speed: zod.z.enum(VIDEO_ANALYSIS_SPEED_EFFECTS).optional(),
|
|
12200
12351
|
visual: zod.z.string().min(1),
|
|
12201
|
-
|
|
12352
|
+
/**
|
|
12353
|
+
* Text burned into the PICTURE of this shot — titles, captions, lower-thirds,
|
|
12354
|
+
* subtitles — verbatim, in its original script. Absent when the frame carries
|
|
12355
|
+
* none.
|
|
12356
|
+
*
|
|
12357
|
+
* Doctrine already asks for on-screen text inside `visual` prose, but a
|
|
12358
|
+
* recreation needs to know discretely whether to RENDER text at all, and
|
|
12359
|
+
* `translateOnScreenTextToEnglish` had no structured field to land in. It is
|
|
12360
|
+
* also the signal the auto-cast frame judge reads: picture text belonging to an
|
|
12361
|
+
* earlier scene's speech means the shot is replayed footage under a voice-over.
|
|
12362
|
+
*/
|
|
12363
|
+
onScreenText: zod.z.string().optional(),
|
|
12364
|
+
/** Effects on this shot's PICTURE. Absent ⇒ a clean image. */
|
|
12365
|
+
effects: zod.z.array(zod.z.enum(VIDEO_ANALYSIS_VISUAL_EFFECTS)).optional(),
|
|
12366
|
+
transitionOut: zod.z.enum(VIDEO_ANALYSIS_TRANSITIONS).optional(),
|
|
12202
12367
|
// Array of concurrent layers (music + speech + sfx together); [] = silence.
|
|
12203
12368
|
audio: zod.z.array(audioLayerSchema),
|
|
12204
12369
|
/** slotId → variationId for slots wearing a NON-default look in this scene
|
|
@@ -12210,6 +12375,9 @@ var windowSceneBase = zod.z.object({
|
|
|
12210
12375
|
var windowSceneSchema = windowSceneBase.refine((s) => s.endSec > s.startSec, { message: "endSec must be > startSec" });
|
|
12211
12376
|
var windowAnalysisSchema = zod.z.object({
|
|
12212
12377
|
language: zod.z.string().optional(),
|
|
12378
|
+
/** The clip-level look as read from THIS window. Merge folds the windows
|
|
12379
|
+
* field-by-field (first non-empty wins), like `language`. */
|
|
12380
|
+
look: clipLookSchema.optional(),
|
|
12213
12381
|
slots: zod.z.array(entitySlotSchema),
|
|
12214
12382
|
scenes: zod.z.array(windowSceneSchema)
|
|
12215
12383
|
});
|
|
@@ -12228,6 +12396,10 @@ var videoAnalysisResultSchema = zod.z.object({
|
|
|
12228
12396
|
title: zod.z.string().optional(),
|
|
12229
12397
|
language: zod.z.string().optional()
|
|
12230
12398
|
}),
|
|
12399
|
+
/** Clip-level look, merged across windows. Deliberately a sibling of `meta`
|
|
12400
|
+
* rather than a member: `meta` is probed fact (ffprobe dimensions, probed
|
|
12401
|
+
* duration), while this is the model's reading of the photography. */
|
|
12402
|
+
look: clipLookSchema.optional(),
|
|
12231
12403
|
slots: zod.z.array(entitySlotSchema),
|
|
12232
12404
|
scenes: zod.z.array(analyzedSceneSchema).min(1),
|
|
12233
12405
|
/** CAST VARIATIONS (§4 cap handling): looks the analyzer's merge FOLDED into
|
|
@@ -12278,6 +12450,33 @@ function dropUnknownBindings(sv, validBySlot) {
|
|
|
12278
12450
|
}
|
|
12279
12451
|
return { kept: Object.keys(kept).length > 0 ? kept : void 0, dropped };
|
|
12280
12452
|
}
|
|
12453
|
+
function rewriteSpeakerSlots(audio, slotRenames) {
|
|
12454
|
+
if (!audio.some((a) => a.speakerSlot !== void 0 && slotRenames[a.speakerSlot])) return audio;
|
|
12455
|
+
return audio.map((a) => {
|
|
12456
|
+
const to = a.speakerSlot !== void 0 ? slotRenames[a.speakerSlot] : void 0;
|
|
12457
|
+
return to ? { ...a, speakerSlot: to } : a;
|
|
12458
|
+
});
|
|
12459
|
+
}
|
|
12460
|
+
function dropUnknownSpeakers(audio, validSlotIds) {
|
|
12461
|
+
const dropped = [];
|
|
12462
|
+
const out = audio.map((a) => {
|
|
12463
|
+
if (a.speakerSlot === void 0) return a;
|
|
12464
|
+
if (a.mode === "speech" && validSlotIds.has(a.speakerSlot)) return a;
|
|
12465
|
+
dropped.push(a.speakerSlot);
|
|
12466
|
+
const { speakerSlot: _drop, ...rest } = a;
|
|
12467
|
+
return rest;
|
|
12468
|
+
});
|
|
12469
|
+
return { audio: dropped.length > 0 ? out : audio, dropped };
|
|
12470
|
+
}
|
|
12471
|
+
function mergeClipLook(looks) {
|
|
12472
|
+
const out = {};
|
|
12473
|
+
for (const look of looks) {
|
|
12474
|
+
for (const [k, v] of Object.entries(look ?? {})) {
|
|
12475
|
+
if (typeof v === "string" && v.trim() && !out[k]) out[k] = v.trim();
|
|
12476
|
+
}
|
|
12477
|
+
}
|
|
12478
|
+
return Object.keys(out).length > 0 ? out : void 0;
|
|
12479
|
+
}
|
|
12281
12480
|
function renderAnalyzedScene(scene, slots, castMap) {
|
|
12282
12481
|
const byId = new Map(slots.map((s) => [s.slotId, s]));
|
|
12283
12482
|
return scene.visual.replace(SLOT_TOKEN_RE, (_whole, id) => castMap?.[id] ?? byId.get(id)?.description ?? id);
|
|
@@ -12311,27 +12510,27 @@ var WINDOW_OVERLAP = 5;
|
|
|
12311
12510
|
var VIDEO_ANALYSIS_WINDOW = { LEN: WINDOW_LEN, STRIDE: WINDOW_STRIDE, OVERLAP: WINDOW_OVERLAP, SINGLE_MAX: 180 };
|
|
12312
12511
|
var VIDEO_ANALYSIS_BUCKET_CREDITS = {
|
|
12313
12512
|
// Legacy fast-tier model (pre-2026-07) — kept for stored raw-id configs.
|
|
12314
|
-
"video-analysis:gemini-3-flash:60s":
|
|
12315
|
-
"video-analysis:gemini-3-flash:180s":
|
|
12316
|
-
"video-analysis:gemini-3-flash:360s":
|
|
12317
|
-
"video-analysis:gemini-3-flash:600s":
|
|
12513
|
+
"video-analysis:gemini-3-flash:60s": 6,
|
|
12514
|
+
"video-analysis:gemini-3-flash:180s": 7,
|
|
12515
|
+
"video-analysis:gemini-3-flash:360s": 18,
|
|
12516
|
+
"video-analysis:gemini-3-flash:600s": 30,
|
|
12318
12517
|
// Current fast tier — regenerated from the private formula for its backing
|
|
12319
12518
|
// model; higher than the legacy fast schedule but still ≤ pro per bucket.
|
|
12320
|
-
"video-analysis:gemini-3.6-flash:60s":
|
|
12321
|
-
"video-analysis:gemini-3.6-flash:180s":
|
|
12322
|
-
"video-analysis:gemini-3.6-flash:360s":
|
|
12323
|
-
"video-analysis:gemini-3.6-flash:600s":
|
|
12324
|
-
"video-analysis:gemini-3.1-pro:60s":
|
|
12325
|
-
"video-analysis:gemini-3.1-pro:180s":
|
|
12326
|
-
"video-analysis:gemini-3.1-pro:360s":
|
|
12327
|
-
"video-analysis:gemini-3.1-pro:600s":
|
|
12519
|
+
"video-analysis:gemini-3.6-flash:60s": 14,
|
|
12520
|
+
"video-analysis:gemini-3.6-flash:180s": 19,
|
|
12521
|
+
"video-analysis:gemini-3.6-flash:360s": 49,
|
|
12522
|
+
"video-analysis:gemini-3.6-flash:600s": 81,
|
|
12523
|
+
"video-analysis:gemini-3.1-pro:60s": 21,
|
|
12524
|
+
"video-analysis:gemini-3.1-pro:180s": 27,
|
|
12525
|
+
"video-analysis:gemini-3.1-pro:360s": 72,
|
|
12526
|
+
"video-analysis:gemini-3.1-pro:600s": 120,
|
|
12328
12527
|
// Mixed tiers (`mixed` + `mixed-fast`) share ONE credit family — they are
|
|
12329
12528
|
// variants of the same engine plan (plan internals live in the private
|
|
12330
12529
|
// analysis plugin). Admin-tunable via model_pricing like every other row.
|
|
12331
|
-
"video-analysis:mixed:60s":
|
|
12332
|
-
"video-analysis:mixed:180s":
|
|
12333
|
-
"video-analysis:mixed:360s":
|
|
12334
|
-
"video-analysis:mixed:600s":
|
|
12530
|
+
"video-analysis:mixed:60s": 34,
|
|
12531
|
+
"video-analysis:mixed:180s": 46,
|
|
12532
|
+
"video-analysis:mixed:360s": 120,
|
|
12533
|
+
"video-analysis:mixed:600s": 200
|
|
12335
12534
|
};
|
|
12336
12535
|
function videoAnalysisCreditSegment(modelOrSentinel) {
|
|
12337
12536
|
return modelOrSentinel === "mixed-fast" ? "mixed" : modelOrSentinel;
|
|
@@ -12365,6 +12564,7 @@ function clampSmartCutWindow(value) {
|
|
|
12365
12564
|
}
|
|
12366
12565
|
|
|
12367
12566
|
exports.ACTIVE_SCENE_HELPERS = ACTIVE_SCENE_HELPERS;
|
|
12567
|
+
exports.ADVANCED_MODE_UNAVAILABLE_REASON = ADVANCED_MODE_UNAVAILABLE_REASON;
|
|
12368
12568
|
exports.AGGREGATEABLE_TYPES = AGGREGATEABLE_TYPES;
|
|
12369
12569
|
exports.AI_AVATAR_DURATION_BUCKETS = AI_AVATAR_DURATION_BUCKETS;
|
|
12370
12570
|
exports.AI_AVATAR_MAX_AUDIO_SEC = AI_AVATAR_MAX_AUDIO_SEC;
|
|
@@ -12731,16 +12931,21 @@ exports.VIDEO_ANALYSIS_DEFAULT_VARIATION = VIDEO_ANALYSIS_DEFAULT_VARIATION;
|
|
|
12731
12931
|
exports.VIDEO_ANALYSIS_DURATION_BUCKETS = VIDEO_ANALYSIS_DURATION_BUCKETS;
|
|
12732
12932
|
exports.VIDEO_ANALYSIS_DURATION_TOLERANCE_SEC = VIDEO_ANALYSIS_DURATION_TOLERANCE_SEC;
|
|
12733
12933
|
exports.VIDEO_ANALYSIS_ENTITY_SOURCES = VIDEO_ANALYSIS_ENTITY_SOURCES;
|
|
12934
|
+
exports.VIDEO_ANALYSIS_FACELESS_ANGLES = VIDEO_ANALYSIS_FACELESS_ANGLES;
|
|
12734
12935
|
exports.VIDEO_ANALYSIS_LEGACY_MODELS = VIDEO_ANALYSIS_LEGACY_MODELS;
|
|
12735
12936
|
exports.VIDEO_ANALYSIS_LLM_MODELS = VIDEO_ANALYSIS_LLM_MODELS;
|
|
12736
12937
|
exports.VIDEO_ANALYSIS_MAX_DURATION_SEC = VIDEO_ANALYSIS_MAX_DURATION_SEC;
|
|
12737
12938
|
exports.VIDEO_ANALYSIS_MAX_SCENE_SEC = VIDEO_ANALYSIS_MAX_SCENE_SEC;
|
|
12738
12939
|
exports.VIDEO_ANALYSIS_MAX_VARIATIONS = VIDEO_ANALYSIS_MAX_VARIATIONS;
|
|
12739
12940
|
exports.VIDEO_ANALYSIS_MIXED_TIERS = VIDEO_ANALYSIS_MIXED_TIERS;
|
|
12941
|
+
exports.VIDEO_ANALYSIS_SHOT_ANGLES = VIDEO_ANALYSIS_SHOT_ANGLES;
|
|
12942
|
+
exports.VIDEO_ANALYSIS_SPEED_EFFECTS = VIDEO_ANALYSIS_SPEED_EFFECTS;
|
|
12740
12943
|
exports.VIDEO_ANALYSIS_TIERS = VIDEO_ANALYSIS_TIERS;
|
|
12741
12944
|
exports.VIDEO_ANALYSIS_TIER_LABELS = VIDEO_ANALYSIS_TIER_LABELS;
|
|
12742
12945
|
exports.VIDEO_ANALYSIS_TIER_ORDER = VIDEO_ANALYSIS_TIER_ORDER;
|
|
12946
|
+
exports.VIDEO_ANALYSIS_TRANSITIONS = VIDEO_ANALYSIS_TRANSITIONS;
|
|
12743
12947
|
exports.VIDEO_ANALYSIS_VARIATION_SLUGS = VIDEO_ANALYSIS_VARIATION_SLUGS;
|
|
12948
|
+
exports.VIDEO_ANALYSIS_VISUAL_EFFECTS = VIDEO_ANALYSIS_VISUAL_EFFECTS;
|
|
12744
12949
|
exports.VIDEO_ANALYSIS_WINDOW = VIDEO_ANALYSIS_WINDOW;
|
|
12745
12950
|
exports.VIDEO_AUDIO_CAPABILITY = VIDEO_AUDIO_CAPABILITY;
|
|
12746
12951
|
exports.VIDEO_CLIP_CREDITS = VIDEO_CLIP_CREDITS;
|
|
@@ -12788,6 +12993,7 @@ exports.aspectRatioFromDims = aspectRatioFromDims;
|
|
|
12788
12993
|
exports.aspectRatioOptionsByKind = aspectRatioOptionsByKind;
|
|
12789
12994
|
exports.aspectRatioToNumber = aspectRatioToNumber;
|
|
12790
12995
|
exports.assembleNarratedVideoCredits = assembleNarratedVideoCredits;
|
|
12996
|
+
exports.availableReasoningEfforts = availableReasoningEfforts;
|
|
12791
12997
|
exports.bucketSecondsFromCreditId = bucketSecondsFromCreditId;
|
|
12792
12998
|
exports.buildBoardPrompt = buildBoardPrompt;
|
|
12793
12999
|
exports.buildChildrenByParent = buildChildrenByParent;
|
|
@@ -12824,6 +13030,7 @@ exports.clampSmartCutWindow = clampSmartCutWindow;
|
|
|
12824
13030
|
exports.cleanOrphanedItems = cleanOrphanedItems;
|
|
12825
13031
|
exports.clearImageCriticMetadata = clearImageCriticMetadata;
|
|
12826
13032
|
exports.clearVideoCriticMetadata = clearVideoCriticMetadata;
|
|
13033
|
+
exports.clipLookSchema = clipLookSchema;
|
|
12827
13034
|
exports.collectAncestorRefs = collectAncestorRefs;
|
|
12828
13035
|
exports.combineSameLabelRefs = combineSameLabelRefs;
|
|
12829
13036
|
exports.countRefModalityEdges = countRefModalityEdges;
|
|
@@ -12839,6 +13046,7 @@ exports.describeEdgeBehavior = describeEdgeBehavior;
|
|
|
12839
13046
|
exports.describeMaskRegion = describeMaskRegion;
|
|
12840
13047
|
exports.describeSlotControl = describeSlotControl;
|
|
12841
13048
|
exports.dropUnknownBindings = dropUnknownBindings;
|
|
13049
|
+
exports.dropUnknownSpeakers = dropUnknownSpeakers;
|
|
12842
13050
|
exports.durationsByMode = durationsByMode;
|
|
12843
13051
|
exports.effectiveReasoningEffort = effectiveReasoningEffort;
|
|
12844
13052
|
exports.encodeProviderItem = encodeProviderItem;
|
|
@@ -12951,6 +13159,7 @@ exports.mapAspectRatio = mapAspectRatio;
|
|
|
12951
13159
|
exports.mapQuality = mapQuality;
|
|
12952
13160
|
exports.mapShotIntentToProviderDirectives = mapShotIntentToProviderDirectives;
|
|
12953
13161
|
exports.matchVariant = matchVariant;
|
|
13162
|
+
exports.mergeClipLook = mergeClipLook;
|
|
12954
13163
|
exports.mergeExposedSettings = mergeExposedSettings;
|
|
12955
13164
|
exports.migrateEdgeOutputMode = migrateEdgeOutputMode;
|
|
12956
13165
|
exports.migrateToItems = migrateToItems;
|
|
@@ -13022,6 +13231,7 @@ exports.resolveVideoProviderForMode = resolveVideoProviderForMode;
|
|
|
13022
13231
|
exports.resolveXfadeName = resolveXfadeName;
|
|
13023
13232
|
exports.rewriteSceneBindings = rewriteSceneBindings;
|
|
13024
13233
|
exports.rewriteSlotTokens = rewriteSlotTokens;
|
|
13234
|
+
exports.rewriteSpeakerSlots = rewriteSpeakerSlots;
|
|
13025
13235
|
exports.rgbaArrayToHex = rgbaArrayToHex;
|
|
13026
13236
|
exports.roleToPhrase = roleToPhrase;
|
|
13027
13237
|
exports.runSelector = runSelector;
|
|
@@ -13047,6 +13257,7 @@ exports.stringifyPathResults = stringifyPathResults;
|
|
|
13047
13257
|
exports.stripExportContent = stripExportContent;
|
|
13048
13258
|
exports.stripTransientRuntimeData = stripTransientRuntimeData;
|
|
13049
13259
|
exports.supportedDefaultDimensions = supportedDefaultDimensions;
|
|
13260
|
+
exports.supportsAdvancedMode = supportsAdvancedMode;
|
|
13050
13261
|
exports.toConnectedReference = toConnectedReference;
|
|
13051
13262
|
exports.toConnectedReferences = toConnectedReferences;
|
|
13052
13263
|
exports.togglePick = togglePick;
|