multi-downloader-nx_mirror/modules/module.args.ts
stratumadev 5f192a31c0 feat(crunchy): add subtitleTimestampFix function (#1121)
Fix Crunchyroll subtitles with invalid durations.
If start > video length  the line is deleted.
If only end > video length  end is trimmed to video duration.
2025-11-28 00:40:11 +01:00

1089 lines
26 KiB
TypeScript

import { dubLanguageCodes, languages, searchLocales, subtitleLanguagesFilter } from './module.langsData';
import { CrunchyVideoPlayStreams, CrunchyAudioPlayStreams } from '../@types/enums';
const groups = {
auth: 'Authentication:',
fonts: 'Fonts:',
search: 'Search:',
dl: 'Downloading:',
mux: 'Muxing:',
fileName: 'Filename Template:',
debug: 'Debug:',
util: 'Utilities:',
help: 'Help:',
gui: 'GUI:'
};
export type AvailableFilenameVars = 'title' | 'episode' | 'showTitle' | 'seriesTitle' | 'season' | 'width' | 'height' | 'service';
const availableFilenameVars: AvailableFilenameVars[] = ['title', 'episode', 'showTitle', 'seriesTitle', 'season', 'width', 'height', 'service'];
export type AvailableMuxer = 'ffmpeg' | 'mkvmerge';
export const muxer: AvailableMuxer[] = ['ffmpeg', 'mkvmerge'];
export type TAppArg<T extends boolean | string | number | unknown[], K = any> = {
name: string;
group: keyof typeof groups;
type: 'boolean' | 'string' | 'number' | 'array';
choices?: T[];
alias?: string;
describe: string;
docDescribe: true | string; // true means use describe for the docs
default?:
| T
| {
default: T | undefined;
name?: string;
};
service: Array<'crunchy' | 'hidive' | 'adn' | 'all'>;
usage: string; // -(-)${name} will be added for each command,
demandOption?: true;
transformer?: (value: T) => K;
};
const args: TAppArg<boolean | number | string | unknown[]>[] = [
{
name: 'absolute',
describe: 'Use absolute numbers for the episode',
docDescribe: 'Use absolute numbers for the episode. If not set, it will use the default index numbers',
group: 'dl',
service: ['crunchy'],
type: 'boolean',
usage: ''
},
{
name: 'auth',
describe: 'Enter authentication mode',
type: 'boolean',
group: 'auth',
service: ['all'],
docDescribe:
'Most of the shows on both services are only accessible if you payed for the service.' +
'\nIn order for them to know who you are you are required to log in.' +
'\nIf you trigger this command, you will be prompted for the username and password for the selected service',
usage: ''
},
{
name: 'dlFonts',
group: 'fonts',
describe: 'Download all required fonts for mkv muxing',
docDescribe: 'Crunchyroll uses a variaty of fonts for the subtitles.' + '\nUse this command to download all the fonts and add them to the muxed **mkv** file.',
service: ['crunchy'],
type: 'boolean',
usage: ''
},
{
name: 'search',
group: 'search',
alias: 'f',
describe: 'Search of an anime by the given string',
type: 'string',
docDescribe: true,
service: ['all'],
usage: '${search}'
},
{
name: 'search-type',
describe: 'Search by type',
docDescribe: 'Search only for type of anime listings (e.g. episodes, series)',
group: 'search',
service: ['crunchy'],
type: 'string',
usage: '${type}',
choices: ['', 'top_results', 'series', 'movie_listing', 'episode'],
default: {
default: ''
}
},
{
name: 'page',
alias: 'p',
describe: 'Set the page number for search results',
docDescribe: 'The output is organized in pages. Use this command to output the items for the given page',
group: 'search',
service: ['crunchy', 'hidive'],
type: 'number',
usage: '${page}'
},
{
name: 'locale',
describe: 'Set the service locale',
docDescribe: 'Set the local that will be used for the API.',
group: 'search',
choices: [...searchLocales.filter((a) => a !== undefined)] as string[],
default: {
default: 'en-US'
},
type: 'string',
service: ['crunchy', 'adn'],
usage: '${locale}'
},
{
group: 'search',
name: 'new',
describe: 'Get last updated series list',
docDescribe: true,
service: ['crunchy', 'hidive'],
type: 'boolean',
usage: ''
},
{
group: 'dl',
alias: 'flm',
name: 'movie-listing',
describe: 'Get video list by Movie Listing ID',
docDescribe: true,
service: ['crunchy'],
type: 'string',
usage: '${ID}'
},
{
group: 'dl',
alias: 'sraw',
name: 'show-raw',
describe: 'Get Raw Show data',
docDescribe: true,
service: ['crunchy'],
type: 'string',
usage: '${ID}'
},
{
group: 'dl',
alias: 'seraw',
name: 'season-raw',
describe: 'Get Raw Season data',
docDescribe: true,
service: ['crunchy'],
type: 'string',
usage: '${ID}'
},
{
group: 'dl',
alias: 'slraw',
name: 'show-list-raw',
describe: 'Get Raw Show list data',
docDescribe: true,
service: ['crunchy'],
type: 'boolean',
usage: ''
},
{
name: 'series',
group: 'dl',
alias: 'srz',
describe: 'Get season list by series ID',
docDescribe: 'Requested is the ID of a show not a season.',
service: ['crunchy'],
type: 'string',
usage: '${ID}'
},
{
name: 's',
group: 'dl',
type: 'string',
describe: 'Set the season ID',
docDescribe: 'Used to set the season ID to download from',
service: ['all'],
usage: '${ID}'
},
{
name: 'e',
group: 'dl',
describe: 'Set the episode(s) to download from any given show',
docDescribe:
'Set the episode(s) to download from any given show.' +
'\nFor multiple selection: 1-4 OR 1,2,3,4 ' +
'\nFor special episodes: S1-4 OR S1,S2,S3,S4 where S is the special letter',
service: ['all'],
type: 'string',
usage: '${selection}',
alias: 'episode'
},
{
name: 'extid',
group: 'dl',
describe: 'Set the external id to lookup/download',
docDescribe: 'Set the external id to lookup/download.' + '\nAllows you to download or view legacy Crunchyroll Ids ',
service: ['crunchy'],
type: 'string',
usage: '${selection}',
alias: 'externalid'
},
{
name: 'q',
group: 'dl',
describe: 'Set the quality level. Use 0 to use the maximum quality.',
default: {
default: 0
},
docDescribe: true,
service: ['all'],
type: 'number',
usage: '${qualityLevel}'
},
{
name: 'dlVideoOnce',
describe: 'Download only once the video with the best selected quality',
type: 'boolean',
group: 'dl',
service: ['crunchy'],
docDescribe:
'If selected, the best selected quality will be downloaded only for the first language,' +
'\nthen the worst video quality with the same audio quality will be downloaded for every other language.' +
'\nBy the later merge of the videos, no quality difference will be present.' +
'\nThis will speed up the download speed, if multiple languages are selected.',
usage: '',
default: {
default: false
}
},
{
name: 'chapters',
describe: 'Will fetch the chapters and add them into the final video',
type: 'boolean',
group: 'dl',
service: ['crunchy', 'adn'],
docDescribe: 'Will fetch the chapters and add them into the final video.',
usage: '',
default: {
default: true
}
},
{
name: 'removeBumpers',
describe: 'Remove bumpers from final video',
type: 'boolean',
group: 'dl',
service: ['hidive'],
docDescribe:
'If selected, it will remove the bumpers such as the hidive intro from the final file.' +
'\nCurrently disabling this sometimes results in bugs such as video/audio desync',
usage: '',
default: {
default: true
}
},
{
name: 'originalFontSize',
describe: 'Keep original font size',
type: 'boolean',
group: 'dl',
service: ['hidive'],
docDescribe: 'If selected, it will prefer to keep the original Font Size defined by the service.',
usage: '',
default: {
default: true
}
},
{
name: 'x',
group: 'dl',
describe: 'Select the server to use',
choices: [1, 2, 3, 4],
default: {
default: 1
},
type: 'number',
alias: 'server',
docDescribe: true,
service: ['all'],
usage: '${server}'
},
{
name: 'cstream',
group: 'dl',
alias: 'cs',
service: ['crunchy'],
type: 'string',
describe: '(Please use --vstream and --astream instead, this will deprecate soon)',
choices: [...Object.keys(CrunchyVideoPlayStreams), 'none'],
docDescribe: true,
usage: '${device}'
},
{
name: 'vstream',
group: 'dl',
alias: 'vs',
service: ['crunchy'],
type: 'string',
describe: 'Select a specific Crunchyroll video playback endpoint by device. androidtv provides the best video (CBR).',
choices: [...Object.keys(CrunchyVideoPlayStreams), 'none'],
default: {
default: 'androidtv'
},
docDescribe: true,
usage: '${device}'
},
{
name: 'astream',
group: 'dl',
alias: 'as',
service: ['crunchy'],
type: 'string',
describe: 'Select a specific Crunchyroll audio playback endpoint by device. android provides the best audio (192 kbps).',
choices: [...Object.keys(CrunchyAudioPlayStreams), 'none'],
default: {
default: 'android'
},
docDescribe: true,
usage: '${device}'
},
{
name: 'tsd',
group: 'dl',
describe: '(Total Session Death) Kills all active Crunchyroll Streaming Sessions to prevent getting the "TOO_MANY_ACTIVE_STREAMS" error.',
docDescribe: true,
service: ['crunchy'],
type: 'boolean',
usage: '',
default: {
default: false
}
},
{
name: 'hslang',
group: 'dl',
describe: 'Download video with specific hardsubs',
choices: subtitleLanguagesFilter.slice(1),
default: {
default: 'none'
},
type: 'string',
usage: '${hslang}',
docDescribe: true,
service: ['crunchy']
},
{
name: 'dlsubs',
group: 'dl',
describe:
'Download subtitles by language tag (space-separated)' +
`\nCrunchy Only: ${languages
.filter((a) => a.cr_locale)
.map((a) => a.locale)
.join(', ')}`,
docDescribe: true,
service: ['all'],
type: 'array',
choices: subtitleLanguagesFilter,
default: {
default: ['all']
},
usage: '${sub1} ${sub2}'
},
{
name: 'skipMuxOnSubFail',
group: 'dl',
describe: 'Skips muxing when a subtitle download fails.',
docDescribe: true,
service: ['all'],
type: 'boolean',
usage: '',
default: {
default: false
}
},
{
name: 'noASSConv',
group: 'dl',
describe: 'Disables VTT conversion to ASS.',
docDescribe: true,
service: ['crunchy', 'hidive'],
type: 'boolean',
usage: '',
default: {
default: false
}
},
{
name: 'noSubFix',
group: 'dl',
describe: 'Disables all subtitle fixes and downloads raw subtitles.',
docDescribe: true,
service: ['crunchy'],
type: 'boolean',
usage: '',
default: {
default: false
}
},
{
name: 'srtAssFix',
group: 'dl',
describe: 'Fixes the recently changed Crunchyroll subtitles provided by Closed Caption Converter.',
docDescribe: true,
service: ['crunchy'],
type: 'boolean',
usage: '',
default: {
default: true
}
},
{
name: 'layoutResFix',
group: 'dl',
describe: 'Applies the LayoutRes Fix to all ASS subtitles.',
docDescribe: true,
service: ['crunchy'],
type: 'boolean',
usage: '',
default: {
default: true
}
},
{
name: 'scaledBorderAndShadowFix',
group: 'dl',
describe: 'Applies the ScaledBorderAndShadow Fix to all ASS subtitles.',
docDescribe: true,
service: ['crunchy'],
type: 'boolean',
usage: '',
default: {
default: true
}
},
{
name: 'scaledBorderAndShadow',
group: 'dl',
service: ['crunchy'],
type: 'string',
describe: 'Select if ScaledBorderAndShadow should be set to "yes" or "no".',
choices: ['yes', 'no'],
default: {
default: 'yes'
},
docDescribe: true,
usage: '${yes/no}'
},
{
name: 'originalScriptFix',
group: 'dl',
describe: 'Removes the URL in the Original Script line of the ASS subtitles, it prevents from bricking the subs in VLC (Fonts not loading when url not returning 200).',
docDescribe: true,
service: ['crunchy'],
type: 'boolean',
usage: '',
default: {
default: true
}
},
{
name: 'subtitleTimestampFix',
group: 'dl',
describe:
'Fixes subtitle dialogues that go over the video length (deletes dialogues where start is over video length and updates the end timestamp when end is over video length).',
docDescribe: true,
service: ['crunchy'],
type: 'boolean',
usage: '',
default: {
default: false
}
},
{
name: 'novids',
group: 'dl',
describe: 'Skip downloading videos',
docDescribe: true,
service: ['all'],
type: 'boolean',
usage: ''
},
{
name: 'noaudio',
group: 'dl',
describe: 'Skip downloading audio',
docDescribe: true,
service: ['crunchy', 'hidive'],
type: 'boolean',
usage: ''
},
{
name: 'nosubs',
group: 'dl',
describe: 'Skip downloading subtitles',
docDescribe: true,
service: ['all'],
type: 'boolean',
usage: ''
},
{
name: 'dubLang',
describe:
'Set the language to download: ' +
`\nCrunchy Only: ${languages
.filter((a) => a.cr_locale)
.map((a) => a.code)
.join(', ')}`,
docDescribe: true,
group: 'dl',
choices: dubLanguageCodes,
default: {
default: [dubLanguageCodes.slice(-1)[0]]
},
service: ['all'],
type: 'array',
usage: '${dub1} ${dub2}'
},
{
name: 'all',
describe: 'Used to download all episodes from the show',
docDescribe: true,
group: 'dl',
service: ['all'],
default: {
default: false
},
type: 'boolean',
usage: ''
},
{
name: 'fontSize',
describe: 'Used to set the fontsize of the subtitles',
default: {
default: 55
},
docDescribe: 'When converting the subtitles to ass, this will change the font size' + '\nIn most cases, requires "--originaFontSize false" to take effect',
group: 'dl',
service: ['all'],
type: 'number',
usage: '${fontSize}'
},
{
name: 'combineLines',
describe: 'Merge adjacent lines with same style and text',
docDescribe: 'If selected, will prevent a line from shifting downwards',
group: 'dl',
service: ['hidive'],
type: 'boolean',
usage: ''
},
{
name: 'allDubs',
describe: 'If selected, all available dubs will get downloaded',
docDescribe: true,
group: 'dl',
service: ['all'],
type: 'boolean',
usage: ''
},
{
name: 'timeout',
group: 'dl',
type: 'number',
describe: 'Set the timeout of all download reqests. Set in millisecods',
docDescribe: true,
service: ['all'],
usage: '${timeout}',
default: {
default: 15 * 1000
}
},
{
name: 'waittime',
group: 'dl',
type: 'number',
describe: 'Set the time the program waits between downloads. Set in millisecods',
docDescribe: true,
service: ['crunchy', 'hidive'],
usage: '${waittime}',
default: {
default: 0 * 1000
}
},
{
name: 'simul',
group: 'dl',
describe: 'Force downloading simulcast version instead of uncut version (if available).',
docDescribe: true,
service: ['hidive'],
type: 'boolean',
usage: '',
default: {
default: false
}
},
{
name: 'mp4',
group: 'mux',
describe: 'Mux video into mp4',
docDescribe: 'If selected, the output file will be an mp4 file (not recommended tho)',
service: ['all'],
type: 'boolean',
usage: '',
default: {
default: false
}
},
{
name: 'keepAllVideos',
group: 'mux',
describe: 'Keeps all videos when merging instead of discarding extras',
docDescribe: 'If set to true, it will keep all videos in the merge process, rather than discarding the extra videos.',
service: ['crunchy', 'hidive'],
type: 'boolean',
usage: '',
default: {
default: false
}
},
{
name: 'syncTiming',
group: 'mux',
describe: 'Attempts to sync timing for multi-dub downloads EXPERIMENTAL',
docDescribe:
'If enabled attempts to sync timing for multi-dub downloads.' +
'\nNOTE: This is currently experimental and syncs audio and subtitles, though subtitles has a lot of guesswork' +
'\nIf you find bugs with this, please report it in the discord or github',
service: ['crunchy', 'hidive'],
type: 'boolean',
usage: '',
default: {
default: false
}
},
{
name: 'skipmux',
describe: 'Skip muxing video, audio and subtitles',
docDescribe: true,
group: 'mux',
service: ['all'],
type: 'boolean',
usage: ''
},
{
name: 'fileName',
group: 'fileName',
describe: `Set the filename template. Use \${variable_name} to insert variables.\nYou can also create folders by inserting a path seperator in the filename\nYou may use ${availableFilenameVars
.map((a) => `'${a}'`)
.join(', ')} as variables.`,
docDescribe: true,
service: ['all'],
type: 'string',
usage: '${fileName}',
default: {
default: '[${service}] ${showTitle} - S${season}E${episode} [${height}p]'
}
},
{
name: 'numbers',
group: 'fileName',
describe: `Set how long a number in the title should be at least.\n${[
[3, 5, '005'],
[2, 1, '01'],
[1, 20, '20']
]
.map((val) => `Set in config: ${val[0]}; Episode number: ${val[1]}; Output: ${val[2]}`)
.join('\n')}`,
type: 'number',
default: {
default: 2
},
docDescribe: true,
service: ['all'],
usage: '${number}'
},
{
name: 'nosess',
group: 'debug',
describe: 'Reset session cookie for testing purposes',
docDescribe: true,
service: ['all'],
type: 'boolean',
usage: '',
default: {
default: false
}
},
{
name: 'debug',
group: 'debug',
describe: 'Debug mode (tokens may be revealed in the console output)',
docDescribe: true,
service: ['all'],
type: 'boolean',
usage: '',
default: {
default: false
}
},
{
name: 'nocleanup',
describe: "Don't delete subtitle, audio and video files after muxing",
docDescribe: true,
group: 'mux',
service: ['all'],
type: 'boolean',
default: {
default: false
},
usage: ''
},
{
name: 'help',
alias: 'h',
describe: 'Show the help output',
docDescribe: true,
group: 'help',
service: ['all'],
type: 'boolean',
usage: ''
},
{
name: 'service',
describe: 'Set the service you want to use',
docDescribe: true,
group: 'util',
service: ['all'],
type: 'string',
choices: ['crunchy', 'hidive', 'adn'],
usage: '${service}',
default: {
default: ''
},
demandOption: true
},
{
name: 'update',
group: 'util',
describe: 'Force the tool to check for updates (code version only)',
docDescribe: true,
service: ['all'],
type: 'boolean',
usage: ''
},
{
name: 'fontName',
group: 'fonts',
describe: 'Set the font to use in subtiles',
docDescribe: true,
service: ['hidive', 'adn'],
type: 'string',
usage: '${fontName}'
},
{
name: 'but',
describe: 'Download everything but the -e selection',
docDescribe: true,
group: 'dl',
service: ['all'],
type: 'boolean',
usage: ''
},
{
name: 'downloadArchive',
describe: 'Used to download all archived shows',
group: 'dl',
docDescribe: true,
service: ['all'],
type: 'boolean',
usage: ''
},
{
name: 'addArchive',
describe: 'Used to add the `-s` and `--srz` to downloadArchive',
group: 'dl',
docDescribe: true,
service: ['all'],
type: 'boolean',
usage: ''
},
{
name: 'skipSubMux',
describe: 'Skip muxing the subtitles',
docDescribe: true,
group: 'mux',
service: ['all'],
type: 'boolean',
usage: '',
default: {
default: false
}
},
{
name: 'partsize',
describe: 'Set the amount of parts to download at once',
docDescribe: 'Set the amount of parts to download at once\nIf you have a good connection try incresing this number to get a higher overall speed',
group: 'dl',
service: ['all'],
type: 'number',
usage: '${amount}',
default: {
default: 10
}
},
{
name: 'username',
describe: 'Set the username to use for the authentication. If not provided, you will be prompted for the input',
docDescribe: true,
group: 'auth',
service: ['all'],
type: 'string',
usage: '${username}',
default: {
default: undefined
}
},
{
name: 'password',
describe: 'Set the password to use for the authentication. If not provided, you will be prompted for the input',
docDescribe: true,
group: 'auth',
service: ['all'],
type: 'string',
usage: '${password}',
default: {
default: undefined
}
},
{
name: 'silentAuth',
describe: 'Authenticate every time the script runs. Use at your own risk.',
docDescribe: true,
group: 'auth',
service: ['crunchy'],
type: 'boolean',
usage: '',
default: {
default: false
}
},
{
name: 'token',
describe: 'Allows you to login with your token (Example on crunchy is Refresh Token/etp-rt cookie)',
docDescribe: true,
group: 'auth',
service: ['crunchy'],
type: 'string',
usage: '${token}',
default: {
default: undefined
}
},
{
name: 'forceMuxer',
describe: "Force the program to use said muxer or don't mux if the given muxer is not present",
docDescribe: true,
group: 'mux',
service: ['all'],
type: 'string',
usage: '${muxer}',
choices: muxer,
default: {
default: undefined
}
},
{
name: 'fsRetryTime',
describe: 'Set the time the downloader waits before retrying if an error while writing the file occurs',
docDescribe: true,
group: 'dl',
service: ['all'],
type: 'number',
usage: '${time in seconds}',
default: {
default: 5
}
},
{
name: 'override',
describe: 'Override a template variable',
docDescribe: true,
group: 'fileName',
service: ['all'],
type: 'array',
usage: '"${toOverride}=\'${value}\'"',
default: {
default: []
}
},
{
name: 'videoTitle',
describe: 'Set the video track name of the merged file',
docDescribe: true,
group: 'mux',
service: ['all'],
type: 'string',
usage: '${title}'
},
{
name: 'skipUpdate',
describe: "If true, the tool won't check for updates",
docDescribe: true,
group: 'util',
service: ['all'],
type: 'boolean',
usage: '',
default: {
default: false
}
},
{
name: 'raw',
describe: 'If true, the tool will output the raw data from the API (Where applicable, the feature is a WIP)',
docDescribe: true,
group: 'util',
service: ['all'],
type: 'boolean',
usage: '',
default: {
default: false
}
},
{
name: 'rawoutput',
describe: 'Provide a path to output the raw data from the API into a file (Where applicable, the feature is a WIP)',
docDescribe: true,
group: 'util',
service: ['all'],
type: 'string',
usage: '',
default: {
default: ''
}
},
{
name: 'force',
describe: "Set the default option for the 'alredy exists' prompt",
docDescribe: 'If a file already exists, the tool will ask you how to proceed. With this, you can answer in advance.',
group: 'dl',
service: ['all'],
type: 'string',
usage: '${option}',
choices: ['y', 'Y', 'n', 'N', 'c', 'C']
},
{
name: 'mkvmergeOptions',
describe: 'Set the options given to mkvmerge',
docDescribe: true,
group: 'mux',
service: ['all'],
type: 'array',
usage: '${args}',
default: {
default: ['--no-date', '--disable-track-statistics-tags', '--engage no_variable_data']
}
},
{
name: 'ffmpegOptions',
describe: 'Set the options given to ffmpeg',
docDescribe: true,
group: 'mux',
service: ['all'],
type: 'array',
usage: '${args}',
default: {
default: []
}
},
{
name: 'defaultAudio',
describe: `Set the default audio track by language code\nPossible Values: ${languages.map((a) => a.code).join(', ')}`,
docDescribe: true,
group: 'mux',
service: ['all'],
type: 'string',
usage: '${args}',
default: {
default: 'eng'
},
transformer: (val) => {
const item = languages.find((a) => a.code === val);
if (!item) {
throw new Error(`Unable to find language code ${val}!`);
}
return item;
}
},
{
name: 'defaultSub',
describe: `Set the default subtitle track by language code\nPossible Values: ${languages.map((a) => a.code).join(', ')}`,
docDescribe: true,
group: 'mux',
service: ['all'],
type: 'string',
usage: '${args}',
default: {
default: 'eng'
},
transformer: (val) => {
const item = languages.find((a) => a.code === val);
if (!item) {
throw new Error(`Unable to find language code ${val}!`);
}
return item;
}
},
{
name: 'ccTag',
describe: 'Used to set the name for subtitles that contain tranlations for none verbal communication (e.g. signs)',
docDescribe: true,
group: 'fileName',
service: ['all'],
type: 'string',
usage: '${tag}',
default: {
default: 'cc'
}
},
{
name: 'proxy',
describe: 'Uses Proxy on geo-restricted or geo-defining endpoints (e.g. https://127.0.0.1:1080 or http://127.0.0.1:1080)',
docDescribe: true,
group: 'util',
service: ['all'],
type: 'string',
usage: '${proxy_url}',
default: {
default: ''
}
},
{
name: 'proxyAll',
describe: 'Proxies everything, not recommended. Proxy needs to be defined.',
docDescribe: true,
group: 'util',
service: ['all'],
type: 'boolean',
usage: '',
default: {
default: false
}
}
];
const getDefault = <T extends boolean | string | number | unknown[]>(name: string, cfg: Record<string, T>): T => {
const option = args.find((item) => item.name === name);
if (!option) throw new Error(`Unable to find option ${name}`);
if (option.default === undefined) throw new Error(`Option ${name} has no default`);
if (typeof option.default === 'object') {
if (Array.isArray(option.default)) return option.default as T;
if (Object.prototype.hasOwnProperty.call(cfg, (option.default as any).name ?? option.name)) {
return cfg[(option.default as any).name ?? option.name];
} else {
return (option.default as any).default as T;
}
} else {
return option.default as T;
}
};
const buildDefault = () => {
const data: Record<string, unknown> = {};
const defaultArgs = args.filter((a) => a.default);
defaultArgs.forEach((item) => {
if (typeof item.default === 'object') {
if (Array.isArray(item.default)) {
data[item.name] = item.default;
} else {
data[(item.default as any).name ?? item.name] = (item.default as any).default;
}
} else {
data[item.name] = item.default;
}
});
return data;
};
export { getDefault, buildDefault, args, groups, availableFilenameVars };