@docyrus/animaguru 0.19.0 → 0.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/README.md +61 -0
- package/dist/animaguru.js +366 -261
- package/package.json +1 -1
- package/skills/animaguru/SKILL.md +9 -1
- package/skills/animaguru/references/sharing.md +3 -0
- package/skills/animaguru-branching-story/SKILL.md +72 -0
- package/skills/animaguru-general-video/SKILL.md +1 -0
- package/skills/animaguru-general-video/references/branching-story.md +13 -0
- package/skills/animaguru-quiz/SKILL.md +80 -0
- package/skills/animaguru-video/SKILL.md +9 -1
- package/templates/video/AGENTS.md +2 -0
- package/templates/video/README.md +8 -0
- package/templates/video/WORKSPACE-AGENTS.md +3 -1
- package/templates/video/scripts/build.mjs +4 -1
- package/templates/video/scripts/export-mp4.mjs +2 -0
- package/templates/video/scripts/html-export.mjs +12 -4
- package/templates/video/scripts/project-context.mjs +20 -3
- package/templates/video/scripts/quiz.d.mts +31 -0
- package/templates/video/scripts/quiz.mjs +126 -0
- package/templates/video/scripts/review.mjs +9 -1
- package/templates/video/scripts/story.d.mts +36 -0
- package/templates/video/scripts/story.mjs +139 -0
- package/templates/video/scripts/workspace-plugin.mjs +9 -4
- package/templates/video/shared/locales/en/player.json +29 -1
- package/templates/video/src/kit/index.ts +1 -0
- package/templates/video/src/kit/story.ts +19 -0
- package/templates/video/src/main.ts +6 -1
- package/templates/video/src/player-types.ts +6 -0
- package/templates/video/src/player.ts +213 -17
- package/templates/video/src/quiz.ts +315 -0
- package/templates/video/src/story.ts +112 -0
- package/templates/video/src/style.css +106 -3
package/dist/animaguru.js
CHANGED
|
@@ -2312,8 +2312,8 @@ var require_lib = __commonJS({
|
|
|
2312
2312
|
if (typeof cb !== "function") {
|
|
2313
2313
|
opts = cb;
|
|
2314
2314
|
cb = null;
|
|
2315
|
-
deferred = new this.Promise(function(
|
|
2316
|
-
deferredResolve =
|
|
2315
|
+
deferred = new this.Promise(function(resolve18, reject) {
|
|
2316
|
+
deferredResolve = resolve18;
|
|
2317
2317
|
deferredReject = reject;
|
|
2318
2318
|
});
|
|
2319
2319
|
}
|
|
@@ -2461,17 +2461,17 @@ var require_lib = __commonJS({
|
|
|
2461
2461
|
if (typeof cb === "function") {
|
|
2462
2462
|
fnx(cb);
|
|
2463
2463
|
} else {
|
|
2464
|
-
return new this.Promise(function(
|
|
2464
|
+
return new this.Promise(function(resolve18, reject) {
|
|
2465
2465
|
if (fnx.length === 1) {
|
|
2466
2466
|
fnx(function(err2, ret) {
|
|
2467
2467
|
if (err2) {
|
|
2468
2468
|
reject(err2);
|
|
2469
2469
|
} else {
|
|
2470
|
-
|
|
2470
|
+
resolve18(ret);
|
|
2471
2471
|
}
|
|
2472
2472
|
});
|
|
2473
2473
|
} else {
|
|
2474
|
-
|
|
2474
|
+
resolve18(fnx());
|
|
2475
2475
|
}
|
|
2476
2476
|
});
|
|
2477
2477
|
}
|
|
@@ -6862,7 +6862,7 @@ var require_pify = __commonJS({
|
|
|
6862
6862
|
"use strict";
|
|
6863
6863
|
var processFn = (fn, options) => function(...args) {
|
|
6864
6864
|
const P = options.promiseModule;
|
|
6865
|
-
return new P((
|
|
6865
|
+
return new P((resolve18, reject) => {
|
|
6866
6866
|
if (options.multiArgs) {
|
|
6867
6867
|
args.push((...result) => {
|
|
6868
6868
|
if (options.errorFirst) {
|
|
@@ -6870,10 +6870,10 @@ var require_pify = __commonJS({
|
|
|
6870
6870
|
reject(result);
|
|
6871
6871
|
} else {
|
|
6872
6872
|
result.shift();
|
|
6873
|
-
|
|
6873
|
+
resolve18(result);
|
|
6874
6874
|
}
|
|
6875
6875
|
} else {
|
|
6876
|
-
|
|
6876
|
+
resolve18(result);
|
|
6877
6877
|
}
|
|
6878
6878
|
});
|
|
6879
6879
|
} else if (options.errorFirst) {
|
|
@@ -6881,11 +6881,11 @@ var require_pify = __commonJS({
|
|
|
6881
6881
|
if (error62) {
|
|
6882
6882
|
reject(error62);
|
|
6883
6883
|
} else {
|
|
6884
|
-
|
|
6884
|
+
resolve18(result);
|
|
6885
6885
|
}
|
|
6886
6886
|
});
|
|
6887
6887
|
} else {
|
|
6888
|
-
args.push(
|
|
6888
|
+
args.push(resolve18);
|
|
6889
6889
|
}
|
|
6890
6890
|
fn.apply(this, args);
|
|
6891
6891
|
});
|
|
@@ -11400,7 +11400,7 @@ ${obj.gpgsig ? obj.gpgsig : ""}`;
|
|
|
11400
11400
|
} catch (err22) {
|
|
11401
11401
|
if (err22 === null || err22.code === "EEXIST") return;
|
|
11402
11402
|
if (err22.code === "ENOENT" && attempt < retries) {
|
|
11403
|
-
await new Promise((
|
|
11403
|
+
await new Promise((resolve18) => setTimeout(resolve18, 0));
|
|
11404
11404
|
continue;
|
|
11405
11405
|
}
|
|
11406
11406
|
throw err22;
|
|
@@ -14616,9 +14616,9 @@ ${obj.gpgsig ? obj.gpgsig : ""}`;
|
|
|
14616
14616
|
throw Error("You cannot write to a FIFO that has already been ended!");
|
|
14617
14617
|
}
|
|
14618
14618
|
if (this._waiting) {
|
|
14619
|
-
const
|
|
14619
|
+
const resolve18 = this._waiting;
|
|
14620
14620
|
this._waiting = null;
|
|
14621
|
-
|
|
14621
|
+
resolve18({ value: chunk });
|
|
14622
14622
|
} else {
|
|
14623
14623
|
this._queue.push(chunk);
|
|
14624
14624
|
}
|
|
@@ -14626,9 +14626,9 @@ ${obj.gpgsig ? obj.gpgsig : ""}`;
|
|
|
14626
14626
|
end() {
|
|
14627
14627
|
this._ended = true;
|
|
14628
14628
|
if (this._waiting) {
|
|
14629
|
-
const
|
|
14629
|
+
const resolve18 = this._waiting;
|
|
14630
14630
|
this._waiting = null;
|
|
14631
|
-
|
|
14631
|
+
resolve18({ done: true });
|
|
14632
14632
|
}
|
|
14633
14633
|
}
|
|
14634
14634
|
destroy(err2) {
|
|
@@ -14647,8 +14647,8 @@ ${obj.gpgsig ? obj.gpgsig : ""}`;
|
|
|
14647
14647
|
"You cannot call read until the previous call to read has returned!"
|
|
14648
14648
|
);
|
|
14649
14649
|
}
|
|
14650
|
-
return new Promise((
|
|
14651
|
-
this._waiting =
|
|
14650
|
+
return new Promise((resolve18) => {
|
|
14651
|
+
this._waiting = resolve18;
|
|
14652
14652
|
});
|
|
14653
14653
|
}
|
|
14654
14654
|
};
|
|
@@ -14803,7 +14803,7 @@ ${obj.gpgsig ? obj.gpgsig : ""}`;
|
|
|
14803
14803
|
const acks = [];
|
|
14804
14804
|
let nak = false;
|
|
14805
14805
|
let done = false;
|
|
14806
|
-
return new Promise((
|
|
14806
|
+
return new Promise((resolve18, reject) => {
|
|
14807
14807
|
forAwait2(packetlines, (data) => {
|
|
14808
14808
|
const line = data.toString("utf8").trim();
|
|
14809
14809
|
if (line.startsWith("shallow")) {
|
|
@@ -14830,11 +14830,11 @@ ${obj.gpgsig ? obj.gpgsig : ""}`;
|
|
|
14830
14830
|
nak = true;
|
|
14831
14831
|
}
|
|
14832
14832
|
if (done) {
|
|
14833
|
-
stream.error ? reject(stream.error) :
|
|
14833
|
+
stream.error ? reject(stream.error) : resolve18({ shallows, unshallows, acks, nak, packfile, progress });
|
|
14834
14834
|
}
|
|
14835
14835
|
}).finally(() => {
|
|
14836
14836
|
if (!done) {
|
|
14837
|
-
stream.error ? reject(stream.error) :
|
|
14837
|
+
stream.error ? reject(stream.error) : resolve18({ shallows, unshallows, acks, nak, packfile, progress });
|
|
14838
14838
|
}
|
|
14839
14839
|
});
|
|
14840
14840
|
});
|
|
@@ -19761,7 +19761,7 @@ var require_ignore2 = __commonJS({
|
|
|
19761
19761
|
// cli/package.json
|
|
19762
19762
|
var package_default = {
|
|
19763
19763
|
name: "@docyrus/animaguru",
|
|
19764
|
-
version: "0.
|
|
19764
|
+
version: "0.22.0",
|
|
19765
19765
|
description: "Agent-friendly AnimaGuru CLI built with CrustJS",
|
|
19766
19766
|
type: "module",
|
|
19767
19767
|
bin: {
|
|
@@ -24295,8 +24295,8 @@ var version = defineExtension(VERSION).factory((extension, value, options = {})
|
|
|
24295
24295
|
|
|
24296
24296
|
// cli/src/commands.ts
|
|
24297
24297
|
import { spawn as spawn3 } from "node:child_process";
|
|
24298
|
-
import { mkdir as mkdir15, readFile as
|
|
24299
|
-
import { dirname as dirname14, resolve as
|
|
24298
|
+
import { mkdir as mkdir15, readFile as readFile19, writeFile as writeFile16 } from "node:fs/promises";
|
|
24299
|
+
import { dirname as dirname14, resolve as resolve17 } from "node:path";
|
|
24300
24300
|
import { setTimeout as delay6 } from "node:timers/promises";
|
|
24301
24301
|
|
|
24302
24302
|
// node_modules/fflate/esm/index.mjs
|
|
@@ -25339,13 +25339,14 @@ var SPRITE_IMAGE_MODEL_IDS = SPRITE_IMAGE_MODELS.map((m) => m.id);
|
|
|
25339
25339
|
function spriteImageModel(id2) {
|
|
25340
25340
|
return SPRITE_IMAGE_MODELS.find((m) => m.id === id2) ?? SPRITE_IMAGE_MODELS[0];
|
|
25341
25341
|
}
|
|
25342
|
-
var VIDEO_WORKFLOWS = ["image-to-video", "text-to-video", "extend-video", "reference-to-video", "lip-sync", "camera-controls", "insert-video", "recast", "relight", "3d-to-video", "style"];
|
|
25342
|
+
var VIDEO_WORKFLOWS = ["image-to-video", "text-to-video", "extend-video", "reference-to-video", "lip-sync", "camera-controls", "insert-video", "recast", "relight", "3d-to-video", "style", "keyframes-to-video"];
|
|
25343
25343
|
var STUDIO_VIDEO_WORKFLOWS = ["image-to-video", "text-to-video", "extend-video"];
|
|
25344
25344
|
var MEDIA_LENGTH_WORKFLOWS = ["lip-sync", "recast", "relight", "3d-to-video"];
|
|
25345
25345
|
var lengthFromMedia = (workflow) => MEDIA_LENGTH_WORKFLOWS.includes(workflow);
|
|
25346
25346
|
var VIDEO_STYLES = ["vhs", "retro-toon-70s", "low-poly", "hand-drawn", "16bit-pixel"];
|
|
25347
25347
|
var VHS_DAMAGE_LEVELS = ["light", "medium", "heavy"];
|
|
25348
|
-
var VIDEO_RESOLUTIONS = ["480p", "768p", "1080p", "2k"];
|
|
25348
|
+
var VIDEO_RESOLUTIONS = ["480p", "720p", "768p", "1080p", "2k"];
|
|
25349
|
+
var H3_RESOLUTIONS = ["480p", "768p", "1080p", "2k"];
|
|
25349
25350
|
var VIDEO_ASPECT_RATIOS = ["21:9", "16:9", "4:3", "1:1", "3:4", "9:16"];
|
|
25350
25351
|
var PROMPT_EXPANSION_MODES = ["disabled", "balanced", "quality"];
|
|
25351
25352
|
var EXTEND_OUTPUTS = ["extended", "continuation"];
|
|
@@ -25359,7 +25360,10 @@ var MAX_RECAST_PEOPLE = 4;
|
|
|
25359
25360
|
var MAX_3D_REFERENCES = 8;
|
|
25360
25361
|
var MAX_CAMERA_KEYFRAMES = 12;
|
|
25361
25362
|
var MAX_CAMERA_TURNS = 32;
|
|
25363
|
+
var MAX_IMAGE_KEYFRAMES = 10;
|
|
25364
|
+
var KEYFRAME_FPS = 24;
|
|
25362
25365
|
var H3_MAX = "minimax/h3-max";
|
|
25366
|
+
var FLUX_3_MAX = "blackforestlabs/flux-3-max";
|
|
25363
25367
|
var h3Endpoints = (route) => ({ "image-to-video": `${route}/image-to-video`, "text-to-video": `${route}/text-to-video`, "extend-video": `${route}/extend-video` });
|
|
25364
25368
|
var SPRITE_VIDEO_MODELS = [
|
|
25365
25369
|
{
|
|
@@ -25388,7 +25392,8 @@ var SPRITE_VIDEO_MODELS = [
|
|
|
25388
25392
|
id: "fal/minimax-h3-max",
|
|
25389
25393
|
label: "MiniMax H3 Max (fal.ai)",
|
|
25390
25394
|
provider: "fal",
|
|
25391
|
-
|
|
25395
|
+
// Every workflow but FLUX 3 Max's keyframes.
|
|
25396
|
+
workflows: VIDEO_WORKFLOWS.filter((w) => w !== "keyframes-to-video"),
|
|
25392
25397
|
endpoints: {
|
|
25393
25398
|
...h3Endpoints(H3_MAX),
|
|
25394
25399
|
"reference-to-video": `${H3_MAX}/reference-to-video`,
|
|
@@ -25402,11 +25407,11 @@ var SPRITE_VIDEO_MODELS = [
|
|
|
25402
25407
|
},
|
|
25403
25408
|
resolutions: ["480p", "768p", "1080p"],
|
|
25404
25409
|
workflowOptions: {
|
|
25405
|
-
"extend-video": { resolutions:
|
|
25406
|
-
"lip-sync": { resolutions:
|
|
25410
|
+
"extend-video": { resolutions: H3_RESOLUTIONS },
|
|
25411
|
+
"lip-sync": { resolutions: H3_RESOLUTIONS, seconds: { min: 5, max: 900, fractional: true } },
|
|
25407
25412
|
"insert-video": { resolutions: ["480p", "768p"], seconds: { min: 5, max: 13, fractional: true }, creditsPerSecond: { "480p": 5, "768p": 6 } },
|
|
25408
25413
|
recast: { resolutions: ["768p", "1080p"], seconds: { min: 5, max: 30, fractional: true }, creditsPerSecond: { "768p": 30, "1080p": 45 } },
|
|
25409
|
-
relight: { resolutions:
|
|
25414
|
+
relight: { resolutions: H3_RESOLUTIONS, seconds: { min: 2.33, max: 15, fractional: true } },
|
|
25410
25415
|
"3d-to-video": { seconds: { min: 1, max: 15, fractional: true } },
|
|
25411
25416
|
style: { resolutions: ["768p"], seconds: { min: 5, max: 15 } }
|
|
25412
25417
|
},
|
|
@@ -25426,7 +25431,7 @@ var SPRITE_VIDEO_MODELS = [
|
|
|
25426
25431
|
workflows: STUDIO_VIDEO_WORKFLOWS,
|
|
25427
25432
|
endpoints: h3Endpoints("minimax/h3-max-turbo"),
|
|
25428
25433
|
resolutions: ["480p", "768p", "1080p"],
|
|
25429
|
-
workflowOptions: { "extend-video": { resolutions:
|
|
25434
|
+
workflowOptions: { "extend-video": { resolutions: H3_RESOLUTIONS } },
|
|
25430
25435
|
modes: ["speed"],
|
|
25431
25436
|
lastFrame: true,
|
|
25432
25437
|
endFrameOnly: true,
|
|
@@ -25435,6 +25440,21 @@ var SPRITE_VIDEO_MODELS = [
|
|
|
25435
25440
|
audio: true,
|
|
25436
25441
|
estimatedCreditsPerSecond: { "480p": { speed: 2.5 }, "768p": { speed: 4 }, "1080p": { speed: 8 }, "2k": { speed: 16 } },
|
|
25437
25442
|
seconds: { min: 1, max: 15, fractional: true }
|
|
25443
|
+
},
|
|
25444
|
+
{
|
|
25445
|
+
id: "fal/flux-3-max",
|
|
25446
|
+
label: "FLUX 3 Max (fal.ai)",
|
|
25447
|
+
provider: "fal",
|
|
25448
|
+
workflows: ["image-to-video", "text-to-video", "keyframes-to-video"],
|
|
25449
|
+
endpoints: { "image-to-video": `${FLUX_3_MAX}/image-to-video`, "text-to-video": `${FLUX_3_MAX}/text-to-video`, "keyframes-to-video": `${FLUX_3_MAX}/keyframes-to-video` },
|
|
25450
|
+
falInputs: "flux",
|
|
25451
|
+
resolutions: ["720p", "1080p"],
|
|
25452
|
+
modes: ["quality"],
|
|
25453
|
+
lastFrame: true,
|
|
25454
|
+
seed: true,
|
|
25455
|
+
promptExpansion: ["balanced", "quality"],
|
|
25456
|
+
estimatedCreditsPerSecond: { "720p": { quality: 17 }, "1080p": { quality: 29 } },
|
|
25457
|
+
seconds: { min: 5, max: 20 }
|
|
25438
25458
|
}
|
|
25439
25459
|
];
|
|
25440
25460
|
var SPRITE_VIDEO_MODEL_IDS = SPRITE_VIDEO_MODELS.map((m) => m.id);
|
|
@@ -30496,7 +30516,7 @@ var recursive = /* @__PURE__ */ new WeakMap();
|
|
|
30496
30516
|
var NONE = 0;
|
|
30497
30517
|
var ASSUMED = 1;
|
|
30498
30518
|
var PROVEN = 2;
|
|
30499
|
-
function isRecursive(inst, stack,
|
|
30519
|
+
function isRecursive(inst, stack, resolve18) {
|
|
30500
30520
|
const cached2 = recursive.get(inst);
|
|
30501
30521
|
if (cached2 !== void 0)
|
|
30502
30522
|
return cached2 ? PROVEN : NONE;
|
|
@@ -30506,7 +30526,7 @@ function isRecursive(inst, stack, resolve17) {
|
|
|
30506
30526
|
let result = NONE;
|
|
30507
30527
|
const check2 = (child) => {
|
|
30508
30528
|
if (result !== PROVEN && child?._zod) {
|
|
30509
|
-
const answer = isRecursive(child, stack,
|
|
30529
|
+
const answer = isRecursive(child, stack, resolve18);
|
|
30510
30530
|
if (answer > result)
|
|
30511
30531
|
result = answer;
|
|
30512
30532
|
}
|
|
@@ -30517,7 +30537,7 @@ function isRecursive(inst, stack, resolve17) {
|
|
|
30517
30537
|
const desc = Object.getOwnPropertyDescriptor(sh, key);
|
|
30518
30538
|
if (spread && !desc.enumerable)
|
|
30519
30539
|
continue;
|
|
30520
|
-
const child = desc.get ? ASSUMED : desc.value?._zod ? isRecursive(desc.value, stack,
|
|
30540
|
+
const child = desc.get ? ASSUMED : desc.value?._zod ? isRecursive(desc.value, stack, resolve18) : NONE;
|
|
30521
30541
|
if (child > answer)
|
|
30522
30542
|
answer = child;
|
|
30523
30543
|
}
|
|
@@ -30581,7 +30601,7 @@ function isRecursive(inst, stack, resolve17) {
|
|
|
30581
30601
|
break;
|
|
30582
30602
|
// `$ZodLazy` caches its inner on the def, so a resolved edge is followed exactly
|
|
30583
30603
|
case "lazy": {
|
|
30584
|
-
const inner = def._cachedInner ?? (
|
|
30604
|
+
const inner = def._cachedInner ?? (resolve18 ? inst._zod.innerType : void 0);
|
|
30585
30605
|
merge2(inner ? isRecursive(inner, stack, false) : ASSUMED);
|
|
30586
30606
|
break;
|
|
30587
30607
|
}
|
|
@@ -45199,6 +45219,12 @@ async function saveWorkspace(origin, workspaceId) {
|
|
|
45199
45219
|
credentials[origin] = { ...saved, workspaceId };
|
|
45200
45220
|
await writeCredentials(credentials);
|
|
45201
45221
|
}
|
|
45222
|
+
async function sessionToken(origin) {
|
|
45223
|
+
const saved = (await readCredentials())[origin];
|
|
45224
|
+
const token3 = process.env.ANIMAGURU_TOKEN ?? (saved && saved.expiresAt > Date.now() ? saved.token : void 0);
|
|
45225
|
+
if (!token3) throw new Error("Sign in with animaguru auth login, or set ANIMAGURU_TOKEN.");
|
|
45226
|
+
return token3;
|
|
45227
|
+
}
|
|
45202
45228
|
var ServiceError = class extends Error {
|
|
45203
45229
|
constructor(status, code, message) {
|
|
45204
45230
|
super(message);
|
|
@@ -45216,12 +45242,7 @@ var Client = class {
|
|
|
45216
45242
|
signal;
|
|
45217
45243
|
origin;
|
|
45218
45244
|
async request(path, options = {}) {
|
|
45219
|
-
|
|
45220
|
-
if (options.authenticated !== false) {
|
|
45221
|
-
const saved = (await readCredentials())[this.origin];
|
|
45222
|
-
token3 = process.env.ANIMAGURU_TOKEN ?? (saved && saved.expiresAt > Date.now() ? saved.token : void 0);
|
|
45223
|
-
if (!token3) throw new Error("Sign in with animaguru auth login, or set ANIMAGURU_TOKEN.");
|
|
45224
|
-
}
|
|
45245
|
+
const token3 = options.authenticated !== false ? await sessionToken(this.origin) : void 0;
|
|
45225
45246
|
const generation = options.method === "POST" && /^\/api\/v1\/sprites\/[^/]+\/(jobs|assets|avatars|avatars\/looks|avatars\/[^/]+\/consent)$/.test(path);
|
|
45226
45247
|
const idempotency = generation ? { "Idempotency-Key": generationRequestId(path, options.body) } : {};
|
|
45227
45248
|
const response = await fetch(`${this.origin}${path}`, {
|
|
@@ -45347,14 +45368,7 @@ var animation_styles_default = {
|
|
|
45347
45368
|
"Masked reveals",
|
|
45348
45369
|
"Speech & beat timing"
|
|
45349
45370
|
],
|
|
45350
|
-
previewCaption: "
|
|
45351
|
-
studyCopy: [
|
|
45352
|
-
"Make",
|
|
45353
|
-
"every",
|
|
45354
|
-
"word",
|
|
45355
|
-
"move.",
|
|
45356
|
-
"TYPE / RHYTHM / EMPHASIS"
|
|
45357
|
-
],
|
|
45371
|
+
previewCaption: "Words slam, mask and split into letters on a 120 bpm beat across four color fields, then settle into a lockup. Drawn entirely in code.",
|
|
45358
45372
|
family: "graphic",
|
|
45359
45373
|
example: false
|
|
45360
45374
|
},
|
|
@@ -45371,11 +45385,7 @@ var animation_styles_default = {
|
|
|
45371
45385
|
"Connected transitions",
|
|
45372
45386
|
"Designed easing"
|
|
45373
45387
|
],
|
|
45374
|
-
previewCaption: "A
|
|
45375
|
-
studyCopy: [
|
|
45376
|
-
"A shape. An idea.",
|
|
45377
|
-
"One connected system."
|
|
45378
|
-
],
|
|
45388
|
+
previewCaption: "A bouncing ball splits and morphs into hills and a sun, a skyline rises, and a cyclist rides into dusk before an iris closes. Every shape is drawn and morphed in code.",
|
|
45379
45389
|
family: "graphic",
|
|
45380
45390
|
example: false
|
|
45381
45391
|
},
|
|
@@ -45392,13 +45402,7 @@ var animation_styles_default = {
|
|
|
45392
45402
|
"Drawn connections",
|
|
45393
45403
|
"Readable drawing holds"
|
|
45394
45404
|
],
|
|
45395
|
-
previewCaption: "
|
|
45396
|
-
studyCopy: [
|
|
45397
|
-
"Draw the connection.",
|
|
45398
|
-
"Imagine",
|
|
45399
|
-
"Connect",
|
|
45400
|
-
"Create"
|
|
45401
|
-
],
|
|
45405
|
+
previewCaption: "A generated hand draws an AnimaGuru-generated marker illustration stroke by stroke, colors it in, titles it and wipes the board clean.",
|
|
45402
45406
|
family: "graphic",
|
|
45403
45407
|
example: false
|
|
45404
45408
|
},
|
|
@@ -45415,13 +45419,7 @@ var animation_styles_default = {
|
|
|
45415
45419
|
"Travelling path markers",
|
|
45416
45420
|
"Controlled camera movement"
|
|
45417
45421
|
],
|
|
45418
|
-
previewCaption: "
|
|
45419
|
-
studyCopy: [
|
|
45420
|
-
"A world, connected.",
|
|
45421
|
-
"Collect",
|
|
45422
|
-
"Shape",
|
|
45423
|
-
"Share"
|
|
45424
|
-
],
|
|
45422
|
+
previewCaption: "An isometric town assembles tile by tile: buildings extrude, trees pop, cars and a train start running and the windows light up at dusk. Built from projected geometry in code.",
|
|
45425
45423
|
family: "technical",
|
|
45426
45424
|
example: false
|
|
45427
45425
|
},
|
|
@@ -45438,14 +45436,7 @@ var animation_styles_default = {
|
|
|
45438
45436
|
"Counted values",
|
|
45439
45437
|
"Comparison highlights"
|
|
45440
45438
|
],
|
|
45441
|
-
previewCaption: "
|
|
45442
|
-
studyCopy: [
|
|
45443
|
-
"Make the comparison clear.",
|
|
45444
|
-
"A",
|
|
45445
|
-
"B",
|
|
45446
|
-
"C",
|
|
45447
|
-
"ILLUSTRATIVE DATA / SAMPLE VALUES"
|
|
45448
|
-
],
|
|
45439
|
+
previewCaption: "A line chart is drawn, counted and annotated, then becomes ranked bars beside a platform donut. Drawn entirely in code from sample values.",
|
|
45449
45440
|
family: "technical",
|
|
45450
45441
|
example: false
|
|
45451
45442
|
},
|
|
@@ -45462,12 +45453,7 @@ var animation_styles_default = {
|
|
|
45462
45453
|
"Parameter changes",
|
|
45463
45454
|
"Semantic transformations"
|
|
45464
45455
|
],
|
|
45465
|
-
previewCaption: "A
|
|
45466
|
-
studyCopy: [
|
|
45467
|
-
"See the relationship.",
|
|
45468
|
-
"radius",
|
|
45469
|
-
"area"
|
|
45470
|
-
],
|
|
45456
|
+
previewCaption: "A unit circle traces a sine wave, then becomes the largest of 90 rotating circles that draw \u03C0. Plotted from a real Fourier series.",
|
|
45471
45457
|
family: "technical",
|
|
45472
45458
|
example: false
|
|
45473
45459
|
},
|
|
@@ -45484,19 +45470,7 @@ var animation_styles_default = {
|
|
|
45484
45470
|
"Bounded zooms",
|
|
45485
45471
|
"State transitions"
|
|
45486
45472
|
],
|
|
45487
|
-
previewCaption: "A
|
|
45488
|
-
studyCopy: [
|
|
45489
|
-
"Focus on what matters.",
|
|
45490
|
-
"CONCEPTUAL INTERFACE",
|
|
45491
|
-
"Workspace",
|
|
45492
|
-
"Overview",
|
|
45493
|
-
"Projects",
|
|
45494
|
-
"Library",
|
|
45495
|
-
"New project",
|
|
45496
|
-
"Project created",
|
|
45497
|
-
"Create your next idea.",
|
|
45498
|
-
"Ready to create"
|
|
45499
|
-
],
|
|
45473
|
+
previewCaption: "A cursor drives a dashboard: a live toggle, hover lift, drag-to-reorder, a modal with typing and loading, then a toast and a phone notification. Every element and interaction is coded.",
|
|
45500
45474
|
family: "technical",
|
|
45501
45475
|
example: false
|
|
45502
45476
|
},
|
|
@@ -45513,10 +45487,7 @@ var animation_styles_default = {
|
|
|
45513
45487
|
"Pose-to-pose timing",
|
|
45514
45488
|
"Gesture & reaction"
|
|
45515
45489
|
],
|
|
45516
|
-
previewCaption: "
|
|
45517
|
-
studyCopy: [
|
|
45518
|
-
"An idea comes to life."
|
|
45519
|
-
],
|
|
45490
|
+
previewCaption: "Pip, a cartoon character drawn and animated in AnimaGuru, walks a generated meadow and hops a log, with a following camera, squash and stretch, and dust on landing.",
|
|
45520
45491
|
family: "character",
|
|
45521
45492
|
example: false
|
|
45522
45493
|
},
|
|
@@ -45533,11 +45504,7 @@ var animation_styles_default = {
|
|
|
45533
45504
|
"Palette accents",
|
|
45534
45505
|
"Pixel transitions"
|
|
45535
45506
|
],
|
|
45536
|
-
previewCaption: "
|
|
45537
|
-
studyCopy: [
|
|
45538
|
-
"SMALL PIXELS.",
|
|
45539
|
-
"BIG POSSIBILITIES."
|
|
45540
|
-
],
|
|
45507
|
+
previewCaption: "Bit, a pixel-art sprite animated in AnimaGuru, runs a side-scrolling level built from generated parallax layers, with coins, sparkles and a score drawn in code.",
|
|
45541
45508
|
family: "character",
|
|
45542
45509
|
example: false
|
|
45543
45510
|
},
|
|
@@ -45554,10 +45521,7 @@ var animation_styles_default = {
|
|
|
45554
45521
|
"Foreground reveals",
|
|
45555
45522
|
"Selective organic motion"
|
|
45556
45523
|
],
|
|
45557
|
-
previewCaption: "
|
|
45558
|
-
studyCopy: [
|
|
45559
|
-
"Let the picture breathe."
|
|
45560
|
-
],
|
|
45524
|
+
previewCaption: "Five AnimaGuru-generated oil-painted planes on a slow dolly, with haze between them, sun rays, drifting pollen and wind in the grass.",
|
|
45561
45525
|
family: "material",
|
|
45562
45526
|
example: false
|
|
45563
45527
|
},
|
|
@@ -45574,13 +45538,8 @@ var animation_styles_default = {
|
|
|
45574
45538
|
"Matched reveals",
|
|
45575
45539
|
"Controlled depth"
|
|
45576
45540
|
],
|
|
45577
|
-
studyCopy: [
|
|
45578
|
-
"Bring the signal forward.",
|
|
45579
|
-
"Concept stage",
|
|
45580
|
-
"One clear focus"
|
|
45581
|
-
],
|
|
45582
45541
|
family: "technical",
|
|
45583
|
-
previewCaption: "
|
|
45542
|
+
previewCaption: "Glass interface panels assemble around a hero card on a reflective stage under sweeping light. Built in code with depth of field and floor reflections.",
|
|
45584
45543
|
example: true
|
|
45585
45544
|
},
|
|
45586
45545
|
"studio-product-reveal": {
|
|
@@ -45596,13 +45555,8 @@ var animation_styles_default = {
|
|
|
45596
45555
|
"Detail close-ups",
|
|
45597
45556
|
"Layered part separation"
|
|
45598
45557
|
],
|
|
45599
|
-
studyCopy: [
|
|
45600
|
-
"Form. Material. Detail.",
|
|
45601
|
-
"Illustrative product",
|
|
45602
|
-
"Light reveals the shape"
|
|
45603
|
-
],
|
|
45604
45558
|
family: "material",
|
|
45605
|
-
previewCaption: "
|
|
45559
|
+
previewCaption: "An unbranded fragrance bottle generated with AnimaGuru turns under a strip light: a macro start, a pull-back, anamorphic glints and a title lockup.",
|
|
45606
45560
|
example: true
|
|
45607
45561
|
},
|
|
45608
45562
|
"risograph-print": {
|
|
@@ -45618,13 +45572,8 @@ var animation_styles_default = {
|
|
|
45618
45572
|
"Registration shifts",
|
|
45619
45573
|
"Held-drawing cadence"
|
|
45620
45574
|
],
|
|
45621
|
-
studyCopy: [
|
|
45622
|
-
"Two inks. More possibilities.",
|
|
45623
|
-
"Separate plates",
|
|
45624
|
-
"Meet in the overprint"
|
|
45625
|
-
],
|
|
45626
45575
|
family: "print",
|
|
45627
|
-
previewCaption: "
|
|
45576
|
+
previewCaption: "An AnimaGuru-generated illustration separated in code into yellow, fluorescent pink and teal drums, then printed pass by pass with riso grain and slight misregistration.",
|
|
45628
45577
|
example: true
|
|
45629
45578
|
},
|
|
45630
45579
|
"halftone-editorial": {
|
|
@@ -45640,13 +45589,8 @@ var animation_styles_default = {
|
|
|
45640
45589
|
"Stamp impacts",
|
|
45641
45590
|
"Layered panel changes"
|
|
45642
45591
|
],
|
|
45643
|
-
studyCopy: [
|
|
45644
|
-
"Give the idea a headline.",
|
|
45645
|
-
"An original print study",
|
|
45646
|
-
"Make the point readable"
|
|
45647
|
-
],
|
|
45648
45592
|
family: "print",
|
|
45649
|
-
previewCaption: "
|
|
45593
|
+
previewCaption: "An AnimaGuru-generated stage photo screened in code into black and orange halftone dots at 45\xB0 and 15\xB0, pulling back from the dots to a magazine spread.",
|
|
45650
45594
|
example: true
|
|
45651
45595
|
},
|
|
45652
45596
|
"silkscreen-poster": {
|
|
@@ -45662,13 +45606,8 @@ var animation_styles_default = {
|
|
|
45662
45606
|
"Layer-by-layer builds",
|
|
45663
45607
|
"Graphic holds"
|
|
45664
45608
|
],
|
|
45665
|
-
studyCopy: [
|
|
45666
|
-
"One ink. One impression.",
|
|
45667
|
-
"Print the composition",
|
|
45668
|
-
"Hold the finished image"
|
|
45669
|
-
],
|
|
45670
45609
|
family: "print",
|
|
45671
|
-
previewCaption: "
|
|
45610
|
+
previewCaption: "AnimaGuru-generated poster art cut into five flat stencils in code and pulled screen by screen with a squeegee, ending on the finished print.",
|
|
45672
45611
|
example: true
|
|
45673
45612
|
},
|
|
45674
45613
|
"art-deco-motion": {
|
|
@@ -45684,13 +45623,8 @@ var animation_styles_default = {
|
|
|
45684
45623
|
"Sequenced lights",
|
|
45685
45624
|
"Mechanical settling"
|
|
45686
45625
|
],
|
|
45687
|
-
studyCopy: [
|
|
45688
|
-
"Geometry catches the light.",
|
|
45689
|
-
"Open from the center",
|
|
45690
|
-
"A measured reveal"
|
|
45691
|
-
],
|
|
45692
45626
|
family: "graphic",
|
|
45693
|
-
previewCaption: "
|
|
45627
|
+
previewCaption: "Gold keylines, stepped arches and a sunburst fan open symmetrically; chevrons and marquee lights run in sequence while a specular sweep crosses the metal. Drawn entirely in code.",
|
|
45694
45628
|
example: true
|
|
45695
45629
|
},
|
|
45696
45630
|
"blueprint-drafting": {
|
|
@@ -45706,13 +45640,8 @@ var animation_styles_default = {
|
|
|
45706
45640
|
"Exploded parts",
|
|
45707
45641
|
"Anchored dimensions"
|
|
45708
45642
|
],
|
|
45709
|
-
studyCopy: [
|
|
45710
|
-
"See how the parts connect.",
|
|
45711
|
-
"Illustrative drawing",
|
|
45712
|
-
"Assemble the relationship"
|
|
45713
|
-
],
|
|
45714
45643
|
family: "technical",
|
|
45715
|
-
previewCaption: "
|
|
45644
|
+
previewCaption: "A drafting point traces an AnimaGuru-generated cyanotype line by line, then dimensions, callouts and a title block are drawn in.",
|
|
45716
45645
|
example: true
|
|
45717
45646
|
},
|
|
45718
45647
|
"holographic-scan": {
|
|
@@ -45728,13 +45657,8 @@ var animation_styles_default = {
|
|
|
45728
45657
|
"Target locking",
|
|
45729
45658
|
"Local part separation"
|
|
45730
45659
|
],
|
|
45731
|
-
studyCopy: [
|
|
45732
|
-
"Scan. Focus. Understand.",
|
|
45733
|
-
"Illustrative part model",
|
|
45734
|
-
"Follow the detail"
|
|
45735
|
-
],
|
|
45736
45660
|
family: "technical",
|
|
45737
|
-
previewCaption: "
|
|
45661
|
+
previewCaption: "A wireframe turbofan grows under a moving scan, locks onto its parts and separates them with callouts. Projected 3D geometry drawn in code.",
|
|
45738
45662
|
example: true
|
|
45739
45663
|
},
|
|
45740
45664
|
"paper-popup": {
|
|
@@ -45750,13 +45674,8 @@ var animation_styles_default = {
|
|
|
45750
45674
|
"Back-to-front arrival",
|
|
45751
45675
|
"Paper-stage transitions"
|
|
45752
45676
|
],
|
|
45753
|
-
studyCopy: [
|
|
45754
|
-
"A flat page gains a world.",
|
|
45755
|
-
"An original paper mechanism",
|
|
45756
|
-
"Fold. Open. Discover."
|
|
45757
|
-
],
|
|
45758
45677
|
family: "paper",
|
|
45759
|
-
previewCaption: "
|
|
45678
|
+
previewCaption: "A book opens and AnimaGuru-generated cardstock layers hinge up from the fold with shadows and spring, while a pull tab lifts the balloon and sun.",
|
|
45760
45679
|
example: true
|
|
45761
45680
|
},
|
|
45762
45681
|
"shadow-theatre": {
|
|
@@ -45772,13 +45691,8 @@ var animation_styles_default = {
|
|
|
45772
45691
|
"Jointed articulation",
|
|
45773
45692
|
"Lamp-distance changes"
|
|
45774
45693
|
],
|
|
45775
|
-
studyCopy: [
|
|
45776
|
-
"A silhouette tells a thousand things.",
|
|
45777
|
-
"Light and carved shape",
|
|
45778
|
-
"Move with the material"
|
|
45779
|
-
],
|
|
45780
45694
|
family: "paper",
|
|
45781
|
-
previewCaption: "
|
|
45695
|
+
previewCaption: "Shadow puppets generated with AnimaGuru perform behind a lit linen screen, finished in code with the cloth's weave, a breathing lamp and a warm glow.",
|
|
45782
45696
|
example: true
|
|
45783
45697
|
},
|
|
45784
45698
|
"engraved-relief-print": {
|
|
@@ -45794,13 +45708,8 @@ var animation_styles_default = {
|
|
|
45794
45708
|
"Progressive hatching",
|
|
45795
45709
|
"Successive impressions"
|
|
45796
45710
|
],
|
|
45797
|
-
studyCopy: [
|
|
45798
|
-
"Every mark has a direction.",
|
|
45799
|
-
"Carve the form",
|
|
45800
|
-
"Pull the impression"
|
|
45801
|
-
],
|
|
45802
45711
|
family: "print",
|
|
45803
|
-
previewCaption: "
|
|
45712
|
+
previewCaption: "A generated woodblock print pulled in order: hatching carved first, then two color impressions rolled into register.",
|
|
45804
45713
|
example: true
|
|
45805
45714
|
},
|
|
45806
45715
|
"ascii-crt": {
|
|
@@ -45816,13 +45725,8 @@ var animation_styles_default = {
|
|
|
45816
45725
|
"Grid-scale reveals",
|
|
45817
45726
|
"Analytic afterglow"
|
|
45818
45727
|
],
|
|
45819
|
-
studyCopy: [
|
|
45820
|
-
"A picture made of characters.",
|
|
45821
|
-
"Signal on the grid",
|
|
45822
|
-
"Pixels become language"
|
|
45823
|
-
],
|
|
45824
45728
|
family: "cinematic",
|
|
45825
|
-
previewCaption: "
|
|
45729
|
+
previewCaption: "An AnimaGuru-generated jellyfish clip rendered as real ASCII on a 128-column green-phosphor terminal, with curvature, glow trails and a typed command.",
|
|
45826
45730
|
example: true
|
|
45827
45731
|
},
|
|
45828
45732
|
"analog-film-tape": {
|
|
@@ -45838,13 +45742,8 @@ var animation_styles_default = {
|
|
|
45838
45742
|
"Deterministic weave",
|
|
45839
45743
|
"Tape-inspired transitions"
|
|
45840
45744
|
],
|
|
45841
|
-
studyCopy: [
|
|
45842
|
-
"A different kind of time.",
|
|
45843
|
-
"Original scene / film variant",
|
|
45844
|
-
"Texture follows the medium"
|
|
45845
|
-
],
|
|
45846
45745
|
family: "cinematic",
|
|
45847
|
-
previewCaption: "
|
|
45746
|
+
previewCaption: "Two generated summer scenes projected at 18 fps, with gate weave, grain, a slipped splice and a film burn.",
|
|
45848
45747
|
example: true
|
|
45849
45748
|
},
|
|
45850
45749
|
"glass-mosaic": {
|
|
@@ -45860,13 +45759,8 @@ var animation_styles_default = {
|
|
|
45860
45759
|
"Moving light bands",
|
|
45861
45760
|
"Identity-preserving rearrangement"
|
|
45862
45761
|
],
|
|
45863
|
-
studyCopy: [
|
|
45864
|
-
"Pieces become a picture.",
|
|
45865
|
-
"Original glass-like mosaic",
|
|
45866
|
-
"Light passes through"
|
|
45867
|
-
],
|
|
45868
45762
|
family: "material",
|
|
45869
|
-
previewCaption: "
|
|
45763
|
+
previewCaption: "An AnimaGuru-generated peacock cut in code into leaded glass pieces that slot in one by one, then lit by a low sun crossing the window.",
|
|
45870
45764
|
example: true
|
|
45871
45765
|
},
|
|
45872
45766
|
"portal-zoom": {
|
|
@@ -45882,13 +45776,8 @@ var animation_styles_default = {
|
|
|
45882
45776
|
"Layered parallax",
|
|
45883
45777
|
"Occluded scene handoffs"
|
|
45884
45778
|
],
|
|
45885
|
-
studyCopy: [
|
|
45886
|
-
"Every frame opens another.",
|
|
45887
|
-
"An original nested world",
|
|
45888
|
-
"Keep the journey continuous"
|
|
45889
|
-
],
|
|
45890
45779
|
family: "cinematic",
|
|
45891
|
-
previewCaption: "
|
|
45780
|
+
previewCaption: "A continuous zoom through four AnimaGuru-generated worlds, each framed inside the last, that ends exactly where it began.",
|
|
45892
45781
|
example: true
|
|
45893
45782
|
},
|
|
45894
45783
|
"photo-footage-editorial": {
|
|
@@ -45904,13 +45793,8 @@ var animation_styles_default = {
|
|
|
45904
45793
|
"Media-wall selection",
|
|
45905
45794
|
"Focused detail reveals"
|
|
45906
45795
|
],
|
|
45907
|
-
studyCopy: [
|
|
45908
|
-
"Give every image its place.",
|
|
45909
|
-
"Original illustrated media cards",
|
|
45910
|
-
"Frame the detail"
|
|
45911
|
-
],
|
|
45912
45796
|
family: "cinematic",
|
|
45913
|
-
previewCaption: "
|
|
45797
|
+
previewCaption: "Six generated photographs on a contact sheet. The circled frame becomes footage, then returns to its strip.",
|
|
45914
45798
|
example: true
|
|
45915
45799
|
},
|
|
45916
45800
|
"stop-motion-objects": {
|
|
@@ -45926,13 +45810,8 @@ var animation_styles_default = {
|
|
|
45926
45810
|
"Physical-looking assembly",
|
|
45927
45811
|
"Held-drawing timing"
|
|
45928
45812
|
],
|
|
45929
|
-
studyCopy: [
|
|
45930
|
-
"Build it one piece at a time.",
|
|
45931
|
-
"Illustrative tactile objects",
|
|
45932
|
-
"Deliberate held poses"
|
|
45933
|
-
],
|
|
45934
45813
|
family: "material",
|
|
45935
|
-
previewCaption: "
|
|
45814
|
+
previewCaption: "Clay footage generated with AnimaGuru, held on twos at 12 fps, with a nudge of the set and a flicker of the lamp between poses.",
|
|
45936
45815
|
example: true
|
|
45937
45816
|
},
|
|
45938
45817
|
"miniature-diorama": {
|
|
@@ -45948,13 +45827,8 @@ var animation_styles_default = {
|
|
|
45948
45827
|
"Elevated camera travel",
|
|
45949
45828
|
"Optional time-lapse activity"
|
|
45950
45829
|
],
|
|
45951
|
-
studyCopy: [
|
|
45952
|
-
"A small world, carefully built.",
|
|
45953
|
-
"An original 2.5D miniature",
|
|
45954
|
-
"Let scale guide the view"
|
|
45955
|
-
],
|
|
45956
45830
|
family: "material",
|
|
45957
|
-
previewCaption: "
|
|
45831
|
+
previewCaption: "A generated harbor model with a slow elevated drift, a focus band on the quay and small time-lapsed activity.",
|
|
45958
45832
|
example: true
|
|
45959
45833
|
},
|
|
45960
45834
|
"sand-drawing": {
|
|
@@ -45970,13 +45844,8 @@ var animation_styles_default = {
|
|
|
45970
45844
|
"Authored reshape paths",
|
|
45971
45845
|
"Material holds"
|
|
45972
45846
|
],
|
|
45973
|
-
studyCopy: [
|
|
45974
|
-
"A shape emerges from grains.",
|
|
45975
|
-
"Original procedural sand study",
|
|
45976
|
-
"Reveal the silhouette"
|
|
45977
|
-
],
|
|
45978
45847
|
family: "material",
|
|
45979
|
-
previewCaption: "
|
|
45848
|
+
previewCaption: "A whale is swept into a seabird on a lit sand table, in one take. Footage generated with AnimaGuru.",
|
|
45980
45849
|
example: true
|
|
45981
45850
|
},
|
|
45982
45851
|
"generative-light-signal": {
|
|
@@ -45992,20 +45861,14 @@ var animation_styles_default = {
|
|
|
45992
45861
|
"Measured cue response",
|
|
45993
45862
|
"Controlled pattern changes"
|
|
45994
45863
|
],
|
|
45995
|
-
studyCopy: [
|
|
45996
|
-
"Turn a signal into movement.",
|
|
45997
|
-
"Original analytic field",
|
|
45998
|
-
"Follow the rhythm"
|
|
45999
|
-
],
|
|
46000
45864
|
family: "graphic",
|
|
46001
|
-
previewCaption: "
|
|
45865
|
+
previewCaption: "Seeded light particles stream along braided ribbons, flaring as timed pulses pass. A seamless loop rendered in code.",
|
|
46002
45866
|
example: true
|
|
46003
45867
|
}
|
|
46004
45868
|
},
|
|
46005
45869
|
browseStyles: "Browse animation styles",
|
|
46006
45870
|
styleCount_one: "{{count}} visual style, guided by your brief",
|
|
46007
45871
|
styleCount_other: "{{count}} visual styles, guided by your brief",
|
|
46008
|
-
previewBrand: "ANIMAGURU / MOTION STUDIES",
|
|
46009
45872
|
families: {
|
|
46010
45873
|
all: "All styles",
|
|
46011
45874
|
graphic: "Graphic & type",
|
|
@@ -46078,6 +45941,10 @@ var animation_styles_default = {
|
|
|
46078
45941
|
"visual-essay": {
|
|
46079
45942
|
name: "Visual Essay & Podcast",
|
|
46080
45943
|
description: "Add accurate graphics and chapters to source text or recorded speech."
|
|
45944
|
+
},
|
|
45945
|
+
"branching-story": {
|
|
45946
|
+
name: "Branching Story",
|
|
45947
|
+
description: "Let viewers choose what happens next and reach one of several endings; plays as an interactive HTML5 film."
|
|
46081
45948
|
}
|
|
46082
45949
|
},
|
|
46083
45950
|
artMotionLink: "Explore Art Motion styles \u2192"
|
|
@@ -46134,7 +46001,8 @@ var VIDEO_WORKFLOW_IDS = [
|
|
|
46134
46001
|
"music-lyrics",
|
|
46135
46002
|
"titles-event",
|
|
46136
46003
|
"ambient-loop",
|
|
46137
|
-
"visual-essay"
|
|
46004
|
+
"visual-essay",
|
|
46005
|
+
"branching-story"
|
|
46138
46006
|
];
|
|
46139
46007
|
var VIDEO_WORKFLOWS2 = VIDEO_WORKFLOW_IDS.map((id2, index2) => ({
|
|
46140
46008
|
id: id2,
|
|
@@ -46200,6 +46068,37 @@ async function installDependencies(directory, options) {
|
|
|
46200
46068
|
child.once("close", (code) => code === 0 ? resolveInstall() : reject(new Error(`npm install exited with code ${code}.`)));
|
|
46201
46069
|
});
|
|
46202
46070
|
}
|
|
46071
|
+
var STARTER_STORY = {
|
|
46072
|
+
version: 1,
|
|
46073
|
+
start: "opening",
|
|
46074
|
+
segments: [
|
|
46075
|
+
{
|
|
46076
|
+
id: "opening",
|
|
46077
|
+
label: "The spark",
|
|
46078
|
+
start: 0,
|
|
46079
|
+
end: 8,
|
|
46080
|
+
prompt: "Where should the story go?",
|
|
46081
|
+
choiceAt: 5,
|
|
46082
|
+
choices: [{ label: "Show the wonder", to: "wonder" }, { label: "Make the invitation", to: "invitation" }]
|
|
46083
|
+
},
|
|
46084
|
+
{ id: "wonder", label: "The possibility", start: 8, end: 16, ending: "A world worth exploring" },
|
|
46085
|
+
{ id: "invitation", label: "The invitation", start: 16, end: 24, ending: "Your next story starts here" }
|
|
46086
|
+
]
|
|
46087
|
+
};
|
|
46088
|
+
var writeStarterStory = (directory) => writeFile6(join6(directory, "story.json"), JSON.stringify(STARTER_STORY, null, 2) + "\n", { flag: "wx" });
|
|
46089
|
+
function starterQuiz(story = false) {
|
|
46090
|
+
return {
|
|
46091
|
+
version: 1,
|
|
46092
|
+
checkpoints: [{ id: "spark", at: story ? 4.5 : 7.5, title: "Quick check", questions: [
|
|
46093
|
+
{ prompt: "Where does every great story begin?", options: ["With a big budget", "With a little spark", "With the ending"], answer: 1, explanation: "The opening scene says it: every great story begins with a little spark." }
|
|
46094
|
+
] }],
|
|
46095
|
+
final: { title: "Quiz", pass: 50, questions: [
|
|
46096
|
+
{ prompt: "What does a quiz film do at a checkpoint?", options: ["Skips to the end", "Pauses and asks a question", "Plays faster"], answer: 1, explanation: "A checkpoint pauses the film on its own frame, and the film goes on once the viewer answers." },
|
|
46097
|
+
{ prompt: "Which of these can a question have?", options: ["One right answer", "Several right answers", "A countdown timer"], answer: [0, 1], explanation: "A question has one right answer, or several when it asks to select all that apply. Questions wait for the viewer." }
|
|
46098
|
+
] }
|
|
46099
|
+
};
|
|
46100
|
+
}
|
|
46101
|
+
var writeStarterQuiz = (directory, story = false) => writeFile6(join6(directory, "quiz.json"), JSON.stringify(starterQuiz(story), null, 2) + "\n", { flag: "wx" });
|
|
46203
46102
|
async function createVideoProject(options, installer = installDependencies) {
|
|
46204
46103
|
if (!options.directory.trim()) throw new Error("Choose a new project folder, for example: animaguru create my-video");
|
|
46205
46104
|
const directory = resolve5(options.directory);
|
|
@@ -46223,7 +46122,7 @@ async function createVideoProject(options, installer = installDependencies) {
|
|
|
46223
46122
|
const manifest = JSON.parse(await readFile4(packagePath, "utf8"));
|
|
46224
46123
|
manifest.name = basename2(directory).toLowerCase().replace(/[^a-z0-9-]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 100) || "animaguru-video";
|
|
46225
46124
|
await writeFile6(packagePath, JSON.stringify(manifest, null, 2) + "\n");
|
|
46226
|
-
const skills = [.../* @__PURE__ */ new Set(["animaguru", "animaguru-video", "animaguru-brand-kit", "animaguru-cutcraft", "animaguru-art-motion", ...VIDEO_PURPOSE_SKILLS, ...VIDEO_TEMPLATES.map((template) => template.skill)])];
|
|
46125
|
+
const skills = [.../* @__PURE__ */ new Set(["animaguru", "animaguru-video", "animaguru-brand-kit", "animaguru-cutcraft", "animaguru-art-motion", "animaguru-branching-story", "animaguru-quiz", ...VIDEO_PURPOSE_SKILLS, ...VIDEO_TEMPLATES.map((template) => template.skill)])];
|
|
46227
46126
|
const pixiSkillsRoot = join6(packageRoot, "vendor/pixijs-skills/skills");
|
|
46228
46127
|
const pixiSkills = (await readdir2(pixiSkillsRoot, { withFileTypes: true })).filter((entry) => entry.isDirectory()).map((entry) => entry.name).sort();
|
|
46229
46128
|
for (const agent of [".agents", ".claude"]) {
|
|
@@ -46247,6 +46146,8 @@ async function createVideoProject(options, installer = installDependencies) {
|
|
|
46247
46146
|
animaguruProjectId: null
|
|
46248
46147
|
}, null, 2) + "\n");
|
|
46249
46148
|
await writeFile6(join6(directory, "animaguru.templates.json"), JSON.stringify(listTemplates(), null, 2) + "\n");
|
|
46149
|
+
if (options.story) await writeStarterStory(directory);
|
|
46150
|
+
if (options.quiz) await writeStarterQuiz(directory, options.story);
|
|
46250
46151
|
if (options.install !== false) {
|
|
46251
46152
|
options.log?.("Installing PixiJS, Vite, TypeScript, and the MP4 tools\u2026");
|
|
46252
46153
|
try {
|
|
@@ -46369,7 +46270,11 @@ async function createAnimation(options) {
|
|
|
46369
46270
|
await mkdir6(join6(directory, "public/assets"), { recursive: true });
|
|
46370
46271
|
await mkdir6(join6(directory, "production"), { recursive: true });
|
|
46371
46272
|
await writeFile6(join6(directory, "animaguru.project.json"), JSON.stringify({ schemaVersion: 4, cliVersion: package_default.version, workspace: "../..", category: null, template: null, workflow: null, skill: null, setupStage: "category", galleryUrl: ANIMATION_STYLE_GALLERY_URL, interview: "pending", animaguruProjectId: null, branding: "inherit" }, null, 2) + "\n");
|
|
46372
|
-
options.
|
|
46273
|
+
if (options.story) await writeStarterStory(directory);
|
|
46274
|
+
if (options.quiz) await writeStarterQuiz(directory, options.story);
|
|
46275
|
+
const kind = options.story && options.quiz ? "Branching story with a quiz" : options.story ? "Branching story" : options.quiz ? "Quiz film" : "Animation";
|
|
46276
|
+
options.log?.(`${kind} ready: ${directory}. It uses the workspace's branding, player and export tools.`);
|
|
46277
|
+
const skills = [...options.story ? ["animaguru-branching-story"] : [], ...options.quiz ? ["animaguru-quiz"] : []];
|
|
46373
46278
|
return {
|
|
46374
46279
|
type: "animation",
|
|
46375
46280
|
path: directory,
|
|
@@ -46377,7 +46282,9 @@ async function createAnimation(options) {
|
|
|
46377
46282
|
name: options.name,
|
|
46378
46283
|
category: null,
|
|
46379
46284
|
template: null,
|
|
46380
|
-
|
|
46285
|
+
...options.story ? { story: "story.json" } : {},
|
|
46286
|
+
...options.quiz ? { quiz: "quiz.json" } : {},
|
|
46287
|
+
nextSteps: skills.length ? [`Describe the ${options.story ? "story" : "film"} in Codex or Claude Code, working in animations/${options.name}; the agent follows the ${skills.join(" and ")} skill${skills.length > 1 ? "s" : ""} and replaces the starter ${[...options.story ? ["story.json"] : [], ...options.quiz ? ["quiz.json"] : []].join(" and ")}.`, `Run npm run dev to play it; ${options.story ? "a branching story" : "a quiz film"} plays as HTML5 only, delivered with npm run export:html -- --animation ${options.name} or animaguru submit.`] : [`Describe the film in Codex or Claude Code, working in animations/${options.name}.`, `Run npm run dev to preview, or npm run export:html -- --animation ${options.name}.`]
|
|
46381
46288
|
};
|
|
46382
46289
|
}
|
|
46383
46290
|
|
|
@@ -46408,6 +46315,7 @@ var cameraKeyframe = external_exports.object({
|
|
|
46408
46315
|
/** Distance from the subject in scene units. */
|
|
46409
46316
|
distance: external_exports.number().gt(0).max(100)
|
|
46410
46317
|
});
|
|
46318
|
+
var imageKeyframe = external_exports.object({ image: blob, seconds: external_exports.number().min(0).max(20) });
|
|
46411
46319
|
var videoAsset = external_exports.object({
|
|
46412
46320
|
name: base.name,
|
|
46413
46321
|
/** Lip-sync and relight take no prompt, and camera-controls, insert-video, recast and 3d-to-video may leave it out. */
|
|
@@ -46434,6 +46342,8 @@ var videoAsset = external_exports.object({
|
|
|
46434
46342
|
referenceVideos: external_exports.array(blob).max(MAX_REFERENCE_VIDEOS).optional(),
|
|
46435
46343
|
/** The camera's path around the reference image: 2–12 keyframes. */
|
|
46436
46344
|
camera: external_exports.array(cameraKeyframe).min(2).max(MAX_CAMERA_KEYFRAMES).optional(),
|
|
46345
|
+
/** Keyframes-to-video: 1–10 images pinned to moments of the clip, each on a frame of its own. */
|
|
46346
|
+
keyframes: external_exports.array(imageKeyframe).min(1).max(MAX_IMAGE_KEYFRAMES).optional(),
|
|
46437
46347
|
/** Seconds into the source where the inserted scene starts, and where the source resumes after it. */
|
|
46438
46348
|
insertAt: external_exports.number().min(1.625).max(60).optional(),
|
|
46439
46349
|
resumeAt: external_exports.number().gt(1.625).max(60).optional(),
|
|
@@ -46477,6 +46387,7 @@ var TAKES = {
|
|
|
46477
46387
|
references: ["reference-to-video", "insert-video", "recast", "3d-to-video"],
|
|
46478
46388
|
referenceVideos: ["reference-to-video", "insert-video"],
|
|
46479
46389
|
camera: ["camera-controls"],
|
|
46390
|
+
keyframes: ["keyframes-to-video"],
|
|
46480
46391
|
insertAt: ["insert-video"],
|
|
46481
46392
|
resumeAt: ["insert-video"],
|
|
46482
46393
|
colorMatch: ["insert-video"],
|
|
@@ -46487,8 +46398,8 @@ var TAKES = {
|
|
|
46487
46398
|
generatedReferences: ["3d-to-video"],
|
|
46488
46399
|
style: ["style"],
|
|
46489
46400
|
damage: ["style"],
|
|
46490
|
-
aspectRatio: ["text-to-video", "extend-video", "reference-to-video", "relight", "style"],
|
|
46491
|
-
promptExpansion: ["image-to-video", "text-to-video", "extend-video", "reference-to-video", "camera-controls", "insert-video"],
|
|
46401
|
+
aspectRatio: ["text-to-video", "extend-video", "reference-to-video", "relight", "style", "keyframes-to-video"],
|
|
46402
|
+
promptExpansion: ["image-to-video", "text-to-video", "extend-video", "reference-to-video", "camera-controls", "insert-video", "keyframes-to-video"],
|
|
46492
46403
|
seed: ALL.filter((w) => w !== "3d-to-video"),
|
|
46493
46404
|
extendOutput: ["extend-video"]
|
|
46494
46405
|
};
|
|
@@ -46499,7 +46410,8 @@ var NEEDS = {
|
|
|
46499
46410
|
recast: ["source", "references", "subjectConsent"],
|
|
46500
46411
|
relight: ["source", "lighting"],
|
|
46501
46412
|
"3d-to-video": ["source"],
|
|
46502
|
-
style: ["style"]
|
|
46413
|
+
style: ["style"],
|
|
46414
|
+
"keyframes-to-video": ["keyframes"]
|
|
46503
46415
|
};
|
|
46504
46416
|
var LABELS = {
|
|
46505
46417
|
reference: "a starting image",
|
|
@@ -46512,6 +46424,7 @@ var LABELS = {
|
|
|
46512
46424
|
references: "reference images",
|
|
46513
46425
|
referenceVideos: "reference videos",
|
|
46514
46426
|
camera: "camera keyframes",
|
|
46427
|
+
keyframes: "keyframe images",
|
|
46515
46428
|
insertAt: "insertAt",
|
|
46516
46429
|
resumeAt: "resumeAt",
|
|
46517
46430
|
colorMatch: "colorMatch",
|
|
@@ -46597,6 +46510,11 @@ function videoChecks(p, ctx) {
|
|
|
46597
46510
|
const travel = p.camera.slice(1).reduce((sum, k, i2) => sum + Math.abs(k.azimuth - p.camera[i2].azimuth), 0);
|
|
46598
46511
|
if (travel > 360 * MAX_CAMERA_TURNS) issue2("camera", `The camera may travel at most ${MAX_CAMERA_TURNS} full turns around the subject.`);
|
|
46599
46512
|
}
|
|
46513
|
+
if (p.keyframes) {
|
|
46514
|
+
const frames = p.keyframes.map((k) => Math.round(k.seconds * KEYFRAME_FPS));
|
|
46515
|
+
if (new Set(frames).size !== frames.length) issue2("keyframes", `Put each keyframe on a frame of its own; the video has ${KEYFRAME_FPS} frames a second.`);
|
|
46516
|
+
if (p.keyframes.some((k) => k.seconds > seconds2)) issue2("keyframes", "Put each keyframe within the video's length.");
|
|
46517
|
+
}
|
|
46600
46518
|
}
|
|
46601
46519
|
var defaultSeconds = (model, workflow) => {
|
|
46602
46520
|
const r = videoSeconds(model, workflow);
|
|
@@ -46703,9 +46621,10 @@ var videoDetails = external_exports.object({
|
|
|
46703
46621
|
agreement: videoAgreementAcceptance.optional()
|
|
46704
46622
|
}).refine((value) => value.visibility !== "public" || !value.recipients.length, "Public videos do not use a recipient list.");
|
|
46705
46623
|
var videoAnimationName = external_exports.string().regex(/^[a-z0-9][a-z0-9-]{0,79}$/, "Use the animation's folder name.");
|
|
46624
|
+
var videoWorkspace = external_exports.union([external_exports.literal("me"), external_exports.string().regex(/^team_[a-f0-9]{32}$/)]);
|
|
46706
46625
|
var videoSubmission = videoDetails.and(external_exports.object({
|
|
46707
46626
|
/** The workspace that keeps the video; a project's video goes to the project's workspace. */
|
|
46708
|
-
workspace:
|
|
46627
|
+
workspace: videoWorkspace.optional(),
|
|
46709
46628
|
project: external_exports.string().regex(/^prj_[A-Za-z0-9]{8,40}$/).optional(),
|
|
46710
46629
|
animation: videoAnimationName.optional(),
|
|
46711
46630
|
format: external_exports.enum(["html5", "mp4"]),
|
|
@@ -46714,6 +46633,7 @@ var videoSubmission = videoDetails.and(external_exports.object({
|
|
|
46714
46633
|
height: external_exports.number().int().min(1).max(4096).default(1080),
|
|
46715
46634
|
duration: external_exports.number().positive().max(7200).optional()
|
|
46716
46635
|
})).refine((value) => value.bytes <= VIDEO_MAX_BYTES[value.format], "HTML5 ZIPs support up to 64 MiB; MP4s support up to 512 MiB.").refine((value) => !value.animation || value.project, "Name the project of the animation.");
|
|
46636
|
+
var videoMove = external_exports.object({ workspace: videoWorkspace }).strict();
|
|
46717
46637
|
function videoPosterType(bytes) {
|
|
46718
46638
|
if (bytes.length >= 8 && bytes.slice(0, 8).every((b, i2) => b === [137, 80, 78, 71, 13, 10, 26, 10][i2])) return "image/png";
|
|
46719
46639
|
if (bytes.length >= 3 && bytes[0] === 255 && bytes[1] === 216 && bytes[2] === 255) return "image/jpeg";
|
|
@@ -47066,7 +46986,7 @@ var CLOUD_PART_BYTES = 8 * 1024 * 1024;
|
|
|
47066
46986
|
var CLOUD_MANIFEST_PATH = ".animaguru-cloud.json";
|
|
47067
46987
|
var CLOUD_MANIFEST_MAX_BYTES = 16 * 1024 * 1024;
|
|
47068
46988
|
var CLOUD_MAX_FILES = 5e4;
|
|
47069
|
-
var CLOUD_STORAGE_BYTES = { pro: 20 * 1024 ** 3, team: 100 * 1024 ** 3 };
|
|
46989
|
+
var CLOUD_STORAGE_BYTES = { starter: 5 * 1024 ** 3, pro: 20 * 1024 ** 3, team: 100 * 1024 ** 3 };
|
|
47070
46990
|
var cloudProjectId = external_exports.string().regex(/^prj_[A-Za-z0-9]{8,40}$/, "Use a project ID such as prj_\u2026.");
|
|
47071
46991
|
var sha256Hex = external_exports.string().regex(/^[a-f0-9]{64}$/, "Use a lowercase SHA-256 hex digest.");
|
|
47072
46992
|
var gitObjectId = external_exports.string().regex(/^[a-f0-9]{40}$/, "Use a full 40-character version ID.");
|
|
@@ -47820,6 +47740,31 @@ async function changeVideoSubtitles(client, id2, options) {
|
|
|
47820
47740
|
parseSubtitles(content.toString("utf8"));
|
|
47821
47741
|
return client.request(path + language.data, { method: "PUT", body: subtitlesBody(content) });
|
|
47822
47742
|
}
|
|
47743
|
+
async function listVideos(client, options) {
|
|
47744
|
+
const scope = options.scope ?? "mine";
|
|
47745
|
+
const page = options.page ?? 1;
|
|
47746
|
+
if (options.workspace !== void 0 && scope !== "mine") throw new Error("--workspace chooses a library; use it with --scope mine.");
|
|
47747
|
+
if (scope === "public") return client.request(`/api/videos?page=${page}`, { authenticated: false });
|
|
47748
|
+
if (scope !== "mine") return client.request(`/api/v1/videos?scope=${scope}&page=${page}`);
|
|
47749
|
+
const workspace3 = options.workspace === void 0 ? await selectedWorkspace(client.origin) : libraryWorkspace(options.workspace);
|
|
47750
|
+
return client.request(`/api/v1/videos?scope=mine&page=${page}${workspace3 === "me" ? "" : `&workspace=${encodeURIComponent(workspace3)}`}`);
|
|
47751
|
+
}
|
|
47752
|
+
async function moveVideos(client, ids, options) {
|
|
47753
|
+
if (!options.yes) throw new Error("Pass --yes to authorize moving these videos. Everyone in a team can watch the videos in its library.");
|
|
47754
|
+
if (!ids.length) throw new Error("Name the videos to move with their IDs from animaguru videos list.");
|
|
47755
|
+
if (options.workspace === void 0) throw new Error("Pass --workspace personal or a team ID from animaguru workspaces list.");
|
|
47756
|
+
const workspace3 = libraryWorkspace(options.workspace);
|
|
47757
|
+
const moved = [];
|
|
47758
|
+
for (const [index2, id2] of ids.entries()) {
|
|
47759
|
+
try {
|
|
47760
|
+
const { video } = await client.request(`/api/v1/videos/${encodeURIComponent(id2)}/workspace`, { method: "PATCH", body: { workspace: workspace3 } });
|
|
47761
|
+
moved.push({ id: video.id, title: video.title, workspaceId: video.workspaceId });
|
|
47762
|
+
} catch (error62) {
|
|
47763
|
+
return { moved, error: `${id2} was not moved: ${error62 instanceof Error ? error62.message : String(error62)}`, notMoved: ids.slice(index2) };
|
|
47764
|
+
}
|
|
47765
|
+
}
|
|
47766
|
+
return { moved };
|
|
47767
|
+
}
|
|
47823
47768
|
|
|
47824
47769
|
// cli/src/assets.ts
|
|
47825
47770
|
import { mkdir as mkdir8, readFile as readFile9, stat as stat4, writeFile as writeFile8 } from "node:fs/promises";
|
|
@@ -47846,6 +47791,7 @@ var extras = {
|
|
|
47846
47791
|
"middle-reference",
|
|
47847
47792
|
"middle-seconds",
|
|
47848
47793
|
"camera",
|
|
47794
|
+
"keyframes",
|
|
47849
47795
|
"insert-at",
|
|
47850
47796
|
"resume-at",
|
|
47851
47797
|
"color-match",
|
|
@@ -47880,11 +47826,16 @@ var regions = (value) => value?.split(";").filter((region) => region.trim()).map
|
|
|
47880
47826
|
if (rest.length || [x2, y, width, height].some((n) => !Number.isFinite(n))) throw new Error(`--mask regions are x,y,width,height in fractions of Image 1, separated by semicolons; "${region.trim()}" is not.`);
|
|
47881
47827
|
return { x: x2, y, width, height };
|
|
47882
47828
|
});
|
|
47883
|
-
var
|
|
47829
|
+
var cameraKeyframes = (value) => value?.split(";").filter((frame) => frame.trim()).map((frame) => {
|
|
47884
47830
|
const [time3, azimuth, elevation, distance, ...rest] = frame.split(",").map((n) => n.trim() ? Number(n) : NaN);
|
|
47885
47831
|
if (rest.length || [time3, azimuth, elevation, distance].some((n) => !Number.isFinite(n))) throw new Error(`--camera keyframes are time,azimuth,elevation,distance (time 0\u20131, angles in degrees), separated by semicolons; "${frame.trim()}" is not.`);
|
|
47886
47832
|
return { time: time3, azimuth, elevation, distance };
|
|
47887
47833
|
});
|
|
47834
|
+
var imageKeyframes = (value) => value?.split(";").filter((frame) => frame.trim()).map((frame) => {
|
|
47835
|
+
const [seconds2, image, ...rest] = frame.split(",").map((part) => part.trim());
|
|
47836
|
+
if (rest.length || !image || !seconds2 || !Number.isFinite(Number(seconds2))) throw new Error(`--keyframes are seconds,image SHA pairs separated by semicolons, e.g. "0,SHA;5,SHA"; "${frame.trim()}" is not.`);
|
|
47837
|
+
return { image, seconds: Number(seconds2) };
|
|
47838
|
+
});
|
|
47888
47839
|
function assetFromFlags(flags) {
|
|
47889
47840
|
if (flags.type === "image" && flags.model) throw new Error("Image models are managed by staff. Omit --model.");
|
|
47890
47841
|
for (const name2 of Object.values(extras).flat()) {
|
|
@@ -47907,7 +47858,8 @@ function assetFromFlags(flags) {
|
|
|
47907
47858
|
referenceVideos: shas(flags["reference-videos"]),
|
|
47908
47859
|
middleReference: flags["middle-reference"],
|
|
47909
47860
|
middleSeconds: flags["middle-seconds"],
|
|
47910
|
-
camera:
|
|
47861
|
+
camera: cameraKeyframes(flags.camera),
|
|
47862
|
+
keyframes: imageKeyframes(flags.keyframes),
|
|
47911
47863
|
insertAt: flags["insert-at"],
|
|
47912
47864
|
resumeAt: flags["resume-at"],
|
|
47913
47865
|
colorMatch: flags["color-match"],
|
|
@@ -48617,8 +48569,8 @@ async function youtubeLogin(options) {
|
|
|
48617
48569
|
let receive;
|
|
48618
48570
|
let fail;
|
|
48619
48571
|
let used = false;
|
|
48620
|
-
const callback = new Promise((
|
|
48621
|
-
receive =
|
|
48572
|
+
const callback = new Promise((resolve18, reject) => {
|
|
48573
|
+
receive = resolve18;
|
|
48622
48574
|
fail = reject;
|
|
48623
48575
|
});
|
|
48624
48576
|
void callback.catch(() => {
|
|
@@ -48656,9 +48608,9 @@ async function youtubeLogin(options) {
|
|
|
48656
48608
|
response.end("Authorization received. Return to your AnimaGuru terminal.");
|
|
48657
48609
|
receive(code);
|
|
48658
48610
|
});
|
|
48659
|
-
await new Promise((
|
|
48611
|
+
await new Promise((resolve18, reject) => {
|
|
48660
48612
|
server.once("error", reject);
|
|
48661
|
-
server.listen(0, "127.0.0.1",
|
|
48613
|
+
server.listen(0, "127.0.0.1", resolve18);
|
|
48662
48614
|
});
|
|
48663
48615
|
const address = server.address();
|
|
48664
48616
|
if (!address || typeof address === "string") throw new Error("Cannot open the local YouTube authorization listener.");
|
|
@@ -48693,7 +48645,7 @@ async function youtubeLogin(options) {
|
|
|
48693
48645
|
clearTimeout(timer);
|
|
48694
48646
|
options.signal?.removeEventListener("abort", abort);
|
|
48695
48647
|
server.closeAllConnections();
|
|
48696
|
-
await new Promise((
|
|
48648
|
+
await new Promise((resolve18) => server.close(() => resolve18()));
|
|
48697
48649
|
}
|
|
48698
48650
|
});
|
|
48699
48651
|
}
|
|
@@ -49797,6 +49749,114 @@ async function referenceGet(client, id2, options) {
|
|
|
49797
49749
|
return report(options.wait ? await finished(client, analysis, options.log) : analysis, options);
|
|
49798
49750
|
}
|
|
49799
49751
|
|
|
49752
|
+
// cli/src/curl.ts
|
|
49753
|
+
import { createWriteStream as createWriteStream2, openAsBlob as openAsBlob2 } from "node:fs";
|
|
49754
|
+
import { readFile as readFile18, unlink as unlink3 } from "node:fs/promises";
|
|
49755
|
+
import { extname as extname4, resolve as resolve16 } from "node:path";
|
|
49756
|
+
import { Readable as Readable2 } from "node:stream";
|
|
49757
|
+
import { pipeline as pipeline2 } from "node:stream/promises";
|
|
49758
|
+
function curlUrl(server, target2) {
|
|
49759
|
+
const origin = serverUrl(server);
|
|
49760
|
+
const url3 = target2.startsWith("/") && !target2.startsWith("//") ? new URL(target2, origin) : URL.canParse(target2) ? new URL(target2) : null;
|
|
49761
|
+
if (!url3 || url3.origin !== origin || url3.username || url3.password) throw new Error(`animaguru curl calls only ${origin}. Pass a path such as /api/v1/me.`);
|
|
49762
|
+
return url3;
|
|
49763
|
+
}
|
|
49764
|
+
function curlHeaders(lines = []) {
|
|
49765
|
+
const headers = new Headers();
|
|
49766
|
+
for (const line of lines) {
|
|
49767
|
+
const colon = line.indexOf(":");
|
|
49768
|
+
const name2 = colon > 0 ? line.slice(0, colon).trim() : "";
|
|
49769
|
+
if (!name2) throw new Error(`Write headers as "Name: value", not "${line}".`);
|
|
49770
|
+
if (/^(authorization|host)$/i.test(name2)) throw new Error(`Leave out the ${name2} header; animaguru curl sends the signed-in session's.`);
|
|
49771
|
+
headers.append(name2, line.slice(colon + 1).trim());
|
|
49772
|
+
}
|
|
49773
|
+
return headers;
|
|
49774
|
+
}
|
|
49775
|
+
var UPLOAD_TYPES = {
|
|
49776
|
+
".mp4": "video/mp4",
|
|
49777
|
+
".webm": "video/webm",
|
|
49778
|
+
".mov": "video/quicktime",
|
|
49779
|
+
".jpg": "image/jpeg",
|
|
49780
|
+
".jpeg": "image/jpeg",
|
|
49781
|
+
".png": "image/png",
|
|
49782
|
+
".webp": "image/webp",
|
|
49783
|
+
".gif": "image/gif",
|
|
49784
|
+
".json": "application/json",
|
|
49785
|
+
".txt": "text/plain"
|
|
49786
|
+
};
|
|
49787
|
+
var isText = (type) => /^(text\/|application\/([\w.+-]*\+)?(json|xml)\b|application\/(javascript|x-www-form-urlencoded)\b)/i.test(type);
|
|
49788
|
+
var readStdin = async () => {
|
|
49789
|
+
const chunks = [];
|
|
49790
|
+
for await (const chunk of process.stdin) chunks.push(Buffer.from(chunk));
|
|
49791
|
+
return Buffer.concat(chunks);
|
|
49792
|
+
};
|
|
49793
|
+
async function bodyValue(value, io) {
|
|
49794
|
+
if (value === "@-") return (io.stdin ?? readStdin)();
|
|
49795
|
+
return value.startsWith("@") ? readFile18(resolve16(value.slice(1))) : value;
|
|
49796
|
+
}
|
|
49797
|
+
var parsesAsJson = (body) => {
|
|
49798
|
+
try {
|
|
49799
|
+
JSON.parse(body.toString());
|
|
49800
|
+
return true;
|
|
49801
|
+
} catch {
|
|
49802
|
+
return false;
|
|
49803
|
+
}
|
|
49804
|
+
};
|
|
49805
|
+
async function curl(options, io) {
|
|
49806
|
+
const url3 = curlUrl(options.server, options.target);
|
|
49807
|
+
const headers = curlHeaders(options.header);
|
|
49808
|
+
if ([options.data, options.json, options.uploadFile].filter((value) => value !== void 0).length > 1) throw new Error("Use one of --data, --json and --upload-file.");
|
|
49809
|
+
let body;
|
|
49810
|
+
if (options.uploadFile !== void 0) {
|
|
49811
|
+
const file2 = resolve16(options.uploadFile);
|
|
49812
|
+
body = await openAsBlob2(file2);
|
|
49813
|
+
if (!headers.has("content-type")) headers.set("Content-Type", UPLOAD_TYPES[extname4(file2).toLowerCase()] ?? "application/octet-stream");
|
|
49814
|
+
} else if (options.json !== void 0) {
|
|
49815
|
+
body = await bodyValue(options.json, io);
|
|
49816
|
+
if (!headers.has("content-type")) headers.set("Content-Type", "application/json");
|
|
49817
|
+
if (!headers.has("accept")) headers.set("Accept", "application/json");
|
|
49818
|
+
} else if (options.data !== void 0) {
|
|
49819
|
+
body = await bodyValue(options.data, io);
|
|
49820
|
+
if (!headers.has("content-type")) headers.set("Content-Type", parsesAsJson(body) ? "application/json" : "application/x-www-form-urlencoded");
|
|
49821
|
+
}
|
|
49822
|
+
headers.set("Authorization", `Bearer ${await sessionToken(url3.origin)}`);
|
|
49823
|
+
if (!headers.has("origin")) headers.set("Origin", url3.origin);
|
|
49824
|
+
const method = (options.request ?? (options.uploadFile !== void 0 ? "PUT" : body !== void 0 ? "POST" : "GET")).toUpperCase();
|
|
49825
|
+
const timeout = AbortSignal.timeout(options.maxTime * 1e3);
|
|
49826
|
+
const response = await fetch(url3, { method, headers, body, redirect: "manual", signal: io.signal ? AbortSignal.any([io.signal, timeout]) : timeout });
|
|
49827
|
+
if (options.include) io.stdout([`HTTP ${response.status} ${response.statusText}`.trim(), ...[...response.headers].map(([name2, value]) => `${name2}: ${value}`), ""].join("\n"));
|
|
49828
|
+
const type = response.headers.get("content-type") ?? "";
|
|
49829
|
+
const failed = options.fail && response.status >= 400;
|
|
49830
|
+
if (options.output !== void 0 && !failed) {
|
|
49831
|
+
const file2 = resolve16(options.output);
|
|
49832
|
+
let bytes = 0;
|
|
49833
|
+
try {
|
|
49834
|
+
await pipeline2(
|
|
49835
|
+
response.body ? Readable2.fromWeb(response.body) : Readable2.from([]),
|
|
49836
|
+
async function* (chunks) {
|
|
49837
|
+
for await (const chunk of chunks) {
|
|
49838
|
+
bytes += chunk.length;
|
|
49839
|
+
yield chunk;
|
|
49840
|
+
}
|
|
49841
|
+
},
|
|
49842
|
+
createWriteStream2(file2, { flags: "wx" })
|
|
49843
|
+
);
|
|
49844
|
+
} catch (error62) {
|
|
49845
|
+
if (error62.code === "EEXIST") throw new Error(`${options.output} already exists; choose another --output.`);
|
|
49846
|
+
await unlink3(file2).catch(() => void 0);
|
|
49847
|
+
throw error62;
|
|
49848
|
+
}
|
|
49849
|
+
io.stdout(JSON.stringify({ status: response.status, contentType: type || null, bytes, file: file2 }));
|
|
49850
|
+
} else if (!response.body || isText(type) || failed) {
|
|
49851
|
+
const text2 = await response.text();
|
|
49852
|
+
if (text2) io.stdout(text2.replace(/\n$/, ""));
|
|
49853
|
+
} else {
|
|
49854
|
+
await response.body.cancel();
|
|
49855
|
+
throw new Error(`The response is ${type || "binary"} (HTTP ${response.status}). Save it with -o FILE.`);
|
|
49856
|
+
}
|
|
49857
|
+
if (failed) throw new Error(`HTTP ${response.status}${response.statusText ? ` ${response.statusText}` : ""} from ${method} ${url3.pathname}.`);
|
|
49858
|
+
}
|
|
49859
|
+
|
|
49800
49860
|
// cli/src/commands.ts
|
|
49801
49861
|
var assetModelIds = [...SPRITE_VIDEO_MODEL_IDS, ...MUSIC_MODELS.map((m) => m.id), ...SPEECH_MODELS.map((m) => m.id), ...SFX_MODELS.map((m) => m.id)];
|
|
49802
49862
|
var assetAuthorizationFlags = [
|
|
@@ -49847,12 +49907,12 @@ var workspaceQuery = async (client) => {
|
|
|
49847
49907
|
async function openBrowser(url3) {
|
|
49848
49908
|
const command = process.platform === "darwin" ? "open" : process.platform === "win32" ? "rundll32" : "xdg-open";
|
|
49849
49909
|
const args = process.platform === "win32" ? ["url.dll,FileProtocolHandler", url3] : [url3];
|
|
49850
|
-
await new Promise((
|
|
49910
|
+
await new Promise((resolve18) => {
|
|
49851
49911
|
const child = spawn3(command, args, { stdio: "ignore", detached: true });
|
|
49852
|
-
child.once("error", () =>
|
|
49912
|
+
child.once("error", () => resolve18());
|
|
49853
49913
|
child.once("spawn", () => {
|
|
49854
49914
|
child.unref();
|
|
49855
|
-
|
|
49915
|
+
resolve18();
|
|
49856
49916
|
});
|
|
49857
49917
|
});
|
|
49858
49918
|
}
|
|
@@ -49902,10 +49962,17 @@ function createCli() {
|
|
|
49902
49962
|
{ name: "out", type: "string", description: "Write the analysis to this file instead" },
|
|
49903
49963
|
{ name: "wait", type: "boolean", description: "Wait until the analysis finishes" },
|
|
49904
49964
|
urlFlag
|
|
49905
|
-
).action(async ({ args, flags, stdout, stderr, signal }) => out(stdout, await referenceGet(new Client(flags.url, signal), args.id, { ...flags, log: stderr })))))).command("create", (creation) => creation.args({ name: "name", type: "string", description: "Legacy standalone project folder; prefer create workspace NAME" }).
|
|
49965
|
+
).action(async ({ args, flags, stdout, stderr, signal }) => out(stdout, await referenceGet(new Client(flags.url, signal), args.id, { ...flags, log: stderr })))))).command("create", (creation) => creation.args({ name: "name", type: "string", description: "Legacy standalone project folder; prefer create workspace NAME" }).flags(
|
|
49966
|
+
{ name: "story", type: "boolean", description: "Start a branching story: viewers choose its path, and it plays as HTML5 only" },
|
|
49967
|
+
{ name: "quiz", type: "boolean", description: "Start a quiz film: questions pause the film at checkpoints and a final quiz follows it (HTML5 only, never MP4)" }
|
|
49968
|
+
).action(async ({ args, flags, stdout, stderr, signal }) => {
|
|
49906
49969
|
if (!args.name) throw new Error("Use animaguru create workspace NAME, or animaguru create animation NAME inside a workspace.");
|
|
49907
|
-
out(stdout, await createVideoProject({ directory: args.name, log: stderr, signal }));
|
|
49908
|
-
}).add(defineCommand("workspace", { description: "Create a shared-brand workspace with one player, dependencies, skills and export toolchain" }, (command) => command.args({ name: "name", type: "string", required: true }).flags({ name: "skip-install", type: "boolean", default: false, description: "Create source files without installing dependencies" }).action(async ({ args, flags, stdout, stderr, signal }) => out(stdout, await createWorkspace({ directory: args.name, install: !flags["skip-install"], log: stderr, signal }))))).add(defineCommand("animation", { description: "Add an animation to a workspace without copying shared runtime, dependencies or skills" }, (command) => command.args({ name: "name", type: "string", required: true }).flags(
|
|
49970
|
+
out(stdout, await createVideoProject({ directory: args.name, story: flags.story, quiz: flags.quiz, log: stderr, signal }));
|
|
49971
|
+
}).add(defineCommand("workspace", { description: "Create a shared-brand workspace with one player, dependencies, skills and export toolchain" }, (command) => command.args({ name: "name", type: "string", required: true }).flags({ name: "skip-install", type: "boolean", default: false, description: "Create source files without installing dependencies" }).action(async ({ args, flags, stdout, stderr, signal }) => out(stdout, await createWorkspace({ directory: args.name, install: !flags["skip-install"], log: stderr, signal }))))).add(defineCommand("animation", { description: "Add an animation to a workspace without copying shared runtime, dependencies or skills" }, (command) => command.args({ name: "name", type: "string", required: true }).flags(
|
|
49972
|
+
{ name: "workspace", type: "string", description: "Workspace folder; otherwise finds it from the current directory" },
|
|
49973
|
+
{ name: "story", type: "boolean", description: "Start a branching story: viewers choose its path at choices, and it plays as HTML5 only (never MP4)" },
|
|
49974
|
+
{ name: "quiz", type: "boolean", description: "Start a quiz film (an educational explainer): checkpoints pause the film to ask questions, a final quiz and score follow its end; it plays as HTML5 only (never MP4)" }
|
|
49975
|
+
).action(async ({ args, flags, stdout, stderr, signal }) => out(stdout, await createAnimation({ name: args.name, workspace: flags.workspace, story: flags.story, quiz: flags.quiz, log: stderr, signal })))))).add(defineCommand("submit", { description: "Upload a finished HTML5 animation or MP4 to AnimaGuru; private unless --visibility public" }, (command) => command.args({ name: "directory", type: "string", default: ".", description: "Video project folder; defaults to current folder" }).flags(
|
|
49909
49976
|
{ name: "url", type: "string", default: "https://animaguru.com", env: { name: "ANIMAGURU_URL" } },
|
|
49910
49977
|
{ name: "animation", type: "string", description: "Animation name when submitting from a multi-animation workspace" },
|
|
49911
49978
|
{ name: "workspace", type: "string", description: "Video library: personal or a team ID; defaults to the selected workspace, or a synced project's" },
|
|
@@ -49919,11 +49986,24 @@ function createCli() {
|
|
|
49919
49986
|
{ name: "subtitle-language", type: "string", description: "Subtitle language code such as en or tr; defaults to the MP4's export receipt or film.config.json" },
|
|
49920
49987
|
{ name: "yes", type: "boolean", description: "Authorize upload and chosen sharing" },
|
|
49921
49988
|
{ name: "dry-run", type: "boolean", description: "Inspect locally without uploading" }
|
|
49922
|
-
).action(async ({ args, flags, stdout, stderr, signal }) => out(stdout, await submitVideo(new Client(flags.url, signal), { ...flags, directory: args.directory, log: stderr, signal }))))).command("videos", (videos) => videos.add(defineCommand("list", { description: "List
|
|
49989
|
+
).action(async ({ args, flags, stdout, stderr, signal }) => out(stdout, await submitVideo(new Client(flags.url, signal), { ...flags, directory: args.directory, log: stderr, signal }))))).command("videos", (videos) => videos.add(defineCommand("list", { description: "List a video library (the selected workspace's by default), privately shared videos, or the public gallery" }, (command) => command.flags(
|
|
49990
|
+
{ name: "url", type: "string", default: "https://animaguru.com", env: { name: "ANIMAGURU_URL" } },
|
|
49991
|
+
{ name: "scope", type: "string", choices: ["mine", "shared", "public"], default: "mine" },
|
|
49992
|
+
{ name: "page", type: "number", default: 1 },
|
|
49993
|
+
{ name: "workspace", type: "string", description: "The library to list with --scope mine: 'personal' or a team ID from workspaces list" }
|
|
49994
|
+
).action(async ({ flags, stdout, signal }) => out(stdout, await listVideos(new Client(flags.url, signal), flags))))).add(defineCommand("get", { description: "Get a video's details or the state of your unfinished upload" }, (command) => command.args({ name: "id", type: "string", required: true }).flags({ name: "url", type: "string", default: "https://animaguru.com", env: { name: "ANIMAGURU_URL" } }).action(async ({ args, flags, stdout, signal }) => out(stdout, await new Client(flags.url, signal).request(`/api/v1/videos/${encodeURIComponent(args.id)}`))))).add(defineCommand("complete", { description: "Retry completion of an already uploaded animation" }, (command) => command.args({ name: "id", type: "string", required: true }).flags({ name: "url", type: "string", default: "https://animaguru.com", env: { name: "ANIMAGURU_URL" } }).action(async ({ args, flags, stdout, signal }) => out(stdout, await new Client(flags.url, signal).request(`/api/v1/videos/${encodeURIComponent(args.id)}/complete`, { method: "POST", timeout: 3e5 }))))).add(defineCommand("share", { description: "Replace a video's visibility and recipient list" }, (command) => command.args({ name: "id", type: "string", required: true }).flags({ name: "url", type: "string", default: "https://animaguru.com", env: { name: "ANIMAGURU_URL" } }, { name: "visibility", type: "string", required: true, choices: ["private", "public"] }, { name: "share", type: "string" }, { name: "yes", type: "boolean", required: true }).action(async ({ args, flags, stdout, signal }) => {
|
|
49923
49995
|
if (!flags.yes) throw new Error("Pass --yes to authorize changing who can see this video.");
|
|
49924
49996
|
const client = new Client(flags.url, signal);
|
|
49925
49997
|
const { video } = await client.request(`/api/videos/${encodeURIComponent(args.id)}`);
|
|
49926
49998
|
out(stdout, await client.request(`/api/v1/videos/${encodeURIComponent(args.id)}`, { method: "PATCH", body: videoDetails.parse({ ...video, visibility: flags.visibility, recipients: sharingEmails(flags.share) }) }));
|
|
49999
|
+
}))).add(defineCommand("move", { description: "Move your videos to another library, Personal or a team you belong to; their links, subtitles and sharing stay" }, (command) => command.args({ name: "ids", type: "string", variadic: true, description: "Video IDs from videos list" }).flags(
|
|
50000
|
+
{ name: "url", type: "string", default: "https://animaguru.com", env: { name: "ANIMAGURU_URL" } },
|
|
50001
|
+
{ name: "workspace", type: "string", required: true, description: "'personal' or a team ID from workspaces list" },
|
|
50002
|
+
{ name: "yes", type: "boolean", required: true }
|
|
50003
|
+
).action(async ({ args, flags, stdout, signal }) => {
|
|
50004
|
+
const result = await moveVideos(new Client(flags.url, signal), args.ids ?? [], flags);
|
|
50005
|
+
out(stdout, result);
|
|
50006
|
+
if (result.error) throw new Error(result.error);
|
|
49927
50007
|
}))).add(defineCommand("subtitles", { description: "Add or replace an MP4's subtitles in one language, or remove a language" }, (command) => command.args({ name: "id", type: "string", required: true }).flags(
|
|
49928
50008
|
{ name: "url", type: "string", default: "https://animaguru.com", env: { name: "ANIMAGURU_URL" } },
|
|
49929
50009
|
{ name: "file", type: "string", description: ".srt or .vtt subtitle file" },
|
|
@@ -49933,7 +50013,7 @@ function createCli() {
|
|
|
49933
50013
|
).action(async ({ args, flags, stdout, signal }) => out(stdout, await changeVideoSubtitles(new Client(flags.url, signal), args.id, flags))))).add(defineCommand("delete", { description: "Remove a video and its hosted files" }, (command) => command.args({ name: "id", type: "string", required: true }).flags({ name: "url", type: "string", default: "https://animaguru.com", env: { name: "ANIMAGURU_URL" } }, { name: "yes", type: "boolean", required: true }).action(async ({ args, flags, stdout, signal }) => {
|
|
49934
50014
|
if (!flags.yes) throw new Error("Pass --yes to delete the video.");
|
|
49935
50015
|
out(stdout, await new Client(flags.url, signal).request(`/api/v1/videos/${encodeURIComponent(args.id)}`, { method: "DELETE" }));
|
|
49936
|
-
})))).command("cloud", (cloud) => cloud.add(defineCommand("list", { description: "List projects with synced animations in the selected workspace, with plan and storage (Individual Pro and Team)" }, (command) => command.flags({ name: "url", type: "string", default: "https://animaguru.com", env: { name: "ANIMAGURU_URL" } }).action(async ({ flags, stdout, signal }) => {
|
|
50016
|
+
})))).command("cloud", (cloud) => cloud.add(defineCommand("list", { description: "List projects with synced animations in the selected workspace, with plan and storage (Individual Starter, Individual Pro and Team)" }, (command) => command.flags({ name: "url", type: "string", default: "https://animaguru.com", env: { name: "ANIMAGURU_URL" } }).action(async ({ flags, stdout, signal }) => {
|
|
49937
50017
|
const client = new Client(flags.url, signal);
|
|
49938
50018
|
out(stdout, await client.request(`/api/v1/cloud${await workspaceQuery(client)}`));
|
|
49939
50019
|
}))).add(defineCommand("link", { description: "Sync this video workspace with a project's animations: --project for an existing project, or a new project in the selected workspace" }, (command) => command.flags(
|
|
@@ -49951,7 +50031,31 @@ function createCli() {
|
|
|
49951
50031
|
}))).add(defineCommand("restore", { description: "Make the folder, or --path parts of it, match an earlier version; push afterwards to save it as the newest" }, (command) => command.args({ name: "version", type: "string", required: true, description: "Version ID or its first characters" }).flags({ name: "dir", type: "string", default: "." }, { name: "path", type: "string", multiple: true, description: "File or folder to restore; repeatable" }, { name: "yes", type: "boolean", description: "Replace unsaved local changes (copies are kept)" }).action(async ({ args, flags, stdout, stderr, signal }) => out(stdout, await restoreCloud({ dir: flags.dir, version: args.version, paths: flags.path, yes: flags.yes, log: stderr, signal }))))).add(defineCommand("clone", { description: "Download the newest version of a project's animations into a new folder" }, (command) => command.args({ name: "id", type: "string", required: true, description: "Project ID" }, { name: "directory", type: "string", description: "New folder; defaults to the project name" }).flags({ name: "url", type: "string", default: "https://animaguru.com", env: { name: "ANIMAGURU_URL" } }).action(async ({ args, flags, stdout, stderr, signal }) => out(stdout, await cloneCloud(new Client(flags.url, signal), { id: args.id, dir: args.directory, log: stderr, signal }))))).add(defineCommand("unlink", { description: "Stop syncing this folder; the project's animations and versions stay" }, (command) => command.flags({ name: "dir", type: "string", default: "." }).action(async ({ flags, stdout }) => out(stdout, await unlinkCloud({ dir: flags.dir }))))).add(defineCommand("delete", { description: "Delete a project's animations with all their versions, drafts and stored files; the project and its assets stay. Requires --yes" }, (command) => command.args({ name: "id", type: "string", required: true, description: "Project ID" }).flags({ name: "url", type: "string", default: "https://animaguru.com", env: { name: "ANIMAGURU_URL" } }, { name: "yes", type: "boolean", required: true }).action(async ({ args, flags, stdout, signal }) => {
|
|
49952
50032
|
if (!flags.yes) throw new Error("Pass --yes to delete the project's animations and every saved version.");
|
|
49953
50033
|
out(stdout, await new Client(flags.url, signal).request(`/api/v1/cloud/${encodeURIComponent(args.id)}`, { method: "DELETE" }));
|
|
49954
|
-
})))).
|
|
50034
|
+
})))).add(defineCommand("curl", { description: "Call an AnimaGuru API path as the signed-in user with curl's flags; the server and the session's token are added for you. Prints the response body, or saves it with -o" }, (command) => command.args({ name: "path", type: "string", required: true, description: "API path such as /api/v1/me (with any query string), or a full URL on the server" }).flags(
|
|
50035
|
+
{ name: "request", short: "X", type: "string", description: "HTTP method; GET by default, POST with --data or --json, PUT with --upload-file" },
|
|
50036
|
+
{ name: "header", short: "H", type: "string", multiple: true, description: '"Name: value" request header; repeatable. Authorization is added for you' },
|
|
50037
|
+
{ name: "data", short: "d", type: "string", aliases: ["data-binary"], description: "Request body: text, @FILE or @- for standard input. JSON is sent as application/json" },
|
|
50038
|
+
{ name: "json", type: "string", description: "JSON body (text, @FILE or @-) with JSON Content-Type and Accept headers" },
|
|
50039
|
+
{ name: "upload-file", short: "T", type: "string", description: "Stream a file as the body (PUT unless -X); Content-Type follows its extension unless -H sets one" },
|
|
50040
|
+
{ name: "output", short: "o", type: "string", description: "Save the response body to a new file and print a JSON summary; needed for video and images" },
|
|
50041
|
+
{ name: "include", short: "i", type: "boolean", noNegate: true, description: "Print the status line and response headers first" },
|
|
50042
|
+
{ name: "fail", short: "f", type: "boolean", default: true, description: "Exit non-zero on HTTP 4xx and 5xx after printing the body; --no-fail exits 0 like plain curl" },
|
|
50043
|
+
{ name: "silent", short: "s", type: "boolean", noNegate: true, description: "Accepted for curl compatibility; nothing else is printed anyway" },
|
|
50044
|
+
{ name: "max-time", short: "m", type: "number", default: 600, description: "Maximum seconds for the whole request" },
|
|
50045
|
+
{ name: "server", type: "string", default: "https://animaguru.com", env: { name: "ANIMAGURU_URL" }, description: "Server origin" }
|
|
50046
|
+
).action(async ({ args, flags, stdout, signal }) => curl({
|
|
50047
|
+
server: flags.server,
|
|
50048
|
+
target: args.path,
|
|
50049
|
+
request: flags.request,
|
|
50050
|
+
header: flags.header,
|
|
50051
|
+
data: flags.data,
|
|
50052
|
+
json: flags.json,
|
|
50053
|
+
uploadFile: flags["upload-file"],
|
|
50054
|
+
output: flags.output,
|
|
50055
|
+
include: flags.include,
|
|
50056
|
+
fail: flags.fail,
|
|
50057
|
+
maxTime: flags["max-time"]
|
|
50058
|
+
}, { stdout, signal })))).command("auth", (auth) => auth.add(defineCommand("login", { description: "Approve a browser sign-in and save a seven-day CLI session" }, (command) => command.flags({ name: "url", type: "string", default: "https://animaguru.com", env: { name: "ANIMAGURU_URL" }, description: "Server origin" }).flags({ name: "browser", type: "boolean", default: true, description: "Open the browser; --no-browser prints the URL only" }).action(async ({ flags, stdout, stderr, signal }) => {
|
|
49955
50059
|
const client = new Client(flags.url, signal);
|
|
49956
50060
|
const device = await client.request("/api/auth/device/code", { method: "POST", authenticated: false, body: { client_id: CLI_CLIENT_ID } });
|
|
49957
50061
|
stderr(`Open ${device.verification_uri_complete}
|
|
@@ -50073,11 +50177,11 @@ Confirm code: ${device.user_code}`);
|
|
|
50073
50177
|
{ name: "name", type: "string", required: true },
|
|
50074
50178
|
{ name: "prompt", type: "string", description: "Description to generate, or verbatim transcript for speech; video lip-sync and relight take none, and camera-controls, insert-video, recast and 3d-to-video may leave it out" },
|
|
50075
50179
|
{ name: "model", type: "string", choices: assetModelIds, description: "Model for the chosen asset type" },
|
|
50076
|
-
{ name: "aspect-ratio", type: "string", choices: [.../* @__PURE__ */ new Set([...IMAGE_ASPECT_RATIOS, "auto", ...VIDEO_ASPECT_RATIOS])], description: "Image: 1:1 (default), 3:2, 2:3, 16:9 or 9:16; a masked edit keeps Image 1's shape. Video from text or a style without --reference: 21:9\u20139:16 (default 16:9). Extension, reference-to-video and
|
|
50180
|
+
{ name: "aspect-ratio", type: "string", choices: [.../* @__PURE__ */ new Set([...IMAGE_ASPECT_RATIOS, "auto", ...VIDEO_ASPECT_RATIOS])], description: "Image: 1:1 (default), 3:2, 2:3, 16:9 or 9:16; a masked edit keeps Image 1's shape. Video from text or a style without --reference: 21:9\u20139:16 (default 16:9). Extension, reference-to-video, relight and keyframes-to-video: auto (default, the inputs' shape) or a ratio to crop to" },
|
|
50077
50181
|
{ name: "transparent", type: "boolean", description: "Image: transparent background" },
|
|
50078
50182
|
{ name: "references", type: "string", description: "Comma-separated blob SHAs of this project's images or sprite art, named Image 1, Image 2\u2026 in the prompt. Image: up to 4 to follow, such as a character sheet. Video: subjects or looks to keep (reference-to-video and insert-video up to 9, 3d-to-video up to 8), or recast's new people, one photo each (up to 4)" },
|
|
50079
50183
|
{ name: "mask", type: "string", description: "Image: edit only these rectangles of Image 1 (the first --references image), as x,y,width,height fractions of its size from the top left, up to 8 separated by semicolons; the rest is kept" },
|
|
50080
|
-
{ name: "workflow", type: "string", choices: VIDEO_WORKFLOWS, description: "Video: image-to-video (default), text-to-video or extend-video; H3 Max also reference-to-video, lip-sync, camera-controls, insert-video, recast, relight, 3d-to-video and style, as the model's videoProfiles allow" },
|
|
50184
|
+
{ name: "workflow", type: "string", choices: VIDEO_WORKFLOWS, description: "Video: image-to-video (default), text-to-video or extend-video; H3 Max also reference-to-video, lip-sync, camera-controls, insert-video, recast, relight, 3d-to-video and style, FLUX 3 Max keyframes-to-video, as the model's videoProfiles allow" },
|
|
50081
50185
|
{ name: "reference", type: "string", description: "Video: starting image blob SHA from this project's assets or sprites; the speaking face for lip-sync, the image the camera moves around for camera-controls" },
|
|
50082
50186
|
{ name: "end-reference", type: "string", description: "Video: ending image blob SHA; models with endFrameOnly accept it without --reference" },
|
|
50083
50187
|
{ name: "middle-reference", type: "string", description: "Reference-to-video: image blob SHA reached at --middle-seconds; needs --reference and --end-reference, 480p or 768p" },
|
|
@@ -50087,6 +50191,7 @@ Confirm code: ${device.user_code}`);
|
|
|
50087
50191
|
{ name: "audio-references", type: "string", description: "Extension or reference-to-video: up to 3 comma-separated audio asset blob SHAs (2\u201315 s each, 15 s total) guiding the sound; refer to them as Audio 1, Audio 2\u2026 in the prompt" },
|
|
50088
50192
|
{ name: "reference-videos", type: "string", description: "Reference-to-video or insert-video: up to 3 comma-separated video asset blob SHAs (2\u201315 s each, 15 s total) guiding motion, named Video 1, Video 2\u2026 in the prompt" },
|
|
50089
50193
|
{ name: "camera", type: "string", description: 'Camera-controls: 2\u201312 keyframes time,azimuth,elevation,distance (time 0\u20131, angles in degrees, elevation \u221290\u201390) separated by semicolons, e.g. "0,0,0,1;1,90,15,1.2"' },
|
|
50194
|
+
{ name: "keyframes", type: "string", description: 'Keyframes-to-video (FLUX 3 Max): 1\u201310 seconds,image-SHA pairs separated by semicolons, e.g. "0,SHA;2.5,SHA;5,SHA"; each image is reached at its second, on a frame of its own of the 24 fps clip, within --seconds' },
|
|
50090
50195
|
{ name: "insert-at", type: "number", description: "Insert-video: seconds into --source where the new scene starts (1.625\u201360)" },
|
|
50091
50196
|
{ name: "resume-at", type: "number", description: "Insert-video: seconds into --source where it resumes after the scene" },
|
|
50092
50197
|
{ name: "color-match", type: "boolean", description: "Insert-video: match the scene's colors to the source (default on; --no-color-match turns it off)" },
|
|
@@ -50097,10 +50202,10 @@ Confirm code: ${device.user_code}`);
|
|
|
50097
50202
|
{ name: "generated-references", type: "number", description: "3d-to-video without --references: how many reference images it may draw, 1\u20138 (default 2)" },
|
|
50098
50203
|
{ name: "style", type: "string", choices: VIDEO_STYLES, description: "Style workflow: the preset look, at 768p for 5\u201315 whole seconds" },
|
|
50099
50204
|
{ name: "damage", type: "string", choices: VHS_DAMAGE_LEVELS, description: "VHS style: tape damage (fal's default medium)" },
|
|
50100
|
-
{ name: "seconds", type: "number", description: "Video or SFX duration in seconds (default 5); H3 takes fractions (6.5); extensions add this many seconds, insert-video's scene is 5\u201313; lip-sync, recast, relight and 3d-to-video take their input's length; SFX supports 0.5\u201330" },
|
|
50205
|
+
{ name: "seconds", type: "number", description: "Video or SFX duration in seconds (default 5); H3 takes fractions (6.5), FLUX 3 Max 5\u201320 whole seconds; extensions add this many seconds, insert-video's scene is 5\u201313; lip-sync, recast, relight and 3d-to-video take their input's length; SFX supports 0.5\u201330" },
|
|
50101
50206
|
{ name: "resolution", type: "string", choices: VIDEO_RESOLUTIONS, description: "Video resolution supported by the selected model and workflow (default its first)" },
|
|
50102
50207
|
{ name: "mode", type: "string", choices: ["cost", "speed", "quality"], description: "Video generation mode supported by the selected model (default its first)" },
|
|
50103
|
-
{ name: "prompt-expansion", type: "string", choices: PROMPT_EXPANSION_MODES, description: "Video: how much the model may rewrite the prompt (default disabled); extensions treat any other value as on" },
|
|
50208
|
+
{ name: "prompt-expansion", type: "string", choices: PROMPT_EXPANSION_MODES, description: "Video: how much the model may rewrite the prompt (default disabled); extensions treat any other value as on. FLUX 3 Max always reasons about the prompt: quality thoroughly, any other value lightly" },
|
|
50104
50209
|
{ name: "seed", type: "number", description: "Video: seed for repeatable results, where the model takes one" },
|
|
50105
50210
|
{ name: "extend-output", type: "string", choices: EXTEND_OUTPUTS, description: "Extension: extended (default, source plus new footage) or continuation (a 1.625 s overlap, then the new footage)" },
|
|
50106
50211
|
{ name: "voice", type: "string", description: "Gemini voice name or ElevenLabs voice ID (defaults: Kore / George)" },
|
|
@@ -50152,7 +50257,7 @@ Confirm code: ${device.user_code}`);
|
|
|
50152
50257
|
out(stdout, await new Client(flags.url, signal).request(flags.look ? `${avatarsPath(args.id)}/looks/${encodeURIComponent(flags.look)}` : `${avatarsPath(args.id)}/${encodeURIComponent(flags.group)}`, { method: "DELETE" }));
|
|
50153
50258
|
})))).command("jobs", (jobs) => jobs.add(defineCommand("list", { description: "List the latest generation jobs" }, (command) => command.flags({ name: "url", type: "string", default: "https://animaguru.com", env: { name: "ANIMAGURU_URL" }, description: "Server origin" }).args(id).action(async ({ args, flags, stdout, signal }) => out(stdout, await new Client(flags.url, signal).request(`${projectPath(args.id)}/jobs`))))).add(defineCommand("submit", { description: "Submit a typed JSON job: character, animate, custom or redraw; spends credits" }, (command) => command.flags({ name: "url", type: "string", default: "https://animaguru.com", env: { name: "ANIMAGURU_URL" }, description: "Server origin" }).args(id).flags(...modelFlags).flags({ name: "file", type: "string", required: true, description: "JSON job file" }, { name: "yes", type: "boolean", required: true, description: "Authorize spending credits" }).action(async ({ args, flags, stdout, signal }) => {
|
|
50154
50259
|
if (!flags.yes) throw new Error("Pass --yes to authorize spending credits.");
|
|
50155
|
-
const body = spriteJobBody.parse({ ...JSON.parse(await
|
|
50260
|
+
const body = spriteJobBody.parse({ ...JSON.parse(await readFile19(flags.file, "utf8")), ...selectedModels(flags) });
|
|
50156
50261
|
out(stdout, await new Client(flags.url, signal).request(`${projectPath(args.id)}/jobs`, { method: "POST", body }));
|
|
50157
50262
|
}))).add(defineCommand("wait", { description: "Wait for a specific job; fail if generation fails or times out" }, (command) => command.flags({ name: "url", type: "string", default: "https://animaguru.com", env: { name: "ANIMAGURU_URL" }, description: "Server origin" }).args(id, { name: "job", type: "string", required: true }).flags({ name: "timeout", type: "number", default: 600, description: "Maximum seconds to wait" }).action(async ({ args, flags, stdout, signal }) => out(stdout, { job: await waitForJob(new Client(flags.url, signal), args.id, args.job, flags.timeout, signal) }))))).add(defineCommand("models", { description: "Set the default video model for future project jobs" }, (command) => command.flags({ name: "url", type: "string", default: "https://animaguru.com", env: { name: "ANIMAGURU_URL" }, description: "Server origin" }).args(id).flags(...modelFlags).action(async ({ args, flags, stdout, signal }) => {
|
|
50158
50263
|
const body = selectedModels(flags);
|
|
@@ -50167,7 +50272,7 @@ Confirm code: ${device.user_code}`);
|
|
|
50167
50272
|
const extension = flags.file.split(".").at(-1)?.toLowerCase();
|
|
50168
50273
|
const type = extension === "png" ? "image/png" : extension === "jpg" || extension === "jpeg" ? "image/jpeg" : extension === "webp" ? "image/webp" : void 0;
|
|
50169
50274
|
if (!type) throw new Error("Use a PNG, JPEG or WebP image.");
|
|
50170
|
-
const bytes = await
|
|
50275
|
+
const bytes = await readFile19(flags.file);
|
|
50171
50276
|
if (bytes.length > 10 * 1024 * 1024) throw new Error("Image must be at most 10 MB.");
|
|
50172
50277
|
const form = new FormData();
|
|
50173
50278
|
form.set("image", new Blob([bytes], { type }), `reference.${extension}`);
|
|
@@ -50183,7 +50288,7 @@ Confirm code: ${device.user_code}`);
|
|
|
50183
50288
|
if (!file2) throw new Error(`No file at ${args.path} in this project. List them with animaguru files list.`);
|
|
50184
50289
|
if (!/^[a-f0-9]{64}$/.test(file2.sha)) throw new Error("Invalid blob hash in manifest.");
|
|
50185
50290
|
const response = await client.request(`/api/v1/blobs/${encodeURIComponent(manifest.ownerId)}/${file2.sha}`, { raw: true });
|
|
50186
|
-
const path =
|
|
50291
|
+
const path = resolve17(flags.output);
|
|
50187
50292
|
await mkdir15(dirname14(path), { recursive: true });
|
|
50188
50293
|
const bytes = new Uint8Array(await response.arrayBuffer());
|
|
50189
50294
|
await writeFile16(path, bytes, { flag: "wx" });
|
|
@@ -50198,7 +50303,7 @@ Confirm code: ${device.user_code}`);
|
|
|
50198
50303
|
const response = await client.request(`/api/v1/blobs/${encodeURIComponent(manifest.ownerId)}/${file2.sha}`, { raw: true });
|
|
50199
50304
|
entries[path2] = new Uint8Array(await response.arrayBuffer());
|
|
50200
50305
|
}
|
|
50201
|
-
const path =
|
|
50306
|
+
const path = resolve17(flags.output);
|
|
50202
50307
|
await writeFile16(path, zipSync(entries, { level: 0 }), { flag: "wx" });
|
|
50203
50308
|
out(stdout, { path, files: manifest.files.length });
|
|
50204
50309
|
})))).extend(version(package_default.version, { format: "plain" })).extend(help()).extend(completion());
|