mirror of
https://github.com/anidl/multi-downloader-nx.git
synced 2026-03-11 17:45:30 +00:00
Fixed #232
This commit is contained in:
parent
fed44e1cea
commit
45c1810fe6
6 changed files with 155 additions and 28 deletions
|
|
@ -28,7 +28,7 @@
|
|||
2
|
||||
],
|
||||
"linebreak-style": [
|
||||
"error",
|
||||
"warn",
|
||||
"windows"
|
||||
],
|
||||
"quotes": [
|
||||
|
|
|
|||
75
@types/funiSubtitleRequest.d.ts
vendored
Normal file
75
@types/funiSubtitleRequest.d.ts
vendored
Normal file
|
|
@ -0,0 +1,75 @@
|
|||
// Generated by https://quicktype.io
|
||||
|
||||
export interface SubtitleRequest {
|
||||
primary: Primary;
|
||||
fallback: Primary[];
|
||||
}
|
||||
|
||||
export interface Primary {
|
||||
venueVideoId: string;
|
||||
alphaPackageId: string;
|
||||
versionContentId: VersionContentID;
|
||||
manifestPath: string;
|
||||
fileExt: PrimaryFileEXT;
|
||||
subtitles: Subtitle[];
|
||||
accessType: AccessType;
|
||||
sessionId: string;
|
||||
audioLanguage: AudioLanguage;
|
||||
version: Version;
|
||||
aips: Aip[];
|
||||
drmToken: string;
|
||||
drmType: string;
|
||||
}
|
||||
|
||||
export enum AccessType {
|
||||
Subscription = 'subscription',
|
||||
}
|
||||
|
||||
export interface Aip {
|
||||
in: number;
|
||||
out: number;
|
||||
}
|
||||
|
||||
export enum AudioLanguage {
|
||||
En = 'en',
|
||||
Ja = 'ja',
|
||||
}
|
||||
|
||||
export enum PrimaryFileEXT {
|
||||
M3U8 = 'm3u8',
|
||||
Mp4 = 'mp4',
|
||||
}
|
||||
|
||||
export interface Subtitle {
|
||||
filePath: string;
|
||||
fileExt: SubtitleFileEXT;
|
||||
contentType: ContentType;
|
||||
languageCode: LanguageCode;
|
||||
}
|
||||
|
||||
export enum ContentType {
|
||||
Cc = 'cc',
|
||||
Full = 'full',
|
||||
}
|
||||
|
||||
export enum SubtitleFileEXT {
|
||||
Dfxp = 'dfxp',
|
||||
Srt = 'srt',
|
||||
Vtt = 'vtt',
|
||||
}
|
||||
|
||||
export enum LanguageCode {
|
||||
En = 'en',
|
||||
Es = 'es',
|
||||
Pt = 'pt',
|
||||
}
|
||||
|
||||
export enum Version {
|
||||
Simulcast = 'simulcast',
|
||||
Uncut = 'uncut',
|
||||
}
|
||||
|
||||
export enum VersionContentID {
|
||||
Akusim0012 = 'AKUSIM0012',
|
||||
Akuunc0012 = 'AKUUNC0012',
|
||||
}
|
||||
5
@types/messageHandler.d.ts
vendored
5
@types/messageHandler.d.ts
vendored
|
|
@ -70,6 +70,11 @@ export type FuniEpisodeData = {
|
|||
episodeID: string,
|
||||
seasonTitle: string,
|
||||
seasonNumber: string,
|
||||
ids: {
|
||||
episode: string,
|
||||
show: string,
|
||||
season: string
|
||||
}
|
||||
};
|
||||
|
||||
export type AuthData = { username: string, password: string };
|
||||
|
|
|
|||
1
TODO.md
1
TODO.md
|
|
@ -9,7 +9,6 @@
|
|||
- [x] Window title
|
||||
- [x] Only open dev tools in test version
|
||||
- [x] Add help information (version, contributor, documentation...)
|
||||
- [ ] App Icon with electron-forge make
|
||||
- [x] ContextMenu
|
||||
- [x] Better episode listing with selectio via left mouse button
|
||||
- [x] Use Child for Context Menu
|
||||
|
|
|
|||
95
funi.ts
95
funi.ts
|
|
@ -39,6 +39,7 @@ import { TitleElement } from './@types/episode';
|
|||
import { AvailableFilenameVars } from './modules/module.args';
|
||||
import { AuthData, AuthResponse, CheckTokenResponse, FuniGetEpisodeData, FuniGetEpisodeResponse, FuniGetShowData, SearchData, FuniSearchReponse, FuniShowResponse, FuniStreamData, FuniSubsData, FuniEpisodeData, ResponseBase } from './@types/messageHandler';
|
||||
import { ServiceClass } from './@types/serviceClassInterface';
|
||||
import { SubtitleRequest } from './@types/funiSubtitleRequest';
|
||||
// check page
|
||||
|
||||
// fn variables
|
||||
|
|
@ -274,7 +275,18 @@ export default class Funi implements ServiceClass {
|
|||
// select
|
||||
is_selected = false;
|
||||
if (data.all || epSelList.isSelected(epStrId)) {
|
||||
fnSlug.push({title:eps[e].item.titleSlug,episode:eps[e].item.episodeSlug, episodeID:epStrId, seasonTitle: eps[e].item.seasonTitle, seasonNumber: eps[e].item.seasonNum});
|
||||
fnSlug.push({
|
||||
title:eps[e].item.titleSlug,
|
||||
episode:eps[e].item.episodeSlug,
|
||||
episodeID:epStrId,
|
||||
seasonTitle: eps[e].item.seasonTitle,
|
||||
seasonNumber: eps[e].item.seasonNum,
|
||||
ids: {
|
||||
episode: eps[e].ids.externalEpisodeId,
|
||||
season: eps[e].ids.externalSeasonId,
|
||||
show: eps[e].ids.externalShowId
|
||||
}
|
||||
});
|
||||
epSelEpsTxt.push(epStrId);
|
||||
is_selected = true;
|
||||
}
|
||||
|
|
@ -347,7 +359,7 @@ export default class Funi implements ServiceClass {
|
|||
console.log('[INFO] Available streams (Non-Encrypted):');
|
||||
}
|
||||
// map medias
|
||||
const media = ep.media.map((m) =>{
|
||||
const media = await Promise.all(ep.media.map(async (m) =>{
|
||||
if(m.mediaType == 'experience'){
|
||||
if(m.version.match(/uncut/i) && m.language){
|
||||
uncut[m.language] = true;
|
||||
|
|
@ -357,13 +369,13 @@ export default class Funi implements ServiceClass {
|
|||
language: m.language,
|
||||
version: m.version,
|
||||
type: m.experienceType,
|
||||
subtitles: this.getSubsUrl(m.mediaChildren, m.language, data.subs)
|
||||
subtitles: await this.getSubsUrl(m.mediaChildren, m.language, data.subs, ep.ids.externalEpisodeId)
|
||||
};
|
||||
}
|
||||
else{
|
||||
return { id: 0, type: '' };
|
||||
}
|
||||
});
|
||||
}));
|
||||
|
||||
// select
|
||||
stDlPath = [];
|
||||
|
|
@ -389,12 +401,18 @@ export default class Funi implements ServiceClass {
|
|||
selected = true;
|
||||
}
|
||||
}
|
||||
if (log)
|
||||
if (log) {
|
||||
const subsToDisplay: langsData.LanguageItem[] = [];
|
||||
localSubs.forEach(a => {
|
||||
if (!subsToDisplay.includes(a.lang))
|
||||
subsToDisplay.push(a.lang);
|
||||
});
|
||||
console.log(`[#${m.id}] ${dub_type} [${m.version}]${(selected?' (selected)':'')}${
|
||||
localSubs && localSubs.length > 0 && selected ? ` (using ${localSubs.map(a => `'${a.lang.name}'`).join(', ')} for subtitles)` : ''
|
||||
localSubs && localSubs.length > 0 && selected ? ` (using ${subsToDisplay.map(a => `'${a.name}'`).join(', ')} for subtitles)` : ''
|
||||
}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const already: string[] = [];
|
||||
stDlPath = stDlPath.filter(a => {
|
||||
|
|
@ -832,31 +850,60 @@ export default class Funi implements ServiceClass {
|
|||
|
||||
return downloadStatus.ok;
|
||||
}
|
||||
public getSubsUrl(m: MediaChild[], parentLanguage: TitleElement|undefined, data: FuniSubsData) : Subtitle[] {
|
||||
|
||||
public async getSubsUrl(m: MediaChild[], parentLanguage: TitleElement|undefined, data: FuniSubsData, episodeID: string) : Promise<Subtitle[]> {
|
||||
if((data.nosubs && !data.sub) || data.dlsubs.includes('none')){
|
||||
return [];
|
||||
}
|
||||
|
||||
const found: Subtitle[] = [];
|
||||
|
||||
const media = m.filter(a => a.filePath.split('.').pop() === 'vtt');
|
||||
for (const me of media) {
|
||||
const lang = langsData.languages.find(a => me.language === (a.funi_name || a.name));
|
||||
if (!lang) {
|
||||
continue;
|
||||
}
|
||||
const pLang = langsData.languages.find(a => (a.funi_name || a.name) === parentLanguage);
|
||||
if (data.dlsubs.includes('all') || data.dlsubs.some(a => a === lang.locale)) {
|
||||
found.push({
|
||||
url: me.filePath,
|
||||
ext: `.${lang.code}${pLang?.code === lang.code ? '.cc' : ''}`,
|
||||
lang,
|
||||
closedCaption: pLang?.code === lang.code
|
||||
const subs = await getData({
|
||||
baseUrl: 'https://playback.prd.funimationsvc.com/v1/play',
|
||||
url: `/${episodeID}`,
|
||||
token: this.token,
|
||||
useToken: true,
|
||||
debug: this.debug,
|
||||
querystring: { deviceType: 'web' }
|
||||
});
|
||||
if (!subs.ok || !subs.res || !subs.res.body) {
|
||||
console.log('[ERROR] Subtitle Request failed.');
|
||||
return [];
|
||||
}
|
||||
}
|
||||
const parsed: SubtitleRequest = JSON.parse(subs.res.body);
|
||||
|
||||
return found;
|
||||
const found: {
|
||||
isCC: boolean;
|
||||
url: string;
|
||||
lang: langsData.LanguageItem;
|
||||
}[] = parsed.primary.subtitles.filter(a => a.fileExt === 'vtt').map(subtitle => {
|
||||
return {
|
||||
isCC: subtitle.contentType === 'cc',
|
||||
url: subtitle.filePath,
|
||||
lang: langsData.languages.find(a => a.funi_locale ?? a.locale === subtitle.languageCode)
|
||||
};
|
||||
}).concat(m.filter(a => a.filePath.split('.').pop() === 'vtt').map(media => {
|
||||
const lang = langsData.languages.find(a => media.language === (a.funi_name || a.name));
|
||||
const pLang = langsData.languages.find(a => (a.funi_name || a.name) === parentLanguage);
|
||||
return {
|
||||
isCC: pLang?.code === lang?.code,
|
||||
url: media.filePath,
|
||||
lang
|
||||
};
|
||||
})).filter((a) => a.lang !== undefined) as {
|
||||
isCC: boolean;
|
||||
url: string;
|
||||
lang: langsData.LanguageItem;
|
||||
}[];
|
||||
|
||||
const ret = found.filter(item => {
|
||||
return data.dlsubs.includes('all') || data.dlsubs.some(a => a === item.lang.locale);
|
||||
});
|
||||
|
||||
return ret.map(a => ({
|
||||
ext: `.${a.lang.code}${a.isCC ? '.cc' : ''}`,
|
||||
lang: a.lang,
|
||||
url: a.url,
|
||||
closedCaption: a.isCC
|
||||
}));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -36,6 +36,7 @@ const getData = async <T = string>(options: Options) => {
|
|||
|
||||
const gOptions = {
|
||||
url: options.url,
|
||||
http2: true,
|
||||
headers: {
|
||||
'user-agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64; rv:70.0) Gecko/20100101 Firefox/70.0',
|
||||
'Accept-Encoding': 'gzip',
|
||||
|
|
|
|||
Loading…
Reference in a new issue