mirror of
https://github.com/anidl/multi-downloader-nx.git
synced 2026-04-21 16:31:55 +00:00
reverted crunchyroll to legacy api
This commit is contained in:
parent
ea53e9071f
commit
160c8a1cb4
2 changed files with 75 additions and 35 deletions
58
crunchy.ts
58
crunchy.ts
|
|
@ -390,15 +390,20 @@ export default class Crunchy implements ServiceClass {
|
||||||
// }
|
// }
|
||||||
|
|
||||||
public async doAuth(data: AuthData): Promise<AuthResponse> {
|
public async doAuth(data: AuthData): Promise<AuthResponse> {
|
||||||
|
const basic = atob(api.basic_auth_token);
|
||||||
|
const client = basic.split(':');
|
||||||
|
|
||||||
const uuid = randomUUID();
|
const uuid = randomUUID();
|
||||||
const authData = new URLSearchParams({
|
const authData = new URLSearchParams({
|
||||||
username: data.username,
|
username: data.username,
|
||||||
password: data.password,
|
password: data.password,
|
||||||
grant_type: 'password',
|
grant_type: 'password',
|
||||||
scope: 'offline_access',
|
scope: 'offline_access',
|
||||||
|
client_id: client[0],
|
||||||
|
client_secret: client[1],
|
||||||
device_id: uuid,
|
device_id: uuid,
|
||||||
device_name: 'iPhone',
|
device_name: 'emu64xa',
|
||||||
device_type: 'iPhone 13'
|
device_type: 'ANDROIDTV'
|
||||||
}).toString();
|
}).toString();
|
||||||
const authReqOpts: FetchParams = {
|
const authReqOpts: FetchParams = {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
|
|
@ -411,6 +416,7 @@ export default class Crunchy implements ServiceClass {
|
||||||
console.error('Authentication failed!');
|
console.error('Authentication failed!');
|
||||||
return { isOk: false, reason: new Error('Authentication failed') };
|
return { isOk: false, reason: new Error('Authentication failed') };
|
||||||
}
|
}
|
||||||
|
|
||||||
// To prevent any Cloudflare errors in the future
|
// To prevent any Cloudflare errors in the future
|
||||||
if (authReq.res.headers.get('Set-Cookie')) {
|
if (authReq.res.headers.get('Set-Cookie')) {
|
||||||
api.crunchyDefHeader['Cookie'] = authReq.res.headers.get('Set-Cookie') as string;
|
api.crunchyDefHeader['Cookie'] = authReq.res.headers.get('Set-Cookie') as string;
|
||||||
|
|
@ -422,6 +428,7 @@ export default class Crunchy implements ServiceClass {
|
||||||
api.crunchyDefHeader['User-Agent'] = authReq.headers['User-Agent'];
|
api.crunchyDefHeader['User-Agent'] = authReq.headers['User-Agent'];
|
||||||
api.crunchyAuthHeader['User-Agent'] = authReq.headers['User-Agent'];
|
api.crunchyAuthHeader['User-Agent'] = authReq.headers['User-Agent'];
|
||||||
}
|
}
|
||||||
|
|
||||||
this.token = await authReq.res.json();
|
this.token = await authReq.res.json();
|
||||||
this.token.device_id = uuid;
|
this.token.device_id = uuid;
|
||||||
this.token.expires = new Date(Date.now() + this.token.expires_in * 1000);
|
this.token.expires = new Date(Date.now() + this.token.expires_in * 1000);
|
||||||
|
|
@ -432,13 +439,18 @@ export default class Crunchy implements ServiceClass {
|
||||||
}
|
}
|
||||||
|
|
||||||
public async doAnonymousAuth() {
|
public async doAnonymousAuth() {
|
||||||
|
const basic = atob(api.basic_auth_token);
|
||||||
|
const client = basic.split(':');
|
||||||
|
|
||||||
const uuid = randomUUID();
|
const uuid = randomUUID();
|
||||||
const authData = new URLSearchParams({
|
const authData = new URLSearchParams({
|
||||||
grant_type: 'client_id',
|
grant_type: 'client_id',
|
||||||
scope: 'offline_access',
|
scope: 'offline_access',
|
||||||
|
client_id: client[0],
|
||||||
|
client_secret: client[1],
|
||||||
device_id: uuid,
|
device_id: uuid,
|
||||||
device_name: 'iPhone',
|
device_name: 'emu64xa',
|
||||||
device_type: 'iPhone 13'
|
device_type: 'ANDROIDTV'
|
||||||
}).toString();
|
}).toString();
|
||||||
const authReqOpts: FetchParams = {
|
const authReqOpts: FetchParams = {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
|
|
@ -462,6 +474,7 @@ export default class Crunchy implements ServiceClass {
|
||||||
api.crunchyDefHeader['User-Agent'] = authReq.headers['User-Agent'];
|
api.crunchyDefHeader['User-Agent'] = authReq.headers['User-Agent'];
|
||||||
api.crunchyAuthHeader['User-Agent'] = authReq.headers['User-Agent'];
|
api.crunchyAuthHeader['User-Agent'] = authReq.headers['User-Agent'];
|
||||||
}
|
}
|
||||||
|
|
||||||
this.token = await authReq.res.json();
|
this.token = await authReq.res.json();
|
||||||
this.token.device_id = uuid;
|
this.token.device_id = uuid;
|
||||||
this.token.expires = new Date(Date.now() + this.token.expires_in * 1000);
|
this.token.expires = new Date(Date.now() + this.token.expires_in * 1000);
|
||||||
|
|
@ -473,6 +486,7 @@ export default class Crunchy implements ServiceClass {
|
||||||
console.error('No access token!');
|
console.error('No access token!');
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
const profileReqOptions = {
|
const profileReqOptions = {
|
||||||
headers: {
|
headers: {
|
||||||
...api.crunchyDefHeader,
|
...api.crunchyDefHeader,
|
||||||
|
|
@ -498,15 +512,20 @@ export default class Crunchy implements ServiceClass {
|
||||||
}
|
}
|
||||||
|
|
||||||
public async loginWithToken(refreshToken: string) {
|
public async loginWithToken(refreshToken: string) {
|
||||||
|
const basic = atob(api.basic_auth_token);
|
||||||
|
const client = basic.split(':');
|
||||||
|
|
||||||
const uuid = randomUUID();
|
const uuid = randomUUID();
|
||||||
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',
|
//'grant_type': 'etp_rt_cookie',
|
||||||
scope: 'offline_access',
|
scope: 'offline_access',
|
||||||
|
client_id: client[0],
|
||||||
|
client_secret: client[1],
|
||||||
device_id: uuid,
|
device_id: uuid,
|
||||||
device_name: 'iPhone',
|
device_name: 'emu64xa',
|
||||||
device_type: 'iPhone 13'
|
device_type: 'ANDROIDTV'
|
||||||
}).toString();
|
}).toString();
|
||||||
const authReqOpts: FetchParams = {
|
const authReqOpts: FetchParams = {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
|
|
@ -533,6 +552,7 @@ export default class Crunchy implements ServiceClass {
|
||||||
api.crunchyDefHeader['User-Agent'] = authReq.headers['User-Agent'];
|
api.crunchyDefHeader['User-Agent'] = authReq.headers['User-Agent'];
|
||||||
api.crunchyAuthHeader['User-Agent'] = authReq.headers['User-Agent'];
|
api.crunchyAuthHeader['User-Agent'] = authReq.headers['User-Agent'];
|
||||||
}
|
}
|
||||||
|
|
||||||
this.token = await authReq.res.json();
|
this.token = await authReq.res.json();
|
||||||
this.token.device_id = uuid;
|
this.token.device_id = uuid;
|
||||||
this.token.expires = new Date(Date.now() + this.token.expires_in * 1000);
|
this.token.expires = new Date(Date.now() + this.token.expires_in * 1000);
|
||||||
|
|
@ -552,17 +572,24 @@ export default class Crunchy implements ServiceClass {
|
||||||
} else {
|
} else {
|
||||||
//console.info('[WARN] The token has expired compleatly. I will try to refresh the token anyway, but you might have to reauth.');
|
//console.info('[WARN] The token has expired compleatly. I will try to refresh the token anyway, but you might have to reauth.');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const basic = atob(api.basic_auth_token);
|
||||||
|
const client = basic.split(':');
|
||||||
|
|
||||||
const uuid = this.token.device_id || randomUUID();
|
const uuid = this.token.device_id || randomUUID();
|
||||||
const authData = new URLSearchParams({
|
const authData = new URLSearchParams({
|
||||||
refresh_token: this.token.refresh_token,
|
|
||||||
grant_type: 'refresh_token',
|
grant_type: 'refresh_token',
|
||||||
|
refresh_token: this.token.refresh_token,
|
||||||
|
scope: 'offline_access',
|
||||||
|
client_id: client[0],
|
||||||
|
client_secret: client[1],
|
||||||
device_id: uuid,
|
device_id: uuid,
|
||||||
device_name: 'iPhone',
|
device_name: 'emu64xa',
|
||||||
device_type: 'iPhone 13'
|
device_type: 'ANDROIDTV'
|
||||||
}).toString();
|
}).toString();
|
||||||
const authReqOpts: FetchParams = {
|
const authReqOpts: FetchParams = {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: { ...api.crunchyAuthHeader, 'ETP-Anonymous-ID': uuid },
|
headers: { ...api.crunchyAuthHeader },
|
||||||
body: authData,
|
body: authData,
|
||||||
useProxy: true
|
useProxy: true
|
||||||
};
|
};
|
||||||
|
|
@ -585,6 +612,7 @@ export default class Crunchy implements ServiceClass {
|
||||||
api.crunchyDefHeader['User-Agent'] = authReq.headers['User-Agent'];
|
api.crunchyDefHeader['User-Agent'] = authReq.headers['User-Agent'];
|
||||||
api.crunchyAuthHeader['User-Agent'] = authReq.headers['User-Agent'];
|
api.crunchyAuthHeader['User-Agent'] = authReq.headers['User-Agent'];
|
||||||
}
|
}
|
||||||
|
|
||||||
this.token = await authReq.res.json();
|
this.token = await authReq.res.json();
|
||||||
this.token.device_id = uuid;
|
this.token.device_id = uuid;
|
||||||
this.token.expires = new Date(Date.now() + this.token.expires_in * 1000);
|
this.token.expires = new Date(Date.now() + this.token.expires_in * 1000);
|
||||||
|
|
@ -1719,7 +1747,7 @@ export default class Crunchy implements ServiceClass {
|
||||||
const me = await this.req.getData(api.me, AuthHeaders);
|
const me = await this.req.getData(api.me, AuthHeaders);
|
||||||
if (me.ok && me.res) {
|
if (me.ok && me.res) {
|
||||||
const data_me = await me.res.json();
|
const data_me = await me.res.json();
|
||||||
const benefits = await this.req.getData(`https://www.crunchyroll.com/subs/v1/subscriptions/${data_me.external_id}/benefits`, AuthHeaders);
|
const benefits = await this.req.getData(`https://beta-api.crunchyroll.com/subs/v1/subscriptions/${data_me.external_id}/benefits`, AuthHeaders);
|
||||||
if (benefits.ok && benefits.res) {
|
if (benefits.ok && benefits.res) {
|
||||||
const data_benefits = (await benefits.res.json()) as { items: { benefit: string }[] };
|
const data_benefits = (await benefits.res.json()) as { items: { benefit: string }[] };
|
||||||
if (data_benefits?.items && !data_benefits.items.find((i) => i.benefit === 'offline_viewing')) {
|
if (data_benefits?.items && !data_benefits.items.find((i) => i.benefit === 'offline_viewing')) {
|
||||||
|
|
@ -1763,14 +1791,14 @@ export default class Crunchy implements ServiceClass {
|
||||||
if (activeStreamsReq.ok && activeStreamsReq.res) {
|
if (activeStreamsReq.ok && activeStreamsReq.res) {
|
||||||
const data = await activeStreamsReq.res.json();
|
const data = await activeStreamsReq.res.json();
|
||||||
for (const s of data.items) {
|
for (const s of data.items) {
|
||||||
await this.req.getData(`https://www.crunchyroll.com/playback/v1/token/${s.contentId}/${s.token}`, { ...{ method: 'DELETE' }, ...AuthHeaders });
|
await this.req.getData(`https://cr-play-service.prd.crunchyrollsvc.com/v1/token/${s.contentId}/${s.token}`, { ...{ method: 'DELETE' }, ...AuthHeaders });
|
||||||
}
|
}
|
||||||
console.warn(`Killed ${data.items?.length ?? 0} Sessions`);
|
console.warn(`Killed ${data.items?.length ?? 0} Sessions`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const videoPlaybackReq = await this.req.getData(
|
const videoPlaybackReq = await this.req.getData(
|
||||||
`https://www.crunchyroll.com/playback/v3/${currentVersion ? currentVersion.guid : currentMediaId}/${CrunchyVideoPlayStreams['androidtv']}/play?queue=0`,
|
`https://cr-play-service.prd.crunchyrollsvc.com/v3/${currentVersion ? currentVersion.guid : currentMediaId}/${CrunchyVideoPlayStreams['androidtv']}/play?queue=0`,
|
||||||
AuthHeaders
|
AuthHeaders
|
||||||
);
|
);
|
||||||
if (!videoPlaybackReq.ok || !videoPlaybackReq.res) {
|
if (!videoPlaybackReq.ok || !videoPlaybackReq.res) {
|
||||||
|
|
@ -1787,7 +1815,7 @@ export default class Crunchy implements ServiceClass {
|
||||||
}
|
}
|
||||||
if (isDLVideoBypass) {
|
if (isDLVideoBypass) {
|
||||||
const videoDLReq = await this.req.getData(
|
const videoDLReq = await this.req.getData(
|
||||||
`https://www.crunchyroll.com/playback/v3/${currentVersion ? currentVersion.guid : currentMediaId}/${CrunchyVideoPlayStreams[options.vstream]}/download`,
|
`https://cr-play-service.prd.crunchyrollsvc.com/v3/${currentVersion ? currentVersion.guid : currentMediaId}/${CrunchyVideoPlayStreams[options.vstream]}/download`,
|
||||||
AuthHeaders
|
AuthHeaders
|
||||||
);
|
);
|
||||||
if (videoDLReq.ok && videoDLReq.res) {
|
if (videoDLReq.ok && videoDLReq.res) {
|
||||||
|
|
@ -1824,7 +1852,7 @@ export default class Crunchy implements ServiceClass {
|
||||||
|
|
||||||
if (!options.cstream && options.vstream !== options.astream && videoStream) {
|
if (!options.cstream && options.vstream !== options.astream && videoStream) {
|
||||||
const audioPlaybackReq = await this.req.getData(
|
const audioPlaybackReq = await this.req.getData(
|
||||||
`https://www.crunchyroll.com/playback/v3/${currentVersion ? currentVersion.guid : currentMediaId}/${CrunchyAudioPlayStreams[options.astream]}/${isDLAudioBypass ? 'download' : 'play?queue=1'}`,
|
`https://cr-play-service.prd.crunchyrollsvc.com/v3/${currentVersion ? currentVersion.guid : currentMediaId}/${CrunchyAudioPlayStreams[options.astream]}/${isDLAudioBypass ? 'download' : 'play?queue=1'}`,
|
||||||
AuthHeaders
|
AuthHeaders
|
||||||
);
|
);
|
||||||
if (!audioPlaybackReq.ok || !audioPlaybackReq.res) {
|
if (!audioPlaybackReq.ok || !audioPlaybackReq.res) {
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,9 @@
|
||||||
// api domains
|
// api domains
|
||||||
const domain = {
|
const domain = {
|
||||||
cr_www: 'https://www.crunchyroll.com',
|
cr_www: 'https://www.crunchyroll.com',
|
||||||
cr_api: 'https://api.crunchyroll.com',
|
cr_api: 'https://beta-api.crunchyroll.com',
|
||||||
|
cr_playback: 'https://cr-play-service.prd.crunchyrollsvc.com',
|
||||||
|
cr_license: 'https://cr-license-proxy.prd.crunchyrollsvc.com',
|
||||||
hd_www: 'https://www.hidive.com',
|
hd_www: 'https://www.hidive.com',
|
||||||
hd_api: 'https://api.hidive.com',
|
hd_api: 'https://api.hidive.com',
|
||||||
hd_new: 'https://dce-frontoffice.imggaming.com'
|
hd_new: 'https://dce-frontoffice.imggaming.com'
|
||||||
|
|
@ -28,8 +30,9 @@ export type APIType = {
|
||||||
cms_auth: string;
|
cms_auth: string;
|
||||||
// Crunchyroll Headers
|
// Crunchyroll Headers
|
||||||
crunchyDefUserAgent: string;
|
crunchyDefUserAgent: string;
|
||||||
crunchyDefHeader: Record<string, string>;
|
crunchyDefHeader: Record<string, any>;
|
||||||
crunchyAuthHeader: Record<string, string>;
|
crunchyAuthHeader: Record<string, string>;
|
||||||
|
crunchyAuthRefreshHeader: Record<string, string>;
|
||||||
// Hidive
|
// Hidive
|
||||||
hd_apikey: string;
|
hd_apikey: string;
|
||||||
hd_devName: string;
|
hd_devName: string;
|
||||||
|
|
@ -50,27 +53,28 @@ const api: APIType = {
|
||||||
bundlejs: 'https://static.crunchyroll.com/vilos-v2/web/vilos/js/bundle.js',
|
bundlejs: 'https://static.crunchyroll.com/vilos-v2/web/vilos/js/bundle.js',
|
||||||
//
|
//
|
||||||
// Crunchyroll API
|
// Crunchyroll API
|
||||||
basic_auth_token: 'bGtlc2k3c25zeTlvb2ptaTJyOWg6LWFHRFhGRk5UbHVaTUxZWEVSbmdOWW5FanZnSDVvZHY=',
|
basic_auth_token: 'bmR0aTZicXlqcm9wNXZnZjF0dnU6elpIcS00SEJJVDlDb2FMcnBPREJjRVRCTUNHai1QNlg=',
|
||||||
auth: `${domain.cr_www}/auth/v1/token`,
|
auth: `${domain.cr_api}/auth/v1/token`,
|
||||||
me: `${domain.cr_www}/accounts/v1/me`,
|
me: `${domain.cr_api}/accounts/v1/me`,
|
||||||
profile: `${domain.cr_www}/accounts/v1/me/profile`,
|
profile: `${domain.cr_api}/accounts/v1/me/profile`,
|
||||||
search: `${domain.cr_www}/content/v2/discover/search`,
|
search: `${domain.cr_api}/content/v2/discover/search`,
|
||||||
content_cms: `${domain.cr_www}/content/v2/cms`,
|
content_cms: `${domain.cr_api}/content/v2/cms`,
|
||||||
content_music: `${domain.cr_www}/content/v2/music`,
|
content_music: `${domain.cr_api}/content/v2/music`,
|
||||||
browse: `${domain.cr_www}/content/v1/browse`,
|
browse: `${domain.cr_api}/content/v1/browse`,
|
||||||
browse_all_series: `${domain.cr_www}/content/v2/discover/browse`,
|
browse_all_series: `${domain.cr_api}/content/v2/discover/browse`,
|
||||||
streaming_sessions: `${domain.cr_www}/playback/v1/sessions/streaming`,
|
streaming_sessions: `${domain.cr_playback}/v1/sessions/streaming`,
|
||||||
drm_widevine: `${domain.cr_www}/license/v1/license/widevine`,
|
drm_widevine: `https://cr-license-proxy.prd.crunchyrollsvc.com/v1/license/widevine`,
|
||||||
drm_playready: `${domain.cr_www}/license/v1/license/playReady`,
|
drm_playready: `https://cr-license-proxy.prd.crunchyrollsvc.com/v1/license/playReady`,
|
||||||
//
|
//
|
||||||
// Crunchyroll Bucket
|
// Crunchyroll Bucket
|
||||||
cms_bucket: `${domain.cr_www}/cms/v2`,
|
cms_bucket: `${domain.cr_api}/cms/v2`,
|
||||||
cms_auth: `${domain.cr_www}/index/v2`,
|
cms_auth: `${domain.cr_api}/index/v2`,
|
||||||
//
|
//
|
||||||
// Crunchyroll Headers
|
// Crunchyroll Headers
|
||||||
crunchyDefUserAgent: 'Crunchyroll/ANDROIDTV/3.49.1_22281 (Android 12; en-US; SHIELD Android TV Build/SR1A.211012.001)',
|
crunchyDefUserAgent: 'Crunchyroll/ANDROIDTV/3.50.0_22282 (Android 16; en-US; sdk_gphone64_x86_64)',
|
||||||
crunchyDefHeader: {},
|
crunchyDefHeader: {},
|
||||||
crunchyAuthHeader: {},
|
crunchyAuthHeader: {},
|
||||||
|
crunchyAuthRefreshHeader: {},
|
||||||
//
|
//
|
||||||
//
|
//
|
||||||
// Hidive
|
// Hidive
|
||||||
|
|
@ -89,7 +93,6 @@ const api: APIType = {
|
||||||
|
|
||||||
api.crunchyDefHeader = {
|
api.crunchyDefHeader = {
|
||||||
'User-Agent': api.crunchyDefUserAgent,
|
'User-Agent': api.crunchyDefUserAgent,
|
||||||
Accept: '*/*',
|
|
||||||
'Accept-Encoding': 'gzip',
|
'Accept-Encoding': 'gzip',
|
||||||
Connection: 'Keep-Alive',
|
Connection: 'Keep-Alive',
|
||||||
Host: 'www.crunchyroll.com'
|
Host: 'www.crunchyroll.com'
|
||||||
|
|
@ -97,10 +100,19 @@ api.crunchyDefHeader = {
|
||||||
|
|
||||||
// set header
|
// set header
|
||||||
api.crunchyAuthHeader = {
|
api.crunchyAuthHeader = {
|
||||||
Authorization: `Basic ${api.basic_auth_token}`,
|
Accept: 'application/json',
|
||||||
'Content-Type': 'application/x-www-form-urlencoded; charset=utf-8',
|
'Accept-Charset': 'UTF-8',
|
||||||
|
'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',
|
||||||
'Request-Type': 'SignIn',
|
'Request-Type': 'SignIn',
|
||||||
...api.crunchyDefHeader
|
...api.crunchyDefHeader
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// set header
|
||||||
|
api.crunchyAuthRefreshHeader = {
|
||||||
|
Accept: 'application/json',
|
||||||
|
'Accept-Charset': 'UTF-8',
|
||||||
|
'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',
|
||||||
|
...api.crunchyDefHeader
|
||||||
|
};
|
||||||
|
|
||||||
export { domain, api };
|
export { domain, api };
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue