videodraft 0.5.0 → 0.7.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 +1 -1
- package/dist/client.d.ts +6 -0
- package/dist/client.js +130 -1
- package/dist/index.js +157 -19
- package/package.json +1 -1
- package/skills/index.json +14 -9
- package/skills/videodraft/SKILL.md +46 -15
- package/skills/videodraft/references/editor.md +104 -0
- package/skills/videodraft/references/examples.md +10 -4
- package/skills/videodraft/references/models.md +1 -1
- package/skills/videodraft/references/pipeline.md +3 -2
package/README.md
CHANGED
|
@@ -124,7 +124,7 @@ npx videodraft skills install # zero-install: npx fetches the C
|
|
|
124
124
|
videodraft skills install # if the CLI is on PATH — auto-detects your installed agents
|
|
125
125
|
videodraft skills install --agent claude,codex # target specific agents (repeatable/comma; --all for every agent)
|
|
126
126
|
videodraft skills install --project # into ./.claude/skills for just this repo (else global)
|
|
127
|
-
videodraft skills show # print the skill (also: skills show models|examples|pipeline|--all)
|
|
127
|
+
videodraft skills show # print the skill (also: skills show editor|models|examples|pipeline|--all)
|
|
128
128
|
|
|
129
129
|
# Or install straight from the repo — no CLI on PATH needed first:
|
|
130
130
|
npx -y skills add videodraft-ai/cli -g # vercel-labs skills tool; npx -y skips npx's install prompt; -g = user scope
|
package/dist/client.d.ts
CHANGED
|
@@ -217,6 +217,12 @@ interface DownloadedFile {
|
|
|
217
217
|
url: string;
|
|
218
218
|
path: string;
|
|
219
219
|
bytes: number;
|
|
220
|
+
/** Downscaled view copy in <dir>/previews/ (images over the size threshold
|
|
221
|
+
* only). Agents should inspect this instead of the full-res original —
|
|
222
|
+
* every full-res image an agent views is re-sent with each later message
|
|
223
|
+
* of its chat. Best-effort: absent when preview generation failed or was
|
|
224
|
+
* skipped. The original at `path` is always the deliverable. */
|
|
225
|
+
preview?: string;
|
|
220
226
|
}
|
|
221
227
|
declare function extFromUrl(url: string, fallback?: string): string;
|
|
222
228
|
declare function renderTemplate(template: string, vars: {
|
package/dist/client.js
CHANGED
|
@@ -818,10 +818,136 @@ async function pollGenerations(client, jobIds, options = {}) {
|
|
|
818
818
|
}
|
|
819
819
|
|
|
820
820
|
// src/core/download.ts
|
|
821
|
+
import { execFile } from "child_process";
|
|
821
822
|
import fs2 from "fs";
|
|
822
823
|
import path2 from "path";
|
|
823
824
|
import { pipeline } from "stream/promises";
|
|
825
|
+
import { promisify } from "util";
|
|
824
826
|
import { Readable } from "stream";
|
|
827
|
+
var execFileAsync = promisify(execFile);
|
|
828
|
+
var PREVIEW_SOURCE_RE = /\.(png|jpe?g|webp|tiff?)$/i;
|
|
829
|
+
var PREVIEW_SKIP_BYTES = 500 * 1024;
|
|
830
|
+
var PREVIEW_MAX_DIM = 1536;
|
|
831
|
+
var PREVIEW_JPEG_QUALITY = 85;
|
|
832
|
+
var PREVIEW_CDN_WIDTH = 1080;
|
|
833
|
+
var PREVIEW_EXTS = ["jpg", "webp", "png"];
|
|
834
|
+
function previewPathFor(originalPath, ext = "jpg") {
|
|
835
|
+
return path2.join(
|
|
836
|
+
path2.dirname(originalPath),
|
|
837
|
+
"previews",
|
|
838
|
+
`${path2.basename(originalPath)}.${ext}`
|
|
839
|
+
);
|
|
840
|
+
}
|
|
841
|
+
function invalidatePreviews(originalPath) {
|
|
842
|
+
for (const ext of PREVIEW_EXTS) {
|
|
843
|
+
try {
|
|
844
|
+
fs2.rmSync(previewPathFor(originalPath, ext), { force: true });
|
|
845
|
+
} catch {
|
|
846
|
+
}
|
|
847
|
+
}
|
|
848
|
+
}
|
|
849
|
+
function shouldWritePreview(originalPath, bytes) {
|
|
850
|
+
return PREVIEW_SOURCE_RE.test(originalPath) && bytes >= PREVIEW_SKIP_BYTES;
|
|
851
|
+
}
|
|
852
|
+
function tempPreviewPath(previewPath) {
|
|
853
|
+
return `${previewPath}.tmp-${process.pid}`;
|
|
854
|
+
}
|
|
855
|
+
function commitPreview(tmpPath, previewPath) {
|
|
856
|
+
try {
|
|
857
|
+
if (fs2.existsSync(tmpPath) && fs2.statSync(tmpPath).size > 0) {
|
|
858
|
+
fs2.renameSync(tmpPath, previewPath);
|
|
859
|
+
return true;
|
|
860
|
+
}
|
|
861
|
+
} catch {
|
|
862
|
+
}
|
|
863
|
+
fs2.rmSync(tmpPath, { force: true });
|
|
864
|
+
return false;
|
|
865
|
+
}
|
|
866
|
+
async function writePreviewWithSips(originalPath, previewPath) {
|
|
867
|
+
if (process.platform !== "darwin") return false;
|
|
868
|
+
const tmp = tempPreviewPath(previewPath);
|
|
869
|
+
try {
|
|
870
|
+
await execFileAsync(
|
|
871
|
+
"sips",
|
|
872
|
+
[
|
|
873
|
+
"-Z",
|
|
874
|
+
String(PREVIEW_MAX_DIM),
|
|
875
|
+
"-s",
|
|
876
|
+
"format",
|
|
877
|
+
"jpeg",
|
|
878
|
+
"-s",
|
|
879
|
+
"formatOptions",
|
|
880
|
+
String(PREVIEW_JPEG_QUALITY),
|
|
881
|
+
originalPath,
|
|
882
|
+
"--out",
|
|
883
|
+
tmp
|
|
884
|
+
],
|
|
885
|
+
{ timeout: 1e4 }
|
|
886
|
+
);
|
|
887
|
+
} catch {
|
|
888
|
+
fs2.rmSync(tmp, { force: true });
|
|
889
|
+
return false;
|
|
890
|
+
}
|
|
891
|
+
return commitPreview(tmp, previewPath);
|
|
892
|
+
}
|
|
893
|
+
var cdnPreviewConsecutiveFailures = 0;
|
|
894
|
+
var PREVIEW_CDN_MAX_CONSECUTIVE_FAILURES = 2;
|
|
895
|
+
var PREVIEW_CDN_TIMEOUT_MS = 1e4;
|
|
896
|
+
async function writePreviewFromCdn(sourceUrl, originalPath, fetchImpl) {
|
|
897
|
+
let host;
|
|
898
|
+
try {
|
|
899
|
+
host = new URL(sourceUrl).hostname;
|
|
900
|
+
} catch {
|
|
901
|
+
return void 0;
|
|
902
|
+
}
|
|
903
|
+
if (host !== "cdn.videodraft.ai") return void 0;
|
|
904
|
+
if (cdnPreviewConsecutiveFailures >= PREVIEW_CDN_MAX_CONSECUTIVE_FAILURES) return void 0;
|
|
905
|
+
const variant = `${DEFAULT_BASE_URL}/_next/image?url=${encodeURIComponent(sourceUrl)}&w=${PREVIEW_CDN_WIDTH}&q=75`;
|
|
906
|
+
let res;
|
|
907
|
+
try {
|
|
908
|
+
res = await fetchImpl(variant, {
|
|
909
|
+
headers: { Accept: "image/webp,image/jpeg,image/*" },
|
|
910
|
+
signal: AbortSignal.timeout(PREVIEW_CDN_TIMEOUT_MS)
|
|
911
|
+
});
|
|
912
|
+
} catch (err) {
|
|
913
|
+
cdnPreviewConsecutiveFailures++;
|
|
914
|
+
throw err;
|
|
915
|
+
}
|
|
916
|
+
if (!res.ok || !res.body) {
|
|
917
|
+
cdnPreviewConsecutiveFailures++;
|
|
918
|
+
return void 0;
|
|
919
|
+
}
|
|
920
|
+
cdnPreviewConsecutiveFailures = 0;
|
|
921
|
+
const contentType = res.headers.get("content-type") ?? "";
|
|
922
|
+
const ext = contentType.includes("webp") ? "webp" : contentType.includes("jpeg") || contentType.includes("jpg") ? "jpg" : contentType.includes("png") ? "png" : "webp";
|
|
923
|
+
const previewPath = previewPathFor(originalPath, ext);
|
|
924
|
+
const tmp = tempPreviewPath(previewPath);
|
|
925
|
+
try {
|
|
926
|
+
await pipeline(Readable.fromWeb(res.body), fs2.createWriteStream(tmp));
|
|
927
|
+
} catch {
|
|
928
|
+
fs2.rmSync(tmp, { force: true });
|
|
929
|
+
return void 0;
|
|
930
|
+
}
|
|
931
|
+
return commitPreview(tmp, previewPath) ? previewPath : void 0;
|
|
932
|
+
}
|
|
933
|
+
async function writePreview(originalPath, sourceUrl, bytes, fetchImpl = fetch) {
|
|
934
|
+
if (!PREVIEW_SOURCE_RE.test(originalPath)) return void 0;
|
|
935
|
+
invalidatePreviews(originalPath);
|
|
936
|
+
if (!shouldWritePreview(originalPath, bytes)) return void 0;
|
|
937
|
+
try {
|
|
938
|
+
const jpgPath = previewPathFor(originalPath, "jpg");
|
|
939
|
+
fs2.mkdirSync(path2.dirname(jpgPath), { recursive: true });
|
|
940
|
+
if (await writePreviewWithSips(originalPath, jpgPath).catch(() => false)) {
|
|
941
|
+
return jpgPath;
|
|
942
|
+
}
|
|
943
|
+
const cdnPath = await writePreviewFromCdn(sourceUrl, originalPath, fetchImpl).catch(
|
|
944
|
+
() => void 0
|
|
945
|
+
);
|
|
946
|
+
if (cdnPath) return cdnPath;
|
|
947
|
+
} catch {
|
|
948
|
+
}
|
|
949
|
+
return void 0;
|
|
950
|
+
}
|
|
825
951
|
function extFromUrl(url, fallback = "bin") {
|
|
826
952
|
try {
|
|
827
953
|
const pathname = new URL(url).pathname;
|
|
@@ -866,7 +992,10 @@ async function downloadOutputs(urls, template, vars, fetchImpl = fetch) {
|
|
|
866
992
|
index: i,
|
|
867
993
|
ext: extFromUrl(url)
|
|
868
994
|
});
|
|
869
|
-
|
|
995
|
+
const file = await downloadUrl(url, dest, fetchImpl);
|
|
996
|
+
const preview = await writePreview(file.path, url, file.bytes, fetchImpl);
|
|
997
|
+
if (preview) file.preview = preview;
|
|
998
|
+
files.push(file);
|
|
870
999
|
}
|
|
871
1000
|
return files;
|
|
872
1001
|
}
|