videodraft 0.1.2 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# videodraft
|
|
2
2
|
|
|
3
|
-
The official [VideoDraft](https://videodraft.ai) CLI — create AI videos, images
|
|
3
|
+
The official [VideoDraft](https://videodraft.ai) CLI — create AI videos, images and audio from your terminal. Built for humans **and** coding agents: every command supports `--json`, exit codes are stable, async jobs are first-class.
|
|
4
4
|
|
|
5
5
|
```bash
|
|
6
6
|
npx videodraft login
|
|
@@ -48,6 +48,10 @@ videodraft generate image "isometric workspace, warm light" --num 4 --download "
|
|
|
48
48
|
videodraft generate video "slow dolly over a misty lake" --model google-veo3.1 --duration 6 --estimate
|
|
49
49
|
videodraft generate voiceover "Welcome to VideoDraft" --download welcome.mp3
|
|
50
50
|
videodraft generate music "minimal ambient, 60 BPM" --download bgm.mp3
|
|
51
|
+
videodraft generate sound-effect "cinematic whoosh, sub hit" --duration 3 --download sfx.mp3
|
|
52
|
+
videodraft generate dialogue --line "elevenlabs-kPzsL2i3teMYv0FxEYQ6:Ready?" --line "elevenlabs-s3TPKV1kjDlVtZbl4Ksh:Let's go." --download dialogue.mp3
|
|
53
|
+
videodraft generate voice-changer ./speech.wav --voice elevenlabs-kPzsL2i3teMYv0FxEYQ6 --duration 12 --download changed.mp3
|
|
54
|
+
videodraft generate dub ./clip.mp4 --to es --duration 30 --download dubbed.mp4
|
|
51
55
|
videodraft upscale image ./photo.png --scale 4x --download ./photo-4x.png
|
|
52
56
|
videodraft avatar create ./founder.jpg --script "$(videodraft avatar script 'our launch' --json | jq -r .script)"
|
|
53
57
|
```
|
|
@@ -57,10 +61,10 @@ videodraft avatar create ./founder.jpg --script "$(videodraft avatar script 'our
|
|
|
57
61
|
| Group | Commands |
|
|
58
62
|
|---|---|
|
|
59
63
|
| Auth | `login` `logout` `whoami` |
|
|
60
|
-
| Account | `credits` `costs [model]` `models [image\|video\|voices\|styles]` `workspaces` `sessions list/create` |
|
|
64
|
+
| Account | `credits` `costs [model]` `models [image\|video\|audio\|voices\|styles]` `workspaces` `sessions list/create` |
|
|
61
65
|
| Projects | `projects list/get/delete/favorite/open` `checkpoint create/list/restore` |
|
|
62
66
|
| Pipeline | `create` `shots` `produce` (`--mode full_video`) `attach` `finalize` `export` `export-status` `video-prompts` |
|
|
63
|
-
| Generate | `generate image/video/voiceover/music` `upscale image/video` `avatar script/create/render/get/list` |
|
|
67
|
+
| Generate | `generate image/video/voiceover/music/sound-effect/dialogue/voice-changer/dub` `upscale image/video` `avatar script/create/render/get/list` |
|
|
64
68
|
| Jobs | `status <job>` `wait <job>` `generations` |
|
|
65
69
|
| Media | `upload <file>` `media list` `describe <url\|file>` `download <url>` |
|
|
66
70
|
| Everything else | `tools list` `tools schema <name>` `call <tool> --args '<json>'` |
|
package/dist/index.js
CHANGED
|
@@ -15,7 +15,7 @@ __export(version_exports, {
|
|
|
15
15
|
VERSION: () => VERSION
|
|
16
16
|
});
|
|
17
17
|
import fs from "fs";
|
|
18
|
-
function
|
|
18
|
+
function readVersionFromDisk() {
|
|
19
19
|
try {
|
|
20
20
|
const pkg = JSON.parse(fs.readFileSync(new URL("../package.json", import.meta.url), "utf8"));
|
|
21
21
|
return typeof pkg.version === "string" ? pkg.version : "0.0.0";
|
|
@@ -23,11 +23,17 @@ function readVersion() {
|
|
|
23
23
|
return "0.0.0";
|
|
24
24
|
}
|
|
25
25
|
}
|
|
26
|
+
function resolveVersion() {
|
|
27
|
+
if ("0.2.0") {
|
|
28
|
+
return "0.2.0";
|
|
29
|
+
}
|
|
30
|
+
return readVersionFromDisk();
|
|
31
|
+
}
|
|
26
32
|
var VERSION;
|
|
27
33
|
var init_version = __esm({
|
|
28
34
|
"src/version.ts"() {
|
|
29
35
|
"use strict";
|
|
30
|
-
VERSION =
|
|
36
|
+
VERSION = resolveVersion();
|
|
31
37
|
}
|
|
32
38
|
});
|
|
33
39
|
|
|
@@ -1068,7 +1074,12 @@ function registerAccountCommands(program) {
|
|
|
1068
1074
|
]);
|
|
1069
1075
|
});
|
|
1070
1076
|
});
|
|
1071
|
-
program.command("costs [model]").description(
|
|
1077
|
+
program.command("costs [model]").description(
|
|
1078
|
+
"Show credit costs \u2014 pass a model id plus settings for an exact estimate"
|
|
1079
|
+
).option("--type <type>", "image | video | audio").option("--duration <seconds>", "video/audio duration in seconds").option("--length <seconds>", "ElevenLabs Music output length in seconds").option("--chars <n>", "ElevenLabs Dialogue character count").option("--resolution <res>", 'e.g. "720p", "1080p", "1K", "2K"').option("--quality <tier>", 'e.g. "standard", "pro", "fast"').option(
|
|
1080
|
+
"--rendering-speed <tier>",
|
|
1081
|
+
'image speed/cost tier, e.g. Ideogram V4 "Turbo"/"Balanced"/"Quality"'
|
|
1082
|
+
).option("--audio", "include native model audio in the estimate").option("--no-audio", "exclude native model audio").option("--num <n>", "image batch size").action(async function(model) {
|
|
1072
1083
|
const ctx = buildContext(this);
|
|
1073
1084
|
const opts = this.opts();
|
|
1074
1085
|
const result = await ctx.client.callTool(
|
|
@@ -1077,6 +1088,8 @@ function registerAccountCommands(program) {
|
|
|
1077
1088
|
model_id: model,
|
|
1078
1089
|
type: opts.type,
|
|
1079
1090
|
duration_seconds: opts.duration ? Number(opts.duration) : void 0,
|
|
1091
|
+
length_seconds: opts.length ? Number(opts.length) : void 0,
|
|
1092
|
+
characters: opts.chars ? Number(opts.chars) : void 0,
|
|
1080
1093
|
resolution: opts.resolution,
|
|
1081
1094
|
quality: opts.quality,
|
|
1082
1095
|
rendering_speed: opts.renderingSpeed,
|
|
@@ -1086,7 +1099,9 @@ function registerAccountCommands(program) {
|
|
|
1086
1099
|
);
|
|
1087
1100
|
emit(ctx.out, result);
|
|
1088
1101
|
});
|
|
1089
|
-
program.command("models [kind]").description(
|
|
1102
|
+
program.command("models [kind]").description(
|
|
1103
|
+
"List available models: image | video | audio | voices | styles (default: image + video + audio)"
|
|
1104
|
+
).action(async function(kind) {
|
|
1090
1105
|
const ctx = buildContext(this);
|
|
1091
1106
|
const wanted = kind ?? "all";
|
|
1092
1107
|
const result = {};
|
|
@@ -1096,8 +1111,15 @@ function registerAccountCommands(program) {
|
|
|
1096
1111
|
if (wanted === "video" || wanted === "all") {
|
|
1097
1112
|
result.video = await ctx.client.callTool("list_available_video_models");
|
|
1098
1113
|
}
|
|
1099
|
-
if (wanted === "
|
|
1100
|
-
|
|
1114
|
+
if (wanted === "audio" || wanted === "all") {
|
|
1115
|
+
result.audio = await ctx.client.callTool("list_available_audio_models");
|
|
1116
|
+
}
|
|
1117
|
+
if (wanted === "voices") {
|
|
1118
|
+
result.voices = await ctx.client.callTool("list_available_voices");
|
|
1119
|
+
}
|
|
1120
|
+
if (wanted === "styles") {
|
|
1121
|
+
result.styles = await ctx.client.callTool("list_available_styles");
|
|
1122
|
+
}
|
|
1101
1123
|
emit(ctx.out, result, (o) => {
|
|
1102
1124
|
for (const [section, payload] of Object.entries(result)) {
|
|
1103
1125
|
const models = Array.isArray(payload) ? payload : payload?.models ?? payload?.voices ?? payload?.styles ?? [];
|
|
@@ -1577,6 +1599,14 @@ async function uploadFile(client, localPath, options = {}) {
|
|
|
1577
1599
|
|
|
1578
1600
|
// src/commands/generate.ts
|
|
1579
1601
|
var URI_SCHEME = /^[a-z][a-z0-9+.-]*:\/\//i;
|
|
1602
|
+
var VIDEO_SOURCE_RE = /\.(mp4|mov|webm|m4v|gif)(?:[?#].*)?$/i;
|
|
1603
|
+
function inferDubMediaType(source, explicit) {
|
|
1604
|
+
if (explicit) {
|
|
1605
|
+
if (explicit === "audio" || explicit === "video") return explicit;
|
|
1606
|
+
throw new Error('--type must be "audio" or "video"');
|
|
1607
|
+
}
|
|
1608
|
+
return VIDEO_SOURCE_RE.test(source) ? "video" : "audio";
|
|
1609
|
+
}
|
|
1580
1610
|
async function resolveRefs(ctx, refs) {
|
|
1581
1611
|
const resolved = [];
|
|
1582
1612
|
for (const ref of refs) {
|
|
@@ -1669,7 +1699,7 @@ async function handleAsyncJob(ctx, submitted, options) {
|
|
|
1669
1699
|
}
|
|
1670
1700
|
}
|
|
1671
1701
|
function registerGenerateCommands(program) {
|
|
1672
|
-
const generate = program.command("generate").description("Generate images, video
|
|
1702
|
+
const generate = program.command("generate").description("Generate images, video and audio");
|
|
1673
1703
|
generate.command("image <prompt...>").description("Generate an image (async; waits by default)").option("--model <id>", "image model id (default nano-banana-2)").option("--ar <ratio>", 'aspect ratio, e.g. "16:9"').option("--resolution <res>", 'e.g. "1K", "2K", "4K"').option("--quality <tier>", "model-specific quality tier").option("--rendering-speed <tier>", 'Ideogram speed/cost tier, e.g. V4 "Turbo"/"Balanced"/"Quality"').option("--num <n>", "variations of this prompt in one call (1-4)").option("--seed <n>", "seed (supported models only, e.g. Flux, Ideogram V4)").option("--ref <url|file>", "reference image (repeatable; local files are uploaded)", collect, []).option("--video-ref <url|file>", "video reference \u2014 nano-banana-2 only (http(s)/gs:///YouTube, or local file)").option("--style <id>", "style preset id").option("--project <id>", "attach to a project").option("--session <id>", "AI Studio session id").option("--scene <n>", "0-based scene index (with --project: writes onto that shot)").option("--shot <n>", "0-based shot index").option("--download <path>", "download outputs (template: {job_id} {index} {ext})").option("--no-wait", "submit and return the job id immediately").option("--estimate", "print the cost estimate and exit (spends nothing)").action(async function(promptWords) {
|
|
1674
1704
|
const ctx = buildContext(this);
|
|
1675
1705
|
const opts = this.opts();
|
|
@@ -1716,7 +1746,10 @@ function registerGenerateCommands(program) {
|
|
|
1716
1746
|
label: "Generating image"
|
|
1717
1747
|
});
|
|
1718
1748
|
});
|
|
1719
|
-
generate.command("video [prompt...]").description("Generate a video clip (async; per-second pricing \u2014 see --estimate)").option("--model <id>", "video model id (default google-veo3.1 fast)").option("--ar <ratio>", 'aspect ratio, e.g. "16:9", "9:16"').option("--duration <seconds>", "clip duration in seconds").option("--resolution <res>", 'e.g. "
|
|
1749
|
+
generate.command("video [prompt...]").description("Generate a video clip (async; per-second pricing \u2014 see --estimate)").option("--model <id>", "video model id (default google-veo3.1 fast)").option("--ar <ratio>", 'aspect ratio, e.g. "16:9", "9:16"').option("--duration <seconds>", "clip duration in seconds").option("--resolution <res>", 'e.g. "480p", "720p", "1080p", "4k"').option(
|
|
1750
|
+
"--quality <tier>",
|
|
1751
|
+
'e.g. "mini", "fast", "standard", "quality", "pro"'
|
|
1752
|
+
).option("--audio", "generate native model audio").option("--no-audio", "disable native model audio").option("--start-image <url|file>", "start frame (image-to-video)").option("--end-image <url|file>", "end frame (supported models only)").option("--ref <url|file>", "reference image (repeatable)", collect, []).option("--ref-video <url|file>", "reference video (repeatable; Seedance 2, Wan 2.7; local files uploaded)", collect, []).option("--ref-audio <url|file>", "reference audio (repeatable; Seedance 2; local files uploaded)", collect, []).option("--segment <prompt:seconds>", "multi-prompt segment (repeatable; Kling 3.0 / 3.0 Turbo / O3)", collect, []).option("--negative <text>", "negative prompt (Kling/Wan/Luma)").option("--seed <n>", "seed").option("--project <id>", "attach to a project").option("--session <id>", "AI Studio session id").option("--scene <n>", "0-based scene index").option("--shot <n>", "0-based shot index").option("--download <path>", "download outputs (template: {job_id} {index} {ext})").option("--no-wait", "submit and return the job id immediately").option("--estimate", "print the cost estimate and exit (spends nothing)").action(async function(promptWords = []) {
|
|
1720
1753
|
const ctx = buildContext(this);
|
|
1721
1754
|
const opts = this.opts();
|
|
1722
1755
|
const prompt = promptWords.join(" ").trim();
|
|
@@ -1804,16 +1837,19 @@ function registerGenerateCommands(program) {
|
|
|
1804
1837
|
for (const f of downloaded ?? []) note(o, fmt.dim(o, `saved ${f.path}`));
|
|
1805
1838
|
});
|
|
1806
1839
|
});
|
|
1807
|
-
generate.command("music <prompt...>").description("Generate background music
|
|
1840
|
+
generate.command("music <prompt...>").description("Generate background music").option("--model <id>", "lyria-3-clip-preview (default) | lyria-3-pro-preview | elevenlabs-music").option("--length <seconds>", "for --model elevenlabs-music: length 10\u2013120s (default 30)").option("--instrumental", "for --model elevenlabs-music: force instrumental (no vocals)").option("--ref <url|file>", "reference image to inspire the music (Lyria only, repeatable)", collect, []).option("--project <id>", "link the generation to a project's AI Studio session").option("--attach <project_id>", "also set the track as that project's background music").option("--volume <n>", "0-100 BGM volume when attaching (default 30)").option("--bgm-disabled", "when attaching, store the BGM as disabled (enabled:false)").option("--session <id>", "AI Studio session id").option("--download <path>", "download the audio file").action(async function(promptWords) {
|
|
1808
1841
|
const ctx = buildContext(this);
|
|
1809
1842
|
const opts = this.opts();
|
|
1810
|
-
const
|
|
1811
|
-
|
|
1843
|
+
const musicModel = opts.model ?? "lyria-3-clip-preview";
|
|
1844
|
+
const refs = musicModel === "elevenlabs-music" ? [] : await resolveRefs(ctx, opts.ref ?? []);
|
|
1845
|
+
capture("cli_generate", { kind: "music", model: musicModel });
|
|
1812
1846
|
const result = await ctx.client.callTool(
|
|
1813
1847
|
"generate_music",
|
|
1814
1848
|
compact({
|
|
1815
1849
|
prompt: promptWords.join(" "),
|
|
1816
|
-
model:
|
|
1850
|
+
model: musicModel,
|
|
1851
|
+
length_seconds: opts.length ? Number(opts.length) : void 0,
|
|
1852
|
+
force_instrumental: opts.instrumental ? true : void 0,
|
|
1817
1853
|
image_urls: refs.length > 0 ? refs : void 0,
|
|
1818
1854
|
project_id: opts.project,
|
|
1819
1855
|
attach_to_project_id: opts.attach,
|
|
@@ -1834,6 +1870,173 @@ function registerGenerateCommands(program) {
|
|
|
1834
1870
|
for (const f of downloaded ?? []) note(o, fmt.dim(o, `saved ${f.path}`));
|
|
1835
1871
|
});
|
|
1836
1872
|
});
|
|
1873
|
+
generate.command("sound-effect <prompt...>").description("Generate a sound effect (ElevenLabs Sound Effects)").option("--duration <seconds>", "length 0.5\u201322s (default 5)").option("--influence <0-1>", "prompt influence (default 0.3)").option("--project <id>", "link to a project's AI Studio session").option("--session <id>", "AI Studio session id").option("--download <path>", "download the audio file").action(async function(promptWords) {
|
|
1874
|
+
const ctx = buildContext(this);
|
|
1875
|
+
const opts = this.opts();
|
|
1876
|
+
capture("cli_generate", { kind: "sound_effect" });
|
|
1877
|
+
const result = await ctx.client.callTool(
|
|
1878
|
+
"generate_sound_effect",
|
|
1879
|
+
compact({
|
|
1880
|
+
prompt: promptWords.join(" "),
|
|
1881
|
+
duration_seconds: opts.duration ? Number(opts.duration) : void 0,
|
|
1882
|
+
prompt_influence: opts.influence ? Number(opts.influence) : void 0,
|
|
1883
|
+
project_id: opts.project,
|
|
1884
|
+
session_id: opts.session
|
|
1885
|
+
})
|
|
1886
|
+
);
|
|
1887
|
+
const urls = extractOutputUrls(result);
|
|
1888
|
+
let downloaded;
|
|
1889
|
+
if (opts.download && urls.length > 0) {
|
|
1890
|
+
downloaded = await downloadOutputs(urls, opts.download, {
|
|
1891
|
+
name: "sound-effect"
|
|
1892
|
+
});
|
|
1893
|
+
}
|
|
1894
|
+
const media = buildMediaDescriptors(urls, "audio");
|
|
1895
|
+
emit(
|
|
1896
|
+
ctx.out,
|
|
1897
|
+
{ ...result, downloaded_files: downloaded, output_media: media },
|
|
1898
|
+
(o) => {
|
|
1899
|
+
for (const url of urls) process.stdout.write(`${url}
|
|
1900
|
+
`);
|
|
1901
|
+
for (const f of downloaded ?? []) note(o, fmt.dim(o, `saved ${f.path}`));
|
|
1902
|
+
}
|
|
1903
|
+
);
|
|
1904
|
+
});
|
|
1905
|
+
generate.command("dialogue").description(
|
|
1906
|
+
"Generate multi-speaker dialogue (ElevenLabs Text-to-Dialogue). Repeat --line."
|
|
1907
|
+
).option(
|
|
1908
|
+
"--line <voiceId:text>",
|
|
1909
|
+
'a dialogue line as "voiceId:text" (repeatable)',
|
|
1910
|
+
collect,
|
|
1911
|
+
[]
|
|
1912
|
+
).option("--stability <0|0.5|1>", "voice stability").option("--language <iso>", "ISO 639-1 language code").option("--project <id>", "link to a project's AI Studio session").option("--session <id>", "AI Studio session id").option("--download <path>", "download the audio file").action(async function() {
|
|
1913
|
+
const ctx = buildContext(this);
|
|
1914
|
+
const opts = this.opts();
|
|
1915
|
+
const lines = opts.line.map((raw) => {
|
|
1916
|
+
const i = raw.indexOf(":");
|
|
1917
|
+
if (i < 0) {
|
|
1918
|
+
throw new Error(`--line must be "voiceId:text" (got "${raw}")`);
|
|
1919
|
+
}
|
|
1920
|
+
return { voice_id: raw.slice(0, i).trim(), text: raw.slice(i + 1).trim() };
|
|
1921
|
+
});
|
|
1922
|
+
if (lines.length === 0) throw new Error("at least one --line is required");
|
|
1923
|
+
capture("cli_generate", { kind: "dialogue" });
|
|
1924
|
+
const result = await ctx.client.callTool(
|
|
1925
|
+
"generate_dialogue",
|
|
1926
|
+
compact({
|
|
1927
|
+
lines,
|
|
1928
|
+
stability: opts.stability !== void 0 ? Number(opts.stability) : void 0,
|
|
1929
|
+
language_code: opts.language,
|
|
1930
|
+
project_id: opts.project,
|
|
1931
|
+
session_id: opts.session
|
|
1932
|
+
})
|
|
1933
|
+
);
|
|
1934
|
+
const urls = extractOutputUrls(result);
|
|
1935
|
+
let downloaded;
|
|
1936
|
+
if (opts.download && urls.length > 0) {
|
|
1937
|
+
downloaded = await downloadOutputs(urls, opts.download, {
|
|
1938
|
+
name: "dialogue"
|
|
1939
|
+
});
|
|
1940
|
+
}
|
|
1941
|
+
const media = buildMediaDescriptors(urls, "audio");
|
|
1942
|
+
emit(
|
|
1943
|
+
ctx.out,
|
|
1944
|
+
{ ...result, downloaded_files: downloaded, output_media: media },
|
|
1945
|
+
(o) => {
|
|
1946
|
+
for (const url of urls) process.stdout.write(`${url}
|
|
1947
|
+
`);
|
|
1948
|
+
for (const f of downloaded ?? []) note(o, fmt.dim(o, `saved ${f.path}`));
|
|
1949
|
+
}
|
|
1950
|
+
);
|
|
1951
|
+
});
|
|
1952
|
+
generate.command("voice-changer <audio>").description("Restyle speech into another ElevenLabs voice (Voice Changer)").option("--voice <id>", "target ElevenLabs voice id (default Brittney)").option(
|
|
1953
|
+
"--duration <seconds>",
|
|
1954
|
+
"length of the source audio in seconds (required, max 300)"
|
|
1955
|
+
).option("--remove-noise", "remove background noise from the input").option("--project <id>", "link to a project's AI Studio session").option("--session <id>", "AI Studio session id").option("--download <path>", "download the audio file").action(async function(source) {
|
|
1956
|
+
const ctx = buildContext(this);
|
|
1957
|
+
const opts = this.opts();
|
|
1958
|
+
if (!opts.duration) {
|
|
1959
|
+
throw new Error(
|
|
1960
|
+
"--duration <seconds> is required (length of the source audio)"
|
|
1961
|
+
);
|
|
1962
|
+
}
|
|
1963
|
+
const [audioUrl] = await resolveRefs(ctx, [source]);
|
|
1964
|
+
capture("cli_generate", { kind: "voice_changer" });
|
|
1965
|
+
const result = await ctx.client.callTool(
|
|
1966
|
+
"change_voice",
|
|
1967
|
+
compact({
|
|
1968
|
+
audio_url: audioUrl,
|
|
1969
|
+
voice_id: opts.voice,
|
|
1970
|
+
duration_seconds: Number(opts.duration),
|
|
1971
|
+
remove_background_noise: opts.removeNoise ? true : void 0,
|
|
1972
|
+
project_id: opts.project,
|
|
1973
|
+
session_id: opts.session
|
|
1974
|
+
})
|
|
1975
|
+
);
|
|
1976
|
+
const urls = extractOutputUrls(result);
|
|
1977
|
+
let downloaded;
|
|
1978
|
+
if (opts.download && urls.length > 0) {
|
|
1979
|
+
downloaded = await downloadOutputs(urls, opts.download, {
|
|
1980
|
+
name: "voice-changed"
|
|
1981
|
+
});
|
|
1982
|
+
}
|
|
1983
|
+
const media = buildMediaDescriptors(urls, "audio");
|
|
1984
|
+
emit(
|
|
1985
|
+
ctx.out,
|
|
1986
|
+
{ ...result, downloaded_files: downloaded, output_media: media },
|
|
1987
|
+
(o) => {
|
|
1988
|
+
for (const url of urls) process.stdout.write(`${url}
|
|
1989
|
+
`);
|
|
1990
|
+
for (const f of downloaded ?? []) note(o, fmt.dim(o, `saved ${f.path}`));
|
|
1991
|
+
}
|
|
1992
|
+
);
|
|
1993
|
+
});
|
|
1994
|
+
generate.command("dub <media>").description("Dub a video/audio file into another language (ElevenLabs Dubbing)").option("--to <iso>", "target language ISO 639-1 code, e.g. es or te (required)").option("--from <iso>", "source language ISO 639-1 code (auto-detected if omitted)").option("--type <audio|video>", "source media type override").option(
|
|
1995
|
+
"--duration <seconds>",
|
|
1996
|
+
"length of the source media in seconds (required, max 300)"
|
|
1997
|
+
).option("--speakers <n>", "number of speakers (auto-detected if omitted)").option("--project <id>", "link to a project's AI Studio session").option("--session <id>", "AI Studio session id").option("--download <path>", "download the dubbed file").action(async function(source) {
|
|
1998
|
+
const ctx = buildContext(this);
|
|
1999
|
+
const opts = this.opts();
|
|
2000
|
+
if (!opts.to) throw new Error("--to <iso> (target language) is required");
|
|
2001
|
+
if (!opts.duration) {
|
|
2002
|
+
throw new Error(
|
|
2003
|
+
"--duration <seconds> is required (length of the source media)"
|
|
2004
|
+
);
|
|
2005
|
+
}
|
|
2006
|
+
const mediaType = inferDubMediaType(source, opts.type);
|
|
2007
|
+
const [mediaUrl] = await resolveRefs(ctx, [source]);
|
|
2008
|
+
capture("cli_generate", { kind: "dub" });
|
|
2009
|
+
const result = await ctx.client.callTool(
|
|
2010
|
+
"dub_media",
|
|
2011
|
+
compact({
|
|
2012
|
+
video_url: mediaType === "video" ? mediaUrl : void 0,
|
|
2013
|
+
audio_url: mediaType === "audio" ? mediaUrl : void 0,
|
|
2014
|
+
target_lang: opts.to,
|
|
2015
|
+
source_lang: opts.from,
|
|
2016
|
+
num_speakers: opts.speakers ? Number(opts.speakers) : void 0,
|
|
2017
|
+
duration_seconds: Number(opts.duration),
|
|
2018
|
+
project_id: opts.project,
|
|
2019
|
+
session_id: opts.session
|
|
2020
|
+
})
|
|
2021
|
+
);
|
|
2022
|
+
const urls = extractOutputUrls(result);
|
|
2023
|
+
let downloaded;
|
|
2024
|
+
if (opts.download && urls.length > 0) {
|
|
2025
|
+
downloaded = await downloadOutputs(urls, opts.download, {
|
|
2026
|
+
name: "dubbed"
|
|
2027
|
+
});
|
|
2028
|
+
}
|
|
2029
|
+
const media = buildMediaDescriptors(urls, mediaType);
|
|
2030
|
+
emit(
|
|
2031
|
+
ctx.out,
|
|
2032
|
+
{ ...result, downloaded_files: downloaded, output_media: media },
|
|
2033
|
+
(o) => {
|
|
2034
|
+
for (const url of urls) process.stdout.write(`${url}
|
|
2035
|
+
`);
|
|
2036
|
+
for (const f of downloaded ?? []) note(o, fmt.dim(o, `saved ${f.path}`));
|
|
2037
|
+
}
|
|
2038
|
+
);
|
|
2039
|
+
});
|
|
1837
2040
|
const upscale = program.command("upscale").description("Upscale images and videos (Topaz)");
|
|
1838
2041
|
upscale.command("image <url|file>").description("Upscale an image (synchronous)").option("--scale <factor>", '"1x" | "2x" | "4x" (default 2x)').option("--session <id>", "AI Studio session id").option("--download <path>", "download the result").action(async function(source) {
|
|
1839
2042
|
const ctx = buildContext(this);
|
|
@@ -2257,7 +2460,7 @@ function registerJobCommands(program) {
|
|
|
2257
2460
|
throw err;
|
|
2258
2461
|
}
|
|
2259
2462
|
});
|
|
2260
|
-
program.command("generations").description("List recent + in-flight generations").option("--type <type>", "image | video").option("--status <status>", "pending | processing | completed | failed").option("--limit <n>", "max rows (default 30)").action(async function() {
|
|
2463
|
+
program.command("generations").description("List recent + in-flight generations").option("--type <type>", "image | video | sounds").option("--status <status>", "pending | processing | completed | failed").option("--limit <n>", "max rows (default 30)").action(async function() {
|
|
2261
2464
|
const ctx = buildContext(this);
|
|
2262
2465
|
const opts = this.opts();
|
|
2263
2466
|
const result = await ctx.client.callTool(
|
|
@@ -2787,7 +2990,7 @@ async function configureProxy() {
|
|
|
2787
2990
|
function buildProgram() {
|
|
2788
2991
|
const program = new Command();
|
|
2789
2992
|
program.name("videodraft").description(
|
|
2790
|
-
"Create AI videos, images
|
|
2993
|
+
"Create AI videos, images and audio from your terminal.\nAgent-friendly: every command supports --json; exit codes are stable\n(0 ok, 1 error, 2 usage, 3 auth, 4 insufficient credits)."
|
|
2791
2994
|
).version(VERSION, "-v, --version", "print the CLI version").option("--json", "machine-readable JSON output").option("--no-color", "disable colored output (NO_COLOR is also respected)").option("--base-url <url>", "VideoDraft server (default https://app.videodraft.ai; env VIDEODRAFT_BASE_URL)").option("--token <vd_mcp_token>", "bearer token for this invocation (env VIDEODRAFT_API_KEY)").option("--profile <name>", "config profile (default: default)").option("--wait-interval <duration>", "poll interval for --wait, e.g. 3s (default)").option("--wait-timeout <duration>", "max wait for --wait, e.g. 10m (default)").showSuggestionAfterError(true).exitOverride();
|
|
2792
2995
|
registerAuthCommands(program);
|
|
2793
2996
|
registerAccountCommands(program);
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "videodraft",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "Official VideoDraft CLI — create AI videos, images
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"description": "Official VideoDraft CLI — create AI videos, images and audio from your terminal. Agent-friendly: --json everywhere, stable exit codes, async job polling.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"homepage": "https://videodraft.ai/cli",
|
|
@@ -1,10 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: videodraft
|
|
3
|
-
description: Create AI videos, images, voiceovers, music, storyboards, avatar videos and product/ad videos with VideoDraft. Use when the user mentions VideoDraft, or asks to generate/make a video, video ad, explainer, storyboard, talking-head/avatar video, AI image, voiceover/TTS,
|
|
4
|
-
license: MIT
|
|
5
|
-
metadata:
|
|
6
|
-
author: VideoDraft (videodraft.ai)
|
|
7
|
-
homepage: https://videodraft.ai/cli
|
|
3
|
+
description: Create AI videos, images, voiceovers, music, sound effects, dialogue, dubbing, storyboards, avatar videos and product/ad videos with VideoDraft. Use when the user mentions VideoDraft, or asks to generate/make a video, video ad, explainer, storyboard, talking-head/avatar video, AI image, voiceover/TTS, background music, sound effects, dialogue audio, voice changing, or dubbing — including batch/programmatic video generation in scripts or CI. Works via the `videodraft` CLI (preferred in terminals) or the VideoDraft MCP connector.
|
|
8
4
|
---
|
|
9
5
|
|
|
10
6
|
# VideoDraft
|
|
@@ -26,7 +22,7 @@ Two equivalent surfaces (same backend, same credits, same projects):
|
|
|
26
22
|
|
|
27
23
|
## First decision: asset or video?
|
|
28
24
|
|
|
29
|
-
- **One standalone asset** (a single image, clip, voiceover,
|
|
25
|
+
- **One standalone asset** (a single image, clip, voiceover, music track, sound effect, dialogue track, voice-changed file, or dubbed media file, no story): generate it directly. Do NOT create a project.
|
|
30
26
|
- `videodraft generate image "a red fox in snow, cinematic" --ar 16:9 --download ./out/`
|
|
31
27
|
- `videodraft generate video "slow dolly over a misty lake" --model google-veo3.1 --duration 6 --download ./out/`
|
|
32
28
|
- **A video / ad / explainer / anything multi-scene**: create a project so the work stays organized, editable in the web app, and exportable.
|
|
@@ -39,10 +35,10 @@ Two equivalent surfaces (same backend, same credits, same projects):
|
|
|
39
35
|
Generation costs credits (video is per-second; shot-image batches are the largest single spend). Before anything expensive:
|
|
40
36
|
|
|
41
37
|
1. `videodraft credits` — check the balance.
|
|
42
|
-
2. `videodraft generate video "..." --estimate` or `videodraft costs <model> --duration 8 --resolution 1080p` — get the quote.
|
|
38
|
+
2. `videodraft generate video "..." --estimate` or `videodraft costs <model> --duration 8 --resolution 1080p` — get the quote. For ElevenLabs audio, use `--type audio` plus `--duration`, `--length`, or `--chars`.
|
|
43
39
|
3. Tell the user the model + settings + rough cost and get a go-ahead. Ask rather than assume aspect ratio, duration, and model when they matter.
|
|
44
40
|
|
|
45
|
-
`videodraft models image|video` lists every model with its supported inputs (aspect ratios, resolutions, reference limits) — consult it instead of guessing capabilities.
|
|
41
|
+
`videodraft models image|video|audio` lists every model with its supported inputs (aspect ratios, resolutions, reference limits, audio billing inputs) — consult it instead of guessing capabilities.
|
|
46
42
|
|
|
47
43
|
## Async jobs
|
|
48
44
|
|
|
@@ -80,7 +76,7 @@ videodraft produce <project_id> # voiceovers + captions + produc
|
|
|
80
76
|
videodraft export <project_id> --download final.mp4
|
|
81
77
|
```
|
|
82
78
|
|
|
83
|
-
Optional between produce and export: per-shot motion clips (`videodraft generate video ... --project <id>` then place it with `videodraft attach <project> --scene N --shot M --media <url|file> --type video --duration <s>`),
|
|
79
|
+
Optional between produce and export: per-shot motion clips (`videodraft generate video ... --project <id>` then place it with `videodraft attach <project> --scene N --shot M --media <url|file> --type video --duration <s>`), music (`videodraft generate music "..." --attach <project_id>`), and standalone audio assets (`generate sound-effect`, `generate dialogue`, `generate voice-changer`, `generate dub`). Details, per-step tools and editing rules: [references/pipeline.md](references/pipeline.md).
|
|
84
80
|
|
|
85
81
|
Avatar/talking-head videos are their own short flow: `videodraft avatar script` → `avatar create` → `avatar render` (paid step).
|
|
86
82
|
|
|
@@ -5,6 +5,7 @@ Always consult the live catalog instead of memorizing this page — models chang
|
|
|
5
5
|
```bash
|
|
6
6
|
videodraft models image --json # every image model + inputs (aspect ratios, resolutions, max refs)
|
|
7
7
|
videodraft models video --json # every video model + inputs + per-second pricing metadata
|
|
8
|
+
videodraft models audio --json # standalone audio/media models + pricing inputs
|
|
8
9
|
videodraft models voices --json # TTS voices
|
|
9
10
|
videodraft models styles --json # visual style presets
|
|
10
11
|
```
|
|
@@ -14,7 +15,8 @@ videodraft models styles --json # visual style presets
|
|
|
14
15
|
- **Image**: `nano-banana-2` (the platform default, 1K). Use `--num 1..4` for variations of one prompt in a single call — never loop for variations.
|
|
15
16
|
- **Video**: `google-veo3.1` at fast quality (6s / 720p) — the platform default.
|
|
16
17
|
- **Voiceover**: ElevenLabs Brittney (default voice).
|
|
17
|
-
- **Music**: `lyria-3-clip-preview` (30s, cheap); `lyria-3-pro-preview` for 180s/quality.
|
|
18
|
+
- **Music**: `lyria-3-clip-preview` (30s, cheap); `lyria-3-pro-preview` for 180s/quality; `elevenlabs-music` for music that can include vocals/lyrics.
|
|
19
|
+
- **ElevenLabs audio**: `generate sound-effect`, `generate dialogue`, `generate voice-changer`, and `generate dub` are synchronous audio/media calls. Voice changer and dubbing require the source media duration in seconds for billing and currently accept source media up to 300s.
|
|
18
20
|
|
|
19
21
|
## Capability gotchas
|
|
20
22
|
|
|
@@ -32,12 +34,15 @@ videodraft models styles --json # visual style presets
|
|
|
32
34
|
- Video: usually credits/second × duration; rate depends on model + resolution + quality + native audio on/off.
|
|
33
35
|
- Shot-image batches: one image per shot (+1 grid image per scene in `--grid` mode) — the largest single spend in the pipeline.
|
|
34
36
|
- Avatar renders: ~10 credits/sec at 480p, ~20/sec at 720p.
|
|
37
|
+
- ElevenLabs audio: sound effects are per second, dialogue is per character, music/voice-changer/dubbing are per started minute. Voice changer and dubbing reject source media above 300s in the current synchronous flow.
|
|
35
38
|
- Upscales: priced by scale and source size.
|
|
36
39
|
|
|
37
40
|
Quote before spending:
|
|
38
41
|
|
|
39
42
|
```bash
|
|
40
43
|
videodraft costs google-veo3.1 --type video --duration 8 --resolution 1080p --audio
|
|
44
|
+
videodraft costs elevenlabs-dubbing --type audio --duration 60
|
|
45
|
+
videodraft costs elevenlabs-dialogue --type audio --chars 350
|
|
41
46
|
videodraft generate video "..." --estimate # same quote, inline
|
|
42
47
|
videodraft credits # current balance
|
|
43
48
|
```
|
|
@@ -16,6 +16,10 @@ Everything here works through the CLI (`videodraft <command>` / `videodraft call
|
|
|
16
16
|
| Motion clip for a shot | `videodraft generate video --project <id>` | `generate_video` |
|
|
17
17
|
| Attach a finished clip to the timeline | `videodraft attach <project> --scene N --shot M --media <url> --type video` | `attach_media_to_shot` |
|
|
18
18
|
| Background music | `videodraft generate music --attach <project>` | `generate_music` / `set_background_music` |
|
|
19
|
+
| Sound effect | `videodraft generate sound-effect "..."` | `generate_sound_effect` |
|
|
20
|
+
| Dialogue audio | `videodraft generate dialogue --line "voice:text"` | `generate_dialogue` |
|
|
21
|
+
| Voice changer | `videodraft generate voice-changer <audio>` | `change_voice` |
|
|
22
|
+
| Dubbing | `videodraft generate dub <audio_or_video>` | `dub_media` |
|
|
19
23
|
| Scene voiceover | `videodraft generate voiceover --project <id> --scene N` | `generate_voiceover` |
|
|
20
24
|
| Final MP4 | `videodraft export <project>` | `export_video` + `check_export_status` |
|
|
21
25
|
|