mirror of
https://github.com/movie-web/movie-web.git
synced 2024-12-25 07:11:50 +01:00
👌 IMPROVE: Update code for review
This commit is contained in:
parent
8dcb954fdf
commit
a442baa5e0
@ -1,12 +1,12 @@
|
|||||||
// this scraper was taken from recloudstream/cloudstream-extensions. much love
|
// this scraper was taken from recloudstream/cloudstream-extensions. much love
|
||||||
import {
|
import {
|
||||||
MWMediaProvider,
|
MWMediaProvider,
|
||||||
MWMediaType,
|
MWMediaType,
|
||||||
MWPortableMedia,
|
MWPortableMedia,
|
||||||
MWMediaStream,
|
MWMediaStream,
|
||||||
MWQuery,
|
MWQuery,
|
||||||
MWMediaSeasons,
|
MWMediaSeasons,
|
||||||
MWProviderMediaResult,
|
MWProviderMediaResult,
|
||||||
} from "providers/types";
|
} from "providers/types";
|
||||||
import { CORS_PROXY_URL, TMDB_API_KEY } from "mw_constants";
|
import { CORS_PROXY_URL, TMDB_API_KEY } from "mw_constants";
|
||||||
import { customAlphabet } from "nanoid";
|
import { customAlphabet } from "nanoid";
|
||||||
@ -22,27 +22,27 @@ const nanoid = customAlphabet("0123456789abcdef", 32);
|
|||||||
const iv = atob("d0VpcGhUbiE=");
|
const iv = atob("d0VpcGhUbiE=");
|
||||||
const key = atob("MTIzZDZjZWRmNjI2ZHk1NDIzM2FhMXc2");
|
const key = atob("MTIzZDZjZWRmNjI2ZHk1NDIzM2FhMXc2");
|
||||||
const apiUrls = [
|
const apiUrls = [
|
||||||
atob("aHR0cHM6Ly9zaG93Ym94LnNoZWd1Lm5ldC9hcGkvYXBpX2NsaWVudC9pbmRleC8="),
|
atob("aHR0cHM6Ly9zaG93Ym94LnNoZWd1Lm5ldC9hcGkvYXBpX2NsaWVudC9pbmRleC8="),
|
||||||
atob("aHR0cHM6Ly9tYnBhcGkuc2hlZ3UubmV0L2FwaS9hcGlfY2xpZW50L2luZGV4Lw=="),
|
atob("aHR0cHM6Ly9tYnBhcGkuc2hlZ3UubmV0L2FwaS9hcGlfY2xpZW50L2luZGV4Lw=="),
|
||||||
];
|
];
|
||||||
const appKey = atob("bW92aWVib3g=");
|
const appKey = atob("bW92aWVib3g=");
|
||||||
const appId = atob("Y29tLnRkby5zaG93Ym94");
|
const appId = atob("Y29tLnRkby5zaG93Ym94");
|
||||||
|
|
||||||
// cryptography stuff
|
// cryptography stuff
|
||||||
const crypto = {
|
const crypto = {
|
||||||
encrypt(str: string) {
|
encrypt(str: string) {
|
||||||
return CryptoJS.TripleDES.encrypt(str, CryptoJS.enc.Utf8.parse(key), {
|
return CryptoJS.TripleDES.encrypt(str, CryptoJS.enc.Utf8.parse(key), {
|
||||||
iv: CryptoJS.enc.Utf8.parse(iv),
|
iv: CryptoJS.enc.Utf8.parse(iv),
|
||||||
}).toString();
|
}).toString();
|
||||||
},
|
},
|
||||||
getVerify(str: string, str2: string, str3: string) {
|
getVerify(str: string, str2: string, str3: string) {
|
||||||
if (str) {
|
if (str) {
|
||||||
return CryptoJS.MD5(
|
return CryptoJS.MD5(
|
||||||
CryptoJS.MD5(str2).toString() + str3 + str,
|
CryptoJS.MD5(str2).toString() + str3 + str,
|
||||||
).toString();
|
).toString();
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
// get expire time
|
// get expire time
|
||||||
@ -50,224 +50,226 @@ const expiry = () => Math.floor(Date.now() / 1000 + 60 * 60 * 12);
|
|||||||
|
|
||||||
// sending requests
|
// sending requests
|
||||||
const get = (data: object, altApi = false) => {
|
const get = (data: object, altApi = false) => {
|
||||||
const defaultData = {
|
const defaultData = {
|
||||||
childmode: "0",
|
childmode: "0",
|
||||||
app_version: "11.5",
|
app_version: "11.5",
|
||||||
appid: appId,
|
appid: appId,
|
||||||
lang: "en",
|
lang: "en",
|
||||||
expired_date: `${expiry()}`,
|
expired_date: `${expiry()}`,
|
||||||
platform: "android",
|
platform: "android",
|
||||||
channel: "Website",
|
channel: "Website",
|
||||||
};
|
};
|
||||||
const encryptedData = crypto.encrypt(
|
const encryptedData = crypto.encrypt(
|
||||||
JSON.stringify({
|
JSON.stringify({
|
||||||
...defaultData,
|
...defaultData,
|
||||||
...data,
|
...data,
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
const appKeyHash = CryptoJS.MD5(appKey).toString();
|
const appKeyHash = CryptoJS.MD5(appKey).toString();
|
||||||
const verify = crypto.getVerify(encryptedData, appKey, key);
|
const verify = crypto.getVerify(encryptedData, appKey, key);
|
||||||
const body = JSON.stringify({
|
const body = JSON.stringify({
|
||||||
app_key: appKeyHash,
|
app_key: appKeyHash,
|
||||||
verify,
|
verify,
|
||||||
encrypt_data: encryptedData,
|
encrypt_data: encryptedData,
|
||||||
});
|
});
|
||||||
const b64Body = btoa(body);
|
const b64Body = btoa(body);
|
||||||
|
|
||||||
const formatted = new URLSearchParams();
|
const formatted = new URLSearchParams();
|
||||||
formatted.append("data", b64Body);
|
formatted.append("data", b64Body);
|
||||||
formatted.append("appid", "27");
|
formatted.append("appid", "27");
|
||||||
formatted.append("platform", "android");
|
formatted.append("platform", "android");
|
||||||
formatted.append("version", "129");
|
formatted.append("version", "129");
|
||||||
formatted.append("medium", "Website");
|
formatted.append("medium", "Website");
|
||||||
|
|
||||||
const requestUrl = altApi ? apiUrls[1] : apiUrls[0];
|
const requestUrl = altApi ? apiUrls[1] : apiUrls[0];
|
||||||
return fetch(`${CORS_PROXY_URL}${requestUrl}`, {
|
return fetch(`${CORS_PROXY_URL}${requestUrl}`, {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
headers: {
|
headers: {
|
||||||
Platform: "android",
|
Platform: "android",
|
||||||
"Content-Type": "application/x-www-form-urlencoded",
|
"Content-Type": "application/x-www-form-urlencoded",
|
||||||
},
|
},
|
||||||
body: `${formatted.toString()}&token${nanoid()}`,
|
body: `${formatted.toString()}&token${nanoid()}`,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
export const superStreamScraper: MWMediaProvider = {
|
export const superStreamScraper: MWMediaProvider = {
|
||||||
id: "superstream",
|
id: "superstream",
|
||||||
enabled: true,
|
enabled: true,
|
||||||
type: [MWMediaType.MOVIE, MWMediaType.SERIES],
|
type: [MWMediaType.MOVIE, MWMediaType.SERIES],
|
||||||
displayName: "SuperStream",
|
displayName: "SuperStream",
|
||||||
|
|
||||||
async getMediaFromPortable(
|
async getMediaFromPortable(
|
||||||
media: MWPortableMedia,
|
media: MWPortableMedia,
|
||||||
): Promise<MWProviderMediaResult> {
|
): Promise<MWProviderMediaResult> {
|
||||||
let apiQuery: any;
|
let apiQuery: any;
|
||||||
if (media.episodeId) {
|
if (media.mediaType == MWMediaType.MOVIE) {
|
||||||
apiQuery = {
|
apiQuery = {
|
||||||
module: "TV_detail_1",
|
module: "TV_detail_1",
|
||||||
display_all: "1",
|
display_all: "1",
|
||||||
tid: media.mediaId,
|
tid: media.mediaId,
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
apiQuery = {
|
apiQuery = {
|
||||||
module: "Movie_detail",
|
module: "Movie_detail",
|
||||||
mid: media.mediaId,
|
mid: media.mediaId,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
const detailRes = (await get(apiQuery, true).then((r) => r.json())).data;
|
const detailRes = (await get(apiQuery, true).then((r) => r.json())).data;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...media,
|
...media,
|
||||||
title: detailRes.title,
|
title: detailRes.title,
|
||||||
year: detailRes.year,
|
year: detailRes.year,
|
||||||
seasonCount: detailRes?.season?.length,
|
seasonCount: detailRes?.season?.length,
|
||||||
} as MWProviderMediaResult;
|
} as MWProviderMediaResult;
|
||||||
},
|
},
|
||||||
|
|
||||||
async searchForMedia(query: MWQuery): Promise<MWProviderMediaResult[]> {
|
async searchForMedia(query: MWQuery): Promise<MWProviderMediaResult[]> {
|
||||||
const apiQuery = {
|
const apiQuery = {
|
||||||
module: "Search3",
|
module: "Search3",
|
||||||
page: "1",
|
page: "1",
|
||||||
type: "all",
|
type: "all",
|
||||||
keyword: query.searchQuery,
|
keyword: query.searchQuery,
|
||||||
pagelimit: "20",
|
pagelimit: "20",
|
||||||
};
|
};
|
||||||
const searchRes = (await get(apiQuery, true).then((r) => r.json())).data;
|
const searchRes = (await get(apiQuery, true).then((r) => r.json())).data;
|
||||||
|
|
||||||
const movieResults: MWProviderMediaResult[] = (searchRes || [])
|
const movieResults: MWProviderMediaResult[] = (searchRes || [])
|
||||||
.filter((item: any) => item.box_type === 1)
|
.filter((item: any) => item.box_type === 1)
|
||||||
.map((item: any) => ({
|
.map((item: any) => ({
|
||||||
title: item.title,
|
title: item.title,
|
||||||
year: item.year,
|
year: item.year,
|
||||||
mediaId: item.id,
|
mediaId: item.id,
|
||||||
}));
|
}));
|
||||||
const seriesResults: MWProviderMediaResult[] = (searchRes || [])
|
const seriesResults: MWProviderMediaResult[] = (searchRes || [])
|
||||||
.filter((item: any) => item.box_type === 2)
|
.filter((item: any) => item.box_type === 2)
|
||||||
.map((item: any) => ({
|
.map((item: any) => ({
|
||||||
title: item.title,
|
title: item.title,
|
||||||
year: item.year,
|
year: item.year,
|
||||||
mediaId: item.id,
|
mediaId: item.id,
|
||||||
seasonId: "1",
|
seasonId: "1",
|
||||||
episodeId: "1",
|
episodeId: "1",
|
||||||
}));
|
}));
|
||||||
|
|
||||||
if (query.type === "movie") {
|
if (query.type === MWMediaType.MOVIE) {
|
||||||
return movieResults;
|
return movieResults;
|
||||||
}
|
} else if (query.type === MWMediaType.SERIES) {
|
||||||
return seriesResults;
|
return seriesResults;
|
||||||
},
|
}
|
||||||
|
return [];
|
||||||
|
},
|
||||||
|
|
||||||
async getStream(media: MWPortableMedia): Promise<MWMediaStream> {
|
async getStream(media: MWPortableMedia): Promise<MWMediaStream> {
|
||||||
if (media.mediaType === MWMediaType.MOVIE) {
|
if (media.mediaType === MWMediaType.MOVIE) {
|
||||||
const apiQuery = {
|
const apiQuery = {
|
||||||
uid: "",
|
uid: "",
|
||||||
module: "Movie_downloadurl_v3",
|
module: "Movie_downloadurl_v3",
|
||||||
mid: media.mediaId,
|
mid: media.mediaId,
|
||||||
oss: "1",
|
oss: "1",
|
||||||
group: "",
|
group: "",
|
||||||
};
|
};
|
||||||
const mediaRes = (await get(apiQuery).then((r) => r.json())).data;
|
const mediaRes = (await get(apiQuery).then((r) => r.json())).data;
|
||||||
const hdQuality =
|
const hdQuality =
|
||||||
mediaRes.list.find((quality: any) => quality.quality === "1080p") ??
|
mediaRes.list.find((quality: any) => quality.quality === "1080p") ??
|
||||||
mediaRes.list.find((quality: any) => quality.quality === "720p");
|
mediaRes.list.find((quality: any) => quality.quality === "720p");
|
||||||
|
|
||||||
const subtitleApiQuery = {
|
const subtitleApiQuery = {
|
||||||
fid: hdQuality.fid,
|
fid: hdQuality.fid,
|
||||||
uid: "",
|
uid: "",
|
||||||
module: "Movie_srt_list_v2",
|
module: "Movie_srt_list_v2",
|
||||||
mid: media.mediaId,
|
mid: media.mediaId,
|
||||||
};
|
};
|
||||||
const subtitleRes = (await get(subtitleApiQuery).then((r) => r.json()))
|
const subtitleRes = (await get(subtitleApiQuery).then((r) => r.json()))
|
||||||
.data;
|
.data;
|
||||||
const mappedCaptions = await Promise.all(
|
const mappedCaptions = await Promise.all(
|
||||||
subtitleRes.list.map(async (subtitle: any) => {
|
subtitleRes.list.map(async (subtitle: any) => {
|
||||||
const captionBlob = await fetch(
|
const captionBlob = await fetch(
|
||||||
`${CORS_PROXY_URL}${subtitle.subtitles[0].file_path}`,
|
`${CORS_PROXY_URL}${subtitle.subtitles[0].file_path}`,
|
||||||
).then((captionRes) => captionRes.blob()); // cross-origin bypass
|
).then((captionRes) => captionRes.blob()); // cross-origin bypass
|
||||||
const captionUrl = await toWebVTT(captionBlob); // convert to vtt so it's playable
|
const captionUrl = await toWebVTT(captionBlob); // convert to vtt so it's playable
|
||||||
return {
|
return {
|
||||||
id: subtitle.language,
|
id: subtitle.language,
|
||||||
url: captionUrl,
|
url: captionUrl,
|
||||||
label: subtitle.language,
|
label: subtitle.language,
|
||||||
};
|
};
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
return { url: hdQuality.path, type: "mp4", captions: mappedCaptions };
|
return { url: hdQuality.path, type: "mp4", captions: mappedCaptions };
|
||||||
}
|
}
|
||||||
|
|
||||||
const apiQuery = {
|
const apiQuery = {
|
||||||
uid: "",
|
uid: "",
|
||||||
module: "TV_downloadurl_v3",
|
module: "TV_downloadurl_v3",
|
||||||
episode: media.episodeId,
|
episode: media.episodeId,
|
||||||
tid: media.mediaId,
|
tid: media.mediaId,
|
||||||
season: media.seasonId,
|
season: media.seasonId,
|
||||||
oss: "1",
|
oss: "1",
|
||||||
group: "",
|
group: "",
|
||||||
};
|
};
|
||||||
const mediaRes = (await get(apiQuery).then((r) => r.json())).data;
|
const mediaRes = (await get(apiQuery).then((r) => r.json())).data;
|
||||||
const hdQuality =
|
const hdQuality =
|
||||||
mediaRes.list.find((quality: any) => quality.quality === "1080p") ??
|
mediaRes.list.find((quality: any) => quality.quality === "1080p") ??
|
||||||
mediaRes.list.find((quality: any) => quality.quality === "720p");
|
mediaRes.list.find((quality: any) => quality.quality === "720p");
|
||||||
|
|
||||||
const subtitleApiQuery = {
|
const subtitleApiQuery = {
|
||||||
fid: hdQuality.fid,
|
fid: hdQuality.fid,
|
||||||
uid: "",
|
uid: "",
|
||||||
module: "TV_srt_list_v2",
|
module: "TV_srt_list_v2",
|
||||||
episode: media.episodeId,
|
episode: media.episodeId,
|
||||||
tid: media.mediaId,
|
tid: media.mediaId,
|
||||||
season: media.seasonId,
|
season: media.seasonId,
|
||||||
};
|
};
|
||||||
const subtitleRes = (await get(subtitleApiQuery).then((r) => r.json()))
|
const subtitleRes = (await get(subtitleApiQuery).then((r) => r.json()))
|
||||||
.data;
|
.data;
|
||||||
const mappedCaptions = await Promise.all(
|
const mappedCaptions = await Promise.all(
|
||||||
subtitleRes.list.map(async (subtitle: any) => {
|
subtitleRes.list.map(async (subtitle: any) => {
|
||||||
const captionBlob = await fetch(
|
const captionBlob = await fetch(
|
||||||
`${CORS_PROXY_URL}${subtitle.subtitles[0].file_path}`,
|
`${CORS_PROXY_URL}${subtitle.subtitles[0].file_path}`,
|
||||||
).then((captionRes) => captionRes.blob()); // cross-origin bypass
|
).then((captionRes) => captionRes.blob()); // cross-origin bypass
|
||||||
const captionUrl = await toWebVTT(captionBlob); // convert to vtt so it's playable
|
const captionUrl = await toWebVTT(captionBlob); // convert to vtt so it's playable
|
||||||
return {
|
return {
|
||||||
id: subtitle.language,
|
id: subtitle.language,
|
||||||
url: captionUrl,
|
url: captionUrl,
|
||||||
label: subtitle.language,
|
label: subtitle.language,
|
||||||
};
|
};
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
return { url: hdQuality.path, type: "mp4", captions: mappedCaptions };
|
return { url: hdQuality.path, type: "mp4", captions: mappedCaptions };
|
||||||
},
|
},
|
||||||
async getSeasonDataFromMedia(
|
async getSeasonDataFromMedia(
|
||||||
media: MWPortableMedia,
|
media: MWPortableMedia,
|
||||||
): Promise<MWMediaSeasons> {
|
): Promise<MWMediaSeasons> {
|
||||||
const apiQuery = {
|
const apiQuery = {
|
||||||
module: "TV_detail_1",
|
module: "TV_detail_1",
|
||||||
display_all: "1",
|
display_all: "1",
|
||||||
tid: media.mediaId,
|
tid: media.mediaId,
|
||||||
};
|
};
|
||||||
const detailRes = (await get(apiQuery, true).then((r) => r.json())).data;
|
const detailRes = (await get(apiQuery, true).then((r) => r.json())).data;
|
||||||
const firstSearchResult = (
|
const firstSearchResult = (
|
||||||
await fetch(
|
await fetch(
|
||||||
`https://api.themoviedb.org/3/search/tv?api_key=${TMDB_API_KEY}&language=en-US&page=1&query=${detailRes.title}&include_adult=false&first_air_date_year=${detailRes.year}`,
|
`https://api.themoviedb.org/3/search/tv?api_key=${TMDB_API_KEY}&language=en-US&page=1&query=${detailRes.title}&include_adult=false&first_air_date_year=${detailRes.year}`,
|
||||||
).then((r) => r.json())
|
).then((r) => r.json())
|
||||||
).results[0];
|
).results[0];
|
||||||
const showDetails = await fetch(
|
const showDetails = await fetch(
|
||||||
`https://api.themoviedb.org/3/tv/${firstSearchResult.id}?api_key=${TMDB_API_KEY}`,
|
`https://api.themoviedb.org/3/tv/${firstSearchResult.id}?api_key=${TMDB_API_KEY}`,
|
||||||
).then((r) => r.json());
|
).then((r) => r.json());
|
||||||
|
|
||||||
return {
|
return {
|
||||||
seasons: showDetails.seasons.map((season: any) => ({
|
seasons: showDetails.seasons.map((season: any) => ({
|
||||||
sort: season.season_number,
|
sort: season.season_number,
|
||||||
id: season.season_number.toString(),
|
id: season.season_number.toString(),
|
||||||
type: season.season_number === 0 ? "special" : "season",
|
type: season.season_number === 0 ? "special" : "season",
|
||||||
episodes: Array.from({ length: season.episode_count }).map(
|
episodes: Array.from({ length: season.episode_count }).map(
|
||||||
(_, epNum) => ({
|
(_, epNum) => ({
|
||||||
title: `Episode ${epNum + 1}`,
|
title: `Episode ${epNum + 1}`,
|
||||||
sort: epNum + 1,
|
sort: epNum + 1,
|
||||||
id: (epNum + 1).toString(),
|
id: (epNum + 1).toString(),
|
||||||
episodeNumber: epNum + 1,
|
episodeNumber: epNum + 1,
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
})),
|
})),
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
Loading…
Reference in New Issue
Block a user