mirror of
https://github.com/movie-web/movie-web.git
synced 2025-09-13 18:13:24 +00:00
155 lines
4.3 KiB
TypeScript
155 lines
4.3 KiB
TypeScript
import { proxiedFetch } from "../helpers/fetch";
|
|
import { registerProvider } from "../helpers/register";
|
|
import {
|
|
MWCaptionType,
|
|
MWStreamQuality,
|
|
MWStreamType,
|
|
} from "../helpers/streams";
|
|
import { MWMediaType } from "../metadata/types";
|
|
|
|
const netfilmBase = "https://net-film.vercel.app";
|
|
|
|
const qualityMap: Record<number, MWStreamQuality> = {
|
|
360: MWStreamQuality.Q360P,
|
|
540: MWStreamQuality.Q540P,
|
|
480: MWStreamQuality.Q480P,
|
|
720: MWStreamQuality.Q720P,
|
|
1080: MWStreamQuality.Q1080P,
|
|
};
|
|
|
|
registerProvider({
|
|
id: "netfilm",
|
|
displayName: "NetFilm",
|
|
rank: 15,
|
|
type: [MWMediaType.MOVIE, MWMediaType.SERIES],
|
|
disabled: true, // The creator has asked us (very nicely) to leave him alone. Until (if) we self-host, netfilm should remain disabled
|
|
|
|
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<any>(
|
|
`/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<any>(
|
|
`/api/episode?id=${netfilmId}`,
|
|
{
|
|
baseURL: netfilmBase,
|
|
}
|
|
);
|
|
|
|
const data = watchInfo.data;
|
|
|
|
// get best quality source
|
|
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<string, any>) => ({
|
|
needsProxy: false,
|
|
url: sub.url.replace("https://convert-srt-to-vtt.vercel.app/?url=", ""),
|
|
type: MWCaptionType.SRT,
|
|
langIso: sub.language,
|
|
}));
|
|
|
|
return {
|
|
embeds: [],
|
|
stream: {
|
|
streamUrl: source.url
|
|
.replace("akm-cdn", "aws-cdn")
|
|
.replace("gg-cdn", "aws-cdn"),
|
|
quality: qualityMap[source.quality],
|
|
type: MWStreamType.HLS,
|
|
captions: mappedCaptions,
|
|
},
|
|
};
|
|
}
|
|
|
|
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) || 1,
|
|
};
|
|
});
|
|
|
|
const foundItem = searchItems.find((v: any) => {
|
|
return v.season === desiredSeason;
|
|
});
|
|
|
|
progress(50);
|
|
const seasonDetail = await proxiedFetch<any>(
|
|
`/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<any>(
|
|
`/api/episode?id=${foundItem.id}&category=1&episode=${episodeData.id}`,
|
|
{
|
|
baseURL: netfilmBase,
|
|
}
|
|
);
|
|
|
|
const data = episodeStream.data;
|
|
|
|
// get best quality source
|
|
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<string, any>) => ({
|
|
needsProxy: false,
|
|
url: sub.url.replace("https://convert-srt-to-vtt.vercel.app/?url=", ""),
|
|
type: MWCaptionType.SRT,
|
|
langIso: sub.language,
|
|
}));
|
|
|
|
return {
|
|
embeds: [],
|
|
stream: {
|
|
streamUrl: source.url
|
|
.replace("akm-cdn", "aws-cdn")
|
|
.replace("gg-cdn", "aws-cdn"),
|
|
quality: qualityMap[source.quality],
|
|
type: MWStreamType.HLS,
|
|
captions: mappedCaptions,
|
|
},
|
|
};
|
|
},
|
|
});
|