diff --git a/src/backend/helpers/streams.ts b/src/backend/helpers/streams.ts index 92943d94..d5985cfa 100644 --- a/src/backend/helpers/streams.ts +++ b/src/backend/helpers/streams.ts @@ -10,6 +10,7 @@ export enum MWCaptionType { export enum MWStreamQuality { Q360P = "360p", + Q540P = "540p", Q480P = "480p", Q720P = "720p", Q1080P = "1080p", diff --git a/src/backend/providers/flixhq.ts b/src/backend/providers/flixhq.ts index 95a09b7b..2232ca9d 100644 --- a/src/backend/providers/flixhq.ts +++ b/src/backend/providers/flixhq.ts @@ -1,7 +1,11 @@ import { compareTitle } from "@/utils/titleMatch"; import { proxiedFetch } from "../helpers/fetch"; import { registerProvider } from "../helpers/register"; -import { MWStreamQuality, MWStreamType } from "../helpers/streams"; +import { + MWCaptionType, + MWStreamQuality, + MWStreamType, +} from "../helpers/streams"; import { MWMediaType } from "../metadata/types"; // const flixHqBase = "https://api.consumet.org/movies/flixhq"; @@ -9,13 +13,52 @@ import { MWMediaType } from "../metadata/types"; // SEE ISSUE: https://github.com/consumet/api.consumet.org/issues/326 const flixHqBase = "https://c.delusionz.xyz/movies/flixhq"; +interface FLIXMediaBase { + id: number; + title: string; + url: string; + image: string; +} + +interface FLIXTVSerie extends FLIXMediaBase { + type: "TV Series"; + seasons: number | null; +} + +interface FLIXMovie extends FLIXMediaBase { + type: "Movie"; + releaseDate: string; +} + +function castSubtitles({ url, lang }: { url: string; lang: string }) { + return { + url, + langIso: lang, + type: + url.substring(url.length - 3) === "vtt" + ? MWCaptionType.VTT + : MWCaptionType.SRT, + }; +} + +const qualityMap: Record = { + "360": MWStreamQuality.Q360P, + "540": MWStreamQuality.Q540P, + "480": MWStreamQuality.Q480P, + "720": MWStreamQuality.Q720P, + "1080": MWStreamQuality.Q1080P, +}; + registerProvider({ id: "flixhq", displayName: "FlixHQ", rank: 100, - type: [MWMediaType.MOVIE], + type: [MWMediaType.MOVIE, MWMediaType.SERIES], async scrape({ media, progress }) { + if (!this.type.includes(media.meta.type)) { + throw new Error("Unsupported type"); + } // search for relevant item const searchResults = await proxiedFetch( `/${encodeURIComponent(media.meta.title)}`, @@ -23,11 +66,22 @@ registerProvider({ baseURL: flixHqBase, } ); - const foundItem = searchResults.results.find((v: any) => { - return ( - compareTitle(v.title, media.meta.title) && - v.releaseDate === media.meta.year - ); + const foundItem = searchResults.results.find((v: FLIXMediaBase) => { + if (media.meta.type === MWMediaType.MOVIE) { + const movie = v as FLIXMovie; + return ( + compareTitle(movie.title, media.meta.title) && + movie.releaseDate === media.meta.year + ); + } + const serie = v as FLIXTVSerie; + if (serie.seasons && media.meta.seasons) { + return ( + compareTitle(serie.title, media.meta.title) && + serie.seasons === media.meta.seasons.length + ); + } + return compareTitle(serie.title, media.meta.title); }); if (!foundItem) throw new Error("No watchable item found"); const flixId = foundItem.id; @@ -40,7 +94,7 @@ registerProvider({ id: flixId, }, }); - + if (!mediaInfo.episodes) throw new Error("No watchable item found"); // get stream info from media progress(75); const watchInfo = await proxiedFetch("/watch", { @@ -51,18 +105,22 @@ registerProvider({ }, }); - // get best quality source - const source = watchInfo.sources.reduce((p: any, c: any) => - c.quality > p.quality ? c : p - ); + if (!watchInfo.sources) throw new Error("No watchable item found"); + // get best quality source + // comes sorted by quality in descending order + const source = watchInfo.sources[0]; return { embeds: [], stream: { streamUrl: source.url, - quality: MWStreamQuality.QUNKNOWN, + quality: qualityMap[source.quality], type: source.isM3U8 ? MWStreamType.HLS : MWStreamType.MP4, - captions: [], + captions: watchInfo.subtitles + .filter( + (x: { url: string; lang: string }) => !x.lang.includes("(maybe)") + ) + .map(castSubtitles), }, }; }, diff --git a/src/backend/providers/netfilm.ts b/src/backend/providers/netfilm.ts index 9b4faafa..23a8cf90 100644 --- a/src/backend/providers/netfilm.ts +++ b/src/backend/providers/netfilm.ts @@ -9,13 +9,13 @@ import { MWMediaType } from "../metadata/types"; const netfilmBase = "https://net-film.vercel.app"; -const qualityMap = { - "360": MWStreamQuality.Q360P, - "480": MWStreamQuality.Q480P, - "720": MWStreamQuality.Q720P, - "1080": MWStreamQuality.Q1080P, +const qualityMap: Record = { + 360: MWStreamQuality.Q360P, + 540: MWStreamQuality.Q540P, + 480: MWStreamQuality.Q480P, + 720: MWStreamQuality.Q720P, + 1080: MWStreamQuality.Q1080P, }; -type QualityInMap = keyof typeof qualityMap; registerProvider({ id: "netfilm", @@ -24,6 +24,9 @@ registerProvider({ type: [MWMediaType.MOVIE, MWMediaType.SERIES], async scrape({ media, episode, progress }) { + if (!this.type.includes(media.meta.type)) { + throw new Error("Unsupported type"); + } // search for relevant item const searchResponse = await proxiedFetch( `/api/search?keyword=${encodeURIComponent(media.meta.title)}`, @@ -54,8 +57,8 @@ registerProvider({ const data = watchInfo.data; // get best quality source - const source = data.qualities.reduce((p: any, c: any) => - c.quality > p.quality ? c : p + const source: { url: string; quality: number } = data.qualities.reduce( + (p: any, c: any) => (c.quality > p.quality ? c : p) ); const mappedCaptions = data.subtitles.map((sub: Record) => ({ @@ -71,7 +74,7 @@ registerProvider({ streamUrl: source.url .replace("akm-cdn", "aws-cdn") .replace("gg-cdn", "aws-cdn"), - quality: qualityMap[source.quality as QualityInMap], + quality: qualityMap[source.quality], type: MWStreamType.HLS, captions: mappedCaptions, }, @@ -124,8 +127,8 @@ registerProvider({ const data = episodeStream.data; // get best quality source - const source = data.qualities.reduce((p: any, c: any) => - c.quality > p.quality ? c : p + const source: { url: string; quality: number } = data.qualities.reduce( + (p: any, c: any) => (c.quality > p.quality ? c : p) ); const mappedCaptions = data.subtitles.map((sub: Record) => ({ @@ -141,7 +144,7 @@ registerProvider({ streamUrl: source.url .replace("akm-cdn", "aws-cdn") .replace("gg-cdn", "aws-cdn"), - quality: qualityMap[source.quality as QualityInMap], + quality: qualityMap[source.quality], type: MWStreamType.HLS, captions: mappedCaptions, },