updated cr bucket requests to www.

This commit is contained in:
stratumadev 2025-08-02 21:34:16 +02:00
parent 025b9d483c
commit b92ab022c7
2 changed files with 135 additions and 155 deletions

View file

@ -73,7 +73,7 @@ export default class Crunchy implements ServiceClass {
}
public checkToken(): boolean {
return Object.keys(this.cmsToken.cms ?? {}).length > 0;
return Object.keys(this.cmsToken.cms_web ?? {}).length > 0;
}
public async cli() {
@ -202,7 +202,7 @@ export default class Crunchy implements ServiceClass {
public async logShowRawById(id: string){
// check token
if(!this.cmsToken.cms){
if(!this.cmsToken.cms_web){
console.error('Authentication required!');
return;
}
@ -215,7 +215,7 @@ export default class Crunchy implements ServiceClass {
useProxy: true
};
// seasons list
const seriesSeasonListReq = await this.req.getData(`${api.cms}/series/${id}/seasons?force_locale=&preferred_audio_language=ja-JP&locale=${this.locale}`, AuthHeaders);
const seriesSeasonListReq = await this.req.getData(`${api.content_cms}/series/${id}/seasons?force_locale=&preferred_audio_language=ja-JP&locale=${this.locale}`, AuthHeaders);
if(!seriesSeasonListReq.ok || !seriesSeasonListReq.res){
console.error('Series Request FAILED!');
return;
@ -231,7 +231,7 @@ export default class Crunchy implements ServiceClass {
public async logSeasonRawById(id: string){
// check token
if(!this.cmsToken.cms){
if(!this.cmsToken.cms_web){
console.error('Authentication required!');
return;
}
@ -248,16 +248,16 @@ export default class Crunchy implements ServiceClass {
//get episode info
const reqEpsListOpts = [
api.cms_bucket,
this.cmsToken.cms.bucket,
this.cmsToken.cms_web.bucket,
'/episodes?',
new URLSearchParams({
'force_locale': '',
'preferred_audio_language': 'ja-JP',
'locale': this.locale,
'season_id': id,
'Policy': this.cmsToken.cms.policy,
'Signature': this.cmsToken.cms.signature,
'Key-Pair-Id': this.cmsToken.cms.key_pair_id,
'Policy': this.cmsToken.cms_web.policy,
'Signature': this.cmsToken.cms_web.signature,
'Key-Pair-Id': this.cmsToken.cms_web.key_pair_id,
}),
].join('');
const reqEpsList = await this.req.getData(reqEpsListOpts, AuthHeaders);
@ -283,7 +283,7 @@ export default class Crunchy implements ServiceClass {
public async logShowListRaw() {
// check token
if(!this.cmsToken.cms){
if(!this.cmsToken.cms_web){
console.error('Authentication required!');
return;
}
@ -624,8 +624,8 @@ export default class Crunchy implements ServiceClass {
return;
}
if (ifNeeded && this.cmsToken.cms) {
if (!(Date.now() >= new Date(this.cmsToken.cms.expires).getTime())) {
if (ifNeeded && this.cmsToken.cms_web) {
if (!(Date.now() >= new Date(this.cmsToken.cms_web.expires).getTime())) {
return;
}
}
@ -637,38 +637,38 @@ export default class Crunchy implements ServiceClass {
},
useProxy: true
};
const cmsTokenReq = await this.req.getData(api.cmsToken, cmsTokenReqOpts);
const cmsTokenReq = await this.req.getData(api.cms_auth, cmsTokenReqOpts);
if(!cmsTokenReq.ok || !cmsTokenReq.res){
console.error('Authentication CMS token failed!');
return;
}
this.cmsToken = await cmsTokenReq.res.json();
console.info('Your Country: %s\n', this.cmsToken.cms?.bucket.split('/')[1]);
console.info('Your Country: %s\n', this.cmsToken.cms_web?.bucket.split('/')[1]);
}
public async getCmsData(){
// check token
if(!this.cmsToken.cms){
if(!this.cmsToken.cms_web){
console.error('Authentication required!');
return;
}
// opts
const indexReqOpts = [
api.cms_bucket,
this.cmsToken.cms.bucket,
this.cmsToken.cms_web.bucket,
'/index?',
new URLSearchParams({
'force_locale': '',
'preferred_audio_language': 'ja-JP',
'locale': this.locale,
'Policy': this.cmsToken.cms.policy,
'Signature': this.cmsToken.cms.signature,
'Key-Pair-Id': this.cmsToken.cms.key_pair_id,
'Policy': this.cmsToken.cms_web.policy,
'Signature': this.cmsToken.cms_web.signature,
'Key-Pair-Id': this.cmsToken.cms_web.key_pair_id,
}),
].join('');
const indexReq = await this.req.getData(indexReqOpts, {
headers: {
'User-Agent': api.defaultUserAgent
'User-Agent': api.crunchyDefUserAgent
}
});
if(!indexReq.ok || ! indexReq.res){
@ -946,7 +946,7 @@ export default class Crunchy implements ServiceClass {
pad = pad || 0;
hideSeriesTitle = hideSeriesTitle !== undefined ? hideSeriesTitle : false;
// check token
if(!this.cmsToken.cms){
if(!this.cmsToken.cms_web){
console.error('Authentication required!');
return;
}
@ -960,7 +960,7 @@ export default class Crunchy implements ServiceClass {
};
// reqs
if(!hideSeriesTitle){
const seriesReq = await this.req.getData(`${api.cms}/series/${id}?force_locale=&preferred_audio_language=ja-JP&locale=${this.locale}`, AuthHeaders);
const seriesReq = await this.req.getData(`${api.content_cms}/series/${id}?force_locale=&preferred_audio_language=ja-JP&locale=${this.locale}`, AuthHeaders);
if(!seriesReq.ok || !seriesReq.res){
console.error('Series Request FAILED!');
return;
@ -969,7 +969,7 @@ export default class Crunchy implements ServiceClass {
await this.logObject(seriesData.data[0], pad, false);
}
// seasons list
const seriesSeasonListReq = await this.req.getData(`${api.cms}/series/${id}/seasons?force_locale=&preferred_audio_language=ja-JP&locale=${this.locale}`, AuthHeaders);
const seriesSeasonListReq = await this.req.getData(`${api.content_cms}/series/${id}/seasons?force_locale=&preferred_audio_language=ja-JP&locale=${this.locale}`, AuthHeaders);
if(!seriesSeasonListReq.ok || !seriesSeasonListReq.res){
console.error('Series Request FAILED!');
return;
@ -987,7 +987,7 @@ export default class Crunchy implements ServiceClass {
public async logMovieListingById(id: string, pad?: number){
pad = pad || 2;
if(!this.cmsToken.cms){
if(!this.cmsToken.cms_web){
console.error('Authentication required!');
return;
}
@ -1002,7 +1002,7 @@ export default class Crunchy implements ServiceClass {
};
//Movie Listing
const movieListingReq = await this.req.getData(`${api.cms}/movie_listings/${id}?force_locale=&preferred_audio_language=ja-JP&locale=${this.locale}`, AuthHeaders);
const movieListingReq = await this.req.getData(`${api.content_cms}/movie_listings/${id}?force_locale=&preferred_audio_language=ja-JP&locale=${this.locale}`, AuthHeaders);
if(!movieListingReq.ok || !movieListingReq.res){
console.error('Movie Listing Request FAILED!');
return;
@ -1017,7 +1017,7 @@ export default class Crunchy implements ServiceClass {
}
//Movies
const moviesListReq = await this.req.getData(`${api.cms}/movie_listings/${id}/movies?force_locale=&preferred_audio_language=ja-JP&locale=${this.locale}`, AuthHeaders);
const moviesListReq = await this.req.getData(`${api.content_cms}/movie_listings/${id}/movies?force_locale=&preferred_audio_language=ja-JP&locale=${this.locale}`, AuthHeaders);
if(!moviesListReq.ok || !moviesListReq.res){
console.error('Movies List Request FAILED!');
return;
@ -1073,14 +1073,14 @@ export default class Crunchy implements ServiceClass {
await this.logObject(i, 2);
}
// calculate pages
const itemPad = parseInt(new URL(newlyAddedResults.__href__, domain.api_beta).searchParams.get('start') as string);
const itemPad = parseInt(new URL(newlyAddedResults.__href__, domain.cr_www).searchParams.get('start') as string);
const pageCur = itemPad > 0 ? Math.ceil(itemPad/25) + 1 : 1;
const pageMax = Math.ceil(newlyAddedResults.total/25);
console.info(` Total results: ${newlyAddedResults.total} (Page: ${pageCur}/${pageMax})`);
}
public async getSeasonById(id: string, numbers: number, e: string|undefined, but: boolean, all: boolean) : Promise<ResponseBase<CrunchyEpMeta[]>> {
if(!this.cmsToken.cms){
if(!this.cmsToken.cms_web){
console.error('Authentication required!');
return { isOk: false, reason: new Error('Authentication required') };
}
@ -1095,7 +1095,7 @@ export default class Crunchy implements ServiceClass {
//get show info
const showInfoReq = await this.req.getData(`${api.cms}/seasons/${id}?force_locale=&preferred_audio_language=ja-JP&locale=${this.locale}`, AuthHeaders);
const showInfoReq = await this.req.getData(`${api.content_cms}/seasons/${id}?force_locale=&preferred_audio_language=ja-JP&locale=${this.locale}`, AuthHeaders);
if(!showInfoReq.ok || !showInfoReq.res){
console.error('Show Request FAILED!');
return { isOk: false, reason: new Error('Show request failed. No more information provided.') };
@ -1107,16 +1107,16 @@ export default class Crunchy implements ServiceClass {
//get episode info
const reqEpsListOpts = [
api.cms_bucket,
this.cmsToken.cms.bucket,
this.cmsToken.cms_web.bucket,
'/episodes?',
new URLSearchParams({
'force_locale': '',
'preferred_audio_language': 'ja-JP',
'locale': this.locale,
'season_id': id,
'Policy': this.cmsToken.cms.policy,
'Signature': this.cmsToken.cms.signature,
'Key-Pair-Id': this.cmsToken.cms.key_pair_id,
'Policy': this.cmsToken.cms_web.policy,
'Signature': this.cmsToken.cms_web.signature,
'Key-Pair-Id': this.cmsToken.cms_web.key_pair_id,
}),
].join('');
const reqEpsList = await this.req.getData(reqEpsListOpts, AuthHeaders);
@ -1255,7 +1255,7 @@ export default class Crunchy implements ServiceClass {
}
public async getObjectById(e?: string, earlyReturn?: boolean, external_id?: boolean): Promise<ObjectInfo|Partial<CrunchyEpMeta>[]|undefined> {
if(!this.cmsToken.cms){
if(!this.cmsToken.cms_web){
console.error('Authentication required!');
return [];
}
@ -1267,7 +1267,7 @@ export default class Crunchy implements ServiceClass {
for (const ob of epFilter.values) {
const extIdReqOpts = [
api.cms_bucket,
this.cmsToken.cms.bucket,
this.cmsToken.cms_web.bucket,
'/channels/crunchyroll/objects',
'?',
new URLSearchParams({
@ -1275,15 +1275,15 @@ export default class Crunchy implements ServiceClass {
'preferred_audio_language': 'ja-JP',
'locale': this.locale,
'external_id': ob,
'Policy': this.cmsToken.cms.policy,
'Signature': this.cmsToken.cms.signature,
'Key-Pair-Id': this.cmsToken.cms.key_pair_id,
'Policy': this.cmsToken.cms_web.policy,
'Signature': this.cmsToken.cms_web.signature,
'Key-Pair-Id': this.cmsToken.cms_web.key_pair_id,
}),
].join('');
const extIdReq = await this.req.getData(extIdReqOpts, {
headers: {
'User-Agent': api.defaultUserAgent
'User-Agent': api.crunchyDefUserAgent
}
});
if (!extIdReq.ok || !extIdReq.res) {
@ -1324,7 +1324,7 @@ export default class Crunchy implements ServiceClass {
let objectInfo: ObjectInfo = { total: 0, data: [], meta: {} };
const objectReqOpts = [
api.cms_bucket,
this.cmsToken.cms.bucket,
this.cmsToken.cms_web.bucket,
'/objects/',
doEpsFilter.values.join(','),
'?',
@ -1332,9 +1332,9 @@ export default class Crunchy implements ServiceClass {
'force_locale': '',
'preferred_audio_language': 'ja-JP',
'locale': this.locale,
'Policy': this.cmsToken.cms.policy,
'Signature': this.cmsToken.cms.signature,
'Key-Pair-Id': this.cmsToken.cms.key_pair_id,
'Policy': this.cmsToken.cms_web.policy,
'Signature': this.cmsToken.cms_web.signature,
'Key-Pair-Id': this.cmsToken.cms_web.key_pair_id,
}),
].join('');
const objectReq = await this.req.getData(objectReqOpts, AuthHeaders);
@ -1438,7 +1438,7 @@ export default class Crunchy implements ServiceClass {
fileName: string,
error: boolean
} | undefined> {
if(!this.cmsToken.cms){
if(!this.cmsToken.cms_web){
console.error('Authentication required!');
return;
}
@ -1588,7 +1588,7 @@ export default class Crunchy implements ServiceClass {
);
//We need the duration of the ep
let epDuration: number | undefined;
const epiMeta = await this.req.getData(`${api.cms}/objects/${currentMediaId}?force_locale=&preferred_audio_language=ja-JP&locale=${this.locale}`, AuthHeaders);
const epiMeta = await this.req.getData(`${api.content_cms}/objects/${currentMediaId}?force_locale=&preferred_audio_language=ja-JP&locale=${this.locale}`, AuthHeaders);
if(!epiMeta.ok || !epiMeta.res){
epDuration = 7200;
} else {
@ -1639,7 +1639,7 @@ export default class Crunchy implements ServiceClass {
if (options.tsd) {
console.warn('Total Session Death Active');
const activeStreamsReq = await this.req.getData(api.streaming, AuthHeaders);
const activeStreamsReq = await this.req.getData(api.streaming_sessions, AuthHeaders);
if (activeStreamsReq.ok && activeStreamsReq.res){
const data = await activeStreamsReq.res.json();
for (const s of data.items) {
@ -2890,7 +2890,7 @@ export default class Crunchy implements ServiceClass {
}
public async parseSeriesById(id: string) {
if(!this.cmsToken.cms){
if(!this.cmsToken.cms_web){
console.error('Authentication required!');
return;
}
@ -2904,7 +2904,7 @@ export default class Crunchy implements ServiceClass {
};
// seasons list
const seriesSeasonListReq = await this.req.getData(`${api.cms}/series/${id}/seasons?force_locale=&preferred_audio_language=ja-JP&locale=${this.locale}`, AuthHeaders);
const seriesSeasonListReq = await this.req.getData(`${api.content_cms}/series/${id}/seasons?force_locale=&preferred_audio_language=ja-JP&locale=${this.locale}`, AuthHeaders);
if(!seriesSeasonListReq.ok || !seriesSeasonListReq.res){
console.error('Series Request FAILED!');
return;
@ -2919,7 +2919,7 @@ export default class Crunchy implements ServiceClass {
}
public async getSeasonDataById(item: SeriesSearchItem, log = false){
if(!this.cmsToken.cms){
if(!this.cmsToken.cms_web){
console.error('Authentication required!');
return;
}
@ -2933,7 +2933,7 @@ export default class Crunchy implements ServiceClass {
};
//get show info
const showInfoReq = await this.req.getData(`${api.cms}/seasons/${item.id}?force_locale=&preferred_audio_language=ja-JP&locale=${this.locale}`, AuthHeaders);
const showInfoReq = await this.req.getData(`${api.content_cms}/seasons/${item.id}?force_locale=&preferred_audio_language=ja-JP&locale=${this.locale}`, AuthHeaders);
if(!showInfoReq.ok || !showInfoReq.res){
console.error('Show Request FAILED!');
return;
@ -2950,16 +2950,16 @@ export default class Crunchy implements ServiceClass {
const reqEpsListOpts = [
api.cms_bucket,
this.cmsToken.cms.bucket,
this.cmsToken.cms_web.bucket,
'/episodes?',
new URLSearchParams({
'force_locale': '',
'preferred_audio_language': 'ja-JP',
'locale': this.locale,
'season_id': id,
'Policy': this.cmsToken.cms.policy,
'Signature': this.cmsToken.cms.signature,
'Key-Pair-Id': this.cmsToken.cms.key_pair_id,
'Policy': this.cmsToken.cms_web.policy,
'Signature': this.cmsToken.cms_web.signature,
'Key-Pair-Id': this.cmsToken.cms_web.key_pair_id,
}),
].join('');
const reqEpsList = await this.req.getData(reqEpsListOpts, AuthHeaders);

View file

@ -1,119 +1,99 @@
// api domains
const domain = {
www: 'https://www.crunchyroll.com',
api: 'https://api.crunchyroll.com',
api_beta: 'https://beta-api.crunchyroll.com',
hd_www: 'https://www.hidive.com',
hd_api: 'https://api.hidive.com',
hd_new: 'https://dce-frontoffice.imggaming.com'
cr_www: 'https://www.crunchyroll.com',
cr_api: 'https://api.crunchyroll.com',
hd_www: 'https://www.hidive.com',
hd_api: 'https://api.hidive.com',
hd_new: 'https://dce-frontoffice.imggaming.com'
};
export type APIType = {
bundlejs: string,
newani: string,
search1: string,
search2: string,
rss_cid: string,
rss_gid: string
media_page: string
series_page: string
auth: string
// mobile api
search3: string
session: string
collections: string
// beta api
defaultUserAgent: string,
profile: string
cmsToken: string
browse_all_series: string,
search: string
cms: string
cms_bucket: string
browse: string
drm: string;
drm_widevine: string;
drm_playready: string;
streaming: string;
/**
* Header
*/
crunchyDefHeader: Record<string, string>,
crunchyAuthHeader: Record<string, string>,
hd_apikey: string,
hd_devName: string,
hd_appId: string,
hd_clientWeb: string,
hd_clientExo: string,
hd_api: string,
hd_new_api: string,
hd_new_apiKey: string,
hd_new_version: string,
}
// Crunchyroll Vilos bundle.js
bundlejs: string;
// Crunchyroll API
auth: string;
profile: string;
search: string;
content_cms: string;
browse: string;
browse_all_series: string;
streaming_sessions: string;
drm_widevine: string;
drm_playready: string;
// Crunchyroll Bucket
cms_bucket: string;
cms_auth: string;
// Crunchyroll Headers
crunchyDefUserAgent: string;
crunchyDefHeader: Record<string, string>;
crunchyAuthHeader: Record<string, string>;
// Hidive
hd_apikey: string;
hd_devName: string;
hd_appId: string;
hd_clientWeb: string;
hd_clientExo: string;
hd_api: string;
hd_new_api: string;
hd_new_apiKey: string;
hd_new_version: string;
};
// api urls
const api: APIType = {
// web
bundlejs: 'https://static.crunchyroll.com/vilos-v2/web/vilos/js/bundle.js',
newani: `${domain.www}/rss/anime`,
search1: `${domain.www}/ajax/?req=RpcApiSearch_GetSearchCandidates`,
search2: `${domain.www}/search_page`,
rss_cid: `${domain.www}/syndication/feed?type=episodes&id=`, // &lang=enUS
rss_gid: `${domain.www}/syndication/feed?type=episodes&group_id=`, // &lang=enUS
media_page: `${domain.www}/media-`,
series_page: `${domain.www}/series-`,
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`,
defaultUserAgent: 'Crunchyroll/4.83.0 (bundle_identifier:com.crunchyroll.iphone; build_number:4254815.324030705) iOS/19.0.0 Gravity/4.83.0',
profile: `${domain.www}/accounts/v1/me/profile`,
cmsToken: `${domain.www}/index/v2`,
search: `${domain.www}/content/v2/discover/search`,
cms: `${domain.www}/content/v2/cms`,
cms_bucket: `${domain.api_beta}/cms/v2`,
browse: `${domain.www}/content/v1/browse`,
browse_all_series: `${domain.www}/content/v2/discover/browse`,
// 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`,
// playready endpoint currently broken
drm_playready: `${domain.www}/license/v1/license/playReady`,
// endpoint to get active streaming sessions
streaming: `${domain.www}/playback/v1/sessions/streaming`,
crunchyDefHeader: {},
crunchyAuthHeader: {},
//hidive API
hd_apikey: '508efd7b42d546e19cc24f4d0b414e57e351ca73',
hd_devName: 'Android',
hd_appId: '24i-Android',
hd_clientWeb: 'okhttp/3.4.1',
hd_clientExo: 'smartexoplayer/1.6.0.R (Linux;Android 6.0) ExoPlayerLib/2.6.0',
hd_api: `${domain.hd_api}/api/v1`,
//Hidive New API
hd_new_api: `${domain.hd_new}/api`,
hd_new_apiKey: '857a1e5d-e35e-4fdf-805b-a87b6f8364bf',
hd_new_version: '6.0.1.bbf09a2'
//
//
// Crunchyroll
// Vilos bundle.js (where we can extract the basic token thats needed for the initial auth)
bundlejs: 'https://static.crunchyroll.com/vilos-v2/web/vilos/js/bundle.js',
//
// Crunchyroll API
auth: `${domain.cr_www}/auth/v1/token`,
profile: `${domain.cr_www}/accounts/v1/me/profile`,
search: `${domain.cr_www}/content/v2/discover/search`,
content_cms: `${domain.cr_www}/content/v2/cms`,
browse: `${domain.cr_www}/content/v1/browse`,
browse_all_series: `${domain.cr_www}/content/v2/discover/browse`,
streaming_sessions: `${domain.cr_www}/playback/v1/sessions/streaming`,
drm_widevine: `${domain.cr_www}/license/v1/license/widevine`,
drm_playready: `${domain.cr_www}/license/v1/license/playReady`,
//
// Crunchyroll Bucket
cms_bucket: `${domain.cr_www}/cms/v2`,
cms_auth: `${domain.cr_www}/index/v2`,
//
// Crunchyroll Headers
crunchyDefUserAgent: 'Crunchyroll/4.83.0 (bundle_identifier:com.crunchyroll.iphone; build_number:4254815.324030705) iOS/19.0.0 Gravity/4.83.0',
crunchyDefHeader: {},
crunchyAuthHeader: {},
//
//
// Hidive
// Hidive API
hd_apikey: '508efd7b42d546e19cc24f4d0b414e57e351ca73',
hd_devName: 'Android',
hd_appId: '24i-Android',
hd_clientWeb: 'okhttp/3.4.1',
hd_clientExo: 'smartexoplayer/1.6.0.R (Linux;Android 6.0) ExoPlayerLib/2.6.0',
hd_api: `${domain.hd_api}/api/v1`,
// Hidive New API
hd_new_api: `${domain.hd_new}/api`,
hd_new_apiKey: '857a1e5d-e35e-4fdf-805b-a87b6f8364bf',
hd_new_version: '6.0.1.bbf09a2'
};
api.crunchyDefHeader = {
'User-Agent': api.defaultUserAgent,
'Accept': '*/*',
'Accept-Encoding': 'gzip;q=1.0, compress;q=0.5',
'Accept-Language': 'de-IT;q=1.0, it-IT;q=0.9, en-GB;q=0.8',
'Connection': 'keep-alive',
'Host': 'www.crunchyroll.com'
'User-Agent': api.crunchyDefUserAgent,
Accept: '*/*',
'Accept-Encoding': 'gzip;q=1.0, compress;q=0.5',
'Accept-Language': 'de-IT;q=1.0, it-IT;q=0.9, en-GB;q=0.8',
Connection: 'keep-alive',
Host: 'www.crunchyroll.com'
};
// set header
api.crunchyAuthHeader = {
'Content-Type': 'application/x-www-form-urlencoded; charset=utf-8',
'Content-Type': 'application/x-www-form-urlencoded; charset=utf-8',
...api.crunchyDefHeader
};
export {
domain, api
};
export { domain, api };