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,232 @@
1
+ import type { ImageModelV3, SpeechModelV3 } from "@ai-sdk/provider";
2
+ import type { MusicModelV3 } from "../ai-sdk/music-model";
3
+ import type {
4
+ Position,
5
+ ResizeMode,
6
+ SizeValue,
7
+ TransitionOptions,
8
+ } from "../ai-sdk/providers/editly/types";
9
+ import type { VideoModelV3 } from "../ai-sdk/video-model";
10
+
11
+ export type VargElementType =
12
+ | "render"
13
+ | "clip"
14
+ | "overlay"
15
+ | "image"
16
+ | "video"
17
+ | "animate"
18
+ | "speech"
19
+ | "talking-head"
20
+ | "title"
21
+ | "subtitle"
22
+ | "music"
23
+ | "captions"
24
+ | "split"
25
+ | "slider"
26
+ | "swipe"
27
+ | "packshot";
28
+
29
+ export interface VargElement<T extends VargElementType = VargElementType> {
30
+ type: T;
31
+ props: Record<string, unknown>;
32
+ children: VargNode[];
33
+ }
34
+
35
+ export type VargNode =
36
+ | VargElement
37
+ | string
38
+ | number
39
+ | null
40
+ | undefined
41
+ | VargNode[];
42
+
43
+ export interface BaseProps {
44
+ key?: string | number;
45
+ }
46
+
47
+ export interface PositionProps {
48
+ left?: SizeValue;
49
+ top?: SizeValue;
50
+ width?: SizeValue;
51
+ height?: SizeValue;
52
+ }
53
+
54
+ export interface VolumeProps {
55
+ volume?: number;
56
+ }
57
+
58
+ export interface AudioProps extends VolumeProps {
59
+ keepAudio?: boolean;
60
+ }
61
+
62
+ export type TrimProps =
63
+ | { cutFrom?: number; cutTo?: number; duration?: never }
64
+ | { cutFrom?: number; cutTo?: never; duration?: number };
65
+
66
+ // Root container - sets dimensions, fps, contains clips
67
+ export interface RenderProps extends BaseProps {
68
+ width?: number;
69
+ height?: number;
70
+ fps?: number;
71
+ normalize?: boolean;
72
+ children?: VargNode;
73
+ }
74
+
75
+ export interface ClipProps extends BaseProps {
76
+ duration?: number | "auto";
77
+ transition?: TransitionOptions;
78
+ children?: VargNode;
79
+ }
80
+
81
+ export interface OverlayProps extends BaseProps, PositionProps, AudioProps {
82
+ children?: VargNode;
83
+ }
84
+
85
+ export type ImageInput = Uint8Array | string | VargElement<"image">;
86
+ export type ImagePrompt = string | { text?: string; images: ImageInput[] };
87
+
88
+ export interface ImageProps extends BaseProps, PositionProps {
89
+ prompt?: ImagePrompt;
90
+ src?: string;
91
+ model?: ImageModelV3;
92
+ aspectRatio?: `${number}:${number}`;
93
+ zoom?: "in" | "out" | "left" | "right";
94
+ resize?: ResizeMode;
95
+ position?: Position;
96
+ size?: { width: string; height: string };
97
+ removeBackground?: boolean;
98
+ }
99
+
100
+ export type VideoPrompt =
101
+ | string
102
+ | {
103
+ text?: string;
104
+ images?: ImageInput[];
105
+ audio?: Uint8Array | string;
106
+ video?: Uint8Array | string;
107
+ };
108
+
109
+ export type VideoProps = BaseProps &
110
+ PositionProps &
111
+ AudioProps &
112
+ TrimProps & {
113
+ prompt?: VideoPrompt;
114
+ src?: string;
115
+ model?: VideoModelV3;
116
+ resize?: ResizeMode;
117
+ };
118
+
119
+ // Image-to-video animation
120
+ export interface AnimateProps extends BaseProps, PositionProps {
121
+ image?: VargElement<"image">;
122
+ src?: string;
123
+ model?: VideoModelV3;
124
+ motion?: string;
125
+ duration?: number;
126
+ }
127
+
128
+ export interface SpeechProps extends BaseProps, VolumeProps {
129
+ voice?: string;
130
+ model?: SpeechModelV3;
131
+ id?: string;
132
+ children?: string;
133
+ }
134
+
135
+ export interface TalkingHeadProps extends BaseProps {
136
+ character?: string;
137
+ src?: string;
138
+ voice?: string;
139
+ model?: VideoModelV3;
140
+ lipsyncModel?: VideoModelV3;
141
+ position?:
142
+ | Position
143
+ | { left?: string; right?: string; top?: string; bottom?: string };
144
+ size?: { width: string; height: string };
145
+ children?: string;
146
+ }
147
+
148
+ export interface TitleProps extends BaseProps {
149
+ position?: Position;
150
+ color?: string;
151
+ start?: number;
152
+ end?: number;
153
+ children?: string;
154
+ }
155
+
156
+ export interface SubtitleProps extends BaseProps {
157
+ backgroundColor?: string;
158
+ children?: string;
159
+ }
160
+
161
+ export type MusicProps = BaseProps &
162
+ VolumeProps &
163
+ TrimProps & {
164
+ prompt?: string;
165
+ model?: MusicModelV3;
166
+ src?: string;
167
+ loop?: boolean;
168
+ ducking?: boolean;
169
+ };
170
+
171
+ export interface CaptionsProps extends BaseProps {
172
+ src?: string | VargElement<"speech">;
173
+ srt?: string;
174
+ style?: "tiktok" | "karaoke" | "bounce" | "typewriter";
175
+ color?: string;
176
+ activeColor?: string;
177
+ fontSize?: number;
178
+ }
179
+
180
+ export interface SplitProps extends BaseProps {
181
+ direction?: "horizontal" | "vertical";
182
+ children?: VargNode;
183
+ }
184
+
185
+ export interface SliderProps extends BaseProps {
186
+ direction?: "horizontal" | "vertical";
187
+ children?: VargNode;
188
+ }
189
+
190
+ export interface SwipeProps extends BaseProps {
191
+ direction?: "left" | "right" | "up" | "down";
192
+ interval?: number;
193
+ children?: VargNode;
194
+ }
195
+
196
+ export interface PackshotProps extends BaseProps {
197
+ background?: VargElement<"image"> | string;
198
+ logo?: string;
199
+ logoPosition?: Position;
200
+ logoSize?: SizeValue;
201
+ cta?: string;
202
+ ctaPosition?: Position;
203
+ ctaColor?: string;
204
+ ctaSize?: number;
205
+ blinkCta?: boolean;
206
+ duration?: number;
207
+ }
208
+
209
+ export interface RenderOptions {
210
+ output?: string;
211
+ cache?: string;
212
+ quiet?: boolean;
213
+ }
214
+
215
+ export interface ElementPropsMap {
216
+ render: RenderProps;
217
+ clip: ClipProps;
218
+ overlay: OverlayProps;
219
+ image: ImageProps;
220
+ video: VideoProps;
221
+ animate: AnimateProps;
222
+ speech: SpeechProps;
223
+ "talking-head": TalkingHeadProps;
224
+ title: TitleProps;
225
+ subtitle: SubtitleProps;
226
+ music: MusicProps;
227
+ captions: CaptionsProps;
228
+ split: SplitProps;
229
+ slider: SliderProps;
230
+ swipe: SwipeProps;
231
+ packshot: PackshotProps;
232
+ }
@@ -0,0 +1,26 @@
1
+ import { resolve } from "node:path";
2
+ import { createStudioServer } from "./server";
3
+
4
+ // Parse arguments: bun run studio [file.tsx] [--cache=.cache/ai]
5
+ let initialFile: string | undefined;
6
+ let cacheDir = ".cache/ai";
7
+
8
+ for (const arg of process.argv.slice(2)) {
9
+ if (arg.startsWith("--cache=")) {
10
+ cacheDir = arg.replace("--cache=", "");
11
+ } else if (arg.endsWith(".tsx") || arg.endsWith(".ts")) {
12
+ initialFile = resolve(arg);
13
+ }
14
+ }
15
+
16
+ console.log("varg studio starting...");
17
+ console.log(`cache folder: ${cacheDir}`);
18
+ if (initialFile) {
19
+ console.log(`initial file: ${initialFile}`);
20
+ }
21
+
22
+ const { port } = createStudioServer({ cacheDir, initialFile });
23
+
24
+ console.log(`\nopen http://localhost:${port}`);
25
+ console.log(" /editor - code editor");
26
+ console.log(" /cache - cache viewer");
@@ -0,0 +1,102 @@
1
+ import { readdir, stat } from "node:fs/promises";
2
+ import { join } from "node:path";
3
+ import type { CacheEntry, CacheItem, ImageData, VideoData } from "./types";
4
+
5
+ function extractBase64(data: ImageData | VideoData): string | null {
6
+ if (
7
+ "uint8ArrayData" in data &&
8
+ data.uint8ArrayData?.__type === "Uint8Array"
9
+ ) {
10
+ return data.uint8ArrayData.data;
11
+ }
12
+ if ("_data" in data && data._data?.__type === "Uint8Array") {
13
+ return data._data.data;
14
+ }
15
+ if ("base64" in data && data.base64) {
16
+ return data.base64;
17
+ }
18
+ return null;
19
+ }
20
+
21
+ function detectMediaType(
22
+ filename: string,
23
+ value: unknown,
24
+ ): "image" | "video" | "unknown" {
25
+ if (filename.startsWith("generateImage")) return "image";
26
+ if (filename.startsWith("generateVideo")) return "video";
27
+
28
+ if (value && typeof value === "object") {
29
+ const v = value as Record<string, unknown>;
30
+ if ("images" in v) return "image";
31
+ if ("video" in v) return "video";
32
+ }
33
+
34
+ return "unknown";
35
+ }
36
+
37
+ export async function scanCacheFolder(cacheDir: string): Promise<CacheItem[]> {
38
+ const items: CacheItem[] = [];
39
+
40
+ try {
41
+ const files = await readdir(cacheDir);
42
+
43
+ for (const file of files) {
44
+ if (!file.endsWith(".json") || file === ".gitkeep") continue;
45
+
46
+ const filePath = join(cacheDir, file);
47
+ const fileStat = await stat(filePath);
48
+
49
+ try {
50
+ const content = await Bun.file(filePath).text();
51
+ const entry = JSON.parse(content) as CacheEntry;
52
+ const type = detectMediaType(file, entry.value);
53
+
54
+ items.push({
55
+ id: file.replace(".json", ""),
56
+ filename: file,
57
+ type,
58
+ size: fileStat.size,
59
+ createdAt: fileStat.mtime,
60
+ metadata: { expires: entry.expires },
61
+ });
62
+ } catch {}
63
+ }
64
+ } catch (err) {
65
+ console.error("failed to scan cache folder:", err);
66
+ }
67
+
68
+ return items.sort((a, b) => b.createdAt.getTime() - a.createdAt.getTime());
69
+ }
70
+
71
+ export async function getCacheItemMedia(
72
+ cacheDir: string,
73
+ id: string,
74
+ ): Promise<{ type: "image" | "video"; data: string; mimeType: string } | null> {
75
+ const filePath = join(cacheDir, `${id}.json`);
76
+
77
+ try {
78
+ const content = await Bun.file(filePath).text();
79
+ const entry = JSON.parse(content) as CacheEntry;
80
+ const value = entry.value as Record<string, unknown>;
81
+
82
+ if ("images" in value && Array.isArray(value.images)) {
83
+ const firstImage = value.images[0] as ImageData;
84
+ const base64 = extractBase64(firstImage);
85
+ if (base64) {
86
+ return { type: "image", data: base64, mimeType: "image/jpeg" };
87
+ }
88
+ }
89
+
90
+ if ("video" in value && typeof value.video === "object") {
91
+ const video = value.video as VideoData;
92
+ const base64 = extractBase64(video);
93
+ if (base64) {
94
+ return { type: "video", data: base64, mimeType: "video/mp4" };
95
+ }
96
+ }
97
+
98
+ return null;
99
+ } catch {
100
+ return null;
101
+ }
102
+ }