reelforge 1.24.0 → 1.24.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.
@@ -1,6 +1,7 @@
1
1
  import fs from "node:fs/promises";
2
2
  import fsSync from "node:fs";
3
3
  import path from "node:path";
4
+ import { uploadToOss } from "./oss-upload.js";
4
5
  export const EXT_TO_MIME = {
5
6
  ".png": "image/png",
6
7
  ".jpg": "image/jpeg",
@@ -57,3 +58,45 @@ export function fmtSize(bytes) {
57
58
  return `${(bytes / 1024).toFixed(1)} KB`;
58
59
  return `${(bytes / 1024 / 1024).toFixed(1)} MB`;
59
60
  }
61
+ export async function resolveUploadInput(input, opts) {
62
+ const t = input.trim();
63
+ if (!t)
64
+ throw new Error(`${opts.flagName}: empty value`);
65
+ if (/^https?:\/\//i.test(t))
66
+ return { url: t, kind: "http-url" };
67
+ if (t.startsWith("data:"))
68
+ return { url: t, kind: "data-uri" };
69
+ const abs = path.resolve(t);
70
+ if (!fsSync.existsSync(abs)) {
71
+ throw new Error(`${opts.flagName}: local file not found: ${abs}\n` +
72
+ ` (Acceptable forms: local path / https:// URL / data: URI. ` +
73
+ `Server-side paths like 'data/uploads/...' do NOT work — CLI commands ` +
74
+ `auto-upload local files, so just pass the local path directly.)`);
75
+ }
76
+ const ext = path.extname(abs).toLowerCase();
77
+ const map = opts.extToMime ?? EXT_TO_MIME;
78
+ const mime = map[ext];
79
+ if (!mime) {
80
+ throw new Error(`${opts.flagName}: unsupported file extension "${ext}" (path: ${abs}). ` +
81
+ `Supported: ${Object.keys(map).join(" / ")}`);
82
+ }
83
+ const bytes = (await fs.stat(abs)).size;
84
+ try {
85
+ const key = await uploadToOss(abs);
86
+ return { key, bytes, kind: "oss-key" };
87
+ }
88
+ catch (e) {
89
+ const reason = e instanceof Error ? e.message : String(e);
90
+ opts.onFallback?.(opts.flagName, reason);
91
+ const buf = await fs.readFile(abs);
92
+ return { url: `data:${mime};base64,${buf.toString("base64")}`, bytes, kind: "local-file-inline" };
93
+ }
94
+ }
95
+ export async function resolveUploadInputs(inputs, opts) {
96
+ const resolved = [];
97
+ for (const input of inputs) {
98
+ resolved.push(await resolveUploadInput(input, opts));
99
+ }
100
+ const totalBytes = resolved.reduce((n, r) => n + (r.bytes ?? 0), 0);
101
+ return { resolved, totalBytes };
102
+ }
@@ -0,0 +1,43 @@
1
+ import fs from "node:fs/promises";
2
+ import fsSync from "node:fs";
3
+ import path from "node:path";
4
+ import { post } from "../client.js";
5
+ const EXT_TO_MIME = {
6
+ ".mp4": "video/mp4",
7
+ ".mov": "video/quicktime",
8
+ ".webm": "video/webm",
9
+ ".mkv": "video/x-matroska",
10
+ ".m4v": "video/mp4",
11
+ ".jpg": "image/jpeg",
12
+ ".jpeg": "image/jpeg",
13
+ ".png": "image/png",
14
+ ".webp": "image/webp",
15
+ ".mp3": "audio/mpeg",
16
+ ".wav": "audio/wav",
17
+ ".m4a": "audio/mp4",
18
+ ".ogg": "audio/ogg",
19
+ ".pdf": "application/pdf",
20
+ };
21
+ export function mimeForFile(filePath) {
22
+ return EXT_TO_MIME[path.extname(filePath).toLowerCase()] || "application/octet-stream";
23
+ }
24
+ export async function uploadToOss(filePath) {
25
+ if (!fsSync.existsSync(filePath))
26
+ throw new Error(`file not found: ${filePath}`);
27
+ const ext = path.extname(filePath).toLowerCase();
28
+ const contentType = mimeForFile(filePath);
29
+ const signed = await post("/api/v1/uploads/sign", { ext, content_type: contentType });
30
+ if (!signed?.put_url || !signed?.key)
31
+ throw new Error("uploads/sign returned no put_url/key");
32
+ const bytes = await fs.readFile(filePath);
33
+ const res = await fetch(signed.put_url, {
34
+ method: "PUT",
35
+ headers: { "Content-Type": contentType },
36
+ body: bytes,
37
+ });
38
+ if (!res.ok) {
39
+ const detail = await res.text().catch(() => "");
40
+ throw new Error(`OSS PUT failed: ${res.status} ${res.statusText} ${detail.slice(0, 200)}`);
41
+ }
42
+ return signed.key;
43
+ }
package/package.json CHANGED
@@ -1,44 +1,44 @@
1
- {
2
- "name": "reelforge",
3
- "version": "1.24.0",
4
- "description": "AI 视频生成 CLI。一句话主题或自己的脚本 → 自动出抖音/TikTok/视频号竖屏 MP4。安装即用:`reelforge` 或短别名 `rf`。",
5
- "license": "Apache-2.0",
6
- "type": "module",
7
- "bin": {
8
- "reelforge": "./bin/reelforge.js",
9
- "rf": "./bin/reelforge.js"
10
- },
11
- "files": [
12
- "bin",
13
- "dist",
14
- "README.md"
15
- ],
16
- "engines": {
17
- "node": ">=22"
18
- },
19
- "scripts": {
20
- "build": "tsc -p tsconfig.json && node scripts/copy-docs.mjs",
21
- "dev": "tsc -p tsconfig.json --watch",
22
- "typecheck": "tsc -p tsconfig.json --noEmit",
23
- "clean": "rimraf dist",
24
- "prepublishOnly": "npm run clean && npm run build"
25
- },
26
- "dependencies": {
27
- "commander": "^12.1.0",
28
- "kleur": "^4.1.5"
29
- },
30
- "devDependencies": {
31
- "@types/node": "^20.14.0",
32
- "rimraf": "^6.0.1",
33
- "typescript": "^5.5.0"
34
- },
35
- "keywords": [
36
- "reelforge",
37
- "ai-video",
38
- "douyin",
39
- "tiktok",
40
- "shorts",
41
- "vertical-video",
42
- "cli"
43
- ]
44
- }
1
+ {
2
+ "name": "reelforge",
3
+ "version": "1.24.1",
4
+ "description": "AI 视频生成 CLI。一句话主题或自己的脚本 → 自动出抖音/TikTok/视频号竖屏 MP4。安装即用:`reelforge` 或短别名 `rf`。",
5
+ "license": "Apache-2.0",
6
+ "type": "module",
7
+ "bin": {
8
+ "reelforge": "./bin/reelforge.js",
9
+ "rf": "./bin/reelforge.js"
10
+ },
11
+ "files": [
12
+ "bin",
13
+ "dist",
14
+ "README.md"
15
+ ],
16
+ "engines": {
17
+ "node": ">=22"
18
+ },
19
+ "scripts": {
20
+ "build": "tsc -p tsconfig.json && node scripts/copy-docs.mjs",
21
+ "dev": "tsc -p tsconfig.json --watch",
22
+ "typecheck": "tsc -p tsconfig.json --noEmit",
23
+ "clean": "rimraf dist",
24
+ "prepublishOnly": "npm run clean && npm run build"
25
+ },
26
+ "dependencies": {
27
+ "commander": "^12.1.0",
28
+ "kleur": "^4.1.5"
29
+ },
30
+ "devDependencies": {
31
+ "@types/node": "^20.14.0",
32
+ "rimraf": "^6.0.1",
33
+ "typescript": "^5.5.0"
34
+ },
35
+ "keywords": [
36
+ "reelforge",
37
+ "ai-video",
38
+ "douyin",
39
+ "tiktok",
40
+ "shorts",
41
+ "vertical-video",
42
+ "cli"
43
+ ]
44
+ }