mirror of
https://github.com/shafat-96/anicrush-api.git
synced 2026-04-17 15:51:44 +00:00
fix source
This commit is contained in:
230
embedHandler.js
230
embedHandler.js
@@ -1,147 +1,107 @@
|
||||
const axios = require('axios');
|
||||
const crypto = require('node:crypto');
|
||||
const axios = require("axios");
|
||||
const crypto = require("crypto");
|
||||
|
||||
class EmbedSource {
|
||||
constructor(file, sourceType) {
|
||||
this.file = file;
|
||||
this.type = sourceType;
|
||||
}
|
||||
}
|
||||
|
||||
class Track {
|
||||
constructor(file, label, kind, isDefault = false) {
|
||||
this.file = file;
|
||||
this.label = label;
|
||||
this.kind = kind;
|
||||
if (isDefault) {
|
||||
this.default = isDefault;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class EmbedSources {
|
||||
constructor(sources = [], tracks = [], t = 0, server = 1, intro = null, outro = null) {
|
||||
this.sources = sources;
|
||||
this.tracks = tracks;
|
||||
this.t = t;
|
||||
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';
|
||||
const KEY_URL = "https://raw.githubusercontent.com/yogesh-hacker/MegacloudKeys/refs/heads/main/keys.json";
|
||||
const DECODE_URL = "https://script.google.com/macros/s/AKfycbx-yHTwupis_JD0lNzoOnxYcEYeXmJZrg7JeMxYnEZnLBy5V0--UxEvP-y9txHyy1TX9Q/exec";
|
||||
const UA = "Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/137.0.0.0 Mobile Safari/537.36";
|
||||
|
||||
// --- 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);
|
||||
let cachedKey = null;
|
||||
|
||||
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]);
|
||||
function extractNonce(html) {
|
||||
const match1 = html.match(/\b[a-zA-Z0-9]{48}\b/);
|
||||
if (match1) return match1[0];
|
||||
|
||||
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 (match2) return match2.groupValues ? match2.groupValues[1] + match2.groupValues[2] + match2.groupValues[3] : match2[1] + match2[2] + match2[3];
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
async function fetchKey() {
|
||||
if (cachedKey) return cachedKey;
|
||||
|
||||
try {
|
||||
const { data } = await axios.get(KEY_URL, { headers: { "User-Agent": UA } });
|
||||
cachedKey = data?.mega;
|
||||
return cachedKey;
|
||||
} catch (error) {
|
||||
console.error("Failed to fetch key:", error.message);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
async function decryptWithGoogleScript(encryptedData, nonce, key) {
|
||||
const fullUrl = `${DECODE_URL}?encrypted_data=${encodeURIComponent(encryptedData)}&nonce=${encodeURIComponent(nonce)}&secret=${encodeURIComponent(key)}`;
|
||||
|
||||
const { data } = await axios.get(fullUrl);
|
||||
const match = data.match(/"file":"(.*?)"/);
|
||||
if (!match) throw new Error("Video URL not found in decrypted response");
|
||||
|
||||
return match[1];
|
||||
}
|
||||
|
||||
async function extract(embedUrl) {
|
||||
try {
|
||||
const headers = {
|
||||
"Accept": "*/*",
|
||||
"X-Requested-With": "XMLHttpRequest",
|
||||
"Referer": MEGACLOUD_URL,
|
||||
"User-Agent": UA
|
||||
};
|
||||
|
||||
const id = embedUrl.split("/").pop().split("?")[0];
|
||||
|
||||
const { data: html } = await axios.get(embedUrl, { headers });
|
||||
const nonce = extractNonce(html);
|
||||
|
||||
if (!nonce) {
|
||||
throw new Error("Could not extract nonce from embed page");
|
||||
}
|
||||
|
||||
const apiUrl = `${MEGACLOUD_URL}/embed-2/v3/e-1/getSources?id=${id}&_k=${nonce}`;
|
||||
const { data: response } = await axios.get(apiUrl, { headers });
|
||||
|
||||
if (!response || !response.sources) {
|
||||
throw new Error("No sources found in API response");
|
||||
}
|
||||
|
||||
const encoded = response.sources;
|
||||
let m3u8Url;
|
||||
|
||||
if (encoded.includes(".m3u8")) {
|
||||
m3u8Url = encoded;
|
||||
} else {
|
||||
const key = await fetchKey();
|
||||
if (!key) {
|
||||
throw new Error("Could not fetch decryption key");
|
||||
}
|
||||
|
||||
m3u8Url = await decryptWithGoogleScript(encoded, nonce, key);
|
||||
}
|
||||
|
||||
return {
|
||||
key: d.subarray(0, keyLen),
|
||||
iv: d.subarray(keyLen, keyLen + ivLen),
|
||||
sources: [{ file: m3u8Url, type: "hls" }],
|
||||
tracks: response.tracks || [],
|
||||
t: response.t || 0,
|
||||
server: response.server || 0,
|
||||
intro: response.intro || null,
|
||||
outro: response.outro || null
|
||||
};
|
||||
|
||||
} catch (error) {
|
||||
console.error("MegaCloud extraction failed:", error.message);
|
||||
return {
|
||||
sources: [],
|
||||
tracks: [],
|
||||
t: 0,
|
||||
server: 0
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// --- Decrypt OpenSSL AES-CBC encoded Base64 payloads
|
||||
function decryptOpenSSL(encBase64, password) {
|
||||
try {
|
||||
const data = Buffer.from(encBase64, 'base64');
|
||||
if (data.subarray(0, 8).toString() !== 'Salted__') return null;
|
||||
|
||||
const salt = data.subarray(8, 16);
|
||||
const { key, iv } = opensslKeyIv(Buffer.from(password, 'utf8'), salt);
|
||||
const decipher = crypto.createDecipheriv('aes-256-cbc', key, iv);
|
||||
const decrypted = Buffer.concat([
|
||||
decipher.update(data.subarray(16)),
|
||||
decipher.final(),
|
||||
]);
|
||||
return decrypted.toString('utf-8');
|
||||
} catch (err) {
|
||||
console.error('Decryption error:', err.message);
|
||||
return null;
|
||||
}
|
||||
async function handleEmbed(embedUrl, referrer) {
|
||||
return await extract(embedUrl);
|
||||
}
|
||||
|
||||
// --- Helpers
|
||||
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 headers = {
|
||||
Referer: referrer || embedUrl,
|
||||
Origin: 'https://megacloud.blog/',
|
||||
'User-Agent': 'Mozilla/5.0',
|
||||
};
|
||||
|
||||
const { data } = await axios.get(apiUrl, { headers });
|
||||
if (!data?.sources) throw new Error('No sources field in response');
|
||||
|
||||
// Parse/Decrypt sources array
|
||||
let rawSources;
|
||||
if (typeof data.sources === 'string') {
|
||||
try {
|
||||
rawSources = JSON.parse(data.sources);
|
||||
} 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 {
|
||||
throw new Error('Unexpected sources format');
|
||||
}
|
||||
|
||||
if (!rawSources.length) throw new Error('No valid sources found');
|
||||
|
||||
const sources = rawSources.map((s) => new EmbedSource(s.file, s.type || s.quality || 'unknown'));
|
||||
const tracks = (data.tracks || []).map((t) => new Track(t.file, t.label, t.kind, t.default));
|
||||
|
||||
return new EmbedSources(
|
||||
sources,
|
||||
tracks,
|
||||
data.t ?? 0,
|
||||
'megacloud',
|
||||
data.intro ?? null,
|
||||
data.outro ?? null
|
||||
);
|
||||
} catch (error) {
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
handleEmbed,
|
||||
EmbedSource,
|
||||
Track,
|
||||
EmbedSources
|
||||
};
|
||||
module.exports = { extract, handleEmbed };
|
||||
|
||||
Reference in New Issue
Block a user