mirror of
https://github.com/anidl/multi-downloader-nx.git
synced 2026-04-21 16:31:55 +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({
|
const authData = new URLSearchParams({
|
||||||
'refresh_token': this.token.refresh_token,
|
'refresh_token': this.token.refresh_token,
|
||||||
'grant_type': 'refresh_token',
|
'grant_type': 'refresh_token',
|
||||||
//'grant_type': 'etp_rt_cookie',
|
|
||||||
'scope': 'offline_access',
|
'scope': 'offline_access',
|
||||||
'device_id': uuid,
|
'device_id': uuid,
|
||||||
'device_name': 'iPhone',
|
'device_name': 'iPhone',
|
||||||
|
|
@ -1587,10 +1586,10 @@ export default class Crunchy implements ServiceClass {
|
||||||
|
|
||||||
// Delete the stream if it's not needed
|
// Delete the stream if it's not needed
|
||||||
if (options.novids && options.noaudio) {
|
if (options.novids && options.noaudio) {
|
||||||
if (playStream) {
|
// if (playStream) {
|
||||||
await this.refreshToken(true, true);
|
// 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});
|
// 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)){
|
if(!dlFailed && curStream !== undefined && !(options.novids && options.noaudio)){
|
||||||
|
|
@ -1602,10 +1601,10 @@ export default class Crunchy implements ServiceClass {
|
||||||
const streamPlaylistBody = await streamPlaylistsReq.res.text();
|
const streamPlaylistBody = await streamPlaylistsReq.res.text();
|
||||||
if (streamPlaylistBody.match('MPD')) {
|
if (streamPlaylistBody.match('MPD')) {
|
||||||
//We have the stream, so go ahead and delete the active stream
|
//We have the stream, so go ahead and delete the active stream
|
||||||
if (playStream) {
|
// if (playStream) {
|
||||||
await this.refreshToken(true, true);
|
// 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});
|
// 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
|
//Parse MPD Playlists
|
||||||
const streamPlaylists = await parse(streamPlaylistBody, langsData.findLang(langsData.fixLanguageTag(pbData.meta.audio_locale as string) || ''), curStream.url.match(/.*\.urlset\//)[0]);
|
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
|
//Handle Decryption if needed
|
||||||
if ((chosenVideoSegments.pssh_wvd ||chosenVideoSegments.pssh_prd || chosenAudioSegments.pssh_wvd || chosenAudioSegments.pssh_prd) && (videoDownloaded || audioDownloaded)) {
|
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 assetIdRegex = chosenVideoSegments.segments[0].uri.match(/\/assets\/(?:p\/)?([^_,]+)/);
|
||||||
const assetId = assetIdRegex ? assetIdRegex[1] : null;
|
// const assetId = assetIdRegex ? assetIdRegex[1] : null;
|
||||||
const sessionId = new Date().getUTCMilliseconds().toString().padStart(3, '0') + process.hrtime.bigint().toString().slice(0, 13);
|
// const sessionId = new Date().getUTCMilliseconds().toString().padStart(3, '0') + process.hrtime.bigint().toString().slice(0, 13);
|
||||||
console.info('Decryption Needed, attempting to decrypt');
|
console.info('Decryption Needed, attempting to decrypt');
|
||||||
|
|
||||||
const decReq = await this.req.getData(`${api.drm}`, {
|
// const decReq = await this.req.getData(`${api.drm}`, {
|
||||||
'method': 'POST',
|
// 'method': 'POST',
|
||||||
'body': JSON.stringify({
|
// 'body': JSON.stringify({
|
||||||
'accounting_id': 'crunchyroll',
|
// 'accounting_id': 'crunchyroll',
|
||||||
'asset_id': assetId,
|
// 'asset_id': assetId,
|
||||||
'session_id': sessionId,
|
// 'session_id': sessionId,
|
||||||
'user_id': this.token.account_id
|
// 'user_id': this.token.account_id
|
||||||
}),
|
// }),
|
||||||
headers: {
|
// headers: {
|
||||||
'User-Agent': api.defaultUserAgent
|
// 'User-Agent': api.defaultUserAgent
|
||||||
}
|
// }
|
||||||
});
|
// });
|
||||||
if(!decReq.ok || !decReq.res){
|
// if(!decReq.ok || !decReq.res){
|
||||||
console.error('Request to DRM Authentication failed:', decReq.error?.res.status, decReq.error?.message);
|
// console.error('Request to DRM Authentication failed:', decReq.error?.res.status, decReq.error?.message);
|
||||||
return undefined;
|
// return undefined;
|
||||||
}
|
// }
|
||||||
const authData = await decReq.res.json() as {'custom_data': string, 'token': string};
|
// const authData = await decReq.res.json() as {'custom_data': string, 'token': string};
|
||||||
|
|
||||||
let encryptionKeys;
|
let encryptionKeys;
|
||||||
|
|
||||||
if (cdm === 'widevine') {
|
if (cdm === 'widevine') {
|
||||||
encryptionKeys = await getKeysWVD(chosenVideoSegments.pssh_wvd, 'https://lic.drmtoday.com/license-proxy-widevine/cenc/', {
|
encryptionKeys = await getKeysWVD(chosenVideoSegments.pssh_wvd, api.drm_widevine, {
|
||||||
'dt-custom-data': authData.custom_data,
|
Authorization: `Bearer ${this.token.access_token}`,
|
||||||
'x-dt-auth-token': authData.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') {
|
// if (cdm === 'playready') {
|
||||||
encryptionKeys = await getKeysPRD(chosenVideoSegments.pssh_prd, 'https://lic.drmtoday.com/license-proxy-headerauth/drmtoday/RightsManager.asmx', {
|
// encryptionKeys = await getKeysPRD(chosenVideoSegments.pssh_prd, 'https://lic.drmtoday.com/license-proxy-headerauth/drmtoday/RightsManager.asmx', {
|
||||||
'dt-custom-data': authData.custom_data,
|
// 'dt-custom-data': authData.custom_data,
|
||||||
'x-dt-auth-token': authData.token
|
// 'x-dt-auth-token': authData.token
|
||||||
});
|
// });
|
||||||
}
|
// }
|
||||||
|
|
||||||
if (!encryptionKeys || encryptionKeys.length == 0) {
|
if (!encryptionKeys || encryptionKeys.length == 0) {
|
||||||
console.error('Failed to get encryption keys');
|
console.error('Failed to get encryption keys');
|
||||||
|
|
|
||||||
|
|
@ -99,12 +99,17 @@ export async function getKeysWVD(
|
||||||
|
|
||||||
//Generate license
|
//Generate license
|
||||||
let response;
|
let response;
|
||||||
try {
|
console.log(licenseServer)
|
||||||
response = await got(licenseServer, {
|
console.log({
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
body: session.createLicenseRequest(),
|
body: session.createLicenseRequest(),
|
||||||
headers: authData,
|
headers: authData
|
||||||
responseType: 'text',
|
} as any)
|
||||||
|
try {
|
||||||
|
response = await fetch(licenseServer, {
|
||||||
|
method: 'POST',
|
||||||
|
body: session.createLicenseRequest(),
|
||||||
|
headers: authData
|
||||||
});
|
});
|
||||||
} catch (_error) {
|
} catch (_error) {
|
||||||
const error = _error as {
|
const error = _error as {
|
||||||
|
|
@ -142,19 +147,21 @@ export async function getKeysWVD(
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (response.statusCode === 200) {
|
if (response.status === 200) {
|
||||||
//Parse License and return keys
|
//Parse License and return keys
|
||||||
|
const buffer = await response.arrayBuffer();
|
||||||
|
const text = new TextDecoder().decode(buffer);
|
||||||
try {
|
try {
|
||||||
const json = JSON.parse(response.body);
|
const json = JSON.parse(text);
|
||||||
return session.parseLicense(Buffer.from(json['license'], 'base64'));
|
return session.parseLicense(Buffer.from(json['license'], 'base64'));
|
||||||
} catch {
|
} catch {
|
||||||
return session.parseLicense(response.rawBody);
|
return session.parseLicense(Buffer.from(new Uint8Array(buffer)));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
console.info(
|
console.info(
|
||||||
'License request failed:',
|
'License request failed:',
|
||||||
response.statusMessage,
|
response.status,
|
||||||
response.body
|
await response.text()
|
||||||
);
|
);
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -59,13 +59,13 @@ const api: APIType = {
|
||||||
rss_gid: `${domain.www}/syndication/feed?type=episodes&group_id=`, // &lang=enUS
|
rss_gid: `${domain.www}/syndication/feed?type=episodes&group_id=`, // &lang=enUS
|
||||||
media_page: `${domain.www}/media-`,
|
media_page: `${domain.www}/media-`,
|
||||||
series_page: `${domain.www}/series-`,
|
series_page: `${domain.www}/series-`,
|
||||||
auth: `${domain.api_beta}/auth/v1/token`,
|
auth: `${domain.www}/auth/v1/token`,
|
||||||
// mobile api
|
// mobile api
|
||||||
search3: `${domain.api}/autocomplete.0.json`,
|
search3: `${domain.api}/autocomplete.0.json`,
|
||||||
session: `${domain.api}/start_session.0.json`,
|
session: `${domain.api}/start_session.0.json`,
|
||||||
collections: `${domain.api}/list_collections.0.json`,
|
collections: `${domain.api}/list_collections.0.json`,
|
||||||
// This User-Agent bypasses Cloudflare security of the newer Endpoint
|
// 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_profile: `${domain.api_beta}/accounts/v1/me/profile`,
|
||||||
beta_cmsToken: `${domain.api_beta}/index/v2`,
|
beta_cmsToken: `${domain.api_beta}/index/v2`,
|
||||||
search: `${domain.api_beta}/content/v2/discover/search`,
|
search: `${domain.api_beta}/content/v2/discover/search`,
|
||||||
|
|
@ -73,6 +73,7 @@ const api: APIType = {
|
||||||
beta_browse: `${domain.api_beta}/content/v1/browse`,
|
beta_browse: `${domain.api_beta}/content/v1/browse`,
|
||||||
beta_cms: `${domain.api_beta}/cms/v2`,
|
beta_cms: `${domain.api_beta}/cms/v2`,
|
||||||
// beta api
|
// beta api
|
||||||
|
// broken - deprecated since 06.05.2025
|
||||||
drm: `${domain.api_beta}/drm/v1/auth`,
|
drm: `${domain.api_beta}/drm/v1/auth`,
|
||||||
// new drm endpoints
|
// new drm endpoints
|
||||||
drm_widevine: `${domain.www}/license/v1/license/widevine`,
|
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