Initial TrueGrowth source import
This commit is contained in:
269
scripts/build-all-gifs.js
Normal file
269
scripts/build-all-gifs.js
Normal file
@@ -0,0 +1,269 @@
|
||||
#!/usr/bin/env node
|
||||
/**
|
||||
* 一键构建所有用户手册 GIF
|
||||
*
|
||||
* 工作流程:
|
||||
* 1. 加载所有 GIF 定义 (*.gif.json)
|
||||
* 2. 运行 Playwright 录制长视频
|
||||
* 3. 更新清单中的视频路径
|
||||
* 4. 根据清单批量裁剪生成 GIF
|
||||
* 5. 重建用户手册
|
||||
*
|
||||
* 使用方法:
|
||||
* node scripts/build-all-gifs.js # 构建所有 GIF
|
||||
* node scripts/build-all-gifs.js <id> # 只构建指定 GIF
|
||||
* node scripts/build-all-gifs.js --list # 列出所有可用的 GIF 定义
|
||||
*/
|
||||
|
||||
const { execSync, spawnSync } = require('child_process');
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
const GIFS_DIR = 'apps/web-e2e/src/manual-gen/gifs';
|
||||
const MANIFEST_PATH = 'apps/web-e2e/test-results/gif-manifest.json';
|
||||
const TEST_RESULTS_DIR = 'apps/web-e2e/test-results';
|
||||
|
||||
/**
|
||||
* 加载所有 GIF 定义
|
||||
*/
|
||||
function loadGifDefinitions() {
|
||||
if (!fs.existsSync(GIFS_DIR)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const files = fs.readdirSync(GIFS_DIR).filter(f => f.endsWith('.gif.json'));
|
||||
const definitions = [];
|
||||
|
||||
for (const file of files) {
|
||||
try {
|
||||
const content = fs.readFileSync(path.join(GIFS_DIR, file), 'utf-8');
|
||||
definitions.push(JSON.parse(content));
|
||||
} catch (error) {
|
||||
console.error(`❌ 加载失败: ${file}`, error.message);
|
||||
}
|
||||
}
|
||||
|
||||
return definitions.sort((a, b) => a.id.localeCompare(b.id));
|
||||
}
|
||||
|
||||
/**
|
||||
* 查找最新的视频文件
|
||||
*/
|
||||
function findLatestVideo() {
|
||||
if (!fs.existsSync(TEST_RESULTS_DIR)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
let latestVideo = null;
|
||||
let latestTime = 0;
|
||||
|
||||
function searchDir(dir) {
|
||||
const items = fs.readdirSync(dir);
|
||||
for (const item of items) {
|
||||
const fullPath = path.join(dir, item);
|
||||
const stat = fs.statSync(fullPath);
|
||||
|
||||
if (stat.isDirectory()) {
|
||||
searchDir(fullPath);
|
||||
} else if (item.endsWith('.webm')) {
|
||||
if (stat.mtimeMs > latestTime) {
|
||||
latestTime = stat.mtimeMs;
|
||||
latestVideo = fullPath;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
searchDir(TEST_RESULTS_DIR);
|
||||
return latestVideo;
|
||||
}
|
||||
|
||||
/**
|
||||
* 运行命令并显示输出
|
||||
*/
|
||||
function runCommand(command, options = {}) {
|
||||
console.log(`\n$ ${command}\n`);
|
||||
try {
|
||||
execSync(command, { stdio: 'inherit', ...options });
|
||||
return true;
|
||||
} catch (error) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 列出所有 GIF 定义
|
||||
*/
|
||||
function listDefinitions() {
|
||||
const definitions = loadGifDefinitions();
|
||||
|
||||
if (definitions.length === 0) {
|
||||
console.log('⚠️ 没有找到 GIF 定义');
|
||||
console.log(` 请在 ${GIFS_DIR} 目录创建 *.gif.json 文件`);
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
console.log(`📋 可用的 GIF 定义 (共 ${definitions.length} 个):\n`);
|
||||
for (const def of definitions) {
|
||||
console.log(` ${def.id}`);
|
||||
console.log(` 名称: ${def.name}`);
|
||||
console.log(` 输出: ${def.output}`);
|
||||
console.log(` 目标: ${def.targetPage}`);
|
||||
console.log('');
|
||||
}
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建所有 GIF
|
||||
*/
|
||||
async function buildAll() {
|
||||
const definitions = loadGifDefinitions();
|
||||
|
||||
if (definitions.length === 0) {
|
||||
console.log('⚠️ 没有找到 GIF 定义');
|
||||
console.log(` 请在 ${GIFS_DIR} 目录创建 *.gif.json 文件`);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
console.log(`📚 找到 ${definitions.length} 个 GIF 定义\n`);
|
||||
|
||||
// 步骤 1: 运行 Playwright 录制
|
||||
console.log('🎬 步骤 1/4: 录制视频...');
|
||||
const recordSuccess = runCommand(
|
||||
'cd apps/web-e2e && CI= npx playwright test --project=manual-video -g "录制所有 GIF"',
|
||||
{ timeout: 600000 } // 10 分钟超时
|
||||
);
|
||||
|
||||
if (!recordSuccess) {
|
||||
console.error('❌ 视频录制失败');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
// 步骤 2: 查找视频并更新清单
|
||||
console.log('\n📹 步骤 2/4: 更新清单...');
|
||||
const videoPath = findLatestVideo();
|
||||
|
||||
if (!videoPath) {
|
||||
console.error('❌ 未找到录制的视频文件');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
if (fs.existsSync(MANIFEST_PATH)) {
|
||||
const manifest = JSON.parse(fs.readFileSync(MANIFEST_PATH, 'utf-8'));
|
||||
manifest.videoPath = videoPath;
|
||||
fs.writeFileSync(MANIFEST_PATH, JSON.stringify(manifest, null, 2));
|
||||
console.log(` 视频路径: ${videoPath}`);
|
||||
} else {
|
||||
console.error('❌ 未找到清单文件');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
// 步骤 3: 根据清单生成 GIF
|
||||
console.log('\n✂️ 步骤 3/4: 生成 GIF...');
|
||||
const gifSuccess = runCommand(`node scripts/video-to-gif.js --manifest ${MANIFEST_PATH}`);
|
||||
|
||||
if (!gifSuccess) {
|
||||
console.error('❌ GIF 生成失败');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
// 步骤 4: 重建用户手册
|
||||
console.log('\n📖 步骤 4/4: 重建用户手册...');
|
||||
runCommand('pnpm manual:build');
|
||||
|
||||
console.log('\n🎉 所有 GIF 构建完成!');
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建指定 GIF
|
||||
*/
|
||||
async function buildOne(gifId) {
|
||||
const definitions = loadGifDefinitions();
|
||||
const definition = definitions.find(d => d.id === gifId);
|
||||
|
||||
if (!definition) {
|
||||
console.error(`❌ 未找到 GIF 定义: ${gifId}`);
|
||||
console.log('\n可用的 GIF ID:');
|
||||
for (const def of definitions) {
|
||||
console.log(` ${def.id}`);
|
||||
}
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
console.log(`📄 构建 GIF: ${definition.name}\n`);
|
||||
|
||||
// 步骤 1: 运行 Playwright 录制
|
||||
console.log('🎬 步骤 1/4: 录制视频...');
|
||||
const recordSuccess = runCommand(
|
||||
`cd apps/web-e2e && GIF_ID=${gifId} CI= npx playwright test --project=manual-video -g "录制指定 GIF"`,
|
||||
{ timeout: 300000 } // 5 分钟超时
|
||||
);
|
||||
|
||||
if (!recordSuccess) {
|
||||
console.error('❌ 视频录制失败');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
// 步骤 2: 查找视频并更新清单
|
||||
console.log('\n📹 步骤 2/4: 更新清单...');
|
||||
const videoPath = findLatestVideo();
|
||||
|
||||
if (!videoPath) {
|
||||
console.error('❌ 未找到录制的视频文件');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
if (fs.existsSync(MANIFEST_PATH)) {
|
||||
const manifest = JSON.parse(fs.readFileSync(MANIFEST_PATH, 'utf-8'));
|
||||
manifest.videoPath = videoPath;
|
||||
fs.writeFileSync(MANIFEST_PATH, JSON.stringify(manifest, null, 2));
|
||||
console.log(` 视频路径: ${videoPath}`);
|
||||
} else {
|
||||
console.error('❌ 未找到清单文件');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
// 步骤 3: 根据清单生成 GIF
|
||||
console.log('\n✂️ 步骤 3/4: 生成 GIF...');
|
||||
const gifSuccess = runCommand(`node scripts/video-to-gif.js --manifest ${MANIFEST_PATH}`);
|
||||
|
||||
if (!gifSuccess) {
|
||||
console.error('❌ GIF 生成失败');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
// 步骤 4: 重建用户手册
|
||||
console.log('\n📖 步骤 4/4: 重建用户手册...');
|
||||
runCommand('pnpm manual:build');
|
||||
|
||||
console.log(`\n🎉 GIF 构建完成: ${definition.output}`);
|
||||
}
|
||||
|
||||
// 主函数
|
||||
async function main() {
|
||||
console.log('🎬 用户手册 GIF 构建工具\n');
|
||||
|
||||
const args = process.argv.slice(2);
|
||||
|
||||
if (args.includes('--list') || args.includes('-l')) {
|
||||
listDefinitions();
|
||||
return;
|
||||
}
|
||||
|
||||
if (args.includes('--help') || args.includes('-h')) {
|
||||
console.log('使用方法:');
|
||||
console.log(' node scripts/build-all-gifs.js # 构建所有 GIF');
|
||||
console.log(' node scripts/build-all-gifs.js <id> # 只构建指定 GIF');
|
||||
console.log(' node scripts/build-all-gifs.js --list # 列出所有可用的 GIF 定义');
|
||||
return;
|
||||
}
|
||||
|
||||
if (args.length > 0 && !args[0].startsWith('-')) {
|
||||
await buildOne(args[0]);
|
||||
} else {
|
||||
await buildAll();
|
||||
}
|
||||
}
|
||||
|
||||
main().catch(console.error);
|
||||
Reference in New Issue
Block a user