@vosjs/cli 0.21.0 → 0.23.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 +1 -1
- package/dist/{chunk-4IWHZBIY.js → chunk-SLCQCRNI.js} +101 -18
- package/dist/chunk-SLCQCRNI.js.map +1 -0
- package/dist/cli.js +1 -1
- package/dist/index.js +1 -1
- package/dist/{run-7AOC4JUE.js → run-UU4WYO4C.js} +2 -2
- package/package.json +5 -5
- package/schema/doc.schema.json +4 -0
- package/dist/chunk-4IWHZBIY.js.map +0 -1
- /package/dist/{run-7AOC4JUE.js.map → run-UU4WYO4C.js.map} +0 -0
package/README.md
CHANGED
|
@@ -91,7 +91,7 @@ vos plan take --reuse # re-time that cut onto
|
|
|
91
91
|
|
|
92
92
|
**A series shares its look by data.** `vos plan <take> --style <seed doc.json | vosId>` copies the seed's `zoomStyle`, `zoomParams`, `speedParams`, `tiltStyle`, `frame`, `cursor`, `cam` and `export` onto a new take (never its spans, overlays or audio) and re-plans the automatic spans under them. A seed that is a POSTER carries its layout too: its card placement, its `stage-*` clips by id (with the release's words from LAUNCH.md's `headline` and `kicker` roles, BRAND.md's `wordmark`, or `--headline` and `--kicker` patched in; the brand's mark from BRAND.md `logoUrl` fetched into `<take>/brand/` for `stage-mark`), its rest lean, its trailing freeze. What could not follow is said in words.
|
|
93
93
|
|
|
94
|
-
**The cut's motion is the document's, in one vocabulary.** Every visual thing carries `anim` (`enter`, `exit`, `idle`; a kind, or a step with its seconds and, for words, `unit`, `direction`, `stagger`): the card (`frame.anim`, entering by `tilt-in`, `pull-out`, `rise` or `fade` and leaving by `recede` or `fade`), a text, image or video clip, a prop. The output lasts until the last clip ends;
|
|
94
|
+
**The cut's motion is the document's, in one vocabulary.** Every visual thing carries `anim` (`enter`, `exit`, `idle`; a kind, or a step with its seconds and, for words, `unit`, `direction`, `stagger`): the card (`frame.anim`, entering by `tilt-in`, `pull-out`, `rise` or `fade` and leaving by `recede` or `fade`), a text, image or video clip, a prop. The output lasts until the last clip ends; the card is on screen exactly while its clip runs (the footage, freezes included) and leaves at its end, so its `exit` plays over the clip's last seconds like every clip's; past the footage the clips play over the ground alone. A FREEZE (`freeze: [{id, at, seconds}]`, a source moment held for output seconds, beside the speed spans on the retime lane) freezes the footage anywhere, the middle of the take included; a segment's older `hold` is read as a freeze at its end. There is no end-card field and no entrance field. A COMPONENT is a TEMPLATE: a plain take on a shelf whose clips carry stable ids, laid onto a take at an anchor and stamped `from` with where its clips came from. A fresh `vos plan` proposes the card's enter, the templates the recipe names (`LAUNCH.md` `with: <ref>[@end|@start|@step:<id>|@<seconds>], …` or `--with`, repeatable; `endCard: <ref>` names one at the end), the END CARD when `endCard` is on or absent: the official `End card` template on vos.so (the platform's `Templates` project, promoted; any official template resolves by its title, `--with "End card"`, `--style "Split cover, landscape"`), laid at the end with the release's words and BRAND.md's mark, stamped `from: endcard`; offline, the house clips stand in (a freeze of the last frame for the card's seconds, the card receding over it, the headline, the release line, the wordmark and the mark as clips over the freeze), a caption per `actions.json` step, a music bed and click sounds, from LAUNCH.md's `entrance`, `endCard`, `with`, `captions`, `music` and `clicks` roles (or the flags), as data in doc.json with stable ids (`bed`, `click-<n>`, `caption-<step>`), so the studio shows what the kit will render; a refresh never re-proposes (a deleted clip stays deleted) and `--motion` re-proposes on purpose, replacing only its own work. A document written in the older spellings (`frame.entrance`, `endCard`, a clip's `enter`/`exit`/`fx`, a prop's `animation`) is read into the vocabulary and `vos validate` says so.
|
|
95
95
|
|
|
96
96
|
**A fresh take opens on a backdrop.** `create`, `record` and `plan` put the first ready loop from `GET /api/backdrops` behind the card (its ground colour as `frame.background`); `--background <slug|url|none>` overrides it, and offline the frame stays bare with a note.
|
|
97
97
|
|
|
@@ -9,7 +9,11 @@ import {
|
|
|
9
9
|
import { mkdir as mkdir9, readFile as readFile14, rm as rm4 } from "fs/promises";
|
|
10
10
|
import { existsSync as existsSync14 } from "fs";
|
|
11
11
|
import { join as join17, resolve as resolve9 } from "path";
|
|
12
|
-
import {
|
|
12
|
+
import {
|
|
13
|
+
END_CARD_FROM as END_CARD_FROM2,
|
|
14
|
+
docOutputDuration as docOutputDuration3,
|
|
15
|
+
migrateHostedDoc as migrateHostedDoc5
|
|
16
|
+
} from "@vosjs/studio-core";
|
|
13
17
|
|
|
14
18
|
// src/plugin/args.ts
|
|
15
19
|
var UsageError = class extends Error {
|
|
@@ -3429,7 +3433,6 @@ function pickMoments(measured, opts = {}) {
|
|
|
3429
3433
|
// src/plugin/motionPlan.ts
|
|
3430
3434
|
import {
|
|
3431
3435
|
END_CARD_FROM,
|
|
3432
|
-
END_CARD_RECEDE,
|
|
3433
3436
|
END_CARD_SECONDS,
|
|
3434
3437
|
applyTemplate,
|
|
3435
3438
|
docOutputDuration,
|
|
@@ -3519,9 +3522,15 @@ function proposeMotion(input, opts) {
|
|
|
3519
3522
|
if (Object.keys(rest).length) doc.frame.anim = rest;
|
|
3520
3523
|
else delete doc.frame.anim;
|
|
3521
3524
|
}
|
|
3525
|
+
const endCardRole = launch.endCard;
|
|
3526
|
+
doc = dropTemplate(doc, END_CARD_FROM);
|
|
3527
|
+
const templates = (opts.templates ?? []).filter(
|
|
3528
|
+
(t) => !(t.from === END_CARD_FROM && off(endCardRole))
|
|
3529
|
+
);
|
|
3530
|
+
const namedEndCard = templates.some((t) => t.from === END_CARD_FROM);
|
|
3522
3531
|
const tWords = templateWords(words);
|
|
3523
3532
|
const keys = opts.mark ? { "stage-mark": opts.mark.key, "endcard-markimg": opts.mark.key } : void 0;
|
|
3524
|
-
for (const t of
|
|
3533
|
+
for (const t of templates) {
|
|
3525
3534
|
const at2 = resolveAnchor(doc, t.at);
|
|
3526
3535
|
if (at2 === null) {
|
|
3527
3536
|
skipped.push(`${t.from}: ${anchorWord(t.at)} was not recorded`);
|
|
@@ -3537,15 +3546,13 @@ function proposeMotion(input, opts) {
|
|
|
3537
3546
|
notes.push(`${t.from} at ${anchorWord(t.at)}`);
|
|
3538
3547
|
for (const n of applied.notes) skipped.push(`${t.from}: ${n}`);
|
|
3539
3548
|
}
|
|
3540
|
-
const endCardRole = launch.endCard;
|
|
3541
|
-
doc = dropTemplate(doc, END_CARD_FROM);
|
|
3542
3549
|
if (off(endCardRole)) {
|
|
3543
3550
|
if (doc.frame.anim?.exit !== void 0) {
|
|
3544
3551
|
const { exit: _exit, ...rest } = doc.frame.anim;
|
|
3545
3552
|
if (Object.keys(rest).length) doc.frame.anim = rest;
|
|
3546
3553
|
else delete doc.frame.anim;
|
|
3547
3554
|
}
|
|
3548
|
-
} else if (onWord(endCardRole)) {
|
|
3555
|
+
} else if (onWord(endCardRole) && !namedEndCard) {
|
|
3549
3556
|
const headline = (words.headline ?? "").trim();
|
|
3550
3557
|
const brand = (words.brand ?? "").trim();
|
|
3551
3558
|
const sub = [brand, (words.release ?? "").trim()].filter(Boolean).join(" ");
|
|
@@ -3556,13 +3563,32 @@ function proposeMotion(input, opts) {
|
|
|
3556
3563
|
if (sub && sub !== headline) card.sub = sub;
|
|
3557
3564
|
if (brand) card.wordmark = brand;
|
|
3558
3565
|
if (opts.mark) card.mark = opts.mark;
|
|
3566
|
+
const endStart = outputLength(doc);
|
|
3567
|
+
const lastSeg = doc.segments.at(-1);
|
|
3568
|
+
if (lastSeg) {
|
|
3569
|
+
const own = (doc.freeze ?? []).filter(
|
|
3570
|
+
(f) => Math.abs(f.at - lastSeg.out) > 1e-9
|
|
3571
|
+
);
|
|
3572
|
+
const taken = new Set(own.map((f) => f.id));
|
|
3573
|
+
let n = 0;
|
|
3574
|
+
while (taken.has(`f${n}`)) n++;
|
|
3575
|
+
doc.freeze = [
|
|
3576
|
+
...own,
|
|
3577
|
+
{
|
|
3578
|
+
id: `f${n}`,
|
|
3579
|
+
at: lastSeg.out,
|
|
3580
|
+
seconds: END_CARD_SECONDS,
|
|
3581
|
+
from: END_CARD_FROM
|
|
3582
|
+
}
|
|
3583
|
+
].sort((a, b) => a.at - b.at);
|
|
3584
|
+
}
|
|
3559
3585
|
doc.overlays = [
|
|
3560
3586
|
...doc.overlays ?? [],
|
|
3561
|
-
...endCardClips(card, doc,
|
|
3587
|
+
...endCardClips(card, doc, endStart)
|
|
3562
3588
|
];
|
|
3563
3589
|
doc.frame.anim = {
|
|
3564
3590
|
...doc.frame.anim ?? {},
|
|
3565
|
-
exit: { kind: "recede", seconds:
|
|
3591
|
+
exit: { kind: "recede", seconds: END_CARD_SECONDS }
|
|
3566
3592
|
};
|
|
3567
3593
|
notes.push("end card");
|
|
3568
3594
|
} else {
|
|
@@ -3623,7 +3649,7 @@ function proposeMotion(input, opts) {
|
|
|
3623
3649
|
fadeIn: 0.6,
|
|
3624
3650
|
fadeOut,
|
|
3625
3651
|
loop: track.duration < length,
|
|
3626
|
-
loopLen: track.duration < length ? length : void 0,
|
|
3652
|
+
loopLen: track.duration < length ? Math.round(length * 1e3) / 1e3 : void 0,
|
|
3627
3653
|
duck: hasMic
|
|
3628
3654
|
});
|
|
3629
3655
|
notes.push(`bed ${track.slug}`);
|
|
@@ -3679,6 +3705,11 @@ function destinationMechanics(d, doc) {
|
|
|
3679
3705
|
notes.push(loop ? "no template clips, no captions" : "no captions");
|
|
3680
3706
|
}
|
|
3681
3707
|
}
|
|
3708
|
+
if (loop) {
|
|
3709
|
+
const keptFreezes = (doc.freeze ?? []).filter((f) => !f.from);
|
|
3710
|
+
if (keptFreezes.length !== (doc.freeze ?? []).length)
|
|
3711
|
+
set.push(`freeze=${JSON.stringify(keptFreezes)}`);
|
|
3712
|
+
}
|
|
3682
3713
|
if (portrait) {
|
|
3683
3714
|
set.push("frame.fit=cover");
|
|
3684
3715
|
set.push('frame.inset={"left":0.06,"right":0.06,"top":0.17,"bottom":0.17}');
|
|
@@ -3709,11 +3740,13 @@ function isLightHexGround(hex2) {
|
|
|
3709
3740
|
import { existsSync as existsSync5 } from "fs";
|
|
3710
3741
|
import { readFile as readFile5 } from "fs/promises";
|
|
3711
3742
|
import { isAbsolute, join as join6, resolve as resolve3 } from "path";
|
|
3743
|
+
import { totalDuration } from "@vosjs/timeline";
|
|
3712
3744
|
import {
|
|
3713
3745
|
computeCardLayout,
|
|
3714
3746
|
docRestTime,
|
|
3715
3747
|
migrateHostedDoc,
|
|
3716
3748
|
overlayRect,
|
|
3749
|
+
ratedSegments as ratedSegments5,
|
|
3717
3750
|
resolveOverlayStyle
|
|
3718
3751
|
} from "@vosjs/studio-core";
|
|
3719
3752
|
|
|
@@ -3955,7 +3988,9 @@ async function findPosterDocs(takeDir, launchRoles) {
|
|
|
3955
3988
|
function posterStillTime(doc, duration) {
|
|
3956
3989
|
const rest = docRestTime(doc);
|
|
3957
3990
|
if (rest != null) return rest;
|
|
3958
|
-
|
|
3991
|
+
const footage = totalDuration(ratedSegments5(doc));
|
|
3992
|
+
const end = footage > 0 ? Math.min(duration, footage) : duration;
|
|
3993
|
+
return Math.max(0, end - 1 / 30);
|
|
3959
3994
|
}
|
|
3960
3995
|
var DESIGN_H = 1080;
|
|
3961
3996
|
function designFrame(px) {
|
|
@@ -4545,6 +4580,21 @@ async function fetchBrandMarks(takeDir, roles) {
|
|
|
4545
4580
|
return out;
|
|
4546
4581
|
}
|
|
4547
4582
|
|
|
4583
|
+
// src/plugin/officialTemplates.ts
|
|
4584
|
+
var OFFICIAL_END_CARD_TITLE = "End card";
|
|
4585
|
+
function templateKey(s) {
|
|
4586
|
+
return s.toLowerCase().replace(/[^a-z0-9]+/g, "");
|
|
4587
|
+
}
|
|
4588
|
+
function findOfficialByRef(rows, ref) {
|
|
4589
|
+
const key = templateKey(ref);
|
|
4590
|
+
if (!key) return null;
|
|
4591
|
+
for (const row of rows) {
|
|
4592
|
+
if (templateKey(row.title) === key) return row;
|
|
4593
|
+
if (row.slug && templateKey(row.slug) === key) return row;
|
|
4594
|
+
}
|
|
4595
|
+
return null;
|
|
4596
|
+
}
|
|
4597
|
+
|
|
4548
4598
|
// src/plugin/music.ts
|
|
4549
4599
|
async function fetchMusicCatalog(origin) {
|
|
4550
4600
|
const base = origin.replace(/\/+$/, "");
|
|
@@ -8924,17 +8974,18 @@ patched in from LAUNCH.md's headline and kicker roles or --headline and
|
|
|
8924
8974
|
The cut's MOTION is the document's too, in ONE vocabulary: every visual
|
|
8925
8975
|
thing (the card, a text, image or video clip, a prop) carries anim.enter,
|
|
8926
8976
|
anim.exit and anim.idle; the output lasts until the last clip ends, and
|
|
8927
|
-
|
|
8928
|
-
|
|
8977
|
+
the card leaves with its clip (a freeze keeps it under what follows).
|
|
8978
|
+
There is no end-card field and no entrance field: a
|
|
8929
8979
|
COMPONENT is a TEMPLATE, a plain take on a shelf whose clips carry stable
|
|
8930
8980
|
ids, laid onto the take at an anchor (LAUNCH.md with: <ref>[@end|@start|
|
|
8931
8981
|
@step:<id>|@<s>], comma list; --with the same, repeatable; endCard: <ref>
|
|
8932
8982
|
names one at the end), its clips stamped with where they came from
|
|
8933
8983
|
(from). plan proposes on a fresh plan (--motion re-proposes, replacing
|
|
8934
8984
|
only its own work): the card's ENTER (tilt-in by default), the templates
|
|
8935
|
-
named, the
|
|
8936
|
-
|
|
8937
|
-
BRAND.md logoUrl
|
|
8985
|
+
named, the END CARD when endCard is on or absent (the official End card
|
|
8986
|
+
template on vos.so, laid at the end with the release's words, the mark
|
|
8987
|
+
from BRAND.md logoUrl and a freeze of the last frame under it, from:
|
|
8988
|
+
endcard; offline, the house clips stand in), a CAPTION per
|
|
8938
8989
|
actions.json step at the step's moment, a music BED from LAUNCH.md's music
|
|
8939
8990
|
role (a catalog slug or a mood) and a click sound on every press when the
|
|
8940
8991
|
take has no mic. LAUNCH.md's entrance, endCard, captions, music and clicks
|
|
@@ -9413,6 +9464,24 @@ async function releaseInputs(dir, flags, r, withRefs = []) {
|
|
|
9413
9464
|
templates.push({ from: found.from, doc: found.doc, at: at2 });
|
|
9414
9465
|
r.log(`template: ${found.from}`);
|
|
9415
9466
|
}
|
|
9467
|
+
if (!endCardRole || /^(on|yes|true)$/i.test(endCardRole.trim())) {
|
|
9468
|
+
const origin = platformOrigin({
|
|
9469
|
+
origin: strFlag(flags, "origin"),
|
|
9470
|
+
api: strFlag(flags, "api")
|
|
9471
|
+
});
|
|
9472
|
+
const official = await officialTemplate(origin, OFFICIAL_END_CARD_TITLE);
|
|
9473
|
+
if (official) {
|
|
9474
|
+
try {
|
|
9475
|
+
const found = await resolveDocRef(official.id, flags, "endCard");
|
|
9476
|
+
templates.push({ from: END_CARD_FROM2, doc: found.doc, at: "end" });
|
|
9477
|
+
r.log(`template: ${found.from} (the official end card)`);
|
|
9478
|
+
} catch {
|
|
9479
|
+
r.log(
|
|
9480
|
+
"end card: the official template is unreadable here; the house clips stand in"
|
|
9481
|
+
);
|
|
9482
|
+
}
|
|
9483
|
+
}
|
|
9484
|
+
}
|
|
9416
9485
|
return {
|
|
9417
9486
|
words,
|
|
9418
9487
|
launchRoles,
|
|
@@ -9703,10 +9772,14 @@ async function resolveDocRef(ref, flags, what) {
|
|
|
9703
9772
|
const key = resolveCredential(strFlag(flags, "key"));
|
|
9704
9773
|
const meta = await apiJson(origin, `/api/vos/${styleRef}`, { key });
|
|
9705
9774
|
const head = meta.body.vos?.currentVersionId;
|
|
9706
|
-
if (meta.status !== 200 || !head)
|
|
9775
|
+
if (meta.status !== 200 || !head) {
|
|
9776
|
+
const official = await officialTemplate(origin, styleRef);
|
|
9777
|
+
if (official && official.id !== styleRef)
|
|
9778
|
+
return resolveDocRef(official.id, flags, what);
|
|
9707
9779
|
throw new UsageError(
|
|
9708
|
-
`${what}: ${styleRef} is neither a doc.json
|
|
9780
|
+
`${what}: ${styleRef} is neither a doc.json, a vos I can read, nor an official template's title`
|
|
9709
9781
|
);
|
|
9782
|
+
}
|
|
9710
9783
|
const doc = await apiJson(
|
|
9711
9784
|
origin,
|
|
9712
9785
|
`/api/vos/${styleRef}/versions/${head}/doc`,
|
|
@@ -9719,6 +9792,16 @@ async function resolveDocRef(ref, flags, what) {
|
|
|
9719
9792
|
doc: migrateHostedDoc5(doc.body)
|
|
9720
9793
|
};
|
|
9721
9794
|
}
|
|
9795
|
+
async function officialTemplate(origin, ref) {
|
|
9796
|
+
try {
|
|
9797
|
+
const list = await apiJson(origin, "/api/vos/official", {});
|
|
9798
|
+
if (list.status !== 200) return null;
|
|
9799
|
+
const rows = list.body.voses ?? [];
|
|
9800
|
+
return findOfficialByRef(rows, ref);
|
|
9801
|
+
} catch {
|
|
9802
|
+
return null;
|
|
9803
|
+
}
|
|
9804
|
+
}
|
|
9722
9805
|
async function cmdDigest(argv) {
|
|
9723
9806
|
const { positionals, flags } = parseArgs(argv, BOOLEAN_FLAGS5);
|
|
9724
9807
|
const dir = positionals[0];
|
|
@@ -10137,4 +10220,4 @@ export {
|
|
|
10137
10220
|
convertAgentBrowser,
|
|
10138
10221
|
run
|
|
10139
10222
|
};
|
|
10140
|
-
//# sourceMappingURL=chunk-
|
|
10223
|
+
//# sourceMappingURL=chunk-SLCQCRNI.js.map
|