mirror of
https://github.com/p-stream/providers.git
synced 2026-05-21 14:22:25 +00:00
125 lines
3.5 KiB
TypeScript
125 lines
3.5 KiB
TypeScript
import { load } from 'cheerio';
|
|
import crypto from 'crypto-js';
|
|
|
|
import { flags } from '@/entrypoint/utils/targets';
|
|
import { makeEmbed } from '@/providers/base';
|
|
import { convertPlaylistsToDataUrls } from '@/utils/playlist';
|
|
|
|
const { AES, MD5 } = crypto;
|
|
|
|
// I didn't even care to take a look at the code
|
|
// it poabably could be better,
|
|
// i don't care
|
|
// Thanks Paradox_77
|
|
function mahoaData(input: string, key: string) {
|
|
const a = AES.encrypt(input, key).toString();
|
|
|
|
const b = a
|
|
.replace('U2FsdGVkX1', '')
|
|
.replace(/\//g, '|a')
|
|
.replace(/\+/g, '|b')
|
|
.replace(/\\=/g, '|c')
|
|
.replace(/\|/g, '-z');
|
|
return b;
|
|
}
|
|
|
|
function caesarShift(str: string, amount: number) {
|
|
if (amount < 0) {
|
|
return caesarShift(str, amount + 26);
|
|
}
|
|
let output = '';
|
|
for (let i = 0; i < str.length; i++) {
|
|
let c = str[i];
|
|
if (c.match(/[a-z]/i)) {
|
|
const code = str.charCodeAt(i);
|
|
if (code >= 65 && code <= 90) {
|
|
c = String.fromCharCode(((code - 65 + amount) % 26) + 65);
|
|
} else if (code >= 97 && code <= 122) {
|
|
c = String.fromCharCode(((code - 97 + amount) % 26) + 97);
|
|
}
|
|
}
|
|
output += c;
|
|
}
|
|
return output;
|
|
}
|
|
|
|
function stringToHex(tmp: string) {
|
|
let str = '';
|
|
for (let i = 0; i < tmp.length; i++) {
|
|
str += tmp[i].charCodeAt(0).toString(16);
|
|
}
|
|
return str;
|
|
}
|
|
|
|
function generateResourceToken(idUser: string, idFile: string, domainRef: string) {
|
|
const dataToken = stringToHex(
|
|
caesarShift(mahoaData(`Win32|${idUser}|${idFile}|${domainRef}`, MD5('plhq@@@2022').toString()), 22),
|
|
);
|
|
const resourceToken = `${dataToken}|${MD5(`${dataToken}plhq@@@22`).toString()}`;
|
|
return resourceToken;
|
|
}
|
|
|
|
const apiUrl = 'https://api-post-iframe-rd.playm4u.xyz/api/playiframe';
|
|
|
|
type apiRes = {
|
|
status: number;
|
|
// i only came across url-m3u8
|
|
type: 'url-m3u8';
|
|
data: string;
|
|
cache: boolean;
|
|
sub?: string | undefined;
|
|
subs?: string | undefined;
|
|
};
|
|
|
|
export const playm4uNMScraper = makeEmbed({
|
|
id: 'playm4u-nm',
|
|
name: 'PlayM4U',
|
|
rank: 240,
|
|
scrape: async (ctx) => {
|
|
// ex: https://play9str.playm4u.xyz/play/648f159ba3115a6f00744a16
|
|
const mainPage$ = load(await ctx.proxiedFetcher<string>(ctx.url));
|
|
|
|
const script = mainPage$(`script:contains("${apiUrl}")`).text();
|
|
if (!script) throw new Error('Failed to get script');
|
|
|
|
ctx.progress(50);
|
|
|
|
const domainRef = 'https://ww2.m4ufree.tv';
|
|
const idFile = script.match(/var\s?idfile\s?=\s?"(.*)";/im)?.[1];
|
|
const idUser = script.match(/var\s?iduser\s?=\s?"(.*)";/im)?.[1];
|
|
if (!idFile || !idUser) throw new Error('Failed to get ids');
|
|
|
|
const charecters = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789=+';
|
|
|
|
const apiRes: apiRes = await ctx.proxiedFetcher<apiRes>(apiUrl, {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
},
|
|
body: JSON.stringify({
|
|
namekey: 'playm4u03',
|
|
token: Array.from({ length: 100 }, () => charecters.charAt(Math.floor(Math.random() * charecters.length))).join(
|
|
'',
|
|
),
|
|
referrer: domainRef,
|
|
data: generateResourceToken(idUser, idFile, domainRef),
|
|
}),
|
|
});
|
|
|
|
if (!apiRes.data || apiRes.type !== 'url-m3u8') throw new Error('Failed to get the stream');
|
|
|
|
ctx.progress(100);
|
|
|
|
return {
|
|
stream: [
|
|
{
|
|
id: 'primary',
|
|
type: 'hls',
|
|
playlist: await convertPlaylistsToDataUrls(ctx.proxiedFetcher, apiRes.data),
|
|
captions: [],
|
|
flags: [flags.CORS_ALLOWED],
|
|
},
|
|
],
|
|
};
|
|
},
|
|
});
|