refactor(node-syncsub): take in a list of langs to sync
All checks were successful
Discord / discord commits (push) Has been skipped

This commit is contained in:
matt1432 2024-03-31 03:44:23 -04:00
parent 551691f061
commit aeddb3eb92
3 changed files with 265 additions and 50 deletions

View file

@ -28,7 +28,6 @@
"default-param-last": ["error"], "default-param-last": ["error"],
"eqeqeq": ["error", "smart"], "eqeqeq": ["error", "smart"],
"func-names": ["warn", "never"], "func-names": ["warn", "never"],
"func-style": ["warn", "expression"],
"logical-assignment-operators": ["warn", "always"], "logical-assignment-operators": ["warn", "always"],
"no-array-constructor": ["error"], "no-array-constructor": ["error"],
"no-empty-function": ["warn"], "no-empty-function": ["warn"],

View file

@ -1,4 +1,4 @@
export const iso6391To3 = new Map([ export const ISO6391To3 = new Map([
['aa', 'aar'], ['aa', 'aar'],
['ab', 'abk'], ['ab', 'abk'],
['af', 'afr'], ['af', 'afr'],
@ -184,3 +184,190 @@ export const iso6391To3 = new Map([
['zh', 'zho'], ['zh', 'zho'],
['zu', 'zul'], ['zu', 'zul'],
]); ]);
export const ISO6393To1 = new Map([
['aar', 'aa'],
['abk', 'ab'],
['afr', 'af'],
['aka', 'ak'],
['amh', 'am'],
['ara', 'ar'],
['arg', 'an'],
['asm', 'as'],
['ava', 'av'],
['ave', 'ae'],
['aym', 'ay'],
['aze', 'az'],
['bak', 'ba'],
['bam', 'bm'],
['bel', 'be'],
['ben', 'bn'],
['bis', 'bi'],
['bod', 'bo'],
['bos', 'bs'],
['bre', 'br'],
['bul', 'bg'],
['cat', 'ca'],
['ces', 'cs'],
['cha', 'ch'],
['che', 'ce'],
['chu', 'cu'],
['chv', 'cv'],
['cor', 'kw'],
['cos', 'co'],
['cre', 'cr'],
['cym', 'cy'],
['dan', 'da'],
['deu', 'de'],
['div', 'dv'],
['dzo', 'dz'],
['ell', 'el'],
['eng', 'en'],
['epo', 'eo'],
['est', 'et'],
['eus', 'eu'],
['ewe', 'ee'],
['fao', 'fo'],
['fas', 'fa'],
['fij', 'fj'],
['fin', 'fi'],
['fra', 'fr'],
['fry', 'fy'],
['ful', 'ff'],
['gla', 'gd'],
['gle', 'ga'],
['glg', 'gl'],
['glv', 'gv'],
['grn', 'gn'],
['guj', 'gu'],
['hat', 'ht'],
['hau', 'ha'],
['hbs', 'sh'],
['heb', 'he'],
['her', 'hz'],
['hin', 'hi'],
['hmo', 'ho'],
['hrv', 'hr'],
['hun', 'hu'],
['hye', 'hy'],
['ibo', 'ig'],
['ido', 'io'],
['iii', 'ii'],
['iku', 'iu'],
['ile', 'ie'],
['ina', 'ia'],
['ind', 'id'],
['ipk', 'ik'],
['isl', 'is'],
['ita', 'it'],
['jav', 'jv'],
['jpn', 'ja'],
['kal', 'kl'],
['kan', 'kn'],
['kas', 'ks'],
['kat', 'ka'],
['kau', 'kr'],
['kaz', 'kk'],
['khm', 'km'],
['kik', 'ki'],
['kin', 'rw'],
['kir', 'ky'],
['kom', 'kv'],
['kon', 'kg'],
['kor', 'ko'],
['kua', 'kj'],
['kur', 'ku'],
['lao', 'lo'],
['lat', 'la'],
['lav', 'lv'],
['lim', 'li'],
['lin', 'ln'],
['lit', 'lt'],
['ltz', 'lb'],
['lub', 'lu'],
['lug', 'lg'],
['mah', 'mh'],
['mal', 'ml'],
['mar', 'mr'],
['mkd', 'mk'],
['mlg', 'mg'],
['mlt', 'mt'],
['mon', 'mn'],
['mri', 'mi'],
['msa', 'ms'],
['mya', 'my'],
['nau', 'na'],
['nav', 'nv'],
['nbl', 'nr'],
['nde', 'nd'],
['ndo', 'ng'],
['nep', 'ne'],
['nld', 'nl'],
['nno', 'nn'],
['nob', 'nb'],
['nor', 'no'],
['nya', 'ny'],
['oci', 'oc'],
['oji', 'oj'],
['ori', 'or'],
['orm', 'om'],
['oss', 'os'],
['pan', 'pa'],
['pli', 'pi'],
['pol', 'pl'],
['por', 'pt'],
['pus', 'ps'],
['que', 'qu'],
['roh', 'rm'],
['ron', 'ro'],
['run', 'rn'],
['rus', 'ru'],
['sag', 'sg'],
['san', 'sa'],
['sin', 'si'],
['slk', 'sk'],
['slv', 'sl'],
['sme', 'se'],
['smo', 'sm'],
['sna', 'sn'],
['snd', 'sd'],
['som', 'so'],
['sot', 'st'],
['spa', 'es'],
['sqi', 'sq'],
['srd', 'sc'],
['srp', 'sr'],
['ssw', 'ss'],
['sun', 'su'],
['swa', 'sw'],
['swe', 'sv'],
['tah', 'ty'],
['tam', 'ta'],
['tat', 'tt'],
['tel', 'te'],
['tgk', 'tg'],
['tgl', 'tl'],
['tha', 'th'],
['tir', 'ti'],
['ton', 'to'],
['tsn', 'tn'],
['tso', 'ts'],
['tuk', 'tk'],
['tur', 'tr'],
['twi', 'tw'],
['uig', 'ug'],
['ukr', 'uk'],
['urd', 'ur'],
['uzb', 'uz'],
['ven', 've'],
['vie', 'vi'],
['vol', 'vo'],
['wln', 'wa'],
['wol', 'wo'],
['xho', 'xh'],
['yid', 'yi'],
['yor', 'yo'],
['zha', 'za'],
['zho', 'zh'],
['zul', 'zu'],
]);

View file

@ -1,55 +1,98 @@
import { readdir } from 'fs'; import {
import { ffprobe } from 'fluent-ffmpeg'; mkdir,
readdir as readDir,
rename as mv,
} from 'fs/promises';
import { ffprobe as ffProbe } from 'fluent-ffmpeg';
import { spawn } from 'child_process'; import { spawn } from 'child_process';
import { iso6391To3 } from './lang-codes';
const SUB_EXT_LENGTH = 7; import { ISO6391To3, ISO6393To1 } from './lang-codes';
const FILE = process.argv[2]; /**
* These are the cli arguments
*
* @param directory the directory in which we want to sync the subtitles
* @param languages a comma-separated list of languages (3 letters) to sync the subtitles
*/
const DIR = process.argv[2];
let langs = process.argv[3].split(',');
const main = () => {
const BASE_NAME = FILE.substring(
FILE.lastIndexOf('/') + 1,
FILE.length - SUB_EXT_LENGTH,
);
const DIR = FILE.substring(0, FILE.lastIndexOf('/')); // Check if there are 2 params
if (DIR && langs) {
main();
}
else {
console.error('Error: no argument passed');
process.exit(1);
}
readdir(DIR, (_, files) => { function getVideoPath(files: string[]) {
const VIDEO = `${DIR}/${files.filter((f) => const fileName = DIR.split('/').at(-1) ?? '';
f.includes(BASE_NAME) &&
!f.endsWith('.nfo') &&
!f.endsWith('.srt'))[0]}`;
ffprobe(VIDEO, (_e, data) => { return `${DIR}/${files.filter((f) =>
const LANG = iso6391To3.get(FILE.split('.').at(-2) ?? 'en') ?? 'eng'; f.includes(fileName) &&
!f.endsWith('.nfo') &&
!f.endsWith('.srt'))[0]}`;
}
const OUT_FILE = `${BASE_NAME}.synced.${LANG.substring(0, 2)}.srt`; async function main() {
const OUT_PATH = `${DIR}/${OUT_FILE}`; const files = await readDir(DIR);
if (files.includes(OUT_FILE)) { const VIDEO = getVideoPath(files);
console.warn('Synced subtitles already exist, not doing anything'); const BASE_NAME = VIDEO.split('/').at(-1)?.replace(/\.[^.]*$/, '');
process.exit(0);
// Check if backup folder already exists and create it if not
if (!files.some((f) => f.endsWith('.srt.bak'))) {
await mkdir(`${DIR}/.srt.bak`);
}
else {
const backups = await readDir(`${DIR}/.srt.bak`);
// Remove synced subtitles from the list to sync
// langs - backups
langs = langs.filter((n) => !backups
.some((s) => n === ISO6391To3.get(s.split('.').at(-2) ?? '')));
}
if (langs.length === 0) {
console.warn('Subtitles have already been synced');
process.exit(0);
}
// ffprobe the video file to see available audio tracks
ffProbe(VIDEO, (_e, data) => {
const AVAIL_LANGS = data.streams
.filter((s) => s.codec_type === 'audio')
.map((s) => s['tags']['language']);
// Sync subtitles one by one
langs.forEach(async(lang) => {
const FILE_NAME = `${BASE_NAME}.${ISO6393To1.get(lang)}.srt`;
const IN_FILE = `${DIR}/.srt.bak/${FILE_NAME}`;
const OUT_FILE = `${DIR}/${FILE_NAME}`;
if (files.includes(FILE_NAME)) {
await mv(OUT_FILE, IN_FILE);
}
else {
// TODO: check data and extract sub
} }
const availLangs = data.streams
.filter((s) => s.codec_type === 'audio')
.map((s) => s['tags']['language']);
const cmd = [ const cmd = [
'--cli sync', '--cli sync',
`--sub-lang ${LANG}`, `--sub-lang ${lang}`,
`--ref-stream-by-lang ${availLangs.includes(LANG) ? LANG : availLangs[0]}`, `--ref-stream-by-lang ${AVAIL_LANGS.includes(lang) ?
lang :
AVAIL_LANGS[0]}`,
'--ref-stream-by-type "audio"', '--ref-stream-by-type "audio"',
`--sub '${FILE}'`, `--sub '${IN_FILE}'`,
`--out '${OUT_PATH}'`, `--out '${OUT_FILE}'`,
// `--out '${OUT_PATH}'`,
`--ref '${VIDEO}'`, `--ref '${VIDEO}'`,
// '--overwrite',
]; ];
spawn('subsync', cmd, { spawn('subsync', cmd, {
@ -58,18 +101,4 @@ const main = () => {
}); });
}); });
}); });
};
if (FILE) {
if (FILE.includes('synced.srt')) {
console.warn('Won\'t sync already synced subtitles, not doing anything');
process.exit(0);
}
else {
main();
}
}
else {
console.error('Error: no argument passed');
process.exit(1);
} }