videodraft 0.1.1 → 0.1.2
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 +7 -2
- package/dist/index.js +78 -15
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -11,10 +11,14 @@ npx videodraft create "30s launch video for our espresso machine" --ar 9:16
|
|
|
11
11
|
## Install
|
|
12
12
|
|
|
13
13
|
```bash
|
|
14
|
-
npm install -g videodraft #
|
|
14
|
+
npm install -g videodraft # global: adds the `videodraft` command to your PATH
|
|
15
|
+
videodraft --version # → confirms it's installed
|
|
16
|
+
|
|
17
|
+
# …or run without installing — npx fetches and runs the latest each time:
|
|
18
|
+
npx videodraft <command>
|
|
15
19
|
```
|
|
16
20
|
|
|
17
|
-
Requires Node ≥ 20.18.1.
|
|
21
|
+
Requires Node ≥ 20.18.1. (Publishing to npm doesn't put `videodraft` on your PATH — `npm i -g` or `npx` does. A Homebrew tap and a `curl | sh` installer are on the way.)
|
|
18
22
|
|
|
19
23
|
## Authenticate
|
|
20
24
|
|
|
@@ -81,6 +85,7 @@ Install the VideoDraft skill so your agent knows the workflow:
|
|
|
81
85
|
npx videodraft skills install # zero-install: npx fetches the CLI and installs the skill
|
|
82
86
|
videodraft skills install # if the CLI is on PATH — auto-detects your installed agents
|
|
83
87
|
videodraft skills install --agent claude,codex # target specific agents (repeatable/comma; --all for every agent)
|
|
88
|
+
videodraft skills install --project # into ./.claude/skills for just this repo (else global)
|
|
84
89
|
npx skills add videodraft-ai/cli # via the vercel-labs skills tool (discovery / 69-agent multiselect)
|
|
85
90
|
```
|
|
86
91
|
|
package/dist/index.js
CHANGED
|
@@ -1413,6 +1413,43 @@ async function pollExport(client, ref, options = {}) {
|
|
|
1413
1413
|
}
|
|
1414
1414
|
}
|
|
1415
1415
|
|
|
1416
|
+
// src/core/media.ts
|
|
1417
|
+
var IMAGE_EXTS = /* @__PURE__ */ new Set(["png", "jpg", "jpeg", "webp", "gif", "bmp", "heic", "heif", "avif", "svg"]);
|
|
1418
|
+
var VIDEO_EXTS = /* @__PURE__ */ new Set(["mp4", "webm", "mov", "m4v"]);
|
|
1419
|
+
var AUDIO_EXTS = /* @__PURE__ */ new Set(["mp3", "wav", "m4a", "ogg", "flac", "aac"]);
|
|
1420
|
+
function kindOf(url, typeHint) {
|
|
1421
|
+
const t = (typeHint ?? "").toLowerCase();
|
|
1422
|
+
if (t.includes("image")) return "image";
|
|
1423
|
+
if (t.includes("video")) return "video";
|
|
1424
|
+
if (t.includes("audio") || t.includes("music") || t.includes("sound") || t.includes("voice") || t.includes("speech") || t.includes("tts")) {
|
|
1425
|
+
return "audio";
|
|
1426
|
+
}
|
|
1427
|
+
const path5 = url.split(/[?#]/)[0]?.toLowerCase() ?? "";
|
|
1428
|
+
if (path5.includes("/img/")) return "image";
|
|
1429
|
+
if (path5.includes("/vid/")) return "video";
|
|
1430
|
+
if (path5.includes("/aud/")) return "audio";
|
|
1431
|
+
const ext = path5.match(/\.([a-z0-9]+)$/)?.[1];
|
|
1432
|
+
if (ext) {
|
|
1433
|
+
if (IMAGE_EXTS.has(ext)) return "image";
|
|
1434
|
+
if (VIDEO_EXTS.has(ext)) return "video";
|
|
1435
|
+
if (AUDIO_EXTS.has(ext)) return "audio";
|
|
1436
|
+
}
|
|
1437
|
+
return null;
|
|
1438
|
+
}
|
|
1439
|
+
function buildMediaDescriptors(urls, typeHint) {
|
|
1440
|
+
if (!Array.isArray(urls)) return [];
|
|
1441
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1442
|
+
const out = [];
|
|
1443
|
+
for (const url of urls) {
|
|
1444
|
+
if (typeof url !== "string" || !/^https?:\/\//i.test(url) || seen.has(url)) continue;
|
|
1445
|
+
const kind = kindOf(url, typeHint);
|
|
1446
|
+
if (!kind) continue;
|
|
1447
|
+
seen.add(url);
|
|
1448
|
+
out.push({ kind, url });
|
|
1449
|
+
}
|
|
1450
|
+
return out;
|
|
1451
|
+
}
|
|
1452
|
+
|
|
1416
1453
|
// src/core/download.ts
|
|
1417
1454
|
import fs3 from "fs";
|
|
1418
1455
|
import path2 from "path";
|
|
@@ -1615,9 +1652,10 @@ async function handleAsyncJob(ctx, submitted, options) {
|
|
|
1615
1652
|
if (options.download && result.outputUrls.length > 0) {
|
|
1616
1653
|
downloaded = await downloadOutputs(result.outputUrls, options.download, { job_id: jobId });
|
|
1617
1654
|
}
|
|
1655
|
+
const media = buildMediaDescriptors(result.outputUrls, result.payload?.type);
|
|
1618
1656
|
emit(
|
|
1619
1657
|
ctx.out,
|
|
1620
|
-
{ job_id: jobId, status: result.status, outputs: result.outputUrls, downloaded_files: downloaded },
|
|
1658
|
+
{ job_id: jobId, status: result.status, outputs: result.outputUrls, downloaded_files: downloaded, output_media: media },
|
|
1621
1659
|
(o) => {
|
|
1622
1660
|
note(o, fmt.green(o, `Completed \u2014 job ${jobId}`));
|
|
1623
1661
|
for (const url of result.outputUrls) process.stdout.write(`${url}
|
|
@@ -1759,7 +1797,8 @@ function registerGenerateCommands(program) {
|
|
|
1759
1797
|
if (opts.download && urls.length > 0) {
|
|
1760
1798
|
downloaded = await downloadOutputs(urls, opts.download, { name: "voiceover" });
|
|
1761
1799
|
}
|
|
1762
|
-
|
|
1800
|
+
const media = buildMediaDescriptors(urls, "audio");
|
|
1801
|
+
emit(ctx.out, { ...result, downloaded_files: downloaded, output_media: media }, (o) => {
|
|
1763
1802
|
for (const url of urls) process.stdout.write(`${url}
|
|
1764
1803
|
`);
|
|
1765
1804
|
for (const f of downloaded ?? []) note(o, fmt.dim(o, `saved ${f.path}`));
|
|
@@ -1788,7 +1827,8 @@ function registerGenerateCommands(program) {
|
|
|
1788
1827
|
if (opts.download && urls.length > 0) {
|
|
1789
1828
|
downloaded = await downloadOutputs(urls, opts.download, { name: "music" });
|
|
1790
1829
|
}
|
|
1791
|
-
|
|
1830
|
+
const media = buildMediaDescriptors(urls, "music");
|
|
1831
|
+
emit(ctx.out, { ...result, downloaded_files: downloaded, output_media: media }, (o) => {
|
|
1792
1832
|
for (const url of urls) process.stdout.write(`${url}
|
|
1793
1833
|
`);
|
|
1794
1834
|
for (const f of downloaded ?? []) note(o, fmt.dim(o, `saved ${f.path}`));
|
|
@@ -1809,7 +1849,8 @@ function registerGenerateCommands(program) {
|
|
|
1809
1849
|
if (opts.download && urls.length > 0) {
|
|
1810
1850
|
downloaded = await downloadOutputs(urls, opts.download, { name: "upscaled" });
|
|
1811
1851
|
}
|
|
1812
|
-
|
|
1852
|
+
const media = buildMediaDescriptors(urls, "image");
|
|
1853
|
+
emit(ctx.out, { ...result, downloaded_files: downloaded, output_media: media }, (o) => {
|
|
1813
1854
|
for (const u of urls) process.stdout.write(`${u}
|
|
1814
1855
|
`);
|
|
1815
1856
|
for (const f of downloaded ?? []) note(o, fmt.dim(o, `saved ${f.path}`));
|
|
@@ -1948,7 +1989,18 @@ function registerPipelineCommands(program) {
|
|
|
1948
1989
|
const failed = results.filter((r) => r.status === "failed").length;
|
|
1949
1990
|
emit(
|
|
1950
1991
|
ctx.out,
|
|
1951
|
-
{
|
|
1992
|
+
{
|
|
1993
|
+
job_ids: jobIds,
|
|
1994
|
+
results: jobIds.map((jobId) => {
|
|
1995
|
+
const result = resultMap.get(jobId);
|
|
1996
|
+
return {
|
|
1997
|
+
job_id: jobId,
|
|
1998
|
+
status: result.status,
|
|
1999
|
+
outputs: result.outputUrls,
|
|
2000
|
+
output_media: buildMediaDescriptors(result.outputUrls, result.payload?.type)
|
|
2001
|
+
};
|
|
2002
|
+
})
|
|
2003
|
+
},
|
|
1952
2004
|
(o) => {
|
|
1953
2005
|
note(
|
|
1954
2006
|
o,
|
|
@@ -2084,12 +2136,17 @@ function registerPipelineCommands(program) {
|
|
|
2084
2136
|
name: "export"
|
|
2085
2137
|
});
|
|
2086
2138
|
}
|
|
2087
|
-
|
|
2088
|
-
|
|
2089
|
-
|
|
2139
|
+
const media = buildMediaDescriptors([result.videoUrl], "video");
|
|
2140
|
+
emit(
|
|
2141
|
+
ctx.out,
|
|
2142
|
+
{ export_id: exportId, video_url: result.videoUrl, downloaded_files: downloaded, output_media: media },
|
|
2143
|
+
(o) => {
|
|
2144
|
+
note(o, fmt.green(o, "Export finished."));
|
|
2145
|
+
process.stdout.write(`${result.videoUrl}
|
|
2090
2146
|
`);
|
|
2091
|
-
|
|
2092
|
-
|
|
2147
|
+
for (const f of downloaded ?? []) note(o, fmt.dim(o, `saved ${f.path}`));
|
|
2148
|
+
}
|
|
2149
|
+
);
|
|
2093
2150
|
} catch (err) {
|
|
2094
2151
|
spin.stop();
|
|
2095
2152
|
throw err;
|
|
@@ -2106,7 +2163,8 @@ function registerPipelineCommands(program) {
|
|
|
2106
2163
|
wait_seconds: opts.wait ? Number(opts.wait) : void 0
|
|
2107
2164
|
})
|
|
2108
2165
|
);
|
|
2109
|
-
|
|
2166
|
+
const media = buildMediaDescriptors(extractOutputUrls(result), "video");
|
|
2167
|
+
emit(ctx.out, { ...result, output_media: media });
|
|
2110
2168
|
});
|
|
2111
2169
|
program.command("video-prompts <project_id>").description("Generate advisory per-shot motion/video prompts for a project").option("--ar <ratio>", "aspect ratio").option("--instructions <text>", "authoring instructions").option("--has-voiceover", "tell the generator the project has a voiceover track").option("--video-audio", "tell the generator that generated videos may include native audio").option("--has-bgm", "tell the generator the project has background music").action(async function(projectId) {
|
|
2112
2170
|
const ctx = buildContext(this);
|
|
@@ -2135,7 +2193,8 @@ function registerJobCommands(program) {
|
|
|
2135
2193
|
"check_generation_status",
|
|
2136
2194
|
compact({ job_id: jobId, project_id: opts.project, scene_id: opts.sceneId })
|
|
2137
2195
|
);
|
|
2138
|
-
|
|
2196
|
+
const media = buildMediaDescriptors(extractOutputUrls(result), result?.type);
|
|
2197
|
+
emit(ctx.out, { ...result, output_media: media }, (o) => {
|
|
2139
2198
|
note(o, `${jobId}: ${result?.status ?? "unknown"}`);
|
|
2140
2199
|
for (const url of extractOutputUrls(result)) process.stdout.write(`${url}
|
|
2141
2200
|
`);
|
|
@@ -2177,6 +2236,7 @@ function registerJobCommands(program) {
|
|
|
2177
2236
|
status: result.status,
|
|
2178
2237
|
outputs: result.outputUrls,
|
|
2179
2238
|
downloaded_files: downloaded,
|
|
2239
|
+
output_media: buildMediaDescriptors(result.outputUrls, result.payload?.type),
|
|
2180
2240
|
...result.status === "failed" ? { error: result.payload?.error } : {}
|
|
2181
2241
|
});
|
|
2182
2242
|
}
|
|
@@ -2403,7 +2463,8 @@ function registerAvatarCommands(program) {
|
|
|
2403
2463
|
target_language: opts.language
|
|
2404
2464
|
})
|
|
2405
2465
|
);
|
|
2406
|
-
|
|
2466
|
+
const media = buildMediaDescriptors(extractOutputUrls(result), "audio");
|
|
2467
|
+
emit(ctx.out, { ...result, output_media: media }, (o) => {
|
|
2407
2468
|
note(o, fmt.green(o, `Avatar video ${result?.avatar_video_id ?? "created"}.`));
|
|
2408
2469
|
note(o, fmt.dim(o, `Render (paid): videodraft avatar render ${result?.avatar_video_id}`));
|
|
2409
2470
|
});
|
|
@@ -2435,7 +2496,8 @@ function registerAvatarCommands(program) {
|
|
|
2435
2496
|
spin.update(`Rendering avatar video \u2014 ${exportStatus}`);
|
|
2436
2497
|
if (exportStatus === "completed") {
|
|
2437
2498
|
spin.stop();
|
|
2438
|
-
|
|
2499
|
+
const media = buildMediaDescriptors(extractOutputUrls(status), "video");
|
|
2500
|
+
emit(ctx.out, { ...status, output_media: media }, (o) => {
|
|
2439
2501
|
note(o, fmt.green(o, "Avatar render completed."));
|
|
2440
2502
|
if (status?.video_url) process.stdout.write(`${status.video_url}
|
|
2441
2503
|
`);
|
|
@@ -2463,7 +2525,8 @@ function registerAvatarCommands(program) {
|
|
|
2463
2525
|
avatar.command("get <avatar_video_id>").description("Fetch one avatar video (status + video_url when rendered)").action(async function(avatarVideoId) {
|
|
2464
2526
|
const ctx = buildContext(this);
|
|
2465
2527
|
const result = await ctx.client.callTool("get_avatar_video", { avatar_video_id: avatarVideoId });
|
|
2466
|
-
|
|
2528
|
+
const media = buildMediaDescriptors(extractOutputUrls(result), "video");
|
|
2529
|
+
emit(ctx.out, { ...result, output_media: media });
|
|
2467
2530
|
});
|
|
2468
2531
|
avatar.command("list").description("List your avatar videos").action(async function() {
|
|
2469
2532
|
const ctx = buildContext(this);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "videodraft",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
4
4
|
"description": "Official VideoDraft CLI — create AI videos, images, voiceovers and music from your terminal. Agent-friendly: --json everywhere, stable exit codes, async job polling.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|