From d2233481c2393a036bc23110f5bf456e7836e9d1 Mon Sep 17 00:00:00 2001 From: TPN Date: Sun, 16 Jun 2024 15:01:19 +0530 Subject: [PATCH] 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: [], + }, + ], + }; + }, +});