@docyrus/animaguru 0.28.0 → 0.29.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 CHANGED
@@ -581,6 +581,13 @@ animaguru videos subtitles VIDEO_ID --remove de --yes
581
581
 
582
582
  Each upload of up to 1 MiB is stored as WebVTT, up to 20 languages per video. HTML5 films draw their own captions and take no subtitle files. New subtitles on a public video send it back for staff review, like a new poster; removing a language does not.
583
583
 
584
+ CLI 0.29.0 lets admins tag a film for the showcase as they upload it: the public gallery films the lead invitation page shows under "Films made with AnimaGuru", grouped by sector with the lead's own first. `--showcase SECTOR` adds it (`cafe`, `restaurant`, `food`, `retail`, `beauty`, `fitness`, `workspace`, `hospitality`, `realestate`, `education`, `health`, `events`, `technology` or `services`). `--showcase-tags` says what it is for (comma-separated: `social`, `website`, `product`, `space`, `offer`, `brand`, `explainer`, `event`). `--showcase-language en|tr|sv|none` gives the language it speaks or shows, and `--showcase-position N` its order (lower first). The film shows there once it is public and approved; admins can change the tags later on Staff → Video reviews. Other accounts get a 403.
585
+
586
+ ```sh
587
+ animaguru submit --file production/exports/film.mp4 --poster production/exports/poster.jpg \
588
+ --showcase cafe --showcase-tags social,offer --showcase-language tr --showcase-position 10 --yes
589
+ ```
590
+
584
591
  To request public gallery publication, upload privately, open the video in My videos on the website, choose Share → Public, and accept the public video sharing agreement. Staff must approve the submission before anyone else can access its public link, download or embed. Direct CLI public-sharing requests without agreement acceptance are refused by the API.
585
592
 
586
593
  `videos list --scope mine|shared|public` lists films. Owners can edit metadata, change visibility and replace recipients on the website, or use `videos share VIDEO_ID --visibility private --share "alex@example.com" --yes`. `videos delete VIDEO_ID --yes` removes the film and files. Making a film private or removing a recipient revokes access to existing playback links.
package/dist/animaguru.js CHANGED
@@ -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.28.0",
19764
+ version: "0.29.0",
19765
19765
  description: "Agent-friendly AnimaGuru CLI built with CrustJS",
19766
19766
  type: "module",
19767
19767
  bin: {
@@ -25288,208 +25288,6 @@ function unzipSync(data, opts) {
25288
25288
  // shared/auth.ts
25289
25289
  var CLI_CLIENT_ID = "animaguru-cli";
25290
25290
 
25291
- // shared/sprites/styles.ts
25292
- var GAME_SPRITE_STYLES = ["hd-cartoon", "pixel", "painted", "vector"];
25293
- var VIDEO_SPRITE_STYLES = [
25294
- "photoreal-cutout",
25295
- "photo-collage",
25296
- "paper-cutout",
25297
- "photoreal-stop-motion",
25298
- "cutout-motion-graphics"
25299
- ];
25300
- var SPRITE_STYLES = [...GAME_SPRITE_STYLES, ...VIDEO_SPRITE_STYLES];
25301
-
25302
- // shared/credits.ts
25303
- var UNITS_PER_CREDIT = 10;
25304
- var toCredits = (units) => Math.round(units) / UNITS_PER_CREDIT;
25305
- var toUnits = (credits) => Math.round(credits * UNITS_PER_CREDIT);
25306
-
25307
- // shared/sprites/types.ts
25308
- var SPRITE_KINDS = ["character", "vehicle", "machine", "effect"];
25309
- var SPRITE_ACTIONS = ["idle", "walk", "run", "skid-stop", "shoot", "jump", "hurt", "death", "slide", "punch", "kick"];
25310
- var SPRITE_KIND_ACTIONS = {
25311
- character: SPRITE_ACTIONS,
25312
- vehicle: ["idle", "move", "fire", "hurt", "destroyed"],
25313
- machine: ["idle", "work", "activate", "fire", "hurt", "break"],
25314
- effect: ["projectile", "charge", "impact", "explosion", "aura"]
25315
- };
25316
- var ALL_SPRITE_ACTIONS = [...new Set(Object.values(SPRITE_KIND_ACTIONS).flat())];
25317
- var SPRITE_TOP_ACTIONS = {
25318
- vehicle: ["hull-move", "hull-destroyed", "turret-fire"],
25319
- machine: ["hull-break", "turret-fire"]
25320
- };
25321
- var ALL_TOP_ACTIONS = [...new Set(Object.values(SPRITE_TOP_ACTIONS).flat())];
25322
- var CUSTOM_FRAMES = { min: 2, max: 12 };
25323
-
25324
- // shared/sprites/models.ts
25325
- var DEFAULT_SPRITE_IMAGE_MODEL = "fal/gpt-image-2.5-medium";
25326
- var SPRITE_IMAGE_MODELS = [
25327
- { id: DEFAULT_SPRITE_IMAGE_MODEL, label: "GPT Image 2.5 \xB7 medium (fal)", provider: "fal", edit: true, nativeAlpha: true, maxImages: 16, relCost: 0.3, quality: "medium", endpoints: { generate: "openai/gpt-image-2.5/flare/text-to-image", edit: "openai/gpt-image-2.5/flare/edit" } },
25328
- { id: "fal/gpt-image-2-low", label: "GPT Image 2 \xB7 low (fal)", provider: "fal", edit: true, spriteBackupCutout: true, maxImages: 16, relCost: 0.2, quality: "low", endpoints: { generate: "openai/gpt-image-2", edit: "openai/gpt-image-2/edit" } },
25329
- { id: "fal/muse-image", label: "Muse Image (fal)", provider: "fal", edit: true, maxImages: 10, relCost: 0.2, endpoints: { generate: "meta/muse-image/text-to-image", edit: "meta/muse-image/edit" } },
25330
- { id: "fal/krea-2-turbo", label: "Krea 2 Turbo (fal)", provider: "fal", edit: false, maxImages: 0, relCost: 0.16, endpoints: { generate: "fal-ai/krea-2/turbo" } },
25331
- { id: "gpt-image-2.5", label: "GPT Image 2.5 (OpenAI)", provider: "openai", edit: true, nativeAlpha: true, maxImages: 16, relCost: 1 },
25332
- { id: "black-forest-labs/flux-2-max", label: "FLUX.2 [max]", provider: "workers-ai", edit: true, maxImages: 8, relCost: 3 },
25333
- { id: "bytedance/seedream-5-pro", label: "Seedream 5 Pro", provider: "workers-ai", edit: true, maxImages: 10, relCost: 1.5 },
25334
- { id: "xai/grok-imagine-image-2.0", label: "Grok Imagine Image 2.0", provider: "workers-ai", edit: true, maxImages: 5, relCost: 1 },
25335
- { id: "alibaba/qwen-image-3.0-pro", label: "Qwen Image 3.0 Pro", provider: "workers-ai", edit: false, maxImages: 0, relCost: 0.5 },
25336
- { id: "krea/krea-2-medium-turbo", label: "Krea 2 Medium Turbo", provider: "workers-ai", edit: false, maxImages: 0, relCost: 0.25 }
25337
- ];
25338
- var SPRITE_IMAGE_MODEL_IDS = SPRITE_IMAGE_MODELS.map((m) => m.id);
25339
- function spriteImageModel(id2) {
25340
- return SPRITE_IMAGE_MODELS.find((m) => m.id === id2) ?? SPRITE_IMAGE_MODELS[0];
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", "keyframes-to-video"];
25343
- var STUDIO_VIDEO_WORKFLOWS = ["image-to-video", "text-to-video", "extend-video"];
25344
- var MEDIA_LENGTH_WORKFLOWS = ["lip-sync", "recast", "relight", "3d-to-video"];
25345
- var lengthFromMedia = (workflow) => MEDIA_LENGTH_WORKFLOWS.includes(workflow);
25346
- var VIDEO_STYLES = ["vhs", "retro-toon-70s", "low-poly", "hand-drawn", "16bit-pixel"];
25347
- var VHS_DAMAGE_LEVELS = ["light", "medium", "heavy"];
25348
- var VIDEO_RESOLUTIONS = ["480p", "720p", "768p", "1080p", "2k"];
25349
- var H3_RESOLUTIONS = ["480p", "768p", "1080p", "2k"];
25350
- var VIDEO_ASPECT_RATIOS = ["21:9", "16:9", "4:3", "1:1", "3:4", "9:16"];
25351
- var PROMPT_EXPANSION_MODES = ["disabled", "balanced", "quality"];
25352
- var EXTEND_OUTPUTS = ["extended", "continuation"];
25353
- var SOUNDTRACK_MAX_BYTES = 15 * 1024 * 1024;
25354
- var EXTEND_SOURCE_MAX_BYTES = 50 * 1024 * 1024;
25355
- var MAX_AUDIO_REFERENCES = 3;
25356
- var MAX_VIDEO_REFERENCE_IMAGES = 9;
25357
- var MAX_REFERENCE_VIDEOS = 3;
25358
- var MAX_REFERENCE_FILES = 12;
25359
- var MAX_RECAST_PEOPLE = 4;
25360
- var MAX_3D_REFERENCES = 8;
25361
- var MAX_CAMERA_KEYFRAMES = 12;
25362
- var MAX_CAMERA_TURNS = 32;
25363
- var MAX_IMAGE_KEYFRAMES = 10;
25364
- var KEYFRAME_FPS = 24;
25365
- var H3_MAX = "minimax/h3-max";
25366
- var FLUX_3_MAX = "blackforestlabs/flux-3-max";
25367
- var h3Endpoints = (route) => ({ "image-to-video": `${route}/image-to-video`, "text-to-video": `${route}/text-to-video`, "extend-video": `${route}/extend-video` });
25368
- var SPRITE_VIDEO_MODELS = [
25369
- {
25370
- id: "pruna/p-video-2-pro",
25371
- label: "Pruna P-Video 2 Pro",
25372
- provider: "pruna",
25373
- workflows: ["image-to-video"],
25374
- resolutions: ["480p", "768p"],
25375
- modes: ["cost", "speed", "quality"],
25376
- lastFrame: true,
25377
- estimatedCreditsPerSecond: { "480p": { cost: 1, speed: 2, quality: 4 }, "768p": { cost: 2.5, speed: 3.5, quality: 7.5 } },
25378
- seconds: { min: 5, max: 15 }
25379
- },
25380
- {
25381
- id: "heygen/heygen-video-1",
25382
- label: "HeyGen Video 1",
25383
- provider: "heygen",
25384
- workflows: ["image-to-video"],
25385
- resolutions: ["480p", "768p"],
25386
- modes: ["cost"],
25387
- lastFrame: false,
25388
- estimatedCreditsPerSecond: { "480p": { cost: 1 }, "768p": { cost: 1.5 } },
25389
- seconds: { min: 5, max: 15 }
25390
- },
25391
- {
25392
- id: "fal/minimax-h3-max",
25393
- label: "MiniMax H3 Max (fal.ai)",
25394
- provider: "fal",
25395
- // Every workflow but FLUX 3 Max's keyframes.
25396
- workflows: VIDEO_WORKFLOWS.filter((w) => w !== "keyframes-to-video"),
25397
- endpoints: {
25398
- ...h3Endpoints(H3_MAX),
25399
- "reference-to-video": `${H3_MAX}/reference-to-video`,
25400
- "lip-sync": `${H3_MAX}/lip-sync/image-to-video`,
25401
- "camera-controls": `${H3_MAX}/camera-controls`,
25402
- "insert-video": `${H3_MAX}/insert-video`,
25403
- recast: `${H3_MAX}/recast`,
25404
- relight: `${H3_MAX}/relight`,
25405
- "3d-to-video": `${H3_MAX}/3d-to-video`,
25406
- style: `${H3_MAX}/styles`
25407
- },
25408
- resolutions: ["480p", "768p", "1080p"],
25409
- workflowOptions: {
25410
- "extend-video": { resolutions: H3_RESOLUTIONS },
25411
- "lip-sync": { resolutions: H3_RESOLUTIONS, seconds: { min: 5, max: 900, fractional: true } },
25412
- "insert-video": { resolutions: ["480p", "768p"], seconds: { min: 5, max: 13, fractional: true }, creditsPerSecond: { "480p": 5, "768p": 6 } },
25413
- recast: { resolutions: ["768p", "1080p"], seconds: { min: 5, max: 30, fractional: true }, creditsPerSecond: { "768p": 30, "1080p": 45 } },
25414
- relight: { resolutions: H3_RESOLUTIONS, seconds: { min: 2.33, max: 15, fractional: true } },
25415
- "3d-to-video": { seconds: { min: 1, max: 15, fractional: true } },
25416
- style: { resolutions: ["768p"], seconds: { min: 5, max: 15 } }
25417
- },
25418
- modes: ["quality"],
25419
- lastFrame: true,
25420
- endFrameOnly: true,
25421
- seed: true,
25422
- promptExpansion: PROMPT_EXPANSION_MODES,
25423
- audio: true,
25424
- estimatedCreditsPerSecond: { "480p": { quality: 5 }, "768p": { quality: 8 }, "1080p": { quality: 16 }, "2k": { quality: 32 } },
25425
- seconds: { min: 1, max: 15, fractional: true }
25426
- },
25427
- {
25428
- id: "fal/minimax-h3-max-turbo",
25429
- label: "MiniMax H3 Max Turbo (fal.ai)",
25430
- provider: "fal",
25431
- workflows: STUDIO_VIDEO_WORKFLOWS,
25432
- endpoints: h3Endpoints("minimax/h3-max-turbo"),
25433
- resolutions: ["480p", "768p", "1080p"],
25434
- workflowOptions: { "extend-video": { resolutions: H3_RESOLUTIONS } },
25435
- modes: ["speed"],
25436
- lastFrame: true,
25437
- endFrameOnly: true,
25438
- seed: true,
25439
- promptExpansion: PROMPT_EXPANSION_MODES,
25440
- audio: true,
25441
- estimatedCreditsPerSecond: { "480p": { speed: 2.5 }, "768p": { speed: 4 }, "1080p": { speed: 8 }, "2k": { speed: 16 } },
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 }
25458
- }
25459
- ];
25460
- var SPRITE_VIDEO_MODEL_IDS = SPRITE_VIDEO_MODELS.map((m) => m.id);
25461
- var DEFAULT_SPRITE_VIDEO_MODEL = "pruna/p-video-2-pro";
25462
- function spriteVideoModel(id2) {
25463
- return SPRITE_VIDEO_MODELS.find((m) => m.id === id2) ?? SPRITE_VIDEO_MODELS[0];
25464
- }
25465
- function videoResolutions(model, workflow = "image-to-video") {
25466
- return model.workflowOptions?.[workflow]?.resolutions ?? model.resolutions;
25467
- }
25468
- function videoSeconds(model, workflow = "image-to-video") {
25469
- return model.workflowOptions?.[workflow]?.seconds ?? model.seconds;
25470
- }
25471
- var INCLUDED_REFERENCE_TOKENS = 4096;
25472
- var imageReferenceTokens = (size) => size && size[0] > 0 && size[1] > 0 ? 32 * Math.ceil(32 * Math.max(...size) / Math.min(...size)) : 1824;
25473
- var clipReferenceTokens = (seconds2, resolution) => Math.ceil(Math.max(0, seconds2) * (resolution === "480p" ? 2700 : 6900));
25474
- var audioReferenceTokens = (seconds2) => Math.ceil(Math.max(0, seconds2) * 80);
25475
- var DEFAULT_GENERATED_REFERENCES = 2;
25476
- function billedVideoSeconds(model, workflow, seconds2) {
25477
- const s = videoSeconds(model, workflow).fractional ? seconds2 : Math.round(seconds2);
25478
- if (workflow === "3d-to-video") return Math.max(5, s);
25479
- return workflow === "lip-sync" && s > 15 ? s * 1.2 : s;
25480
- }
25481
- function videoCreditsPerSecond(model, p) {
25482
- const override = model.workflowOptions?.[p.workflow ?? "image-to-video"]?.creditsPerSecond?.[p.resolution];
25483
- if (override !== void 0) return override;
25484
- const byMode = model.estimatedCreditsPerSecond[p.resolution] ?? model.estimatedCreditsPerSecond[model.resolutions[0]];
25485
- return byMode[p.mode] ?? byMode[model.modes[0]] ?? 0;
25486
- }
25487
- function spriteVideoEstimate(model, p, extras2 = {}) {
25488
- const seconds2 = billedVideoSeconds(model, p.workflow ?? "image-to-video", p.seconds);
25489
- const beyond = Math.max(0, (extras2.referenceTokens ?? 0) - INCLUDED_REFERENCE_TOKENS) * 2e-3 + (extras2.generatedReferences ?? 0) * 3;
25490
- return Math.max(1, Math.ceil((videoCreditsPerSecond(model, p) * seconds2 + beyond) * UNITS_PER_CREDIT));
25491
- }
25492
-
25493
25291
  // node_modules/zod/v4/classic/external.js
25494
25292
  var external_exports = {};
25495
25293
  __export(external_exports, {
@@ -45158,6 +44956,220 @@ function date4(params) {
45158
44956
  return _coercedDate(ZodDate, params);
45159
44957
  }
45160
44958
 
44959
+ // shared/showcase.ts
44960
+ var SHOWCASE_SECTORS = ["cafe", "restaurant", "food", "retail", "beauty", "fitness", "workspace", "hospitality", "realestate", "education", "health", "events", "technology", "services"];
44961
+ var SHOWCASE_TAGS = ["social", "website", "product", "space", "offer", "brand", "explainer", "event"];
44962
+ var SHOWCASE_LOCALES = ["", "en", "tr", "sv"];
44963
+ var showcaseEntry = external_exports.object({
44964
+ sector: external_exports.enum(SHOWCASE_SECTORS),
44965
+ tags: external_exports.array(external_exports.enum(SHOWCASE_TAGS)).max(SHOWCASE_TAGS.length).default([]).transform((tags) => [...new Set(tags)]),
44966
+ locale: external_exports.enum(SHOWCASE_LOCALES).default(""),
44967
+ /** Lower comes first; films with the same position show newest first. */
44968
+ position: external_exports.number().int().min(0).max(1e3).default(0)
44969
+ }).strict();
44970
+
44971
+ // shared/sprites/styles.ts
44972
+ var GAME_SPRITE_STYLES = ["hd-cartoon", "pixel", "painted", "vector"];
44973
+ var VIDEO_SPRITE_STYLES = [
44974
+ "photoreal-cutout",
44975
+ "photo-collage",
44976
+ "paper-cutout",
44977
+ "photoreal-stop-motion",
44978
+ "cutout-motion-graphics"
44979
+ ];
44980
+ var SPRITE_STYLES = [...GAME_SPRITE_STYLES, ...VIDEO_SPRITE_STYLES];
44981
+
44982
+ // shared/credits.ts
44983
+ var UNITS_PER_CREDIT = 10;
44984
+ var toCredits = (units) => Math.round(units) / UNITS_PER_CREDIT;
44985
+ var toUnits = (credits) => Math.round(credits * UNITS_PER_CREDIT);
44986
+
44987
+ // shared/sprites/types.ts
44988
+ var SPRITE_KINDS = ["character", "vehicle", "machine", "effect"];
44989
+ var SPRITE_ACTIONS = ["idle", "walk", "run", "skid-stop", "shoot", "jump", "hurt", "death", "slide", "punch", "kick"];
44990
+ var SPRITE_KIND_ACTIONS = {
44991
+ character: SPRITE_ACTIONS,
44992
+ vehicle: ["idle", "move", "fire", "hurt", "destroyed"],
44993
+ machine: ["idle", "work", "activate", "fire", "hurt", "break"],
44994
+ effect: ["projectile", "charge", "impact", "explosion", "aura"]
44995
+ };
44996
+ var ALL_SPRITE_ACTIONS = [...new Set(Object.values(SPRITE_KIND_ACTIONS).flat())];
44997
+ var SPRITE_TOP_ACTIONS = {
44998
+ vehicle: ["hull-move", "hull-destroyed", "turret-fire"],
44999
+ machine: ["hull-break", "turret-fire"]
45000
+ };
45001
+ var ALL_TOP_ACTIONS = [...new Set(Object.values(SPRITE_TOP_ACTIONS).flat())];
45002
+ var CUSTOM_FRAMES = { min: 2, max: 12 };
45003
+
45004
+ // shared/sprites/models.ts
45005
+ var DEFAULT_SPRITE_IMAGE_MODEL = "fal/gpt-image-2.5-medium";
45006
+ var SPRITE_IMAGE_MODELS = [
45007
+ { id: DEFAULT_SPRITE_IMAGE_MODEL, label: "GPT Image 2.5 \xB7 medium (fal)", provider: "fal", edit: true, nativeAlpha: true, maxImages: 16, relCost: 0.3, quality: "medium", endpoints: { generate: "openai/gpt-image-2.5/flare/text-to-image", edit: "openai/gpt-image-2.5/flare/edit" } },
45008
+ { id: "fal/gpt-image-2-low", label: "GPT Image 2 \xB7 low (fal)", provider: "fal", edit: true, spriteBackupCutout: true, maxImages: 16, relCost: 0.2, quality: "low", endpoints: { generate: "openai/gpt-image-2", edit: "openai/gpt-image-2/edit" } },
45009
+ { id: "fal/muse-image", label: "Muse Image (fal)", provider: "fal", edit: true, maxImages: 10, relCost: 0.2, endpoints: { generate: "meta/muse-image/text-to-image", edit: "meta/muse-image/edit" } },
45010
+ { id: "fal/krea-2-turbo", label: "Krea 2 Turbo (fal)", provider: "fal", edit: false, maxImages: 0, relCost: 0.16, endpoints: { generate: "fal-ai/krea-2/turbo" } },
45011
+ { id: "gpt-image-2.5", label: "GPT Image 2.5 (OpenAI)", provider: "openai", edit: true, nativeAlpha: true, maxImages: 16, relCost: 1 },
45012
+ { id: "black-forest-labs/flux-2-max", label: "FLUX.2 [max]", provider: "workers-ai", edit: true, maxImages: 8, relCost: 3 },
45013
+ { id: "bytedance/seedream-5-pro", label: "Seedream 5 Pro", provider: "workers-ai", edit: true, maxImages: 10, relCost: 1.5 },
45014
+ { id: "xai/grok-imagine-image-2.0", label: "Grok Imagine Image 2.0", provider: "workers-ai", edit: true, maxImages: 5, relCost: 1 },
45015
+ { id: "alibaba/qwen-image-3.0-pro", label: "Qwen Image 3.0 Pro", provider: "workers-ai", edit: false, maxImages: 0, relCost: 0.5 },
45016
+ { id: "krea/krea-2-medium-turbo", label: "Krea 2 Medium Turbo", provider: "workers-ai", edit: false, maxImages: 0, relCost: 0.25 }
45017
+ ];
45018
+ var SPRITE_IMAGE_MODEL_IDS = SPRITE_IMAGE_MODELS.map((m) => m.id);
45019
+ function spriteImageModel(id2) {
45020
+ return SPRITE_IMAGE_MODELS.find((m) => m.id === id2) ?? SPRITE_IMAGE_MODELS[0];
45021
+ }
45022
+ 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"];
45023
+ var STUDIO_VIDEO_WORKFLOWS = ["image-to-video", "text-to-video", "extend-video"];
45024
+ var MEDIA_LENGTH_WORKFLOWS = ["lip-sync", "recast", "relight", "3d-to-video"];
45025
+ var lengthFromMedia = (workflow) => MEDIA_LENGTH_WORKFLOWS.includes(workflow);
45026
+ var VIDEO_STYLES = ["vhs", "retro-toon-70s", "low-poly", "hand-drawn", "16bit-pixel"];
45027
+ var VHS_DAMAGE_LEVELS = ["light", "medium", "heavy"];
45028
+ var VIDEO_RESOLUTIONS = ["480p", "720p", "768p", "1080p", "2k"];
45029
+ var H3_RESOLUTIONS = ["480p", "768p", "1080p", "2k"];
45030
+ var VIDEO_ASPECT_RATIOS = ["21:9", "16:9", "4:3", "1:1", "3:4", "9:16"];
45031
+ var PROMPT_EXPANSION_MODES = ["disabled", "balanced", "quality"];
45032
+ var EXTEND_OUTPUTS = ["extended", "continuation"];
45033
+ var SOUNDTRACK_MAX_BYTES = 15 * 1024 * 1024;
45034
+ var EXTEND_SOURCE_MAX_BYTES = 50 * 1024 * 1024;
45035
+ var MAX_AUDIO_REFERENCES = 3;
45036
+ var MAX_VIDEO_REFERENCE_IMAGES = 9;
45037
+ var MAX_REFERENCE_VIDEOS = 3;
45038
+ var MAX_REFERENCE_FILES = 12;
45039
+ var MAX_RECAST_PEOPLE = 4;
45040
+ var MAX_3D_REFERENCES = 8;
45041
+ var MAX_CAMERA_KEYFRAMES = 12;
45042
+ var MAX_CAMERA_TURNS = 32;
45043
+ var MAX_IMAGE_KEYFRAMES = 10;
45044
+ var KEYFRAME_FPS = 24;
45045
+ var H3_MAX = "minimax/h3-max";
45046
+ var FLUX_3_MAX = "blackforestlabs/flux-3-max";
45047
+ var h3Endpoints = (route) => ({ "image-to-video": `${route}/image-to-video`, "text-to-video": `${route}/text-to-video`, "extend-video": `${route}/extend-video` });
45048
+ var SPRITE_VIDEO_MODELS = [
45049
+ {
45050
+ id: "pruna/p-video-2-pro",
45051
+ label: "Pruna P-Video 2 Pro",
45052
+ provider: "pruna",
45053
+ workflows: ["image-to-video"],
45054
+ resolutions: ["480p", "768p"],
45055
+ modes: ["cost", "speed", "quality"],
45056
+ lastFrame: true,
45057
+ estimatedCreditsPerSecond: { "480p": { cost: 1, speed: 2, quality: 4 }, "768p": { cost: 2.5, speed: 3.5, quality: 7.5 } },
45058
+ seconds: { min: 5, max: 15 }
45059
+ },
45060
+ {
45061
+ id: "heygen/heygen-video-1",
45062
+ label: "HeyGen Video 1",
45063
+ provider: "heygen",
45064
+ workflows: ["image-to-video"],
45065
+ resolutions: ["480p", "768p"],
45066
+ modes: ["cost"],
45067
+ lastFrame: false,
45068
+ estimatedCreditsPerSecond: { "480p": { cost: 1 }, "768p": { cost: 1.5 } },
45069
+ seconds: { min: 5, max: 15 }
45070
+ },
45071
+ {
45072
+ id: "fal/minimax-h3-max",
45073
+ label: "MiniMax H3 Max (fal.ai)",
45074
+ provider: "fal",
45075
+ // Every workflow but FLUX 3 Max's keyframes.
45076
+ workflows: VIDEO_WORKFLOWS.filter((w) => w !== "keyframes-to-video"),
45077
+ endpoints: {
45078
+ ...h3Endpoints(H3_MAX),
45079
+ "reference-to-video": `${H3_MAX}/reference-to-video`,
45080
+ "lip-sync": `${H3_MAX}/lip-sync/image-to-video`,
45081
+ "camera-controls": `${H3_MAX}/camera-controls`,
45082
+ "insert-video": `${H3_MAX}/insert-video`,
45083
+ recast: `${H3_MAX}/recast`,
45084
+ relight: `${H3_MAX}/relight`,
45085
+ "3d-to-video": `${H3_MAX}/3d-to-video`,
45086
+ style: `${H3_MAX}/styles`
45087
+ },
45088
+ resolutions: ["480p", "768p", "1080p"],
45089
+ workflowOptions: {
45090
+ "extend-video": { resolutions: H3_RESOLUTIONS },
45091
+ "lip-sync": { resolutions: H3_RESOLUTIONS, seconds: { min: 5, max: 900, fractional: true } },
45092
+ "insert-video": { resolutions: ["480p", "768p"], seconds: { min: 5, max: 13, fractional: true }, creditsPerSecond: { "480p": 5, "768p": 6 } },
45093
+ recast: { resolutions: ["768p", "1080p"], seconds: { min: 5, max: 30, fractional: true }, creditsPerSecond: { "768p": 30, "1080p": 45 } },
45094
+ relight: { resolutions: H3_RESOLUTIONS, seconds: { min: 2.33, max: 15, fractional: true } },
45095
+ "3d-to-video": { seconds: { min: 1, max: 15, fractional: true } },
45096
+ style: { resolutions: ["768p"], seconds: { min: 5, max: 15 } }
45097
+ },
45098
+ modes: ["quality"],
45099
+ lastFrame: true,
45100
+ endFrameOnly: true,
45101
+ seed: true,
45102
+ promptExpansion: PROMPT_EXPANSION_MODES,
45103
+ audio: true,
45104
+ estimatedCreditsPerSecond: { "480p": { quality: 5 }, "768p": { quality: 8 }, "1080p": { quality: 16 }, "2k": { quality: 32 } },
45105
+ seconds: { min: 1, max: 15, fractional: true }
45106
+ },
45107
+ {
45108
+ id: "fal/minimax-h3-max-turbo",
45109
+ label: "MiniMax H3 Max Turbo (fal.ai)",
45110
+ provider: "fal",
45111
+ workflows: STUDIO_VIDEO_WORKFLOWS,
45112
+ endpoints: h3Endpoints("minimax/h3-max-turbo"),
45113
+ resolutions: ["480p", "768p", "1080p"],
45114
+ workflowOptions: { "extend-video": { resolutions: H3_RESOLUTIONS } },
45115
+ modes: ["speed"],
45116
+ lastFrame: true,
45117
+ endFrameOnly: true,
45118
+ seed: true,
45119
+ promptExpansion: PROMPT_EXPANSION_MODES,
45120
+ audio: true,
45121
+ estimatedCreditsPerSecond: { "480p": { speed: 2.5 }, "768p": { speed: 4 }, "1080p": { speed: 8 }, "2k": { speed: 16 } },
45122
+ seconds: { min: 1, max: 15, fractional: true }
45123
+ },
45124
+ {
45125
+ id: "fal/flux-3-max",
45126
+ label: "FLUX 3 Max (fal.ai)",
45127
+ provider: "fal",
45128
+ workflows: ["image-to-video", "text-to-video", "keyframes-to-video"],
45129
+ 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` },
45130
+ falInputs: "flux",
45131
+ resolutions: ["720p", "1080p"],
45132
+ modes: ["quality"],
45133
+ lastFrame: true,
45134
+ seed: true,
45135
+ promptExpansion: ["balanced", "quality"],
45136
+ estimatedCreditsPerSecond: { "720p": { quality: 17 }, "1080p": { quality: 29 } },
45137
+ seconds: { min: 5, max: 20 }
45138
+ }
45139
+ ];
45140
+ var SPRITE_VIDEO_MODEL_IDS = SPRITE_VIDEO_MODELS.map((m) => m.id);
45141
+ var DEFAULT_SPRITE_VIDEO_MODEL = "pruna/p-video-2-pro";
45142
+ function spriteVideoModel(id2) {
45143
+ return SPRITE_VIDEO_MODELS.find((m) => m.id === id2) ?? SPRITE_VIDEO_MODELS[0];
45144
+ }
45145
+ function videoResolutions(model, workflow = "image-to-video") {
45146
+ return model.workflowOptions?.[workflow]?.resolutions ?? model.resolutions;
45147
+ }
45148
+ function videoSeconds(model, workflow = "image-to-video") {
45149
+ return model.workflowOptions?.[workflow]?.seconds ?? model.seconds;
45150
+ }
45151
+ var INCLUDED_REFERENCE_TOKENS = 4096;
45152
+ var imageReferenceTokens = (size) => size && size[0] > 0 && size[1] > 0 ? 32 * Math.ceil(32 * Math.max(...size) / Math.min(...size)) : 1824;
45153
+ var clipReferenceTokens = (seconds2, resolution) => Math.ceil(Math.max(0, seconds2) * (resolution === "480p" ? 2700 : 6900));
45154
+ var audioReferenceTokens = (seconds2) => Math.ceil(Math.max(0, seconds2) * 80);
45155
+ var DEFAULT_GENERATED_REFERENCES = 2;
45156
+ function billedVideoSeconds(model, workflow, seconds2) {
45157
+ const s = videoSeconds(model, workflow).fractional ? seconds2 : Math.round(seconds2);
45158
+ if (workflow === "3d-to-video") return Math.max(5, s);
45159
+ return workflow === "lip-sync" && s > 15 ? s * 1.2 : s;
45160
+ }
45161
+ function videoCreditsPerSecond(model, p) {
45162
+ const override = model.workflowOptions?.[p.workflow ?? "image-to-video"]?.creditsPerSecond?.[p.resolution];
45163
+ if (override !== void 0) return override;
45164
+ const byMode = model.estimatedCreditsPerSecond[p.resolution] ?? model.estimatedCreditsPerSecond[model.resolutions[0]];
45165
+ return byMode[p.mode] ?? byMode[model.modes[0]] ?? 0;
45166
+ }
45167
+ function spriteVideoEstimate(model, p, extras2 = {}) {
45168
+ const seconds2 = billedVideoSeconds(model, p.workflow ?? "image-to-video", p.seconds);
45169
+ const beyond = Math.max(0, (extras2.referenceTokens ?? 0) - INCLUDED_REFERENCE_TOKENS) * 2e-3 + (extras2.generatedReferences ?? 0) * 3;
45170
+ return Math.max(1, Math.ceil((videoCreditsPerSecond(model, p) * seconds2 + beyond) * UNITS_PER_CREDIT));
45171
+ }
45172
+
45161
45173
  // shared/sprites/generation-models.ts
45162
45174
  var generationModelShape = {
45163
45175
  videoModel: external_exports.enum(SPRITE_VIDEO_MODEL_IDS).optional()
@@ -47336,7 +47348,9 @@ var videoSubmission = videoDetails.and(external_exports.object({
47336
47348
  bytes: external_exports.number().int().positive(),
47337
47349
  width: external_exports.number().int().min(1).max(4096).default(1920),
47338
47350
  height: external_exports.number().int().min(1).max(4096).default(1080),
47339
- duration: external_exports.number().positive().max(7200).optional()
47351
+ duration: external_exports.number().positive().max(7200).optional(),
47352
+ /** Admins only: adds the video to the showcase (shared/showcase.ts), where it shows once it is public and approved. */
47353
+ showcase: showcaseEntry.optional()
47340
47354
  })).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.");
47341
47355
  var videoMove = external_exports.object({ workspace: videoWorkspace }).strict();
47342
47356
  function videoPosterType(bytes) {
@@ -48282,6 +48296,17 @@ async function restoreCloud(options) {
48282
48296
  }
48283
48297
 
48284
48298
  // cli/src/videos.ts
48299
+ function showcaseOption(options) {
48300
+ const extra = options["showcase-tags"] !== void 0 || options["showcase-language"] !== void 0 || options["showcase-position"] !== void 0;
48301
+ if (!options.showcase) {
48302
+ if (extra) throw new Error("Pass --showcase SECTOR to add the video to the showcase.");
48303
+ return {};
48304
+ }
48305
+ const tags = options["showcase-tags"] ? options["showcase-tags"].split(",").map((tag) => tag.trim()).filter(Boolean) : [];
48306
+ const parsed = showcaseEntry.safeParse({ sector: options.showcase, tags, locale: options["showcase-language"] === "none" ? "" : options["showcase-language"] ?? "", position: options["showcase-position"] ?? 0 });
48307
+ if (!parsed.success) throw new Error(`Check the showcase tags: ${parsed.error.issues[0]?.message ?? "invalid"} (tags are ${SHOWCASE_TAGS.join(", ")}).`);
48308
+ return { showcase: parsed.data };
48309
+ }
48285
48310
  var subtitlesBody = (content) => new Blob([new Uint8Array(content)], { type: "text/plain; charset=utf-8" });
48286
48311
  function libraryWorkspace(value) {
48287
48312
  if (value === "personal" || value === "me") return "me";
@@ -48326,6 +48351,7 @@ async function exportHtml(root, animation, signal, log) {
48326
48351
  async function submitVideo(client, options) {
48327
48352
  if (!options.yes && !options["dry-run"]) throw new Error("Pass --yes to authorize uploading and sharing this animation, or --dry-run to inspect the submission.");
48328
48353
  const workspace3 = options.workspace === void 0 ? void 0 : libraryWorkspace(options.workspace);
48354
+ const showcase = showcaseOption(options);
48329
48355
  const root = resolve10(options.directory ?? ".");
48330
48356
  const context = await localProject(root, options.animation);
48331
48357
  const file2 = options.file ? resolve10(root, options.file) : await exportHtml(context.toolsRoot, context.animation, options.signal, options.log);
@@ -48370,7 +48396,8 @@ async function submitVideo(client, options) {
48370
48396
  bytes: info.size,
48371
48397
  width: metadata.width,
48372
48398
  height: metadata.height,
48373
- duration: metadata.duration
48399
+ duration: metadata.duration,
48400
+ ...showcase
48374
48401
  });
48375
48402
  let subtitles = null;
48376
48403
  const choice = options.subtitles ?? "auto";
@@ -50091,6 +50118,7 @@ var brandCreate = brandFields.extend({ requestId: external_exports.string().uuid
50091
50118
  var brandUpdate = external_exports.object(fieldShape).partial().strict();
50092
50119
  var brandResearch = external_exports.object({ requestId: external_exports.string().uuid(), workspace, url: brandWebsite, name: external_exports.string().trim().min(1).max(120).optional() }).strict();
50093
50120
  var brandRefresh = external_exports.object({ requestId: external_exports.string().uuid(), url: brandWebsite.optional() }).strict();
50121
+ var brandTransfer = external_exports.object({ workspace }).strict();
50094
50122
  var DEFAULT_BRAND_COLORS = { primary: "#2563eb", background: "#ffffff", text: "#111827", accent: null, palette: [] };
50095
50123
  var DEFAULT_BRAND_FONTS = { heading: { family: "Inter, sans-serif", file: null }, body: { family: "Inter, sans-serif", file: null } };
50096
50124
  var brandDraftFields = brandFields.extend({
@@ -50702,6 +50730,10 @@ function createCli() {
50702
50730
  { name: "poster", type: "string", description: "PNG, JPEG or WebP cover image" },
50703
50731
  { name: "subtitles", type: "string", default: "auto", description: "MP4 subtitle file (.vtt or .srt) the player shows over the picture; auto uses FILM.vtt or FILM.srt beside FILM.mp4; none skips" },
50704
50732
  { 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" },
50733
+ { name: "showcase", type: "string", choices: [...SHOWCASE_SECTORS], description: "Admins: add the video to the showcase under this sector; it shows on lead pages once public and approved" },
50734
+ { name: "showcase-tags", type: "string", description: `Admins: what the film is for, comma-separated: ${SHOWCASE_TAGS.join(", ")}` },
50735
+ { name: "showcase-language", type: "string", choices: ["en", "tr", "sv", "none"], description: "Admins: the language the film speaks or shows; none for music and pictures only (default)" },
50736
+ { name: "showcase-position", type: "number", description: "Admins: order in the showcase, lower first (default 0)" },
50705
50737
  { name: "yes", type: "boolean", description: "Authorize upload and chosen sharing" },
50706
50738
  { name: "dry-run", type: "boolean", description: "Inspect locally without uploading" }
50707
50739
  ).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(
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@docyrus/animaguru",
3
- "version": "0.28.0",
3
+ "version": "0.29.0",
4
4
  "description": "Agent-friendly AnimaGuru CLI built with CrustJS",
5
5
  "type": "module",
6
6
  "bin": {
@@ -112,7 +112,7 @@ Run `npm run build`. Review the actual browser at desktop and mobile sizes, play
112
112
 
113
113
  Deliver the playable HTML film first unless the user asked for MP4 too. A branching story (`story.json`) and a quiz film (`quiz.json`) are HTML5 only. For an embeddable HTML5 deliverable, use the download menu or `npm run export:html`. Unzip and test the static package over HTTP(S), including its iframe example; it should play without the authoring workspace or an AnimaGuru account. `npm run build` includes the same download in the static player. Preserve embed mode and hide authoring controls there.
114
114
 
115
- For MP4 on the user's own computer, run `npm run export:mp4 -- --headless`; it captures in a local headless Chrome (installed Chrome or Edge, `PLAYWRIGHT_CHROMIUM_EXECUTABLE_PATH`, or `npx playwright install chromium`). Without `--headless`, open the printed local capture URL in a browser and keep it open until completion. The script verifies H.264/AAC, dimensions, duration and exact frame count and writes a JSON report next to the MP4. Captions stay out of the picture; the `captions` resource track becomes `.srt` (YouTube, desktop players) and `.vtt` (HTML5) subtitle files with the MP4's name, so keep that track's clips equal to the spoken lines. For social feeds that need open captions, add `-- --burn-captions`; it saves `SLUG-LANGUAGE-captioned.mp4` with captions in the picture and no subtitle files. Set `"poster"` in `film.config.json` to the seconds of the film's most representative frame (title, product or end card, never a fade or a blank moment); the export saves it beside the MP4 as a 1280-pixel JPEG thumbnail, which `animaguru youtube upload` and video pages can use. Exporting existing local assets spends no generation credits. If browser control is unavailable, give the user the printed URL and explain the one manual step. The cloud Animator makes no MP4s and `export:mp4` refuses to run there: the user applies the draft and exports the MP4 in their browser.
115
+ For MP4 on the user's own computer, run `npm run export:mp4 -- --headless`; it captures in a local headless Chrome (installed Chrome or Edge, `PLAYWRIGHT_CHROMIUM_EXECUTABLE_PATH`, or `npx playwright install chromium`). Without `--headless`, open the printed local capture URL in a browser and keep it open until completion. The script verifies H.264/AAC, dimensions, duration and exact frame count and writes a JSON report next to the MP4. Captions stay out of the picture; the `captions` resource track becomes `.srt` (YouTube, desktop players) and `.vtt` (HTML5) subtitle files with the MP4's name, so keep that track's clips equal to the spoken lines. For social feeds that need open captions, add `-- --burn-captions`; it saves `SLUG-LANGUAGE-captioned.mp4` with captions in the picture and no subtitle files. Set `"poster"` in `film.config.json` to the seconds of the film's most representative frame (title, product or end card, never a fade or a blank moment); the export saves it beside the MP4 as a 1280-pixel JPEG thumbnail, which `animaguru youtube upload` and video pages can use. Exporting existing local assets spends no generation credits. If browser control is unavailable, give the user the printed URL and explain the one manual step. The cloud Animator makes no MP4s and `export:mp4` refuses to run there: the user exports the MP4 in their browser from the draft page.
116
116
 
117
117
  `npm run dev` starts the preview. The capture API and file map are documented in the generated project README. Publishing a website, package or campaign is separate from creating the local video.
118
118
 
@@ -12,8 +12,8 @@ import { workspacePlugin } from "./workspace-plugin.mjs";
12
12
  import { subtitleCues, toSrt, toVtt } from "./subtitles.mjs";
13
13
  import { captureHeadless, takeHeadlessFlag } from "./headless-browser.mjs";
14
14
 
15
- // The cloud Animator makes no MP4s: once the user applies the draft, they export it in their own browser.
16
- if (process.env.ANIMAGURU_CLOUD_WORKSPACE) throw new Error("MP4 export is not available in the cloud Animator. After the user applies the draft, they export the MP4 in their browser from the draft page.");
15
+ // The cloud Animator makes no MP4s: the user exports the film in their own browser, from the draft page.
16
+ if (process.env.ANIMAGURU_CLOUD_WORKSPACE) throw new Error("MP4 export is not available in the cloud Animator. The user exports the MP4 in their browser from the draft page.");
17
17
 
18
18
  const root = fileURLToPath(new URL("../", import.meta.url));
19
19
  const selection = projectArguments(process.argv.slice(2));