Update embedHandler.js

This commit is contained in:
shafat-96
2025-07-26 11:49:14 +06:00
committed by GitHub
parent faf27594f4
commit e7cf468521

View File

@@ -1,159 +1,147 @@
import axios from 'axios'; const axios = require('axios');
const crypto = require('node:crypto');
const MAIN_URL = "https://megacloud.blog"; class EmbedSource {
const KEY_URL = "https://raw.githubusercontent.com/yogesh-hacker/MegacloudKeys/refs/heads/main/keys.json"; constructor(file, sourceType) {
const DECODE_URL = "https://script.google.com/macros/s/AKfycbx-yHTwupis_JD0lNzoOnxYcEYeXmJZrg7JeMxYnEZnLBy5V0--UxEvP-y9txHyy1TX9Q/exec"; this.file = file;
const USER_AGENT = this.type = sourceType;
"Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/137.0.0.0 Mobile Safari/537.36";
export type track = {
file: string;
label?: string;
kind: string;
default?: boolean;
};
export type unencryptedSrc = {
file: string;
type: string;
};
export type extractedSrc = {
sources: string | unencryptedSrc[];
tracks: track[];
t: number;
server: number;
};
type ExtractedData = Pick<extractedSrc, "tracks" | "t" | "server"> & {
sources: { file: string; type: string }[];
};
// 🛠 Decrypt using Google Apps Script
async function decryptWithGoogleScript(
encryptedData: string,
nonce: string,
secret: string
): Promise<string> {
try {
const params = new URLSearchParams({
encrypted_data: encryptedData,
nonce: nonce,
secret: secret,
});
const { data } = await axios.get(`${DECODE_URL}?${params.toString()}`);
const fileMatch = data.match(/"file":"(.*?)"/)?.[1];
if (!fileMatch) throw new Error('Video URL not found in decrypted response');
return fileMatch;
} catch (error: any) {
console.error('Google Apps Script decryption failed:', error.message);
throw error;
} }
} }
// 🧠 Extract nonce from embed HTML class Track {
function extractNonce(html: string): string | null { constructor(file, label, kind, isDefault = false) {
const match1 = html.match(/\b[a-zA-Z0-9]{48}\b/); this.file = file;
if (match1) return match1[0]; this.label = label;
this.kind = kind;
const match2 = html.match(/\b([a-zA-Z0-9]{16})\b.*?\b([a-zA-Z0-9]{16})\b.*?\b([a-zA-Z0-9]{16})\b/); if (isDefault) {
if (match2) return match2[1] + match2[2] + match2[3]; this.default = isDefault;
}
return null; }
} }
export class MegaCloud { class EmbedSources {
static async extract(url: string): Promise<{ sources: any[]; tracks?: track[] }> { constructor(sources = [], tracks = [], t = 0, server = 1, intro = null, outro = null) {
try { this.sources = sources;
const embedUrl = new URL(url); this.tracks = tracks;
const instance = new MegaCloud(); this.t = t;
const result = await instance.extract2(embedUrl); this.server = server;
if (intro) this.intro = intro;
if (outro) this.outro = outro;
}
}
// Constants for Megacloud
const MEGACLOUD_URL = 'https://megacloud.blog';
const KEY_URL = 'https://raw.githubusercontent.com/yogesh-hacker/MegacloudKeys/refs/heads/main/keys.json';
// --- OpenSSL-compatible key+IV derivation (same algorithm used by Megacloud)
function opensslKeyIv(password, salt, keyLen = 32, ivLen = 16) {
let d = Buffer.alloc(0);
let prev = Buffer.alloc(0);
while (d.length < keyLen + ivLen) {
const hash = crypto.createHash('md5');
hash.update(Buffer.concat([prev, password, salt]));
prev = hash.digest();
d = Buffer.concat([d, prev]);
}
return { return {
sources: result.sources, key: d.subarray(0, keyLen),
tracks: result.tracks, iv: d.subarray(keyLen, keyLen + ivLen),
};
} catch (err: any) {
console.error("MegaCloud extraction error:", err.message);
return { sources: [] };
}
}
async extract2(embedIframeURL: URL): Promise<ExtractedData> {
const extractedData: ExtractedData = {
sources: [],
tracks: [],
t: 0,
server: 0,
}; };
}
// --- Decrypt OpenSSL AES-CBC encoded Base64 payloads
function decryptOpenSSL(encBase64, password) {
try { try {
const id = embedIframeURL.pathname.split("/").pop()?.split("?")[0] || ""; const data = Buffer.from(encBase64, 'base64');
let nonce: string | null = null; if (data.subarray(0, 8).toString() !== 'Salted__') return null;
const { data: html } = await axios.get<string>(embedIframeURL.href, { const salt = data.subarray(8, 16);
headers: { const { key, iv } = opensslKeyIv(Buffer.from(password, 'utf8'), salt);
Accept: '*/*', const decipher = crypto.createDecipheriv('aes-256-cbc', key, iv);
'X-Requested-With': 'XMLHttpRequest', const decrypted = Buffer.concat([
Referer: MAIN_URL, decipher.update(data.subarray(16)),
'User-Agent': USER_AGENT, decipher.final(),
}, ]);
}); return decrypted.toString('utf-8');
} catch (err) {
console.error('Decryption error:', err.message);
return null;
}
}
nonce = extractNonce(html); // --- Helpers
if (!nonce) throw new Error("Nonce not found"); function extractId(url) {
return url.split('/').pop().split('?')[0];
}
async function getDecryptionKey() {
try {
const res = await axios.get(KEY_URL);
return typeof res.data === 'string' ? JSON.parse(res.data).mega : res.data?.mega;
} catch (e) {
console.error('Failed to fetch key:', e.message);
return null;
}
}
const handleEmbed = async (embedUrl, referrer = 'https://megacloud.blog') => {
try {
if (!embedUrl) throw new Error('embedUrl is required');
const id = extractId(embedUrl);
const apiUrl = `${MEGACLOUD_URL}/embed-2/v2/e-1/getSources?id=${id}`;
const apiUrl = `${MAIN_URL}/embed-1/v3/e-1/getSources?id=${id}&_k=${nonce}`;
const headers = { const headers = {
Accept: '*/*', Referer: referrer || embedUrl,
'X-Requested-With': 'XMLHttpRequest', Origin: 'https://megacloud.blog/',
Referer: MAIN_URL, 'User-Agent': 'Mozilla/5.0',
'User-Agent': USER_AGENT,
}; };
const { data } = await axios.get<extractedSrc>(apiUrl, { headers }); const { data } = await axios.get(apiUrl, { headers });
if (!data) return extractedData; if (!data?.sources) throw new Error('No sources field in response');
// 🔐 Encrypted source (string) // Parse/Decrypt sources array
let rawSources;
if (typeof data.sources === 'string') { if (typeof data.sources === 'string') {
try { try {
if (data.sources.includes('.m3u8')) { rawSources = JSON.parse(data.sources);
extractedData.sources = [{ file: data.sources, type: 'hls' }]; } catch (_) {
const key = await getDecryptionKey();
if (!key) throw new Error('Failed to fetch decryption key');
const decrypted = decryptOpenSSL(data.sources, key);
if (!decrypted) throw new Error('Decryption failed');
rawSources = JSON.parse(decrypted);
}
} else if (Array.isArray(data.sources)) {
rawSources = data.sources;
} else { } else {
const { data: keyData } = await axios.get(KEY_URL); throw new Error('Unexpected sources format');
const secret = keyData?.mega;
if (!secret) throw new Error('No decryption key found for MegaCloud');
const decryptedUrl = await decryptWithGoogleScript(data.sources, nonce, secret);
extractedData.sources = [{ file: decryptedUrl, type: 'hls' }];
}
} catch (err: any) {
console.error("Failed to decrypt video source:", err.message);
}
} }
// 🔓 Unencrypted source (array) if (!rawSources.length) throw new Error('No valid sources found');
else if (Array.isArray(data.sources)) {
extractedData.sources = data.sources.map(src => ({
file: src.file,
type: src.type || 'hls',
}));
}
// 🧾 Subtitles/tracks const sources = rawSources.map((s) => new EmbedSource(s.file, s.type || s.quality || 'unknown'));
extractedData.tracks = (data.tracks || []).filter(track => const tracks = (data.tracks || []).map((t) => new Track(t.file, t.label, t.kind, t.default));
track.kind === 'captions' || track.kind === 'subtitles'
return new EmbedSources(
sources,
tracks,
data.t ?? 0,
'megacloud',
data.intro ?? null,
data.outro ?? null
); );
extractedData.t = data.t || 0; } catch (error) {
extractedData.server = data.server || 0; throw error;
}
};
return extractedData; module.exports = {
} catch (err: any) { handleEmbed,
console.error("Extraction error in extract2:", err.message); EmbedSource,
return extractedData; Track,
} EmbedSources
} };
}