From 8b7e840814a5322eb90470ae49432036f30fb566 Mon Sep 17 00:00:00 2001 From: Jorrin Date: Wed, 27 Dec 2023 02:03:53 +0100 Subject: [PATCH 01/11] vidsrcto draft --- .eslintrc.js | 1 + src/dev-cli/tmdb.ts | 2 + src/providers/all.ts | 6 +++ src/providers/embeds/filemoon.ts | 34 ++++++++++++++ src/providers/embeds/vidplay/common.ts | 53 ++++++++++++++++++++++ src/providers/embeds/vidplay/index.ts | 32 +++++++++++++ src/providers/embeds/vidplay/types.ts | 11 +++++ src/providers/sources/vidsrcto/common.ts | 51 +++++++++++++++++++++ src/providers/sources/vidsrcto/index.ts | 58 ++++++++++++++++++++++++ src/providers/sources/vidsrcto/types.ts | 15 ++++++ 10 files changed, 263 insertions(+) create mode 100644 src/providers/embeds/filemoon.ts create mode 100644 src/providers/embeds/vidplay/common.ts create mode 100644 src/providers/embeds/vidplay/index.ts create mode 100644 src/providers/embeds/vidplay/types.ts create mode 100644 src/providers/sources/vidsrcto/common.ts create mode 100644 src/providers/sources/vidsrcto/index.ts create mode 100644 src/providers/sources/vidsrcto/types.ts diff --git a/.eslintrc.js b/.eslintrc.js index 0e7322b..2bb81f9 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -18,6 +18,7 @@ module.exports = { }, plugins: ['@typescript-eslint', 'import', 'prettier'], rules: { + 'no-bitwise': 'off', 'no-underscore-dangle': 'off', '@typescript-eslint/no-explicit-any': 'off', 'no-console': 'off', diff --git a/src/dev-cli/tmdb.ts b/src/dev-cli/tmdb.ts index 7490336..90e87f8 100644 --- a/src/dev-cli/tmdb.ts +++ b/src/dev-cli/tmdb.ts @@ -45,6 +45,7 @@ export async function getMovieMediaDetails(id: string): Promise { title: movie.title, releaseYear: Number(movie.release_date.split('-')[0]), tmdbId: id, + imdbId: movie.imdb_id, }; } @@ -91,5 +92,6 @@ export async function getShowMediaDetails(id: string, seasonNumber: string, epis number: season.season_number, tmdbId: season.id, }, + imdbId: series.imdb_id, }; } diff --git a/src/providers/all.ts b/src/providers/all.ts index d1e7885..07458b9 100644 --- a/src/providers/all.ts +++ b/src/providers/all.ts @@ -14,9 +14,12 @@ import { remotestreamScraper } from '@/providers/sources/remotestream'; import { showboxScraper } from '@/providers/sources/showbox/index'; import { zoechipScraper } from '@/providers/sources/zoechip'; +import { fileMoonScraper } from './embeds/filemoon'; import { smashyStreamDScraper } from './embeds/smashystream/dued'; import { smashyStreamFScraper } from './embeds/smashystream/video1'; +import { vidplayScraper } from './embeds/vidplay'; import { smashyStreamScraper } from './sources/smashystream'; +import { vidSrcToScraper } from './sources/vidsrcto'; export function gatherAllSources(): Array { // all sources are gathered here @@ -29,6 +32,7 @@ export function gatherAllSources(): Array { zoechipScraper, lookmovieScraper, smashyStreamScraper, + vidSrcToScraper, ]; } @@ -44,5 +48,7 @@ export function gatherAllEmbeds(): Array { mixdropScraper, smashyStreamFScraper, smashyStreamDScraper, + fileMoonScraper, + vidplayScraper, ]; } diff --git a/src/providers/embeds/filemoon.ts b/src/providers/embeds/filemoon.ts new file mode 100644 index 0000000..9b092a2 --- /dev/null +++ b/src/providers/embeds/filemoon.ts @@ -0,0 +1,34 @@ +import { unpack } from 'unpacker'; + +import { flags } from '@/entrypoint/utils/targets'; + +import { makeEmbed } from '../base'; + +const evalCodeRegex = /eval\((.*)\)/g; +const fileRegex = /file:"(.*?)"/g; + +export const fileMoonScraper = makeEmbed({ + id: 'filemoon', + name: 'FileMoon', + rank: 501, + scrape: async (ctx) => { + const embedRes = await ctx.fetcher(ctx.url); + const evalCode = evalCodeRegex.exec(embedRes); + if (!evalCode) throw new Error('Failed to find eval code'); + const unpacked = unpack(evalCode[1]); + const file = fileRegex.exec(unpacked); + if (!file?.[1]) throw new Error('Failed to find file'); + + return { + stream: [ + { + id: 'primary', + type: 'hls', + playlist: file[1], + flags: [flags.CORS_ALLOWED], + captions: [], + }, + ], + }; + }, +}); diff --git a/src/providers/embeds/vidplay/common.ts b/src/providers/embeds/vidplay/common.ts new file mode 100644 index 0000000..9fbc8d2 --- /dev/null +++ b/src/providers/embeds/vidplay/common.ts @@ -0,0 +1,53 @@ +import { createCipheriv } from 'crypto'; +import { Buffer } from 'node:buffer'; + +import { EmbedScrapeContext } from '@/utils/context'; + +export const vidplayBase = 'https://vidplay.site'; + +export const getDecryptionKeys = async (ctx: EmbedScrapeContext): Promise => { + const res = await ctx.fetcher( + 'https://raw.githubusercontent.com/Claudemirovsky/worstsource-keys/keys/keys.json', + ); + return JSON.parse(res); +}; + +// TODO: Fix this so its cross platform compatible +export const getEncodedId = async (ctx: EmbedScrapeContext) => { + const id = ctx.url.split('/e/')[1].split('?')[0]; + const keys = await getDecryptionKeys(ctx); + const c1 = createCipheriv('rc4', Buffer.from(keys[0]), ''); + const c2 = createCipheriv('rc4', Buffer.from(keys[1]), ''); + + let input = Buffer.from(id); + input = Buffer.concat([c1.update(input), c1.final()]); + input = Buffer.concat([c2.update(input), c2.final()]); + + return input.toString('base64').replace('/', '_'); +}; + +export const getFuTokenKey = async (ctx: EmbedScrapeContext) => { + const id = await getEncodedId(ctx); + console.log(`ENCODED ID: ${id}`); + const fuTokenRes = await ctx.proxiedFetcher('/futoken', { + baseUrl: vidplayBase, + headers: { + referer: ctx.url, + }, + }); + const fuKey = fuTokenRes.match(/var\s+k\s*=\s*'([^']+)'/)?.[1]; + console.log(`FU KEY: ${fuKey}`); + if (!fuKey) throw new Error('No fuKey found'); + const tokens = []; + for (let i = 0; i < id.length; i += 1) { + tokens.push(fuKey.charCodeAt(i % fuKey.length) + id.charCodeAt(i)); + } + console.log(`${fuKey},${tokens.join(',')}`); + return `${fuKey},${tokens.join(',')}`; +}; + +export const getFileUrl = async (ctx: EmbedScrapeContext) => { + console.log(ctx.url); + const fuToken = await getFuTokenKey(ctx); + return `${vidplayBase}/mediainfo/${fuToken}?${ctx.url.split('?')[1]}`; +}; diff --git a/src/providers/embeds/vidplay/index.ts b/src/providers/embeds/vidplay/index.ts new file mode 100644 index 0000000..93243e5 --- /dev/null +++ b/src/providers/embeds/vidplay/index.ts @@ -0,0 +1,32 @@ +import { makeEmbed } from '@/providers/base'; + +import { getFileUrl } from './common'; +import { VidplaySourceResponse } from './types'; + +export const vidplayScraper = makeEmbed({ + id: 'vidplay', + name: 'VidPlay', + rank: 499, + scrape: async (ctx) => { + const fileUrl = await getFileUrl(ctx); + console.log(fileUrl); + const fileUrlRes = await ctx.proxiedFetcher(`${fileUrl}&autostart=true`, { + headers: { + referer: ctx.url, + }, + }); + const source = fileUrlRes.result.sources[0].file; + + return { + stream: [ + { + id: 'primary', + type: 'hls', + playlist: source, + flags: [], + captions: [], + }, + ], + }; + }, +}); diff --git a/src/providers/embeds/vidplay/types.ts b/src/providers/embeds/vidplay/types.ts new file mode 100644 index 0000000..14c33a8 --- /dev/null +++ b/src/providers/embeds/vidplay/types.ts @@ -0,0 +1,11 @@ +export type VidplaySourceResponse = { + result: { + sources: { + file: string; + tracks: { + file: string; + kind: string; + }[]; + }[]; + }; +}; diff --git a/src/providers/sources/vidsrcto/common.ts b/src/providers/sources/vidsrcto/common.ts new file mode 100644 index 0000000..e31b58d --- /dev/null +++ b/src/providers/sources/vidsrcto/common.ts @@ -0,0 +1,51 @@ +const DECRYPTION_KEY = '8z5Ag5wgagfsOuhz'; + +export const decodeBase64UrlSafe = (str: string) => { + const standardizedInput = str.replace(/_/g, '/').replace(/-/g, '+'); + + const binaryData = Buffer.from(standardizedInput, 'base64').toString('binary'); + + const bytes = new Uint8Array(binaryData.length); + for (let i = 0; i < bytes.length; i += 1) { + bytes[i] = binaryData.charCodeAt(i); + } + + return bytes; +}; + +export const decode = (str: Uint8Array) => { + const keyBytes = new TextEncoder().encode(DECRYPTION_KEY); + + let j = 0; + const s = new Uint8Array(256); + for (let i = 0; i < 256; i += 1) { + s[i] = i; + } + + for (let i = 0, k = 0; i < 256; i += 1) { + j = (j + s[i] + keyBytes[k % keyBytes.length]) & 0xff; + [s[i], s[j]] = [s[j], s[i]]; + k += 1; + } + + const decoded = new Uint8Array(str.length); + let i = 0; + let k = 0; + for (let index = 0; index < str.length; index += 1) { + i = (i + 1) & 0xff; + k = (k + s[i]) & 0xff; + [s[i], s[k]] = [s[k], s[i]]; + const t = (s[i] + s[k]) & 0xff; + decoded[index] = str[index] ^ s[t]; + } + + return decoded; +}; + +export const decryptSourceUrl = (sourceUrl: string) => { + const encoded = decodeBase64UrlSafe(sourceUrl); + const decoded = decode(encoded); + const decodedText = new TextDecoder().decode(decoded); + + return decodeURIComponent(decodeURIComponent(decodedText)); +}; diff --git a/src/providers/sources/vidsrcto/index.ts b/src/providers/sources/vidsrcto/index.ts new file mode 100644 index 0000000..eec22d4 --- /dev/null +++ b/src/providers/sources/vidsrcto/index.ts @@ -0,0 +1,58 @@ +import { load } from 'cheerio'; + +import { SourcererEmbed, SourcererOutput, makeSourcerer } from '@/providers/base'; +import { MovieScrapeContext, ShowScrapeContext } from '@/utils/context'; + +import { decryptSourceUrl } from './common'; +import { SourceResult, SourcesResult } from './types'; + +const vidSrcToBase = 'https://vidsrc.to'; + +const universalScraper = async (ctx: ShowScrapeContext | MovieScrapeContext): Promise => { + const imdbId = ctx.media.imdbId; + const url = + ctx.media.type === 'movie' + ? `${vidSrcToBase}/embed/movie/${imdbId}` + : `${vidSrcToBase}}/embed/tv/${imdbId}/${ctx.media.season.number}/${ctx.media.episode.number}`; + + const mainPage = await ctx.fetcher(url); + const mainPage$ = load(mainPage); + const dataId = mainPage$('a[data-id]').attr('data-id'); + if (!dataId) throw new Error('No data-id found'); + const sources = await ctx.fetcher(`/ajax/embed/episode/${dataId}/sources`, { + baseUrl: vidSrcToBase, + }); + if (sources.status !== 200) throw new Error('No sources found'); + + const embeds: SourcererEmbed[] = []; + for (const source of sources.result) { + const sourceRes = await ctx.fetcher(`/ajax/embed/source/${source.id}`, { + baseUrl: vidSrcToBase, + }); + const decryptedUrl = decryptSourceUrl(sourceRes.result.url); + if (source.title === 'Filemoon') { + embeds.push({ + embedId: 'filemoon', + url: decryptedUrl, + }); + } + if (source.title === 'Vidplay') { + embeds.push({ + embedId: 'vidplay', + url: decryptedUrl, + }); + } + } + return { + embeds, + }; +}; + +export const vidSrcToScraper = makeSourcerer({ + id: 'vidsrcto', + name: 'VidSrcTo', + scrapeMovie: universalScraper, + scrapeShow: universalScraper, + flags: [], + rank: 500, +}); diff --git a/src/providers/sources/vidsrcto/types.ts b/src/providers/sources/vidsrcto/types.ts new file mode 100644 index 0000000..0694b15 --- /dev/null +++ b/src/providers/sources/vidsrcto/types.ts @@ -0,0 +1,15 @@ +export type VidSrcToResponse = { + status: number; + result: T; +}; + +export type SourcesResult = VidSrcToResponse< + { + id: string; + title: 'Filemoon' | 'Vidplay'; + }[] +>; + +export type SourceResult = VidSrcToResponse<{ + url: string; +}>; From f39aaca3e32fd11a3fca7f56711675f331592473 Mon Sep 17 00:00:00 2001 From: Jorrin Date: Wed, 27 Dec 2023 20:35:08 +0100 Subject: [PATCH 02/11] fix vidplay, add captions to filemoon --- src/providers/embeds/filemoon.ts | 34 --------------- src/providers/embeds/filemoon/index.ts | 56 +++++++++++++++++++++++++ src/providers/embeds/filemoon/types.ts | 5 +++ src/providers/embeds/vidplay/common.ts | 53 +++++++++++++++-------- src/providers/embeds/vidplay/index.ts | 29 +++++++++++-- src/providers/embeds/vidplay/types.ts | 26 ++++++++---- src/providers/sources/vidsrcto/index.ts | 29 +++++++++---- 7 files changed, 160 insertions(+), 72 deletions(-) delete mode 100644 src/providers/embeds/filemoon.ts create mode 100644 src/providers/embeds/filemoon/index.ts create mode 100644 src/providers/embeds/filemoon/types.ts diff --git a/src/providers/embeds/filemoon.ts b/src/providers/embeds/filemoon.ts deleted file mode 100644 index 9b092a2..0000000 --- a/src/providers/embeds/filemoon.ts +++ /dev/null @@ -1,34 +0,0 @@ -import { unpack } from 'unpacker'; - -import { flags } from '@/entrypoint/utils/targets'; - -import { makeEmbed } from '../base'; - -const evalCodeRegex = /eval\((.*)\)/g; -const fileRegex = /file:"(.*?)"/g; - -export const fileMoonScraper = makeEmbed({ - id: 'filemoon', - name: 'FileMoon', - rank: 501, - scrape: async (ctx) => { - const embedRes = await ctx.fetcher(ctx.url); - const evalCode = evalCodeRegex.exec(embedRes); - if (!evalCode) throw new Error('Failed to find eval code'); - const unpacked = unpack(evalCode[1]); - const file = fileRegex.exec(unpacked); - if (!file?.[1]) throw new Error('Failed to find file'); - - return { - stream: [ - { - id: 'primary', - type: 'hls', - playlist: file[1], - flags: [flags.CORS_ALLOWED], - captions: [], - }, - ], - }; - }, -}); diff --git a/src/providers/embeds/filemoon/index.ts b/src/providers/embeds/filemoon/index.ts new file mode 100644 index 0000000..584be7e --- /dev/null +++ b/src/providers/embeds/filemoon/index.ts @@ -0,0 +1,56 @@ +import { unpack } from 'unpacker'; + +import { flags } from '@/entrypoint/utils/targets'; + +import { SubtitleResult } from './types'; +import { makeEmbed } from '../../base'; +import { Caption, getCaptionTypeFromUrl, labelToLanguageCode } from '../../captions'; + +const evalCodeRegex = /eval\((.*)\)/g; +const fileRegex = /file:"(.*?)"/g; + +export const fileMoonScraper = makeEmbed({ + id: 'filemoon', + name: 'Filemoon', + rank: 501, + scrape: async (ctx) => { + const embedRes = await ctx.fetcher(ctx.url); + const evalCode = evalCodeRegex.exec(embedRes); + if (!evalCode) throw new Error('Failed to find eval code'); + const unpacked = unpack(evalCode[1]); + const file = fileRegex.exec(unpacked); + if (!file?.[1]) throw new Error('Failed to find file'); + + const url = new URL(ctx.url); + const subtitlesLink = url.searchParams.get('sub.info'); + const captions: Caption[] = []; + if (subtitlesLink) { + const captionsResult = await ctx.fetcher(subtitlesLink); + + for (const caption of captionsResult) { + const language = labelToLanguageCode(caption.label); + const captionType = getCaptionTypeFromUrl(caption.file); + if (!language || !captionType) continue; + captions.push({ + id: caption.file, + url: caption.file, + type: captionType, + language, + hasCorsRestrictions: false, + }); + } + } + + return { + stream: [ + { + id: 'primary', + type: 'hls', + playlist: file[1], + flags: [flags.CORS_ALLOWED], + captions, + }, + ], + }; + }, +}); diff --git a/src/providers/embeds/filemoon/types.ts b/src/providers/embeds/filemoon/types.ts new file mode 100644 index 0000000..caa27af --- /dev/null +++ b/src/providers/embeds/filemoon/types.ts @@ -0,0 +1,5 @@ +export type SubtitleResult = { + file: string; + label: string; + kind: string; +}[]; diff --git a/src/providers/embeds/vidplay/common.ts b/src/providers/embeds/vidplay/common.ts index 9fbc8d2..cf2eb0f 100644 --- a/src/providers/embeds/vidplay/common.ts +++ b/src/providers/embeds/vidplay/common.ts @@ -1,10 +1,34 @@ -import { createCipheriv } from 'crypto'; -import { Buffer } from 'node:buffer'; - import { EmbedScrapeContext } from '@/utils/context'; export const vidplayBase = 'https://vidplay.site'; +export function keyPermutation(key: string, data: any) { + const state = Array.from(Array(256).keys()); + let index1 = 0; + for (let i = 0; i < 256; i += 1) { + index1 = (index1 + state[i] + key.charCodeAt(i % key.length)) % 256; + const temp = state[i]; + state[i] = state[index1]; + state[index1] = temp; + } + index1 = 0; + let index2 = 0; + let finalKey = ''; + for (let char = 0; char < data.length; char += 1) { + index1 = (index1 + 1) % 256; + index2 = (index2 + state[index1]) % 256; + const temp = state[index1]; + state[index1] = state[index2]; + state[index2] = temp; + if (typeof data[char] === 'string') { + finalKey += String.fromCharCode(data[char].charCodeAt(0) ^ state[(state[index1] + state[index2]) % 256]); + } else if (typeof data[char] === 'number') { + finalKey += String.fromCharCode(data[char] ^ state[(state[index1] + state[index2]) % 256]); + } + } + return finalKey; +} + export const getDecryptionKeys = async (ctx: EmbedScrapeContext): Promise => { const res = await ctx.fetcher( 'https://raw.githubusercontent.com/Claudemirovsky/worstsource-keys/keys/keys.json', @@ -12,23 +36,19 @@ export const getDecryptionKeys = async (ctx: EmbedScrapeContext): Promise { - const id = ctx.url.split('/e/')[1].split('?')[0]; - const keys = await getDecryptionKeys(ctx); - const c1 = createCipheriv('rc4', Buffer.from(keys[0]), ''); - const c2 = createCipheriv('rc4', Buffer.from(keys[1]), ''); + const url = new URL(ctx.url); + const id = url.pathname.replace('/e/', ''); + const keyList = await getDecryptionKeys(ctx); - let input = Buffer.from(id); - input = Buffer.concat([c1.update(input), c1.final()]); - input = Buffer.concat([c2.update(input), c2.final()]); - - return input.toString('base64').replace('/', '_'); + const decodedId = keyPermutation(keyList[0], id); + const encodedResult = keyPermutation(keyList[1], decodedId); + const base64 = btoa(encodedResult); + return base64.replace('/', '_'); }; export const getFuTokenKey = async (ctx: EmbedScrapeContext) => { const id = await getEncodedId(ctx); - console.log(`ENCODED ID: ${id}`); const fuTokenRes = await ctx.proxiedFetcher('/futoken', { baseUrl: vidplayBase, headers: { @@ -36,18 +56,15 @@ export const getFuTokenKey = async (ctx: EmbedScrapeContext) => { }, }); const fuKey = fuTokenRes.match(/var\s+k\s*=\s*'([^']+)'/)?.[1]; - console.log(`FU KEY: ${fuKey}`); if (!fuKey) throw new Error('No fuKey found'); const tokens = []; for (let i = 0; i < id.length; i += 1) { tokens.push(fuKey.charCodeAt(i % fuKey.length) + id.charCodeAt(i)); } - console.log(`${fuKey},${tokens.join(',')}`); return `${fuKey},${tokens.join(',')}`; }; export const getFileUrl = async (ctx: EmbedScrapeContext) => { - console.log(ctx.url); const fuToken = await getFuTokenKey(ctx); - return `${vidplayBase}/mediainfo/${fuToken}?${ctx.url.split('?')[1]}`; + return `${vidplayBase}/mediainfo/${fuToken}${new URL(ctx.url).search}&autostart=true`; }; diff --git a/src/providers/embeds/vidplay/index.ts b/src/providers/embeds/vidplay/index.ts index 93243e5..425d583 100644 --- a/src/providers/embeds/vidplay/index.ts +++ b/src/providers/embeds/vidplay/index.ts @@ -1,7 +1,8 @@ import { makeEmbed } from '@/providers/base'; +import { Caption, getCaptionTypeFromUrl, labelToLanguageCode } from '@/providers/captions'; import { getFileUrl } from './common'; -import { VidplaySourceResponse } from './types'; +import { SubtitleResult, VidplaySourceResponse } from './types'; export const vidplayScraper = makeEmbed({ id: 'vidplay', @@ -9,14 +10,34 @@ export const vidplayScraper = makeEmbed({ rank: 499, scrape: async (ctx) => { const fileUrl = await getFileUrl(ctx); - console.log(fileUrl); - const fileUrlRes = await ctx.proxiedFetcher(`${fileUrl}&autostart=true`, { + const fileUrlRes = await ctx.proxiedFetcher(fileUrl, { headers: { referer: ctx.url, }, }); + if (typeof fileUrlRes.result === 'number') throw new Error('File not found'); const source = fileUrlRes.result.sources[0].file; + const url = new URL(ctx.url); + const subtitlesLink = url.searchParams.get('sub.info'); + const captions: Caption[] = []; + if (subtitlesLink) { + const captionsResult = await ctx.fetcher(subtitlesLink); + + for (const caption of captionsResult) { + const language = labelToLanguageCode(caption.label); + const captionType = getCaptionTypeFromUrl(caption.file); + if (!language || !captionType) continue; + captions.push({ + id: caption.file, + url: caption.file, + type: captionType, + language, + hasCorsRestrictions: false, + }); + } + } + return { stream: [ { @@ -24,7 +45,7 @@ export const vidplayScraper = makeEmbed({ type: 'hls', playlist: source, flags: [], - captions: [], + captions, }, ], }; diff --git a/src/providers/embeds/vidplay/types.ts b/src/providers/embeds/vidplay/types.ts index 14c33a8..29cde1d 100644 --- a/src/providers/embeds/vidplay/types.ts +++ b/src/providers/embeds/vidplay/types.ts @@ -1,11 +1,19 @@ export type VidplaySourceResponse = { - result: { - sources: { - file: string; - tracks: { - file: string; - kind: string; - }[]; - }[]; - }; + result: + | { + sources: { + file: string; + tracks: { + file: string; + kind: string; + }[]; + }[]; + } + | number; }; + +export type SubtitleResult = { + file: string; + label: string; + kind: string; +}[]; diff --git a/src/providers/sources/vidsrcto/index.ts b/src/providers/sources/vidsrcto/index.ts index eec22d4..8b43e47 100644 --- a/src/providers/sources/vidsrcto/index.ts +++ b/src/providers/sources/vidsrcto/index.ts @@ -25,24 +25,39 @@ const universalScraper = async (ctx: ShowScrapeContext | MovieScrapeContext): Pr if (sources.status !== 200) throw new Error('No sources found'); const embeds: SourcererEmbed[] = []; + const embedUrls = []; for (const source of sources.result) { const sourceRes = await ctx.fetcher(`/ajax/embed/source/${source.id}`, { baseUrl: vidSrcToBase, }); const decryptedUrl = decryptSourceUrl(sourceRes.result.url); - if (source.title === 'Filemoon') { - embeds.push({ - embedId: 'filemoon', - url: decryptedUrl, - }); - } + embedUrls.push(decryptedUrl); + } + + // Originally Filemoon does not have subtitles. But we can use the ones from Vidplay. + const subtitleUrl = new URL(embedUrls.find((v) => v.includes('sub.info')) ?? '').searchParams.get('sub.info'); + for (const source of sources.result) { if (source.title === 'Vidplay') { + const embedUrl = embedUrls.find((v) => v.includes('vidplay')); + if (!embedUrl) continue; embeds.push({ embedId: 'vidplay', - url: decryptedUrl, + url: embedUrl, + }); + } + + if (source.title === 'Filemoon') { + const embedUrl = embedUrls.find((v) => v.includes('filemoon')); + if (!embedUrl) continue; + const fullUrl = new URL(embedUrl); + if (subtitleUrl) fullUrl.searchParams.set('sub.info', subtitleUrl); + embeds.push({ + embedId: 'filemoon', + url: fullUrl.toString(), }); } } + return { embeds, }; From 30e6067a3fa46fe5f75decca38554e7693eabc73 Mon Sep 17 00:00:00 2001 From: Jorrin Date: Wed, 27 Dec 2023 20:36:50 +0100 Subject: [PATCH 03/11] ranks --- src/providers/embeds/filemoon/index.ts | 2 +- src/providers/embeds/vidplay/index.ts | 2 +- src/providers/sources/vidsrcto/index.ts | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/providers/embeds/filemoon/index.ts b/src/providers/embeds/filemoon/index.ts index 584be7e..f1eda29 100644 --- a/src/providers/embeds/filemoon/index.ts +++ b/src/providers/embeds/filemoon/index.ts @@ -12,7 +12,7 @@ const fileRegex = /file:"(.*?)"/g; export const fileMoonScraper = makeEmbed({ id: 'filemoon', name: 'Filemoon', - rank: 501, + rank: 301, scrape: async (ctx) => { const embedRes = await ctx.fetcher(ctx.url); const evalCode = evalCodeRegex.exec(embedRes); diff --git a/src/providers/embeds/vidplay/index.ts b/src/providers/embeds/vidplay/index.ts index 425d583..9225eff 100644 --- a/src/providers/embeds/vidplay/index.ts +++ b/src/providers/embeds/vidplay/index.ts @@ -7,7 +7,7 @@ import { SubtitleResult, VidplaySourceResponse } from './types'; export const vidplayScraper = makeEmbed({ id: 'vidplay', name: 'VidPlay', - rank: 499, + rank: 300, scrape: async (ctx) => { const fileUrl = await getFileUrl(ctx); const fileUrlRes = await ctx.proxiedFetcher(fileUrl, { diff --git a/src/providers/sources/vidsrcto/index.ts b/src/providers/sources/vidsrcto/index.ts index 8b43e47..3b22bbb 100644 --- a/src/providers/sources/vidsrcto/index.ts +++ b/src/providers/sources/vidsrcto/index.ts @@ -69,5 +69,5 @@ export const vidSrcToScraper = makeSourcerer({ scrapeMovie: universalScraper, scrapeShow: universalScraper, flags: [], - rank: 500, + rank: 300, }); From c44d13f0bd9be6307c4d0a43d20726c83f101992 Mon Sep 17 00:00:00 2001 From: Jorrin Date: Wed, 27 Dec 2023 20:42:22 +0100 Subject: [PATCH 04/11] fix ranks --- src/providers/embeds/filemoon/index.ts | 2 +- src/providers/embeds/vidplay/index.ts | 2 +- src/providers/sources/vidsrcto/index.ts | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/providers/embeds/filemoon/index.ts b/src/providers/embeds/filemoon/index.ts index f1eda29..2729b5c 100644 --- a/src/providers/embeds/filemoon/index.ts +++ b/src/providers/embeds/filemoon/index.ts @@ -12,7 +12,7 @@ const fileRegex = /file:"(.*?)"/g; export const fileMoonScraper = makeEmbed({ id: 'filemoon', name: 'Filemoon', - rank: 301, + rank: 400, scrape: async (ctx) => { const embedRes = await ctx.fetcher(ctx.url); const evalCode = evalCodeRegex.exec(embedRes); diff --git a/src/providers/embeds/vidplay/index.ts b/src/providers/embeds/vidplay/index.ts index 9225eff..f7adca2 100644 --- a/src/providers/embeds/vidplay/index.ts +++ b/src/providers/embeds/vidplay/index.ts @@ -7,7 +7,7 @@ import { SubtitleResult, VidplaySourceResponse } from './types'; export const vidplayScraper = makeEmbed({ id: 'vidplay', name: 'VidPlay', - rank: 300, + rank: 401, scrape: async (ctx) => { const fileUrl = await getFileUrl(ctx); const fileUrlRes = await ctx.proxiedFetcher(fileUrl, { diff --git a/src/providers/sources/vidsrcto/index.ts b/src/providers/sources/vidsrcto/index.ts index 3b22bbb..09a0320 100644 --- a/src/providers/sources/vidsrcto/index.ts +++ b/src/providers/sources/vidsrcto/index.ts @@ -69,5 +69,5 @@ export const vidSrcToScraper = makeSourcerer({ scrapeMovie: universalScraper, scrapeShow: universalScraper, flags: [], - rank: 300, + rank: 400, }); From a208aef364d60ea7967f4490f88b309046feb336 Mon Sep 17 00:00:00 2001 From: Jorrin Date: Wed, 27 Dec 2023 21:14:18 +0100 Subject: [PATCH 05/11] remove Buffer --- src/providers/sources/vidsrcto/common.ts | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/providers/sources/vidsrcto/common.ts b/src/providers/sources/vidsrcto/common.ts index e31b58d..fb9890d 100644 --- a/src/providers/sources/vidsrcto/common.ts +++ b/src/providers/sources/vidsrcto/common.ts @@ -2,12 +2,11 @@ const DECRYPTION_KEY = '8z5Ag5wgagfsOuhz'; export const decodeBase64UrlSafe = (str: string) => { const standardizedInput = str.replace(/_/g, '/').replace(/-/g, '+'); + const decodedData = atob(standardizedInput); - const binaryData = Buffer.from(standardizedInput, 'base64').toString('binary'); - - const bytes = new Uint8Array(binaryData.length); + const bytes = new Uint8Array(decodedData.length); for (let i = 0; i < bytes.length; i += 1) { - bytes[i] = binaryData.charCodeAt(i); + bytes[i] = decodedData.charCodeAt(i); } return bytes; From 9b338b6f3bd2eb2d76dffbf2a3a181b9d499efa7 Mon Sep 17 00:00:00 2001 From: Jorrin Date: Thu, 28 Dec 2023 18:39:49 +0100 Subject: [PATCH 06/11] fixed feedback, added external_ids --- src/dev-cli/tmdb.ts | 16 ++++++++++------ src/providers/embeds/vidplay/common.ts | 9 ++++++++- src/providers/sources/vidsrcto/index.ts | 12 +++++++----- 3 files changed, 25 insertions(+), 12 deletions(-) diff --git a/src/dev-cli/tmdb.ts b/src/dev-cli/tmdb.ts index 90e87f8..c03307d 100644 --- a/src/dev-cli/tmdb.ts +++ b/src/dev-cli/tmdb.ts @@ -2,7 +2,7 @@ import { getConfig } from '@/dev-cli/config'; import { MovieMedia, ShowMedia } from '..'; -export async function makeTMDBRequest(url: string): Promise { +export async function makeTMDBRequest(url: string, appendToResponse?: string): Promise { const headers: { accept: 'application/json'; authorization?: string; @@ -10,7 +10,7 @@ export async function makeTMDBRequest(url: string): Promise { accept: 'application/json', }; - let requestURL = url; + const requestURL = new URL(url); const key = getConfig().tmdbApiKey; // * JWT keys always start with ey and are ONLY valid as a header. @@ -19,7 +19,11 @@ export async function makeTMDBRequest(url: string): Promise { if (key.startsWith('ey')) { headers.authorization = `Bearer ${key}`; } else { - requestURL += `?api_key=${key}`; + requestURL.searchParams.append('api_key', key); + } + + if (appendToResponse) { + requestURL.searchParams.append('append_to_response', appendToResponse); } return fetch(requestURL, { @@ -29,7 +33,7 @@ export async function makeTMDBRequest(url: string): Promise { } export async function getMovieMediaDetails(id: string): Promise { - const response = await makeTMDBRequest(`https://api.themoviedb.org/3/movie/${id}`); + const response = await makeTMDBRequest(`https://api.themoviedb.org/3/movie/${id}`, 'external_ids'); const movie = await response.json(); if (movie.success === false) { @@ -52,7 +56,7 @@ export async function getMovieMediaDetails(id: string): Promise { export async function getShowMediaDetails(id: string, seasonNumber: string, episodeNumber: string): Promise { // * TV shows require the TMDB ID for the series, season, and episode // * and the name of the series. Needs multiple requests - let response = await makeTMDBRequest(`https://api.themoviedb.org/3/tv/${id}`); + let response = await makeTMDBRequest(`https://api.themoviedb.org/3/tv/${id}`, 'external_ids'); const series = await response.json(); if (series.success === false) { @@ -92,6 +96,6 @@ export async function getShowMediaDetails(id: string, seasonNumber: string, epis number: season.season_number, tmdbId: season.id, }, - imdbId: series.imdb_id, + imdbId: series.external_ids.imdb_id, }; } diff --git a/src/providers/embeds/vidplay/common.ts b/src/providers/embeds/vidplay/common.ts index cf2eb0f..765edc7 100644 --- a/src/providers/embeds/vidplay/common.ts +++ b/src/providers/embeds/vidplay/common.ts @@ -1,3 +1,4 @@ +import { makeFullUrl } from '@/fetchers/common'; import { EmbedScrapeContext } from '@/utils/context'; export const vidplayBase = 'https://vidplay.site'; @@ -66,5 +67,11 @@ export const getFuTokenKey = async (ctx: EmbedScrapeContext) => { export const getFileUrl = async (ctx: EmbedScrapeContext) => { const fuToken = await getFuTokenKey(ctx); - return `${vidplayBase}/mediainfo/${fuToken}${new URL(ctx.url).search}&autostart=true`; + return makeFullUrl(`/mediainfo/${fuToken}`, { + baseUrl: vidplayBase, + query: { + ...Object.fromEntries(new URL(ctx.url).searchParams.entries()), + autostart: 'true', + }, + }); }; diff --git a/src/providers/sources/vidsrcto/index.ts b/src/providers/sources/vidsrcto/index.ts index 09a0320..5e73bc1 100644 --- a/src/providers/sources/vidsrcto/index.ts +++ b/src/providers/sources/vidsrcto/index.ts @@ -1,5 +1,6 @@ import { load } from 'cheerio'; +import { flags } from '@/entrypoint/utils/targets'; import { SourcererEmbed, SourcererOutput, makeSourcerer } from '@/providers/base'; import { MovieScrapeContext, ShowScrapeContext } from '@/utils/context'; @@ -12,10 +13,11 @@ const universalScraper = async (ctx: ShowScrapeContext | MovieScrapeContext): Pr const imdbId = ctx.media.imdbId; const url = ctx.media.type === 'movie' - ? `${vidSrcToBase}/embed/movie/${imdbId}` - : `${vidSrcToBase}}/embed/tv/${imdbId}/${ctx.media.season.number}/${ctx.media.episode.number}`; - - const mainPage = await ctx.fetcher(url); + ? `/embed/movie/${imdbId}` + : `/embed/tv/${imdbId}/${ctx.media.season.number}/${ctx.media.episode.number}`; + const mainPage = await ctx.fetcher(url, { + baseUrl: vidSrcToBase, + }); const mainPage$ = load(mainPage); const dataId = mainPage$('a[data-id]').attr('data-id'); if (!dataId) throw new Error('No data-id found'); @@ -68,6 +70,6 @@ export const vidSrcToScraper = makeSourcerer({ name: 'VidSrcTo', scrapeMovie: universalScraper, scrapeShow: universalScraper, - flags: [], + flags: [flags.CORS_ALLOWED], rank: 400, }); From a07f54e0cfdae270d82f11c911abf64f6fa91ebd Mon Sep 17 00:00:00 2001 From: Jorrin <43169049+JorrinKievit@users.noreply.github.com> Date: Fri, 29 Dec 2023 22:03:54 +0100 Subject: [PATCH 07/11] add credits to decryption code --- src/providers/embeds/vidplay/common.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/providers/embeds/vidplay/common.ts b/src/providers/embeds/vidplay/common.ts index 765edc7..57b94bb 100644 --- a/src/providers/embeds/vidplay/common.ts +++ b/src/providers/embeds/vidplay/common.ts @@ -3,6 +3,8 @@ import { EmbedScrapeContext } from '@/utils/context'; export const vidplayBase = 'https://vidplay.site'; +// This file is based on https://github.com/Ciarands/vidsrc-to-resolver/blob/960afb11c30aa6497804b4691fb1c401e539cfe7/vidsrc.py#L10 +// Full credits to @Ciarands! export function keyPermutation(key: string, data: any) { const state = Array.from(Array(256).keys()); let index1 = 0; From d7134d4daf289570c55af5857cb8bb6fe44833ad Mon Sep 17 00:00:00 2001 From: Jorrin Date: Thu, 4 Jan 2024 20:18:25 +0100 Subject: [PATCH 08/11] proxied fetcher --- src/providers/embeds/filemoon/index.ts | 8 ++++++-- src/providers/embeds/vidplay/index.ts | 2 +- src/providers/sources/vidsrcto/index.ts | 16 +++++++++++++--- 3 files changed, 20 insertions(+), 6 deletions(-) diff --git a/src/providers/embeds/filemoon/index.ts b/src/providers/embeds/filemoon/index.ts index 2729b5c..3f8a2f3 100644 --- a/src/providers/embeds/filemoon/index.ts +++ b/src/providers/embeds/filemoon/index.ts @@ -14,7 +14,11 @@ export const fileMoonScraper = makeEmbed({ name: 'Filemoon', rank: 400, scrape: async (ctx) => { - const embedRes = await ctx.fetcher(ctx.url); + const embedRes = await ctx.proxiedFetcher(ctx.url, { + headers: { + referer: ctx.url, + }, + }); const evalCode = evalCodeRegex.exec(embedRes); if (!evalCode) throw new Error('Failed to find eval code'); const unpacked = unpack(evalCode[1]); @@ -25,7 +29,7 @@ export const fileMoonScraper = makeEmbed({ const subtitlesLink = url.searchParams.get('sub.info'); const captions: Caption[] = []; if (subtitlesLink) { - const captionsResult = await ctx.fetcher(subtitlesLink); + const captionsResult = await ctx.proxiedFetcher(subtitlesLink); for (const caption of captionsResult) { const language = labelToLanguageCode(caption.label); diff --git a/src/providers/embeds/vidplay/index.ts b/src/providers/embeds/vidplay/index.ts index f7adca2..3c1f6a2 100644 --- a/src/providers/embeds/vidplay/index.ts +++ b/src/providers/embeds/vidplay/index.ts @@ -22,7 +22,7 @@ export const vidplayScraper = makeEmbed({ const subtitlesLink = url.searchParams.get('sub.info'); const captions: Caption[] = []; if (subtitlesLink) { - const captionsResult = await ctx.fetcher(subtitlesLink); + const captionsResult = await ctx.proxiedFetcher(subtitlesLink); for (const caption of captionsResult) { const language = labelToLanguageCode(caption.label); diff --git a/src/providers/sources/vidsrcto/index.ts b/src/providers/sources/vidsrcto/index.ts index 5e73bc1..983fd12 100644 --- a/src/providers/sources/vidsrcto/index.ts +++ b/src/providers/sources/vidsrcto/index.ts @@ -8,6 +8,7 @@ import { decryptSourceUrl } from './common'; import { SourceResult, SourcesResult } from './types'; const vidSrcToBase = 'https://vidsrc.to'; +const referer = `${vidSrcToBase}/`; const universalScraper = async (ctx: ShowScrapeContext | MovieScrapeContext): Promise => { const imdbId = ctx.media.imdbId; @@ -15,22 +16,31 @@ const universalScraper = async (ctx: ShowScrapeContext | MovieScrapeContext): Pr ctx.media.type === 'movie' ? `/embed/movie/${imdbId}` : `/embed/tv/${imdbId}/${ctx.media.season.number}/${ctx.media.episode.number}`; - const mainPage = await ctx.fetcher(url, { + const mainPage = await ctx.proxiedFetcher(url, { baseUrl: vidSrcToBase, + headers: { + referer, + }, }); const mainPage$ = load(mainPage); const dataId = mainPage$('a[data-id]').attr('data-id'); if (!dataId) throw new Error('No data-id found'); - const sources = await ctx.fetcher(`/ajax/embed/episode/${dataId}/sources`, { + const sources = await ctx.proxiedFetcher(`/ajax/embed/episode/${dataId}/sources`, { baseUrl: vidSrcToBase, + headers: { + referer, + }, }); if (sources.status !== 200) throw new Error('No sources found'); const embeds: SourcererEmbed[] = []; const embedUrls = []; for (const source of sources.result) { - const sourceRes = await ctx.fetcher(`/ajax/embed/source/${source.id}`, { + const sourceRes = await ctx.proxiedFetcher(`/ajax/embed/source/${source.id}`, { baseUrl: vidSrcToBase, + headers: { + referer, + }, }); const decryptedUrl = decryptSourceUrl(sourceRes.result.url); embedUrls.push(decryptedUrl); From 605b9d78d1b8cdf82c6ad4092005bda4eac70577 Mon Sep 17 00:00:00 2001 From: Jorrin Date: Fri, 5 Jan 2024 16:12:19 +0100 Subject: [PATCH 09/11] filemoon added CORS --- src/providers/embeds/filemoon/index.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/providers/embeds/filemoon/index.ts b/src/providers/embeds/filemoon/index.ts index 3f8a2f3..9f96a07 100644 --- a/src/providers/embeds/filemoon/index.ts +++ b/src/providers/embeds/filemoon/index.ts @@ -1,7 +1,5 @@ import { unpack } from 'unpacker'; -import { flags } from '@/entrypoint/utils/targets'; - import { SubtitleResult } from './types'; import { makeEmbed } from '../../base'; import { Caption, getCaptionTypeFromUrl, labelToLanguageCode } from '../../captions'; @@ -51,7 +49,7 @@ export const fileMoonScraper = makeEmbed({ id: 'primary', type: 'hls', playlist: file[1], - flags: [flags.CORS_ALLOWED], + flags: [], captions, }, ], From 1970e11443cfdb07652ad1ccc0092a3985952102 Mon Sep 17 00:00:00 2001 From: Jorrin Date: Fri, 5 Jan 2024 17:01:54 +0100 Subject: [PATCH 10/11] applied feedback from ciarands --- src/providers/embeds/vidplay/common.ts | 37 +++------------- src/providers/sources/vidsrcto/common.ts | 55 ++++++++++++------------ 2 files changed, 33 insertions(+), 59 deletions(-) diff --git a/src/providers/embeds/vidplay/common.ts b/src/providers/embeds/vidplay/common.ts index 57b94bb..224e3dc 100644 --- a/src/providers/embeds/vidplay/common.ts +++ b/src/providers/embeds/vidplay/common.ts @@ -1,36 +1,11 @@ import { makeFullUrl } from '@/fetchers/common'; +import { decodeData } from '@/providers/sources/vidsrcto/common'; import { EmbedScrapeContext } from '@/utils/context'; export const vidplayBase = 'https://vidplay.site'; -// This file is based on https://github.com/Ciarands/vidsrc-to-resolver/blob/960afb11c30aa6497804b4691fb1c401e539cfe7/vidsrc.py#L10 +// This file is based on https://github.com/Ciarands/vidsrc-to-resolver/blob/dffa45e726a4b944cb9af0c9e7630476c93c0213/vidsrc.py#L16 // Full credits to @Ciarands! -export function keyPermutation(key: string, data: any) { - const state = Array.from(Array(256).keys()); - let index1 = 0; - for (let i = 0; i < 256; i += 1) { - index1 = (index1 + state[i] + key.charCodeAt(i % key.length)) % 256; - const temp = state[i]; - state[i] = state[index1]; - state[index1] = temp; - } - index1 = 0; - let index2 = 0; - let finalKey = ''; - for (let char = 0; char < data.length; char += 1) { - index1 = (index1 + 1) % 256; - index2 = (index2 + state[index1]) % 256; - const temp = state[index1]; - state[index1] = state[index2]; - state[index2] = temp; - if (typeof data[char] === 'string') { - finalKey += String.fromCharCode(data[char].charCodeAt(0) ^ state[(state[index1] + state[index2]) % 256]); - } else if (typeof data[char] === 'number') { - finalKey += String.fromCharCode(data[char] ^ state[(state[index1] + state[index2]) % 256]); - } - } - return finalKey; -} export const getDecryptionKeys = async (ctx: EmbedScrapeContext): Promise => { const res = await ctx.fetcher( @@ -44,10 +19,10 @@ export const getEncodedId = async (ctx: EmbedScrapeContext) => { const id = url.pathname.replace('/e/', ''); const keyList = await getDecryptionKeys(ctx); - const decodedId = keyPermutation(keyList[0], id); - const encodedResult = keyPermutation(keyList[1], decodedId); - const base64 = btoa(encodedResult); - return base64.replace('/', '_'); + const decodedId = decodeData(keyList[0], id); + const encodedResult = decodeData(keyList[1], decodedId); + const b64encoded = btoa(encodedResult); + return b64encoded.replace('/', '_'); }; export const getFuTokenKey = async (ctx: EmbedScrapeContext) => { diff --git a/src/providers/sources/vidsrcto/common.ts b/src/providers/sources/vidsrcto/common.ts index fb9890d..2c7272f 100644 --- a/src/providers/sources/vidsrcto/common.ts +++ b/src/providers/sources/vidsrcto/common.ts @@ -1,3 +1,6 @@ +// This file is based on https://github.com/Ciarands/vidsrc-to-resolver/blob/dffa45e726a4b944cb9af0c9e7630476c93c0213/vidsrc.py#L16 +// Full credits to @Ciarands! + const DECRYPTION_KEY = '8z5Ag5wgagfsOuhz'; export const decodeBase64UrlSafe = (str: string) => { @@ -12,39 +15,35 @@ export const decodeBase64UrlSafe = (str: string) => { return bytes; }; -export const decode = (str: Uint8Array) => { - const keyBytes = new TextEncoder().encode(DECRYPTION_KEY); - - let j = 0; - const s = new Uint8Array(256); +export const decodeData = (key: string, data: any) => { + const state = Array.from(Array(256).keys()); + let index1 = 0; for (let i = 0; i < 256; i += 1) { - s[i] = i; + index1 = (index1 + state[i] + key.charCodeAt(i % key.length)) % 256; + const temp = state[i]; + state[i] = state[index1]; + state[index1] = temp; } - - for (let i = 0, k = 0; i < 256; i += 1) { - j = (j + s[i] + keyBytes[k % keyBytes.length]) & 0xff; - [s[i], s[j]] = [s[j], s[i]]; - k += 1; + index1 = 0; + let index2 = 0; + let finalKey = ''; + for (let char = 0; char < data.length; char += 1) { + index1 = (index1 + 1) % 256; + index2 = (index2 + state[index1]) % 256; + const temp = state[index1]; + state[index1] = state[index2]; + state[index2] = temp; + if (typeof data[char] === 'string') { + finalKey += String.fromCharCode(data[char].charCodeAt(0) ^ state[(state[index1] + state[index2]) % 256]); + } else if (typeof data[char] === 'number') { + finalKey += String.fromCharCode(data[char] ^ state[(state[index1] + state[index2]) % 256]); + } } - - const decoded = new Uint8Array(str.length); - let i = 0; - let k = 0; - for (let index = 0; index < str.length; index += 1) { - i = (i + 1) & 0xff; - k = (k + s[i]) & 0xff; - [s[i], s[k]] = [s[k], s[i]]; - const t = (s[i] + s[k]) & 0xff; - decoded[index] = str[index] ^ s[t]; - } - - return decoded; + return finalKey; }; export const decryptSourceUrl = (sourceUrl: string) => { const encoded = decodeBase64UrlSafe(sourceUrl); - const decoded = decode(encoded); - const decodedText = new TextDecoder().decode(decoded); - - return decodeURIComponent(decodeURIComponent(decodedText)); + const decoded = decodeData(DECRYPTION_KEY, encoded); + return decodeURIComponent(decodeURIComponent(decoded)); }; From 9276694ead47d46f8a2d8f8c693f75fb17bcfdbb Mon Sep 17 00:00:00 2001 From: Jorrin Date: Fri, 5 Jan 2024 19:52:28 +0100 Subject: [PATCH 11/11] remove flag --- src/providers/sources/vidsrcto/index.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/providers/sources/vidsrcto/index.ts b/src/providers/sources/vidsrcto/index.ts index 983fd12..b85b068 100644 --- a/src/providers/sources/vidsrcto/index.ts +++ b/src/providers/sources/vidsrcto/index.ts @@ -1,6 +1,5 @@ import { load } from 'cheerio'; -import { flags } from '@/entrypoint/utils/targets'; import { SourcererEmbed, SourcererOutput, makeSourcerer } from '@/providers/base'; import { MovieScrapeContext, ShowScrapeContext } from '@/utils/context'; @@ -80,6 +79,6 @@ export const vidSrcToScraper = makeSourcerer({ name: 'VidSrcTo', scrapeMovie: universalScraper, scrapeShow: universalScraper, - flags: [flags.CORS_ALLOWED], + flags: [], rank: 400, });