@koda-sl/baker-cli 0.270.0-dev.1f1c09c80 → 0.271.0-dev.1f1c09c80
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
|
@@ -58,7 +58,7 @@ import {
|
|
|
58
58
|
ulid,
|
|
59
59
|
validateCanvasDeep,
|
|
60
60
|
ytDlpBlockSignal
|
|
61
|
-
} from "./chunk-
|
|
61
|
+
} from "./chunk-4IJX4R4J.js";
|
|
62
62
|
import {
|
|
63
63
|
csvOrJson,
|
|
64
64
|
daysAgoIso,
|
|
@@ -27320,6 +27320,17 @@ var RecurringElement = z27.object({
|
|
|
27320
27320
|
* slot emits a TODO for somebody to drop one by hand.
|
|
27321
27321
|
*/
|
|
27322
27322
|
source_image: z27.string().optional(),
|
|
27323
|
+
/**
|
|
27324
|
+
* The supplied picture is ALREADY a multi-view reference sheet, so no sheet is
|
|
27325
|
+
* built from it.
|
|
27326
|
+
*
|
|
27327
|
+
* A cast Avatar's identity sheet is exactly that: a rendered turnaround of one
|
|
27328
|
+
* person, which is what `buildElementSheets` exists to produce. Fanning it into a
|
|
27329
|
+
* second sheet paid for an image generation to make a generation of a generation,
|
|
27330
|
+
* and every frame then grounded on the copy rather than on the avatar's own
|
|
27331
|
+
* identity — which is the drift the sheet is supposed to prevent.
|
|
27332
|
+
*/
|
|
27333
|
+
source_is_sheet: z27.boolean().optional(),
|
|
27323
27334
|
// Scenes the element appears in. Either a bare list of scene indices (both
|
|
27324
27335
|
// edges) or per-{scene,edge} entries. Both forms are accepted and merged.
|
|
27325
27336
|
scenes: z27.array(z27.number()).optional(),
|
|
@@ -27493,6 +27504,7 @@ function buildElementSlots(elements) {
|
|
|
27493
27504
|
description: el.description,
|
|
27494
27505
|
sameAs: el.same_as ?? void 0,
|
|
27495
27506
|
castId: el.cast_id ?? void 0,
|
|
27507
|
+
sourceIsSheet: el.source_is_sheet === true,
|
|
27496
27508
|
presence: presenceOf(el)
|
|
27497
27509
|
});
|
|
27498
27510
|
});
|
|
@@ -27536,8 +27548,12 @@ var SHEET_SUBJECT_TYPE = {
|
|
|
27536
27548
|
product: "product",
|
|
27537
27549
|
location: "location"
|
|
27538
27550
|
};
|
|
27539
|
-
function buildElementSheets(slots, nodes) {
|
|
27551
|
+
function buildElementSheets(slots, nodes, sheetModel) {
|
|
27540
27552
|
for (const slot of slots) {
|
|
27553
|
+
if (slot.sourceIsSheet) {
|
|
27554
|
+
slot.sheetBacked = true;
|
|
27555
|
+
continue;
|
|
27556
|
+
}
|
|
27541
27557
|
const subjectType = SHEET_SUBJECT_TYPE[slot.type.toLowerCase()];
|
|
27542
27558
|
if (!subjectType) continue;
|
|
27543
27559
|
if (slot.sameAs) continue;
|
|
@@ -27551,7 +27567,7 @@ function buildElementSheets(slots, nodes) {
|
|
|
27551
27567
|
// The lone dropped ingest is the source; the sheet fans it into a turnaround.
|
|
27552
27568
|
inputs: { references: [slot.ref] },
|
|
27553
27569
|
params: {
|
|
27554
|
-
model: ACTOR_SHEET_MODEL,
|
|
27570
|
+
model: sheetModel ?? ACTOR_SHEET_MODEL,
|
|
27555
27571
|
// The clean-plate clause mirrors the frame prompts' CLEAN PLATE block: a sheet
|
|
27556
27572
|
// that comes back with a fake camera app baked in (P2-22) poisons EVERY frame
|
|
27557
27573
|
// grounded on it, so the suppression must live on the sheet too.
|
|
@@ -29616,7 +29632,7 @@ function scaffoldVideoCanvas(input, elementsInput, opts) {
|
|
|
29616
29632
|
}
|
|
29617
29633
|
});
|
|
29618
29634
|
});
|
|
29619
|
-
buildElementSheets(slots, nodes);
|
|
29635
|
+
buildElementSheets(slots, nodes, opts.sheetModel);
|
|
29620
29636
|
const { clips, voTracks, vo_segments, talking_scenes, routes } = buildTimeline(blueprint, slots, opts, nodes);
|
|
29621
29637
|
const outAspect = resolveAspect(blueprint.source?.aspect_ratio, opts.aspect, genAspectsFor(opts.videoModel));
|
|
29622
29638
|
let videoRef = buildSpine(clips, opts.seamDedup ?? "head", canvasDims(outAspect.outAr), nodes);
|
|
@@ -32658,7 +32674,18 @@ function adSpecCastElements(spec, avatar) {
|
|
|
32658
32674
|
const scenes = spec.beats.map((beat, i) => ({ beat, i })).filter(({ beat, i }) => beat.cast !== false && !(endCardWanted(spec) && i === lastIndex)).map(({ i }) => i);
|
|
32659
32675
|
if (scenes.length === 0) return [];
|
|
32660
32676
|
const sheet = avatar?.sheetUrl?.trim();
|
|
32661
|
-
return [
|
|
32677
|
+
return [
|
|
32678
|
+
{
|
|
32679
|
+
type: "person",
|
|
32680
|
+
label: "customer",
|
|
32681
|
+
description,
|
|
32682
|
+
scenes,
|
|
32683
|
+
// An avatar's identity sheet IS the turnaround the engine would otherwise render,
|
|
32684
|
+
// so it is handed over as one. Left unmarked, the run paid to generate a sheet OF
|
|
32685
|
+
// a sheet and every frame grounded on the copy instead of the avatar.
|
|
32686
|
+
...sheet ? { source_image: sheet, source_is_sheet: true } : {}
|
|
32687
|
+
}
|
|
32688
|
+
];
|
|
32662
32689
|
}
|
|
32663
32690
|
function adSpecToBlueprint(spec) {
|
|
32664
32691
|
const total = spec.beats.length;
|
|
@@ -32805,26 +32832,34 @@ function adBrandOverlayCss() {
|
|
|
32805
32832
|
" viewBox is wider than it is tall rendered clipped \u2014 the first real ad drew",
|
|
32806
32833
|
" `greenlea`. Constraining the height and letting the width follow keeps any",
|
|
32807
32834
|
" aspect ratio whole, and the max-width stops a very wide mark spanning the frame. */",
|
|
32808
|
-
" /*
|
|
32809
|
-
"
|
|
32810
|
-
"
|
|
32811
|
-
"
|
|
32812
|
-
"
|
|
32813
|
-
"
|
|
32814
|
-
"
|
|
32835
|
+
" /* A LIGHT panel behind the mark, always. A brand mark is drawn to sit on white",
|
|
32836
|
+
" \u2014 this one is #1D5D47 on transparent \u2014 so a dark backing hid it on the corner",
|
|
32837
|
+
" and the brand-coloured closing plate erased it completely: the end card came",
|
|
32838
|
+
" out as an empty green frame with a single leaf floating in it. A near-white",
|
|
32839
|
+
" panel is the one background every mark in a brand kit is designed against. */",
|
|
32840
|
+
" .brand-mark { position: absolute; top: 90px; left: 56px; height: 52px; width: auto;",
|
|
32841
|
+
" max-width: 420px; object-fit: contain; padding: 14px 22px; border-radius: 18px;",
|
|
32842
|
+
" background: rgba(255,255,255,0.94);",
|
|
32843
|
+
" filter: drop-shadow(0 3px 10px rgba(0,0,0,0.35)); }",
|
|
32815
32844
|
" .endcard { position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%);",
|
|
32816
32845
|
" display: flex; flex-direction: column; align-items: center; gap: 56px;",
|
|
32817
32846
|
" width: 100%; max-width: 1080px; padding: 0 60px; text-align: center; }",
|
|
32818
32847
|
" /* Sized to FILL, not merely bounded. Given only max-* constraints an SVG",
|
|
32819
32848
|
" renders at its own intrinsic size, and a small viewBox drew a closing mark",
|
|
32820
32849
|
" a few dozen pixels tall in the middle of a 1080px frame. */",
|
|
32821
|
-
" .endcard-logo { width: 62%; height: auto; max-height:
|
|
32850
|
+
" .endcard-logo { width: 62%; height: auto; max-height: 300px; object-fit: contain;",
|
|
32851
|
+
" padding: 46px 56px; border-radius: 40px; background: rgba(255,255,255,0.96);",
|
|
32852
|
+
" box-shadow: 0 10px 40px rgba(0,0,0,0.28); }",
|
|
32822
32853
|
" /* Capped and wrapping, not sized to its content: measured in a real browser,",
|
|
32823
32854
|
" `GET YOUR FREE QUOTE` renders 948px wide against a 1080px canvas, so the next",
|
|
32824
32855
|
" slightly longer call to action runs off the frame. Two lines beat an edge. */",
|
|
32825
|
-
" /*
|
|
32826
|
-
"
|
|
32827
|
-
"
|
|
32856
|
+
" /* The SAME family the caption track uses, and only names the compiler maps.",
|
|
32857
|
+
" Declaring one matters \u2014 rendered without it the call to action inherited the",
|
|
32858
|
+
" document default and came out in Times beside sans-serif captions. But the",
|
|
32859
|
+
" first attempt led with 'BrandFont', which the overlay composition defines and",
|
|
32860
|
+
" the caption composition does not: HyperFrames refuses an unmapped family and",
|
|
32861
|
+
" the whole render died with `No deterministic font mapping for: BrandFont`. */",
|
|
32862
|
+
" .endcard-cta { font-family: 'Arial Black', 'Helvetica Neue', Arial, sans-serif;",
|
|
32828
32863
|
" font-size: 64px; font-weight: 900; text-transform: uppercase;",
|
|
32829
32864
|
" letter-spacing: 0.01em; color: #fff; padding: 28px 58px; border-radius: 56px;",
|
|
32830
32865
|
" max-width: 900px; line-height: 1.15; background: rgba(0,0,0,0.82); }"
|
|
@@ -33031,6 +33066,9 @@ var scaffoldAdCommand = defineCommand104({
|
|
|
33031
33066
|
const logoPath = spec.data.brand?.logo?.trim();
|
|
33032
33067
|
const opts = {
|
|
33033
33068
|
imageModel: AD_IMAGE_MODEL,
|
|
33069
|
+
// The sheet on the SAME model as the frames it feeds. A sheet from another
|
|
33070
|
+
// generator hands every frame a look its own model then reinterprets.
|
|
33071
|
+
sheetModel: AD_IMAGE_MODEL,
|
|
33034
33072
|
videoModel: DEFAULT_VIDEO_GENERATE_MODEL,
|
|
33035
33073
|
overlayCompositionPath: path24.relative(outDir, compositionDest),
|
|
33036
33074
|
captionsCompositionPath: path24.relative(outDir, captionsDest),
|
|
@@ -33084,6 +33122,9 @@ var scaffoldAdCommand = defineCommand104({
|
|
|
33084
33122
|
...logoPath && !staged ? [`Could not read the logo at \`${logoPath}\` \u2014 the ad has no mark on screen. Give the path as it appears in src/brand/BRAND.md, relative to the workspace root.`] : [],
|
|
33085
33123
|
...staged ? ["The brand mark is drawn by the composition \u2014 top-left throughout, and large on the closing card. It is never generated as a picture, so it cannot come back garbled."] : [],
|
|
33086
33124
|
...endCardWanted(spec.data) ? ["The last beat closes on a flat brand plate with the call to action over it, instead of one more photograph."] : [],
|
|
33125
|
+
...spec.data.brand?.logo && !filledLogo && !spec.data.brand.logo.startsWith(`${BRAND_DIR}/`) ? [
|
|
33126
|
+
`The mark at \`${spec.data.brand.logo}\` is not in \`${BRAND_DIR}/logos/\`. If you drew it for this ad, it is not the client's logo and the ad is going out wearing something you invented \u2014 put the real one in the brand folder (\`/brand-build\`) and re-run. A hand-drawn wordmark also tends to overflow its own viewBox, which renders as a clipped word.`
|
|
33127
|
+
] : [],
|
|
33087
33128
|
...filledLogo || filledPalette ? [
|
|
33088
33129
|
`The spec left the brand empty, so it was read from the workspace: ${[filledLogo ? `logo \`${filledLogo}\`` : null, filledPalette ? `palette ${filledPalette.slice(0, 3).join(", ")}` : null].filter(Boolean).join(" and ")}. Pass them yourself when the ad needs a different mark or colour.`
|
|
33089
33130
|
] : [],
|