mirror of
https://github.com/p-stream/providers.git
synced 2026-05-12 19:11:00 +00:00
104 lines
3 KiB
TypeScript
104 lines
3 KiB
TypeScript
import { makeEmbed } from '@/providers/base';
|
|
import { createM3U8ProxyUrl } from '@/utils/proxy';
|
|
|
|
// 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);
|
|
|
|
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];
|
|
|
|
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 encodedJuiceKey = JSON.parse(
|
|
await ctx.proxiedFetcher('/api/cucked/juice_key', {
|
|
baseUrl,
|
|
headers: {
|
|
referer: ctx.url,
|
|
'User-Agent':
|
|
'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/135.0.0.0 Safari/537.36',
|
|
Accept: '*/*',
|
|
'Accept-Language': 'en-US,en;q=0.9',
|
|
Connection: 'keep-alive',
|
|
'Content-Type': 'application/json',
|
|
'X-Turbo': 'TurboVidClient',
|
|
'Sec-Fetch-Dest': 'empty',
|
|
'Sec-Fetch-Mode': 'cors',
|
|
'Sec-Fetch-Site': 'same-origin',
|
|
},
|
|
}),
|
|
).juice;
|
|
|
|
if (!encodedJuiceKey) throw new Error('Failed to fetch the key');
|
|
|
|
const juiceKey = atob(encodedJuiceKey);
|
|
|
|
ctx.progress(60);
|
|
|
|
const data = JSON.parse(
|
|
await ctx.proxiedFetcher('/api/cucked/the_juice_v2/', {
|
|
baseUrl,
|
|
query: {
|
|
[apkey]: xxid,
|
|
},
|
|
headers: {
|
|
'User-Agent':
|
|
'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/135.0.0.0 Safari/537.36',
|
|
Accept: '*/*',
|
|
'Accept-Language': 'en-US,en;q=0.9',
|
|
Connection: 'keep-alive',
|
|
'Content-Type': 'application/json',
|
|
'X-Turbo': 'TurboVidClient',
|
|
'Sec-Fetch-Dest': 'empty',
|
|
'Sec-Fetch-Mode': 'cors',
|
|
'Sec-Fetch-Site': 'same-origin',
|
|
referer: ctx.url,
|
|
},
|
|
}),
|
|
).data;
|
|
|
|
if (!data) throw new Error('Failed to fetch required data');
|
|
|
|
ctx.progress(90);
|
|
|
|
const playlist = decrypt(data, juiceKey);
|
|
|
|
const streamHeaders = {
|
|
referer: `${baseUrl}/`,
|
|
origin: baseUrl,
|
|
};
|
|
|
|
return {
|
|
stream: [
|
|
{
|
|
type: 'hls',
|
|
id: 'primary',
|
|
playlist: createM3U8ProxyUrl(playlist, streamHeaders),
|
|
flags: [],
|
|
captions: [],
|
|
},
|
|
],
|
|
};
|
|
},
|
|
});
|