From 6054a2d187f8a03953bd6e2a88c67920045356a7 Mon Sep 17 00:00:00 2001 From: TPN Date: Sun, 16 Jun 2024 13:43:56 +0530 Subject: [PATCH 1/6] fix nepu --- src/providers/sources/nepu/index.ts | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/src/providers/sources/nepu/index.ts b/src/providers/sources/nepu/index.ts index 0e19721..d5805ac 100644 --- a/src/providers/sources/nepu/index.ts +++ b/src/providers/sources/nepu/index.ts @@ -1,5 +1,6 @@ import { load } from 'cheerio'; +import { flags } from '@/entrypoint/utils/targets'; import { SourcererOutput, makeSourcerer } from '@/providers/base'; import { compareTitle } from '@/utils/compare'; import { MovieScrapeContext, ShowScrapeContext } from '@/utils/context'; @@ -7,8 +8,7 @@ import { NotFoundError } from '@/utils/errors'; import { SearchResults } from './types'; -const nepuBase = 'https://nepu.to'; -const nepuReferer = `${nepuBase}/`; +const nepuBase = 'https://nepu.io'; const universalScraper = async (ctx: MovieScrapeContext | ShowScrapeContext) => { const searchResultRequest = await ctx.proxiedFetcher('/ajax/posts', { @@ -63,11 +63,7 @@ const universalScraper = async (ctx: MovieScrapeContext | ShowScrapeContext) => captions: [], playlist: streamUrl[1], type: 'hls', - flags: [], - headers: { - Origin: nepuBase, - Referer: nepuReferer, - }, + flags: [flags.CORS_ALLOWED], }, ], } as SourcererOutput; @@ -77,8 +73,7 @@ export const nepuScraper = makeSourcerer({ id: 'nepu', name: 'Nepu', rank: 80, - flags: [], - disabled: true, + flags: [flags.CORS_ALLOWED], scrapeMovie: universalScraper, scrapeShow: universalScraper, }); From 26d32b5531ef476c5e2a7cdf4bd700fd3e473adc Mon Sep 17 00:00:00 2001 From: TPN Date: Sun, 16 Jun 2024 14:01:05 +0530 Subject: [PATCH 2/6] Add catflix source --- src/providers/all.ts | 2 + src/providers/sources/catflix.ts | 66 ++++++++++++++++++++++++++++++++ 2 files changed, 68 insertions(+) create mode 100644 src/providers/sources/catflix.ts diff --git a/src/providers/all.ts b/src/providers/all.ts index 90903a6..cc93cad 100644 --- a/src/providers/all.ts +++ b/src/providers/all.ts @@ -13,6 +13,7 @@ import { upstreamScraper } from '@/providers/embeds/upstream'; import { vidsrcembedScraper } from '@/providers/embeds/vidsrc'; import { vTubeScraper } from '@/providers/embeds/vtube'; import { autoembedScraper } from '@/providers/sources/autoembed'; +import { catflixScraper } from '@/providers/sources/catflix'; import { flixhqScraper } from '@/providers/sources/flixhq/index'; import { goMoviesScraper } from '@/providers/sources/gomovies/index'; import { insertunitScraper } from '@/providers/sources/insertunit'; @@ -65,6 +66,7 @@ import { warezcdnScraper } from './sources/warezcdn'; export function gatherAllSources(): Array { // all sources are gathered here return [ + catflixScraper, flixhqScraper, remotestreamScraper, kissAsianScraper, diff --git a/src/providers/sources/catflix.ts b/src/providers/sources/catflix.ts new file mode 100644 index 0000000..0f10bc7 --- /dev/null +++ b/src/providers/sources/catflix.ts @@ -0,0 +1,66 @@ +import { load } from 'cheerio'; + +import { SourcererOutput, makeSourcerer } from '@/providers/base'; +import { compareMedia } from '@/utils/compare'; +import { MovieScrapeContext, ShowScrapeContext } from '@/utils/context'; +import { NotFoundError } from '@/utils/errors'; + +const baseUrl = 'https://catflix.su'; + +async function comboScraper(ctx: ShowScrapeContext | MovieScrapeContext): Promise { + const searchPage = await ctx.proxiedFetcher('/', { + baseUrl, + query: { + s: ctx.media.title, + }, + }); + + const $search = load(searchPage); + const searchResults: { title: string; year?: number | undefined; url: string }[] = []; + + $search('li').each((_, element) => { + const title = $search(element).find('h2').first().text().trim(); + // the year is always present, but I sitll decided to make it nullable since the impl isn't as future-proof + const year = Number($search(element).find('.text-xs > span').eq(1).text().trim()) || undefined; + const url = $search(element).find('a').attr('href'); + + if (!title || !url) return; + + searchResults.push({ title, year, url }); + }); + + let watchPageUrl = searchResults.find((x) => x && compareMedia(ctx.media, x.title, x.year))?.url; + if (!watchPageUrl) throw new NotFoundError('No watchable item found'); + + if (ctx.media.type === 'show') { + const match = watchPageUrl.match(/\/series\/([^/]+)\/?/); + if (!match) throw new Error('Failed to parse watch page url'); + watchPageUrl = watchPageUrl.replace( + `/series/${match[1]}`, + `/episode/${match[1]}-${ctx.media.season.number}x${ctx.media.episode.number}`, + ); + } + + const watchPage = load(await ctx.proxiedFetcher(watchPageUrl)); + + const url = watchPage('iframe').first().attr('src'); // I couldn't think of a better way + if (!url) throw new Error('Failed to find embed url'); + + return { + embeds: [ + { + embedId: 'turbovid', + url, + }, + ], + }; +} + +export const catflixScraper = makeSourcerer({ + id: 'catflix', + name: 'Catflix', + rank: 122, + flags: [], + scrapeMovie: comboScraper, + scrapeShow: comboScraper, +}); From d2233481c2393a036bc23110f5bf456e7836e9d1 Mon Sep 17 00:00:00 2001 From: TPN Date: Sun, 16 Jun 2024 15:01:19 +0530 Subject: [PATCH 3/6] Add turbovid embed --- src/providers/all.ts | 2 + src/providers/embeds/turbovid.ts | 74 ++++++++++++++++++++++++++++++++ 2 files changed, 76 insertions(+) create mode 100644 src/providers/embeds/turbovid.ts diff --git a/src/providers/all.ts b/src/providers/all.ts index cc93cad..fa56f43 100644 --- a/src/providers/all.ts +++ b/src/providers/all.ts @@ -8,6 +8,7 @@ import { mixdropScraper } from '@/providers/embeds/mixdrop'; import { mp4uploadScraper } from '@/providers/embeds/mp4upload'; import { streambucketScraper } from '@/providers/embeds/streambucket'; import { streamsbScraper } from '@/providers/embeds/streamsb'; +import { turbovidScraper } from '@/providers/embeds/turbovid'; import { upcloudScraper } from '@/providers/embeds/upcloud'; import { upstreamScraper } from '@/providers/embeds/upstream'; import { vidsrcembedScraper } from '@/providers/embeds/vidsrc'; @@ -133,5 +134,6 @@ export function gatherAllEmbeds(): Array { autoembedBengaliScraper, autoembedTamilScraper, autoembedTeluguScraper, + turbovidScraper, ]; } diff --git a/src/providers/embeds/turbovid.ts b/src/providers/embeds/turbovid.ts new file mode 100644 index 0000000..184fd15 --- /dev/null +++ b/src/providers/embeds/turbovid.ts @@ -0,0 +1,74 @@ +import { flags } from '@/entrypoint/utils/targets'; +import { makeEmbed } from '@/providers/base'; + +// Thanks to Paradox_77 for helping with the decryption +function hexToChar(hex: string): string { + return String.fromCharCode(parseInt(hex, 16)); +} + +function decrypt(data: string, key: string): string { + const formatedData = data.match(/../g)?.map(hexToChar).join('') || ''; + return formatedData + .split('') + .map((char, i) => String.fromCharCode(char.charCodeAt(0) ^ key.charCodeAt(i % key.length))) + .join(''); +} + +export const turbovidScraper = makeEmbed({ + id: 'turbovid', + name: 'Turbovid', + rank: 122, + async scrape(ctx) { + const baseUrl = new URL(ctx.url).origin; + const embedPage = await ctx.proxiedFetcher(ctx.url); + + // the whitespace is for future-proofing the regex a bit + const apkey = embedPage.match(/const\s+apkey\s*=\s*"(.*?)";/)?.[1]; + const xxid = embedPage.match(/const\s+xxid\s*=\s*"(.*?)";/)?.[1]; + + if (!apkey || !xxid) throw new Error('Failed to get required values'); + + // json isn't parsed by proxiedFetcher due to content-type being text/html + const juiceKey = JSON.parse( + await ctx.proxiedFetcher('/api/cucked/juice_key', { + baseUrl, + headers: { + referer: ctx.url, + }, + }), + ).juice; + + if (!juiceKey) throw new Error('Failed to fetch the key'); + + const data = JSON.parse( + await ctx.proxiedFetcher('/api/cucked/the_juice/', { + baseUrl, + query: { + [apkey]: xxid, + }, + headers: { + referer: ctx.url, + }, + }), + ).data; + + if (!data) throw new Error('Failed to fetch required data'); + + const playlist = decrypt(data, juiceKey); + + return { + stream: [ + { + type: 'hls', + id: 'primary', + playlist, + headers: { + referer: baseUrl, + }, + flags: [flags.CORS_ALLOWED], + captions: [], + }, + ], + }; + }, +}); From 837a6da0b4c0d200209f4b59dd2d07ff7d112b0b Mon Sep 17 00:00:00 2001 From: TPN Date: Sun, 16 Jun 2024 15:08:41 +0530 Subject: [PATCH 4/6] Add context progress calls for catflix --- src/providers/embeds/turbovid.ts | 6 ++++++ src/providers/sources/catflix.ts | 8 ++++++++ 2 files changed, 14 insertions(+) diff --git a/src/providers/embeds/turbovid.ts b/src/providers/embeds/turbovid.ts index 184fd15..717a2da 100644 --- a/src/providers/embeds/turbovid.ts +++ b/src/providers/embeds/turbovid.ts @@ -22,6 +22,8 @@ export const turbovidScraper = makeEmbed({ const baseUrl = new URL(ctx.url).origin; const embedPage = await ctx.proxiedFetcher(ctx.url); + ctx.progress(30); + // the whitespace is for future-proofing the regex a bit const apkey = embedPage.match(/const\s+apkey\s*=\s*"(.*?)";/)?.[1]; const xxid = embedPage.match(/const\s+xxid\s*=\s*"(.*?)";/)?.[1]; @@ -40,6 +42,8 @@ export const turbovidScraper = makeEmbed({ if (!juiceKey) throw new Error('Failed to fetch the key'); + ctx.progress(60); + const data = JSON.parse( await ctx.proxiedFetcher('/api/cucked/the_juice/', { baseUrl, @@ -54,6 +58,8 @@ export const turbovidScraper = makeEmbed({ if (!data) throw new Error('Failed to fetch required data'); + ctx.progress(90); + const playlist = decrypt(data, juiceKey); return { diff --git a/src/providers/sources/catflix.ts b/src/providers/sources/catflix.ts index 0f10bc7..03c9456 100644 --- a/src/providers/sources/catflix.ts +++ b/src/providers/sources/catflix.ts @@ -15,6 +15,8 @@ async function comboScraper(ctx: ShowScrapeContext | MovieScrapeContext): Promis }, }); + ctx.progress(40); + const $search = load(searchPage); const searchResults: { title: string; year?: number | undefined; url: string }[] = []; @@ -32,6 +34,8 @@ async function comboScraper(ctx: ShowScrapeContext | MovieScrapeContext): Promis let watchPageUrl = searchResults.find((x) => x && compareMedia(ctx.media, x.title, x.year))?.url; if (!watchPageUrl) throw new NotFoundError('No watchable item found'); + ctx.progress(60); + if (ctx.media.type === 'show') { const match = watchPageUrl.match(/\/series\/([^/]+)\/?/); if (!match) throw new Error('Failed to parse watch page url'); @@ -43,9 +47,13 @@ async function comboScraper(ctx: ShowScrapeContext | MovieScrapeContext): Promis const watchPage = load(await ctx.proxiedFetcher(watchPageUrl)); + ctx.progress(80); + const url = watchPage('iframe').first().attr('src'); // I couldn't think of a better way if (!url) throw new Error('Failed to find embed url'); + ctx.progress(90); + return { embeds: [ { From 19ddec8eed6fcd78c65f2020db18096c52d6e181 Mon Sep 17 00:00:00 2001 From: TPN Date: Wed, 3 Jul 2024 14:37:14 +0100 Subject: [PATCH 5/6] Update catflix --- src/providers/embeds/turbovid.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/providers/embeds/turbovid.ts b/src/providers/embeds/turbovid.ts index 717a2da..7f7e3fc 100644 --- a/src/providers/embeds/turbovid.ts +++ b/src/providers/embeds/turbovid.ts @@ -1,4 +1,3 @@ -import { flags } from '@/entrypoint/utils/targets'; import { makeEmbed } from '@/providers/base'; // Thanks to Paradox_77 for helping with the decryption @@ -71,7 +70,7 @@ export const turbovidScraper = makeEmbed({ headers: { referer: baseUrl, }, - flags: [flags.CORS_ALLOWED], + flags: [], captions: [], }, ], From f1b02800f73b0cdfe509d50a5dbedae719441be5 Mon Sep 17 00:00:00 2001 From: TPN Date: Wed, 3 Jul 2024 14:57:54 +0100 Subject: [PATCH 6/6] Disable Nepu --- src/providers/sources/nepu/index.ts | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/providers/sources/nepu/index.ts b/src/providers/sources/nepu/index.ts index d5805ac..f3be5a1 100644 --- a/src/providers/sources/nepu/index.ts +++ b/src/providers/sources/nepu/index.ts @@ -9,6 +9,7 @@ import { NotFoundError } from '@/utils/errors'; import { SearchResults } from './types'; const nepuBase = 'https://nepu.io'; +const nepuReferer = 'https://nepu.to'; const universalScraper = async (ctx: MovieScrapeContext | ShowScrapeContext) => { const searchResultRequest = await ctx.proxiedFetcher('/ajax/posts', { @@ -63,7 +64,11 @@ const universalScraper = async (ctx: MovieScrapeContext | ShowScrapeContext) => captions: [], playlist: streamUrl[1], type: 'hls', - flags: [flags.CORS_ALLOWED], + headers: { + Origin: nepuReferer, + Referer: `${nepuReferer}/`, + }, + flags: [], }, ], } as SourcererOutput; @@ -73,7 +78,8 @@ export const nepuScraper = makeSourcerer({ id: 'nepu', name: 'Nepu', rank: 80, - flags: [flags.CORS_ALLOWED], + disabled: true, + flags: [], scrapeMovie: universalScraper, scrapeShow: universalScraper, });