Hotfix for CR

This does not include DRM decryption, that will come later, however, this does work for however long CR allows it to.
This commit is contained in:
AnimeDL 2023-12-15 09:21:05 -08:00
parent 31867e216f
commit df7dd06235
4 changed files with 152 additions and 71 deletions

4
.gitignore vendored
View file

@ -33,3 +33,7 @@ gui/react/build/
docker-compose.yml
crunchyendpoints
.vscode
/logs
/tmp/*/
/videos/*/
/tmp/*.*

56
@types/crunchyAndroidStreams.d.ts vendored Normal file
View file

@ -0,0 +1,56 @@
export interface CrunchyAndroidStreams {
__class__: string;
__href__: string;
__resource_key__: string;
__links__: Links;
__actions__: Actions;
media_id: string;
audio_locale: string;
subtitles: { [key: string]: Subtitle };
closed_captions: Actions;
streams: Streams;
bifs: string[];
versions: Version[];
captions: Actions;
}
export interface Actions {
}
export interface Links {
resource: Resource;
}
export interface Resource {
href: string;
}
export interface Streams {
[key: string]: { [key: string]: Download };
}
export interface Download {
hardsub_locale: string;
hardsub_lang?: string;
url: string;
}
export interface Urls {
'': Download;
}
export interface Subtitle {
locale: string;
url: string;
format: string;
}
export interface Version {
audio_locale: string;
guid: string;
original: boolean;
variant: string;
season_guid: string;
media_guid: string;
is_premium_only: boolean;
}

View file

@ -27,12 +27,13 @@ import { CrunchyEpisodeList, CrunchyEpisode } from './@types/crunchyEpisodeList'
import { CrunchyDownloadOptions, CrunchyEpMeta, CrunchyMuxOptions, CurnchyMultiDownload, DownloadedMedia, ParseItem, SeriesSearch, SeriesSearchItem } from './@types/crunchyTypes';
import { ObjectInfo } from './@types/objectInfo';
import parseFileName, { Variable } from './modules/module.filename';
import { PlaybackData } from './@types/playbackData';
//import { PlaybackData } from './@types/playbackData';
import { downloaded } from './modules/module.downloadArchive';
import parseSelect from './modules/module.parseSelect';
import { AvailableFilenameVars, getDefault } from './modules/module.args';
import { AuthData, AuthResponse, Episode, ResponseBase, SearchData, SearchResponse, SearchResponseItem } from './@types/messageHandler';
import { ServiceClass } from './@types/serviceClassInterface';
import { CrunchyAndroidStreams } from './@types/crunchyAndroidStreams';
export type sxItem = {
language: langsData.LanguageItem,
@ -1086,7 +1087,23 @@ export default class Crunchy implements ServiceClass {
if (mediaId.includes(':'))
mediaId = mediaId.split(':')[1];
let playbackReq = await this.req.getData(`${api.cms}/videos/${mediaId}/streams`, AuthHeaders);
// /cms/v2/US/M3/crunchyroll/videos/MEDIAID/streams
const videoStreamsReq = [
domain.api_beta,
`/cms/v2/US/M3/crunchyroll/videos/${mediaId}/streams`,
'?',
new URLSearchParams({
streams: 'all',
textType: 'all',
'Policy': this.cmsToken.cms.policy,
'Signature': this.cmsToken.cms.signature,
'Key-Pair-Id': this.cmsToken.cms.key_pair_id,
}),
].join('');
let playbackReq = await this.req.getData(videoStreamsReq as string, AuthHeaders);
//console.info(playbackReq.res.body);
//let playbackReq = await this.req.getData(`${api.cms}/videos/${mediaId}/streams`, AuthHeaders);
if(!playbackReq.ok || !playbackReq.res){
console.error('Request Stream URLs FAILED! Attempting fallback');
playbackReq = await this.req.getData(`${domain.api_beta}${mMeta.playback}`, AuthHeaders);
@ -1096,7 +1113,8 @@ export default class Crunchy implements ServiceClass {
}
}
const pbData = JSON.parse(playbackReq.res.body) as PlaybackData;
//const pbData = JSON.parse(playbackReq.res.body) as PlaybackData;
const pbData = JSON.parse(playbackReq.res.body) as CrunchyAndroidStreams;
variables.push(...([
['title', medias.episodeTitle, true],
@ -1116,10 +1134,11 @@ export default class Crunchy implements ServiceClass {
let streams: any[] = [];
let hsLangs: string[] = [];
const pbStreams = pbData.data[0];
const pbStreams = pbData.streams;
for(const s of Object.keys(pbStreams)){
if(s.match(/hls/) && !s.match(/drm/) && !s.match(/trailer/)) {
//if((s.match(/hls/) || s.match(/dash/)) && !s.match(/trailer/)) {
const pb = Object.values(pbStreams[s]).map(v => {
v.hardsub_lang = v.hardsub_locale
? langsData.fixAndFindCrLC(v.hardsub_locale).locale
@ -1141,7 +1160,7 @@ export default class Crunchy implements ServiceClass {
return undefined;
}
const audDub = langsData.findLang(langsData.fixLanguageTag(pbData.meta.audio_locale as string) || '').code;
const audDub = langsData.findLang(langsData.fixLanguageTag(pbData.audio_locale as string) || '').code;
hsLangs = langsData.sortTags(hsLangs);
streams = streams.map((s) => {
@ -1211,6 +1230,8 @@ export default class Crunchy implements ServiceClass {
console.info('Playlists URL: %s (%s)', curStream.url, curStream.type);
}
let tsFile = undefined;
if(!options.novids && !dlFailed && curStream !== undefined){
const streamPlaylistsReq = await this.req.getData(curStream.url);
if(!streamPlaylistsReq.ok || !streamPlaylistsReq.res){
@ -1332,7 +1353,7 @@ export default class Crunchy implements ServiceClass {
const mathParts = Math.ceil(totalParts / options.partsize);
const mathMsg = `(${mathParts}*${options.partsize})`;
console.info('Total parts in stream:', totalParts, mathMsg);
const tsFile = path.isAbsolute(outFile as string) ? outFile : path.join(this.cfg.dir.content, outFile);
tsFile = path.isAbsolute(outFile as string) ? outFile : path.join(this.cfg.dir.content, outFile);
const split = outFile.split(path.sep).slice(0, -1);
split.forEach((val, ind, arr) => {
const isAbsolut = path.isAbsolute(outFile as string);
@ -1397,8 +1418,8 @@ export default class Crunchy implements ServiceClass {
}
if(!options.skipsubs && options.dlsubs.indexOf('none') == -1){
if(pbData.meta.subtitles && Object.values(pbData.meta.subtitles).length > 0){
const subsData = Object.values(pbData.meta.subtitles);
if(pbData.subtitles && Object.values(pbData.subtitles).length > 0){
const subsData = Object.values(pbData.subtitles);
const subsDataMapped = subsData.map((s) => {
const subLang = langsData.fixAndFindCrLC(s.locale);
return {
@ -1433,7 +1454,7 @@ export default class Crunchy implements ServiceClass {
files.push({
type: 'Subtitle',
...sxData as sxItem,
cc: isCC
cc: isCC,
});
}
else{
@ -1473,7 +1494,7 @@ export default class Crunchy implements ServiceClass {
return {
file: a.path,
language: a.language,
closedCaption: a.cc
closedCaption: a.cc,
};
}),
simul: false,

View file

@ -247,7 +247,7 @@ const args: TAppArg<boolean|number|string|unknown[]>[] = [
group: 'dl',
alias: 'k',
describe: 'Select specific stream',
choices: [1, 2, 3, 4, 5, 6, 7],
choices: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
default: {
default: 1
},