capcut-pro-free
软件下载地址
字幕工具
提示词
https://jy2.mzh.ren/index_61f7cabff92c9fafcd77.js 分析下这段代码的作用
你资深的产品经理,通过提问帮助我理解需求,我需要一个在我电脑上可以使用的版本,我的电脑上有安装nodejs,有什么需要我澄清的请提问?
字幕导出JS代码
const fs = require('fs');
const path = require('path');
const os = require('os');
/**
* 过滤字幕文本中的 XML 样式标签(如 <color>, <size> 等)
*/
function cleanText(text) {
if (!text) return '';
// 正则匹配并移除所有的 XML/HTML 标签
return text.replace(/<\/?[^>]+(>|$)/g, "").trim();
}
/**
* 将微秒(microseconds)转换为 SRT 格式的时间戳 (HH:MM:SS,mmm)
*/
function formatTime(us) {
const msTotal = Math.floor(us / 1000);
const ms = msTotal % 1000;
const sTotal = Math.floor(msTotal / 1000);
const s = sTotal % 60;
const mTotal = Math.floor(sTotal / 60);
const m = mTotal % 60;
const h = Math.floor(mTotal / 60);
const pad = (num, size) => num.toString().padStart(size, '0');
return `${pad(h, 2)}:${pad(m, 2)}:${pad(s, 2)},${pad(ms, 3)}`;
}
/**
* 寻找 Windows 下载文件夹中不冲突的固定文件名
*/
function getUniqueOutputPath(downloadsDir, baseName, ext) {
let candidate = path.join(downloadsDir, `${baseName}${ext}`);
if (!fs.existsSync(candidate)) {
return candidate;
}
let counter = 1;
while (true) {
candidate = path.join(downloadsDir, `${baseName} (${counter})${ext}`);
if (!fs.existsSync(candidate)) {
return candidate;
}
counter++;
}
}
function main() {
// 1. 校验输入参数
const inputPathRaw = process.argv[2];
if (!inputPathRaw) {
console.error('\x1b[31m[错误] 请提供 draft_content.json 的路径。\x1b[0m');
console.log('\n使用方法:');
console.log(' node export.js <文件路径>');
console.log('\n示例:');
console.log(' node export.js "C:\\Users\\YourName\\AppData\\Local\\JianyingPro\\User Data\\Projects\\com.lveditor.draft\\Draft_1\\draft_content.json"\n');
process.exit(1);
}
// 去除 Windows 路径可能携带的双引号
const inputPath = inputPathRaw.replace(/^"|"$/g, '');
if (!fs.existsSync(inputPath)) {
console.error(`\x1b[31m[错误] 找不到指定的文件,请检查路径是否正确:\n${inputPath}\x1b[0m`);
process.exit(1);
}
console.log('正在读取并解析文件...');
try {
const fileContent = fs.readFileSync(inputPath, 'utf-8');
const jsonData = JSON.parse(fileContent);
// 2. 建立 material_id 到字幕文本的映射表
const contentMap = new Map();
if (jsonData.materials && Array.isArray(jsonData.materials.texts)) {
jsonData.materials.texts.forEach(item => {
let textVal = '';
try {
// 剪映新版 content 是一个 JSON 字符串,需要二次解析
const parsed = JSON.parse(item.content);
textVal = parsed.text || '';
} catch (e) {
// 如果解析失败,则按普通文本处理
textVal = item.content || '';
}
contentMap.set(item.id, cleanText(textVal));
});
}
// 3. 提取所有字幕片段(从文字轨道中匹配时间轴与内容)
const srtSegments = [];
if (Array.isArray(jsonData.tracks)) {
jsonData.tracks.forEach(track => {
// 筛选文本/字幕轨道
if (track.type === 'text' || track.type === 'subtitle') {
if (Array.isArray(track.segments)) {
track.segments.forEach(seg => {
const text = contentMap.get(seg.material_id);
if (text && seg.target_timerange) {
const start = parseInt(seg.target_timerange.start, 10) || 0;
const duration = parseInt(seg.target_timerange.duration, 10) || 0;
const end = start + duration;
srtSegments.push({ start, end, text });
}
});
}
}
});
}
if (srtSegments.length === 0) {
console.warn('\x1b[33m[警告] 未在草稿文件中找到任何有效的字幕内容。\x1b[0m');
process.exit(0);
}
// 4. 按开始时间进行升序排序
srtSegments.sort((a, b) => a.start - b.start);
// 5. 格式化为标准 SRT 字幕格式
let srtContent = '';
srtSegments.forEach((seg, index) => {
const num = index + 1;
const startStr = formatTime(seg.start);
const endStr = formatTime(seg.end);
srtContent += `${num}\n${startStr} --> ${endStr}\n${seg.text}\n\n`;
});
// 6. 写入“下载”目录
const homeDir = os.homedir();
const downloadsDir = path.join(homeDir, 'Downloads');
// 确保下载目录存在
if (!fs.existsSync(downloadsDir)) {
fs.mkdirSync(downloadsDir, { recursive: true });
}
const finalPath = getUniqueOutputPath(downloadsDir, 'subtitles', '.srt');
fs.writeFileSync(finalPath, srtContent, 'utf-8');
console.log(`\n\x1b[32m[成功] 已成功提取 ${srtSegments.length} 条字幕!\x1b[0m`);
console.log(`\x1b[36m[输出] 文件已保存至:${finalPath}\x1b[0m\n`);
} catch (error) {
console.error('\x1b[31m[发生错误] 解析文件时失败,错误信息如下:\x1b[0m');
console.error(error);
process.exit(1);
}
}
main();