mirror of
https://github.com/anidl/multi-downloader-nx.git
synced 2026-03-11 17:45:30 +00:00
Rest of the tab conversion
This commit is contained in:
parent
460b4c1d0e
commit
a14466ec5d
76 changed files with 4315 additions and 4313 deletions
|
|
@ -11,9 +11,9 @@
|
|||
"requirePragma": false,
|
||||
"semi": true,
|
||||
"singleQuote": true,
|
||||
"tabWidth": 2,
|
||||
"tabWidth": 4,
|
||||
"trailingComma": "none",
|
||||
"useTabs": false,
|
||||
"useTabs": true,
|
||||
"vueIndentScriptAndStyle": false,
|
||||
"printWidth": 180,
|
||||
"endOfLine": "auto"
|
||||
|
|
|
|||
54
@types/adnPlayerConfig.d.ts
vendored
54
@types/adnPlayerConfig.d.ts
vendored
|
|
@ -1,50 +1,50 @@
|
|||
export interface ADNPlayerConfig {
|
||||
player: Player;
|
||||
player: Player;
|
||||
}
|
||||
|
||||
export interface Player {
|
||||
image: string;
|
||||
options: Options;
|
||||
image: string;
|
||||
options: Options;
|
||||
}
|
||||
|
||||
export interface Options {
|
||||
user: User;
|
||||
chromecast: Chromecast;
|
||||
ios: Ios;
|
||||
video: Video;
|
||||
dock: any[];
|
||||
preference: Preference;
|
||||
user: User;
|
||||
chromecast: Chromecast;
|
||||
ios: Ios;
|
||||
video: Video;
|
||||
dock: any[];
|
||||
preference: Preference;
|
||||
}
|
||||
|
||||
export interface Chromecast {
|
||||
appId: string;
|
||||
refreshTokenUrl: string;
|
||||
appId: string;
|
||||
refreshTokenUrl: string;
|
||||
}
|
||||
|
||||
export interface Ios {
|
||||
videoUrl: string;
|
||||
appUrl: string;
|
||||
title: string;
|
||||
videoUrl: string;
|
||||
appUrl: string;
|
||||
title: string;
|
||||
}
|
||||
|
||||
export interface Preference {
|
||||
quality: string;
|
||||
autoplay: boolean;
|
||||
language: string;
|
||||
green: boolean;
|
||||
quality: string;
|
||||
autoplay: boolean;
|
||||
language: string;
|
||||
green: boolean;
|
||||
}
|
||||
|
||||
export interface User {
|
||||
hasAccess: boolean;
|
||||
profileId: number;
|
||||
refreshToken: string;
|
||||
refreshTokenUrl: string;
|
||||
hasAccess: boolean;
|
||||
profileId: number;
|
||||
refreshToken: string;
|
||||
refreshTokenUrl: string;
|
||||
}
|
||||
|
||||
export interface Video {
|
||||
startDate: null;
|
||||
currentDate: Date;
|
||||
available: boolean;
|
||||
free: boolean;
|
||||
url: string;
|
||||
startDate: null;
|
||||
currentDate: Date;
|
||||
available: boolean;
|
||||
free: boolean;
|
||||
url: string;
|
||||
}
|
||||
|
|
|
|||
76
@types/adnSearch.d.ts
vendored
76
@types/adnSearch.d.ts
vendored
|
|
@ -1,46 +1,46 @@
|
|||
export interface ADNSearch {
|
||||
shows: ADNSearchShow[];
|
||||
total: number;
|
||||
shows: ADNSearchShow[];
|
||||
total: number;
|
||||
}
|
||||
|
||||
export interface ADNSearchShow {
|
||||
id: number;
|
||||
title: string;
|
||||
type: string;
|
||||
originalTitle: string;
|
||||
shortTitle: string;
|
||||
reference: string;
|
||||
age: string;
|
||||
languages: string[];
|
||||
summary: string;
|
||||
image: string;
|
||||
image2x: string;
|
||||
imageHorizontal: string;
|
||||
imageHorizontal2x: string;
|
||||
url: string;
|
||||
urlPath: string;
|
||||
episodeCount: number;
|
||||
genres: string[];
|
||||
copyright: string;
|
||||
rating: number;
|
||||
ratingsCount: number;
|
||||
commentsCount: number;
|
||||
qualities: string[];
|
||||
simulcast: boolean;
|
||||
free: boolean;
|
||||
available: boolean;
|
||||
download: boolean;
|
||||
basedOn: string;
|
||||
tagline: null;
|
||||
firstReleaseYear: string;
|
||||
productionStudio: string;
|
||||
countryOfOrigin: string;
|
||||
productionTeam: ProductionTeam[];
|
||||
nextVideoReleaseDate: null;
|
||||
indexable: boolean;
|
||||
id: number;
|
||||
title: string;
|
||||
type: string;
|
||||
originalTitle: string;
|
||||
shortTitle: string;
|
||||
reference: string;
|
||||
age: string;
|
||||
languages: string[];
|
||||
summary: string;
|
||||
image: string;
|
||||
image2x: string;
|
||||
imageHorizontal: string;
|
||||
imageHorizontal2x: string;
|
||||
url: string;
|
||||
urlPath: string;
|
||||
episodeCount: number;
|
||||
genres: string[];
|
||||
copyright: string;
|
||||
rating: number;
|
||||
ratingsCount: number;
|
||||
commentsCount: number;
|
||||
qualities: string[];
|
||||
simulcast: boolean;
|
||||
free: boolean;
|
||||
available: boolean;
|
||||
download: boolean;
|
||||
basedOn: string;
|
||||
tagline: null;
|
||||
firstReleaseYear: string;
|
||||
productionStudio: string;
|
||||
countryOfOrigin: string;
|
||||
productionTeam: ProductionTeam[];
|
||||
nextVideoReleaseDate: null;
|
||||
indexable: boolean;
|
||||
}
|
||||
|
||||
export interface ProductionTeam {
|
||||
role: string;
|
||||
name: string;
|
||||
role: string;
|
||||
name: string;
|
||||
}
|
||||
|
|
|
|||
62
@types/adnStreams.d.ts
vendored
62
@types/adnStreams.d.ts
vendored
|
|
@ -1,51 +1,51 @@
|
|||
export interface ADNStreams {
|
||||
links: Links;
|
||||
video: Video;
|
||||
metadata: Metadata;
|
||||
links: Links;
|
||||
video: Video;
|
||||
metadata: Metadata;
|
||||
}
|
||||
|
||||
export interface Links {
|
||||
streaming: Streaming;
|
||||
subtitles: Subtitles;
|
||||
history: string;
|
||||
nextVideoUrl: string;
|
||||
previousVideoUrl: string;
|
||||
streaming: Streaming;
|
||||
subtitles: Subtitles;
|
||||
history: string;
|
||||
nextVideoUrl: string;
|
||||
previousVideoUrl: string;
|
||||
}
|
||||
|
||||
export interface Streaming {
|
||||
[streams: string]: Streams;
|
||||
[streams: string]: Streams;
|
||||
}
|
||||
|
||||
export interface Streams {
|
||||
mobile: string;
|
||||
sd: string;
|
||||
hd: string;
|
||||
fhd: string;
|
||||
auto: string;
|
||||
mobile: string;
|
||||
sd: string;
|
||||
hd: string;
|
||||
fhd: string;
|
||||
auto: string;
|
||||
}
|
||||
|
||||
export interface Subtitles {
|
||||
all: string;
|
||||
all: string;
|
||||
}
|
||||
|
||||
export interface Metadata {
|
||||
title: string;
|
||||
subtitle: string;
|
||||
summary: null;
|
||||
rating: number;
|
||||
title: string;
|
||||
subtitle: string;
|
||||
summary: null;
|
||||
rating: number;
|
||||
}
|
||||
|
||||
export interface Video {
|
||||
guid: string;
|
||||
id: number;
|
||||
currentTime: number;
|
||||
duration: number;
|
||||
url: string;
|
||||
image: string;
|
||||
tcEpisodeStart?:string;
|
||||
tcEpisodeEnd?: string;
|
||||
tcIntroStart?: string;
|
||||
tcIntroEnd?: string;
|
||||
tcEndingStart?: string;
|
||||
tcEndingEnd?: string;
|
||||
guid: string;
|
||||
id: number;
|
||||
currentTime: number;
|
||||
duration: number;
|
||||
url: string;
|
||||
image: string;
|
||||
tcEpisodeStart?:string;
|
||||
tcEpisodeEnd?: string;
|
||||
tcIntroStart?: string;
|
||||
tcIntroEnd?: string;
|
||||
tcEndingStart?: string;
|
||||
tcEndingEnd?: string;
|
||||
}
|
||||
|
|
|
|||
12
@types/adnSubtitles.d.ts
vendored
12
@types/adnSubtitles.d.ts
vendored
|
|
@ -1,11 +1,11 @@
|
|||
export interface ADNSubtitles {
|
||||
[subtitleLang: string]: Subtitle[];
|
||||
[subtitleLang: string]: Subtitle[];
|
||||
}
|
||||
|
||||
export interface Subtitle {
|
||||
startTime: number;
|
||||
endTime: number;
|
||||
positionAlign: string;
|
||||
lineAlign: string;
|
||||
text: string;
|
||||
startTime: number;
|
||||
endTime: number;
|
||||
positionAlign: string;
|
||||
lineAlign: string;
|
||||
text: string;
|
||||
}
|
||||
|
|
|
|||
132
@types/adnVideos.d.ts
vendored
132
@types/adnVideos.d.ts
vendored
|
|
@ -1,77 +1,77 @@
|
|||
export interface ADNVideos {
|
||||
videos: ADNVideo[];
|
||||
videos: ADNVideo[];
|
||||
}
|
||||
|
||||
export interface ADNVideo {
|
||||
id: number;
|
||||
title: string;
|
||||
name: string;
|
||||
number: string;
|
||||
shortNumber: string;
|
||||
season: string;
|
||||
reference: string;
|
||||
type: string;
|
||||
order: number;
|
||||
image: string;
|
||||
image2x: string;
|
||||
summary: string;
|
||||
releaseDate: Date;
|
||||
duration: number;
|
||||
url: string;
|
||||
urlPath: string;
|
||||
embeddedUrl: string;
|
||||
languages: string[];
|
||||
qualities: string[];
|
||||
rating: number;
|
||||
ratingsCount: number;
|
||||
commentsCount: number;
|
||||
available: boolean;
|
||||
download: boolean;
|
||||
free: boolean;
|
||||
freeWithAds: boolean;
|
||||
show: Show;
|
||||
indexable: boolean;
|
||||
isSelected?: boolean;
|
||||
id: number;
|
||||
title: string;
|
||||
name: string;
|
||||
number: string;
|
||||
shortNumber: string;
|
||||
season: string;
|
||||
reference: string;
|
||||
type: string;
|
||||
order: number;
|
||||
image: string;
|
||||
image2x: string;
|
||||
summary: string;
|
||||
releaseDate: Date;
|
||||
duration: number;
|
||||
url: string;
|
||||
urlPath: string;
|
||||
embeddedUrl: string;
|
||||
languages: string[];
|
||||
qualities: string[];
|
||||
rating: number;
|
||||
ratingsCount: number;
|
||||
commentsCount: number;
|
||||
available: boolean;
|
||||
download: boolean;
|
||||
free: boolean;
|
||||
freeWithAds: boolean;
|
||||
show: Show;
|
||||
indexable: boolean;
|
||||
isSelected?: boolean;
|
||||
}
|
||||
|
||||
export interface Show {
|
||||
id: number;
|
||||
title: string;
|
||||
type: string;
|
||||
originalTitle: string;
|
||||
shortTitle: string;
|
||||
reference: string;
|
||||
age: string;
|
||||
languages: string[];
|
||||
summary: string;
|
||||
image: string;
|
||||
image2x: string;
|
||||
imageHorizontal: string;
|
||||
imageHorizontal2x: string;
|
||||
url: string;
|
||||
urlPath: string;
|
||||
episodeCount: number;
|
||||
genres: string[];
|
||||
copyright: string;
|
||||
rating: number;
|
||||
ratingsCount: number;
|
||||
commentsCount: number;
|
||||
qualities: string[];
|
||||
simulcast: boolean;
|
||||
free: boolean;
|
||||
available: boolean;
|
||||
download: boolean;
|
||||
basedOn: string;
|
||||
tagline: string;
|
||||
firstReleaseYear: string;
|
||||
productionStudio: string;
|
||||
countryOfOrigin: string;
|
||||
productionTeam: ProductionTeam[];
|
||||
nextVideoReleaseDate: Date;
|
||||
indexable: boolean;
|
||||
id: number;
|
||||
title: string;
|
||||
type: string;
|
||||
originalTitle: string;
|
||||
shortTitle: string;
|
||||
reference: string;
|
||||
age: string;
|
||||
languages: string[];
|
||||
summary: string;
|
||||
image: string;
|
||||
image2x: string;
|
||||
imageHorizontal: string;
|
||||
imageHorizontal2x: string;
|
||||
url: string;
|
||||
urlPath: string;
|
||||
episodeCount: number;
|
||||
genres: string[];
|
||||
copyright: string;
|
||||
rating: number;
|
||||
ratingsCount: number;
|
||||
commentsCount: number;
|
||||
qualities: string[];
|
||||
simulcast: boolean;
|
||||
free: boolean;
|
||||
available: boolean;
|
||||
download: boolean;
|
||||
basedOn: string;
|
||||
tagline: string;
|
||||
firstReleaseYear: string;
|
||||
productionStudio: string;
|
||||
countryOfOrigin: string;
|
||||
productionTeam: ProductionTeam[];
|
||||
nextVideoReleaseDate: Date;
|
||||
indexable: boolean;
|
||||
}
|
||||
|
||||
export interface ProductionTeam {
|
||||
role: string;
|
||||
name: string;
|
||||
role: string;
|
||||
name: string;
|
||||
}
|
||||
|
|
|
|||
166
@types/animeOnegaiSearch.d.ts
vendored
166
@types/animeOnegaiSearch.d.ts
vendored
|
|
@ -1,88 +1,88 @@
|
|||
export interface AnimeOnegaiSearch {
|
||||
text: string;
|
||||
list: AOSearchResult[];
|
||||
text: string;
|
||||
list: AOSearchResult[];
|
||||
}
|
||||
|
||||
export interface AOSearchResult {
|
||||
/**
|
||||
* Asset ID
|
||||
*/
|
||||
ID: number;
|
||||
CreatedAt: Date;
|
||||
UpdatedAt: Date;
|
||||
DeletedAt: null;
|
||||
title: string;
|
||||
active: boolean;
|
||||
excerpt: string;
|
||||
description: string;
|
||||
bg: string;
|
||||
poster: string;
|
||||
entry: string;
|
||||
code_name: string;
|
||||
/**
|
||||
* The Video ID required to get the streams
|
||||
*/
|
||||
video_entry: string;
|
||||
trailer: string;
|
||||
year: number;
|
||||
/**
|
||||
* Asset Type, Known Possibilities
|
||||
* * 1 - Video
|
||||
* * 2 - Series
|
||||
*/
|
||||
asset_type: 1 | 2;
|
||||
status: number;
|
||||
permalink: string;
|
||||
duration: string;
|
||||
subtitles: boolean;
|
||||
price: number;
|
||||
rent_price: number;
|
||||
rating: number;
|
||||
color: number | null;
|
||||
classification: number;
|
||||
brazil_classification: null | string;
|
||||
likes: number;
|
||||
views: number;
|
||||
button: string;
|
||||
stream_url: string;
|
||||
stream_url_backup: string;
|
||||
copyright: null | string;
|
||||
skip_intro: null | string;
|
||||
ending: null | string;
|
||||
bumper_intro: string;
|
||||
ads: string;
|
||||
age_restriction: boolean | null;
|
||||
epg: null;
|
||||
allow_languages: string[] | null;
|
||||
allow_countries: string[] | null;
|
||||
classification_text: string;
|
||||
locked: boolean;
|
||||
resign: boolean;
|
||||
favorite: boolean;
|
||||
actors_list: null;
|
||||
voiceactors_list: null;
|
||||
artdirectors_list: null;
|
||||
audios_list: null;
|
||||
awards_list: null;
|
||||
companies_list: null;
|
||||
countries_list: null;
|
||||
directors_list: null;
|
||||
edition_list: null;
|
||||
genres_list: null;
|
||||
music_list: null;
|
||||
photograpy_list: null;
|
||||
producer_list: null;
|
||||
screenwriter_list: null;
|
||||
season_list: null;
|
||||
tags_list: null;
|
||||
chapter_id: number;
|
||||
chapter_entry: string;
|
||||
chapter_poster: string;
|
||||
progress_time: number;
|
||||
progress_percent: number;
|
||||
included_subscription: number;
|
||||
paid_content: number;
|
||||
rent_content: number;
|
||||
objectID: string;
|
||||
lang: string;
|
||||
/**
|
||||
* Asset ID
|
||||
*/
|
||||
ID: number;
|
||||
CreatedAt: Date;
|
||||
UpdatedAt: Date;
|
||||
DeletedAt: null;
|
||||
title: string;
|
||||
active: boolean;
|
||||
excerpt: string;
|
||||
description: string;
|
||||
bg: string;
|
||||
poster: string;
|
||||
entry: string;
|
||||
code_name: string;
|
||||
/**
|
||||
* The Video ID required to get the streams
|
||||
*/
|
||||
video_entry: string;
|
||||
trailer: string;
|
||||
year: number;
|
||||
/**
|
||||
* Asset Type, Known Possibilities
|
||||
* * 1 - Video
|
||||
* * 2 - Series
|
||||
*/
|
||||
asset_type: 1 | 2;
|
||||
status: number;
|
||||
permalink: string;
|
||||
duration: string;
|
||||
subtitles: boolean;
|
||||
price: number;
|
||||
rent_price: number;
|
||||
rating: number;
|
||||
color: number | null;
|
||||
classification: number;
|
||||
brazil_classification: null | string;
|
||||
likes: number;
|
||||
views: number;
|
||||
button: string;
|
||||
stream_url: string;
|
||||
stream_url_backup: string;
|
||||
copyright: null | string;
|
||||
skip_intro: null | string;
|
||||
ending: null | string;
|
||||
bumper_intro: string;
|
||||
ads: string;
|
||||
age_restriction: boolean | null;
|
||||
epg: null;
|
||||
allow_languages: string[] | null;
|
||||
allow_countries: string[] | null;
|
||||
classification_text: string;
|
||||
locked: boolean;
|
||||
resign: boolean;
|
||||
favorite: boolean;
|
||||
actors_list: null;
|
||||
voiceactors_list: null;
|
||||
artdirectors_list: null;
|
||||
audios_list: null;
|
||||
awards_list: null;
|
||||
companies_list: null;
|
||||
countries_list: null;
|
||||
directors_list: null;
|
||||
edition_list: null;
|
||||
genres_list: null;
|
||||
music_list: null;
|
||||
photograpy_list: null;
|
||||
producer_list: null;
|
||||
screenwriter_list: null;
|
||||
season_list: null;
|
||||
tags_list: null;
|
||||
chapter_id: number;
|
||||
chapter_entry: string;
|
||||
chapter_poster: string;
|
||||
progress_time: number;
|
||||
progress_percent: number;
|
||||
included_subscription: number;
|
||||
paid_content: number;
|
||||
rent_content: number;
|
||||
objectID: string;
|
||||
lang: string;
|
||||
}
|
||||
62
@types/animeOnegaiSeasons.d.ts
vendored
62
@types/animeOnegaiSeasons.d.ts
vendored
|
|
@ -1,36 +1,36 @@
|
|||
export interface AnimeOnegaiSeasons {
|
||||
ID: number;
|
||||
CreatedAt: Date;
|
||||
UpdatedAt: Date;
|
||||
DeletedAt: null;
|
||||
name: string;
|
||||
number: number;
|
||||
asset_id: number;
|
||||
entry: string;
|
||||
description: string;
|
||||
active: boolean;
|
||||
allow_languages: string[];
|
||||
allow_countries: string[];
|
||||
list: Episode[];
|
||||
ID: number;
|
||||
CreatedAt: Date;
|
||||
UpdatedAt: Date;
|
||||
DeletedAt: null;
|
||||
name: string;
|
||||
number: number;
|
||||
asset_id: number;
|
||||
entry: string;
|
||||
description: string;
|
||||
active: boolean;
|
||||
allow_languages: string[];
|
||||
allow_countries: string[];
|
||||
list: Episode[];
|
||||
}
|
||||
|
||||
export interface Episode {
|
||||
ID: number;
|
||||
CreatedAt: Date;
|
||||
UpdatedAt: Date;
|
||||
DeletedAt: null;
|
||||
name: string;
|
||||
number: number;
|
||||
description: string;
|
||||
thumbnail: string;
|
||||
entry: string;
|
||||
video_entry: string;
|
||||
active: boolean;
|
||||
season_id: number;
|
||||
stream_url: string;
|
||||
skip_intro: null;
|
||||
ending: null;
|
||||
open_free: boolean;
|
||||
asset_id: number;
|
||||
age_restriction: boolean;
|
||||
ID: number;
|
||||
CreatedAt: Date;
|
||||
UpdatedAt: Date;
|
||||
DeletedAt: null;
|
||||
name: string;
|
||||
number: number;
|
||||
description: string;
|
||||
thumbnail: string;
|
||||
entry: string;
|
||||
video_entry: string;
|
||||
active: boolean;
|
||||
season_id: number;
|
||||
stream_url: string;
|
||||
skip_intro: null;
|
||||
ending: null;
|
||||
open_free: boolean;
|
||||
asset_id: number;
|
||||
age_restriction: boolean;
|
||||
}
|
||||
|
|
|
|||
200
@types/animeOnegaiSeries.d.ts
vendored
200
@types/animeOnegaiSeries.d.ts
vendored
|
|
@ -1,111 +1,111 @@
|
|||
export interface AnimeOnegaiSeries {
|
||||
ID: number;
|
||||
CreatedAt: Date;
|
||||
UpdatedAt: Date;
|
||||
DeletedAt: null;
|
||||
title: string;
|
||||
active: boolean;
|
||||
excerpt: string;
|
||||
description: string;
|
||||
bg: string;
|
||||
poster: string;
|
||||
entry: string;
|
||||
code_name: string;
|
||||
/**
|
||||
* The Video ID required to get the streams
|
||||
*/
|
||||
video_entry: string;
|
||||
trailer: string;
|
||||
year: number;
|
||||
asset_type: number;
|
||||
status: number;
|
||||
permalink: string;
|
||||
duration: string;
|
||||
subtitles: boolean;
|
||||
price: number;
|
||||
rent_price: number;
|
||||
rating: number;
|
||||
color: number;
|
||||
classification: number;
|
||||
brazil_classification: string;
|
||||
likes: number;
|
||||
views: number;
|
||||
button: string;
|
||||
stream_url: string;
|
||||
stream_url_backup: string;
|
||||
copyright: string;
|
||||
skip_intro: null;
|
||||
ending: null;
|
||||
bumper_intro: string;
|
||||
ads: string;
|
||||
age_restriction: boolean;
|
||||
epg: null;
|
||||
allow_languages: string[];
|
||||
allow_countries: string[];
|
||||
classification_text: string;
|
||||
locked: boolean;
|
||||
resign: boolean;
|
||||
favorite: boolean;
|
||||
actors_list: CtorsList[];
|
||||
voiceactors_list: CtorsList[];
|
||||
artdirectors_list: any[];
|
||||
audios_list: SList[];
|
||||
awards_list: any[];
|
||||
companies_list: any[];
|
||||
countries_list: any[];
|
||||
directors_list: CtorsList[];
|
||||
edition_list: any[];
|
||||
genres_list: SList[];
|
||||
music_list: any[];
|
||||
photograpy_list: any[];
|
||||
producer_list: any[];
|
||||
screenwriter_list: any[];
|
||||
season_list: any[];
|
||||
tags_list: TagsList[];
|
||||
chapter_id: number;
|
||||
chapter_entry: string;
|
||||
chapter_poster: string;
|
||||
progress_time: number;
|
||||
progress_percent: number;
|
||||
included_subscription: number;
|
||||
paid_content: number;
|
||||
rent_content: number;
|
||||
objectID: string;
|
||||
lang: string;
|
||||
ID: number;
|
||||
CreatedAt: Date;
|
||||
UpdatedAt: Date;
|
||||
DeletedAt: null;
|
||||
title: string;
|
||||
active: boolean;
|
||||
excerpt: string;
|
||||
description: string;
|
||||
bg: string;
|
||||
poster: string;
|
||||
entry: string;
|
||||
code_name: string;
|
||||
/**
|
||||
* The Video ID required to get the streams
|
||||
*/
|
||||
video_entry: string;
|
||||
trailer: string;
|
||||
year: number;
|
||||
asset_type: number;
|
||||
status: number;
|
||||
permalink: string;
|
||||
duration: string;
|
||||
subtitles: boolean;
|
||||
price: number;
|
||||
rent_price: number;
|
||||
rating: number;
|
||||
color: number;
|
||||
classification: number;
|
||||
brazil_classification: string;
|
||||
likes: number;
|
||||
views: number;
|
||||
button: string;
|
||||
stream_url: string;
|
||||
stream_url_backup: string;
|
||||
copyright: string;
|
||||
skip_intro: null;
|
||||
ending: null;
|
||||
bumper_intro: string;
|
||||
ads: string;
|
||||
age_restriction: boolean;
|
||||
epg: null;
|
||||
allow_languages: string[];
|
||||
allow_countries: string[];
|
||||
classification_text: string;
|
||||
locked: boolean;
|
||||
resign: boolean;
|
||||
favorite: boolean;
|
||||
actors_list: CtorsList[];
|
||||
voiceactors_list: CtorsList[];
|
||||
artdirectors_list: any[];
|
||||
audios_list: SList[];
|
||||
awards_list: any[];
|
||||
companies_list: any[];
|
||||
countries_list: any[];
|
||||
directors_list: CtorsList[];
|
||||
edition_list: any[];
|
||||
genres_list: SList[];
|
||||
music_list: any[];
|
||||
photograpy_list: any[];
|
||||
producer_list: any[];
|
||||
screenwriter_list: any[];
|
||||
season_list: any[];
|
||||
tags_list: TagsList[];
|
||||
chapter_id: number;
|
||||
chapter_entry: string;
|
||||
chapter_poster: string;
|
||||
progress_time: number;
|
||||
progress_percent: number;
|
||||
included_subscription: number;
|
||||
paid_content: number;
|
||||
rent_content: number;
|
||||
objectID: string;
|
||||
lang: string;
|
||||
}
|
||||
|
||||
export interface CtorsList {
|
||||
ID: number;
|
||||
CreatedAt: Date;
|
||||
UpdatedAt: Date;
|
||||
DeletedAt: null;
|
||||
name: string;
|
||||
Permalink?: string;
|
||||
country: number | null;
|
||||
year: number | null;
|
||||
death: number | null;
|
||||
image: string;
|
||||
genre: null;
|
||||
description: string;
|
||||
permalink?: string;
|
||||
background?: string;
|
||||
ID: number;
|
||||
CreatedAt: Date;
|
||||
UpdatedAt: Date;
|
||||
DeletedAt: null;
|
||||
name: string;
|
||||
Permalink?: string;
|
||||
country: number | null;
|
||||
year: number | null;
|
||||
death: number | null;
|
||||
image: string;
|
||||
genre: null;
|
||||
description: string;
|
||||
permalink?: string;
|
||||
background?: string;
|
||||
}
|
||||
|
||||
export interface SList {
|
||||
ID: number;
|
||||
CreatedAt: Date;
|
||||
UpdatedAt: Date;
|
||||
DeletedAt: null;
|
||||
name: string;
|
||||
age_restriction?: number;
|
||||
ID: number;
|
||||
CreatedAt: Date;
|
||||
UpdatedAt: Date;
|
||||
DeletedAt: null;
|
||||
name: string;
|
||||
age_restriction?: number;
|
||||
}
|
||||
|
||||
export interface TagsList {
|
||||
ID: number;
|
||||
CreatedAt: Date;
|
||||
UpdatedAt: Date;
|
||||
DeletedAt: null;
|
||||
name: string;
|
||||
position: number;
|
||||
status: boolean;
|
||||
ID: number;
|
||||
CreatedAt: Date;
|
||||
UpdatedAt: Date;
|
||||
DeletedAt: null;
|
||||
name: string;
|
||||
position: number;
|
||||
status: boolean;
|
||||
}
|
||||
|
|
|
|||
72
@types/animeOnegaiStream.d.ts
vendored
72
@types/animeOnegaiStream.d.ts
vendored
|
|
@ -1,41 +1,41 @@
|
|||
export interface AnimeOnegaiStream {
|
||||
ID: number;
|
||||
CreatedAt: Date;
|
||||
UpdatedAt: Date;
|
||||
DeletedAt: null;
|
||||
name: string;
|
||||
source_url: string;
|
||||
backup_url: string;
|
||||
live: boolean;
|
||||
token_handler: number;
|
||||
entry: string;
|
||||
job: string;
|
||||
drm: boolean;
|
||||
transcoding_content_id: string;
|
||||
transcoding_asset_id: string;
|
||||
status: number;
|
||||
thumbnail: string;
|
||||
hls: string;
|
||||
dash: string;
|
||||
widevine_proxy: string;
|
||||
playready_proxy: string;
|
||||
apple_licence: string;
|
||||
apple_certificate: string;
|
||||
dpath: string;
|
||||
dbin: string;
|
||||
subtitles: Subtitle[];
|
||||
origin: number;
|
||||
offline_entry: string;
|
||||
offline_status: boolean;
|
||||
ID: number;
|
||||
CreatedAt: Date;
|
||||
UpdatedAt: Date;
|
||||
DeletedAt: null;
|
||||
name: string;
|
||||
source_url: string;
|
||||
backup_url: string;
|
||||
live: boolean;
|
||||
token_handler: number;
|
||||
entry: string;
|
||||
job: string;
|
||||
drm: boolean;
|
||||
transcoding_content_id: string;
|
||||
transcoding_asset_id: string;
|
||||
status: number;
|
||||
thumbnail: string;
|
||||
hls: string;
|
||||
dash: string;
|
||||
widevine_proxy: string;
|
||||
playready_proxy: string;
|
||||
apple_licence: string;
|
||||
apple_certificate: string;
|
||||
dpath: string;
|
||||
dbin: string;
|
||||
subtitles: Subtitle[];
|
||||
origin: number;
|
||||
offline_entry: string;
|
||||
offline_status: boolean;
|
||||
}
|
||||
|
||||
export interface Subtitle {
|
||||
ID: number;
|
||||
CreatedAt: Date;
|
||||
UpdatedAt: Date;
|
||||
DeletedAt: null;
|
||||
name: string;
|
||||
lang: string;
|
||||
entry_id: string;
|
||||
url: string;
|
||||
ID: number;
|
||||
CreatedAt: Date;
|
||||
UpdatedAt: Date;
|
||||
DeletedAt: null;
|
||||
name: string;
|
||||
lang: string;
|
||||
entry_id: string;
|
||||
url: string;
|
||||
}
|
||||
|
|
|
|||
208
@types/crunchyAndroidEpisodes.d.ts
vendored
208
@types/crunchyAndroidEpisodes.d.ts
vendored
|
|
@ -1,136 +1,136 @@
|
|||
import { Images } from './crunchyEpisodeList';
|
||||
|
||||
export interface CrunchyAndroidEpisodes {
|
||||
__class__: string;
|
||||
__href__: string;
|
||||
__resource_key__: string;
|
||||
__links__: object;
|
||||
__actions__: object;
|
||||
total: number;
|
||||
items: CrunchyAndroidEpisode[];
|
||||
__class__: string;
|
||||
__href__: string;
|
||||
__resource_key__: string;
|
||||
__links__: object;
|
||||
__actions__: object;
|
||||
total: number;
|
||||
items: CrunchyAndroidEpisode[];
|
||||
}
|
||||
|
||||
export interface CrunchyAndroidEpisode {
|
||||
__class__: string;
|
||||
__href__: string;
|
||||
__resource_key__: string;
|
||||
__links__: Links;
|
||||
__actions__: Actions;
|
||||
playback: string;
|
||||
id: string;
|
||||
channel_id: ChannelID;
|
||||
series_id: string;
|
||||
series_title: string;
|
||||
series_slug_title: string;
|
||||
season_id: string;
|
||||
season_title: string;
|
||||
season_slug_title: string;
|
||||
season_number: number;
|
||||
episode: string;
|
||||
episode_number: number;
|
||||
sequence_number: number;
|
||||
production_episode_id: string;
|
||||
title: string;
|
||||
slug_title: string;
|
||||
description: string;
|
||||
next_episode_id: string;
|
||||
next_episode_title: string;
|
||||
hd_flag: boolean;
|
||||
maturity_ratings: MaturityRating[];
|
||||
extended_maturity_rating: Actions;
|
||||
is_mature: boolean;
|
||||
mature_blocked: boolean;
|
||||
episode_air_date: Date;
|
||||
upload_date: Date;
|
||||
availability_starts: Date;
|
||||
availability_ends: Date;
|
||||
eligible_region: string;
|
||||
available_date: Date;
|
||||
free_available_date: Date;
|
||||
premium_date: Date;
|
||||
premium_available_date: Date;
|
||||
is_subbed: boolean;
|
||||
is_dubbed: boolean;
|
||||
is_clip: boolean;
|
||||
seo_title: string;
|
||||
seo_description: string;
|
||||
season_tags: string[];
|
||||
available_offline: boolean;
|
||||
subtitle_locales: Locale[];
|
||||
availability_notes: string;
|
||||
audio_locale: Locale;
|
||||
versions: Version[];
|
||||
closed_captions_available: boolean;
|
||||
identifier: string;
|
||||
media_type: MediaType;
|
||||
slug: string;
|
||||
images: Images;
|
||||
duration_ms: number;
|
||||
is_premium_only: boolean;
|
||||
listing_id: string;
|
||||
hide_season_title?: boolean;
|
||||
hide_season_number?: boolean;
|
||||
isSelected?: boolean;
|
||||
seq_id: string;
|
||||
__class__: string;
|
||||
__href__: string;
|
||||
__resource_key__: string;
|
||||
__links__: Links;
|
||||
__actions__: Actions;
|
||||
playback: string;
|
||||
id: string;
|
||||
channel_id: ChannelID;
|
||||
series_id: string;
|
||||
series_title: string;
|
||||
series_slug_title: string;
|
||||
season_id: string;
|
||||
season_title: string;
|
||||
season_slug_title: string;
|
||||
season_number: number;
|
||||
episode: string;
|
||||
episode_number: number;
|
||||
sequence_number: number;
|
||||
production_episode_id: string;
|
||||
title: string;
|
||||
slug_title: string;
|
||||
description: string;
|
||||
next_episode_id: string;
|
||||
next_episode_title: string;
|
||||
hd_flag: boolean;
|
||||
maturity_ratings: MaturityRating[];
|
||||
extended_maturity_rating: Actions;
|
||||
is_mature: boolean;
|
||||
mature_blocked: boolean;
|
||||
episode_air_date: Date;
|
||||
upload_date: Date;
|
||||
availability_starts: Date;
|
||||
availability_ends: Date;
|
||||
eligible_region: string;
|
||||
available_date: Date;
|
||||
free_available_date: Date;
|
||||
premium_date: Date;
|
||||
premium_available_date: Date;
|
||||
is_subbed: boolean;
|
||||
is_dubbed: boolean;
|
||||
is_clip: boolean;
|
||||
seo_title: string;
|
||||
seo_description: string;
|
||||
season_tags: string[];
|
||||
available_offline: boolean;
|
||||
subtitle_locales: Locale[];
|
||||
availability_notes: string;
|
||||
audio_locale: Locale;
|
||||
versions: Version[];
|
||||
closed_captions_available: boolean;
|
||||
identifier: string;
|
||||
media_type: MediaType;
|
||||
slug: string;
|
||||
images: Images;
|
||||
duration_ms: number;
|
||||
is_premium_only: boolean;
|
||||
listing_id: string;
|
||||
hide_season_title?: boolean;
|
||||
hide_season_number?: boolean;
|
||||
isSelected?: boolean;
|
||||
seq_id: string;
|
||||
}
|
||||
|
||||
export interface Links {
|
||||
'episode/channel': Link;
|
||||
'episode/next_episode': Link;
|
||||
'episode/season': Link;
|
||||
'episode/series': Link;
|
||||
streams: Link;
|
||||
'episode/channel': Link;
|
||||
'episode/next_episode': Link;
|
||||
'episode/season': Link;
|
||||
'episode/series': Link;
|
||||
streams: Link;
|
||||
}
|
||||
|
||||
export interface Link {
|
||||
href: string;
|
||||
href: string;
|
||||
}
|
||||
|
||||
export interface Thumbnail {
|
||||
width: number;
|
||||
height: number;
|
||||
type: string;
|
||||
source: string;
|
||||
width: number;
|
||||
height: number;
|
||||
type: string;
|
||||
source: string;
|
||||
}
|
||||
|
||||
export enum Locale {
|
||||
enUS = 'en-US',
|
||||
esLA = 'es-LA',
|
||||
es419 = 'es-419',
|
||||
esES = 'es-ES',
|
||||
ptBR = 'pt-BR',
|
||||
frFR = 'fr-FR',
|
||||
deDE = 'de-DE',
|
||||
arME = 'ar-ME',
|
||||
arSA = 'ar-SA',
|
||||
itIT = 'it-IT',
|
||||
ruRU = 'ru-RU',
|
||||
trTR = 'tr-TR',
|
||||
hiIN = 'hi-IN',
|
||||
zhCN = 'zh-CN',
|
||||
koKR = 'ko-KR',
|
||||
jaJP = 'ja-JP',
|
||||
enUS = 'en-US',
|
||||
esLA = 'es-LA',
|
||||
es419 = 'es-419',
|
||||
esES = 'es-ES',
|
||||
ptBR = 'pt-BR',
|
||||
frFR = 'fr-FR',
|
||||
deDE = 'de-DE',
|
||||
arME = 'ar-ME',
|
||||
arSA = 'ar-SA',
|
||||
itIT = 'it-IT',
|
||||
ruRU = 'ru-RU',
|
||||
trTR = 'tr-TR',
|
||||
hiIN = 'hi-IN',
|
||||
zhCN = 'zh-CN',
|
||||
koKR = 'ko-KR',
|
||||
jaJP = 'ja-JP',
|
||||
}
|
||||
|
||||
export enum MediaType {
|
||||
Episode = 'episode',
|
||||
Episode = 'episode',
|
||||
}
|
||||
|
||||
export enum ChannelID {
|
||||
Crunchyroll = 'crunchyroll',
|
||||
Crunchyroll = 'crunchyroll',
|
||||
}
|
||||
|
||||
export enum MaturityRating {
|
||||
Tv14 = 'TV-14',
|
||||
Tv14 = 'TV-14',
|
||||
}
|
||||
|
||||
export interface Version {
|
||||
audio_locale: Locale;
|
||||
guid: string;
|
||||
original: boolean;
|
||||
variant: string;
|
||||
season_guid: string;
|
||||
media_guid: string;
|
||||
is_premium_only: boolean;
|
||||
audio_locale: Locale;
|
||||
guid: string;
|
||||
original: boolean;
|
||||
variant: string;
|
||||
season_guid: string;
|
||||
media_guid: string;
|
||||
is_premium_only: boolean;
|
||||
}
|
||||
|
||||
|
|
|
|||
306
@types/crunchyAndroidObject.d.ts
vendored
306
@types/crunchyAndroidObject.d.ts
vendored
|
|
@ -1,186 +1,186 @@
|
|||
import { ImageType, Images, Image } from './objectInfo';
|
||||
|
||||
export interface CrunchyAndroidObject {
|
||||
__class__: string;
|
||||
__href__: string;
|
||||
__resource_key__: string;
|
||||
__links__: object;
|
||||
__actions__: object;
|
||||
total: number;
|
||||
items: AndroidObject[];
|
||||
__class__: string;
|
||||
__href__: string;
|
||||
__resource_key__: string;
|
||||
__links__: object;
|
||||
__actions__: object;
|
||||
total: number;
|
||||
items: AndroidObject[];
|
||||
}
|
||||
|
||||
export interface AndroidObject {
|
||||
__class__: string;
|
||||
__href__: string;
|
||||
__links__: Links;
|
||||
__actions__: Actions;
|
||||
id: string;
|
||||
external_id: string;
|
||||
channel_id: string;
|
||||
title: string;
|
||||
description: string;
|
||||
promo_title: string;
|
||||
promo_description: string;
|
||||
type: string;
|
||||
slug: string;
|
||||
slug_title: string;
|
||||
images: Images;
|
||||
movie_listing_metadata?: MovieListingMetadata;
|
||||
movie_metadata?: MovieMetadata;
|
||||
playback?: string;
|
||||
episode_metadata?: EpisodeMetadata;
|
||||
streams_link?: string;
|
||||
season_metadata?: SeasonMetadata;
|
||||
linked_resource_key: string;
|
||||
isSelected?: boolean;
|
||||
f_num: string;
|
||||
s_num: string;
|
||||
__class__: string;
|
||||
__href__: string;
|
||||
__links__: Links;
|
||||
__actions__: Actions;
|
||||
id: string;
|
||||
external_id: string;
|
||||
channel_id: string;
|
||||
title: string;
|
||||
description: string;
|
||||
promo_title: string;
|
||||
promo_description: string;
|
||||
type: string;
|
||||
slug: string;
|
||||
slug_title: string;
|
||||
images: Images;
|
||||
movie_listing_metadata?: MovieListingMetadata;
|
||||
movie_metadata?: MovieMetadata;
|
||||
playback?: string;
|
||||
episode_metadata?: EpisodeMetadata;
|
||||
streams_link?: string;
|
||||
season_metadata?: SeasonMetadata;
|
||||
linked_resource_key: string;
|
||||
isSelected?: boolean;
|
||||
f_num: string;
|
||||
s_num: string;
|
||||
}
|
||||
|
||||
export interface Links {
|
||||
'episode/season': LinkData;
|
||||
'episode/series': LinkData;
|
||||
resource: LinkData;
|
||||
'resource/channel': LinkData;
|
||||
streams: LinkData;
|
||||
'episode/season': LinkData;
|
||||
'episode/series': LinkData;
|
||||
resource: LinkData;
|
||||
'resource/channel': LinkData;
|
||||
streams: LinkData;
|
||||
}
|
||||
|
||||
export interface LinkData {
|
||||
href: string;
|
||||
href: string;
|
||||
}
|
||||
|
||||
export interface EpisodeMetadata {
|
||||
audio_locale: Locale;
|
||||
availability_ends: Date;
|
||||
availability_notes: string;
|
||||
availability_starts: Date;
|
||||
available_date: null;
|
||||
available_offline: boolean;
|
||||
closed_captions_available: boolean;
|
||||
duration_ms: number;
|
||||
eligible_region: string;
|
||||
episode: string;
|
||||
episode_air_date: Date;
|
||||
episode_number: number;
|
||||
extended_maturity_rating: Record<unknown>;
|
||||
free_available_date: Date;
|
||||
identifier: string;
|
||||
is_clip: boolean;
|
||||
is_dubbed: boolean;
|
||||
is_mature: boolean;
|
||||
is_premium_only: boolean;
|
||||
is_subbed: boolean;
|
||||
mature_blocked: boolean;
|
||||
maturity_ratings: string[];
|
||||
premium_available_date: Date;
|
||||
premium_date: null;
|
||||
season_id: string;
|
||||
season_number: number;
|
||||
season_slug_title: string;
|
||||
season_title: string;
|
||||
sequence_number: number;
|
||||
series_id: string;
|
||||
series_slug_title: string;
|
||||
series_title: string;
|
||||
subtitle_locales: Locale[];
|
||||
tenant_categories?: string[];
|
||||
upload_date: Date;
|
||||
versions: EpisodeMetadataVersion[];
|
||||
audio_locale: Locale;
|
||||
availability_ends: Date;
|
||||
availability_notes: string;
|
||||
availability_starts: Date;
|
||||
available_date: null;
|
||||
available_offline: boolean;
|
||||
closed_captions_available: boolean;
|
||||
duration_ms: number;
|
||||
eligible_region: string;
|
||||
episode: string;
|
||||
episode_air_date: Date;
|
||||
episode_number: number;
|
||||
extended_maturity_rating: Record<unknown>;
|
||||
free_available_date: Date;
|
||||
identifier: string;
|
||||
is_clip: boolean;
|
||||
is_dubbed: boolean;
|
||||
is_mature: boolean;
|
||||
is_premium_only: boolean;
|
||||
is_subbed: boolean;
|
||||
mature_blocked: boolean;
|
||||
maturity_ratings: string[];
|
||||
premium_available_date: Date;
|
||||
premium_date: null;
|
||||
season_id: string;
|
||||
season_number: number;
|
||||
season_slug_title: string;
|
||||
season_title: string;
|
||||
sequence_number: number;
|
||||
series_id: string;
|
||||
series_slug_title: string;
|
||||
series_title: string;
|
||||
subtitle_locales: Locale[];
|
||||
tenant_categories?: string[];
|
||||
upload_date: Date;
|
||||
versions: EpisodeMetadataVersion[];
|
||||
}
|
||||
|
||||
export interface MovieListingMetadata {
|
||||
availability_notes: string;
|
||||
available_date: null;
|
||||
available_offline: boolean;
|
||||
duration_ms: number;
|
||||
extended_description: string;
|
||||
extended_maturity_rating: Record<unknown>;
|
||||
first_movie_id: string;
|
||||
free_available_date: Date;
|
||||
is_dubbed: boolean;
|
||||
is_mature: boolean;
|
||||
is_premium_only: boolean;
|
||||
is_subbed: boolean;
|
||||
mature_blocked: boolean;
|
||||
maturity_ratings: string[];
|
||||
movie_release_year: number;
|
||||
premium_available_date: Date;
|
||||
premium_date: null;
|
||||
subtitle_locales: Locale[];
|
||||
tenant_categories: string[];
|
||||
availability_notes: string;
|
||||
available_date: null;
|
||||
available_offline: boolean;
|
||||
duration_ms: number;
|
||||
extended_description: string;
|
||||
extended_maturity_rating: Record<unknown>;
|
||||
first_movie_id: string;
|
||||
free_available_date: Date;
|
||||
is_dubbed: boolean;
|
||||
is_mature: boolean;
|
||||
is_premium_only: boolean;
|
||||
is_subbed: boolean;
|
||||
mature_blocked: boolean;
|
||||
maturity_ratings: string[];
|
||||
movie_release_year: number;
|
||||
premium_available_date: Date;
|
||||
premium_date: null;
|
||||
subtitle_locales: Locale[];
|
||||
tenant_categories: string[];
|
||||
}
|
||||
|
||||
export interface MovieMetadata {
|
||||
availability_notes: string;
|
||||
available_offline: boolean;
|
||||
closed_captions_available: boolean;
|
||||
duration_ms: number;
|
||||
extended_maturity_rating: Record<unknown>;
|
||||
is_dubbed: boolean;
|
||||
is_mature: boolean;
|
||||
is_premium_only: boolean;
|
||||
is_subbed: boolean;
|
||||
mature_blocked: boolean;
|
||||
maturity_ratings: string[];
|
||||
movie_listing_id: string;
|
||||
movie_listing_slug_title: string;
|
||||
movie_listing_title: string;
|
||||
availability_notes: string;
|
||||
available_offline: boolean;
|
||||
closed_captions_available: boolean;
|
||||
duration_ms: number;
|
||||
extended_maturity_rating: Record<unknown>;
|
||||
is_dubbed: boolean;
|
||||
is_mature: boolean;
|
||||
is_premium_only: boolean;
|
||||
is_subbed: boolean;
|
||||
mature_blocked: boolean;
|
||||
maturity_ratings: string[];
|
||||
movie_listing_id: string;
|
||||
movie_listing_slug_title: string;
|
||||
movie_listing_title: string;
|
||||
}
|
||||
|
||||
export interface SeasonMetadata {
|
||||
audio_locale: Locale;
|
||||
audio_locales: Locale[];
|
||||
extended_maturity_rating: Record<unknown>;
|
||||
identifier: string;
|
||||
is_mature: boolean;
|
||||
mature_blocked: boolean;
|
||||
maturity_ratings: string[];
|
||||
season_display_number: string;
|
||||
season_sequence_number: number;
|
||||
subtitle_locales: Locale[];
|
||||
versions: SeasonMetadataVersion[];
|
||||
audio_locale: Locale;
|
||||
audio_locales: Locale[];
|
||||
extended_maturity_rating: Record<unknown>;
|
||||
identifier: string;
|
||||
is_mature: boolean;
|
||||
mature_blocked: boolean;
|
||||
maturity_ratings: string[];
|
||||
season_display_number: string;
|
||||
season_sequence_number: number;
|
||||
subtitle_locales: Locale[];
|
||||
versions: SeasonMetadataVersion[];
|
||||
}
|
||||
|
||||
export interface SeasonMetadataVersion {
|
||||
audio_locale: Locale;
|
||||
guid: string;
|
||||
original: boolean;
|
||||
variant: string;
|
||||
audio_locale: Locale;
|
||||
guid: string;
|
||||
original: boolean;
|
||||
variant: string;
|
||||
}
|
||||
export interface SeriesMetadata {
|
||||
audio_locales: Locale[];
|
||||
availability_notes: string;
|
||||
episode_count: number;
|
||||
extended_description: string;
|
||||
extended_maturity_rating: Record<unknown>;
|
||||
is_dubbed: boolean;
|
||||
is_mature: boolean;
|
||||
is_simulcast: boolean;
|
||||
is_subbed: boolean;
|
||||
mature_blocked: boolean;
|
||||
maturity_ratings: string[];
|
||||
season_count: number;
|
||||
series_launch_year: number;
|
||||
subtitle_locales: Locale[];
|
||||
tenant_categories?: string[];
|
||||
audio_locales: Locale[];
|
||||
availability_notes: string;
|
||||
episode_count: number;
|
||||
extended_description: string;
|
||||
extended_maturity_rating: Record<unknown>;
|
||||
is_dubbed: boolean;
|
||||
is_mature: boolean;
|
||||
is_simulcast: boolean;
|
||||
is_subbed: boolean;
|
||||
mature_blocked: boolean;
|
||||
maturity_ratings: string[];
|
||||
season_count: number;
|
||||
series_launch_year: number;
|
||||
subtitle_locales: Locale[];
|
||||
tenant_categories?: string[];
|
||||
}
|
||||
|
||||
export enum Locale {
|
||||
enUS = 'en-US',
|
||||
esLA = 'es-LA',
|
||||
es419 = 'es-419',
|
||||
esES = 'es-ES',
|
||||
ptBR = 'pt-BR',
|
||||
frFR = 'fr-FR',
|
||||
deDE = 'de-DE',
|
||||
arME = 'ar-ME',
|
||||
arSA = 'ar-SA',
|
||||
itIT = 'it-IT',
|
||||
ruRU = 'ru-RU',
|
||||
trTR = 'tr-TR',
|
||||
hiIN = 'hi-IN',
|
||||
zhCN = 'zh-CN',
|
||||
koKR = 'ko-KR',
|
||||
jaJP = 'ja-JP',
|
||||
enUS = 'en-US',
|
||||
esLA = 'es-LA',
|
||||
es419 = 'es-419',
|
||||
esES = 'es-ES',
|
||||
ptBR = 'pt-BR',
|
||||
frFR = 'fr-FR',
|
||||
deDE = 'de-DE',
|
||||
arME = 'ar-ME',
|
||||
arSA = 'ar-SA',
|
||||
itIT = 'it-IT',
|
||||
ruRU = 'ru-RU',
|
||||
trTR = 'tr-TR',
|
||||
hiIN = 'hi-IN',
|
||||
zhCN = 'zh-CN',
|
||||
koKR = 'ko-KR',
|
||||
jaJP = 'ja-JP',
|
||||
}
|
||||
128
@types/crunchyAndroidStreams.d.ts
vendored
128
@types/crunchyAndroidStreams.d.ts
vendored
|
|
@ -1,93 +1,93 @@
|
|||
export interface CrunchyAndroidStreams {
|
||||
__class__: string;
|
||||
__href__: string;
|
||||
__resource_key__: string;
|
||||
__links__: Links;
|
||||
__actions__: Record<unknown, unknown>;
|
||||
media_id: string;
|
||||
audio_locale: Locale;
|
||||
subtitles: Subtitles;
|
||||
closed_captions: Subtitles;
|
||||
streams: Streams;
|
||||
bifs: string[];
|
||||
versions: Version[];
|
||||
captions: Record<unknown, unknown>;
|
||||
__class__: string;
|
||||
__href__: string;
|
||||
__resource_key__: string;
|
||||
__links__: Links;
|
||||
__actions__: Record<unknown, unknown>;
|
||||
media_id: string;
|
||||
audio_locale: Locale;
|
||||
subtitles: Subtitles;
|
||||
closed_captions: Subtitles;
|
||||
streams: Streams;
|
||||
bifs: string[];
|
||||
versions: Version[];
|
||||
captions: Record<unknown, unknown>;
|
||||
}
|
||||
|
||||
export interface Subtitles {
|
||||
'': Subtitle;
|
||||
'en-US'?: Subtitle;
|
||||
'es-LA'?: Subtitle;
|
||||
'es-419'?: Subtitle;
|
||||
'es-ES'?: Subtitle;
|
||||
'pt-BR'?: Subtitle;
|
||||
'fr-FR'?: Subtitle;
|
||||
'de-DE'?: Subtitle;
|
||||
'ar-ME'?: Subtitle;
|
||||
'ar-SA'?: Subtitle;
|
||||
'it-IT'?: Subtitle;
|
||||
'ru-RU'?: Subtitle;
|
||||
'tr-TR'?: Subtitle;
|
||||
'hi-IN'?: Subtitle;
|
||||
'zh-CN'?: Subtitle;
|
||||
'ko-KR'?: Subtitle;
|
||||
'ja-JP'?: Subtitle;
|
||||
'': Subtitle;
|
||||
'en-US'?: Subtitle;
|
||||
'es-LA'?: Subtitle;
|
||||
'es-419'?: Subtitle;
|
||||
'es-ES'?: Subtitle;
|
||||
'pt-BR'?: Subtitle;
|
||||
'fr-FR'?: Subtitle;
|
||||
'de-DE'?: Subtitle;
|
||||
'ar-ME'?: Subtitle;
|
||||
'ar-SA'?: Subtitle;
|
||||
'it-IT'?: Subtitle;
|
||||
'ru-RU'?: Subtitle;
|
||||
'tr-TR'?: Subtitle;
|
||||
'hi-IN'?: Subtitle;
|
||||
'zh-CN'?: Subtitle;
|
||||
'ko-KR'?: Subtitle;
|
||||
'ja-JP'?: Subtitle;
|
||||
}
|
||||
|
||||
export interface Links {
|
||||
resource: Resource;
|
||||
resource: Resource;
|
||||
}
|
||||
|
||||
export interface Resource {
|
||||
href: string;
|
||||
href: string;
|
||||
}
|
||||
|
||||
export interface Streams {
|
||||
[key: string]: { [key: string]: Download };
|
||||
[key: string]: { [key: string]: Download };
|
||||
}
|
||||
|
||||
export interface Download {
|
||||
hardsub_locale: Locale;
|
||||
hardsub_lang?: string;
|
||||
url: string;
|
||||
hardsub_locale: Locale;
|
||||
hardsub_lang?: string;
|
||||
url: string;
|
||||
}
|
||||
|
||||
export interface Urls {
|
||||
'': Download;
|
||||
'': Download;
|
||||
}
|
||||
|
||||
export interface Subtitle {
|
||||
locale: Locale;
|
||||
url: string;
|
||||
format: string;
|
||||
locale: Locale;
|
||||
url: string;
|
||||
format: string;
|
||||
}
|
||||
|
||||
export interface Version {
|
||||
audio_locale: Locale;
|
||||
guid: string;
|
||||
original: boolean;
|
||||
variant: string;
|
||||
season_guid: string;
|
||||
media_guid: string;
|
||||
is_premium_only: boolean;
|
||||
audio_locale: Locale;
|
||||
guid: string;
|
||||
original: boolean;
|
||||
variant: string;
|
||||
season_guid: string;
|
||||
media_guid: string;
|
||||
is_premium_only: boolean;
|
||||
}
|
||||
|
||||
export enum Locale {
|
||||
default = '',
|
||||
enUS = 'en-US',
|
||||
esLA = 'es-LA',
|
||||
es419 = 'es-419',
|
||||
esES = 'es-ES',
|
||||
ptBR = 'pt-BR',
|
||||
frFR = 'fr-FR',
|
||||
deDE = 'de-DE',
|
||||
arME = 'ar-ME',
|
||||
arSA = 'ar-SA',
|
||||
itIT = 'it-IT',
|
||||
ruRU = 'ru-RU',
|
||||
trTR = 'tr-TR',
|
||||
hiIN = 'hi-IN',
|
||||
zhCN = 'zh-CN',
|
||||
koKR = 'ko-KR',
|
||||
jaJP = 'ja-JP',
|
||||
default = '',
|
||||
enUS = 'en-US',
|
||||
esLA = 'es-LA',
|
||||
es419 = 'es-419',
|
||||
esES = 'es-ES',
|
||||
ptBR = 'pt-BR',
|
||||
frFR = 'fr-FR',
|
||||
deDE = 'de-DE',
|
||||
arME = 'ar-ME',
|
||||
arSA = 'ar-SA',
|
||||
itIT = 'it-IT',
|
||||
ruRU = 'ru-RU',
|
||||
trTR = 'tr-TR',
|
||||
hiIN = 'hi-IN',
|
||||
zhCN = 'zh-CN',
|
||||
koKR = 'ko-KR',
|
||||
jaJP = 'ja-JP',
|
||||
}
|
||||
200
@types/crunchyEpisodeList.d.ts
vendored
200
@types/crunchyEpisodeList.d.ts
vendored
|
|
@ -1,134 +1,134 @@
|
|||
import { Links } from './crunchyAndroidEpisodes';
|
||||
|
||||
export interface CrunchyEpisodeList {
|
||||
total: number;
|
||||
data: CrunchyEpisode[];
|
||||
meta: Meta;
|
||||
total: number;
|
||||
data: CrunchyEpisode[];
|
||||
meta: Meta;
|
||||
}
|
||||
|
||||
export interface CrunchyEpisode {
|
||||
next_episode_id: string;
|
||||
series_id: string;
|
||||
season_number: number;
|
||||
next_episode_title: string;
|
||||
availability_notes: string;
|
||||
duration_ms: number;
|
||||
series_slug_title: string;
|
||||
series_title: string;
|
||||
is_dubbed: boolean;
|
||||
versions: Version[] | null;
|
||||
identifier: string;
|
||||
sequence_number: number;
|
||||
eligible_region: Record<unknown>;
|
||||
availability_starts: Date;
|
||||
images: Images;
|
||||
season_id: string;
|
||||
seo_title: string;
|
||||
is_premium_only: boolean;
|
||||
extended_maturity_rating: Record<unknown>;
|
||||
title: string;
|
||||
production_episode_id: string;
|
||||
premium_available_date: Date;
|
||||
season_title: string;
|
||||
seo_description: string;
|
||||
audio_locale: Locale;
|
||||
id: string;
|
||||
media_type: MediaType;
|
||||
availability_ends: Date;
|
||||
free_available_date: Date;
|
||||
playback: string;
|
||||
channel_id: ChannelID;
|
||||
episode: string;
|
||||
is_mature: boolean;
|
||||
listing_id: string;
|
||||
episode_air_date: Date;
|
||||
slug: string;
|
||||
available_date: Date;
|
||||
subtitle_locales: Locale[];
|
||||
slug_title: string;
|
||||
available_offline: boolean;
|
||||
description: string;
|
||||
is_subbed: boolean;
|
||||
premium_date: Date;
|
||||
upload_date: Date;
|
||||
season_slug_title: string;
|
||||
closed_captions_available: boolean;
|
||||
episode_number: number;
|
||||
season_tags: any[];
|
||||
maturity_ratings: MaturityRating[];
|
||||
streams_link?: string;
|
||||
mature_blocked: boolean;
|
||||
is_clip: boolean;
|
||||
hd_flag: boolean;
|
||||
hide_season_title?: boolean;
|
||||
hide_season_number?: boolean;
|
||||
isSelected?: boolean;
|
||||
seq_id: string;
|
||||
__links__?: Links;
|
||||
next_episode_id: string;
|
||||
series_id: string;
|
||||
season_number: number;
|
||||
next_episode_title: string;
|
||||
availability_notes: string;
|
||||
duration_ms: number;
|
||||
series_slug_title: string;
|
||||
series_title: string;
|
||||
is_dubbed: boolean;
|
||||
versions: Version[] | null;
|
||||
identifier: string;
|
||||
sequence_number: number;
|
||||
eligible_region: Record<unknown>;
|
||||
availability_starts: Date;
|
||||
images: Images;
|
||||
season_id: string;
|
||||
seo_title: string;
|
||||
is_premium_only: boolean;
|
||||
extended_maturity_rating: Record<unknown>;
|
||||
title: string;
|
||||
production_episode_id: string;
|
||||
premium_available_date: Date;
|
||||
season_title: string;
|
||||
seo_description: string;
|
||||
audio_locale: Locale;
|
||||
id: string;
|
||||
media_type: MediaType;
|
||||
availability_ends: Date;
|
||||
free_available_date: Date;
|
||||
playback: string;
|
||||
channel_id: ChannelID;
|
||||
episode: string;
|
||||
is_mature: boolean;
|
||||
listing_id: string;
|
||||
episode_air_date: Date;
|
||||
slug: string;
|
||||
available_date: Date;
|
||||
subtitle_locales: Locale[];
|
||||
slug_title: string;
|
||||
available_offline: boolean;
|
||||
description: string;
|
||||
is_subbed: boolean;
|
||||
premium_date: Date;
|
||||
upload_date: Date;
|
||||
season_slug_title: string;
|
||||
closed_captions_available: boolean;
|
||||
episode_number: number;
|
||||
season_tags: any[];
|
||||
maturity_ratings: MaturityRating[];
|
||||
streams_link?: string;
|
||||
mature_blocked: boolean;
|
||||
is_clip: boolean;
|
||||
hd_flag: boolean;
|
||||
hide_season_title?: boolean;
|
||||
hide_season_number?: boolean;
|
||||
isSelected?: boolean;
|
||||
seq_id: string;
|
||||
__links__?: Links;
|
||||
}
|
||||
|
||||
export enum Locale {
|
||||
enUS = 'en-US',
|
||||
esLA = 'es-LA',
|
||||
es419 = 'es-419',
|
||||
esES = 'es-ES',
|
||||
ptBR = 'pt-BR',
|
||||
frFR = 'fr-FR',
|
||||
deDE = 'de-DE',
|
||||
arME = 'ar-ME',
|
||||
arSA = 'ar-SA',
|
||||
itIT = 'it-IT',
|
||||
ruRU = 'ru-RU',
|
||||
trTR = 'tr-TR',
|
||||
hiIN = 'hi-IN',
|
||||
zhCN = 'zh-CN',
|
||||
koKR = 'ko-KR',
|
||||
jaJP = 'ja-JP',
|
||||
enUS = 'en-US',
|
||||
esLA = 'es-LA',
|
||||
es419 = 'es-419',
|
||||
esES = 'es-ES',
|
||||
ptBR = 'pt-BR',
|
||||
frFR = 'fr-FR',
|
||||
deDE = 'de-DE',
|
||||
arME = 'ar-ME',
|
||||
arSA = 'ar-SA',
|
||||
itIT = 'it-IT',
|
||||
ruRU = 'ru-RU',
|
||||
trTR = 'tr-TR',
|
||||
hiIN = 'hi-IN',
|
||||
zhCN = 'zh-CN',
|
||||
koKR = 'ko-KR',
|
||||
jaJP = 'ja-JP',
|
||||
}
|
||||
|
||||
export enum ChannelID {
|
||||
Crunchyroll = 'crunchyroll',
|
||||
Crunchyroll = 'crunchyroll',
|
||||
}
|
||||
|
||||
export interface Images {
|
||||
poster_tall?: Array<Image[]>;
|
||||
poster_wide?: Array<Image[]>;
|
||||
promo_image?: Array<Image[]>;
|
||||
thumbnail?: Array<Image[]>;
|
||||
poster_tall?: Array<Image[]>;
|
||||
poster_wide?: Array<Image[]>;
|
||||
promo_image?: Array<Image[]>;
|
||||
thumbnail?: Array<Image[]>;
|
||||
}
|
||||
|
||||
export interface Image {
|
||||
height: number;
|
||||
source: string;
|
||||
type: ImageType;
|
||||
width: number;
|
||||
height: number;
|
||||
source: string;
|
||||
type: ImageType;
|
||||
width: number;
|
||||
}
|
||||
|
||||
export enum ImageType {
|
||||
PosterTall = 'poster_tall',
|
||||
PosterWide = 'poster_wide',
|
||||
PromoImage = 'promo_image',
|
||||
Thumbnail = 'thumbnail',
|
||||
PosterTall = 'poster_tall',
|
||||
PosterWide = 'poster_wide',
|
||||
PromoImage = 'promo_image',
|
||||
Thumbnail = 'thumbnail',
|
||||
}
|
||||
|
||||
export enum MaturityRating {
|
||||
Tv14 = 'TV-14',
|
||||
Tv14 = 'TV-14',
|
||||
}
|
||||
|
||||
export enum MediaType {
|
||||
Episode = 'episode',
|
||||
Episode = 'episode',
|
||||
}
|
||||
|
||||
export interface Version {
|
||||
audio_locale: Locale;
|
||||
guid: string;
|
||||
is_premium_only: boolean;
|
||||
media_guid: string;
|
||||
original: boolean;
|
||||
season_guid: string;
|
||||
variant: string;
|
||||
audio_locale: Locale;
|
||||
guid: string;
|
||||
is_premium_only: boolean;
|
||||
media_guid: string;
|
||||
original: boolean;
|
||||
season_guid: string;
|
||||
variant: string;
|
||||
}
|
||||
|
||||
export interface Meta {
|
||||
versions_considered?: boolean;
|
||||
versions_considered?: boolean;
|
||||
}
|
||||
274
@types/crunchySearch.d.ts
vendored
274
@types/crunchySearch.d.ts
vendored
|
|
@ -1,183 +1,183 @@
|
|||
// Generated by https://quicktype.io
|
||||
|
||||
export interface CrunchySearch {
|
||||
total: number;
|
||||
data: CrunchySearchData[];
|
||||
meta: Record<string, unknown>;
|
||||
total: number;
|
||||
data: CrunchySearchData[];
|
||||
meta: Record<string, unknown>;
|
||||
}
|
||||
|
||||
export interface CrunchySearchData {
|
||||
type: string;
|
||||
count: number;
|
||||
items: CrunchySearchItem[];
|
||||
type: string;
|
||||
count: number;
|
||||
items: CrunchySearchItem[];
|
||||
}
|
||||
|
||||
export interface CrunchySearchItem {
|
||||
title: string;
|
||||
images: Images;
|
||||
series_metadata?: SeriesMetadata;
|
||||
promo_description: string;
|
||||
external_id: string;
|
||||
slug: string;
|
||||
new: boolean;
|
||||
slug_title: string;
|
||||
channel_id: ChannelID;
|
||||
description: string;
|
||||
linked_resource_key: string;
|
||||
type: ItemType;
|
||||
id: string;
|
||||
promo_title: string;
|
||||
search_metadata: SearchMetadata;
|
||||
movie_listing_metadata?: MovieListingMetadata;
|
||||
playback?: string;
|
||||
streams_link?: string;
|
||||
episode_metadata?: EpisodeMetadata;
|
||||
title: string;
|
||||
images: Images;
|
||||
series_metadata?: SeriesMetadata;
|
||||
promo_description: string;
|
||||
external_id: string;
|
||||
slug: string;
|
||||
new: boolean;
|
||||
slug_title: string;
|
||||
channel_id: ChannelID;
|
||||
description: string;
|
||||
linked_resource_key: string;
|
||||
type: ItemType;
|
||||
id: string;
|
||||
promo_title: string;
|
||||
search_metadata: SearchMetadata;
|
||||
movie_listing_metadata?: MovieListingMetadata;
|
||||
playback?: string;
|
||||
streams_link?: string;
|
||||
episode_metadata?: EpisodeMetadata;
|
||||
}
|
||||
|
||||
export enum ChannelID {
|
||||
Crunchyroll = 'crunchyroll',
|
||||
Crunchyroll = 'crunchyroll',
|
||||
}
|
||||
|
||||
export interface EpisodeMetadata {
|
||||
audio_locale: Locale;
|
||||
availability_ends: Date;
|
||||
availability_notes: string;
|
||||
availability_starts: Date;
|
||||
available_date: null;
|
||||
available_offline: boolean;
|
||||
closed_captions_available: boolean;
|
||||
duration_ms: number;
|
||||
eligible_region: string[];
|
||||
episode: string;
|
||||
episode_air_date: Date;
|
||||
episode_number: number;
|
||||
extended_maturity_rating: Record<unknown>;
|
||||
free_available_date: Date;
|
||||
identifier: string;
|
||||
is_clip: boolean;
|
||||
is_dubbed: boolean;
|
||||
is_mature: boolean;
|
||||
is_premium_only: boolean;
|
||||
is_subbed: boolean;
|
||||
mature_blocked: boolean;
|
||||
maturity_ratings: MaturityRating[];
|
||||
premium_available_date: Date;
|
||||
premium_date: null;
|
||||
season_id: string;
|
||||
season_number: number;
|
||||
season_slug_title: string;
|
||||
season_title: string;
|
||||
sequence_number: number;
|
||||
series_id: string;
|
||||
series_slug_title: string;
|
||||
series_title: string;
|
||||
subtitle_locales: Locale[];
|
||||
upload_date: Date;
|
||||
versions: Version[] | null;
|
||||
tenant_categories?: string[];
|
||||
audio_locale: Locale;
|
||||
availability_ends: Date;
|
||||
availability_notes: string;
|
||||
availability_starts: Date;
|
||||
available_date: null;
|
||||
available_offline: boolean;
|
||||
closed_captions_available: boolean;
|
||||
duration_ms: number;
|
||||
eligible_region: string[];
|
||||
episode: string;
|
||||
episode_air_date: Date;
|
||||
episode_number: number;
|
||||
extended_maturity_rating: Record<unknown>;
|
||||
free_available_date: Date;
|
||||
identifier: string;
|
||||
is_clip: boolean;
|
||||
is_dubbed: boolean;
|
||||
is_mature: boolean;
|
||||
is_premium_only: boolean;
|
||||
is_subbed: boolean;
|
||||
mature_blocked: boolean;
|
||||
maturity_ratings: MaturityRating[];
|
||||
premium_available_date: Date;
|
||||
premium_date: null;
|
||||
season_id: string;
|
||||
season_number: number;
|
||||
season_slug_title: string;
|
||||
season_title: string;
|
||||
sequence_number: number;
|
||||
series_id: string;
|
||||
series_slug_title: string;
|
||||
series_title: string;
|
||||
subtitle_locales: Locale[];
|
||||
upload_date: Date;
|
||||
versions: Version[] | null;
|
||||
tenant_categories?: string[];
|
||||
}
|
||||
|
||||
export enum Locale {
|
||||
enUS = 'en-US',
|
||||
esLA = 'es-LA',
|
||||
es419 = 'es-419',
|
||||
esES = 'es-ES',
|
||||
ptBR = 'pt-BR',
|
||||
frFR = 'fr-FR',
|
||||
deDE = 'de-DE',
|
||||
arME = 'ar-ME',
|
||||
arSA = 'ar-SA',
|
||||
itIT = 'it-IT',
|
||||
ruRU = 'ru-RU',
|
||||
trTR = 'tr-TR',
|
||||
hiIN = 'hi-IN',
|
||||
zhCN = 'zh-CN',
|
||||
koKR = 'ko-KR',
|
||||
jaJP = 'ja-JP',
|
||||
enUS = 'en-US',
|
||||
esLA = 'es-LA',
|
||||
es419 = 'es-419',
|
||||
esES = 'es-ES',
|
||||
ptBR = 'pt-BR',
|
||||
frFR = 'fr-FR',
|
||||
deDE = 'de-DE',
|
||||
arME = 'ar-ME',
|
||||
arSA = 'ar-SA',
|
||||
itIT = 'it-IT',
|
||||
ruRU = 'ru-RU',
|
||||
trTR = 'tr-TR',
|
||||
hiIN = 'hi-IN',
|
||||
zhCN = 'zh-CN',
|
||||
koKR = 'ko-KR',
|
||||
jaJP = 'ja-JP',
|
||||
}
|
||||
|
||||
export enum MaturityRating {
|
||||
Tv14 = 'TV-14',
|
||||
TvMa = 'TV-MA',
|
||||
Tv14 = 'TV-14',
|
||||
TvMa = 'TV-MA',
|
||||
}
|
||||
|
||||
export interface Version {
|
||||
audio_locale: Locale;
|
||||
guid: string;
|
||||
is_premium_only: boolean;
|
||||
media_guid: string;
|
||||
original: boolean;
|
||||
season_guid: string;
|
||||
variant: string;
|
||||
audio_locale: Locale;
|
||||
guid: string;
|
||||
is_premium_only: boolean;
|
||||
media_guid: string;
|
||||
original: boolean;
|
||||
season_guid: string;
|
||||
variant: string;
|
||||
}
|
||||
|
||||
export interface Images {
|
||||
poster_tall?: Array<Image[]>;
|
||||
poster_wide?: Array<Image[]>;
|
||||
promo_image?: Array<Image[]>;
|
||||
thumbnail?: Array<Image[]>;
|
||||
poster_tall?: Array<Image[]>;
|
||||
poster_wide?: Array<Image[]>;
|
||||
promo_image?: Array<Image[]>;
|
||||
thumbnail?: Array<Image[]>;
|
||||
}
|
||||
|
||||
export interface Image {
|
||||
height: number;
|
||||
source: string;
|
||||
type: ImageType;
|
||||
width: number;
|
||||
height: number;
|
||||
source: string;
|
||||
type: ImageType;
|
||||
width: number;
|
||||
}
|
||||
|
||||
export enum ImageType {
|
||||
PosterTall = 'poster_tall',
|
||||
PosterWide = 'poster_wide',
|
||||
PromoImage = 'promo_image',
|
||||
Thumbnail = 'thumbnail',
|
||||
PosterTall = 'poster_tall',
|
||||
PosterWide = 'poster_wide',
|
||||
PromoImage = 'promo_image',
|
||||
Thumbnail = 'thumbnail',
|
||||
}
|
||||
|
||||
export interface MovieListingMetadata {
|
||||
availability_notes: string;
|
||||
available_date: null;
|
||||
available_offline: boolean;
|
||||
duration_ms: number;
|
||||
extended_description: string;
|
||||
extended_maturity_rating: Record<unknown>;
|
||||
first_movie_id: string;
|
||||
free_available_date: Date;
|
||||
is_dubbed: boolean;
|
||||
is_mature: boolean;
|
||||
is_premium_only: boolean;
|
||||
is_subbed: boolean;
|
||||
mature_blocked: boolean;
|
||||
maturity_ratings: string[];
|
||||
movie_release_year: number;
|
||||
premium_available_date: Date;
|
||||
premium_date: null;
|
||||
subtitle_locales: any[];
|
||||
tenant_categories: string[];
|
||||
availability_notes: string;
|
||||
available_date: null;
|
||||
available_offline: boolean;
|
||||
duration_ms: number;
|
||||
extended_description: string;
|
||||
extended_maturity_rating: Record<unknown>;
|
||||
first_movie_id: string;
|
||||
free_available_date: Date;
|
||||
is_dubbed: boolean;
|
||||
is_mature: boolean;
|
||||
is_premium_only: boolean;
|
||||
is_subbed: boolean;
|
||||
mature_blocked: boolean;
|
||||
maturity_ratings: string[];
|
||||
movie_release_year: number;
|
||||
premium_available_date: Date;
|
||||
premium_date: null;
|
||||
subtitle_locales: any[];
|
||||
tenant_categories: string[];
|
||||
}
|
||||
|
||||
export interface SearchMetadata {
|
||||
score: number;
|
||||
score: number;
|
||||
}
|
||||
|
||||
export interface SeriesMetadata {
|
||||
audio_locales: Locale[];
|
||||
availability_notes: string;
|
||||
episode_count: number;
|
||||
extended_description: string;
|
||||
extended_maturity_rating: Record<unknown>;
|
||||
is_dubbed: boolean;
|
||||
is_mature: boolean;
|
||||
is_simulcast: boolean;
|
||||
is_subbed: boolean;
|
||||
mature_blocked: boolean;
|
||||
maturity_ratings: MaturityRating[];
|
||||
season_count: number;
|
||||
series_launch_year: number;
|
||||
subtitle_locales: Locale[];
|
||||
tenant_categories?: string[];
|
||||
audio_locales: Locale[];
|
||||
availability_notes: string;
|
||||
episode_count: number;
|
||||
extended_description: string;
|
||||
extended_maturity_rating: Record<unknown>;
|
||||
is_dubbed: boolean;
|
||||
is_mature: boolean;
|
||||
is_simulcast: boolean;
|
||||
is_subbed: boolean;
|
||||
mature_blocked: boolean;
|
||||
maturity_ratings: MaturityRating[];
|
||||
season_count: number;
|
||||
series_launch_year: number;
|
||||
subtitle_locales: Locale[];
|
||||
tenant_categories?: string[];
|
||||
}
|
||||
|
||||
export enum ItemType {
|
||||
Episode = 'episode',
|
||||
MovieListing = 'movie_listing',
|
||||
Series = 'series',
|
||||
Episode = 'episode',
|
||||
MovieListing = 'movie_listing',
|
||||
Series = 'series',
|
||||
}
|
||||
344
@types/crunchyTypes.d.ts
vendored
344
@types/crunchyTypes.d.ts
vendored
|
|
@ -5,211 +5,211 @@ import { DownloadInfo } from './messageHandler';
|
|||
import { CrunchyVideoPlayStreams, CrunchyAudioPlayStreams } from './enums';
|
||||
|
||||
export type CrunchyDownloadOptions = {
|
||||
hslang: string,
|
||||
// kstream: number,
|
||||
cstream: keyof typeof CrunchyVideoPlayStreams,
|
||||
vstream: keyof typeof CrunchyVideoPlayStreams,
|
||||
astream: keyof typeof CrunchyAudioPlayStreams,
|
||||
tsd?: boolean,
|
||||
novids?: boolean,
|
||||
noaudio?: boolean,
|
||||
x: number,
|
||||
q: number,
|
||||
fileName: string,
|
||||
numbers: number,
|
||||
partsize: number,
|
||||
callbackMaker?: (data: DownloadInfo) => HLSCallback,
|
||||
timeout: number,
|
||||
waittime: number,
|
||||
fsRetryTime: number,
|
||||
dlsubs: string[],
|
||||
skipsubs: boolean,
|
||||
nosubs?: boolean,
|
||||
mp4: boolean,
|
||||
override: string[],
|
||||
videoTitle: string,
|
||||
force: 'Y'|'y'|'N'|'n'|'C'|'c',
|
||||
ffmpegOptions: string[],
|
||||
mkvmergeOptions: string[],
|
||||
defaultSub: LanguageItem,
|
||||
defaultAudio: LanguageItem,
|
||||
ccTag: string,
|
||||
dlVideoOnce: boolean,
|
||||
skipmux?: boolean,
|
||||
syncTiming: boolean,
|
||||
nocleanup: boolean,
|
||||
chapters: boolean,
|
||||
fontName: string | undefined,
|
||||
originalFontSize: boolean,
|
||||
fontSize: number,
|
||||
dubLang: string[],
|
||||
hslang: string,
|
||||
// kstream: number,
|
||||
cstream: keyof typeof CrunchyVideoPlayStreams,
|
||||
vstream: keyof typeof CrunchyVideoPlayStreams,
|
||||
astream: keyof typeof CrunchyAudioPlayStreams,
|
||||
tsd?: boolean,
|
||||
novids?: boolean,
|
||||
noaudio?: boolean,
|
||||
x: number,
|
||||
q: number,
|
||||
fileName: string,
|
||||
numbers: number,
|
||||
partsize: number,
|
||||
callbackMaker?: (data: DownloadInfo) => HLSCallback,
|
||||
timeout: number,
|
||||
waittime: number,
|
||||
fsRetryTime: number,
|
||||
dlsubs: string[],
|
||||
skipsubs: boolean,
|
||||
nosubs?: boolean,
|
||||
mp4: boolean,
|
||||
override: string[],
|
||||
videoTitle: string,
|
||||
force: 'Y'|'y'|'N'|'n'|'C'|'c',
|
||||
ffmpegOptions: string[],
|
||||
mkvmergeOptions: string[],
|
||||
defaultSub: LanguageItem,
|
||||
defaultAudio: LanguageItem,
|
||||
ccTag: string,
|
||||
dlVideoOnce: boolean,
|
||||
skipmux?: boolean,
|
||||
syncTiming: boolean,
|
||||
nocleanup: boolean,
|
||||
chapters: boolean,
|
||||
fontName: string | undefined,
|
||||
originalFontSize: boolean,
|
||||
fontSize: number,
|
||||
dubLang: string[],
|
||||
}
|
||||
|
||||
export type CrunchyMultiDownload = {
|
||||
absolute?: boolean,
|
||||
dubLang: string[],
|
||||
all?: boolean,
|
||||
but?: boolean,
|
||||
e?: string,
|
||||
s?: string
|
||||
absolute?: boolean,
|
||||
dubLang: string[],
|
||||
all?: boolean,
|
||||
but?: boolean,
|
||||
e?: string,
|
||||
s?: string
|
||||
}
|
||||
|
||||
export type CrunchyMuxOptions = {
|
||||
output: string,
|
||||
skipSubMux?: boolean
|
||||
keepAllVideos?: bolean
|
||||
novids?: boolean,
|
||||
mp4: boolean,
|
||||
forceMuxer?: 'ffmpeg'|'mkvmerge',
|
||||
nocleanup?: boolean,
|
||||
videoTitle: string,
|
||||
ffmpegOptions: string[],
|
||||
mkvmergeOptions: string[],
|
||||
defaultSub: LanguageItem,
|
||||
defaultAudio: LanguageItem,
|
||||
ccTag: string,
|
||||
syncTiming: boolean,
|
||||
output: string,
|
||||
skipSubMux?: boolean
|
||||
keepAllVideos?: bolean
|
||||
novids?: boolean,
|
||||
mp4: boolean,
|
||||
forceMuxer?: 'ffmpeg'|'mkvmerge',
|
||||
nocleanup?: boolean,
|
||||
videoTitle: string,
|
||||
ffmpegOptions: string[],
|
||||
mkvmergeOptions: string[],
|
||||
defaultSub: LanguageItem,
|
||||
defaultAudio: LanguageItem,
|
||||
ccTag: string,
|
||||
syncTiming: boolean,
|
||||
}
|
||||
|
||||
export type CrunchyEpMeta = {
|
||||
data: {
|
||||
mediaId: string,
|
||||
lang?: LanguageItem,
|
||||
playback?: string,
|
||||
versions?: EpisodeVersion[] | null,
|
||||
isSubbed: boolean,
|
||||
isDubbed: boolean,
|
||||
}[],
|
||||
seriesTitle: string,
|
||||
seasonTitle: string,
|
||||
episodeNumber: string,
|
||||
episodeTitle: string,
|
||||
seasonID: string,
|
||||
season: number,
|
||||
showID: string,
|
||||
e: string,
|
||||
image: string,
|
||||
data: {
|
||||
mediaId: string,
|
||||
lang?: LanguageItem,
|
||||
playback?: string,
|
||||
versions?: EpisodeVersion[] | null,
|
||||
isSubbed: boolean,
|
||||
isDubbed: boolean,
|
||||
}[],
|
||||
seriesTitle: string,
|
||||
seasonTitle: string,
|
||||
episodeNumber: string,
|
||||
episodeTitle: string,
|
||||
seasonID: string,
|
||||
season: number,
|
||||
showID: string,
|
||||
e: string,
|
||||
image: string,
|
||||
}
|
||||
|
||||
export type DownloadedMedia = {
|
||||
type: 'Video',
|
||||
lang: LanguageItem,
|
||||
path: string,
|
||||
isPrimary?: boolean
|
||||
type: 'Video',
|
||||
lang: LanguageItem,
|
||||
path: string,
|
||||
isPrimary?: boolean
|
||||
} | {
|
||||
type: 'Audio',
|
||||
lang: LanguageItem,
|
||||
path: string,
|
||||
isPrimary?: boolean
|
||||
type: 'Audio',
|
||||
lang: LanguageItem,
|
||||
path: string,
|
||||
isPrimary?: boolean
|
||||
} | {
|
||||
type: 'Chapters',
|
||||
lang: LanguageItem,
|
||||
path: string
|
||||
type: 'Chapters',
|
||||
lang: LanguageItem,
|
||||
path: string
|
||||
} | ({
|
||||
type: 'Subtitle',
|
||||
signs: boolean,
|
||||
cc: boolean
|
||||
type: 'Subtitle',
|
||||
signs: boolean,
|
||||
cc: boolean
|
||||
} & sxItem )
|
||||
|
||||
export type ParseItem = {
|
||||
__class__?: string;
|
||||
isSelected?: boolean,
|
||||
type?: string,
|
||||
id: string,
|
||||
title: string,
|
||||
playback?: string,
|
||||
season_number?: number|string,
|
||||
episode_number?: number|string,
|
||||
season_count?: number|string,
|
||||
is_premium_only?: boolean,
|
||||
hide_metadata?: boolean,
|
||||
seq_id?: string,
|
||||
f_num?: string,
|
||||
s_num?: string
|
||||
external_id?: string,
|
||||
ep_num?: string
|
||||
last_public?: string,
|
||||
subtitle_locales?: string[],
|
||||
availability_notes?: string,
|
||||
identifier?: string,
|
||||
versions?: Version[] | null,
|
||||
media_type?: string | null,
|
||||
movie_release_year?: number | null,
|
||||
__class__?: string;
|
||||
isSelected?: boolean,
|
||||
type?: string,
|
||||
id: string,
|
||||
title: string,
|
||||
playback?: string,
|
||||
season_number?: number|string,
|
||||
episode_number?: number|string,
|
||||
season_count?: number|string,
|
||||
is_premium_only?: boolean,
|
||||
hide_metadata?: boolean,
|
||||
seq_id?: string,
|
||||
f_num?: string,
|
||||
s_num?: string
|
||||
external_id?: string,
|
||||
ep_num?: string
|
||||
last_public?: string,
|
||||
subtitle_locales?: string[],
|
||||
availability_notes?: string,
|
||||
identifier?: string,
|
||||
versions?: Version[] | null,
|
||||
media_type?: string | null,
|
||||
movie_release_year?: number | null,
|
||||
}
|
||||
|
||||
export interface SeriesSearch {
|
||||
total: number;
|
||||
data: SeriesSearchItem[];
|
||||
meta: Meta;
|
||||
total: number;
|
||||
data: SeriesSearchItem[];
|
||||
meta: Meta;
|
||||
}
|
||||
|
||||
export interface SeriesSearchItem {
|
||||
description: string;
|
||||
seo_description: string;
|
||||
number_of_episodes: number;
|
||||
is_dubbed: boolean;
|
||||
identifier: string;
|
||||
channel_id: string;
|
||||
slug_title: string;
|
||||
season_sequence_number: number;
|
||||
season_tags: string[];
|
||||
extended_maturity_rating: Record<unknown>;
|
||||
is_mature: boolean;
|
||||
audio_locale: string;
|
||||
season_number: number;
|
||||
images: Record<unknown>;
|
||||
mature_blocked: boolean;
|
||||
versions: Version[];
|
||||
title: string;
|
||||
is_subbed: boolean;
|
||||
id: string;
|
||||
audio_locales: string[];
|
||||
subtitle_locales: string[];
|
||||
availability_notes: string;
|
||||
series_id: string;
|
||||
season_display_number: string;
|
||||
is_complete: boolean;
|
||||
keywords: any[];
|
||||
maturity_ratings: string[];
|
||||
is_simulcast: boolean;
|
||||
seo_title: string;
|
||||
description: string;
|
||||
seo_description: string;
|
||||
number_of_episodes: number;
|
||||
is_dubbed: boolean;
|
||||
identifier: string;
|
||||
channel_id: string;
|
||||
slug_title: string;
|
||||
season_sequence_number: number;
|
||||
season_tags: string[];
|
||||
extended_maturity_rating: Record<unknown>;
|
||||
is_mature: boolean;
|
||||
audio_locale: string;
|
||||
season_number: number;
|
||||
images: Record<unknown>;
|
||||
mature_blocked: boolean;
|
||||
versions: Version[];
|
||||
title: string;
|
||||
is_subbed: boolean;
|
||||
id: string;
|
||||
audio_locales: string[];
|
||||
subtitle_locales: string[];
|
||||
availability_notes: string;
|
||||
series_id: string;
|
||||
season_display_number: string;
|
||||
is_complete: boolean;
|
||||
keywords: any[];
|
||||
maturity_ratings: string[];
|
||||
is_simulcast: boolean;
|
||||
seo_title: string;
|
||||
}
|
||||
export interface Version {
|
||||
audio_locale: Locale;
|
||||
guid: string;
|
||||
original: boolean;
|
||||
variant: string;
|
||||
audio_locale: Locale;
|
||||
guid: string;
|
||||
original: boolean;
|
||||
variant: string;
|
||||
}
|
||||
|
||||
export interface EpisodeVersion {
|
||||
audio_locale: Locale;
|
||||
guid: string;
|
||||
is_premium_only: boolean;
|
||||
media_guid: string;
|
||||
original: boolean;
|
||||
season_guid: string;
|
||||
variant: string;
|
||||
audio_locale: Locale;
|
||||
guid: string;
|
||||
is_premium_only: boolean;
|
||||
media_guid: string;
|
||||
original: boolean;
|
||||
season_guid: string;
|
||||
variant: string;
|
||||
}
|
||||
|
||||
export enum Locale {
|
||||
enUS = 'en-US',
|
||||
esLA = 'es-LA',
|
||||
es419 = 'es-419',
|
||||
esES = 'es-ES',
|
||||
ptBR = 'pt-BR',
|
||||
frFR = 'fr-FR',
|
||||
deDE = 'de-DE',
|
||||
arME = 'ar-ME',
|
||||
arSA = 'ar-SA',
|
||||
itIT = 'it-IT',
|
||||
ruRU = 'ru-RU',
|
||||
trTR = 'tr-TR',
|
||||
hiIN = 'hi-IN',
|
||||
zhCN = 'zh-CN',
|
||||
koKR = 'ko-KR',
|
||||
jaJP = 'ja-JP',
|
||||
enUS = 'en-US',
|
||||
esLA = 'es-LA',
|
||||
es419 = 'es-419',
|
||||
esES = 'es-ES',
|
||||
ptBR = 'pt-BR',
|
||||
frFR = 'fr-FR',
|
||||
deDE = 'de-DE',
|
||||
arME = 'ar-ME',
|
||||
arSA = 'ar-SA',
|
||||
itIT = 'it-IT',
|
||||
ruRU = 'ru-RU',
|
||||
trTR = 'tr-TR',
|
||||
hiIN = 'hi-IN',
|
||||
zhCN = 'zh-CN',
|
||||
koKR = 'ko-KR',
|
||||
jaJP = 'ja-JP',
|
||||
}
|
||||
|
||||
export interface Meta {
|
||||
versions_considered: boolean;
|
||||
versions_considered: boolean;
|
||||
}
|
||||
|
|
|
|||
4
@types/downloadedFile.d.ts
vendored
4
@types/downloadedFile.d.ts
vendored
|
|
@ -1,6 +1,6 @@
|
|||
import { LanguageItem } from '../modules/module.langsData';
|
||||
|
||||
export type DownloadedFile = {
|
||||
path: string,
|
||||
lang: LanguageItem
|
||||
path: string,
|
||||
lang: LanguageItem
|
||||
}
|
||||
|
|
@ -1,11 +1,11 @@
|
|||
export enum CrunchyVideoPlayStreams {
|
||||
'androidtv' = 'tv/android_tv',
|
||||
'android' = 'android/phone',
|
||||
'androidtab'= 'android/tablet'
|
||||
'androidtv' = 'tv/android_tv',
|
||||
'android' = 'android/phone',
|
||||
'androidtab'= 'android/tablet'
|
||||
}
|
||||
|
||||
export enum CrunchyAudioPlayStreams {
|
||||
'androidtv' = 'tv/android_tv',
|
||||
'android' = 'android/phone',
|
||||
'androidtab'= 'android/tablet'
|
||||
'androidtv' = 'tv/android_tv',
|
||||
'android' = 'android/phone',
|
||||
'androidtab'= 'android/tablet'
|
||||
}
|
||||
106
@types/hidiveDashboard.d.ts
vendored
106
@types/hidiveDashboard.d.ts
vendored
|
|
@ -1,70 +1,70 @@
|
|||
export interface HidiveDashboard {
|
||||
Code: number;
|
||||
Status: string;
|
||||
Message: null;
|
||||
Messages: object;
|
||||
Data: Data;
|
||||
Timestamp: string;
|
||||
IPAddress: string;
|
||||
Code: number;
|
||||
Status: string;
|
||||
Message: null;
|
||||
Messages: object;
|
||||
Data: Data;
|
||||
Timestamp: string;
|
||||
IPAddress: string;
|
||||
}
|
||||
|
||||
export interface Data {
|
||||
TitleRows: TitleRow[];
|
||||
LoadTime: number;
|
||||
TitleRows: TitleRow[];
|
||||
LoadTime: number;
|
||||
}
|
||||
|
||||
export interface TitleRow {
|
||||
Name: string;
|
||||
Titles: Title[];
|
||||
LoadTime: number;
|
||||
Name: string;
|
||||
Titles: Title[];
|
||||
LoadTime: number;
|
||||
}
|
||||
|
||||
export interface Title {
|
||||
Id: number;
|
||||
Name: string;
|
||||
ShortSynopsis: string;
|
||||
MediumSynopsis: string;
|
||||
LongSynopsis: string;
|
||||
KeyArtUrl: string;
|
||||
MasterArtUrl: string;
|
||||
Rating: null | string;
|
||||
OverallRating: number;
|
||||
RatingCount: number;
|
||||
MALScore: null;
|
||||
UserRating: number;
|
||||
RunTime: number | null;
|
||||
ShowInfoTitle: string;
|
||||
FirstPremiereDate: Date;
|
||||
EpisodeCount: number;
|
||||
SeasonName: string;
|
||||
RokuHDArtUrl: string;
|
||||
RokuSDArtUrl: string;
|
||||
IsRateable: boolean;
|
||||
InQueue: boolean;
|
||||
IsFavorite: boolean;
|
||||
IsContinueWatching: boolean;
|
||||
ContinueWatching: ContinueWatching;
|
||||
Episodes: any[];
|
||||
LoadTime: number;
|
||||
Id: number;
|
||||
Name: string;
|
||||
ShortSynopsis: string;
|
||||
MediumSynopsis: string;
|
||||
LongSynopsis: string;
|
||||
KeyArtUrl: string;
|
||||
MasterArtUrl: string;
|
||||
Rating: null | string;
|
||||
OverallRating: number;
|
||||
RatingCount: number;
|
||||
MALScore: null;
|
||||
UserRating: number;
|
||||
RunTime: number | null;
|
||||
ShowInfoTitle: string;
|
||||
FirstPremiereDate: Date;
|
||||
EpisodeCount: number;
|
||||
SeasonName: string;
|
||||
RokuHDArtUrl: string;
|
||||
RokuSDArtUrl: string;
|
||||
IsRateable: boolean;
|
||||
InQueue: boolean;
|
||||
IsFavorite: boolean;
|
||||
IsContinueWatching: boolean;
|
||||
ContinueWatching: ContinueWatching;
|
||||
Episodes: any[];
|
||||
LoadTime: number;
|
||||
}
|
||||
|
||||
export interface ContinueWatching {
|
||||
Id: string;
|
||||
ProfileId: number;
|
||||
EpisodeId: number;
|
||||
Status: Status | null;
|
||||
CurrentTime: number;
|
||||
UserId: number;
|
||||
TitleId: number;
|
||||
SeasonId: number;
|
||||
VideoId: number;
|
||||
TotalSeconds: number;
|
||||
CreatedDT: Date;
|
||||
ModifiedDT: Date | null;
|
||||
Id: string;
|
||||
ProfileId: number;
|
||||
EpisodeId: number;
|
||||
Status: Status | null;
|
||||
CurrentTime: number;
|
||||
UserId: number;
|
||||
TitleId: number;
|
||||
SeasonId: number;
|
||||
VideoId: number;
|
||||
TotalSeconds: number;
|
||||
CreatedDT: Date;
|
||||
ModifiedDT: Date | null;
|
||||
}
|
||||
|
||||
export enum Status {
|
||||
Paused = 'Paused',
|
||||
Playing = 'Playing',
|
||||
Watching = 'Watching',
|
||||
Paused = 'Paused',
|
||||
Playing = 'Playing',
|
||||
Watching = 'Watching',
|
||||
}
|
||||
134
@types/hidiveEpisodeList.d.ts
vendored
134
@types/hidiveEpisodeList.d.ts
vendored
|
|
@ -1,84 +1,84 @@
|
|||
export interface HidiveEpisodeList {
|
||||
Code: number;
|
||||
Status: string;
|
||||
Message: null;
|
||||
Messages: Record<unknown, unknown>;
|
||||
Data: Data;
|
||||
Timestamp: string;
|
||||
IPAddress: string;
|
||||
Code: number;
|
||||
Status: string;
|
||||
Message: null;
|
||||
Messages: Record<unknown, unknown>;
|
||||
Data: Data;
|
||||
Timestamp: string;
|
||||
IPAddress: string;
|
||||
}
|
||||
|
||||
export interface Data {
|
||||
Title: HidiveTitle;
|
||||
Title: HidiveTitle;
|
||||
}
|
||||
|
||||
export interface HidiveTitle {
|
||||
Id: number;
|
||||
Name: string;
|
||||
ShortSynopsis: string;
|
||||
MediumSynopsis: string;
|
||||
LongSynopsis: string;
|
||||
KeyArtUrl: string;
|
||||
MasterArtUrl: string;
|
||||
Rating: string;
|
||||
OverallRating: number;
|
||||
RatingCount: number;
|
||||
MALScore: null;
|
||||
UserRating: number;
|
||||
RunTime: number;
|
||||
ShowInfoTitle: string;
|
||||
FirstPremiereDate: Date;
|
||||
EpisodeCount: number;
|
||||
SeasonName: string;
|
||||
RokuHDArtUrl: string;
|
||||
RokuSDArtUrl: string;
|
||||
IsRateable: boolean;
|
||||
InQueue: boolean;
|
||||
IsFavorite: boolean;
|
||||
IsContinueWatching: boolean;
|
||||
ContinueWatching: ContinueWatching;
|
||||
Episodes: HidiveEpisode[];
|
||||
LoadTime: number;
|
||||
Id: number;
|
||||
Name: string;
|
||||
ShortSynopsis: string;
|
||||
MediumSynopsis: string;
|
||||
LongSynopsis: string;
|
||||
KeyArtUrl: string;
|
||||
MasterArtUrl: string;
|
||||
Rating: string;
|
||||
OverallRating: number;
|
||||
RatingCount: number;
|
||||
MALScore: null;
|
||||
UserRating: number;
|
||||
RunTime: number;
|
||||
ShowInfoTitle: string;
|
||||
FirstPremiereDate: Date;
|
||||
EpisodeCount: number;
|
||||
SeasonName: string;
|
||||
RokuHDArtUrl: string;
|
||||
RokuSDArtUrl: string;
|
||||
IsRateable: boolean;
|
||||
InQueue: boolean;
|
||||
IsFavorite: boolean;
|
||||
IsContinueWatching: boolean;
|
||||
ContinueWatching: ContinueWatching;
|
||||
Episodes: HidiveEpisode[];
|
||||
LoadTime: number;
|
||||
}
|
||||
|
||||
export interface ContinueWatching {
|
||||
Id: string;
|
||||
ProfileId: number;
|
||||
EpisodeId: number;
|
||||
Status: string;
|
||||
CurrentTime: number;
|
||||
UserId: number;
|
||||
TitleId: number;
|
||||
SeasonId: number;
|
||||
VideoId: number;
|
||||
TotalSeconds: number;
|
||||
CreatedDT: Date;
|
||||
ModifiedDT: Date;
|
||||
Id: string;
|
||||
ProfileId: number;
|
||||
EpisodeId: number;
|
||||
Status: string;
|
||||
CurrentTime: number;
|
||||
UserId: number;
|
||||
TitleId: number;
|
||||
SeasonId: number;
|
||||
VideoId: number;
|
||||
TotalSeconds: number;
|
||||
CreatedDT: Date;
|
||||
ModifiedDT: Date;
|
||||
}
|
||||
|
||||
export interface HidiveEpisode {
|
||||
Id: number;
|
||||
Number: number;
|
||||
Name: string;
|
||||
Summary: string;
|
||||
HIDIVEPremiereDate: Date;
|
||||
ScreenShotSmallUrl: string;
|
||||
ScreenShotCompressedUrl: string;
|
||||
SeasonNumber: number;
|
||||
TitleId: number;
|
||||
SeasonNumberValue: number;
|
||||
EpisodeNumberValue: number;
|
||||
VideoKey: string;
|
||||
DisplayNameLong: string;
|
||||
PercentProgress: number;
|
||||
LoadTime: number;
|
||||
Id: number;
|
||||
Number: number;
|
||||
Name: string;
|
||||
Summary: string;
|
||||
HIDIVEPremiereDate: Date;
|
||||
ScreenShotSmallUrl: string;
|
||||
ScreenShotCompressedUrl: string;
|
||||
SeasonNumber: number;
|
||||
TitleId: number;
|
||||
SeasonNumberValue: number;
|
||||
EpisodeNumberValue: number;
|
||||
VideoKey: string;
|
||||
DisplayNameLong: string;
|
||||
PercentProgress: number;
|
||||
LoadTime: number;
|
||||
}
|
||||
|
||||
export interface HidiveEpisodeExtra extends HidiveEpisode {
|
||||
titleId: number;
|
||||
epKey: string;
|
||||
nameLong: string;
|
||||
seriesTitle: string;
|
||||
seriesId?: number;
|
||||
isSelected: boolean;
|
||||
titleId: number;
|
||||
epKey: string;
|
||||
nameLong: string;
|
||||
seriesTitle: string;
|
||||
seriesId?: number;
|
||||
isSelected: boolean;
|
||||
}
|
||||
78
@types/hidiveSearch.d.ts
vendored
78
@types/hidiveSearch.d.ts
vendored
|
|
@ -1,47 +1,47 @@
|
|||
export interface HidiveSearch {
|
||||
Code: number;
|
||||
Status: string;
|
||||
Message: null;
|
||||
Messages: Record<unknown, unknown>;
|
||||
Data: HidiveSearchData;
|
||||
Timestamp: string;
|
||||
IPAddress: string;
|
||||
Code: number;
|
||||
Status: string;
|
||||
Message: null;
|
||||
Messages: Record<unknown, unknown>;
|
||||
Data: HidiveSearchData;
|
||||
Timestamp: string;
|
||||
IPAddress: string;
|
||||
}
|
||||
|
||||
export interface HidiveSearchData {
|
||||
Query: string;
|
||||
Slug: string;
|
||||
TitleResults: HidiveSearchItem[];
|
||||
SearchId: number;
|
||||
IsSearchPinned: boolean;
|
||||
IsPinnedSearchAvailable: boolean;
|
||||
Query: string;
|
||||
Slug: string;
|
||||
TitleResults: HidiveSearchItem[];
|
||||
SearchId: number;
|
||||
IsSearchPinned: boolean;
|
||||
IsPinnedSearchAvailable: boolean;
|
||||
}
|
||||
|
||||
export interface HidiveSearchItem {
|
||||
Id: number;
|
||||
Name: string;
|
||||
ShortSynopsis: string;
|
||||
MediumSynopsis: string;
|
||||
LongSynopsis: string;
|
||||
KeyArtUrl: string;
|
||||
MasterArtUrl: string;
|
||||
Rating: string;
|
||||
OverallRating: number;
|
||||
RatingCount: number;
|
||||
MALScore: null;
|
||||
UserRating: number;
|
||||
RunTime: number | null;
|
||||
ShowInfoTitle: string;
|
||||
FirstPremiereDate: Date;
|
||||
EpisodeCount: number;
|
||||
SeasonName: string;
|
||||
RokuHDArtUrl: string;
|
||||
RokuSDArtUrl: string;
|
||||
IsRateable: boolean;
|
||||
InQueue: boolean;
|
||||
IsFavorite: boolean;
|
||||
IsContinueWatching: boolean;
|
||||
ContinueWatching: null;
|
||||
Episodes: any[];
|
||||
LoadTime: number;
|
||||
Id: number;
|
||||
Name: string;
|
||||
ShortSynopsis: string;
|
||||
MediumSynopsis: string;
|
||||
LongSynopsis: string;
|
||||
KeyArtUrl: string;
|
||||
MasterArtUrl: string;
|
||||
Rating: string;
|
||||
OverallRating: number;
|
||||
RatingCount: number;
|
||||
MALScore: null;
|
||||
UserRating: number;
|
||||
RunTime: number | null;
|
||||
ShowInfoTitle: string;
|
||||
FirstPremiereDate: Date;
|
||||
EpisodeCount: number;
|
||||
SeasonName: string;
|
||||
RokuHDArtUrl: string;
|
||||
RokuSDArtUrl: string;
|
||||
IsRateable: boolean;
|
||||
InQueue: boolean;
|
||||
IsFavorite: boolean;
|
||||
IsContinueWatching: boolean;
|
||||
ContinueWatching: null;
|
||||
Episodes: any[];
|
||||
LoadTime: number;
|
||||
}
|
||||
86
@types/hidiveTypes.d.ts
vendored
86
@types/hidiveTypes.d.ts
vendored
|
|
@ -1,61 +1,61 @@
|
|||
export interface HidiveVideoList {
|
||||
Code: number;
|
||||
Status: string;
|
||||
Message: null;
|
||||
Messages: Record<unknown, unknown>;
|
||||
Data: HidiveVideo;
|
||||
Timestamp: string;
|
||||
IPAddress: string;
|
||||
Code: number;
|
||||
Status: string;
|
||||
Message: null;
|
||||
Messages: Record<unknown, unknown>;
|
||||
Data: HidiveVideo;
|
||||
Timestamp: string;
|
||||
IPAddress: string;
|
||||
}
|
||||
|
||||
export interface HidiveVideo {
|
||||
ShowAds: boolean;
|
||||
CaptionCssUrl: string;
|
||||
FontSize: number;
|
||||
FontScale: number;
|
||||
CaptionLanguages: string[];
|
||||
CaptionLanguage: string;
|
||||
CaptionVttUrls: Record<string, string>;
|
||||
VideoLanguages: string[];
|
||||
VideoLanguage: string;
|
||||
VideoUrls: Record<string, HidiveStreamList>;
|
||||
FontColorName: string;
|
||||
AutoPlayNextEpisode: boolean;
|
||||
MaxStreams: number;
|
||||
CurrentTime: number;
|
||||
FontColorCode: string;
|
||||
RunTime: number;
|
||||
AdUrl: null;
|
||||
ShowAds: boolean;
|
||||
CaptionCssUrl: string;
|
||||
FontSize: number;
|
||||
FontScale: number;
|
||||
CaptionLanguages: string[];
|
||||
CaptionLanguage: string;
|
||||
CaptionVttUrls: Record<string, string>;
|
||||
VideoLanguages: string[];
|
||||
VideoLanguage: string;
|
||||
VideoUrls: Record<string, HidiveStreamList>;
|
||||
FontColorName: string;
|
||||
AutoPlayNextEpisode: boolean;
|
||||
MaxStreams: number;
|
||||
CurrentTime: number;
|
||||
FontColorCode: string;
|
||||
RunTime: number;
|
||||
AdUrl: null;
|
||||
}
|
||||
|
||||
export interface HidiveStreamList {
|
||||
hls: string[];
|
||||
drm: string[];
|
||||
drmEnabled: boolean;
|
||||
hls: string[];
|
||||
drm: string[];
|
||||
drmEnabled: boolean;
|
||||
}
|
||||
|
||||
export interface HidiveStreamInfo extends HidiveStreamList {
|
||||
language?: string;
|
||||
episodeTitle?: string;
|
||||
seriesTitle?: string;
|
||||
season?: number;
|
||||
episodeNumber?: number;
|
||||
uncut?: boolean;
|
||||
image?: string;
|
||||
language?: string;
|
||||
episodeTitle?: string;
|
||||
seriesTitle?: string;
|
||||
season?: number;
|
||||
episodeNumber?: number;
|
||||
uncut?: boolean;
|
||||
image?: string;
|
||||
}
|
||||
|
||||
export interface HidiveSubtitleInfo {
|
||||
language: string;
|
||||
cc: boolean;
|
||||
url: string;
|
||||
language: string;
|
||||
cc: boolean;
|
||||
url: string;
|
||||
}
|
||||
|
||||
export type DownloadedMedia = {
|
||||
type: 'Video',
|
||||
lang: LanguageItem,
|
||||
path: string,
|
||||
uncut: boolean
|
||||
type: 'Video',
|
||||
lang: LanguageItem,
|
||||
path: string,
|
||||
uncut: boolean
|
||||
} | ({
|
||||
type: 'Subtitle',
|
||||
cc: boolean
|
||||
type: 'Subtitle',
|
||||
cc: boolean
|
||||
} & sxItem )
|
||||
14
@types/iso639.d.ts
vendored
14
@types/iso639.d.ts
vendored
|
|
@ -1,9 +1,9 @@
|
|||
declare module 'iso-639' {
|
||||
export type iso639Type = {
|
||||
[key: string]: {
|
||||
'639-1'?: string,
|
||||
'639-2'?: string
|
||||
}
|
||||
}
|
||||
export const iso_639_2: iso639Type;
|
||||
export type iso639Type = {
|
||||
[key: string]: {
|
||||
'639-1'?: string,
|
||||
'639-2'?: string
|
||||
}
|
||||
}
|
||||
export const iso_639_2: iso639Type;
|
||||
}
|
||||
262
@types/items.d.ts
vendored
262
@types/items.d.ts
vendored
|
|
@ -1,169 +1,169 @@
|
|||
export interface Item {
|
||||
// Added later
|
||||
id: string,
|
||||
id_split: (number|string)[]
|
||||
// Added from the start
|
||||
mostRecentSvodJpnUs: MostRecentSvodJpnUs;
|
||||
synopsis: string;
|
||||
mediaCategory: ContentType;
|
||||
mostRecentSvodUsEndTimestamp: number;
|
||||
quality: QualityClass;
|
||||
genres: Genre[];
|
||||
titleImages: TitleImages;
|
||||
engAllTerritoryAvail: EngAllTerritoryAvail;
|
||||
thumb: string;
|
||||
mostRecentSvodJpnAllTerrStartTimestamp: number;
|
||||
title: string;
|
||||
starRating: number;
|
||||
primaryAvail: PrimaryAvail;
|
||||
access: Access[];
|
||||
version: Version[];
|
||||
mostRecentSvodJpnAllTerrEndTimestamp: number;
|
||||
itemId: number;
|
||||
versionAudio: VersionAudio;
|
||||
contentType: ContentType;
|
||||
mostRecentSvodUsStartTimestamp: number;
|
||||
poster: string;
|
||||
mostRecentSvodEngAllTerrEndTimestamp: number;
|
||||
mostRecentSvodJpnUsStartTimestamp: number;
|
||||
mostRecentSvodJpnUsEndTimestamp: number;
|
||||
mostRecentSvodStartTimestamp: number;
|
||||
mostRecentSvod: MostRecent;
|
||||
altAvail: AltAvail;
|
||||
ids: IDs;
|
||||
mostRecentSvodUs: MostRecent;
|
||||
item: Item;
|
||||
mostRecentSvodEngAllTerrStartTimestamp: number;
|
||||
audio: string[];
|
||||
mostRecentAvod: MostRecent;
|
||||
// Added later
|
||||
id: string,
|
||||
id_split: (number|string)[]
|
||||
// Added from the start
|
||||
mostRecentSvodJpnUs: MostRecentSvodJpnUs;
|
||||
synopsis: string;
|
||||
mediaCategory: ContentType;
|
||||
mostRecentSvodUsEndTimestamp: number;
|
||||
quality: QualityClass;
|
||||
genres: Genre[];
|
||||
titleImages: TitleImages;
|
||||
engAllTerritoryAvail: EngAllTerritoryAvail;
|
||||
thumb: string;
|
||||
mostRecentSvodJpnAllTerrStartTimestamp: number;
|
||||
title: string;
|
||||
starRating: number;
|
||||
primaryAvail: PrimaryAvail;
|
||||
access: Access[];
|
||||
version: Version[];
|
||||
mostRecentSvodJpnAllTerrEndTimestamp: number;
|
||||
itemId: number;
|
||||
versionAudio: VersionAudio;
|
||||
contentType: ContentType;
|
||||
mostRecentSvodUsStartTimestamp: number;
|
||||
poster: string;
|
||||
mostRecentSvodEngAllTerrEndTimestamp: number;
|
||||
mostRecentSvodJpnUsStartTimestamp: number;
|
||||
mostRecentSvodJpnUsEndTimestamp: number;
|
||||
mostRecentSvodStartTimestamp: number;
|
||||
mostRecentSvod: MostRecent;
|
||||
altAvail: AltAvail;
|
||||
ids: IDs;
|
||||
mostRecentSvodUs: MostRecent;
|
||||
item: Item;
|
||||
mostRecentSvodEngAllTerrStartTimestamp: number;
|
||||
audio: string[];
|
||||
mostRecentAvod: MostRecent;
|
||||
}
|
||||
|
||||
export enum ContentType {
|
||||
Episode = 'episode',
|
||||
Ova = 'ova',
|
||||
Episode = 'episode',
|
||||
Ova = 'ova',
|
||||
}
|
||||
|
||||
export interface IDs {
|
||||
externalShowId: ID;
|
||||
externalSeasonId: ExternalSeasonID;
|
||||
externalEpisodeId: string;
|
||||
externalAsianId?: string
|
||||
externalShowId: ID;
|
||||
externalSeasonId: ExternalSeasonID;
|
||||
externalEpisodeId: string;
|
||||
externalAsianId?: string
|
||||
}
|
||||
|
||||
export interface Item {
|
||||
seasonTitle: string;
|
||||
seasonId: number;
|
||||
episodeOrder: number;
|
||||
episodeSlug: string;
|
||||
created: Date;
|
||||
titleSlug: string;
|
||||
episodeNum: string;
|
||||
episodeId: number;
|
||||
titleId: number;
|
||||
seasonNum: string;
|
||||
ratings: Array<string[]>;
|
||||
showImage: string;
|
||||
titleName: string;
|
||||
runtime: string;
|
||||
episodeName: string;
|
||||
seasonOrder: number;
|
||||
titleExternalId: string;
|
||||
seasonTitle: string;
|
||||
seasonId: number;
|
||||
episodeOrder: number;
|
||||
episodeSlug: string;
|
||||
created: Date;
|
||||
titleSlug: string;
|
||||
episodeNum: string;
|
||||
episodeId: number;
|
||||
titleId: number;
|
||||
seasonNum: string;
|
||||
ratings: Array<string[]>;
|
||||
showImage: string;
|
||||
titleName: string;
|
||||
runtime: string;
|
||||
episodeName: string;
|
||||
seasonOrder: number;
|
||||
titleExternalId: string;
|
||||
}
|
||||
|
||||
|
||||
export interface MostRecent {
|
||||
image?: string;
|
||||
siblingStartTimestamp?: string;
|
||||
devices?: Device[];
|
||||
availId?: number;
|
||||
distributor?: Distributor;
|
||||
quality?: MostRecentAvodQuality;
|
||||
endTimestamp?: string;
|
||||
mediaCategory?: ContentType;
|
||||
isPromo?: boolean;
|
||||
siblingType?: Purchase;
|
||||
version?: Version;
|
||||
territory?: Territory;
|
||||
startDate?: Date;
|
||||
endDate?: Date;
|
||||
versionId?: number;
|
||||
tier?: Device | null;
|
||||
purchase?: Purchase;
|
||||
startTimestamp?: string;
|
||||
language?: Audio;
|
||||
itemTitle?: string;
|
||||
ids?: MostRecentAvodIDS;
|
||||
experience?: number;
|
||||
siblingEndTimestamp?: string;
|
||||
item?: Item;
|
||||
subscriptionRequired?: boolean;
|
||||
purchased?: boolean;
|
||||
image?: string;
|
||||
siblingStartTimestamp?: string;
|
||||
devices?: Device[];
|
||||
availId?: number;
|
||||
distributor?: Distributor;
|
||||
quality?: MostRecentAvodQuality;
|
||||
endTimestamp?: string;
|
||||
mediaCategory?: ContentType;
|
||||
isPromo?: boolean;
|
||||
siblingType?: Purchase;
|
||||
version?: Version;
|
||||
territory?: Territory;
|
||||
startDate?: Date;
|
||||
endDate?: Date;
|
||||
versionId?: number;
|
||||
tier?: Device | null;
|
||||
purchase?: Purchase;
|
||||
startTimestamp?: string;
|
||||
language?: Audio;
|
||||
itemTitle?: string;
|
||||
ids?: MostRecentAvodIDS;
|
||||
experience?: number;
|
||||
siblingEndTimestamp?: string;
|
||||
item?: Item;
|
||||
subscriptionRequired?: boolean;
|
||||
purchased?: boolean;
|
||||
}
|
||||
|
||||
export interface MostRecentAvodIDS {
|
||||
externalSeasonId: ExternalSeasonID;
|
||||
externalAsianId: null;
|
||||
externalShowId: ID;
|
||||
externalEpisodeId: string;
|
||||
externalEnglishId: string;
|
||||
externalAlphaId: string;
|
||||
externalSeasonId: ExternalSeasonID;
|
||||
externalAsianId: null;
|
||||
externalShowId: ID;
|
||||
externalEpisodeId: string;
|
||||
externalEnglishId: string;
|
||||
externalAlphaId: string;
|
||||
}
|
||||
|
||||
export enum Purchase {
|
||||
AVOD = 'A-VOD',
|
||||
Dfov = 'DFOV',
|
||||
Est = 'EST',
|
||||
Svod = 'SVOD',
|
||||
AVOD = 'A-VOD',
|
||||
Dfov = 'DFOV',
|
||||
Est = 'EST',
|
||||
Svod = 'SVOD',
|
||||
}
|
||||
|
||||
export enum Version {
|
||||
Simulcast = 'Simulcast',
|
||||
Uncut = 'Uncut',
|
||||
Simulcast = 'Simulcast',
|
||||
Uncut = 'Uncut',
|
||||
}
|
||||
|
||||
export type MostRecentSvodJpnUs = Record<string, any>
|
||||
|
||||
export interface QualityClass {
|
||||
quality: QualityQuality;
|
||||
height: number;
|
||||
quality: QualityQuality;
|
||||
height: number;
|
||||
}
|
||||
|
||||
export enum QualityQuality {
|
||||
HD = 'HD',
|
||||
SD = 'SD',
|
||||
HD = 'HD',
|
||||
SD = 'SD',
|
||||
}
|
||||
|
||||
export interface TitleImages {
|
||||
showThumbnail: string;
|
||||
showBackgroundSite: string;
|
||||
showDetailHeaderDesktop: string;
|
||||
continueWatchingDesktop: string;
|
||||
showDetailHeroSite: string;
|
||||
appleHorizontalBannerShow: string;
|
||||
backgroundImageXbox_360: string;
|
||||
applePosterCover: string;
|
||||
showDetailBoxArtTablet: string;
|
||||
featuredShowBackgroundTablet: string;
|
||||
backgroundImageAppletvfiretv: string;
|
||||
newShowDetailHero: string;
|
||||
showDetailHeroDesktop: string;
|
||||
showKeyart: string;
|
||||
continueWatchingMobile: string;
|
||||
featuredSpotlightShowPhone: string;
|
||||
appleHorizontalBannerMovie: string;
|
||||
featuredSpotlightShowTablet: string;
|
||||
showDetailBoxArtPhone: string;
|
||||
featuredShowBackgroundPhone: string;
|
||||
appleSquareCover: string;
|
||||
backgroundVideo: string;
|
||||
showMasterKeyArt: string;
|
||||
newShowDetailHeroPhone: string;
|
||||
showDetailBoxArtXbox_360: string;
|
||||
showDetailHeaderMobile: string;
|
||||
showLogo: string;
|
||||
showThumbnail: string;
|
||||
showBackgroundSite: string;
|
||||
showDetailHeaderDesktop: string;
|
||||
continueWatchingDesktop: string;
|
||||
showDetailHeroSite: string;
|
||||
appleHorizontalBannerShow: string;
|
||||
backgroundImageXbox_360: string;
|
||||
applePosterCover: string;
|
||||
showDetailBoxArtTablet: string;
|
||||
featuredShowBackgroundTablet: string;
|
||||
backgroundImageAppletvfiretv: string;
|
||||
newShowDetailHero: string;
|
||||
showDetailHeroDesktop: string;
|
||||
showKeyart: string;
|
||||
continueWatchingMobile: string;
|
||||
featuredSpotlightShowPhone: string;
|
||||
appleHorizontalBannerMovie: string;
|
||||
featuredSpotlightShowTablet: string;
|
||||
showDetailBoxArtPhone: string;
|
||||
featuredShowBackgroundPhone: string;
|
||||
appleSquareCover: string;
|
||||
backgroundVideo: string;
|
||||
showMasterKeyArt: string;
|
||||
newShowDetailHeroPhone: string;
|
||||
showDetailBoxArtXbox_360: string;
|
||||
showDetailHeaderMobile: string;
|
||||
showLogo: string;
|
||||
}
|
||||
|
||||
export interface VersionAudio {
|
||||
Uncut?: Audio[];
|
||||
Simulcast: Audio[];
|
||||
Uncut?: Audio[];
|
||||
Simulcast: Audio[];
|
||||
}
|
||||
94
@types/m3u8-parsed.d.ts
vendored
94
@types/m3u8-parsed.d.ts
vendored
|
|
@ -1,49 +1,49 @@
|
|||
declare module 'm3u8-parsed' {
|
||||
export type M3U8 = {
|
||||
allowCache: boolean,
|
||||
discontinuityStarts: [],
|
||||
segments: {
|
||||
duration: number,
|
||||
byterange?: {
|
||||
length: number,
|
||||
offset: number
|
||||
},
|
||||
uri: string,
|
||||
key: {
|
||||
method: string,
|
||||
uri: string,
|
||||
},
|
||||
timeline: number
|
||||
}[],
|
||||
version: number,
|
||||
mediaGroups: {
|
||||
[type: string]: {
|
||||
[index: string]: {
|
||||
[language: string]: {
|
||||
default: boolean,
|
||||
autoselect: boolean,
|
||||
language: string,
|
||||
uri: string
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
playlists: {
|
||||
uri: string,
|
||||
timeline: number,
|
||||
attributes: {
|
||||
'CLOSED-CAPTIONS': string,
|
||||
'AUDIO': string,
|
||||
'FRAME-RATE': number,
|
||||
'RESOLUTION': {
|
||||
width: number,
|
||||
height: number
|
||||
},
|
||||
'CODECS': string,
|
||||
'AVERAGE-BANDWIDTH': string,
|
||||
'BANDWIDTH': number
|
||||
}
|
||||
}[],
|
||||
}
|
||||
export default function (data: string): M3U8;
|
||||
export type M3U8 = {
|
||||
allowCache: boolean,
|
||||
discontinuityStarts: [],
|
||||
segments: {
|
||||
duration: number,
|
||||
byterange?: {
|
||||
length: number,
|
||||
offset: number
|
||||
},
|
||||
uri: string,
|
||||
key: {
|
||||
method: string,
|
||||
uri: string,
|
||||
},
|
||||
timeline: number
|
||||
}[],
|
||||
version: number,
|
||||
mediaGroups: {
|
||||
[type: string]: {
|
||||
[index: string]: {
|
||||
[language: string]: {
|
||||
default: boolean,
|
||||
autoselect: boolean,
|
||||
language: string,
|
||||
uri: string
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
playlists: {
|
||||
uri: string,
|
||||
timeline: number,
|
||||
attributes: {
|
||||
'CLOSED-CAPTIONS': string,
|
||||
'AUDIO': string,
|
||||
'FRAME-RATE': number,
|
||||
'RESOLUTION': {
|
||||
width: number,
|
||||
height: number
|
||||
},
|
||||
'CODECS': string,
|
||||
'AVERAGE-BANDWIDTH': string,
|
||||
'BANDWIDTH': number
|
||||
}
|
||||
}[],
|
||||
}
|
||||
export default function (data: string): M3U8;
|
||||
}
|
||||
194
@types/messageHandler.d.ts
vendored
194
@types/messageHandler.d.ts
vendored
|
|
@ -4,97 +4,97 @@ import type { AvailableMuxer } from '../modules/module.args';
|
|||
import { LanguageItem } from '../modules/module.langsData';
|
||||
|
||||
export interface MessageHandler {
|
||||
name: string
|
||||
auth: (data: AuthData) => Promise<AuthResponse>;
|
||||
version: () => Promise<string>;
|
||||
checkToken: () => Promise<CheckTokenResponse>;
|
||||
search: (data: SearchData) => Promise<SearchResponse>,
|
||||
availableDubCodes: () => Promise<string[]>,
|
||||
availableSubCodes: () => Promise<string[]>,
|
||||
handleDefault: (name: string) => Promise<any>,
|
||||
resolveItems: (data: ResolveItemsData) => Promise<boolean>,
|
||||
listEpisodes: (id: string) => Promise<EpisodeListResponse>,
|
||||
downloadItem: (data: QueueItem) => void,
|
||||
isDownloading: () => Promise<boolean>,
|
||||
openFolder: (path: FolderTypes) => void,
|
||||
openFile: (data: [FolderTypes, string]) => void,
|
||||
openURL: (data: string) => void;
|
||||
getQueue: () => Promise<QueueItem[]>,
|
||||
removeFromQueue: (index: number) => void,
|
||||
clearQueue: () => void,
|
||||
setDownloadQueue: (data: boolean) => void,
|
||||
getDownloadQueue: () => Promise<boolean>
|
||||
name: string
|
||||
auth: (data: AuthData) => Promise<AuthResponse>;
|
||||
version: () => Promise<string>;
|
||||
checkToken: () => Promise<CheckTokenResponse>;
|
||||
search: (data: SearchData) => Promise<SearchResponse>,
|
||||
availableDubCodes: () => Promise<string[]>,
|
||||
availableSubCodes: () => Promise<string[]>,
|
||||
handleDefault: (name: string) => Promise<any>,
|
||||
resolveItems: (data: ResolveItemsData) => Promise<boolean>,
|
||||
listEpisodes: (id: string) => Promise<EpisodeListResponse>,
|
||||
downloadItem: (data: QueueItem) => void,
|
||||
isDownloading: () => Promise<boolean>,
|
||||
openFolder: (path: FolderTypes) => void,
|
||||
openFile: (data: [FolderTypes, string]) => void,
|
||||
openURL: (data: string) => void;
|
||||
getQueue: () => Promise<QueueItem[]>,
|
||||
removeFromQueue: (index: number) => void,
|
||||
clearQueue: () => void,
|
||||
setDownloadQueue: (data: boolean) => void,
|
||||
getDownloadQueue: () => Promise<boolean>
|
||||
}
|
||||
|
||||
export type FolderTypes = 'content' | 'config';
|
||||
|
||||
export type QueueItem = {
|
||||
title: string,
|
||||
episode: string,
|
||||
fileName: string,
|
||||
dlsubs: string[],
|
||||
parent: {
|
||||
title: string,
|
||||
season: string
|
||||
},
|
||||
q: number,
|
||||
dlVideoOnce: boolean,
|
||||
dubLang: string[],
|
||||
image: string,
|
||||
title: string,
|
||||
episode: string,
|
||||
fileName: string,
|
||||
dlsubs: string[],
|
||||
parent: {
|
||||
title: string,
|
||||
season: string
|
||||
},
|
||||
q: number,
|
||||
dlVideoOnce: boolean,
|
||||
dubLang: string[],
|
||||
image: string,
|
||||
} & ResolveItemsData
|
||||
|
||||
export type ResolveItemsData = {
|
||||
id: string,
|
||||
dubLang: string[],
|
||||
all: boolean,
|
||||
but: boolean,
|
||||
novids: boolean,
|
||||
noaudio: boolean
|
||||
dlVideoOnce: boolean,
|
||||
e: string,
|
||||
fileName: string,
|
||||
q: number,
|
||||
dlsubs: string[]
|
||||
id: string,
|
||||
dubLang: string[],
|
||||
all: boolean,
|
||||
but: boolean,
|
||||
novids: boolean,
|
||||
noaudio: boolean
|
||||
dlVideoOnce: boolean,
|
||||
e: string,
|
||||
fileName: string,
|
||||
q: number,
|
||||
dlsubs: string[]
|
||||
}
|
||||
|
||||
export type SearchResponseItem = {
|
||||
image: string,
|
||||
name: string,
|
||||
desc?: string,
|
||||
id: string,
|
||||
lang?: string[],
|
||||
rating: number
|
||||
image: string,
|
||||
name: string,
|
||||
desc?: string,
|
||||
id: string,
|
||||
lang?: string[],
|
||||
rating: number
|
||||
};
|
||||
|
||||
export type Episode = {
|
||||
e: string,
|
||||
lang: string[],
|
||||
name: string,
|
||||
season: string,
|
||||
seasonTitle: string,
|
||||
episode: string,
|
||||
id: string,
|
||||
img: string,
|
||||
description: string,
|
||||
time: string
|
||||
e: string,
|
||||
lang: string[],
|
||||
name: string,
|
||||
season: string,
|
||||
seasonTitle: string,
|
||||
episode: string,
|
||||
id: string,
|
||||
img: string,
|
||||
description: string,
|
||||
time: string
|
||||
}
|
||||
|
||||
export type SearchResponse = ResponseBase<SearchResponseItem[]>
|
||||
export type EpisodeListResponse = ResponseBase<Episode[]>
|
||||
|
||||
export type FuniEpisodeData = {
|
||||
title: string,
|
||||
episode: string,
|
||||
epsiodeNumber: string,
|
||||
episodeID: string,
|
||||
seasonTitle: string,
|
||||
seasonNumber: string,
|
||||
ids: {
|
||||
episode: string,
|
||||
show: string,
|
||||
season: string
|
||||
},
|
||||
image: string
|
||||
title: string,
|
||||
episode: string,
|
||||
epsiodeNumber: string,
|
||||
episodeID: string,
|
||||
seasonTitle: string,
|
||||
seasonNumber: string,
|
||||
ids: {
|
||||
episode: string,
|
||||
show: string,
|
||||
season: string
|
||||
},
|
||||
image: string
|
||||
};
|
||||
|
||||
export type AuthData = { username: string, password: string };
|
||||
|
|
@ -102,12 +102,12 @@ export type SearchData = { search: string, page?: number, 'search-type'?: string
|
|||
export type FuniGetShowData = { id: number, e?: string, but: boolean, all: boolean };
|
||||
export type FuniGetEpisodeData = { subs: FuniSubsData, fnSlug: FuniEpisodeData, simul?: boolean; dubLang: string[], s: string }
|
||||
export type FuniStreamData = { force?: 'Y'|'y'|'N'|'n'|'C'|'c', callbackMaker?: (data: DownloadInfo) => HLSCallback, q: number, x: number, fileName: string, numbers: number, novids?: boolean,
|
||||
timeout: number, partsize: number, fsRetryTime: number, noaudio?: boolean, mp4: boolean, ass: boolean, fontSize: number, fontName?: string, skipmux?: boolean,
|
||||
forceMuxer: AvailableMuxer | undefined, simul: boolean, skipSubMux: boolean, nocleanup: boolean, override: string[], videoTitle: string,
|
||||
ffmpegOptions: string[], mkvmergeOptions: string[], defaultAudio: LanguageItem, defaultSub: LanguageItem, ccTag: string }
|
||||
timeout: number, partsize: number, fsRetryTime: number, noaudio?: boolean, mp4: boolean, ass: boolean, fontSize: number, fontName?: string, skipmux?: boolean,
|
||||
forceMuxer: AvailableMuxer | undefined, simul: boolean, skipSubMux: boolean, nocleanup: boolean, override: string[], videoTitle: string,
|
||||
ffmpegOptions: string[], mkvmergeOptions: string[], defaultAudio: LanguageItem, defaultSub: LanguageItem, ccTag: string }
|
||||
export type FuniSubsData = { nosubs?: boolean, sub: boolean, dlsubs: string[], ccTag: string }
|
||||
export type DownloadData = {
|
||||
hslang?: string; id: string, e: string, dubLang: string[], dlsubs: string[], fileName: string, q: number, novids: boolean, noaudio: boolean, dlVideoOnce: boolean
|
||||
hslang?: string; id: string, e: string, dubLang: string[], dlsubs: string[], fileName: string, q: number, novids: boolean, noaudio: boolean, dlVideoOnce: boolean
|
||||
}
|
||||
|
||||
export type AuthResponse = ResponseBase<undefined>;
|
||||
|
|
@ -118,44 +118,44 @@ export type CheckTokenResponse = ResponseBase<undefined>;
|
|||
|
||||
|
||||
export type ResponseBase<T> = ({
|
||||
isOk: true,
|
||||
value: T
|
||||
isOk: true,
|
||||
value: T
|
||||
} | {
|
||||
isOk: false,
|
||||
reason: Error
|
||||
isOk: false,
|
||||
reason: Error
|
||||
});
|
||||
|
||||
export type ProgressData = {
|
||||
total: number,
|
||||
cur: number,
|
||||
percent: number|string,
|
||||
time: number,
|
||||
downloadSpeed: number,
|
||||
bytes: number
|
||||
total: number,
|
||||
cur: number,
|
||||
percent: number|string,
|
||||
time: number,
|
||||
downloadSpeed: number,
|
||||
bytes: number
|
||||
};
|
||||
|
||||
export type PossibleMessages = keyof ServiceHandler;
|
||||
|
||||
export type DownloadInfo = {
|
||||
image: string,
|
||||
parent: {
|
||||
title: string
|
||||
},
|
||||
title: string,
|
||||
language: LanguageItem,
|
||||
fileName: string
|
||||
image: string,
|
||||
parent: {
|
||||
title: string
|
||||
},
|
||||
title: string,
|
||||
language: LanguageItem,
|
||||
fileName: string
|
||||
}
|
||||
|
||||
export type ExtendedProgress = {
|
||||
progress: ProgressData,
|
||||
downloadInfo: DownloadInfo
|
||||
progress: ProgressData,
|
||||
downloadInfo: DownloadInfo
|
||||
}
|
||||
|
||||
export type GuiState = {
|
||||
setup: boolean,
|
||||
services: Record<string, GuiStateService>
|
||||
setup: boolean,
|
||||
services: Record<string, GuiStateService>
|
||||
}
|
||||
|
||||
export type GuiStateService = {
|
||||
queue: QueueItem[]
|
||||
queue: QueueItem[]
|
||||
}
|
||||
192
@types/mpd-parser.d.ts
vendored
192
@types/mpd-parser.d.ts
vendored
|
|
@ -1,101 +1,101 @@
|
|||
declare module 'mpd-parser' {
|
||||
export type Segment = {
|
||||
uri: string,
|
||||
timeline: number,
|
||||
duration: number,
|
||||
resolvedUri: string,
|
||||
map: {
|
||||
uri: string,
|
||||
resolvedUri: string,
|
||||
byterange?: {
|
||||
length: number,
|
||||
offset: number
|
||||
}
|
||||
},
|
||||
byterange?: {
|
||||
length: number,
|
||||
offset: number
|
||||
},
|
||||
number: number,
|
||||
presentationTime: number
|
||||
}
|
||||
export type Segment = {
|
||||
uri: string,
|
||||
timeline: number,
|
||||
duration: number,
|
||||
resolvedUri: string,
|
||||
map: {
|
||||
uri: string,
|
||||
resolvedUri: string,
|
||||
byterange?: {
|
||||
length: number,
|
||||
offset: number
|
||||
}
|
||||
},
|
||||
byterange?: {
|
||||
length: number,
|
||||
offset: number
|
||||
},
|
||||
number: number,
|
||||
presentationTime: number
|
||||
}
|
||||
|
||||
export type Sidx = {
|
||||
uri: string,
|
||||
resolvedUri: string,
|
||||
byterange: {
|
||||
length: number,
|
||||
offset: number
|
||||
},
|
||||
map: {
|
||||
uri: string,
|
||||
resolvedUri: string,
|
||||
byterange: {
|
||||
length: number,
|
||||
offset: number
|
||||
}
|
||||
},
|
||||
duration: number,
|
||||
timeline: number,
|
||||
presentationTime: number,
|
||||
number: number
|
||||
}
|
||||
export type Sidx = {
|
||||
uri: string,
|
||||
resolvedUri: string,
|
||||
byterange: {
|
||||
length: number,
|
||||
offset: number
|
||||
},
|
||||
map: {
|
||||
uri: string,
|
||||
resolvedUri: string,
|
||||
byterange: {
|
||||
length: number,
|
||||
offset: number
|
||||
}
|
||||
},
|
||||
duration: number,
|
||||
timeline: number,
|
||||
presentationTime: number,
|
||||
number: number
|
||||
}
|
||||
|
||||
export type Playlist = {
|
||||
attributes: {
|
||||
NAME: string,
|
||||
BANDWIDTH: number,
|
||||
CODECS: string,
|
||||
'PROGRAM-ID': number,
|
||||
// Following for video only
|
||||
'FRAME-RATE'?: number,
|
||||
AUDIO?: string, // audio stream name
|
||||
SUBTITLES?: string,
|
||||
RESOLUTION?: {
|
||||
width: number,
|
||||
height: number
|
||||
}
|
||||
},
|
||||
uri: string,
|
||||
endList: boolean,
|
||||
timeline: number,
|
||||
resolvedUri: string,
|
||||
targetDuration: number,
|
||||
discontinuitySequence: number,
|
||||
discontinuityStarts: [],
|
||||
timelineStarts: {
|
||||
start: number,
|
||||
timeline: number
|
||||
}[],
|
||||
mediaSequence: number,
|
||||
contentProtection?: {
|
||||
[type: string]: {
|
||||
pssh?: Uint8Array
|
||||
}
|
||||
}
|
||||
segments: Segment[]
|
||||
sidx?: Sidx
|
||||
}
|
||||
export type Playlist = {
|
||||
attributes: {
|
||||
NAME: string,
|
||||
BANDWIDTH: number,
|
||||
CODECS: string,
|
||||
'PROGRAM-ID': number,
|
||||
// Following for video only
|
||||
'FRAME-RATE'?: number,
|
||||
AUDIO?: string, // audio stream name
|
||||
SUBTITLES?: string,
|
||||
RESOLUTION?: {
|
||||
width: number,
|
||||
height: number
|
||||
}
|
||||
},
|
||||
uri: string,
|
||||
endList: boolean,
|
||||
timeline: number,
|
||||
resolvedUri: string,
|
||||
targetDuration: number,
|
||||
discontinuitySequence: number,
|
||||
discontinuityStarts: [],
|
||||
timelineStarts: {
|
||||
start: number,
|
||||
timeline: number
|
||||
}[],
|
||||
mediaSequence: number,
|
||||
contentProtection?: {
|
||||
[type: string]: {
|
||||
pssh?: Uint8Array
|
||||
}
|
||||
}
|
||||
segments: Segment[]
|
||||
sidx?: Sidx
|
||||
}
|
||||
|
||||
export type Manifest = {
|
||||
allowCache: boolean,
|
||||
discontinuityStarts: [],
|
||||
segments: [],
|
||||
endList: true,
|
||||
duration: number,
|
||||
playlists: Playlist[],
|
||||
mediaGroups: {
|
||||
AUDIO: {
|
||||
audio: {
|
||||
[name: string]: {
|
||||
language: string,
|
||||
autoselect: boolean,
|
||||
default: boolean,
|
||||
playlists: Playlist[]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
export function parse(manifest: string): Manifest
|
||||
export type Manifest = {
|
||||
allowCache: boolean,
|
||||
discontinuityStarts: [],
|
||||
segments: [],
|
||||
endList: true,
|
||||
duration: number,
|
||||
playlists: Playlist[],
|
||||
mediaGroups: {
|
||||
AUDIO: {
|
||||
audio: {
|
||||
[name: string]: {
|
||||
language: string,
|
||||
autoselect: boolean,
|
||||
default: boolean,
|
||||
playlists: Playlist[]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
export function parse(manifest: string): Manifest
|
||||
}
|
||||
|
|
|
|||
64
@types/newHidiveEpisode.d.ts
vendored
64
@types/newHidiveEpisode.d.ts
vendored
|
|
@ -1,43 +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;
|
||||
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;
|
||||
permission: string;
|
||||
period: string;
|
||||
}
|
||||
|
||||
export interface EpisodeInformation {
|
||||
seasonNumber: number;
|
||||
episodeNumber: number;
|
||||
season: number;
|
||||
seasonNumber: number;
|
||||
episodeNumber: number;
|
||||
season: number;
|
||||
}
|
||||
|
||||
export interface Rating {
|
||||
rating: string;
|
||||
descriptors: any[];
|
||||
rating: string;
|
||||
descriptors: any[];
|
||||
}
|
||||
38
@types/newHidivePlayback.d.ts
vendored
38
@types/newHidivePlayback.d.ts
vendored
|
|
@ -1,33 +1,33 @@
|
|||
export interface NewHidivePlayback {
|
||||
watermark: null;
|
||||
skipMarkers: any[];
|
||||
annotations: null;
|
||||
dash: Format[];
|
||||
hls: Format[];
|
||||
watermark: null;
|
||||
skipMarkers: any[];
|
||||
annotations: null;
|
||||
dash: Format[];
|
||||
hls: Format[];
|
||||
}
|
||||
|
||||
export interface Format {
|
||||
subtitles: Subtitle[];
|
||||
url: string;
|
||||
drm: DRM;
|
||||
subtitles: Subtitle[];
|
||||
url: string;
|
||||
drm: DRM;
|
||||
}
|
||||
|
||||
export interface DRM {
|
||||
encryptionMode: string;
|
||||
containerType: string;
|
||||
jwtToken: string;
|
||||
url: string;
|
||||
keySystems: string[];
|
||||
encryptionMode: string;
|
||||
containerType: string;
|
||||
jwtToken: string;
|
||||
url: string;
|
||||
keySystems: string[];
|
||||
}
|
||||
|
||||
export interface Subtitle {
|
||||
format: Formats;
|
||||
language: string;
|
||||
url: string;
|
||||
format: Formats;
|
||||
language: string;
|
||||
url: string;
|
||||
}
|
||||
|
||||
export enum Formats {
|
||||
Scc = 'scc',
|
||||
Srt = 'srt',
|
||||
Vtt = 'vtt',
|
||||
Scc = 'scc',
|
||||
Srt = 'srt',
|
||||
Vtt = 'vtt',
|
||||
}
|
||||
|
|
|
|||
100
@types/newHidiveSearch.d.ts
vendored
100
@types/newHidiveSearch.d.ts
vendored
|
|
@ -1,88 +1,88 @@
|
|||
export interface NewHidiveSearch {
|
||||
results: Result[];
|
||||
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: object;
|
||||
processingTimeMS: number;
|
||||
processingTimingsMS: ProcessingTimingsMS;
|
||||
serverTimeMS: number;
|
||||
hits: Hit[];
|
||||
nbHits: number;
|
||||
page: number;
|
||||
nbPages: number;
|
||||
hitsPerPage: number;
|
||||
exhaustiveNbHits: boolean;
|
||||
exhaustiveTypo: boolean;
|
||||
exhaustive: Exhaustive;
|
||||
query: string;
|
||||
params: string;
|
||||
index: string;
|
||||
renderingContent: object;
|
||||
processingTimeMS: number;
|
||||
processingTimingsMS: ProcessingTimingsMS;
|
||||
serverTimeMS: number;
|
||||
}
|
||||
|
||||
export interface Exhaustive {
|
||||
nbHits: boolean;
|
||||
typo: boolean;
|
||||
nbHits: boolean;
|
||||
typo: boolean;
|
||||
}
|
||||
|
||||
export interface Hit {
|
||||
type: string;
|
||||
weight: number;
|
||||
id: number;
|
||||
name: string;
|
||||
description: string;
|
||||
meta: object;
|
||||
coverUrl: string;
|
||||
smallCoverUrl: string;
|
||||
seasonsCount: number;
|
||||
tags: string[];
|
||||
localisations: HitLocalisations;
|
||||
ratings: Ratings;
|
||||
objectID: string;
|
||||
_highlightResult: HighlightResult;
|
||||
type: string;
|
||||
weight: number;
|
||||
id: number;
|
||||
name: string;
|
||||
description: string;
|
||||
meta: object;
|
||||
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;
|
||||
name: Description;
|
||||
description: Description;
|
||||
tags: Description[];
|
||||
localisations: HighlightResultLocalisations;
|
||||
}
|
||||
|
||||
export interface Description {
|
||||
value: string;
|
||||
matchLevel: string;
|
||||
matchedWords: string[];
|
||||
fullyHighlighted?: boolean;
|
||||
value: string;
|
||||
matchLevel: string;
|
||||
matchedWords: string[];
|
||||
fullyHighlighted?: boolean;
|
||||
}
|
||||
|
||||
export interface HighlightResultLocalisations {
|
||||
en_US: PurpleEnUS;
|
||||
en_US: PurpleEnUS;
|
||||
}
|
||||
|
||||
export interface PurpleEnUS {
|
||||
title: Description;
|
||||
description: Description;
|
||||
title: Description;
|
||||
description: Description;
|
||||
}
|
||||
|
||||
export interface HitLocalisations {
|
||||
[language: string]: HitLocalization;
|
||||
[language: string]: HitLocalization;
|
||||
}
|
||||
|
||||
export interface HitLocalization {
|
||||
title: string;
|
||||
description: string;
|
||||
title: string;
|
||||
description: string;
|
||||
}
|
||||
|
||||
export interface Ratings {
|
||||
US: string[];
|
||||
US: string[];
|
||||
}
|
||||
|
||||
export interface ProcessingTimingsMS {
|
||||
_request: Request;
|
||||
_request: Request;
|
||||
}
|
||||
|
||||
export interface Request {
|
||||
queue: number;
|
||||
roundTrip: number;
|
||||
queue: number;
|
||||
roundTrip: number;
|
||||
}
|
||||
|
|
|
|||
126
@types/newHidiveSeason.d.ts
vendored
126
@types/newHidiveSeason.d.ts
vendored
|
|
@ -1,89 +1,89 @@
|
|||
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[];
|
||||
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[];
|
||||
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[];
|
||||
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;
|
||||
permission: string;
|
||||
}
|
||||
|
||||
export interface EpisodeInformation {
|
||||
seasonNumber: number;
|
||||
episodeNumber: number;
|
||||
season: number;
|
||||
seasonNumber: number;
|
||||
episodeNumber: number;
|
||||
season: number;
|
||||
}
|
||||
|
||||
export interface Paging {
|
||||
moreDataAvailable: boolean;
|
||||
lastSeen: number;
|
||||
moreDataAvailable: boolean;
|
||||
lastSeen: number;
|
||||
}
|
||||
|
||||
export interface Series {
|
||||
seriesId: number;
|
||||
title: string;
|
||||
description: string;
|
||||
longDescription: string;
|
||||
displayableTags: any[];
|
||||
rating: Rating;
|
||||
contentRating: Rating;
|
||||
seriesId: number;
|
||||
title: string;
|
||||
description: string;
|
||||
longDescription: string;
|
||||
displayableTags: any[];
|
||||
rating: Rating;
|
||||
contentRating: Rating;
|
||||
}
|
||||
|
||||
export interface NewHidiveSeriesExtra extends Series {
|
||||
season: NewHidiveSeason;
|
||||
season: NewHidiveSeason;
|
||||
}
|
||||
|
||||
export interface NewHidiveEpisodeExtra extends Episode {
|
||||
titleId: number;
|
||||
nameLong: string;
|
||||
seasonTitle: string;
|
||||
seriesTitle: string;
|
||||
seriesId?: number;
|
||||
isSelected: boolean;
|
||||
jwtToken?: string;
|
||||
titleId: number;
|
||||
nameLong: string;
|
||||
seasonTitle: string;
|
||||
seriesTitle: string;
|
||||
seriesId?: number;
|
||||
isSelected: boolean;
|
||||
jwtToken?: string;
|
||||
}
|
||||
48
@types/newHidiveSeries.d.ts
vendored
48
@types/newHidiveSeries.d.ts
vendored
|
|
@ -1,35 +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;
|
||||
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[];
|
||||
rating: string;
|
||||
descriptors: any[];
|
||||
}
|
||||
|
||||
export interface Paging {
|
||||
moreDataAvailable: boolean;
|
||||
lastSeen: number;
|
||||
moreDataAvailable: boolean;
|
||||
lastSeen: number;
|
||||
}
|
||||
|
||||
export interface Season {
|
||||
title: string;
|
||||
description: string;
|
||||
longDescription: string;
|
||||
seasonNumber: number;
|
||||
episodeCount: number;
|
||||
displayableTags: any[];
|
||||
id: number;
|
||||
title: string;
|
||||
description: string;
|
||||
longDescription: string;
|
||||
seasonNumber: number;
|
||||
episodeCount: number;
|
||||
displayableTags: any[];
|
||||
id: number;
|
||||
}
|
||||
|
|
|
|||
332
@types/objectInfo.d.ts
vendored
332
@types/objectInfo.d.ts
vendored
|
|
@ -1,211 +1,211 @@
|
|||
// Generated by https://quicktype.io
|
||||
|
||||
export interface ObjectInfo {
|
||||
total: number;
|
||||
data: CrunchyObject[];
|
||||
meta: Record<unknown>;
|
||||
total: number;
|
||||
data: CrunchyObject[];
|
||||
meta: Record<unknown>;
|
||||
}
|
||||
|
||||
export interface CrunchyObject {
|
||||
__links__?: Links;
|
||||
channel_id: string;
|
||||
slug: string;
|
||||
images: Images;
|
||||
linked_resource_key: string;
|
||||
description: string;
|
||||
promo_description: string;
|
||||
external_id: string;
|
||||
title: string;
|
||||
series_metadata?: SeriesMetadata;
|
||||
id: string;
|
||||
slug_title: string;
|
||||
type: string;
|
||||
promo_title: string;
|
||||
movie_listing_metadata?: MovieListingMetadata;
|
||||
movie_metadata?: MovieMetadata;
|
||||
playback?: string;
|
||||
episode_metadata?: EpisodeMetadata;
|
||||
streams_link?: string;
|
||||
season_metadata?: SeasonMetadata;
|
||||
isSelected?: boolean;
|
||||
f_num: string;
|
||||
s_num: string;
|
||||
__links__?: Links;
|
||||
channel_id: string;
|
||||
slug: string;
|
||||
images: Images;
|
||||
linked_resource_key: string;
|
||||
description: string;
|
||||
promo_description: string;
|
||||
external_id: string;
|
||||
title: string;
|
||||
series_metadata?: SeriesMetadata;
|
||||
id: string;
|
||||
slug_title: string;
|
||||
type: string;
|
||||
promo_title: string;
|
||||
movie_listing_metadata?: MovieListingMetadata;
|
||||
movie_metadata?: MovieMetadata;
|
||||
playback?: string;
|
||||
episode_metadata?: EpisodeMetadata;
|
||||
streams_link?: string;
|
||||
season_metadata?: SeasonMetadata;
|
||||
isSelected?: boolean;
|
||||
f_num: string;
|
||||
s_num: string;
|
||||
}
|
||||
|
||||
export interface Links {
|
||||
'episode/season': LinkData;
|
||||
'episode/series': LinkData;
|
||||
resource: LinkData;
|
||||
'resource/channel': LinkData;
|
||||
streams: LinkData;
|
||||
'episode/season': LinkData;
|
||||
'episode/series': LinkData;
|
||||
resource: LinkData;
|
||||
'resource/channel': LinkData;
|
||||
streams: LinkData;
|
||||
}
|
||||
|
||||
export interface LinkData {
|
||||
href: string;
|
||||
href: string;
|
||||
}
|
||||
|
||||
export interface EpisodeMetadata {
|
||||
audio_locale: Locale;
|
||||
availability_ends: Date;
|
||||
availability_notes: string;
|
||||
availability_starts: Date;
|
||||
available_date: null;
|
||||
available_offline: boolean;
|
||||
closed_captions_available: boolean;
|
||||
duration_ms: number;
|
||||
eligible_region: string;
|
||||
episode: string;
|
||||
episode_air_date: Date;
|
||||
episode_number: number;
|
||||
extended_maturity_rating: Record<unknown>;
|
||||
free_available_date: Date;
|
||||
identifier: string;
|
||||
is_clip: boolean;
|
||||
is_dubbed: boolean;
|
||||
is_mature: boolean;
|
||||
is_premium_only: boolean;
|
||||
is_subbed: boolean;
|
||||
mature_blocked: boolean;
|
||||
maturity_ratings: string[];
|
||||
premium_available_date: Date;
|
||||
premium_date: null;
|
||||
season_id: string;
|
||||
season_number: number;
|
||||
season_slug_title: string;
|
||||
season_title: string;
|
||||
sequence_number: number;
|
||||
series_id: string;
|
||||
series_slug_title: string;
|
||||
series_title: string;
|
||||
subtitle_locales: Locale[];
|
||||
tenant_categories?: string[];
|
||||
upload_date: Date;
|
||||
versions: EpisodeMetadataVersion[];
|
||||
audio_locale: Locale;
|
||||
availability_ends: Date;
|
||||
availability_notes: string;
|
||||
availability_starts: Date;
|
||||
available_date: null;
|
||||
available_offline: boolean;
|
||||
closed_captions_available: boolean;
|
||||
duration_ms: number;
|
||||
eligible_region: string;
|
||||
episode: string;
|
||||
episode_air_date: Date;
|
||||
episode_number: number;
|
||||
extended_maturity_rating: Record<unknown>;
|
||||
free_available_date: Date;
|
||||
identifier: string;
|
||||
is_clip: boolean;
|
||||
is_dubbed: boolean;
|
||||
is_mature: boolean;
|
||||
is_premium_only: boolean;
|
||||
is_subbed: boolean;
|
||||
mature_blocked: boolean;
|
||||
maturity_ratings: string[];
|
||||
premium_available_date: Date;
|
||||
premium_date: null;
|
||||
season_id: string;
|
||||
season_number: number;
|
||||
season_slug_title: string;
|
||||
season_title: string;
|
||||
sequence_number: number;
|
||||
series_id: string;
|
||||
series_slug_title: string;
|
||||
series_title: string;
|
||||
subtitle_locales: Locale[];
|
||||
tenant_categories?: string[];
|
||||
upload_date: Date;
|
||||
versions: EpisodeMetadataVersion[];
|
||||
}
|
||||
|
||||
export interface EpisodeMetadataVersion {
|
||||
audio_locale: Locale;
|
||||
guid: string;
|
||||
is_premium_only: boolean;
|
||||
media_guid: string;
|
||||
original: boolean;
|
||||
season_guid: string;
|
||||
variant: string;
|
||||
audio_locale: Locale;
|
||||
guid: string;
|
||||
is_premium_only: boolean;
|
||||
media_guid: string;
|
||||
original: boolean;
|
||||
season_guid: string;
|
||||
variant: string;
|
||||
}
|
||||
|
||||
export interface Images {
|
||||
poster_tall?: Array<Image[]>;
|
||||
poster_wide?: Array<Image[]>;
|
||||
promo_image?: Array<Image[]>;
|
||||
thumbnail?: Array<Image[]>;
|
||||
poster_tall?: Array<Image[]>;
|
||||
poster_wide?: Array<Image[]>;
|
||||
promo_image?: Array<Image[]>;
|
||||
thumbnail?: Array<Image[]>;
|
||||
}
|
||||
|
||||
export interface Image {
|
||||
height: number;
|
||||
source: string;
|
||||
type: ImageType;
|
||||
width: number;
|
||||
height: number;
|
||||
source: string;
|
||||
type: ImageType;
|
||||
width: number;
|
||||
}
|
||||
|
||||
export enum ImageType {
|
||||
PosterTall = 'poster_tall',
|
||||
PosterWide = 'poster_wide',
|
||||
PromoImage = 'promo_image',
|
||||
Thumbnail = 'thumbnail',
|
||||
PosterTall = 'poster_tall',
|
||||
PosterWide = 'poster_wide',
|
||||
PromoImage = 'promo_image',
|
||||
Thumbnail = 'thumbnail',
|
||||
}
|
||||
|
||||
export interface MovieListingMetadata {
|
||||
availability_notes: string;
|
||||
available_date: null;
|
||||
available_offline: boolean;
|
||||
duration_ms: number;
|
||||
extended_description: string;
|
||||
extended_maturity_rating: Record<unknown>;
|
||||
first_movie_id: string;
|
||||
free_available_date: Date;
|
||||
is_dubbed: boolean;
|
||||
is_mature: boolean;
|
||||
is_premium_only: boolean;
|
||||
is_subbed: boolean;
|
||||
mature_blocked: boolean;
|
||||
maturity_ratings: string[];
|
||||
movie_release_year: number;
|
||||
premium_available_date: Date;
|
||||
premium_date: null;
|
||||
subtitle_locales: Locale[];
|
||||
tenant_categories: string[];
|
||||
availability_notes: string;
|
||||
available_date: null;
|
||||
available_offline: boolean;
|
||||
duration_ms: number;
|
||||
extended_description: string;
|
||||
extended_maturity_rating: Record<unknown>;
|
||||
first_movie_id: string;
|
||||
free_available_date: Date;
|
||||
is_dubbed: boolean;
|
||||
is_mature: boolean;
|
||||
is_premium_only: boolean;
|
||||
is_subbed: boolean;
|
||||
mature_blocked: boolean;
|
||||
maturity_ratings: string[];
|
||||
movie_release_year: number;
|
||||
premium_available_date: Date;
|
||||
premium_date: null;
|
||||
subtitle_locales: Locale[];
|
||||
tenant_categories: string[];
|
||||
}
|
||||
|
||||
export interface MovieMetadata {
|
||||
availability_notes: string;
|
||||
available_offline: boolean;
|
||||
closed_captions_available: boolean;
|
||||
duration_ms: number;
|
||||
extended_maturity_rating: Record<unknown>;
|
||||
is_dubbed: boolean;
|
||||
is_mature: boolean;
|
||||
is_premium_only: boolean;
|
||||
is_subbed: boolean;
|
||||
mature_blocked: boolean;
|
||||
maturity_ratings: string[];
|
||||
movie_listing_id: string;
|
||||
movie_listing_slug_title: string;
|
||||
movie_listing_title: string;
|
||||
availability_notes: string;
|
||||
available_offline: boolean;
|
||||
closed_captions_available: boolean;
|
||||
duration_ms: number;
|
||||
extended_maturity_rating: Record<unknown>;
|
||||
is_dubbed: boolean;
|
||||
is_mature: boolean;
|
||||
is_premium_only: boolean;
|
||||
is_subbed: boolean;
|
||||
mature_blocked: boolean;
|
||||
maturity_ratings: string[];
|
||||
movie_listing_id: string;
|
||||
movie_listing_slug_title: string;
|
||||
movie_listing_title: string;
|
||||
}
|
||||
|
||||
export interface SeasonMetadata {
|
||||
audio_locale: Locale;
|
||||
audio_locales: Locale[];
|
||||
extended_maturity_rating: Record<unknown>;
|
||||
identifier: string;
|
||||
is_mature: boolean;
|
||||
mature_blocked: boolean;
|
||||
maturity_ratings: string[];
|
||||
season_display_number: string;
|
||||
season_sequence_number: number;
|
||||
subtitle_locales: Locale[];
|
||||
versions: SeasonMetadataVersion[];
|
||||
audio_locale: Locale;
|
||||
audio_locales: Locale[];
|
||||
extended_maturity_rating: Record<unknown>;
|
||||
identifier: string;
|
||||
is_mature: boolean;
|
||||
mature_blocked: boolean;
|
||||
maturity_ratings: string[];
|
||||
season_display_number: string;
|
||||
season_sequence_number: number;
|
||||
subtitle_locales: Locale[];
|
||||
versions: SeasonMetadataVersion[];
|
||||
}
|
||||
|
||||
export interface SeasonMetadataVersion {
|
||||
audio_locale: Locale;
|
||||
guid: string;
|
||||
original: boolean;
|
||||
variant: string;
|
||||
audio_locale: Locale;
|
||||
guid: string;
|
||||
original: boolean;
|
||||
variant: string;
|
||||
}
|
||||
export interface SeriesMetadata {
|
||||
audio_locales: Locale[];
|
||||
availability_notes: string;
|
||||
episode_count: number;
|
||||
extended_description: string;
|
||||
extended_maturity_rating: Record<unknown>;
|
||||
is_dubbed: boolean;
|
||||
is_mature: boolean;
|
||||
is_simulcast: boolean;
|
||||
is_subbed: boolean;
|
||||
mature_blocked: boolean;
|
||||
maturity_ratings: string[];
|
||||
season_count: number;
|
||||
series_launch_year: number;
|
||||
subtitle_locales: Locale[];
|
||||
tenant_categories?: string[];
|
||||
audio_locales: Locale[];
|
||||
availability_notes: string;
|
||||
episode_count: number;
|
||||
extended_description: string;
|
||||
extended_maturity_rating: Record<unknown>;
|
||||
is_dubbed: boolean;
|
||||
is_mature: boolean;
|
||||
is_simulcast: boolean;
|
||||
is_subbed: boolean;
|
||||
mature_blocked: boolean;
|
||||
maturity_ratings: string[];
|
||||
season_count: number;
|
||||
series_launch_year: number;
|
||||
subtitle_locales: Locale[];
|
||||
tenant_categories?: string[];
|
||||
}
|
||||
|
||||
export enum Locale {
|
||||
enUS = 'en-US',
|
||||
esLA = 'es-LA',
|
||||
es419 = 'es-419',
|
||||
esES = 'es-ES',
|
||||
ptBR = 'pt-BR',
|
||||
frFR = 'fr-FR',
|
||||
deDE = 'de-DE',
|
||||
arME = 'ar-ME',
|
||||
arSA = 'ar-SA',
|
||||
itIT = 'it-IT',
|
||||
ruRU = 'ru-RU',
|
||||
trTR = 'tr-TR',
|
||||
hiIN = 'hi-IN',
|
||||
zhCN = 'zh-CN',
|
||||
koKR = 'ko-KR',
|
||||
jaJP = 'ja-JP',
|
||||
enUS = 'en-US',
|
||||
esLA = 'es-LA',
|
||||
es419 = 'es-419',
|
||||
esES = 'es-ES',
|
||||
ptBR = 'pt-BR',
|
||||
frFR = 'fr-FR',
|
||||
deDE = 'de-DE',
|
||||
arME = 'ar-ME',
|
||||
arSA = 'ar-SA',
|
||||
itIT = 'it-IT',
|
||||
ruRU = 'ru-RU',
|
||||
trTR = 'tr-TR',
|
||||
hiIN = 'hi-IN',
|
||||
zhCN = 'zh-CN',
|
||||
koKR = 'ko-KR',
|
||||
jaJP = 'ja-JP',
|
||||
}
|
||||
2
@types/pkg.d.ts
vendored
2
@types/pkg.d.ts
vendored
|
|
@ -1,3 +1,3 @@
|
|||
declare module 'pkg' {
|
||||
export async function exec(config: string[]);
|
||||
export async function exec(config: string[]);
|
||||
}
|
||||
188
@types/playbackData.d.ts
vendored
188
@types/playbackData.d.ts
vendored
|
|
@ -1,120 +1,120 @@
|
|||
// Generated by https://quicktype.io
|
||||
export interface PlaybackData {
|
||||
total: number;
|
||||
vpb: { [key: string]: { [key: string]: StreamDetails } };
|
||||
apb: { [key: string]: { [key: string]: StreamDetails } };
|
||||
meta: Meta;
|
||||
total: number;
|
||||
vpb: { [key: string]: { [key: string]: StreamDetails } };
|
||||
apb: { [key: string]: { [key: string]: StreamDetails } };
|
||||
meta: Meta;
|
||||
}
|
||||
|
||||
export interface StreamList {
|
||||
download_hls: CrunchyStreams;
|
||||
drm_adaptive_hls: CrunchyStreams;
|
||||
multitrack_adaptive_hls_v2: CrunchyStreams;
|
||||
vo_adaptive_hls: CrunchyStreams;
|
||||
vo_drm_adaptive_hls: CrunchyStreams;
|
||||
adaptive_hls: CrunchyStreams;
|
||||
drm_download_dash: CrunchyStreams;
|
||||
drm_download_hls: CrunchyStreams;
|
||||
drm_multitrack_adaptive_hls_v2: CrunchyStreams;
|
||||
vo_drm_adaptive_dash: CrunchyStreams;
|
||||
adaptive_dash: CrunchyStreams;
|
||||
urls: CrunchyStreams;
|
||||
vo_adaptive_dash: CrunchyStreams;
|
||||
download_dash: CrunchyStreams;
|
||||
drm_adaptive_dash: CrunchyStreams;
|
||||
download_hls: CrunchyStreams;
|
||||
drm_adaptive_hls: CrunchyStreams;
|
||||
multitrack_adaptive_hls_v2: CrunchyStreams;
|
||||
vo_adaptive_hls: CrunchyStreams;
|
||||
vo_drm_adaptive_hls: CrunchyStreams;
|
||||
adaptive_hls: CrunchyStreams;
|
||||
drm_download_dash: CrunchyStreams;
|
||||
drm_download_hls: CrunchyStreams;
|
||||
drm_multitrack_adaptive_hls_v2: CrunchyStreams;
|
||||
vo_drm_adaptive_dash: CrunchyStreams;
|
||||
adaptive_dash: CrunchyStreams;
|
||||
urls: CrunchyStreams;
|
||||
vo_adaptive_dash: CrunchyStreams;
|
||||
download_dash: CrunchyStreams;
|
||||
drm_adaptive_dash: CrunchyStreams;
|
||||
}
|
||||
|
||||
export interface CrunchyStreams {
|
||||
'': StreamDetails;
|
||||
'en-US'?: StreamDetails;
|
||||
'es-LA'?: StreamDetails;
|
||||
'es-419'?: StreamDetails;
|
||||
'es-ES'?: StreamDetails;
|
||||
'pt-BR'?: StreamDetails;
|
||||
'fr-FR'?: StreamDetails;
|
||||
'de-DE'?: StreamDetails;
|
||||
'ar-ME'?: StreamDetails;
|
||||
'ar-SA'?: StreamDetails;
|
||||
'it-IT'?: StreamDetails;
|
||||
'ru-RU'?: StreamDetails;
|
||||
'tr-TR'?: StreamDetails;
|
||||
'hi-IN'?: StreamDetails;
|
||||
'zh-CN'?: StreamDetails;
|
||||
'ko-KR'?: StreamDetails;
|
||||
'ja-JP'?: StreamDetails;
|
||||
[string: string]: StreamDetails;
|
||||
'': StreamDetails;
|
||||
'en-US'?: StreamDetails;
|
||||
'es-LA'?: StreamDetails;
|
||||
'es-419'?: StreamDetails;
|
||||
'es-ES'?: StreamDetails;
|
||||
'pt-BR'?: StreamDetails;
|
||||
'fr-FR'?: StreamDetails;
|
||||
'de-DE'?: StreamDetails;
|
||||
'ar-ME'?: StreamDetails;
|
||||
'ar-SA'?: StreamDetails;
|
||||
'it-IT'?: StreamDetails;
|
||||
'ru-RU'?: StreamDetails;
|
||||
'tr-TR'?: StreamDetails;
|
||||
'hi-IN'?: StreamDetails;
|
||||
'zh-CN'?: StreamDetails;
|
||||
'ko-KR'?: StreamDetails;
|
||||
'ja-JP'?: StreamDetails;
|
||||
[string: string]: StreamDetails;
|
||||
}
|
||||
|
||||
export interface StreamDetails {
|
||||
//hardsub_locale: Locale;
|
||||
hardsub_locale: string;
|
||||
url: string;
|
||||
hardsub_lang?: string;
|
||||
audio_lang?: string;
|
||||
type?: string;
|
||||
//hardsub_locale: Locale;
|
||||
hardsub_locale: string;
|
||||
url: string;
|
||||
hardsub_lang?: string;
|
||||
audio_lang?: string;
|
||||
type?: string;
|
||||
}
|
||||
export interface Meta {
|
||||
media_id: string;
|
||||
subtitles: Subtitles;
|
||||
bifs: string[];
|
||||
versions: Version[];
|
||||
audio_locale: Locale;
|
||||
closed_captions: Subtitles;
|
||||
captions: Subtitles;
|
||||
media_id: string;
|
||||
subtitles: Subtitles;
|
||||
bifs: string[];
|
||||
versions: Version[];
|
||||
audio_locale: Locale;
|
||||
closed_captions: Subtitles;
|
||||
captions: Subtitles;
|
||||
}
|
||||
|
||||
export interface Subtitles {
|
||||
''?: SubtitleInfo;
|
||||
'en-US'?: SubtitleInfo;
|
||||
'es-LA'?: SubtitleInfo;
|
||||
'es-419'?: SubtitleInfo;
|
||||
'es-ES'?: SubtitleInfo;
|
||||
'pt-BR'?: SubtitleInfo;
|
||||
'fr-FR'?: SubtitleInfo;
|
||||
'de-DE'?: SubtitleInfo;
|
||||
'ar-ME'?: SubtitleInfo;
|
||||
'ar-SA'?: SubtitleInfo;
|
||||
'it-IT'?: SubtitleInfo;
|
||||
'ru-RU'?: SubtitleInfo;
|
||||
'tr-TR'?: SubtitleInfo;
|
||||
'hi-IN'?: SubtitleInfo;
|
||||
'zh-CN'?: SubtitleInfo;
|
||||
'ko-KR'?: SubtitleInfo;
|
||||
'ja-JP'?: SubtitleInfo;
|
||||
''?: SubtitleInfo;
|
||||
'en-US'?: SubtitleInfo;
|
||||
'es-LA'?: SubtitleInfo;
|
||||
'es-419'?: SubtitleInfo;
|
||||
'es-ES'?: SubtitleInfo;
|
||||
'pt-BR'?: SubtitleInfo;
|
||||
'fr-FR'?: SubtitleInfo;
|
||||
'de-DE'?: SubtitleInfo;
|
||||
'ar-ME'?: SubtitleInfo;
|
||||
'ar-SA'?: SubtitleInfo;
|
||||
'it-IT'?: SubtitleInfo;
|
||||
'ru-RU'?: SubtitleInfo;
|
||||
'tr-TR'?: SubtitleInfo;
|
||||
'hi-IN'?: SubtitleInfo;
|
||||
'zh-CN'?: SubtitleInfo;
|
||||
'ko-KR'?: SubtitleInfo;
|
||||
'ja-JP'?: SubtitleInfo;
|
||||
}
|
||||
|
||||
|
||||
export interface SubtitleInfo {
|
||||
format: string;
|
||||
locale: Locale;
|
||||
url: string;
|
||||
format: string;
|
||||
locale: Locale;
|
||||
url: string;
|
||||
}
|
||||
export interface Version {
|
||||
audio_locale: Locale;
|
||||
guid: string;
|
||||
is_premium_only: boolean;
|
||||
media_guid: string;
|
||||
original: boolean;
|
||||
season_guid: string;
|
||||
variant: string;
|
||||
audio_locale: Locale;
|
||||
guid: string;
|
||||
is_premium_only: boolean;
|
||||
media_guid: string;
|
||||
original: boolean;
|
||||
season_guid: string;
|
||||
variant: string;
|
||||
}
|
||||
|
||||
export enum Locale {
|
||||
default = '',
|
||||
enUS = 'en-US',
|
||||
esLA = 'es-LA',
|
||||
es419 = 'es-419',
|
||||
esES = 'es-ES',
|
||||
ptBR = 'pt-BR',
|
||||
frFR = 'fr-FR',
|
||||
deDE = 'de-DE',
|
||||
arME = 'ar-ME',
|
||||
arSA = 'ar-SA',
|
||||
itIT = 'it-IT',
|
||||
ruRU = 'ru-RU',
|
||||
trTR = 'tr-TR',
|
||||
hiIN = 'hi-IN',
|
||||
zhCN = 'zh-CN',
|
||||
koKR = 'ko-KR',
|
||||
jaJP = 'ja-JP',
|
||||
default = '',
|
||||
enUS = 'en-US',
|
||||
esLA = 'es-LA',
|
||||
es419 = 'es-419',
|
||||
esES = 'es-ES',
|
||||
ptBR = 'pt-BR',
|
||||
frFR = 'fr-FR',
|
||||
deDE = 'de-DE',
|
||||
arME = 'ar-ME',
|
||||
arSA = 'ar-SA',
|
||||
itIT = 'it-IT',
|
||||
ruRU = 'ru-RU',
|
||||
trTR = 'tr-TR',
|
||||
hiIN = 'hi-IN',
|
||||
zhCN = 'zh-CN',
|
||||
koKR = 'ko-KR',
|
||||
jaJP = 'ja-JP',
|
||||
}
|
||||
12
@types/randomEvents.d.ts
vendored
12
@types/randomEvents.d.ts
vendored
|
|
@ -1,15 +1,15 @@
|
|||
import { ExtendedProgress, QueueItem } from './messageHandler';
|
||||
|
||||
export type RandomEvents = {
|
||||
progress: ExtendedProgress,
|
||||
finish: undefined,
|
||||
queueChange: QueueItem[],
|
||||
current: QueueItem|undefined
|
||||
progress: ExtendedProgress,
|
||||
finish: undefined,
|
||||
queueChange: QueueItem[],
|
||||
current: QueueItem|undefined
|
||||
}
|
||||
|
||||
export interface RandomEvent<T extends keyof RandomEvents> {
|
||||
name: T,
|
||||
data: RandomEvents[T]
|
||||
name: T,
|
||||
data: RandomEvents[T]
|
||||
}
|
||||
|
||||
export type Handler<T extends keyof RandomEvents> = (data: RandomEvent<T>) => unknown;
|
||||
2
@types/removeNPMAbsolutePaths.d.ts
vendored
2
@types/removeNPMAbsolutePaths.d.ts
vendored
|
|
@ -1,3 +1,3 @@
|
|||
declare module 'removeNPMAbsolutePaths' {
|
||||
export default async function modulesCleanup(path: string);
|
||||
export default async function modulesCleanup(path: string);
|
||||
}
|
||||
2
@types/serviceClassInterface.d.ts
vendored
2
@types/serviceClassInterface.d.ts
vendored
|
|
@ -1,3 +1,3 @@
|
|||
export interface ServiceClass {
|
||||
cli: () => Promise<boolean|undefined|void>
|
||||
cli: () => Promise<boolean|undefined|void>
|
||||
}
|
||||
30
@types/streamData.d.ts
vendored
30
@types/streamData.d.ts
vendored
|
|
@ -1,28 +1,28 @@
|
|||
// Generated by https://quicktype.io
|
||||
|
||||
export interface StreamData {
|
||||
items: Item[];
|
||||
watchHistorySaveInterval: number;
|
||||
errors?: Error[]
|
||||
items: Item[];
|
||||
watchHistorySaveInterval: number;
|
||||
errors?: Error[]
|
||||
}
|
||||
|
||||
export interface Error {
|
||||
detail: string,
|
||||
code: number
|
||||
detail: string,
|
||||
code: number
|
||||
}
|
||||
|
||||
export interface Item {
|
||||
src: string;
|
||||
kind: string;
|
||||
isPromo: boolean;
|
||||
videoType: string;
|
||||
aips: Aip[];
|
||||
experienceId: string;
|
||||
showAds: boolean;
|
||||
id: number;
|
||||
src: string;
|
||||
kind: string;
|
||||
isPromo: boolean;
|
||||
videoType: string;
|
||||
aips: Aip[];
|
||||
experienceId: string;
|
||||
showAds: boolean;
|
||||
id: number;
|
||||
}
|
||||
|
||||
export interface Aip {
|
||||
out: number;
|
||||
in: number;
|
||||
out: number;
|
||||
in: number;
|
||||
}
|
||||
|
|
|
|||
4
@types/updateFile.d.ts
vendored
4
@types/updateFile.d.ts
vendored
|
|
@ -1,4 +1,4 @@
|
|||
export type UpdateFile = {
|
||||
lastCheck: number,
|
||||
nextCheck: number
|
||||
lastCheck: number,
|
||||
nextCheck: number
|
||||
}
|
||||
62
@types/ws.d.ts
vendored
62
@types/ws.d.ts
vendored
|
|
@ -2,44 +2,44 @@ import { GUIConfig } from '../modules/module.cfg-loader';
|
|||
import { AuthResponse, CheckTokenResponse, EpisodeListResponse, FolderTypes, QueueItem, ResolveItemsData, SearchData, SearchResponse } from './messageHandler';
|
||||
|
||||
export type WSMessage<T extends keyof MessageTypes, P extends 0|1 = 0> = {
|
||||
name: T,
|
||||
data: MessageTypes[T][P]
|
||||
name: T,
|
||||
data: MessageTypes[T][P]
|
||||
}
|
||||
|
||||
export type WSMessageWithID<T extends keyof MessageTypes, P extends 0|1 = 0> = WSMessage<T, P> & {
|
||||
id: string
|
||||
id: string
|
||||
}
|
||||
|
||||
export type UnknownWSMessage = {
|
||||
name: keyof MessageTypes,
|
||||
data: MessageTypes[keyof MessageTypes][0],
|
||||
id: string
|
||||
name: keyof MessageTypes,
|
||||
data: MessageTypes[keyof MessageTypes][0],
|
||||
id: string
|
||||
}
|
||||
|
||||
export type MessageTypes = {
|
||||
'auth': [AuthData, AuthResponse],
|
||||
'version': [undefined, string],
|
||||
'checkToken': [undefined, CheckTokenResponse],
|
||||
'search': [SearchData, SearchResponse],
|
||||
'default': [string, unknown],
|
||||
'availableDubCodes': [undefined, string[]],
|
||||
'availableSubCodes': [undefined, string[]],
|
||||
'resolveItems': [ResolveItemsData, boolean],
|
||||
'listEpisodes': [string, EpisodeListResponse],
|
||||
'downloadItem': [QueueItem, undefined],
|
||||
'isDownloading': [undefined, boolean],
|
||||
'openFolder': [FolderTypes, undefined],
|
||||
'changeProvider': [undefined, boolean],
|
||||
'type': [undefined, 'crunchy'|'hidive'|'ao'|'adn'|undefined],
|
||||
'setup': ['crunchy'|'hidive'|'ao'|'adn'|undefined, undefined],
|
||||
'openFile': [[FolderTypes, string], undefined],
|
||||
'openURL': [string, undefined],
|
||||
'isSetup': [undefined, boolean],
|
||||
'setupServer': [GUIConfig, boolean],
|
||||
'requirePassword': [undefined, boolean],
|
||||
'getQueue': [undefined, QueueItem[]],
|
||||
'removeFromQueue': [number, undefined],
|
||||
'clearQueue': [undefined, undefined],
|
||||
'setDownloadQueue': [boolean, undefined],
|
||||
'getDownloadQueue': [undefined, boolean]
|
||||
'auth': [AuthData, AuthResponse],
|
||||
'version': [undefined, string],
|
||||
'checkToken': [undefined, CheckTokenResponse],
|
||||
'search': [SearchData, SearchResponse],
|
||||
'default': [string, unknown],
|
||||
'availableDubCodes': [undefined, string[]],
|
||||
'availableSubCodes': [undefined, string[]],
|
||||
'resolveItems': [ResolveItemsData, boolean],
|
||||
'listEpisodes': [string, EpisodeListResponse],
|
||||
'downloadItem': [QueueItem, undefined],
|
||||
'isDownloading': [undefined, boolean],
|
||||
'openFolder': [FolderTypes, undefined],
|
||||
'changeProvider': [undefined, boolean],
|
||||
'type': [undefined, 'crunchy'|'hidive'|'ao'|'adn'|undefined],
|
||||
'setup': ['crunchy'|'hidive'|'ao'|'adn'|undefined, undefined],
|
||||
'openFile': [[FolderTypes, string], undefined],
|
||||
'openURL': [string, undefined],
|
||||
'isSetup': [undefined, boolean],
|
||||
'setupServer': [GUIConfig, boolean],
|
||||
'requirePassword': [undefined, boolean],
|
||||
'getQueue': [undefined, QueueItem[]],
|
||||
'removeFromQueue': [number, undefined],
|
||||
'clearQueue': [undefined, undefined],
|
||||
'setDownloadQueue': [boolean, undefined],
|
||||
'getDownloadQueue': [undefined, boolean]
|
||||
}
|
||||
|
|
@ -56,8 +56,10 @@ export default tseslint.config(
|
|||
files: ['gui/react/**/*'],
|
||||
rules: {
|
||||
'no-console': 0,
|
||||
// Disabled because ESLint bugs around on .tsx files somehow?
|
||||
indent: 'off'
|
||||
'indent': [
|
||||
'error',
|
||||
4
|
||||
],
|
||||
}
|
||||
}
|
||||
);
|
||||
|
|
@ -1,3 +1,3 @@
|
|||
{
|
||||
"presets": ["@babel/preset-env","@babel/preset-react", "@babel/preset-typescript"]
|
||||
"presets": ["@babel/preset-env","@babel/preset-react", "@babel/preset-typescript"]
|
||||
}
|
||||
|
|
@ -1,57 +1,57 @@
|
|||
{
|
||||
"name": "anidl-gui",
|
||||
"version": "1.0.0",
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@emotion/react": "^11.14.0",
|
||||
"@emotion/styled": "^11.14.0",
|
||||
"@mui/icons-material": "^7.1.2",
|
||||
"@mui/lab": "7.0.0-beta.12",
|
||||
"@mui/material": "^7.1.2",
|
||||
"concurrently": "^9.2.0",
|
||||
"notistack": "^3.0.2",
|
||||
"react": "^19.1.0",
|
||||
"react-dom": "^19.1.0",
|
||||
"typescript": "^5.8.3",
|
||||
"uuid": "^11.1.0",
|
||||
"ws": "^8.18.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/cli": "^7.27.2",
|
||||
"@babel/core": "^7.27.4",
|
||||
"@babel/preset-env": "^7.27.2",
|
||||
"@babel/preset-react": "^7.27.1",
|
||||
"@babel/preset-typescript": "^7.27.1",
|
||||
"@types/node": "^22.15.32",
|
||||
"@types/react": "^19.1.8",
|
||||
"@types/react-dom": "^19.1.6",
|
||||
"@types/uuid": "^10.0.0",
|
||||
"babel-loader": "^10.0.0",
|
||||
"css-loader": "^7.1.2",
|
||||
"html-webpack-plugin": "^5.6.3",
|
||||
"style-loader": "^4.0.0",
|
||||
"ts-node": "^10.9.2",
|
||||
"webpack": "^5.99.9",
|
||||
"webpack-cli": "^6.0.1",
|
||||
"webpack-dev-server": "^5.2.2"
|
||||
},
|
||||
"proxy": "http://localhost:3000",
|
||||
"scripts": {
|
||||
"build": "npx tsc && npx webpack",
|
||||
"start": "npx concurrently -k npm:frontend npm:backend",
|
||||
"frontend": "npx webpack-dev-server",
|
||||
"backend": "npx ts-node -T ../../gui.ts"
|
||||
},
|
||||
"browserslist": {
|
||||
"production": [
|
||||
">0.2%",
|
||||
"not dead",
|
||||
"not op_mini all"
|
||||
],
|
||||
"development": [
|
||||
"last 1 chrome version",
|
||||
"last 1 firefox version",
|
||||
"last 1 safari version"
|
||||
]
|
||||
}
|
||||
}
|
||||
"name": "anidl-gui",
|
||||
"version": "1.0.0",
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@emotion/react": "^11.14.0",
|
||||
"@emotion/styled": "^11.14.0",
|
||||
"@mui/icons-material": "^7.1.2",
|
||||
"@mui/lab": "7.0.0-beta.12",
|
||||
"@mui/material": "^7.1.2",
|
||||
"concurrently": "^9.2.0",
|
||||
"notistack": "^3.0.2",
|
||||
"react": "^19.1.0",
|
||||
"react-dom": "^19.1.0",
|
||||
"typescript": "^5.8.3",
|
||||
"uuid": "^11.1.0",
|
||||
"ws": "^8.18.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/cli": "^7.27.2",
|
||||
"@babel/core": "^7.27.4",
|
||||
"@babel/preset-env": "^7.27.2",
|
||||
"@babel/preset-react": "^7.27.1",
|
||||
"@babel/preset-typescript": "^7.27.1",
|
||||
"@types/node": "^22.15.32",
|
||||
"@types/react": "^19.1.8",
|
||||
"@types/react-dom": "^19.1.6",
|
||||
"@types/uuid": "^10.0.0",
|
||||
"babel-loader": "^10.0.0",
|
||||
"css-loader": "^7.1.2",
|
||||
"html-webpack-plugin": "^5.6.3",
|
||||
"style-loader": "^4.0.0",
|
||||
"ts-node": "^10.9.2",
|
||||
"webpack": "^5.99.9",
|
||||
"webpack-cli": "^6.0.1",
|
||||
"webpack-dev-server": "^5.2.2"
|
||||
},
|
||||
"proxy": "http://localhost:3000",
|
||||
"scripts": {
|
||||
"build": "npx tsc && npx webpack",
|
||||
"start": "npx concurrently -k npm:frontend npm:backend",
|
||||
"frontend": "npx webpack-dev-server",
|
||||
"backend": "npx ts-node -T ../../gui.ts"
|
||||
},
|
||||
"browserslist": {
|
||||
"production": [
|
||||
">0.2%",
|
||||
"not dead",
|
||||
"not op_mini all"
|
||||
],
|
||||
"development": [
|
||||
"last 1 chrome version",
|
||||
"last 1 firefox version",
|
||||
"last 1 safari version"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
@ -1,15 +1,15 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Multi Downloader</title>
|
||||
<link rel="icon" type="image/webp" href="favicon.webp">
|
||||
<meta charset="UTF-8">
|
||||
<meta
|
||||
http-equiv="Content-Security-Policy"
|
||||
content="script-src 'self' 'unsafe-eval'"
|
||||
/>
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
</body>
|
||||
<head>
|
||||
<title>Multi Downloader</title>
|
||||
<link rel="icon" type="image/webp" href="favicon.webp">
|
||||
<meta charset="UTF-8">
|
||||
<meta
|
||||
http-equiv="Content-Security-Policy"
|
||||
content="script-src 'self' 'unsafe-eval'"
|
||||
/>
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
2
gui/react/src/@types/FC.d.ts
vendored
2
gui/react/src/@types/FC.d.ts
vendored
|
|
@ -1,3 +1,3 @@
|
|||
type FCWithChildren<T = object> = React.FC<{
|
||||
children?: React.ReactNode[]|React.ReactNode
|
||||
children?: React.ReactNode[]|React.ReactNode
|
||||
} & T>
|
||||
|
|
@ -2,9 +2,9 @@ import React from 'react';
|
|||
import Layout from './Layout';
|
||||
|
||||
const App: React.FC = () => {
|
||||
return (
|
||||
<Layout />
|
||||
);
|
||||
return (
|
||||
<Layout />
|
||||
);
|
||||
};
|
||||
|
||||
export default App;
|
||||
|
|
|
|||
|
|
@ -11,28 +11,28 @@ import MenuBar from './components/MenuBar/MenuBar';
|
|||
|
||||
const Layout: React.FC = () => {
|
||||
|
||||
const messageHandler = React.useContext(messageChannelContext);
|
||||
const messageHandler = React.useContext(messageChannelContext);
|
||||
|
||||
return <Box sx={{ display: 'flex', flexDirection: 'column', justifyContent: 'center', width: '100%', alignItems: 'center',}}>
|
||||
<MenuBar />
|
||||
<Box sx={{
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'space-between',
|
||||
width: '93vw',
|
||||
maxWidth: '93rem',
|
||||
maxHeight: '3rem'
|
||||
//backgroundColor: '#ffffff',
|
||||
}}>
|
||||
<LogoutButton />
|
||||
<AuthButton />
|
||||
<Button variant="contained" startIcon={<Folder />} onClick={() => messageHandler?.openFolder('content')} sx={{ height: '37px' }}>Open Output Directory</Button>
|
||||
<Button variant="contained" startIcon={<ClearAll />} onClick={() => messageHandler?.clearQueue() } sx={{ height: '37px' }}>Clear Queue</Button>
|
||||
<AddToQueue />
|
||||
<StartQueueButton />
|
||||
</Box>
|
||||
<MainFrame />
|
||||
</Box>;
|
||||
return <Box sx={{ display: 'flex', flexDirection: 'column', justifyContent: 'center', width: '100%', alignItems: 'center',}}>
|
||||
<MenuBar />
|
||||
<Box sx={{
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'space-between',
|
||||
width: '93vw',
|
||||
maxWidth: '93rem',
|
||||
maxHeight: '3rem'
|
||||
//backgroundColor: '#ffffff',
|
||||
}}>
|
||||
<LogoutButton />
|
||||
<AuthButton />
|
||||
<Button variant="contained" startIcon={<Folder />} onClick={() => messageHandler?.openFolder('content')} sx={{ height: '37px' }}>Open Output Directory</Button>
|
||||
<Button variant="contained" startIcon={<ClearAll />} onClick={() => messageHandler?.clearQueue() } sx={{ height: '37px' }}>Clear Queue</Button>
|
||||
<AddToQueue />
|
||||
<StartQueueButton />
|
||||
</Box>
|
||||
<MainFrame />
|
||||
</Box>;
|
||||
};
|
||||
|
||||
export default Layout;
|
||||
|
|
@ -2,18 +2,18 @@ import React from 'react';
|
|||
import { Container, Box, ThemeProvider, createTheme, Theme } from '@mui/material';
|
||||
|
||||
const makeTheme = (mode: 'dark'|'light') : Partial<Theme> => {
|
||||
return createTheme({
|
||||
palette: {
|
||||
mode,
|
||||
},
|
||||
});
|
||||
return createTheme({
|
||||
palette: {
|
||||
mode,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
const Style: FCWithChildren = ({children}) => {
|
||||
return <ThemeProvider theme={makeTheme('dark')}>
|
||||
<Box sx={{ }}/>
|
||||
{children}
|
||||
</ThemeProvider>;
|
||||
return <ThemeProvider theme={makeTheme('dark')}>
|
||||
<Box sx={{ }}/>
|
||||
{children}
|
||||
</ThemeProvider>;
|
||||
};
|
||||
|
||||
export default Style;
|
||||
|
|
@ -6,22 +6,22 @@ import EpisodeListing from './DownloadSelector/Listing/EpisodeListing';
|
|||
import SearchBox from './SearchBox/SearchBox';
|
||||
|
||||
const AddToQueue: React.FC = () => {
|
||||
const [isOpen, setOpen] = React.useState(false);
|
||||
const [isOpen, setOpen] = React.useState(false);
|
||||
|
||||
return <Box>
|
||||
<EpisodeListing />
|
||||
<Dialog open={isOpen} onClose={() => setOpen(false)} maxWidth='md' PaperProps={{ elevation:4 }}>
|
||||
<Box>
|
||||
<SearchBox />
|
||||
<Divider variant='middle'/>
|
||||
<DownloadSelector onFinish={() => setOpen(false)} />
|
||||
</Box>
|
||||
</Dialog>
|
||||
<Button variant='contained' onClick={() => setOpen(true)} sx={{ maxHeight: '2.3rem' }}>
|
||||
<Add />
|
||||
return <Box>
|
||||
<EpisodeListing />
|
||||
<Dialog open={isOpen} onClose={() => setOpen(false)} maxWidth='md' PaperProps={{ elevation:4 }}>
|
||||
<Box>
|
||||
<SearchBox />
|
||||
<Divider variant='middle'/>
|
||||
<DownloadSelector onFinish={() => setOpen(false)} />
|
||||
</Box>
|
||||
</Dialog>
|
||||
<Button variant='contained' onClick={() => setOpen(true)} sx={{ maxHeight: '2.3rem' }}>
|
||||
<Add />
|
||||
Add to Queue
|
||||
</Button>
|
||||
</Box>;
|
||||
</Button>
|
||||
</Box>;
|
||||
};
|
||||
|
||||
export default AddToQueue;
|
||||
|
|
@ -12,316 +12,316 @@ type DownloadSelectorProps = {
|
|||
}
|
||||
|
||||
const DownloadSelector: React.FC<DownloadSelectorProps> = ({ onFinish }) => {
|
||||
const messageHandler = React.useContext(messageChannelContext);
|
||||
const [store, dispatch] = useStore();
|
||||
const [availableDubs, setAvailableDubs] = React.useState<string[]>([]);
|
||||
const [availableSubs, setAvailableSubs ] = React.useState<string[]>([]);
|
||||
const [ loading, setLoading ] = React.useState(false);
|
||||
const { enqueueSnackbar } = useSnackbar();
|
||||
const ITEM_HEIGHT = 48;
|
||||
const ITEM_PADDING_TOP = 8;
|
||||
const messageHandler = React.useContext(messageChannelContext);
|
||||
const [store, dispatch] = useStore();
|
||||
const [availableDubs, setAvailableDubs] = React.useState<string[]>([]);
|
||||
const [availableSubs, setAvailableSubs ] = React.useState<string[]>([]);
|
||||
const [ loading, setLoading ] = React.useState(false);
|
||||
const { enqueueSnackbar } = useSnackbar();
|
||||
const ITEM_HEIGHT = 48;
|
||||
const ITEM_PADDING_TOP = 8;
|
||||
|
||||
React.useEffect(() => {
|
||||
(async () => {
|
||||
/* If we don't wait the response is undefined? */
|
||||
await new Promise((resolve) => setTimeout(() => resolve(undefined), 100));
|
||||
const dubLang = messageHandler?.handleDefault('dubLang');
|
||||
const subLang = messageHandler?.handleDefault('dlsubs');
|
||||
const q = messageHandler?.handleDefault('q');
|
||||
const fileName = messageHandler?.handleDefault('fileName');
|
||||
const dlVideoOnce = messageHandler?.handleDefault('dlVideoOnce');
|
||||
const result = await Promise.all([dubLang, subLang, q, fileName, dlVideoOnce]);
|
||||
dispatch({
|
||||
type: 'downloadOptions',
|
||||
payload: {
|
||||
...store.downloadOptions,
|
||||
dubLang: result[0],
|
||||
dlsubs: result[1],
|
||||
q: result[2],
|
||||
fileName: result[3],
|
||||
dlVideoOnce: result[4],
|
||||
React.useEffect(() => {
|
||||
(async () => {
|
||||
/* If we don't wait the response is undefined? */
|
||||
await new Promise((resolve) => setTimeout(() => resolve(undefined), 100));
|
||||
const dubLang = messageHandler?.handleDefault('dubLang');
|
||||
const subLang = messageHandler?.handleDefault('dlsubs');
|
||||
const q = messageHandler?.handleDefault('q');
|
||||
const fileName = messageHandler?.handleDefault('fileName');
|
||||
const dlVideoOnce = messageHandler?.handleDefault('dlVideoOnce');
|
||||
const result = await Promise.all([dubLang, subLang, q, fileName, dlVideoOnce]);
|
||||
dispatch({
|
||||
type: 'downloadOptions',
|
||||
payload: {
|
||||
...store.downloadOptions,
|
||||
dubLang: result[0],
|
||||
dlsubs: result[1],
|
||||
q: result[2],
|
||||
fileName: result[3],
|
||||
dlVideoOnce: result[4],
|
||||
}
|
||||
});
|
||||
setAvailableDubs(await messageHandler?.availableDubCodes() ?? []);
|
||||
setAvailableSubs(await messageHandler?.availableSubCodes() ?? []);
|
||||
})();
|
||||
}, []);
|
||||
|
||||
const addToQueue = async () => {
|
||||
setLoading(true);
|
||||
const res = await messageHandler?.resolveItems(store.downloadOptions);
|
||||
if (!res)
|
||||
return enqueueSnackbar('The request failed. Please check if the ID is correct.', {
|
||||
variant: 'error'
|
||||
});
|
||||
setLoading(false);
|
||||
if (onFinish)
|
||||
onFinish();
|
||||
};
|
||||
|
||||
const listEpisodes = async () => {
|
||||
if (!store.downloadOptions.id) {
|
||||
return enqueueSnackbar('Please enter a ID', {
|
||||
variant: 'error'
|
||||
});
|
||||
}
|
||||
});
|
||||
setAvailableDubs(await messageHandler?.availableDubCodes() ?? []);
|
||||
setAvailableSubs(await messageHandler?.availableSubCodes() ?? []);
|
||||
})();
|
||||
}, []);
|
||||
|
||||
const addToQueue = async () => {
|
||||
setLoading(true);
|
||||
const res = await messageHandler?.resolveItems(store.downloadOptions);
|
||||
if (!res)
|
||||
return enqueueSnackbar('The request failed. Please check if the ID is correct.', {
|
||||
variant: 'error'
|
||||
});
|
||||
setLoading(false);
|
||||
if (onFinish)
|
||||
onFinish();
|
||||
};
|
||||
|
||||
const listEpisodes = async () => {
|
||||
if (!store.downloadOptions.id) {
|
||||
return enqueueSnackbar('Please enter a ID', {
|
||||
variant: 'error'
|
||||
});
|
||||
}
|
||||
setLoading(true);
|
||||
const res = await messageHandler?.listEpisodes(store.downloadOptions.id);
|
||||
if (!res || !res.isOk) {
|
||||
setLoading(false);
|
||||
return enqueueSnackbar('The request failed. Please check if the ID is correct.', {
|
||||
variant: 'error'
|
||||
});
|
||||
} else {
|
||||
dispatch({
|
||||
type: 'episodeListing',
|
||||
payload: res.value
|
||||
});
|
||||
}
|
||||
setLoading(false);
|
||||
};
|
||||
|
||||
return <Box sx={{ display: 'flex', flexDirection: 'column', justifyContent: 'center', alignItems: 'center' }}>
|
||||
<Box sx={{display: 'flex',
|
||||
flexDirection: 'column',
|
||||
alignItems: 'center',
|
||||
margin: '5px',
|
||||
}}>
|
||||
<Box sx={{
|
||||
width: '50rem',
|
||||
height: '21rem',
|
||||
margin: '10px',
|
||||
display: 'flex',
|
||||
justifyContent: 'space-between',
|
||||
//backgroundColor: '#ffffff30',
|
||||
}}>
|
||||
<Box sx={{
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
alignItems: 'center',
|
||||
gap: '0.7rem',
|
||||
//backgroundColor: '#ff000030'
|
||||
}}>
|
||||
<Typography sx={{fontSize: '1.4rem'}}>
|
||||
General Options
|
||||
</Typography>
|
||||
<TextField value={store.downloadOptions.id} required onChange={e => {
|
||||
dispatch({
|
||||
type: 'downloadOptions',
|
||||
payload: { ...store.downloadOptions, id: e.target.value }
|
||||
setLoading(true);
|
||||
const res = await messageHandler?.listEpisodes(store.downloadOptions.id);
|
||||
if (!res || !res.isOk) {
|
||||
setLoading(false);
|
||||
return enqueueSnackbar('The request failed. Please check if the ID is correct.', {
|
||||
variant: 'error'
|
||||
});
|
||||
}} label='Show ID'/>
|
||||
<TextField type='number' value={store.downloadOptions.q} required onChange={e => {
|
||||
const parsed = parseInt(e.target.value);
|
||||
if (isNaN(parsed) || parsed < 0 || parsed > 10)
|
||||
return;
|
||||
} else {
|
||||
dispatch({
|
||||
type: 'downloadOptions',
|
||||
payload: { ...store.downloadOptions, q: parsed }
|
||||
type: 'episodeListing',
|
||||
payload: res.value
|
||||
});
|
||||
}} label='Quality Level (0 for max)'/>
|
||||
<Box sx={{ display: 'flex', gap: '5px' }}>
|
||||
<Button sx={{ textTransform: 'none'}} onClick={() => dispatch({ type: 'downloadOptions', payload: { ...store.downloadOptions, noaudio: !store.downloadOptions.noaudio } })} variant={store.downloadOptions.noaudio ? 'contained' : 'outlined'}>Skip Audio</Button>
|
||||
<Button sx={{ textTransform: 'none'}} onClick={() => dispatch({ type: 'downloadOptions', payload: { ...store.downloadOptions, novids: !store.downloadOptions.novids } })} variant={store.downloadOptions.novids ? 'contained' : 'outlined'}>Skip Video</Button>
|
||||
</Box>
|
||||
<Button sx={{ textTransform: 'none'}} onClick={() => dispatch({ type: 'downloadOptions', payload: { ...store.downloadOptions, dlVideoOnce: !store.downloadOptions.dlVideoOnce } })} variant={store.downloadOptions.dlVideoOnce ? 'contained' : 'outlined'}>Skip Unnecessary</Button>
|
||||
<Tooltip title={store.service == 'hidive' ? '' :
|
||||
<Typography>
|
||||
Simulcast is only supported on Hidive
|
||||
</Typography>}
|
||||
arrow placement='top'
|
||||
>
|
||||
<Box>
|
||||
<Button sx={{ textTransform: 'none'}} disabled={store.service != 'hidive'} onClick={() => dispatch({ type: 'downloadOptions', payload: { ...store.downloadOptions, simul: !store.downloadOptions.simul } })} variant={store.downloadOptions.simul ? 'contained' : 'outlined'}>Download Simulcast ver.</Button>
|
||||
</Box>
|
||||
</Tooltip>
|
||||
</Box>
|
||||
<Box sx={{
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
alignItems: 'center',
|
||||
gap: '0.7rem',
|
||||
//backgroundColor: '#00000020'
|
||||
}}>
|
||||
<Typography sx={{fontSize: '1.4rem'}}>
|
||||
Episode Options
|
||||
</Typography>
|
||||
<Box sx={{
|
||||
display: 'flex',
|
||||
}
|
||||
setLoading(false);
|
||||
};
|
||||
|
||||
return <Box sx={{ display: 'flex', flexDirection: 'column', justifyContent: 'center', alignItems: 'center' }}>
|
||||
<Box sx={{display: 'flex',
|
||||
flexDirection: 'column',
|
||||
gap: '1px'
|
||||
}}>
|
||||
<Box sx={{
|
||||
borderColor: '#595959',
|
||||
borderStyle: 'solid',
|
||||
borderWidth: '1px',
|
||||
borderRadius: '5px',
|
||||
//backgroundColor: '#ff4567',
|
||||
width: '15rem',
|
||||
height: '3.5rem',
|
||||
display: 'flex',
|
||||
'&:hover' : {
|
||||
borderColor: '#ffffff',
|
||||
},
|
||||
}}>
|
||||
<InputBase sx={{
|
||||
ml: 2,
|
||||
flex: 1,
|
||||
}}
|
||||
disabled={store.downloadOptions.all} value={store.downloadOptions.e} required onChange={e => {
|
||||
dispatch({
|
||||
type: 'downloadOptions',
|
||||
payload: { ...store.downloadOptions, e: e.target.value }
|
||||
});
|
||||
}} placeholder='Episode Select'/>
|
||||
<Divider orientation='vertical'/>
|
||||
<LoadingButton loading={loading} disableElevation disableFocusRipple disableRipple disableTouchRipple onClick={listEpisodes} variant='text' sx={{ textTransform: 'none'}}><Typography>List<br/>Episodes</Typography></LoadingButton>
|
||||
</Box>
|
||||
</Box>
|
||||
<Button sx={{ textTransform: 'none'}} onClick={() => dispatch({ type: 'downloadOptions', payload: { ...store.downloadOptions, all: !store.downloadOptions.all } })} variant={store.downloadOptions.all ? 'contained' : 'outlined'}>Download All</Button>
|
||||
<Button sx={{ textTransform: 'none'}} onClick={() => dispatch({ type: 'downloadOptions', payload: { ...store.downloadOptions, but: !store.downloadOptions.but } })} variant={store.downloadOptions.but ? 'contained' : 'outlined'}>Download All but</Button>
|
||||
</Box>
|
||||
<Box sx={{
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
alignItems: 'center',
|
||||
gap: '0.7rem',
|
||||
//backgroundColor: '#00ff0020'
|
||||
alignItems: 'center',
|
||||
margin: '5px',
|
||||
}}>
|
||||
<Typography sx={{fontSize: '1.4rem'}}>
|
||||
Language Options
|
||||
</Typography>
|
||||
<MultiSelect
|
||||
title='Dub Languages'
|
||||
values={availableDubs}
|
||||
selected={store.downloadOptions.dubLang}
|
||||
onChange={(e) => {
|
||||
dispatch({
|
||||
type: 'downloadOptions',
|
||||
payload: { ...store.downloadOptions, dubLang: e }
|
||||
});
|
||||
}}
|
||||
allOption
|
||||
/>
|
||||
|
||||
<MultiSelect
|
||||
title='Sub Languages'
|
||||
values={availableSubs}
|
||||
selected={store.downloadOptions.dlsubs}
|
||||
onChange={(e) => {
|
||||
dispatch({
|
||||
type: 'downloadOptions',
|
||||
payload: { ...store.downloadOptions, dlsubs: e }
|
||||
});
|
||||
}}
|
||||
/>
|
||||
<Tooltip title={store.service == 'crunchy' ? '' :
|
||||
<Typography>
|
||||
Hardsubs are only supported on Crunchyroll
|
||||
</Typography>
|
||||
}
|
||||
arrow placement='top'>
|
||||
<Box sx={{
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
width: '100%',
|
||||
gap: '1rem'
|
||||
}}>
|
||||
|
||||
<Box sx={{
|
||||
borderRadius: '5px',
|
||||
//backgroundColor: '#ff4567',
|
||||
width: '15rem',
|
||||
height: '3.5rem',
|
||||
width: '50rem',
|
||||
height: '21rem',
|
||||
margin: '10px',
|
||||
display: 'flex',
|
||||
}}>
|
||||
<FormControl fullWidth>
|
||||
<InputLabel id='hsLabel'>Hardsub Language</InputLabel>
|
||||
<Select
|
||||
MenuProps={{
|
||||
PaperProps: {
|
||||
style: {
|
||||
maxHeight: ITEM_HEIGHT * 4.5 + ITEM_PADDING_TOP,
|
||||
width: 250
|
||||
}
|
||||
}
|
||||
}}
|
||||
labelId='hsLabel'
|
||||
label='Hardsub Language'
|
||||
disabled={store.service != 'crunchy'}
|
||||
value={store.downloadOptions.hslang}
|
||||
onChange={(e) => {
|
||||
dispatch({
|
||||
type: 'downloadOptions',
|
||||
payload: { ...store.downloadOptions, hslang: (e.target.value as string) === '' ? undefined : e.target.value as string }
|
||||
});
|
||||
}}
|
||||
>
|
||||
<MenuItem value=''>No Hardsub</MenuItem>
|
||||
{availableSubs.map((lang) => {
|
||||
if(lang === 'all' || lang === 'none')
|
||||
return undefined;
|
||||
return <MenuItem value={lang}>{lang}</MenuItem>;
|
||||
})}
|
||||
</Select>
|
||||
</FormControl>
|
||||
</Box>
|
||||
justifyContent: 'space-between',
|
||||
//backgroundColor: '#ffffff30',
|
||||
}}>
|
||||
<Box sx={{
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
alignItems: 'center',
|
||||
gap: '0.7rem',
|
||||
//backgroundColor: '#ff000030'
|
||||
}}>
|
||||
<Typography sx={{fontSize: '1.4rem'}}>
|
||||
General Options
|
||||
</Typography>
|
||||
<TextField value={store.downloadOptions.id} required onChange={e => {
|
||||
dispatch({
|
||||
type: 'downloadOptions',
|
||||
payload: { ...store.downloadOptions, id: e.target.value }
|
||||
});
|
||||
}} label='Show ID'/>
|
||||
<TextField type='number' value={store.downloadOptions.q} required onChange={e => {
|
||||
const parsed = parseInt(e.target.value);
|
||||
if (isNaN(parsed) || parsed < 0 || parsed > 10)
|
||||
return;
|
||||
dispatch({
|
||||
type: 'downloadOptions',
|
||||
payload: { ...store.downloadOptions, q: parsed }
|
||||
});
|
||||
}} label='Quality Level (0 for max)'/>
|
||||
<Box sx={{ display: 'flex', gap: '5px' }}>
|
||||
<Button sx={{ textTransform: 'none'}} onClick={() => dispatch({ type: 'downloadOptions', payload: { ...store.downloadOptions, noaudio: !store.downloadOptions.noaudio } })} variant={store.downloadOptions.noaudio ? 'contained' : 'outlined'}>Skip Audio</Button>
|
||||
<Button sx={{ textTransform: 'none'}} onClick={() => dispatch({ type: 'downloadOptions', payload: { ...store.downloadOptions, novids: !store.downloadOptions.novids } })} variant={store.downloadOptions.novids ? 'contained' : 'outlined'}>Skip Video</Button>
|
||||
</Box>
|
||||
<Button sx={{ textTransform: 'none'}} onClick={() => dispatch({ type: 'downloadOptions', payload: { ...store.downloadOptions, dlVideoOnce: !store.downloadOptions.dlVideoOnce } })} variant={store.downloadOptions.dlVideoOnce ? 'contained' : 'outlined'}>Skip Unnecessary</Button>
|
||||
<Tooltip title={store.service == 'hidive' ? '' :
|
||||
<Typography>
|
||||
Simulcast is only supported on Hidive
|
||||
</Typography>}
|
||||
arrow placement='top'
|
||||
>
|
||||
<Box>
|
||||
<Button sx={{ textTransform: 'none'}} disabled={store.service != 'hidive'} onClick={() => dispatch({ type: 'downloadOptions', payload: { ...store.downloadOptions, simul: !store.downloadOptions.simul } })} variant={store.downloadOptions.simul ? 'contained' : 'outlined'}>Download Simulcast ver.</Button>
|
||||
</Box>
|
||||
</Tooltip>
|
||||
</Box>
|
||||
<Box sx={{
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
alignItems: 'center',
|
||||
gap: '0.7rem',
|
||||
//backgroundColor: '#00000020'
|
||||
}}>
|
||||
<Typography sx={{fontSize: '1.4rem'}}>
|
||||
Episode Options
|
||||
</Typography>
|
||||
<Box sx={{
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
gap: '1px'
|
||||
}}>
|
||||
<Box sx={{
|
||||
borderColor: '#595959',
|
||||
borderStyle: 'solid',
|
||||
borderWidth: '1px',
|
||||
borderRadius: '5px',
|
||||
//backgroundColor: '#ff4567',
|
||||
width: '15rem',
|
||||
height: '3.5rem',
|
||||
display: 'flex',
|
||||
'&:hover' : {
|
||||
borderColor: '#ffffff',
|
||||
},
|
||||
}}>
|
||||
<InputBase sx={{
|
||||
ml: 2,
|
||||
flex: 1,
|
||||
}}
|
||||
disabled={store.downloadOptions.all} value={store.downloadOptions.e} required onChange={e => {
|
||||
dispatch({
|
||||
type: 'downloadOptions',
|
||||
payload: { ...store.downloadOptions, e: e.target.value }
|
||||
});
|
||||
}} placeholder='Episode Select'/>
|
||||
<Divider orientation='vertical'/>
|
||||
<LoadingButton loading={loading} disableElevation disableFocusRipple disableRipple disableTouchRipple onClick={listEpisodes} variant='text' sx={{ textTransform: 'none'}}><Typography>List<br/>Episodes</Typography></LoadingButton>
|
||||
</Box>
|
||||
</Box>
|
||||
<Button sx={{ textTransform: 'none'}} onClick={() => dispatch({ type: 'downloadOptions', payload: { ...store.downloadOptions, all: !store.downloadOptions.all } })} variant={store.downloadOptions.all ? 'contained' : 'outlined'}>Download All</Button>
|
||||
<Button sx={{ textTransform: 'none'}} onClick={() => dispatch({ type: 'downloadOptions', payload: { ...store.downloadOptions, but: !store.downloadOptions.but } })} variant={store.downloadOptions.but ? 'contained' : 'outlined'}>Download All but</Button>
|
||||
</Box>
|
||||
<Box sx={{
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
alignItems: 'center',
|
||||
gap: '0.7rem',
|
||||
//backgroundColor: '#00ff0020'
|
||||
}}>
|
||||
<Typography sx={{fontSize: '1.4rem'}}>
|
||||
Language Options
|
||||
</Typography>
|
||||
<MultiSelect
|
||||
title='Dub Languages'
|
||||
values={availableDubs}
|
||||
selected={store.downloadOptions.dubLang}
|
||||
onChange={(e) => {
|
||||
dispatch({
|
||||
type: 'downloadOptions',
|
||||
payload: { ...store.downloadOptions, dubLang: e }
|
||||
});
|
||||
}}
|
||||
allOption
|
||||
/>
|
||||
|
||||
<MultiSelect
|
||||
title='Sub Languages'
|
||||
values={availableSubs}
|
||||
selected={store.downloadOptions.dlsubs}
|
||||
onChange={(e) => {
|
||||
dispatch({
|
||||
type: 'downloadOptions',
|
||||
payload: { ...store.downloadOptions, dlsubs: e }
|
||||
});
|
||||
}}
|
||||
/>
|
||||
<Tooltip title={store.service == 'crunchy' ? '' :
|
||||
<Typography>
|
||||
Hardsubs are only supported on Crunchyroll
|
||||
</Typography>
|
||||
}
|
||||
arrow placement='top'>
|
||||
<Box sx={{
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
width: '100%',
|
||||
gap: '1rem'
|
||||
}}>
|
||||
|
||||
<Box sx={{
|
||||
borderRadius: '5px',
|
||||
//backgroundColor: '#ff4567',
|
||||
width: '15rem',
|
||||
height: '3.5rem',
|
||||
display: 'flex',
|
||||
}}>
|
||||
<FormControl fullWidth>
|
||||
<InputLabel id='hsLabel'>Hardsub Language</InputLabel>
|
||||
<Select
|
||||
MenuProps={{
|
||||
PaperProps: {
|
||||
style: {
|
||||
maxHeight: ITEM_HEIGHT * 4.5 + ITEM_PADDING_TOP,
|
||||
width: 250
|
||||
}
|
||||
}
|
||||
}}
|
||||
labelId='hsLabel'
|
||||
label='Hardsub Language'
|
||||
disabled={store.service != 'crunchy'}
|
||||
value={store.downloadOptions.hslang}
|
||||
onChange={(e) => {
|
||||
dispatch({
|
||||
type: 'downloadOptions',
|
||||
payload: { ...store.downloadOptions, hslang: (e.target.value as string) === '' ? undefined : e.target.value as string }
|
||||
});
|
||||
}}
|
||||
>
|
||||
<MenuItem value=''>No Hardsub</MenuItem>
|
||||
{availableSubs.map((lang) => {
|
||||
if(lang === 'all' || lang === 'none')
|
||||
return undefined;
|
||||
return <MenuItem value={lang}>{lang}</MenuItem>;
|
||||
})}
|
||||
</Select>
|
||||
</FormControl>
|
||||
</Box>
|
||||
|
||||
|
||||
<Tooltip title={
|
||||
<Typography>
|
||||
<Tooltip title={
|
||||
<Typography>
|
||||
Downloads the hardsub version of the selected subtitle.<br/>Subtitles are displayed <b>PERMANENTLY!</b><br/>You can choose only <b>1</b> subtitle per video!
|
||||
</Typography>
|
||||
} arrow placement='top'>
|
||||
<InfoOutlinedIcon sx={{
|
||||
transition: '100ms',
|
||||
ml: '0.35rem',
|
||||
mr: '0.65rem',
|
||||
'&:hover' : {
|
||||
color: '#ffffff30',
|
||||
}
|
||||
}} />
|
||||
</Tooltip>
|
||||
</Typography>
|
||||
} arrow placement='top'>
|
||||
<InfoOutlinedIcon sx={{
|
||||
transition: '100ms',
|
||||
ml: '0.35rem',
|
||||
mr: '0.65rem',
|
||||
'&:hover' : {
|
||||
color: '#ffffff30',
|
||||
}
|
||||
}} />
|
||||
</Tooltip>
|
||||
</Box>
|
||||
</Tooltip>
|
||||
</Box>
|
||||
</Box>
|
||||
</Tooltip>
|
||||
</Box>
|
||||
</Box>
|
||||
<Box sx={{width: '95%', height: '0.3rem', backgroundColor: '#ffffff50', borderRadius: '10px', marginBottom: '20px'}}/>
|
||||
<Box sx={{
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
width: '100%',
|
||||
gap: '15px'
|
||||
}}>
|
||||
<TextField value={store.downloadOptions.fileName} onChange={e => {
|
||||
dispatch({
|
||||
type: 'downloadOptions',
|
||||
payload: { ...store.downloadOptions, fileName: e.target.value }
|
||||
});
|
||||
}} sx={{ width: '87%' }} label='Filename Overwrite' />
|
||||
<Tooltip title={
|
||||
<Typography>
|
||||
<Box sx={{width: '95%', height: '0.3rem', backgroundColor: '#ffffff50', borderRadius: '10px', marginBottom: '20px'}}/>
|
||||
<Box sx={{
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
width: '100%',
|
||||
gap: '15px'
|
||||
}}>
|
||||
<TextField value={store.downloadOptions.fileName} onChange={e => {
|
||||
dispatch({
|
||||
type: 'downloadOptions',
|
||||
payload: { ...store.downloadOptions, fileName: e.target.value }
|
||||
});
|
||||
}} sx={{ width: '87%' }} label='Filename Overwrite' />
|
||||
<Tooltip title={
|
||||
<Typography>
|
||||
Click here to see the documentation
|
||||
</Typography>
|
||||
} arrow placement='top'>
|
||||
<Link href='https://github.com/anidl/multi-downloader-nx/blob/master/docs/DOCUMENTATION.md#filename-template' rel="noopener noreferrer" target="_blank">
|
||||
<InfoOutlinedIcon sx={{
|
||||
transition: '100ms',
|
||||
'&:hover' : {
|
||||
color: '#ffffff30',
|
||||
}
|
||||
}} />
|
||||
</Link>
|
||||
</Tooltip>
|
||||
</Box>
|
||||
</Box>
|
||||
<Box sx={{width: '95%', height: '0.3rem', backgroundColor: '#ffffff50', borderRadius: '10px', marginTop: '10px'}}/>
|
||||
</Typography>
|
||||
} arrow placement='top'>
|
||||
<Link href='https://github.com/anidl/multi-downloader-nx/blob/master/docs/DOCUMENTATION.md#filename-template' rel="noopener noreferrer" target="_blank">
|
||||
<InfoOutlinedIcon sx={{
|
||||
transition: '100ms',
|
||||
'&:hover' : {
|
||||
color: '#ffffff30',
|
||||
}
|
||||
}} />
|
||||
</Link>
|
||||
</Tooltip>
|
||||
</Box>
|
||||
</Box>
|
||||
<Box sx={{width: '95%', height: '0.3rem', backgroundColor: '#ffffff50', borderRadius: '10px', marginTop: '10px'}}/>
|
||||
|
||||
<LoadingButton sx={{ margin: '15px', textTransform: 'none' }} loading={loading} onClick={addToQueue} variant='contained'>Add to Queue</LoadingButton>
|
||||
<LoadingButton sx={{ margin: '15px', textTransform: 'none' }} loading={loading} onClick={addToQueue} variant='contained'>Add to Queue</LoadingButton>
|
||||
|
||||
</Box>;
|
||||
</Box>;
|
||||
};
|
||||
|
||||
export default DownloadSelector;
|
||||
|
|
@ -7,185 +7,185 @@ import { useSnackbar } from 'notistack';
|
|||
|
||||
|
||||
const EpisodeListing: React.FC = () => {
|
||||
const [store, dispatch] = useStore();
|
||||
const [store, dispatch] = useStore();
|
||||
|
||||
const [season, setSeason] = React.useState<'all'|string>('all');
|
||||
const { enqueueSnackbar } = useSnackbar();
|
||||
const [season, setSeason] = React.useState<'all'|string>('all');
|
||||
const { enqueueSnackbar } = useSnackbar();
|
||||
|
||||
const seasons = React.useMemo(() => {
|
||||
const s: string[] = [];
|
||||
for (const {season} of store.episodeListing) {
|
||||
if (s.includes(season))
|
||||
continue;
|
||||
s.push(season);
|
||||
}
|
||||
return s;
|
||||
}, [ store.episodeListing ]);
|
||||
const seasons = React.useMemo(() => {
|
||||
const s: string[] = [];
|
||||
for (const {season} of store.episodeListing) {
|
||||
if (s.includes(season))
|
||||
continue;
|
||||
s.push(season);
|
||||
}
|
||||
return s;
|
||||
}, [ store.episodeListing ]);
|
||||
|
||||
const [selected, setSelected] = React.useState<string[]>([]);
|
||||
const [selected, setSelected] = React.useState<string[]>([]);
|
||||
|
||||
React.useEffect(() => {
|
||||
setSelected(parseSelect(store.downloadOptions.e));
|
||||
}, [ store.episodeListing ]);
|
||||
React.useEffect(() => {
|
||||
setSelected(parseSelect(store.downloadOptions.e));
|
||||
}, [ store.episodeListing ]);
|
||||
|
||||
const close = () => {
|
||||
dispatch({
|
||||
type: 'episodeListing',
|
||||
payload: []
|
||||
});
|
||||
dispatch({
|
||||
type: 'downloadOptions',
|
||||
payload: {
|
||||
...store.downloadOptions,
|
||||
e: `${([...new Set([...parseSelect(store.downloadOptions.e), ...selected])]).join(',')}`
|
||||
}
|
||||
});
|
||||
};
|
||||
const close = () => {
|
||||
dispatch({
|
||||
type: 'episodeListing',
|
||||
payload: []
|
||||
});
|
||||
dispatch({
|
||||
type: 'downloadOptions',
|
||||
payload: {
|
||||
...store.downloadOptions,
|
||||
e: `${([...new Set([...parseSelect(store.downloadOptions.e), ...selected])]).join(',')}`
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const getEpisodesForSeason = (season: string|'all') => {
|
||||
return store.episodeListing.filter((a) => season === 'all' ? true : a.season === season);
|
||||
};
|
||||
const getEpisodesForSeason = (season: string|'all') => {
|
||||
return store.episodeListing.filter((a) => season === 'all' ? true : a.season === season);
|
||||
};
|
||||
|
||||
return <Dialog open={store.episodeListing.length > 0} onClose={close} scroll='paper' maxWidth='xl' sx={{ p: 2 }}>
|
||||
<Box sx={{ display: 'grid', gridTemplateColumns: '1fr 200px 20px' }}>
|
||||
<Typography color='text.primary' variant="h5" sx={{ textAlign: 'center', alignItems: 'center', justifyContent: 'center', display: 'flex' }}>
|
||||
return <Dialog open={store.episodeListing.length > 0} onClose={close} scroll='paper' maxWidth='xl' sx={{ p: 2 }}>
|
||||
<Box sx={{ display: 'grid', gridTemplateColumns: '1fr 200px 20px' }}>
|
||||
<Typography color='text.primary' variant="h5" sx={{ textAlign: 'center', alignItems: 'center', justifyContent: 'center', display: 'flex' }}>
|
||||
Episodes
|
||||
</Typography>
|
||||
<FormControl sx={{ mr: 2, mt: 2 }}>
|
||||
<InputLabel id='seasonSelectLabel'>Season</InputLabel>
|
||||
<Select labelId="seasonSelectLabel" label='Season' value={season} onChange={(e) => setSeason(e.target.value)}>
|
||||
<MenuItem value='all'>Show all Epsiodes</MenuItem>
|
||||
{seasons.map((a, index) => {
|
||||
return <MenuItem value={a} key={`MenuItem_SeasonSelect_${index}`}>
|
||||
{a}
|
||||
</MenuItem>;
|
||||
})}
|
||||
</Select>
|
||||
</FormControl>
|
||||
</Box>
|
||||
<List>
|
||||
<ListItem sx={{ display: 'grid', gridTemplateColumns: '25px 1fr 5fr' }}>
|
||||
<Checkbox
|
||||
indeterminate={store.episodeListing.some(a => selected.includes(a.e)) && !store.episodeListing.every(a => selected.includes(a.e))}
|
||||
checked={store.episodeListing.every(a => selected.includes(a.e))}
|
||||
onChange={() => {
|
||||
if (selected.length > 0) {
|
||||
setSelected([]);
|
||||
} else {
|
||||
setSelected(getEpisodesForSeason(season).map(a => a.e));
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</ListItem>
|
||||
{getEpisodesForSeason(season).map((item, index, { length }) => {
|
||||
const e = isNaN(parseInt(item.e)) ? item.e : parseInt(item.e);
|
||||
const idStr = `S${item.season}E${e}`;
|
||||
const isSelected = selected.includes(e.toString());
|
||||
const imageRef = React.createRef<HTMLImageElement>();
|
||||
const summaryRef = React.createRef<HTMLParagraphElement>();
|
||||
return <Box {...{ mouseData: isSelected }} key={`Episode_List_Item_${index}`}>
|
||||
<ListItem sx={{backdropFilter: isSelected ? 'brightness(1.5)' : '', '&:hover': {backdropFilter: 'brightness(1.5)'}, display: 'grid', gridTemplateColumns: '25px 50px 1fr 5fr' }}
|
||||
onClick={() => {
|
||||
let arr: string[] = [];
|
||||
if (isSelected) {
|
||||
arr = [...selected.filter(a => a !== e.toString())];
|
||||
} else {
|
||||
arr = [...selected, e.toString()];
|
||||
}
|
||||
setSelected(arr.filter(a => a.length > 0));
|
||||
}}>
|
||||
{ isSelected ? <CheckBox /> : <CheckBoxOutlineBlank /> }
|
||||
<Typography color='text.primary' sx={{ textAlign: 'center' }}>
|
||||
{idStr}
|
||||
</Typography>
|
||||
<img ref={imageRef} style={{ width: 'inherit', maxHeight: '200px', minWidth: '150px' }} src={item.img} alt="thumbnail" />
|
||||
<Box sx={{ display: 'flex', flexDirection: 'column', pl: 1 }}>
|
||||
<Box sx={{ display: 'grid', gridTemplateColumns: '1fr min-content' }}>
|
||||
<Typography color='text.primary' variant="h5">
|
||||
{item.name}
|
||||
</Typography>
|
||||
<Typography color='text.primary'>
|
||||
{item.time.startsWith('00:') ? item.time.slice(3) : item.time}
|
||||
</Typography>
|
||||
</Box>
|
||||
<Typography color='text.primary' ref={summaryRef}>
|
||||
{item.description}
|
||||
</Typography>
|
||||
<Box sx={{ display: 'grid', gridTemplateColumns: 'fit-content 1fr' }}>
|
||||
<Typography>
|
||||
<br />
|
||||
<FormControl sx={{ mr: 2, mt: 2 }}>
|
||||
<InputLabel id='seasonSelectLabel'>Season</InputLabel>
|
||||
<Select labelId="seasonSelectLabel" label='Season' value={season} onChange={(e) => setSeason(e.target.value)}>
|
||||
<MenuItem value='all'>Show all Epsiodes</MenuItem>
|
||||
{seasons.map((a, index) => {
|
||||
return <MenuItem value={a} key={`MenuItem_SeasonSelect_${index}`}>
|
||||
{a}
|
||||
</MenuItem>;
|
||||
})}
|
||||
</Select>
|
||||
</FormControl>
|
||||
</Box>
|
||||
<List>
|
||||
<ListItem sx={{ display: 'grid', gridTemplateColumns: '25px 1fr 5fr' }}>
|
||||
<Checkbox
|
||||
indeterminate={store.episodeListing.some(a => selected.includes(a.e)) && !store.episodeListing.every(a => selected.includes(a.e))}
|
||||
checked={store.episodeListing.every(a => selected.includes(a.e))}
|
||||
onChange={() => {
|
||||
if (selected.length > 0) {
|
||||
setSelected([]);
|
||||
} else {
|
||||
setSelected(getEpisodesForSeason(season).map(a => a.e));
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</ListItem>
|
||||
{getEpisodesForSeason(season).map((item, index, { length }) => {
|
||||
const e = isNaN(parseInt(item.e)) ? item.e : parseInt(item.e);
|
||||
const idStr = `S${item.season}E${e}`;
|
||||
const isSelected = selected.includes(e.toString());
|
||||
const imageRef = React.createRef<HTMLImageElement>();
|
||||
const summaryRef = React.createRef<HTMLParagraphElement>();
|
||||
return <Box {...{ mouseData: isSelected }} key={`Episode_List_Item_${index}`}>
|
||||
<ListItem sx={{backdropFilter: isSelected ? 'brightness(1.5)' : '', '&:hover': {backdropFilter: 'brightness(1.5)'}, display: 'grid', gridTemplateColumns: '25px 50px 1fr 5fr' }}
|
||||
onClick={() => {
|
||||
let arr: string[] = [];
|
||||
if (isSelected) {
|
||||
arr = [...selected.filter(a => a !== e.toString())];
|
||||
} else {
|
||||
arr = [...selected, e.toString()];
|
||||
}
|
||||
setSelected(arr.filter(a => a.length > 0));
|
||||
}}>
|
||||
{ isSelected ? <CheckBox /> : <CheckBoxOutlineBlank /> }
|
||||
<Typography color='text.primary' sx={{ textAlign: 'center' }}>
|
||||
{idStr}
|
||||
</Typography>
|
||||
<img ref={imageRef} style={{ width: 'inherit', maxHeight: '200px', minWidth: '150px' }} src={item.img} alt="thumbnail" />
|
||||
<Box sx={{ display: 'flex', flexDirection: 'column', pl: 1 }}>
|
||||
<Box sx={{ display: 'grid', gridTemplateColumns: '1fr min-content' }}>
|
||||
<Typography color='text.primary' variant="h5">
|
||||
{item.name}
|
||||
</Typography>
|
||||
<Typography color='text.primary'>
|
||||
{item.time.startsWith('00:') ? item.time.slice(3) : item.time}
|
||||
</Typography>
|
||||
</Box>
|
||||
<Typography color='text.primary' ref={summaryRef}>
|
||||
{item.description}
|
||||
</Typography>
|
||||
<Box sx={{ display: 'grid', gridTemplateColumns: 'fit-content 1fr' }}>
|
||||
<Typography>
|
||||
<br />
|
||||
Available audio languages: {item.lang.join(', ')}
|
||||
</Typography>
|
||||
</Box>
|
||||
</Box>
|
||||
</ListItem>
|
||||
<ContextMenu options={[ { text: 'Copy image URL', onClick: async () => {
|
||||
await navigator.clipboard.writeText(item.img);
|
||||
enqueueSnackbar('Copied URL to clipboard', {
|
||||
variant: 'info'
|
||||
});
|
||||
}},
|
||||
{
|
||||
text: 'Open image in new tab',
|
||||
onClick: () => {
|
||||
window.open(item.img);
|
||||
}
|
||||
} ]} popupItem={imageRef as RefObject<HTMLElement>} />
|
||||
<ContextMenu options={[
|
||||
{
|
||||
onClick: async () => {
|
||||
await navigator.clipboard.writeText(item.description!);
|
||||
enqueueSnackbar('Copied summary to clipboard', {
|
||||
variant: 'info'
|
||||
});
|
||||
},
|
||||
text: 'Copy summary to clipboard'
|
||||
}
|
||||
]} popupItem={summaryRef as RefObject<HTMLElement>} />
|
||||
{index < length - 1 && <Divider />}
|
||||
</Box>;
|
||||
})}
|
||||
</List>
|
||||
</Dialog>;
|
||||
</Typography>
|
||||
</Box>
|
||||
</Box>
|
||||
</ListItem>
|
||||
<ContextMenu options={[ { text: 'Copy image URL', onClick: async () => {
|
||||
await navigator.clipboard.writeText(item.img);
|
||||
enqueueSnackbar('Copied URL to clipboard', {
|
||||
variant: 'info'
|
||||
});
|
||||
}},
|
||||
{
|
||||
text: 'Open image in new tab',
|
||||
onClick: () => {
|
||||
window.open(item.img);
|
||||
}
|
||||
} ]} popupItem={imageRef as RefObject<HTMLElement>} />
|
||||
<ContextMenu options={[
|
||||
{
|
||||
onClick: async () => {
|
||||
await navigator.clipboard.writeText(item.description!);
|
||||
enqueueSnackbar('Copied summary to clipboard', {
|
||||
variant: 'info'
|
||||
});
|
||||
},
|
||||
text: 'Copy summary to clipboard'
|
||||
}
|
||||
]} popupItem={summaryRef as RefObject<HTMLElement>} />
|
||||
{index < length - 1 && <Divider />}
|
||||
</Box>;
|
||||
})}
|
||||
</List>
|
||||
</Dialog>;
|
||||
};
|
||||
|
||||
const parseSelect = (s: string): string[] => {
|
||||
const ret: string[] = [];
|
||||
s.split(',').forEach(item => {
|
||||
if (item.includes('-')) {
|
||||
const split = item.split('-');
|
||||
if (split.length !== 2)
|
||||
return;
|
||||
const match = split[0].match(/[A-Za-z]+/);
|
||||
if (match && match.length > 0) {
|
||||
if (match.index && match.index !== 0) {
|
||||
return;
|
||||
}
|
||||
const letters = split[0].substring(0, match[0].length);
|
||||
const number = parseInt(split[0].substring(match[0].length));
|
||||
const b = parseInt(split[1]);
|
||||
if (isNaN(number) || isNaN(b)) {
|
||||
return;
|
||||
}
|
||||
for (let i = number; i <= b; i++) {
|
||||
ret.push(`${letters}${i}`);
|
||||
}
|
||||
const ret: string[] = [];
|
||||
s.split(',').forEach(item => {
|
||||
if (item.includes('-')) {
|
||||
const split = item.split('-');
|
||||
if (split.length !== 2)
|
||||
return;
|
||||
const match = split[0].match(/[A-Za-z]+/);
|
||||
if (match && match.length > 0) {
|
||||
if (match.index && match.index !== 0) {
|
||||
return;
|
||||
}
|
||||
const letters = split[0].substring(0, match[0].length);
|
||||
const number = parseInt(split[0].substring(match[0].length));
|
||||
const b = parseInt(split[1]);
|
||||
if (isNaN(number) || isNaN(b)) {
|
||||
return;
|
||||
}
|
||||
for (let i = number; i <= b; i++) {
|
||||
ret.push(`${letters}${i}`);
|
||||
}
|
||||
|
||||
} else {
|
||||
const a = parseInt(split[0]);
|
||||
const b = parseInt(split[1]);
|
||||
if (isNaN(a) || isNaN(b)) {
|
||||
return;
|
||||
} else {
|
||||
const a = parseInt(split[0]);
|
||||
const b = parseInt(split[1]);
|
||||
if (isNaN(a) || isNaN(b)) {
|
||||
return;
|
||||
}
|
||||
for (let i = a; i <= b; i++) {
|
||||
ret.push(`${i}`);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
ret.push(item);
|
||||
}
|
||||
for (let i = a; i <= b; i++) {
|
||||
ret.push(`${i}`);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
ret.push(item);
|
||||
}
|
||||
});
|
||||
return [...new Set(ret)];
|
||||
});
|
||||
return [...new Set(ret)];
|
||||
};
|
||||
|
||||
export default EpisodeListing;
|
||||
|
|
|
|||
|
|
@ -8,112 +8,112 @@ import ContextMenu from '../../reusable/ContextMenu';
|
|||
import { useSnackbar } from 'notistack';
|
||||
|
||||
const SearchBox: React.FC = () => {
|
||||
const messageHandler = React.useContext(messageChannelContext);
|
||||
const [store, dispatch] = useStore();
|
||||
const [search, setSearch] = React.useState('');
|
||||
const messageHandler = React.useContext(messageChannelContext);
|
||||
const [store, dispatch] = useStore();
|
||||
const [search, setSearch] = React.useState('');
|
||||
|
||||
const [focus, setFocus] = React.useState(false);
|
||||
const [focus, setFocus] = React.useState(false);
|
||||
|
||||
const [searchResult, setSearchResult] = React.useState<undefined|SearchResponse>();
|
||||
const anchor = React.useRef<HTMLDivElement>(null);
|
||||
const [searchResult, setSearchResult] = React.useState<undefined|SearchResponse>();
|
||||
const anchor = React.useRef<HTMLDivElement>(null);
|
||||
|
||||
const { enqueueSnackbar } = useSnackbar();
|
||||
const { enqueueSnackbar } = useSnackbar();
|
||||
|
||||
const selectItem = (id: string) => {
|
||||
dispatch({
|
||||
type: 'downloadOptions',
|
||||
payload: {
|
||||
...store.downloadOptions,
|
||||
id
|
||||
}
|
||||
});
|
||||
};
|
||||
const selectItem = (id: string) => {
|
||||
dispatch({
|
||||
type: 'downloadOptions',
|
||||
payload: {
|
||||
...store.downloadOptions,
|
||||
id
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
React.useEffect(() => {
|
||||
if (search.trim().length === 0)
|
||||
return setSearchResult({ isOk: true, value: [] });
|
||||
React.useEffect(() => {
|
||||
if (search.trim().length === 0)
|
||||
return setSearchResult({ isOk: true, value: [] });
|
||||
|
||||
const timeOutId = setTimeout(async () => {
|
||||
if (search.trim().length > 3) {
|
||||
const s = await messageHandler?.search({search});
|
||||
if (s && s.isOk)
|
||||
s.value = s.value.slice(0, 10);
|
||||
setSearchResult(s);
|
||||
}
|
||||
}, 500);
|
||||
return () => clearTimeout(timeOutId);
|
||||
}, [search]);
|
||||
const timeOutId = setTimeout(async () => {
|
||||
if (search.trim().length > 3) {
|
||||
const s = await messageHandler?.search({search});
|
||||
if (s && s.isOk)
|
||||
s.value = s.value.slice(0, 10);
|
||||
setSearchResult(s);
|
||||
}
|
||||
}, 500);
|
||||
return () => clearTimeout(timeOutId);
|
||||
}, [search]);
|
||||
|
||||
const anchorBounding = anchor.current?.getBoundingClientRect();
|
||||
return <ClickAwayListener onClickAway={() => setFocus(false)}>
|
||||
<Box sx={{ m: 2 }}>
|
||||
<TextField ref={anchor} value={search} onClick={() => setFocus(true)} onChange={e => setSearch(e.target.value)} variant='outlined' label='Search' fullWidth />
|
||||
{searchResult !== undefined && searchResult.isOk && searchResult.value.length > 0 && focus &&
|
||||
const anchorBounding = anchor.current?.getBoundingClientRect();
|
||||
return <ClickAwayListener onClickAway={() => setFocus(false)}>
|
||||
<Box sx={{ m: 2 }}>
|
||||
<TextField ref={anchor} value={search} onClick={() => setFocus(true)} onChange={e => setSearch(e.target.value)} variant='outlined' label='Search' fullWidth />
|
||||
{searchResult !== undefined && searchResult.isOk && searchResult.value.length > 0 && focus &&
|
||||
<Paper sx={{ position: 'fixed', maxHeight: '50%', width: `${anchorBounding?.width}px`,
|
||||
left: anchorBounding?.x, top: (anchorBounding?.y ?? 0) + (anchorBounding?.height ?? 0), zIndex: 99, overflowY: 'scroll'}}>
|
||||
<List>
|
||||
{searchResult && searchResult.isOk ?
|
||||
searchResult.value.map((a, ind, arr) => {
|
||||
const imageRef = React.createRef<HTMLImageElement>();
|
||||
const summaryRef = React.createRef<HTMLParagraphElement>();
|
||||
return <Box key={a.id}>
|
||||
<ListItem className='listitem-hover' onClick={() => {
|
||||
selectItem(a.id);
|
||||
setFocus(false);
|
||||
}}>
|
||||
<Box sx={{ display: 'flex' }}>
|
||||
<Box sx={{ width: '20%', height: '100%', pr: 2 }}>
|
||||
<img ref={imageRef} src={a.image} style={{ width: '100%', height: 'auto' }} alt="thumbnail"/>
|
||||
</Box>
|
||||
<Box sx={{ display: 'flex', flexDirection: 'column', maxWidth: '70%' }}>
|
||||
<Typography variant='h6' component='h6' color='text.primary' sx={{ }}>
|
||||
{a.name}
|
||||
</Typography>
|
||||
{a.desc && <Typography variant='caption' component='p' color='text.primary' sx={{ pt: 1, pb: 1 }} ref={summaryRef}>
|
||||
{a.desc}
|
||||
</Typography>}
|
||||
{a.lang && <Typography variant='caption' component='p' color='text.primary' sx={{ }}>
|
||||
left: anchorBounding?.x, top: (anchorBounding?.y ?? 0) + (anchorBounding?.height ?? 0), zIndex: 99, overflowY: 'scroll'}}>
|
||||
<List>
|
||||
{searchResult && searchResult.isOk ?
|
||||
searchResult.value.map((a, ind, arr) => {
|
||||
const imageRef = React.createRef<HTMLImageElement>();
|
||||
const summaryRef = React.createRef<HTMLParagraphElement>();
|
||||
return <Box key={a.id}>
|
||||
<ListItem className='listitem-hover' onClick={() => {
|
||||
selectItem(a.id);
|
||||
setFocus(false);
|
||||
}}>
|
||||
<Box sx={{ display: 'flex' }}>
|
||||
<Box sx={{ width: '20%', height: '100%', pr: 2 }}>
|
||||
<img ref={imageRef} src={a.image} style={{ width: '100%', height: 'auto' }} alt="thumbnail"/>
|
||||
</Box>
|
||||
<Box sx={{ display: 'flex', flexDirection: 'column', maxWidth: '70%' }}>
|
||||
<Typography variant='h6' component='h6' color='text.primary' sx={{ }}>
|
||||
{a.name}
|
||||
</Typography>
|
||||
{a.desc && <Typography variant='caption' component='p' color='text.primary' sx={{ pt: 1, pb: 1 }} ref={summaryRef}>
|
||||
{a.desc}
|
||||
</Typography>}
|
||||
{a.lang && <Typography variant='caption' component='p' color='text.primary' sx={{ }}>
|
||||
Languages: {a.lang.join(', ')}
|
||||
</Typography>}
|
||||
<Typography variant='caption' component='p' color='text.primary' sx={{ }}>
|
||||
</Typography>}
|
||||
<Typography variant='caption' component='p' color='text.primary' sx={{ }}>
|
||||
ID: {a.id}
|
||||
</Typography>
|
||||
</Box>
|
||||
</Box>
|
||||
</ListItem>
|
||||
<ContextMenu options={[ { text: 'Copy image URL', onClick: async () => {
|
||||
await navigator.clipboard.writeText(a.image);
|
||||
enqueueSnackbar('Copied URL to clipboard', {
|
||||
variant: 'info'
|
||||
});
|
||||
}},
|
||||
{
|
||||
text: 'Open image in new tab',
|
||||
onClick: () => {
|
||||
window.open(a.image);
|
||||
}
|
||||
} ]} popupItem={imageRef as RefObject<HTMLElement>} />
|
||||
{a.desc &&
|
||||
</Typography>
|
||||
</Box>
|
||||
</Box>
|
||||
</ListItem>
|
||||
<ContextMenu options={[ { text: 'Copy image URL', onClick: async () => {
|
||||
await navigator.clipboard.writeText(a.image);
|
||||
enqueueSnackbar('Copied URL to clipboard', {
|
||||
variant: 'info'
|
||||
});
|
||||
}},
|
||||
{
|
||||
text: 'Open image in new tab',
|
||||
onClick: () => {
|
||||
window.open(a.image);
|
||||
}
|
||||
} ]} popupItem={imageRef as RefObject<HTMLElement>} />
|
||||
{a.desc &&
|
||||
<ContextMenu options={[
|
||||
{
|
||||
onClick: async () => {
|
||||
await navigator.clipboard.writeText(a.desc!);
|
||||
enqueueSnackbar('Copied summary to clipboard', {
|
||||
variant: 'info'
|
||||
});
|
||||
},
|
||||
text: 'Copy summary to clipboard'
|
||||
}
|
||||
{
|
||||
onClick: async () => {
|
||||
await navigator.clipboard.writeText(a.desc!);
|
||||
enqueueSnackbar('Copied summary to clipboard', {
|
||||
variant: 'info'
|
||||
});
|
||||
},
|
||||
text: 'Copy summary to clipboard'
|
||||
}
|
||||
]} popupItem={summaryRef as RefObject<HTMLElement>} />
|
||||
}
|
||||
{(ind < arr.length - 1) && <Divider />}
|
||||
</Box>;
|
||||
})
|
||||
: <></>}
|
||||
</List>
|
||||
}
|
||||
{(ind < arr.length - 1) && <Divider />}
|
||||
</Box>;
|
||||
})
|
||||
: <></>}
|
||||
</List>
|
||||
</Paper>}
|
||||
</Box>
|
||||
</ClickAwayListener>;
|
||||
</Box>
|
||||
</ClickAwayListener>;
|
||||
};
|
||||
|
||||
export default SearchBox;
|
||||
|
|
|
|||
|
|
@ -6,107 +6,107 @@ import Require from './Require';
|
|||
import { useSnackbar } from 'notistack';
|
||||
|
||||
const AuthButton: React.FC = () => {
|
||||
const snackbar = useSnackbar();
|
||||
const snackbar = useSnackbar();
|
||||
|
||||
const [open, setOpen] = React.useState(false);
|
||||
const [open, setOpen] = React.useState(false);
|
||||
|
||||
const [username, setUsername] = React.useState('');
|
||||
const [password, setPassword] = React.useState('');
|
||||
const [username, setUsername] = React.useState('');
|
||||
const [password, setPassword] = React.useState('');
|
||||
|
||||
const [usernameError, setUsernameError] = React.useState(false);
|
||||
const [passwordError, setPasswordError] = React.useState(false);
|
||||
const [usernameError, setUsernameError] = React.useState(false);
|
||||
const [passwordError, setPasswordError] = React.useState(false);
|
||||
|
||||
const messageChannel = React.useContext(messageChannelContext);
|
||||
const messageChannel = React.useContext(messageChannelContext);
|
||||
|
||||
const [loading, setLoading] = React.useState(false);
|
||||
const [error, setError] = React.useState<Error|undefined>(undefined);
|
||||
const [authed, setAuthed] = React.useState(false);
|
||||
const [loading, setLoading] = React.useState(false);
|
||||
const [error, setError] = React.useState<Error|undefined>(undefined);
|
||||
const [authed, setAuthed] = React.useState(false);
|
||||
|
||||
const checkAuth = async () => {
|
||||
setAuthed((await messageChannel?.checkToken())?.isOk ?? false);
|
||||
};
|
||||
const checkAuth = async () => {
|
||||
setAuthed((await messageChannel?.checkToken())?.isOk ?? false);
|
||||
};
|
||||
|
||||
React.useEffect(() => { checkAuth(); }, []);
|
||||
React.useEffect(() => { checkAuth(); }, []);
|
||||
|
||||
const handleSubmit = async () => {
|
||||
if (!messageChannel)
|
||||
throw new Error('Invalid state'); //The components to confirm only render if the messageChannel is not undefinded
|
||||
if (username.trim().length === 0)
|
||||
return setUsernameError(true);
|
||||
if (password.trim().length === 0)
|
||||
return setPasswordError(true);
|
||||
setUsernameError(false);
|
||||
setPasswordError(false);
|
||||
setLoading(true);
|
||||
const handleSubmit = async () => {
|
||||
if (!messageChannel)
|
||||
throw new Error('Invalid state'); //The components to confirm only render if the messageChannel is not undefinded
|
||||
if (username.trim().length === 0)
|
||||
return setUsernameError(true);
|
||||
if (password.trim().length === 0)
|
||||
return setPasswordError(true);
|
||||
setUsernameError(false);
|
||||
setPasswordError(false);
|
||||
setLoading(true);
|
||||
|
||||
const res = await messageChannel.auth({ username, password });
|
||||
if (res.isOk) {
|
||||
setOpen(false);
|
||||
snackbar.enqueueSnackbar('Logged in', {
|
||||
variant: 'success'
|
||||
});
|
||||
setUsername('');
|
||||
setPassword('');
|
||||
} else {
|
||||
setError(res.reason);
|
||||
}
|
||||
await checkAuth();
|
||||
setLoading(false);
|
||||
};
|
||||
const res = await messageChannel.auth({ username, password });
|
||||
if (res.isOk) {
|
||||
setOpen(false);
|
||||
snackbar.enqueueSnackbar('Logged in', {
|
||||
variant: 'success'
|
||||
});
|
||||
setUsername('');
|
||||
setPassword('');
|
||||
} else {
|
||||
setError(res.reason);
|
||||
}
|
||||
await checkAuth();
|
||||
setLoading(false);
|
||||
};
|
||||
|
||||
return <Require value={messageChannel}>
|
||||
<Dialog open={open}>
|
||||
<Dialog open={!!error}>
|
||||
<DialogTitle>Error during Authentication</DialogTitle>
|
||||
<DialogContentText>
|
||||
{error?.name}
|
||||
{error?.message}
|
||||
</DialogContentText>
|
||||
<DialogActions>
|
||||
<Button onClick={() => setError(undefined)}>Close</Button>
|
||||
</DialogActions>
|
||||
</Dialog>
|
||||
<DialogTitle sx={{ flexGrow: 1 }}>Authentication</DialogTitle>
|
||||
<DialogContent>
|
||||
<DialogContentText>
|
||||
return <Require value={messageChannel}>
|
||||
<Dialog open={open}>
|
||||
<Dialog open={!!error}>
|
||||
<DialogTitle>Error during Authentication</DialogTitle>
|
||||
<DialogContentText>
|
||||
{error?.name}
|
||||
{error?.message}
|
||||
</DialogContentText>
|
||||
<DialogActions>
|
||||
<Button onClick={() => setError(undefined)}>Close</Button>
|
||||
</DialogActions>
|
||||
</Dialog>
|
||||
<DialogTitle sx={{ flexGrow: 1 }}>Authentication</DialogTitle>
|
||||
<DialogContent>
|
||||
<DialogContentText>
|
||||
Here, you need to enter your username (most likely your Email) and your password.<br />
|
||||
These information are not stored anywhere and are only used to authenticate with the service once.
|
||||
</DialogContentText>
|
||||
<TextField
|
||||
error={usernameError}
|
||||
helperText={usernameError ? 'Please enter something before submiting' : undefined}
|
||||
margin="dense"
|
||||
id="username"
|
||||
label="Username"
|
||||
type="text"
|
||||
fullWidth
|
||||
variant="standard"
|
||||
value={username}
|
||||
onChange={(e) => setUsername(e.target.value)}
|
||||
disabled={loading}
|
||||
/>
|
||||
<TextField
|
||||
error={passwordError}
|
||||
helperText={passwordError ? 'Please enter something before submiting' : undefined}
|
||||
margin="dense"
|
||||
id="password"
|
||||
label="Password"
|
||||
type="password"
|
||||
fullWidth
|
||||
variant="standard"
|
||||
value={password}
|
||||
onChange={(e) => setPassword(e.target.value)}
|
||||
disabled={loading}
|
||||
/>
|
||||
</DialogContent>
|
||||
<DialogActions>
|
||||
{loading && <CircularProgress size={30}/>}
|
||||
<Button disabled={loading} onClick={() => setOpen(false)}>Close</Button>
|
||||
<Button disabled={loading} onClick={() => handleSubmit()}>Authenticate</Button>
|
||||
</DialogActions>
|
||||
</Dialog>
|
||||
<Button startIcon={authed ? <Check />: <Close />} variant="contained" onClick={() => setOpen(true)}>Authenticate</Button>
|
||||
</Require>;
|
||||
</DialogContentText>
|
||||
<TextField
|
||||
error={usernameError}
|
||||
helperText={usernameError ? 'Please enter something before submiting' : undefined}
|
||||
margin="dense"
|
||||
id="username"
|
||||
label="Username"
|
||||
type="text"
|
||||
fullWidth
|
||||
variant="standard"
|
||||
value={username}
|
||||
onChange={(e) => setUsername(e.target.value)}
|
||||
disabled={loading}
|
||||
/>
|
||||
<TextField
|
||||
error={passwordError}
|
||||
helperText={passwordError ? 'Please enter something before submiting' : undefined}
|
||||
margin="dense"
|
||||
id="password"
|
||||
label="Password"
|
||||
type="password"
|
||||
fullWidth
|
||||
variant="standard"
|
||||
value={password}
|
||||
onChange={(e) => setPassword(e.target.value)}
|
||||
disabled={loading}
|
||||
/>
|
||||
</DialogContent>
|
||||
<DialogActions>
|
||||
{loading && <CircularProgress size={30}/>}
|
||||
<Button disabled={loading} onClick={() => setOpen(false)}>Close</Button>
|
||||
<Button disabled={loading} onClick={() => handleSubmit()}>Authenticate</Button>
|
||||
</DialogActions>
|
||||
</Dialog>
|
||||
<Button startIcon={authed ? <Check />: <Close />} variant="contained" onClick={() => setOpen(true)}>Authenticate</Button>
|
||||
</Require>;
|
||||
};
|
||||
|
||||
export default AuthButton;
|
||||
|
|
@ -6,31 +6,31 @@ import { messageChannelContext } from '../provider/MessageChannel';
|
|||
import Require from './Require';
|
||||
|
||||
const LogoutButton: React.FC = () => {
|
||||
const messageChannel = React.useContext(messageChannelContext);
|
||||
const [, dispatch] = useStore();
|
||||
const messageChannel = React.useContext(messageChannelContext);
|
||||
const [, dispatch] = useStore();
|
||||
|
||||
const logout = async () => {
|
||||
if (await messageChannel?.isDownloading())
|
||||
return alert('You are currently downloading. Please finish the download first.');
|
||||
if (await messageChannel?.logout())
|
||||
dispatch({
|
||||
type: 'service',
|
||||
payload: undefined
|
||||
});
|
||||
else
|
||||
alert('Unable to change service');
|
||||
};
|
||||
const logout = async () => {
|
||||
if (await messageChannel?.isDownloading())
|
||||
return alert('You are currently downloading. Please finish the download first.');
|
||||
if (await messageChannel?.logout())
|
||||
dispatch({
|
||||
type: 'service',
|
||||
payload: undefined
|
||||
});
|
||||
else
|
||||
alert('Unable to change service');
|
||||
};
|
||||
|
||||
return <Require value={messageChannel}>
|
||||
<Button
|
||||
startIcon={<ExitToApp />}
|
||||
variant='contained'
|
||||
onClick={logout}
|
||||
sx={{ maxHeight: '2.3rem' }}
|
||||
>
|
||||
return <Require value={messageChannel}>
|
||||
<Button
|
||||
startIcon={<ExitToApp />}
|
||||
variant='contained'
|
||||
onClick={logout}
|
||||
sx={{ maxHeight: '2.3rem' }}
|
||||
>
|
||||
Service select
|
||||
</Button>
|
||||
</Require>;
|
||||
</Button>
|
||||
</Require>;
|
||||
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -4,37 +4,37 @@ import { RandomEvent } from '../../../../../../@types/randomEvents';
|
|||
import { messageChannelContext } from '../../../provider/MessageChannel';
|
||||
|
||||
const useDownloadManager = () => {
|
||||
const messageHandler = React.useContext(messageChannelContext);
|
||||
const messageHandler = React.useContext(messageChannelContext);
|
||||
|
||||
const [progressData, setProgressData] = React.useState<ExtendedProgress|undefined>();
|
||||
const [current, setCurrent] = React.useState<undefined|QueueItem>();
|
||||
const [progressData, setProgressData] = React.useState<ExtendedProgress|undefined>();
|
||||
const [current, setCurrent] = React.useState<undefined|QueueItem>();
|
||||
|
||||
React.useEffect(() => {
|
||||
const handler = (ev: RandomEvent<'progress'>) => {
|
||||
console.log(ev.data);
|
||||
setProgressData(ev.data);
|
||||
};
|
||||
React.useEffect(() => {
|
||||
const handler = (ev: RandomEvent<'progress'>) => {
|
||||
console.log(ev.data);
|
||||
setProgressData(ev.data);
|
||||
};
|
||||
|
||||
const currentHandler = (ev: RandomEvent<'current'>) => {
|
||||
setCurrent(ev.data);
|
||||
};
|
||||
const currentHandler = (ev: RandomEvent<'current'>) => {
|
||||
setCurrent(ev.data);
|
||||
};
|
||||
|
||||
const finishHandler = () => {
|
||||
setProgressData(undefined);
|
||||
};
|
||||
const finishHandler = () => {
|
||||
setProgressData(undefined);
|
||||
};
|
||||
|
||||
messageHandler?.randomEvents.on('progress', handler);
|
||||
messageHandler?.randomEvents.on('current', currentHandler);
|
||||
messageHandler?.randomEvents.on('finish', finishHandler);
|
||||
messageHandler?.randomEvents.on('progress', handler);
|
||||
messageHandler?.randomEvents.on('current', currentHandler);
|
||||
messageHandler?.randomEvents.on('finish', finishHandler);
|
||||
|
||||
return () => {
|
||||
messageHandler?.randomEvents.removeListener('progress', handler);
|
||||
messageHandler?.randomEvents.removeListener('finish', finishHandler);
|
||||
messageHandler?.randomEvents.removeListener('current', currentHandler);
|
||||
};
|
||||
}, [messageHandler]);
|
||||
return () => {
|
||||
messageHandler?.randomEvents.removeListener('progress', handler);
|
||||
messageHandler?.randomEvents.removeListener('finish', finishHandler);
|
||||
messageHandler?.randomEvents.removeListener('current', currentHandler);
|
||||
};
|
||||
}, [messageHandler]);
|
||||
|
||||
return { data: progressData, current};
|
||||
return { data: progressData, current};
|
||||
};
|
||||
|
||||
export default useDownloadManager;
|
||||
|
|
@ -3,9 +3,9 @@ import React from 'react';
|
|||
import Queue from './Queue/Queue';
|
||||
|
||||
const MainFrame: React.FC = () => {
|
||||
return <Box sx={{ }}>
|
||||
<Queue />
|
||||
</Box>;
|
||||
return <Box sx={{ }}>
|
||||
<Queue />
|
||||
</Box>;
|
||||
};
|
||||
|
||||
export default MainFrame;
|
||||
|
|
@ -7,414 +7,414 @@ import DeleteIcon from '@mui/icons-material/Delete';
|
|||
import useDownloadManager from '../DownloadManager/DownloadManager';
|
||||
|
||||
const Queue: React.FC = () => {
|
||||
const { data, current } = useDownloadManager();
|
||||
const queue = React.useContext(queueContext);
|
||||
const msg = React.useContext(messageChannelContext);
|
||||
const { data, current } = useDownloadManager();
|
||||
const queue = React.useContext(queueContext);
|
||||
const msg = React.useContext(messageChannelContext);
|
||||
|
||||
|
||||
if (!msg)
|
||||
return <>Never</>;
|
||||
if (!msg)
|
||||
return <>Never</>;
|
||||
|
||||
return data || queue.length > 0 ? <>
|
||||
{data && <>
|
||||
<Box sx={{
|
||||
display: 'flex',
|
||||
width: '100%',
|
||||
flexDirection: 'column',
|
||||
alignItems: 'center',
|
||||
}}>
|
||||
<Box sx={{
|
||||
marginTop: '2rem',
|
||||
marginBottom: '1rem',
|
||||
height: '12rem',
|
||||
width: '93vw',
|
||||
maxWidth: '93rem',
|
||||
backgroundColor: '#282828',
|
||||
boxShadow: '0px 0px 50px #00000090',
|
||||
borderRadius: '10px',
|
||||
display: 'flex',
|
||||
transition: '250ms'
|
||||
}}>
|
||||
<img style={{
|
||||
borderRadius: '5px',
|
||||
margin: '5px',
|
||||
boxShadow: '0px 0px 10px #00000090',
|
||||
userSelect: 'none',
|
||||
}}
|
||||
src={data.downloadInfo.image} height='auto' width='auto' alt="Thumbnail" />
|
||||
<Box
|
||||
sx={{
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
width: '100%',
|
||||
justifyContent: 'center'
|
||||
}}>
|
||||
return data || queue.length > 0 ? <>
|
||||
{data && <>
|
||||
<Box sx={{
|
||||
display: 'flex',
|
||||
}}>
|
||||
<Box sx={{
|
||||
//backgroundColor: '#ff0000',
|
||||
width: '70%',
|
||||
marginLeft: '10px'
|
||||
}}>
|
||||
<Box sx={{
|
||||
flexDirection: 'column',
|
||||
display: 'flex',
|
||||
justifyContent: 'space-between',
|
||||
}}>
|
||||
<Typography color='text.primary' sx={{
|
||||
fontSize: '1.8rem',
|
||||
}}>
|
||||
{data.downloadInfo.parent.title}
|
||||
</Typography>
|
||||
<Typography color='text.primary' sx={{
|
||||
fontSize: '1.2rem',
|
||||
}}>
|
||||
{data.downloadInfo.title}
|
||||
</Typography>
|
||||
</Box>
|
||||
</Box>
|
||||
<Box sx={{
|
||||
//backgroundColor: '#00ff00',
|
||||
width: '30%',
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
}}>
|
||||
<Typography color='text.primary' sx={{
|
||||
fontSize: '1.8rem',
|
||||
}}>
|
||||
Downloading: {data.downloadInfo.language.name}
|
||||
</Typography>
|
||||
</Box>
|
||||
</Box>
|
||||
<Box sx={{
|
||||
height: '50%',
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
//backgroundColor: '#0000ff',
|
||||
}}>
|
||||
<LinearProgress variant='determinate'
|
||||
sx={{
|
||||
height: '20px',
|
||||
width: '97.53%',
|
||||
margin: '10px',
|
||||
boxShadow: '0px 0px 10px #00000090',
|
||||
borderRadius: '10px',
|
||||
}} value={(typeof data.progress.percent === 'string' ? parseInt(data.progress.percent) : data.progress.percent)}
|
||||
/>
|
||||
<Box>
|
||||
<Typography color='text.primary'
|
||||
sx={{
|
||||
fontSize: '1.3rem',
|
||||
}}>
|
||||
{data.progress.cur} / {(data.progress.total)} parts ({data.progress.percent}% | {formatTime(data.progress.time)} | {(data.progress.downloadSpeed / 1024 / 1024).toFixed(2)} MB/s | {(data.progress.bytes / 1024 / 1024).toFixed(2)}MB)
|
||||
</Typography>
|
||||
</Box>
|
||||
</Box>
|
||||
</Box>
|
||||
</Box>
|
||||
</Box>
|
||||
</>
|
||||
}
|
||||
{
|
||||
current && !data && <>
|
||||
<Box sx={{
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
alignItems: 'center',
|
||||
}}>
|
||||
<Box sx={{
|
||||
marginTop: '2rem',
|
||||
marginBottom: '1rem',
|
||||
height: '12rem',
|
||||
width: '93vw',
|
||||
maxWidth: '93rem',
|
||||
backgroundColor: '#282828',
|
||||
boxShadow: '0px 0px 50px #00000090',
|
||||
borderRadius: '10px',
|
||||
display: 'flex',
|
||||
overflow: 'hidden',
|
||||
transition: '250ms'
|
||||
}}>
|
||||
<img style={{
|
||||
borderRadius: '5px',
|
||||
margin: '5px',
|
||||
boxShadow: '0px 0px 10px #00000090',
|
||||
userSelect: 'none',
|
||||
maxWidth: '20.5rem',
|
||||
}}
|
||||
src={current.image} height='auto' width='auto' alt="Thumbnail" />
|
||||
<Box
|
||||
sx={{
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
width: '100%',
|
||||
justifyContent: 'center',
|
||||
//backgroundColor: '#ffffff0f'
|
||||
}}>
|
||||
<Box sx={{
|
||||
display: 'flex',
|
||||
|
||||
}}>
|
||||
<Box sx={{
|
||||
width: '70%',
|
||||
marginLeft: '10px'
|
||||
}}>
|
||||
<Box sx={{
|
||||
flexDirection: 'column',
|
||||
display: 'flex',
|
||||
justifyContent: 'space-between',
|
||||
}}>
|
||||
<Typography color='text.primary' sx={{
|
||||
fontSize: '1.8rem',
|
||||
}}>
|
||||
{current.parent.title}
|
||||
</Typography>
|
||||
<Typography color='text.primary' sx={{
|
||||
fontSize: '1.2rem',
|
||||
}}>
|
||||
{current.title}
|
||||
</Typography>
|
||||
</Box>
|
||||
</Box>
|
||||
<Box sx={{
|
||||
//backgroundColor: '#00ff00',
|
||||
width: '30%',
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
}}>
|
||||
<Box sx={{
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'space-between',
|
||||
position: 'relative',
|
||||
}}>
|
||||
<Typography color='text.primary' sx={{
|
||||
fontSize: '1.8rem',
|
||||
}}>
|
||||
Downloading:
|
||||
</Typography>
|
||||
<CircularProgress variant="indeterminate" sx={{
|
||||
marginLeft: '2rem',
|
||||
}}/>
|
||||
</Box>
|
||||
</Box>
|
||||
</Box>
|
||||
<Box sx={{
|
||||
height: '50%',
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
//backgroundColor: '#0000ff',
|
||||
}}>
|
||||
<LinearProgress variant='indeterminate'
|
||||
sx={{
|
||||
height: '20px',
|
||||
width: '97.53%',
|
||||
margin: '10px',
|
||||
}}>
|
||||
<Box sx={{
|
||||
marginTop: '2rem',
|
||||
marginBottom: '1rem',
|
||||
height: '12rem',
|
||||
width: '93vw',
|
||||
maxWidth: '93rem',
|
||||
backgroundColor: '#282828',
|
||||
boxShadow: '0px 0px 50px #00000090',
|
||||
borderRadius: '10px',
|
||||
display: 'flex',
|
||||
transition: '250ms'
|
||||
}}>
|
||||
<img style={{
|
||||
borderRadius: '5px',
|
||||
margin: '5px',
|
||||
boxShadow: '0px 0px 10px #00000090',
|
||||
userSelect: 'none',
|
||||
}}
|
||||
src={data.downloadInfo.image} height='auto' width='auto' alt="Thumbnail" />
|
||||
<Box
|
||||
sx={{
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
width: '100%',
|
||||
justifyContent: 'center'
|
||||
}}>
|
||||
<Box sx={{
|
||||
display: 'flex',
|
||||
}}>
|
||||
<Box sx={{
|
||||
//backgroundColor: '#ff0000',
|
||||
width: '70%',
|
||||
marginLeft: '10px'
|
||||
}}>
|
||||
<Box sx={{
|
||||
flexDirection: 'column',
|
||||
display: 'flex',
|
||||
justifyContent: 'space-between',
|
||||
}}>
|
||||
<Typography color='text.primary' sx={{
|
||||
fontSize: '1.8rem',
|
||||
}}>
|
||||
{data.downloadInfo.parent.title}
|
||||
</Typography>
|
||||
<Typography color='text.primary' sx={{
|
||||
fontSize: '1.2rem',
|
||||
}}>
|
||||
{data.downloadInfo.title}
|
||||
</Typography>
|
||||
</Box>
|
||||
</Box>
|
||||
<Box sx={{
|
||||
//backgroundColor: '#00ff00',
|
||||
width: '30%',
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
}}>
|
||||
<Typography color='text.primary' sx={{
|
||||
fontSize: '1.8rem',
|
||||
}}>
|
||||
Downloading: {data.downloadInfo.language.name}
|
||||
</Typography>
|
||||
</Box>
|
||||
</Box>
|
||||
<Box sx={{
|
||||
height: '50%',
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
//backgroundColor: '#0000ff',
|
||||
}}>
|
||||
<LinearProgress variant='determinate'
|
||||
sx={{
|
||||
height: '20px',
|
||||
width: '97.53%',
|
||||
margin: '10px',
|
||||
boxShadow: '0px 0px 10px #00000090',
|
||||
borderRadius: '10px',
|
||||
}} value={(typeof data.progress.percent === 'string' ? parseInt(data.progress.percent) : data.progress.percent)}
|
||||
/>
|
||||
<Box>
|
||||
<Typography color='text.primary'
|
||||
sx={{
|
||||
fontSize: '1.3rem',
|
||||
}}>
|
||||
{data.progress.cur} / {(data.progress.total)} parts ({data.progress.percent}% | {formatTime(data.progress.time)} | {(data.progress.downloadSpeed / 1024 / 1024).toFixed(2)} MB/s | {(data.progress.bytes / 1024 / 1024).toFixed(2)}MB)
|
||||
</Typography>
|
||||
</Box>
|
||||
</Box>
|
||||
</Box>
|
||||
</Box>
|
||||
</Box>
|
||||
</>
|
||||
}
|
||||
{
|
||||
current && !data && <>
|
||||
<Box sx={{
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
alignItems: 'center',
|
||||
}}>
|
||||
<Box sx={{
|
||||
marginTop: '2rem',
|
||||
marginBottom: '1rem',
|
||||
height: '12rem',
|
||||
width: '93vw',
|
||||
maxWidth: '93rem',
|
||||
backgroundColor: '#282828',
|
||||
boxShadow: '0px 0px 50px #00000090',
|
||||
borderRadius: '10px',
|
||||
display: 'flex',
|
||||
overflow: 'hidden',
|
||||
transition: '250ms'
|
||||
}}>
|
||||
<img style={{
|
||||
borderRadius: '5px',
|
||||
margin: '5px',
|
||||
boxShadow: '0px 0px 10px #00000090',
|
||||
userSelect: 'none',
|
||||
maxWidth: '20.5rem',
|
||||
}}
|
||||
src={current.image} height='auto' width='auto' alt="Thumbnail" />
|
||||
<Box
|
||||
sx={{
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
width: '100%',
|
||||
justifyContent: 'center',
|
||||
//backgroundColor: '#ffffff0f'
|
||||
}}>
|
||||
<Box sx={{
|
||||
display: 'flex',
|
||||
|
||||
}}>
|
||||
<Box sx={{
|
||||
width: '70%',
|
||||
marginLeft: '10px'
|
||||
}}>
|
||||
<Box sx={{
|
||||
flexDirection: 'column',
|
||||
display: 'flex',
|
||||
justifyContent: 'space-between',
|
||||
}}>
|
||||
<Typography color='text.primary' sx={{
|
||||
fontSize: '1.8rem',
|
||||
}}>
|
||||
{current.parent.title}
|
||||
</Typography>
|
||||
<Typography color='text.primary' sx={{
|
||||
fontSize: '1.2rem',
|
||||
}}>
|
||||
{current.title}
|
||||
</Typography>
|
||||
</Box>
|
||||
</Box>
|
||||
<Box sx={{
|
||||
//backgroundColor: '#00ff00',
|
||||
width: '30%',
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
}}>
|
||||
<Box sx={{
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'space-between',
|
||||
position: 'relative',
|
||||
}}>
|
||||
<Typography color='text.primary' sx={{
|
||||
fontSize: '1.8rem',
|
||||
}}>
|
||||
Downloading:
|
||||
</Typography>
|
||||
<CircularProgress variant="indeterminate" sx={{
|
||||
marginLeft: '2rem',
|
||||
}}/>
|
||||
</Box>
|
||||
</Box>
|
||||
</Box>
|
||||
<Box sx={{
|
||||
height: '50%',
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
//backgroundColor: '#0000ff',
|
||||
}}>
|
||||
<LinearProgress variant='indeterminate'
|
||||
sx={{
|
||||
height: '20px',
|
||||
width: '97.53%',
|
||||
margin: '10px',
|
||||
boxShadow: '0px 0px 10px #00000090',
|
||||
borderRadius: '10px',
|
||||
}}
|
||||
/>
|
||||
<Box>
|
||||
<Typography color='text.primary'
|
||||
sx={{
|
||||
fontSize: '1.3rem',
|
||||
}}>
|
||||
0 / ? parts (0% | XX:XX | 0 MB/s | 0MB)
|
||||
</Typography>
|
||||
</Box>
|
||||
</Box>
|
||||
</Box>
|
||||
</Box>
|
||||
</Box>
|
||||
</>
|
||||
}
|
||||
{queue.map((queueItem, index, { length }) => {
|
||||
return <Box key={`queue_item_${index}`} sx={{
|
||||
display: 'flex',
|
||||
mb: '-1.5rem',
|
||||
flexDirection: 'column',
|
||||
alignItems: 'center',
|
||||
}}>
|
||||
<Box sx={{
|
||||
marginTop: '1.5rem',
|
||||
marginBottom: '1.5rem',
|
||||
height: '11rem',
|
||||
width: '90vw',
|
||||
maxWidth: '90rem',
|
||||
backgroundColor: '#282828',
|
||||
boxShadow: '0px 0px 10px #00000090',
|
||||
borderRadius: '10px',
|
||||
}}
|
||||
/>
|
||||
<Box>
|
||||
<Typography color='text.primary'
|
||||
sx={{
|
||||
fontSize: '1.3rem',
|
||||
display: 'flex',
|
||||
overflow: 'hidden',
|
||||
}}>
|
||||
<img style={{
|
||||
borderRadius: '5px',
|
||||
margin: '5px',
|
||||
boxShadow: '0px 0px 5px #00000090',
|
||||
userSelect: 'none',
|
||||
maxWidth: '18.5rem'
|
||||
}}
|
||||
src={queueItem.image} height='auto' width='auto' alt="Thumbnail" />
|
||||
<Box sx={{
|
||||
margin: '5px',
|
||||
display: 'flex',
|
||||
width: '100%',
|
||||
justifyContent: 'space-between',
|
||||
}}>
|
||||
0 / ? parts (0% | XX:XX | 0 MB/s | 0MB)
|
||||
</Typography>
|
||||
<Box sx={{
|
||||
width: '30%',
|
||||
marginRight: '5px',
|
||||
marginLeft: '5px',
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
justifyContent: 'space-between',
|
||||
overflow: 'hidden',
|
||||
textOverflow: 'ellipsis',
|
||||
}}>
|
||||
<Typography color='text.primary' sx={{
|
||||
fontSize: '1.8rem',
|
||||
overflow: 'hidden',
|
||||
whiteSpace: 'nowrap',
|
||||
textOverflow: 'ellipsis',
|
||||
}}>
|
||||
{queueItem.parent.title}
|
||||
</Typography>
|
||||
<Typography color='text.primary' sx={{
|
||||
fontSize: '1.6rem',
|
||||
marginTop: '-0.4rem',
|
||||
marginBottom: '0.4rem',
|
||||
}}>
|
||||
S{queueItem.parent.season}E{queueItem.episode}
|
||||
</Typography>
|
||||
<Typography color='text.primary' sx={{
|
||||
fontSize: '1.2rem',
|
||||
marginTop: '-0.4rem',
|
||||
marginBottom: '0.4rem',
|
||||
textOverflow: 'ellipsis',
|
||||
}}>
|
||||
{queueItem.title}
|
||||
</Typography>
|
||||
</Box>
|
||||
<Box sx={{
|
||||
width: '40%',
|
||||
marginRight: '5px',
|
||||
marginLeft: '5px',
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
overflow: 'hidden',
|
||||
whiteSpace: 'nowrap',
|
||||
justifyContent: 'space-between',
|
||||
}}>
|
||||
<Typography color='text.primary' sx={{
|
||||
fontSize: '1.8rem',
|
||||
overflow: 'hidden',
|
||||
whiteSpace: 'nowrap',
|
||||
textOverflow: 'ellipsis',
|
||||
}}>
|
||||
Dub(s): {queueItem.dubLang.join(', ')}
|
||||
</Typography>
|
||||
<Typography color='text.primary' sx={{
|
||||
fontSize: '1.8rem',
|
||||
overflow: 'hidden',
|
||||
whiteSpace: 'nowrap',
|
||||
textOverflow: 'ellipsis',
|
||||
}}>
|
||||
Sub(s): {queueItem.dlsubs.join(', ')}
|
||||
</Typography>
|
||||
<Typography color='text.primary' sx={{
|
||||
fontSize: '1.8rem',
|
||||
|
||||
}}>
|
||||
Quality: {queueItem.q}
|
||||
</Typography>
|
||||
</Box>
|
||||
<Box sx={{
|
||||
marginRight: '5px',
|
||||
marginLeft: '5px',
|
||||
width: '30%',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
display: 'flex'
|
||||
}}>
|
||||
<Tooltip title="Delete from queue" arrow placement='top'>
|
||||
<IconButton
|
||||
onClick={() => {
|
||||
msg.removeFromQueue(index);
|
||||
}}
|
||||
sx={{
|
||||
backgroundColor: '#ff573a25',
|
||||
height: '40px',
|
||||
transition: '250ms',
|
||||
'&:hover' : {
|
||||
backgroundColor: '#ff573a',
|
||||
}
|
||||
}}>
|
||||
<DeleteIcon />
|
||||
</IconButton>
|
||||
</Tooltip>
|
||||
</Box>
|
||||
</Box>
|
||||
</Box>
|
||||
</Box>
|
||||
</Box>
|
||||
</Box>
|
||||
</Box>
|
||||
</>
|
||||
}
|
||||
{queue.map((queueItem, index, { length }) => {
|
||||
return <Box key={`queue_item_${index}`} sx={{
|
||||
;
|
||||
})}
|
||||
</> : <Box sx={{
|
||||
display: 'flex',
|
||||
mb: '-1.5rem',
|
||||
width: '100%',
|
||||
height: '12rem',
|
||||
flexDirection: 'column',
|
||||
alignItems: 'center',
|
||||
}}>
|
||||
<Box sx={{
|
||||
marginTop: '1.5rem',
|
||||
marginBottom: '1.5rem',
|
||||
height: '11rem',
|
||||
width: '90vw',
|
||||
maxWidth: '90rem',
|
||||
backgroundColor: '#282828',
|
||||
boxShadow: '0px 0px 10px #00000090',
|
||||
borderRadius: '10px',
|
||||
display: 'flex',
|
||||
overflow: 'hidden',
|
||||
}}>
|
||||
<Typography color='text.primary' sx={{
|
||||
fontSize: '2rem',
|
||||
margin: '10px'
|
||||
}}>
|
||||
<img style={{
|
||||
borderRadius: '5px',
|
||||
margin: '5px',
|
||||
boxShadow: '0px 0px 5px #00000090',
|
||||
userSelect: 'none',
|
||||
maxWidth: '18.5rem'
|
||||
}}
|
||||
src={queueItem.image} height='auto' width='auto' alt="Thumbnail" />
|
||||
<Box sx={{
|
||||
margin: '5px',
|
||||
display: 'flex',
|
||||
width: '100%',
|
||||
justifyContent: 'space-between',
|
||||
}}>
|
||||
<Box sx={{
|
||||
width: '30%',
|
||||
marginRight: '5px',
|
||||
marginLeft: '5px',
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
justifyContent: 'space-between',
|
||||
overflow: 'hidden',
|
||||
textOverflow: 'ellipsis',
|
||||
}}>
|
||||
<Typography color='text.primary' sx={{
|
||||
fontSize: '1.8rem',
|
||||
overflow: 'hidden',
|
||||
whiteSpace: 'nowrap',
|
||||
textOverflow: 'ellipsis',
|
||||
}}>
|
||||
{queueItem.parent.title}
|
||||
</Typography>
|
||||
<Typography color='text.primary' sx={{
|
||||
fontSize: '1.6rem',
|
||||
marginTop: '-0.4rem',
|
||||
marginBottom: '0.4rem',
|
||||
}}>
|
||||
S{queueItem.parent.season}E{queueItem.episode}
|
||||
</Typography>
|
||||
<Typography color='text.primary' sx={{
|
||||
fontSize: '1.2rem',
|
||||
marginTop: '-0.4rem',
|
||||
marginBottom: '0.4rem',
|
||||
textOverflow: 'ellipsis',
|
||||
}}>
|
||||
{queueItem.title}
|
||||
</Typography>
|
||||
</Box>
|
||||
<Box sx={{
|
||||
width: '40%',
|
||||
marginRight: '5px',
|
||||
marginLeft: '5px',
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
overflow: 'hidden',
|
||||
whiteSpace: 'nowrap',
|
||||
justifyContent: 'space-between',
|
||||
}}>
|
||||
<Typography color='text.primary' sx={{
|
||||
fontSize: '1.8rem',
|
||||
overflow: 'hidden',
|
||||
whiteSpace: 'nowrap',
|
||||
textOverflow: 'ellipsis',
|
||||
}}>
|
||||
Dub(s): {queueItem.dubLang.join(', ')}
|
||||
</Typography>
|
||||
<Typography color='text.primary' sx={{
|
||||
fontSize: '1.8rem',
|
||||
overflow: 'hidden',
|
||||
whiteSpace: 'nowrap',
|
||||
textOverflow: 'ellipsis',
|
||||
}}>
|
||||
Sub(s): {queueItem.dlsubs.join(', ')}
|
||||
</Typography>
|
||||
<Typography color='text.primary' sx={{
|
||||
fontSize: '1.8rem',
|
||||
|
||||
}}>
|
||||
Quality: {queueItem.q}
|
||||
</Typography>
|
||||
</Box>
|
||||
<Box sx={{
|
||||
marginRight: '5px',
|
||||
marginLeft: '5px',
|
||||
width: '30%',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
display: 'flex'
|
||||
}}>
|
||||
<Tooltip title="Delete from queue" arrow placement='top'>
|
||||
<IconButton
|
||||
onClick={() => {
|
||||
msg.removeFromQueue(index);
|
||||
}}
|
||||
sx={{
|
||||
backgroundColor: '#ff573a25',
|
||||
height: '40px',
|
||||
transition: '250ms',
|
||||
'&:hover' : {
|
||||
backgroundColor: '#ff573a',
|
||||
}
|
||||
}}>
|
||||
<DeleteIcon />
|
||||
</IconButton>
|
||||
</Tooltip>
|
||||
</Box>
|
||||
</Box>
|
||||
</Box>
|
||||
</Box>
|
||||
;
|
||||
})}
|
||||
</> : <Box sx={{
|
||||
display: 'flex',
|
||||
width: '100%',
|
||||
height: '12rem',
|
||||
flexDirection: 'column',
|
||||
alignItems: 'center',
|
||||
}}>
|
||||
<Typography color='text.primary' sx={{
|
||||
fontSize: '2rem',
|
||||
margin: '10px'
|
||||
}}>
|
||||
Selected episodes will be shown here
|
||||
</Typography>
|
||||
<Box sx={{
|
||||
display: 'flex',
|
||||
margin: '10px'
|
||||
}}>
|
||||
<Skeleton variant='rectangular' height={'10rem'} width={'20rem'} sx={{ margin: '5px', borderRadius: '5px' }}/>
|
||||
<Box sx={{
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
}}>
|
||||
<Skeleton variant='text' height={'100%'} width={'30rem'} sx={{ margin: '5px', borderRadius: '5px' }}/>
|
||||
<Skeleton variant='text' height={'100%'} width={'30rem'} sx={{ margin: '5px', borderRadius: '5px' }}/>
|
||||
</Box>
|
||||
</Box>
|
||||
<Box sx={{
|
||||
display: 'flex',
|
||||
margin: '10px'
|
||||
}}>
|
||||
<Skeleton variant='rectangular' height={'10rem'} width={'20rem'} sx={{ margin: '5px', borderRadius: '5px' }}/>
|
||||
<Box sx={{
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
}}>
|
||||
<Skeleton variant='text' height={'100%'} width={'30rem'} sx={{ margin: '5px', borderRadius: '5px' }}/>
|
||||
<Skeleton variant='text' height={'100%'} width={'30rem'} sx={{ margin: '5px', borderRadius: '5px' }}/>
|
||||
</Box>
|
||||
</Box>
|
||||
</Box>;
|
||||
</Typography>
|
||||
<Box sx={{
|
||||
display: 'flex',
|
||||
margin: '10px'
|
||||
}}>
|
||||
<Skeleton variant='rectangular' height={'10rem'} width={'20rem'} sx={{ margin: '5px', borderRadius: '5px' }}/>
|
||||
<Box sx={{
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
}}>
|
||||
<Skeleton variant='text' height={'100%'} width={'30rem'} sx={{ margin: '5px', borderRadius: '5px' }}/>
|
||||
<Skeleton variant='text' height={'100%'} width={'30rem'} sx={{ margin: '5px', borderRadius: '5px' }}/>
|
||||
</Box>
|
||||
</Box>
|
||||
<Box sx={{
|
||||
display: 'flex',
|
||||
margin: '10px'
|
||||
}}>
|
||||
<Skeleton variant='rectangular' height={'10rem'} width={'20rem'} sx={{ margin: '5px', borderRadius: '5px' }}/>
|
||||
<Box sx={{
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
}}>
|
||||
<Skeleton variant='text' height={'100%'} width={'30rem'} sx={{ margin: '5px', borderRadius: '5px' }}/>
|
||||
<Skeleton variant='text' height={'100%'} width={'30rem'} sx={{ margin: '5px', borderRadius: '5px' }}/>
|
||||
</Box>
|
||||
</Box>
|
||||
</Box>;
|
||||
};
|
||||
|
||||
const formatTime = (time: number) => {
|
||||
time = Math.floor(time / 1000);
|
||||
const minutes = Math.floor(time / 60);
|
||||
time = time % 60;
|
||||
time = Math.floor(time / 1000);
|
||||
const minutes = Math.floor(time / 60);
|
||||
time = time % 60;
|
||||
|
||||
return `${minutes.toFixed(0).length < 2 ? `0${minutes}` : minutes}m${time.toFixed(0).length < 2 ? `0${time}` : time}s`;
|
||||
return `${minutes.toFixed(0).length < 2 ? `0${minutes}` : minutes}m${time.toFixed(0).length < 2 ? `0${time}` : time}s`;
|
||||
};
|
||||
|
||||
export default Queue;
|
||||
|
|
@ -5,120 +5,120 @@ import useStore from '../../hooks/useStore';
|
|||
import { StoreState } from '../../provider/Store';
|
||||
|
||||
const MenuBar: React.FC = () => {
|
||||
const [ openMenu, setMenuOpen ] = React.useState<'settings'|'help'|undefined>();
|
||||
const [anchorEl, setAnchorEl] = React.useState<null | HTMLElement>(null);
|
||||
const [store, dispatch] = useStore();
|
||||
const [ openMenu, setMenuOpen ] = React.useState<'settings'|'help'|undefined>();
|
||||
const [anchorEl, setAnchorEl] = React.useState<null | HTMLElement>(null);
|
||||
const [store, dispatch] = useStore();
|
||||
|
||||
const messageChannel = React.useContext(messageChannelContext);
|
||||
const messageChannel = React.useContext(messageChannelContext);
|
||||
|
||||
React.useEffect(() => {
|
||||
(async () => {
|
||||
if (!messageChannel || store.version !== '')
|
||||
return;
|
||||
dispatch({
|
||||
type: 'version',
|
||||
payload: await messageChannel.version()
|
||||
});
|
||||
})();
|
||||
}, [messageChannel]);
|
||||
React.useEffect(() => {
|
||||
(async () => {
|
||||
if (!messageChannel || store.version !== '')
|
||||
return;
|
||||
dispatch({
|
||||
type: 'version',
|
||||
payload: await messageChannel.version()
|
||||
});
|
||||
})();
|
||||
}, [messageChannel]);
|
||||
|
||||
const transformService = (service: StoreState['service']) => {
|
||||
switch(service) {
|
||||
case 'crunchy':
|
||||
return 'Crunchyroll';
|
||||
case 'hidive':
|
||||
return 'Hidive';
|
||||
case 'ao':
|
||||
return 'AnimeOnegai';
|
||||
case 'adn':
|
||||
return 'AnimationDigitalNetwork';
|
||||
}
|
||||
};
|
||||
const transformService = (service: StoreState['service']) => {
|
||||
switch(service) {
|
||||
case 'crunchy':
|
||||
return 'Crunchyroll';
|
||||
case 'hidive':
|
||||
return 'Hidive';
|
||||
case 'ao':
|
||||
return 'AnimeOnegai';
|
||||
case 'adn':
|
||||
return 'AnimationDigitalNetwork';
|
||||
}
|
||||
};
|
||||
|
||||
const msg = React.useContext(messageChannelContext);
|
||||
const msg = React.useContext(messageChannelContext);
|
||||
|
||||
const handleClick = (event: React.MouseEvent<HTMLElement>, n: 'settings'|'help') => {
|
||||
setAnchorEl(event.currentTarget);
|
||||
setMenuOpen(n);
|
||||
};
|
||||
const handleClose = () => {
|
||||
setAnchorEl(null);
|
||||
setMenuOpen(undefined);
|
||||
};
|
||||
const handleClick = (event: React.MouseEvent<HTMLElement>, n: 'settings'|'help') => {
|
||||
setAnchorEl(event.currentTarget);
|
||||
setMenuOpen(n);
|
||||
};
|
||||
const handleClose = () => {
|
||||
setAnchorEl(null);
|
||||
setMenuOpen(undefined);
|
||||
};
|
||||
|
||||
if (!msg)
|
||||
return <></>;
|
||||
if (!msg)
|
||||
return <></>;
|
||||
|
||||
return <Box sx={{ display: 'flex', marginBottom: '1rem', width: '100%', alignItems: 'center' }}>
|
||||
<Box sx={{ position: 'relative', left: '0%', width: '50%'}}>
|
||||
<Button onClick={(e) => handleClick(e, 'settings')}>
|
||||
return <Box sx={{ display: 'flex', marginBottom: '1rem', width: '100%', alignItems: 'center' }}>
|
||||
<Box sx={{ position: 'relative', left: '0%', width: '50%'}}>
|
||||
<Button onClick={(e) => handleClick(e, 'settings')}>
|
||||
Settings
|
||||
</Button>
|
||||
<Button onClick={(e) => handleClick(e, 'help')}>
|
||||
</Button>
|
||||
<Button onClick={(e) => handleClick(e, 'help')}>
|
||||
Help
|
||||
</Button>
|
||||
</Box>
|
||||
<Menu open={openMenu === 'settings'} anchorEl={anchorEl} onClose={handleClose}>
|
||||
<MenuItem onClick={() => {
|
||||
msg.openFolder('config');
|
||||
handleClose();
|
||||
}}>
|
||||
</Button>
|
||||
</Box>
|
||||
<Menu open={openMenu === 'settings'} anchorEl={anchorEl} onClose={handleClose}>
|
||||
<MenuItem onClick={() => {
|
||||
msg.openFolder('config');
|
||||
handleClose();
|
||||
}}>
|
||||
Open settings folder
|
||||
</MenuItem>
|
||||
<MenuItem onClick={() => {
|
||||
msg.openFile(['config', 'bin-path.yml']);
|
||||
handleClose();
|
||||
}}>
|
||||
</MenuItem>
|
||||
<MenuItem onClick={() => {
|
||||
msg.openFile(['config', 'bin-path.yml']);
|
||||
handleClose();
|
||||
}}>
|
||||
Open FFmpeg/Mkvmerge file
|
||||
</MenuItem>
|
||||
<MenuItem onClick={() => {
|
||||
msg.openFile(['config', 'cli-defaults.yml']);
|
||||
handleClose();
|
||||
}}>
|
||||
</MenuItem>
|
||||
<MenuItem onClick={() => {
|
||||
msg.openFile(['config', 'cli-defaults.yml']);
|
||||
handleClose();
|
||||
}}>
|
||||
Open advanced options
|
||||
</MenuItem>
|
||||
<MenuItem onClick={() => {
|
||||
msg.openFolder('content');
|
||||
handleClose();
|
||||
}}>
|
||||
</MenuItem>
|
||||
<MenuItem onClick={() => {
|
||||
msg.openFolder('content');
|
||||
handleClose();
|
||||
}}>
|
||||
Open output path
|
||||
</MenuItem>
|
||||
</Menu>
|
||||
<Menu open={openMenu === 'help'} anchorEl={anchorEl} onClose={handleClose}>
|
||||
<MenuItem onClick={() => {
|
||||
msg.openURL('https://github.com/anidl/multi-downloader-nx');
|
||||
handleClose();
|
||||
}}>
|
||||
</MenuItem>
|
||||
</Menu>
|
||||
<Menu open={openMenu === 'help'} anchorEl={anchorEl} onClose={handleClose}>
|
||||
<MenuItem onClick={() => {
|
||||
msg.openURL('https://github.com/anidl/multi-downloader-nx');
|
||||
handleClose();
|
||||
}}>
|
||||
GitHub
|
||||
</MenuItem>
|
||||
<MenuItem onClick={() => {
|
||||
msg.openURL('https://github.com/anidl/multi-downloader-nx/issues/new?assignees=AnimeDL,AnidlSupport&labels=bug&template=bug.yml&title=BUG');
|
||||
handleClose();
|
||||
}}>
|
||||
</MenuItem>
|
||||
<MenuItem onClick={() => {
|
||||
msg.openURL('https://github.com/anidl/multi-downloader-nx/issues/new?assignees=AnimeDL,AnidlSupport&labels=bug&template=bug.yml&title=BUG');
|
||||
handleClose();
|
||||
}}>
|
||||
Report a bug
|
||||
</MenuItem>
|
||||
<MenuItem onClick={() => {
|
||||
msg.openURL('https://github.com/anidl/multi-downloader-nx/graphs/contributors');
|
||||
handleClose();
|
||||
}}>
|
||||
</MenuItem>
|
||||
<MenuItem onClick={() => {
|
||||
msg.openURL('https://github.com/anidl/multi-downloader-nx/graphs/contributors');
|
||||
handleClose();
|
||||
}}>
|
||||
Contributors
|
||||
</MenuItem>
|
||||
<MenuItem onClick={() => {
|
||||
msg.openURL('https://discord.gg/qEpbWen5vq');
|
||||
handleClose();
|
||||
}}>
|
||||
</MenuItem>
|
||||
<MenuItem onClick={() => {
|
||||
msg.openURL('https://discord.gg/qEpbWen5vq');
|
||||
handleClose();
|
||||
}}>
|
||||
Discord
|
||||
</MenuItem>
|
||||
<MenuItem onClick={() => {
|
||||
handleClose();
|
||||
}}>
|
||||
</MenuItem>
|
||||
<MenuItem onClick={() => {
|
||||
handleClose();
|
||||
}}>
|
||||
Version: {store.version}
|
||||
</MenuItem>
|
||||
</Menu>
|
||||
<Typography variant="h5" color="text.primary">
|
||||
{transformService(store.service)}
|
||||
</Typography>
|
||||
</Box>;
|
||||
</MenuItem>
|
||||
</Menu>
|
||||
<Typography variant="h5" color="text.primary">
|
||||
{transformService(store.service)}
|
||||
</Typography>
|
||||
</Box>;
|
||||
};
|
||||
|
||||
export default MenuBar;
|
||||
|
|
|
|||
|
|
@ -6,9 +6,9 @@ export type RequireType<T> = {
|
|||
}
|
||||
|
||||
const Require = <T, >(props: React.PropsWithChildren<RequireType<T>>) => {
|
||||
return props.value === undefined ? <Backdrop open>
|
||||
<CircularProgress />
|
||||
</Backdrop> : <Box>{props.children}</Box>;
|
||||
return props.value === undefined ? <Backdrop open>
|
||||
<CircularProgress />
|
||||
</Backdrop> : <Box>{props.children}</Box>;
|
||||
};
|
||||
|
||||
export default Require;
|
||||
|
|
@ -5,37 +5,37 @@ import { messageChannelContext } from '../provider/MessageChannel';
|
|||
import Require from './Require';
|
||||
|
||||
const StartQueueButton: React.FC = () => {
|
||||
const messageChannel = React.useContext(messageChannelContext);
|
||||
const [start, setStart] = React.useState(false);
|
||||
const msg = React.useContext(messageChannelContext);
|
||||
const messageChannel = React.useContext(messageChannelContext);
|
||||
const [start, setStart] = React.useState(false);
|
||||
const msg = React.useContext(messageChannelContext);
|
||||
|
||||
React.useEffect(() => {
|
||||
(async () => {
|
||||
if (!msg)
|
||||
return alert('Invalid state: msg not found');
|
||||
setStart(await msg.getDownloadQueue());
|
||||
})();
|
||||
}, []);
|
||||
React.useEffect(() => {
|
||||
(async () => {
|
||||
if (!msg)
|
||||
return alert('Invalid state: msg not found');
|
||||
setStart(await msg.getDownloadQueue());
|
||||
})();
|
||||
}, []);
|
||||
|
||||
const change = async () => {
|
||||
if (await messageChannel?.isDownloading())
|
||||
alert('The current download will be finished before the queue stops');
|
||||
msg?.setDownloadQueue(!start);
|
||||
setStart(!start);
|
||||
};
|
||||
const change = async () => {
|
||||
if (await messageChannel?.isDownloading())
|
||||
alert('The current download will be finished before the queue stops');
|
||||
msg?.setDownloadQueue(!start);
|
||||
setStart(!start);
|
||||
};
|
||||
|
||||
return <Require value={messageChannel}>
|
||||
<Button
|
||||
startIcon={start ? <PauseCircleFilled /> : <PlayCircleFilled /> }
|
||||
variant='contained'
|
||||
onClick={change}
|
||||
sx={{ maxHeight: '2.3rem' }}
|
||||
>
|
||||
{
|
||||
start ? 'Stop Queue' : 'Start Queue'
|
||||
}
|
||||
</Button>
|
||||
</Require>;
|
||||
return <Require value={messageChannel}>
|
||||
<Button
|
||||
startIcon={start ? <PauseCircleFilled /> : <PlayCircleFilled /> }
|
||||
variant='contained'
|
||||
onClick={change}
|
||||
sx={{ maxHeight: '2.3rem' }}
|
||||
>
|
||||
{
|
||||
start ? 'Stop Queue' : 'Start Queue'
|
||||
}
|
||||
</Button>
|
||||
</Require>;
|
||||
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -12,54 +12,54 @@ export type ContextMenuProps<T extends HTMLElement> = {
|
|||
}
|
||||
|
||||
const buttonSx: SxProps = {
|
||||
'&:hover': {
|
||||
background: 'rgb(0, 30, 60)'
|
||||
},
|
||||
fontSize: '0.7rem',
|
||||
minHeight: '30px',
|
||||
justifyContent: 'center',
|
||||
p: 0
|
||||
'&:hover': {
|
||||
background: 'rgb(0, 30, 60)'
|
||||
},
|
||||
fontSize: '0.7rem',
|
||||
minHeight: '30px',
|
||||
justifyContent: 'center',
|
||||
p: 0
|
||||
};
|
||||
|
||||
function ContextMenu<T extends HTMLElement, >(props: ContextMenuProps<T>) {
|
||||
const [anchor, setAnchor] = React.useState( { x: 0, y: 0 } );
|
||||
const [anchor, setAnchor] = React.useState( { x: 0, y: 0 } );
|
||||
|
||||
const [show, setShow] = React.useState(false);
|
||||
const [show, setShow] = React.useState(false);
|
||||
|
||||
React.useEffect(() => {
|
||||
const { popupItem: ref } = props;
|
||||
if (ref.current === null)
|
||||
return;
|
||||
const listener = (ev: MouseEvent) => {
|
||||
ev.preventDefault();
|
||||
setAnchor({ x: ev.x + 10, y: ev.y + 10 });
|
||||
setShow(true);
|
||||
};
|
||||
ref.current.addEventListener('contextmenu', listener);
|
||||
React.useEffect(() => {
|
||||
const { popupItem: ref } = props;
|
||||
if (ref.current === null)
|
||||
return;
|
||||
const listener = (ev: MouseEvent) => {
|
||||
ev.preventDefault();
|
||||
setAnchor({ x: ev.x + 10, y: ev.y + 10 });
|
||||
setShow(true);
|
||||
};
|
||||
ref.current.addEventListener('contextmenu', listener);
|
||||
|
||||
return () => {
|
||||
if (ref.current)
|
||||
ref.current.removeEventListener('contextmenu', listener);
|
||||
};
|
||||
}, [ props.popupItem ]);
|
||||
return () => {
|
||||
if (ref.current)
|
||||
ref.current.removeEventListener('contextmenu', listener);
|
||||
};
|
||||
}, [ props.popupItem ]);
|
||||
|
||||
return show ? <Box sx={{ zIndex: 1400, p: 1, background: 'rgba(0, 0, 0, 0.75)', backdropFilter: 'blur(5px)', position: 'fixed', left: anchor.x, top: anchor.y }}>
|
||||
<List sx={{ p: 0, m: 0, display: 'flex', flexDirection: 'column' }}>
|
||||
{props.options.map((item, i) => {
|
||||
return item === 'divider' ? <Divider key={`ContextMenu_Divider_${i}_${item}`}/> :
|
||||
<Button color='inherit' key={`ContextMenu_Value_${i}_${item}`} onClick={() => {
|
||||
item.onClick();
|
||||
setShow(false);
|
||||
}} sx={buttonSx}>
|
||||
{item.text}
|
||||
</Button>;
|
||||
})}
|
||||
<Divider />
|
||||
<Button fullWidth color='inherit' onClick={() => setShow(false)} sx={buttonSx} >
|
||||
return show ? <Box sx={{ zIndex: 1400, p: 1, background: 'rgba(0, 0, 0, 0.75)', backdropFilter: 'blur(5px)', position: 'fixed', left: anchor.x, top: anchor.y }}>
|
||||
<List sx={{ p: 0, m: 0, display: 'flex', flexDirection: 'column' }}>
|
||||
{props.options.map((item, i) => {
|
||||
return item === 'divider' ? <Divider key={`ContextMenu_Divider_${i}_${item}`}/> :
|
||||
<Button color='inherit' key={`ContextMenu_Value_${i}_${item}`} onClick={() => {
|
||||
item.onClick();
|
||||
setShow(false);
|
||||
}} sx={buttonSx}>
|
||||
{item.text}
|
||||
</Button>;
|
||||
})}
|
||||
<Divider />
|
||||
<Button fullWidth color='inherit' onClick={() => setShow(false)} sx={buttonSx} >
|
||||
Close
|
||||
</Button>
|
||||
</List>
|
||||
</Box> : <></>;
|
||||
</Button>
|
||||
</List>
|
||||
</Box> : <></>;
|
||||
}
|
||||
|
||||
export default ContextMenu;
|
||||
|
|
|
|||
|
|
@ -7,18 +7,18 @@ import React from 'react';
|
|||
export type LinearProgressWithLabelProps = LinearProgressProps & { value: number };
|
||||
|
||||
const LinearProgressWithLabel: React.FC<LinearProgressWithLabelProps> = (props) => {
|
||||
return (
|
||||
<Box sx={{ display: 'flex', alignItems: 'center' }}>
|
||||
<Box sx={{ width: '100%', mr: 1 }}>
|
||||
<LinearProgress variant="determinate" {...props} />
|
||||
</Box>
|
||||
<Box sx={{ minWidth: 35 }}>
|
||||
<Typography variant="body2" color="text.secondary">{`${Math.round(
|
||||
props.value,
|
||||
)}%`}</Typography>
|
||||
</Box>
|
||||
</Box>
|
||||
);
|
||||
return (
|
||||
<Box sx={{ display: 'flex', alignItems: 'center' }}>
|
||||
<Box sx={{ width: '100%', mr: 1 }}>
|
||||
<LinearProgress variant="determinate" {...props} />
|
||||
</Box>
|
||||
<Box sx={{ minWidth: 35 }}>
|
||||
<Typography variant="body2" color="text.secondary">{`${Math.round(
|
||||
props.value,
|
||||
)}%`}</Typography>
|
||||
</Box>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
export default LinearProgressWithLabel;
|
||||
|
|
@ -12,63 +12,63 @@ export type MultiSelectProps = {
|
|||
const ITEM_HEIGHT = 48;
|
||||
const ITEM_PADDING_TOP = 8;
|
||||
const MenuProps = {
|
||||
PaperProps: {
|
||||
style: {
|
||||
maxHeight: ITEM_HEIGHT * 4.5 + ITEM_PADDING_TOP,
|
||||
width: 250
|
||||
PaperProps: {
|
||||
style: {
|
||||
maxHeight: ITEM_HEIGHT * 4.5 + ITEM_PADDING_TOP,
|
||||
width: 250
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
function getStyles(name: string, personName: readonly string[], theme: Theme) {
|
||||
return {
|
||||
fontWeight:
|
||||
return {
|
||||
fontWeight:
|
||||
(personName ?? []).indexOf(name) === -1
|
||||
? theme.typography.fontWeightRegular
|
||||
: theme.typography.fontWeightMedium
|
||||
};
|
||||
? theme.typography.fontWeightRegular
|
||||
: theme.typography.fontWeightMedium
|
||||
};
|
||||
}
|
||||
|
||||
const MultiSelect: React.FC<MultiSelectProps> = (props) => {
|
||||
const theme = useTheme();
|
||||
const theme = useTheme();
|
||||
|
||||
return <div>
|
||||
<FormControl sx={{ width: 300 }}>
|
||||
<InputLabel id="multi-select-label">{props.title}</InputLabel>
|
||||
<Select
|
||||
labelId="multi-select-label"
|
||||
id="multi-select"
|
||||
multiple
|
||||
value={(props.selected ?? [])}
|
||||
onChange={e => {
|
||||
const val = typeof e.target.value === 'string' ? e.target.value.split(',') : e.target.value;
|
||||
if (props.allOption && val.includes('all')) {
|
||||
if (props.values.length === val.length - 1)
|
||||
props.onChange([]);
|
||||
else
|
||||
props.onChange(props.values);
|
||||
} else {
|
||||
props.onChange(val);
|
||||
}
|
||||
}}
|
||||
input={<OutlinedInput id="select-multiple-chip" label={props.title} />}
|
||||
renderValue={(selected) => (
|
||||
selected.join(', ')
|
||||
)}
|
||||
MenuProps={MenuProps}
|
||||
>
|
||||
{props.values.concat(props.allOption ? 'all' : []).map((name) => (
|
||||
<MenuItem
|
||||
key={`${props.title}_${name}`}
|
||||
value={name}
|
||||
style={getStyles(name, props.selected, theme)}
|
||||
>
|
||||
{name}
|
||||
</MenuItem>
|
||||
))}
|
||||
</Select>
|
||||
</FormControl>
|
||||
</div>;
|
||||
return <div>
|
||||
<FormControl sx={{ width: 300 }}>
|
||||
<InputLabel id="multi-select-label">{props.title}</InputLabel>
|
||||
<Select
|
||||
labelId="multi-select-label"
|
||||
id="multi-select"
|
||||
multiple
|
||||
value={(props.selected ?? [])}
|
||||
onChange={e => {
|
||||
const val = typeof e.target.value === 'string' ? e.target.value.split(',') : e.target.value;
|
||||
if (props.allOption && val.includes('all')) {
|
||||
if (props.values.length === val.length - 1)
|
||||
props.onChange([]);
|
||||
else
|
||||
props.onChange(props.values);
|
||||
} else {
|
||||
props.onChange(val);
|
||||
}
|
||||
}}
|
||||
input={<OutlinedInput id="select-multiple-chip" label={props.title} />}
|
||||
renderValue={(selected) => (
|
||||
selected.join(', ')
|
||||
)}
|
||||
MenuProps={MenuProps}
|
||||
>
|
||||
{props.values.concat(props.allOption ? 'all' : []).map((name) => (
|
||||
<MenuItem
|
||||
key={`${props.title}_${name}`}
|
||||
value={name}
|
||||
style={getStyles(name, props.selected, theme)}
|
||||
>
|
||||
{name}
|
||||
</MenuItem>
|
||||
))}
|
||||
</Select>
|
||||
</FormControl>
|
||||
</div>;
|
||||
};
|
||||
|
||||
export default MultiSelect;
|
||||
|
|
@ -2,11 +2,11 @@ import React from 'react';
|
|||
import { StoreAction, StoreContext, StoreState } from '../provider/Store';
|
||||
|
||||
const useStore = () => {
|
||||
const context = React.useContext(StoreContext as unknown as React.Context<[StoreState, React.Dispatch<StoreAction<keyof StoreState>>]>);
|
||||
if (!context) {
|
||||
throw new Error('useStore must be used under Store');
|
||||
}
|
||||
return context;
|
||||
const context = React.useContext(StoreContext as unknown as React.Context<[StoreState, React.Dispatch<StoreAction<keyof StoreState>>]>);
|
||||
if (!context) {
|
||||
throw new Error('useStore must be used under Store');
|
||||
}
|
||||
return context;
|
||||
};
|
||||
|
||||
export default useStore;
|
||||
|
|
@ -17,35 +17,35 @@ document.body.style.justifyContent = 'center';
|
|||
|
||||
const notistackRef = React.createRef<SnackbarProvider>();
|
||||
const onClickDismiss = (key: SnackbarKey | undefined) => () => {
|
||||
if (notistackRef.current)
|
||||
notistackRef.current.closeSnackbar(key);
|
||||
if (notistackRef.current)
|
||||
notistackRef.current.closeSnackbar(key);
|
||||
};
|
||||
|
||||
const container = document.getElementById('root');
|
||||
const root = createRoot(container as HTMLElement);
|
||||
root.render(
|
||||
<ErrorHandler>
|
||||
<Store>
|
||||
<SnackbarProvider
|
||||
ref={notistackRef}
|
||||
action={(key) => (
|
||||
<IconButton onClick={onClickDismiss(key)} color="inherit">
|
||||
<CloseOutlined />
|
||||
</IconButton>
|
||||
)}
|
||||
>
|
||||
<Style>
|
||||
<MessageChannel>
|
||||
<ServiceProvider>
|
||||
<QueueProvider>
|
||||
<Box>
|
||||
<App />
|
||||
</Box>
|
||||
</QueueProvider>
|
||||
</ServiceProvider>
|
||||
</MessageChannel>
|
||||
</Style>
|
||||
</SnackbarProvider>
|
||||
</Store>
|
||||
</ErrorHandler>
|
||||
<ErrorHandler>
|
||||
<Store>
|
||||
<SnackbarProvider
|
||||
ref={notistackRef}
|
||||
action={(key) => (
|
||||
<IconButton onClick={onClickDismiss(key)} color="inherit">
|
||||
<CloseOutlined />
|
||||
</IconButton>
|
||||
)}
|
||||
>
|
||||
<Style>
|
||||
<MessageChannel>
|
||||
<ServiceProvider>
|
||||
<QueueProvider>
|
||||
<Box>
|
||||
<App />
|
||||
</Box>
|
||||
</QueueProvider>
|
||||
</ServiceProvider>
|
||||
</MessageChannel>
|
||||
</Style>
|
||||
</SnackbarProvider>
|
||||
</Store>
|
||||
</ErrorHandler>
|
||||
);
|
||||
|
|
@ -10,30 +10,30 @@ export default class ErrorHandler extends React.Component<{
|
|||
}
|
||||
}> {
|
||||
|
||||
constructor(props: {
|
||||
constructor(props: {
|
||||
children: React.ReactNode|React.ReactNode[]
|
||||
}) {
|
||||
super(props);
|
||||
this.state = { error: undefined };
|
||||
}
|
||||
super(props);
|
||||
this.state = { error: undefined };
|
||||
}
|
||||
|
||||
componentDidCatch(er: Error, stack: React.ErrorInfo) {
|
||||
this.setState({ error: { er, stack } });
|
||||
}
|
||||
componentDidCatch(er: Error, stack: React.ErrorInfo) {
|
||||
this.setState({ error: { er, stack } });
|
||||
}
|
||||
|
||||
render(): React.ReactNode {
|
||||
return this.state.error ?
|
||||
<Box sx={{ display: 'flex', flexDirection: 'column', justifyContent: 'center', alignItems: 'center', p: 2 }}>
|
||||
<Typography variant='body1' color='red'>
|
||||
{`${this.state.error.er.name}: ${this.state.error.er.message}`}
|
||||
<br/>
|
||||
{this.state.error.stack.componentStack?.split('\n').map(a => {
|
||||
return <>
|
||||
{a}
|
||||
<br/>
|
||||
</>;
|
||||
})}
|
||||
</Typography>
|
||||
</Box> : this.props.children;
|
||||
}
|
||||
render(): React.ReactNode {
|
||||
return this.state.error ?
|
||||
<Box sx={{ display: 'flex', flexDirection: 'column', justifyContent: 'center', alignItems: 'center', p: 2 }}>
|
||||
<Typography variant='body1' color='red'>
|
||||
{`${this.state.error.er.name}: ${this.state.error.er.message}`}
|
||||
<br/>
|
||||
{this.state.error.stack.componentStack?.split('\n').map(a => {
|
||||
return <>
|
||||
{a}
|
||||
<br/>
|
||||
</>;
|
||||
})}
|
||||
</Typography>
|
||||
</Box> : this.props.children;
|
||||
}
|
||||
}
|
||||
|
|
@ -12,233 +12,233 @@ import { GUIConfig } from '../../../../modules/module.cfg-loader';
|
|||
export type FrontEndMessages = (MessageHandler & { randomEvents: RandomEventHandler, logout: () => Promise<boolean> });
|
||||
|
||||
export class RandomEventHandler {
|
||||
private handler: {
|
||||
private handler: {
|
||||
[eventName in keyof RandomEvents]: Handler<eventName>[]
|
||||
} = {
|
||||
progress: [],
|
||||
finish: [],
|
||||
queueChange: [],
|
||||
current: []
|
||||
};
|
||||
progress: [],
|
||||
finish: [],
|
||||
queueChange: [],
|
||||
current: []
|
||||
};
|
||||
|
||||
public on<T extends keyof RandomEvents>(name: T, listener: Handler<T>) {
|
||||
if (Object.prototype.hasOwnProperty.call(this.handler, name)) {
|
||||
this.handler[name].push(listener as any);
|
||||
} else {
|
||||
this.handler[name] = [ listener as any ];
|
||||
public on<T extends keyof RandomEvents>(name: T, listener: Handler<T>) {
|
||||
if (Object.prototype.hasOwnProperty.call(this.handler, name)) {
|
||||
this.handler[name].push(listener as any);
|
||||
} else {
|
||||
this.handler[name] = [ listener as any ];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public emit<T extends keyof RandomEvents>(name: keyof RandomEvents, data: RandomEvent<T>) {
|
||||
(this.handler[name] ?? []).forEach(handler => handler(data as any));
|
||||
}
|
||||
public emit<T extends keyof RandomEvents>(name: keyof RandomEvents, data: RandomEvent<T>) {
|
||||
(this.handler[name] ?? []).forEach(handler => handler(data as any));
|
||||
}
|
||||
|
||||
public removeListener<T extends keyof RandomEvents>(name: T, listener: Handler<T>) {
|
||||
this.handler[name] = (this.handler[name] as Handler<T>[]).filter(a => a !== listener) as any;
|
||||
}
|
||||
public removeListener<T extends keyof RandomEvents>(name: T, listener: Handler<T>) {
|
||||
this.handler[name] = (this.handler[name] as Handler<T>[]).filter(a => a !== listener) as any;
|
||||
}
|
||||
}
|
||||
|
||||
export const messageChannelContext = React.createContext<FrontEndMessages|undefined>(undefined);
|
||||
|
||||
async function messageAndResponse<T extends keyof MessageTypes>(socket: WebSocket, msg: WSMessage<T>): Promise<WSMessage<T, 1>> {
|
||||
const id = v4();
|
||||
const ret = new Promise<WSMessage<T, 1>>((resolve) => {
|
||||
const handler = function({ data }: MessageEvent) {
|
||||
const parsed = JSON.parse(data.toString()) as WSMessageWithID<T, 1>;
|
||||
if (parsed.id === id) {
|
||||
socket.removeEventListener('message', handler);
|
||||
resolve(parsed);
|
||||
}
|
||||
};
|
||||
socket.addEventListener('message', handler);
|
||||
});
|
||||
const toSend = msg as WSMessageWithID<T>;
|
||||
toSend.id = id;
|
||||
const id = v4();
|
||||
const ret = new Promise<WSMessage<T, 1>>((resolve) => {
|
||||
const handler = function({ data }: MessageEvent) {
|
||||
const parsed = JSON.parse(data.toString()) as WSMessageWithID<T, 1>;
|
||||
if (parsed.id === id) {
|
||||
socket.removeEventListener('message', handler);
|
||||
resolve(parsed);
|
||||
}
|
||||
};
|
||||
socket.addEventListener('message', handler);
|
||||
});
|
||||
const toSend = msg as WSMessageWithID<T>;
|
||||
toSend.id = id;
|
||||
|
||||
socket.send(JSON.stringify(toSend));
|
||||
return ret;
|
||||
socket.send(JSON.stringify(toSend));
|
||||
return ret;
|
||||
}
|
||||
|
||||
const MessageChannelProvider: FCWithChildren = ({ children }) => {
|
||||
|
||||
const [store, dispatch] = useStore();
|
||||
const [socket, setSocket] = React.useState<undefined|WebSocket>();
|
||||
const [publicWS, setPublicWS] = React.useState<undefined|WebSocket>();
|
||||
const [usePassword, setUsePassword] = React.useState<'waiting'|'yes'|'no'>('waiting');
|
||||
const [isSetup, setIsSetup] = React.useState<'waiting'|'yes'|'no'>('waiting');
|
||||
const [store, dispatch] = useStore();
|
||||
const [socket, setSocket] = React.useState<undefined|WebSocket>();
|
||||
const [publicWS, setPublicWS] = React.useState<undefined|WebSocket>();
|
||||
const [usePassword, setUsePassword] = React.useState<'waiting'|'yes'|'no'>('waiting');
|
||||
const [isSetup, setIsSetup] = React.useState<'waiting'|'yes'|'no'>('waiting');
|
||||
|
||||
const { enqueueSnackbar } = useSnackbar();
|
||||
const { enqueueSnackbar } = useSnackbar();
|
||||
|
||||
React.useEffect(() => {
|
||||
const wss = new WebSocket(`${location.protocol == 'https:' ? 'wss' : 'ws'}://${process.env.NODE_ENV === 'development' ? 'localhost:3000' : window.location.host}/public`);
|
||||
wss.addEventListener('open', () => {
|
||||
setPublicWS(wss);
|
||||
});
|
||||
wss.addEventListener('error', () => {
|
||||
enqueueSnackbar('Unable to connect to server. Please reload the page to try again.', { variant: 'error' });
|
||||
});
|
||||
}, []);
|
||||
|
||||
React.useEffect(() => {
|
||||
(async () => {
|
||||
if (!publicWS)
|
||||
return;
|
||||
setUsePassword((await messageAndResponse(publicWS, { name: 'requirePassword', data: undefined })).data ? 'yes' : 'no');
|
||||
setIsSetup((await messageAndResponse(publicWS, { name: 'isSetup', data: undefined })).data ? 'yes' : 'no');
|
||||
})();
|
||||
}, [publicWS]);
|
||||
|
||||
const connect = (ev?: React.FormEvent<HTMLFormElement>) => {
|
||||
let search = new URLSearchParams();
|
||||
if (ev) {
|
||||
ev.preventDefault();
|
||||
const formData = new FormData(ev.currentTarget);
|
||||
const password = formData.get('password')?.toString();
|
||||
if (!password)
|
||||
return enqueueSnackbar('Please provide both a username and password', {
|
||||
variant: 'error'
|
||||
React.useEffect(() => {
|
||||
const wss = new WebSocket(`${location.protocol == 'https:' ? 'wss' : 'ws'}://${process.env.NODE_ENV === 'development' ? 'localhost:3000' : window.location.host}/public`);
|
||||
wss.addEventListener('open', () => {
|
||||
setPublicWS(wss);
|
||||
});
|
||||
search = new URLSearchParams({
|
||||
password
|
||||
});
|
||||
}
|
||||
wss.addEventListener('error', () => {
|
||||
enqueueSnackbar('Unable to connect to server. Please reload the page to try again.', { variant: 'error' });
|
||||
});
|
||||
}, []);
|
||||
|
||||
const wws = new WebSocket(`${location.protocol == 'https:' ? 'wss' : 'ws'}://${process.env.NODE_ENV === 'development' ? 'localhost:3000' : window.location.host}/private?${search}`, );
|
||||
wws.addEventListener('open', () => {
|
||||
console.log('[INFO] [WS] Connected');
|
||||
setSocket(wws);
|
||||
});
|
||||
wws.addEventListener('error', (er) => {
|
||||
console.error('[ERROR] [WS]', er);
|
||||
enqueueSnackbar('Unable to connect to server. Please check the password and try again.', {
|
||||
variant: 'error'
|
||||
});
|
||||
});
|
||||
};
|
||||
React.useEffect(() => {
|
||||
(async () => {
|
||||
if (!publicWS)
|
||||
return;
|
||||
setUsePassword((await messageAndResponse(publicWS, { name: 'requirePassword', data: undefined })).data ? 'yes' : 'no');
|
||||
setIsSetup((await messageAndResponse(publicWS, { name: 'isSetup', data: undefined })).data ? 'yes' : 'no');
|
||||
})();
|
||||
}, [publicWS]);
|
||||
|
||||
const setup = async (ev: React.FormEvent<HTMLFormElement>) => {
|
||||
ev.preventDefault();
|
||||
if (!socket)
|
||||
return enqueueSnackbar('Invalid state: socket not found', { variant: 'error' });
|
||||
const formData = new FormData(ev.currentTarget);
|
||||
const password = formData.get('password');
|
||||
const data = {
|
||||
port: parseInt(formData.get('port')?.toString() ?? '') ?? 3000,
|
||||
password: password ? password.toString() : undefined
|
||||
} as GUIConfig;
|
||||
await messageAndResponse(socket, { name: 'setupServer', data });
|
||||
enqueueSnackbar(`The following settings have been set: Port=${data.port}, Password=${data.password ?? 'noPasswordRequired'}`, {
|
||||
variant: 'success',
|
||||
persist: true
|
||||
});
|
||||
enqueueSnackbar('Please restart the server now.', {
|
||||
variant: 'info',
|
||||
persist: true
|
||||
});
|
||||
};
|
||||
const connect = (ev?: React.FormEvent<HTMLFormElement>) => {
|
||||
let search = new URLSearchParams();
|
||||
if (ev) {
|
||||
ev.preventDefault();
|
||||
const formData = new FormData(ev.currentTarget);
|
||||
const password = formData.get('password')?.toString();
|
||||
if (!password)
|
||||
return enqueueSnackbar('Please provide both a username and password', {
|
||||
variant: 'error'
|
||||
});
|
||||
search = new URLSearchParams({
|
||||
password
|
||||
});
|
||||
}
|
||||
|
||||
const randomEventHandler = React.useMemo(() => new RandomEventHandler(), []);
|
||||
|
||||
React.useEffect(() => {
|
||||
(async () => {
|
||||
if (!socket)
|
||||
return;
|
||||
const currentService = await messageAndResponse(socket, { name: 'type', data: undefined });
|
||||
if (currentService.data !== undefined)
|
||||
return dispatch({ type: 'service', payload: currentService.data });
|
||||
if (store.service !== currentService.data)
|
||||
messageAndResponse(socket, { name: 'setup', data: store.service });
|
||||
})();
|
||||
}, [store.service, dispatch, socket]);
|
||||
|
||||
React.useEffect(() => {
|
||||
if (!socket)
|
||||
return;
|
||||
/* finish is a placeholder */
|
||||
const listener = (initalData: MessageEvent<string>) => {
|
||||
const data = JSON.parse(initalData.data) as RandomEvent<'finish'>;
|
||||
randomEventHandler.emit(data.name, data);
|
||||
const wws = new WebSocket(`${location.protocol == 'https:' ? 'wss' : 'ws'}://${process.env.NODE_ENV === 'development' ? 'localhost:3000' : window.location.host}/private?${search}`, );
|
||||
wws.addEventListener('open', () => {
|
||||
console.log('[INFO] [WS] Connected');
|
||||
setSocket(wws);
|
||||
});
|
||||
wws.addEventListener('error', (er) => {
|
||||
console.error('[ERROR] [WS]', er);
|
||||
enqueueSnackbar('Unable to connect to server. Please check the password and try again.', {
|
||||
variant: 'error'
|
||||
});
|
||||
});
|
||||
};
|
||||
socket.addEventListener('message', listener);
|
||||
return () => {
|
||||
socket.removeEventListener('message', listener);
|
||||
|
||||
const setup = async (ev: React.FormEvent<HTMLFormElement>) => {
|
||||
ev.preventDefault();
|
||||
if (!socket)
|
||||
return enqueueSnackbar('Invalid state: socket not found', { variant: 'error' });
|
||||
const formData = new FormData(ev.currentTarget);
|
||||
const password = formData.get('password');
|
||||
const data = {
|
||||
port: parseInt(formData.get('port')?.toString() ?? '') ?? 3000,
|
||||
password: password ? password.toString() : undefined
|
||||
} as GUIConfig;
|
||||
await messageAndResponse(socket, { name: 'setupServer', data });
|
||||
enqueueSnackbar(`The following settings have been set: Port=${data.port}, Password=${data.password ?? 'noPasswordRequired'}`, {
|
||||
variant: 'success',
|
||||
persist: true
|
||||
});
|
||||
enqueueSnackbar('Please restart the server now.', {
|
||||
variant: 'info',
|
||||
persist: true
|
||||
});
|
||||
};
|
||||
}, [ socket ]);
|
||||
|
||||
if (usePassword === 'waiting')
|
||||
return <></>;
|
||||
const randomEventHandler = React.useMemo(() => new RandomEventHandler(), []);
|
||||
|
||||
if (socket === undefined) {
|
||||
if (usePassword === 'no') {
|
||||
connect(undefined);
|
||||
return <></>;
|
||||
}
|
||||
return <Box sx={{ mt: 3, display: 'flex', flexDirection: 'column', justifyItems: 'center', alignItems: 'center' }}>
|
||||
<Avatar sx={{ m: 1, bgcolor: 'secondary.main' }}>
|
||||
<LockOutlined />
|
||||
</Avatar>
|
||||
<Typography component="h1" variant="h5" color="text.primary">
|
||||
React.useEffect(() => {
|
||||
(async () => {
|
||||
if (!socket)
|
||||
return;
|
||||
const currentService = await messageAndResponse(socket, { name: 'type', data: undefined });
|
||||
if (currentService.data !== undefined)
|
||||
return dispatch({ type: 'service', payload: currentService.data });
|
||||
if (store.service !== currentService.data)
|
||||
messageAndResponse(socket, { name: 'setup', data: store.service });
|
||||
})();
|
||||
}, [store.service, dispatch, socket]);
|
||||
|
||||
React.useEffect(() => {
|
||||
if (!socket)
|
||||
return;
|
||||
/* finish is a placeholder */
|
||||
const listener = (initalData: MessageEvent<string>) => {
|
||||
const data = JSON.parse(initalData.data) as RandomEvent<'finish'>;
|
||||
randomEventHandler.emit(data.name, data);
|
||||
};
|
||||
socket.addEventListener('message', listener);
|
||||
return () => {
|
||||
socket.removeEventListener('message', listener);
|
||||
};
|
||||
}, [ socket ]);
|
||||
|
||||
if (usePassword === 'waiting')
|
||||
return <></>;
|
||||
|
||||
if (socket === undefined) {
|
||||
if (usePassword === 'no') {
|
||||
connect(undefined);
|
||||
return <></>;
|
||||
}
|
||||
return <Box sx={{ mt: 3, display: 'flex', flexDirection: 'column', justifyItems: 'center', alignItems: 'center' }}>
|
||||
<Avatar sx={{ m: 1, bgcolor: 'secondary.main' }}>
|
||||
<LockOutlined />
|
||||
</Avatar>
|
||||
<Typography component="h1" variant="h5" color="text.primary">
|
||||
Login
|
||||
</Typography>
|
||||
<Box component="form" onSubmit={connect} sx={{ mt: 1 }}>
|
||||
<TextField name="password" margin='normal' type="password" fullWidth variant="filled" required label={'Password'} />
|
||||
<Button type='submit' variant='contained' sx={{ mt: 3, mb: 2 }} fullWidth>Login</Button>
|
||||
<Typography color="text.secondary" align='center' component="p" variant='body2'>
|
||||
</Typography>
|
||||
<Box component="form" onSubmit={connect} sx={{ mt: 1 }}>
|
||||
<TextField name="password" margin='normal' type="password" fullWidth variant="filled" required label={'Password'} />
|
||||
<Button type='submit' variant='contained' sx={{ mt: 3, mb: 2 }} fullWidth>Login</Button>
|
||||
<Typography color="text.secondary" align='center' component="p" variant='body2'>
|
||||
You need to login in order to use this tool.
|
||||
</Typography>
|
||||
</Box>
|
||||
</Box>;
|
||||
}
|
||||
</Typography>
|
||||
</Box>
|
||||
</Box>;
|
||||
}
|
||||
|
||||
if (isSetup === 'no') {
|
||||
return <Box sx={{ mt: 3, display: 'flex', flexDirection: 'column', justifyItems: 'center', alignItems: 'center' }}>
|
||||
<Avatar sx={{ m: 1, bgcolor: 'secondary.main' }}>
|
||||
<PowerSettingsNew />
|
||||
</Avatar>
|
||||
<Typography component="h1" variant="h5" color="text.primary">
|
||||
if (isSetup === 'no') {
|
||||
return <Box sx={{ mt: 3, display: 'flex', flexDirection: 'column', justifyItems: 'center', alignItems: 'center' }}>
|
||||
<Avatar sx={{ m: 1, bgcolor: 'secondary.main' }}>
|
||||
<PowerSettingsNew />
|
||||
</Avatar>
|
||||
<Typography component="h1" variant="h5" color="text.primary">
|
||||
Confirm
|
||||
</Typography>
|
||||
<Box component="form" onSubmit={setup} sx={{ mt: 1 }}>
|
||||
<TextField name="port" margin='normal' type="number" fullWidth variant="filled" required label={'Port'} defaultValue={3000} />
|
||||
<TextField name="password" margin='normal' type="password" fullWidth variant="filled" label={'Password'} />
|
||||
<Button type='submit' variant='contained' sx={{ mt: 3, mb: 2 }} fullWidth>Confirm</Button>
|
||||
<Typography color="text.secondary" align='center' component="p" variant='body2'>
|
||||
</Typography>
|
||||
<Box component="form" onSubmit={setup} sx={{ mt: 1 }}>
|
||||
<TextField name="port" margin='normal' type="number" fullWidth variant="filled" required label={'Port'} defaultValue={3000} />
|
||||
<TextField name="password" margin='normal' type="password" fullWidth variant="filled" label={'Password'} />
|
||||
<Button type='submit' variant='contained' sx={{ mt: 3, mb: 2 }} fullWidth>Confirm</Button>
|
||||
<Typography color="text.secondary" align='center' component="p" variant='body2'>
|
||||
Please enter data that will be set to use this tool.
|
||||
<br />
|
||||
<br />
|
||||
Leave blank to use no password (NOT RECOMMENDED)!
|
||||
</Typography>
|
||||
</Box>
|
||||
</Box>;
|
||||
}
|
||||
</Typography>
|
||||
</Box>
|
||||
</Box>;
|
||||
}
|
||||
|
||||
const messageHandler: FrontEndMessages = {
|
||||
name: 'default',
|
||||
auth: async (data) => (await messageAndResponse(socket, { name: 'auth', data })).data,
|
||||
version: async () => (await messageAndResponse(socket, { name: 'version', data: undefined })).data,
|
||||
checkToken: async () => (await messageAndResponse(socket, { name: 'checkToken', data: undefined })).data,
|
||||
search: async (data) => (await messageAndResponse(socket, { name: 'search', data })).data,
|
||||
handleDefault: async (data) => (await messageAndResponse(socket, { name: 'default', data })).data,
|
||||
availableDubCodes: async () => (await messageAndResponse(socket, { name: 'availableDubCodes', data: undefined})).data,
|
||||
availableSubCodes: async () => (await messageAndResponse(socket, { name: 'availableSubCodes', data: undefined })).data,
|
||||
resolveItems: async (data) => (await messageAndResponse(socket, { name: 'resolveItems', data })).data,
|
||||
listEpisodes: async (data) => (await messageAndResponse(socket, { name: 'listEpisodes', data })).data,
|
||||
randomEvents: randomEventHandler,
|
||||
downloadItem: (data) => messageAndResponse(socket, { name: 'downloadItem', data }),
|
||||
isDownloading: async () => (await messageAndResponse(socket, { name: 'isDownloading', data: undefined })).data,
|
||||
openFolder: async (data) => messageAndResponse(socket, { name: 'openFolder', data }),
|
||||
logout: async () => (await messageAndResponse(socket, { name: 'changeProvider', data: undefined })).data,
|
||||
openFile: async (data) => await messageAndResponse(socket, { name: 'openFile', data }),
|
||||
openURL: async (data) => await messageAndResponse(socket, { name: 'openURL', data }),
|
||||
getQueue: async () => (await messageAndResponse(socket, { name: 'getQueue', data: undefined })).data,
|
||||
removeFromQueue: async (data) => await messageAndResponse(socket, { name: 'removeFromQueue', data }),
|
||||
clearQueue: async () => await messageAndResponse(socket, { name: 'clearQueue', data: undefined }),
|
||||
setDownloadQueue: async (data) => await messageAndResponse(socket, { name: 'setDownloadQueue', data }),
|
||||
getDownloadQueue: async () => (await messageAndResponse(socket, { name: 'getDownloadQueue', data: undefined })).data,
|
||||
};
|
||||
const messageHandler: FrontEndMessages = {
|
||||
name: 'default',
|
||||
auth: async (data) => (await messageAndResponse(socket, { name: 'auth', data })).data,
|
||||
version: async () => (await messageAndResponse(socket, { name: 'version', data: undefined })).data,
|
||||
checkToken: async () => (await messageAndResponse(socket, { name: 'checkToken', data: undefined })).data,
|
||||
search: async (data) => (await messageAndResponse(socket, { name: 'search', data })).data,
|
||||
handleDefault: async (data) => (await messageAndResponse(socket, { name: 'default', data })).data,
|
||||
availableDubCodes: async () => (await messageAndResponse(socket, { name: 'availableDubCodes', data: undefined})).data,
|
||||
availableSubCodes: async () => (await messageAndResponse(socket, { name: 'availableSubCodes', data: undefined })).data,
|
||||
resolveItems: async (data) => (await messageAndResponse(socket, { name: 'resolveItems', data })).data,
|
||||
listEpisodes: async (data) => (await messageAndResponse(socket, { name: 'listEpisodes', data })).data,
|
||||
randomEvents: randomEventHandler,
|
||||
downloadItem: (data) => messageAndResponse(socket, { name: 'downloadItem', data }),
|
||||
isDownloading: async () => (await messageAndResponse(socket, { name: 'isDownloading', data: undefined })).data,
|
||||
openFolder: async (data) => messageAndResponse(socket, { name: 'openFolder', data }),
|
||||
logout: async () => (await messageAndResponse(socket, { name: 'changeProvider', data: undefined })).data,
|
||||
openFile: async (data) => await messageAndResponse(socket, { name: 'openFile', data }),
|
||||
openURL: async (data) => await messageAndResponse(socket, { name: 'openURL', data }),
|
||||
getQueue: async () => (await messageAndResponse(socket, { name: 'getQueue', data: undefined })).data,
|
||||
removeFromQueue: async (data) => await messageAndResponse(socket, { name: 'removeFromQueue', data }),
|
||||
clearQueue: async () => await messageAndResponse(socket, { name: 'clearQueue', data: undefined }),
|
||||
setDownloadQueue: async (data) => await messageAndResponse(socket, { name: 'setDownloadQueue', data }),
|
||||
getDownloadQueue: async () => (await messageAndResponse(socket, { name: 'getDownloadQueue', data: undefined })).data,
|
||||
};
|
||||
|
||||
return <messageChannelContext.Provider value={messageHandler}>
|
||||
{children}
|
||||
</messageChannelContext.Provider>;
|
||||
return <messageChannelContext.Provider value={messageHandler}>
|
||||
{children}
|
||||
</messageChannelContext.Provider>;
|
||||
};
|
||||
|
||||
export default MessageChannelProvider;
|
||||
|
|
|
|||
|
|
@ -6,30 +6,30 @@ import { RandomEvent } from '../../../../@types/randomEvents';
|
|||
export const queueContext = React.createContext<QueueItem[]>([]);
|
||||
|
||||
const QueueProvider: FCWithChildren = ({ children }) => {
|
||||
const msg = React.useContext(messageChannelContext);
|
||||
const msg = React.useContext(messageChannelContext);
|
||||
|
||||
const [ready, setReady] = React.useState(false);
|
||||
const [queue, setQueue] = React.useState<QueueItem[]>([]);
|
||||
const [ready, setReady] = React.useState(false);
|
||||
const [queue, setQueue] = React.useState<QueueItem[]>([]);
|
||||
|
||||
React.useEffect(() => {
|
||||
if (msg && !ready) {
|
||||
msg.getQueue().then(data => {
|
||||
setQueue(data);
|
||||
setReady(true);
|
||||
});
|
||||
}
|
||||
const listener = (ev: RandomEvent<'queueChange'>) => {
|
||||
setQueue(ev.data);
|
||||
};
|
||||
msg?.randomEvents.on('queueChange', listener);
|
||||
return () => {
|
||||
msg?.randomEvents.removeListener('queueChange', listener);
|
||||
};
|
||||
}, [ msg ]);
|
||||
React.useEffect(() => {
|
||||
if (msg && !ready) {
|
||||
msg.getQueue().then(data => {
|
||||
setQueue(data);
|
||||
setReady(true);
|
||||
});
|
||||
}
|
||||
const listener = (ev: RandomEvent<'queueChange'>) => {
|
||||
setQueue(ev.data);
|
||||
};
|
||||
msg?.randomEvents.on('queueChange', listener);
|
||||
return () => {
|
||||
msg?.randomEvents.removeListener('queueChange', listener);
|
||||
};
|
||||
}, [ msg ]);
|
||||
|
||||
return <queueContext.Provider value={queue}>
|
||||
{children}
|
||||
</queueContext.Provider>;
|
||||
return <queueContext.Provider value={queue}>
|
||||
{children}
|
||||
</queueContext.Provider>;
|
||||
};
|
||||
|
||||
export default QueueProvider;
|
||||
|
|
@ -8,28 +8,28 @@ type Services = 'crunchy'|'hidive'|'ao'|'adn';
|
|||
export const serviceContext = React.createContext<Services|undefined>(undefined);
|
||||
|
||||
const ServiceProvider: FCWithChildren = ({ children }) => {
|
||||
const [ { service }, dispatch ] = useStore();
|
||||
const [ { service }, dispatch ] = useStore();
|
||||
|
||||
const setService = (s: StoreState['service']) => {
|
||||
dispatch({
|
||||
type: 'service',
|
||||
payload: s
|
||||
});
|
||||
};
|
||||
const setService = (s: StoreState['service']) => {
|
||||
dispatch({
|
||||
type: 'service',
|
||||
payload: s
|
||||
});
|
||||
};
|
||||
|
||||
return service === undefined ?
|
||||
<Box sx={{ justifyContent: 'center', alignItems: 'center', display: 'flex', flexDirection: 'column', position: 'relative', top: '40vh'}}>
|
||||
<Typography color="text.primary" variant='h3' sx={{ textAlign: 'center', mb: 5 }}>Please select your service</Typography>
|
||||
<Box sx={{ display: 'flex', gap: 2, justifyContent: 'center' }}>
|
||||
<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://static.diceplatform.com/prod/original/dce.hidive/settings/HIDIVE_AppLogo_1024x1024.0G0vK.jpg'} />}>Hidive</Button>
|
||||
<Button size='large' variant="contained" onClick={() => setService('ao')} startIcon={<Avatar src={'https://www.animeonegai.com/assets/img/anime/general/ao3-favicon.png'} />}>AnimeOnegai</Button>
|
||||
<Button size='large' variant="contained" onClick={() => setService('adn')} startIcon={<Avatar src={'https://animationdigitalnetwork.com/favicon.ico'} />}>AnimationDigitalNetwork</Button>
|
||||
</Box>
|
||||
</Box>
|
||||
: <serviceContext.Provider value={service}>
|
||||
{children}
|
||||
</serviceContext.Provider>;
|
||||
return service === undefined ?
|
||||
<Box sx={{ justifyContent: 'center', alignItems: 'center', display: 'flex', flexDirection: 'column', position: 'relative', top: '40vh'}}>
|
||||
<Typography color="text.primary" variant='h3' sx={{ textAlign: 'center', mb: 5 }}>Please select your service</Typography>
|
||||
<Box sx={{ display: 'flex', gap: 2, justifyContent: 'center' }}>
|
||||
<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://static.diceplatform.com/prod/original/dce.hidive/settings/HIDIVE_AppLogo_1024x1024.0G0vK.jpg'} />}>Hidive</Button>
|
||||
<Button size='large' variant="contained" onClick={() => setService('ao')} startIcon={<Avatar src={'https://www.animeonegai.com/assets/img/anime/general/ao3-favicon.png'} />}>AnimeOnegai</Button>
|
||||
<Button size='large' variant="contained" onClick={() => setService('adn')} startIcon={<Avatar src={'https://animationdigitalnetwork.com/favicon.ico'} />}>AnimationDigitalNetwork</Button>
|
||||
</Box>
|
||||
</Box>
|
||||
: <serviceContext.Provider value={service}>
|
||||
{children}
|
||||
</serviceContext.Provider>;
|
||||
};
|
||||
|
||||
export default ServiceProvider;
|
||||
|
|
@ -32,35 +32,35 @@ export type StoreAction<T extends (keyof StoreState)> = {
|
|||
}
|
||||
|
||||
const Reducer = <T extends keyof StoreState,>(state: StoreState, action: StoreAction<T>): StoreState => {
|
||||
switch(action.type) {
|
||||
default:
|
||||
return { ...state, [action.type]: action.payload };
|
||||
}
|
||||
switch(action.type) {
|
||||
default:
|
||||
return { ...state, [action.type]: action.payload };
|
||||
}
|
||||
};
|
||||
|
||||
const initialState: StoreState = {
|
||||
downloadOptions: {
|
||||
id: '',
|
||||
q: 0,
|
||||
e: '',
|
||||
dubLang: [ 'jpn' ],
|
||||
dlsubs: [ 'all' ],
|
||||
fileName: '',
|
||||
dlVideoOnce: false,
|
||||
all: false,
|
||||
but: false,
|
||||
noaudio: false,
|
||||
novids: false,
|
||||
simul: false
|
||||
},
|
||||
service: undefined,
|
||||
episodeListing: [],
|
||||
version: '',
|
||||
downloadOptions: {
|
||||
id: '',
|
||||
q: 0,
|
||||
e: '',
|
||||
dubLang: [ 'jpn' ],
|
||||
dlsubs: [ 'all' ],
|
||||
fileName: '',
|
||||
dlVideoOnce: false,
|
||||
all: false,
|
||||
but: false,
|
||||
noaudio: false,
|
||||
novids: false,
|
||||
simul: false
|
||||
},
|
||||
service: undefined,
|
||||
episodeListing: [],
|
||||
version: '',
|
||||
};
|
||||
|
||||
const Store: FCWithChildren = ({children}) => {
|
||||
const [state, dispatch] = React.useReducer(Reducer, initialState);
|
||||
/*React.useEffect(() => {
|
||||
const [state, dispatch] = React.useReducer(Reducer, initialState);
|
||||
/*React.useEffect(() => {
|
||||
if (!state.unsavedChanges.has)
|
||||
return;
|
||||
const unsavedChanges = (ev: BeforeUnloadEvent, lang: LanguageContextType) => {
|
||||
|
|
@ -79,11 +79,11 @@ const Store: FCWithChildren = ({children}) => {
|
|||
return () => window.removeEventListener('beforeunload', windowListener);
|
||||
}, [state.unsavedChanges.has]);*/
|
||||
|
||||
return (
|
||||
<StoreContext.Provider value={[state, dispatch]}>
|
||||
{children}
|
||||
</StoreContext.Provider>
|
||||
);
|
||||
return (
|
||||
<StoreContext.Provider value={[state, dispatch]}>
|
||||
{children}
|
||||
</StoreContext.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
/* Importent Notice -- The 'queue' generic will be overriden */
|
||||
|
|
|
|||
|
|
@ -1,29 +1,29 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"outDir": "./build",
|
||||
"target": "es5",
|
||||
"lib": [
|
||||
"dom",
|
||||
"dom.iterable",
|
||||
"esnext"
|
||||
],
|
||||
"allowJs": true,
|
||||
"skipLibCheck": true,
|
||||
"esModuleInterop": true,
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"strict": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"noFallthroughCasesInSwitch": true,
|
||||
"module": "CommonJS",
|
||||
"moduleResolution": "node",
|
||||
"resolveJsonModule": true,
|
||||
"isolatedModules": true,
|
||||
//"noEmit": true,
|
||||
"jsx": "react-jsx",
|
||||
"downlevelIteration": true
|
||||
},
|
||||
"include": [
|
||||
"./src",
|
||||
"./webpack.config.ts"
|
||||
]
|
||||
"compilerOptions": {
|
||||
"outDir": "./build",
|
||||
"target": "es5",
|
||||
"lib": [
|
||||
"dom",
|
||||
"dom.iterable",
|
||||
"esnext"
|
||||
],
|
||||
"allowJs": true,
|
||||
"skipLibCheck": true,
|
||||
"esModuleInterop": true,
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"strict": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"noFallthroughCasesInSwitch": true,
|
||||
"module": "CommonJS",
|
||||
"moduleResolution": "node",
|
||||
"resolveJsonModule": true,
|
||||
"isolatedModules": true,
|
||||
//"noEmit": true,
|
||||
"jsx": "react-jsx",
|
||||
"downlevelIteration": true
|
||||
},
|
||||
"include": [
|
||||
"./src",
|
||||
"./webpack.config.ts"
|
||||
]
|
||||
}
|
||||
|
|
@ -4,55 +4,55 @@ import path from 'path';
|
|||
import type { Configuration as DevServerConfig } from 'webpack-dev-server';
|
||||
|
||||
const config: Configuration & DevServerConfig = {
|
||||
devServer: {
|
||||
proxy: [
|
||||
{
|
||||
target: 'http://localhost:3000',
|
||||
context: ['/public', '/private'],
|
||||
ws: true
|
||||
}
|
||||
],
|
||||
},
|
||||
entry: './src/index.tsx',
|
||||
mode: 'production',
|
||||
output: {
|
||||
path: path.resolve(process.cwd(), './build'),
|
||||
filename: 'index.js',
|
||||
},
|
||||
target: 'web',
|
||||
resolve: {
|
||||
extensions: ['.js', '.jsx', '.ts', '.tsx', '.json'],
|
||||
},
|
||||
performance: false,
|
||||
module: {
|
||||
rules: [
|
||||
{
|
||||
test: /\.(ts|tsx)$/,
|
||||
exclude: /node_modules/,
|
||||
use: {
|
||||
'loader': 'babel-loader',
|
||||
options: {
|
||||
presets: [
|
||||
'@babel/typescript',
|
||||
'@babel/preset-react',
|
||||
['@babel/preset-env', {
|
||||
targets: 'defaults'
|
||||
}]
|
||||
]
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
test: /\.css$/i,
|
||||
use: ['style-loader', 'css-loader'],
|
||||
},
|
||||
],
|
||||
},
|
||||
plugins: [
|
||||
new HtmlWebpackPlugin({
|
||||
template: path.join(process.cwd(), 'public', 'index.html')
|
||||
})
|
||||
]
|
||||
devServer: {
|
||||
proxy: [
|
||||
{
|
||||
target: 'http://localhost:3000',
|
||||
context: ['/public', '/private'],
|
||||
ws: true
|
||||
}
|
||||
],
|
||||
},
|
||||
entry: './src/index.tsx',
|
||||
mode: 'production',
|
||||
output: {
|
||||
path: path.resolve(process.cwd(), './build'),
|
||||
filename: 'index.js',
|
||||
},
|
||||
target: 'web',
|
||||
resolve: {
|
||||
extensions: ['.js', '.jsx', '.ts', '.tsx', '.json'],
|
||||
},
|
||||
performance: false,
|
||||
module: {
|
||||
rules: [
|
||||
{
|
||||
test: /\.(ts|tsx)$/,
|
||||
exclude: /node_modules/,
|
||||
use: {
|
||||
'loader': 'babel-loader',
|
||||
options: {
|
||||
presets: [
|
||||
'@babel/typescript',
|
||||
'@babel/preset-react',
|
||||
['@babel/preset-env', {
|
||||
targets: 'defaults'
|
||||
}]
|
||||
]
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
test: /\.css$/i,
|
||||
use: ['style-loader', 'css-loader'],
|
||||
},
|
||||
],
|
||||
},
|
||||
plugins: [
|
||||
new HtmlWebpackPlugin({
|
||||
template: path.join(process.cwd(), 'public', 'index.html')
|
||||
})
|
||||
]
|
||||
};
|
||||
|
||||
export default config;
|
||||
|
|
|
|||
238
package.json
238
package.json
|
|
@ -1,120 +1,120 @@
|
|||
{
|
||||
"name": "multi-downloader-nx",
|
||||
"short_name": "aniDL",
|
||||
"version": "5.5.6",
|
||||
"description": "Downloader for Crunchyroll, Hidive, AnimeOnegai, and AnimationDigitalNetwork with CLI and GUI",
|
||||
"keywords": [
|
||||
"download",
|
||||
"downloader",
|
||||
"hidive",
|
||||
"crunchy",
|
||||
"crunchyroll",
|
||||
"util",
|
||||
"utility",
|
||||
"cli",
|
||||
"gui"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18",
|
||||
"pnpm": ">=7"
|
||||
},
|
||||
"author": "AnimeDL <AnimeDL@users.noreply.github.com>",
|
||||
"contributors": [
|
||||
{
|
||||
"name": "AnimeDL <AnimeDL@users.noreply.github.com>"
|
||||
},
|
||||
{
|
||||
"name": "AniDL <AniDL@users.noreply.github.com>"
|
||||
},
|
||||
{
|
||||
"name": "AnidlSupport <AnidlSupport@users.noreply.github.com>"
|
||||
}
|
||||
],
|
||||
"homepage": "https://github.com/anidl/multi-downloader-nx",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/anidl/multi-downloader-nx.git"
|
||||
},
|
||||
"bugs": {
|
||||
"url": "https://github.com/anidl/multi-downloader-nx/issues"
|
||||
},
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@bufbuild/buf": "^1.57.2",
|
||||
"@bufbuild/protobuf": "^2.8.0",
|
||||
"@bufbuild/protoc-gen-es": "^2.8.0",
|
||||
"@yao-pkg/pkg": "^6.6.0",
|
||||
"binary-parser": "^2.2.1",
|
||||
"binary-parser-encoder": "^1.5.3",
|
||||
"bn.js": "^5.2.2",
|
||||
"cors": "^2.8.5",
|
||||
"elliptic": "^6.6.1",
|
||||
"esbuild": "^0.25.10",
|
||||
"express": "^5.1.0",
|
||||
"fast-xml-parser": "^5.2.5",
|
||||
"ffprobe": "^1.1.2",
|
||||
"fs-extra": "^11.3.2",
|
||||
"iso-639": "^0.2.2",
|
||||
"leven": "^3.1.0",
|
||||
"log4js": "^6.9.1",
|
||||
"long": "^5.3.2",
|
||||
"lookpath": "^1.2.3",
|
||||
"m3u8-parsed": "^2.0.0",
|
||||
"mpd-parser": "^1.3.1",
|
||||
"node-forge": "^1.3.1",
|
||||
"ofetch": "^1.4.1",
|
||||
"open": "^8.4.2",
|
||||
"protobufjs": "^7.5.4",
|
||||
"puppeteer-real-browser": "^1.4.4",
|
||||
"ws": "^8.18.3",
|
||||
"yaml": "^2.8.1",
|
||||
"yargs": "^17.7.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@eslint/js": "^9.35.0",
|
||||
"@types/bn.js": "^5.2.0",
|
||||
"@types/cors": "^2.8.19",
|
||||
"@types/elliptic": "^6.4.18",
|
||||
"@types/express": "^5.0.3",
|
||||
"@types/ffprobe": "^1.1.8",
|
||||
"@types/fs-extra": "^11.0.4",
|
||||
"@types/node": "^24.5.1",
|
||||
"@types/node-forge": "^1.3.14",
|
||||
"@types/ws": "^8.18.1",
|
||||
"@types/yargs": "^17.0.33",
|
||||
"@typescript-eslint/eslint-plugin": "^8.44.0",
|
||||
"@typescript-eslint/parser": "^8.44.0",
|
||||
"eslint": "^9.35.0",
|
||||
"protoc": "^1.1.3",
|
||||
"removeNPMAbsolutePaths": "^3.0.1",
|
||||
"ts-node": "^10.9.2",
|
||||
"typescript": "^5.9.2",
|
||||
"typescript-eslint": "^8.44.0"
|
||||
},
|
||||
"scripts": {
|
||||
"prestart": "pnpm run tsc test",
|
||||
"start": "pnpm prestart && cd lib && node gui.js",
|
||||
"gui": "cd ./gui/react/ && pnpm start",
|
||||
"docs": "ts-node modules/build-docs.ts",
|
||||
"tsc": "ts-node tsc.ts",
|
||||
"proto:compile": "protoc --plugin=protoc-gen-ts_proto=.\\node_modules\\.bin\\protoc-gen-ts_proto.cmd --ts_proto_opt=\"esModuleInterop=true\" --ts_proto_opt=\"forceLong=long\" --ts_proto_opt=\"env=node\" --ts_proto_out=. modules/*.proto",
|
||||
"prebuild-cli": "pnpm run tsc false false",
|
||||
"build-windows-cli": "pnpm run prebuild-cli && cd lib && node modules/build windows-x64",
|
||||
"build-linux-cli": "pnpm run prebuild-cli && cd lib && node modules/build linuxstatic-x64",
|
||||
"build-arm-cli": "pnpm run prebuild-cli && cd lib && node modules/build linux-arm64",
|
||||
"build-macos-cli": "pnpm run prebuild-cli && cd lib && node modules/build macos-x64",
|
||||
"build-alpine-cli": "pnpm run prebuild-cli && cd lib && node modules/build alpine-x64",
|
||||
"build-android-cli": "pnpm run prebuild-cli && cd lib && node modules/build linuxstatic-armv7",
|
||||
"prebuild-gui": "pnpm run tsc",
|
||||
"build-windows-gui": "pnpm run prebuild-gui && cd lib && node modules/build windows-x64 true",
|
||||
"build-linux-gui": "pnpm run prebuild-gui && cd lib && node modules/build linuxstatic-x64 true",
|
||||
"build-arm-gui": "pnpm run prebuild-gui && cd lib && node modules/build linux-arm64 true",
|
||||
"build-macos-gui": "pnpm run prebuild-gui && cd lib && node modules/build macos-x64 true",
|
||||
"build-alpine-gui": "pnpm run prebuild-gui && cd lib && node modules/build alpine-x64 true",
|
||||
"build-android-gui": "pnpm run prebuild-gui && cd lib && node modules/build linuxstatic-armv7 true",
|
||||
"eslint": "npx eslint .",
|
||||
"eslint-fix": "npx eslint . --fix",
|
||||
"pretest": "pnpm run tsc",
|
||||
"test": "pnpm run pretest && cd lib && node modules/build windows-x64 && node modules/build linuxstatic-x64 && node modules/build macos-x64"
|
||||
}
|
||||
}
|
||||
"name": "multi-downloader-nx",
|
||||
"short_name": "aniDL",
|
||||
"version": "5.5.6",
|
||||
"description": "Downloader for Crunchyroll, Hidive, AnimeOnegai, and AnimationDigitalNetwork with CLI and GUI",
|
||||
"keywords": [
|
||||
"download",
|
||||
"downloader",
|
||||
"hidive",
|
||||
"crunchy",
|
||||
"crunchyroll",
|
||||
"util",
|
||||
"utility",
|
||||
"cli",
|
||||
"gui"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18",
|
||||
"pnpm": ">=7"
|
||||
},
|
||||
"author": "AnimeDL <AnimeDL@users.noreply.github.com>",
|
||||
"contributors": [
|
||||
{
|
||||
"name": "AnimeDL <AnimeDL@users.noreply.github.com>"
|
||||
},
|
||||
{
|
||||
"name": "AniDL <AniDL@users.noreply.github.com>"
|
||||
},
|
||||
{
|
||||
"name": "AnidlSupport <AnidlSupport@users.noreply.github.com>"
|
||||
}
|
||||
],
|
||||
"homepage": "https://github.com/anidl/multi-downloader-nx",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/anidl/multi-downloader-nx.git"
|
||||
},
|
||||
"bugs": {
|
||||
"url": "https://github.com/anidl/multi-downloader-nx/issues"
|
||||
},
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@bufbuild/buf": "^1.57.2",
|
||||
"@bufbuild/protobuf": "^2.8.0",
|
||||
"@bufbuild/protoc-gen-es": "^2.8.0",
|
||||
"@yao-pkg/pkg": "^6.6.0",
|
||||
"binary-parser": "^2.2.1",
|
||||
"binary-parser-encoder": "^1.5.3",
|
||||
"bn.js": "^5.2.2",
|
||||
"cors": "^2.8.5",
|
||||
"elliptic": "^6.6.1",
|
||||
"esbuild": "^0.25.10",
|
||||
"express": "^5.1.0",
|
||||
"fast-xml-parser": "^5.2.5",
|
||||
"ffprobe": "^1.1.2",
|
||||
"fs-extra": "^11.3.2",
|
||||
"iso-639": "^0.2.2",
|
||||
"leven": "^3.1.0",
|
||||
"log4js": "^6.9.1",
|
||||
"long": "^5.3.2",
|
||||
"lookpath": "^1.2.3",
|
||||
"m3u8-parsed": "^2.0.0",
|
||||
"mpd-parser": "^1.3.1",
|
||||
"node-forge": "^1.3.1",
|
||||
"ofetch": "^1.4.1",
|
||||
"open": "^8.4.2",
|
||||
"protobufjs": "^7.5.4",
|
||||
"puppeteer-real-browser": "^1.4.4",
|
||||
"ws": "^8.18.3",
|
||||
"yaml": "^2.8.1",
|
||||
"yargs": "^17.7.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@eslint/js": "^9.35.0",
|
||||
"@types/bn.js": "^5.2.0",
|
||||
"@types/cors": "^2.8.19",
|
||||
"@types/elliptic": "^6.4.18",
|
||||
"@types/express": "^5.0.3",
|
||||
"@types/ffprobe": "^1.1.8",
|
||||
"@types/fs-extra": "^11.0.4",
|
||||
"@types/node": "^24.5.1",
|
||||
"@types/node-forge": "^1.3.14",
|
||||
"@types/ws": "^8.18.1",
|
||||
"@types/yargs": "^17.0.33",
|
||||
"@typescript-eslint/eslint-plugin": "^8.44.0",
|
||||
"@typescript-eslint/parser": "^8.44.0",
|
||||
"eslint": "^9.35.0",
|
||||
"protoc": "^1.1.3",
|
||||
"removeNPMAbsolutePaths": "^3.0.1",
|
||||
"ts-node": "^10.9.2",
|
||||
"typescript": "^5.9.2",
|
||||
"typescript-eslint": "^8.44.0"
|
||||
},
|
||||
"scripts": {
|
||||
"prestart": "pnpm run tsc test",
|
||||
"start": "pnpm prestart && cd lib && node gui.js",
|
||||
"gui": "cd ./gui/react/ && pnpm start",
|
||||
"docs": "ts-node modules/build-docs.ts",
|
||||
"tsc": "ts-node tsc.ts",
|
||||
"proto:compile": "protoc --plugin=protoc-gen-ts_proto=.\\node_modules\\.bin\\protoc-gen-ts_proto.cmd --ts_proto_opt=\"esModuleInterop=true\" --ts_proto_opt=\"forceLong=long\" --ts_proto_opt=\"env=node\" --ts_proto_out=. modules/*.proto",
|
||||
"prebuild-cli": "pnpm run tsc false false",
|
||||
"build-windows-cli": "pnpm run prebuild-cli && cd lib && node modules/build windows-x64",
|
||||
"build-linux-cli": "pnpm run prebuild-cli && cd lib && node modules/build linuxstatic-x64",
|
||||
"build-arm-cli": "pnpm run prebuild-cli && cd lib && node modules/build linux-arm64",
|
||||
"build-macos-cli": "pnpm run prebuild-cli && cd lib && node modules/build macos-x64",
|
||||
"build-alpine-cli": "pnpm run prebuild-cli && cd lib && node modules/build alpine-x64",
|
||||
"build-android-cli": "pnpm run prebuild-cli && cd lib && node modules/build linuxstatic-armv7",
|
||||
"prebuild-gui": "pnpm run tsc",
|
||||
"build-windows-gui": "pnpm run prebuild-gui && cd lib && node modules/build windows-x64 true",
|
||||
"build-linux-gui": "pnpm run prebuild-gui && cd lib && node modules/build linuxstatic-x64 true",
|
||||
"build-arm-gui": "pnpm run prebuild-gui && cd lib && node modules/build linux-arm64 true",
|
||||
"build-macos-gui": "pnpm run prebuild-gui && cd lib && node modules/build macos-x64 true",
|
||||
"build-alpine-gui": "pnpm run prebuild-gui && cd lib && node modules/build alpine-x64 true",
|
||||
"build-android-gui": "pnpm run prebuild-gui && cd lib && node modules/build linuxstatic-armv7 true",
|
||||
"eslint": "npx eslint .",
|
||||
"eslint-fix": "npx eslint . --fix",
|
||||
"pretest": "pnpm run tsc",
|
||||
"test": "pnpm run pretest && cd lib && node modules/build windows-x64 && node modules/build linuxstatic-x64 && node modules/build macos-x64"
|
||||
}
|
||||
}
|
||||
242
tsc.ts
242
tsc.ts
|
|
@ -10,156 +10,156 @@ const isTest = argv.length > 0 && argv[0] === 'test';
|
|||
const isGUI = !(argv.length > 1 && argv[1] === 'false');
|
||||
|
||||
if (!isTest)
|
||||
buildIgnore = [
|
||||
'*/\\.env',
|
||||
'./config/setup.json'
|
||||
];
|
||||
buildIgnore = [
|
||||
'*/\\.env',
|
||||
'./config/setup.json'
|
||||
];
|
||||
|
||||
if (!isGUI)
|
||||
buildIgnore = buildIgnore.concat([
|
||||
'./gui*',
|
||||
'./build*',
|
||||
'gui.ts'
|
||||
]);
|
||||
buildIgnore = buildIgnore.concat([
|
||||
'./gui*',
|
||||
'./build*',
|
||||
'gui.ts'
|
||||
]);
|
||||
|
||||
|
||||
const ignore = [
|
||||
...buildIgnore,
|
||||
'*/\\.git*',
|
||||
'./lib*',
|
||||
'*/@types*',
|
||||
'./out*',
|
||||
'./bin/mkvtoolnix*',
|
||||
'./config/token.yml$',
|
||||
'./config/updates.json$',
|
||||
'./config/*_token.yml$',
|
||||
'./config/*_sess.yml$',
|
||||
'./config/*_profile.yml$',
|
||||
'*/\\.eslint*',
|
||||
'*/*\\.tsx?$',
|
||||
'./fonts*',
|
||||
'./gui/react*',
|
||||
'./dev.js$',
|
||||
'*/node_modules/*',
|
||||
'./widevine/*',
|
||||
'./playready/*',
|
||||
'./videos/*',
|
||||
'./logs/*',
|
||||
...buildIgnore,
|
||||
'*/\\.git*',
|
||||
'./lib*',
|
||||
'*/@types*',
|
||||
'./out*',
|
||||
'./bin/mkvtoolnix*',
|
||||
'./config/token.yml$',
|
||||
'./config/updates.json$',
|
||||
'./config/*_token.yml$',
|
||||
'./config/*_sess.yml$',
|
||||
'./config/*_profile.yml$',
|
||||
'*/\\.eslint*',
|
||||
'*/*\\.tsx?$',
|
||||
'./fonts*',
|
||||
'./gui/react*',
|
||||
'./dev.js$',
|
||||
'*/node_modules/*',
|
||||
'./widevine/*',
|
||||
'./playready/*',
|
||||
'./videos/*',
|
||||
'./logs/*',
|
||||
].map(a => a.replace(/\*/g, '[^]*').replace(/\.\//g, escapeRegExp(__dirname) + '/').replace(/\//g, path.sep === '\\' ? '\\\\' : '/')).map(a => new RegExp(a, 'i'));
|
||||
|
||||
export { ignore };
|
||||
|
||||
(async () => {
|
||||
|
||||
const waitForProcess = async (proc: ChildProcess) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
proc.stdout?.on('data', console.log);
|
||||
proc.stderr?.on('data', console.error);
|
||||
proc.on('close', resolve);
|
||||
proc.on('error', reject);
|
||||
});
|
||||
};
|
||||
const waitForProcess = async (proc: ChildProcess) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
proc.stdout?.on('data', console.log);
|
||||
proc.stderr?.on('data', console.error);
|
||||
proc.on('close', resolve);
|
||||
proc.on('error', reject);
|
||||
});
|
||||
};
|
||||
|
||||
process.stdout.write('Removing lib dir... ');
|
||||
removeSync('lib');
|
||||
process.stdout.write('✓\nRunning tsc... ');
|
||||
const tsc = exec('npx tsc');
|
||||
process.stdout.write('Removing lib dir... ');
|
||||
removeSync('lib');
|
||||
process.stdout.write('✓\nRunning tsc... ');
|
||||
const tsc = exec('npx tsc');
|
||||
|
||||
await waitForProcess(tsc);
|
||||
|
||||
if (!isGUI) {
|
||||
fs.emptyDirSync(path.join('lib', 'gui'));
|
||||
fs.rmdirSync(path.join('lib', 'gui'));
|
||||
}
|
||||
await waitForProcess(tsc);
|
||||
|
||||
if (!isTest && isGUI) {
|
||||
process.stdout.write('✓\nBuilding react... ');
|
||||
if (!isGUI) {
|
||||
fs.emptyDirSync(path.join('lib', 'gui'));
|
||||
fs.rmdirSync(path.join('lib', 'gui'));
|
||||
}
|
||||
|
||||
const installReactDependencies = exec('pnpm install', {
|
||||
cwd: path.join(__dirname, 'gui', 'react'),
|
||||
});
|
||||
if (!isTest && isGUI) {
|
||||
process.stdout.write('✓\nBuilding react... ');
|
||||
|
||||
await waitForProcess(installReactDependencies);
|
||||
|
||||
const react = exec('pnpm run build', {
|
||||
cwd: path.join(__dirname, 'gui', 'react'),
|
||||
env: {
|
||||
...process.env,
|
||||
CI: 'false'
|
||||
}
|
||||
});
|
||||
|
||||
await waitForProcess(react);
|
||||
}
|
||||
const installReactDependencies = exec('pnpm install', {
|
||||
cwd: path.join(__dirname, 'gui', 'react'),
|
||||
});
|
||||
|
||||
process.stdout.write('✓\nCopying files... ');
|
||||
if (!isTest && isGUI) {
|
||||
copyDir(path.join(__dirname, 'gui', 'react', 'build'), path.join(__dirname, 'lib', 'gui', 'server', 'build'));
|
||||
}
|
||||
await waitForProcess(installReactDependencies);
|
||||
|
||||
const files = readDir(__dirname);
|
||||
files.forEach(item => {
|
||||
const itemPath = path.join(__dirname, 'lib', item.path.replace(__dirname, ''));
|
||||
if (item.stats.isDirectory()) {
|
||||
if (!fs.existsSync(itemPath))
|
||||
fs.mkdirSync(itemPath);
|
||||
} else {
|
||||
copyFileSync(item.path, itemPath);
|
||||
}
|
||||
});
|
||||
const react = exec('pnpm run build', {
|
||||
cwd: path.join(__dirname, 'gui', 'react'),
|
||||
env: {
|
||||
...process.env,
|
||||
CI: 'false'
|
||||
}
|
||||
});
|
||||
|
||||
process.stdout.write('✓\nInstalling dependencies... ');
|
||||
if (!isTest) {
|
||||
const dependencies = exec(`pnpm install ${isGUI ? '' : '-P'}`, {
|
||||
cwd: path.join(__dirname, 'lib')
|
||||
});
|
||||
await waitForProcess(dependencies);
|
||||
}
|
||||
await waitForProcess(react);
|
||||
}
|
||||
|
||||
process.stdout.write('✓\n');
|
||||
process.stdout.write('✓\nCopying files... ');
|
||||
if (!isTest && isGUI) {
|
||||
copyDir(path.join(__dirname, 'gui', 'react', 'build'), path.join(__dirname, 'lib', 'gui', 'server', 'build'));
|
||||
}
|
||||
|
||||
const files = readDir(__dirname);
|
||||
files.forEach(item => {
|
||||
const itemPath = path.join(__dirname, 'lib', item.path.replace(__dirname, ''));
|
||||
if (item.stats.isDirectory()) {
|
||||
if (!fs.existsSync(itemPath))
|
||||
fs.mkdirSync(itemPath);
|
||||
} else {
|
||||
copyFileSync(item.path, itemPath);
|
||||
}
|
||||
});
|
||||
|
||||
process.stdout.write('✓\nInstalling dependencies... ');
|
||||
if (!isTest) {
|
||||
const dependencies = exec(`pnpm install ${isGUI ? '' : '-P'}`, {
|
||||
cwd: path.join(__dirname, 'lib')
|
||||
});
|
||||
await waitForProcess(dependencies);
|
||||
}
|
||||
|
||||
process.stdout.write('✓\n');
|
||||
})();
|
||||
|
||||
function readDir (dir: string): {
|
||||
path: string,
|
||||
stats: fs.Stats
|
||||
function readDir(dir: string): {
|
||||
path: string,
|
||||
stats: fs.Stats
|
||||
}[] {
|
||||
const items: {
|
||||
path: string,
|
||||
stats: fs.Stats
|
||||
}[] = [];
|
||||
const content = fs.readdirSync(dir);
|
||||
itemLoop: for (const item of content) {
|
||||
const itemPath = path.join(dir, item);
|
||||
for (const ignoreItem of ignore) {
|
||||
if (ignoreItem.test(itemPath))
|
||||
continue itemLoop;
|
||||
}
|
||||
const stats = fs.statSync(itemPath);
|
||||
items.push({
|
||||
path: itemPath,
|
||||
stats
|
||||
});
|
||||
if (stats.isDirectory()) {
|
||||
items.push(...readDir(itemPath));
|
||||
}
|
||||
}
|
||||
return items;
|
||||
const items: {
|
||||
path: string,
|
||||
stats: fs.Stats
|
||||
}[] = [];
|
||||
const content = fs.readdirSync(dir);
|
||||
itemLoop: for (const item of content) {
|
||||
const itemPath = path.join(dir, item);
|
||||
for (const ignoreItem of ignore) {
|
||||
if (ignoreItem.test(itemPath))
|
||||
continue itemLoop;
|
||||
}
|
||||
const stats = fs.statSync(itemPath);
|
||||
items.push({
|
||||
path: itemPath,
|
||||
stats
|
||||
});
|
||||
if (stats.isDirectory()) {
|
||||
items.push(...readDir(itemPath));
|
||||
}
|
||||
}
|
||||
return items;
|
||||
}
|
||||
|
||||
async function copyDir(src: string, dest: string) {
|
||||
await fs.promises.mkdir(dest, { recursive: true });
|
||||
const entries = await fs.promises.readdir(src, { withFileTypes: true });
|
||||
await fs.promises.mkdir(dest, { recursive: true });
|
||||
const entries = await fs.promises.readdir(src, { withFileTypes: true });
|
||||
|
||||
for (const entry of entries) {
|
||||
const srcPath = path.join(src, entry.name);
|
||||
const destPath = path.join(dest, entry.name);
|
||||
for (const entry of entries) {
|
||||
const srcPath = path.join(src, entry.name);
|
||||
const destPath = path.join(dest, entry.name);
|
||||
|
||||
entry.isDirectory() ?
|
||||
await copyDir(srcPath, destPath) :
|
||||
await fs.promises.copyFile(srcPath, destPath);
|
||||
}
|
||||
entry.isDirectory() ?
|
||||
await copyDir(srcPath, destPath) :
|
||||
await fs.promises.copyFile(srcPath, destPath);
|
||||
}
|
||||
}
|
||||
|
||||
function escapeRegExp(string: string): string {
|
||||
return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); // $& means the whole matched string
|
||||
return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); // $& means the whole matched string
|
||||
}
|
||||
|
|
@ -1,20 +1,20 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"target": "ES2020",
|
||||
"module": "commonjs",
|
||||
"outDir": "./lib",
|
||||
"strict": true,
|
||||
"esModuleInterop": true,
|
||||
"resolveJsonModule": true,
|
||||
"skipLibCheck": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"downlevelIteration": true,
|
||||
"jsx": "react"
|
||||
},
|
||||
"exclude": [
|
||||
"./videos",
|
||||
"./tsc.ts",
|
||||
"lib/**/*",
|
||||
"gui/react/**/*"
|
||||
]
|
||||
"compilerOptions": {
|
||||
"target": "ES2020",
|
||||
"module": "commonjs",
|
||||
"outDir": "./lib",
|
||||
"strict": true,
|
||||
"esModuleInterop": true,
|
||||
"resolveJsonModule": true,
|
||||
"skipLibCheck": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"downlevelIteration": true,
|
||||
"jsx": "react"
|
||||
},
|
||||
"exclude": [
|
||||
"./videos",
|
||||
"./tsc.ts",
|
||||
"lib/**/*",
|
||||
"gui/react/**/*"
|
||||
]
|
||||
}
|
||||
Loading…
Reference in a new issue