refactor(extract-subs): move to apps and split up code better
All checks were successful
Discord / discord commits (push) Has been skipped
All checks were successful
Discord / discord commits (push) Has been skipped
This commit is contained in:
parent
b5d17e6307
commit
655eac9ad6
19 changed files with 273 additions and 235 deletions
|
@ -10,5 +10,6 @@
|
||||||
type = "app";
|
type = "app";
|
||||||
};
|
};
|
||||||
in {
|
in {
|
||||||
|
extract-subs = mkApp ./extract-subs;
|
||||||
updateFlake = mkApp ./update;
|
updateFlake = mkApp ./update;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1 +1,2 @@
|
||||||
use flake $FLAKE#subtitles-dev
|
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;
|
||||||
|
}
|
|
@ -30,6 +30,13 @@ export default tseslint.config({
|
||||||
'class-methods-use-this': 'off',
|
'class-methods-use-this': 'off',
|
||||||
'@stylistic/no-multiple-empty-lines': 'off',
|
'@stylistic/no-multiple-empty-lines': 'off',
|
||||||
'@stylistic/jsx-indent-props': '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
|
// Pre-flat config
|
||||||
'@typescript-eslint/no-unused-vars': [
|
'@typescript-eslint/no-unused-vars': [
|
||||||
|
@ -64,12 +71,6 @@ export default tseslint.config({
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
'no-use-before-define': [
|
|
||||||
'error',
|
|
||||||
{
|
|
||||||
functions: false,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
'block-scoped-var': [
|
'block-scoped-var': [
|
||||||
'error',
|
'error',
|
||||||
],
|
],
|
||||||
|
@ -255,6 +256,7 @@ export default tseslint.config({
|
||||||
'@stylistic/brace-style': [
|
'@stylistic/brace-style': [
|
||||||
'warn',
|
'warn',
|
||||||
'stroustrup',
|
'stroustrup',
|
||||||
|
{ allowSingleLine: true },
|
||||||
],
|
],
|
||||||
'@stylistic/comma-dangle': [
|
'@stylistic/comma-dangle': [
|
||||||
'warn',
|
'warn',
|
Binary file not shown.
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);
|
||||||
|
});
|
||||||
|
});
|
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
|
use flake $FLAKE#node
|
||||||
|
npm ci
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
import { spawnSync } from 'node:child_process';
|
import { spawnSync } from 'node:child_process';
|
||||||
import { writeFileSync } from 'node:fs';
|
import { writeFileSync } from 'node:fs';
|
||||||
|
|
||||||
import { parseArgs } from './lib.ts';
|
import { parseArgs } from './lib';
|
||||||
|
|
||||||
import { updateDocker } from './docker.ts';
|
import { updateDocker } from './docker';
|
||||||
import { updateFirefoxAddons } from '././firefox.ts';
|
import { updateFirefoxAddons } from '././firefox';
|
||||||
import { updateFlakeInputs } from './flake.ts';
|
import { updateFlakeInputs } from './flake';
|
||||||
import { updateCustomPackage, updateVuetorrent } from './misc.ts';
|
import { updateCustomPackage, updateVuetorrent } from './misc';
|
||||||
|
|
||||||
|
|
||||||
/* Constants */
|
/* Constants */
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import { writeFileSync } from 'node:fs';
|
import { writeFileSync } from 'node:fs';
|
||||||
import { spawnSync } from 'node:child_process';
|
import { spawnSync } from 'node:child_process';
|
||||||
|
|
||||||
import { parseFetchurl } from './lib.ts';
|
import { parseFetchurl } from './lib';
|
||||||
|
|
||||||
|
|
||||||
/* Constants */
|
/* Constants */
|
||||||
|
|
|
@ -1,27 +1,6 @@
|
||||||
{
|
{
|
||||||
"compilerOptions": {
|
"$schema": "https://json.schemastore.org/tsconfig",
|
||||||
// Env
|
"extends": "../tsconfig.json",
|
||||||
"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
|
|
||||||
},
|
|
||||||
"includes": [
|
"includes": [
|
||||||
"*.ts",
|
"*.ts",
|
||||||
"**/*.ts",
|
"**/*.ts",
|
||||||
|
|
|
@ -1,49 +0,0 @@
|
||||||
{
|
|
||||||
buildNpmPackage,
|
|
||||||
ffmpeg-full,
|
|
||||||
nodejs_20,
|
|
||||||
typescript,
|
|
||||||
writeShellApplication,
|
|
||||||
...
|
|
||||||
}: let
|
|
||||||
pname = "extract-subs";
|
|
||||||
|
|
||||||
extract-subs = buildNpmPackage {
|
|
||||||
name = "${pname}-npm";
|
|
||||||
src = ./.;
|
|
||||||
npmDepsHash = "sha256-WXkg4e5Nh3+haCbm+XJ1CB7rsA2uV/7eZUaOUl/NVk0=";
|
|
||||||
|
|
||||||
nativeBuildInputs = [
|
|
||||||
nodejs_20
|
|
||||||
typescript
|
|
||||||
];
|
|
||||||
|
|
||||||
buildPhase = ''
|
|
||||||
tsc -p tsconfig.json
|
|
||||||
'';
|
|
||||||
|
|
||||||
installPhase = ''
|
|
||||||
mkdir -p $out/bin
|
|
||||||
mv node_modules package.json $out
|
|
||||||
|
|
||||||
echo '#!/usr/bin/env node' > $out/bin/${pname}
|
|
||||||
cat ./build/main.js >> $out/bin/${pname}
|
|
||||||
rm ./build/main.js
|
|
||||||
chmod +x $out/bin/${pname}
|
|
||||||
|
|
||||||
mv ./build/**.js $out/bin
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
in
|
|
||||||
writeShellApplication {
|
|
||||||
name = pname;
|
|
||||||
|
|
||||||
runtimeInputs = [
|
|
||||||
ffmpeg-full
|
|
||||||
extract-subs
|
|
||||||
];
|
|
||||||
|
|
||||||
text = ''
|
|
||||||
exec ${pname} "$@"
|
|
||||||
'';
|
|
||||||
}
|
|
|
@ -1,114 +0,0 @@
|
||||||
import Ffmpeg from 'fluent-ffmpeg';
|
|
||||||
import { spawnSync as spawn } from 'child_process';
|
|
||||||
|
|
||||||
import { ISO6393To1 } from './lang-codes';
|
|
||||||
|
|
||||||
|
|
||||||
const SPAWN_OPTS = {
|
|
||||||
shell: true,
|
|
||||||
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(',');
|
|
||||||
|
|
||||||
|
|
||||||
const getSubPath = (baseName: string, sub: Ffmpeg.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`;
|
|
||||||
};
|
|
||||||
|
|
||||||
const main = (videoPath: string) => {
|
|
||||||
const subIndexes: number[] = [];
|
|
||||||
const baseName = videoPath.split('/').at(-1)!.replace(/\.[^.]*$/, '');
|
|
||||||
|
|
||||||
// ffprobe the video file to see available sub tracks
|
|
||||||
Ffmpeg.ffprobe(videoPath, (_e, data) => {
|
|
||||||
if (!data?.streams) {
|
|
||||||
console.error('Couldn\'t find streams in video file');
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
languages.forEach((lang) => {
|
|
||||||
let subs = data.streams.filter((s) => {
|
|
||||||
return s['tags'] &&
|
|
||||||
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
|
|
||||||
subs = subs.filter((s) => s.codec_name !== 'hdmv_pgs_subtitle');
|
|
||||||
|
|
||||||
if (subs.length === 0) {
|
|
||||||
console.warn(`No subtitle tracks were found for ${lang}`);
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
subs.forEach((sub) => {
|
|
||||||
const subFile = getSubPath(baseName, sub);
|
|
||||||
|
|
||||||
// Extract subtitle
|
|
||||||
spawn('ffmpeg', [
|
|
||||||
'-i', `'${videoPath}'`,
|
|
||||||
'-map', `"0:${sub.index}"`, `'${subFile}'`,
|
|
||||||
], SPAWN_OPTS);
|
|
||||||
|
|
||||||
subIndexes.push(sub.index);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
// Delete subtitles from video
|
|
||||||
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);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const escapePath = (p: string): string => p.replaceAll("'", "'\\''");
|
|
||||||
|
|
||||||
// Check if there are 2 params
|
|
||||||
if (video && languages) {
|
|
||||||
main(escapePath(video));
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
console.error('Error: no argument passed');
|
|
||||||
process.exit(1);
|
|
||||||
}
|
|
|
@ -1,18 +0,0 @@
|
||||||
{
|
|
||||||
"devDependencies": {
|
|
||||||
"@eslint/js": "9.12.0",
|
|
||||||
"@stylistic/eslint-plugin": "2.9.0",
|
|
||||||
"@types/eslint__js": "8.42.3",
|
|
||||||
"@types/node": "22.7.5",
|
|
||||||
"eslint": "9.12.0",
|
|
||||||
"eslint-plugin-jsdoc": "50.3.2",
|
|
||||||
"fzf": "0.5.2",
|
|
||||||
"jiti": "2.3.3",
|
|
||||||
"typescript": "5.6.3",
|
|
||||||
"typescript-eslint": "8.8.1"
|
|
||||||
},
|
|
||||||
"dependencies": {
|
|
||||||
"@types/fluent-ffmpeg": "2.1.27",
|
|
||||||
"fluent-ffmpeg": "2.1.3"
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,19 +0,0 @@
|
||||||
{
|
|
||||||
"compilerOptions": {
|
|
||||||
"target": "ESNEXT",
|
|
||||||
"module": "commonjs",
|
|
||||||
"lib": [
|
|
||||||
"ES2022"
|
|
||||||
],
|
|
||||||
"outDir": "build",
|
|
||||||
"strict": true,
|
|
||||||
"moduleResolution": "node",
|
|
||||||
"baseUrl": ".",
|
|
||||||
"types": [
|
|
||||||
"@types/fluent-ffmpeg"
|
|
||||||
],
|
|
||||||
"skipLibCheck": true,
|
|
||||||
"forceConsistentCasingInFileNames": true,
|
|
||||||
"esModuleInterop": true
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
Reference in a new issue