[HD] Initial support for new Hidive API
The new API can be accessed with `--hdapi new`
This commit is contained in:
parent
5e95f600d9
commit
1c39e349ad
17 changed files with 1315 additions and 135 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -24,6 +24,7 @@ cr_token.yml
|
|||
hd_profile.yml
|
||||
hd_sess.yml
|
||||
hd_token.yml
|
||||
hd_new_token.yml
|
||||
archive.json
|
||||
guistate.json
|
||||
fonts
|
||||
|
|
|
|||
43
@types/newHidiveEpisode.d.ts
vendored
Normal file
43
@types/newHidiveEpisode.d.ts
vendored
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
export interface NewHidiveEpisode {
|
||||
description: string;
|
||||
duration: number;
|
||||
title: string;
|
||||
categories: string[];
|
||||
contentDownload: ContentDownload;
|
||||
favourite: boolean;
|
||||
subEvents: any[];
|
||||
thumbnailUrl: string;
|
||||
longDescription: string;
|
||||
posterUrl: string;
|
||||
offlinePlaybackLanguages: string[];
|
||||
externalAssetId: string;
|
||||
maxHeight: number;
|
||||
rating: Rating;
|
||||
episodeInformation: EpisodeInformation;
|
||||
id: number;
|
||||
accessLevel: string;
|
||||
playerUrlCallback: string;
|
||||
thumbnailsPreview: string;
|
||||
displayableTags: any[];
|
||||
plugins: any[];
|
||||
watchStatus: string;
|
||||
computedReleases: any[];
|
||||
licences: any[];
|
||||
type: string;
|
||||
}
|
||||
|
||||
export interface ContentDownload {
|
||||
permission: string;
|
||||
period: string;
|
||||
}
|
||||
|
||||
export interface EpisodeInformation {
|
||||
seasonNumber: number;
|
||||
episodeNumber: number;
|
||||
season: number;
|
||||
}
|
||||
|
||||
export interface Rating {
|
||||
rating: string;
|
||||
descriptors: any[];
|
||||
}
|
||||
33
@types/newHidivePlayback.d.ts
vendored
Normal file
33
@types/newHidivePlayback.d.ts
vendored
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
export interface NewHidivePlayback {
|
||||
watermark: null;
|
||||
skipMarkers: any[];
|
||||
annotations: null;
|
||||
dash: Format[];
|
||||
hls: Format[];
|
||||
}
|
||||
|
||||
export interface Format {
|
||||
subtitles: Subtitle[];
|
||||
url: string;
|
||||
drm: DRM;
|
||||
}
|
||||
|
||||
export interface DRM {
|
||||
encryptionMode: string;
|
||||
containerType: string;
|
||||
jwtToken: string;
|
||||
url: string;
|
||||
keySystems: string[];
|
||||
}
|
||||
|
||||
export interface Subtitle {
|
||||
format: Formats;
|
||||
language: string;
|
||||
url: string;
|
||||
}
|
||||
|
||||
export enum Formats {
|
||||
Scc = 'scc',
|
||||
Srt = 'srt',
|
||||
Vtt = 'vtt',
|
||||
}
|
||||
91
@types/newHidiveSearch.d.ts
vendored
Normal file
91
@types/newHidiveSearch.d.ts
vendored
Normal file
|
|
@ -0,0 +1,91 @@
|
|||
export interface NewHidiveSearch {
|
||||
results: Result[];
|
||||
}
|
||||
|
||||
export interface Result {
|
||||
hits: Hit[];
|
||||
nbHits: number;
|
||||
page: number;
|
||||
nbPages: number;
|
||||
hitsPerPage: number;
|
||||
exhaustiveNbHits: boolean;
|
||||
exhaustiveTypo: boolean;
|
||||
exhaustive: Exhaustive;
|
||||
query: string;
|
||||
params: string;
|
||||
index: string;
|
||||
renderingContent: RenderingContent;
|
||||
processingTimeMS: number;
|
||||
processingTimingsMS: ProcessingTimingsMS;
|
||||
serverTimeMS: number;
|
||||
}
|
||||
|
||||
export interface Exhaustive {
|
||||
nbHits: boolean;
|
||||
typo: boolean;
|
||||
}
|
||||
|
||||
export interface Hit {
|
||||
type: string;
|
||||
weight: number;
|
||||
id: number;
|
||||
name: string;
|
||||
description: string;
|
||||
meta: RenderingContent;
|
||||
coverUrl: string;
|
||||
smallCoverUrl: string;
|
||||
seasonsCount: number;
|
||||
tags: string[];
|
||||
localisations: HitLocalisations;
|
||||
ratings: Ratings;
|
||||
objectID: string;
|
||||
_highlightResult: HighlightResult;
|
||||
}
|
||||
|
||||
export interface HighlightResult {
|
||||
name: Description;
|
||||
description: Description;
|
||||
tags: Description[];
|
||||
localisations: HighlightResultLocalisations;
|
||||
}
|
||||
|
||||
export interface Description {
|
||||
value: string;
|
||||
matchLevel: string;
|
||||
matchedWords: string[];
|
||||
fullyHighlighted?: boolean;
|
||||
}
|
||||
|
||||
export interface HighlightResultLocalisations {
|
||||
en_US: PurpleEnUS;
|
||||
}
|
||||
|
||||
export interface PurpleEnUS {
|
||||
title: Description;
|
||||
description: Description;
|
||||
}
|
||||
|
||||
export interface HitLocalisations {
|
||||
[language: string]: HitLocalization;
|
||||
}
|
||||
|
||||
export interface HitLocalization {
|
||||
title: string;
|
||||
description: string;
|
||||
}
|
||||
|
||||
export interface RenderingContent {
|
||||
}
|
||||
|
||||
export interface Ratings {
|
||||
US: string[];
|
||||
}
|
||||
|
||||
export interface ProcessingTimingsMS {
|
||||
_request: Request;
|
||||
}
|
||||
|
||||
export interface Request {
|
||||
queue: number;
|
||||
roundTrip: number;
|
||||
}
|
||||
85
@types/newHidiveSeason.d.ts
vendored
Normal file
85
@types/newHidiveSeason.d.ts
vendored
Normal file
|
|
@ -0,0 +1,85 @@
|
|||
export interface NewHidiveSeason {
|
||||
title: string;
|
||||
description: string;
|
||||
longDescription: string;
|
||||
smallCoverUrl: string;
|
||||
coverUrl: string;
|
||||
titleUrl: string;
|
||||
posterUrl: string;
|
||||
seasonNumber: number;
|
||||
episodeCount: number;
|
||||
displayableTags: any[];
|
||||
rating: Rating;
|
||||
contentRating: Rating;
|
||||
id: number;
|
||||
series: Series;
|
||||
episodes: Episode[];
|
||||
paging: Paging;
|
||||
licences: any[];
|
||||
}
|
||||
|
||||
export interface Rating {
|
||||
rating: string;
|
||||
descriptors: any[];
|
||||
}
|
||||
|
||||
export interface Episode {
|
||||
accessLevel: string;
|
||||
availablePurchases: any[];
|
||||
licenceIds: any[];
|
||||
type: string;
|
||||
id: number;
|
||||
title: string;
|
||||
description: string;
|
||||
thumbnailUrl: string;
|
||||
posterUrl: string;
|
||||
duration: number;
|
||||
favourite: boolean;
|
||||
contentDownload: ContentDownload;
|
||||
offlinePlaybackLanguages: string[];
|
||||
externalAssetId: string;
|
||||
subEvents: any[];
|
||||
maxHeight: number;
|
||||
thumbnailsPreview: string;
|
||||
longDescription: string;
|
||||
episodeInformation: EpisodeInformation;
|
||||
categories: string[];
|
||||
displayableTags: any[];
|
||||
watchStatus: string;
|
||||
computedReleases: any[];
|
||||
}
|
||||
|
||||
export interface ContentDownload {
|
||||
permission: string;
|
||||
}
|
||||
|
||||
export interface EpisodeInformation {
|
||||
seasonNumber: number;
|
||||
episodeNumber: number;
|
||||
season: number;
|
||||
}
|
||||
|
||||
export interface Paging {
|
||||
moreDataAvailable: boolean;
|
||||
lastSeen: number;
|
||||
}
|
||||
|
||||
export interface Series {
|
||||
seriesId: number;
|
||||
title: string;
|
||||
description: string;
|
||||
longDescription: string;
|
||||
displayableTags: any[];
|
||||
rating: Rating;
|
||||
contentRating: Rating;
|
||||
}
|
||||
|
||||
export interface NewHidiveEpisodeExtra extends Episode {
|
||||
titleId: number;
|
||||
nameLong: string;
|
||||
seasonTitle: string;
|
||||
seriesTitle: string;
|
||||
seriesId?: number;
|
||||
isSelected: boolean;
|
||||
jwtToken?: string;
|
||||
}
|
||||
35
@types/newHidiveSeries.d.ts
vendored
Normal file
35
@types/newHidiveSeries.d.ts
vendored
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
export interface NewHidiveSeries {
|
||||
id: number;
|
||||
title: string;
|
||||
description: string;
|
||||
longDescription: string;
|
||||
smallCoverUrl: string;
|
||||
coverUrl: string;
|
||||
titleUrl: string;
|
||||
posterUrl: string;
|
||||
seasons: Season[];
|
||||
rating: Rating;
|
||||
contentRating: Rating;
|
||||
displayableTags: any[];
|
||||
paging: Paging;
|
||||
}
|
||||
|
||||
export interface Rating {
|
||||
rating: string;
|
||||
descriptors: any[];
|
||||
}
|
||||
|
||||
export interface Paging {
|
||||
moreDataAvailable: boolean;
|
||||
lastSeen: number;
|
||||
}
|
||||
|
||||
export interface Season {
|
||||
title: string;
|
||||
description: string;
|
||||
longDescription: string;
|
||||
seasonNumber: number;
|
||||
episodeCount: number;
|
||||
displayableTags: any[];
|
||||
id: number;
|
||||
}
|
||||
|
|
@ -23,7 +23,7 @@ const ServiceProvider: FCWithChildren = ({ children }) => {
|
|||
<Box sx={{ display: 'flex', gap: 2, justifyContent: 'center' }}>
|
||||
<Button size='large' variant="contained" onClick={() => setService('funi')} startIcon={<Avatar src={'https://static.funimation.com/static/img/favicon.ico'} />}>Funimation</Button>
|
||||
<Button size='large' variant="contained" onClick={() => setService('crunchy')} startIcon={<Avatar src={'https://static.crunchyroll.com/cxweb/assets/img/favicons/favicon-32x32.png'} />}>Crunchyroll</Button>
|
||||
<Button size='large' variant="contained" onClick={() => setService('hidive')} startIcon={<Avatar src={'https://www.hidive.com/favicon.ico'} />}>Hidive</Button>
|
||||
<Button size='large' variant="contained" onClick={() => setService('hidive')} startIcon={<Avatar src={'https://static.diceplatform.com/prod/original/dce.hidive/settings/HIDIVE_AppLogo_1024x1024.0G0vK.jpg'} />}>Hidive</Button>
|
||||
</Box>
|
||||
</Box>
|
||||
: <serviceContext.Provider value={service}>
|
||||
|
|
|
|||
|
|
@ -26,7 +26,13 @@ class HidiveHandler extends Base implements MessageHandler {
|
|||
return { isOk: true, value: undefined };
|
||||
}
|
||||
|
||||
public async getAPIVersion() {
|
||||
const _default = yargs.appArgv(this.hidive.cfg.cli, true);
|
||||
this.hidive.api = _default.hdapi;
|
||||
}
|
||||
|
||||
public async search(data: SearchData): Promise<SearchResponse> {
|
||||
await this.getAPIVersion();
|
||||
console.debug(`Got search options: ${JSON.stringify(data)}`);
|
||||
const hidiveSearch = await this.hidive.doSearch(data);
|
||||
if (!hidiveSearch.isOk) {
|
||||
|
|
@ -42,7 +48,7 @@ class HidiveHandler extends Base implements MessageHandler {
|
|||
public async availableDubCodes(): Promise<string[]> {
|
||||
const dubLanguageCodesArray: string[] = [];
|
||||
for(const language of languages){
|
||||
if (language.hd_locale)
|
||||
if (language.new_hd_locale)
|
||||
dubLanguageCodesArray.push(language.code);
|
||||
}
|
||||
return [...new Set(dubLanguageCodesArray)];
|
||||
|
|
@ -51,7 +57,7 @@ class HidiveHandler extends Base implements MessageHandler {
|
|||
public async availableSubCodes(): Promise<string[]> {
|
||||
const subLanguageCodesArray: string[] = [];
|
||||
for(const language of languages){
|
||||
if (language.hd_locale)
|
||||
if (language.new_hd_locale)
|
||||
subLanguageCodesArray.push(language.locale);
|
||||
}
|
||||
return ['all', 'none', ...new Set(subLanguageCodesArray)];
|
||||
|
|
@ -62,63 +68,120 @@ class HidiveHandler extends Base implements MessageHandler {
|
|||
if (isNaN(parse) || parse <= 0)
|
||||
return false;
|
||||
console.debug(`Got resolve options: ${JSON.stringify(data)}`);
|
||||
const res = await this.hidive.getShow(parseInt(data.id), data.e, data.but, data.all);
|
||||
if (!res.isOk || !res.value)
|
||||
return res.isOk;
|
||||
this.addToQueue(res.value.map(item => {
|
||||
return {
|
||||
...data,
|
||||
ids: [item.Id],
|
||||
title: item.Name,
|
||||
parent: {
|
||||
title: item.seriesTitle,
|
||||
season: parseFloat(item.SeasonNumberValue+'')+''
|
||||
},
|
||||
image: item.ScreenShotSmallUrl,
|
||||
e: parseFloat(item.EpisodeNumberValue+'')+'',
|
||||
episode: parseFloat(item.EpisodeNumberValue+'')+'',
|
||||
};
|
||||
}));
|
||||
return true;
|
||||
await this.getAPIVersion();
|
||||
if (this.hidive.api == 'old') {
|
||||
const res = await this.hidive.getShow(parseInt(data.id), data.e, data.but, data.all);
|
||||
if (!res.isOk || !res.value)
|
||||
return res.isOk;
|
||||
this.addToQueue(res.value.map(item => {
|
||||
return {
|
||||
...data,
|
||||
ids: [item.Id],
|
||||
title: item.Name,
|
||||
parent: {
|
||||
title: item.seriesTitle,
|
||||
season: parseFloat(item.SeasonNumberValue+'')+''
|
||||
},
|
||||
image: item.ScreenShotSmallUrl,
|
||||
e: parseFloat(item.EpisodeNumberValue+'')+'',
|
||||
episode: parseFloat(item.EpisodeNumberValue+'')+'',
|
||||
};
|
||||
}));
|
||||
return true;
|
||||
} else {
|
||||
const res = await this.hidive.selectSeries(parseInt(data.id), data.e, data.but, data.all);
|
||||
if (!res.isOk || !res.value)
|
||||
return res.isOk;
|
||||
this.addToQueue(res.value.map(item => {
|
||||
return {
|
||||
...data,
|
||||
ids: [item.id],
|
||||
title: item.title,
|
||||
parent: {
|
||||
title: item.seriesTitle,
|
||||
season: item.episodeInformation.seasonNumber+''
|
||||
},
|
||||
image: item.thumbnailUrl,
|
||||
e: item.episodeInformation.episodeNumber+'',
|
||||
episode: item.episodeInformation.episodeNumber+'',
|
||||
};
|
||||
}));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public async listEpisodes(id: string): Promise<EpisodeListResponse> {
|
||||
const parse = parseInt(id);
|
||||
if (isNaN(parse) || parse <= 0)
|
||||
return { isOk: false, reason: new Error('The ID is invalid') };
|
||||
const request = await this.hidive.listShow(parse);
|
||||
if (!request.isOk || !request.value)
|
||||
return {isOk: false, reason: new Error('Unknown upstream error, check for additional logs')};
|
||||
|
||||
return { isOk: true, value: request.value.Episodes.map(function(item) {
|
||||
const language = item.Summary.match(/^Audio: (.*)/m);
|
||||
language?.shift();
|
||||
const description = item.Summary.split('\r\n');
|
||||
return {
|
||||
e: parseFloat(item.EpisodeNumberValue+'')+'',
|
||||
lang: language ? language[0].split(', ') : [],
|
||||
name: item.Name,
|
||||
season: parseFloat(item.SeasonNumberValue+'')+'',
|
||||
seasonTitle: request.value.Name,
|
||||
episode: parseFloat(item.EpisodeNumberValue+'')+'',
|
||||
id: item.Id+'',
|
||||
img: item.ScreenShotSmallUrl,
|
||||
description: description ? description[0] : '',
|
||||
time: ''
|
||||
};
|
||||
})};
|
||||
await this.getAPIVersion();
|
||||
if (this.hidive.api == 'old') {
|
||||
const request = await this.hidive.listShow(parse);
|
||||
if (!request.isOk || !request.value)
|
||||
return {isOk: false, reason: new Error('Unknown upstream error, check for additional logs')};
|
||||
|
||||
return { isOk: true, value: request.value.Episodes.map(function(item) {
|
||||
const language = item.Summary.match(/^Audio: (.*)/m);
|
||||
language?.shift();
|
||||
const description = item.Summary.split('\r\n');
|
||||
return {
|
||||
e: parseFloat(item.EpisodeNumberValue+'')+'',
|
||||
lang: language ? language[0].split(', ') : [],
|
||||
name: item.Name,
|
||||
season: parseFloat(item.SeasonNumberValue+'')+'',
|
||||
seasonTitle: request.value.Name,
|
||||
episode: parseFloat(item.EpisodeNumberValue+'')+'',
|
||||
id: item.Id+'',
|
||||
img: item.ScreenShotSmallUrl,
|
||||
description: description ? description[0] : '',
|
||||
time: ''
|
||||
};
|
||||
})};
|
||||
} else {
|
||||
const request = await this.hidive.listSeries(parse);
|
||||
if (!request.isOk || !request.value)
|
||||
return {isOk: false, reason: new Error('Unknown upstream error, check for additional logs')};
|
||||
|
||||
return { isOk: true, value: request.value.map(function(item) {
|
||||
const description = item.description.split('\r\n');
|
||||
return {
|
||||
e: item.episodeInformation.episodeNumber+'',
|
||||
lang: [],
|
||||
name: item.title,
|
||||
season: item.episodeInformation.seasonNumber+'',
|
||||
seasonTitle: request.series.seasons[item.episodeInformation.seasonNumber-1].title,
|
||||
episode: item.episodeInformation.episodeNumber+'',
|
||||
id: item.id+'',
|
||||
img: item.thumbnailUrl,
|
||||
description: description ? description[0] : '',
|
||||
time: ''
|
||||
};
|
||||
})};
|
||||
}
|
||||
}
|
||||
|
||||
public async downloadItem(data: DownloadData) {
|
||||
this.setDownloading(true);
|
||||
console.debug(`Got download options: ${JSON.stringify(data)}`);
|
||||
const _default = yargs.appArgv(this.hidive.cfg.cli, true);
|
||||
const res = await this.hidive.getShow(parseInt(data.id), data.e, false, false);
|
||||
if (!res.isOk || !res.showData)
|
||||
return this.alertError(new Error('Download failed upstream, check for additional logs'));
|
||||
this.hidive.api = _default.hdapi;
|
||||
if (this.hidive.api == 'old') {
|
||||
const res = await this.hidive.getShow(parseInt(data.id), data.e, false, false);
|
||||
if (!res.isOk || !res.showData)
|
||||
return this.alertError(new Error('Download failed upstream, check for additional logs'));
|
||||
|
||||
for (const ep of res.value) {
|
||||
await this.hidive.getEpisode(ep, {..._default, callbackMaker: this.makeProgressHandler.bind(this), dubLang: data.dubLang, dlsubs: data.dlsubs, fileName: data.fileName, q: data.q, force: 'y', noaudio: data.noaudio, novids: data.novids });
|
||||
for (const ep of res.value) {
|
||||
await this.hidive.getEpisode(ep, {..._default, callbackMaker: this.makeProgressHandler.bind(this), dubLang: data.dubLang, dlsubs: data.dlsubs, fileName: data.fileName, q: data.q, force: 'y', noaudio: data.noaudio, novids: data.novids });
|
||||
}
|
||||
} else {
|
||||
const res = await this.hidive.selectSeries(parseInt(data.id), data.e, false, false);
|
||||
if (!res.isOk || !res.showData)
|
||||
return this.alertError(new Error('Download failed upstream, check for additional logs'));
|
||||
|
||||
for (const ep of res.value) {
|
||||
await this.hidive.downloadEpisode(ep, {..._default, callbackMaker: this.makeProgressHandler.bind(this), dubLang: data.dubLang, dlsubs: data.dlsubs, fileName: data.fileName, q: data.q, force: 'y', noaudio: data.noaudio, novids: data.novids });
|
||||
}
|
||||
}
|
||||
this.sendMessage({ name: 'finish', data: undefined });
|
||||
this.setDownloading(false);
|
||||
|
|
|
|||
|
|
@ -7,7 +7,8 @@ const domain = {
|
|||
www_beta: 'https://beta.crunchyroll.com',
|
||||
api_beta: 'https://beta-api.crunchyroll.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'
|
||||
};
|
||||
|
||||
export type APIType = {
|
||||
|
|
@ -41,6 +42,9 @@ export type APIType = {
|
|||
hd_clientWeb: string,
|
||||
hd_clientExo: string,
|
||||
hd_api: string,
|
||||
hd_new_api: string,
|
||||
hd_new_apiKey: string,
|
||||
hd_new_version: string,
|
||||
}
|
||||
|
||||
// api urls
|
||||
|
|
@ -77,6 +81,10 @@ const api: APIType = {
|
|||
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'
|
||||
};
|
||||
|
||||
// set header
|
||||
|
|
|
|||
|
|
@ -66,6 +66,7 @@ let argvC: {
|
|||
dlVideoOnce: boolean;
|
||||
chapters: boolean;
|
||||
crapi: 'android' | 'web';
|
||||
hdapi: 'old' | 'new';
|
||||
removeBumpers: boolean;
|
||||
originalFontSize: boolean;
|
||||
keepAllVideos: boolean;
|
||||
|
|
|
|||
|
|
@ -230,6 +230,20 @@ const args: TAppArg<boolean|number|string|unknown[]>[] = [
|
|||
default: 'android'
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'hdapi',
|
||||
describe: 'Selects the API type for Hidive',
|
||||
type: 'string',
|
||||
group: 'dl',
|
||||
service: ['hidive'],
|
||||
docDescribe: 'If set to Old, it has lower quality, but Non-DRM streams, but some people can\'t use it,'
|
||||
+ '\nIf set to New, it has a higher quality stream, but everything is DRM.',
|
||||
usage: '',
|
||||
choices: ['old', 'new'],
|
||||
default: {
|
||||
default: 'old'
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'removeBumpers',
|
||||
describe: 'Remove bumpers from final video',
|
||||
|
|
|
|||
|
|
@ -26,7 +26,8 @@ const stateFile = path.join(workingDir, 'config', 'guistate');
|
|||
const tokenFile = {
|
||||
funi: path.join(workingDir, 'config', 'funi_token'),
|
||||
cr: path.join(workingDir, 'config', 'cr_token'),
|
||||
hd: path.join(workingDir, 'config', 'hd_token')
|
||||
hd: path.join(workingDir, 'config', 'hd_token'),
|
||||
hdNew: path.join(workingDir, 'config', 'hd_new_token')
|
||||
};
|
||||
|
||||
export const ensureConfig = () => {
|
||||
|
|
@ -242,7 +243,7 @@ const saveHDSession = (data: Record<string, unknown>) => {
|
|||
|
||||
|
||||
const loadHDToken = () => {
|
||||
let token = loadYamlCfgFile(tokenFile.cr, true);
|
||||
let token = loadYamlCfgFile(tokenFile.hd, true);
|
||||
if(typeof token !== 'object' || token === null || Array.isArray(token)){
|
||||
token = {};
|
||||
}
|
||||
|
|
@ -292,6 +293,25 @@ const loadHDProfile = () => {
|
|||
return profile;
|
||||
};
|
||||
|
||||
const loadNewHDToken = () => {
|
||||
let token = loadYamlCfgFile(tokenFile.hdNew, true);
|
||||
if(typeof token !== 'object' || token === null || Array.isArray(token)){
|
||||
token = {};
|
||||
}
|
||||
return token;
|
||||
};
|
||||
|
||||
const saveNewHDToken = (data: Record<string, unknown>) => {
|
||||
const cfgFolder = path.dirname(tokenFile.hdNew);
|
||||
try{
|
||||
fs.ensureDirSync(cfgFolder);
|
||||
fs.writeFileSync(`${tokenFile.hdNew}.yml`, yaml.stringify(data));
|
||||
}
|
||||
catch(e){
|
||||
console.error('Can\'t save token file to disk!');
|
||||
}
|
||||
};
|
||||
|
||||
const loadFuniToken = () => {
|
||||
const loadedToken = loadYamlCfgFile<{
|
||||
token?: string
|
||||
|
|
@ -363,6 +383,8 @@ export {
|
|||
loadHDSession,
|
||||
saveHDToken,
|
||||
loadHDToken,
|
||||
saveNewHDToken,
|
||||
loadNewHDToken,
|
||||
saveHDProfile,
|
||||
loadHDProfile,
|
||||
getState,
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
export type LanguageItem = {
|
||||
cr_locale?: string,
|
||||
hd_locale?: string,
|
||||
new_hd_locale?: string,
|
||||
locale: string,
|
||||
code: string,
|
||||
name: string,
|
||||
|
|
@ -13,12 +14,12 @@ export type LanguageItem = {
|
|||
}
|
||||
|
||||
const languages: LanguageItem[] = [
|
||||
{ cr_locale: 'en-US', hd_locale: 'English', funi_locale: 'enUS', locale: 'en', code: 'eng', name: 'English' },
|
||||
{ cr_locale: 'en-US', new_hd_locale: 'en-US', hd_locale: 'English', funi_locale: 'enUS', locale: 'en', code: 'eng', name: 'English' },
|
||||
{ cr_locale: 'en-IN', locale: 'en-IN', code: 'eng', name: 'English (India)', },
|
||||
{ cr_locale: 'es-LA', hd_locale: 'Spanish LatAm', funi_name: 'Spanish (LAS)', funi_name_lagacy: 'Spanish (Latin Am)', funi_locale: 'esLA', locale: 'es-419', code: 'spa', name: 'Spanish', language: 'Latin American Spanish' },
|
||||
{ cr_locale: 'es-LA', new_hd_locale: 'es-MX', hd_locale: 'Spanish LatAm', funi_name: 'Spanish (LAS)', funi_name_lagacy: 'Spanish (Latin Am)', funi_locale: 'esLA', locale: 'es-419', code: 'spa', name: 'Spanish', language: 'Latin American Spanish' },
|
||||
{ cr_locale: 'es-419',hd_locale: 'Spanish', locale: 'es-419', code: 'spa-419', name: 'Spanish', language: 'Latin American Spanish' },
|
||||
{ cr_locale: 'es-ES', hd_locale: 'Spanish Europe', locale: 'es-ES', code: 'spa-ES', name: 'Castilian', language: 'European Spanish' },
|
||||
{ cr_locale: 'pt-BR', hd_locale: 'Portuguese', funi_name: 'Portuguese (Brazil)', funi_locale: 'ptBR', locale: 'pt-BR', code: 'por', name: 'Portuguese', language: 'Brazilian Portuguese' },
|
||||
{ cr_locale: 'pt-BR', new_hd_locale: 'pt-BR', hd_locale: 'Portuguese', funi_name: 'Portuguese (Brazil)', funi_locale: 'ptBR', locale: 'pt-BR', code: 'por', name: 'Portuguese', language: 'Brazilian Portuguese' },
|
||||
{ cr_locale: 'pt-PT', locale: 'pt-PT', code: 'por', name: 'Portuguese (Portugal)', language: 'Portugues (Portugal)' },
|
||||
{ cr_locale: 'fr-FR', hd_locale: 'French', locale: 'fr', code: 'fra', name: 'French' },
|
||||
{ cr_locale: 'de-DE', hd_locale: 'German', locale: 'de', code: 'deu', name: 'German' },
|
||||
|
|
|
|||
|
|
@ -61,7 +61,9 @@ class Req {
|
|||
options.headers = {...options.headers, ...params.headers};
|
||||
}
|
||||
if(options.method == 'POST'){
|
||||
(options.headers as Headers)['Content-Type'] = 'application/x-www-form-urlencoded';
|
||||
if (!(options.headers as Headers)['Content-Type']) {
|
||||
(options.headers as Headers)['Content-Type'] = 'application/x-www-form-urlencoded';
|
||||
}
|
||||
}
|
||||
if(params.body){
|
||||
options.body = params.body;
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { Playlist, parse as mpdParse } from 'mpd-parser';
|
||||
import { LanguageItem } from './module.langsData';
|
||||
import { parse as mpdParse } from 'mpd-parser';
|
||||
import { LanguageItem, findLang, languages } from './module.langsData';
|
||||
|
||||
type Segment = {
|
||||
uri: string;
|
||||
|
|
@ -20,7 +20,8 @@ export type PlaylistItem = {
|
|||
|
||||
|
||||
type AudioPlayList = {
|
||||
language: LanguageItem
|
||||
language: LanguageItem,
|
||||
default: boolean
|
||||
} & PlaylistItem
|
||||
|
||||
type VideoPlayList = {
|
||||
|
|
@ -37,9 +38,9 @@ export type MPDParsed = {
|
|||
}
|
||||
}
|
||||
|
||||
export function parse(manifest: string, language: LanguageItem, url?: string) {
|
||||
export function parse(manifest: string, language?: LanguageItem, url?: string) {
|
||||
if (!manifest.includes('BaseURL') && url) {
|
||||
manifest = manifest.replace(/(<MPD[^]^[^]*?>)/gm, `$1<BaseURL>${url}</BaseURL>`);
|
||||
manifest = manifest.replace(/(<MPD*\b[^>]*>)/gm, `$1<BaseURL>${url}</BaseURL>`);
|
||||
}
|
||||
const parsed = mpdParse(manifest);
|
||||
const ret: MPDParsed = {};
|
||||
|
|
@ -50,9 +51,18 @@ export function parse(manifest: string, language: LanguageItem, url?: string) {
|
|||
if (!Object.prototype.hasOwnProperty.call(ret, host))
|
||||
ret[host] = { audio: [], video: [] };
|
||||
|
||||
//Find and add audio language if it is found in the MPD
|
||||
let audiolang: LanguageItem;
|
||||
const foundlanguage = findLang(languages.find(a => a.code === item.language)?.cr_locale ?? 'unknown');
|
||||
if (item.language) {
|
||||
audiolang = foundlanguage;
|
||||
} else {
|
||||
audiolang = language ? language : foundlanguage;
|
||||
}
|
||||
const pItem: AudioPlayList = {
|
||||
bandwidth: playlist.attributes.BANDWIDTH,
|
||||
language: language,
|
||||
language: audiolang,
|
||||
default: item.default,
|
||||
segments: playlist.segments.map((segment): Segment => {
|
||||
const uri = segment.resolvedUri;
|
||||
const map_uri = segment.map.resolvedUri;
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"name": "multi-downloader-nx",
|
||||
"short_name": "aniDL",
|
||||
"version": "4.5.0",
|
||||
"version": "4.5.0rc2",
|
||||
"description": "Downloader for Crunchyroll, Funimation, or Hidive via CLI or GUI",
|
||||
"keywords": [
|
||||
"download",
|
||||
|
|
|
|||
Loading…
Reference in a new issue