vargai 0.4.0-alpha4 → 0.4.0-alpha40

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.
Files changed (114) hide show
  1. package/.env.example +6 -0
  2. package/README.md +483 -61
  3. package/assets/fonts/TikTokSans-Bold.ttf +0 -0
  4. package/examples/grok-imagine-test.tsx +155 -0
  5. package/launch-videos/06-kawaii-fruits.tsx +93 -0
  6. package/launch-videos/07-ugc-weight-loss.tsx +132 -0
  7. package/launch-videos/08-talking-head-varg.tsx +107 -0
  8. package/launch-videos/09-girl.tsx +160 -0
  9. package/launch-videos/README.md +42 -0
  10. package/package.json +10 -4
  11. package/pipeline/cookbooks/round-video-character.md +1 -1
  12. package/skills/varg-video-generation/SKILL.md +224 -0
  13. package/skills/varg-video-generation/references/templates.md +380 -0
  14. package/skills/varg-video-generation/scripts/setup.ts +265 -0
  15. package/src/ai-sdk/cache.ts +1 -3
  16. package/src/ai-sdk/examples/google-image.ts +62 -0
  17. package/src/ai-sdk/index.ts +10 -0
  18. package/src/ai-sdk/middleware/wrap-image-model.ts +4 -21
  19. package/src/ai-sdk/middleware/wrap-music-model.ts +4 -16
  20. package/src/ai-sdk/middleware/wrap-video-model.ts +5 -17
  21. package/src/ai-sdk/providers/CONTRIBUTING.md +457 -0
  22. package/src/ai-sdk/providers/editly/backends/index.ts +8 -0
  23. package/src/ai-sdk/providers/editly/backends/local.ts +94 -0
  24. package/src/ai-sdk/providers/editly/backends/types.ts +74 -0
  25. package/src/ai-sdk/providers/editly/editly.test.ts +49 -1
  26. package/src/ai-sdk/providers/editly/index.ts +164 -80
  27. package/src/ai-sdk/providers/editly/layers.ts +58 -6
  28. package/src/ai-sdk/providers/editly/rendi/editly-with-rendi-backend.test.ts +335 -0
  29. package/src/ai-sdk/providers/editly/rendi/index.ts +289 -0
  30. package/src/ai-sdk/providers/editly/rendi/rendi.test.ts +35 -0
  31. package/src/ai-sdk/providers/editly/types.ts +30 -0
  32. package/src/ai-sdk/providers/elevenlabs.ts +10 -2
  33. package/src/ai-sdk/providers/fal.test.ts +214 -0
  34. package/src/ai-sdk/providers/fal.ts +435 -40
  35. package/src/ai-sdk/providers/google.ts +423 -0
  36. package/src/ai-sdk/providers/together.ts +191 -0
  37. package/src/cli/commands/find.tsx +1 -0
  38. package/src/cli/commands/frame.tsx +616 -0
  39. package/src/cli/commands/hello.ts +85 -0
  40. package/src/cli/commands/help.tsx +18 -30
  41. package/src/cli/commands/index.ts +11 -2
  42. package/src/cli/commands/init.tsx +570 -0
  43. package/src/cli/commands/list.tsx +1 -0
  44. package/src/cli/commands/render.tsx +322 -76
  45. package/src/cli/commands/run.tsx +1 -0
  46. package/src/cli/commands/storyboard.tsx +1714 -0
  47. package/src/cli/commands/which.tsx +1 -0
  48. package/src/cli/index.ts +23 -4
  49. package/src/cli/ui/components/Badge.tsx +1 -0
  50. package/src/cli/ui/components/DataTable.tsx +1 -0
  51. package/src/cli/ui/components/Header.tsx +1 -0
  52. package/src/cli/ui/components/HelpBlock.tsx +1 -0
  53. package/src/cli/ui/components/KeyValue.tsx +1 -0
  54. package/src/cli/ui/components/OptionRow.tsx +1 -0
  55. package/src/cli/ui/components/Separator.tsx +1 -0
  56. package/src/cli/ui/components/StatusBox.tsx +1 -0
  57. package/src/cli/ui/components/VargBox.tsx +1 -0
  58. package/src/cli/ui/components/VargProgress.tsx +1 -0
  59. package/src/cli/ui/components/VargSpinner.tsx +1 -0
  60. package/src/cli/ui/components/VargText.tsx +1 -0
  61. package/src/definitions/actions/grok-edit.ts +133 -0
  62. package/src/definitions/actions/index.ts +16 -0
  63. package/src/definitions/actions/qwen-angles.ts +218 -0
  64. package/src/index.ts +1 -0
  65. package/src/providers/fal.ts +196 -0
  66. package/src/react/assets.ts +9 -0
  67. package/src/react/elements.ts +0 -5
  68. package/src/react/examples/branching.tsx +6 -4
  69. package/src/react/examples/character-video.tsx +13 -10
  70. package/src/react/examples/local-files-test.tsx +19 -0
  71. package/src/react/examples/ltx2-test.tsx +25 -0
  72. package/src/react/examples/madi.tsx +13 -10
  73. package/src/react/examples/mcmeows.tsx +40 -0
  74. package/src/react/examples/music-defaults.tsx +24 -0
  75. package/src/react/examples/quickstart-test.tsx +101 -0
  76. package/src/react/examples/qwen-angles-test.tsx +72 -0
  77. package/src/react/index.ts +3 -3
  78. package/src/react/layouts/grid.tsx +1 -1
  79. package/src/react/layouts/index.ts +2 -1
  80. package/src/react/layouts/slot.tsx +85 -0
  81. package/src/react/layouts/split.tsx +18 -0
  82. package/src/react/react.test.ts +60 -11
  83. package/src/react/renderers/burn-captions.ts +95 -0
  84. package/src/react/renderers/cache.test.ts +182 -0
  85. package/src/react/renderers/captions.ts +25 -6
  86. package/src/react/renderers/clip.ts +56 -25
  87. package/src/react/renderers/context.ts +5 -2
  88. package/src/react/renderers/image.ts +5 -2
  89. package/src/react/renderers/index.ts +0 -1
  90. package/src/react/renderers/music.ts +8 -3
  91. package/src/react/renderers/packshot/blinking-button.ts +413 -0
  92. package/src/react/renderers/packshot.ts +170 -8
  93. package/src/react/renderers/progress.ts +4 -3
  94. package/src/react/renderers/render.ts +127 -71
  95. package/src/react/renderers/speech.ts +2 -2
  96. package/src/react/renderers/split.ts +34 -13
  97. package/src/react/renderers/utils.test.ts +80 -0
  98. package/src/react/renderers/utils.ts +37 -1
  99. package/src/react/renderers/video.ts +47 -9
  100. package/src/react/types.ts +70 -17
  101. package/src/studio/stages.ts +40 -39
  102. package/src/studio/step-renderer.ts +14 -24
  103. package/src/studio/ui/index.html +2 -2
  104. package/src/tests/all.test.ts +4 -4
  105. package/src/tests/index.ts +1 -1
  106. package/test-slot-grid.tsx +19 -0
  107. package/test-slot-userland.tsx +30 -0
  108. package/test-sync-v2.ts +30 -0
  109. package/test-sync-v2.tsx +29 -0
  110. package/tsconfig.json +1 -1
  111. package/video.tsx +7 -0
  112. package/src/ai-sdk/providers/editly/ffmpeg.ts +0 -60
  113. package/src/react/renderers/animate.ts +0 -59
  114. /package/src/cli/commands/{studio.tsx → studio.ts} +0 -0
@@ -763,8 +763,8 @@
763
763
  <script src="https://unpkg.com/drawflow@0.0.60/dist/drawflow.min.js"></script>
764
764
  <script src="https://cdn.jsdelivr.net/npm/monaco-editor@0.45.0/min/vs/loader.js"></script>
765
765
  <script>
766
- const DEFAULT_CODE = `import { fal } from "../fal-provider";
767
- import { Clip, Image, Render, Video } from "../react";
766
+ const DEFAULT_CODE = `import { fal } from "vargai/ai";
767
+ import { Clip, Image, Render, Video } from "vargai/react";
768
768
 
769
769
  export default (
770
770
  <Render width={1080} height={1920}>
@@ -5,7 +5,7 @@
5
5
  * Run with: bun run src/tests/all.test.ts
6
6
  *
7
7
  * Note: Most tests require API keys to be set in environment variables:
8
- * - FAL_KEY
8
+ * - FAL_API_KEY (or FAL_KEY)
9
9
  * - REPLICATE_API_TOKEN
10
10
  * - ELEVENLABS_API_KEY
11
11
  * - GROQ_API_KEY
@@ -318,7 +318,7 @@ await test(
318
318
  }
319
319
  console.log(` Generated: ${result.data.images[0].url}`);
320
320
  },
321
- !hasApiKey("FAL_KEY"),
321
+ !hasApiKey(["FAL_API_KEY", "FAL_KEY"]),
322
322
  );
323
323
 
324
324
  await test(
@@ -334,7 +334,7 @@ await test(
334
334
  }
335
335
  console.log(` Generated: ${result.data.video.url}`);
336
336
  },
337
- !hasApiKey("FAL_KEY"),
337
+ !hasApiKey(["FAL_API_KEY", "FAL_KEY"]),
338
338
  );
339
339
 
340
340
  // Replicate tests
@@ -455,7 +455,7 @@ await test(
455
455
  }
456
456
  console.log(` Output: ${JSON.stringify(result.output).slice(0, 100)}...`);
457
457
  },
458
- !hasApiKey("FAL_KEY"),
458
+ !hasApiKey(["FAL_API_KEY", "FAL_KEY"]),
459
459
  );
460
460
 
461
461
  await test(
@@ -20,7 +20,7 @@ Available test files:
20
20
  bun run src/tests/all.test.ts
21
21
  Comprehensive tests including live API calls.
22
22
  Requires API keys set in environment variables:
23
- - FAL_KEY
23
+ - FAL_API_KEY (or FAL_KEY)
24
24
  - REPLICATE_API_TOKEN
25
25
  - ELEVENLABS_API_KEY
26
26
  - GROQ_API_KEY
@@ -0,0 +1,19 @@
1
+ import { Clip, Render, render, Video } from "./src/react";
2
+ import { Grid, Slot } from "./src/react/layouts";
3
+
4
+ const video1 = <Video src="media/fitness-demo.mp4" />;
5
+ const video2 = <Video src="media/kangaroo-scene.mp4" />;
6
+
7
+ await render(
8
+ <Render width={1080} height={1920}>
9
+ <Clip duration={3}>
10
+ <Grid columns={1} rows={2}>
11
+ <Slot class="fit-cover pos-top">{video1}</Slot>
12
+ <Slot class="fit-cover pos-bottom">{video2}</Slot>
13
+ </Grid>
14
+ </Clip>
15
+ </Render>,
16
+ { output: "output/test-slot-grid.mp4" },
17
+ );
18
+
19
+ console.log("done: output/test-slot-grid.mp4");
@@ -0,0 +1,30 @@
1
+ import { Clip, Render, render, Video } from "./src/react";
2
+ import { Slot, Split } from "./src/react/layouts";
3
+
4
+ const video1 = <Video src="media/fitness-demo.mp4" />;
5
+ const video2 = <Video src="media/kangaroo-scene.mp4" />;
6
+
7
+ const positions = [
8
+ ["pos-top", "pos-top"],
9
+ ["pos-bottom", "pos-bottom"],
10
+ ["pos-center", "pos-center"],
11
+ ["pos-top", "pos-bottom"],
12
+ ] as const;
13
+
14
+ for (const [pos1, pos2] of positions) {
15
+ const outFile = `output/test-slot-userland-${pos1}-${pos2}.mp4`;
16
+
17
+ await render(
18
+ <Render width={1080} height={1920}>
19
+ <Clip duration={3}>
20
+ <Split direction="vertical">
21
+ <Slot class={`fit-cover ${pos1}`}>{video1}</Slot>
22
+ <Slot class={`fit-cover ${pos2}`}>{video2}</Slot>
23
+ </Split>
24
+ </Clip>
25
+ </Render>,
26
+ { output: outFile },
27
+ );
28
+
29
+ console.log(`done: ${outFile}`);
30
+ }
@@ -0,0 +1,30 @@
1
+ /**
2
+ * Quick sync-v2 lipsync test
3
+ * Required env: FAL_API_KEY
4
+ */
5
+
6
+ import { File, fal, generateVideo } from "./src/ai-sdk/index";
7
+
8
+ async function main() {
9
+ const videoPath = "output/extracted-videos/tyler/tyler-10.mp4";
10
+ const audioPath = "output/extracted-videos/tyler/tyler-10.mp4"; // use same video's audio for now
11
+
12
+ console.log("loading media files...");
13
+ const videoFile = File.fromPath(videoPath);
14
+ const audioFile = File.fromPath(audioPath);
15
+
16
+ console.log("lipsyncing with sync-v2...");
17
+ const { video } = await generateVideo({
18
+ model: fal.videoModel("sync-v2"),
19
+ prompt: {
20
+ video: await videoFile.data(),
21
+ audio: await audioFile.data(),
22
+ },
23
+ });
24
+
25
+ console.log(`lipsynced video: ${video.uint8Array.byteLength} bytes`);
26
+ await Bun.write("output/test-sync-v2.mp4", video.uint8Array);
27
+ console.log("done! saved to output/test-sync-v2.mp4");
28
+ }
29
+
30
+ main().catch(console.error);
@@ -0,0 +1,29 @@
1
+ /**
2
+ * Simple sync-v2 lipsync test in React format
3
+ * Takes an existing video + audio and lipsyncs them together
4
+ *
5
+ * Run: bunx vargai render test-sync-v2.tsx
6
+ */
7
+ import { fal } from "vargai/ai";
8
+ import { Clip, Render, Video } from "vargai/react";
9
+
10
+ // Source video (existing talking head video)
11
+ const SOURCE_VIDEO = "output/extracted-videos/tyler/tyler-10.mp4";
12
+
13
+ // Source audio (pre-generated speech)
14
+ const SOURCE_AUDIO = "output/test-speech.mp3";
15
+
16
+ export default (
17
+ <Render width={1080} height={1920}>
18
+ <Clip duration={10}>
19
+ {/* Lipsync: video + audio -> sync-v2 */}
20
+ <Video
21
+ prompt={{
22
+ video: SOURCE_VIDEO,
23
+ audio: SOURCE_AUDIO,
24
+ }}
25
+ model={fal.videoModel("sync-v2")}
26
+ />
27
+ </Clip>
28
+ </Render>
29
+ );
package/tsconfig.json CHANGED
@@ -35,7 +35,7 @@
35
35
  "vargai/jsx-dev-runtime": ["./src/react/runtime/jsx-dev-runtime.ts"]
36
36
  }
37
37
  },
38
- "include": ["src/**/*"],
38
+ "include": ["src/**/*", "launch-videos/**/*"],
39
39
  "exclude": [
40
40
  "node_modules",
41
41
  "action",
package/video.tsx ADDED
@@ -0,0 +1,7 @@
1
+ export default (
2
+ <Render>
3
+ <Clip>
4
+ <Video prompt="a cat coding" model={fal.videoModel("wan-2.5")} />
5
+ </Clip>
6
+ </Render>
7
+ );
@@ -1,60 +0,0 @@
1
- import { $ } from "bun";
2
- import type { VideoInfo } from "./types";
3
-
4
- const FFMPEG_COMMON_ARGS = ["-hide_banner", "-loglevel", "error"];
5
-
6
- export async function ffmpeg(
7
- args: string[],
8
- options?: { stdin?: "pipe" | "ignore"; stdout?: "pipe" | "inherit" },
9
- ): Promise<{ stdout: Buffer; exitCode: number }> {
10
- const proc = Bun.spawn(["ffmpeg", ...FFMPEG_COMMON_ARGS, ...args], {
11
- stdin: options?.stdin ?? "ignore",
12
- stdout: options?.stdout === "inherit" ? "inherit" : "pipe",
13
- stderr: "inherit",
14
- });
15
-
16
- const stdout =
17
- options?.stdout === "inherit"
18
- ? Buffer.alloc(0)
19
- : Buffer.from(await new Response(proc.stdout).arrayBuffer());
20
- const exitCode = await proc.exited;
21
-
22
- return { stdout, exitCode };
23
- }
24
-
25
- export async function ffprobe(path: string): Promise<VideoInfo> {
26
- const result =
27
- await $`ffprobe -v error -show_entries stream=width,height,r_frame_rate,codec_type -show_entries format=duration -of json ${path}`.json();
28
-
29
- const videoStream = result.streams?.find(
30
- (s: { codec_type: string }) => s.codec_type === "video",
31
- );
32
- const duration = parseFloat(result.format?.duration ?? "0");
33
-
34
- let fps: number | undefined;
35
- const framerateStr: string | undefined = videoStream?.r_frame_rate;
36
- if (framerateStr) {
37
- const parts = framerateStr.split("/").map(Number);
38
- const num = parts[0];
39
- const den = parts[1];
40
- if (den && den > 0 && num) fps = num / den;
41
- }
42
-
43
- return {
44
- duration,
45
- width: videoStream?.width,
46
- height: videoStream?.height,
47
- fps,
48
- framerateStr,
49
- };
50
- }
51
-
52
- export async function readDuration(path: string): Promise<number> {
53
- const result =
54
- await $`ffprobe -v error -show_entries format=duration -of default=noprint_wrappers=1:nokey=1 ${path}`.text();
55
- return parseFloat(result.trim());
56
- }
57
-
58
- export function multipleOf2(n: number): number {
59
- return Math.round(n / 2) * 2;
60
- }
@@ -1,59 +0,0 @@
1
- import { File } from "../../ai-sdk/file";
2
- import type { generateVideo } from "../../ai-sdk/generate-video";
3
- import type { AnimateProps, VargElement } from "../types";
4
- import type { RenderContext } from "./context";
5
- import { renderImage } from "./image";
6
- import { addTask, completeTask, startTask } from "./progress";
7
- import { computeCacheKey, resolvePath } from "./utils";
8
-
9
- export async function renderAnimate(
10
- element: VargElement<"animate">,
11
- ctx: RenderContext,
12
- ): Promise<string> {
13
- const props = element.props as AnimateProps;
14
-
15
- let imagePath: string;
16
- if (props.src) {
17
- imagePath = props.src;
18
- } else if (props.image) {
19
- if (props.image.type !== "image") {
20
- throw new Error(
21
- `Animate 'image' prop must be an <Image /> element, got <${props.image.type} />`,
22
- );
23
- }
24
- imagePath = await renderImage(props.image as VargElement<"image">, ctx);
25
- } else {
26
- throw new Error("Animate element requires either 'src' or 'image' prop");
27
- }
28
-
29
- const model = props.model;
30
- if (!model) {
31
- throw new Error("Animate element requires 'model' prop");
32
- }
33
-
34
- const imageData = await Bun.file(resolvePath(imagePath)).arrayBuffer();
35
- const cacheKey = computeCacheKey(element);
36
-
37
- const modelId = typeof model === "string" ? model : model.modelId;
38
- const taskId = ctx.progress
39
- ? addTask(ctx.progress, "animate", modelId)
40
- : null;
41
- if (taskId && ctx.progress) startTask(ctx.progress, taskId);
42
-
43
- const { video } = await ctx.generateVideo({
44
- model,
45
- prompt: {
46
- text: props.motion ?? "",
47
- images: [new Uint8Array(imageData)],
48
- },
49
- duration: props.duration ?? 5,
50
- cacheKey,
51
- } as Parameters<typeof generateVideo>[0]);
52
-
53
- if (taskId && ctx.progress) completeTask(ctx.progress, taskId);
54
-
55
- const tempPath = await File.toTemp(video);
56
- ctx.tempFiles.push(tempPath);
57
-
58
- return tempPath;
59
- }
File without changes