Repository navigation
Expand file tree
/
Copy pathindex.js
More file actions
executable file
·89 lines (78 loc) · 3.3 KB
/
Copy pathindex.js
File metadata and controls
executable file
·89 lines (78 loc) · 3.3 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
#!/usr/bin/env node
import fs from 'fs';
import path from 'path';
import readline from 'readline';
import subtitleMerge from "subtitle-merge";
import { config } from './config.js';
import { findSub } from './findSub.js';
import { analyzeMedia } from "./analyzeMedia.js";
import { extractSub } from "./extractSub.js";
import { deleteFile, removeExtension, waitForInterruptWithCountdown } from "./utils.js";
import { t } from './i18n.js';
const main = async () => {
const mediaFiles = fs.readdirSync(config.workdir).filter((file) => config.exts.includes(path.extname(file)));
if (mediaFiles.length === 0) {
console.log(t('noVideoFiles'));
return;
}
const targetFiles = await selectMediaFiles(mediaFiles);
for (const file of targetFiles) {
console.log(t('processingFile', { file }));
const subTitles = await analyzeMedia(file);
const targetSubs = await findSub(subTitles);
const srts = await extractSub(file, targetSubs);
// 使用字幕的 code 生成输出文件名;流里是 chi 时对外输出为 chs(简体中文)
const toOutputCode = (code) => (code === 'chi' ? 'chs' : code);
const code1 = toOutputCode(targetSubs[0].code) || `sub${targetSubs[0].index}`;
const code2 = toOutputCode(targetSubs[1].code) || `sub${targetSubs[1].index}`;
const outputSrt = `${config.workdir}${removeExtension(file)}.${code1}-${code2}.srt`;
subtitleMerge(config.workdir + srts[0], config.workdir + srts[1], outputSrt);
deleteFile(srts[0]);
deleteFile(srts[1]);
}
}
/**
* 多个视频时先倒计时提示:超时默认处理全部;按键中断则列出视频,由用户输入序号选择。
*/
const selectMediaFiles = async (mediaFiles) => {
if (mediaFiles.length <= 1) return mediaFiles;
const interrupted = await waitForInterruptWithCountdown(
3000,
t('multiVideoCountdownPrefix', { count: mediaFiles.length }),
);
if (!interrupted) return mediaFiles;
console.log(t('videoList'));
mediaFiles.forEach((file, idx) => {
console.log(t('videoListItem'), idx + 1, file);
});
return await promptForVideoIndexes(mediaFiles);
};
/**
* 支持输入多个序号,用逗号或空格分隔,如 "1,3" / "1 3"
*/
const promptForVideoIndexes = (mediaFiles) => {
return new Promise((resolve) => {
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
const ask = () => {
rl.question(t('promptVideoIndex', { max: mediaFiles.length }), (answer) => {
const trimmed = answer.trim();
if (trimmed === '') {
console.log(t('exited'));
rl.close();
process.exit(0);
}
const values = trimmed.split(/[\s,,]+/).filter(Boolean).map(Number);
const valid = values.every((v) => Number.isInteger(v) && v >= 1 && v <= mediaFiles.length);
if (!valid) {
console.log(t('invalidVideoIndex', { max: mediaFiles.length }));
ask();
return;
}
rl.close();
resolve([...new Set(values)].map((v) => mediaFiles[v - 1]));
});
};
ask();
});
};
main();