mirror of
https://github.com/anidl/multi-downloader-nx.git
synced 2026-03-11 17:45:30 +00:00
crunchy hotfix
This commit is contained in:
parent
a0a0bf827a
commit
41a83734b0
4 changed files with 4763 additions and 3830 deletions
80
crunchy.ts
80
crunchy.ts
|
|
@ -382,7 +382,6 @@ export default class Crunchy implements ServiceClass {
|
|||
const authData = new URLSearchParams({
|
||||
'refresh_token': this.token.refresh_token,
|
||||
'grant_type': 'refresh_token',
|
||||
//'grant_type': 'etp_rt_cookie',
|
||||
'scope': 'offline_access',
|
||||
'device_id': uuid,
|
||||
'device_name': 'iPhone',
|
||||
|
|
@ -1587,10 +1586,10 @@ export default class Crunchy implements ServiceClass {
|
|||
|
||||
// Delete the stream if it's not needed
|
||||
if (options.novids && options.noaudio) {
|
||||
if (playStream) {
|
||||
await this.refreshToken(true, true);
|
||||
await this.req.getData(`https://cr-play-service.prd.crunchyrollsvc.com/v1/token/${currentVersion ? currentVersion.guid : currentMediaId}/${playStream.token}`, {...{method: 'DELETE'}, ...AuthHeaders});
|
||||
}
|
||||
// if (playStream) {
|
||||
// await this.refreshToken(true, true);
|
||||
// await this.req.getData(`https://cr-play-service.prd.crunchyrollsvc.com/v1/token/${currentVersion ? currentVersion.guid : currentMediaId}/${playStream.token}`, {...{method: 'DELETE'}, ...AuthHeaders});
|
||||
// }
|
||||
}
|
||||
|
||||
if(!dlFailed && curStream !== undefined && !(options.novids && options.noaudio)){
|
||||
|
|
@ -1602,10 +1601,10 @@ export default class Crunchy implements ServiceClass {
|
|||
const streamPlaylistBody = await streamPlaylistsReq.res.text();
|
||||
if (streamPlaylistBody.match('MPD')) {
|
||||
//We have the stream, so go ahead and delete the active stream
|
||||
if (playStream) {
|
||||
await this.refreshToken(true, true);
|
||||
await this.req.getData(`https://cr-play-service.prd.crunchyrollsvc.com/v1/token/${currentVersion ? currentVersion.guid : currentMediaId}/${playStream.token}`, {...{method: 'DELETE'}, ...AuthHeaders});
|
||||
}
|
||||
// if (playStream) {
|
||||
// await this.refreshToken(true, true);
|
||||
// await this.req.getData(`https://cr-play-service.prd.crunchyrollsvc.com/v1/token/${currentVersion ? currentVersion.guid : currentMediaId}/${playStream.token}`, {...{method: 'DELETE'}, ...AuthHeaders});
|
||||
// }
|
||||
|
||||
//Parse MPD Playlists
|
||||
const streamPlaylists = await parse(streamPlaylistBody, langsData.findLang(langsData.fixLanguageTag(pbData.meta.audio_locale as string) || ''), curStream.url.match(/.*\.urlset\//)[0]);
|
||||
|
|
@ -1776,44 +1775,49 @@ export default class Crunchy implements ServiceClass {
|
|||
|
||||
//Handle Decryption if needed
|
||||
if ((chosenVideoSegments.pssh_wvd ||chosenVideoSegments.pssh_prd || chosenAudioSegments.pssh_wvd || chosenAudioSegments.pssh_prd) && (videoDownloaded || audioDownloaded)) {
|
||||
const assetIdRegex = chosenVideoSegments.segments[0].uri.match(/\/assets\/(?:p\/)?([^_,]+)/);
|
||||
const assetId = assetIdRegex ? assetIdRegex[1] : null;
|
||||
const sessionId = new Date().getUTCMilliseconds().toString().padStart(3, '0') + process.hrtime.bigint().toString().slice(0, 13);
|
||||
// const assetIdRegex = chosenVideoSegments.segments[0].uri.match(/\/assets\/(?:p\/)?([^_,]+)/);
|
||||
// const assetId = assetIdRegex ? assetIdRegex[1] : null;
|
||||
// const sessionId = new Date().getUTCMilliseconds().toString().padStart(3, '0') + process.hrtime.bigint().toString().slice(0, 13);
|
||||
console.info('Decryption Needed, attempting to decrypt');
|
||||
|
||||
const decReq = await this.req.getData(`${api.drm}`, {
|
||||
'method': 'POST',
|
||||
'body': JSON.stringify({
|
||||
'accounting_id': 'crunchyroll',
|
||||
'asset_id': assetId,
|
||||
'session_id': sessionId,
|
||||
'user_id': this.token.account_id
|
||||
}),
|
||||
headers: {
|
||||
'User-Agent': api.defaultUserAgent
|
||||
}
|
||||
});
|
||||
if(!decReq.ok || !decReq.res){
|
||||
console.error('Request to DRM Authentication failed:', decReq.error?.res.status, decReq.error?.message);
|
||||
return undefined;
|
||||
}
|
||||
const authData = await decReq.res.json() as {'custom_data': string, 'token': string};
|
||||
// const decReq = await this.req.getData(`${api.drm}`, {
|
||||
// 'method': 'POST',
|
||||
// 'body': JSON.stringify({
|
||||
// 'accounting_id': 'crunchyroll',
|
||||
// 'asset_id': assetId,
|
||||
// 'session_id': sessionId,
|
||||
// 'user_id': this.token.account_id
|
||||
// }),
|
||||
// headers: {
|
||||
// 'User-Agent': api.defaultUserAgent
|
||||
// }
|
||||
// });
|
||||
// if(!decReq.ok || !decReq.res){
|
||||
// console.error('Request to DRM Authentication failed:', decReq.error?.res.status, decReq.error?.message);
|
||||
// return undefined;
|
||||
// }
|
||||
// const authData = await decReq.res.json() as {'custom_data': string, 'token': string};
|
||||
|
||||
let encryptionKeys;
|
||||
|
||||
if (cdm === 'widevine') {
|
||||
encryptionKeys = await getKeysWVD(chosenVideoSegments.pssh_wvd, 'https://lic.drmtoday.com/license-proxy-widevine/cenc/', {
|
||||
'dt-custom-data': authData.custom_data,
|
||||
'x-dt-auth-token': authData.token
|
||||
encryptionKeys = await getKeysWVD(chosenVideoSegments.pssh_wvd, api.drm_widevine, {
|
||||
Authorization: `Bearer ${this.token.access_token}`,
|
||||
'User-Agent': api.defaultUserAgent,
|
||||
Pragma: 'no-cache',
|
||||
'Cache-Control': 'no-cache',
|
||||
'content-type': 'application/octet-stream',
|
||||
'x-cr-content-id': currentVersion!.guid,
|
||||
'x-cr-video-token': playStream!.token
|
||||
});
|
||||
}
|
||||
|
||||
if (cdm === 'playready') {
|
||||
encryptionKeys = await getKeysPRD(chosenVideoSegments.pssh_prd, 'https://lic.drmtoday.com/license-proxy-headerauth/drmtoday/RightsManager.asmx', {
|
||||
'dt-custom-data': authData.custom_data,
|
||||
'x-dt-auth-token': authData.token
|
||||
});
|
||||
}
|
||||
// if (cdm === 'playready') {
|
||||
// encryptionKeys = await getKeysPRD(chosenVideoSegments.pssh_prd, 'https://lic.drmtoday.com/license-proxy-headerauth/drmtoday/RightsManager.asmx', {
|
||||
// 'dt-custom-data': authData.custom_data,
|
||||
// 'x-dt-auth-token': authData.token
|
||||
// });
|
||||
// }
|
||||
|
||||
if (!encryptionKeys || encryptionKeys.length == 0) {
|
||||
console.error('Failed to get encryption keys');
|
||||
|
|
|
|||
|
|
@ -99,12 +99,17 @@ export async function getKeysWVD(
|
|||
|
||||
//Generate license
|
||||
let response;
|
||||
try {
|
||||
response = await got(licenseServer, {
|
||||
console.log(licenseServer)
|
||||
console.log({
|
||||
method: 'POST',
|
||||
body: session.createLicenseRequest(),
|
||||
headers: authData,
|
||||
responseType: 'text',
|
||||
headers: authData
|
||||
} as any)
|
||||
try {
|
||||
response = await fetch(licenseServer, {
|
||||
method: 'POST',
|
||||
body: session.createLicenseRequest(),
|
||||
headers: authData
|
||||
});
|
||||
} catch (_error) {
|
||||
const error = _error as {
|
||||
|
|
@ -142,19 +147,21 @@ export async function getKeysWVD(
|
|||
return [];
|
||||
}
|
||||
|
||||
if (response.statusCode === 200) {
|
||||
if (response.status === 200) {
|
||||
//Parse License and return keys
|
||||
const buffer = await response.arrayBuffer();
|
||||
const text = new TextDecoder().decode(buffer);
|
||||
try {
|
||||
const json = JSON.parse(response.body);
|
||||
const json = JSON.parse(text);
|
||||
return session.parseLicense(Buffer.from(json['license'], 'base64'));
|
||||
} catch {
|
||||
return session.parseLicense(response.rawBody);
|
||||
return session.parseLicense(Buffer.from(new Uint8Array(buffer)));
|
||||
}
|
||||
} else {
|
||||
console.info(
|
||||
'License request failed:',
|
||||
response.statusMessage,
|
||||
response.body
|
||||
response.status,
|
||||
await response.text()
|
||||
);
|
||||
return [];
|
||||
}
|
||||
|
|
|
|||
|
|
@ -59,13 +59,13 @@ const api: APIType = {
|
|||
rss_gid: `${domain.www}/syndication/feed?type=episodes&group_id=`, // &lang=enUS
|
||||
media_page: `${domain.www}/media-`,
|
||||
series_page: `${domain.www}/series-`,
|
||||
auth: `${domain.api_beta}/auth/v1/token`,
|
||||
auth: `${domain.www}/auth/v1/token`,
|
||||
// mobile api
|
||||
search3: `${domain.api}/autocomplete.0.json`,
|
||||
session: `${domain.api}/start_session.0.json`,
|
||||
collections: `${domain.api}/list_collections.0.json`,
|
||||
// This User-Agent bypasses Cloudflare security of the newer Endpoint
|
||||
defaultUserAgent: 'Crunchyroll/4.75.0 (bundle_identifier:com.crunchyroll.iphone; build_number:4100608.433889621) iOS/18.3.2 Gravity/4.75.0',
|
||||
defaultUserAgent: 'Crunchyroll/4.77.2 (bundle_identifier:com.crunchyroll.iphone; build_number:4139672.438176041) iOS/18.3.2 Gravity/4.77.2',
|
||||
beta_profile: `${domain.api_beta}/accounts/v1/me/profile`,
|
||||
beta_cmsToken: `${domain.api_beta}/index/v2`,
|
||||
search: `${domain.api_beta}/content/v2/discover/search`,
|
||||
|
|
@ -73,6 +73,7 @@ const api: APIType = {
|
|||
beta_browse: `${domain.api_beta}/content/v1/browse`,
|
||||
beta_cms: `${domain.api_beta}/cms/v2`,
|
||||
// beta api
|
||||
// broken - deprecated since 06.05.2025
|
||||
drm: `${domain.api_beta}/drm/v1/auth`,
|
||||
// new drm endpoints
|
||||
drm_widevine: `${domain.www}/license/v1/license/widevine`,
|
||||
|
|
|
|||
8483
pnpm-lock.yaml
8483
pnpm-lock.yaml
File diff suppressed because it is too large
Load diff
Loading…
Reference in a new issue