hotfix crunchyroll 192 audio + cbr video dl

This commit is contained in:
stratumadev 2025-09-04 22:27:40 +02:00
parent dedf284844
commit ec1f4e6ba9
6 changed files with 50 additions and 38 deletions

View file

@ -2,14 +2,14 @@ import { HLSCallback } from 'hls-download';
import { sxItem } from '../crunchy';
import { LanguageItem } from '../modules/module.langsData';
import { DownloadInfo } from './messageHandler';
import { CrunchyPlayStreams } from './enums';
import { CrunchyVideoPlayStreams, CrunchyAudioPlayStreams } from './enums';
export type CrunchyDownloadOptions = {
hslang: string,
// kstream: number,
cstream: keyof typeof CrunchyPlayStreams,
vstream: keyof typeof CrunchyPlayStreams,
astream: keyof typeof CrunchyPlayStreams,
cstream: keyof typeof CrunchyVideoPlayStreams,
vstream: keyof typeof CrunchyVideoPlayStreams,
astream: keyof typeof CrunchyAudioPlayStreams,
tsd?: boolean,
novids?: boolean,
noaudio?: boolean,

View file

@ -1,19 +1,8 @@
export enum CrunchyPlayStreams {
'chrome' = 'web/chrome',
'firefox' = 'web/firefox',
'safari' = 'web/safari',
'edge' = 'web/edge',
'fallback' = 'web/fallback',
'ps4' = 'console/ps4',
'ps5' = 'console/ps5',
'switch' = 'console/switch',
'xboxone' = 'console/xbox_one',
'vidaa' = 'tv/vidaa',
'samsungtv' = 'tv/samsung',
'lgtv' = 'tv/lg',
'rokutv' = 'tv/roku',
'chromecast'= 'tv/chromecast',
'firetv' = 'tv/fire_tv',
export enum CrunchyVideoPlayStreams {
'androidtv' = 'tv/android_tv'
}
export enum CrunchyAudioPlayStreams {
'androidtv' = 'tv/android_tv',
'android' = 'android/phone',
'androidtab'= 'android/tablet'

View file

@ -43,7 +43,7 @@ import { CrunchyAndroidObject } from './@types/crunchyAndroidObject';
import { CrunchyChapters, CrunchyChapter, CrunchyOldChapter } from './@types/crunchyChapters';
import vtt2ass from './modules/module.vtt2ass';
import { CrunchyPlayStream } from './@types/crunchyPlayStreams';
import { CrunchyPlayStreams } from './@types/enums';
import { CrunchyVideoPlayStreams, CrunchyAudioPlayStreams } from './@types/enums';
import { randomUUID } from 'node:crypto';
export type sxItem = {
@ -1434,6 +1434,20 @@ export default class Crunchy implements ServiceClass {
return selectedMedia;
}
private convertDownloadToPlayback(audioUrl: string, videoUrl: string): string {
try {
const url = new URL(audioUrl);
const urla = new URL(videoUrl);
url.pathname = url.pathname.replace('/manifest/download/', '/manifest/');
url.searchParams.delete('downloadGuid');
url.searchParams.set('playbackGuid', urla.searchParams.get('playbackGuid') as string);
return url.toString();
} catch (err) {
return audioUrl;
}
}
public async downloadMediaList(medias: CrunchyEpMeta, options: CrunchyDownloadOptions) : Promise<{
data: DownloadedMedia[],
fileName: string,
@ -1637,6 +1651,7 @@ export default class Crunchy implements ServiceClass {
let videoStream: CrunchyPlayStream | null = null;
let audioStream: CrunchyPlayStream | null = null;
const isDLBypass: boolean = options.astream === 'android' || options.astream === 'androidtab' ? true : false;
if (options.tsd) {
console.warn('Total Session Death Active');
@ -1650,7 +1665,7 @@ export default class Crunchy implements ServiceClass {
}
}
const videoPlaybackReq = await this.req.getData(`https://www.crunchyroll.com/playback/v3/${currentVersion ? currentVersion.guid : currentMediaId}/${CrunchyPlayStreams[options.vstream]}/play`, AuthHeaders);
const videoPlaybackReq = await this.req.getData(`https://www.crunchyroll.com/playback/v3/${currentVersion ? currentVersion.guid : currentMediaId}/${CrunchyVideoPlayStreams[options.vstream]}/play`, AuthHeaders);
if (!videoPlaybackReq.ok || !videoPlaybackReq.res) {
console.warn('Request Video Stream URLs FAILED!');
} else {
@ -1681,8 +1696,8 @@ export default class Crunchy implements ServiceClass {
};
}
if (!options.cstream && (options.vstream !== options.astream)) {
const audioPlaybackReq = await this.req.getData(`https://www.crunchyroll.com/playback/v3/${currentVersion ? currentVersion.guid : currentMediaId}/${CrunchyPlayStreams[options.astream]}/play`, AuthHeaders);
if (!options.cstream && (options.vstream !== options.astream) && videoStream) {
const audioPlaybackReq = await this.req.getData(`https://www.crunchyroll.com/playback/v3/${currentVersion ? currentVersion.guid : currentMediaId}/${CrunchyAudioPlayStreams[options.astream]}/${isDLBypass ? 'download' : 'play'}`, AuthHeaders);
if (!audioPlaybackReq.ok || !audioPlaybackReq.res) {
console.warn('Request Audio Stream URLs FAILED!');
} else {
@ -1695,9 +1710,17 @@ export default class Crunchy implements ServiceClass {
'hardsub_locale': stream.hlang
};
}
derivedPlaystreams[''] = {
url: audioStream.url,
hardsub_locale: ''
if (isDLBypass) {
audioStream.token = videoStream.token;
derivedPlaystreams[''] = {
url: this.convertDownloadToPlayback(audioStream.url, videoStream.url),
hardsub_locale: ''
};
} else {
derivedPlaystreams[''] = {
url: audioStream.url,
hardsub_locale: ''
};
};
pbData.meta = {
audio_locale: audioStream.audioLocale,

View file

@ -48,7 +48,7 @@ const api: APIType = {
bundlejs: 'https://static.crunchyroll.com/vilos-v2/web/vilos/js/bundle.js',
//
// Crunchyroll API
basic_auth_token: 'Ym1icmt4eXgzZDd1NmpzZnlsYTQ6QUlONEQ1VkVfY3Awd1Z6Zk5vUDBZcUhVcllGcDloU2c=',
basic_auth_token: 'cHVnMG43eW11YW9sa2tnaTNsYmo6WGlraXNhQ2FYRllCY1hxb09sa1NUMWg2b1pYbHdESk4=',
auth: `${domain.cr_www}/auth/v1/token`,
profile: `${domain.cr_www}/accounts/v1/me/profile`,
search: `${domain.cr_www}/content/v2/discover/search`,
@ -64,7 +64,7 @@ const api: APIType = {
cms_auth: `${domain.cr_www}/index/v2`,
//
// Crunchyroll Headers
crunchyDefUserAgent: 'Crunchyroll/ANDROIDTV/3.42.1_22267 (Android 16; en-US; sdk_gphone64_x86_64)',
crunchyDefUserAgent: 'Crunchyroll/ANDROIDTV/3.45.0_22272 (Android 16; en-US; sdk_gphone64_x86_64)',
crunchyDefHeader: {},
crunchyAuthHeader: {},
//

View file

@ -5,7 +5,7 @@ import { DownloadInfo } from '../@types/messageHandler';
import { HLSCallback } from './hls-download';
import leven from 'leven';
import { console } from './log';
import { CrunchyPlayStreams } from '../@types/enums';
import { CrunchyVideoPlayStreams, CrunchyAudioPlayStreams } from '../@types/enums';
let argvC: {
[x: string]: unknown;
@ -46,9 +46,9 @@ let argvC: {
q: number;
x: number;
// kstream: number;
cstream: keyof typeof CrunchyPlayStreams;
vstream: keyof typeof CrunchyPlayStreams;
astream: keyof typeof CrunchyPlayStreams;
cstream: keyof typeof CrunchyVideoPlayStreams;
vstream: keyof typeof CrunchyVideoPlayStreams;
astream: keyof typeof CrunchyAudioPlayStreams;
tsd: boolean | undefined;
partsize: number;
hslang: string;

View file

@ -1,5 +1,5 @@
import { aoSearchLocales, dubLanguageCodes, languages, searchLocales, subtitleLanguagesFilter } from './module.langsData';
import { CrunchyPlayStreams } from '../@types/enums';
import { CrunchyVideoPlayStreams, CrunchyAudioPlayStreams } from '../@types/enums';
const groups = {
'auth': 'Authentication:',
@ -332,7 +332,7 @@ const args: TAppArg<boolean|number|string|unknown[]>[] = [
service: ['crunchy'],
type: 'string',
describe: '(Please use --vstream and --astream instead, this will deprecate soon) Select a specific Crunchyroll playback endpoint by device. Since Crunchyroll has started rolling out their new VBR encodes, we highly recommend using a TV endpoint (e.g. vidaa, samsungtv, lgtv, rokutv, chromecast, firetv, androidtv) to access the old CBR encodes. Please note: The older encodes do not include the new 192 kbps audio, the new audio is only available with the new VBR encodes.',
choices: [...Object.keys(CrunchyPlayStreams), 'none'],
choices: [...Object.keys(CrunchyVideoPlayStreams), 'none'],
docDescribe: true,
usage: '${device}'
},
@ -343,7 +343,7 @@ const args: TAppArg<boolean|number|string|unknown[]>[] = [
service: ['crunchy'],
type: 'string',
describe: 'Select a specific Crunchyroll video playback endpoint by device.',
choices: [...Object.keys(CrunchyPlayStreams), 'none'],
choices: [...Object.keys(CrunchyVideoPlayStreams), 'none'],
default: {
default: 'androidtv'
},
@ -357,9 +357,9 @@ const args: TAppArg<boolean|number|string|unknown[]>[] = [
service: ['crunchy'],
type: 'string',
describe: 'Select a specific Crunchyroll audio playback endpoint by device.',
choices: [...Object.keys(CrunchyPlayStreams), 'none'],
choices: [...Object.keys(CrunchyAudioPlayStreams), 'none'],
default: {
default: 'androidtv'
default: 'android'
},
docDescribe: true,
usage: '${device}'