multi-downloader-nx_mirror/modules/module.app-args.ts
2025-11-26 18:56:53 -08:00

191 lines
5.5 KiB
TypeScript

import yargs, { Choices } from 'yargs';
import { args, AvailableMuxer, groups } from './module.args';
import { LanguageItem } from './module.langsData';
import { DownloadInfo } from '../@types/messageHandler';
import { HLSCallback } from './hls-download';
import leven from 'leven';
import { console } from './log';
import { CrunchyVideoPlayStreams, CrunchyAudioPlayStreams } from '../@types/enums';
import pj from '../package.json';
export let argvC: {
[x: string]: unknown;
ccTag: string;
defaultAudio: LanguageItem;
defaultSub: LanguageItem;
ffmpegOptions: string[];
mkvmergeOptions: string[];
force: 'Y' | 'y' | 'N' | 'n' | 'C' | 'c';
skipUpdate: boolean;
videoTitle: string;
override: string[];
fsRetryTime: number;
forceMuxer: AvailableMuxer | undefined;
username: string | undefined;
password: string | undefined;
token: string | undefined;
silentAuth: boolean;
skipSubMux: boolean;
downloadArchive: boolean;
addArchive: boolean;
but: boolean;
auth: boolean | undefined;
dlFonts: boolean | undefined;
search: string | undefined;
'search-type': string;
page: number | undefined;
locale: string;
new: boolean | undefined;
'movie-listing': string | undefined;
'show-raw': string | undefined;
'season-raw': string | undefined;
series: string | undefined;
s: string | undefined;
srz: string | undefined;
e: string | undefined;
extid: string | undefined;
q: number;
x: number;
cstream: keyof typeof CrunchyVideoPlayStreams;
vstream: keyof typeof CrunchyVideoPlayStreams;
astream: keyof typeof CrunchyAudioPlayStreams;
tsd: boolean | undefined;
partsize: number;
hslang: string;
dlsubs: string[];
skipMuxOnSubFail: boolean;
novids: boolean | undefined;
noaudio: boolean | undefined;
nosubs: boolean | undefined;
dubLang: string[];
all: boolean;
fontSize: number;
combineLines: boolean;
allDubs: boolean;
timeout: number;
waittime: number;
simul: boolean;
mp4: boolean;
skipmux: boolean | undefined;
fileName: string;
numbers: number;
nosess: string;
debug: boolean | undefined;
raw: boolean;
rawoutput: string;
nocleanup: boolean;
help: boolean | undefined;
service: 'crunchy' | 'hidive' | 'adn';
update: boolean;
fontName: string | undefined;
_: (string | number)[];
$0: string;
dlVideoOnce: boolean;
chapters: boolean;
removeBumpers: boolean;
originalFontSize: boolean;
keepAllVideos: boolean;
syncTiming: boolean;
callbackMaker?: (data: DownloadInfo) => HLSCallback;
// Subtitle Fix Options
noASSConv: boolean;
noSubFix: boolean;
srtAssFix: boolean;
layoutResFix: boolean;
scaledBorderAndShadowFix: boolean;
scaledBorderAndShadow: 'yes' | 'no';
originalScriptFix: boolean;
// Proxy
proxy: string;
proxyAll: boolean;
};
export type ArgvType = typeof argvC;
const appArgv = (
cfg: {
[key: string]: unknown;
},
isGUI = false
) => {
if (argvC) return argvC;
yargs(process.argv.slice(2));
const argv = getArgv(cfg, isGUI).parseSync();
argvC = argv;
return argv;
};
const overrideArguments = (cfg: { [key: string]: unknown }, override: Partial<typeof argvC>, isGUI = false) => {
const argv = getArgv(cfg, isGUI)
.middleware((ar) => {
for (const key of Object.keys(override)) {
ar[key] = override[key];
}
})
.parseSync();
argvC = argv;
};
export { appArgv, overrideArguments };
const getArgv = (cfg: { [key: string]: unknown }, isGUI: boolean) => {
const parseDefault = <T = unknown>(key: string, _default: T): T => {
if (Object.prototype.hasOwnProperty.call(cfg, key)) {
return cfg[key] as T;
} else return _default;
};
const argv = yargs
.parserConfiguration({
'duplicate-arguments-array': false,
'camel-case-expansion': false
})
.wrap(yargs.terminalWidth())
.usage('Usage: $0 [options]')
.version(pj.version)
.help(true);
//.strictOptions()
const data = args.map((a) => {
return {
...a,
demandOption: !isGUI && a.demandOption,
group: groups[a.group],
default: typeof a.default === 'object' && !Array.isArray(a.default) ? parseDefault((a.default as any).name || a.name, (a.default as any).default) : a.default
};
});
for (const item of data)
argv.option(item.name, {
...item,
coerce: (value) => {
if (item.transformer) {
return item.transformer(value);
} else {
return value;
}
},
choices: item.name === 'service' && isGUI ? undefined : (item.choices as unknown as Choices)
});
// Custom logic for suggesting corrections for misspelled options
argv.middleware((argv: Record<string, any>) => {
// List of valid options
const validOptions = [...args.map((a) => a.name), ...(args.map((a) => a.alias).filter((alias) => alias !== undefined) as string[])];
const unknownOptions = Object.keys(argv).filter((key) => !validOptions.includes(key) && key !== '_' && key !== '$0'); // Filter out known options
const suggestedOptions: Record<string, boolean> = {};
unknownOptions.forEach((actualOption) => {
const closestOption = validOptions.find((option) => {
const levenVal = leven(option, actualOption);
return levenVal <= 2 && levenVal > 0;
});
if (closestOption && !suggestedOptions[closestOption]) {
suggestedOptions[closestOption] = true;
console.info(`Unknown option ${actualOption}, did you mean ${closestOption}?`);
} else if (!suggestedOptions[actualOption]) {
suggestedOptions[actualOption] = true;
console.info(`Unknown option ${actualOption}`);
}
});
});
return argv as unknown as yargs.Argv<typeof argvC>;
};