Bump versions to 0.3.17 for English and German Dubs; update global extractor to version 1.2.0 with enhanced stream URL extraction and new extractor functions for oneupload, packer, and smoothpre. Refactor stream handling to support headers and improve error handling in fetch requests.

This commit is contained in:
JMcrafter26
2026-01-03 19:42:27 +01:00
parent 71f7d30321
commit 92f3a20556
21 changed files with 2467 additions and 1295 deletions
+1 -1
View File
@@ -5,7 +5,7 @@
"name": "50/50 & Cufiy", "name": "50/50 & Cufiy",
"icon": "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQ3122kQwublLkZ6rf1fEpUP79BxZOFmH9BSA&s" "icon": "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQ3122kQwublLkZ6rf1fEpUP79BxZOFmH9BSA&s"
}, },
"version": "1.2.13", "version": "1.2.14",
"language": "German (DUB/SUB)", "language": "German (DUB/SUB)",
"streamType": "MP4", "streamType": "MP4",
"quality": "1080p", "quality": "1080p",
+222 -110
View File
@@ -313,7 +313,7 @@ async function sendLog(message) {
// EDITING THIS FILE COULD BREAK THE UPDATER AND CAUSE ISSUES WITH THE EXTRACTOR // EDITING THIS FILE COULD BREAK THE UPDATER AND CAUSE ISSUES WITH THE EXTRACTOR
/* {GE START} */ /* {GE START} */
/* {VERSION: 1.1.8} */ /* {VERSION: 1.2.0} */
/** /**
* @name global_extractor.js * @name global_extractor.js
@@ -321,8 +321,8 @@ async function sendLog(message) {
* @author Cufiy * @author Cufiy
* @url https://github.com/JMcrafter26/sora-global-extractor * @url https://github.com/JMcrafter26/sora-global-extractor
* @license CUSTOM LICENSE - see https://github.com/JMcrafter26/sora-global-extractor/blob/main/LICENSE * @license CUSTOM LICENSE - see https://github.com/JMcrafter26/sora-global-extractor/blob/main/LICENSE
* @date 2025-11-05 15:44:57 * @date 2026-01-03 19:28:28
* @version 1.1.8 * @version 1.2.0
* @note This file was generated automatically. * @note This file was generated automatically.
* The global extractor comes with an auto-updating feature, so you can always get the latest version. https://github.com/JMcrafter26/sora-global-extractor#-auto-updater * The global extractor comes with an auto-updating feature, so you can always get the latest version. https://github.com/JMcrafter26/sora-global-extractor#-auto-updater
*/ */
@@ -332,12 +332,20 @@ function globalExtractor(providers) {
for (const [url, provider] of Object.entries(providers)) { for (const [url, provider] of Object.entries(providers)) {
try { try {
const streamUrl = extractStreamUrlByProvider(url, provider); const streamUrl = extractStreamUrlByProvider(url, provider);
// check if streamUrl is an object with streamUrl property
if (streamUrl && typeof streamUrl === "object" && !Array.isArray(streamUrl) && streamUrl.streamUrl) {
return streamUrl.streamUrl;
}
// check if streamUrl is not null, a string, and starts with http or https // check if streamUrl is not null, a string, and starts with http or https
if (streamUrl && typeof streamUrl === "string" && (streamUrl.startsWith("http"))) { if (
streamUrl &&
typeof streamUrl === "string" &&
streamUrl.startsWith("http")
) {
return streamUrl; return streamUrl;
// if its an array, get the value that starts with http // if its an array, get the value that starts with http
} else if (Array.isArray(streamUrl)) { } else if (Array.isArray(streamUrl)) {
const httpStream = streamUrl.find(url => url.startsWith("http")); const httpStream = streamUrl.find((url) => url.startsWith("http"));
if (httpStream) { if (httpStream) {
return httpStream; return httpStream;
} }
@@ -345,7 +353,6 @@ function globalExtractor(providers) {
// check if it's a valid stream URL // check if it's a valid stream URL
return null; return null;
} }
} catch (error) { } catch (error) {
// Ignore the error and try the next provider // Ignore the error and try the next provider
} }
@@ -357,16 +364,30 @@ async function multiExtractor(providers) {
/* this scheme should be returned as a JSON object /* this scheme should be returned as a JSON object
{ {
"streams": [ "streams": [
"FileMoon", {
"https://filemoon.example/stream1.m3u8", "title": "FileMoon",
"StreamWish", "streamUrl": "https://filemoon.example/stream1.m3u8",
"https://streamwish.example/stream2.m3u8", },
"Okru", {
"https://okru.example/stream3.m3u8", "title": "StreamWish",
"MP4", "streamUrl": "https://streamwish.example/stream2.m3u8",
"https://mp4upload.example/stream4.mp4", },
"Default", {
"https://default.example/stream5.m3u8" "title": "Okru",
"streamUrl": "https://okru.example/stream3.m3u8",
"headers": { // Optional headers for the stream
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.3",
"Referer": "https://okru.example/",
},
},
{
"title": "MP4",
"streamUrl": "https://mp4upload.example/stream4.mp4",
},
{
"title": "Default",
"streamUrl": "https://default.example/stream5.m3u8"
}
] ]
} }
*/ */
@@ -378,20 +399,21 @@ async function multiExtractor(providers) {
// if provider starts with "direct-", then add the url to the streams array directly // if provider starts with "direct-", then add the url to the streams array directly
if (provider.startsWith("direct-")) { if (provider.startsWith("direct-")) {
const directName = provider.slice(7); // remove "direct-" prefix const directName = provider.slice(7); // remove "direct-" prefix
if (directName && directName.length > 0) { const title = (directName && directName.length > 0) ? directName : "Direct";
streams.push(directName, url); streams.push({
} else { title: title,
streams.push("Direct", url); // fallback to "Direct" if no name is provided streamUrl: url
} });
continue; // skip to the next provider continue; // skip to the next provider
} }
if (provider.startsWith("direct")) { if (provider.startsWith("direct")) {
provider = provider.slice(7); // remove "direct-" prefix provider = provider.slice(7); // remove "direct-" prefix
if (provider && provider.length > 0) { const title = (provider && provider.length > 0) ? provider : "Direct";
streams.push(provider, url); streams.push({
} else { title: title,
streams.push("Direct", url); // fallback to "Direct" if no name is provided streamUrl: url
} });
continue; // skip to the next provider
} }
let customName = null; // to store the custom name if provided let customName = null; // to store the custom name if provided
@@ -408,15 +430,24 @@ async function multiExtractor(providers) {
console.log(`Skipping ${provider} as it has already 3 streams`); console.log(`Skipping ${provider} as it has already 3 streams`);
continue; continue;
} }
let streamUrl = await extractStreamUrlByProvider(url, provider); let result = await extractStreamUrlByProvider(url, provider);
let streamUrl = null;
let headers = null;
if (streamUrl && Array.isArray(streamUrl)) { // Check if result is an object with streamUrl and optional headers
const httpStream = streamUrl.find(url => url.startsWith("http")); if (result && typeof result === "object" && !Array.isArray(result) && result.streamUrl) {
streamUrl = result.streamUrl;
headers = result.headers || null;
} else if (result && Array.isArray(result)) {
const httpStream = result.find((url) => url.startsWith("http"));
if (httpStream) { if (httpStream) {
streamUrl = httpStream; streamUrl = httpStream;
} }
} else if (result && typeof result === "string") {
streamUrl = result;
} }
// check if provider is already in streams, if it is, add a number to it
// check if streamUrl is valid
if ( if (
!streamUrl || !streamUrl ||
typeof streamUrl !== "string" || typeof streamUrl !== "string" ||
@@ -430,22 +461,29 @@ async function multiExtractor(providers) {
provider = customName; provider = customName;
} }
let title;
if (providersCount[provider]) { if (providersCount[provider]) {
providersCount[provider]++; providersCount[provider]++;
streams.push( title = provider.charAt(0).toUpperCase() +
provider.charAt(0).toUpperCase() +
provider.slice(1) + provider.slice(1) +
"-" + "-" +
(providersCount[provider] - 1), // add a number to the provider name (providersCount[provider] - 1); // add a number to the provider name
streamUrl
);
} else { } else {
providersCount[provider] = 1; providersCount[provider] = 1;
streams.push( title = provider.charAt(0).toUpperCase() + provider.slice(1);
provider.charAt(0).toUpperCase() + provider.slice(1),
streamUrl
);
} }
const streamObject = {
title: title,
streamUrl: streamUrl
};
// Add headers if they exist
if (headers && typeof headers === "object" && Object.keys(headers).length > 0) {
streamObject.headers = headers;
}
streams.push(streamObject);
} catch (error) { } catch (error) {
// Ignore the error and try the next provider // Ignore the error and try the next provider
} }
@@ -456,33 +494,64 @@ async function multiExtractor(providers) {
async function extractStreamUrlByProvider(url, provider) { async function extractStreamUrlByProvider(url, provider) {
if (eval(`typeof ${provider}Extractor`) !== "function") { if (eval(`typeof ${provider}Extractor`) !== "function") {
// skip if the extractor is not defined // skip if the extractor is not defined
console.log(`Extractor for provider ${provider} is not defined, skipping...`); console.log(
`Extractor for provider ${provider} is not defined, skipping...`
);
return null; return null;
} }
let uas = [
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.3",
"Mozilla/5.0 (iPhone; CPU iPhone OS 18_1_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/18.1.1 Mobile/15E148 Safari/604.1",
"Mozilla/5.0 (Linux; Android 10; SM-G973F) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Mobile Safari/537.36",
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.3",
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.1.2 Safari/605.1.15",
"Mozilla/5.0 (Linux; Android 11; Pixel 4 XL) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Mobile Safari/537.36",
];
let headers = { let headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.3", "User-Agent": uas[(url.length + provider.length) % uas.length], // use a different user agent based on the url and provider
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8", "Accept":
"text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8",
"Accept-Language": "en-US,en;q=0.5", "Accept-Language": "en-US,en;q=0.5",
"Referer": url, "Referer": url,
"Connection": "keep-alive", "Connection": "keep-alive",
"x-Requested-With": "XMLHttpRequest" "x-Requested-With": "XMLHttpRequest",
}; };
if(provider == 'bigwarp') {
switch (provider) {
case "bigwarp":
delete headers["User-Agent"]; delete headers["User-Agent"];
headers["x-requested-with"] = "XMLHttpRequest"; break;
} else if (provider == 'vk') { case "vk":
case "sibnet":
headers["encoding"] = "windows-1251"; // required headers["encoding"] = "windows-1251"; // required
} else if (provider == 'sibnet') { break;
headers["encoding"] = "windows-1251"; // required case "supervideo":
} else if (provider == 'supervideo') { case "savefiles":
delete headers["User-Agent"]; headers = {
"Accept": "*/*",
"Accept-Encoding": "gzip, deflate, br",
"User-Agent": "EchoapiRuntime/1.1.0",
"Connection": "keep-alive",
"Cache-Control": "no-cache",
"Host": url.match(/https?:\/\/([^\/]+)/)[1],
};
break;
case "streamtape":
headers = {
"User-Agent":
"Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:145.0) Gecko/20100101 Firefox/145.0",
"Accept":
"text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
};
break;
} }
// console.log("Using headers: " + JSON.stringify(headers));
// fetch the url // fetch the url
// and pass the response to the extractor function // and pass the response to the extractor function
console.log("Fetching URL: " + url); console.log("Fetching URL: " + url);
const response = await soraFetch(url, { const response = await soraFetch(url, {
headers headers,
}); });
console.log("Response: " + response.status); console.log("Response: " + response.status);
@@ -490,33 +559,27 @@ async function extractStreamUrlByProvider(url, provider) {
// if title contains redirect, then get the redirect url // if title contains redirect, then get the redirect url
const title = html.match(/<title>(.*?)<\/title>/); const title = html.match(/<title>(.*?)<\/title>/);
if (title && title[1].toLowerCase().includes("redirect")) { if (title && title[1].toLowerCase().includes("redirect")) {
const redirectUrl = html.match(/<meta http-equiv="refresh" content="0;url=(.*?)"/); const matches = [
const redirectUrl2 = html.match(/window\.location\.href\s*=\s*["'](.*?)["']/); /<meta http-equiv="refresh" content="0;url=(.*?)"/,
const redirectUrl3 = html.match(/window\.location\.replace\s*\(\s*["'](.*?)["']\s*\)/); /window\.location\.href\s*=\s*["'](.*?)["']/,
if (redirectUrl) { /window\.location\.replace\s*\(\s*["'](.*?)["']\s*\)/,
console.log("Redirect URL: " + redirectUrl[1]); /window\.location\s*=\s*["'](.*?)["']/,
/window\.location\.assign\s*\(\s*["'](.*?)["']\s*\)/,
/top\.location\s*=\s*["'](.*?)["']/,
/top\.location\.replace\s*\(\s*["'](.*?)["']\s*\)/,
];
for (const match of matches) {
const redirectUrl = html.match(match);
if (redirectUrl && redirectUrl[1] && typeof redirectUrl[1] === "string" && redirectUrl[1].startsWith("http")) {
console.log("Redirect URL found: " + redirectUrl[1]);
url = redirectUrl[1]; url = redirectUrl[1];
headers['Referer'] = url;
headers['Host'] = url.match(/https?:\/\/([^\/]+)/)[1];
html = await soraFetch(url, { html = await soraFetch(url, {
headers headers,
}); }).then((res) => res.text());
html = html.text ? await html.text() : html; break;
}
} else if (redirectUrl2) {
console.log("Redirect URL 2: " + redirectUrl2[1]);
url = redirectUrl2[1];
html = await soraFetch(url, {
headers
});
html = html.text ? await html.text() : html;
} else if (redirectUrl3) {
console.log("Redirect URL 3: " + redirectUrl3[1]);
url = redirectUrl3[1];
html = await soraFetch(url, {
headers
});
html = html.text ? await html.text() : html;
} else {
console.log("No redirect URL found");
} }
} }
@@ -571,6 +634,20 @@ async function extractStreamUrlByProvider(url, provider) {
console.log("Error extracting stream URL from mp4upload:", error); console.log("Error extracting stream URL from mp4upload:", error);
return null; return null;
} }
case "oneupload":
try {
return await oneuploadExtractor(html, url);
} catch (error) {
console.log("Error extracting stream URL from oneupload:", error);
return null;
}
case "packer":
try {
return await packerExtractor(html, url);
} catch (error) {
console.log("Error extracting stream URL from packer:", error);
return null;
}
case "sendvid": case "sendvid":
try { try {
return await sendvidExtractor(html, url); return await sendvidExtractor(html, url);
@@ -585,6 +662,13 @@ async function extractStreamUrlByProvider(url, provider) {
console.log("Error extracting stream URL from sibnet:", error); console.log("Error extracting stream URL from sibnet:", error);
return null; return null;
} }
case "smoothpre":
try {
return await smoothpreExtractor(html, url);
} catch (error) {
console.log("Error extracting stream URL from smoothpre:", error);
return null;
}
case "streamtape": case "streamtape":
try { try {
return await streamtapeExtractor(html, url); return await streamtapeExtractor(html, url);
@@ -599,13 +683,6 @@ async function extractStreamUrlByProvider(url, provider) {
console.log("Error extracting stream URL from streamup:", error); console.log("Error extracting stream URL from streamup:", error);
return null; return null;
} }
case "supervideo":
try {
return await supervideoExtractor(html, url);
} catch (error) {
console.log("Error extracting stream URL from supervideo:", error);
return null;
}
case "uploadcx": case "uploadcx":
try { try {
return await uploadcxExtractor(html, url); return await uploadcxExtractor(html, url);
@@ -654,7 +731,6 @@ async function extractStreamUrlByProvider(url, provider) {
} }
} }
//////////////////////////////////////////////// ////////////////////////////////////////////////
// EXTRACTORS // // EXTRACTORS //
//////////////////////////////////////////////// ////////////////////////////////////////////////
@@ -1141,7 +1217,6 @@ async function megacloudExtractor(html, embedUrl) {
* @author Cufiy * @author Cufiy
*/ */
async function mp4uploadExtractor(html, url = null) { async function mp4uploadExtractor(html, url = null) {
// src: "https://a4.mp4upload.com:183/d/xkx3b4etz3b4quuo66rbmyqtjjoivahfxp27f35pti45rzapbvj5xwb4wuqtlpewdz4dirfp/video.mp4"
const regex = /src:\s*"([^"]+)"/; const regex = /src:\s*"([^"]+)"/;
const match = html.match(regex); const match = html.match(regex);
if (match) { if (match) {
@@ -1151,6 +1226,32 @@ async function mp4uploadExtractor(html, url = null) {
return null; return null;
} }
} }
/* --- oneupload --- */
/**
* @name oneuploadExtractor
* @author 50/50
*/
async function oneuploadExtractor(data, url = null) {
const match = data.match(/sources:\s*\[\{file:"([^"]+)"\}\]/);
const fileUrl = match ? match[1] : null;
return fileUrl;
}
/* --- packer --- */
/* {REQUIRED PLUGINS: unbaser} */
/**
* @name packerExtractor
* @author 50/50
*/
async function packerExtractor(data, url = null) {
const obfuscatedScript = data.match(/<script[^>]*>\s*(eval\(function\(p,a,c,k,e,d.*?\)[\s\S]*?)<\/script>/);
const unpackedScript = unpack(obfuscatedScript[1]);
const m3u8Match = unpackedScript.match(/"hls2"\s*:\s*"([^"]+)"/);
const m3u8Url = m3u8Match[1];
return m3u8Url;
}
/* --- sendvid --- */ /* --- sendvid --- */
/** /**
@@ -1186,6 +1287,29 @@ async function sibnetExtractor(html, embedUrl) {
return null; return null;
} }
} }
/* --- smoothpre --- */
/* {REQUIRED PLUGINS: unbaser} */
/**
* @name SmoothPre Extractor
* @author 50/50
*/
async function smoothpreExtractor(data, url = null) {
console.log("Using SmoothPre Extractor");
console.log("Data Length: " + data.length);
const obfuscatedScript = data.match(/<script[^>]*>\s*(eval\(function\(p,a,c,k,e,d.*?\)[\s\S]*?)<\/script>/);
if (!obfuscatedScript || !obfuscatedScript[1]) {
console.log("No obfuscated script found");
return null;
}
const unpackedScript = unpack(obfuscatedScript[1]);
const hls2Match = unpackedScript.match(/"hls2"\s*:\s*"([^"]+)"/);
const hls2Url = hls2Match ? hls2Match[1] : null;
return hls2Url;
}
/* --- streamtape --- */ /* --- streamtape --- */
/** /**
@@ -1269,26 +1393,6 @@ async function streamupExtractor(data, url = null) {
return null; return null;
} }
} }
/* --- supervideo --- */
/* {REQUIRED PLUGINS: unbaser} */
/**
* @name SuperVideo Extractor
* @author 50/50
*/
async function supervideoExtractor(data, url = null) {
const obfuscatedScript = data.match(/<script[^>]*>\s*(eval\(function\(p,a,c,k,e,d.*?\)[\s\S]*?)<\/script>/);
const unpackedScript = unpack(obfuscatedScript[1]);
const regex = /file:\s*"([^"]+\.m3u8)"/;
const match = regex.exec(unpackedScript);
if (match) {
const fileUrl = match[1];
console.log("File URL:" + fileUrl);
return fileUrl;
}
return "No stream found";
}
/* --- uploadcx --- */ /* --- uploadcx --- */
/** /**
@@ -1353,7 +1457,7 @@ async function vidmolyExtractor(html, url = null) {
const streamUrl = iframeMatch[1].startsWith("//") const streamUrl = iframeMatch[1].startsWith("//")
? "https:" + iframeMatch[1] ? "https:" + iframeMatch[1]
: iframeMatch[1]; : iframeMatch[1];
const responseTwo = await fetchv2(streamUrl); const responseTwo = await soraFetch(streamUrl);
const htmlTwo = await responseTwo.text(); const htmlTwo = await responseTwo.text();
const m3u8Match = htmlTwo.match(/sources:\s*\[\{file:"([^"]+\.m3u8)"/); const m3u8Match = htmlTwo.match(/sources:\s*\[\{file:"([^"]+\.m3u8)"/);
return m3u8Match ? m3u8Match[1] : null; return m3u8Match ? m3u8Match[1] : null;
@@ -1479,6 +1583,7 @@ function voeShiftChars(str, shift) {
.join(""); .join("");
} }
//////////////////////////////////////////////// ////////////////////////////////////////////////
// PLUGINS // // PLUGINS //
//////////////////////////////////////////////// ////////////////////////////////////////////////
@@ -1496,14 +1601,22 @@ function voeShiftChars(str, shift) {
* @returns {Promise<Response|null>} The response from the server, or null if the * @returns {Promise<Response|null>} The response from the server, or null if the
* request failed. * request failed.
*/ */
async function soraFetch(url, options = { headers: {}, method: 'GET', body: null }) { async function soraFetch(
url,
options = { headers: {}, method: "GET", body: null }
) {
try { try {
return await fetchv2(url, options.headers ?? {}, options.method ?? 'GET', options.body ?? null); return await fetchv2(
url,
options.headers ?? {},
options.method ?? "GET",
options.body ?? null
);
} catch (e) { } catch (e) {
try { try {
return await fetch(url, options); return await fetch(url, options);
} catch (error) { } catch (error) {
await console.log('soraFetch error: ' + error.message); await console.log("soraFetch error: " + error.message);
return null; return null;
} }
} }
@@ -1611,5 +1724,4 @@ function unpack(source) {
} }
/* {GE END} */ /* {GE END} */
+4 -3
View File
@@ -2,10 +2,11 @@
"sourceName": "AniWorld (ENG SUB)", "sourceName": "AniWorld (ENG SUB)",
"iconUrl": "https://git.luna-app.eu/50n50/sources/raw/branch/main/aniworld/aniworld.png", "iconUrl": "https://git.luna-app.eu/50n50/sources/raw/branch/main/aniworld/aniworld.png",
"author": { "author": {
"name": "Hamzo & Cufiy", "name": "Cufiy & Hamzo",
"icon": "https://cdn.discordapp.com/avatars/623644371819954226/591ecab10b0b4535e859bb0b9bbe62e5?size=1024" "icon": "https://files.catbox.moe/ttj4fc.gif",
"url": "https://github.com/JMcrafter26"
}, },
"version": "0.2.8", "version": "0.3.1",
"language": "English (SUB)", "language": "English (SUB)",
"streamType": "HLS", "streamType": "HLS",
"quality": "720p", "quality": "720p",
+4 -3
View File
@@ -3,15 +3,16 @@
"iconUrl": "https://git.luna-app.eu/50n50/sources/raw/branch/main/aniworld/aniworld.png", "iconUrl": "https://git.luna-app.eu/50n50/sources/raw/branch/main/aniworld/aniworld.png",
"author": { "author": {
"name": "Cufiy", "name": "Cufiy",
"icon": "https://cdn.discordapp.com/avatars/623644371819954226/591ecab10b0b4535e859bb0b9bbe62e5?size=1024" "icon": "https://files.catbox.moe/ttj4fc.gif",
"url": "https://github.com/JMcrafter26"
}, },
"version": "0.2.52", "version": "0.3.194",
"language": "German (DUB)", "language": "German (DUB)",
"streamType": "HLS", "streamType": "HLS",
"quality": "720p", "quality": "720p",
"baseUrl": "https://vidmoly.to/", "baseUrl": "https://vidmoly.to/",
"searchBaseUrl": "https://aniworld.to/ajax/seriesSearch?keyword=%s", "searchBaseUrl": "https://aniworld.to/ajax/seriesSearch?keyword=%s",
"scriptUrl": "http://192.168.2.130/sora-module-repos/sources/aniworld/v2/AniWorldGerDub_v2.js", "scriptUrl": "http://192.168.2.130/sora-module-repos/sources-fork/aniworld/v2/AniWorldGerDub_v2.js",
"asyncJS": true, "asyncJS": true,
"type": "anime", "type": "anime",
"supportsMojuru": true, "supportsMojuru": true,
+4 -3
View File
@@ -2,10 +2,11 @@
"sourceName": "AniWorld (GER DUB)", "sourceName": "AniWorld (GER DUB)",
"iconUrl": "https://git.luna-app.eu/50n50/sources/raw/branch/main/aniworld/aniworld.png", "iconUrl": "https://git.luna-app.eu/50n50/sources/raw/branch/main/aniworld/aniworld.png",
"author": { "author": {
"name": "Hamzo & Cufiy", "name": "Cufiy & Hamzo",
"icon": "https://cdn.discordapp.com/avatars/623644371819954226/591ecab10b0b4535e859bb0b9bbe62e5?size=1024" "icon": "https://files.catbox.moe/ttj4fc.gif",
"url": "https://github.com/JMcrafter26"
}, },
"version": "0.2.8", "version": "0.3.1",
"language": "German (DUB)", "language": "German (DUB)",
"streamType": "HLS", "streamType": "HLS",
"quality": "720p", "quality": "720p",
+4 -3
View File
@@ -2,10 +2,11 @@
"sourceName": "AniWorld (GER SUB)", "sourceName": "AniWorld (GER SUB)",
"iconUrl": "https://git.luna-app.eu/50n50/sources/raw/branch/main/aniworld/aniworld.png", "iconUrl": "https://git.luna-app.eu/50n50/sources/raw/branch/main/aniworld/aniworld.png",
"author": { "author": {
"name": "Hamzo & Cufiy", "name": "Cufiy & Hamzo",
"icon": "https://cdn.discordapp.com/avatars/623644371819954226/591ecab10b0b4535e859bb0b9bbe62e5?size=1024" "icon": "https://files.catbox.moe/ttj4fc.gif",
"url": "https://github.com/JMcrafter26"
}, },
"version": "0.2.8", "version": "0.3.1",
"language": "German (SUB)", "language": "German (SUB)",
"streamType": "HLS", "streamType": "HLS",
"quality": "720p", "quality": "720p",
+250 -123
View File
@@ -6,9 +6,9 @@ async function searchResults(keyword) {
try { try {
const encodedKeyword = encodeURIComponent(keyword); const encodedKeyword = encodeURIComponent(keyword);
const searchApiUrl = `https://aniworld.to/ajax/seriesSearch?keyword=${encodedKeyword}`; const searchApiUrl = `https://aniworld.to/ajax/seriesSearch?keyword=${encodedKeyword}`;
const responseText = await fetch(searchApiUrl); const responseText = await soraFetch(searchApiUrl);
// console.log("Search API Response: " + await responseText.text()); // console.log("Search API Response: " + await responseText.text());
const data = await JSON.parse(responseText); const data = await responseText.json() || await JSON.parse(responseText);
console.log("Search API Data: ", data); console.log("Search API Data: ", data);
const transformedResults = data.map((anime) => ({ const transformedResults = data.map((anime) => ({
@@ -84,9 +84,9 @@ async function extractEpisodes(url) {
} }
// Replace the field "number" with the current index of each item, starting from 1 // Replace the field "number" with the current index of each item, starting from 1
finishedList.forEach((item, index) => { // finishedList.forEach((item, index) => {
item.number = index + 1; // item.number = index + 1;
}); // });
return JSON.stringify(finishedList); return JSON.stringify(finishedList);
} catch (error) { } catch (error) {
@@ -99,6 +99,7 @@ async function extractStreamUrl(url) {
try { try {
const baseUrl = "https://aniworld.to"; const baseUrl = "https://aniworld.to";
const fetchUrl = `${url}`; const fetchUrl = `${url}`;
sendLog("Fetching URL: " + fetchUrl);
const response = await fetch(fetchUrl); const response = await fetch(fetchUrl);
const text = response.text ? await response.text() : response; const text = response.text ? await response.text() : response;
@@ -177,9 +178,7 @@ function selectHoster(finishedList) {
// Define the preferred providers and languages // Define the preferred providers and languages
const providerList = ["VOE", "Filemoon", "SpeedFiles", "Vidmoly", "DoodStream", "Vidoza", "mp4upload"]; const providerList = ["VOE", "Filemoon", "SpeedFiles", "Vidmoly", "DoodStream", "Vidoza", "mp4upload"];
const languageList = ["mit Untertitel Englisch", "mit Untertitel Deutsch", "Deutsch"]; const languageList = ["mit Untertitel Englisch", "Englisch", "mit Untertitel Deutsch", "Deutsch"];
for (const language of languageList) { for (const language of languageList) {
for (const providerName of providerList) { for (const providerName of providerList) {
@@ -249,23 +248,39 @@ async function fetchSeasonEpisodes(url) {
const response = await fetch(fetchUrl); const response = await fetch(fetchUrl);
const text = response.text ? await response.text() : response; const text = response.text ? await response.text() : response;
// if is filme, e.g. https://aniworld.to/anime/stream/jujutsu-kaisen/filme
let isFilme = false;
if (url.endsWith("/filme") || url.includes("/filme/")) {
isFilme = true;
}
// Updated regex to allow empty <strong> content // Updated regex to allow empty <strong> content
const regex = const regex =
/<td class="seasonEpisodeTitle">\s*<a[^>]*href="([^"]+)"[^>]*>.*?<strong>([^<]*)<\/strong>.*?<span>([^<]+)<\/span>.*?<\/a>/g; /<td class="seasonEpisodeTitle">\s*<a[^>]*href="([^"]+)"[^>]*>.*?<strong>([^<]*)<\/strong>.*?<span>([^<]+)<\/span>.*?<\/a>/g;
const matches = []; const matches = [];
let match; let match;
let holderNumber = 0; let number = 0;
while ((match = regex.exec(text)) !== null) { while ((match = regex.exec(text)) !== null) {
const [_, link] = match; const [_, link, titleRaw, span] = match;
matches.push({ number: holderNumber, href: `${baseUrl}${link}` }); number += 1;
// sendLog("Episode found:", { number, link, title, span });
let title = titleRaw.trim() || span.trim();
if (isFilme) {
title = `[FILM] ${title || span.trim() || "Untitled"}`;
} }
matches.push({ number, href: `${baseUrl}${link}`, title});
}
sendLog("Season Episodes:" + JSON.stringify(matches));
return matches; return matches;
} catch (error) { } catch (error) {
sendLog("FetchSeasonEpisodes helper function error:" + error); sendLog("FetchSeasonEpisodes helper function error:" + error);
return [{ number: "0", href: "https://error.org" }]; return [{ number: "0", href: "https://error.org", title: "Error" }];
} }
} }
@@ -337,7 +352,7 @@ function base64Decode(str) {
async function sendLog(message) { async function sendLog(message) {
// send http://192.168.2.130/sora-module/log.php?action=add&message=message // send http://192.168.2.130/sora-module/log.php?action=add&message=message
console.log(message); console.log(message);
return; // return;
await fetch('http://192.168.2.130/sora-module/log.php?action=add&message=' + encodeURIComponent(message)) await fetch('http://192.168.2.130/sora-module/log.php?action=add&message=' + encodeURIComponent(message))
.catch(error => { .catch(error => {
@@ -350,7 +365,7 @@ async function sendLog(message) {
// EDITING THIS FILE COULD BREAK THE UPDATER AND CAUSE ISSUES WITH THE EXTRACTOR // EDITING THIS FILE COULD BREAK THE UPDATER AND CAUSE ISSUES WITH THE EXTRACTOR
/* {GE START} */ /* {GE START} */
/* {VERSION: 1.1.8} */ /* {VERSION: 1.2.0} */
/** /**
* @name global_extractor.js * @name global_extractor.js
@@ -358,8 +373,8 @@ async function sendLog(message) {
* @author Cufiy * @author Cufiy
* @url https://github.com/JMcrafter26/sora-global-extractor * @url https://github.com/JMcrafter26/sora-global-extractor
* @license CUSTOM LICENSE - see https://github.com/JMcrafter26/sora-global-extractor/blob/main/LICENSE * @license CUSTOM LICENSE - see https://github.com/JMcrafter26/sora-global-extractor/blob/main/LICENSE
* @date 2025-11-05 15:44:57 * @date 2026-01-03 19:28:28
* @version 1.1.8 * @version 1.2.0
* @note This file was generated automatically. * @note This file was generated automatically.
* The global extractor comes with an auto-updating feature, so you can always get the latest version. https://github.com/JMcrafter26/sora-global-extractor#-auto-updater * The global extractor comes with an auto-updating feature, so you can always get the latest version. https://github.com/JMcrafter26/sora-global-extractor#-auto-updater
*/ */
@@ -369,12 +384,20 @@ function globalExtractor(providers) {
for (const [url, provider] of Object.entries(providers)) { for (const [url, provider] of Object.entries(providers)) {
try { try {
const streamUrl = extractStreamUrlByProvider(url, provider); const streamUrl = extractStreamUrlByProvider(url, provider);
// check if streamUrl is an object with streamUrl property
if (streamUrl && typeof streamUrl === "object" && !Array.isArray(streamUrl) && streamUrl.streamUrl) {
return streamUrl.streamUrl;
}
// check if streamUrl is not null, a string, and starts with http or https // check if streamUrl is not null, a string, and starts with http or https
if (streamUrl && typeof streamUrl === "string" && (streamUrl.startsWith("http"))) { if (
streamUrl &&
typeof streamUrl === "string" &&
streamUrl.startsWith("http")
) {
return streamUrl; return streamUrl;
// if its an array, get the value that starts with http // if its an array, get the value that starts with http
} else if (Array.isArray(streamUrl)) { } else if (Array.isArray(streamUrl)) {
const httpStream = streamUrl.find(url => url.startsWith("http")); const httpStream = streamUrl.find((url) => url.startsWith("http"));
if (httpStream) { if (httpStream) {
return httpStream; return httpStream;
} }
@@ -382,7 +405,6 @@ function globalExtractor(providers) {
// check if it's a valid stream URL // check if it's a valid stream URL
return null; return null;
} }
} catch (error) { } catch (error) {
// Ignore the error and try the next provider // Ignore the error and try the next provider
} }
@@ -394,16 +416,30 @@ async function multiExtractor(providers) {
/* this scheme should be returned as a JSON object /* this scheme should be returned as a JSON object
{ {
"streams": [ "streams": [
"FileMoon", {
"https://filemoon.example/stream1.m3u8", "title": "FileMoon",
"StreamWish", "streamUrl": "https://filemoon.example/stream1.m3u8",
"https://streamwish.example/stream2.m3u8", },
"Okru", {
"https://okru.example/stream3.m3u8", "title": "StreamWish",
"MP4", "streamUrl": "https://streamwish.example/stream2.m3u8",
"https://mp4upload.example/stream4.mp4", },
"Default", {
"https://default.example/stream5.m3u8" "title": "Okru",
"streamUrl": "https://okru.example/stream3.m3u8",
"headers": { // Optional headers for the stream
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.3",
"Referer": "https://okru.example/",
},
},
{
"title": "MP4",
"streamUrl": "https://mp4upload.example/stream4.mp4",
},
{
"title": "Default",
"streamUrl": "https://default.example/stream5.m3u8"
}
] ]
} }
*/ */
@@ -415,20 +451,21 @@ async function multiExtractor(providers) {
// if provider starts with "direct-", then add the url to the streams array directly // if provider starts with "direct-", then add the url to the streams array directly
if (provider.startsWith("direct-")) { if (provider.startsWith("direct-")) {
const directName = provider.slice(7); // remove "direct-" prefix const directName = provider.slice(7); // remove "direct-" prefix
if (directName && directName.length > 0) { const title = (directName && directName.length > 0) ? directName : "Direct";
streams.push(directName, url); streams.push({
} else { title: title,
streams.push("Direct", url); // fallback to "Direct" if no name is provided streamUrl: url
} });
continue; // skip to the next provider continue; // skip to the next provider
} }
if (provider.startsWith("direct")) { if (provider.startsWith("direct")) {
provider = provider.slice(7); // remove "direct-" prefix provider = provider.slice(7); // remove "direct-" prefix
if (provider && provider.length > 0) { const title = (provider && provider.length > 0) ? provider : "Direct";
streams.push(provider, url); streams.push({
} else { title: title,
streams.push("Direct", url); // fallback to "Direct" if no name is provided streamUrl: url
} });
continue; // skip to the next provider
} }
let customName = null; // to store the custom name if provided let customName = null; // to store the custom name if provided
@@ -445,15 +482,24 @@ async function multiExtractor(providers) {
console.log(`Skipping ${provider} as it has already 3 streams`); console.log(`Skipping ${provider} as it has already 3 streams`);
continue; continue;
} }
let streamUrl = await extractStreamUrlByProvider(url, provider); let result = await extractStreamUrlByProvider(url, provider);
let streamUrl = null;
let headers = null;
if (streamUrl && Array.isArray(streamUrl)) { // Check if result is an object with streamUrl and optional headers
const httpStream = streamUrl.find(url => url.startsWith("http")); if (result && typeof result === "object" && !Array.isArray(result) && result.streamUrl) {
streamUrl = result.streamUrl;
headers = result.headers || null;
} else if (result && Array.isArray(result)) {
const httpStream = result.find((url) => url.startsWith("http"));
if (httpStream) { if (httpStream) {
streamUrl = httpStream; streamUrl = httpStream;
} }
} else if (result && typeof result === "string") {
streamUrl = result;
} }
// check if provider is already in streams, if it is, add a number to it
// check if streamUrl is valid
if ( if (
!streamUrl || !streamUrl ||
typeof streamUrl !== "string" || typeof streamUrl !== "string" ||
@@ -467,22 +513,29 @@ async function multiExtractor(providers) {
provider = customName; provider = customName;
} }
let title;
if (providersCount[provider]) { if (providersCount[provider]) {
providersCount[provider]++; providersCount[provider]++;
streams.push( title = provider.charAt(0).toUpperCase() +
provider.charAt(0).toUpperCase() +
provider.slice(1) + provider.slice(1) +
"-" + "-" +
(providersCount[provider] - 1), // add a number to the provider name (providersCount[provider] - 1); // add a number to the provider name
streamUrl
);
} else { } else {
providersCount[provider] = 1; providersCount[provider] = 1;
streams.push( title = provider.charAt(0).toUpperCase() + provider.slice(1);
provider.charAt(0).toUpperCase() + provider.slice(1),
streamUrl
);
} }
const streamObject = {
title: title,
streamUrl: streamUrl
};
// Add headers if they exist
if (headers && typeof headers === "object" && Object.keys(headers).length > 0) {
streamObject.headers = headers;
}
streams.push(streamObject);
} catch (error) { } catch (error) {
// Ignore the error and try the next provider // Ignore the error and try the next provider
} }
@@ -493,33 +546,64 @@ async function multiExtractor(providers) {
async function extractStreamUrlByProvider(url, provider) { async function extractStreamUrlByProvider(url, provider) {
if (eval(`typeof ${provider}Extractor`) !== "function") { if (eval(`typeof ${provider}Extractor`) !== "function") {
// skip if the extractor is not defined // skip if the extractor is not defined
console.log(`Extractor for provider ${provider} is not defined, skipping...`); console.log(
`Extractor for provider ${provider} is not defined, skipping...`
);
return null; return null;
} }
let uas = [
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.3",
"Mozilla/5.0 (iPhone; CPU iPhone OS 18_1_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/18.1.1 Mobile/15E148 Safari/604.1",
"Mozilla/5.0 (Linux; Android 10; SM-G973F) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Mobile Safari/537.36",
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.3",
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.1.2 Safari/605.1.15",
"Mozilla/5.0 (Linux; Android 11; Pixel 4 XL) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Mobile Safari/537.36",
];
let headers = { let headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.3", "User-Agent": uas[(url.length + provider.length) % uas.length], // use a different user agent based on the url and provider
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8", "Accept":
"text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8",
"Accept-Language": "en-US,en;q=0.5", "Accept-Language": "en-US,en;q=0.5",
"Referer": url, "Referer": url,
"Connection": "keep-alive", "Connection": "keep-alive",
"x-Requested-With": "XMLHttpRequest" "x-Requested-With": "XMLHttpRequest",
}; };
if(provider == 'bigwarp') {
switch (provider) {
case "bigwarp":
delete headers["User-Agent"]; delete headers["User-Agent"];
headers["x-requested-with"] = "XMLHttpRequest"; break;
} else if (provider == 'vk') { case "vk":
case "sibnet":
headers["encoding"] = "windows-1251"; // required headers["encoding"] = "windows-1251"; // required
} else if (provider == 'sibnet') { break;
headers["encoding"] = "windows-1251"; // required case "supervideo":
} else if (provider == 'supervideo') { case "savefiles":
delete headers["User-Agent"]; headers = {
"Accept": "*/*",
"Accept-Encoding": "gzip, deflate, br",
"User-Agent": "EchoapiRuntime/1.1.0",
"Connection": "keep-alive",
"Cache-Control": "no-cache",
"Host": url.match(/https?:\/\/([^\/]+)/)[1],
};
break;
case "streamtape":
headers = {
"User-Agent":
"Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:145.0) Gecko/20100101 Firefox/145.0",
"Accept":
"text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
};
break;
} }
// console.log("Using headers: " + JSON.stringify(headers));
// fetch the url // fetch the url
// and pass the response to the extractor function // and pass the response to the extractor function
console.log("Fetching URL: " + url); console.log("Fetching URL: " + url);
const response = await soraFetch(url, { const response = await soraFetch(url, {
headers headers,
}); });
console.log("Response: " + response.status); console.log("Response: " + response.status);
@@ -527,33 +611,27 @@ async function extractStreamUrlByProvider(url, provider) {
// if title contains redirect, then get the redirect url // if title contains redirect, then get the redirect url
const title = html.match(/<title>(.*?)<\/title>/); const title = html.match(/<title>(.*?)<\/title>/);
if (title && title[1].toLowerCase().includes("redirect")) { if (title && title[1].toLowerCase().includes("redirect")) {
const redirectUrl = html.match(/<meta http-equiv="refresh" content="0;url=(.*?)"/); const matches = [
const redirectUrl2 = html.match(/window\.location\.href\s*=\s*["'](.*?)["']/); /<meta http-equiv="refresh" content="0;url=(.*?)"/,
const redirectUrl3 = html.match(/window\.location\.replace\s*\(\s*["'](.*?)["']\s*\)/); /window\.location\.href\s*=\s*["'](.*?)["']/,
if (redirectUrl) { /window\.location\.replace\s*\(\s*["'](.*?)["']\s*\)/,
console.log("Redirect URL: " + redirectUrl[1]); /window\.location\s*=\s*["'](.*?)["']/,
/window\.location\.assign\s*\(\s*["'](.*?)["']\s*\)/,
/top\.location\s*=\s*["'](.*?)["']/,
/top\.location\.replace\s*\(\s*["'](.*?)["']\s*\)/,
];
for (const match of matches) {
const redirectUrl = html.match(match);
if (redirectUrl && redirectUrl[1] && typeof redirectUrl[1] === "string" && redirectUrl[1].startsWith("http")) {
console.log("Redirect URL found: " + redirectUrl[1]);
url = redirectUrl[1]; url = redirectUrl[1];
headers['Referer'] = url;
headers['Host'] = url.match(/https?:\/\/([^\/]+)/)[1];
html = await soraFetch(url, { html = await soraFetch(url, {
headers headers,
}); }).then((res) => res.text());
html = html.text ? await html.text() : html; break;
}
} else if (redirectUrl2) {
console.log("Redirect URL 2: " + redirectUrl2[1]);
url = redirectUrl2[1];
html = await soraFetch(url, {
headers
});
html = html.text ? await html.text() : html;
} else if (redirectUrl3) {
console.log("Redirect URL 3: " + redirectUrl3[1]);
url = redirectUrl3[1];
html = await soraFetch(url, {
headers
});
html = html.text ? await html.text() : html;
} else {
console.log("No redirect URL found");
} }
} }
@@ -608,6 +686,20 @@ async function extractStreamUrlByProvider(url, provider) {
console.log("Error extracting stream URL from mp4upload:", error); console.log("Error extracting stream URL from mp4upload:", error);
return null; return null;
} }
case "oneupload":
try {
return await oneuploadExtractor(html, url);
} catch (error) {
console.log("Error extracting stream URL from oneupload:", error);
return null;
}
case "packer":
try {
return await packerExtractor(html, url);
} catch (error) {
console.log("Error extracting stream URL from packer:", error);
return null;
}
case "sendvid": case "sendvid":
try { try {
return await sendvidExtractor(html, url); return await sendvidExtractor(html, url);
@@ -622,6 +714,13 @@ async function extractStreamUrlByProvider(url, provider) {
console.log("Error extracting stream URL from sibnet:", error); console.log("Error extracting stream URL from sibnet:", error);
return null; return null;
} }
case "smoothpre":
try {
return await smoothpreExtractor(html, url);
} catch (error) {
console.log("Error extracting stream URL from smoothpre:", error);
return null;
}
case "streamtape": case "streamtape":
try { try {
return await streamtapeExtractor(html, url); return await streamtapeExtractor(html, url);
@@ -636,13 +735,6 @@ async function extractStreamUrlByProvider(url, provider) {
console.log("Error extracting stream URL from streamup:", error); console.log("Error extracting stream URL from streamup:", error);
return null; return null;
} }
case "supervideo":
try {
return await supervideoExtractor(html, url);
} catch (error) {
console.log("Error extracting stream URL from supervideo:", error);
return null;
}
case "uploadcx": case "uploadcx":
try { try {
return await uploadcxExtractor(html, url); return await uploadcxExtractor(html, url);
@@ -691,7 +783,6 @@ async function extractStreamUrlByProvider(url, provider) {
} }
} }
//////////////////////////////////////////////// ////////////////////////////////////////////////
// EXTRACTORS // // EXTRACTORS //
//////////////////////////////////////////////// ////////////////////////////////////////////////
@@ -1178,7 +1269,6 @@ async function megacloudExtractor(html, embedUrl) {
* @author Cufiy * @author Cufiy
*/ */
async function mp4uploadExtractor(html, url = null) { async function mp4uploadExtractor(html, url = null) {
// src: "https://a4.mp4upload.com:183/d/xkx3b4etz3b4quuo66rbmyqtjjoivahfxp27f35pti45rzapbvj5xwb4wuqtlpewdz4dirfp/video.mp4"
const regex = /src:\s*"([^"]+)"/; const regex = /src:\s*"([^"]+)"/;
const match = html.match(regex); const match = html.match(regex);
if (match) { if (match) {
@@ -1188,6 +1278,32 @@ async function mp4uploadExtractor(html, url = null) {
return null; return null;
} }
} }
/* --- oneupload --- */
/**
* @name oneuploadExtractor
* @author 50/50
*/
async function oneuploadExtractor(data, url = null) {
const match = data.match(/sources:\s*\[\{file:"([^"]+)"\}\]/);
const fileUrl = match ? match[1] : null;
return fileUrl;
}
/* --- packer --- */
/* {REQUIRED PLUGINS: unbaser} */
/**
* @name packerExtractor
* @author 50/50
*/
async function packerExtractor(data, url = null) {
const obfuscatedScript = data.match(/<script[^>]*>\s*(eval\(function\(p,a,c,k,e,d.*?\)[\s\S]*?)<\/script>/);
const unpackedScript = unpack(obfuscatedScript[1]);
const m3u8Match = unpackedScript.match(/"hls2"\s*:\s*"([^"]+)"/);
const m3u8Url = m3u8Match[1];
return m3u8Url;
}
/* --- sendvid --- */ /* --- sendvid --- */
/** /**
@@ -1223,6 +1339,29 @@ async function sibnetExtractor(html, embedUrl) {
return null; return null;
} }
} }
/* --- smoothpre --- */
/* {REQUIRED PLUGINS: unbaser} */
/**
* @name SmoothPre Extractor
* @author 50/50
*/
async function smoothpreExtractor(data, url = null) {
console.log("Using SmoothPre Extractor");
console.log("Data Length: " + data.length);
const obfuscatedScript = data.match(/<script[^>]*>\s*(eval\(function\(p,a,c,k,e,d.*?\)[\s\S]*?)<\/script>/);
if (!obfuscatedScript || !obfuscatedScript[1]) {
console.log("No obfuscated script found");
return null;
}
const unpackedScript = unpack(obfuscatedScript[1]);
const hls2Match = unpackedScript.match(/"hls2"\s*:\s*"([^"]+)"/);
const hls2Url = hls2Match ? hls2Match[1] : null;
return hls2Url;
}
/* --- streamtape --- */ /* --- streamtape --- */
/** /**
@@ -1306,26 +1445,6 @@ async function streamupExtractor(data, url = null) {
return null; return null;
} }
} }
/* --- supervideo --- */
/* {REQUIRED PLUGINS: unbaser} */
/**
* @name SuperVideo Extractor
* @author 50/50
*/
async function supervideoExtractor(data, url = null) {
const obfuscatedScript = data.match(/<script[^>]*>\s*(eval\(function\(p,a,c,k,e,d.*?\)[\s\S]*?)<\/script>/);
const unpackedScript = unpack(obfuscatedScript[1]);
const regex = /file:\s*"([^"]+\.m3u8)"/;
const match = regex.exec(unpackedScript);
if (match) {
const fileUrl = match[1];
console.log("File URL:" + fileUrl);
return fileUrl;
}
return "No stream found";
}
/* --- uploadcx --- */ /* --- uploadcx --- */
/** /**
@@ -1390,7 +1509,7 @@ async function vidmolyExtractor(html, url = null) {
const streamUrl = iframeMatch[1].startsWith("//") const streamUrl = iframeMatch[1].startsWith("//")
? "https:" + iframeMatch[1] ? "https:" + iframeMatch[1]
: iframeMatch[1]; : iframeMatch[1];
const responseTwo = await fetchv2(streamUrl); const responseTwo = await soraFetch(streamUrl);
const htmlTwo = await responseTwo.text(); const htmlTwo = await responseTwo.text();
const m3u8Match = htmlTwo.match(/sources:\s*\[\{file:"([^"]+\.m3u8)"/); const m3u8Match = htmlTwo.match(/sources:\s*\[\{file:"([^"]+\.m3u8)"/);
return m3u8Match ? m3u8Match[1] : null; return m3u8Match ? m3u8Match[1] : null;
@@ -1516,6 +1635,7 @@ function voeShiftChars(str, shift) {
.join(""); .join("");
} }
//////////////////////////////////////////////// ////////////////////////////////////////////////
// PLUGINS // // PLUGINS //
//////////////////////////////////////////////// ////////////////////////////////////////////////
@@ -1533,14 +1653,22 @@ function voeShiftChars(str, shift) {
* @returns {Promise<Response|null>} The response from the server, or null if the * @returns {Promise<Response|null>} The response from the server, or null if the
* request failed. * request failed.
*/ */
async function soraFetch(url, options = { headers: {}, method: 'GET', body: null }) { async function soraFetch(
url,
options = { headers: {}, method: "GET", body: null }
) {
try { try {
return await fetchv2(url, options.headers ?? {}, options.method ?? 'GET', options.body ?? null); return await fetchv2(
url,
options.headers ?? {},
options.method ?? "GET",
options.body ?? null
);
} catch (e) { } catch (e) {
try { try {
return await fetch(url, options); return await fetch(url, options);
} catch (error) { } catch (error) {
await console.log('soraFetch error: ' + error.message); await console.log("soraFetch error: " + error.message);
return null; return null;
} }
} }
@@ -1648,5 +1776,4 @@ function unpack(source) {
} }
/* {GE END} */ /* {GE END} */
+248 -120
View File
@@ -6,9 +6,9 @@ async function searchResults(keyword) {
try { try {
const encodedKeyword = encodeURIComponent(keyword); const encodedKeyword = encodeURIComponent(keyword);
const searchApiUrl = `https://aniworld.to/ajax/seriesSearch?keyword=${encodedKeyword}`; const searchApiUrl = `https://aniworld.to/ajax/seriesSearch?keyword=${encodedKeyword}`;
const responseText = await fetch(searchApiUrl); const responseText = await soraFetch(searchApiUrl);
// console.log("Search API Response: " + await responseText.text()); // console.log("Search API Response: " + await responseText.text());
const data = await JSON.parse(responseText); const data = await responseText.json() || await JSON.parse(responseText);
console.log("Search API Data: ", data); console.log("Search API Data: ", data);
const transformedResults = data.map((anime) => ({ const transformedResults = data.map((anime) => ({
@@ -84,9 +84,9 @@ async function extractEpisodes(url) {
} }
// Replace the field "number" with the current index of each item, starting from 1 // Replace the field "number" with the current index of each item, starting from 1
finishedList.forEach((item, index) => { // finishedList.forEach((item, index) => {
item.number = index + 1; // item.number = index + 1;
}); // });
return JSON.stringify(finishedList); return JSON.stringify(finishedList);
} catch (error) { } catch (error) {
@@ -250,23 +250,39 @@ async function fetchSeasonEpisodes(url) {
const response = await fetch(fetchUrl); const response = await fetch(fetchUrl);
const text = response.text ? await response.text() : response; const text = response.text ? await response.text() : response;
// if is filme, e.g. https://aniworld.to/anime/stream/jujutsu-kaisen/filme
let isFilme = false;
if (url.endsWith("/filme") || url.includes("/filme/")) {
isFilme = true;
}
// Updated regex to allow empty <strong> content // Updated regex to allow empty <strong> content
const regex = const regex =
/<td class="seasonEpisodeTitle">\s*<a[^>]*href="([^"]+)"[^>]*>.*?<strong>([^<]*)<\/strong>.*?<span>([^<]+)<\/span>.*?<\/a>/g; /<td class="seasonEpisodeTitle">\s*<a[^>]*href="([^"]+)"[^>]*>.*?<strong>([^<]*)<\/strong>.*?<span>([^<]+)<\/span>.*?<\/a>/g;
const matches = []; const matches = [];
let match; let match;
let holderNumber = 0; let number = 0;
while ((match = regex.exec(text)) !== null) { while ((match = regex.exec(text)) !== null) {
const [_, link] = match; const [_, link, titleRaw, span] = match;
matches.push({ number: holderNumber, href: `${baseUrl}${link}` }); number += 1;
// sendLog("Episode found:", { number, link, title, span });
let title = titleRaw.trim() || span.trim();
if (isFilme) {
title = `[FILM] ${title || span.trim() || "Untitled"}`;
} }
matches.push({ number, href: `${baseUrl}${link}`, title});
}
sendLog("Season Episodes:" + JSON.stringify(matches));
return matches; return matches;
} catch (error) { } catch (error) {
sendLog("FetchSeasonEpisodes helper function error:" + error); sendLog("FetchSeasonEpisodes helper function error:" + error);
return [{ number: "0", href: "https://error.org" }]; return [{ number: "0", href: "https://error.org", title: "Error" }];
} }
} }
@@ -338,7 +354,7 @@ function base64Decode(str) {
async function sendLog(message) { async function sendLog(message) {
// send http://192.168.2.130/sora-module/log.php?action=add&message=message // send http://192.168.2.130/sora-module/log.php?action=add&message=message
console.log(message); console.log(message);
return; // return;
await fetch('http://192.168.2.130/sora-module/log.php?action=add&message=' + encodeURIComponent(message)) await fetch('http://192.168.2.130/sora-module/log.php?action=add&message=' + encodeURIComponent(message))
.catch(error => { .catch(error => {
@@ -351,7 +367,7 @@ async function sendLog(message) {
// EDITING THIS FILE COULD BREAK THE UPDATER AND CAUSE ISSUES WITH THE EXTRACTOR // EDITING THIS FILE COULD BREAK THE UPDATER AND CAUSE ISSUES WITH THE EXTRACTOR
/* {GE START} */ /* {GE START} */
/* {VERSION: 1.1.8} */ /* {VERSION: 1.2.0} */
/** /**
* @name global_extractor.js * @name global_extractor.js
@@ -359,8 +375,8 @@ async function sendLog(message) {
* @author Cufiy * @author Cufiy
* @url https://github.com/JMcrafter26/sora-global-extractor * @url https://github.com/JMcrafter26/sora-global-extractor
* @license CUSTOM LICENSE - see https://github.com/JMcrafter26/sora-global-extractor/blob/main/LICENSE * @license CUSTOM LICENSE - see https://github.com/JMcrafter26/sora-global-extractor/blob/main/LICENSE
* @date 2025-11-05 15:44:57 * @date 2026-01-03 19:28:28
* @version 1.1.8 * @version 1.2.0
* @note This file was generated automatically. * @note This file was generated automatically.
* The global extractor comes with an auto-updating feature, so you can always get the latest version. https://github.com/JMcrafter26/sora-global-extractor#-auto-updater * The global extractor comes with an auto-updating feature, so you can always get the latest version. https://github.com/JMcrafter26/sora-global-extractor#-auto-updater
*/ */
@@ -370,12 +386,20 @@ function globalExtractor(providers) {
for (const [url, provider] of Object.entries(providers)) { for (const [url, provider] of Object.entries(providers)) {
try { try {
const streamUrl = extractStreamUrlByProvider(url, provider); const streamUrl = extractStreamUrlByProvider(url, provider);
// check if streamUrl is an object with streamUrl property
if (streamUrl && typeof streamUrl === "object" && !Array.isArray(streamUrl) && streamUrl.streamUrl) {
return streamUrl.streamUrl;
}
// check if streamUrl is not null, a string, and starts with http or https // check if streamUrl is not null, a string, and starts with http or https
if (streamUrl && typeof streamUrl === "string" && (streamUrl.startsWith("http"))) { if (
streamUrl &&
typeof streamUrl === "string" &&
streamUrl.startsWith("http")
) {
return streamUrl; return streamUrl;
// if its an array, get the value that starts with http // if its an array, get the value that starts with http
} else if (Array.isArray(streamUrl)) { } else if (Array.isArray(streamUrl)) {
const httpStream = streamUrl.find(url => url.startsWith("http")); const httpStream = streamUrl.find((url) => url.startsWith("http"));
if (httpStream) { if (httpStream) {
return httpStream; return httpStream;
} }
@@ -383,7 +407,6 @@ function globalExtractor(providers) {
// check if it's a valid stream URL // check if it's a valid stream URL
return null; return null;
} }
} catch (error) { } catch (error) {
// Ignore the error and try the next provider // Ignore the error and try the next provider
} }
@@ -395,16 +418,30 @@ async function multiExtractor(providers) {
/* this scheme should be returned as a JSON object /* this scheme should be returned as a JSON object
{ {
"streams": [ "streams": [
"FileMoon", {
"https://filemoon.example/stream1.m3u8", "title": "FileMoon",
"StreamWish", "streamUrl": "https://filemoon.example/stream1.m3u8",
"https://streamwish.example/stream2.m3u8", },
"Okru", {
"https://okru.example/stream3.m3u8", "title": "StreamWish",
"MP4", "streamUrl": "https://streamwish.example/stream2.m3u8",
"https://mp4upload.example/stream4.mp4", },
"Default", {
"https://default.example/stream5.m3u8" "title": "Okru",
"streamUrl": "https://okru.example/stream3.m3u8",
"headers": { // Optional headers for the stream
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.3",
"Referer": "https://okru.example/",
},
},
{
"title": "MP4",
"streamUrl": "https://mp4upload.example/stream4.mp4",
},
{
"title": "Default",
"streamUrl": "https://default.example/stream5.m3u8"
}
] ]
} }
*/ */
@@ -416,20 +453,21 @@ async function multiExtractor(providers) {
// if provider starts with "direct-", then add the url to the streams array directly // if provider starts with "direct-", then add the url to the streams array directly
if (provider.startsWith("direct-")) { if (provider.startsWith("direct-")) {
const directName = provider.slice(7); // remove "direct-" prefix const directName = provider.slice(7); // remove "direct-" prefix
if (directName && directName.length > 0) { const title = (directName && directName.length > 0) ? directName : "Direct";
streams.push(directName, url); streams.push({
} else { title: title,
streams.push("Direct", url); // fallback to "Direct" if no name is provided streamUrl: url
} });
continue; // skip to the next provider continue; // skip to the next provider
} }
if (provider.startsWith("direct")) { if (provider.startsWith("direct")) {
provider = provider.slice(7); // remove "direct-" prefix provider = provider.slice(7); // remove "direct-" prefix
if (provider && provider.length > 0) { const title = (provider && provider.length > 0) ? provider : "Direct";
streams.push(provider, url); streams.push({
} else { title: title,
streams.push("Direct", url); // fallback to "Direct" if no name is provided streamUrl: url
} });
continue; // skip to the next provider
} }
let customName = null; // to store the custom name if provided let customName = null; // to store the custom name if provided
@@ -446,15 +484,24 @@ async function multiExtractor(providers) {
console.log(`Skipping ${provider} as it has already 3 streams`); console.log(`Skipping ${provider} as it has already 3 streams`);
continue; continue;
} }
let streamUrl = await extractStreamUrlByProvider(url, provider); let result = await extractStreamUrlByProvider(url, provider);
let streamUrl = null;
let headers = null;
if (streamUrl && Array.isArray(streamUrl)) { // Check if result is an object with streamUrl and optional headers
const httpStream = streamUrl.find(url => url.startsWith("http")); if (result && typeof result === "object" && !Array.isArray(result) && result.streamUrl) {
streamUrl = result.streamUrl;
headers = result.headers || null;
} else if (result && Array.isArray(result)) {
const httpStream = result.find((url) => url.startsWith("http"));
if (httpStream) { if (httpStream) {
streamUrl = httpStream; streamUrl = httpStream;
} }
} else if (result && typeof result === "string") {
streamUrl = result;
} }
// check if provider is already in streams, if it is, add a number to it
// check if streamUrl is valid
if ( if (
!streamUrl || !streamUrl ||
typeof streamUrl !== "string" || typeof streamUrl !== "string" ||
@@ -468,22 +515,29 @@ async function multiExtractor(providers) {
provider = customName; provider = customName;
} }
let title;
if (providersCount[provider]) { if (providersCount[provider]) {
providersCount[provider]++; providersCount[provider]++;
streams.push( title = provider.charAt(0).toUpperCase() +
provider.charAt(0).toUpperCase() +
provider.slice(1) + provider.slice(1) +
"-" + "-" +
(providersCount[provider] - 1), // add a number to the provider name (providersCount[provider] - 1); // add a number to the provider name
streamUrl
);
} else { } else {
providersCount[provider] = 1; providersCount[provider] = 1;
streams.push( title = provider.charAt(0).toUpperCase() + provider.slice(1);
provider.charAt(0).toUpperCase() + provider.slice(1),
streamUrl
);
} }
const streamObject = {
title: title,
streamUrl: streamUrl
};
// Add headers if they exist
if (headers && typeof headers === "object" && Object.keys(headers).length > 0) {
streamObject.headers = headers;
}
streams.push(streamObject);
} catch (error) { } catch (error) {
// Ignore the error and try the next provider // Ignore the error and try the next provider
} }
@@ -494,33 +548,64 @@ async function multiExtractor(providers) {
async function extractStreamUrlByProvider(url, provider) { async function extractStreamUrlByProvider(url, provider) {
if (eval(`typeof ${provider}Extractor`) !== "function") { if (eval(`typeof ${provider}Extractor`) !== "function") {
// skip if the extractor is not defined // skip if the extractor is not defined
console.log(`Extractor for provider ${provider} is not defined, skipping...`); console.log(
`Extractor for provider ${provider} is not defined, skipping...`
);
return null; return null;
} }
let uas = [
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.3",
"Mozilla/5.0 (iPhone; CPU iPhone OS 18_1_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/18.1.1 Mobile/15E148 Safari/604.1",
"Mozilla/5.0 (Linux; Android 10; SM-G973F) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Mobile Safari/537.36",
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.3",
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.1.2 Safari/605.1.15",
"Mozilla/5.0 (Linux; Android 11; Pixel 4 XL) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Mobile Safari/537.36",
];
let headers = { let headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.3", "User-Agent": uas[(url.length + provider.length) % uas.length], // use a different user agent based on the url and provider
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8", "Accept":
"text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8",
"Accept-Language": "en-US,en;q=0.5", "Accept-Language": "en-US,en;q=0.5",
"Referer": url, "Referer": url,
"Connection": "keep-alive", "Connection": "keep-alive",
"x-Requested-With": "XMLHttpRequest" "x-Requested-With": "XMLHttpRequest",
}; };
if(provider == 'bigwarp') {
switch (provider) {
case "bigwarp":
delete headers["User-Agent"]; delete headers["User-Agent"];
headers["x-requested-with"] = "XMLHttpRequest"; break;
} else if (provider == 'vk') { case "vk":
case "sibnet":
headers["encoding"] = "windows-1251"; // required headers["encoding"] = "windows-1251"; // required
} else if (provider == 'sibnet') { break;
headers["encoding"] = "windows-1251"; // required case "supervideo":
} else if (provider == 'supervideo') { case "savefiles":
delete headers["User-Agent"]; headers = {
"Accept": "*/*",
"Accept-Encoding": "gzip, deflate, br",
"User-Agent": "EchoapiRuntime/1.1.0",
"Connection": "keep-alive",
"Cache-Control": "no-cache",
"Host": url.match(/https?:\/\/([^\/]+)/)[1],
};
break;
case "streamtape":
headers = {
"User-Agent":
"Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:145.0) Gecko/20100101 Firefox/145.0",
"Accept":
"text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
};
break;
} }
// console.log("Using headers: " + JSON.stringify(headers));
// fetch the url // fetch the url
// and pass the response to the extractor function // and pass the response to the extractor function
console.log("Fetching URL: " + url); console.log("Fetching URL: " + url);
const response = await soraFetch(url, { const response = await soraFetch(url, {
headers headers,
}); });
console.log("Response: " + response.status); console.log("Response: " + response.status);
@@ -528,33 +613,27 @@ async function extractStreamUrlByProvider(url, provider) {
// if title contains redirect, then get the redirect url // if title contains redirect, then get the redirect url
const title = html.match(/<title>(.*?)<\/title>/); const title = html.match(/<title>(.*?)<\/title>/);
if (title && title[1].toLowerCase().includes("redirect")) { if (title && title[1].toLowerCase().includes("redirect")) {
const redirectUrl = html.match(/<meta http-equiv="refresh" content="0;url=(.*?)"/); const matches = [
const redirectUrl2 = html.match(/window\.location\.href\s*=\s*["'](.*?)["']/); /<meta http-equiv="refresh" content="0;url=(.*?)"/,
const redirectUrl3 = html.match(/window\.location\.replace\s*\(\s*["'](.*?)["']\s*\)/); /window\.location\.href\s*=\s*["'](.*?)["']/,
if (redirectUrl) { /window\.location\.replace\s*\(\s*["'](.*?)["']\s*\)/,
console.log("Redirect URL: " + redirectUrl[1]); /window\.location\s*=\s*["'](.*?)["']/,
/window\.location\.assign\s*\(\s*["'](.*?)["']\s*\)/,
/top\.location\s*=\s*["'](.*?)["']/,
/top\.location\.replace\s*\(\s*["'](.*?)["']\s*\)/,
];
for (const match of matches) {
const redirectUrl = html.match(match);
if (redirectUrl && redirectUrl[1] && typeof redirectUrl[1] === "string" && redirectUrl[1].startsWith("http")) {
console.log("Redirect URL found: " + redirectUrl[1]);
url = redirectUrl[1]; url = redirectUrl[1];
headers['Referer'] = url;
headers['Host'] = url.match(/https?:\/\/([^\/]+)/)[1];
html = await soraFetch(url, { html = await soraFetch(url, {
headers headers,
}); }).then((res) => res.text());
html = html.text ? await html.text() : html; break;
}
} else if (redirectUrl2) {
console.log("Redirect URL 2: " + redirectUrl2[1]);
url = redirectUrl2[1];
html = await soraFetch(url, {
headers
});
html = html.text ? await html.text() : html;
} else if (redirectUrl3) {
console.log("Redirect URL 3: " + redirectUrl3[1]);
url = redirectUrl3[1];
html = await soraFetch(url, {
headers
});
html = html.text ? await html.text() : html;
} else {
console.log("No redirect URL found");
} }
} }
@@ -609,6 +688,20 @@ async function extractStreamUrlByProvider(url, provider) {
console.log("Error extracting stream URL from mp4upload:", error); console.log("Error extracting stream URL from mp4upload:", error);
return null; return null;
} }
case "oneupload":
try {
return await oneuploadExtractor(html, url);
} catch (error) {
console.log("Error extracting stream URL from oneupload:", error);
return null;
}
case "packer":
try {
return await packerExtractor(html, url);
} catch (error) {
console.log("Error extracting stream URL from packer:", error);
return null;
}
case "sendvid": case "sendvid":
try { try {
return await sendvidExtractor(html, url); return await sendvidExtractor(html, url);
@@ -623,6 +716,13 @@ async function extractStreamUrlByProvider(url, provider) {
console.log("Error extracting stream URL from sibnet:", error); console.log("Error extracting stream URL from sibnet:", error);
return null; return null;
} }
case "smoothpre":
try {
return await smoothpreExtractor(html, url);
} catch (error) {
console.log("Error extracting stream URL from smoothpre:", error);
return null;
}
case "streamtape": case "streamtape":
try { try {
return await streamtapeExtractor(html, url); return await streamtapeExtractor(html, url);
@@ -637,13 +737,6 @@ async function extractStreamUrlByProvider(url, provider) {
console.log("Error extracting stream URL from streamup:", error); console.log("Error extracting stream URL from streamup:", error);
return null; return null;
} }
case "supervideo":
try {
return await supervideoExtractor(html, url);
} catch (error) {
console.log("Error extracting stream URL from supervideo:", error);
return null;
}
case "uploadcx": case "uploadcx":
try { try {
return await uploadcxExtractor(html, url); return await uploadcxExtractor(html, url);
@@ -692,7 +785,6 @@ async function extractStreamUrlByProvider(url, provider) {
} }
} }
//////////////////////////////////////////////// ////////////////////////////////////////////////
// EXTRACTORS // // EXTRACTORS //
//////////////////////////////////////////////// ////////////////////////////////////////////////
@@ -1179,7 +1271,6 @@ async function megacloudExtractor(html, embedUrl) {
* @author Cufiy * @author Cufiy
*/ */
async function mp4uploadExtractor(html, url = null) { async function mp4uploadExtractor(html, url = null) {
// src: "https://a4.mp4upload.com:183/d/xkx3b4etz3b4quuo66rbmyqtjjoivahfxp27f35pti45rzapbvj5xwb4wuqtlpewdz4dirfp/video.mp4"
const regex = /src:\s*"([^"]+)"/; const regex = /src:\s*"([^"]+)"/;
const match = html.match(regex); const match = html.match(regex);
if (match) { if (match) {
@@ -1189,6 +1280,32 @@ async function mp4uploadExtractor(html, url = null) {
return null; return null;
} }
} }
/* --- oneupload --- */
/**
* @name oneuploadExtractor
* @author 50/50
*/
async function oneuploadExtractor(data, url = null) {
const match = data.match(/sources:\s*\[\{file:"([^"]+)"\}\]/);
const fileUrl = match ? match[1] : null;
return fileUrl;
}
/* --- packer --- */
/* {REQUIRED PLUGINS: unbaser} */
/**
* @name packerExtractor
* @author 50/50
*/
async function packerExtractor(data, url = null) {
const obfuscatedScript = data.match(/<script[^>]*>\s*(eval\(function\(p,a,c,k,e,d.*?\)[\s\S]*?)<\/script>/);
const unpackedScript = unpack(obfuscatedScript[1]);
const m3u8Match = unpackedScript.match(/"hls2"\s*:\s*"([^"]+)"/);
const m3u8Url = m3u8Match[1];
return m3u8Url;
}
/* --- sendvid --- */ /* --- sendvid --- */
/** /**
@@ -1224,6 +1341,29 @@ async function sibnetExtractor(html, embedUrl) {
return null; return null;
} }
} }
/* --- smoothpre --- */
/* {REQUIRED PLUGINS: unbaser} */
/**
* @name SmoothPre Extractor
* @author 50/50
*/
async function smoothpreExtractor(data, url = null) {
console.log("Using SmoothPre Extractor");
console.log("Data Length: " + data.length);
const obfuscatedScript = data.match(/<script[^>]*>\s*(eval\(function\(p,a,c,k,e,d.*?\)[\s\S]*?)<\/script>/);
if (!obfuscatedScript || !obfuscatedScript[1]) {
console.log("No obfuscated script found");
return null;
}
const unpackedScript = unpack(obfuscatedScript[1]);
const hls2Match = unpackedScript.match(/"hls2"\s*:\s*"([^"]+)"/);
const hls2Url = hls2Match ? hls2Match[1] : null;
return hls2Url;
}
/* --- streamtape --- */ /* --- streamtape --- */
/** /**
@@ -1307,26 +1447,6 @@ async function streamupExtractor(data, url = null) {
return null; return null;
} }
} }
/* --- supervideo --- */
/* {REQUIRED PLUGINS: unbaser} */
/**
* @name SuperVideo Extractor
* @author 50/50
*/
async function supervideoExtractor(data, url = null) {
const obfuscatedScript = data.match(/<script[^>]*>\s*(eval\(function\(p,a,c,k,e,d.*?\)[\s\S]*?)<\/script>/);
const unpackedScript = unpack(obfuscatedScript[1]);
const regex = /file:\s*"([^"]+\.m3u8)"/;
const match = regex.exec(unpackedScript);
if (match) {
const fileUrl = match[1];
console.log("File URL:" + fileUrl);
return fileUrl;
}
return "No stream found";
}
/* --- uploadcx --- */ /* --- uploadcx --- */
/** /**
@@ -1391,7 +1511,7 @@ async function vidmolyExtractor(html, url = null) {
const streamUrl = iframeMatch[1].startsWith("//") const streamUrl = iframeMatch[1].startsWith("//")
? "https:" + iframeMatch[1] ? "https:" + iframeMatch[1]
: iframeMatch[1]; : iframeMatch[1];
const responseTwo = await fetchv2(streamUrl); const responseTwo = await soraFetch(streamUrl);
const htmlTwo = await responseTwo.text(); const htmlTwo = await responseTwo.text();
const m3u8Match = htmlTwo.match(/sources:\s*\[\{file:"([^"]+\.m3u8)"/); const m3u8Match = htmlTwo.match(/sources:\s*\[\{file:"([^"]+\.m3u8)"/);
return m3u8Match ? m3u8Match[1] : null; return m3u8Match ? m3u8Match[1] : null;
@@ -1517,6 +1637,7 @@ function voeShiftChars(str, shift) {
.join(""); .join("");
} }
//////////////////////////////////////////////// ////////////////////////////////////////////////
// PLUGINS // // PLUGINS //
//////////////////////////////////////////////// ////////////////////////////////////////////////
@@ -1534,14 +1655,22 @@ function voeShiftChars(str, shift) {
* @returns {Promise<Response|null>} The response from the server, or null if the * @returns {Promise<Response|null>} The response from the server, or null if the
* request failed. * request failed.
*/ */
async function soraFetch(url, options = { headers: {}, method: 'GET', body: null }) { async function soraFetch(
url,
options = { headers: {}, method: "GET", body: null }
) {
try { try {
return await fetchv2(url, options.headers ?? {}, options.method ?? 'GET', options.body ?? null); return await fetchv2(
url,
options.headers ?? {},
options.method ?? "GET",
options.body ?? null
);
} catch (e) { } catch (e) {
try { try {
return await fetch(url, options); return await fetch(url, options);
} catch (error) { } catch (error) {
await console.log('soraFetch error: ' + error.message); await console.log("soraFetch error: " + error.message);
return null; return null;
} }
} }
@@ -1649,5 +1778,4 @@ function unpack(source) {
} }
/* {GE END} */ /* {GE END} */
+249 -120
View File
@@ -6,9 +6,9 @@ async function searchResults(keyword) {
try { try {
const encodedKeyword = encodeURIComponent(keyword); const encodedKeyword = encodeURIComponent(keyword);
const searchApiUrl = `https://aniworld.to/ajax/seriesSearch?keyword=${encodedKeyword}`; const searchApiUrl = `https://aniworld.to/ajax/seriesSearch?keyword=${encodedKeyword}`;
const responseText = await fetch(searchApiUrl); const responseText = await soraFetch(searchApiUrl);
// console.log("Search API Response: " + await responseText.text()); // console.log("Search API Response: " + await responseText.text());
const data = await JSON.parse(responseText); const data = await responseText.json() || await JSON.parse(responseText);
console.log("Search API Data: ", data); console.log("Search API Data: ", data);
const transformedResults = data.map((anime) => ({ const transformedResults = data.map((anime) => ({
@@ -84,9 +84,9 @@ async function extractEpisodes(url) {
} }
// Replace the field "number" with the current index of each item, starting from 1 // Replace the field "number" with the current index of each item, starting from 1
finishedList.forEach((item, index) => { // finishedList.forEach((item, index) => {
item.number = index + 1; // item.number = index + 1;
}); // });
return JSON.stringify(finishedList); return JSON.stringify(finishedList);
} catch (error) { } catch (error) {
@@ -99,6 +99,7 @@ async function extractStreamUrl(url) {
try { try {
const baseUrl = "https://aniworld.to"; const baseUrl = "https://aniworld.to";
const fetchUrl = `${url}`; const fetchUrl = `${url}`;
sendLog("Fetching URL: " + fetchUrl);
const response = await fetch(fetchUrl); const response = await fetch(fetchUrl);
const text = response.text ? await response.text() : response; const text = response.text ? await response.text() : response;
@@ -249,23 +250,39 @@ async function fetchSeasonEpisodes(url) {
const response = await fetch(fetchUrl); const response = await fetch(fetchUrl);
const text = response.text ? await response.text() : response; const text = response.text ? await response.text() : response;
// if is filme, e.g. https://aniworld.to/anime/stream/jujutsu-kaisen/filme
let isFilme = false;
if (url.endsWith("/filme") || url.includes("/filme/")) {
isFilme = true;
}
// Updated regex to allow empty <strong> content // Updated regex to allow empty <strong> content
const regex = const regex =
/<td class="seasonEpisodeTitle">\s*<a[^>]*href="([^"]+)"[^>]*>.*?<strong>([^<]*)<\/strong>.*?<span>([^<]+)<\/span>.*?<\/a>/g; /<td class="seasonEpisodeTitle">\s*<a[^>]*href="([^"]+)"[^>]*>.*?<strong>([^<]*)<\/strong>.*?<span>([^<]+)<\/span>.*?<\/a>/g;
const matches = []; const matches = [];
let match; let match;
let holderNumber = 0; let number = 0;
while ((match = regex.exec(text)) !== null) { while ((match = regex.exec(text)) !== null) {
const [_, link] = match; const [_, link, titleRaw, span] = match;
matches.push({ number: holderNumber, href: `${baseUrl}${link}` }); number += 1;
// sendLog("Episode found:", { number, link, title, span });
let title = titleRaw.trim() || span.trim();
if (isFilme) {
title = `[FILM] ${title || span.trim() || "Untitled"}`;
} }
matches.push({ number, href: `${baseUrl}${link}`, title});
}
sendLog("Season Episodes:" + JSON.stringify(matches));
return matches; return matches;
} catch (error) { } catch (error) {
sendLog("FetchSeasonEpisodes helper function error:" + error); sendLog("FetchSeasonEpisodes helper function error:" + error);
return [{ number: "0", href: "https://error.org" }]; return [{ number: "0", href: "https://error.org", title: "Error" }];
} }
} }
@@ -337,7 +354,7 @@ function base64Decode(str) {
async function sendLog(message) { async function sendLog(message) {
// send http://192.168.2.130/sora-module/log.php?action=add&message=message // send http://192.168.2.130/sora-module/log.php?action=add&message=message
console.log(message); console.log(message);
return; // return;
await fetch('http://192.168.2.130/sora-module/log.php?action=add&message=' + encodeURIComponent(message)) await fetch('http://192.168.2.130/sora-module/log.php?action=add&message=' + encodeURIComponent(message))
.catch(error => { .catch(error => {
@@ -350,7 +367,7 @@ async function sendLog(message) {
// EDITING THIS FILE COULD BREAK THE UPDATER AND CAUSE ISSUES WITH THE EXTRACTOR // EDITING THIS FILE COULD BREAK THE UPDATER AND CAUSE ISSUES WITH THE EXTRACTOR
/* {GE START} */ /* {GE START} */
/* {VERSION: 1.1.8} */ /* {VERSION: 1.2.0} */
/** /**
* @name global_extractor.js * @name global_extractor.js
@@ -358,8 +375,8 @@ async function sendLog(message) {
* @author Cufiy * @author Cufiy
* @url https://github.com/JMcrafter26/sora-global-extractor * @url https://github.com/JMcrafter26/sora-global-extractor
* @license CUSTOM LICENSE - see https://github.com/JMcrafter26/sora-global-extractor/blob/main/LICENSE * @license CUSTOM LICENSE - see https://github.com/JMcrafter26/sora-global-extractor/blob/main/LICENSE
* @date 2025-11-05 15:44:57 * @date 2026-01-03 19:28:28
* @version 1.1.8 * @version 1.2.0
* @note This file was generated automatically. * @note This file was generated automatically.
* The global extractor comes with an auto-updating feature, so you can always get the latest version. https://github.com/JMcrafter26/sora-global-extractor#-auto-updater * The global extractor comes with an auto-updating feature, so you can always get the latest version. https://github.com/JMcrafter26/sora-global-extractor#-auto-updater
*/ */
@@ -369,12 +386,20 @@ function globalExtractor(providers) {
for (const [url, provider] of Object.entries(providers)) { for (const [url, provider] of Object.entries(providers)) {
try { try {
const streamUrl = extractStreamUrlByProvider(url, provider); const streamUrl = extractStreamUrlByProvider(url, provider);
// check if streamUrl is an object with streamUrl property
if (streamUrl && typeof streamUrl === "object" && !Array.isArray(streamUrl) && streamUrl.streamUrl) {
return streamUrl.streamUrl;
}
// check if streamUrl is not null, a string, and starts with http or https // check if streamUrl is not null, a string, and starts with http or https
if (streamUrl && typeof streamUrl === "string" && (streamUrl.startsWith("http"))) { if (
streamUrl &&
typeof streamUrl === "string" &&
streamUrl.startsWith("http")
) {
return streamUrl; return streamUrl;
// if its an array, get the value that starts with http // if its an array, get the value that starts with http
} else if (Array.isArray(streamUrl)) { } else if (Array.isArray(streamUrl)) {
const httpStream = streamUrl.find(url => url.startsWith("http")); const httpStream = streamUrl.find((url) => url.startsWith("http"));
if (httpStream) { if (httpStream) {
return httpStream; return httpStream;
} }
@@ -382,7 +407,6 @@ function globalExtractor(providers) {
// check if it's a valid stream URL // check if it's a valid stream URL
return null; return null;
} }
} catch (error) { } catch (error) {
// Ignore the error and try the next provider // Ignore the error and try the next provider
} }
@@ -394,16 +418,30 @@ async function multiExtractor(providers) {
/* this scheme should be returned as a JSON object /* this scheme should be returned as a JSON object
{ {
"streams": [ "streams": [
"FileMoon", {
"https://filemoon.example/stream1.m3u8", "title": "FileMoon",
"StreamWish", "streamUrl": "https://filemoon.example/stream1.m3u8",
"https://streamwish.example/stream2.m3u8", },
"Okru", {
"https://okru.example/stream3.m3u8", "title": "StreamWish",
"MP4", "streamUrl": "https://streamwish.example/stream2.m3u8",
"https://mp4upload.example/stream4.mp4", },
"Default", {
"https://default.example/stream5.m3u8" "title": "Okru",
"streamUrl": "https://okru.example/stream3.m3u8",
"headers": { // Optional headers for the stream
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.3",
"Referer": "https://okru.example/",
},
},
{
"title": "MP4",
"streamUrl": "https://mp4upload.example/stream4.mp4",
},
{
"title": "Default",
"streamUrl": "https://default.example/stream5.m3u8"
}
] ]
} }
*/ */
@@ -415,20 +453,21 @@ async function multiExtractor(providers) {
// if provider starts with "direct-", then add the url to the streams array directly // if provider starts with "direct-", then add the url to the streams array directly
if (provider.startsWith("direct-")) { if (provider.startsWith("direct-")) {
const directName = provider.slice(7); // remove "direct-" prefix const directName = provider.slice(7); // remove "direct-" prefix
if (directName && directName.length > 0) { const title = (directName && directName.length > 0) ? directName : "Direct";
streams.push(directName, url); streams.push({
} else { title: title,
streams.push("Direct", url); // fallback to "Direct" if no name is provided streamUrl: url
} });
continue; // skip to the next provider continue; // skip to the next provider
} }
if (provider.startsWith("direct")) { if (provider.startsWith("direct")) {
provider = provider.slice(7); // remove "direct-" prefix provider = provider.slice(7); // remove "direct-" prefix
if (provider && provider.length > 0) { const title = (provider && provider.length > 0) ? provider : "Direct";
streams.push(provider, url); streams.push({
} else { title: title,
streams.push("Direct", url); // fallback to "Direct" if no name is provided streamUrl: url
} });
continue; // skip to the next provider
} }
let customName = null; // to store the custom name if provided let customName = null; // to store the custom name if provided
@@ -445,15 +484,24 @@ async function multiExtractor(providers) {
console.log(`Skipping ${provider} as it has already 3 streams`); console.log(`Skipping ${provider} as it has already 3 streams`);
continue; continue;
} }
let streamUrl = await extractStreamUrlByProvider(url, provider); let result = await extractStreamUrlByProvider(url, provider);
let streamUrl = null;
let headers = null;
if (streamUrl && Array.isArray(streamUrl)) { // Check if result is an object with streamUrl and optional headers
const httpStream = streamUrl.find(url => url.startsWith("http")); if (result && typeof result === "object" && !Array.isArray(result) && result.streamUrl) {
streamUrl = result.streamUrl;
headers = result.headers || null;
} else if (result && Array.isArray(result)) {
const httpStream = result.find((url) => url.startsWith("http"));
if (httpStream) { if (httpStream) {
streamUrl = httpStream; streamUrl = httpStream;
} }
} else if (result && typeof result === "string") {
streamUrl = result;
} }
// check if provider is already in streams, if it is, add a number to it
// check if streamUrl is valid
if ( if (
!streamUrl || !streamUrl ||
typeof streamUrl !== "string" || typeof streamUrl !== "string" ||
@@ -467,22 +515,29 @@ async function multiExtractor(providers) {
provider = customName; provider = customName;
} }
let title;
if (providersCount[provider]) { if (providersCount[provider]) {
providersCount[provider]++; providersCount[provider]++;
streams.push( title = provider.charAt(0).toUpperCase() +
provider.charAt(0).toUpperCase() +
provider.slice(1) + provider.slice(1) +
"-" + "-" +
(providersCount[provider] - 1), // add a number to the provider name (providersCount[provider] - 1); // add a number to the provider name
streamUrl
);
} else { } else {
providersCount[provider] = 1; providersCount[provider] = 1;
streams.push( title = provider.charAt(0).toUpperCase() + provider.slice(1);
provider.charAt(0).toUpperCase() + provider.slice(1),
streamUrl
);
} }
const streamObject = {
title: title,
streamUrl: streamUrl
};
// Add headers if they exist
if (headers && typeof headers === "object" && Object.keys(headers).length > 0) {
streamObject.headers = headers;
}
streams.push(streamObject);
} catch (error) { } catch (error) {
// Ignore the error and try the next provider // Ignore the error and try the next provider
} }
@@ -493,33 +548,64 @@ async function multiExtractor(providers) {
async function extractStreamUrlByProvider(url, provider) { async function extractStreamUrlByProvider(url, provider) {
if (eval(`typeof ${provider}Extractor`) !== "function") { if (eval(`typeof ${provider}Extractor`) !== "function") {
// skip if the extractor is not defined // skip if the extractor is not defined
console.log(`Extractor for provider ${provider} is not defined, skipping...`); console.log(
`Extractor for provider ${provider} is not defined, skipping...`
);
return null; return null;
} }
let uas = [
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.3",
"Mozilla/5.0 (iPhone; CPU iPhone OS 18_1_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/18.1.1 Mobile/15E148 Safari/604.1",
"Mozilla/5.0 (Linux; Android 10; SM-G973F) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Mobile Safari/537.36",
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.3",
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.1.2 Safari/605.1.15",
"Mozilla/5.0 (Linux; Android 11; Pixel 4 XL) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Mobile Safari/537.36",
];
let headers = { let headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.3", "User-Agent": uas[(url.length + provider.length) % uas.length], // use a different user agent based on the url and provider
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8", "Accept":
"text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8",
"Accept-Language": "en-US,en;q=0.5", "Accept-Language": "en-US,en;q=0.5",
"Referer": url, "Referer": url,
"Connection": "keep-alive", "Connection": "keep-alive",
"x-Requested-With": "XMLHttpRequest" "x-Requested-With": "XMLHttpRequest",
}; };
if(provider == 'bigwarp') {
switch (provider) {
case "bigwarp":
delete headers["User-Agent"]; delete headers["User-Agent"];
headers["x-requested-with"] = "XMLHttpRequest"; break;
} else if (provider == 'vk') { case "vk":
case "sibnet":
headers["encoding"] = "windows-1251"; // required headers["encoding"] = "windows-1251"; // required
} else if (provider == 'sibnet') { break;
headers["encoding"] = "windows-1251"; // required case "supervideo":
} else if (provider == 'supervideo') { case "savefiles":
delete headers["User-Agent"]; headers = {
"Accept": "*/*",
"Accept-Encoding": "gzip, deflate, br",
"User-Agent": "EchoapiRuntime/1.1.0",
"Connection": "keep-alive",
"Cache-Control": "no-cache",
"Host": url.match(/https?:\/\/([^\/]+)/)[1],
};
break;
case "streamtape":
headers = {
"User-Agent":
"Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:145.0) Gecko/20100101 Firefox/145.0",
"Accept":
"text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
};
break;
} }
// console.log("Using headers: " + JSON.stringify(headers));
// fetch the url // fetch the url
// and pass the response to the extractor function // and pass the response to the extractor function
console.log("Fetching URL: " + url); console.log("Fetching URL: " + url);
const response = await soraFetch(url, { const response = await soraFetch(url, {
headers headers,
}); });
console.log("Response: " + response.status); console.log("Response: " + response.status);
@@ -527,33 +613,27 @@ async function extractStreamUrlByProvider(url, provider) {
// if title contains redirect, then get the redirect url // if title contains redirect, then get the redirect url
const title = html.match(/<title>(.*?)<\/title>/); const title = html.match(/<title>(.*?)<\/title>/);
if (title && title[1].toLowerCase().includes("redirect")) { if (title && title[1].toLowerCase().includes("redirect")) {
const redirectUrl = html.match(/<meta http-equiv="refresh" content="0;url=(.*?)"/); const matches = [
const redirectUrl2 = html.match(/window\.location\.href\s*=\s*["'](.*?)["']/); /<meta http-equiv="refresh" content="0;url=(.*?)"/,
const redirectUrl3 = html.match(/window\.location\.replace\s*\(\s*["'](.*?)["']\s*\)/); /window\.location\.href\s*=\s*["'](.*?)["']/,
if (redirectUrl) { /window\.location\.replace\s*\(\s*["'](.*?)["']\s*\)/,
console.log("Redirect URL: " + redirectUrl[1]); /window\.location\s*=\s*["'](.*?)["']/,
/window\.location\.assign\s*\(\s*["'](.*?)["']\s*\)/,
/top\.location\s*=\s*["'](.*?)["']/,
/top\.location\.replace\s*\(\s*["'](.*?)["']\s*\)/,
];
for (const match of matches) {
const redirectUrl = html.match(match);
if (redirectUrl && redirectUrl[1] && typeof redirectUrl[1] === "string" && redirectUrl[1].startsWith("http")) {
console.log("Redirect URL found: " + redirectUrl[1]);
url = redirectUrl[1]; url = redirectUrl[1];
headers['Referer'] = url;
headers['Host'] = url.match(/https?:\/\/([^\/]+)/)[1];
html = await soraFetch(url, { html = await soraFetch(url, {
headers headers,
}); }).then((res) => res.text());
html = html.text ? await html.text() : html; break;
}
} else if (redirectUrl2) {
console.log("Redirect URL 2: " + redirectUrl2[1]);
url = redirectUrl2[1];
html = await soraFetch(url, {
headers
});
html = html.text ? await html.text() : html;
} else if (redirectUrl3) {
console.log("Redirect URL 3: " + redirectUrl3[1]);
url = redirectUrl3[1];
html = await soraFetch(url, {
headers
});
html = html.text ? await html.text() : html;
} else {
console.log("No redirect URL found");
} }
} }
@@ -608,6 +688,20 @@ async function extractStreamUrlByProvider(url, provider) {
console.log("Error extracting stream URL from mp4upload:", error); console.log("Error extracting stream URL from mp4upload:", error);
return null; return null;
} }
case "oneupload":
try {
return await oneuploadExtractor(html, url);
} catch (error) {
console.log("Error extracting stream URL from oneupload:", error);
return null;
}
case "packer":
try {
return await packerExtractor(html, url);
} catch (error) {
console.log("Error extracting stream URL from packer:", error);
return null;
}
case "sendvid": case "sendvid":
try { try {
return await sendvidExtractor(html, url); return await sendvidExtractor(html, url);
@@ -622,6 +716,13 @@ async function extractStreamUrlByProvider(url, provider) {
console.log("Error extracting stream URL from sibnet:", error); console.log("Error extracting stream URL from sibnet:", error);
return null; return null;
} }
case "smoothpre":
try {
return await smoothpreExtractor(html, url);
} catch (error) {
console.log("Error extracting stream URL from smoothpre:", error);
return null;
}
case "streamtape": case "streamtape":
try { try {
return await streamtapeExtractor(html, url); return await streamtapeExtractor(html, url);
@@ -636,13 +737,6 @@ async function extractStreamUrlByProvider(url, provider) {
console.log("Error extracting stream URL from streamup:", error); console.log("Error extracting stream URL from streamup:", error);
return null; return null;
} }
case "supervideo":
try {
return await supervideoExtractor(html, url);
} catch (error) {
console.log("Error extracting stream URL from supervideo:", error);
return null;
}
case "uploadcx": case "uploadcx":
try { try {
return await uploadcxExtractor(html, url); return await uploadcxExtractor(html, url);
@@ -691,7 +785,6 @@ async function extractStreamUrlByProvider(url, provider) {
} }
} }
//////////////////////////////////////////////// ////////////////////////////////////////////////
// EXTRACTORS // // EXTRACTORS //
//////////////////////////////////////////////// ////////////////////////////////////////////////
@@ -1178,7 +1271,6 @@ async function megacloudExtractor(html, embedUrl) {
* @author Cufiy * @author Cufiy
*/ */
async function mp4uploadExtractor(html, url = null) { async function mp4uploadExtractor(html, url = null) {
// src: "https://a4.mp4upload.com:183/d/xkx3b4etz3b4quuo66rbmyqtjjoivahfxp27f35pti45rzapbvj5xwb4wuqtlpewdz4dirfp/video.mp4"
const regex = /src:\s*"([^"]+)"/; const regex = /src:\s*"([^"]+)"/;
const match = html.match(regex); const match = html.match(regex);
if (match) { if (match) {
@@ -1188,6 +1280,32 @@ async function mp4uploadExtractor(html, url = null) {
return null; return null;
} }
} }
/* --- oneupload --- */
/**
* @name oneuploadExtractor
* @author 50/50
*/
async function oneuploadExtractor(data, url = null) {
const match = data.match(/sources:\s*\[\{file:"([^"]+)"\}\]/);
const fileUrl = match ? match[1] : null;
return fileUrl;
}
/* --- packer --- */
/* {REQUIRED PLUGINS: unbaser} */
/**
* @name packerExtractor
* @author 50/50
*/
async function packerExtractor(data, url = null) {
const obfuscatedScript = data.match(/<script[^>]*>\s*(eval\(function\(p,a,c,k,e,d.*?\)[\s\S]*?)<\/script>/);
const unpackedScript = unpack(obfuscatedScript[1]);
const m3u8Match = unpackedScript.match(/"hls2"\s*:\s*"([^"]+)"/);
const m3u8Url = m3u8Match[1];
return m3u8Url;
}
/* --- sendvid --- */ /* --- sendvid --- */
/** /**
@@ -1223,6 +1341,29 @@ async function sibnetExtractor(html, embedUrl) {
return null; return null;
} }
} }
/* --- smoothpre --- */
/* {REQUIRED PLUGINS: unbaser} */
/**
* @name SmoothPre Extractor
* @author 50/50
*/
async function smoothpreExtractor(data, url = null) {
console.log("Using SmoothPre Extractor");
console.log("Data Length: " + data.length);
const obfuscatedScript = data.match(/<script[^>]*>\s*(eval\(function\(p,a,c,k,e,d.*?\)[\s\S]*?)<\/script>/);
if (!obfuscatedScript || !obfuscatedScript[1]) {
console.log("No obfuscated script found");
return null;
}
const unpackedScript = unpack(obfuscatedScript[1]);
const hls2Match = unpackedScript.match(/"hls2"\s*:\s*"([^"]+)"/);
const hls2Url = hls2Match ? hls2Match[1] : null;
return hls2Url;
}
/* --- streamtape --- */ /* --- streamtape --- */
/** /**
@@ -1306,26 +1447,6 @@ async function streamupExtractor(data, url = null) {
return null; return null;
} }
} }
/* --- supervideo --- */
/* {REQUIRED PLUGINS: unbaser} */
/**
* @name SuperVideo Extractor
* @author 50/50
*/
async function supervideoExtractor(data, url = null) {
const obfuscatedScript = data.match(/<script[^>]*>\s*(eval\(function\(p,a,c,k,e,d.*?\)[\s\S]*?)<\/script>/);
const unpackedScript = unpack(obfuscatedScript[1]);
const regex = /file:\s*"([^"]+\.m3u8)"/;
const match = regex.exec(unpackedScript);
if (match) {
const fileUrl = match[1];
console.log("File URL:" + fileUrl);
return fileUrl;
}
return "No stream found";
}
/* --- uploadcx --- */ /* --- uploadcx --- */
/** /**
@@ -1390,7 +1511,7 @@ async function vidmolyExtractor(html, url = null) {
const streamUrl = iframeMatch[1].startsWith("//") const streamUrl = iframeMatch[1].startsWith("//")
? "https:" + iframeMatch[1] ? "https:" + iframeMatch[1]
: iframeMatch[1]; : iframeMatch[1];
const responseTwo = await fetchv2(streamUrl); const responseTwo = await soraFetch(streamUrl);
const htmlTwo = await responseTwo.text(); const htmlTwo = await responseTwo.text();
const m3u8Match = htmlTwo.match(/sources:\s*\[\{file:"([^"]+\.m3u8)"/); const m3u8Match = htmlTwo.match(/sources:\s*\[\{file:"([^"]+\.m3u8)"/);
return m3u8Match ? m3u8Match[1] : null; return m3u8Match ? m3u8Match[1] : null;
@@ -1516,6 +1637,7 @@ function voeShiftChars(str, shift) {
.join(""); .join("");
} }
//////////////////////////////////////////////// ////////////////////////////////////////////////
// PLUGINS // // PLUGINS //
//////////////////////////////////////////////// ////////////////////////////////////////////////
@@ -1533,14 +1655,22 @@ function voeShiftChars(str, shift) {
* @returns {Promise<Response|null>} The response from the server, or null if the * @returns {Promise<Response|null>} The response from the server, or null if the
* request failed. * request failed.
*/ */
async function soraFetch(url, options = { headers: {}, method: 'GET', body: null }) { async function soraFetch(
url,
options = { headers: {}, method: "GET", body: null }
) {
try { try {
return await fetchv2(url, options.headers ?? {}, options.method ?? 'GET', options.body ?? null); return await fetchv2(
url,
options.headers ?? {},
options.method ?? "GET",
options.body ?? null
);
} catch (e) { } catch (e) {
try { try {
return await fetch(url, options); return await fetch(url, options);
} catch (error) { } catch (error) {
await console.log('soraFetch error: ' + error.message); await console.log("soraFetch error: " + error.message);
return null; return null;
} }
} }
@@ -1648,5 +1778,4 @@ function unpack(source) {
} }
/* {GE END} */ /* {GE END} */
+222 -110
View File
@@ -147,7 +147,7 @@ function cleanHtmlSymbols(string) {
// EDITING THIS FILE COULD BREAK THE UPDATER AND CAUSE ISSUES WITH THE EXTRACTOR // EDITING THIS FILE COULD BREAK THE UPDATER AND CAUSE ISSUES WITH THE EXTRACTOR
/* {GE START} */ /* {GE START} */
/* {VERSION: 1.1.8} */ /* {VERSION: 1.2.0} */
/** /**
* @name global_extractor.js * @name global_extractor.js
@@ -155,8 +155,8 @@ function cleanHtmlSymbols(string) {
* @author Cufiy * @author Cufiy
* @url https://github.com/JMcrafter26/sora-global-extractor * @url https://github.com/JMcrafter26/sora-global-extractor
* @license CUSTOM LICENSE - see https://github.com/JMcrafter26/sora-global-extractor/blob/main/LICENSE * @license CUSTOM LICENSE - see https://github.com/JMcrafter26/sora-global-extractor/blob/main/LICENSE
* @date 2025-11-05 15:44:57 * @date 2026-01-03 19:28:28
* @version 1.1.8 * @version 1.2.0
* @note This file was generated automatically. * @note This file was generated automatically.
* The global extractor comes with an auto-updating feature, so you can always get the latest version. https://github.com/JMcrafter26/sora-global-extractor#-auto-updater * The global extractor comes with an auto-updating feature, so you can always get the latest version. https://github.com/JMcrafter26/sora-global-extractor#-auto-updater
*/ */
@@ -166,12 +166,20 @@ function globalExtractor(providers) {
for (const [url, provider] of Object.entries(providers)) { for (const [url, provider] of Object.entries(providers)) {
try { try {
const streamUrl = extractStreamUrlByProvider(url, provider); const streamUrl = extractStreamUrlByProvider(url, provider);
// check if streamUrl is an object with streamUrl property
if (streamUrl && typeof streamUrl === "object" && !Array.isArray(streamUrl) && streamUrl.streamUrl) {
return streamUrl.streamUrl;
}
// check if streamUrl is not null, a string, and starts with http or https // check if streamUrl is not null, a string, and starts with http or https
if (streamUrl && typeof streamUrl === "string" && (streamUrl.startsWith("http"))) { if (
streamUrl &&
typeof streamUrl === "string" &&
streamUrl.startsWith("http")
) {
return streamUrl; return streamUrl;
// if its an array, get the value that starts with http // if its an array, get the value that starts with http
} else if (Array.isArray(streamUrl)) { } else if (Array.isArray(streamUrl)) {
const httpStream = streamUrl.find(url => url.startsWith("http")); const httpStream = streamUrl.find((url) => url.startsWith("http"));
if (httpStream) { if (httpStream) {
return httpStream; return httpStream;
} }
@@ -179,7 +187,6 @@ function globalExtractor(providers) {
// check if it's a valid stream URL // check if it's a valid stream URL
return null; return null;
} }
} catch (error) { } catch (error) {
// Ignore the error and try the next provider // Ignore the error and try the next provider
} }
@@ -191,16 +198,30 @@ async function multiExtractor(providers) {
/* this scheme should be returned as a JSON object /* this scheme should be returned as a JSON object
{ {
"streams": [ "streams": [
"FileMoon", {
"https://filemoon.example/stream1.m3u8", "title": "FileMoon",
"StreamWish", "streamUrl": "https://filemoon.example/stream1.m3u8",
"https://streamwish.example/stream2.m3u8", },
"Okru", {
"https://okru.example/stream3.m3u8", "title": "StreamWish",
"MP4", "streamUrl": "https://streamwish.example/stream2.m3u8",
"https://mp4upload.example/stream4.mp4", },
"Default", {
"https://default.example/stream5.m3u8" "title": "Okru",
"streamUrl": "https://okru.example/stream3.m3u8",
"headers": { // Optional headers for the stream
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.3",
"Referer": "https://okru.example/",
},
},
{
"title": "MP4",
"streamUrl": "https://mp4upload.example/stream4.mp4",
},
{
"title": "Default",
"streamUrl": "https://default.example/stream5.m3u8"
}
] ]
} }
*/ */
@@ -212,20 +233,21 @@ async function multiExtractor(providers) {
// if provider starts with "direct-", then add the url to the streams array directly // if provider starts with "direct-", then add the url to the streams array directly
if (provider.startsWith("direct-")) { if (provider.startsWith("direct-")) {
const directName = provider.slice(7); // remove "direct-" prefix const directName = provider.slice(7); // remove "direct-" prefix
if (directName && directName.length > 0) { const title = (directName && directName.length > 0) ? directName : "Direct";
streams.push(directName, url); streams.push({
} else { title: title,
streams.push("Direct", url); // fallback to "Direct" if no name is provided streamUrl: url
} });
continue; // skip to the next provider continue; // skip to the next provider
} }
if (provider.startsWith("direct")) { if (provider.startsWith("direct")) {
provider = provider.slice(7); // remove "direct-" prefix provider = provider.slice(7); // remove "direct-" prefix
if (provider && provider.length > 0) { const title = (provider && provider.length > 0) ? provider : "Direct";
streams.push(provider, url); streams.push({
} else { title: title,
streams.push("Direct", url); // fallback to "Direct" if no name is provided streamUrl: url
} });
continue; // skip to the next provider
} }
let customName = null; // to store the custom name if provided let customName = null; // to store the custom name if provided
@@ -242,15 +264,24 @@ async function multiExtractor(providers) {
console.log(`Skipping ${provider} as it has already 3 streams`); console.log(`Skipping ${provider} as it has already 3 streams`);
continue; continue;
} }
let streamUrl = await extractStreamUrlByProvider(url, provider); let result = await extractStreamUrlByProvider(url, provider);
let streamUrl = null;
let headers = null;
if (streamUrl && Array.isArray(streamUrl)) { // Check if result is an object with streamUrl and optional headers
const httpStream = streamUrl.find(url => url.startsWith("http")); if (result && typeof result === "object" && !Array.isArray(result) && result.streamUrl) {
streamUrl = result.streamUrl;
headers = result.headers || null;
} else if (result && Array.isArray(result)) {
const httpStream = result.find((url) => url.startsWith("http"));
if (httpStream) { if (httpStream) {
streamUrl = httpStream; streamUrl = httpStream;
} }
} else if (result && typeof result === "string") {
streamUrl = result;
} }
// check if provider is already in streams, if it is, add a number to it
// check if streamUrl is valid
if ( if (
!streamUrl || !streamUrl ||
typeof streamUrl !== "string" || typeof streamUrl !== "string" ||
@@ -264,22 +295,29 @@ async function multiExtractor(providers) {
provider = customName; provider = customName;
} }
let title;
if (providersCount[provider]) { if (providersCount[provider]) {
providersCount[provider]++; providersCount[provider]++;
streams.push( title = provider.charAt(0).toUpperCase() +
provider.charAt(0).toUpperCase() +
provider.slice(1) + provider.slice(1) +
"-" + "-" +
(providersCount[provider] - 1), // add a number to the provider name (providersCount[provider] - 1); // add a number to the provider name
streamUrl
);
} else { } else {
providersCount[provider] = 1; providersCount[provider] = 1;
streams.push( title = provider.charAt(0).toUpperCase() + provider.slice(1);
provider.charAt(0).toUpperCase() + provider.slice(1),
streamUrl
);
} }
const streamObject = {
title: title,
streamUrl: streamUrl
};
// Add headers if they exist
if (headers && typeof headers === "object" && Object.keys(headers).length > 0) {
streamObject.headers = headers;
}
streams.push(streamObject);
} catch (error) { } catch (error) {
// Ignore the error and try the next provider // Ignore the error and try the next provider
} }
@@ -290,33 +328,64 @@ async function multiExtractor(providers) {
async function extractStreamUrlByProvider(url, provider) { async function extractStreamUrlByProvider(url, provider) {
if (eval(`typeof ${provider}Extractor`) !== "function") { if (eval(`typeof ${provider}Extractor`) !== "function") {
// skip if the extractor is not defined // skip if the extractor is not defined
console.log(`Extractor for provider ${provider} is not defined, skipping...`); console.log(
`Extractor for provider ${provider} is not defined, skipping...`
);
return null; return null;
} }
let uas = [
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.3",
"Mozilla/5.0 (iPhone; CPU iPhone OS 18_1_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/18.1.1 Mobile/15E148 Safari/604.1",
"Mozilla/5.0 (Linux; Android 10; SM-G973F) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Mobile Safari/537.36",
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.3",
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.1.2 Safari/605.1.15",
"Mozilla/5.0 (Linux; Android 11; Pixel 4 XL) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Mobile Safari/537.36",
];
let headers = { let headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.3", "User-Agent": uas[(url.length + provider.length) % uas.length], // use a different user agent based on the url and provider
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8", "Accept":
"text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8",
"Accept-Language": "en-US,en;q=0.5", "Accept-Language": "en-US,en;q=0.5",
"Referer": url, "Referer": url,
"Connection": "keep-alive", "Connection": "keep-alive",
"x-Requested-With": "XMLHttpRequest" "x-Requested-With": "XMLHttpRequest",
}; };
if(provider == 'bigwarp') {
switch (provider) {
case "bigwarp":
delete headers["User-Agent"]; delete headers["User-Agent"];
headers["x-requested-with"] = "XMLHttpRequest"; break;
} else if (provider == 'vk') { case "vk":
case "sibnet":
headers["encoding"] = "windows-1251"; // required headers["encoding"] = "windows-1251"; // required
} else if (provider == 'sibnet') { break;
headers["encoding"] = "windows-1251"; // required case "supervideo":
} else if (provider == 'supervideo') { case "savefiles":
delete headers["User-Agent"]; headers = {
"Accept": "*/*",
"Accept-Encoding": "gzip, deflate, br",
"User-Agent": "EchoapiRuntime/1.1.0",
"Connection": "keep-alive",
"Cache-Control": "no-cache",
"Host": url.match(/https?:\/\/([^\/]+)/)[1],
};
break;
case "streamtape":
headers = {
"User-Agent":
"Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:145.0) Gecko/20100101 Firefox/145.0",
"Accept":
"text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
};
break;
} }
// console.log("Using headers: " + JSON.stringify(headers));
// fetch the url // fetch the url
// and pass the response to the extractor function // and pass the response to the extractor function
console.log("Fetching URL: " + url); console.log("Fetching URL: " + url);
const response = await soraFetch(url, { const response = await soraFetch(url, {
headers headers,
}); });
console.log("Response: " + response.status); console.log("Response: " + response.status);
@@ -324,33 +393,27 @@ async function extractStreamUrlByProvider(url, provider) {
// if title contains redirect, then get the redirect url // if title contains redirect, then get the redirect url
const title = html.match(/<title>(.*?)<\/title>/); const title = html.match(/<title>(.*?)<\/title>/);
if (title && title[1].toLowerCase().includes("redirect")) { if (title && title[1].toLowerCase().includes("redirect")) {
const redirectUrl = html.match(/<meta http-equiv="refresh" content="0;url=(.*?)"/); const matches = [
const redirectUrl2 = html.match(/window\.location\.href\s*=\s*["'](.*?)["']/); /<meta http-equiv="refresh" content="0;url=(.*?)"/,
const redirectUrl3 = html.match(/window\.location\.replace\s*\(\s*["'](.*?)["']\s*\)/); /window\.location\.href\s*=\s*["'](.*?)["']/,
if (redirectUrl) { /window\.location\.replace\s*\(\s*["'](.*?)["']\s*\)/,
console.log("Redirect URL: " + redirectUrl[1]); /window\.location\s*=\s*["'](.*?)["']/,
/window\.location\.assign\s*\(\s*["'](.*?)["']\s*\)/,
/top\.location\s*=\s*["'](.*?)["']/,
/top\.location\.replace\s*\(\s*["'](.*?)["']\s*\)/,
];
for (const match of matches) {
const redirectUrl = html.match(match);
if (redirectUrl && redirectUrl[1] && typeof redirectUrl[1] === "string" && redirectUrl[1].startsWith("http")) {
console.log("Redirect URL found: " + redirectUrl[1]);
url = redirectUrl[1]; url = redirectUrl[1];
headers['Referer'] = url;
headers['Host'] = url.match(/https?:\/\/([^\/]+)/)[1];
html = await soraFetch(url, { html = await soraFetch(url, {
headers headers,
}); }).then((res) => res.text());
html = html.text ? await html.text() : html; break;
}
} else if (redirectUrl2) {
console.log("Redirect URL 2: " + redirectUrl2[1]);
url = redirectUrl2[1];
html = await soraFetch(url, {
headers
});
html = html.text ? await html.text() : html;
} else if (redirectUrl3) {
console.log("Redirect URL 3: " + redirectUrl3[1]);
url = redirectUrl3[1];
html = await soraFetch(url, {
headers
});
html = html.text ? await html.text() : html;
} else {
console.log("No redirect URL found");
} }
} }
@@ -405,6 +468,20 @@ async function extractStreamUrlByProvider(url, provider) {
console.log("Error extracting stream URL from mp4upload:", error); console.log("Error extracting stream URL from mp4upload:", error);
return null; return null;
} }
case "oneupload":
try {
return await oneuploadExtractor(html, url);
} catch (error) {
console.log("Error extracting stream URL from oneupload:", error);
return null;
}
case "packer":
try {
return await packerExtractor(html, url);
} catch (error) {
console.log("Error extracting stream URL from packer:", error);
return null;
}
case "sendvid": case "sendvid":
try { try {
return await sendvidExtractor(html, url); return await sendvidExtractor(html, url);
@@ -419,6 +496,13 @@ async function extractStreamUrlByProvider(url, provider) {
console.log("Error extracting stream URL from sibnet:", error); console.log("Error extracting stream URL from sibnet:", error);
return null; return null;
} }
case "smoothpre":
try {
return await smoothpreExtractor(html, url);
} catch (error) {
console.log("Error extracting stream URL from smoothpre:", error);
return null;
}
case "streamtape": case "streamtape":
try { try {
return await streamtapeExtractor(html, url); return await streamtapeExtractor(html, url);
@@ -433,13 +517,6 @@ async function extractStreamUrlByProvider(url, provider) {
console.log("Error extracting stream URL from streamup:", error); console.log("Error extracting stream URL from streamup:", error);
return null; return null;
} }
case "supervideo":
try {
return await supervideoExtractor(html, url);
} catch (error) {
console.log("Error extracting stream URL from supervideo:", error);
return null;
}
case "uploadcx": case "uploadcx":
try { try {
return await uploadcxExtractor(html, url); return await uploadcxExtractor(html, url);
@@ -488,7 +565,6 @@ async function extractStreamUrlByProvider(url, provider) {
} }
} }
//////////////////////////////////////////////// ////////////////////////////////////////////////
// EXTRACTORS // // EXTRACTORS //
//////////////////////////////////////////////// ////////////////////////////////////////////////
@@ -975,7 +1051,6 @@ async function megacloudExtractor(html, embedUrl) {
* @author Cufiy * @author Cufiy
*/ */
async function mp4uploadExtractor(html, url = null) { async function mp4uploadExtractor(html, url = null) {
// src: "https://a4.mp4upload.com:183/d/xkx3b4etz3b4quuo66rbmyqtjjoivahfxp27f35pti45rzapbvj5xwb4wuqtlpewdz4dirfp/video.mp4"
const regex = /src:\s*"([^"]+)"/; const regex = /src:\s*"([^"]+)"/;
const match = html.match(regex); const match = html.match(regex);
if (match) { if (match) {
@@ -985,6 +1060,32 @@ async function mp4uploadExtractor(html, url = null) {
return null; return null;
} }
} }
/* --- oneupload --- */
/**
* @name oneuploadExtractor
* @author 50/50
*/
async function oneuploadExtractor(data, url = null) {
const match = data.match(/sources:\s*\[\{file:"([^"]+)"\}\]/);
const fileUrl = match ? match[1] : null;
return fileUrl;
}
/* --- packer --- */
/* {REQUIRED PLUGINS: unbaser} */
/**
* @name packerExtractor
* @author 50/50
*/
async function packerExtractor(data, url = null) {
const obfuscatedScript = data.match(/<script[^>]*>\s*(eval\(function\(p,a,c,k,e,d.*?\)[\s\S]*?)<\/script>/);
const unpackedScript = unpack(obfuscatedScript[1]);
const m3u8Match = unpackedScript.match(/"hls2"\s*:\s*"([^"]+)"/);
const m3u8Url = m3u8Match[1];
return m3u8Url;
}
/* --- sendvid --- */ /* --- sendvid --- */
/** /**
@@ -1020,6 +1121,29 @@ async function sibnetExtractor(html, embedUrl) {
return null; return null;
} }
} }
/* --- smoothpre --- */
/* {REQUIRED PLUGINS: unbaser} */
/**
* @name SmoothPre Extractor
* @author 50/50
*/
async function smoothpreExtractor(data, url = null) {
console.log("Using SmoothPre Extractor");
console.log("Data Length: " + data.length);
const obfuscatedScript = data.match(/<script[^>]*>\s*(eval\(function\(p,a,c,k,e,d.*?\)[\s\S]*?)<\/script>/);
if (!obfuscatedScript || !obfuscatedScript[1]) {
console.log("No obfuscated script found");
return null;
}
const unpackedScript = unpack(obfuscatedScript[1]);
const hls2Match = unpackedScript.match(/"hls2"\s*:\s*"([^"]+)"/);
const hls2Url = hls2Match ? hls2Match[1] : null;
return hls2Url;
}
/* --- streamtape --- */ /* --- streamtape --- */
/** /**
@@ -1103,26 +1227,6 @@ async function streamupExtractor(data, url = null) {
return null; return null;
} }
} }
/* --- supervideo --- */
/* {REQUIRED PLUGINS: unbaser} */
/**
* @name SuperVideo Extractor
* @author 50/50
*/
async function supervideoExtractor(data, url = null) {
const obfuscatedScript = data.match(/<script[^>]*>\s*(eval\(function\(p,a,c,k,e,d.*?\)[\s\S]*?)<\/script>/);
const unpackedScript = unpack(obfuscatedScript[1]);
const regex = /file:\s*"([^"]+\.m3u8)"/;
const match = regex.exec(unpackedScript);
if (match) {
const fileUrl = match[1];
console.log("File URL:" + fileUrl);
return fileUrl;
}
return "No stream found";
}
/* --- uploadcx --- */ /* --- uploadcx --- */
/** /**
@@ -1187,7 +1291,7 @@ async function vidmolyExtractor(html, url = null) {
const streamUrl = iframeMatch[1].startsWith("//") const streamUrl = iframeMatch[1].startsWith("//")
? "https:" + iframeMatch[1] ? "https:" + iframeMatch[1]
: iframeMatch[1]; : iframeMatch[1];
const responseTwo = await fetchv2(streamUrl); const responseTwo = await soraFetch(streamUrl);
const htmlTwo = await responseTwo.text(); const htmlTwo = await responseTwo.text();
const m3u8Match = htmlTwo.match(/sources:\s*\[\{file:"([^"]+\.m3u8)"/); const m3u8Match = htmlTwo.match(/sources:\s*\[\{file:"([^"]+\.m3u8)"/);
return m3u8Match ? m3u8Match[1] : null; return m3u8Match ? m3u8Match[1] : null;
@@ -1313,6 +1417,7 @@ function voeShiftChars(str, shift) {
.join(""); .join("");
} }
//////////////////////////////////////////////// ////////////////////////////////////////////////
// PLUGINS // // PLUGINS //
//////////////////////////////////////////////// ////////////////////////////////////////////////
@@ -1330,14 +1435,22 @@ function voeShiftChars(str, shift) {
* @returns {Promise<Response|null>} The response from the server, or null if the * @returns {Promise<Response|null>} The response from the server, or null if the
* request failed. * request failed.
*/ */
async function soraFetch(url, options = { headers: {}, method: 'GET', body: null }) { async function soraFetch(
url,
options = { headers: {}, method: "GET", body: null }
) {
try { try {
return await fetchv2(url, options.headers ?? {}, options.method ?? 'GET', options.body ?? null); return await fetchv2(
url,
options.headers ?? {},
options.method ?? "GET",
options.body ?? null
);
} catch (e) { } catch (e) {
try { try {
return await fetch(url, options); return await fetch(url, options);
} catch (error) { } catch (error) {
await console.log('soraFetch error: ' + error.message); await console.log("soraFetch error: " + error.message);
return null; return null;
} }
} }
@@ -1445,5 +1558,4 @@ function unpack(source) {
} }
/* {GE END} */ /* {GE END} */
+1 -1
View File
@@ -5,7 +5,7 @@
"name": "50/50", "name": "50/50",
"icon": "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQ3122kQwublLkZ6rf1fEpUP79BxZOFmH9BSA&s" "icon": "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQ3122kQwublLkZ6rf1fEpUP79BxZOFmH9BSA&s"
}, },
"version": "1.0.2", "version": "1.0.3",
"language": "Hindi", "language": "Hindi",
"streamType": "HLS", "streamType": "HLS",
"quality": "1080p", "quality": "1080p",
+1 -1
View File
@@ -5,7 +5,7 @@
"name": "50/50 & Cufiy", "name": "50/50 & Cufiy",
"icon": "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQ3122kQwublLkZ6rf1fEpUP79BxZOFmH9BSA&s" "icon": "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQ3122kQwublLkZ6rf1fEpUP79BxZOFmH9BSA&s"
}, },
"version": "1.1.3", "version": "1.1.4",
"language": "German (SUB)", "language": "German (SUB)",
"streamType": "HLS", "streamType": "HLS",
"quality": "1080p", "quality": "1080p",
+1 -1
View File
@@ -5,7 +5,7 @@
"name": "50/50 & Cufiy", "name": "50/50 & Cufiy",
"icon": "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQ3122kQwublLkZ6rf1fEpUP79BxZOFmH9BSA&s" "icon": "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQ3122kQwublLkZ6rf1fEpUP79BxZOFmH9BSA&s"
}, },
"version": "1.1.3", "version": "1.1.4",
"language": "German (DUB)", "language": "German (DUB)",
"streamType": "HLS", "streamType": "HLS",
"quality": "1080p", "quality": "1080p",
+1 -1
View File
@@ -5,7 +5,7 @@
"name": "50/50 & Cufiy", "name": "50/50 & Cufiy",
"icon": "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQ3122kQwublLkZ6rf1fEpUP79BxZOFmH9BSA&s" "icon": "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQ3122kQwublLkZ6rf1fEpUP79BxZOFmH9BSA&s"
}, },
"version": "1.1.3", "version": "1.1.4",
"language": "English (SUB)", "language": "English (SUB)",
"streamType": "HLS", "streamType": "HLS",
"quality": "1080p", "quality": "1080p",
+222 -110
View File
@@ -190,7 +190,7 @@ async function sendLog(message) {
// EDITING THIS FILE COULD BREAK THE UPDATER AND CAUSE ISSUES WITH THE EXTRACTOR // EDITING THIS FILE COULD BREAK THE UPDATER AND CAUSE ISSUES WITH THE EXTRACTOR
/* {GE START} */ /* {GE START} */
/* {VERSION: 1.1.8} */ /* {VERSION: 1.2.0} */
/** /**
* @name global_extractor.js * @name global_extractor.js
@@ -198,8 +198,8 @@ async function sendLog(message) {
* @author Cufiy * @author Cufiy
* @url https://github.com/JMcrafter26/sora-global-extractor * @url https://github.com/JMcrafter26/sora-global-extractor
* @license CUSTOM LICENSE - see https://github.com/JMcrafter26/sora-global-extractor/blob/main/LICENSE * @license CUSTOM LICENSE - see https://github.com/JMcrafter26/sora-global-extractor/blob/main/LICENSE
* @date 2025-11-05 15:44:57 * @date 2026-01-03 19:28:28
* @version 1.1.8 * @version 1.2.0
* @note This file was generated automatically. * @note This file was generated automatically.
* The global extractor comes with an auto-updating feature, so you can always get the latest version. https://github.com/JMcrafter26/sora-global-extractor#-auto-updater * The global extractor comes with an auto-updating feature, so you can always get the latest version. https://github.com/JMcrafter26/sora-global-extractor#-auto-updater
*/ */
@@ -209,12 +209,20 @@ function globalExtractor(providers) {
for (const [url, provider] of Object.entries(providers)) { for (const [url, provider] of Object.entries(providers)) {
try { try {
const streamUrl = extractStreamUrlByProvider(url, provider); const streamUrl = extractStreamUrlByProvider(url, provider);
// check if streamUrl is an object with streamUrl property
if (streamUrl && typeof streamUrl === "object" && !Array.isArray(streamUrl) && streamUrl.streamUrl) {
return streamUrl.streamUrl;
}
// check if streamUrl is not null, a string, and starts with http or https // check if streamUrl is not null, a string, and starts with http or https
if (streamUrl && typeof streamUrl === "string" && (streamUrl.startsWith("http"))) { if (
streamUrl &&
typeof streamUrl === "string" &&
streamUrl.startsWith("http")
) {
return streamUrl; return streamUrl;
// if its an array, get the value that starts with http // if its an array, get the value that starts with http
} else if (Array.isArray(streamUrl)) { } else if (Array.isArray(streamUrl)) {
const httpStream = streamUrl.find(url => url.startsWith("http")); const httpStream = streamUrl.find((url) => url.startsWith("http"));
if (httpStream) { if (httpStream) {
return httpStream; return httpStream;
} }
@@ -222,7 +230,6 @@ function globalExtractor(providers) {
// check if it's a valid stream URL // check if it's a valid stream URL
return null; return null;
} }
} catch (error) { } catch (error) {
// Ignore the error and try the next provider // Ignore the error and try the next provider
} }
@@ -234,16 +241,30 @@ async function multiExtractor(providers) {
/* this scheme should be returned as a JSON object /* this scheme should be returned as a JSON object
{ {
"streams": [ "streams": [
"FileMoon", {
"https://filemoon.example/stream1.m3u8", "title": "FileMoon",
"StreamWish", "streamUrl": "https://filemoon.example/stream1.m3u8",
"https://streamwish.example/stream2.m3u8", },
"Okru", {
"https://okru.example/stream3.m3u8", "title": "StreamWish",
"MP4", "streamUrl": "https://streamwish.example/stream2.m3u8",
"https://mp4upload.example/stream4.mp4", },
"Default", {
"https://default.example/stream5.m3u8" "title": "Okru",
"streamUrl": "https://okru.example/stream3.m3u8",
"headers": { // Optional headers for the stream
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.3",
"Referer": "https://okru.example/",
},
},
{
"title": "MP4",
"streamUrl": "https://mp4upload.example/stream4.mp4",
},
{
"title": "Default",
"streamUrl": "https://default.example/stream5.m3u8"
}
] ]
} }
*/ */
@@ -255,20 +276,21 @@ async function multiExtractor(providers) {
// if provider starts with "direct-", then add the url to the streams array directly // if provider starts with "direct-", then add the url to the streams array directly
if (provider.startsWith("direct-")) { if (provider.startsWith("direct-")) {
const directName = provider.slice(7); // remove "direct-" prefix const directName = provider.slice(7); // remove "direct-" prefix
if (directName && directName.length > 0) { const title = (directName && directName.length > 0) ? directName : "Direct";
streams.push(directName, url); streams.push({
} else { title: title,
streams.push("Direct", url); // fallback to "Direct" if no name is provided streamUrl: url
} });
continue; // skip to the next provider continue; // skip to the next provider
} }
if (provider.startsWith("direct")) { if (provider.startsWith("direct")) {
provider = provider.slice(7); // remove "direct-" prefix provider = provider.slice(7); // remove "direct-" prefix
if (provider && provider.length > 0) { const title = (provider && provider.length > 0) ? provider : "Direct";
streams.push(provider, url); streams.push({
} else { title: title,
streams.push("Direct", url); // fallback to "Direct" if no name is provided streamUrl: url
} });
continue; // skip to the next provider
} }
let customName = null; // to store the custom name if provided let customName = null; // to store the custom name if provided
@@ -285,15 +307,24 @@ async function multiExtractor(providers) {
console.log(`Skipping ${provider} as it has already 3 streams`); console.log(`Skipping ${provider} as it has already 3 streams`);
continue; continue;
} }
let streamUrl = await extractStreamUrlByProvider(url, provider); let result = await extractStreamUrlByProvider(url, provider);
let streamUrl = null;
let headers = null;
if (streamUrl && Array.isArray(streamUrl)) { // Check if result is an object with streamUrl and optional headers
const httpStream = streamUrl.find(url => url.startsWith("http")); if (result && typeof result === "object" && !Array.isArray(result) && result.streamUrl) {
streamUrl = result.streamUrl;
headers = result.headers || null;
} else if (result && Array.isArray(result)) {
const httpStream = result.find((url) => url.startsWith("http"));
if (httpStream) { if (httpStream) {
streamUrl = httpStream; streamUrl = httpStream;
} }
} else if (result && typeof result === "string") {
streamUrl = result;
} }
// check if provider is already in streams, if it is, add a number to it
// check if streamUrl is valid
if ( if (
!streamUrl || !streamUrl ||
typeof streamUrl !== "string" || typeof streamUrl !== "string" ||
@@ -307,22 +338,29 @@ async function multiExtractor(providers) {
provider = customName; provider = customName;
} }
let title;
if (providersCount[provider]) { if (providersCount[provider]) {
providersCount[provider]++; providersCount[provider]++;
streams.push( title = provider.charAt(0).toUpperCase() +
provider.charAt(0).toUpperCase() +
provider.slice(1) + provider.slice(1) +
"-" + "-" +
(providersCount[provider] - 1), // add a number to the provider name (providersCount[provider] - 1); // add a number to the provider name
streamUrl
);
} else { } else {
providersCount[provider] = 1; providersCount[provider] = 1;
streams.push( title = provider.charAt(0).toUpperCase() + provider.slice(1);
provider.charAt(0).toUpperCase() + provider.slice(1),
streamUrl
);
} }
const streamObject = {
title: title,
streamUrl: streamUrl
};
// Add headers if they exist
if (headers && typeof headers === "object" && Object.keys(headers).length > 0) {
streamObject.headers = headers;
}
streams.push(streamObject);
} catch (error) { } catch (error) {
// Ignore the error and try the next provider // Ignore the error and try the next provider
} }
@@ -333,33 +371,64 @@ async function multiExtractor(providers) {
async function extractStreamUrlByProvider(url, provider) { async function extractStreamUrlByProvider(url, provider) {
if (eval(`typeof ${provider}Extractor`) !== "function") { if (eval(`typeof ${provider}Extractor`) !== "function") {
// skip if the extractor is not defined // skip if the extractor is not defined
console.log(`Extractor for provider ${provider} is not defined, skipping...`); console.log(
`Extractor for provider ${provider} is not defined, skipping...`
);
return null; return null;
} }
let uas = [
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.3",
"Mozilla/5.0 (iPhone; CPU iPhone OS 18_1_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/18.1.1 Mobile/15E148 Safari/604.1",
"Mozilla/5.0 (Linux; Android 10; SM-G973F) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Mobile Safari/537.36",
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.3",
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.1.2 Safari/605.1.15",
"Mozilla/5.0 (Linux; Android 11; Pixel 4 XL) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Mobile Safari/537.36",
];
let headers = { let headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.3", "User-Agent": uas[(url.length + provider.length) % uas.length], // use a different user agent based on the url and provider
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8", "Accept":
"text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8",
"Accept-Language": "en-US,en;q=0.5", "Accept-Language": "en-US,en;q=0.5",
"Referer": url, "Referer": url,
"Connection": "keep-alive", "Connection": "keep-alive",
"x-Requested-With": "XMLHttpRequest" "x-Requested-With": "XMLHttpRequest",
}; };
if(provider == 'bigwarp') {
switch (provider) {
case "bigwarp":
delete headers["User-Agent"]; delete headers["User-Agent"];
headers["x-requested-with"] = "XMLHttpRequest"; break;
} else if (provider == 'vk') { case "vk":
case "sibnet":
headers["encoding"] = "windows-1251"; // required headers["encoding"] = "windows-1251"; // required
} else if (provider == 'sibnet') { break;
headers["encoding"] = "windows-1251"; // required case "supervideo":
} else if (provider == 'supervideo') { case "savefiles":
delete headers["User-Agent"]; headers = {
"Accept": "*/*",
"Accept-Encoding": "gzip, deflate, br",
"User-Agent": "EchoapiRuntime/1.1.0",
"Connection": "keep-alive",
"Cache-Control": "no-cache",
"Host": url.match(/https?:\/\/([^\/]+)/)[1],
};
break;
case "streamtape":
headers = {
"User-Agent":
"Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:145.0) Gecko/20100101 Firefox/145.0",
"Accept":
"text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
};
break;
} }
// console.log("Using headers: " + JSON.stringify(headers));
// fetch the url // fetch the url
// and pass the response to the extractor function // and pass the response to the extractor function
console.log("Fetching URL: " + url); console.log("Fetching URL: " + url);
const response = await soraFetch(url, { const response = await soraFetch(url, {
headers headers,
}); });
console.log("Response: " + response.status); console.log("Response: " + response.status);
@@ -367,33 +436,27 @@ async function extractStreamUrlByProvider(url, provider) {
// if title contains redirect, then get the redirect url // if title contains redirect, then get the redirect url
const title = html.match(/<title>(.*?)<\/title>/); const title = html.match(/<title>(.*?)<\/title>/);
if (title && title[1].toLowerCase().includes("redirect")) { if (title && title[1].toLowerCase().includes("redirect")) {
const redirectUrl = html.match(/<meta http-equiv="refresh" content="0;url=(.*?)"/); const matches = [
const redirectUrl2 = html.match(/window\.location\.href\s*=\s*["'](.*?)["']/); /<meta http-equiv="refresh" content="0;url=(.*?)"/,
const redirectUrl3 = html.match(/window\.location\.replace\s*\(\s*["'](.*?)["']\s*\)/); /window\.location\.href\s*=\s*["'](.*?)["']/,
if (redirectUrl) { /window\.location\.replace\s*\(\s*["'](.*?)["']\s*\)/,
console.log("Redirect URL: " + redirectUrl[1]); /window\.location\s*=\s*["'](.*?)["']/,
/window\.location\.assign\s*\(\s*["'](.*?)["']\s*\)/,
/top\.location\s*=\s*["'](.*?)["']/,
/top\.location\.replace\s*\(\s*["'](.*?)["']\s*\)/,
];
for (const match of matches) {
const redirectUrl = html.match(match);
if (redirectUrl && redirectUrl[1] && typeof redirectUrl[1] === "string" && redirectUrl[1].startsWith("http")) {
console.log("Redirect URL found: " + redirectUrl[1]);
url = redirectUrl[1]; url = redirectUrl[1];
headers['Referer'] = url;
headers['Host'] = url.match(/https?:\/\/([^\/]+)/)[1];
html = await soraFetch(url, { html = await soraFetch(url, {
headers headers,
}); }).then((res) => res.text());
html = html.text ? await html.text() : html; break;
}
} else if (redirectUrl2) {
console.log("Redirect URL 2: " + redirectUrl2[1]);
url = redirectUrl2[1];
html = await soraFetch(url, {
headers
});
html = html.text ? await html.text() : html;
} else if (redirectUrl3) {
console.log("Redirect URL 3: " + redirectUrl3[1]);
url = redirectUrl3[1];
html = await soraFetch(url, {
headers
});
html = html.text ? await html.text() : html;
} else {
console.log("No redirect URL found");
} }
} }
@@ -448,6 +511,20 @@ async function extractStreamUrlByProvider(url, provider) {
console.log("Error extracting stream URL from mp4upload:", error); console.log("Error extracting stream URL from mp4upload:", error);
return null; return null;
} }
case "oneupload":
try {
return await oneuploadExtractor(html, url);
} catch (error) {
console.log("Error extracting stream URL from oneupload:", error);
return null;
}
case "packer":
try {
return await packerExtractor(html, url);
} catch (error) {
console.log("Error extracting stream URL from packer:", error);
return null;
}
case "sendvid": case "sendvid":
try { try {
return await sendvidExtractor(html, url); return await sendvidExtractor(html, url);
@@ -462,6 +539,13 @@ async function extractStreamUrlByProvider(url, provider) {
console.log("Error extracting stream URL from sibnet:", error); console.log("Error extracting stream URL from sibnet:", error);
return null; return null;
} }
case "smoothpre":
try {
return await smoothpreExtractor(html, url);
} catch (error) {
console.log("Error extracting stream URL from smoothpre:", error);
return null;
}
case "streamtape": case "streamtape":
try { try {
return await streamtapeExtractor(html, url); return await streamtapeExtractor(html, url);
@@ -476,13 +560,6 @@ async function extractStreamUrlByProvider(url, provider) {
console.log("Error extracting stream URL from streamup:", error); console.log("Error extracting stream URL from streamup:", error);
return null; return null;
} }
case "supervideo":
try {
return await supervideoExtractor(html, url);
} catch (error) {
console.log("Error extracting stream URL from supervideo:", error);
return null;
}
case "uploadcx": case "uploadcx":
try { try {
return await uploadcxExtractor(html, url); return await uploadcxExtractor(html, url);
@@ -531,7 +608,6 @@ async function extractStreamUrlByProvider(url, provider) {
} }
} }
//////////////////////////////////////////////// ////////////////////////////////////////////////
// EXTRACTORS // // EXTRACTORS //
//////////////////////////////////////////////// ////////////////////////////////////////////////
@@ -1018,7 +1094,6 @@ async function megacloudExtractor(html, embedUrl) {
* @author Cufiy * @author Cufiy
*/ */
async function mp4uploadExtractor(html, url = null) { async function mp4uploadExtractor(html, url = null) {
// src: "https://a4.mp4upload.com:183/d/xkx3b4etz3b4quuo66rbmyqtjjoivahfxp27f35pti45rzapbvj5xwb4wuqtlpewdz4dirfp/video.mp4"
const regex = /src:\s*"([^"]+)"/; const regex = /src:\s*"([^"]+)"/;
const match = html.match(regex); const match = html.match(regex);
if (match) { if (match) {
@@ -1028,6 +1103,32 @@ async function mp4uploadExtractor(html, url = null) {
return null; return null;
} }
} }
/* --- oneupload --- */
/**
* @name oneuploadExtractor
* @author 50/50
*/
async function oneuploadExtractor(data, url = null) {
const match = data.match(/sources:\s*\[\{file:"([^"]+)"\}\]/);
const fileUrl = match ? match[1] : null;
return fileUrl;
}
/* --- packer --- */
/* {REQUIRED PLUGINS: unbaser} */
/**
* @name packerExtractor
* @author 50/50
*/
async function packerExtractor(data, url = null) {
const obfuscatedScript = data.match(/<script[^>]*>\s*(eval\(function\(p,a,c,k,e,d.*?\)[\s\S]*?)<\/script>/);
const unpackedScript = unpack(obfuscatedScript[1]);
const m3u8Match = unpackedScript.match(/"hls2"\s*:\s*"([^"]+)"/);
const m3u8Url = m3u8Match[1];
return m3u8Url;
}
/* --- sendvid --- */ /* --- sendvid --- */
/** /**
@@ -1063,6 +1164,29 @@ async function sibnetExtractor(html, embedUrl) {
return null; return null;
} }
} }
/* --- smoothpre --- */
/* {REQUIRED PLUGINS: unbaser} */
/**
* @name SmoothPre Extractor
* @author 50/50
*/
async function smoothpreExtractor(data, url = null) {
console.log("Using SmoothPre Extractor");
console.log("Data Length: " + data.length);
const obfuscatedScript = data.match(/<script[^>]*>\s*(eval\(function\(p,a,c,k,e,d.*?\)[\s\S]*?)<\/script>/);
if (!obfuscatedScript || !obfuscatedScript[1]) {
console.log("No obfuscated script found");
return null;
}
const unpackedScript = unpack(obfuscatedScript[1]);
const hls2Match = unpackedScript.match(/"hls2"\s*:\s*"([^"]+)"/);
const hls2Url = hls2Match ? hls2Match[1] : null;
return hls2Url;
}
/* --- streamtape --- */ /* --- streamtape --- */
/** /**
@@ -1146,26 +1270,6 @@ async function streamupExtractor(data, url = null) {
return null; return null;
} }
} }
/* --- supervideo --- */
/* {REQUIRED PLUGINS: unbaser} */
/**
* @name SuperVideo Extractor
* @author 50/50
*/
async function supervideoExtractor(data, url = null) {
const obfuscatedScript = data.match(/<script[^>]*>\s*(eval\(function\(p,a,c,k,e,d.*?\)[\s\S]*?)<\/script>/);
const unpackedScript = unpack(obfuscatedScript[1]);
const regex = /file:\s*"([^"]+\.m3u8)"/;
const match = regex.exec(unpackedScript);
if (match) {
const fileUrl = match[1];
console.log("File URL:" + fileUrl);
return fileUrl;
}
return "No stream found";
}
/* --- uploadcx --- */ /* --- uploadcx --- */
/** /**
@@ -1230,7 +1334,7 @@ async function vidmolyExtractor(html, url = null) {
const streamUrl = iframeMatch[1].startsWith("//") const streamUrl = iframeMatch[1].startsWith("//")
? "https:" + iframeMatch[1] ? "https:" + iframeMatch[1]
: iframeMatch[1]; : iframeMatch[1];
const responseTwo = await fetchv2(streamUrl); const responseTwo = await soraFetch(streamUrl);
const htmlTwo = await responseTwo.text(); const htmlTwo = await responseTwo.text();
const m3u8Match = htmlTwo.match(/sources:\s*\[\{file:"([^"]+\.m3u8)"/); const m3u8Match = htmlTwo.match(/sources:\s*\[\{file:"([^"]+\.m3u8)"/);
return m3u8Match ? m3u8Match[1] : null; return m3u8Match ? m3u8Match[1] : null;
@@ -1356,6 +1460,7 @@ function voeShiftChars(str, shift) {
.join(""); .join("");
} }
//////////////////////////////////////////////// ////////////////////////////////////////////////
// PLUGINS // // PLUGINS //
//////////////////////////////////////////////// ////////////////////////////////////////////////
@@ -1373,14 +1478,22 @@ function voeShiftChars(str, shift) {
* @returns {Promise<Response|null>} The response from the server, or null if the * @returns {Promise<Response|null>} The response from the server, or null if the
* request failed. * request failed.
*/ */
async function soraFetch(url, options = { headers: {}, method: 'GET', body: null }) { async function soraFetch(
url,
options = { headers: {}, method: "GET", body: null }
) {
try { try {
return await fetchv2(url, options.headers ?? {}, options.method ?? 'GET', options.body ?? null); return await fetchv2(
url,
options.headers ?? {},
options.method ?? "GET",
options.body ?? null
);
} catch (e) { } catch (e) {
try { try {
return await fetch(url, options); return await fetch(url, options);
} catch (error) { } catch (error) {
await console.log('soraFetch error: ' + error.message); await console.log("soraFetch error: " + error.message);
return null; return null;
} }
} }
@@ -1488,5 +1601,4 @@ function unpack(source) {
} }
/* {GE END} */ /* {GE END} */
+222 -110
View File
@@ -191,7 +191,7 @@ async function sendLog(message) {
// EDITING THIS FILE COULD BREAK THE UPDATER AND CAUSE ISSUES WITH THE EXTRACTOR // EDITING THIS FILE COULD BREAK THE UPDATER AND CAUSE ISSUES WITH THE EXTRACTOR
/* {GE START} */ /* {GE START} */
/* {VERSION: 1.1.8} */ /* {VERSION: 1.2.0} */
/** /**
* @name global_extractor.js * @name global_extractor.js
@@ -199,8 +199,8 @@ async function sendLog(message) {
* @author Cufiy * @author Cufiy
* @url https://github.com/JMcrafter26/sora-global-extractor * @url https://github.com/JMcrafter26/sora-global-extractor
* @license CUSTOM LICENSE - see https://github.com/JMcrafter26/sora-global-extractor/blob/main/LICENSE * @license CUSTOM LICENSE - see https://github.com/JMcrafter26/sora-global-extractor/blob/main/LICENSE
* @date 2025-11-05 15:44:57 * @date 2026-01-03 19:28:28
* @version 1.1.8 * @version 1.2.0
* @note This file was generated automatically. * @note This file was generated automatically.
* The global extractor comes with an auto-updating feature, so you can always get the latest version. https://github.com/JMcrafter26/sora-global-extractor#-auto-updater * The global extractor comes with an auto-updating feature, so you can always get the latest version. https://github.com/JMcrafter26/sora-global-extractor#-auto-updater
*/ */
@@ -210,12 +210,20 @@ function globalExtractor(providers) {
for (const [url, provider] of Object.entries(providers)) { for (const [url, provider] of Object.entries(providers)) {
try { try {
const streamUrl = extractStreamUrlByProvider(url, provider); const streamUrl = extractStreamUrlByProvider(url, provider);
// check if streamUrl is an object with streamUrl property
if (streamUrl && typeof streamUrl === "object" && !Array.isArray(streamUrl) && streamUrl.streamUrl) {
return streamUrl.streamUrl;
}
// check if streamUrl is not null, a string, and starts with http or https // check if streamUrl is not null, a string, and starts with http or https
if (streamUrl && typeof streamUrl === "string" && (streamUrl.startsWith("http"))) { if (
streamUrl &&
typeof streamUrl === "string" &&
streamUrl.startsWith("http")
) {
return streamUrl; return streamUrl;
// if its an array, get the value that starts with http // if its an array, get the value that starts with http
} else if (Array.isArray(streamUrl)) { } else if (Array.isArray(streamUrl)) {
const httpStream = streamUrl.find(url => url.startsWith("http")); const httpStream = streamUrl.find((url) => url.startsWith("http"));
if (httpStream) { if (httpStream) {
return httpStream; return httpStream;
} }
@@ -223,7 +231,6 @@ function globalExtractor(providers) {
// check if it's a valid stream URL // check if it's a valid stream URL
return null; return null;
} }
} catch (error) { } catch (error) {
// Ignore the error and try the next provider // Ignore the error and try the next provider
} }
@@ -235,16 +242,30 @@ async function multiExtractor(providers) {
/* this scheme should be returned as a JSON object /* this scheme should be returned as a JSON object
{ {
"streams": [ "streams": [
"FileMoon", {
"https://filemoon.example/stream1.m3u8", "title": "FileMoon",
"StreamWish", "streamUrl": "https://filemoon.example/stream1.m3u8",
"https://streamwish.example/stream2.m3u8", },
"Okru", {
"https://okru.example/stream3.m3u8", "title": "StreamWish",
"MP4", "streamUrl": "https://streamwish.example/stream2.m3u8",
"https://mp4upload.example/stream4.mp4", },
"Default", {
"https://default.example/stream5.m3u8" "title": "Okru",
"streamUrl": "https://okru.example/stream3.m3u8",
"headers": { // Optional headers for the stream
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.3",
"Referer": "https://okru.example/",
},
},
{
"title": "MP4",
"streamUrl": "https://mp4upload.example/stream4.mp4",
},
{
"title": "Default",
"streamUrl": "https://default.example/stream5.m3u8"
}
] ]
} }
*/ */
@@ -256,20 +277,21 @@ async function multiExtractor(providers) {
// if provider starts with "direct-", then add the url to the streams array directly // if provider starts with "direct-", then add the url to the streams array directly
if (provider.startsWith("direct-")) { if (provider.startsWith("direct-")) {
const directName = provider.slice(7); // remove "direct-" prefix const directName = provider.slice(7); // remove "direct-" prefix
if (directName && directName.length > 0) { const title = (directName && directName.length > 0) ? directName : "Direct";
streams.push(directName, url); streams.push({
} else { title: title,
streams.push("Direct", url); // fallback to "Direct" if no name is provided streamUrl: url
} });
continue; // skip to the next provider continue; // skip to the next provider
} }
if (provider.startsWith("direct")) { if (provider.startsWith("direct")) {
provider = provider.slice(7); // remove "direct-" prefix provider = provider.slice(7); // remove "direct-" prefix
if (provider && provider.length > 0) { const title = (provider && provider.length > 0) ? provider : "Direct";
streams.push(provider, url); streams.push({
} else { title: title,
streams.push("Direct", url); // fallback to "Direct" if no name is provided streamUrl: url
} });
continue; // skip to the next provider
} }
let customName = null; // to store the custom name if provided let customName = null; // to store the custom name if provided
@@ -286,15 +308,24 @@ async function multiExtractor(providers) {
console.log(`Skipping ${provider} as it has already 3 streams`); console.log(`Skipping ${provider} as it has already 3 streams`);
continue; continue;
} }
let streamUrl = await extractStreamUrlByProvider(url, provider); let result = await extractStreamUrlByProvider(url, provider);
let streamUrl = null;
let headers = null;
if (streamUrl && Array.isArray(streamUrl)) { // Check if result is an object with streamUrl and optional headers
const httpStream = streamUrl.find(url => url.startsWith("http")); if (result && typeof result === "object" && !Array.isArray(result) && result.streamUrl) {
streamUrl = result.streamUrl;
headers = result.headers || null;
} else if (result && Array.isArray(result)) {
const httpStream = result.find((url) => url.startsWith("http"));
if (httpStream) { if (httpStream) {
streamUrl = httpStream; streamUrl = httpStream;
} }
} else if (result && typeof result === "string") {
streamUrl = result;
} }
// check if provider is already in streams, if it is, add a number to it
// check if streamUrl is valid
if ( if (
!streamUrl || !streamUrl ||
typeof streamUrl !== "string" || typeof streamUrl !== "string" ||
@@ -308,22 +339,29 @@ async function multiExtractor(providers) {
provider = customName; provider = customName;
} }
let title;
if (providersCount[provider]) { if (providersCount[provider]) {
providersCount[provider]++; providersCount[provider]++;
streams.push( title = provider.charAt(0).toUpperCase() +
provider.charAt(0).toUpperCase() +
provider.slice(1) + provider.slice(1) +
"-" + "-" +
(providersCount[provider] - 1), // add a number to the provider name (providersCount[provider] - 1); // add a number to the provider name
streamUrl
);
} else { } else {
providersCount[provider] = 1; providersCount[provider] = 1;
streams.push( title = provider.charAt(0).toUpperCase() + provider.slice(1);
provider.charAt(0).toUpperCase() + provider.slice(1),
streamUrl
);
} }
const streamObject = {
title: title,
streamUrl: streamUrl
};
// Add headers if they exist
if (headers && typeof headers === "object" && Object.keys(headers).length > 0) {
streamObject.headers = headers;
}
streams.push(streamObject);
} catch (error) { } catch (error) {
// Ignore the error and try the next provider // Ignore the error and try the next provider
} }
@@ -334,33 +372,64 @@ async function multiExtractor(providers) {
async function extractStreamUrlByProvider(url, provider) { async function extractStreamUrlByProvider(url, provider) {
if (eval(`typeof ${provider}Extractor`) !== "function") { if (eval(`typeof ${provider}Extractor`) !== "function") {
// skip if the extractor is not defined // skip if the extractor is not defined
console.log(`Extractor for provider ${provider} is not defined, skipping...`); console.log(
`Extractor for provider ${provider} is not defined, skipping...`
);
return null; return null;
} }
let uas = [
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.3",
"Mozilla/5.0 (iPhone; CPU iPhone OS 18_1_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/18.1.1 Mobile/15E148 Safari/604.1",
"Mozilla/5.0 (Linux; Android 10; SM-G973F) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Mobile Safari/537.36",
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.3",
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.1.2 Safari/605.1.15",
"Mozilla/5.0 (Linux; Android 11; Pixel 4 XL) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Mobile Safari/537.36",
];
let headers = { let headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.3", "User-Agent": uas[(url.length + provider.length) % uas.length], // use a different user agent based on the url and provider
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8", "Accept":
"text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8",
"Accept-Language": "en-US,en;q=0.5", "Accept-Language": "en-US,en;q=0.5",
"Referer": url, "Referer": url,
"Connection": "keep-alive", "Connection": "keep-alive",
"x-Requested-With": "XMLHttpRequest" "x-Requested-With": "XMLHttpRequest",
}; };
if(provider == 'bigwarp') {
switch (provider) {
case "bigwarp":
delete headers["User-Agent"]; delete headers["User-Agent"];
headers["x-requested-with"] = "XMLHttpRequest"; break;
} else if (provider == 'vk') { case "vk":
case "sibnet":
headers["encoding"] = "windows-1251"; // required headers["encoding"] = "windows-1251"; // required
} else if (provider == 'sibnet') { break;
headers["encoding"] = "windows-1251"; // required case "supervideo":
} else if (provider == 'supervideo') { case "savefiles":
delete headers["User-Agent"]; headers = {
"Accept": "*/*",
"Accept-Encoding": "gzip, deflate, br",
"User-Agent": "EchoapiRuntime/1.1.0",
"Connection": "keep-alive",
"Cache-Control": "no-cache",
"Host": url.match(/https?:\/\/([^\/]+)/)[1],
};
break;
case "streamtape":
headers = {
"User-Agent":
"Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:145.0) Gecko/20100101 Firefox/145.0",
"Accept":
"text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
};
break;
} }
// console.log("Using headers: " + JSON.stringify(headers));
// fetch the url // fetch the url
// and pass the response to the extractor function // and pass the response to the extractor function
console.log("Fetching URL: " + url); console.log("Fetching URL: " + url);
const response = await soraFetch(url, { const response = await soraFetch(url, {
headers headers,
}); });
console.log("Response: " + response.status); console.log("Response: " + response.status);
@@ -368,33 +437,27 @@ async function extractStreamUrlByProvider(url, provider) {
// if title contains redirect, then get the redirect url // if title contains redirect, then get the redirect url
const title = html.match(/<title>(.*?)<\/title>/); const title = html.match(/<title>(.*?)<\/title>/);
if (title && title[1].toLowerCase().includes("redirect")) { if (title && title[1].toLowerCase().includes("redirect")) {
const redirectUrl = html.match(/<meta http-equiv="refresh" content="0;url=(.*?)"/); const matches = [
const redirectUrl2 = html.match(/window\.location\.href\s*=\s*["'](.*?)["']/); /<meta http-equiv="refresh" content="0;url=(.*?)"/,
const redirectUrl3 = html.match(/window\.location\.replace\s*\(\s*["'](.*?)["']\s*\)/); /window\.location\.href\s*=\s*["'](.*?)["']/,
if (redirectUrl) { /window\.location\.replace\s*\(\s*["'](.*?)["']\s*\)/,
console.log("Redirect URL: " + redirectUrl[1]); /window\.location\s*=\s*["'](.*?)["']/,
/window\.location\.assign\s*\(\s*["'](.*?)["']\s*\)/,
/top\.location\s*=\s*["'](.*?)["']/,
/top\.location\.replace\s*\(\s*["'](.*?)["']\s*\)/,
];
for (const match of matches) {
const redirectUrl = html.match(match);
if (redirectUrl && redirectUrl[1] && typeof redirectUrl[1] === "string" && redirectUrl[1].startsWith("http")) {
console.log("Redirect URL found: " + redirectUrl[1]);
url = redirectUrl[1]; url = redirectUrl[1];
headers['Referer'] = url;
headers['Host'] = url.match(/https?:\/\/([^\/]+)/)[1];
html = await soraFetch(url, { html = await soraFetch(url, {
headers headers,
}); }).then((res) => res.text());
html = html.text ? await html.text() : html; break;
}
} else if (redirectUrl2) {
console.log("Redirect URL 2: " + redirectUrl2[1]);
url = redirectUrl2[1];
html = await soraFetch(url, {
headers
});
html = html.text ? await html.text() : html;
} else if (redirectUrl3) {
console.log("Redirect URL 3: " + redirectUrl3[1]);
url = redirectUrl3[1];
html = await soraFetch(url, {
headers
});
html = html.text ? await html.text() : html;
} else {
console.log("No redirect URL found");
} }
} }
@@ -449,6 +512,20 @@ async function extractStreamUrlByProvider(url, provider) {
console.log("Error extracting stream URL from mp4upload:", error); console.log("Error extracting stream URL from mp4upload:", error);
return null; return null;
} }
case "oneupload":
try {
return await oneuploadExtractor(html, url);
} catch (error) {
console.log("Error extracting stream URL from oneupload:", error);
return null;
}
case "packer":
try {
return await packerExtractor(html, url);
} catch (error) {
console.log("Error extracting stream URL from packer:", error);
return null;
}
case "sendvid": case "sendvid":
try { try {
return await sendvidExtractor(html, url); return await sendvidExtractor(html, url);
@@ -463,6 +540,13 @@ async function extractStreamUrlByProvider(url, provider) {
console.log("Error extracting stream URL from sibnet:", error); console.log("Error extracting stream URL from sibnet:", error);
return null; return null;
} }
case "smoothpre":
try {
return await smoothpreExtractor(html, url);
} catch (error) {
console.log("Error extracting stream URL from smoothpre:", error);
return null;
}
case "streamtape": case "streamtape":
try { try {
return await streamtapeExtractor(html, url); return await streamtapeExtractor(html, url);
@@ -477,13 +561,6 @@ async function extractStreamUrlByProvider(url, provider) {
console.log("Error extracting stream URL from streamup:", error); console.log("Error extracting stream URL from streamup:", error);
return null; return null;
} }
case "supervideo":
try {
return await supervideoExtractor(html, url);
} catch (error) {
console.log("Error extracting stream URL from supervideo:", error);
return null;
}
case "uploadcx": case "uploadcx":
try { try {
return await uploadcxExtractor(html, url); return await uploadcxExtractor(html, url);
@@ -532,7 +609,6 @@ async function extractStreamUrlByProvider(url, provider) {
} }
} }
//////////////////////////////////////////////// ////////////////////////////////////////////////
// EXTRACTORS // // EXTRACTORS //
//////////////////////////////////////////////// ////////////////////////////////////////////////
@@ -1019,7 +1095,6 @@ async function megacloudExtractor(html, embedUrl) {
* @author Cufiy * @author Cufiy
*/ */
async function mp4uploadExtractor(html, url = null) { async function mp4uploadExtractor(html, url = null) {
// src: "https://a4.mp4upload.com:183/d/xkx3b4etz3b4quuo66rbmyqtjjoivahfxp27f35pti45rzapbvj5xwb4wuqtlpewdz4dirfp/video.mp4"
const regex = /src:\s*"([^"]+)"/; const regex = /src:\s*"([^"]+)"/;
const match = html.match(regex); const match = html.match(regex);
if (match) { if (match) {
@@ -1029,6 +1104,32 @@ async function mp4uploadExtractor(html, url = null) {
return null; return null;
} }
} }
/* --- oneupload --- */
/**
* @name oneuploadExtractor
* @author 50/50
*/
async function oneuploadExtractor(data, url = null) {
const match = data.match(/sources:\s*\[\{file:"([^"]+)"\}\]/);
const fileUrl = match ? match[1] : null;
return fileUrl;
}
/* --- packer --- */
/* {REQUIRED PLUGINS: unbaser} */
/**
* @name packerExtractor
* @author 50/50
*/
async function packerExtractor(data, url = null) {
const obfuscatedScript = data.match(/<script[^>]*>\s*(eval\(function\(p,a,c,k,e,d.*?\)[\s\S]*?)<\/script>/);
const unpackedScript = unpack(obfuscatedScript[1]);
const m3u8Match = unpackedScript.match(/"hls2"\s*:\s*"([^"]+)"/);
const m3u8Url = m3u8Match[1];
return m3u8Url;
}
/* --- sendvid --- */ /* --- sendvid --- */
/** /**
@@ -1064,6 +1165,29 @@ async function sibnetExtractor(html, embedUrl) {
return null; return null;
} }
} }
/* --- smoothpre --- */
/* {REQUIRED PLUGINS: unbaser} */
/**
* @name SmoothPre Extractor
* @author 50/50
*/
async function smoothpreExtractor(data, url = null) {
console.log("Using SmoothPre Extractor");
console.log("Data Length: " + data.length);
const obfuscatedScript = data.match(/<script[^>]*>\s*(eval\(function\(p,a,c,k,e,d.*?\)[\s\S]*?)<\/script>/);
if (!obfuscatedScript || !obfuscatedScript[1]) {
console.log("No obfuscated script found");
return null;
}
const unpackedScript = unpack(obfuscatedScript[1]);
const hls2Match = unpackedScript.match(/"hls2"\s*:\s*"([^"]+)"/);
const hls2Url = hls2Match ? hls2Match[1] : null;
return hls2Url;
}
/* --- streamtape --- */ /* --- streamtape --- */
/** /**
@@ -1147,26 +1271,6 @@ async function streamupExtractor(data, url = null) {
return null; return null;
} }
} }
/* --- supervideo --- */
/* {REQUIRED PLUGINS: unbaser} */
/**
* @name SuperVideo Extractor
* @author 50/50
*/
async function supervideoExtractor(data, url = null) {
const obfuscatedScript = data.match(/<script[^>]*>\s*(eval\(function\(p,a,c,k,e,d.*?\)[\s\S]*?)<\/script>/);
const unpackedScript = unpack(obfuscatedScript[1]);
const regex = /file:\s*"([^"]+\.m3u8)"/;
const match = regex.exec(unpackedScript);
if (match) {
const fileUrl = match[1];
console.log("File URL:" + fileUrl);
return fileUrl;
}
return "No stream found";
}
/* --- uploadcx --- */ /* --- uploadcx --- */
/** /**
@@ -1231,7 +1335,7 @@ async function vidmolyExtractor(html, url = null) {
const streamUrl = iframeMatch[1].startsWith("//") const streamUrl = iframeMatch[1].startsWith("//")
? "https:" + iframeMatch[1] ? "https:" + iframeMatch[1]
: iframeMatch[1]; : iframeMatch[1];
const responseTwo = await fetchv2(streamUrl); const responseTwo = await soraFetch(streamUrl);
const htmlTwo = await responseTwo.text(); const htmlTwo = await responseTwo.text();
const m3u8Match = htmlTwo.match(/sources:\s*\[\{file:"([^"]+\.m3u8)"/); const m3u8Match = htmlTwo.match(/sources:\s*\[\{file:"([^"]+\.m3u8)"/);
return m3u8Match ? m3u8Match[1] : null; return m3u8Match ? m3u8Match[1] : null;
@@ -1357,6 +1461,7 @@ function voeShiftChars(str, shift) {
.join(""); .join("");
} }
//////////////////////////////////////////////// ////////////////////////////////////////////////
// PLUGINS // // PLUGINS //
//////////////////////////////////////////////// ////////////////////////////////////////////////
@@ -1374,14 +1479,22 @@ function voeShiftChars(str, shift) {
* @returns {Promise<Response|null>} The response from the server, or null if the * @returns {Promise<Response|null>} The response from the server, or null if the
* request failed. * request failed.
*/ */
async function soraFetch(url, options = { headers: {}, method: 'GET', body: null }) { async function soraFetch(
url,
options = { headers: {}, method: "GET", body: null }
) {
try { try {
return await fetchv2(url, options.headers ?? {}, options.method ?? 'GET', options.body ?? null); return await fetchv2(
url,
options.headers ?? {},
options.method ?? "GET",
options.body ?? null
);
} catch (e) { } catch (e) {
try { try {
return await fetch(url, options); return await fetch(url, options);
} catch (error) { } catch (error) {
await console.log('soraFetch error: ' + error.message); await console.log("soraFetch error: " + error.message);
return null; return null;
} }
} }
@@ -1489,5 +1602,4 @@ function unpack(source) {
} }
/* {GE END} */ /* {GE END} */
+222 -110
View File
@@ -190,7 +190,7 @@ async function sendLog(message) {
// EDITING THIS FILE COULD BREAK THE UPDATER AND CAUSE ISSUES WITH THE EXTRACTOR // EDITING THIS FILE COULD BREAK THE UPDATER AND CAUSE ISSUES WITH THE EXTRACTOR
/* {GE START} */ /* {GE START} */
/* {VERSION: 1.1.8} */ /* {VERSION: 1.2.0} */
/** /**
* @name global_extractor.js * @name global_extractor.js
@@ -198,8 +198,8 @@ async function sendLog(message) {
* @author Cufiy * @author Cufiy
* @url https://github.com/JMcrafter26/sora-global-extractor * @url https://github.com/JMcrafter26/sora-global-extractor
* @license CUSTOM LICENSE - see https://github.com/JMcrafter26/sora-global-extractor/blob/main/LICENSE * @license CUSTOM LICENSE - see https://github.com/JMcrafter26/sora-global-extractor/blob/main/LICENSE
* @date 2025-11-05 15:44:57 * @date 2026-01-03 19:28:28
* @version 1.1.8 * @version 1.2.0
* @note This file was generated automatically. * @note This file was generated automatically.
* The global extractor comes with an auto-updating feature, so you can always get the latest version. https://github.com/JMcrafter26/sora-global-extractor#-auto-updater * The global extractor comes with an auto-updating feature, so you can always get the latest version. https://github.com/JMcrafter26/sora-global-extractor#-auto-updater
*/ */
@@ -209,12 +209,20 @@ function globalExtractor(providers) {
for (const [url, provider] of Object.entries(providers)) { for (const [url, provider] of Object.entries(providers)) {
try { try {
const streamUrl = extractStreamUrlByProvider(url, provider); const streamUrl = extractStreamUrlByProvider(url, provider);
// check if streamUrl is an object with streamUrl property
if (streamUrl && typeof streamUrl === "object" && !Array.isArray(streamUrl) && streamUrl.streamUrl) {
return streamUrl.streamUrl;
}
// check if streamUrl is not null, a string, and starts with http or https // check if streamUrl is not null, a string, and starts with http or https
if (streamUrl && typeof streamUrl === "string" && (streamUrl.startsWith("http"))) { if (
streamUrl &&
typeof streamUrl === "string" &&
streamUrl.startsWith("http")
) {
return streamUrl; return streamUrl;
// if its an array, get the value that starts with http // if its an array, get the value that starts with http
} else if (Array.isArray(streamUrl)) { } else if (Array.isArray(streamUrl)) {
const httpStream = streamUrl.find(url => url.startsWith("http")); const httpStream = streamUrl.find((url) => url.startsWith("http"));
if (httpStream) { if (httpStream) {
return httpStream; return httpStream;
} }
@@ -222,7 +230,6 @@ function globalExtractor(providers) {
// check if it's a valid stream URL // check if it's a valid stream URL
return null; return null;
} }
} catch (error) { } catch (error) {
// Ignore the error and try the next provider // Ignore the error and try the next provider
} }
@@ -234,16 +241,30 @@ async function multiExtractor(providers) {
/* this scheme should be returned as a JSON object /* this scheme should be returned as a JSON object
{ {
"streams": [ "streams": [
"FileMoon", {
"https://filemoon.example/stream1.m3u8", "title": "FileMoon",
"StreamWish", "streamUrl": "https://filemoon.example/stream1.m3u8",
"https://streamwish.example/stream2.m3u8", },
"Okru", {
"https://okru.example/stream3.m3u8", "title": "StreamWish",
"MP4", "streamUrl": "https://streamwish.example/stream2.m3u8",
"https://mp4upload.example/stream4.mp4", },
"Default", {
"https://default.example/stream5.m3u8" "title": "Okru",
"streamUrl": "https://okru.example/stream3.m3u8",
"headers": { // Optional headers for the stream
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.3",
"Referer": "https://okru.example/",
},
},
{
"title": "MP4",
"streamUrl": "https://mp4upload.example/stream4.mp4",
},
{
"title": "Default",
"streamUrl": "https://default.example/stream5.m3u8"
}
] ]
} }
*/ */
@@ -255,20 +276,21 @@ async function multiExtractor(providers) {
// if provider starts with "direct-", then add the url to the streams array directly // if provider starts with "direct-", then add the url to the streams array directly
if (provider.startsWith("direct-")) { if (provider.startsWith("direct-")) {
const directName = provider.slice(7); // remove "direct-" prefix const directName = provider.slice(7); // remove "direct-" prefix
if (directName && directName.length > 0) { const title = (directName && directName.length > 0) ? directName : "Direct";
streams.push(directName, url); streams.push({
} else { title: title,
streams.push("Direct", url); // fallback to "Direct" if no name is provided streamUrl: url
} });
continue; // skip to the next provider continue; // skip to the next provider
} }
if (provider.startsWith("direct")) { if (provider.startsWith("direct")) {
provider = provider.slice(7); // remove "direct-" prefix provider = provider.slice(7); // remove "direct-" prefix
if (provider && provider.length > 0) { const title = (provider && provider.length > 0) ? provider : "Direct";
streams.push(provider, url); streams.push({
} else { title: title,
streams.push("Direct", url); // fallback to "Direct" if no name is provided streamUrl: url
} });
continue; // skip to the next provider
} }
let customName = null; // to store the custom name if provided let customName = null; // to store the custom name if provided
@@ -285,15 +307,24 @@ async function multiExtractor(providers) {
console.log(`Skipping ${provider} as it has already 3 streams`); console.log(`Skipping ${provider} as it has already 3 streams`);
continue; continue;
} }
let streamUrl = await extractStreamUrlByProvider(url, provider); let result = await extractStreamUrlByProvider(url, provider);
let streamUrl = null;
let headers = null;
if (streamUrl && Array.isArray(streamUrl)) { // Check if result is an object with streamUrl and optional headers
const httpStream = streamUrl.find(url => url.startsWith("http")); if (result && typeof result === "object" && !Array.isArray(result) && result.streamUrl) {
streamUrl = result.streamUrl;
headers = result.headers || null;
} else if (result && Array.isArray(result)) {
const httpStream = result.find((url) => url.startsWith("http"));
if (httpStream) { if (httpStream) {
streamUrl = httpStream; streamUrl = httpStream;
} }
} else if (result && typeof result === "string") {
streamUrl = result;
} }
// check if provider is already in streams, if it is, add a number to it
// check if streamUrl is valid
if ( if (
!streamUrl || !streamUrl ||
typeof streamUrl !== "string" || typeof streamUrl !== "string" ||
@@ -307,22 +338,29 @@ async function multiExtractor(providers) {
provider = customName; provider = customName;
} }
let title;
if (providersCount[provider]) { if (providersCount[provider]) {
providersCount[provider]++; providersCount[provider]++;
streams.push( title = provider.charAt(0).toUpperCase() +
provider.charAt(0).toUpperCase() +
provider.slice(1) + provider.slice(1) +
"-" + "-" +
(providersCount[provider] - 1), // add a number to the provider name (providersCount[provider] - 1); // add a number to the provider name
streamUrl
);
} else { } else {
providersCount[provider] = 1; providersCount[provider] = 1;
streams.push( title = provider.charAt(0).toUpperCase() + provider.slice(1);
provider.charAt(0).toUpperCase() + provider.slice(1),
streamUrl
);
} }
const streamObject = {
title: title,
streamUrl: streamUrl
};
// Add headers if they exist
if (headers && typeof headers === "object" && Object.keys(headers).length > 0) {
streamObject.headers = headers;
}
streams.push(streamObject);
} catch (error) { } catch (error) {
// Ignore the error and try the next provider // Ignore the error and try the next provider
} }
@@ -333,33 +371,64 @@ async function multiExtractor(providers) {
async function extractStreamUrlByProvider(url, provider) { async function extractStreamUrlByProvider(url, provider) {
if (eval(`typeof ${provider}Extractor`) !== "function") { if (eval(`typeof ${provider}Extractor`) !== "function") {
// skip if the extractor is not defined // skip if the extractor is not defined
console.log(`Extractor for provider ${provider} is not defined, skipping...`); console.log(
`Extractor for provider ${provider} is not defined, skipping...`
);
return null; return null;
} }
let uas = [
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.3",
"Mozilla/5.0 (iPhone; CPU iPhone OS 18_1_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/18.1.1 Mobile/15E148 Safari/604.1",
"Mozilla/5.0 (Linux; Android 10; SM-G973F) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Mobile Safari/537.36",
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.3",
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.1.2 Safari/605.1.15",
"Mozilla/5.0 (Linux; Android 11; Pixel 4 XL) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Mobile Safari/537.36",
];
let headers = { let headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.3", "User-Agent": uas[(url.length + provider.length) % uas.length], // use a different user agent based on the url and provider
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8", "Accept":
"text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8",
"Accept-Language": "en-US,en;q=0.5", "Accept-Language": "en-US,en;q=0.5",
"Referer": url, "Referer": url,
"Connection": "keep-alive", "Connection": "keep-alive",
"x-Requested-With": "XMLHttpRequest" "x-Requested-With": "XMLHttpRequest",
}; };
if(provider == 'bigwarp') {
switch (provider) {
case "bigwarp":
delete headers["User-Agent"]; delete headers["User-Agent"];
headers["x-requested-with"] = "XMLHttpRequest"; break;
} else if (provider == 'vk') { case "vk":
case "sibnet":
headers["encoding"] = "windows-1251"; // required headers["encoding"] = "windows-1251"; // required
} else if (provider == 'sibnet') { break;
headers["encoding"] = "windows-1251"; // required case "supervideo":
} else if (provider == 'supervideo') { case "savefiles":
delete headers["User-Agent"]; headers = {
"Accept": "*/*",
"Accept-Encoding": "gzip, deflate, br",
"User-Agent": "EchoapiRuntime/1.1.0",
"Connection": "keep-alive",
"Cache-Control": "no-cache",
"Host": url.match(/https?:\/\/([^\/]+)/)[1],
};
break;
case "streamtape":
headers = {
"User-Agent":
"Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:145.0) Gecko/20100101 Firefox/145.0",
"Accept":
"text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
};
break;
} }
// console.log("Using headers: " + JSON.stringify(headers));
// fetch the url // fetch the url
// and pass the response to the extractor function // and pass the response to the extractor function
console.log("Fetching URL: " + url); console.log("Fetching URL: " + url);
const response = await soraFetch(url, { const response = await soraFetch(url, {
headers headers,
}); });
console.log("Response: " + response.status); console.log("Response: " + response.status);
@@ -367,33 +436,27 @@ async function extractStreamUrlByProvider(url, provider) {
// if title contains redirect, then get the redirect url // if title contains redirect, then get the redirect url
const title = html.match(/<title>(.*?)<\/title>/); const title = html.match(/<title>(.*?)<\/title>/);
if (title && title[1].toLowerCase().includes("redirect")) { if (title && title[1].toLowerCase().includes("redirect")) {
const redirectUrl = html.match(/<meta http-equiv="refresh" content="0;url=(.*?)"/); const matches = [
const redirectUrl2 = html.match(/window\.location\.href\s*=\s*["'](.*?)["']/); /<meta http-equiv="refresh" content="0;url=(.*?)"/,
const redirectUrl3 = html.match(/window\.location\.replace\s*\(\s*["'](.*?)["']\s*\)/); /window\.location\.href\s*=\s*["'](.*?)["']/,
if (redirectUrl) { /window\.location\.replace\s*\(\s*["'](.*?)["']\s*\)/,
console.log("Redirect URL: " + redirectUrl[1]); /window\.location\s*=\s*["'](.*?)["']/,
/window\.location\.assign\s*\(\s*["'](.*?)["']\s*\)/,
/top\.location\s*=\s*["'](.*?)["']/,
/top\.location\.replace\s*\(\s*["'](.*?)["']\s*\)/,
];
for (const match of matches) {
const redirectUrl = html.match(match);
if (redirectUrl && redirectUrl[1] && typeof redirectUrl[1] === "string" && redirectUrl[1].startsWith("http")) {
console.log("Redirect URL found: " + redirectUrl[1]);
url = redirectUrl[1]; url = redirectUrl[1];
headers['Referer'] = url;
headers['Host'] = url.match(/https?:\/\/([^\/]+)/)[1];
html = await soraFetch(url, { html = await soraFetch(url, {
headers headers,
}); }).then((res) => res.text());
html = html.text ? await html.text() : html; break;
}
} else if (redirectUrl2) {
console.log("Redirect URL 2: " + redirectUrl2[1]);
url = redirectUrl2[1];
html = await soraFetch(url, {
headers
});
html = html.text ? await html.text() : html;
} else if (redirectUrl3) {
console.log("Redirect URL 3: " + redirectUrl3[1]);
url = redirectUrl3[1];
html = await soraFetch(url, {
headers
});
html = html.text ? await html.text() : html;
} else {
console.log("No redirect URL found");
} }
} }
@@ -448,6 +511,20 @@ async function extractStreamUrlByProvider(url, provider) {
console.log("Error extracting stream URL from mp4upload:", error); console.log("Error extracting stream URL from mp4upload:", error);
return null; return null;
} }
case "oneupload":
try {
return await oneuploadExtractor(html, url);
} catch (error) {
console.log("Error extracting stream URL from oneupload:", error);
return null;
}
case "packer":
try {
return await packerExtractor(html, url);
} catch (error) {
console.log("Error extracting stream URL from packer:", error);
return null;
}
case "sendvid": case "sendvid":
try { try {
return await sendvidExtractor(html, url); return await sendvidExtractor(html, url);
@@ -462,6 +539,13 @@ async function extractStreamUrlByProvider(url, provider) {
console.log("Error extracting stream URL from sibnet:", error); console.log("Error extracting stream URL from sibnet:", error);
return null; return null;
} }
case "smoothpre":
try {
return await smoothpreExtractor(html, url);
} catch (error) {
console.log("Error extracting stream URL from smoothpre:", error);
return null;
}
case "streamtape": case "streamtape":
try { try {
return await streamtapeExtractor(html, url); return await streamtapeExtractor(html, url);
@@ -476,13 +560,6 @@ async function extractStreamUrlByProvider(url, provider) {
console.log("Error extracting stream URL from streamup:", error); console.log("Error extracting stream URL from streamup:", error);
return null; return null;
} }
case "supervideo":
try {
return await supervideoExtractor(html, url);
} catch (error) {
console.log("Error extracting stream URL from supervideo:", error);
return null;
}
case "uploadcx": case "uploadcx":
try { try {
return await uploadcxExtractor(html, url); return await uploadcxExtractor(html, url);
@@ -531,7 +608,6 @@ async function extractStreamUrlByProvider(url, provider) {
} }
} }
//////////////////////////////////////////////// ////////////////////////////////////////////////
// EXTRACTORS // // EXTRACTORS //
//////////////////////////////////////////////// ////////////////////////////////////////////////
@@ -1018,7 +1094,6 @@ async function megacloudExtractor(html, embedUrl) {
* @author Cufiy * @author Cufiy
*/ */
async function mp4uploadExtractor(html, url = null) { async function mp4uploadExtractor(html, url = null) {
// src: "https://a4.mp4upload.com:183/d/xkx3b4etz3b4quuo66rbmyqtjjoivahfxp27f35pti45rzapbvj5xwb4wuqtlpewdz4dirfp/video.mp4"
const regex = /src:\s*"([^"]+)"/; const regex = /src:\s*"([^"]+)"/;
const match = html.match(regex); const match = html.match(regex);
if (match) { if (match) {
@@ -1028,6 +1103,32 @@ async function mp4uploadExtractor(html, url = null) {
return null; return null;
} }
} }
/* --- oneupload --- */
/**
* @name oneuploadExtractor
* @author 50/50
*/
async function oneuploadExtractor(data, url = null) {
const match = data.match(/sources:\s*\[\{file:"([^"]+)"\}\]/);
const fileUrl = match ? match[1] : null;
return fileUrl;
}
/* --- packer --- */
/* {REQUIRED PLUGINS: unbaser} */
/**
* @name packerExtractor
* @author 50/50
*/
async function packerExtractor(data, url = null) {
const obfuscatedScript = data.match(/<script[^>]*>\s*(eval\(function\(p,a,c,k,e,d.*?\)[\s\S]*?)<\/script>/);
const unpackedScript = unpack(obfuscatedScript[1]);
const m3u8Match = unpackedScript.match(/"hls2"\s*:\s*"([^"]+)"/);
const m3u8Url = m3u8Match[1];
return m3u8Url;
}
/* --- sendvid --- */ /* --- sendvid --- */
/** /**
@@ -1063,6 +1164,29 @@ async function sibnetExtractor(html, embedUrl) {
return null; return null;
} }
} }
/* --- smoothpre --- */
/* {REQUIRED PLUGINS: unbaser} */
/**
* @name SmoothPre Extractor
* @author 50/50
*/
async function smoothpreExtractor(data, url = null) {
console.log("Using SmoothPre Extractor");
console.log("Data Length: " + data.length);
const obfuscatedScript = data.match(/<script[^>]*>\s*(eval\(function\(p,a,c,k,e,d.*?\)[\s\S]*?)<\/script>/);
if (!obfuscatedScript || !obfuscatedScript[1]) {
console.log("No obfuscated script found");
return null;
}
const unpackedScript = unpack(obfuscatedScript[1]);
const hls2Match = unpackedScript.match(/"hls2"\s*:\s*"([^"]+)"/);
const hls2Url = hls2Match ? hls2Match[1] : null;
return hls2Url;
}
/* --- streamtape --- */ /* --- streamtape --- */
/** /**
@@ -1146,26 +1270,6 @@ async function streamupExtractor(data, url = null) {
return null; return null;
} }
} }
/* --- supervideo --- */
/* {REQUIRED PLUGINS: unbaser} */
/**
* @name SuperVideo Extractor
* @author 50/50
*/
async function supervideoExtractor(data, url = null) {
const obfuscatedScript = data.match(/<script[^>]*>\s*(eval\(function\(p,a,c,k,e,d.*?\)[\s\S]*?)<\/script>/);
const unpackedScript = unpack(obfuscatedScript[1]);
const regex = /file:\s*"([^"]+\.m3u8)"/;
const match = regex.exec(unpackedScript);
if (match) {
const fileUrl = match[1];
console.log("File URL:" + fileUrl);
return fileUrl;
}
return "No stream found";
}
/* --- uploadcx --- */ /* --- uploadcx --- */
/** /**
@@ -1230,7 +1334,7 @@ async function vidmolyExtractor(html, url = null) {
const streamUrl = iframeMatch[1].startsWith("//") const streamUrl = iframeMatch[1].startsWith("//")
? "https:" + iframeMatch[1] ? "https:" + iframeMatch[1]
: iframeMatch[1]; : iframeMatch[1];
const responseTwo = await fetchv2(streamUrl); const responseTwo = await soraFetch(streamUrl);
const htmlTwo = await responseTwo.text(); const htmlTwo = await responseTwo.text();
const m3u8Match = htmlTwo.match(/sources:\s*\[\{file:"([^"]+\.m3u8)"/); const m3u8Match = htmlTwo.match(/sources:\s*\[\{file:"([^"]+\.m3u8)"/);
return m3u8Match ? m3u8Match[1] : null; return m3u8Match ? m3u8Match[1] : null;
@@ -1356,6 +1460,7 @@ function voeShiftChars(str, shift) {
.join(""); .join("");
} }
//////////////////////////////////////////////// ////////////////////////////////////////////////
// PLUGINS // // PLUGINS //
//////////////////////////////////////////////// ////////////////////////////////////////////////
@@ -1373,14 +1478,22 @@ function voeShiftChars(str, shift) {
* @returns {Promise<Response|null>} The response from the server, or null if the * @returns {Promise<Response|null>} The response from the server, or null if the
* request failed. * request failed.
*/ */
async function soraFetch(url, options = { headers: {}, method: 'GET', body: null }) { async function soraFetch(
url,
options = { headers: {}, method: "GET", body: null }
) {
try { try {
return await fetchv2(url, options.headers ?? {}, options.method ?? 'GET', options.body ?? null); return await fetchv2(
url,
options.headers ?? {},
options.method ?? "GET",
options.body ?? null
);
} catch (e) { } catch (e) {
try { try {
return await fetch(url, options); return await fetch(url, options);
} catch (error) { } catch (error) {
await console.log('soraFetch error: ' + error.message); await console.log("soraFetch error: " + error.message);
return null; return null;
} }
} }
@@ -1488,5 +1601,4 @@ function unpack(source) {
} }
/* {GE END} */ /* {GE END} */
+1 -1
View File
@@ -5,7 +5,7 @@
"name": "Cufiy", "name": "Cufiy",
"icon": "https://files.catbox.moe/ttj4fc.gif" "icon": "https://files.catbox.moe/ttj4fc.gif"
}, },
"version": "0.3.16", "version": "0.3.17",
"language": "English (DUB)", "language": "English (DUB)",
"streamType": "HLS", "streamType": "HLS",
"quality": "720p", "quality": "720p",
+222 -110
View File
@@ -376,7 +376,7 @@ function base64Decode(str) {
// EDITING THIS FILE COULD BREAK THE UPDATER AND CAUSE ISSUES WITH THE EXTRACTOR // EDITING THIS FILE COULD BREAK THE UPDATER AND CAUSE ISSUES WITH THE EXTRACTOR
/* {GE START} */ /* {GE START} */
/* {VERSION: 1.1.8} */ /* {VERSION: 1.2.0} */
/** /**
* @name global_extractor.js * @name global_extractor.js
@@ -384,8 +384,8 @@ function base64Decode(str) {
* @author Cufiy * @author Cufiy
* @url https://github.com/JMcrafter26/sora-global-extractor * @url https://github.com/JMcrafter26/sora-global-extractor
* @license CUSTOM LICENSE - see https://github.com/JMcrafter26/sora-global-extractor/blob/main/LICENSE * @license CUSTOM LICENSE - see https://github.com/JMcrafter26/sora-global-extractor/blob/main/LICENSE
* @date 2025-11-05 15:44:57 * @date 2026-01-03 19:28:28
* @version 1.1.8 * @version 1.2.0
* @note This file was generated automatically. * @note This file was generated automatically.
* The global extractor comes with an auto-updating feature, so you can always get the latest version. https://github.com/JMcrafter26/sora-global-extractor#-auto-updater * The global extractor comes with an auto-updating feature, so you can always get the latest version. https://github.com/JMcrafter26/sora-global-extractor#-auto-updater
*/ */
@@ -395,12 +395,20 @@ function globalExtractor(providers) {
for (const [url, provider] of Object.entries(providers)) { for (const [url, provider] of Object.entries(providers)) {
try { try {
const streamUrl = extractStreamUrlByProvider(url, provider); const streamUrl = extractStreamUrlByProvider(url, provider);
// check if streamUrl is an object with streamUrl property
if (streamUrl && typeof streamUrl === "object" && !Array.isArray(streamUrl) && streamUrl.streamUrl) {
return streamUrl.streamUrl;
}
// check if streamUrl is not null, a string, and starts with http or https // check if streamUrl is not null, a string, and starts with http or https
if (streamUrl && typeof streamUrl === "string" && (streamUrl.startsWith("http"))) { if (
streamUrl &&
typeof streamUrl === "string" &&
streamUrl.startsWith("http")
) {
return streamUrl; return streamUrl;
// if its an array, get the value that starts with http // if its an array, get the value that starts with http
} else if (Array.isArray(streamUrl)) { } else if (Array.isArray(streamUrl)) {
const httpStream = streamUrl.find(url => url.startsWith("http")); const httpStream = streamUrl.find((url) => url.startsWith("http"));
if (httpStream) { if (httpStream) {
return httpStream; return httpStream;
} }
@@ -408,7 +416,6 @@ function globalExtractor(providers) {
// check if it's a valid stream URL // check if it's a valid stream URL
return null; return null;
} }
} catch (error) { } catch (error) {
// Ignore the error and try the next provider // Ignore the error and try the next provider
} }
@@ -420,16 +427,30 @@ async function multiExtractor(providers) {
/* this scheme should be returned as a JSON object /* this scheme should be returned as a JSON object
{ {
"streams": [ "streams": [
"FileMoon", {
"https://filemoon.example/stream1.m3u8", "title": "FileMoon",
"StreamWish", "streamUrl": "https://filemoon.example/stream1.m3u8",
"https://streamwish.example/stream2.m3u8", },
"Okru", {
"https://okru.example/stream3.m3u8", "title": "StreamWish",
"MP4", "streamUrl": "https://streamwish.example/stream2.m3u8",
"https://mp4upload.example/stream4.mp4", },
"Default", {
"https://default.example/stream5.m3u8" "title": "Okru",
"streamUrl": "https://okru.example/stream3.m3u8",
"headers": { // Optional headers for the stream
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.3",
"Referer": "https://okru.example/",
},
},
{
"title": "MP4",
"streamUrl": "https://mp4upload.example/stream4.mp4",
},
{
"title": "Default",
"streamUrl": "https://default.example/stream5.m3u8"
}
] ]
} }
*/ */
@@ -441,20 +462,21 @@ async function multiExtractor(providers) {
// if provider starts with "direct-", then add the url to the streams array directly // if provider starts with "direct-", then add the url to the streams array directly
if (provider.startsWith("direct-")) { if (provider.startsWith("direct-")) {
const directName = provider.slice(7); // remove "direct-" prefix const directName = provider.slice(7); // remove "direct-" prefix
if (directName && directName.length > 0) { const title = (directName && directName.length > 0) ? directName : "Direct";
streams.push(directName, url); streams.push({
} else { title: title,
streams.push("Direct", url); // fallback to "Direct" if no name is provided streamUrl: url
} });
continue; // skip to the next provider continue; // skip to the next provider
} }
if (provider.startsWith("direct")) { if (provider.startsWith("direct")) {
provider = provider.slice(7); // remove "direct-" prefix provider = provider.slice(7); // remove "direct-" prefix
if (provider && provider.length > 0) { const title = (provider && provider.length > 0) ? provider : "Direct";
streams.push(provider, url); streams.push({
} else { title: title,
streams.push("Direct", url); // fallback to "Direct" if no name is provided streamUrl: url
} });
continue; // skip to the next provider
} }
let customName = null; // to store the custom name if provided let customName = null; // to store the custom name if provided
@@ -471,15 +493,24 @@ async function multiExtractor(providers) {
console.log(`Skipping ${provider} as it has already 3 streams`); console.log(`Skipping ${provider} as it has already 3 streams`);
continue; continue;
} }
let streamUrl = await extractStreamUrlByProvider(url, provider); let result = await extractStreamUrlByProvider(url, provider);
let streamUrl = null;
let headers = null;
if (streamUrl && Array.isArray(streamUrl)) { // Check if result is an object with streamUrl and optional headers
const httpStream = streamUrl.find(url => url.startsWith("http")); if (result && typeof result === "object" && !Array.isArray(result) && result.streamUrl) {
streamUrl = result.streamUrl;
headers = result.headers || null;
} else if (result && Array.isArray(result)) {
const httpStream = result.find((url) => url.startsWith("http"));
if (httpStream) { if (httpStream) {
streamUrl = httpStream; streamUrl = httpStream;
} }
} else if (result && typeof result === "string") {
streamUrl = result;
} }
// check if provider is already in streams, if it is, add a number to it
// check if streamUrl is valid
if ( if (
!streamUrl || !streamUrl ||
typeof streamUrl !== "string" || typeof streamUrl !== "string" ||
@@ -493,22 +524,29 @@ async function multiExtractor(providers) {
provider = customName; provider = customName;
} }
let title;
if (providersCount[provider]) { if (providersCount[provider]) {
providersCount[provider]++; providersCount[provider]++;
streams.push( title = provider.charAt(0).toUpperCase() +
provider.charAt(0).toUpperCase() +
provider.slice(1) + provider.slice(1) +
"-" + "-" +
(providersCount[provider] - 1), // add a number to the provider name (providersCount[provider] - 1); // add a number to the provider name
streamUrl
);
} else { } else {
providersCount[provider] = 1; providersCount[provider] = 1;
streams.push( title = provider.charAt(0).toUpperCase() + provider.slice(1);
provider.charAt(0).toUpperCase() + provider.slice(1),
streamUrl
);
} }
const streamObject = {
title: title,
streamUrl: streamUrl
};
// Add headers if they exist
if (headers && typeof headers === "object" && Object.keys(headers).length > 0) {
streamObject.headers = headers;
}
streams.push(streamObject);
} catch (error) { } catch (error) {
// Ignore the error and try the next provider // Ignore the error and try the next provider
} }
@@ -519,33 +557,64 @@ async function multiExtractor(providers) {
async function extractStreamUrlByProvider(url, provider) { async function extractStreamUrlByProvider(url, provider) {
if (eval(`typeof ${provider}Extractor`) !== "function") { if (eval(`typeof ${provider}Extractor`) !== "function") {
// skip if the extractor is not defined // skip if the extractor is not defined
console.log(`Extractor for provider ${provider} is not defined, skipping...`); console.log(
`Extractor for provider ${provider} is not defined, skipping...`
);
return null; return null;
} }
let uas = [
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.3",
"Mozilla/5.0 (iPhone; CPU iPhone OS 18_1_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/18.1.1 Mobile/15E148 Safari/604.1",
"Mozilla/5.0 (Linux; Android 10; SM-G973F) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Mobile Safari/537.36",
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.3",
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.1.2 Safari/605.1.15",
"Mozilla/5.0 (Linux; Android 11; Pixel 4 XL) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Mobile Safari/537.36",
];
let headers = { let headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.3", "User-Agent": uas[(url.length + provider.length) % uas.length], // use a different user agent based on the url and provider
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8", "Accept":
"text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8",
"Accept-Language": "en-US,en;q=0.5", "Accept-Language": "en-US,en;q=0.5",
"Referer": url, "Referer": url,
"Connection": "keep-alive", "Connection": "keep-alive",
"x-Requested-With": "XMLHttpRequest" "x-Requested-With": "XMLHttpRequest",
}; };
if(provider == 'bigwarp') {
switch (provider) {
case "bigwarp":
delete headers["User-Agent"]; delete headers["User-Agent"];
headers["x-requested-with"] = "XMLHttpRequest"; break;
} else if (provider == 'vk') { case "vk":
case "sibnet":
headers["encoding"] = "windows-1251"; // required headers["encoding"] = "windows-1251"; // required
} else if (provider == 'sibnet') { break;
headers["encoding"] = "windows-1251"; // required case "supervideo":
} else if (provider == 'supervideo') { case "savefiles":
delete headers["User-Agent"]; headers = {
"Accept": "*/*",
"Accept-Encoding": "gzip, deflate, br",
"User-Agent": "EchoapiRuntime/1.1.0",
"Connection": "keep-alive",
"Cache-Control": "no-cache",
"Host": url.match(/https?:\/\/([^\/]+)/)[1],
};
break;
case "streamtape":
headers = {
"User-Agent":
"Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:145.0) Gecko/20100101 Firefox/145.0",
"Accept":
"text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
};
break;
} }
// console.log("Using headers: " + JSON.stringify(headers));
// fetch the url // fetch the url
// and pass the response to the extractor function // and pass the response to the extractor function
console.log("Fetching URL: " + url); console.log("Fetching URL: " + url);
const response = await soraFetch(url, { const response = await soraFetch(url, {
headers headers,
}); });
console.log("Response: " + response.status); console.log("Response: " + response.status);
@@ -553,33 +622,27 @@ async function extractStreamUrlByProvider(url, provider) {
// if title contains redirect, then get the redirect url // if title contains redirect, then get the redirect url
const title = html.match(/<title>(.*?)<\/title>/); const title = html.match(/<title>(.*?)<\/title>/);
if (title && title[1].toLowerCase().includes("redirect")) { if (title && title[1].toLowerCase().includes("redirect")) {
const redirectUrl = html.match(/<meta http-equiv="refresh" content="0;url=(.*?)"/); const matches = [
const redirectUrl2 = html.match(/window\.location\.href\s*=\s*["'](.*?)["']/); /<meta http-equiv="refresh" content="0;url=(.*?)"/,
const redirectUrl3 = html.match(/window\.location\.replace\s*\(\s*["'](.*?)["']\s*\)/); /window\.location\.href\s*=\s*["'](.*?)["']/,
if (redirectUrl) { /window\.location\.replace\s*\(\s*["'](.*?)["']\s*\)/,
console.log("Redirect URL: " + redirectUrl[1]); /window\.location\s*=\s*["'](.*?)["']/,
/window\.location\.assign\s*\(\s*["'](.*?)["']\s*\)/,
/top\.location\s*=\s*["'](.*?)["']/,
/top\.location\.replace\s*\(\s*["'](.*?)["']\s*\)/,
];
for (const match of matches) {
const redirectUrl = html.match(match);
if (redirectUrl && redirectUrl[1] && typeof redirectUrl[1] === "string" && redirectUrl[1].startsWith("http")) {
console.log("Redirect URL found: " + redirectUrl[1]);
url = redirectUrl[1]; url = redirectUrl[1];
headers['Referer'] = url;
headers['Host'] = url.match(/https?:\/\/([^\/]+)/)[1];
html = await soraFetch(url, { html = await soraFetch(url, {
headers headers,
}); }).then((res) => res.text());
html = html.text ? await html.text() : html; break;
}
} else if (redirectUrl2) {
console.log("Redirect URL 2: " + redirectUrl2[1]);
url = redirectUrl2[1];
html = await soraFetch(url, {
headers
});
html = html.text ? await html.text() : html;
} else if (redirectUrl3) {
console.log("Redirect URL 3: " + redirectUrl3[1]);
url = redirectUrl3[1];
html = await soraFetch(url, {
headers
});
html = html.text ? await html.text() : html;
} else {
console.log("No redirect URL found");
} }
} }
@@ -634,6 +697,20 @@ async function extractStreamUrlByProvider(url, provider) {
console.log("Error extracting stream URL from mp4upload:", error); console.log("Error extracting stream URL from mp4upload:", error);
return null; return null;
} }
case "oneupload":
try {
return await oneuploadExtractor(html, url);
} catch (error) {
console.log("Error extracting stream URL from oneupload:", error);
return null;
}
case "packer":
try {
return await packerExtractor(html, url);
} catch (error) {
console.log("Error extracting stream URL from packer:", error);
return null;
}
case "sendvid": case "sendvid":
try { try {
return await sendvidExtractor(html, url); return await sendvidExtractor(html, url);
@@ -648,6 +725,13 @@ async function extractStreamUrlByProvider(url, provider) {
console.log("Error extracting stream URL from sibnet:", error); console.log("Error extracting stream URL from sibnet:", error);
return null; return null;
} }
case "smoothpre":
try {
return await smoothpreExtractor(html, url);
} catch (error) {
console.log("Error extracting stream URL from smoothpre:", error);
return null;
}
case "streamtape": case "streamtape":
try { try {
return await streamtapeExtractor(html, url); return await streamtapeExtractor(html, url);
@@ -662,13 +746,6 @@ async function extractStreamUrlByProvider(url, provider) {
console.log("Error extracting stream URL from streamup:", error); console.log("Error extracting stream URL from streamup:", error);
return null; return null;
} }
case "supervideo":
try {
return await supervideoExtractor(html, url);
} catch (error) {
console.log("Error extracting stream URL from supervideo:", error);
return null;
}
case "uploadcx": case "uploadcx":
try { try {
return await uploadcxExtractor(html, url); return await uploadcxExtractor(html, url);
@@ -717,7 +794,6 @@ async function extractStreamUrlByProvider(url, provider) {
} }
} }
//////////////////////////////////////////////// ////////////////////////////////////////////////
// EXTRACTORS // // EXTRACTORS //
//////////////////////////////////////////////// ////////////////////////////////////////////////
@@ -1204,7 +1280,6 @@ async function megacloudExtractor(html, embedUrl) {
* @author Cufiy * @author Cufiy
*/ */
async function mp4uploadExtractor(html, url = null) { async function mp4uploadExtractor(html, url = null) {
// src: "https://a4.mp4upload.com:183/d/xkx3b4etz3b4quuo66rbmyqtjjoivahfxp27f35pti45rzapbvj5xwb4wuqtlpewdz4dirfp/video.mp4"
const regex = /src:\s*"([^"]+)"/; const regex = /src:\s*"([^"]+)"/;
const match = html.match(regex); const match = html.match(regex);
if (match) { if (match) {
@@ -1214,6 +1289,32 @@ async function mp4uploadExtractor(html, url = null) {
return null; return null;
} }
} }
/* --- oneupload --- */
/**
* @name oneuploadExtractor
* @author 50/50
*/
async function oneuploadExtractor(data, url = null) {
const match = data.match(/sources:\s*\[\{file:"([^"]+)"\}\]/);
const fileUrl = match ? match[1] : null;
return fileUrl;
}
/* --- packer --- */
/* {REQUIRED PLUGINS: unbaser} */
/**
* @name packerExtractor
* @author 50/50
*/
async function packerExtractor(data, url = null) {
const obfuscatedScript = data.match(/<script[^>]*>\s*(eval\(function\(p,a,c,k,e,d.*?\)[\s\S]*?)<\/script>/);
const unpackedScript = unpack(obfuscatedScript[1]);
const m3u8Match = unpackedScript.match(/"hls2"\s*:\s*"([^"]+)"/);
const m3u8Url = m3u8Match[1];
return m3u8Url;
}
/* --- sendvid --- */ /* --- sendvid --- */
/** /**
@@ -1249,6 +1350,29 @@ async function sibnetExtractor(html, embedUrl) {
return null; return null;
} }
} }
/* --- smoothpre --- */
/* {REQUIRED PLUGINS: unbaser} */
/**
* @name SmoothPre Extractor
* @author 50/50
*/
async function smoothpreExtractor(data, url = null) {
console.log("Using SmoothPre Extractor");
console.log("Data Length: " + data.length);
const obfuscatedScript = data.match(/<script[^>]*>\s*(eval\(function\(p,a,c,k,e,d.*?\)[\s\S]*?)<\/script>/);
if (!obfuscatedScript || !obfuscatedScript[1]) {
console.log("No obfuscated script found");
return null;
}
const unpackedScript = unpack(obfuscatedScript[1]);
const hls2Match = unpackedScript.match(/"hls2"\s*:\s*"([^"]+)"/);
const hls2Url = hls2Match ? hls2Match[1] : null;
return hls2Url;
}
/* --- streamtape --- */ /* --- streamtape --- */
/** /**
@@ -1332,26 +1456,6 @@ async function streamupExtractor(data, url = null) {
return null; return null;
} }
} }
/* --- supervideo --- */
/* {REQUIRED PLUGINS: unbaser} */
/**
* @name SuperVideo Extractor
* @author 50/50
*/
async function supervideoExtractor(data, url = null) {
const obfuscatedScript = data.match(/<script[^>]*>\s*(eval\(function\(p,a,c,k,e,d.*?\)[\s\S]*?)<\/script>/);
const unpackedScript = unpack(obfuscatedScript[1]);
const regex = /file:\s*"([^"]+\.m3u8)"/;
const match = regex.exec(unpackedScript);
if (match) {
const fileUrl = match[1];
console.log("File URL:" + fileUrl);
return fileUrl;
}
return "No stream found";
}
/* --- uploadcx --- */ /* --- uploadcx --- */
/** /**
@@ -1416,7 +1520,7 @@ async function vidmolyExtractor(html, url = null) {
const streamUrl = iframeMatch[1].startsWith("//") const streamUrl = iframeMatch[1].startsWith("//")
? "https:" + iframeMatch[1] ? "https:" + iframeMatch[1]
: iframeMatch[1]; : iframeMatch[1];
const responseTwo = await fetchv2(streamUrl); const responseTwo = await soraFetch(streamUrl);
const htmlTwo = await responseTwo.text(); const htmlTwo = await responseTwo.text();
const m3u8Match = htmlTwo.match(/sources:\s*\[\{file:"([^"]+\.m3u8)"/); const m3u8Match = htmlTwo.match(/sources:\s*\[\{file:"([^"]+\.m3u8)"/);
return m3u8Match ? m3u8Match[1] : null; return m3u8Match ? m3u8Match[1] : null;
@@ -1542,6 +1646,7 @@ function voeShiftChars(str, shift) {
.join(""); .join("");
} }
//////////////////////////////////////////////// ////////////////////////////////////////////////
// PLUGINS // // PLUGINS //
//////////////////////////////////////////////// ////////////////////////////////////////////////
@@ -1559,14 +1664,22 @@ function voeShiftChars(str, shift) {
* @returns {Promise<Response|null>} The response from the server, or null if the * @returns {Promise<Response|null>} The response from the server, or null if the
* request failed. * request failed.
*/ */
async function soraFetch(url, options = { headers: {}, method: 'GET', body: null }) { async function soraFetch(
url,
options = { headers: {}, method: "GET", body: null }
) {
try { try {
return await fetchv2(url, options.headers ?? {}, options.method ?? 'GET', options.body ?? null); return await fetchv2(
url,
options.headers ?? {},
options.method ?? "GET",
options.body ?? null
);
} catch (e) { } catch (e) {
try { try {
return await fetch(url, options); return await fetch(url, options);
} catch (error) { } catch (error) {
await console.log('soraFetch error: ' + error.message); await console.log("soraFetch error: " + error.message);
return null; return null;
} }
} }
@@ -1674,6 +1787,5 @@ function unpack(source) {
} }
/* {GE END} */ /* {GE END} */
+1 -1
View File
@@ -5,7 +5,7 @@
"name": "Hamzo & Cufiy", "name": "Hamzo & Cufiy",
"icon": "https://cdn.discordapp.com/avatars/623644371819954226/591ecab10b0b4535e859bb0b9bbe62e5?size=1024" "icon": "https://cdn.discordapp.com/avatars/623644371819954226/591ecab10b0b4535e859bb0b9bbe62e5?size=1024"
}, },
"version": "0.3.16", "version": "0.3.17",
"language": "German (DUB)", "language": "German (DUB)",
"streamType": "HLS", "streamType": "HLS",
"quality": "720p", "quality": "720p",
+222 -110
View File
@@ -376,7 +376,7 @@ function base64Decode(str) {
// EDITING THIS FILE COULD BREAK THE UPDATER AND CAUSE ISSUES WITH THE EXTRACTOR // EDITING THIS FILE COULD BREAK THE UPDATER AND CAUSE ISSUES WITH THE EXTRACTOR
/* {GE START} */ /* {GE START} */
/* {VERSION: 1.1.8} */ /* {VERSION: 1.2.0} */
/** /**
* @name global_extractor.js * @name global_extractor.js
@@ -384,8 +384,8 @@ function base64Decode(str) {
* @author Cufiy * @author Cufiy
* @url https://github.com/JMcrafter26/sora-global-extractor * @url https://github.com/JMcrafter26/sora-global-extractor
* @license CUSTOM LICENSE - see https://github.com/JMcrafter26/sora-global-extractor/blob/main/LICENSE * @license CUSTOM LICENSE - see https://github.com/JMcrafter26/sora-global-extractor/blob/main/LICENSE
* @date 2025-11-05 15:44:57 * @date 2026-01-03 19:28:28
* @version 1.1.8 * @version 1.2.0
* @note This file was generated automatically. * @note This file was generated automatically.
* The global extractor comes with an auto-updating feature, so you can always get the latest version. https://github.com/JMcrafter26/sora-global-extractor#-auto-updater * The global extractor comes with an auto-updating feature, so you can always get the latest version. https://github.com/JMcrafter26/sora-global-extractor#-auto-updater
*/ */
@@ -395,12 +395,20 @@ function globalExtractor(providers) {
for (const [url, provider] of Object.entries(providers)) { for (const [url, provider] of Object.entries(providers)) {
try { try {
const streamUrl = extractStreamUrlByProvider(url, provider); const streamUrl = extractStreamUrlByProvider(url, provider);
// check if streamUrl is an object with streamUrl property
if (streamUrl && typeof streamUrl === "object" && !Array.isArray(streamUrl) && streamUrl.streamUrl) {
return streamUrl.streamUrl;
}
// check if streamUrl is not null, a string, and starts with http or https // check if streamUrl is not null, a string, and starts with http or https
if (streamUrl && typeof streamUrl === "string" && (streamUrl.startsWith("http"))) { if (
streamUrl &&
typeof streamUrl === "string" &&
streamUrl.startsWith("http")
) {
return streamUrl; return streamUrl;
// if its an array, get the value that starts with http // if its an array, get the value that starts with http
} else if (Array.isArray(streamUrl)) { } else if (Array.isArray(streamUrl)) {
const httpStream = streamUrl.find(url => url.startsWith("http")); const httpStream = streamUrl.find((url) => url.startsWith("http"));
if (httpStream) { if (httpStream) {
return httpStream; return httpStream;
} }
@@ -408,7 +416,6 @@ function globalExtractor(providers) {
// check if it's a valid stream URL // check if it's a valid stream URL
return null; return null;
} }
} catch (error) { } catch (error) {
// Ignore the error and try the next provider // Ignore the error and try the next provider
} }
@@ -420,16 +427,30 @@ async function multiExtractor(providers) {
/* this scheme should be returned as a JSON object /* this scheme should be returned as a JSON object
{ {
"streams": [ "streams": [
"FileMoon", {
"https://filemoon.example/stream1.m3u8", "title": "FileMoon",
"StreamWish", "streamUrl": "https://filemoon.example/stream1.m3u8",
"https://streamwish.example/stream2.m3u8", },
"Okru", {
"https://okru.example/stream3.m3u8", "title": "StreamWish",
"MP4", "streamUrl": "https://streamwish.example/stream2.m3u8",
"https://mp4upload.example/stream4.mp4", },
"Default", {
"https://default.example/stream5.m3u8" "title": "Okru",
"streamUrl": "https://okru.example/stream3.m3u8",
"headers": { // Optional headers for the stream
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.3",
"Referer": "https://okru.example/",
},
},
{
"title": "MP4",
"streamUrl": "https://mp4upload.example/stream4.mp4",
},
{
"title": "Default",
"streamUrl": "https://default.example/stream5.m3u8"
}
] ]
} }
*/ */
@@ -441,20 +462,21 @@ async function multiExtractor(providers) {
// if provider starts with "direct-", then add the url to the streams array directly // if provider starts with "direct-", then add the url to the streams array directly
if (provider.startsWith("direct-")) { if (provider.startsWith("direct-")) {
const directName = provider.slice(7); // remove "direct-" prefix const directName = provider.slice(7); // remove "direct-" prefix
if (directName && directName.length > 0) { const title = (directName && directName.length > 0) ? directName : "Direct";
streams.push(directName, url); streams.push({
} else { title: title,
streams.push("Direct", url); // fallback to "Direct" if no name is provided streamUrl: url
} });
continue; // skip to the next provider continue; // skip to the next provider
} }
if (provider.startsWith("direct")) { if (provider.startsWith("direct")) {
provider = provider.slice(7); // remove "direct-" prefix provider = provider.slice(7); // remove "direct-" prefix
if (provider && provider.length > 0) { const title = (provider && provider.length > 0) ? provider : "Direct";
streams.push(provider, url); streams.push({
} else { title: title,
streams.push("Direct", url); // fallback to "Direct" if no name is provided streamUrl: url
} });
continue; // skip to the next provider
} }
let customName = null; // to store the custom name if provided let customName = null; // to store the custom name if provided
@@ -471,15 +493,24 @@ async function multiExtractor(providers) {
console.log(`Skipping ${provider} as it has already 3 streams`); console.log(`Skipping ${provider} as it has already 3 streams`);
continue; continue;
} }
let streamUrl = await extractStreamUrlByProvider(url, provider); let result = await extractStreamUrlByProvider(url, provider);
let streamUrl = null;
let headers = null;
if (streamUrl && Array.isArray(streamUrl)) { // Check if result is an object with streamUrl and optional headers
const httpStream = streamUrl.find(url => url.startsWith("http")); if (result && typeof result === "object" && !Array.isArray(result) && result.streamUrl) {
streamUrl = result.streamUrl;
headers = result.headers || null;
} else if (result && Array.isArray(result)) {
const httpStream = result.find((url) => url.startsWith("http"));
if (httpStream) { if (httpStream) {
streamUrl = httpStream; streamUrl = httpStream;
} }
} else if (result && typeof result === "string") {
streamUrl = result;
} }
// check if provider is already in streams, if it is, add a number to it
// check if streamUrl is valid
if ( if (
!streamUrl || !streamUrl ||
typeof streamUrl !== "string" || typeof streamUrl !== "string" ||
@@ -493,22 +524,29 @@ async function multiExtractor(providers) {
provider = customName; provider = customName;
} }
let title;
if (providersCount[provider]) { if (providersCount[provider]) {
providersCount[provider]++; providersCount[provider]++;
streams.push( title = provider.charAt(0).toUpperCase() +
provider.charAt(0).toUpperCase() +
provider.slice(1) + provider.slice(1) +
"-" + "-" +
(providersCount[provider] - 1), // add a number to the provider name (providersCount[provider] - 1); // add a number to the provider name
streamUrl
);
} else { } else {
providersCount[provider] = 1; providersCount[provider] = 1;
streams.push( title = provider.charAt(0).toUpperCase() + provider.slice(1);
provider.charAt(0).toUpperCase() + provider.slice(1),
streamUrl
);
} }
const streamObject = {
title: title,
streamUrl: streamUrl
};
// Add headers if they exist
if (headers && typeof headers === "object" && Object.keys(headers).length > 0) {
streamObject.headers = headers;
}
streams.push(streamObject);
} catch (error) { } catch (error) {
// Ignore the error and try the next provider // Ignore the error and try the next provider
} }
@@ -519,33 +557,64 @@ async function multiExtractor(providers) {
async function extractStreamUrlByProvider(url, provider) { async function extractStreamUrlByProvider(url, provider) {
if (eval(`typeof ${provider}Extractor`) !== "function") { if (eval(`typeof ${provider}Extractor`) !== "function") {
// skip if the extractor is not defined // skip if the extractor is not defined
console.log(`Extractor for provider ${provider} is not defined, skipping...`); console.log(
`Extractor for provider ${provider} is not defined, skipping...`
);
return null; return null;
} }
let uas = [
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.3",
"Mozilla/5.0 (iPhone; CPU iPhone OS 18_1_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/18.1.1 Mobile/15E148 Safari/604.1",
"Mozilla/5.0 (Linux; Android 10; SM-G973F) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Mobile Safari/537.36",
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.3",
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.1.2 Safari/605.1.15",
"Mozilla/5.0 (Linux; Android 11; Pixel 4 XL) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Mobile Safari/537.36",
];
let headers = { let headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.3", "User-Agent": uas[(url.length + provider.length) % uas.length], // use a different user agent based on the url and provider
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8", "Accept":
"text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8",
"Accept-Language": "en-US,en;q=0.5", "Accept-Language": "en-US,en;q=0.5",
"Referer": url, "Referer": url,
"Connection": "keep-alive", "Connection": "keep-alive",
"x-Requested-With": "XMLHttpRequest" "x-Requested-With": "XMLHttpRequest",
}; };
if(provider == 'bigwarp') {
switch (provider) {
case "bigwarp":
delete headers["User-Agent"]; delete headers["User-Agent"];
headers["x-requested-with"] = "XMLHttpRequest"; break;
} else if (provider == 'vk') { case "vk":
case "sibnet":
headers["encoding"] = "windows-1251"; // required headers["encoding"] = "windows-1251"; // required
} else if (provider == 'sibnet') { break;
headers["encoding"] = "windows-1251"; // required case "supervideo":
} else if (provider == 'supervideo') { case "savefiles":
delete headers["User-Agent"]; headers = {
"Accept": "*/*",
"Accept-Encoding": "gzip, deflate, br",
"User-Agent": "EchoapiRuntime/1.1.0",
"Connection": "keep-alive",
"Cache-Control": "no-cache",
"Host": url.match(/https?:\/\/([^\/]+)/)[1],
};
break;
case "streamtape":
headers = {
"User-Agent":
"Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:145.0) Gecko/20100101 Firefox/145.0",
"Accept":
"text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
};
break;
} }
// console.log("Using headers: " + JSON.stringify(headers));
// fetch the url // fetch the url
// and pass the response to the extractor function // and pass the response to the extractor function
console.log("Fetching URL: " + url); console.log("Fetching URL: " + url);
const response = await soraFetch(url, { const response = await soraFetch(url, {
headers headers,
}); });
console.log("Response: " + response.status); console.log("Response: " + response.status);
@@ -553,33 +622,27 @@ async function extractStreamUrlByProvider(url, provider) {
// if title contains redirect, then get the redirect url // if title contains redirect, then get the redirect url
const title = html.match(/<title>(.*?)<\/title>/); const title = html.match(/<title>(.*?)<\/title>/);
if (title && title[1].toLowerCase().includes("redirect")) { if (title && title[1].toLowerCase().includes("redirect")) {
const redirectUrl = html.match(/<meta http-equiv="refresh" content="0;url=(.*?)"/); const matches = [
const redirectUrl2 = html.match(/window\.location\.href\s*=\s*["'](.*?)["']/); /<meta http-equiv="refresh" content="0;url=(.*?)"/,
const redirectUrl3 = html.match(/window\.location\.replace\s*\(\s*["'](.*?)["']\s*\)/); /window\.location\.href\s*=\s*["'](.*?)["']/,
if (redirectUrl) { /window\.location\.replace\s*\(\s*["'](.*?)["']\s*\)/,
console.log("Redirect URL: " + redirectUrl[1]); /window\.location\s*=\s*["'](.*?)["']/,
/window\.location\.assign\s*\(\s*["'](.*?)["']\s*\)/,
/top\.location\s*=\s*["'](.*?)["']/,
/top\.location\.replace\s*\(\s*["'](.*?)["']\s*\)/,
];
for (const match of matches) {
const redirectUrl = html.match(match);
if (redirectUrl && redirectUrl[1] && typeof redirectUrl[1] === "string" && redirectUrl[1].startsWith("http")) {
console.log("Redirect URL found: " + redirectUrl[1]);
url = redirectUrl[1]; url = redirectUrl[1];
headers['Referer'] = url;
headers['Host'] = url.match(/https?:\/\/([^\/]+)/)[1];
html = await soraFetch(url, { html = await soraFetch(url, {
headers headers,
}); }).then((res) => res.text());
html = html.text ? await html.text() : html; break;
}
} else if (redirectUrl2) {
console.log("Redirect URL 2: " + redirectUrl2[1]);
url = redirectUrl2[1];
html = await soraFetch(url, {
headers
});
html = html.text ? await html.text() : html;
} else if (redirectUrl3) {
console.log("Redirect URL 3: " + redirectUrl3[1]);
url = redirectUrl3[1];
html = await soraFetch(url, {
headers
});
html = html.text ? await html.text() : html;
} else {
console.log("No redirect URL found");
} }
} }
@@ -634,6 +697,20 @@ async function extractStreamUrlByProvider(url, provider) {
console.log("Error extracting stream URL from mp4upload:", error); console.log("Error extracting stream URL from mp4upload:", error);
return null; return null;
} }
case "oneupload":
try {
return await oneuploadExtractor(html, url);
} catch (error) {
console.log("Error extracting stream URL from oneupload:", error);
return null;
}
case "packer":
try {
return await packerExtractor(html, url);
} catch (error) {
console.log("Error extracting stream URL from packer:", error);
return null;
}
case "sendvid": case "sendvid":
try { try {
return await sendvidExtractor(html, url); return await sendvidExtractor(html, url);
@@ -648,6 +725,13 @@ async function extractStreamUrlByProvider(url, provider) {
console.log("Error extracting stream URL from sibnet:", error); console.log("Error extracting stream URL from sibnet:", error);
return null; return null;
} }
case "smoothpre":
try {
return await smoothpreExtractor(html, url);
} catch (error) {
console.log("Error extracting stream URL from smoothpre:", error);
return null;
}
case "streamtape": case "streamtape":
try { try {
return await streamtapeExtractor(html, url); return await streamtapeExtractor(html, url);
@@ -662,13 +746,6 @@ async function extractStreamUrlByProvider(url, provider) {
console.log("Error extracting stream URL from streamup:", error); console.log("Error extracting stream URL from streamup:", error);
return null; return null;
} }
case "supervideo":
try {
return await supervideoExtractor(html, url);
} catch (error) {
console.log("Error extracting stream URL from supervideo:", error);
return null;
}
case "uploadcx": case "uploadcx":
try { try {
return await uploadcxExtractor(html, url); return await uploadcxExtractor(html, url);
@@ -717,7 +794,6 @@ async function extractStreamUrlByProvider(url, provider) {
} }
} }
//////////////////////////////////////////////// ////////////////////////////////////////////////
// EXTRACTORS // // EXTRACTORS //
//////////////////////////////////////////////// ////////////////////////////////////////////////
@@ -1204,7 +1280,6 @@ async function megacloudExtractor(html, embedUrl) {
* @author Cufiy * @author Cufiy
*/ */
async function mp4uploadExtractor(html, url = null) { async function mp4uploadExtractor(html, url = null) {
// src: "https://a4.mp4upload.com:183/d/xkx3b4etz3b4quuo66rbmyqtjjoivahfxp27f35pti45rzapbvj5xwb4wuqtlpewdz4dirfp/video.mp4"
const regex = /src:\s*"([^"]+)"/; const regex = /src:\s*"([^"]+)"/;
const match = html.match(regex); const match = html.match(regex);
if (match) { if (match) {
@@ -1214,6 +1289,32 @@ async function mp4uploadExtractor(html, url = null) {
return null; return null;
} }
} }
/* --- oneupload --- */
/**
* @name oneuploadExtractor
* @author 50/50
*/
async function oneuploadExtractor(data, url = null) {
const match = data.match(/sources:\s*\[\{file:"([^"]+)"\}\]/);
const fileUrl = match ? match[1] : null;
return fileUrl;
}
/* --- packer --- */
/* {REQUIRED PLUGINS: unbaser} */
/**
* @name packerExtractor
* @author 50/50
*/
async function packerExtractor(data, url = null) {
const obfuscatedScript = data.match(/<script[^>]*>\s*(eval\(function\(p,a,c,k,e,d.*?\)[\s\S]*?)<\/script>/);
const unpackedScript = unpack(obfuscatedScript[1]);
const m3u8Match = unpackedScript.match(/"hls2"\s*:\s*"([^"]+)"/);
const m3u8Url = m3u8Match[1];
return m3u8Url;
}
/* --- sendvid --- */ /* --- sendvid --- */
/** /**
@@ -1249,6 +1350,29 @@ async function sibnetExtractor(html, embedUrl) {
return null; return null;
} }
} }
/* --- smoothpre --- */
/* {REQUIRED PLUGINS: unbaser} */
/**
* @name SmoothPre Extractor
* @author 50/50
*/
async function smoothpreExtractor(data, url = null) {
console.log("Using SmoothPre Extractor");
console.log("Data Length: " + data.length);
const obfuscatedScript = data.match(/<script[^>]*>\s*(eval\(function\(p,a,c,k,e,d.*?\)[\s\S]*?)<\/script>/);
if (!obfuscatedScript || !obfuscatedScript[1]) {
console.log("No obfuscated script found");
return null;
}
const unpackedScript = unpack(obfuscatedScript[1]);
const hls2Match = unpackedScript.match(/"hls2"\s*:\s*"([^"]+)"/);
const hls2Url = hls2Match ? hls2Match[1] : null;
return hls2Url;
}
/* --- streamtape --- */ /* --- streamtape --- */
/** /**
@@ -1332,26 +1456,6 @@ async function streamupExtractor(data, url = null) {
return null; return null;
} }
} }
/* --- supervideo --- */
/* {REQUIRED PLUGINS: unbaser} */
/**
* @name SuperVideo Extractor
* @author 50/50
*/
async function supervideoExtractor(data, url = null) {
const obfuscatedScript = data.match(/<script[^>]*>\s*(eval\(function\(p,a,c,k,e,d.*?\)[\s\S]*?)<\/script>/);
const unpackedScript = unpack(obfuscatedScript[1]);
const regex = /file:\s*"([^"]+\.m3u8)"/;
const match = regex.exec(unpackedScript);
if (match) {
const fileUrl = match[1];
console.log("File URL:" + fileUrl);
return fileUrl;
}
return "No stream found";
}
/* --- uploadcx --- */ /* --- uploadcx --- */
/** /**
@@ -1416,7 +1520,7 @@ async function vidmolyExtractor(html, url = null) {
const streamUrl = iframeMatch[1].startsWith("//") const streamUrl = iframeMatch[1].startsWith("//")
? "https:" + iframeMatch[1] ? "https:" + iframeMatch[1]
: iframeMatch[1]; : iframeMatch[1];
const responseTwo = await fetchv2(streamUrl); const responseTwo = await soraFetch(streamUrl);
const htmlTwo = await responseTwo.text(); const htmlTwo = await responseTwo.text();
const m3u8Match = htmlTwo.match(/sources:\s*\[\{file:"([^"]+\.m3u8)"/); const m3u8Match = htmlTwo.match(/sources:\s*\[\{file:"([^"]+\.m3u8)"/);
return m3u8Match ? m3u8Match[1] : null; return m3u8Match ? m3u8Match[1] : null;
@@ -1542,6 +1646,7 @@ function voeShiftChars(str, shift) {
.join(""); .join("");
} }
//////////////////////////////////////////////// ////////////////////////////////////////////////
// PLUGINS // // PLUGINS //
//////////////////////////////////////////////// ////////////////////////////////////////////////
@@ -1559,14 +1664,22 @@ function voeShiftChars(str, shift) {
* @returns {Promise<Response|null>} The response from the server, or null if the * @returns {Promise<Response|null>} The response from the server, or null if the
* request failed. * request failed.
*/ */
async function soraFetch(url, options = { headers: {}, method: 'GET', body: null }) { async function soraFetch(
url,
options = { headers: {}, method: "GET", body: null }
) {
try { try {
return await fetchv2(url, options.headers ?? {}, options.method ?? 'GET', options.body ?? null); return await fetchv2(
url,
options.headers ?? {},
options.method ?? "GET",
options.body ?? null
);
} catch (e) { } catch (e) {
try { try {
return await fetch(url, options); return await fetch(url, options);
} catch (error) { } catch (error) {
await console.log('soraFetch error: ' + error.message); await console.log("soraFetch error: " + error.message);
return null; return null;
} }
} }
@@ -1674,6 +1787,5 @@ function unpack(source) {
} }
/* {GE END} */ /* {GE END} */