byteplan-cli 1.0.2 → 1.2.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/package.json +7 -3
- package/skills/byteplan-analysis/SKILL.md +1078 -0
- package/skills/byteplan-api/API_REFERENCE.md +249 -0
- package/skills/byteplan-api/SKILL.md +96 -0
- package/skills/byteplan-api/package.json +16 -0
- package/skills/byteplan-api/scripts/api.js +973 -0
- package/skills/byteplan-excel/SKILL.md +212 -0
- package/skills/byteplan-excel/examples/margin-analysis.json +40 -0
- package/skills/byteplan-excel/package.json +12 -0
- package/skills/byteplan-excel/pnpm-lock.yaml +68 -0
- package/skills/byteplan-excel/scripts/generate_excel.js +156 -0
- package/skills/byteplan-html/SKILL.md +490 -0
- package/skills/byteplan-html/examples/example-output.html +184 -0
- package/skills/byteplan-html/examples/generate-ppt-style-html.js +611 -0
- package/skills/byteplan-html/examples/margin-contribution-analysis.json +152 -0
- package/skills/byteplan-html/package.json +18 -0
- package/skills/byteplan-html/scripts/generate_html.js +517 -0
- package/skills/byteplan-ppt/SKILL.md +394 -0
- package/skills/byteplan-ppt/examples/margin-contribution-analysis.json +152 -0
- package/skills/byteplan-ppt/package.json +16 -0
- package/skills/byteplan-ppt/pnpm-lock.yaml +138 -0
- package/skills/byteplan-ppt/scripts/check_ppt_overlap.js +318 -0
- package/skills/byteplan-ppt/scripts/generate_ppt.js +680 -0
- package/skills/byteplan-video/SKILL.md +606 -0
- package/skills/byteplan-video/examples/sample-video-data.json +82 -0
- package/skills/byteplan-video/remotion-project/package.json +22 -0
- package/skills/byteplan-video/remotion-project/pnpm-lock.yaml +1646 -0
- package/skills/byteplan-video/remotion-project/remotion.config.ts +6 -0
- package/skills/byteplan-video/remotion-project/scene_durations.json +32 -0
- package/skills/byteplan-video/remotion-project/scripts/generate_audio.js +279 -0
- package/skills/byteplan-video/remotion-project/src/DynamicReport.tsx +172 -0
- package/skills/byteplan-video/remotion-project/src/Root.tsx +51 -0
- package/skills/byteplan-video/remotion-project/src/SalesReport.tsx +107 -0
- package/skills/byteplan-video/remotion-project/src/index.tsx +4 -0
- package/skills/byteplan-video/remotion-project/src/scenes/ChartSlide.tsx +201 -0
- package/skills/byteplan-video/remotion-project/src/scenes/CoverSlide.tsx +61 -0
- package/skills/byteplan-video/remotion-project/src/scenes/EndSlide.tsx +60 -0
- package/skills/byteplan-video/remotion-project/src/scenes/InsightSlide.tsx +101 -0
- package/skills/byteplan-video/remotion-project/src/scenes/KpiSlide.tsx +84 -0
- package/skills/byteplan-video/remotion-project/src/scenes/RecommendationSlide.tsx +100 -0
- package/skills/byteplan-video/remotion-project/tsconfig.json +13 -0
- package/skills/byteplan-video/remotion-project/video_data.json +76 -0
- package/skills/byteplan-video/scripts/generate_video.js +270 -0
- package/skills/byteplan-video/templates/package.json +31 -0
- package/skills/byteplan-video/templates/pnpm-lock.yaml +2200 -0
- package/skills/byteplan-video/templates/remotion.config.ts +9 -0
- package/skills/byteplan-video/templates/scripts/generate-audio.ts +55 -0
- package/skills/byteplan-video/templates/src/components/BarChartScene.tsx +153 -0
- package/skills/byteplan-video/templates/src/components/InsightScene.tsx +135 -0
- package/skills/byteplan-video/templates/src/components/LineChartScene.tsx +214 -0
- package/skills/byteplan-video/templates/src/components/SceneFactory.tsx +34 -0
- package/skills/byteplan-video/templates/src/components/SummaryScene.tsx +155 -0
- package/skills/byteplan-video/templates/src/components/TitleScene.tsx +130 -0
- package/skills/byteplan-video/templates/src/compositions/AnalysisVideo.tsx +39 -0
- package/skills/byteplan-video/templates/src/index.tsx +28 -0
- package/skills/byteplan-video/templates/src/register-root.tsx +4 -0
- package/skills/byteplan-video/templates/src/storyboard/types.ts +46 -0
- package/skills/byteplan-video/templates/tsconfig.json +17 -0
- package/skills/byteplan-video/templates/tsconfig.scripts.json +13 -0
- package/skills/byteplan-word/SKILL.md +233 -0
- package/skills/byteplan-word/package.json +12 -0
- package/skills/byteplan-word/pnpm-lock.yaml +120 -0
- package/skills/byteplan-word/scripts/generate_word.js +548 -0
- package/src/api.js +78 -22
- package/src/cli.js +11 -0
- package/src/commands/skills.js +279 -0
|
@@ -0,0 +1,270 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* BytePlan 视频生成脚本
|
|
5
|
+
* 根据数据文件生成数据可视化视频
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { Command } from 'commander';
|
|
9
|
+
import { readFileSync, writeFileSync, existsSync, mkdirSync } from 'fs';
|
|
10
|
+
import { execSync } from 'child_process';
|
|
11
|
+
import { resolve, dirname } from 'path';
|
|
12
|
+
import { fileURLToPath } from 'url';
|
|
13
|
+
|
|
14
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
15
|
+
|
|
16
|
+
const program = new Command();
|
|
17
|
+
|
|
18
|
+
program
|
|
19
|
+
.name('generate-video')
|
|
20
|
+
.description('生成 BytePlan 数据可视化视频')
|
|
21
|
+
.option('-o, --output <file>', '输出文件名', 'report.mp4')
|
|
22
|
+
.option('-d, --data <file>', '数据文件路径', 'video_data.json')
|
|
23
|
+
.option('--width <number>', '视频宽度', '1920')
|
|
24
|
+
.option('--height <number>', '视频高度', '1080')
|
|
25
|
+
.option('--fps <number>', '帧率', '30')
|
|
26
|
+
.option('--skip-audio', '跳过语音生成', false)
|
|
27
|
+
.parse(process.argv);
|
|
28
|
+
|
|
29
|
+
const options = program.opts();
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* 检查依赖是否安装
|
|
33
|
+
*/
|
|
34
|
+
function checkDependencies() {
|
|
35
|
+
console.log('🔍 检查依赖...');
|
|
36
|
+
|
|
37
|
+
// 检查 edge-tts
|
|
38
|
+
try {
|
|
39
|
+
execSync('which edge-tts', { stdio: 'ignore' });
|
|
40
|
+
console.log(' ✅ edge-tts 已安装');
|
|
41
|
+
} catch {
|
|
42
|
+
console.log(' ⚠️ edge-tts 未安装,语音功能将不可用');
|
|
43
|
+
console.log(' 安装命令: npm install -g edge-tts');
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// 检查 node_modules
|
|
47
|
+
if (!existsSync(resolve(__dirname, '../node_modules'))) {
|
|
48
|
+
console.log(' 📦 正在安装依赖...');
|
|
49
|
+
execSync('pnpm install', { cwd: resolve(__dirname, '..'), stdio: 'inherit' });
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* 加载数据文件
|
|
55
|
+
*/
|
|
56
|
+
function loadData(dataPath) {
|
|
57
|
+
const fullPath = resolve(dataPath);
|
|
58
|
+
|
|
59
|
+
if (!existsSync(fullPath)) {
|
|
60
|
+
console.error(`❌ 数据文件不存在: ${fullPath}`);
|
|
61
|
+
console.log('\n请确保数据文件存在,或使用 -d 参数指定正确的路径。');
|
|
62
|
+
process.exit(1);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
console.log(`📄 加载数据文件: ${fullPath}`);
|
|
66
|
+
const content = readFileSync(fullPath, 'utf-8');
|
|
67
|
+
return JSON.parse(content);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* 验证数据格式
|
|
72
|
+
*/
|
|
73
|
+
function validateData(data) {
|
|
74
|
+
const required = ['title', 'scenes'];
|
|
75
|
+
const missing = required.filter(key => !data[key]);
|
|
76
|
+
|
|
77
|
+
if (missing.length > 0) {
|
|
78
|
+
console.error(`❌ 数据格式错误,缺少必需字段: ${missing.join(', ')}`);
|
|
79
|
+
process.exit(1);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
if (!Array.isArray(data.scenes) || data.scenes.length === 0) {
|
|
83
|
+
console.error('❌ 数据格式错误: scenes 必须是非空数组');
|
|
84
|
+
process.exit(1);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
console.log(` ✅ 数据验证通过: ${data.scenes.length} 个场景`);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* 生成场景配置文件
|
|
92
|
+
*/
|
|
93
|
+
function generateSceneConfig(data) {
|
|
94
|
+
const configPath = resolve(__dirname, '../src/data/scenes.json');
|
|
95
|
+
|
|
96
|
+
// 确保目录存在
|
|
97
|
+
mkdirSync(dirname(configPath), { recursive: true });
|
|
98
|
+
|
|
99
|
+
// 计算总时长
|
|
100
|
+
const totalDuration = data.scenes.reduce((sum, scene) => sum + (scene.duration || 10), 0);
|
|
101
|
+
|
|
102
|
+
const config = {
|
|
103
|
+
title: data.title,
|
|
104
|
+
subtitle: data.subtitle || '',
|
|
105
|
+
period: data.period || '',
|
|
106
|
+
source: data.source || 'BytePlan 数据平台',
|
|
107
|
+
scenes: data.scenes,
|
|
108
|
+
totalDuration,
|
|
109
|
+
totalFrames: totalDuration * parseInt(options.fps)
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
writeFileSync(configPath, JSON.stringify(config, null, 2));
|
|
113
|
+
console.log(` ✅ 场景配置已生成: ${configPath}`);
|
|
114
|
+
|
|
115
|
+
return config;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* 生成图表数据文件
|
|
120
|
+
*/
|
|
121
|
+
function generateChartData(data) {
|
|
122
|
+
const chartPath = resolve(__dirname, '../src/data/chartData.json');
|
|
123
|
+
|
|
124
|
+
const chartData = {
|
|
125
|
+
title: data.title,
|
|
126
|
+
scenes: data.scenes.map(scene => {
|
|
127
|
+
if (scene.data) {
|
|
128
|
+
return {
|
|
129
|
+
type: scene.type,
|
|
130
|
+
title: scene.title,
|
|
131
|
+
data: scene.data
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
return null;
|
|
135
|
+
}).filter(Boolean)
|
|
136
|
+
};
|
|
137
|
+
|
|
138
|
+
writeFileSync(chartPath, JSON.stringify(chartData, null, 2));
|
|
139
|
+
console.log(` ✅ 图表数据已生成: ${chartPath}`);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* 生成语音文件
|
|
144
|
+
*/
|
|
145
|
+
async function generateAudio(data) {
|
|
146
|
+
if (options.skipAudio) {
|
|
147
|
+
console.log(' ⏭️ 跳过语音生成');
|
|
148
|
+
return data;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// 检查 edge-tts 是否安装
|
|
152
|
+
try {
|
|
153
|
+
execSync('which edge-tts', { stdio: 'ignore' });
|
|
154
|
+
} catch {
|
|
155
|
+
console.log(' ⚠️ edge-tts 未安装,跳过语音生成');
|
|
156
|
+
console.log(' 安装命令: npm install -g edge-tts');
|
|
157
|
+
return data;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
const audioDir = resolve(__dirname, '../templates/public/audio');
|
|
161
|
+
mkdirSync(audioDir, { recursive: true });
|
|
162
|
+
|
|
163
|
+
console.log('🎙️ 生成语音文件...');
|
|
164
|
+
|
|
165
|
+
const voice = 'zh-CN-XiaoxiaoNeural';
|
|
166
|
+
let totalAudioFiles = 0;
|
|
167
|
+
|
|
168
|
+
for (let i = 0; i < data.scenes.length; i++) {
|
|
169
|
+
const scene = data.scenes[i];
|
|
170
|
+
if (scene.narration) {
|
|
171
|
+
const audioFile = resolve(audioDir, `scene_${i}.mp3`);
|
|
172
|
+
|
|
173
|
+
try {
|
|
174
|
+
// 生成语音
|
|
175
|
+
execSync(
|
|
176
|
+
`edge-tts --voice ${voice} --text "${scene.narration.replace(/"/g, '\\"')}" --write-media "${audioFile}"`,
|
|
177
|
+
{ stdio: 'pipe' }
|
|
178
|
+
);
|
|
179
|
+
|
|
180
|
+
// 获取音频时长
|
|
181
|
+
const durationOutput = execSync(
|
|
182
|
+
`ffprobe -i "${audioFile}" -show_entries format=duration -v quiet -of csv="p=0"`,
|
|
183
|
+
{ encoding: 'utf-8' }
|
|
184
|
+
);
|
|
185
|
+
const audioDuration = parseFloat(durationOutput.trim());
|
|
186
|
+
|
|
187
|
+
// 更新场景配置
|
|
188
|
+
scene.audioPath = `/audio/scene_${i}.mp3`;
|
|
189
|
+
scene.audioDuration = Math.ceil(audioDuration);
|
|
190
|
+
scene.duration = scene.audioDuration;
|
|
191
|
+
|
|
192
|
+
console.log(` ✅ 场景 ${i + 1}: "${scene.narration.substring(0, 20)}..." (${audioDuration.toFixed(1)}s)`);
|
|
193
|
+
totalAudioFiles++;
|
|
194
|
+
} catch (err) {
|
|
195
|
+
console.log(` ⚠️ 场景 ${i + 1} 语音生成失败`);
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
console.log(` ✅ 共生成 ${totalAudioFiles} 个语音文件`);
|
|
201
|
+
return data;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
/**
|
|
205
|
+
* 渲染视频
|
|
206
|
+
*/
|
|
207
|
+
function renderVideo(outputPath) {
|
|
208
|
+
console.log('🎬 渲染视频中...');
|
|
209
|
+
console.log(` 输出: ${outputPath}`);
|
|
210
|
+
console.log(` 分辨率: ${options.width}x${options.height}`);
|
|
211
|
+
console.log(` 帧率: ${options.fps} fps`);
|
|
212
|
+
|
|
213
|
+
const renderCmd = [
|
|
214
|
+
'remotion render AnalysisVideo',
|
|
215
|
+
resolve(outputPath),
|
|
216
|
+
`--codec h264`,
|
|
217
|
+
`--concurrency 1`,
|
|
218
|
+
`--width ${options.width}`,
|
|
219
|
+
`--height ${options.height}`,
|
|
220
|
+
`--fps ${options.fps}`
|
|
221
|
+
].join(' ');
|
|
222
|
+
|
|
223
|
+
try {
|
|
224
|
+
execSync(renderCmd, {
|
|
225
|
+
cwd: resolve(__dirname, '..'),
|
|
226
|
+
stdio: 'inherit'
|
|
227
|
+
});
|
|
228
|
+
console.log('✅ 视频渲染完成!');
|
|
229
|
+
} catch (err) {
|
|
230
|
+
console.error('❌ 视频渲染失败');
|
|
231
|
+
console.error(err.message);
|
|
232
|
+
process.exit(1);
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
/**
|
|
237
|
+
* 主函数
|
|
238
|
+
*/
|
|
239
|
+
async function main() {
|
|
240
|
+
console.log('\n🎥 BytePlan 视频生成器\n');
|
|
241
|
+
|
|
242
|
+
// 1. 检查依赖
|
|
243
|
+
checkDependencies();
|
|
244
|
+
|
|
245
|
+
// 2. 加载数据
|
|
246
|
+
let data = loadData(options.data);
|
|
247
|
+
|
|
248
|
+
// 3. 验证数据
|
|
249
|
+
validateData(data);
|
|
250
|
+
|
|
251
|
+
// 4. 生成语音(会更新 data.scenes 中的 audioDuration)
|
|
252
|
+
console.log('\n');
|
|
253
|
+
data = await generateAudio(data);
|
|
254
|
+
|
|
255
|
+
// 5. 生成配置文件
|
|
256
|
+
console.log('\n📝 生成配置文件...');
|
|
257
|
+
generateSceneConfig(data);
|
|
258
|
+
generateChartData(data);
|
|
259
|
+
|
|
260
|
+
// 6. 渲染视频
|
|
261
|
+
console.log('\n');
|
|
262
|
+
renderVideo(options.output);
|
|
263
|
+
|
|
264
|
+
console.log(`\n📁 输出文件: ${resolve(options.output)}\n`);
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
main().catch(err => {
|
|
268
|
+
console.error('❌ 发生错误:', err.message);
|
|
269
|
+
process.exit(1);
|
|
270
|
+
});
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "byteplan-video",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "BytePlan 数据可视化视频生成器",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"start": "remotion studio",
|
|
8
|
+
"render": "remotion render AnalysisVideo out/video.mp4 --codec h264 --concurrency 1",
|
|
9
|
+
"generate": "node scripts/generate_video.js",
|
|
10
|
+
"generate:audio": "ts-node --project tsconfig.scripts.json scripts/generate-audio.ts"
|
|
11
|
+
},
|
|
12
|
+
"dependencies": {
|
|
13
|
+
"@remotion/cli": "4.0.429",
|
|
14
|
+
"@remotion/player": "4.0.429",
|
|
15
|
+
"commander": "^12.0.0",
|
|
16
|
+
"dotenv": "^16.3.1",
|
|
17
|
+
"edge-tts": "^1.0.1",
|
|
18
|
+
"react": "^18.2.0",
|
|
19
|
+
"react-dom": "^18.2.0",
|
|
20
|
+
"remotion": "4.0.429"
|
|
21
|
+
},
|
|
22
|
+
"devDependencies": {
|
|
23
|
+
"@types/node": "^20.10.0",
|
|
24
|
+
"@types/react": "^18.2.0",
|
|
25
|
+
"@types/react-dom": "^18.2.0",
|
|
26
|
+
"get-audio-duration": "^4.0.0",
|
|
27
|
+
"react-refresh": "^0.18.0",
|
|
28
|
+
"ts-node": "^10.9.2",
|
|
29
|
+
"typescript": "^5.3.0"
|
|
30
|
+
}
|
|
31
|
+
}
|