diff --git a/src/backend/helpers/streams.ts b/src/backend/helpers/streams.ts index 628bad4a..3c80f7a6 100644 --- a/src/backend/helpers/streams.ts +++ b/src/backend/helpers/streams.ts @@ -5,6 +5,7 @@ export enum MWStreamType { export enum MWStreamQuality { Q360P = "360p", + Q480P = "480p", Q720P = "720p", Q1080P = "1080p", QUNKNOWN = "unknown", diff --git a/src/backend/index.ts b/src/backend/index.ts index 46764b1f..7859f2cd 100644 --- a/src/backend/index.ts +++ b/src/backend/index.ts @@ -3,6 +3,7 @@ import { initializeScraperStore } from "./helpers/register"; // providers import "./providers/gdriveplayer"; import "./providers/flixhq"; +import "./providers/superstream/superstream"; // embeds // -- nothing here yet diff --git a/src/backend/providers/superstream/superstream.ts b/src/backend/providers/superstream/superstream.ts index 86987ecd..cb0c3928 100644 --- a/src/backend/providers/superstream/superstream.ts +++ b/src/backend/providers/superstream/superstream.ts @@ -5,9 +5,20 @@ import { conf } from "@/setup/config"; import { customAlphabet } from "nanoid"; // import toWebVTT from "srt-webvtt"; import CryptoJS from "crypto-js"; +import { proxiedFetch } from "@/backend/helpers/fetch"; +import { MWStreamQuality, MWStreamType } from "@/backend/helpers/streams"; +import { MetadataSchema } from "hls.js"; const nanoid = customAlphabet("0123456789abcdef", 32); +const qualityMap = { + "360p": MWStreamQuality.Q360P, + "480p": MWStreamQuality.Q480P, + "720p": MWStreamQuality.Q720P, + "1080p": MWStreamQuality.Q1080P, +}; +type QualityInMap = keyof typeof qualityMap; + // CONSTANTS, read below (taken from og) // We do not want content scanners to notice this scraping going on so we've hidden all constants // The source has its origins in China so I added some extra security with banned words @@ -76,8 +87,9 @@ const get = (data: object, altApi = false) => { formatted.append("medium", "Website"); const requestUrl = altApi ? apiUrls[1] : apiUrls[0]; - return fetch(`${conf().CORS_PROXY_URL}${requestUrl}`, { + return proxiedFetch(requestUrl, { method: "POST", + parseResponse: JSON.parse, headers: { Platform: "android", "Content-Type": "application/x-www-form-urlencoded", @@ -88,26 +100,44 @@ const get = (data: object, altApi = false) => { registerProvider({ id: "superstream", - rank: 50, + displayName: "Superstream", + rank: 200, type: [MWMediaType.MOVIE, MWMediaType.SERIES], - disabled: true, - async scrape({ - media: { - meta: { type }, - tmdbId, - }, - }) { - if (type === MWMediaType.MOVIE) { + async scrape({ media, episode, progress }) { + // Find Superstream ID for show + const searchQuery = { + module: "Search3", + page: "1", + type: "all", + keyword: media.meta.title, + pagelimit: "20", + }; + const searchRes = (await get(searchQuery, true)).data; + progress(33); + + // TODO: add fuzzy search and normalise strings before matching + const superstreamEntry = searchRes.find( + (res: any) => + res.title === media.meta.title && res.year === Number(media.meta.year) + ); + + if (!superstreamEntry) throw new Error("No entry found on SuperStream"); + const superstreamId = superstreamEntry.id; + + // Movie logic + if (media.meta.type === MWMediaType.MOVIE) { const apiQuery = { uid: "", module: "Movie_downloadurl_v3", - mid: tmdbId, + mid: superstreamId, oss: "1", group: "", }; - const mediaRes = (await get(apiQuery).then((r) => r.json())).data; + const mediaRes = (await get(apiQuery)).data; + progress(50); + const hdQuality = mediaRes.list.find( (quality: any) => quality.quality === "1080p" && quality.path @@ -124,6 +154,8 @@ registerProvider({ if (!hdQuality) throw new Error("No quality could be found."); + console.log(hdQuality); + // const subtitleApiQuery = { // fid: hdQuality.fid, // uid: "", @@ -147,34 +179,52 @@ registerProvider({ // }) // ); - return { embeds: [], stream: hdQuality.path }; + return { + embeds: [], + stream: { + streamUrl: hdQuality.path, + quality: qualityMap[hdQuality.quality as QualityInMap], + type: MWStreamType.MP4, + }, + }; } - // const apiQuery = { - // uid: "", - // module: "TV_downloadurl_v3", - // episode: media.episodeId, - // tid: media.mediaId, - // season: media.seasonId, - // oss: "1", - // group: "", - // }; - // const mediaRes = (await get(apiQuery).then((r) => r.json())).data; - // const hdQuality = - // mediaRes.list.find( - // (quality: any) => quality.quality === "1080p" && quality.path - // ) ?? - // mediaRes.list.find( - // (quality: any) => quality.quality === "720p" && quality.path - // ) ?? - // mediaRes.list.find( - // (quality: any) => quality.quality === "480p" && quality.path - // ) ?? - // mediaRes.list.find( - // (quality: any) => quality.quality === "360p" && quality.path - // ); + if (media.meta.type !== MWMediaType.SERIES) + throw new Error("Unsupported type"); - // if (!hdQuality) throw new Error("No quality could be found."); + // Fetch requested episode + const apiQuery = { + uid: "", + module: "TV_downloadurl_v3", + tid: superstreamId, + season: media.meta.seasonData.number.toString(), + episode: ( + media.meta.seasonData.episodes.find( + (episodeInfo) => episodeInfo.id === episode + )?.number ?? 1 + ).toString(), + oss: "1", + group: "", + }; + + const mediaRes = (await get(apiQuery)).data; + progress(66); + + const hdQuality = + mediaRes.list.find( + (quality: any) => quality.quality === "1080p" && quality.path + ) ?? + mediaRes.list.find( + (quality: any) => quality.quality === "720p" && quality.path + ) ?? + mediaRes.list.find( + (quality: any) => quality.quality === "480p" && quality.path + ) ?? + mediaRes.list.find( + (quality: any) => quality.quality === "360p" && quality.path + ); + + if (!hdQuality) throw new Error("No quality could be found."); // const subtitleApiQuery = { // fid: hdQuality.fid, @@ -200,6 +250,15 @@ registerProvider({ // }) // ); - return { embeds: [] }; + return { + embeds: [], + stream: { + quality: qualityMap[ + hdQuality.quality as QualityInMap + ] as MWStreamQuality, + streamUrl: hdQuality.path, + type: MWStreamType.MP4, + }, + }; }, }); diff --git a/yarn.lock b/yarn.lock index 8378343c..2b105f10 100644 --- a/yarn.lock +++ b/yarn.lock @@ -20,9 +20,9 @@ "@colors/colors@1.5.0": "version" "1.5.0" -"@esbuild/linux-x64@0.16.5": - "integrity" "sha512-vsOwzKN+4NenUTyuoWLmg5dAuO8JKuLD9MXSeENA385XucuOZbblmOMwwgPlHsgVRtSjz38riqPJU2ALI/CWYQ==" - "resolved" "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.16.5.tgz" +"@esbuild/darwin-arm64@0.16.5": + "integrity" "sha512-4HlbUMy50cRaHGVriBjShs46WRPshtnVOqkxEGhEuDuJhgZ3regpWzaQxXOcDXFvVwue8RiqDAAcOi/QlVLE6Q==" + "resolved" "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.16.5.tgz" "version" "0.16.5" "@eslint/eslintrc@^1.3.3": @@ -98,8 +98,8 @@ "@nodelib/fs.scandir" "2.1.5" "fastq" "^1.6.0" -"@npmcli/arborist@^6.1.5": - "version" "6.1.5" +"@npmcli/arborist@^6.1.6": + "version" "6.1.6" dependencies: "@isaacs/string-locale-compare" "^1.1.0" "@npmcli/fs" "^3.1.0" @@ -135,8 +135,8 @@ "treeverse" "^3.0.0" "walk-up-path" "^1.0.0" -"@npmcli/config@^6.1.0": - "version" "6.1.0" +"@npmcli/config@^6.1.1": + "version" "6.1.1" dependencies: "@npmcli/map-workspaces" "^3.0.0" "ini" "^3.0.0" @@ -233,14 +233,9 @@ "read-package-json-fast" "^3.0.0" "which" "^3.0.0" -"@swc/core-linux-x64-gnu@1.3.22": - "integrity" "sha512-FLkbiqsdXsVIFZi6iedx4rSBGX8x0vo/5aDlklSxJAAYOcQpO0QADKP5Yr65iMT1d6ABCt2d+/StpGLF7GWOcA==" - "resolved" "https://registry.npmjs.org/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.3.22.tgz" - "version" "1.3.22" - -"@swc/core-linux-x64-musl@1.3.22": - "integrity" "sha512-giBuw+Z0Bq6fpZ0Y5TcfpcQwf9p/cE1fOQyO/K1XSTn/haQOqFi7421Jq/dFThSARZiXw1u9Om9VFbwxr8VI+A==" - "resolved" "https://registry.npmjs.org/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.3.22.tgz" +"@swc/core-darwin-arm64@1.3.22": + "integrity" "sha512-MMhtPsuXp8gpUgr9bs+RZQ2IyFGiUNDG93usCDAFgAF+6VVp+YaAVjET/3/Bx5Lk2WAt0RxT62C9KTEw1YMo3w==" + "resolved" "https://registry.npmjs.org/@swc/core-darwin-arm64/-/core-darwin-arm64-1.3.22.tgz" "version" "1.3.22" "@swc/core@^1.3.21": @@ -891,9 +886,9 @@ "version" "3.26.1" "core-js@^3.6.5": - "integrity" "sha512-GutwJLBChfGCpwwhbYoqfv03LAfmiz7e7D/BNxzeMxwQf10GRSzqiOjx7AmtEk+heiD/JWmBuyBPgFtx0Sg1ww==" - "resolved" "https://registry.npmjs.org/core-js/-/core-js-3.27.1.tgz" - "version" "3.27.1" + "integrity" "sha512-9ashVQskuh5AZEZ1JdQWp1GqSoC1e1G87MzRqg2gIfVAQ7Qn9K+uFj8EcniUFA4P2NLZfV+TOlX1SzoKfo+s7w==" + "resolved" "https://registry.npmjs.org/core-js/-/core-js-3.27.2.tgz" + "version" "3.27.2" "cross-fetch@3.1.5": "integrity" "sha512-lvb1SBsI0Z7GDwmuid+mU3kWVBwTVUbe7S0H52yaaAdQOXq2YktTCZdlAcNKFzE6QtRz0snpw9bNiPeOIkkQvw==" @@ -1055,13 +1050,6 @@ "resolved" "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz" "version" "9.2.2" -"encoding@^0.1.0": - "integrity" "sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==" - "resolved" "https://registry.npmjs.org/encoding/-/encoding-0.1.13.tgz" - "version" "0.1.13" - dependencies: - "iconv-lite" "^0.6.2" - "encoding@^0.1.13": "version" "0.1.13" dependencies: @@ -1501,6 +1489,11 @@ "resolved" "https://registry.npmjs.org/fscreen/-/fscreen-1.2.0.tgz" "version" "1.2.0" +"fsevents@~2.3.2": + "integrity" "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==" + "resolved" "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz" + "version" "2.3.2" + "function-bind@^1.1.1": "integrity" "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" "resolved" "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz" @@ -2022,16 +2015,16 @@ "version" "1.1.4" "json5@^1.0.1": - "integrity" "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==" - "resolved" "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz" - "version" "1.0.2" + "integrity" "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==" + "resolved" "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz" + "version" "1.0.1" dependencies: "minimist" "^1.2.0" "json5@^2.2.0": - "integrity" "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==" - "resolved" "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz" - "version" "2.2.3" + "integrity" "sha512-1hqLFMSrGHRHxav9q9gNjJ5EXznIxGVO09xQRrwplcS8qs28pZ8s8hupZAmqDwZUmVZ2Qb2jnyPOWcDH8m8dlA==" + "resolved" "https://registry.npmjs.org/json5/-/json5-2.2.1.tgz" + "version" "2.2.1" "jsonparse@^1.3.1": "version" "1.3.1" @@ -2076,10 +2069,10 @@ "npm-package-arg" "^10.1.0" "npm-registry-fetch" "^14.0.3" -"libnpmdiff@^5.0.6": - "version" "5.0.6" +"libnpmdiff@^5.0.7": + "version" "5.0.7" dependencies: - "@npmcli/arborist" "^6.1.5" + "@npmcli/arborist" "^6.1.6" "@npmcli/disparity-colors" "^3.0.0" "@npmcli/installed-package-contents" "^2.0.0" "binary-extensions" "^2.2.0" @@ -2089,10 +2082,10 @@ "pacote" "^15.0.7" "tar" "^6.1.13" -"libnpmexec@^5.0.6": - "version" "5.0.6" +"libnpmexec@^5.0.7": + "version" "5.0.7" dependencies: - "@npmcli/arborist" "^6.1.5" + "@npmcli/arborist" "^6.1.6" "@npmcli/run-script" "^6.0.0" "chalk" "^4.1.0" "ci-info" "^3.7.0" @@ -2105,10 +2098,10 @@ "semver" "^7.3.7" "walk-up-path" "^1.0.0" -"libnpmfund@^4.0.6": - "version" "4.0.6" +"libnpmfund@^4.0.7": + "version" "4.0.7" dependencies: - "@npmcli/arborist" "^6.1.5" + "@npmcli/arborist" "^6.1.6" "libnpmhook@^9.0.1": "version" "9.0.1" @@ -2122,10 +2115,10 @@ "aproba" "^2.0.0" "npm-registry-fetch" "^14.0.3" -"libnpmpack@^5.0.6": - "version" "5.0.6" +"libnpmpack@^5.0.7": + "version" "5.0.7" dependencies: - "@npmcli/arborist" "^6.1.5" + "@npmcli/arborist" "^6.1.6" "@npmcli/run-script" "^6.0.0" "npm-package-arg" "^10.1.0" "pacote" "^15.0.7" @@ -2288,9 +2281,9 @@ "encoding" "^0.1.13" "minipass-fetch@^3.0.0": - "version" "3.0.0" + "version" "3.0.1" dependencies: - "minipass" "^3.1.6" + "minipass" "^4.0.0" "minipass-sized" "^1.0.3" "minizlib" "^2.1.2" optionalDependencies: @@ -2499,13 +2492,13 @@ "version" "1.0.1" "npm@^9.2.0": - "integrity" "sha512-oypVdaWGHDuV79RXLvp+B9gh6gDyAmoHKrQ0/JBYTWWx5D8/+AAxFdZC84fSIiyDdyW4qfrSyYGKhekxDOaMXQ==" - "resolved" "https://registry.npmjs.org/npm/-/npm-9.2.0.tgz" - "version" "9.2.0" + "integrity" "sha512-ydRVmnWEVXmc3DCM+F9BjiNj3IHkZ3Mwz5VbJYS2BpY/6d4PcKxNW+Xb0vzGeE6PkVhLcPxwhoIi+RFV2fSfEA==" + "resolved" "https://registry.npmjs.org/npm/-/npm-9.3.1.tgz" + "version" "9.3.1" dependencies: "@isaacs/string-locale-compare" "^1.1.0" - "@npmcli/arborist" "^6.1.5" - "@npmcli/config" "^6.1.0" + "@npmcli/arborist" "^6.1.6" + "@npmcli/config" "^6.1.1" "@npmcli/map-workspaces" "^3.0.0" "@npmcli/package-json" "^3.0.0" "@npmcli/run-script" "^6.0.0" @@ -2527,12 +2520,12 @@ "is-cidr" "^4.0.2" "json-parse-even-better-errors" "^3.0.0" "libnpmaccess" "^7.0.1" - "libnpmdiff" "^5.0.6" - "libnpmexec" "^5.0.6" - "libnpmfund" "^4.0.6" + "libnpmdiff" "^5.0.7" + "libnpmexec" "^5.0.7" + "libnpmfund" "^4.0.7" "libnpmhook" "^9.0.1" "libnpmorg" "^5.0.1" - "libnpmpack" "^5.0.6" + "libnpmpack" "^5.0.7" "libnpmpublish" "^7.0.6" "libnpmsearch" "^6.0.1" "libnpmteam" "^5.0.1" @@ -2561,7 +2554,6 @@ "read" "~1.0.7" "read-package-json" "^6.0.0" "read-package-json-fast" "^3.0.1" - "rimraf" "^3.0.2" "semver" "^7.3.8" "ssri" "^10.0.1" "tar" "^6.1.13"