diff --git a/crunchy.ts b/crunchy.ts index 3e54fc5..77d3ef5 100644 --- a/crunchy.ts +++ b/crunchy.ts @@ -144,6 +144,16 @@ export default class Crunchy implements ServiceClass { } } return true; + } else if (argv.extid) { + await this.refreshToken(); + const selected = await this.getObjectById(argv.extid, false, true); + for (const select of selected as Partial[]) { + if (!(await this.downloadEpisode(select as CrunchyEpMeta, {...argv, skipsubs: false }))) { + console.error(`Unable to download selected episode ${select.episodeNumber}`); + return false; + } + } + return true; } else{ console.info('No option selected or invalid value entered. Try --help.'); @@ -852,13 +862,48 @@ export default class Crunchy implements ServiceClass { return true; } - public async getObjectById(e?: string, earlyReturn?: boolean): Promise[]|undefined> { + public async getObjectById(e?: string, earlyReturn?: boolean, external_id?: boolean): Promise[]|undefined> { if(!this.cmsToken.cms){ console.error('Authentication required!'); return; } + + let convertedObjects; + if (external_id) { + const epFilter = parseSelect(e as string); + const objectIds = []; + for (const ob of epFilter.values) { + const extIdReqOpts = [ + api.beta_cms, + this.cmsToken.cms.bucket, + '/channels/crunchyroll/objects', + '?', + new URLSearchParams({ + 'external_id': ob, + 'Policy': this.cmsToken.cms.policy, + 'Signature': this.cmsToken.cms.signature, + 'Key-Pair-Id': this.cmsToken.cms.key_pair_id, + }), + ].join(''); + + const extIdReq = await this.req.getData(extIdReqOpts); + if (!extIdReq.ok || !extIdReq.res) { + console.error('Objects Request FAILED!'); + if (extIdReq.error && extIdReq.error.res && extIdReq.error.res.body) { + console.info('[INFO] Body:', extIdReq.error.res.body); + } + continue; + } + + const oldObjectInfo = JSON.parse(extIdReq.res.body) as Record; + for (const object of oldObjectInfo.items) { + objectIds.push(object.id); + } + } + convertedObjects = objectIds.join(','); + } - const doEpsFilter = parseSelect(e as string); + const doEpsFilter = parseSelect(convertedObjects ?? e as string); if(doEpsFilter.values.length < 1){ console.info('\nObjects not selected!\n'); @@ -951,7 +996,7 @@ export default class Crunchy implements ServiceClass { await this.logObject(item, 2); } console.info(''); - return selectedMedia; + return selectedMedia; } public async downloadMediaList(medias: CrunchyEpMeta, options: CrunchyDownloadOptions) : Promise<{ diff --git a/modules/module.app-args.ts b/modules/module.app-args.ts index 0cf7a3e..729e87c 100644 --- a/modules/module.app-args.ts +++ b/modules/module.app-args.ts @@ -33,6 +33,7 @@ let argvC: { series: string | undefined; s: string | undefined; e: string | undefined; + extid: string | undefined; q: number; x: number; kstream: number; diff --git a/modules/module.args.ts b/modules/module.args.ts index f13a308..be8961a 100644 --- a/modules/module.args.ts +++ b/modules/module.args.ts @@ -165,6 +165,17 @@ const args: TAppArg[] = [ 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', diff --git a/modules/module.parseSelect.ts b/modules/module.parseSelect.ts index 28bdef4..6376097 100644 --- a/modules/module.parseSelect.ts +++ b/modules/module.parseSelect.ts @@ -54,6 +54,9 @@ const parseSelect = (selectString: string, but = false) : { if (part.match(/[0-9A-Z]{9}/)) { select.push(part); return; + } else if (part.match(/[A-Z]{3}\.[0-9]*/)) { + select.push(part); + return; } const match = part.match(/[A-Za-z]+/); if (match && match.length > 0) {