Major Update : Added 2Embed, Update api routes, update json response format.

This commit is contained in:
Inside4ndroid
2025-04-04 23:36:41 +01:00
parent 179c8c148f
commit ce1288490d
7 changed files with 1039 additions and 62 deletions
+116 -53
View File
@@ -1,5 +1,16 @@
import fetch from 'node-fetch';
function createErrorObject(errorMessage) {
return {
provider: "EmbedSU",
ERROR: [{
error: `ERROR`,
what_happened: errorMessage,
report_issue: 'https://github.com/Inside4ndroid/vidsrc-api-js/issues'
}]
};
}
export async function getEmbedSu(tmdb_id, s, e) {
const DOMAIN = "https://embed.su";
const headers = {
@@ -11,82 +22,133 @@ export async function getEmbedSu(tmdb_id, s, e) {
try {
const urlSearch = s && e ? `${DOMAIN}/embed/tv/${tmdb_id}/${s}/${e}` : `${DOMAIN}/embed/movie/${tmdb_id}`;
const htmlSearch = await fetch(urlSearch, { method: 'GET', headers });
const textSearch = await htmlSearch.text();
if (!htmlSearch.ok) {
return { sources: [createErrorObject(`Failed to fetch initial page: HTTP ${htmlSearch.status}`)] };
}
const textSearch = await htmlSearch.text();
const hashEncodeMatch = textSearch.match(/JSON\.parse\(atob\(\`([^\`]+)/i);
const hashEncode = hashEncodeMatch ? hashEncodeMatch[1] : "";
if (!hashEncode) return;
if (!hashEncode) {
return { sources: [createErrorObject("No encoded hash found in initial page")] };
}
let hashDecode;
try {
hashDecode = JSON.parse(await stringAtob(hashEncode));
} catch (e) {
return { sources: [createErrorObject(`Failed to decode initial hash: ${e.message}`)] };
}
const hashDecode = JSON.parse(await stringAtob(hashEncode));
const mEncrypt = hashDecode.hash;
if (!mEncrypt) return;
if (!mEncrypt) {
return { sources: [createErrorObject("No encrypted hash found in decoded data")] };
}
const firstDecode = (await stringAtob(mEncrypt)).split(".").map(item => item.split("").reverse().join(""));
const secondDecode = JSON.parse(await stringAtob(firstDecode.join("").split("").reverse().join("")));
let firstDecode;
try {
firstDecode = (await stringAtob(mEncrypt)).split(".").map(item => item.split("").reverse().join(""));
} catch (e) {
return { sources: [createErrorObject(`Failed to decode first layer: ${e.message}`)] };
}
if (!secondDecode || secondDecode.length === 0) return;
let secondDecode;
try {
secondDecode = JSON.parse(await stringAtob(firstDecode.join("").split("").reverse().join("")));
} catch (e) {
return { sources: [createErrorObject(`Failed to decode second layer: ${e.message}`)] };
}
if (!secondDecode || secondDecode.length === 0) {
return { sources: [createErrorObject("No valid sources found after decoding")] };
}
const sources = [];
const subtitles = [];
for (const item of secondDecode) {
const urlDirect = `${DOMAIN}/api/e/${item.hash}`;
const dataDirect = await requestGet(urlDirect, {
"Referer": DOMAIN,
"User-Agent": headers['User-Agent'],
"Origin": DOMAIN
});
if (!dataDirect.source) continue;
const tracks = dataDirect.subtitles.map(sub => ({
url: sub.file,
lang: sub.label.split('-')[0].trim()
})).filter(track => track.lang);
const requestDirectSize = await fetch(dataDirect.source, { headers, method: "GET" });
const parseRequest = await requestDirectSize.text();
const patternSize = parseRequest.split('\n').filter(item => item.includes('/proxy/'));
const directQuality = patternSize.map(patternItem => {
const sizeQuality = getSizeQuality(patternItem);
let dURL = `${DOMAIN}${patternItem}`;
dURL = dURL.replace(".png", ".m3u8");
return { file: dURL, type: 'hls', quality: `${sizeQuality}p`, lang: 'en' };
});
if (!directQuality.length) continue;
sources.push({
provider: "EmbedSu",
files: directQuality,
headers: {
try {
const urlDirect = `${DOMAIN}/api/e/${item.hash}`;
const dataDirect = await requestGet(urlDirect, {
"Referer": DOMAIN,
"User-Agent": headers['User-Agent'],
"Origin": DOMAIN
}
});
});
subtitles.push(...tracks);
if (!dataDirect || !dataDirect.source) {
console.warn(`No source found for hash ${item.hash}`);
continue;
}
const tracks = (dataDirect.subtitles || []).map(sub => ({
url: sub.file,
lang: sub.label ? sub.label.split('-')[0].trim() : 'en'
})).filter(track => track.url);
const requestDirectSize = await fetch(dataDirect.source, { headers, method: "GET" });
if (!requestDirectSize.ok) {
console.warn(`Failed to fetch source ${dataDirect.source}: HTTP ${requestDirectSize.status}`);
continue;
}
const parseRequest = await requestDirectSize.text();
const patternSize = parseRequest.split('\n').filter(item => item.includes('/proxy/'));
const directQuality = patternSize.map(patternItem => {
try {
const sizeQuality = getSizeQuality(patternItem);
let dURL = `${DOMAIN}${patternItem}`;
dURL = dURL.replace(".png", ".m3u8");
return { file: dURL, type: 'hls', quality: `${sizeQuality}p`, lang: 'en' };
} catch (e) {
console.warn(`Failed to process quality for pattern: ${patternItem}`, e);
return null;
}
}).filter(item => item !== null);
if (!directQuality.length) {
console.warn(`No valid qualities found for source ${dataDirect.source}`);
continue;
}
sources.push({
provider: "EmbedSu",
files: directQuality,
subtitles: tracks,
headers: {
"Referer": DOMAIN,
"User-Agent": headers['User-Agent'],
"Origin": DOMAIN
}
});
} catch (e) {
console.error(`Error processing item ${item.hash}:`, e);
// Continue to next item instead of failing entire request
}
}
return {
sources,
subtitles
};
if (sources.length === 0) {
return { sources: [createErrorObject("No valid sources found after processing all items")] };
}
return { sources };
} catch (e) {
return { provider: "EmbedSu", sources: [], subtitles: [] };
return { sources: [createErrorObject(`Unexpected error: ${e.message}`)] };
}
}
function getSizeQuality(url) {
const parts = url.split('/');
const base64Part = parts[parts.length - 2];
const decodedPart = atob(base64Part);
const sizeQuality = Number(decodedPart) || 1080;
return sizeQuality;
try {
const parts = url.split('/');
const base64Part = parts[parts.length - 2];
const decodedPart = atob(base64Part);
return Number(decodedPart) || 1080;
} catch (e) {
console.warn(`Failed to get size quality for URL ${url}:`, e);
return 720; // Default quality if parsing fails
}
}
async function stringAtob(input) {
@@ -115,6 +177,7 @@ async function requestGet(url, headers = {}) {
return await response.json();
} catch (error) {
return "";
console.error(`Request failed for ${url}:`, error);
return null;
}
}