diff --git a/src/backend/index.ts b/src/backend/index.ts index 5e4f7989..bb752003 100644 --- a/src/backend/index.ts +++ b/src/backend/index.ts @@ -4,6 +4,7 @@ import { initializeScraperStore } from "./helpers/register"; import "./providers/gdriveplayer"; import "./providers/flixhq"; import "./providers/superstream"; +import "./providers/netfilm"; // embeds // -- nothing here yet diff --git a/src/backend/providers/netfilm.ts b/src/backend/providers/netfilm.ts new file mode 100644 index 00000000..6d773974 --- /dev/null +++ b/src/backend/providers/netfilm.ts @@ -0,0 +1,120 @@ +import { proxiedFetch } from "../helpers/fetch"; +import { registerProvider } from "../helpers/register"; +import { MWStreamType } from "../helpers/streams"; +import { MWMediaType } from "../metadata/types"; + +const netfilmBase = "https://net-film.vercel.app"; + +registerProvider({ + id: "netfilm", + displayName: "NetFilm", + rank: 999, + type: [MWMediaType.MOVIE, MWMediaType.SERIES], + + async scrape({ media, episode, progress }) { + // // search for relevant item + const searchResponse = await proxiedFetch( + `/api/search?keyword=${encodeURIComponent(media.meta.title)}`, + { + baseURL: netfilmBase, + } + ); + + const searchResults = searchResponse.data.results; + progress(25); + + if (media.meta.type === MWMediaType.MOVIE) { + const foundItem = searchResults.find((v: any) => { + return v.name === media.meta.title && v.releaseTime === media.meta.year; + }); + if (!foundItem) throw new Error("No watchable item found"); + const netfilmId = foundItem.id; + + // get stream info from media + progress(75); + const watchInfo = await proxiedFetch( + `/api/episode?id=${netfilmId}`, + { + baseURL: netfilmBase, + } + ); + + const { qualities } = watchInfo.data; + + // get best quality source + const source = qualities.reduce((p: any, c: any) => + c.quality > p.quality ? c : p + ); + + return { + embeds: [], + stream: { + streamUrl: source.url, + quality: source.quality, + type: MWStreamType.HLS, + // captions: [], + }, + }; + } + + if (media.meta.type !== MWMediaType.SERIES) + throw new Error("Unsupported type"); + + const desiredSeason = media.meta.seasonData.number; + + const searchItems = searchResults + .filter((v: any) => { + return v.name.includes(media.meta.title); + }) + .map((v: any) => { + return { + ...v, + season: parseInt(v.name.split(" ").at(-1), 10), + }; + }); + + const foundItem = searchItems.find((v: any) => { + return v.season === desiredSeason; + }); + + progress(50); + const seasonDetail = await proxiedFetch( + `/api/detail?id=${foundItem.id}&category=${foundItem.categoryTag[0].id}`, + { + baseURL: netfilmBase, + } + ); + + const episodeNo = media.meta.seasonData.episodes.find( + (v: any) => v.id === episode + )?.number; + const episodeData = seasonDetail.data.episodeVo.find( + (v: any) => v.seriesNo === episodeNo + ); + + progress(75); + const episodeStream = await proxiedFetch( + `/api/episode?id=${foundItem.id}&category=1&episode=${episodeData.id}`, + { + baseURL: netfilmBase, + } + ); + + const { qualities } = episodeStream.data; + + // get best quality source + const source = qualities.reduce((p: any, c: any) => + c.quality > p.quality ? c : p + ); + + return { + embeds: [], + stream: { + streamUrl: source.url, + quality: source.quality, + type: MWStreamType.HLS, + // captions: [], + }, + }; + }, +}); diff --git a/src/components/video/hooks/controlVideo.ts b/src/components/video/hooks/controlVideo.ts index 7a34543c..a8552b0b 100644 --- a/src/components/video/hooks/controlVideo.ts +++ b/src/components/video/hooks/controlVideo.ts @@ -178,7 +178,7 @@ export function populateControls( return; } - const hls = new Hls(); + const hls = new Hls({ enableWorker: false }); hls.on(Hls.Events.ERROR, (event, data) => { if (data.fatal) {