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.
- package/.claude/settings.local.json +1 -1
- package/.env.example +3 -0
- package/.github/workflows/ci.yml +23 -0
- package/.husky/README.md +102 -0
- package/.husky/commit-msg +6 -0
- package/.husky/pre-commit +9 -0
- package/.husky/pre-push +6 -0
- package/.size-limit.json +8 -0
- package/.test-hooks.ts +5 -0
- package/CLAUDE.md +10 -3
- package/CONTRIBUTING.md +150 -0
- package/LICENSE.md +53 -0
- package/README.md +56 -209
- package/SKILLS.md +26 -10
- package/biome.json +7 -1
- package/bun.lock +1286 -0
- package/commitlint.config.js +22 -0
- package/docs/index.html +1130 -0
- package/docs/prompting.md +326 -0
- package/docs/react.md +834 -0
- package/docs/sdk.md +812 -0
- package/ffmpeg/CLAUDE.md +68 -0
- package/package.json +43 -10
- package/pipeline/cookbooks/scripts/animate-frames-parallel.ts +84 -0
- package/pipeline/cookbooks/scripts/combine-scenes.sh +53 -0
- package/pipeline/cookbooks/scripts/generate-frames-parallel.ts +99 -0
- package/pipeline/cookbooks/scripts/still-to-video.sh +37 -0
- package/pipeline/cookbooks/text-to-tiktok.md +669 -0
- package/pipeline/cookbooks/trendwatching.md +156 -0
- package/plan.md +281 -0
- package/scripts/.gitkeep +0 -0
- package/src/ai-sdk/cache.ts +142 -0
- package/src/ai-sdk/examples/cached-generation.ts +53 -0
- package/src/ai-sdk/examples/duet-scene-4.ts +53 -0
- package/src/ai-sdk/examples/duet-scene-5-audio.ts +32 -0
- package/src/ai-sdk/examples/duet-video.ts +56 -0
- package/src/ai-sdk/examples/editly-composition.ts +63 -0
- package/src/ai-sdk/examples/editly-test.ts +57 -0
- package/src/ai-sdk/examples/editly-video-test.ts +52 -0
- package/src/ai-sdk/examples/fal-lipsync.ts +43 -0
- package/src/ai-sdk/examples/higgsfield-image.ts +61 -0
- package/src/ai-sdk/examples/music-generation.ts +19 -0
- package/src/ai-sdk/examples/openai-sora.ts +34 -0
- package/src/ai-sdk/examples/replicate-bg-removal.ts +52 -0
- package/src/ai-sdk/examples/simpsons-scene.ts +61 -0
- package/src/ai-sdk/examples/talking-lion.ts +55 -0
- package/src/ai-sdk/examples/video-generation.ts +39 -0
- package/src/ai-sdk/examples/workflow-animated-girl.ts +104 -0
- package/src/ai-sdk/examples/workflow-before-after.ts +114 -0
- package/src/ai-sdk/examples/workflow-character-grid.ts +112 -0
- package/src/ai-sdk/examples/workflow-slideshow.ts +161 -0
- package/src/ai-sdk/file-cache.ts +112 -0
- package/src/ai-sdk/file.ts +238 -0
- package/src/ai-sdk/generate-element.ts +92 -0
- package/src/ai-sdk/generate-music.ts +46 -0
- package/src/ai-sdk/generate-video.ts +165 -0
- package/src/ai-sdk/index.ts +72 -0
- package/src/ai-sdk/music-model.ts +110 -0
- package/src/ai-sdk/providers/editly/editly.test.ts +1108 -0
- package/src/ai-sdk/providers/editly/ffmpeg.ts +60 -0
- package/src/ai-sdk/providers/editly/index.ts +817 -0
- package/src/ai-sdk/providers/editly/layers.ts +776 -0
- package/src/ai-sdk/providers/editly/plan.md +144 -0
- package/src/ai-sdk/providers/editly/types.ts +328 -0
- package/src/ai-sdk/providers/elevenlabs-provider.ts +255 -0
- package/src/ai-sdk/providers/fal-provider.ts +512 -0
- package/src/ai-sdk/providers/higgsfield.ts +379 -0
- package/src/ai-sdk/providers/openai.ts +251 -0
- package/src/ai-sdk/providers/replicate.ts +16 -0
- package/src/ai-sdk/video-model.ts +185 -0
- package/src/cli/commands/find.tsx +137 -0
- package/src/cli/commands/help.tsx +85 -0
- package/src/cli/commands/index.ts +6 -0
- package/src/cli/commands/list.tsx +238 -0
- package/src/cli/commands/render.tsx +71 -0
- package/src/cli/commands/run.tsx +511 -0
- package/src/cli/commands/which.tsx +253 -0
- package/src/cli/index.ts +114 -0
- package/src/cli/quiet.ts +44 -0
- package/src/cli/types.ts +32 -0
- package/src/cli/ui/components/Badge.tsx +29 -0
- package/src/cli/ui/components/DataTable.tsx +51 -0
- package/src/cli/ui/components/Header.tsx +23 -0
- package/src/cli/ui/components/HelpBlock.tsx +44 -0
- package/src/cli/ui/components/KeyValue.tsx +33 -0
- package/src/cli/ui/components/OptionRow.tsx +81 -0
- package/src/cli/ui/components/Separator.tsx +23 -0
- package/src/cli/ui/components/StatusBox.tsx +108 -0
- package/src/cli/ui/components/VargBox.tsx +51 -0
- package/src/cli/ui/components/VargProgress.tsx +36 -0
- package/src/cli/ui/components/VargSpinner.tsx +34 -0
- package/src/cli/ui/components/VargText.tsx +56 -0
- package/src/cli/ui/components/index.ts +19 -0
- package/src/cli/ui/index.ts +12 -0
- package/src/cli/ui/render.ts +35 -0
- package/src/cli/ui/theme.ts +63 -0
- package/src/cli/utils.ts +78 -0
- package/src/core/executor/executor.ts +201 -0
- package/src/core/executor/index.ts +13 -0
- package/src/core/executor/job.ts +214 -0
- package/src/core/executor/pipeline.ts +222 -0
- package/src/core/index.ts +11 -0
- package/src/core/registry/index.ts +9 -0
- package/src/core/registry/loader.ts +149 -0
- package/src/core/registry/registry.ts +221 -0
- package/src/core/registry/resolver.ts +206 -0
- package/src/core/schema/helpers.ts +134 -0
- package/src/core/schema/index.ts +8 -0
- package/src/core/schema/shared.ts +102 -0
- package/src/core/schema/types.ts +279 -0
- package/src/core/schema/validator.ts +92 -0
- package/src/definitions/actions/captions.ts +261 -0
- package/src/definitions/actions/edit.ts +298 -0
- package/src/definitions/actions/image.ts +125 -0
- package/src/definitions/actions/index.ts +114 -0
- package/src/definitions/actions/music.ts +205 -0
- package/src/definitions/actions/sync.ts +128 -0
- package/{action/transcribe/index.ts → src/definitions/actions/transcribe.ts} +58 -68
- package/src/definitions/actions/upload.ts +111 -0
- package/src/definitions/actions/video.ts +163 -0
- package/src/definitions/actions/voice.ts +119 -0
- package/src/definitions/index.ts +23 -0
- package/src/definitions/models/elevenlabs.ts +50 -0
- package/src/definitions/models/flux.ts +56 -0
- package/src/definitions/models/index.ts +36 -0
- package/src/definitions/models/kling.ts +56 -0
- package/src/definitions/models/llama.ts +54 -0
- package/src/definitions/models/nano-banana-pro.ts +102 -0
- package/src/definitions/models/sonauto.ts +68 -0
- package/src/definitions/models/soul.ts +65 -0
- package/src/definitions/models/wan.ts +54 -0
- package/src/definitions/models/whisper.ts +44 -0
- package/src/definitions/skills/index.ts +12 -0
- package/src/definitions/skills/talking-character.ts +87 -0
- package/src/definitions/skills/text-to-tiktok.ts +97 -0
- package/src/index.ts +118 -0
- package/src/providers/apify.ts +269 -0
- package/src/providers/base.ts +264 -0
- package/src/providers/elevenlabs.ts +217 -0
- package/src/providers/fal.ts +392 -0
- package/src/providers/ffmpeg.ts +544 -0
- package/src/providers/fireworks.ts +193 -0
- package/src/providers/groq.ts +149 -0
- package/src/providers/higgsfield.ts +145 -0
- package/src/providers/index.ts +143 -0
- package/src/providers/replicate.ts +147 -0
- package/src/providers/storage.ts +206 -0
- package/src/react/cli.ts +52 -0
- package/src/react/elements.ts +146 -0
- package/src/react/examples/branching.tsx +66 -0
- package/src/react/examples/captions-demo.tsx +37 -0
- package/src/react/examples/character-video.tsx +84 -0
- package/src/react/examples/grid.tsx +53 -0
- package/src/react/examples/layouts-demo.tsx +57 -0
- package/src/react/examples/madi.tsx +60 -0
- package/src/react/examples/music-test.tsx +35 -0
- package/src/react/examples/onlyfans-1m/workflow.tsx +88 -0
- package/src/react/examples/orange-portrait.tsx +41 -0
- package/src/react/examples/split-element-demo.tsx +60 -0
- package/src/react/examples/split-layout-demo.tsx +60 -0
- package/src/react/examples/split.tsx +41 -0
- package/src/react/examples/video-grid.tsx +46 -0
- package/src/react/index.ts +43 -0
- package/src/react/layouts/grid.tsx +28 -0
- package/src/react/layouts/index.ts +2 -0
- package/src/react/layouts/split.tsx +20 -0
- package/src/react/react.test.ts +309 -0
- package/src/react/render.ts +21 -0
- package/src/react/renderers/animate.ts +59 -0
- package/src/react/renderers/captions.ts +297 -0
- package/src/react/renderers/clip.ts +248 -0
- package/src/react/renderers/context.ts +17 -0
- package/src/react/renderers/image.ts +109 -0
- package/src/react/renderers/index.ts +22 -0
- package/src/react/renderers/music.ts +60 -0
- package/src/react/renderers/packshot.ts +84 -0
- package/src/react/renderers/progress.ts +173 -0
- package/src/react/renderers/render.ts +243 -0
- package/src/react/renderers/slider.ts +69 -0
- package/src/react/renderers/speech.ts +53 -0
- package/src/react/renderers/split.ts +91 -0
- package/src/react/renderers/subtitle.ts +16 -0
- package/src/react/renderers/swipe.ts +75 -0
- package/src/react/renderers/title.ts +17 -0
- package/src/react/renderers/utils.ts +124 -0
- package/src/react/renderers/video.ts +127 -0
- package/src/react/runtime/jsx-dev-runtime.ts +43 -0
- package/src/react/runtime/jsx-runtime.ts +35 -0
- package/src/react/types.ts +232 -0
- package/src/studio/index.ts +26 -0
- package/src/studio/scanner.ts +102 -0
- package/src/studio/server.ts +554 -0
- package/src/studio/stages.ts +251 -0
- package/src/studio/step-renderer.ts +279 -0
- package/src/studio/types.ts +60 -0
- package/src/studio/ui/cache.html +303 -0
- package/src/studio/ui/index.html +1820 -0
- package/src/tests/all.test.ts +509 -0
- package/src/tests/index.ts +33 -0
- package/src/tests/unit.test.ts +403 -0
- package/tsconfig.cli.json +8 -0
- package/tsconfig.json +21 -3
- package/TEST_RESULTS.md +0 -122
- package/action/captions/SKILL.md +0 -170
- package/action/captions/index.ts +0 -169
- package/action/edit/SKILL.md +0 -235
- package/action/edit/index.ts +0 -437
- package/action/image/SKILL.md +0 -140
- package/action/image/index.ts +0 -105
- package/action/sync/SKILL.md +0 -136
- package/action/sync/index.ts +0 -145
- package/action/transcribe/SKILL.md +0 -179
- package/action/video/SKILL.md +0 -116
- package/action/video/index.ts +0 -125
- package/action/voice/SKILL.md +0 -125
- package/action/voice/index.ts +0 -136
- package/cli/commands/find.ts +0 -58
- package/cli/commands/help.ts +0 -70
- package/cli/commands/list.ts +0 -49
- package/cli/commands/run.ts +0 -237
- package/cli/commands/which.ts +0 -66
- package/cli/discover.ts +0 -66
- package/cli/index.ts +0 -33
- package/cli/runner.ts +0 -65
- package/cli/types.ts +0 -49
- package/cli/ui.ts +0 -185
- package/index.ts +0 -75
- package/lib/README.md +0 -144
- package/lib/ai-sdk/fal.ts +0 -106
- package/lib/ai-sdk/replicate.ts +0 -107
- package/lib/elevenlabs.ts +0 -382
- package/lib/fal.ts +0 -467
- package/lib/ffmpeg.ts +0 -467
- package/lib/fireworks.ts +0 -235
- package/lib/groq.ts +0 -246
- package/lib/higgsfield.ts +0 -176
- package/lib/remotion/SKILL.md +0 -823
- package/lib/remotion/cli.ts +0 -115
- package/lib/remotion/functions.ts +0 -283
- package/lib/remotion/index.ts +0 -19
- package/lib/remotion/templates.ts +0 -73
- package/lib/replicate.ts +0 -304
- package/output.txt +0 -1
- package/test-import.ts +0 -7
- package/test-services.ts +0 -97
- package/utilities/s3.ts +0 -147
package/lib/ai-sdk/fal.ts
DELETED
|
@@ -1,106 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env bun
|
|
2
|
-
/**
|
|
3
|
-
* fal.ai wrapper using @ai-sdk/fal provider
|
|
4
|
-
* recommended for standard image generation with vercel ai sdk
|
|
5
|
-
*
|
|
6
|
-
* usage: bun run lib/ai-sdk/fal.ts <command> <args>
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
import { fal } from "@ai-sdk/fal";
|
|
10
|
-
import { experimental_generateImage as generateImageAI } from "ai";
|
|
11
|
-
|
|
12
|
-
export async function generateImage(args: {
|
|
13
|
-
prompt: string;
|
|
14
|
-
model?: string;
|
|
15
|
-
aspectRatio?: "1:1" | "16:9" | "9:16" | "4:3" | "3:4";
|
|
16
|
-
}) {
|
|
17
|
-
const modelId = args.model || "fal-ai/flux/dev";
|
|
18
|
-
|
|
19
|
-
console.log(`[ai-sdk/fal] generating image with ${modelId}`);
|
|
20
|
-
console.log(`[ai-sdk/fal] prompt: ${args.prompt}`);
|
|
21
|
-
if (args.aspectRatio) {
|
|
22
|
-
console.log(`[ai-sdk/fal] aspect ratio: ${args.aspectRatio}`);
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
try {
|
|
26
|
-
const { image, providerMetadata } = await generateImageAI({
|
|
27
|
-
model: fal.image(modelId),
|
|
28
|
-
prompt: args.prompt,
|
|
29
|
-
aspectRatio: args.aspectRatio,
|
|
30
|
-
});
|
|
31
|
-
|
|
32
|
-
console.log("[ai-sdk/fal] completed!");
|
|
33
|
-
|
|
34
|
-
// return in consistent format
|
|
35
|
-
return {
|
|
36
|
-
image: {
|
|
37
|
-
url: image.base64 ? `data:image/png;base64,${image.base64}` : undefined,
|
|
38
|
-
uint8Array: image.uint8Array,
|
|
39
|
-
},
|
|
40
|
-
metadata: providerMetadata?.fal,
|
|
41
|
-
};
|
|
42
|
-
} catch (error) {
|
|
43
|
-
console.error("[ai-sdk/fal] error:", error);
|
|
44
|
-
throw error;
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
// cli runner
|
|
49
|
-
if (import.meta.main) {
|
|
50
|
-
const [command, ...args] = process.argv.slice(2);
|
|
51
|
-
|
|
52
|
-
switch (command) {
|
|
53
|
-
case "generate_image": {
|
|
54
|
-
if (!args[0]) {
|
|
55
|
-
console.log(`
|
|
56
|
-
usage:
|
|
57
|
-
bun run lib/ai-sdk/fal.ts generate_image <prompt> [model] [aspectRatio]
|
|
58
|
-
|
|
59
|
-
examples:
|
|
60
|
-
bun run lib/ai-sdk/fal.ts generate_image "sunset over ocean" "fal-ai/flux/dev" "16:9"
|
|
61
|
-
bun run lib/ai-sdk/fal.ts generate_image "portrait photo" "fal-ai/flux-pro/v1.1" "9:16"
|
|
62
|
-
|
|
63
|
-
available models:
|
|
64
|
-
- fal-ai/flux/dev (default, fast)
|
|
65
|
-
- fal-ai/flux-pro/v1.1 (high quality)
|
|
66
|
-
- fal-ai/flux/schnell (very fast)
|
|
67
|
-
- fal-ai/ideogram/character (character consistency)
|
|
68
|
-
`);
|
|
69
|
-
process.exit(1);
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
const result = await generateImage({
|
|
73
|
-
prompt: args[0],
|
|
74
|
-
model: args[1],
|
|
75
|
-
aspectRatio: args[2] as
|
|
76
|
-
| "1:1"
|
|
77
|
-
| "16:9"
|
|
78
|
-
| "9:16"
|
|
79
|
-
| "4:3"
|
|
80
|
-
| "3:4"
|
|
81
|
-
| undefined,
|
|
82
|
-
});
|
|
83
|
-
|
|
84
|
-
// save image to file
|
|
85
|
-
if (result.image.uint8Array) {
|
|
86
|
-
const filename = `/tmp/fal-ai-sdk-${Date.now()}.png`;
|
|
87
|
-
await Bun.write(filename, result.image.uint8Array);
|
|
88
|
-
console.log(`\nimage saved to: ${filename}`);
|
|
89
|
-
|
|
90
|
-
// open image
|
|
91
|
-
await Bun.spawn(["open", filename]);
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
console.log("\nmetadata:");
|
|
95
|
-
console.log(JSON.stringify(result.metadata, null, 2));
|
|
96
|
-
break;
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
default:
|
|
100
|
-
console.log(`
|
|
101
|
-
usage:
|
|
102
|
-
bun run lib/ai-sdk/fal.ts generate_image <prompt> [model] [aspectRatio]
|
|
103
|
-
`);
|
|
104
|
-
process.exit(1);
|
|
105
|
-
}
|
|
106
|
-
}
|
package/lib/ai-sdk/replicate.ts
DELETED
|
@@ -1,107 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env bun
|
|
2
|
-
/**
|
|
3
|
-
* replicate wrapper using @ai-sdk/replicate provider
|
|
4
|
-
* recommended for standard image generation with vercel ai sdk
|
|
5
|
-
*
|
|
6
|
-
* usage: bun run lib/ai-sdk/replicate.ts <command> <args>
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
import { replicate } from "@ai-sdk/replicate";
|
|
10
|
-
import { experimental_generateImage as generateImageAI } from "ai";
|
|
11
|
-
|
|
12
|
-
export async function generateImage(args: {
|
|
13
|
-
prompt: string;
|
|
14
|
-
model?: string;
|
|
15
|
-
aspectRatio?: "1:1" | "16:9" | "9:16" | "4:3" | "3:4";
|
|
16
|
-
}) {
|
|
17
|
-
const modelId = args.model || "black-forest-labs/flux-dev";
|
|
18
|
-
|
|
19
|
-
console.log(`[ai-sdk/replicate] generating image with ${modelId}`);
|
|
20
|
-
console.log(`[ai-sdk/replicate] prompt: ${args.prompt}`);
|
|
21
|
-
if (args.aspectRatio) {
|
|
22
|
-
console.log(`[ai-sdk/replicate] aspect ratio: ${args.aspectRatio}`);
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
try {
|
|
26
|
-
const { image, providerMetadata } = await generateImageAI({
|
|
27
|
-
model: replicate.image(modelId),
|
|
28
|
-
prompt: args.prompt,
|
|
29
|
-
aspectRatio: args.aspectRatio,
|
|
30
|
-
});
|
|
31
|
-
|
|
32
|
-
console.log("[ai-sdk/replicate] completed!");
|
|
33
|
-
|
|
34
|
-
// return in consistent format
|
|
35
|
-
return {
|
|
36
|
-
image: {
|
|
37
|
-
url: image.base64 ? `data:image/png;base64,${image.base64}` : undefined,
|
|
38
|
-
uint8Array: image.uint8Array,
|
|
39
|
-
},
|
|
40
|
-
metadata: providerMetadata?.replicate,
|
|
41
|
-
};
|
|
42
|
-
} catch (error) {
|
|
43
|
-
console.error("[ai-sdk/replicate] error:", error);
|
|
44
|
-
throw error;
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
// cli runner
|
|
49
|
-
if (import.meta.main) {
|
|
50
|
-
const [command, ...args] = process.argv.slice(2);
|
|
51
|
-
|
|
52
|
-
switch (command) {
|
|
53
|
-
case "generate_image": {
|
|
54
|
-
if (!args[0]) {
|
|
55
|
-
console.log(`
|
|
56
|
-
usage:
|
|
57
|
-
bun run lib/ai-sdk/replicate.ts generate_image <prompt> [model] [aspectRatio]
|
|
58
|
-
|
|
59
|
-
examples:
|
|
60
|
-
bun run lib/ai-sdk/replicate.ts generate_image "sunset over ocean" "black-forest-labs/flux-dev" "16:9"
|
|
61
|
-
bun run lib/ai-sdk/replicate.ts generate_image "portrait photo" "black-forest-labs/flux-1.1-pro" "9:16"
|
|
62
|
-
bun run lib/ai-sdk/replicate.ts generate_image "cyberpunk city" "black-forest-labs/flux-schnell" "1:1"
|
|
63
|
-
|
|
64
|
-
available models:
|
|
65
|
-
- black-forest-labs/flux-dev (default, fast)
|
|
66
|
-
- black-forest-labs/flux-1.1-pro (high quality)
|
|
67
|
-
- black-forest-labs/flux-schnell (very fast)
|
|
68
|
-
- stability-ai/sdxl (stable diffusion xl)
|
|
69
|
-
`);
|
|
70
|
-
process.exit(1);
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
const result = await generateImage({
|
|
74
|
-
prompt: args[0],
|
|
75
|
-
model: args[1],
|
|
76
|
-
aspectRatio: args[2] as
|
|
77
|
-
| "1:1"
|
|
78
|
-
| "16:9"
|
|
79
|
-
| "9:16"
|
|
80
|
-
| "4:3"
|
|
81
|
-
| "3:4"
|
|
82
|
-
| undefined,
|
|
83
|
-
});
|
|
84
|
-
|
|
85
|
-
// save image to file
|
|
86
|
-
if (result.image.uint8Array) {
|
|
87
|
-
const filename = `/tmp/replicate-ai-sdk-${Date.now()}.png`;
|
|
88
|
-
await Bun.write(filename, result.image.uint8Array);
|
|
89
|
-
console.log(`\nimage saved to: ${filename}`);
|
|
90
|
-
|
|
91
|
-
// open image
|
|
92
|
-
await Bun.spawn(["open", filename]);
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
console.log("\nmetadata:");
|
|
96
|
-
console.log(JSON.stringify(result.metadata, null, 2));
|
|
97
|
-
break;
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
default:
|
|
101
|
-
console.log(`
|
|
102
|
-
usage:
|
|
103
|
-
bun run lib/ai-sdk/replicate.ts generate_image <prompt> [model] [aspectRatio]
|
|
104
|
-
`);
|
|
105
|
-
process.exit(1);
|
|
106
|
-
}
|
|
107
|
-
}
|
package/lib/elevenlabs.ts
DELETED
|
@@ -1,382 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env bun
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* elevenlabs api wrapper for voice generation and text-to-speech
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
import { writeFileSync } from "node:fs";
|
|
8
|
-
import { join } from "node:path";
|
|
9
|
-
import { ElevenLabsClient } from "@elevenlabs/elevenlabs-js";
|
|
10
|
-
|
|
11
|
-
const elevenlabs = new ElevenLabsClient({
|
|
12
|
-
apiKey: process.env.ELEVENLABS_API_KEY,
|
|
13
|
-
});
|
|
14
|
-
|
|
15
|
-
// types
|
|
16
|
-
export interface TextToSpeechOptions {
|
|
17
|
-
text: string;
|
|
18
|
-
voiceId?: string;
|
|
19
|
-
modelId?: string;
|
|
20
|
-
outputPath?: string;
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
export interface VoiceSettings {
|
|
24
|
-
stability?: number;
|
|
25
|
-
similarityBoost?: number;
|
|
26
|
-
style?: number;
|
|
27
|
-
useSpeakerBoost?: boolean;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
export interface MusicGenerationOptions {
|
|
31
|
-
prompt?: string;
|
|
32
|
-
musicLengthMs?: number;
|
|
33
|
-
outputPath?: string;
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
export interface SoundEffectOptions {
|
|
37
|
-
text: string;
|
|
38
|
-
durationSeconds?: number;
|
|
39
|
-
promptInfluence?: number;
|
|
40
|
-
loop?: boolean;
|
|
41
|
-
outputPath?: string;
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
// popular voices
|
|
45
|
-
export const VOICES = {
|
|
46
|
-
RACHEL: "21m00Tcm4TlvDq8ikWAM",
|
|
47
|
-
DOMI: "AZnzlk1XvdvUeBnXmlld",
|
|
48
|
-
BELLA: "EXAVITQu4vr4xnSDxMaL",
|
|
49
|
-
ANTONI: "ErXwobaYiN019PkySvjV",
|
|
50
|
-
ELLI: "MF3mGyEYCl7XYWbV9V6O",
|
|
51
|
-
JOSH: "TxGEqnHWrfWFTfGW9XjX",
|
|
52
|
-
ARNOLD: "VR6AewLTigWG4xSOukaG",
|
|
53
|
-
ADAM: "pNInz6obpgDQGcFmaJgB",
|
|
54
|
-
SAM: "yoZ06aMxZJJ28mfd3POQ",
|
|
55
|
-
};
|
|
56
|
-
|
|
57
|
-
// core functions
|
|
58
|
-
export async function textToSpeech(options: TextToSpeechOptions) {
|
|
59
|
-
const {
|
|
60
|
-
text,
|
|
61
|
-
voiceId = VOICES.RACHEL,
|
|
62
|
-
modelId = "eleven_multilingual_v2",
|
|
63
|
-
outputPath,
|
|
64
|
-
} = options;
|
|
65
|
-
|
|
66
|
-
if (!text) {
|
|
67
|
-
throw new Error("text is required");
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
console.log(`[elevenlabs] generating speech with voice ${voiceId}...`);
|
|
71
|
-
|
|
72
|
-
try {
|
|
73
|
-
const audio = await elevenlabs.textToSpeech.convert(voiceId, {
|
|
74
|
-
text,
|
|
75
|
-
modelId,
|
|
76
|
-
outputFormat: "mp3_44100_128",
|
|
77
|
-
});
|
|
78
|
-
|
|
79
|
-
// convert readablestream to buffer
|
|
80
|
-
const reader = audio.getReader();
|
|
81
|
-
const chunks: Uint8Array[] = [];
|
|
82
|
-
|
|
83
|
-
while (true) {
|
|
84
|
-
const { done, value } = await reader.read();
|
|
85
|
-
if (done) break;
|
|
86
|
-
chunks.push(value);
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
const buffer = Buffer.concat(chunks);
|
|
90
|
-
|
|
91
|
-
// save to file if path provided
|
|
92
|
-
if (outputPath) {
|
|
93
|
-
writeFileSync(outputPath, buffer);
|
|
94
|
-
console.log(`[elevenlabs] saved to ${outputPath}`);
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
console.log(`[elevenlabs] generated ${buffer.length} bytes`);
|
|
98
|
-
return buffer;
|
|
99
|
-
} catch (error) {
|
|
100
|
-
console.error(`[elevenlabs] error:`, error);
|
|
101
|
-
throw error;
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
export async function listVoices() {
|
|
106
|
-
console.log(`[elevenlabs] fetching voices...`);
|
|
107
|
-
|
|
108
|
-
try {
|
|
109
|
-
const response = await elevenlabs.voices.getAll();
|
|
110
|
-
console.log(`[elevenlabs] found ${response.voices.length} voices`);
|
|
111
|
-
return response.voices;
|
|
112
|
-
} catch (error) {
|
|
113
|
-
console.error(`[elevenlabs] error:`, error);
|
|
114
|
-
throw error;
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
export async function getVoice(voiceId: string) {
|
|
119
|
-
console.log(`[elevenlabs] fetching voice ${voiceId}...`);
|
|
120
|
-
|
|
121
|
-
try {
|
|
122
|
-
const voice = await elevenlabs.voices.get(voiceId);
|
|
123
|
-
console.log(`[elevenlabs] found voice: ${voice.name}`);
|
|
124
|
-
return voice;
|
|
125
|
-
} catch (error) {
|
|
126
|
-
console.error(`[elevenlabs] error:`, error);
|
|
127
|
-
throw error;
|
|
128
|
-
}
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
export async function generateMusic(options: MusicGenerationOptions) {
|
|
132
|
-
const { prompt, musicLengthMs, outputPath } = options;
|
|
133
|
-
|
|
134
|
-
if (!prompt) {
|
|
135
|
-
throw new Error("prompt is required");
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
console.log(`[elevenlabs] generating music from prompt: "${prompt}"...`);
|
|
139
|
-
|
|
140
|
-
try {
|
|
141
|
-
const audio = await elevenlabs.music.compose({
|
|
142
|
-
prompt,
|
|
143
|
-
musicLengthMs,
|
|
144
|
-
modelId: "music_v1",
|
|
145
|
-
});
|
|
146
|
-
|
|
147
|
-
// convert readablestream to buffer
|
|
148
|
-
const reader = audio.getReader();
|
|
149
|
-
const chunks: Uint8Array[] = [];
|
|
150
|
-
|
|
151
|
-
while (true) {
|
|
152
|
-
const { done, value } = await reader.read();
|
|
153
|
-
if (done) break;
|
|
154
|
-
chunks.push(value);
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
const buffer = Buffer.concat(chunks);
|
|
158
|
-
|
|
159
|
-
// save to file if path provided
|
|
160
|
-
if (outputPath) {
|
|
161
|
-
writeFileSync(outputPath, buffer);
|
|
162
|
-
console.log(`[elevenlabs] saved to ${outputPath}`);
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
console.log(`[elevenlabs] generated ${buffer.length} bytes`);
|
|
166
|
-
return buffer;
|
|
167
|
-
} catch (error) {
|
|
168
|
-
console.error(`[elevenlabs] error:`, error);
|
|
169
|
-
throw error;
|
|
170
|
-
}
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
export async function generateSoundEffect(options: SoundEffectOptions) {
|
|
174
|
-
const {
|
|
175
|
-
text,
|
|
176
|
-
durationSeconds,
|
|
177
|
-
promptInfluence = 0.3,
|
|
178
|
-
loop = false,
|
|
179
|
-
outputPath,
|
|
180
|
-
} = options;
|
|
181
|
-
|
|
182
|
-
if (!text) {
|
|
183
|
-
throw new Error("text is required");
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
console.log(`[elevenlabs] generating sound effect: "${text}"...`);
|
|
187
|
-
|
|
188
|
-
try {
|
|
189
|
-
const audio = await elevenlabs.textToSoundEffects.convert({
|
|
190
|
-
text,
|
|
191
|
-
durationSeconds,
|
|
192
|
-
promptInfluence,
|
|
193
|
-
loop,
|
|
194
|
-
});
|
|
195
|
-
|
|
196
|
-
// convert readablestream to buffer
|
|
197
|
-
const reader = audio.getReader();
|
|
198
|
-
const chunks: Uint8Array[] = [];
|
|
199
|
-
|
|
200
|
-
while (true) {
|
|
201
|
-
const { done, value } = await reader.read();
|
|
202
|
-
if (done) break;
|
|
203
|
-
chunks.push(value);
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
const buffer = Buffer.concat(chunks);
|
|
207
|
-
|
|
208
|
-
// save to file if path provided
|
|
209
|
-
if (outputPath) {
|
|
210
|
-
writeFileSync(outputPath, buffer);
|
|
211
|
-
console.log(`[elevenlabs] saved to ${outputPath}`);
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
console.log(`[elevenlabs] generated ${buffer.length} bytes`);
|
|
215
|
-
return buffer;
|
|
216
|
-
} catch (error) {
|
|
217
|
-
console.error(`[elevenlabs] error:`, error);
|
|
218
|
-
throw error;
|
|
219
|
-
}
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
// cli
|
|
223
|
-
async function cli() {
|
|
224
|
-
const args = process.argv.slice(2);
|
|
225
|
-
const command = args[0];
|
|
226
|
-
|
|
227
|
-
if (!command || command === "help") {
|
|
228
|
-
console.log(`
|
|
229
|
-
usage:
|
|
230
|
-
bun run lib/elevenlabs.ts <command> [args]
|
|
231
|
-
|
|
232
|
-
commands:
|
|
233
|
-
tts <text> [voiceId] [outputPath] generate speech from text
|
|
234
|
-
music <prompt> [lengthMs] [outputPath] generate music from prompt
|
|
235
|
-
sfx <text> [durationSec] [outputPath] generate sound effect
|
|
236
|
-
voices list available voices
|
|
237
|
-
voice <voiceId> get voice details
|
|
238
|
-
help show this help
|
|
239
|
-
|
|
240
|
-
examples:
|
|
241
|
-
bun run lib/elevenlabs.ts tts "hello world" rachel output.mp3
|
|
242
|
-
bun run lib/elevenlabs.ts music "upbeat electronic dance music" 30000 music.mp3
|
|
243
|
-
bun run lib/elevenlabs.ts sfx "ocean waves crashing" 5 waves.mp3
|
|
244
|
-
bun run lib/elevenlabs.ts voices
|
|
245
|
-
bun run lib/elevenlabs.ts voice 21m00Tcm4TlvDq8ikWAM
|
|
246
|
-
|
|
247
|
-
popular voices:
|
|
248
|
-
rachel - 21m00Tcm4TlvDq8ikWAM (american female)
|
|
249
|
-
domi - AZnzlk1XvdvUeBnXmlld (american female)
|
|
250
|
-
bella - EXAVITQu4vr4xnSDxMaL (american female)
|
|
251
|
-
antoni - ErXwobaYiN019PkySvjV (american male)
|
|
252
|
-
elli - MF3mGyEYCl7XYWbV9V6O (american female)
|
|
253
|
-
josh - TxGEqnHWrfWFTfGW9XjX (american male)
|
|
254
|
-
arnold - VR6AewLTigWG4xSOukaG (american male)
|
|
255
|
-
adam - pNInz6obpgDQGcFmaJgB (american male)
|
|
256
|
-
sam - yoZ06aMxZJJ28mfd3POQ (american male)
|
|
257
|
-
|
|
258
|
-
environment:
|
|
259
|
-
ELEVENLABS_API_KEY - your elevenlabs api key
|
|
260
|
-
`);
|
|
261
|
-
process.exit(0);
|
|
262
|
-
}
|
|
263
|
-
|
|
264
|
-
try {
|
|
265
|
-
switch (command) {
|
|
266
|
-
case "tts": {
|
|
267
|
-
const text = args[1];
|
|
268
|
-
let voiceId = args[2];
|
|
269
|
-
const outputPath = args[3];
|
|
270
|
-
|
|
271
|
-
if (!text) {
|
|
272
|
-
throw new Error("text is required");
|
|
273
|
-
}
|
|
274
|
-
|
|
275
|
-
// map voice names to ids
|
|
276
|
-
const voiceNameMap: Record<string, string> = {
|
|
277
|
-
rachel: VOICES.RACHEL,
|
|
278
|
-
domi: VOICES.DOMI,
|
|
279
|
-
bella: VOICES.BELLA,
|
|
280
|
-
antoni: VOICES.ANTONI,
|
|
281
|
-
elli: VOICES.ELLI,
|
|
282
|
-
josh: VOICES.JOSH,
|
|
283
|
-
arnold: VOICES.ARNOLD,
|
|
284
|
-
adam: VOICES.ADAM,
|
|
285
|
-
sam: VOICES.SAM,
|
|
286
|
-
};
|
|
287
|
-
|
|
288
|
-
if (voiceId && voiceNameMap[voiceId.toLowerCase()]) {
|
|
289
|
-
voiceId = voiceNameMap[voiceId.toLowerCase()];
|
|
290
|
-
}
|
|
291
|
-
|
|
292
|
-
const buffer = await textToSpeech({
|
|
293
|
-
text,
|
|
294
|
-
voiceId,
|
|
295
|
-
outputPath: outputPath || join(process.cwd(), "output.mp3"),
|
|
296
|
-
});
|
|
297
|
-
|
|
298
|
-
console.log(`[elevenlabs] generated ${buffer.length} bytes`);
|
|
299
|
-
break;
|
|
300
|
-
}
|
|
301
|
-
|
|
302
|
-
case "voices": {
|
|
303
|
-
const voices = await listVoices();
|
|
304
|
-
console.log(
|
|
305
|
-
`\navailable voices:\n${voices.map((v) => ` ${v.voiceId} - ${v.name}`).join("\n")}`,
|
|
306
|
-
);
|
|
307
|
-
break;
|
|
308
|
-
}
|
|
309
|
-
|
|
310
|
-
case "voice": {
|
|
311
|
-
const voiceId = args[1];
|
|
312
|
-
|
|
313
|
-
if (!voiceId) {
|
|
314
|
-
throw new Error("voiceId is required");
|
|
315
|
-
}
|
|
316
|
-
|
|
317
|
-
const voice = await getVoice(voiceId);
|
|
318
|
-
console.log(`\nvoice details:`, {
|
|
319
|
-
id: voice.voiceId,
|
|
320
|
-
name: voice.name,
|
|
321
|
-
category: voice.category,
|
|
322
|
-
labels: voice.labels,
|
|
323
|
-
});
|
|
324
|
-
break;
|
|
325
|
-
}
|
|
326
|
-
|
|
327
|
-
case "music": {
|
|
328
|
-
const prompt = args[1];
|
|
329
|
-
const musicLengthMs = args[2]
|
|
330
|
-
? Number.parseInt(args[2], 10)
|
|
331
|
-
: undefined;
|
|
332
|
-
const outputPath = args[3];
|
|
333
|
-
|
|
334
|
-
if (!prompt) {
|
|
335
|
-
throw new Error("prompt is required");
|
|
336
|
-
}
|
|
337
|
-
|
|
338
|
-
const buffer = await generateMusic({
|
|
339
|
-
prompt,
|
|
340
|
-
musicLengthMs,
|
|
341
|
-
outputPath: outputPath || join(process.cwd(), "music.mp3"),
|
|
342
|
-
});
|
|
343
|
-
|
|
344
|
-
console.log(`[elevenlabs] generated ${buffer.length} bytes`);
|
|
345
|
-
break;
|
|
346
|
-
}
|
|
347
|
-
|
|
348
|
-
case "sfx": {
|
|
349
|
-
const text = args[1];
|
|
350
|
-
const durationSeconds = args[2]
|
|
351
|
-
? Number.parseFloat(args[2])
|
|
352
|
-
: undefined;
|
|
353
|
-
const outputPath = args[3];
|
|
354
|
-
|
|
355
|
-
if (!text) {
|
|
356
|
-
throw new Error("text is required");
|
|
357
|
-
}
|
|
358
|
-
|
|
359
|
-
const buffer = await generateSoundEffect({
|
|
360
|
-
text,
|
|
361
|
-
durationSeconds,
|
|
362
|
-
outputPath: outputPath || join(process.cwd(), "sfx.mp3"),
|
|
363
|
-
});
|
|
364
|
-
|
|
365
|
-
console.log(`[elevenlabs] generated ${buffer.length} bytes`);
|
|
366
|
-
break;
|
|
367
|
-
}
|
|
368
|
-
|
|
369
|
-
default:
|
|
370
|
-
console.error(`unknown command: ${command}`);
|
|
371
|
-
console.log(`run 'bun run lib/elevenlabs.ts help' for usage`);
|
|
372
|
-
process.exit(1);
|
|
373
|
-
}
|
|
374
|
-
} catch (error) {
|
|
375
|
-
console.error(`[elevenlabs] error:`, error);
|
|
376
|
-
process.exit(1);
|
|
377
|
-
}
|
|
378
|
-
}
|
|
379
|
-
|
|
380
|
-
if (import.meta.main) {
|
|
381
|
-
cli();
|
|
382
|
-
}
|