@koda-sl/baker-cli 0.295.0-dev.f941e60fb → 0.297.0-dev.306c0952c

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/cli.js CHANGED
@@ -54,6 +54,7 @@ import {
54
54
  sha256Hex,
55
55
  spineInputFlags,
56
56
  spineInputOps,
57
+ splitScriptFor,
57
58
  spokenWordsIn,
58
59
  supportsLastFrame,
59
60
  supportsParam,
@@ -62,7 +63,7 @@ import {
62
63
  ulid,
63
64
  validateCanvasDeep,
64
65
  ytDlpBlockSignal
65
- } from "./chunk-ER3KCAW3.js";
66
+ } from "./chunk-YMHERSTK.js";
66
67
  import {
67
68
  csvOrJson,
68
69
  daysAgoIso,
@@ -38222,6 +38223,15 @@ var scaffoldStaticAdCommand = defineCommand110({
38222
38223
  }
38223
38224
  });
38224
38225
 
38226
+ // src/commands/studio/avatarModel.ts
38227
+ function pinnedModelHint(input) {
38228
+ if (!input.avatar || !input.model) return null;
38229
+ if (input.model === DEFAULT_VIDEO_GENERATE_MODEL) return null;
38230
+ const speaks = input.generateAudio ?? true;
38231
+ if (!speaks) return null;
38232
+ return `You pinned --model ${input.model} on a speaking avatar. Without it this clip routes to ${DEFAULT_VIDEO_GENERATE_MODEL}, which was chosen for exactly this case: the better picture, both orientations, and audio always on. Drop --model unless the user asked for a specific one \u2014 and note ${input.model} may not take the length or the reference you are counting on.`;
38233
+ }
38234
+
38225
38235
  // src/commands/canvas/scaffold-ad.ts
38226
38236
  import { copyFile, cp, mkdir as mkdir7, readFile as readFile16, stat as stat4, writeFile as writeFile9 } from "fs/promises";
38227
38237
  import path24 from "path";
@@ -38953,6 +38963,10 @@ function resolveShippedCanvasDir(name, startDir, exists2 = existsSync5, maxDepth
38953
38963
  }
38954
38964
 
38955
38965
  // src/commands/canvas/scaffold-ad.ts
38966
+ function pinnedOnAvatar(handle, model) {
38967
+ const hint = pinnedModelHint({ avatar: handle ?? void 0, model, generateAudio: true });
38968
+ return hint ? [hint] : [];
38969
+ }
38956
38970
  var AD_IMAGE_MODEL = "openai/gpt-image-2.5-sunburst";
38957
38971
  var SHIPPED_COMPOSITION_DIR = resolveShippedCanvasDir("video-overlay-composition", import.meta.dirname);
38958
38972
  var SHIPPED_CAPTIONS_DIR = resolveShippedCanvasDir("tiktok-captions-composition", import.meta.dirname);
@@ -39275,6 +39289,10 @@ var scaffoldAdCommand = defineCommand111({
39275
39289
  ok: true,
39276
39290
  data: { canvas: outPath, beats: spec.data.beats.length, slug },
39277
39291
  hints: [
39292
+ // Same reasoning as `studio animate`: a cast avatar that speaks routes to the
39293
+ // default model on purpose, and pinning one discards that silently. An ad's
39294
+ // beats always speak, so there is no silent case to exclude here.
39295
+ ...pinnedOnAvatar(handle, requestedModel),
39278
39296
  // `--until frames` on anything carrying a face or a mark, because that is where
39279
39297
  // getting it wrong is expensive: the pictures cost a fraction of the clips, and
39280
39298
  // every defect anyone has caught in a finished ad was visible in them. The run
@@ -57381,15 +57399,6 @@ import { defineCommand as defineCommand208 } from "citty";
57381
57399
  // src/commands/studio/animate.ts
57382
57400
  import { defineCommand as defineCommand200 } from "citty";
57383
57401
 
57384
- // src/commands/studio/avatarModel.ts
57385
- function pinnedModelHint(input) {
57386
- if (!input.avatar || !input.model) return null;
57387
- if (input.model === DEFAULT_VIDEO_GENERATE_MODEL) return null;
57388
- const speaks = input.generateAudio ?? true;
57389
- if (!speaks) return null;
57390
- return `You pinned --model ${input.model} on a speaking avatar. Without it this clip routes to ${DEFAULT_VIDEO_GENERATE_MODEL}, which was chosen for exactly this case: the better picture, both orientations, and audio always on. Drop --model unless the user asked for a specific one \u2014 and note ${input.model} may not take the length or the reference you are counting on.`;
57391
- }
57392
-
57393
57402
  // src/commands/studio/batch.ts
57394
57403
  function projectBatch(generation, full) {
57395
57404
  const compact3 = {
@@ -57610,13 +57619,18 @@ var spokenWords = spokenWordsIn;
57610
57619
  function wordsThatFit(seconds) {
57611
57620
  return Math.floor(seconds * WORDS_PER_SECOND2);
57612
57621
  }
57613
- function spokenPaceHint(prompt, seconds) {
57614
- if (!seconds) return null;
57622
+ function spokenPaceHint(prompt, seconds, model) {
57615
57623
  const words2 = spokenWords(prompt);
57624
+ if (words2 === 0) return null;
57625
+ const split = model ? splitScriptFor(words2, model) : null;
57626
+ if (split) {
57627
+ return `The line is ${words2} words \u2014 about ${Math.ceil(words2 / WORDS_PER_SECOND2)}s of speech \u2014 and ${model} renders at most ${split.maxSeconds}s (~${Math.floor(split.maxSeconds * WORDS_PER_SECOND2)} words). It will not refuse and it will not cut: it will RUSH, swallowing syllables. Split the script into ${split.clips} takes of roughly ${split.wordsPerClip} words each, rendered separately and cut together \u2014 break it where the thought breaks, not mid-sentence. Or move to a model that renders longer (bytedance/seedance-2.5 goes to 30s).`;
57628
+ }
57629
+ if (!seconds) return null;
57616
57630
  const fits = wordsThatFit(seconds);
57617
- if (words2 === 0 || words2 <= fits) return null;
57631
+ if (words2 <= fits) return null;
57618
57632
  const rate = (words2 / seconds).toFixed(1);
57619
- return `The line is ${words2} words and this clip is ${seconds}s \u2014 that is ${rate} words a second against a natural 2.5. The model will not run over, it will RUSH: syllables swallowed, endings clipped, and the take unusable however good the picture. Cut the line to about ${fits} words, or give it the length it needs (${Math.ceil(words2 / 2.5)}s). Do not re-render the same line at the same length and hope.`;
57633
+ return `The line is ${words2} words and this clip is ${seconds}s \u2014 that is ${rate} words a second against a natural ${WORDS_PER_SECOND2}. The model will not run over, it will RUSH: syllables swallowed, endings clipped, and the take unusable however good the picture. Cut the line to about ${fits} words, or give it the length it needs (${Math.ceil(words2 / WORDS_PER_SECOND2)}s). Do not re-render the same line at the same length and hope.`;
57620
57634
  }
57621
57635
 
57622
57636
  // src/commands/studio/animate.ts
@@ -57849,7 +57863,7 @@ function costHintsFor(body) {
57849
57863
  ];
57850
57864
  const pinned = pinnedModelHint({ avatar: body.avatar, model: body.model, generateAudio: body.generateAudio });
57851
57865
  if (pinned) hints2.push(pinned);
57852
- const pace = spokenPaceHint(body.prompt, body.duration);
57866
+ const pace = spokenPaceHint(body.prompt, body.duration, model);
57853
57867
  if (pace) hints2.push(pace);
57854
57868
  if (count > 1) {
57855
57869
  hints2.push(