autocrew 0.3.9 → 0.4.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/package.json
CHANGED
|
@@ -100,6 +100,7 @@ import { cmd as trashCmd } from "./trash.js";
|
|
|
100
100
|
import { cmd as restoreCmd } from "./restore.js";
|
|
101
101
|
import { cmd as migrateCmd } from "./migrate.js";
|
|
102
102
|
import { cmd as draftCmd } from "./draft.js";
|
|
103
|
+
import { cmd as renderCmd } from "./render.js";
|
|
103
104
|
|
|
104
105
|
export const commands: CommandDef[] = [
|
|
105
106
|
statusCmd,
|
|
@@ -130,4 +131,5 @@ export const commands: CommandDef[] = [
|
|
|
130
131
|
restoreCmd,
|
|
131
132
|
migrateCmd,
|
|
132
133
|
draftCmd,
|
|
134
|
+
renderCmd,
|
|
133
135
|
];
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import type { CommandDef } from "./index.js";
|
|
2
|
+
import { getOption } from "./index.js";
|
|
3
|
+
|
|
4
|
+
export const cmd: CommandDef = {
|
|
5
|
+
name: "render",
|
|
6
|
+
description: "Render a video timeline (TTS + screenshots + Jianying export)",
|
|
7
|
+
usage: "autocrew render <project-slug> [--voice <voiceId>] [--ratio <9:16|16:9>]",
|
|
8
|
+
action: async (args, runner) => {
|
|
9
|
+
const slug = args[0];
|
|
10
|
+
if (!slug) {
|
|
11
|
+
console.error("Usage: autocrew render <project-slug> [--voice <voiceId>] [--ratio <9:16|16:9>]");
|
|
12
|
+
process.exitCode = 1;
|
|
13
|
+
return;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const voice = getOption(args, "--voice") || "default";
|
|
17
|
+
const ratio = getOption(args, "--ratio") || "9:16";
|
|
18
|
+
|
|
19
|
+
// Find project and its timeline
|
|
20
|
+
try {
|
|
21
|
+
const { findProject } = await import("../../storage/pipeline-store.js");
|
|
22
|
+
const path = await import("node:path");
|
|
23
|
+
const fs = await import("node:fs/promises");
|
|
24
|
+
|
|
25
|
+
const found = await findProject(slug);
|
|
26
|
+
if (!found) {
|
|
27
|
+
console.error(`Project not found: "${slug}"`);
|
|
28
|
+
process.exitCode = 1;
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const timelinePath = path.join(found.dir, "timeline.json");
|
|
33
|
+
let timeline;
|
|
34
|
+
try {
|
|
35
|
+
const raw = await fs.readFile(timelinePath, "utf-8");
|
|
36
|
+
timeline = JSON.parse(raw);
|
|
37
|
+
} catch {
|
|
38
|
+
console.error(`No timeline.json found in project "${slug}".`);
|
|
39
|
+
console.error("Generate a timeline first with autocrew_timeline action='generate'.");
|
|
40
|
+
process.exitCode = 1;
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// Try to load studio
|
|
45
|
+
let renderTimeline: any;
|
|
46
|
+
let loadConfig: any;
|
|
47
|
+
try {
|
|
48
|
+
const studio = await import("autocrew-studio");
|
|
49
|
+
renderTimeline = studio.renderTimeline;
|
|
50
|
+
loadConfig = studio.loadConfig;
|
|
51
|
+
} catch {
|
|
52
|
+
console.error("autocrew-studio is not installed. Install it with:");
|
|
53
|
+
console.error(" npm install autocrew-studio");
|
|
54
|
+
process.exitCode = 1;
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const config = loadConfig();
|
|
59
|
+
const outputDir = path.join(found.dir, "render");
|
|
60
|
+
|
|
61
|
+
console.log(`Rendering timeline for "${slug}" (${ratio}, voice: ${voice})...`);
|
|
62
|
+
|
|
63
|
+
const result = await renderTimeline({
|
|
64
|
+
timeline,
|
|
65
|
+
outputDir,
|
|
66
|
+
tts: config.tts,
|
|
67
|
+
screenshot: config.screenshot,
|
|
68
|
+
exporter: config.exporter,
|
|
69
|
+
voice: { voiceId: voice },
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
console.log(`Render complete: ${result.path} (${result.format})`);
|
|
73
|
+
} catch (err: any) {
|
|
74
|
+
console.error(`Render failed: ${err.message || err}`);
|
|
75
|
+
process.exitCode = 1;
|
|
76
|
+
}
|
|
77
|
+
},
|
|
78
|
+
};
|
package/src/tools/review.ts
CHANGED
|
@@ -212,10 +212,14 @@ export async function executeReview(params: Record<string, unknown>) {
|
|
|
212
212
|
const scanResult = await scanText(fullText, platform, dataDir);
|
|
213
213
|
let fixedText = scanResult.autoFixedText || fullText;
|
|
214
214
|
|
|
215
|
-
// 2. Humanizer pass
|
|
215
|
+
// 2. Humanizer pass (substitution + deletion only, never insertion)
|
|
216
216
|
const humanResult = humanizeZh({ text: fixedText });
|
|
217
217
|
fixedText = humanResult.humanizedText;
|
|
218
218
|
|
|
219
|
+
// 3. Safety guard — strip any hardcoded phrases that old humanizer versions
|
|
220
|
+
// may have inserted. This is a belt-and-suspenders defense.
|
|
221
|
+
fixedText = fixedText.replace(/\n*说白了,这件事拼的不是工具数量,而是表达和执行。\n*/g, "\n\n");
|
|
222
|
+
|
|
219
223
|
// Save back if content_id provided
|
|
220
224
|
if (contentId) {
|
|
221
225
|
await updateContent(contentId, { body: fixedText }, dataDir);
|