parent
b5d17e6307
commit
655eac9ad6
19 changed files with 836 additions and 524 deletions
|
@ -10,5 +10,6 @@
|
|||
type = "app";
|
||||
};
|
||||
in {
|
||||
extract-subs = mkApp ./extract-subs;
|
||||
updateFlake = mkApp ./update;
|
||||
}
|
||||
|
|
2
apps/extract-subs/.envrc
Normal file
2
apps/extract-subs/.envrc
Normal file
|
@ -0,0 +1,2 @@
|
|||
use flake $FLAKE#subtitles-dev
|
||||
npm ci
|
32
apps/extract-subs/default.nix
Normal file
32
apps/extract-subs/default.nix
Normal file
|
@ -0,0 +1,32 @@
|
|||
{
|
||||
lib,
|
||||
buildNpmPackage,
|
||||
ffmpeg-full,
|
||||
makeWrapper,
|
||||
nodejs_latest,
|
||||
...
|
||||
}: let
|
||||
inherit (lib) concatMapStringsSep getBin;
|
||||
|
||||
packageJSON = builtins.fromJSON (builtins.readFile ./package.json);
|
||||
in
|
||||
buildNpmPackage rec {
|
||||
pname = packageJSON.name;
|
||||
inherit (packageJSON) version;
|
||||
|
||||
src = ./.;
|
||||
npmDepsHash = "sha256-edIAvY03eA3hqPHjAXz8pq3M5NzekOAYAR4o7j/Wf5Y=";
|
||||
|
||||
runtimeInputs = [
|
||||
ffmpeg-full
|
||||
];
|
||||
nativeBuildInputs = [makeWrapper];
|
||||
|
||||
postInstall = ''
|
||||
wrapProgram $out/bin/${pname} \
|
||||
--prefix PATH : ${concatMapStringsSep ":" (p: getBin p) runtimeInputs}
|
||||
'';
|
||||
|
||||
nodejs = nodejs_latest;
|
||||
meta.mainProgram = pname;
|
||||
}
|
454
apps/extract-subs/eslint.config.ts
Normal file
454
apps/extract-subs/eslint.config.ts
Normal file
|
@ -0,0 +1,454 @@
|
|||
import eslint from '@eslint/js';
|
||||
import jsdoc from 'eslint-plugin-jsdoc';
|
||||
import stylistic from '@stylistic/eslint-plugin';
|
||||
import tseslint from 'typescript-eslint';
|
||||
|
||||
|
||||
export default tseslint.config({
|
||||
files: ['**/*.{js,ts,tsx}'],
|
||||
ignores: ['node_modules/**', 'types/**'],
|
||||
|
||||
extends: [
|
||||
eslint.configs.recommended,
|
||||
jsdoc.configs['flat/recommended-typescript'],
|
||||
stylistic.configs['recommended-flat'],
|
||||
...tseslint.configs.recommended,
|
||||
...tseslint.configs.stylistic,
|
||||
],
|
||||
|
||||
rules: {
|
||||
// JSDoc settings
|
||||
'jsdoc/tag-lines': ['warn', 'any', { startLines: 1 }],
|
||||
'jsdoc/check-line-alignment': ['warn', 'always', {
|
||||
tags: ['param', 'arg', 'argument', 'property', 'prop'],
|
||||
}],
|
||||
'jsdoc/no-types': 'off',
|
||||
|
||||
// Newer settings
|
||||
'@typescript-eslint/no-extraneous-class': ['off'],
|
||||
'@typescript-eslint/no-implied-eval': ['off'],
|
||||
'class-methods-use-this': 'off',
|
||||
'@stylistic/no-multiple-empty-lines': 'off',
|
||||
'@stylistic/jsx-indent-props': 'off',
|
||||
'no-use-before-define': 'off',
|
||||
'@typescript-eslint/no-use-before-define': 'error',
|
||||
'@stylistic/indent-binary-ops': 'off',
|
||||
'@stylistic/max-statements-per-line': [
|
||||
'error',
|
||||
{ max: 2 },
|
||||
],
|
||||
|
||||
// Pre-flat config
|
||||
'@typescript-eslint/no-unused-vars': [
|
||||
'error',
|
||||
{
|
||||
args: 'all',
|
||||
argsIgnorePattern: '^_',
|
||||
caughtErrors: 'all',
|
||||
caughtErrorsIgnorePattern: '^_',
|
||||
destructuredArrayIgnorePattern: '^_',
|
||||
varsIgnorePattern: '^_',
|
||||
ignoreRestSiblings: true,
|
||||
},
|
||||
],
|
||||
|
||||
'array-callback-return': [
|
||||
'error',
|
||||
{
|
||||
allowImplicit: true,
|
||||
checkForEach: true,
|
||||
},
|
||||
],
|
||||
'no-constructor-return': [
|
||||
'error',
|
||||
],
|
||||
'no-unreachable-loop': [
|
||||
'error',
|
||||
{
|
||||
ignore: [
|
||||
'ForInStatement',
|
||||
'ForOfStatement',
|
||||
],
|
||||
},
|
||||
],
|
||||
'block-scoped-var': [
|
||||
'error',
|
||||
],
|
||||
'curly': [
|
||||
'warn',
|
||||
],
|
||||
'default-case-last': [
|
||||
'warn',
|
||||
],
|
||||
'default-param-last': [
|
||||
'error',
|
||||
],
|
||||
'eqeqeq': [
|
||||
'error',
|
||||
'smart',
|
||||
],
|
||||
'func-names': [
|
||||
'warn',
|
||||
'never',
|
||||
],
|
||||
'func-style': [
|
||||
'warn',
|
||||
'expression',
|
||||
],
|
||||
'logical-assignment-operators': [
|
||||
'warn',
|
||||
'always',
|
||||
],
|
||||
'no-array-constructor': [
|
||||
'error',
|
||||
],
|
||||
'no-empty-function': [
|
||||
'warn',
|
||||
],
|
||||
'no-empty-static-block': [
|
||||
'warn',
|
||||
],
|
||||
'no-extend-native': [
|
||||
'error',
|
||||
],
|
||||
'no-extra-bind': [
|
||||
'warn',
|
||||
],
|
||||
'no-implicit-coercion': [
|
||||
'warn',
|
||||
],
|
||||
'no-iterator': [
|
||||
'error',
|
||||
],
|
||||
'no-labels': [
|
||||
'error',
|
||||
],
|
||||
'no-lone-blocks': [
|
||||
'error',
|
||||
],
|
||||
'no-lonely-if': [
|
||||
'error',
|
||||
],
|
||||
'no-loop-func': [
|
||||
'error',
|
||||
],
|
||||
'no-magic-numbers': [
|
||||
'error',
|
||||
{
|
||||
ignore: [
|
||||
-1,
|
||||
0.1,
|
||||
0,
|
||||
1,
|
||||
2,
|
||||
3,
|
||||
4,
|
||||
5,
|
||||
10,
|
||||
12,
|
||||
33,
|
||||
66,
|
||||
100,
|
||||
255,
|
||||
360,
|
||||
450,
|
||||
500,
|
||||
1000,
|
||||
],
|
||||
ignoreDefaultValues: true,
|
||||
ignoreClassFieldInitialValues: true,
|
||||
},
|
||||
],
|
||||
'no-multi-assign': [
|
||||
'error',
|
||||
],
|
||||
'no-new-wrappers': [
|
||||
'error',
|
||||
],
|
||||
'no-object-constructor': [
|
||||
'error',
|
||||
],
|
||||
'no-proto': [
|
||||
'error',
|
||||
],
|
||||
'no-return-assign': [
|
||||
'error',
|
||||
],
|
||||
'no-sequences': [
|
||||
'error',
|
||||
],
|
||||
'no-shadow': [
|
||||
'error',
|
||||
{
|
||||
builtinGlobals: true,
|
||||
allow: [
|
||||
'Window',
|
||||
],
|
||||
},
|
||||
],
|
||||
'no-undef-init': [
|
||||
'warn',
|
||||
],
|
||||
'no-undefined': [
|
||||
'error',
|
||||
],
|
||||
'no-useless-constructor': [
|
||||
'warn',
|
||||
],
|
||||
'no-useless-escape': [
|
||||
'off',
|
||||
],
|
||||
'no-useless-return': [
|
||||
'error',
|
||||
],
|
||||
'no-var': [
|
||||
'error',
|
||||
],
|
||||
'no-void': [
|
||||
'off',
|
||||
],
|
||||
'no-with': [
|
||||
'error',
|
||||
],
|
||||
'object-shorthand': [
|
||||
'error',
|
||||
'always',
|
||||
],
|
||||
'one-var': [
|
||||
'error',
|
||||
'never',
|
||||
],
|
||||
'operator-assignment': [
|
||||
'warn',
|
||||
'always',
|
||||
],
|
||||
'prefer-arrow-callback': [
|
||||
'error',
|
||||
],
|
||||
'prefer-const': [
|
||||
'error',
|
||||
],
|
||||
'prefer-object-has-own': [
|
||||
'error',
|
||||
],
|
||||
'prefer-regex-literals': [
|
||||
'error',
|
||||
],
|
||||
'prefer-template': [
|
||||
'warn',
|
||||
],
|
||||
'no-prototype-builtins': 'off',
|
||||
'@typescript-eslint/no-var-requires': [
|
||||
'off',
|
||||
],
|
||||
'@stylistic/array-bracket-newline': [
|
||||
'warn',
|
||||
'consistent',
|
||||
],
|
||||
'@stylistic/array-bracket-spacing': [
|
||||
'warn',
|
||||
'never',
|
||||
],
|
||||
'@stylistic/arrow-parens': [
|
||||
'warn',
|
||||
'always',
|
||||
],
|
||||
'@stylistic/brace-style': [
|
||||
'warn',
|
||||
'stroustrup',
|
||||
{ allowSingleLine: true },
|
||||
],
|
||||
'@stylistic/comma-dangle': [
|
||||
'warn',
|
||||
'always-multiline',
|
||||
],
|
||||
'@stylistic/comma-spacing': [
|
||||
'warn',
|
||||
{
|
||||
before: false,
|
||||
after: true,
|
||||
},
|
||||
],
|
||||
'@stylistic/comma-style': [
|
||||
'error',
|
||||
'last',
|
||||
],
|
||||
'@stylistic/dot-location': [
|
||||
'error',
|
||||
'property',
|
||||
],
|
||||
'@stylistic/function-call-argument-newline': [
|
||||
'warn',
|
||||
'consistent',
|
||||
],
|
||||
'@stylistic/function-paren-newline': [
|
||||
'warn',
|
||||
'consistent',
|
||||
],
|
||||
'@stylistic/indent': [
|
||||
'warn',
|
||||
4,
|
||||
{
|
||||
SwitchCase: 1,
|
||||
ignoreComments: true,
|
||||
ignoredNodes: ['TemplateLiteral > *'],
|
||||
},
|
||||
],
|
||||
'@stylistic/key-spacing': [
|
||||
'warn',
|
||||
{
|
||||
beforeColon: false,
|
||||
afterColon: true,
|
||||
},
|
||||
],
|
||||
'@stylistic/keyword-spacing': [
|
||||
'warn',
|
||||
{
|
||||
before: true,
|
||||
},
|
||||
],
|
||||
'@stylistic/linebreak-style': [
|
||||
'error',
|
||||
'unix',
|
||||
],
|
||||
'@stylistic/lines-between-class-members': [
|
||||
'warn',
|
||||
'always',
|
||||
{
|
||||
exceptAfterSingleLine: true,
|
||||
},
|
||||
],
|
||||
'@stylistic/max-len': [
|
||||
'warn',
|
||||
{
|
||||
code: 105,
|
||||
ignoreComments: true,
|
||||
ignoreTrailingComments: true,
|
||||
ignoreUrls: true,
|
||||
},
|
||||
],
|
||||
'@stylistic/multiline-ternary': [
|
||||
'warn',
|
||||
'always-multiline',
|
||||
],
|
||||
'@stylistic/new-parens': [
|
||||
'error',
|
||||
],
|
||||
'@stylistic/no-mixed-operators': [
|
||||
'warn',
|
||||
],
|
||||
'@stylistic/no-mixed-spaces-and-tabs': [
|
||||
'error',
|
||||
],
|
||||
'@stylistic/no-multi-spaces': [
|
||||
'error',
|
||||
],
|
||||
'@stylistic/no-tabs': [
|
||||
'error',
|
||||
],
|
||||
'@stylistic/no-trailing-spaces': [
|
||||
'error',
|
||||
],
|
||||
'@stylistic/no-whitespace-before-property': [
|
||||
'warn',
|
||||
],
|
||||
'@stylistic/nonblock-statement-body-position': [
|
||||
'error',
|
||||
'below',
|
||||
],
|
||||
'@stylistic/object-curly-newline': [
|
||||
'warn',
|
||||
{
|
||||
consistent: true,
|
||||
},
|
||||
],
|
||||
'@stylistic/object-curly-spacing': [
|
||||
'warn',
|
||||
'always',
|
||||
],
|
||||
'@stylistic/operator-linebreak': [
|
||||
'warn',
|
||||
'after',
|
||||
],
|
||||
'@stylistic/padded-blocks': [
|
||||
'error',
|
||||
'never',
|
||||
],
|
||||
'@stylistic/padding-line-between-statements': [
|
||||
'warn',
|
||||
{
|
||||
blankLine: 'always',
|
||||
prev: '*',
|
||||
next: 'return',
|
||||
},
|
||||
{
|
||||
blankLine: 'always',
|
||||
prev: [
|
||||
'const',
|
||||
'let',
|
||||
'var',
|
||||
],
|
||||
next: '*',
|
||||
},
|
||||
{
|
||||
blankLine: 'any',
|
||||
prev: [
|
||||
'const',
|
||||
'let',
|
||||
'var',
|
||||
],
|
||||
next: [
|
||||
'const',
|
||||
'let',
|
||||
'var',
|
||||
],
|
||||
},
|
||||
{
|
||||
blankLine: 'always',
|
||||
prev: [
|
||||
'case',
|
||||
'default',
|
||||
],
|
||||
next: '*',
|
||||
},
|
||||
],
|
||||
'@stylistic/quote-props': [
|
||||
'error',
|
||||
'consistent-as-needed',
|
||||
],
|
||||
'@stylistic/quotes': [
|
||||
'error',
|
||||
'single',
|
||||
{
|
||||
avoidEscape: true,
|
||||
},
|
||||
],
|
||||
'@stylistic/semi': [
|
||||
'error',
|
||||
'always',
|
||||
],
|
||||
'@stylistic/semi-spacing': [
|
||||
'warn',
|
||||
],
|
||||
'@stylistic/space-before-blocks': [
|
||||
'warn',
|
||||
],
|
||||
'@stylistic/space-before-function-paren': [
|
||||
'warn',
|
||||
'never',
|
||||
],
|
||||
'@stylistic/space-infix-ops': [
|
||||
'warn',
|
||||
],
|
||||
'@stylistic/spaced-comment': [
|
||||
'warn',
|
||||
'always',
|
||||
],
|
||||
'@stylistic/switch-colon-spacing': [
|
||||
'warn',
|
||||
],
|
||||
'@stylistic/wrap-regex': [
|
||||
'warn',
|
||||
],
|
||||
},
|
||||
});
|
2196
apps/extract-subs/package-lock.json
generated
Normal file
2196
apps/extract-subs/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load diff
22
apps/extract-subs/package.json
Normal file
22
apps/extract-subs/package.json
Normal file
|
@ -0,0 +1,22 @@
|
|||
{
|
||||
"name": "extract-subs",
|
||||
"version": "0.0.0",
|
||||
"bin": "out/bin/app.cjs",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"build": "node_ver=$(node -v); esbuild src/app.ts --bundle --platform=node --target=\"node${node_ver:1:2}\" --outfile=out/bin/app.cjs"
|
||||
},
|
||||
"dependencies": {
|
||||
"@eslint/js": "9.14.0",
|
||||
"@stylistic/eslint-plugin": "2.10.1",
|
||||
"@types/fluent-ffmpeg": "2.1.27",
|
||||
"@types/node": "22.9.0",
|
||||
"esbuild": "0.24.0",
|
||||
"eslint": "9.14.0",
|
||||
"eslint-plugin-jsdoc": "50.5.0",
|
||||
"fluent-ffmpeg": "2.1.3",
|
||||
"jiti": "2.4.0",
|
||||
"typescript": "5.6.3",
|
||||
"typescript-eslint": "8.14.0"
|
||||
}
|
||||
}
|
157
apps/extract-subs/src/app.ts
Normal file
157
apps/extract-subs/src/app.ts
Normal file
|
@ -0,0 +1,157 @@
|
|||
import { spawnSync as spawn } from 'child_process';
|
||||
|
||||
import ffprobe from './ffprobe';
|
||||
import { ISO6393To1 } from './lang-codes';
|
||||
|
||||
/* Types */
|
||||
import { FfprobeStream } from 'fluent-ffmpeg';
|
||||
|
||||
|
||||
const SPAWN_OPTS = {
|
||||
stdio: [process.stdin, process.stdout, process.stderr],
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* These are the cli arguments
|
||||
*
|
||||
* @param videoPath 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 video = process.argv[2];
|
||||
const languages = process.argv[3]?.split(',');
|
||||
|
||||
|
||||
// Global Vars
|
||||
const subIndexes: number[] = [];
|
||||
let videoPath: string;
|
||||
let baseName: string;
|
||||
|
||||
|
||||
/**
|
||||
* Gets the relative path to the subtitle file of a ffmpeg stream.
|
||||
*
|
||||
* @param sub the stream of the subtitles to extract
|
||||
* @returns the path of the subtitle file
|
||||
*/
|
||||
const getSubPath = (sub: FfprobeStream): string => {
|
||||
const language = ISO6393To1.get(sub.tags.language);
|
||||
|
||||
const forced = sub.disposition?.forced === 0 ?
|
||||
'' :
|
||||
'.forced';
|
||||
|
||||
const hearingImpaired = sub.disposition?.hearing_impaired === 0 ?
|
||||
'' :
|
||||
'.sdh';
|
||||
|
||||
return `${baseName}${forced}.${language}${hearingImpaired}.srt`;
|
||||
};
|
||||
|
||||
/**
|
||||
* Removes all subtitles streams from the video file.
|
||||
*/
|
||||
const removeContainerSubs = (): void => {
|
||||
spawn('mv', [
|
||||
videoPath,
|
||||
`${videoPath}.bak`,
|
||||
], SPAWN_OPTS);
|
||||
|
||||
spawn('ffmpeg', [
|
||||
'-i', `${videoPath}.bak`,
|
||||
'-map', '0',
|
||||
...subIndexes.map((i) => ['-map', `-0:${i}`]).flat(),
|
||||
'-c', 'copy', videoPath,
|
||||
], SPAWN_OPTS);
|
||||
|
||||
spawn('rm', [
|
||||
`${videoPath}.bak`,
|
||||
], SPAWN_OPTS);
|
||||
};
|
||||
|
||||
/**
|
||||
* Extracts a sub of a video file to a subtitle file.
|
||||
*
|
||||
* @param sub the stream of the subtitles to extract
|
||||
*/
|
||||
const extractSub = (sub: FfprobeStream): void => {
|
||||
const subFile = getSubPath(sub);
|
||||
|
||||
spawn('ffmpeg', [
|
||||
'-i', videoPath,
|
||||
'-map', `0:${sub.index}`, subFile,
|
||||
], SPAWN_OPTS);
|
||||
|
||||
subIndexes.push(sub.index);
|
||||
};
|
||||
|
||||
/**
|
||||
* Sorts the list of streams to only keep subtitles
|
||||
* that can be extracted.
|
||||
*
|
||||
* @param lang the language of the subtitles
|
||||
* @param streams the streams
|
||||
* @returns the streams that represent subtitles
|
||||
*/
|
||||
const findSubs = (
|
||||
lang: string,
|
||||
streams: FfprobeStream[],
|
||||
): FfprobeStream[] => {
|
||||
const subs = streams.filter((s) => s.tags?.language &&
|
||||
s.tags.language === lang &&
|
||||
s.codec_type === 'subtitle');
|
||||
|
||||
const pgs = subs.filter((s) => s.codec_name === 'hdmv_pgs_subtitle');
|
||||
|
||||
// If we only have PGS subs, warn user
|
||||
if (pgs.length === subs.length) {
|
||||
console.warn(`No SRT subtitle tracks were found for ${lang}`);
|
||||
}
|
||||
|
||||
// Remove PGS streams from subs
|
||||
return subs.filter((s) => s.codec_name !== 'hdmv_pgs_subtitle');
|
||||
};
|
||||
|
||||
/**
|
||||
* Where the magic happens.
|
||||
*/
|
||||
const main = async(): Promise<void> => {
|
||||
// Get rid of video extension
|
||||
baseName = videoPath.split('/').at(-1)!.replace(/\.[^.]*$/, '');
|
||||
|
||||
// ffprobe the video file to see available sub tracks
|
||||
const data = await ffprobe(videoPath);
|
||||
|
||||
if (!data?.streams) {
|
||||
console.error('Couldn\'t find streams in video file');
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Check for languages wanted
|
||||
languages.forEach((lang) => {
|
||||
const subs = findSubs(lang, data.streams);
|
||||
|
||||
if (subs.length === 0) {
|
||||
console.warn(`No subtitle tracks were found for ${lang}`);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Extract all subs
|
||||
subs.forEach((sub) => { extractSub(sub); });
|
||||
});
|
||||
|
||||
removeContainerSubs();
|
||||
};
|
||||
|
||||
|
||||
// Check if there are 2 params
|
||||
if (video && languages) {
|
||||
videoPath = video;
|
||||
main();
|
||||
}
|
||||
else {
|
||||
console.error('Error: no argument passed');
|
||||
process.exit(1);
|
||||
}
|
8
apps/extract-subs/src/ffprobe.ts
Normal file
8
apps/extract-subs/src/ffprobe.ts
Normal file
|
@ -0,0 +1,8 @@
|
|||
import Ffmpeg from 'fluent-ffmpeg';
|
||||
|
||||
|
||||
export default (videoPath: string) => new Promise<Ffmpeg.FfprobeData>((resolve) => {
|
||||
Ffmpeg.ffprobe(videoPath, (_e, data) => {
|
||||
resolve(data);
|
||||
});
|
||||
});
|
373
apps/extract-subs/src/lang-codes.ts
Normal file
373
apps/extract-subs/src/lang-codes.ts
Normal file
|
@ -0,0 +1,373 @@
|
|||
export const ISO6391To3 = new Map([
|
||||
['aa', 'aar'],
|
||||
['ab', 'abk'],
|
||||
['af', 'afr'],
|
||||
['ak', 'aka'],
|
||||
['am', 'amh'],
|
||||
['ar', 'ara'],
|
||||
['an', 'arg'],
|
||||
['as', 'asm'],
|
||||
['av', 'ava'],
|
||||
['ae', 'ave'],
|
||||
['ay', 'aym'],
|
||||
['az', 'aze'],
|
||||
['ba', 'bak'],
|
||||
['bm', 'bam'],
|
||||
['be', 'bel'],
|
||||
['bn', 'ben'],
|
||||
['bi', 'bis'],
|
||||
['bo', 'bod'],
|
||||
['bs', 'bos'],
|
||||
['br', 'bre'],
|
||||
['bg', 'bul'],
|
||||
['ca', 'cat'],
|
||||
['cs', 'ces'],
|
||||
['ch', 'cha'],
|
||||
['ce', 'che'],
|
||||
['cu', 'chu'],
|
||||
['cv', 'chv'],
|
||||
['kw', 'cor'],
|
||||
['co', 'cos'],
|
||||
['cr', 'cre'],
|
||||
['cy', 'cym'],
|
||||
['da', 'dan'],
|
||||
['de', 'deu'],
|
||||
['dv', 'div'],
|
||||
['dz', 'dzo'],
|
||||
['el', 'ell'],
|
||||
['en', 'eng'],
|
||||
['eo', 'epo'],
|
||||
['et', 'est'],
|
||||
['eu', 'eus'],
|
||||
['ee', 'ewe'],
|
||||
['fo', 'fao'],
|
||||
['fa', 'fas'],
|
||||
['fj', 'fij'],
|
||||
['fi', 'fin'],
|
||||
['fr', 'fre'],
|
||||
['fy', 'fry'],
|
||||
['ff', 'ful'],
|
||||
['gd', 'gla'],
|
||||
['ga', 'gle'],
|
||||
['gl', 'glg'],
|
||||
['gv', 'glv'],
|
||||
['gn', 'grn'],
|
||||
['gu', 'guj'],
|
||||
['ht', 'hat'],
|
||||
['ha', 'hau'],
|
||||
['sh', 'hbs'],
|
||||
['he', 'heb'],
|
||||
['hz', 'her'],
|
||||
['hi', 'hin'],
|
||||
['ho', 'hmo'],
|
||||
['hr', 'hrv'],
|
||||
['hu', 'hun'],
|
||||
['hy', 'hye'],
|
||||
['ig', 'ibo'],
|
||||
['io', 'ido'],
|
||||
['ii', 'iii'],
|
||||
['iu', 'iku'],
|
||||
['ie', 'ile'],
|
||||
['ia', 'ina'],
|
||||
['id', 'ind'],
|
||||
['ik', 'ipk'],
|
||||
['is', 'isl'],
|
||||
['it', 'ita'],
|
||||
['jv', 'jav'],
|
||||
['ja', 'jpn'],
|
||||
['kl', 'kal'],
|
||||
['kn', 'kan'],
|
||||
['ks', 'kas'],
|
||||
['ka', 'kat'],
|
||||
['kr', 'kau'],
|
||||
['kk', 'kaz'],
|
||||
['km', 'khm'],
|
||||
['ki', 'kik'],
|
||||
['rw', 'kin'],
|
||||
['ky', 'kir'],
|
||||
['kv', 'kom'],
|
||||
['kg', 'kon'],
|
||||
['ko', 'kor'],
|
||||
['kj', 'kua'],
|
||||
['ku', 'kur'],
|
||||
['lo', 'lao'],
|
||||
['la', 'lat'],
|
||||
['lv', 'lav'],
|
||||
['li', 'lim'],
|
||||
['ln', 'lin'],
|
||||
['lt', 'lit'],
|
||||
['lb', 'ltz'],
|
||||
['lu', 'lub'],
|
||||
['lg', 'lug'],
|
||||
['mh', 'mah'],
|
||||
['ml', 'mal'],
|
||||
['mr', 'mar'],
|
||||
['mk', 'mkd'],
|
||||
['mg', 'mlg'],
|
||||
['mt', 'mlt'],
|
||||
['mn', 'mon'],
|
||||
['mi', 'mri'],
|
||||
['ms', 'msa'],
|
||||
['my', 'mya'],
|
||||
['na', 'nau'],
|
||||
['nv', 'nav'],
|
||||
['nr', 'nbl'],
|
||||
['nd', 'nde'],
|
||||
['ng', 'ndo'],
|
||||
['ne', 'nep'],
|
||||
['nl', 'nld'],
|
||||
['nn', 'nno'],
|
||||
['nb', 'nob'],
|
||||
['no', 'nor'],
|
||||
['ny', 'nya'],
|
||||
['oc', 'oci'],
|
||||
['oj', 'oji'],
|
||||
['or', 'ori'],
|
||||
['om', 'orm'],
|
||||
['os', 'oss'],
|
||||
['pa', 'pan'],
|
||||
['pi', 'pli'],
|
||||
['pl', 'pol'],
|
||||
['pt', 'por'],
|
||||
['ps', 'pus'],
|
||||
['qu', 'que'],
|
||||
['rm', 'roh'],
|
||||
['ro', 'ron'],
|
||||
['rn', 'run'],
|
||||
['ru', 'rus'],
|
||||
['sg', 'sag'],
|
||||
['sa', 'san'],
|
||||
['si', 'sin'],
|
||||
['sk', 'slk'],
|
||||
['sl', 'slv'],
|
||||
['se', 'sme'],
|
||||
['sm', 'smo'],
|
||||
['sn', 'sna'],
|
||||
['sd', 'snd'],
|
||||
['so', 'som'],
|
||||
['st', 'sot'],
|
||||
['es', 'spa'],
|
||||
['sq', 'sqi'],
|
||||
['sc', 'srd'],
|
||||
['sr', 'srp'],
|
||||
['ss', 'ssw'],
|
||||
['su', 'sun'],
|
||||
['sw', 'swa'],
|
||||
['sv', 'swe'],
|
||||
['ty', 'tah'],
|
||||
['ta', 'tam'],
|
||||
['tt', 'tat'],
|
||||
['te', 'tel'],
|
||||
['tg', 'tgk'],
|
||||
['tl', 'tgl'],
|
||||
['th', 'tha'],
|
||||
['ti', 'tir'],
|
||||
['to', 'ton'],
|
||||
['tn', 'tsn'],
|
||||
['ts', 'tso'],
|
||||
['tk', 'tuk'],
|
||||
['tr', 'tur'],
|
||||
['tw', 'twi'],
|
||||
['ug', 'uig'],
|
||||
['uk', 'ukr'],
|
||||
['ur', 'urd'],
|
||||
['uz', 'uzb'],
|
||||
['ve', 'ven'],
|
||||
['vi', 'vie'],
|
||||
['vo', 'vol'],
|
||||
['wa', 'wln'],
|
||||
['wo', 'wol'],
|
||||
['xh', 'xho'],
|
||||
['yi', 'yid'],
|
||||
['yo', 'yor'],
|
||||
['za', 'zha'],
|
||||
['zh', 'zho'],
|
||||
['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'],
|
||||
['fre', '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'],
|
||||
]);
|
10
apps/extract-subs/tsconfig.json
Normal file
10
apps/extract-subs/tsconfig.json
Normal file
|
@ -0,0 +1,10 @@
|
|||
{
|
||||
"$schema": "https://json.schemastore.org/tsconfig",
|
||||
"extends": "../tsconfig.json",
|
||||
"includes": [
|
||||
"*.ts",
|
||||
"**/*.ts",
|
||||
"*.js",
|
||||
"**/*.js"
|
||||
]
|
||||
}
|
25
apps/tsconfig.json
Normal file
25
apps/tsconfig.json
Normal file
|
@ -0,0 +1,25 @@
|
|||
{
|
||||
"$schema": "https://json.schemastore.org/tsconfig",
|
||||
"compilerOptions": {
|
||||
// Env
|
||||
"target": "ESNext",
|
||||
"lib": [
|
||||
"ESNext"
|
||||
],
|
||||
// Module
|
||||
"module": "nodenext",
|
||||
"moduleResolution": "bundler",
|
||||
"baseUrl": ".",
|
||||
// Emit
|
||||
"noEmit": true,
|
||||
"newLine": "LF",
|
||||
// Interop
|
||||
"esModuleInterop": true,
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"isolatedModules": true,
|
||||
// Type Checking
|
||||
"strict": true,
|
||||
"noImplicitAny": false
|
||||
}
|
||||
}
|
|
@ -1 +1,2 @@
|
|||
use flake $FLAKE#node
|
||||
npm ci
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
import { spawnSync } from 'node:child_process';
|
||||
import { writeFileSync } from 'node:fs';
|
||||
|
||||
import { parseArgs } from './lib.ts';
|
||||
import { parseArgs } from './lib';
|
||||
|
||||
import { updateDocker } from './docker.ts';
|
||||
import { updateFirefoxAddons } from '././firefox.ts';
|
||||
import { updateFlakeInputs } from './flake.ts';
|
||||
import { updateCustomPackage, updateVuetorrent } from './misc.ts';
|
||||
import { updateDocker } from './docker';
|
||||
import { updateFirefoxAddons } from '././firefox';
|
||||
import { updateFlakeInputs } from './flake';
|
||||
import { updateCustomPackage, updateVuetorrent } from './misc';
|
||||
|
||||
|
||||
/* Constants */
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { writeFileSync } from 'node:fs';
|
||||
import { spawnSync } from 'node:child_process';
|
||||
|
||||
import { parseFetchurl } from './lib.ts';
|
||||
import { parseFetchurl } from './lib';
|
||||
|
||||
|
||||
/* Constants */
|
||||
|
|
|
@ -1,27 +1,6 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
// Env
|
||||
"target": "ESNext",
|
||||
"lib": ["ESNext"],
|
||||
// Module
|
||||
"module": "nodenext",
|
||||
"moduleResolution": "nodenext",
|
||||
"allowImportingTsExtensions": true,
|
||||
"baseUrl": ".",
|
||||
// Emit
|
||||
"noEmit": true,
|
||||
"newLine": "LF",
|
||||
// Interop
|
||||
"esModuleInterop": true,
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"isolatedModules": true,
|
||||
// Type Checking
|
||||
"strict": true,
|
||||
"noImplicitAny": false,
|
||||
"allowJs": true,
|
||||
"checkJs": true
|
||||
},
|
||||
"$schema": "https://json.schemastore.org/tsconfig",
|
||||
"extends": "../tsconfig.json",
|
||||
"includes": [
|
||||
"*.ts",
|
||||
"**/*.ts",
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue