From 0c7aa1756b06b5fba25f66d8c235947813599752 Mon Sep 17 00:00:00 2001 From: lonelil <51315646+lonelil@users.noreply.github.com> Date: Sat, 6 Apr 2024 15:53:28 +0800 Subject: [PATCH 01/30] add WarezCDN (broken) --- src/providers/all.ts | 4 + src/providers/embeds/warezcdn.ts | 128 +++++++++++++++++++++++ src/providers/sources/warezcdn/common.ts | 3 + src/providers/sources/warezcdn/index.ts | 74 +++++++++++++ 4 files changed, 209 insertions(+) create mode 100644 src/providers/embeds/warezcdn.ts create mode 100644 src/providers/sources/warezcdn/common.ts create mode 100644 src/providers/sources/warezcdn/index.ts diff --git a/src/providers/all.ts b/src/providers/all.ts index 63be995..2f01869 100644 --- a/src/providers/all.ts +++ b/src/providers/all.ts @@ -31,6 +31,7 @@ import { streamvidScraper } from './embeds/streamvid'; import { vidCloudScraper } from './embeds/vidcloud'; import { vidplayScraper } from './embeds/vidplay'; import { voeScraper } from './embeds/voe'; +import { warezcdnembedScraper } from './embeds/warezcdn'; import { wootlyScraper } from './embeds/wootly'; import { goojaraScraper } from './sources/goojara'; import { hdRezkaScraper } from './sources/hdrezka'; @@ -39,6 +40,7 @@ import { primewireScraper } from './sources/primewire'; import { ridooMoviesScraper } from './sources/ridomovies'; import { smashyStreamScraper } from './sources/smashystream'; import { vidSrcToScraper } from './sources/vidsrcto'; +import { warezcdnScraper } from './sources/warezcdn'; export function gatherAllSources(): Array { // all sources are gathered here @@ -58,6 +60,7 @@ export function gatherAllSources(): Array { goojaraScraper, hdRezkaScraper, primewireScraper, + warezcdnScraper, ]; } @@ -88,5 +91,6 @@ export function gatherAllEmbeds(): Array { droploadScraper, filelionsScraper, vTubeScraper, + warezcdnembedScraper, ]; } diff --git a/src/providers/embeds/warezcdn.ts b/src/providers/embeds/warezcdn.ts new file mode 100644 index 0000000..8223bfa --- /dev/null +++ b/src/providers/embeds/warezcdn.ts @@ -0,0 +1,128 @@ +import { makeEmbed } from '@/providers/base'; +import { warezcdnPlayerBase } from '@/providers/sources/warezcdn/common'; +import { EmbedScrapeContext } from '@/utils/context'; +import { NotFoundError } from '@/utils/errors'; + +import { Stream } from '../streams'; + +const cdnListing = [50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64]; + +function decrypt(input: string) { + let output = atob(input); + + // Remove leading and trailing whitespaces + output = output.trim(); + + // Reverse the string + output = output.split('').reverse().join(''); + + // Get the last 5 characters and reverse them + let last = output.slice(-5); + last = last.split('').reverse().join(''); + + // Remove the last 5 characters from the original string + output = output.slice(0, -5); + + // Return the original string concatenated with the reversed last 5 characters + return `${output}${last}`; +} + +async function checkUrls(ctx: EmbedScrapeContext, fileId: string) { + for (const id of cdnListing) { + const url = `https://cloclo${id}.cloud.mail.ru/weblink/view/${fileId}`; + const response = await ctx.proxiedFetcher.full(url, { + method: 'GET', + headers: { + Range: 'bytes=0-1', + }, + }); + if (response.statusCode === 206) return url; + } + return null; +} + +async function getVideoOwlStream(ctx: EmbedScrapeContext, decryptedId: string) { + const sharePage = await ctx.proxiedFetcher('https://cloud.mail.ru/public/uaRH/2PYWcJRpH'); + + const cloudSettings = sharePage.match(/window\.cloudSettings=(\{.*?\})<\/script>/)?.[1]; + if (!cloudSettings) throw new NotFoundError('Failed to get cloudSettings'); + + const parsedCloudSettings = JSON.parse(JSON.stringify(cloudSettings)); + + console.log(parsedCloudSettings); + + const videoOwlUrl = parsedCloudSettings.dispatcher.videowl_view.url; + if (!videoOwlUrl) throw new NotFoundError('Failed to get videoOwlUrl'); + + return `${videoOwlUrl}/${btoa(decryptedId)}.m3u8?${new URLSearchParams({ + double_encode: '1', + })}`; +} + +async function getStream(ctx: EmbedScrapeContext, decryptedId: string): Promise { + try { + const streamUrl = await getVideoOwlStream(ctx, decryptedId); + console.log(streamUrl); + return { + id: 'primary', + type: 'hls', + flags: [], + captions: [], + playlist: streamUrl, + }; + } catch (err) { + console.error(err); + const streamUrl = await checkUrls(ctx, decryptedId); + return { + id: 'primary', + type: 'file', + flags: [], + captions: [], + qualities: { + unknown: { + type: 'mp4', + url: streamUrl!, + }, + }, + }; + } +} + +export const warezcdnembedScraper = makeEmbed({ + id: 'warezcdnembed', // WarezCDN is both a source and an embed host + name: 'WarezCDN', + rank: 82, + async scrape(ctx) { + const page = await ctx.proxiedFetcher(`/player.php?${new URLSearchParams({ id: ctx.url })}`, { + baseUrl: warezcdnPlayerBase, + headers: { + Referer: `${warezcdnPlayerBase}/getEmbed.php?${new URLSearchParams({ + id: ctx.url, + sv: 'warezcdn', + })}`, + }, + }); + const allowanceKey = page.match(/let allowanceKey = "(.*?)";/)?.[1]; + if (!allowanceKey) throw new NotFoundError('Failed to get allowanceKey'); + + const streamData = await ctx.proxiedFetcher('/functions.php', { + baseUrl: warezcdnPlayerBase, + method: 'POST', + body: new URLSearchParams({ + getVideo: ctx.url, + key: allowanceKey, + }), + }); + const stream = JSON.parse(streamData); + + if (!stream.id) throw new NotFoundError("can't get stream id"); + + const decryptedId = decrypt(stream.id); + + if (!decryptedId) throw new NotFoundError("can't get file id"); + + return { + stream: [await getStream(ctx, decryptedId)], + }; + }, +}); diff --git a/src/providers/sources/warezcdn/common.ts b/src/providers/sources/warezcdn/common.ts new file mode 100644 index 0000000..5fcde55 --- /dev/null +++ b/src/providers/sources/warezcdn/common.ts @@ -0,0 +1,3 @@ +export const warezcdnBase = 'https://embed.warezcdn.com'; +export const warezcdnApiBase = 'https://warezcdn.com/embed'; +export const warezcdnPlayerBase = 'https://warezcdn.com/player'; diff --git a/src/providers/sources/warezcdn/index.ts b/src/providers/sources/warezcdn/index.ts new file mode 100644 index 0000000..f2660dc --- /dev/null +++ b/src/providers/sources/warezcdn/index.ts @@ -0,0 +1,74 @@ +import { load } from 'cheerio'; + +import { SourcererEmbed, SourcererOutput, makeSourcerer } from '@/providers/base'; +import { mixdropScraper } from '@/providers/embeds/mixdrop'; +import { MovieScrapeContext, ShowScrapeContext } from '@/utils/context'; +import { NotFoundError } from '@/utils/errors'; + +import { warezcdnApiBase, warezcdnBase } from './common'; + +const embeds = { + warezcdn: mixdropScraper.id, + mixdrop: mixdropScraper.id, +}; + +const universalScraper = async (ctx: MovieScrapeContext | ShowScrapeContext) => { + if (!ctx.media.imdbId) throw new NotFoundError('This source requires IMDB id.'); + + let id = `filme/${ctx.media.imdbId}`; + if (ctx.media.type === 'show') + id = `serie/${ctx.media.imdbId}/${ctx.media.season.number}/${ctx.media.episode.number}`; + + const serversPage = await ctx.proxiedFetcher(`/${id}`, { + baseUrl: warezcdnBase, + }); + const $ = load(serversPage); + + const servers = await Promise.all( + $('.hostList.active [data-load-embed]') + .filter((_index, element) => { + const embed = $(element).attr('data-load-embed-host'); + return !!embed && !!embeds[embed as keyof typeof embeds]; + }) + .map(async (_index, element) => { + const embedHost = $(element).attr('data-load-embed-host'); + const embedId = embeds[embedHost as keyof typeof embeds]; + let embedUrl = $(element).attr('data-load-embed')!; + + if (embedHost === 'mixdrop') { + const params = new URLSearchParams({ + id: embedUrl, + sv: 'mixdrop', + }); + const realUrl = await ctx.proxiedFetcher(`/getPlay.php?${params}`, { + baseUrl: warezcdnApiBase, + headers: { + Referer: `${warezcdnApiBase}/getEmbed.php?${params}`, + }, + }); + + const realEmbedUrl = realUrl.match(/window\.location\.href="([^"]*)";/); + embedUrl = realEmbedUrl[1]; + } + + return { + embedId, + url: embedUrl, + } as SourcererEmbed; + }) + .get(), + ); + + return { + embeds: servers, + } as SourcererOutput; +}; + +export const warezcdnScraper = makeSourcerer({ + id: 'warezcdn', + name: 'WarezCDN', + rank: 81, + flags: [], + scrapeMovie: universalScraper, + scrapeShow: universalScraper, +}); From d99a22d7348a64150b875649e633892e7fa03c70 Mon Sep 17 00:00:00 2001 From: teddyHV11 Date: Sun, 7 Apr 2024 10:53:09 +0300 Subject: [PATCH 02/30] Add insertunit --- src/providers/all.ts | 2 + src/providers/sources/insertunit.ts | 72 +++++++++++++++++++++++++++++ 2 files changed, 74 insertions(+) create mode 100644 src/providers/sources/insertunit.ts diff --git a/src/providers/all.ts b/src/providers/all.ts index d20e14b..49521c3 100644 --- a/src/providers/all.ts +++ b/src/providers/all.ts @@ -11,6 +11,7 @@ import { upstreamScraper } from '@/providers/embeds/upstream'; import { vidsrcembedScraper } from '@/providers/embeds/vidsrc'; import { flixhqScraper } from '@/providers/sources/flixhq/index'; import { goMoviesScraper } from '@/providers/sources/gomovies/index'; +import { insertunitScraper } from '@/providers/sources/insertunit'; import { kissAsianScraper } from '@/providers/sources/kissasian/index'; import { lookmovieScraper } from '@/providers/sources/lookmovie'; import { remotestreamScraper } from '@/providers/sources/remotestream'; @@ -55,6 +56,7 @@ export function gatherAllSources(): Array { goojaraScraper, hdRezkaScraper, primewireScraper, + insertunitScraper, ]; } diff --git a/src/providers/sources/insertunit.ts b/src/providers/sources/insertunit.ts new file mode 100644 index 0000000..b9d5f07 --- /dev/null +++ b/src/providers/sources/insertunit.ts @@ -0,0 +1,72 @@ +import { flags } from '@/entrypoint/utils/targets'; +import { makeSourcerer } from '@/providers/base'; +import { NotFoundError } from '@/utils/errors'; + +const insertunitbase = 'https://api.insertunit.ws/'; + +export const insertunitScraper = makeSourcerer({ + id: 'insertunit', + name: 'Insert Unit', + disabled: false, + rank: 60, + flags: [flags.CORS_ALLOWED], + async scrapeShow(ctx) { + const playerData = await ctx.fetcher(`${insertunitbase}/embed/imdb/${ctx.media.imdbId}`); + ctx.progress(30); + + const seasonDataJSONregex = /seasons:(.*)/; + const seasonData = seasonDataJSONregex.exec(playerData); + + if (seasonData === null || seasonData[1] === null) { + throw new NotFoundError('No result found'); + } + ctx.progress(60); + + const seasonTable = JSON.parse(seasonData[1]); + for (const season of seasonTable) { + if (season.season === ctx.media.season.number) { + if (season.episodes[ctx.media.episode.number] && season.episodes[ctx.media.episode.number].hls) { + return { + embeds: [], + stream: [ + { + id: 'primary', + captions: [], + playlist: season.episodes[ctx.media.episode.number].hls, + type: 'hls', + flags: [flags.CORS_ALLOWED], + }, + ], + }; + } + } + } + + throw new NotFoundError('No result found'); + }, + async scrapeMovie(ctx) { + const playerData = await ctx.fetcher(`${insertunitbase}/embed/imdb/${ctx.media.imdbId}`); + ctx.progress(50); + + const streamRegex = /hls: "([^"]*)/; + const streamData = streamRegex.exec(playerData); + + if (streamData === null || streamData[1] === null) { + throw new NotFoundError('No result found'); + } + ctx.progress(90); + + return { + embeds: [], + stream: [ + { + id: 'primary', + type: 'hls', + playlist: streamData[1], + flags: [flags.CORS_ALLOWED], + captions: [], + }, + ], + }; + }, +}); From 8a711265cb7e3b1677ddaaa4bac1e6ff6819eccd Mon Sep 17 00:00:00 2001 From: infvortx <53519839+infvortx@users.noreply.github.com> Date: Sun, 7 Apr 2024 11:48:04 +0300 Subject: [PATCH 03/30] Update insertunit.ts --- src/providers/sources/insertunit.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/providers/sources/insertunit.ts b/src/providers/sources/insertunit.ts index b9d5f07..bf70a51 100644 --- a/src/providers/sources/insertunit.ts +++ b/src/providers/sources/insertunit.ts @@ -6,7 +6,7 @@ const insertunitbase = 'https://api.insertunit.ws/'; export const insertunitScraper = makeSourcerer({ id: 'insertunit', - name: 'Insert Unit', + name: 'Insertunit', disabled: false, rank: 60, flags: [flags.CORS_ALLOWED], From 68fe85065f2c99d61fe95905eaedfbf630d36410 Mon Sep 17 00:00:00 2001 From: teddyHV11 Date: Sun, 7 Apr 2024 11:54:34 +0300 Subject: [PATCH 04/30] Updated provider name --- src/providers/sources/insertunit.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/providers/sources/insertunit.ts b/src/providers/sources/insertunit.ts index b9d5f07..bf70a51 100644 --- a/src/providers/sources/insertunit.ts +++ b/src/providers/sources/insertunit.ts @@ -6,7 +6,7 @@ const insertunitbase = 'https://api.insertunit.ws/'; export const insertunitScraper = makeSourcerer({ id: 'insertunit', - name: 'Insert Unit', + name: 'Insertunit', disabled: false, rank: 60, flags: [flags.CORS_ALLOWED], From fa8e3fa7e138b75f41112997f006ff22377a3d15 Mon Sep 17 00:00:00 2001 From: TPN Date: Sun, 7 Apr 2024 11:21:48 +0000 Subject: [PATCH 05/30] fix vidsrc --- .vscode/settings.json | 5 ++++- src/providers/embeds/vidsrc.ts | 27 +++++++++++++++------------ 2 files changed, 19 insertions(+), 13 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index e4df0f2..1a2362a 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,5 +1,8 @@ { "editor.formatOnSave": true, "editor.defaultFormatter": "dbaeumer.vscode-eslint", - "eslint.format.enable": true + "eslint.format.enable": true, + "[typescript]": { + "editor.defaultFormatter": "vscode.typescript-language-features" + } } diff --git a/src/providers/embeds/vidsrc.ts b/src/providers/embeds/vidsrc.ts index 069c2f6..ae945d2 100644 --- a/src/providers/embeds/vidsrc.ts +++ b/src/providers/embeds/vidsrc.ts @@ -32,21 +32,24 @@ export const vidsrcembedScraper = makeEmbed({ if (!finalUrl.includes('.m3u8')) throw new Error('Unable to find HLS playlist'); let setPassLink = html.match(setPassRegex)?.[1]; - if (!setPassLink) throw new Error('Unable to find set_pass.php link'); - if (setPassLink.startsWith('//')) { - setPassLink = `https:${setPassLink}`; + // isn't neeeded, the stream works without it anyway + // shouldn't fail if the setpass link is not found + if (setPassLink) { + if (setPassLink.startsWith('//')) { + setPassLink = `https:${setPassLink}`; + } + + // VidSrc uses a password endpoint to temporarily whitelist the user's IP. This is called in an interval by the player. + // It currently has no effect on the player itself, the content plays fine without it. + // In the future we might have to introduce hooks for the frontend to call this endpoint. + await ctx.proxiedFetcher(setPassLink, { + headers: { + referer: ctx.url, + }, + }); } - // VidSrc uses a password endpoint to temporarily whitelist the user's IP. This is called in an interval by the player. - // It currently has no effect on the player itself, the content plays fine without it. - // In the future we might have to introduce hooks for the frontend to call this endpoint. - await ctx.proxiedFetcher(setPassLink, { - headers: { - referer: ctx.url, - }, - }); - return { stream: [ { From e75fe05ad2ce3a44d3a592a098c299cd8568599e Mon Sep 17 00:00:00 2001 From: TPN Date: Sun, 7 Apr 2024 11:37:38 +0000 Subject: [PATCH 06/30] revert settings.json --- .vscode/settings.json | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index 1a2362a..e4df0f2 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,8 +1,5 @@ { "editor.formatOnSave": true, "editor.defaultFormatter": "dbaeumer.vscode-eslint", - "eslint.format.enable": true, - "[typescript]": { - "editor.defaultFormatter": "vscode.typescript-language-features" - } + "eslint.format.enable": true } From 7dc5a5ac8324deb8d1ae1006b5d46058d8e847e2 Mon Sep 17 00:00:00 2001 From: teddyHV11 Date: Sun, 7 Apr 2024 15:33:04 +0300 Subject: [PATCH 07/30] Fix series scraping Series scraping was off for some series, this fixes it + also adds support for episodes that are in multiple parts (continuation of each other) as the provider returns them as a single file in episode ranges. --- src/providers/sources/insertunit.ts | 32 +++++++++++++++-------------- 1 file changed, 17 insertions(+), 15 deletions(-) diff --git a/src/providers/sources/insertunit.ts b/src/providers/sources/insertunit.ts index bf70a51..4dd9795 100644 --- a/src/providers/sources/insertunit.ts +++ b/src/providers/sources/insertunit.ts @@ -23,21 +23,23 @@ export const insertunitScraper = makeSourcerer({ ctx.progress(60); const seasonTable = JSON.parse(seasonData[1]); - for (const season of seasonTable) { - if (season.season === ctx.media.season.number) { - if (season.episodes[ctx.media.episode.number] && season.episodes[ctx.media.episode.number].hls) { - return { - embeds: [], - stream: [ - { - id: 'primary', - captions: [], - playlist: season.episodes[ctx.media.episode.number].hls, - type: 'hls', - flags: [flags.CORS_ALLOWED], - }, - ], - }; + for (const seasonElement of seasonTable) { + if (seasonElement.season === ctx.media.season.number) { + for (const episodeElement of seasonElement.episodes) { + if (episodeElement.episode.includes(ctx.media.episode.number)) { + return { + embeds: [], + stream: [ + { + id: 'primary', + captions: [], + playlist: episodeElement.hls, + type: 'hls', + flags: [flags.CORS_ALLOWED], + }, + ], + }; + } } } } From 51c58aae75ff74eafe904282f252c5ff46a81163 Mon Sep 17 00:00:00 2001 From: lonelil <51315646+lonelil@users.noreply.github.com> Date: Sun, 7 Apr 2024 22:53:52 +0800 Subject: [PATCH 08/30] split embeds --- src/providers/all.ts | 6 +- src/providers/embeds/warezcdn.ts | 128 ------------------------ src/providers/embeds/warezcdn/common.ts | 55 ++++++++++ src/providers/embeds/warezcdn/hls.ts | 44 ++++++++ src/providers/embeds/warezcdn/mp4.ts | 58 +++++++++++ src/providers/sources/warezcdn/index.ts | 73 +++++++------- 6 files changed, 198 insertions(+), 166 deletions(-) delete mode 100644 src/providers/embeds/warezcdn.ts create mode 100644 src/providers/embeds/warezcdn/common.ts create mode 100644 src/providers/embeds/warezcdn/hls.ts create mode 100644 src/providers/embeds/warezcdn/mp4.ts diff --git a/src/providers/all.ts b/src/providers/all.ts index 2f01869..e32790e 100644 --- a/src/providers/all.ts +++ b/src/providers/all.ts @@ -31,7 +31,8 @@ import { streamvidScraper } from './embeds/streamvid'; import { vidCloudScraper } from './embeds/vidcloud'; import { vidplayScraper } from './embeds/vidplay'; import { voeScraper } from './embeds/voe'; -import { warezcdnembedScraper } from './embeds/warezcdn'; +import { warezcdnembedHlsScraper } from './embeds/warezcdn/hls'; +import { warezcdnembedMp4Scraper } from './embeds/warezcdn/mp4'; import { wootlyScraper } from './embeds/wootly'; import { goojaraScraper } from './sources/goojara'; import { hdRezkaScraper } from './sources/hdrezka'; @@ -91,6 +92,7 @@ export function gatherAllEmbeds(): Array { droploadScraper, filelionsScraper, vTubeScraper, - warezcdnembedScraper, + warezcdnembedHlsScraper, + warezcdnembedMp4Scraper, ]; } diff --git a/src/providers/embeds/warezcdn.ts b/src/providers/embeds/warezcdn.ts deleted file mode 100644 index 8223bfa..0000000 --- a/src/providers/embeds/warezcdn.ts +++ /dev/null @@ -1,128 +0,0 @@ -import { makeEmbed } from '@/providers/base'; -import { warezcdnPlayerBase } from '@/providers/sources/warezcdn/common'; -import { EmbedScrapeContext } from '@/utils/context'; -import { NotFoundError } from '@/utils/errors'; - -import { Stream } from '../streams'; - -const cdnListing = [50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64]; - -function decrypt(input: string) { - let output = atob(input); - - // Remove leading and trailing whitespaces - output = output.trim(); - - // Reverse the string - output = output.split('').reverse().join(''); - - // Get the last 5 characters and reverse them - let last = output.slice(-5); - last = last.split('').reverse().join(''); - - // Remove the last 5 characters from the original string - output = output.slice(0, -5); - - // Return the original string concatenated with the reversed last 5 characters - return `${output}${last}`; -} - -async function checkUrls(ctx: EmbedScrapeContext, fileId: string) { - for (const id of cdnListing) { - const url = `https://cloclo${id}.cloud.mail.ru/weblink/view/${fileId}`; - const response = await ctx.proxiedFetcher.full(url, { - method: 'GET', - headers: { - Range: 'bytes=0-1', - }, - }); - if (response.statusCode === 206) return url; - } - return null; -} - -async function getVideoOwlStream(ctx: EmbedScrapeContext, decryptedId: string) { - const sharePage = await ctx.proxiedFetcher('https://cloud.mail.ru/public/uaRH/2PYWcJRpH'); - - const cloudSettings = sharePage.match(/window\.cloudSettings=(\{.*?\})<\/script>/)?.[1]; - if (!cloudSettings) throw new NotFoundError('Failed to get cloudSettings'); - - const parsedCloudSettings = JSON.parse(JSON.stringify(cloudSettings)); - - console.log(parsedCloudSettings); - - const videoOwlUrl = parsedCloudSettings.dispatcher.videowl_view.url; - if (!videoOwlUrl) throw new NotFoundError('Failed to get videoOwlUrl'); - - return `${videoOwlUrl}/${btoa(decryptedId)}.m3u8?${new URLSearchParams({ - double_encode: '1', - })}`; -} - -async function getStream(ctx: EmbedScrapeContext, decryptedId: string): Promise { - try { - const streamUrl = await getVideoOwlStream(ctx, decryptedId); - console.log(streamUrl); - return { - id: 'primary', - type: 'hls', - flags: [], - captions: [], - playlist: streamUrl, - }; - } catch (err) { - console.error(err); - const streamUrl = await checkUrls(ctx, decryptedId); - return { - id: 'primary', - type: 'file', - flags: [], - captions: [], - qualities: { - unknown: { - type: 'mp4', - url: streamUrl!, - }, - }, - }; - } -} - -export const warezcdnembedScraper = makeEmbed({ - id: 'warezcdnembed', // WarezCDN is both a source and an embed host - name: 'WarezCDN', - rank: 82, - async scrape(ctx) { - const page = await ctx.proxiedFetcher(`/player.php?${new URLSearchParams({ id: ctx.url })}`, { - baseUrl: warezcdnPlayerBase, - headers: { - Referer: `${warezcdnPlayerBase}/getEmbed.php?${new URLSearchParams({ - id: ctx.url, - sv: 'warezcdn', - })}`, - }, - }); - const allowanceKey = page.match(/let allowanceKey = "(.*?)";/)?.[1]; - if (!allowanceKey) throw new NotFoundError('Failed to get allowanceKey'); - - const streamData = await ctx.proxiedFetcher('/functions.php', { - baseUrl: warezcdnPlayerBase, - method: 'POST', - body: new URLSearchParams({ - getVideo: ctx.url, - key: allowanceKey, - }), - }); - const stream = JSON.parse(streamData); - - if (!stream.id) throw new NotFoundError("can't get stream id"); - - const decryptedId = decrypt(stream.id); - - if (!decryptedId) throw new NotFoundError("can't get file id"); - - return { - stream: [await getStream(ctx, decryptedId)], - }; - }, -}); diff --git a/src/providers/embeds/warezcdn/common.ts b/src/providers/embeds/warezcdn/common.ts new file mode 100644 index 0000000..3a18bb6 --- /dev/null +++ b/src/providers/embeds/warezcdn/common.ts @@ -0,0 +1,55 @@ +import { warezcdnPlayerBase } from '@/providers/sources/warezcdn/common'; +import { EmbedScrapeContext } from '@/utils/context'; +import { NotFoundError } from '@/utils/errors'; + +function decrypt(input: string) { + let output = atob(input); + + // Remove leading and trailing whitespaces + output = output.trim(); + + // Reverse the string + output = output.split('').reverse().join(''); + + // Get the last 5 characters and reverse them + let last = output.slice(-5); + last = last.split('').reverse().join(''); + + // Remove the last 5 characters from the original string + output = output.slice(0, -5); + + // Return the original string concatenated with the reversed last 5 characters + return `${output}${last}`; +} + +export async function getDecryptedId(ctx: EmbedScrapeContext) { + const page = await ctx.proxiedFetcher(`/player.php?${new URLSearchParams({ id: ctx.url })}`, { + baseUrl: warezcdnPlayerBase, + headers: { + Referer: `${warezcdnPlayerBase}/getEmbed.php?${new URLSearchParams({ + id: ctx.url, + sv: 'warezcdn', + })}`, + }, + }); + const allowanceKey = page.match(/let allowanceKey = "(.*?)";/)?.[1]; + if (!allowanceKey) throw new NotFoundError('Failed to get allowanceKey'); + + const streamData = await ctx.proxiedFetcher('/functions.php', { + baseUrl: warezcdnPlayerBase, + method: 'POST', + body: new URLSearchParams({ + getVideo: ctx.url, + key: allowanceKey, + }), + }); + const stream = JSON.parse(streamData); + + if (!stream.id) throw new NotFoundError("can't get stream id"); + + const decryptedId = decrypt(stream.id); + + if (!decryptedId) throw new NotFoundError("can't get file id"); + + return decryptedId; +} diff --git a/src/providers/embeds/warezcdn/hls.ts b/src/providers/embeds/warezcdn/hls.ts new file mode 100644 index 0000000..957dd07 --- /dev/null +++ b/src/providers/embeds/warezcdn/hls.ts @@ -0,0 +1,44 @@ +import { flags } from '@/entrypoint/utils/targets'; +import { makeEmbed } from '@/providers/base'; +import { EmbedScrapeContext } from '@/utils/context'; +import { NotFoundError } from '@/utils/errors'; + +import { getDecryptedId } from './common'; + +// Method found by atpn +async function getVideowlUrlStream(ctx: EmbedScrapeContext, decryptedId: string) { + const sharePage = await ctx.proxiedFetcher('https://cloud.mail.ru/public/uaRH/2PYWcJRpH'); + const regex = /"videowl_view":\{"count":"(\d+)","url":"([^"]+)"\}/g; + const videowlUrl = regex.exec(sharePage)?.[2]; + + if (!videowlUrl) throw new NotFoundError('Failed to get videoOwlUrl'); + + return `${videowlUrl}/${btoa(decryptedId)}.m3u8?${new URLSearchParams({ + double_encode: '1', + })}`; +} + +export const warezcdnembedHlsScraper = makeEmbed({ + id: 'warezcdnembedhls', // WarezCDN is both a source and an embed host + name: 'WarezCDN HLS', + rank: 82, + async scrape(ctx) { + const decryptedId = await getDecryptedId(ctx); + + if (!decryptedId) throw new NotFoundError("can't get file id"); + + const streamUrl = await getVideowlUrlStream(ctx, decryptedId); + + return { + stream: [ + { + id: 'primary', + type: 'hls', + flags: [flags.IP_LOCKED], + captions: [], + playlist: streamUrl, + }, + ], + }; + }, +}); diff --git a/src/providers/embeds/warezcdn/mp4.ts b/src/providers/embeds/warezcdn/mp4.ts new file mode 100644 index 0000000..53b85dd --- /dev/null +++ b/src/providers/embeds/warezcdn/mp4.ts @@ -0,0 +1,58 @@ +import { makeEmbed } from '@/providers/base'; +import { EmbedScrapeContext } from '@/utils/context'; +import { NotFoundError } from '@/utils/errors'; + +import { getDecryptedId } from './common'; + +const cdnListing = [50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64]; + +async function checkUrls(ctx: EmbedScrapeContext, fileId: string) { + for (const id of cdnListing) { + const url = `https://cloclo${id}.cloud.mail.ru/weblink/view/${fileId}`; + const response = await ctx.proxiedFetcher.full(url, { + method: 'GET', + headers: { + Range: 'bytes=0-1', + }, + }); + if (response.statusCode === 206) return url; + } + return null; +} + +export const warezcdnembedMp4Scraper = makeEmbed({ + id: 'warezcdnembedmp4', // WarezCDN is both a source and an embed host + name: 'WarezCDN MP4', + rank: 83, + disabled: true, // crashes movie-web player, disabling for now + async scrape(ctx) { + const decryptedId = await getDecryptedId(ctx); + + if (!decryptedId) throw new NotFoundError("can't get file id"); + + const streamUrl = await checkUrls(ctx, decryptedId); + + if (!streamUrl) throw new NotFoundError("can't get stream id"); + + return { + stream: [ + { + id: 'primary', + captions: [], + qualities: { + unknown: { + type: 'mp4', + url: streamUrl, + }, + }, + type: 'file', + flags: [], + headers: { + Origin: 'https://cloud.mail.ru', + Referer: 'https://cloud.mail.ru/', + }, + }, + ], + }; + }, +}); diff --git a/src/providers/sources/warezcdn/index.ts b/src/providers/sources/warezcdn/index.ts index f2660dc..6925855 100644 --- a/src/providers/sources/warezcdn/index.ts +++ b/src/providers/sources/warezcdn/index.ts @@ -2,16 +2,13 @@ import { load } from 'cheerio'; import { SourcererEmbed, SourcererOutput, makeSourcerer } from '@/providers/base'; import { mixdropScraper } from '@/providers/embeds/mixdrop'; +import { warezcdnembedHlsScraper } from '@/providers/embeds/warezcdn/hls'; +import { warezcdnembedMp4Scraper } from '@/providers/embeds/warezcdn/mp4'; import { MovieScrapeContext, ShowScrapeContext } from '@/utils/context'; import { NotFoundError } from '@/utils/errors'; import { warezcdnApiBase, warezcdnBase } from './common'; -const embeds = { - warezcdn: mixdropScraper.id, - mixdrop: mixdropScraper.id, -}; - const universalScraper = async (ctx: MovieScrapeContext | ShowScrapeContext) => { if (!ctx.media.imdbId) throw new NotFoundError('This source requires IMDB id.'); @@ -24,43 +21,47 @@ const universalScraper = async (ctx: MovieScrapeContext | ShowScrapeContext) => }); const $ = load(serversPage); - const servers = await Promise.all( - $('.hostList.active [data-load-embed]') - .filter((_index, element) => { - const embed = $(element).attr('data-load-embed-host'); - return !!embed && !!embeds[embed as keyof typeof embeds]; - }) - .map(async (_index, element) => { - const embedHost = $(element).attr('data-load-embed-host'); - const embedId = embeds[embedHost as keyof typeof embeds]; - let embedUrl = $(element).attr('data-load-embed')!; + const embedsHost = $('.hostList.active [data-load-embed]').get(); - if (embedHost === 'mixdrop') { - const params = new URLSearchParams({ - id: embedUrl, - sv: 'mixdrop', - }); - const realUrl = await ctx.proxiedFetcher(`/getPlay.php?${params}`, { - baseUrl: warezcdnApiBase, - headers: { - Referer: `${warezcdnApiBase}/getEmbed.php?${params}`, - }, - }); + const embeds: SourcererEmbed[] = []; - const realEmbedUrl = realUrl.match(/window\.location\.href="([^"]*)";/); - embedUrl = realEmbedUrl[1]; - } + embedsHost.forEach(async (element) => { + const embedHost = $(element).attr('data-load-embed-host')!; + const embedUrl = $(element).attr('data-load-embed')!; - return { - embedId, + if (embedHost === 'mixdrop') { + const params = new URLSearchParams({ + id: embedUrl, + sv: 'mixdrop', + }); + const realUrl = await ctx.proxiedFetcher(`/getPlay.php?${params}`, { + baseUrl: warezcdnApiBase, + headers: { + Referer: `${warezcdnApiBase}/getEmbed.php?${params}`, + }, + }); + + const realEmbedUrl = realUrl.match(/window\.location\.href="([^"]*)";/); + embeds.push({ + embedId: mixdropScraper.id, + url: realEmbedUrl[1], + }); + } else if (embedHost === 'warezcdn') { + embeds.push( + { + embedId: warezcdnembedHlsScraper.id, url: embedUrl, - } as SourcererEmbed; - }) - .get(), - ); + }, + { + embedId: warezcdnembedMp4Scraper.id, + url: embedUrl, + }, + ); + } + }); return { - embeds: servers, + embeds, } as SourcererOutput; }; From 1a754ad95171578318ba247ee120ac05ba5a017c Mon Sep 17 00:00:00 2001 From: lonelil <51315646+lonelil@users.noreply.github.com> Date: Mon, 8 Apr 2024 15:05:41 +0800 Subject: [PATCH 09/30] fix hls url format --- src/providers/embeds/warezcdn/hls.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/providers/embeds/warezcdn/hls.ts b/src/providers/embeds/warezcdn/hls.ts index 957dd07..827bf94 100644 --- a/src/providers/embeds/warezcdn/hls.ts +++ b/src/providers/embeds/warezcdn/hls.ts @@ -13,7 +13,7 @@ async function getVideowlUrlStream(ctx: EmbedScrapeContext, decryptedId: string) if (!videowlUrl) throw new NotFoundError('Failed to get videoOwlUrl'); - return `${videowlUrl}/${btoa(decryptedId)}.m3u8?${new URLSearchParams({ + return `${videowlUrl}/0p/${btoa(decryptedId)}.m3u8?${new URLSearchParams({ double_encode: '1', })}`; } From 2daded1d799feec58181afab8de774a9ee210211 Mon Sep 17 00:00:00 2001 From: Jorrin Date: Mon, 8 Apr 2024 15:37:41 +0200 Subject: [PATCH 10/30] Disable showbox --- src/providers/sources/showbox/index.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/providers/sources/showbox/index.ts b/src/providers/sources/showbox/index.ts index c22979a..9901a52 100644 --- a/src/providers/sources/showbox/index.ts +++ b/src/providers/sources/showbox/index.ts @@ -42,6 +42,7 @@ export const showboxScraper = makeSourcerer({ id: 'showbox', name: 'Showbox', rank: 150, + disabled: true, flags: [flags.CORS_ALLOWED, flags.CF_BLOCKED], scrapeShow: comboScraper, scrapeMovie: comboScraper, From 921c35b3ed0221ffcd55560b73429d0ce2a5d5f1 Mon Sep 17 00:00:00 2001 From: teddyHV11 Date: Tue, 9 Apr 2024 07:16:41 +0300 Subject: [PATCH 11/30] Add caption scraping + types --- src/providers/all.ts | 2 +- src/providers/sources/insertunit.ts | 74 --------- .../sources/insertunit/insertunit.ts | 147 ++++++++++++++++++ src/providers/sources/insertunit/types.ts | 30 ++++ 4 files changed, 178 insertions(+), 75 deletions(-) delete mode 100644 src/providers/sources/insertunit.ts create mode 100644 src/providers/sources/insertunit/insertunit.ts create mode 100644 src/providers/sources/insertunit/types.ts diff --git a/src/providers/all.ts b/src/providers/all.ts index 887290d..675aa36 100644 --- a/src/providers/all.ts +++ b/src/providers/all.ts @@ -14,7 +14,7 @@ import { vidsrcembedScraper } from '@/providers/embeds/vidsrc'; import { vTubeScraper } from '@/providers/embeds/vtube'; import { flixhqScraper } from '@/providers/sources/flixhq/index'; import { goMoviesScraper } from '@/providers/sources/gomovies/index'; -import { insertunitScraper } from '@/providers/sources/insertunit'; +import { insertunitScraper } from '@/providers/sources/insertunit/insertunit'; import { kissAsianScraper } from '@/providers/sources/kissasian/index'; import { lookmovieScraper } from '@/providers/sources/lookmovie'; import { remotestreamScraper } from '@/providers/sources/remotestream'; diff --git a/src/providers/sources/insertunit.ts b/src/providers/sources/insertunit.ts deleted file mode 100644 index 4dd9795..0000000 --- a/src/providers/sources/insertunit.ts +++ /dev/null @@ -1,74 +0,0 @@ -import { flags } from '@/entrypoint/utils/targets'; -import { makeSourcerer } from '@/providers/base'; -import { NotFoundError } from '@/utils/errors'; - -const insertunitbase = 'https://api.insertunit.ws/'; - -export const insertunitScraper = makeSourcerer({ - id: 'insertunit', - name: 'Insertunit', - disabled: false, - rank: 60, - flags: [flags.CORS_ALLOWED], - async scrapeShow(ctx) { - const playerData = await ctx.fetcher(`${insertunitbase}/embed/imdb/${ctx.media.imdbId}`); - ctx.progress(30); - - const seasonDataJSONregex = /seasons:(.*)/; - const seasonData = seasonDataJSONregex.exec(playerData); - - if (seasonData === null || seasonData[1] === null) { - throw new NotFoundError('No result found'); - } - ctx.progress(60); - - const seasonTable = JSON.parse(seasonData[1]); - for (const seasonElement of seasonTable) { - if (seasonElement.season === ctx.media.season.number) { - for (const episodeElement of seasonElement.episodes) { - if (episodeElement.episode.includes(ctx.media.episode.number)) { - return { - embeds: [], - stream: [ - { - id: 'primary', - captions: [], - playlist: episodeElement.hls, - type: 'hls', - flags: [flags.CORS_ALLOWED], - }, - ], - }; - } - } - } - } - - throw new NotFoundError('No result found'); - }, - async scrapeMovie(ctx) { - const playerData = await ctx.fetcher(`${insertunitbase}/embed/imdb/${ctx.media.imdbId}`); - ctx.progress(50); - - const streamRegex = /hls: "([^"]*)/; - const streamData = streamRegex.exec(playerData); - - if (streamData === null || streamData[1] === null) { - throw new NotFoundError('No result found'); - } - ctx.progress(90); - - return { - embeds: [], - stream: [ - { - id: 'primary', - type: 'hls', - playlist: streamData[1], - flags: [flags.CORS_ALLOWED], - captions: [], - }, - ], - }; - }, -}); diff --git a/src/providers/sources/insertunit/insertunit.ts b/src/providers/sources/insertunit/insertunit.ts new file mode 100644 index 0000000..5c89663 --- /dev/null +++ b/src/providers/sources/insertunit/insertunit.ts @@ -0,0 +1,147 @@ +import { flags } from '@/entrypoint/utils/targets'; +import { makeSourcerer } from '@/providers/base'; +import { Caption, removeDuplicatedLanguages } from '@/providers/captions'; +import { NotFoundError } from '@/utils/errors'; + +import { Season, Subtitle } from './types'; + +const insertUnitBase = 'https://api.insertunit.ws/'; + +export const insertunitScraper = makeSourcerer({ + id: 'insertunit', + name: 'Insertunit', + disabled: false, + rank: 60, + flags: [flags.CORS_ALLOWED], + async scrapeShow(ctx) { + const playerData = await ctx.fetcher(`/embed/imdb/${ctx.media.imdbId}`, { + baseUrl: insertUnitBase, + }); + ctx.progress(30); + + const seasonDataJSONregex = /seasons:(.*)/; + const seasonData = seasonDataJSONregex.exec(playerData); + + if (seasonData === null || seasonData[1] === null) { + throw new NotFoundError('No result found'); + } + ctx.progress(60); + + const seasonTable: Season[] = JSON.parse(seasonData[1]) as Season[]; + + const currentSeason = seasonTable.find( + (seasonElement) => seasonElement.season === ctx.media.season.number && !seasonElement.blocked, + ); + + const currentEpisode = currentSeason?.episodes.find((episodeElement) => + episodeElement.episode.includes(ctx.media.episode.number.toString()), + ); + + if (!currentEpisode?.hls) throw new NotFoundError('No result found'); + + let captions: Caption[] = []; + + ctx.progress(80); + + if (currentEpisode.cc != null) { + let subtitle: Subtitle; + for (subtitle of currentEpisode.cc) { + let language = ''; + + if (subtitle.name.includes('Рус')) { + language = 'ru'; + } else if (subtitle.name.includes('Укр')) { + language = 'uk'; + } else if (subtitle.name.includes('Eng')) { + language = 'en'; + } else { + continue; + } + + captions.push({ + id: subtitle.url, + url: subtitle.url, + type: 'vtt', + language, + hasCorsRestrictions: false, + }); + } + captions = removeDuplicatedLanguages(captions); + } + + ctx.progress(95); + + return { + embeds: [], + stream: [ + { + id: 'primary', + playlist: currentEpisode.hls, + type: 'hls', + flags: [flags.CORS_ALLOWED], + captions, + }, + ], + }; + }, + async scrapeMovie(ctx) { + const playerData = await ctx.fetcher(`/embed/imdb/${ctx.media.imdbId}`, { + baseUrl: insertUnitBase, + }); + ctx.progress(35); + + const streamRegex = /hls: "([^"]*)/; + const streamData = streamRegex.exec(playerData); + + if (streamData === null || streamData[1] === null) { + throw new NotFoundError('No result found'); + } + ctx.progress(75); + + const subtitleRegex = /cc: (.*)/; + const subtitleJSONData = subtitleRegex.exec(playerData); + + let captions: Caption[] = []; + + if (subtitleJSONData != null && subtitleJSONData[1] != null) { + const subtitleData = JSON.parse(subtitleJSONData[1]); + let subtitle: Subtitle; + for (subtitle of subtitleData as Subtitle[]) { + let language = ''; + + if (subtitle.name.includes('Рус')) { + language = 'ru'; + } else if (subtitle.name.includes('Укр')) { + language = 'uk'; + } else if (subtitle.name.includes('Eng')) { + language = 'en'; + } else { + continue; + } + + captions.push({ + id: subtitle.url, + url: subtitle.url, + type: 'vtt', + language, + hasCorsRestrictions: false, + }); + } + captions = removeDuplicatedLanguages(captions); + } + + + return { + embeds: [], + stream: [ + { + id: 'primary', + type: 'hls', + playlist: streamData[1], + flags: [flags.CORS_ALLOWED], + captions, + }, + ], + }; + }, +}); diff --git a/src/providers/sources/insertunit/types.ts b/src/providers/sources/insertunit/types.ts new file mode 100644 index 0000000..0649275 --- /dev/null +++ b/src/providers/sources/insertunit/types.ts @@ -0,0 +1,30 @@ +export interface Episode { + episode: string; + id: number; + videoKey: string; + hls: string; + audio: { + names: []; + order: []; + } + cc: [] + duration: number; + title: string; + download: string; + sections: [] + poster: string; + preview: { + src: string; + } +} + +export interface Subtitle { + url: string; + name: string; +} + +export interface Season { + season: number, + blocked: boolean, + episodes: Episode[] +} \ No newline at end of file From 5b836a48396c1780d236d93b01fd6a5b106dc5ba Mon Sep 17 00:00:00 2001 From: teddyHV11 Date: Tue, 9 Apr 2024 17:44:41 +0300 Subject: [PATCH 12/30] Fix mistake done with captions Swap languages and type's place --- src/providers/sources/insertunit/insertunit.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/providers/sources/insertunit/insertunit.ts b/src/providers/sources/insertunit/insertunit.ts index 5c89663..47455ae 100644 --- a/src/providers/sources/insertunit/insertunit.ts +++ b/src/providers/sources/insertunit/insertunit.ts @@ -61,8 +61,8 @@ export const insertunitScraper = makeSourcerer({ captions.push({ id: subtitle.url, url: subtitle.url, - type: 'vtt', language, + type: 'vtt', hasCorsRestrictions: false, }); } @@ -122,14 +122,15 @@ export const insertunitScraper = makeSourcerer({ captions.push({ id: subtitle.url, url: subtitle.url, - type: 'vtt', language, + type: 'vtt', hasCorsRestrictions: false, }); } captions = removeDuplicatedLanguages(captions); } + ctx.progress(90); return { embeds: [], From 5d6b93385ea5da5a7c6481f4e90f065a604c98f5 Mon Sep 17 00:00:00 2001 From: teddyHV11 Date: Wed, 10 Apr 2024 07:24:53 +0300 Subject: [PATCH 13/30] Types adjustment, extract captions to a function --- src/providers/sources/insertunit/captions.ts | 32 +++++++++++ .../sources/insertunit/insertunit.ts | 54 +++---------------- src/providers/sources/insertunit/types.ts | 8 +-- 3 files changed, 43 insertions(+), 51 deletions(-) create mode 100644 src/providers/sources/insertunit/captions.ts diff --git a/src/providers/sources/insertunit/captions.ts b/src/providers/sources/insertunit/captions.ts new file mode 100644 index 0000000..92ac7d9 --- /dev/null +++ b/src/providers/sources/insertunit/captions.ts @@ -0,0 +1,32 @@ +import { Caption } from "@/providers/captions"; +import { Subtitle } from "./types"; + +import { removeDuplicatedLanguages } from "@/providers/captions"; + +export async function getCaptions(data: Subtitle[]) { + let captions: Caption[] = []; + let subtitle: Subtitle; + for (subtitle of data) { + let language = ''; + + if (subtitle.name.includes('Рус')) { + language = 'ru'; + } else if (subtitle.name.includes('Укр')) { + language = 'uk'; + } else if (subtitle.name.includes('Eng')) { + language = 'en'; + } else { + continue; + } + + captions.push({ + id: subtitle.url, + url: subtitle.url, + language, + type: 'vtt', + hasCorsRestrictions: false, + }); + } + captions = removeDuplicatedLanguages(captions); + return(captions) +} \ No newline at end of file diff --git a/src/providers/sources/insertunit/insertunit.ts b/src/providers/sources/insertunit/insertunit.ts index 47455ae..3cecf52 100644 --- a/src/providers/sources/insertunit/insertunit.ts +++ b/src/providers/sources/insertunit/insertunit.ts @@ -2,6 +2,7 @@ import { flags } from '@/entrypoint/utils/targets'; import { makeSourcerer } from '@/providers/base'; import { Caption, removeDuplicatedLanguages } from '@/providers/captions'; import { NotFoundError } from '@/utils/errors'; +import { getCaptions } from './captions'; import { Season, Subtitle } from './types'; @@ -38,35 +39,16 @@ export const insertunitScraper = makeSourcerer({ ); if (!currentEpisode?.hls) throw new NotFoundError('No result found'); + + let captions: Caption[] = [] - let captions: Caption[] = []; + if (currentEpisode.cc != null) { + captions = await getCaptions(currentEpisode.cc) + } ctx.progress(80); if (currentEpisode.cc != null) { - let subtitle: Subtitle; - for (subtitle of currentEpisode.cc) { - let language = ''; - - if (subtitle.name.includes('Рус')) { - language = 'ru'; - } else if (subtitle.name.includes('Укр')) { - language = 'uk'; - } else if (subtitle.name.includes('Eng')) { - language = 'en'; - } else { - continue; - } - - captions.push({ - id: subtitle.url, - url: subtitle.url, - language, - type: 'vtt', - hasCorsRestrictions: false, - }); - } - captions = removeDuplicatedLanguages(captions); } ctx.progress(95); @@ -105,29 +87,7 @@ export const insertunitScraper = makeSourcerer({ if (subtitleJSONData != null && subtitleJSONData[1] != null) { const subtitleData = JSON.parse(subtitleJSONData[1]); - let subtitle: Subtitle; - for (subtitle of subtitleData as Subtitle[]) { - let language = ''; - - if (subtitle.name.includes('Рус')) { - language = 'ru'; - } else if (subtitle.name.includes('Укр')) { - language = 'uk'; - } else if (subtitle.name.includes('Eng')) { - language = 'en'; - } else { - continue; - } - - captions.push({ - id: subtitle.url, - url: subtitle.url, - language, - type: 'vtt', - hasCorsRestrictions: false, - }); - } - captions = removeDuplicatedLanguages(captions); + captions = await getCaptions(subtitleData) } ctx.progress(90); diff --git a/src/providers/sources/insertunit/types.ts b/src/providers/sources/insertunit/types.ts index 0649275..282c3c4 100644 --- a/src/providers/sources/insertunit/types.ts +++ b/src/providers/sources/insertunit/types.ts @@ -4,14 +4,14 @@ export interface Episode { videoKey: string; hls: string; audio: { - names: []; - order: []; + names: string[]; + order: number[]; } - cc: [] + cc: Subtitle[]; duration: number; title: string; download: string; - sections: [] + sections: string[] poster: string; preview: { src: string; From ec6524389c317a29a6a8e796ff6d5052fd55a487 Mon Sep 17 00:00:00 2001 From: Jorrin Date: Wed, 10 Apr 2024 22:40:55 +0200 Subject: [PATCH 14/30] add soapertv source --- src/providers/all.ts | 2 + src/providers/sources/soapertv/index.ts | 120 ++++++++++++++++++++++++ src/providers/sources/soapertv/types.ts | 15 +++ 3 files changed, 137 insertions(+) create mode 100644 src/providers/sources/soapertv/index.ts create mode 100644 src/providers/sources/soapertv/types.ts diff --git a/src/providers/all.ts b/src/providers/all.ts index 63be995..d91af17 100644 --- a/src/providers/all.ts +++ b/src/providers/all.ts @@ -38,6 +38,7 @@ import { nepuScraper } from './sources/nepu'; import { primewireScraper } from './sources/primewire'; import { ridooMoviesScraper } from './sources/ridomovies'; import { smashyStreamScraper } from './sources/smashystream'; +import { soaperTvScraper } from './sources/soapertv'; import { vidSrcToScraper } from './sources/vidsrcto'; export function gatherAllSources(): Array { @@ -58,6 +59,7 @@ export function gatherAllSources(): Array { goojaraScraper, hdRezkaScraper, primewireScraper, + soaperTvScraper, ]; } diff --git a/src/providers/sources/soapertv/index.ts b/src/providers/sources/soapertv/index.ts new file mode 100644 index 0000000..feee555 --- /dev/null +++ b/src/providers/sources/soapertv/index.ts @@ -0,0 +1,120 @@ +import { load } from 'cheerio'; + +import { flags } from '@/entrypoint/utils/targets'; +import { Caption, labelToLanguageCode } from '@/providers/captions'; +import { MovieScrapeContext, ShowScrapeContext } from '@/utils/context'; +import { NotFoundError } from '@/utils/errors'; + +import { InfoResponse } from './types'; +import { SourcererOutput, makeSourcerer } from '../../base'; + +const baseUrl = 'https://soaper.tv'; + +const universalScraper = async (ctx: MovieScrapeContext | ShowScrapeContext): Promise => { + const searchResult = await ctx.proxiedFetcher('/search.html', { + baseUrl, + query: { + keyword: ctx.media.title, + }, + }); + const searchResult$ = load(searchResult); + let showLink = searchResult$('a') + .filter((_, el) => searchResult$(el).text() === ctx.media.title) + .attr('href'); + if (!showLink) throw new NotFoundError('Content not found'); + + if (ctx.media.type === 'show') { + const seasonNumber = ctx.media.season.number; + const episodeNumber = ctx.media.episode.number; + const showPage = await ctx.proxiedFetcher(showLink, { baseUrl }); + const showPage$ = load(showPage); + const seasonBlock = showPage$('h4') + .filter((_, el) => showPage$(el).text().trim().split(':')[0].trim() === `Season${seasonNumber}`) + .parent(); + const episodes = seasonBlock.find('a').toArray(); + showLink = showPage$( + episodes.find((el) => parseInt(showPage$(el).text().split('.')[0], 10) === episodeNumber), + ).attr('href'); + } + if (!showLink) throw new NotFoundError('Content not found'); + const contentPage = await ctx.proxiedFetcher(showLink, { baseUrl }); + const contentPage$ = load(contentPage); + + const pass = contentPage$('#hId').attr('value'); + const param = contentPage$('#divU').text(); + + if (!pass || !param) throw new NotFoundError('Content not found'); + + const formData = new URLSearchParams(); + formData.append('pass', pass); + formData.append('param', param); + formData.append('e2', '0'); + formData.append('server', '0'); + + const infoEndpoint = ctx.media.type === 'show' ? '/home/index/getEInfoAjax' : '/home/index/getMInfoAjax'; + const streamRes = await ctx.proxiedFetcher(infoEndpoint, { + baseUrl, + method: 'POST', + body: formData, + headers: { + referer: `${baseUrl}${showLink}`, + }, + }); + + const streamResJson: InfoResponse = JSON.parse(streamRes); + + const captions: Caption[] = []; + for (const sub of streamResJson.subs) { + // Some subtitles are named .srt, some are named :hi, or just + let language: string | null = ''; + if (sub.name.includes('.srt')) { + language = labelToLanguageCode(sub.name.split('.srt')[0]); + } else if (sub.name.includes(':')) { + language = sub.name.split(':')[0]; + } else { + language = sub.name; + } + if (!language) continue; + + captions.push({ + id: sub.path, + url: sub.path, + type: 'srt', + hasCorsRestrictions: false, + language, + }); + } + + return { + embeds: [], + stream: [ + { + id: 'primary', + playlist: streamResJson.val, + type: 'hls', + flags: [flags.IP_LOCKED], + captions, + }, + ...(streamResJson.val_bak + ? [ + { + id: 'backup', + playlist: streamResJson.val_bak, + type: 'hls' as const, + flags: [flags.IP_LOCKED], + captions, + }, + ] + : []), + ], + }; +}; + +export const soaperTvScraper = makeSourcerer({ + id: 'soapertv', + name: 'SoaperTV', + rank: 115, + flags: [flags.IP_LOCKED], + scrapeMovie: universalScraper, + scrapeShow: universalScraper, +}); diff --git a/src/providers/sources/soapertv/types.ts b/src/providers/sources/soapertv/types.ts new file mode 100644 index 0000000..70fb602 --- /dev/null +++ b/src/providers/sources/soapertv/types.ts @@ -0,0 +1,15 @@ +export interface Subtitle { + path: string; + name: string; +} + +export interface InfoResponse { + key: boolean; + val: string; + vtt: string; + val_bak: string; + pos: number; + type: string; + subs: Subtitle[]; + ip: string; +} From 6b038a288c7eab22bfc079b2c1c4b3eaa8c4af99 Mon Sep 17 00:00:00 2001 From: teddyHV11 Date: Thu, 11 Apr 2024 03:57:42 +0300 Subject: [PATCH 15/30] Clarity fixes --- src/providers/sources/insertunit/captions.ts | 3 +-- src/providers/sources/insertunit/insertunit.ts | 5 ----- 2 files changed, 1 insertion(+), 7 deletions(-) diff --git a/src/providers/sources/insertunit/captions.ts b/src/providers/sources/insertunit/captions.ts index 92ac7d9..74fa166 100644 --- a/src/providers/sources/insertunit/captions.ts +++ b/src/providers/sources/insertunit/captions.ts @@ -5,8 +5,7 @@ import { removeDuplicatedLanguages } from "@/providers/captions"; export async function getCaptions(data: Subtitle[]) { let captions: Caption[] = []; - let subtitle: Subtitle; - for (subtitle of data) { + for (const subtitle of data) { let language = ''; if (subtitle.name.includes('Рус')) { diff --git a/src/providers/sources/insertunit/insertunit.ts b/src/providers/sources/insertunit/insertunit.ts index 3cecf52..104deff 100644 --- a/src/providers/sources/insertunit/insertunit.ts +++ b/src/providers/sources/insertunit/insertunit.ts @@ -46,11 +46,6 @@ export const insertunitScraper = makeSourcerer({ captions = await getCaptions(currentEpisode.cc) } - ctx.progress(80); - - if (currentEpisode.cc != null) { - } - ctx.progress(95); return { From 8796b39a6312696238ec57e34ed2c9db14ec62ea Mon Sep 17 00:00:00 2001 From: teddyHV11 Date: Thu, 11 Apr 2024 04:18:38 +0300 Subject: [PATCH 16/30] Fix prettier errors --- src/providers/sources/insertunit/captions.ts | 51 +++++++++---------- .../sources/insertunit/insertunit.ts | 14 ++--- 2 files changed, 32 insertions(+), 33 deletions(-) diff --git a/src/providers/sources/insertunit/captions.ts b/src/providers/sources/insertunit/captions.ts index 74fa166..881c9c2 100644 --- a/src/providers/sources/insertunit/captions.ts +++ b/src/providers/sources/insertunit/captions.ts @@ -1,31 +1,30 @@ -import { Caption } from "@/providers/captions"; -import { Subtitle } from "./types"; +import { Caption, removeDuplicatedLanguages } from '@/providers/captions'; -import { removeDuplicatedLanguages } from "@/providers/captions"; +import { Subtitle } from './types'; export async function getCaptions(data: Subtitle[]) { - let captions: Caption[] = []; - for (const subtitle of data) { - let language = ''; + let captions: Caption[] = []; + for (const subtitle of data) { + let language = ''; - if (subtitle.name.includes('Рус')) { - language = 'ru'; - } else if (subtitle.name.includes('Укр')) { - language = 'uk'; - } else if (subtitle.name.includes('Eng')) { - language = 'en'; - } else { - continue; - } - - captions.push({ - id: subtitle.url, - url: subtitle.url, - language, - type: 'vtt', - hasCorsRestrictions: false, - }); + if (subtitle.name.includes('Рус')) { + language = 'ru'; + } else if (subtitle.name.includes('Укр')) { + language = 'uk'; + } else if (subtitle.name.includes('Eng')) { + language = 'en'; + } else { + continue; } - captions = removeDuplicatedLanguages(captions); - return(captions) -} \ No newline at end of file + + captions.push({ + id: subtitle.url, + url: subtitle.url, + language, + type: 'vtt', + hasCorsRestrictions: false, + }); + } + captions = removeDuplicatedLanguages(captions); + return captions; +} diff --git a/src/providers/sources/insertunit/insertunit.ts b/src/providers/sources/insertunit/insertunit.ts index 104deff..9a54866 100644 --- a/src/providers/sources/insertunit/insertunit.ts +++ b/src/providers/sources/insertunit/insertunit.ts @@ -1,10 +1,10 @@ import { flags } from '@/entrypoint/utils/targets'; import { makeSourcerer } from '@/providers/base'; -import { Caption, removeDuplicatedLanguages } from '@/providers/captions'; +import { Caption } from '@/providers/captions'; import { NotFoundError } from '@/utils/errors'; -import { getCaptions } from './captions'; -import { Season, Subtitle } from './types'; +import { getCaptions } from './captions'; +import { Season } from './types'; const insertUnitBase = 'https://api.insertunit.ws/'; @@ -39,11 +39,11 @@ export const insertunitScraper = makeSourcerer({ ); if (!currentEpisode?.hls) throw new NotFoundError('No result found'); - - let captions: Caption[] = [] + + let captions: Caption[] = []; if (currentEpisode.cc != null) { - captions = await getCaptions(currentEpisode.cc) + captions = await getCaptions(currentEpisode.cc); } ctx.progress(95); @@ -82,7 +82,7 @@ export const insertunitScraper = makeSourcerer({ if (subtitleJSONData != null && subtitleJSONData[1] != null) { const subtitleData = JSON.parse(subtitleJSONData[1]); - captions = await getCaptions(subtitleData) + captions = await getCaptions(subtitleData); } ctx.progress(90); From 8281c3141aa04c4444ec44ad120527e827e3cf72 Mon Sep 17 00:00:00 2001 From: teddyHV11 Date: Thu, 11 Apr 2024 06:31:53 +0300 Subject: [PATCH 17/30] Rename insertunit.ts to index.ts --- src/providers/all.ts | 2 +- src/providers/sources/insertunit/{insertunit.ts => index.ts} | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename src/providers/sources/insertunit/{insertunit.ts => index.ts} (100%) diff --git a/src/providers/all.ts b/src/providers/all.ts index 675aa36..887290d 100644 --- a/src/providers/all.ts +++ b/src/providers/all.ts @@ -14,7 +14,7 @@ import { vidsrcembedScraper } from '@/providers/embeds/vidsrc'; import { vTubeScraper } from '@/providers/embeds/vtube'; import { flixhqScraper } from '@/providers/sources/flixhq/index'; import { goMoviesScraper } from '@/providers/sources/gomovies/index'; -import { insertunitScraper } from '@/providers/sources/insertunit/insertunit'; +import { insertunitScraper } from '@/providers/sources/insertunit'; import { kissAsianScraper } from '@/providers/sources/kissasian/index'; import { lookmovieScraper } from '@/providers/sources/lookmovie'; import { remotestreamScraper } from '@/providers/sources/remotestream'; diff --git a/src/providers/sources/insertunit/insertunit.ts b/src/providers/sources/insertunit/index.ts similarity index 100% rename from src/providers/sources/insertunit/insertunit.ts rename to src/providers/sources/insertunit/index.ts From 9e06035e27ca6724b76755993271e5761c282950 Mon Sep 17 00:00:00 2001 From: Ciarands <74070993+Ciarands@users.noreply.github.com> Date: Thu, 11 Apr 2024 12:03:04 +0100 Subject: [PATCH 18/30] Update RCP base url --- src/providers/sources/vidsrc/common.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/providers/sources/vidsrc/common.ts b/src/providers/sources/vidsrc/common.ts index 4ccc93c..6f6fd71 100644 --- a/src/providers/sources/vidsrc/common.ts +++ b/src/providers/sources/vidsrc/common.ts @@ -1,2 +1,2 @@ export const vidsrcBase = 'https://vidsrc.me'; -export const vidsrcRCPBase = 'https://rcp.vidsrc.me'; +export const vidsrcRCPBase = 'https://vidsrc.stream'; From 125021e4325d6c86ff2883a6b155b8e95de4eacb Mon Sep 17 00:00:00 2001 From: Ciarands <74070993+Ciarands@users.noreply.github.com> Date: Thu, 11 Apr 2024 12:08:03 +0100 Subject: [PATCH 19/30] remove CORS_ALLOWED flag --- src/providers/sources/vidsrc/index.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/providers/sources/vidsrc/index.ts b/src/providers/sources/vidsrc/index.ts index d6b15a8..3046fb8 100644 --- a/src/providers/sources/vidsrc/index.ts +++ b/src/providers/sources/vidsrc/index.ts @@ -1,4 +1,3 @@ -import { flags } from '@/entrypoint/utils/targets'; import { makeSourcerer } from '@/providers/base'; import { scrapeMovie } from '@/providers/sources/vidsrc/scrape-movie'; import { scrapeShow } from '@/providers/sources/vidsrc/scrape-show'; @@ -7,7 +6,7 @@ export const vidsrcScraper = makeSourcerer({ id: 'vidsrc', name: 'VidSrc', rank: 90, - flags: [flags.CORS_ALLOWED], + flags: [], scrapeMovie, scrapeShow, }); From 61952b0e87f0ca67f6ed1f32d7514aa0e2f2bd52 Mon Sep 17 00:00:00 2001 From: Ciarands <74070993+Ciarands@users.noreply.github.com> Date: Thu, 11 Apr 2024 12:08:31 +0100 Subject: [PATCH 20/30] Remove CORS_ALLOWED flag and add headers --- src/providers/embeds/vidsrc.ts | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/providers/embeds/vidsrc.ts b/src/providers/embeds/vidsrc.ts index ae945d2..716c82c 100644 --- a/src/providers/embeds/vidsrc.ts +++ b/src/providers/embeds/vidsrc.ts @@ -1,5 +1,5 @@ -import { flags } from '@/entrypoint/utils/targets'; import { makeEmbed } from '@/providers/base'; +import { vidsrcRCPBase } from '@/providers/sources/vidsrc/common'; const hlsURLRegex = /file:"(.*?)"/; const setPassRegex = /var pass_path = "(.*set_pass\.php.*)";/; @@ -56,7 +56,11 @@ export const vidsrcembedScraper = makeEmbed({ id: 'primary', type: 'hls', playlist: finalUrl, - flags: [flags.CORS_ALLOWED], + headers: { + Referer: vidsrcRCPBase, + Origin: vidsrcRCPBase, + }, + flags: [], captions: [], }, ], From c17b74c8ba5b333f7057030123ed0e07dde242d3 Mon Sep 17 00:00:00 2001 From: lonelil <51315646+lonelil@users.noreply.github.com> Date: Thu, 11 Apr 2024 19:23:46 +0800 Subject: [PATCH 21/30] use worker proxy --- src/providers/embeds/warezcdn/common.ts | 2 ++ src/providers/embeds/warezcdn/mp4.ts | 12 +++++------- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/providers/embeds/warezcdn/common.ts b/src/providers/embeds/warezcdn/common.ts index 3a18bb6..47dfed0 100644 --- a/src/providers/embeds/warezcdn/common.ts +++ b/src/providers/embeds/warezcdn/common.ts @@ -53,3 +53,5 @@ export async function getDecryptedId(ctx: EmbedScrapeContext) { return decryptedId; } + +export const warezcdnWorkerProxy = 'https://workerproxy.warezcdn.workers.dev'; diff --git a/src/providers/embeds/warezcdn/mp4.ts b/src/providers/embeds/warezcdn/mp4.ts index 53b85dd..d4a5cff 100644 --- a/src/providers/embeds/warezcdn/mp4.ts +++ b/src/providers/embeds/warezcdn/mp4.ts @@ -2,7 +2,7 @@ import { makeEmbed } from '@/providers/base'; import { EmbedScrapeContext } from '@/utils/context'; import { NotFoundError } from '@/utils/errors'; -import { getDecryptedId } from './common'; +import { getDecryptedId, warezcdnWorkerProxy } from './common'; const cdnListing = [50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64]; @@ -24,7 +24,7 @@ export const warezcdnembedMp4Scraper = makeEmbed({ id: 'warezcdnembedmp4', // WarezCDN is both a source and an embed host name: 'WarezCDN MP4', rank: 83, - disabled: true, // crashes movie-web player, disabling for now + disabled: false, async scrape(ctx) { const decryptedId = await getDecryptedId(ctx); @@ -42,15 +42,13 @@ export const warezcdnembedMp4Scraper = makeEmbed({ qualities: { unknown: { type: 'mp4', - url: streamUrl, + url: `${warezcdnWorkerProxy}/?${new URLSearchParams({ + url: streamUrl, + })}`, }, }, type: 'file', flags: [], - headers: { - Origin: 'https://cloud.mail.ru', - Referer: 'https://cloud.mail.ru/', - }, }, ], }; From 9ba05ab99109d9b6b7efb48b30373acf58eb0187 Mon Sep 17 00:00:00 2001 From: lonelil <51315646+lonelil@users.noreply.github.com> Date: Thu, 11 Apr 2024 19:29:35 +0800 Subject: [PATCH 22/30] add types --- src/providers/embeds/warezcdn/common.ts | 2 +- src/providers/embeds/warezcdn/hls.ts | 2 +- src/providers/sources/warezcdn/index.ts | 5 +++-- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/providers/embeds/warezcdn/common.ts b/src/providers/embeds/warezcdn/common.ts index 47dfed0..5c944d7 100644 --- a/src/providers/embeds/warezcdn/common.ts +++ b/src/providers/embeds/warezcdn/common.ts @@ -35,7 +35,7 @@ export async function getDecryptedId(ctx: EmbedScrapeContext) { const allowanceKey = page.match(/let allowanceKey = "(.*?)";/)?.[1]; if (!allowanceKey) throw new NotFoundError('Failed to get allowanceKey'); - const streamData = await ctx.proxiedFetcher('/functions.php', { + const streamData = await ctx.proxiedFetcher('/functions.php', { baseUrl: warezcdnPlayerBase, method: 'POST', body: new URLSearchParams({ diff --git a/src/providers/embeds/warezcdn/hls.ts b/src/providers/embeds/warezcdn/hls.ts index 827bf94..1fbbef3 100644 --- a/src/providers/embeds/warezcdn/hls.ts +++ b/src/providers/embeds/warezcdn/hls.ts @@ -7,7 +7,7 @@ import { getDecryptedId } from './common'; // Method found by atpn async function getVideowlUrlStream(ctx: EmbedScrapeContext, decryptedId: string) { - const sharePage = await ctx.proxiedFetcher('https://cloud.mail.ru/public/uaRH/2PYWcJRpH'); + const sharePage = await ctx.proxiedFetcher('https://cloud.mail.ru/public/uaRH/2PYWcJRpH'); const regex = /"videowl_view":\{"count":"(\d+)","url":"([^"]+)"\}/g; const videowlUrl = regex.exec(sharePage)?.[2]; diff --git a/src/providers/sources/warezcdn/index.ts b/src/providers/sources/warezcdn/index.ts index 6925855..e504e41 100644 --- a/src/providers/sources/warezcdn/index.ts +++ b/src/providers/sources/warezcdn/index.ts @@ -16,7 +16,7 @@ const universalScraper = async (ctx: MovieScrapeContext | ShowScrapeContext) => if (ctx.media.type === 'show') id = `serie/${ctx.media.imdbId}/${ctx.media.season.number}/${ctx.media.episode.number}`; - const serversPage = await ctx.proxiedFetcher(`/${id}`, { + const serversPage = await ctx.proxiedFetcher(`/${id}`, { baseUrl: warezcdnBase, }); const $ = load(serversPage); @@ -34,7 +34,7 @@ const universalScraper = async (ctx: MovieScrapeContext | ShowScrapeContext) => id: embedUrl, sv: 'mixdrop', }); - const realUrl = await ctx.proxiedFetcher(`/getPlay.php?${params}`, { + const realUrl = await ctx.proxiedFetcher(`/getPlay.php?${params}`, { baseUrl: warezcdnApiBase, headers: { Referer: `${warezcdnApiBase}/getEmbed.php?${params}`, @@ -42,6 +42,7 @@ const universalScraper = async (ctx: MovieScrapeContext | ShowScrapeContext) => }); const realEmbedUrl = realUrl.match(/window\.location\.href="([^"]*)";/); + if (!realEmbedUrl) throw new Error('Could not find embed url'); embeds.push({ embedId: mixdropScraper.id, url: realEmbedUrl[1], From cffbc844e0e65035aa3750705f29bfbefd5d5f38 Mon Sep 17 00:00:00 2001 From: lonelil <51315646+lonelil@users.noreply.github.com> Date: Thu, 11 Apr 2024 22:06:53 +0800 Subject: [PATCH 23/30] complete requested changes --- src/providers/embeds/warezcdn/common.ts | 7 ++++--- src/providers/embeds/warezcdn/mp4.ts | 3 ++- src/providers/sources/warezcdn/common.ts | 1 + src/providers/sources/warezcdn/index.ts | 13 +++++++------ 4 files changed, 14 insertions(+), 10 deletions(-) diff --git a/src/providers/embeds/warezcdn/common.ts b/src/providers/embeds/warezcdn/common.ts index 5c944d7..2762ed8 100644 --- a/src/providers/embeds/warezcdn/common.ts +++ b/src/providers/embeds/warezcdn/common.ts @@ -23,7 +23,7 @@ function decrypt(input: string) { } export async function getDecryptedId(ctx: EmbedScrapeContext) { - const page = await ctx.proxiedFetcher(`/player.php?${new URLSearchParams({ id: ctx.url })}`, { + const page = await ctx.proxiedFetcher(`/player.php`, { baseUrl: warezcdnPlayerBase, headers: { Referer: `${warezcdnPlayerBase}/getEmbed.php?${new URLSearchParams({ @@ -31,6 +31,9 @@ export async function getDecryptedId(ctx: EmbedScrapeContext) { sv: 'warezcdn', })}`, }, + query: { + id: ctx.url, + }, }); const allowanceKey = page.match(/let allowanceKey = "(.*?)";/)?.[1]; if (!allowanceKey) throw new NotFoundError('Failed to get allowanceKey'); @@ -53,5 +56,3 @@ export async function getDecryptedId(ctx: EmbedScrapeContext) { return decryptedId; } - -export const warezcdnWorkerProxy = 'https://workerproxy.warezcdn.workers.dev'; diff --git a/src/providers/embeds/warezcdn/mp4.ts b/src/providers/embeds/warezcdn/mp4.ts index d4a5cff..caac7ae 100644 --- a/src/providers/embeds/warezcdn/mp4.ts +++ b/src/providers/embeds/warezcdn/mp4.ts @@ -1,8 +1,9 @@ import { makeEmbed } from '@/providers/base'; +import { warezcdnWorkerProxy } from '@/providers/sources/warezcdn/common'; import { EmbedScrapeContext } from '@/utils/context'; import { NotFoundError } from '@/utils/errors'; -import { getDecryptedId, warezcdnWorkerProxy } from './common'; +import { getDecryptedId } from './common'; const cdnListing = [50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64]; diff --git a/src/providers/sources/warezcdn/common.ts b/src/providers/sources/warezcdn/common.ts index 5fcde55..83e94bc 100644 --- a/src/providers/sources/warezcdn/common.ts +++ b/src/providers/sources/warezcdn/common.ts @@ -1,3 +1,4 @@ export const warezcdnBase = 'https://embed.warezcdn.com'; export const warezcdnApiBase = 'https://warezcdn.com/embed'; export const warezcdnPlayerBase = 'https://warezcdn.com/player'; +export const warezcdnWorkerProxy = 'https://workerproxy.warezcdn.workers.dev'; diff --git a/src/providers/sources/warezcdn/index.ts b/src/providers/sources/warezcdn/index.ts index e504e41..bad2438 100644 --- a/src/providers/sources/warezcdn/index.ts +++ b/src/providers/sources/warezcdn/index.ts @@ -1,6 +1,6 @@ import { load } from 'cheerio'; -import { SourcererEmbed, SourcererOutput, makeSourcerer } from '@/providers/base'; +import { SourcererEmbed, makeSourcerer } from '@/providers/base'; import { mixdropScraper } from '@/providers/embeds/mixdrop'; import { warezcdnembedHlsScraper } from '@/providers/embeds/warezcdn/hls'; import { warezcdnembedMp4Scraper } from '@/providers/embeds/warezcdn/mp4'; @@ -30,15 +30,16 @@ const universalScraper = async (ctx: MovieScrapeContext | ShowScrapeContext) => const embedUrl = $(element).attr('data-load-embed')!; if (embedHost === 'mixdrop') { - const params = new URLSearchParams({ + const params = { id: embedUrl, sv: 'mixdrop', - }); - const realUrl = await ctx.proxiedFetcher(`/getPlay.php?${params}`, { + }; + const realUrl = await ctx.proxiedFetcher(`/getPlay.php`, { baseUrl: warezcdnApiBase, headers: { - Referer: `${warezcdnApiBase}/getEmbed.php?${params}`, + Referer: `${warezcdnApiBase}/getEmbed.php?${new URLSearchParams(params)}`, }, + query: params, }); const realEmbedUrl = realUrl.match(/window\.location\.href="([^"]*)";/); @@ -63,7 +64,7 @@ const universalScraper = async (ctx: MovieScrapeContext | ShowScrapeContext) => return { embeds, - } as SourcererOutput; + }; }; export const warezcdnScraper = makeSourcerer({ From 6a0ac52908f178306faf41f29273e1bae6bedf6a Mon Sep 17 00:00:00 2001 From: teddyHV11 Date: Thu, 11 Apr 2024 17:49:32 +0300 Subject: [PATCH 24/30] Fix prettier errors --- src/providers/sources/insertunit/types.ts | 50 +++++++++++------------ 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/src/providers/sources/insertunit/types.ts b/src/providers/sources/insertunit/types.ts index 282c3c4..587ae36 100644 --- a/src/providers/sources/insertunit/types.ts +++ b/src/providers/sources/insertunit/types.ts @@ -1,30 +1,30 @@ -export interface Episode { - episode: string; - id: number; - videoKey: string; - hls: string; - audio: { - names: string[]; - order: number[]; - } - cc: Subtitle[]; - duration: number; - title: string; - download: string; - sections: string[] - poster: string; - preview: { - src: string; - } +export interface Subtitle { + url: string; + name: string; } -export interface Subtitle { - url: string; - name: string; +export interface Episode { + episode: string; + id: number; + videoKey: string; + hls: string; + audio: { + names: string[]; + order: number[]; + }; + cc: Subtitle[]; + duration: number; + title: string; + download: string; + sections: string[]; + poster: string; + preview: { + src: string; + }; } export interface Season { - season: number, - blocked: boolean, - episodes: Episode[] -} \ No newline at end of file + season: number; + blocked: boolean; + episodes: Episode[]; +} From 089c6aa6ce08b962c965d2c96d8ea555e0a577fe Mon Sep 17 00:00:00 2001 From: lonelil <51315646+lonelil@users.noreply.github.com> Date: Fri, 12 Apr 2024 01:11:29 +0800 Subject: [PATCH 25/30] add flag and change rank --- src/providers/embeds/warezcdn/hls.ts | 2 +- src/providers/embeds/warezcdn/mp4.ts | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/providers/embeds/warezcdn/hls.ts b/src/providers/embeds/warezcdn/hls.ts index 1fbbef3..2e49852 100644 --- a/src/providers/embeds/warezcdn/hls.ts +++ b/src/providers/embeds/warezcdn/hls.ts @@ -21,7 +21,7 @@ async function getVideowlUrlStream(ctx: EmbedScrapeContext, decryptedId: string) export const warezcdnembedHlsScraper = makeEmbed({ id: 'warezcdnembedhls', // WarezCDN is both a source and an embed host name: 'WarezCDN HLS', - rank: 82, + rank: 83, async scrape(ctx) { const decryptedId = await getDecryptedId(ctx); diff --git a/src/providers/embeds/warezcdn/mp4.ts b/src/providers/embeds/warezcdn/mp4.ts index caac7ae..ada781d 100644 --- a/src/providers/embeds/warezcdn/mp4.ts +++ b/src/providers/embeds/warezcdn/mp4.ts @@ -1,3 +1,4 @@ +import { flags } from '@/entrypoint/utils/targets'; import { makeEmbed } from '@/providers/base'; import { warezcdnWorkerProxy } from '@/providers/sources/warezcdn/common'; import { EmbedScrapeContext } from '@/utils/context'; @@ -24,7 +25,7 @@ async function checkUrls(ctx: EmbedScrapeContext, fileId: string) { export const warezcdnembedMp4Scraper = makeEmbed({ id: 'warezcdnembedmp4', // WarezCDN is both a source and an embed host name: 'WarezCDN MP4', - rank: 83, + rank: 82, disabled: false, async scrape(ctx) { const decryptedId = await getDecryptedId(ctx); @@ -49,7 +50,7 @@ export const warezcdnembedMp4Scraper = makeEmbed({ }, }, type: 'file', - flags: [], + flags: [flags.CORS_ALLOWED], }, ], }; From 94bfcd0c316ede250bd917eda575d1a183da289c Mon Sep 17 00:00:00 2001 From: Jorrin Date: Thu, 11 Apr 2024 20:42:37 +0200 Subject: [PATCH 26/30] skip validation check for warezcdnmp4 --- src/providers/sources/warezcdn/index.ts | 3 ++- src/runners/individualRunner.ts | 4 ++-- src/runners/runner.ts | 4 ++-- src/utils/valid.ts | 11 ++++++++++- 4 files changed, 16 insertions(+), 6 deletions(-) diff --git a/src/providers/sources/warezcdn/index.ts b/src/providers/sources/warezcdn/index.ts index bad2438..1c9ad00 100644 --- a/src/providers/sources/warezcdn/index.ts +++ b/src/providers/sources/warezcdn/index.ts @@ -1,5 +1,6 @@ import { load } from 'cheerio'; +import { flags } from '@/entrypoint/utils/targets'; import { SourcererEmbed, makeSourcerer } from '@/providers/base'; import { mixdropScraper } from '@/providers/embeds/mixdrop'; import { warezcdnembedHlsScraper } from '@/providers/embeds/warezcdn/hls'; @@ -71,7 +72,7 @@ export const warezcdnScraper = makeSourcerer({ id: 'warezcdn', name: 'WarezCDN', rank: 81, - flags: [], + flags: [flags.CORS_ALLOWED], scrapeMovie: universalScraper, scrapeShow: universalScraper, }); diff --git a/src/runners/individualRunner.ts b/src/runners/individualRunner.ts index 6c7e452..b309180 100644 --- a/src/runners/individualRunner.ts +++ b/src/runners/individualRunner.ts @@ -71,7 +71,7 @@ export async function scrapeInvidualSource( // only check for playable streams if there are streams, and if there are no embeds if (output.stream && output.stream.length > 0 && output.embeds.length === 0) { - const playableStreams = await validatePlayableStreams(output.stream, ops); + const playableStreams = await validatePlayableStreams(output.stream, ops, sourceScraper.id); if (playableStreams.length === 0) throw new NotFoundError('No playable streams found'); output.stream = playableStreams; } @@ -112,7 +112,7 @@ export async function scrapeIndividualEmbed( .filter((stream) => flagsAllowedInFeatures(ops.features, stream.flags)); if (output.stream.length === 0) throw new NotFoundError('No streams found'); - const playableStreams = await validatePlayableStreams(output.stream, ops); + const playableStreams = await validatePlayableStreams(output.stream, ops, embedScraper.id); if (playableStreams.length === 0) throw new NotFoundError('No playable streams found'); output.stream = playableStreams; diff --git a/src/runners/runner.ts b/src/runners/runner.ts index c3bc9eb..c5f5de3 100644 --- a/src/runners/runner.ts +++ b/src/runners/runner.ts @@ -104,7 +104,7 @@ export async function runAllProviders(list: ProviderList, ops: ProviderRunnerOpt // return stream is there are any if (output.stream?.[0]) { - const playableStream = await validatePlayableStream(output.stream[0], ops); + const playableStream = await validatePlayableStream(output.stream[0], ops, source.id); if (!playableStream) throw new NotFoundError('No streams found'); return { sourceId: source.id, @@ -151,7 +151,7 @@ export async function runAllProviders(list: ProviderList, ops: ProviderRunnerOpt if (embedOutput.stream.length === 0) { throw new NotFoundError('No streams found'); } - const playableStream = await validatePlayableStream(embedOutput.stream[0], ops); + const playableStream = await validatePlayableStream(embedOutput.stream[0], ops, embed.embedId); if (!playableStream) throw new NotFoundError('No streams found'); embedOutput.stream = [playableStream]; } catch (error) { diff --git a/src/utils/valid.ts b/src/utils/valid.ts index fb9ef0f..e4ea664 100644 --- a/src/utils/valid.ts +++ b/src/utils/valid.ts @@ -1,7 +1,10 @@ +import { warezcdnembedMp4Scraper } from '@/providers/embeds/warezcdn/mp4'; import { Stream } from '@/providers/streams'; import { IndividualEmbedRunnerOptions } from '@/runners/individualRunner'; import { ProviderRunnerOptions } from '@/runners/runner'; +const SKIP_VALIDATION_CHECK_IDS = [warezcdnembedMp4Scraper.id]; + export function isValidStream(stream: Stream | undefined): boolean { if (!stream) return false; if (stream.type === 'hls') { @@ -21,7 +24,10 @@ export function isValidStream(stream: Stream | undefined): boolean { export async function validatePlayableStream( stream: Stream, ops: ProviderRunnerOptions | IndividualEmbedRunnerOptions, + sourcererId: string, ): Promise { + if (SKIP_VALIDATION_CHECK_IDS.includes(sourcererId)) return stream; + if (stream.type === 'hls') { const result = await ops.proxiedFetcher.full(stream.playlist, { method: 'GET', @@ -63,8 +69,11 @@ export async function validatePlayableStream( export async function validatePlayableStreams( streams: Stream[], ops: ProviderRunnerOptions | IndividualEmbedRunnerOptions, + sourcererId: string, ): Promise { - return (await Promise.all(streams.map((stream) => validatePlayableStream(stream, ops)))).filter( + if (SKIP_VALIDATION_CHECK_IDS.includes(sourcererId)) return streams; + + return (await Promise.all(streams.map((stream) => validatePlayableStream(stream, ops, sourcererId)))).filter( (v) => v !== null, ) as Stream[]; } From 79a8fdca6d591070ef7b3bf7d04144a8ef9f91fb Mon Sep 17 00:00:00 2001 From: lonelil <51315646+lonelil@users.noreply.github.com> Date: Fri, 12 Apr 2024 16:56:14 +0800 Subject: [PATCH 27/30] fix series --- src/providers/sources/warezcdn/common.ts | 22 ++++ src/providers/sources/warezcdn/index.ts | 160 ++++++++++++++--------- src/providers/sources/warezcdn/types.ts | 16 +++ 3 files changed, 136 insertions(+), 62 deletions(-) create mode 100644 src/providers/sources/warezcdn/types.ts diff --git a/src/providers/sources/warezcdn/common.ts b/src/providers/sources/warezcdn/common.ts index 83e94bc..56d23e8 100644 --- a/src/providers/sources/warezcdn/common.ts +++ b/src/providers/sources/warezcdn/common.ts @@ -1,4 +1,26 @@ +import { ScrapeContext } from '@/utils/context'; + export const warezcdnBase = 'https://embed.warezcdn.com'; export const warezcdnApiBase = 'https://warezcdn.com/embed'; export const warezcdnPlayerBase = 'https://warezcdn.com/player'; export const warezcdnWorkerProxy = 'https://workerproxy.warezcdn.workers.dev'; + +export async function getExternalPlayerUrl(ctx: ScrapeContext, embedId: string, embedUrl: string) { + const params = { + id: embedUrl, + sv: embedId, + }; + const realUrl = await ctx.proxiedFetcher(`/getPlay.php`, { + baseUrl: warezcdnApiBase, + headers: { + Referer: `${warezcdnApiBase}/getEmbed.php?${new URLSearchParams(params)}`, + 'User-Agent': + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36', + }, + query: params, + }); + + const realEmbedUrl = realUrl.match(/window\.location\.href="([^"]*)";/); + if (!realEmbedUrl) throw new Error('Could not find embed url'); + return realEmbedUrl[1]; +} diff --git a/src/providers/sources/warezcdn/index.ts b/src/providers/sources/warezcdn/index.ts index 1c9ad00..f27b052 100644 --- a/src/providers/sources/warezcdn/index.ts +++ b/src/providers/sources/warezcdn/index.ts @@ -5,74 +5,110 @@ import { SourcererEmbed, makeSourcerer } from '@/providers/base'; import { mixdropScraper } from '@/providers/embeds/mixdrop'; import { warezcdnembedHlsScraper } from '@/providers/embeds/warezcdn/hls'; import { warezcdnembedMp4Scraper } from '@/providers/embeds/warezcdn/mp4'; -import { MovieScrapeContext, ShowScrapeContext } from '@/utils/context'; import { NotFoundError } from '@/utils/errors'; -import { warezcdnApiBase, warezcdnBase } from './common'; - -const universalScraper = async (ctx: MovieScrapeContext | ShowScrapeContext) => { - if (!ctx.media.imdbId) throw new NotFoundError('This source requires IMDB id.'); - - let id = `filme/${ctx.media.imdbId}`; - if (ctx.media.type === 'show') - id = `serie/${ctx.media.imdbId}/${ctx.media.season.number}/${ctx.media.episode.number}`; - - const serversPage = await ctx.proxiedFetcher(`/${id}`, { - baseUrl: warezcdnBase, - }); - const $ = load(serversPage); - - const embedsHost = $('.hostList.active [data-load-embed]').get(); - - const embeds: SourcererEmbed[] = []; - - embedsHost.forEach(async (element) => { - const embedHost = $(element).attr('data-load-embed-host')!; - const embedUrl = $(element).attr('data-load-embed')!; - - if (embedHost === 'mixdrop') { - const params = { - id: embedUrl, - sv: 'mixdrop', - }; - const realUrl = await ctx.proxiedFetcher(`/getPlay.php`, { - baseUrl: warezcdnApiBase, - headers: { - Referer: `${warezcdnApiBase}/getEmbed.php?${new URLSearchParams(params)}`, - }, - query: params, - }); - - const realEmbedUrl = realUrl.match(/window\.location\.href="([^"]*)";/); - if (!realEmbedUrl) throw new Error('Could not find embed url'); - embeds.push({ - embedId: mixdropScraper.id, - url: realEmbedUrl[1], - }); - } else if (embedHost === 'warezcdn') { - embeds.push( - { - embedId: warezcdnembedHlsScraper.id, - url: embedUrl, - }, - { - embedId: warezcdnembedMp4Scraper.id, - url: embedUrl, - }, - ); - } - }); - - return { - embeds, - }; -}; +import { getExternalPlayerUrl, warezcdnBase } from './common'; +import { SerieAjaxResponse } from './types'; export const warezcdnScraper = makeSourcerer({ id: 'warezcdn', name: 'WarezCDN', rank: 81, flags: [flags.CORS_ALLOWED], - scrapeMovie: universalScraper, - scrapeShow: universalScraper, + scrapeMovie: async (ctx) => { + if (!ctx.media.imdbId) throw new NotFoundError('This source requires IMDB id.'); + + const serversPage = await ctx.proxiedFetcher(`/filme/${ctx.media.imdbId}`, { + baseUrl: warezcdnBase, + }); + const $ = load(serversPage); + + const embedsHost = $('.hostList.active [data-load-embed]').get(); + + const embeds: SourcererEmbed[] = []; + + embedsHost.forEach(async (element) => { + const embedHost = $(element).attr('data-load-embed-host')!; + const embedUrl = $(element).attr('data-load-embed')!; + + if (embedHost === 'mixdrop') { + const realEmbedUrl = await getExternalPlayerUrl(ctx, 'mixdrop', embedUrl); + if (!realEmbedUrl) throw new Error('Could not find embed url'); + embeds.push({ + embedId: mixdropScraper.id, + url: realEmbedUrl, + }); + } else if (embedHost === 'warezcdn') { + embeds.push( + { + embedId: warezcdnembedHlsScraper.id, + url: embedUrl, + }, + { + embedId: warezcdnembedMp4Scraper.id, + url: embedUrl, + }, + ); + } + }); + + return { + embeds, + }; + }, + scrapeShow: async (ctx) => { + if (!ctx.media.imdbId) throw new NotFoundError('This source requires IMDB id.'); + + const url = `${warezcdnBase}/serie/${ctx.media.imdbId}/${ctx.media.season.number}/${ctx.media.episode.number}`; + + const serversPage = await ctx.proxiedFetcher(url); + + const episodeId = serversPage.match(/\$\('\[data-load-episode-content="(\d+)"\]'\)/)?.[1]; + + if (!episodeId) throw new NotFoundError('Failed to find episode id'); + + const streamsData = await ctx.proxiedFetcher(`/serieAjax.php`, { + method: 'POST', + baseUrl: warezcdnBase, + body: new URLSearchParams({ + getAudios: episodeId, + }), + headers: { + Origin: warezcdnBase, + Referer: url, + 'X-Requested-With': 'XMLHttpRequest', + }, + }); + + const streams: SerieAjaxResponse = JSON.parse(streamsData); + const list = streams.list['0']; + const embeds: SourcererEmbed[] = []; + + // 3 means ok + if (list.mixdropStatus === '3') { + const realEmbedUrl = await getExternalPlayerUrl(ctx, 'mixdrop', list.id); + if (!realEmbedUrl) throw new Error('Could not find embed url'); + embeds.push({ + embedId: mixdropScraper.id, + url: realEmbedUrl, + }); + } + + if (list.warezcdnStatus === '3') { + embeds.push( + { + embedId: warezcdnembedHlsScraper.id, + url: list.id, + }, + { + embedId: warezcdnembedMp4Scraper.id, + url: list.id, + }, + ); + } + + return { + embeds, + }; + }, }); diff --git a/src/providers/sources/warezcdn/types.ts b/src/providers/sources/warezcdn/types.ts new file mode 100644 index 0000000..38711ff --- /dev/null +++ b/src/providers/sources/warezcdn/types.ts @@ -0,0 +1,16 @@ +interface Data { + id: string; + audio: string; + mixdropStatus: string; + fembedStatus: string; + streamtapeStatus: string; + warezcdnStatus: string; +} + +type List = { + [key: string]: Data; +}; + +export interface SerieAjaxResponse { + list: List; +} From 0a2259b1f4a8d6c67b6e031256b9433b966b41c9 Mon Sep 17 00:00:00 2001 From: lonelil <51315646+lonelil@users.noreply.github.com> Date: Fri, 12 Apr 2024 16:59:51 +0800 Subject: [PATCH 28/30] remove user-agent --- src/providers/sources/warezcdn/common.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/providers/sources/warezcdn/common.ts b/src/providers/sources/warezcdn/common.ts index 56d23e8..182b2b6 100644 --- a/src/providers/sources/warezcdn/common.ts +++ b/src/providers/sources/warezcdn/common.ts @@ -14,8 +14,6 @@ export async function getExternalPlayerUrl(ctx: ScrapeContext, embedId: string, baseUrl: warezcdnApiBase, headers: { Referer: `${warezcdnApiBase}/getEmbed.php?${new URLSearchParams(params)}`, - 'User-Agent': - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36', }, query: params, }); From 8de7e61eabc47b326a2230406a7c2fde55a9cfe6 Mon Sep 17 00:00:00 2001 From: Jorrin Date: Fri, 12 Apr 2024 17:42:44 +0200 Subject: [PATCH 29/30] bump version + changelog --- .docs/content/1.get-started/4.changelog.md | 5 +++++ package.json | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/.docs/content/1.get-started/4.changelog.md b/.docs/content/1.get-started/4.changelog.md index 0836b31..d8e727f 100644 --- a/.docs/content/1.get-started/4.changelog.md +++ b/.docs/content/1.get-started/4.changelog.md @@ -2,6 +2,11 @@ title: 'Changelog' --- +# Version 2.3.0 +- Fixed RidoMovies search results +- Added Insertunit, SoaperTV, and WarezCDN providers +- Disabled Showbox + # Version 2.2.9 - Fixed VidSrcTo (both Vidplay and Filemoon embeds) - Added dropload, filelions and vtube embeds to Primewire diff --git a/package.json b/package.json index 69cfad1..c329891 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@movie-web/providers", - "version": "2.2.9", + "version": "2.3.0", "description": "Package that contains all the providers of movie-web", "type": "module", "main": "./lib/index.js", From 89a78e4ae945cc7ab8e58553122d88e7ec91c84f Mon Sep 17 00:00:00 2001 From: Jorrin Date: Fri, 12 Apr 2024 18:30:21 +0200 Subject: [PATCH 30/30] disable vidsrc --- .docs/content/1.get-started/4.changelog.md | 2 +- src/providers/sources/vidsrc/index.ts | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/.docs/content/1.get-started/4.changelog.md b/.docs/content/1.get-started/4.changelog.md index d8e727f..d792e30 100644 --- a/.docs/content/1.get-started/4.changelog.md +++ b/.docs/content/1.get-started/4.changelog.md @@ -5,7 +5,7 @@ title: 'Changelog' # Version 2.3.0 - Fixed RidoMovies search results - Added Insertunit, SoaperTV, and WarezCDN providers -- Disabled Showbox +- Disabled Showbox and VidSrc # Version 2.2.9 - Fixed VidSrcTo (both Vidplay and Filemoon embeds) diff --git a/src/providers/sources/vidsrc/index.ts b/src/providers/sources/vidsrc/index.ts index 3046fb8..d4df2f7 100644 --- a/src/providers/sources/vidsrc/index.ts +++ b/src/providers/sources/vidsrc/index.ts @@ -6,6 +6,7 @@ export const vidsrcScraper = makeSourcerer({ id: 'vidsrc', name: 'VidSrc', rank: 90, + disabled: true, flags: [], scrapeMovie, scrapeShow,