This commit is contained in:
Izuco 2021-11-01 20:38:58 +01:00
parent 8b557abdf1
commit 3c599cd48e
No known key found for this signature in database
GPG key ID: 318460063D70949F
20 changed files with 1646 additions and 1714 deletions

View file

@ -1,2 +1,3 @@
lib
/videos/*.ts
/videos/*.ts
crunchy

View file

@ -2,21 +2,18 @@ export interface CrunchyEpisodeList {
__class__: string;
__href__: string;
__resource_key__: string;
__links__: Actions;
__actions__: Actions;
__links__: unknown;
__actions__: unknown;
total: number;
items: Item[];
}
export interface Actions {
}
export interface Item {
__class__: Class;
__href__: string;
__resource_key__: string;
__links__: Links;
__actions__: Actions;
__actions__: unknown;
id: string;
channel_id: ChannelID;
series_id: string;
@ -64,15 +61,15 @@ export interface Item {
}
export enum Class {
Episode = "episode",
Episode = 'episode',
}
export interface Links {
ads: Ads;
"episode/channel": Ads;
"episode/next_episode"?: Ads;
"episode/season": Ads;
"episode/series": Ads;
'episode/channel': Ads;
'episode/next_episode'?: Ads;
'episode/season': Ads;
'episode/series': Ads;
streams?: Ads;
}
@ -86,12 +83,12 @@ export interface AdBreak {
}
export enum AdBreakType {
Midroll = "midroll",
Preroll = "preroll",
Midroll = 'midroll',
Preroll = 'preroll',
}
export enum ChannelID {
Crunchyroll = "crunchyroll",
Crunchyroll = 'crunchyroll',
}
export interface Images {
@ -106,17 +103,17 @@ export interface Thumbnail {
}
export enum ThumbnailType {
Thumbnail = "thumbnail",
Thumbnail = 'thumbnail',
}
export enum SubtitleLocale {
ArSA = "ar-SA",
DeDE = "de-DE",
EnUS = "en-US",
Es419 = "es-419",
EsES = "es-ES",
FrFR = "fr-FR",
ItIT = "it-IT",
PtBR = "pt-BR",
RuRU = "ru-RU",
ArSA = 'ar-SA',
DeDE = 'de-DE',
EnUS = 'en-US',
Es419 = 'es-419',
EsES = 'es-ES',
FrFR = 'fr-FR',
ItIT = 'it-IT',
PtBR = 'pt-BR',
RuRU = 'ru-RU',
}

View file

@ -5,13 +5,11 @@ export interface CrunchySearch {
__href__: string;
__resource_key__: string;
__links__: CrunchySearchLinks;
__actions__: Actions;
__actions__: unknown;
total: number;
items: CrunchySearchItem[];
}
export interface Actions {
}
export interface CrunchySearchLinks {
continuation?: Continuation;
@ -26,14 +24,14 @@ export interface CrunchySearchItem {
__href__: string;
__resource_key__: string;
__links__: CrunchySearchLinks;
__actions__: Actions;
__actions__: unknown;
type: string;
total: number;
items: ItemItem[];
}
export interface ItemItem {
__actions__: Actions;
__actions__: unknown;
__class__: Class;
__href__: string;
__links__: PurpleLinks;
@ -69,19 +67,19 @@ export interface ItemItem {
}
export enum Class {
Panel = "panel",
Panel = 'panel',
}
export interface PurpleLinks {
resource: Continuation;
"resource/channel": Continuation;
"episode/season"?: Continuation;
"episode/series"?: Continuation;
'resource/channel': Continuation;
'episode/season'?: Continuation;
'episode/series'?: Continuation;
streams?: Continuation;
}
export enum ChannelID {
Crunchyroll = "crunchyroll",
Crunchyroll = 'crunchyroll',
}
export interface EpisodeMetadata {
@ -119,14 +117,14 @@ export interface AdBreak {
}
export enum AdBreakType {
Midroll = "midroll",
Preroll = "preroll",
Midroll = 'midroll',
Preroll = 'preroll',
}
export enum TenantCategory {
Action = "Action",
Drama = "Drama",
SciFi = "Sci-Fi",
Action = 'Action',
Drama = 'Drama',
SciFi = 'Sci-Fi',
}
export interface Images {
@ -143,9 +141,9 @@ export interface PosterTall {
}
export enum PosterTallType {
PosterTall = "poster_tall",
PosterWide = "poster_wide",
Thumbnail = "thumbnail",
PosterTall = 'poster_tall',
PosterWide = 'poster_wide',
Thumbnail = 'thumbnail',
}
export interface SearchMetadata {

View file

@ -4,20 +4,16 @@ export interface ObjectInfo {
__class__: string;
__href__: string;
__resource_key__: string;
__links__: Actions;
__actions__: Actions;
__links__: unknown;
__actions__: unknown;
total: number;
items: Item[];
}
export interface Actions {
}
export interface Item {
__class__: string;
__href__: string;
__links__: Links;
__actions__: Actions;
__actions__: unknown;
id: string;
external_id: string;
channel_id: string;
@ -43,10 +39,10 @@ export interface Item {
}
export interface Links {
"episode/season": EpisodeSeason;
"episode/series": EpisodeSeason;
'episode/season': EpisodeSeason;
'episode/series': EpisodeSeason;
resource: EpisodeSeason;
"resource/channel": EpisodeSeason;
'resource/channel': EpisodeSeason;
streams: EpisodeSeason;
}

View file

@ -24,7 +24,7 @@ export interface Stream {
export enum Vcodec {
H264 = "h264",
H264 = 'h264',
}
export interface Subtitle {

View file

@ -10,6 +10,6 @@ declare module 'sei-helper' {
domain: string;
secure: boolean;
}>
}
};
export function formatTime(time: number): string
}

File diff suppressed because it is too large Load diff

1189
crunchy.ts Normal file

File diff suppressed because it is too large Load diff

View file

@ -1,7 +1,8 @@
import { appArgv } from "./modules/module.app-args";
import { appArgv } from './modules/module.app-args';
import * as yamlCfg from './modules/module.cfg-loader';
import funimation from './funi'
import funimation from './funi';
import crunchy from './crunchy';
(async () => {
const cfg = yamlCfg.loadCfg();
@ -9,9 +10,9 @@ import funimation from './funi'
const argv = appArgv(cfg.cli);
if (argv.service === 'funi') {
await funimation()
await funimation();
} else if (argv.service === 'crunchy') {
await crunchy();
}
})()
})();

View file

@ -1,4 +1,4 @@
import { Headers } from "got/dist/source";
import { Headers } from 'got/dist/source';
// api domains
const domain = {

View file

@ -1,5 +1,5 @@
import yargs from 'yargs';
import * as langsData from "./module.langsData";
import * as langsData from './module.langsData';
yargs(process.argv.slice(2));
@ -12,7 +12,7 @@ const groups = {
'fileName': 'Filename Template:',
'debug': 'Debug:',
'util': 'Utilities:'
}
};
export type AvailableFilenameVars = 'title' | 'episode' | 'showTitle' | 'season' | 'width' | 'height' | 'service'
@ -46,240 +46,247 @@ const appArgv = (cfg: {
};
const argv = yargs.parserConfiguration({
"duplicate-arguments-array": false,
"camel-case-expansion": false
'duplicate-arguments-array': false,
'camel-case-expansion': false
})
.wrap(yargs.terminalWidth())
.usage('Usage: $0 [options]')
.help(false).version(false)
.option('auth', {
group: groups.auth,
describe: 'Enter authentication mode',
type: 'boolean'
})
.option('dlFonts', {
group: groups.fonts,
describe: 'Download all required fonts for mkv muxing',
type: 'boolean'
})
.option('search', {
group: groups.search,
alias: 'f',
describe: 'Search for an anime',
type: 'string'
})
.option('search-type', {
group: groups.search,
describe: 'Search type used for crunchyroll',
choices: [ '', 'top_results', 'series', 'movie_listing', 'episode' ],
default: '',
type: 'string',
})
.option('page', {
group: groups.search,
alias: 'p',
describe: 'Page number for search results',
type: 'number',
})
.option('search-locale', {
group: groups.search,
describe: 'Search locale used for crunchyroll',
choices: langsData.searchLocales,
default: '',
type: 'string',
})
.option('new', {
group: groups.dl,
describe: 'Get last updated series list from crunchyroll',
type: 'boolean',
})
.option('movie-listing', {
group: groups.dl,
alias: 'flm',
describe: 'Get video list by Movie Listing ID',
type: 'string',
})
.option('series', {
group: groups.dl,
alias: 'srz',
describe: 'Get season list by Series ID',
type: 'string',
})
.option('s', {
group: groups.dl,
describe: 'Set the season ID',
type: 'string'
})
.option('e', {
group: groups.dl,
alias: 'episode',
describe: 'Sets the Episode Number/IDs (comma-separated, hyphen-sequence)',
type: 'string',
})
.option('q', {
group: groups.dl,
describe: 'Set the quality layer. Use 0 to get the best quality.',
default: parseDefault<number>('videoLayer', 7),
type: 'number'
})
.option('x', {
group: groups.dl,
alias: 'server',
describe: 'Select server',
choices: [1, 2, 3, 4],
default: parseDefault<number>('nServer', 1),
type: 'number',
})
.option('kstream', {
group: groups.dl,
alias: 'k',
describe: 'Select specific stream for crunchyroll',
choices: [1, 2, 3, 4, 5, 6, 7],
default: parseDefault<number>('kStream', 1),
type: 'number',
})
.option('partsize', {
group: groups.dl,
describe: 'Set the amount of parts that should be downloaded in paralell',
type: 'number',
default: parseDefault<number>('partsize', 10)
})
.option('hslang', {
group: groups.dl,
describe: 'Download video with specific hardsubs',
choices: langsData.subtitleLanguagesFilter.slice(1),
default: parseDefault<string>('hsLang', 'none'),
type: 'string',
})
.option('subLang', {
group: groups.dl,
describe: 'Set the subtitles to download (Funi only)',
choices: subLang,
default: parseDefault<string[]>('subLang', []),
type: 'array'
})
.option('novids', {
group: groups.dl,
describe: 'Skip downloading videos',
type: 'boolean'
})
.option('noaudio', {
group: groups.dl,
describe: 'Skip downloading audio',
type: 'boolean'
})
.option('nosubs', {
group: groups.dl,
describe: 'Skip downloading subtitles',
type: 'boolean'
})
.option('dub', {
group: groups.dl,
describe: 'Set languages to download (funi only)',
choices: dubLang,
default: parseDefault<possibleDubs>('dub', ['enUS']),
type: 'array'
})
.option('dubLang', {
group: groups.dl,
describe: 'Set the language to download (Crunchy only)',
choices: langsData.dubLanguageCodes,
default: parseDefault('dubLanguage', langsData.dubLanguageCodes.slice(-1)[0]),
type: 'string',
})
.option('all', {
group: groups.dl,
describe: 'Used to download all episodes from the show (Funi only)',
type: 'boolean',
default: parseDefault<boolean>('all', false)
})
.option('fontSize', {
group: groups.dl,
describe: 'Used to set the fontsize of the subtitles',
default: parseDefault<number>('fontSize', 55),
type: 'number'
})
.option('allSubs', {
group: groups.dl,
describe: 'If set to true, all available subs will get downloaded (Funi only)',
default: false,
type: 'boolean'
})
.option('allDubs', {
group: groups.dl,
describe: 'If set to true, all available dubs will get downloaded (Funi only)',
default: false,
type: 'boolean'
})
.option('timeout', {
group: groups.dl,
describe: 'Set the timeout of all download reqests. Set in millisecods',
type: 'number',
default: parseDefault('timeout', 60 * 1000)
})
.option('simul', {
group: groups.dl,
describe: 'Force downloading simulcast ver. instead of uncut ver. (if uncut ver. available) (Funi only)',
default: parseDefault<boolean>('forceSimul', false),
type: 'boolean',
})
.option('mp4', {
group: groups.mux,
describe: 'Mux video into mp4',
default: parseDefault<boolean>('mp4mux', false),
type: 'boolean'
})
.option('skipmux', {
group: groups.mux,
describe: 'Skip muxing video and subtitles',
type: 'boolean'
})
.option('fileName', {
group: groups.fileName,
describe: `Set the filename template. Use \${variable_name} to insert variables.\nYou may use ${availableFilenameVars
.map(a => `'${a}'`).join(', ')} as variables.`,
type: 'string',
default: parseDefault<string>('fileName', '[${service}] ${showTitle} - ${episode} [${height}p]')
})
.option('numbers', {
group: groups.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: parseDefault<number>('numbers', 2)
})
.option('nosess', {
group: groups.debug,
type: 'boolean',
default: 'Reset session cookie for testing purposes'
})
.option('debug', {
group: groups.debug,
describe: 'Debug mode (tokens may be revield in the console output)',
type: 'boolean'
})
.option('nocleanup', {
group: groups.util,
describe: 'Don\'t delete subtitles and videos after muxing',
default: parseDefault<boolean>('noCleanUp', false),
type: 'boolean'
})
.option('help', {
alias: 'h',
group: 'Help:',
describe: 'Show this help',
type: 'boolean'
})
.option('service', {
group: groups.util,
describe: 'Set the service to use',
choices: ['funi', 'crunchy'],
demandOption: true,
default: parseDefault<'crunchy'|'funi'|undefined>('service', undefined)
})
.parseSync();
.wrap(yargs.terminalWidth())
.usage('Usage: $0 [options]')
.help(false).version(false)
.option('auth', {
group: groups.auth,
describe: 'Enter authentication mode',
type: 'boolean'
})
.option('dlFonts', {
group: groups.fonts,
describe: 'Download all required fonts for mkv muxing',
type: 'boolean'
})
.option('search', {
group: groups.search,
alias: 'f',
describe: 'Search for an anime',
type: 'string'
})
.option('search-type', {
group: groups.search,
describe: 'Search type used for crunchyroll',
choices: [ '', 'top_results', 'series', 'movie_listing', 'episode' ],
default: '',
type: 'string',
})
.option('page', {
group: groups.search,
alias: 'p',
describe: 'Page number for search results',
type: 'number',
})
.option('search-locale', {
group: groups.search,
describe: 'Search locale used for crunchyroll',
choices: langsData.searchLocales,
default: '',
type: 'string',
})
.option('new', {
group: groups.dl,
describe: 'Get last updated series list from crunchyroll',
type: 'boolean',
})
.option('movie-listing', {
group: groups.dl,
alias: 'flm',
describe: 'Get video list by Movie Listing ID',
type: 'string',
})
.option('series', {
group: groups.dl,
alias: 'srz',
describe: 'Get season list by Series ID',
type: 'string',
})
.option('s', {
group: groups.dl,
describe: 'Set the season ID',
type: 'string'
})
.option('e', {
group: groups.dl,
alias: 'episode',
describe: 'Sets the Episode Number/IDs (comma-separated, hyphen-sequence)',
type: 'string',
})
.option('q', {
group: groups.dl,
describe: 'Set the quality layer. Use 0 to get the best quality.',
default: parseDefault<number>('videoLayer', 7),
type: 'number'
})
.option('x', {
group: groups.dl,
alias: 'server',
describe: 'Select server',
choices: [1, 2, 3, 4],
default: parseDefault<number>('nServer', 1),
type: 'number',
})
.option('kstream', {
group: groups.dl,
alias: 'k',
describe: 'Select specific stream for crunchyroll',
choices: [1, 2, 3, 4, 5, 6, 7],
default: parseDefault<number>('kStream', 1),
type: 'number',
})
.option('partsize', {
group: groups.dl,
describe: 'Set the amount of parts that should be downloaded in paralell',
type: 'number',
default: parseDefault<number>('partsize', 10)
})
.option('hslang', {
group: groups.dl,
describe: 'Download video with specific hardsubs',
choices: langsData.subtitleLanguagesFilter.slice(1),
default: parseDefault<string>('hsLang', 'none'),
type: 'string',
})
.option('subLang', {
group: groups.dl,
describe: 'Set the subtitles to download (Funi only)',
choices: subLang,
default: parseDefault<string[]>('subLang', []),
type: 'array'
})
.option('dlsubs', {
group: groups.dl,
describe: 'Download subtitles by language tag (space-separated) (crunchy only)',
choices: langsData.subtitleLanguagesFilter,
default: parseDefault<string|string[]>('dlSubs', 'all'),
type: 'array',
})
.option('novids', {
group: groups.dl,
describe: 'Skip downloading videos',
type: 'boolean'
})
.option('noaudio', {
group: groups.dl,
describe: 'Skip downloading audio',
type: 'boolean'
})
.option('nosubs', {
group: groups.dl,
describe: 'Skip downloading subtitles',
type: 'boolean'
})
.option('dub', {
group: groups.dl,
describe: 'Set languages to download (funi only)',
choices: dubLang,
default: parseDefault<possibleDubs>('dub', ['enUS']),
type: 'array'
})
.option('dubLang', {
group: groups.dl,
describe: 'Set the language to download (Crunchy only)',
choices: langsData.dubLanguageCodes,
default: parseDefault('dubLanguage', langsData.dubLanguageCodes.slice(-1)[0]),
type: 'string',
})
.option('all', {
group: groups.dl,
describe: 'Used to download all episodes from the show (Funi only)',
type: 'boolean',
default: parseDefault<boolean>('all', false)
})
.option('fontSize', {
group: groups.dl,
describe: 'Used to set the fontsize of the subtitles',
default: parseDefault<number>('fontSize', 55),
type: 'number'
})
.option('allSubs', {
group: groups.dl,
describe: 'If set to true, all available subs will get downloaded (Funi only)',
default: false,
type: 'boolean'
})
.option('allDubs', {
group: groups.dl,
describe: 'If set to true, all available dubs will get downloaded (Funi only)',
default: false,
type: 'boolean'
})
.option('timeout', {
group: groups.dl,
describe: 'Set the timeout of all download reqests. Set in millisecods',
type: 'number',
default: parseDefault('timeout', 60 * 1000)
})
.option('simul', {
group: groups.dl,
describe: 'Force downloading simulcast ver. instead of uncut ver. (if uncut ver. available) (Funi only)',
default: parseDefault<boolean>('forceSimul', false),
type: 'boolean',
})
.option('mp4', {
group: groups.mux,
describe: 'Mux video into mp4',
default: parseDefault<boolean>('mp4mux', false),
type: 'boolean'
})
.option('skipmux', {
group: groups.mux,
describe: 'Skip muxing video and subtitles',
type: 'boolean'
})
.option('fileName', {
group: groups.fileName,
describe: `Set the filename template. Use \${variable_name} to insert variables.\nYou may use ${availableFilenameVars
.map(a => `'${a}'`).join(', ')} as variables.`,
type: 'string',
default: parseDefault<string>('fileName', '[${service}] ${showTitle} - ${episode} [${height}p]')
})
.option('numbers', {
group: groups.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: parseDefault<number>('numbers', 2)
})
.option('nosess', {
group: groups.debug,
type: 'boolean',
default: 'Reset session cookie for testing purposes'
})
.option('debug', {
group: groups.debug,
describe: 'Debug mode (tokens may be revield in the console output)',
type: 'boolean'
})
.option('nocleanup', {
group: groups.util,
describe: 'Don\'t delete subtitles and videos after muxing',
default: parseDefault<boolean>('noCleanUp', false),
type: 'boolean'
})
.option('help', {
alias: 'h',
group: 'Help:',
describe: 'Show this help',
type: 'boolean'
})
.option('service', {
group: groups.util,
describe: 'Set the service to use',
choices: ['funi', 'crunchy'],
demandOption: true,
default: parseDefault<'crunchy'|'funi'|undefined>('service', undefined)
})
.parseSync();
return argv;
}
};
const showHelp = yargs.showHelp;
@ -289,4 +296,4 @@ export {
availableFilenameVars,
subLang,
dubLang
}
};

View file

@ -171,7 +171,7 @@ const loadFuniToken = () => {
}>(tokenFile.funi, true);
let token: false|string = false;
if (loadedToken && loadedToken.token)
token = loadedToken.token;
token = loadedToken.token;
// info if token not set
if(!token){
console.log('[INFO] Token not set!\n');

View file

@ -27,7 +27,7 @@ export type Res = {
// req
const curlReq = async (curlBin: string, url: string, options: CurlOptions, cache: string) => {
let curlOpt = [
const curlOpt = [
`"${curlBin}"`,
`"${url}"`,
];
@ -35,8 +35,8 @@ const curlReq = async (curlBin: string, url: string, options: CurlOptions, cache
options = options || {};
if(options.headers && Object.keys(options.headers).length > 0){
for(let h of Object.keys(options.headers)){
let hC = options.headers[h];
for(const h of Object.keys(options.headers)){
const hC = options.headers[h];
curlOpt.push('-H', `"${h}: ${hC}"`);
}
}
@ -103,7 +103,7 @@ const curlReq = async (curlBin: string, url: string, options: CurlOptions, cache
fs.unlinkSync(bodyFile);
fs.unlinkSync(errFile);
let res: Res = {
const res: Res = {
httpVersion: '',
statusCode: '',
statusMessage: '',
@ -113,20 +113,20 @@ const curlReq = async (curlBin: string, url: string, options: CurlOptions, cache
body: rawBody.toString(),
};
let headersCont = rawHeaders.replace(/\r/g, '').split('\n');
const headersCont = rawHeaders.replace(/\r/g, '').split('\n');
for(let h of headersCont){
for(const h of headersCont){
if( h == '' ){ continue; }
if(!h.match(':')){
let statusRes = h.split(' ');
const statusRes = h.split(' ');
res.httpVersion = statusRes[0].split('/')[1];
res.statusCode = statusRes[1];
res.statusMessage = statusRes.slice(2).join(' ');
}
else{
let resHeader = h.split(': ');
let resHeadName = resHeader[0].toLowerCase();
let resHeadCont = resHeader.slice(1).join(': ');
const resHeader = h.split(': ');
const resHeadName = resHeader[0].toLowerCase();
const resHeadCont = resHeader.slice(1).join(': ');
if(resHeadName == 'set-cookie'){
if(!Object.prototype.hasOwnProperty.call(res.headers, resHeadName)){
res.headers[resHeadName] = [];
@ -140,7 +140,7 @@ const curlReq = async (curlBin: string, url: string, options: CurlOptions, cache
}
if(!res.statusCode.match(/^(2|3)\d\d$/)){
let httpStatusMessage = res.statusMessage ? ` (${res.statusMessage})` : '';
const httpStatusMessage = res.statusMessage ? ` (${res.statusMessage})` : '';
throw {
name: 'HTTPError',
message: `Response code ${res.statusCode}${httpStatusMessage}`,
@ -154,7 +154,7 @@ const curlReq = async (curlBin: string, url: string, options: CurlOptions, cache
function uuidv4() {
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
let r = Math.random() * 16 | 0, v = c == 'x' ? r : (r & 0x3 | 0x8);
const r = Math.random() * 16 | 0, v = c == 'x' ? r : (r & 0x3 | 0x8);
return v.toString(16);
});
}

View file

@ -7,7 +7,6 @@ const betaEpRegex = new RegExp (/^[0-9A-Z]{9}$/);
const epLtReg = new RegExp (/(?:E|S|M)/);
class doFilter {
constructor(){}
ifMaxEp(type: keyof typeof epNumLen, num: number){
const maxEp = Math.pow(10, epNumLen[type]) - 1;
return num > maxEp ? true : false;
@ -40,7 +39,7 @@ class doFilter {
eRange[0] = this.ifMaxEp(epLetter, eRange[0]) ? this.powNum(epLetter) - 1 : eRange[0];
eRange[1] = eRange[1].toString().match(/^\d+$/) ? parseInt(eRange[1] as string) : 0;
eRange[1] = this.ifMaxEp(epLetter, eRange[1]) ? this.powNum(epLetter) - 1 : eRange[1];
console.log(eRange)
console.log(eRange);
// check if correct range
if (eRange[0] > eRange[1]){
const parsedEl = [
@ -76,7 +75,7 @@ class doFilter {
return '';
});
// end
const filteredArr1 = [...new Set(filteredArr.concat(inputEpsRange))]
const filteredArr1 = [...new Set(filteredArr.concat(inputEpsRange))];
const filteredArr2 = filteredArr1.indexOf('') > -1 ? filteredArr1.slice(1) : filteredArr1;
return filteredArr2;
}

View file

@ -1,6 +1,6 @@
import * as shlp from "sei-helper";
import path from "path";
import { AvailableFilenameVars } from "./module.app-args";
import * as shlp from 'sei-helper';
import path from 'path';
import { AvailableFilenameVars } from './module.app-args';
export type Variable = ({
@ -23,7 +23,7 @@ const parseFileName = (input: string, variables: Variable[], numbers: number): s
const varName = type.slice(2, -1);
const use = variables.find(a => a.name === varName);
if (use === undefined) {
console.log(`[ERROR] Found variable '${type}' in fileName but no values was internally found!`)
console.log(`[ERROR] Found variable '${type}' in fileName but no values was internally found!`);
continue;
}
@ -32,10 +32,10 @@ const parseFileName = (input: string, variables: Variable[], numbers: number): s
const replaceStr = len < numbers ? '0'.repeat(numbers - len) + use.replaceWith : use.replaceWith.toFixed(0);
input = input.replace(type, replaceStr);
} else {
input = input.replace(type, use.replaceWith)
input = input.replace(type, use.replaceWith);
}
}
return input.split(path.sep).map(a => shlp.cleanupFilename(a));
}
};
export default parseFileName;

View file

@ -67,11 +67,11 @@ const fonts = {
// collect styles from ass string
function assFonts(ass: string){
let strings = ass.replace(/\r/g,'').split('\n');
let styles = [];
for(let s of strings){
const strings = ass.replace(/\r/g,'').split('\n');
const styles = [];
for(const s of strings){
if(s.match(/^Style: /)){
let addStyle = s.split(',');
const addStyle = s.split(',');
styles.push(addStyle[1]);
}
}

View file

@ -26,7 +26,7 @@ const languages: LanguageItem[] = [
// add en language names
(() =>{
for(let languageIndex in languages){
for(const languageIndex in languages){
if(!languages[languageIndex].language){
languages[languageIndex].language = languages[languageIndex].name;
}
@ -99,16 +99,18 @@ const parseSubtitlesArray = (tags: string[]) => {
};
// sort subtitles
const sortSubtitles = (data: Partial<LanguageItem>[], sortkey: keyof LanguageItem = 'locale') => {
const sortSubtitles = <T extends {
[key: string]: unknown
} = Record<string, string>> (data: T[], sortkey?: keyof T) : T[] => {
const idx: Record<string, number> = {};
sortkey = sortkey || 'locale';
const key = sortkey || 'locale' as keyof T;
const tags = [...new Set(Object.values(languages).map(e => e.locale))];
for(const l of tags){
idx[l] = Object.keys(idx).length + 1;
}
data.sort((a, b) => {
const ia = idx[a[sortkey] as string] ? idx[a[sortkey] as string] : 50;
const ib = idx[b[sortkey] as string] ? idx[b[sortkey] as string] : 50;
const ia = idx[a[key] as string] ? idx[a[key] as string] : 50;
const ib = idx[b[key] as string] ? idx[b[key] as string] : 50;
return ia - ib;
});
return data;

View file

@ -1,18 +1,20 @@
import * as iso639 from 'iso-639';
import { fonts, fontMime } from "./module.fontsData";
import path from "path";
import fs from "fs";
import { fonts, fontMime } from './module.fontsData';
import path from 'path';
import fs from 'fs';
import { LanguageItem } from './module.langsData';
export type MergerInput = {
path: string,
lang: string,
lookup?: false,
}
export type SubtitleInput = {
language: string,
file: string,
fonts?: ParsedFont[]
title?: string
lookup?: false,
}
export type Font = keyof typeof fonts;
@ -30,6 +32,7 @@ export type MergerOptions = {
subtitels: SubtitleInput[],
output: string,
simul?: boolean,
fonts?: ParsedFont[]
}
class Merger {
@ -131,7 +134,7 @@ class Merger {
'--video-tracks 0',
'--no-audio'
);
const trackName = this.subDict[vid.lang] + (this.options.simul ? ' [Simulcast]' : ' [Uncut]');
const trackName = (vid.lookup === false ? vid.lang : this.subDict[vid.lang]) + (this.options.simul ? ' [Simulcast]' : ' [Uncut]');
args.push('--track-name', `0:"${trackName}"`);
args.push(`--language 0:${Merger.getLanguageCode(vid.lang, vid.lang)}`);
hasVideo = true;
@ -145,7 +148,7 @@ class Merger {
'--video-tracks 0',
'--audio-tracks 1'
);
const trackName = this.subDict[vid.lang] + (this.options.simul ? ' [Simulcast]' : ' [Uncut]');
const trackName = (vid.lookup === false ? vid.lang : this.subDict[vid.lang]) + (this.options.simul ? ' [Simulcast]' : ' [Uncut]');
args.push('--track-name', `0:"${trackName}"`);
args.push('--track-name', `1:"${trackName}"`);
args.push(`--language 1:${Merger.getLanguageCode(vid.lang, vid.lang)}`);
@ -155,7 +158,7 @@ class Merger {
'--no-video',
'--audio-tracks 1'
);
const trackName = this.subDict[vid.lang] + (this.options.simul ? ' [Simulcast]' : ' [Uncut]');
const trackName = (vid.lookup === false ? vid.lang : this.subDict[vid.lang]) + (this.options.simul ? ' [Simulcast]' : ' [Uncut]');
args.push('--track-name', `1:"${trackName}"`);
args.push(`--language 1:${Merger.getLanguageCode(vid.lang, vid.lang)}`);
}
@ -163,7 +166,7 @@ class Merger {
}
for (const aud of this.options.onlyAudio) {
const trackName = this.subDict[aud.lang] + (this.options.simul ? ' [Simulcast]' : ' [Uncut]');
const trackName = (aud.lookup === false ? aud.lang : this.subDict[aud.lang]) + (this.options.simul ? ' [Simulcast]' : ' [Uncut]');
args.push('--track-name', `0:"${trackName}"`);
args.push(`--language 0:${Merger.getLanguageCode(aud.lang, aud.lang)}`);
args.push(
@ -175,26 +178,27 @@ class Merger {
if (this.options.subtitels.length > 0) {
for (const subObj of this.options.subtitels) {
const trackName = this.subDict[subObj.language] + (this.options.simul ? ' [Simulcast]' : ' [Uncut]');
args.push('--track-name', `0:"${trackName}"`);
args.push('--language', `0:${Merger.getLanguageCode(subObj.language)}`);
args.push('--track-name', (subObj.title !== undefined ? `0:"${subObj.title}"` : `0:"${subObj.lookup === false ? subObj.language : Merger.getLanguageCode(subObj.language)}"`));
args.push('--language', `0:"${subObj.lookup === false ? subObj.language : Merger.getLanguageCode(subObj.language)}"`);
args.push(`"${subObj.file}"`);
if (subObj.fonts && subObj.fonts.length > 0) {
for (const f of subObj.fonts) {
args.push('--attachment-name', f.name);
args.push('--attachment-mime-type', f.mime);
args.push('--attach-file', f.path);
}
}
}
} else {
args.push(
'--no-subtitles',
);
}
if (this.options.fonts && this.options.fonts.length > 0) {
for (const f of this.options.fonts) {
args.push('--attachment-name', f.name);
args.push('--attachment-mime-type', f.mime);
args.push('--attach-file', f.path);
}
} else {
args.push(
'--no-attachments'
);
}
return args.join(' ');
};
@ -225,7 +229,7 @@ class Merger {
language: LanguageItem,
fonts: Font[]
}[]) : ParsedFont[] {
let fontsNameList: Font[] = [], fontsList = [], subsList = [], isNstr = true;
let fontsNameList: Font[] = []; const fontsList = [], subsList = []; let isNstr = true;
for(const s of subs){
fontsNameList.push(...s.fonts);
subsList.push(s.language.locale);
@ -253,7 +257,12 @@ class Merger {
}
}
return fontsList;
};
}
public cleanUp() {
this.options.onlyAudio.concat(this.options.onlyVid).concat(this.options.videoAndAudio).forEach(a => fs.unlinkSync(a.path));
this.options.subtitels.forEach(a => fs.unlinkSync(a.file));
}
}

View file

@ -1,6 +1,3 @@
import path from 'path';
import fs from 'fs-extra';
import shlp from 'sei-helper';
import got, { Headers, Method, Options, ReadError, Response } from 'got';
import cookieFile from './module.cookieFile';
@ -44,7 +41,7 @@ class Req {
async getData<T = string> (durl: string, params?: Params) {
params = params || {};
// options
let options: Options & {
const options: Options & {
minVersion?: string,
maxVersion?: string
curlDebug?: boolean
@ -83,7 +80,6 @@ class Req {
}
}*/
// if auth
let cookie = [];
const loc = new URL(durl);
// avoid cloudflare protection
if(loc.origin == this.domain.www){
@ -124,7 +120,7 @@ class Req {
name: string
} & ReadError & {
res: Response<unknown>
}
};
if(error.response && error.response.statusCode && error.response.statusMessage){
console.log(`[ERROR] ${error.name} ${error.response.statusCode}: ${error.response.statusMessage}`);
}
@ -140,122 +136,122 @@ class Req {
}
if(error.res && error.res.body && error.response.statusCode
&& error.response.statusCode != 404 && error.response.statusCode != 403){
console.log('[ERROR] Body:', error.res.body);
}
return {
ok: false,
error,
};
console.log('[ERROR] Body:', error.res.body);
}
return {
ok: false,
error,
};
}
}
setNewCookie(setCookie: Record<string, string>, isAuth: boolean, fileData?: string){
const cookieUpdated = []; let lastExp = 0;
console.trace('Type of setCookie:', typeof setCookie, setCookie);
const parsedCookie = fileData ? cookieFile(fileData) : shlp.cookie.parse(setCookie);
for(const cookieName of Object.keys(parsedCookie)){
if(parsedCookie[cookieName] && parsedCookie[cookieName].value && parsedCookie[cookieName].value == 'deleted'){
delete parsedCookie[cookieName];
}
}
setNewCookie(setCookie: Record<string, string>, isAuth: boolean, fileData?: string){
let cookieUpdated = [], lastExp = 0;
console.trace('Type of setCookie:', typeof setCookie, setCookie)
const parsedCookie = fileData ? cookieFile(fileData) : shlp.cookie.parse(setCookie);
for(let cookieName of Object.keys(parsedCookie)){
if(parsedCookie[cookieName] && parsedCookie[cookieName].value && parsedCookie[cookieName].value == 'deleted'){
delete parsedCookie[cookieName];
}
for(const uCookie of usefulCookies.auth){
const cookieForceExp = 60*60*24*7;
const cookieExpCur = this.session[uCookie] ? this.session[uCookie] : { expires: 0 };
const cookieExp = new Date(cookieExpCur.expires).getTime() - cookieForceExp;
if(cookieExp > lastExp){
lastExp = cookieExp;
}
for(let uCookie of usefulCookies.auth){
const cookieForceExp = 60*60*24*7;
const cookieExpCur = this.session[uCookie] ? this.session[uCookie] : { expires: 0 };
const cookieExp = new Date(cookieExpCur.expires).getTime() - cookieForceExp;
if(cookieExp > lastExp){
lastExp = cookieExp;
}
}
for(const uCookie of usefulCookies.auth){
if(!parsedCookie[uCookie]){
continue;
}
for(let uCookie of usefulCookies.auth){
if(!parsedCookie[uCookie]){
continue;
}
if(isAuth || parsedCookie[uCookie] && Date.now() > lastExp){
this.session[uCookie] = parsedCookie[uCookie];
cookieUpdated.push(uCookie);
}
if(isAuth || parsedCookie[uCookie] && Date.now() > lastExp){
this.session[uCookie] = parsedCookie[uCookie];
cookieUpdated.push(uCookie);
}
for(let uCookie of usefulCookies.sess){
if(!parsedCookie[uCookie]){
continue;
}
if(
isAuth
}
for(const uCookie of usefulCookies.sess){
if(!parsedCookie[uCookie]){
continue;
}
if(
isAuth
|| this.argv.nosess && parsedCookie[uCookie]
|| parsedCookie[uCookie] && !this.checkSessId(this.session[uCookie])
){
const sessionExp = 60*60;
this.session[uCookie] = parsedCookie[uCookie];
this.session[uCookie].expires = new Date(Date.now() + sessionExp*1000);
this.session[uCookie]['Max-Age'] = sessionExp.toString();
cookieUpdated.push(uCookie);
}
}
if(cookieUpdated.length > 0){
if(this.argv.debug){
console.log('[SAVING FILE]',`${this.sessCfg}.yml`);
}
yamlCfg.saveCRSession(this.session);
console.log(`[INFO] Cookies were updated! (${cookieUpdated.join(', ')})\n`);
}
){
const sessionExp = 60*60;
this.session[uCookie] = parsedCookie[uCookie];
this.session[uCookie].expires = new Date(Date.now() + sessionExp*1000);
this.session[uCookie]['Max-Age'] = sessionExp.toString();
cookieUpdated.push(uCookie);
}
checkCookieVal(chcookie: Record<string, string>){
return chcookie
}
if(cookieUpdated.length > 0){
if(this.argv.debug){
console.log('[SAVING FILE]',`${this.sessCfg}.yml`);
}
yamlCfg.saveCRSession(this.session);
console.log(`[INFO] Cookies were updated! (${cookieUpdated.join(', ')})\n`);
}
}
checkCookieVal(chcookie: Record<string, string>){
return chcookie
&& chcookie.toString() == '[object Object]'
&& typeof chcookie.value == 'string'
? true : false;
}
checkSessId(session_id: Record<string, unknown>){
if(session_id && typeof session_id.expires == 'string'){
session_id.expires = new Date(session_id.expires);
}
return session_id
? true : false;
}
checkSessId(session_id: Record<string, unknown>){
if(session_id && typeof session_id.expires == 'string'){
session_id.expires = new Date(session_id.expires);
}
return session_id
&& session_id.toString() == '[object Object]'
&& typeof session_id.expires == 'object'
&& Date.now() < new Date(session_id.expires as any).getTime()
&& typeof session_id.value == 'string'
? true : false;
}
uuidv4(){
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
let r = Math.random() * 16 | 0, v = c == 'x' ? r : (r & 0x3 | 0x8);
return v.toString(16);
});
}
};
? true : false;
}
uuidv4(){
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
const r = Math.random() * 16 | 0, v = c == 'x' ? r : (r & 0x3 | 0x8);
return v.toString(16);
});
}
}
function buildProxy(proxyBaseUrl: string, proxyAuth: string){
if(!proxyBaseUrl.match(/^(https?|socks4|socks5):/)){
proxyBaseUrl = 'http://' + proxyBaseUrl;
}
function buildProxy(proxyBaseUrl: string, proxyAuth: string){
if(!proxyBaseUrl.match(/^(https?|socks4|socks5):/)){
proxyBaseUrl = 'http://' + proxyBaseUrl;
}
let proxyCfg = new URL(proxyBaseUrl);
let proxyStr = `${proxyCfg.protocol}//`;
const proxyCfg = new URL(proxyBaseUrl);
let proxyStr = `${proxyCfg.protocol}//`;
if(typeof proxyCfg.hostname != 'string' || proxyCfg.hostname == ''){
throw new Error('[ERROR] Hostname and port required for proxy!');
}
if(typeof proxyCfg.hostname != 'string' || proxyCfg.hostname == ''){
throw new Error('[ERROR] Hostname and port required for proxy!');
}
if(proxyAuth && typeof proxyAuth == 'string' && proxyAuth.match(':')){
proxyCfg.username = proxyAuth.split(':')[0];
proxyCfg.password = proxyAuth.split(':')[1];
proxyStr += `${proxyCfg.username}:${proxyCfg.password}@`;
}
if(proxyAuth && typeof proxyAuth == 'string' && proxyAuth.match(':')){
proxyCfg.username = proxyAuth.split(':')[0];
proxyCfg.password = proxyAuth.split(':')[1];
proxyStr += `${proxyCfg.username}:${proxyCfg.password}@`;
}
proxyStr += proxyCfg.hostname;
proxyStr += proxyCfg.hostname;
if(!proxyCfg.port && proxyCfg.protocol == 'http:'){
proxyStr += ':80';
}
else if(!proxyCfg.port && proxyCfg.protocol == 'https:'){
proxyStr += ':443';
}
if(!proxyCfg.port && proxyCfg.protocol == 'http:'){
proxyStr += ':80';
}
else if(!proxyCfg.port && proxyCfg.protocol == 'https:'){
proxyStr += ':443';
}
return proxyStr;
}
return proxyStr;
}
export {
buildProxy,
usefulCookies,
Req,
};
export {
buildProxy,
usefulCookies,
Req,
};

View file

@ -22,7 +22,7 @@
"url": "https://github.com/izu-co/funimation-downloader-nx/issues"
},
"license": "MIT",
"main": "funi.js",
"main": "index.js",
"dependencies": {
"cheerio": "^1.0.0-rc.10",
"form-data": "^4.0.0",