varg.ai-sdk 0.1.1 → 0.4.0-alpha.1

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 (246) hide show
  1. package/.claude/settings.local.json +1 -1
  2. package/.env.example +3 -0
  3. package/.github/workflows/ci.yml +23 -0
  4. package/.husky/README.md +102 -0
  5. package/.husky/commit-msg +6 -0
  6. package/.husky/pre-commit +9 -0
  7. package/.husky/pre-push +6 -0
  8. package/.size-limit.json +8 -0
  9. package/.test-hooks.ts +5 -0
  10. package/CLAUDE.md +10 -3
  11. package/CONTRIBUTING.md +150 -0
  12. package/LICENSE.md +53 -0
  13. package/README.md +56 -209
  14. package/SKILLS.md +26 -10
  15. package/biome.json +7 -1
  16. package/bun.lock +1286 -0
  17. package/commitlint.config.js +22 -0
  18. package/docs/index.html +1130 -0
  19. package/docs/prompting.md +326 -0
  20. package/docs/react.md +834 -0
  21. package/docs/sdk.md +812 -0
  22. package/ffmpeg/CLAUDE.md +68 -0
  23. package/package.json +43 -10
  24. package/pipeline/cookbooks/scripts/animate-frames-parallel.ts +84 -0
  25. package/pipeline/cookbooks/scripts/combine-scenes.sh +53 -0
  26. package/pipeline/cookbooks/scripts/generate-frames-parallel.ts +99 -0
  27. package/pipeline/cookbooks/scripts/still-to-video.sh +37 -0
  28. package/pipeline/cookbooks/text-to-tiktok.md +669 -0
  29. package/pipeline/cookbooks/trendwatching.md +156 -0
  30. package/plan.md +281 -0
  31. package/scripts/.gitkeep +0 -0
  32. package/src/ai-sdk/cache.ts +142 -0
  33. package/src/ai-sdk/examples/cached-generation.ts +53 -0
  34. package/src/ai-sdk/examples/duet-scene-4.ts +53 -0
  35. package/src/ai-sdk/examples/duet-scene-5-audio.ts +32 -0
  36. package/src/ai-sdk/examples/duet-video.ts +56 -0
  37. package/src/ai-sdk/examples/editly-composition.ts +63 -0
  38. package/src/ai-sdk/examples/editly-test.ts +57 -0
  39. package/src/ai-sdk/examples/editly-video-test.ts +52 -0
  40. package/src/ai-sdk/examples/fal-lipsync.ts +43 -0
  41. package/src/ai-sdk/examples/higgsfield-image.ts +61 -0
  42. package/src/ai-sdk/examples/music-generation.ts +19 -0
  43. package/src/ai-sdk/examples/openai-sora.ts +34 -0
  44. package/src/ai-sdk/examples/replicate-bg-removal.ts +52 -0
  45. package/src/ai-sdk/examples/simpsons-scene.ts +61 -0
  46. package/src/ai-sdk/examples/talking-lion.ts +55 -0
  47. package/src/ai-sdk/examples/video-generation.ts +39 -0
  48. package/src/ai-sdk/examples/workflow-animated-girl.ts +104 -0
  49. package/src/ai-sdk/examples/workflow-before-after.ts +114 -0
  50. package/src/ai-sdk/examples/workflow-character-grid.ts +112 -0
  51. package/src/ai-sdk/examples/workflow-slideshow.ts +161 -0
  52. package/src/ai-sdk/file-cache.ts +112 -0
  53. package/src/ai-sdk/file.ts +238 -0
  54. package/src/ai-sdk/generate-element.ts +92 -0
  55. package/src/ai-sdk/generate-music.ts +46 -0
  56. package/src/ai-sdk/generate-video.ts +165 -0
  57. package/src/ai-sdk/index.ts +72 -0
  58. package/src/ai-sdk/music-model.ts +110 -0
  59. package/src/ai-sdk/providers/editly/editly.test.ts +1108 -0
  60. package/src/ai-sdk/providers/editly/ffmpeg.ts +60 -0
  61. package/src/ai-sdk/providers/editly/index.ts +817 -0
  62. package/src/ai-sdk/providers/editly/layers.ts +776 -0
  63. package/src/ai-sdk/providers/editly/plan.md +144 -0
  64. package/src/ai-sdk/providers/editly/types.ts +328 -0
  65. package/src/ai-sdk/providers/elevenlabs-provider.ts +255 -0
  66. package/src/ai-sdk/providers/fal-provider.ts +512 -0
  67. package/src/ai-sdk/providers/higgsfield.ts +379 -0
  68. package/src/ai-sdk/providers/openai.ts +251 -0
  69. package/src/ai-sdk/providers/replicate.ts +16 -0
  70. package/src/ai-sdk/video-model.ts +185 -0
  71. package/src/cli/commands/find.tsx +137 -0
  72. package/src/cli/commands/help.tsx +85 -0
  73. package/src/cli/commands/index.ts +6 -0
  74. package/src/cli/commands/list.tsx +238 -0
  75. package/src/cli/commands/render.tsx +71 -0
  76. package/src/cli/commands/run.tsx +511 -0
  77. package/src/cli/commands/which.tsx +253 -0
  78. package/src/cli/index.ts +114 -0
  79. package/src/cli/quiet.ts +44 -0
  80. package/src/cli/types.ts +32 -0
  81. package/src/cli/ui/components/Badge.tsx +29 -0
  82. package/src/cli/ui/components/DataTable.tsx +51 -0
  83. package/src/cli/ui/components/Header.tsx +23 -0
  84. package/src/cli/ui/components/HelpBlock.tsx +44 -0
  85. package/src/cli/ui/components/KeyValue.tsx +33 -0
  86. package/src/cli/ui/components/OptionRow.tsx +81 -0
  87. package/src/cli/ui/components/Separator.tsx +23 -0
  88. package/src/cli/ui/components/StatusBox.tsx +108 -0
  89. package/src/cli/ui/components/VargBox.tsx +51 -0
  90. package/src/cli/ui/components/VargProgress.tsx +36 -0
  91. package/src/cli/ui/components/VargSpinner.tsx +34 -0
  92. package/src/cli/ui/components/VargText.tsx +56 -0
  93. package/src/cli/ui/components/index.ts +19 -0
  94. package/src/cli/ui/index.ts +12 -0
  95. package/src/cli/ui/render.ts +35 -0
  96. package/src/cli/ui/theme.ts +63 -0
  97. package/src/cli/utils.ts +78 -0
  98. package/src/core/executor/executor.ts +201 -0
  99. package/src/core/executor/index.ts +13 -0
  100. package/src/core/executor/job.ts +214 -0
  101. package/src/core/executor/pipeline.ts +222 -0
  102. package/src/core/index.ts +11 -0
  103. package/src/core/registry/index.ts +9 -0
  104. package/src/core/registry/loader.ts +149 -0
  105. package/src/core/registry/registry.ts +221 -0
  106. package/src/core/registry/resolver.ts +206 -0
  107. package/src/core/schema/helpers.ts +134 -0
  108. package/src/core/schema/index.ts +8 -0
  109. package/src/core/schema/shared.ts +102 -0
  110. package/src/core/schema/types.ts +279 -0
  111. package/src/core/schema/validator.ts +92 -0
  112. package/src/definitions/actions/captions.ts +261 -0
  113. package/src/definitions/actions/edit.ts +298 -0
  114. package/src/definitions/actions/image.ts +125 -0
  115. package/src/definitions/actions/index.ts +114 -0
  116. package/src/definitions/actions/music.ts +205 -0
  117. package/src/definitions/actions/sync.ts +128 -0
  118. package/{action/transcribe/index.ts → src/definitions/actions/transcribe.ts} +58 -68
  119. package/src/definitions/actions/upload.ts +111 -0
  120. package/src/definitions/actions/video.ts +163 -0
  121. package/src/definitions/actions/voice.ts +119 -0
  122. package/src/definitions/index.ts +23 -0
  123. package/src/definitions/models/elevenlabs.ts +50 -0
  124. package/src/definitions/models/flux.ts +56 -0
  125. package/src/definitions/models/index.ts +36 -0
  126. package/src/definitions/models/kling.ts +56 -0
  127. package/src/definitions/models/llama.ts +54 -0
  128. package/src/definitions/models/nano-banana-pro.ts +102 -0
  129. package/src/definitions/models/sonauto.ts +68 -0
  130. package/src/definitions/models/soul.ts +65 -0
  131. package/src/definitions/models/wan.ts +54 -0
  132. package/src/definitions/models/whisper.ts +44 -0
  133. package/src/definitions/skills/index.ts +12 -0
  134. package/src/definitions/skills/talking-character.ts +87 -0
  135. package/src/definitions/skills/text-to-tiktok.ts +97 -0
  136. package/src/index.ts +118 -0
  137. package/src/providers/apify.ts +269 -0
  138. package/src/providers/base.ts +264 -0
  139. package/src/providers/elevenlabs.ts +217 -0
  140. package/src/providers/fal.ts +392 -0
  141. package/src/providers/ffmpeg.ts +544 -0
  142. package/src/providers/fireworks.ts +193 -0
  143. package/src/providers/groq.ts +149 -0
  144. package/src/providers/higgsfield.ts +145 -0
  145. package/src/providers/index.ts +143 -0
  146. package/src/providers/replicate.ts +147 -0
  147. package/src/providers/storage.ts +206 -0
  148. package/src/react/cli.ts +52 -0
  149. package/src/react/elements.ts +146 -0
  150. package/src/react/examples/branching.tsx +66 -0
  151. package/src/react/examples/captions-demo.tsx +37 -0
  152. package/src/react/examples/character-video.tsx +84 -0
  153. package/src/react/examples/grid.tsx +53 -0
  154. package/src/react/examples/layouts-demo.tsx +57 -0
  155. package/src/react/examples/madi.tsx +60 -0
  156. package/src/react/examples/music-test.tsx +35 -0
  157. package/src/react/examples/onlyfans-1m/workflow.tsx +88 -0
  158. package/src/react/examples/orange-portrait.tsx +41 -0
  159. package/src/react/examples/split-element-demo.tsx +60 -0
  160. package/src/react/examples/split-layout-demo.tsx +60 -0
  161. package/src/react/examples/split.tsx +41 -0
  162. package/src/react/examples/video-grid.tsx +46 -0
  163. package/src/react/index.ts +43 -0
  164. package/src/react/layouts/grid.tsx +28 -0
  165. package/src/react/layouts/index.ts +2 -0
  166. package/src/react/layouts/split.tsx +20 -0
  167. package/src/react/react.test.ts +309 -0
  168. package/src/react/render.ts +21 -0
  169. package/src/react/renderers/animate.ts +59 -0
  170. package/src/react/renderers/captions.ts +297 -0
  171. package/src/react/renderers/clip.ts +248 -0
  172. package/src/react/renderers/context.ts +17 -0
  173. package/src/react/renderers/image.ts +109 -0
  174. package/src/react/renderers/index.ts +22 -0
  175. package/src/react/renderers/music.ts +60 -0
  176. package/src/react/renderers/packshot.ts +84 -0
  177. package/src/react/renderers/progress.ts +173 -0
  178. package/src/react/renderers/render.ts +243 -0
  179. package/src/react/renderers/slider.ts +69 -0
  180. package/src/react/renderers/speech.ts +53 -0
  181. package/src/react/renderers/split.ts +91 -0
  182. package/src/react/renderers/subtitle.ts +16 -0
  183. package/src/react/renderers/swipe.ts +75 -0
  184. package/src/react/renderers/title.ts +17 -0
  185. package/src/react/renderers/utils.ts +124 -0
  186. package/src/react/renderers/video.ts +127 -0
  187. package/src/react/runtime/jsx-dev-runtime.ts +43 -0
  188. package/src/react/runtime/jsx-runtime.ts +35 -0
  189. package/src/react/types.ts +232 -0
  190. package/src/studio/index.ts +26 -0
  191. package/src/studio/scanner.ts +102 -0
  192. package/src/studio/server.ts +554 -0
  193. package/src/studio/stages.ts +251 -0
  194. package/src/studio/step-renderer.ts +279 -0
  195. package/src/studio/types.ts +60 -0
  196. package/src/studio/ui/cache.html +303 -0
  197. package/src/studio/ui/index.html +1820 -0
  198. package/src/tests/all.test.ts +509 -0
  199. package/src/tests/index.ts +33 -0
  200. package/src/tests/unit.test.ts +403 -0
  201. package/tsconfig.cli.json +8 -0
  202. package/tsconfig.json +21 -3
  203. package/TEST_RESULTS.md +0 -122
  204. package/action/captions/SKILL.md +0 -170
  205. package/action/captions/index.ts +0 -169
  206. package/action/edit/SKILL.md +0 -235
  207. package/action/edit/index.ts +0 -437
  208. package/action/image/SKILL.md +0 -140
  209. package/action/image/index.ts +0 -105
  210. package/action/sync/SKILL.md +0 -136
  211. package/action/sync/index.ts +0 -145
  212. package/action/transcribe/SKILL.md +0 -179
  213. package/action/video/SKILL.md +0 -116
  214. package/action/video/index.ts +0 -125
  215. package/action/voice/SKILL.md +0 -125
  216. package/action/voice/index.ts +0 -136
  217. package/cli/commands/find.ts +0 -58
  218. package/cli/commands/help.ts +0 -70
  219. package/cli/commands/list.ts +0 -49
  220. package/cli/commands/run.ts +0 -237
  221. package/cli/commands/which.ts +0 -66
  222. package/cli/discover.ts +0 -66
  223. package/cli/index.ts +0 -33
  224. package/cli/runner.ts +0 -65
  225. package/cli/types.ts +0 -49
  226. package/cli/ui.ts +0 -185
  227. package/index.ts +0 -75
  228. package/lib/README.md +0 -144
  229. package/lib/ai-sdk/fal.ts +0 -106
  230. package/lib/ai-sdk/replicate.ts +0 -107
  231. package/lib/elevenlabs.ts +0 -382
  232. package/lib/fal.ts +0 -467
  233. package/lib/ffmpeg.ts +0 -467
  234. package/lib/fireworks.ts +0 -235
  235. package/lib/groq.ts +0 -246
  236. package/lib/higgsfield.ts +0 -176
  237. package/lib/remotion/SKILL.md +0 -823
  238. package/lib/remotion/cli.ts +0 -115
  239. package/lib/remotion/functions.ts +0 -283
  240. package/lib/remotion/index.ts +0 -19
  241. package/lib/remotion/templates.ts +0 -73
  242. package/lib/replicate.ts +0 -304
  243. package/output.txt +0 -1
  244. package/test-import.ts +0 -7
  245. package/test-services.ts +0 -97
  246. package/utilities/s3.ts +0 -147
@@ -0,0 +1,68 @@
1
+ # ffmpeg video mixing lessons
2
+
3
+ ## problem: audio/video desync when mixing clips
4
+
5
+ ### what went wrong
6
+
7
+ using `-ss X -t Y` to pre-trim input, then applying relative trim filters caused timing drift:
8
+
9
+ ```bash
10
+ # BAD: relative timestamps after pre-trim
11
+ ffmpeg -ss 64 -t 36 -i original.mp4 ...
12
+ -filter_complex "
13
+ [0:v]split=5[orig1][orig2]...;
14
+ [orig1]trim=0:4,setpts=PTS-STARTPTS[o1];
15
+ [orig2]trim=4:11,setpts=PTS-STARTPTS[o2];
16
+ ..."
17
+ ```
18
+
19
+ this produced wrong duration (38s instead of 36s) with audio desync.
20
+
21
+ ### solution: use absolute timestamps from full input
22
+
23
+ trim directly from full original using absolute timestamps:
24
+
25
+ ```bash
26
+ # GOOD: absolute timestamps from full file
27
+ ffmpeg -i original.mp4 -i scene1.mp4 -i scene2.mp4 ...
28
+ -filter_complex "
29
+ [0:v]trim=64:68,setpts=PTS-STARTPTS[o1];
30
+ [1:v]scale=1280:720,trim=4:6,setpts=PTS-STARTPTS[s1];
31
+ [0:v]trim=70:75,setpts=PTS-STARTPTS[o2];
32
+ ...
33
+ [o1][s1][o2]...concat=n=N:v=1:a=0[outv];
34
+ [0:a]atrim=64:100,asetpts=PTS-STARTPTS[outa]
35
+ "
36
+ -map "[outv]" -map "[outa]"
37
+ ```
38
+
39
+ ### key points
40
+
41
+ 1. **absolute timestamps**: trim from full input file, not pre-trimmed
42
+ 2. **separate audio handling**: use `atrim` on audio stream independently
43
+ 3. **setpts reset**: always use `setpts=PTS-STARTPTS` after trim to reset timestamps
44
+ 4. **scale before trim**: when mixing different resolutions, scale first then trim
45
+ 5. **video duration = audio duration**: ensure total video segments match audio segment length
46
+
47
+ ### example: inserting clips into original
48
+
49
+ to insert generated clips at specific timestamps while keeping continuous audio:
50
+
51
+ ```bash
52
+ ffmpeg -y \
53
+ -i original.mp4 \
54
+ -i generated-scene.mp4 \
55
+ -filter_complex "
56
+ [0:v]trim=START1:END1,setpts=PTS-STARTPTS[o1];
57
+ [1:v]scale=1280:720,trim=0:DURATION,setpts=PTS-STARTPTS[s1];
58
+ [0:v]trim=START2:END2,setpts=PTS-STARTPTS[o2];
59
+ [o1][s1][o2]concat=n=3:v=1:a=0[outv];
60
+ [0:a]atrim=AUDIO_START:AUDIO_END,asetpts=PTS-STARTPTS[outa]
61
+ " \
62
+ -map "[outv]" -map "[outa]" \
63
+ -c:v libx264 -preset fast -crf 18 \
64
+ -c:a aac -b:a 192k \
65
+ output.mp4
66
+ ```
67
+
68
+ timestamps must add up: `(END1-START1) + DURATION + (END2-START2) = AUDIO_END - AUDIO_START`
package/package.json CHANGED
@@ -1,42 +1,75 @@
1
1
  {
2
2
  "name": "varg.ai-sdk",
3
- "module": "index.ts",
3
+ "module": "src/index.ts",
4
4
  "type": "module",
5
5
  "bin": {
6
- "varg": "./cli/index.ts"
6
+ "varg": "./src/cli/index.ts"
7
7
  },
8
8
  "scripts": {
9
+ "check": "biome check . && tsc --noEmit",
9
10
  "lint": "biome check .",
10
- "format": "biome format --write ."
11
+ "format": "biome format --write .",
12
+ "type-check": "tsc --noEmit",
13
+ "prepare": "husky || true",
14
+ "size": "size-limit",
15
+ "studio": "bun run src/studio/index.ts"
16
+ },
17
+ "lint-staged": {
18
+ "*.{js,ts,tsx}": [
19
+ "biome check --write --no-errors-on-unmatched"
20
+ ],
21
+ "*.json": [
22
+ "biome format --write"
23
+ ]
11
24
  },
12
25
  "devDependencies": {
13
26
  "@biomejs/biome": "^2.3.7",
14
- "@types/bun": "latest"
27
+ "@commitlint/cli": "^20.1.0",
28
+ "@commitlint/config-conventional": "^20.0.0",
29
+ "@size-limit/preset-small-lib": "^11.2.0",
30
+ "@types/bun": "latest",
31
+ "@types/react": "^19.2.7",
32
+ "husky": "^9.1.7",
33
+ "lint-staged": "^16.2.7"
15
34
  },
16
35
  "peerDependencies": {
17
36
  "typescript": "^5"
18
37
  },
19
38
  "dependencies": {
20
39
  "@ai-sdk/fal": "^1.0.23",
21
- "@ai-sdk/replicate": "^1.0.18",
40
+ "@ai-sdk/fireworks": "^2.0.16",
41
+ "@ai-sdk/groq": "^3.0.12",
42
+ "@ai-sdk/openai": "^3.0.9",
43
+ "@ai-sdk/provider": "^3.0.2",
44
+ "@ai-sdk/replicate": "^2.0.5",
22
45
  "@aws-sdk/client-s3": "^3.937.0",
23
46
  "@aws-sdk/s3-request-presigner": "^3.937.0",
24
- "@elevenlabs/elevenlabs-js": "^2.25.0",
47
+ "@elevenlabs/elevenlabs-js": "^2.28.0",
25
48
  "@fal-ai/client": "^1.7.2",
26
49
  "@higgsfield/client": "^0.1.2",
50
+ "@inkjs/ui": "^2.0.0",
27
51
  "@remotion/cli": "^4.0.377",
28
52
  "@types/fluent-ffmpeg": "^2.1.28",
29
- "ai": "^5.0.98",
53
+ "ai": "^6.0.26",
54
+ "apify-client": "^2.20.0",
30
55
  "citty": "^0.1.6",
31
56
  "fluent-ffmpeg": "^2.1.3",
32
57
  "groq-sdk": "^0.36.0",
58
+ "ink": "^6.5.1",
33
59
  "react": "^19.2.0",
34
60
  "react-dom": "^19.2.0",
35
61
  "remotion": "^4.0.377",
36
- "replicate": "^1.4.0"
62
+ "replicate": "^1.4.0",
63
+ "zod": "^4.2.1"
37
64
  },
38
- "version": "0.1.1",
65
+ "version": "0.4.0-alpha.1",
39
66
  "exports": {
40
- ".": "./index.ts"
67
+ ".": "./src/index.ts",
68
+ "./ai": "./src/ai-sdk/index.ts",
69
+ "./core": "./src/core/index.ts",
70
+ "./providers": "./src/providers/index.ts",
71
+ "./definitions": "./src/definitions/index.ts",
72
+ "./react": "./src/react/index.ts",
73
+ "./studio": "./src/studio/index.ts"
41
74
  }
42
75
  }
@@ -0,0 +1,84 @@
1
+ /**
2
+ * Animate multiple frames in parallel using kling
3
+ * Usage: bun run pipeline/cookbooks/scripts/animate-frames-parallel.ts
4
+ */
5
+
6
+ import { fal } from "@fal-ai/client";
7
+
8
+ interface VideoConfig {
9
+ name: string;
10
+ framePath: string;
11
+ prompt: string;
12
+ duration?: "5" | "10";
13
+ }
14
+
15
+ async function animateFrames(configs: VideoConfig[], outputDir: string) {
16
+ console.log(`Animating ${configs.length} frames in parallel...\n`);
17
+
18
+ // Upload all frames first
19
+ const frameUrls: string[] = [];
20
+ for (const config of configs) {
21
+ const url = await fal.storage.upload(Bun.file(config.framePath));
22
+ frameUrls.push(url);
23
+ }
24
+
25
+ const promises = configs.map((config, i) => {
26
+ return fal.subscribe("fal-ai/kling-video/v2.5-turbo/pro/image-to-video", {
27
+ input: {
28
+ prompt: `${config.prompt}, NO talking NO lip movement`,
29
+ image_url: frameUrls[i],
30
+ duration: config.duration || "5",
31
+ // note: aspect_ratio is determined by input image dimensions
32
+ },
33
+ });
34
+ });
35
+
36
+ const results = await Promise.all(promises);
37
+
38
+ for (let i = 0; i < results.length; i++) {
39
+ const result = results[i] as { data?: { video?: { url?: string } } };
40
+ const url = result.data?.video?.url;
41
+ const config = configs[i];
42
+ if (url && config) {
43
+ const response = await fetch(url);
44
+ const buffer = await response.arrayBuffer();
45
+ await Bun.write(`${outputDir}/${config.name}_video.mp4`, buffer);
46
+ console.log(`${config.name}_video.mp4 saved`);
47
+ } else {
48
+ console.error(`No URL for ${config?.name ?? "unknown"}`);
49
+ }
50
+ }
51
+
52
+ console.log("\nAll videos saved!");
53
+ }
54
+
55
+ // Example usage:
56
+ async function main() {
57
+ const outputDir = "media/girl-ruined-you";
58
+
59
+ const configs: VideoConfig[] = [
60
+ {
61
+ name: "scene6",
62
+ framePath: `${outputDir}/scene6_frame.jpg`,
63
+ prompt:
64
+ "3D pixar animation, two cats meet eyes in coffee shop, warm romantic moment",
65
+ duration: "5",
66
+ },
67
+ {
68
+ name: "scene7",
69
+ framePath: `${outputDir}/scene7_frame.jpg`,
70
+ prompt: "3D pixar animation, two cats walking together, sunset, romantic",
71
+ duration: "5",
72
+ },
73
+ {
74
+ name: "scene14",
75
+ framePath: `${outputDir}/scene14_frame.jpg`,
76
+ prompt: "3D pixar animation, cat looks at sunrise, hopeful realization",
77
+ duration: "5",
78
+ },
79
+ ];
80
+
81
+ await animateFrames(configs, outputDir);
82
+ }
83
+
84
+ main().catch(console.error);
@@ -0,0 +1,53 @@
1
+ #!/bin/bash
2
+ # Combine multiple scene videos with audio clips
3
+ # Usage: ./combine-scenes.sh <project_dir>
4
+
5
+ PROJECT_DIR=${1:-"media/girl-ruined-you"}
6
+
7
+ # Scene timing configuration (adjust as needed)
8
+ # Format: scene_num:start_time:duration
9
+ SCENES=(
10
+ "1:0:3.5"
11
+ "2:3.5:6.5"
12
+ "3:10:10"
13
+ "4:20:15"
14
+ "5:35:7"
15
+ )
16
+
17
+ echo "Extracting audio clips..."
18
+ for scene_config in "${SCENES[@]}"; do
19
+ IFS=':' read -r num start dur <<< "$scene_config"
20
+ ffmpeg -y -i "$PROJECT_DIR/voiceover.mp3" -ss "$start" -t "$dur" "$PROJECT_DIR/audio_scene${num}.mp3" 2>/dev/null
21
+ echo " audio_scene${num}.mp3 ($dur sec)"
22
+ done
23
+
24
+ echo ""
25
+ echo "Combining videos with audio..."
26
+ for scene_config in "${SCENES[@]}"; do
27
+ IFS=':' read -r num start dur <<< "$scene_config"
28
+
29
+ # Calculate loop count needed (5s videos)
30
+ loops=$(echo "($dur / 5) - 1" | bc)
31
+ if [ "$loops" -lt 0 ]; then loops=0; fi
32
+
33
+ ffmpeg -y -stream_loop "$loops" -i "$PROJECT_DIR/scene${num}_video.mp4" \
34
+ -i "$PROJECT_DIR/audio_scene${num}.mp3" \
35
+ -t "$dur" -c:v libx264 -preset fast -crf 20 -c:a aac -b:a 128k -shortest \
36
+ "$PROJECT_DIR/scene${num}_final.mp4" 2>/dev/null
37
+ echo " scene${num}_final.mp4"
38
+ done
39
+
40
+ echo ""
41
+ echo "Creating concat file..."
42
+ rm -f "$PROJECT_DIR/scenes.txt"
43
+ for scene_config in "${SCENES[@]}"; do
44
+ IFS=':' read -r num start dur <<< "$scene_config"
45
+ echo "file 'scene${num}_final.mp4'" >> "$PROJECT_DIR/scenes.txt"
46
+ done
47
+
48
+ echo "Concatenating all scenes..."
49
+ cd "$PROJECT_DIR" && ffmpeg -y -f concat -safe 0 -i scenes.txt -c copy combined_scenes.mp4 2>/dev/null
50
+
51
+ duration=$(ffprobe -v error -show_entries format=duration -of default=noprint_wrappers=1:nokey=1 combined_scenes.mp4)
52
+ echo ""
53
+ echo "Done! combined_scenes.mp4 ($duration sec)"
@@ -0,0 +1,99 @@
1
+ /**
2
+ * Generate multiple scene frames in parallel using flux kontext
3
+ * Usage: bun run pipeline/cookbooks/scripts/generate-frames-parallel.ts
4
+ */
5
+
6
+ import { fal } from "@fal-ai/client";
7
+
8
+ interface FrameConfig {
9
+ name: string;
10
+ prompt: string;
11
+ imageUrls: string[]; // character reference URLs
12
+ multi?: boolean; // use kontext/multi for multiple characters
13
+ }
14
+
15
+ async function generateFrames(configs: FrameConfig[], outputDir: string) {
16
+ console.log(`Generating ${configs.length} frames in parallel...\n`);
17
+
18
+ const promises = configs.map((config) => {
19
+ if (config.multi) {
20
+ return fal.subscribe("fal-ai/flux-pro/kontext/multi", {
21
+ input: {
22
+ prompt: config.prompt,
23
+ image_urls: config.imageUrls,
24
+ aspect_ratio: "9:16" as const,
25
+ },
26
+ });
27
+ } else {
28
+ return fal.subscribe("fal-ai/flux-pro/kontext", {
29
+ input: {
30
+ prompt: config.prompt,
31
+ image_url: config.imageUrls[0] ?? "",
32
+ aspect_ratio: "9:16" as const,
33
+ },
34
+ });
35
+ }
36
+ });
37
+
38
+ const results = await Promise.all(promises);
39
+
40
+ for (let i = 0; i < results.length; i++) {
41
+ const result = results[i] as {
42
+ data?: { images?: Array<{ url?: string }> };
43
+ };
44
+ const url = result.data?.images?.[0]?.url;
45
+ const config = configs[i];
46
+ if (url && config) {
47
+ const response = await fetch(url);
48
+ const buffer = await response.arrayBuffer();
49
+ await Bun.write(`${outputDir}/${config.name}_frame.jpg`, buffer);
50
+ console.log(`${config.name}_frame.jpg saved`);
51
+ } else {
52
+ console.error(`No URL for ${config?.name ?? "unknown"}`);
53
+ }
54
+ }
55
+
56
+ console.log("\nAll frames saved!");
57
+ }
58
+
59
+ // Example usage:
60
+ async function main() {
61
+ const outputDir = "media/girl-ruined-you";
62
+
63
+ // Upload character references first
64
+ const protagonist = await fal.storage.upload(
65
+ Bun.file(`${outputDir}/cat_protagonist.png`),
66
+ );
67
+ const secondGirl = await fal.storage.upload(
68
+ Bun.file(`${outputDir}/cat_second_girl.png`),
69
+ );
70
+
71
+ const configs: FrameConfig[] = [
72
+ {
73
+ name: "scene6",
74
+ prompt:
75
+ "3D pixar style: male cat in hoodie (first) and elegant female cat (second) meeting eyes in coffee shop, warm golden lighting, vertical portrait 9:16",
76
+ imageUrls: [protagonist, secondGirl],
77
+ multi: true,
78
+ },
79
+ {
80
+ name: "scene7",
81
+ prompt:
82
+ "3D pixar style: male cat and female cat walking together, sunset, romantic, vertical portrait 9:16",
83
+ imageUrls: [protagonist, secondGirl],
84
+ multi: true,
85
+ },
86
+ // Single character scene
87
+ {
88
+ name: "scene14",
89
+ prompt:
90
+ "Place this cat looking at sunrise through window, hopeful, vertical portrait 9:16",
91
+ imageUrls: [protagonist],
92
+ multi: false,
93
+ },
94
+ ];
95
+
96
+ await generateFrames(configs, outputDir);
97
+ }
98
+
99
+ main().catch(console.error);
@@ -0,0 +1,37 @@
1
+ #!/bin/bash
2
+ # Convert still frame to video with ken burns effect (slow zoom)
3
+ # Usage: ./still-to-video.sh <input.jpg> <output.mp4> <duration> [zoom_direction]
4
+ # zoom_direction: in (default), out
5
+
6
+ INPUT=$1
7
+ OUTPUT=$2
8
+ DURATION=$3
9
+ ZOOM=${4:-"in"}
10
+
11
+ if [ -z "$INPUT" ] || [ -z "$OUTPUT" ] || [ -z "$DURATION" ]; then
12
+ echo "Usage: ./still-to-video.sh <input.jpg> <output.mp4> <duration> [in|out]"
13
+ exit 1
14
+ fi
15
+
16
+ # Get input dimensions
17
+ WIDTH=$(ffprobe -v error -select_streams v:0 -show_entries stream=width -of csv=p=0 "$INPUT")
18
+ HEIGHT=$(ffprobe -v error -select_streams v:0 -show_entries stream=height -of csv=p=0 "$INPUT")
19
+
20
+ echo "Creating $DURATION sec video from $INPUT ($WIDTH x $HEIGHT)..."
21
+
22
+ if [ "$ZOOM" = "out" ]; then
23
+ # Zoom out: start zoomed in, end at normal
24
+ FILTER="zoompan=z='1.2-0.2*on/(${DURATION}*25)':x='iw/2-(iw/zoom/2)':y='ih/2-(ih/zoom/2)':d=${DURATION}*25:s=${WIDTH}x${HEIGHT}:fps=25"
25
+ else
26
+ # Zoom in: start normal, end zoomed
27
+ FILTER="zoompan=z='1+0.2*on/(${DURATION}*25)':x='iw/2-(iw/zoom/2)':y='ih/2-(ih/zoom/2)':d=${DURATION}*25:s=${WIDTH}x${HEIGHT}:fps=25"
28
+ fi
29
+
30
+ ffmpeg -y -loop 1 -i "$INPUT" \
31
+ -vf "$FILTER" \
32
+ -t "$DURATION" \
33
+ -c:v libx264 -preset fast -crf 20 \
34
+ -pix_fmt yuv420p \
35
+ "$OUTPUT"
36
+
37
+ echo "Done: $OUTPUT"