Merge pull request 'Improve AniWorld module and update GlobalExtractor' (#5) from Cufiy/sources-fork:main into main
Reviewed-on: https://git.luna-app.eu/50n50/sources/pulls/5
This commit was merged in pull request #5.
This commit is contained in:
@@ -5,7 +5,7 @@
|
||||
"name": "50/50 & Cufiy",
|
||||
"icon": "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQ3122kQwublLkZ6rf1fEpUP79BxZOFmH9BSA&s"
|
||||
},
|
||||
"version": "1.2.13",
|
||||
"version": "1.2.14",
|
||||
"language": "German (DUB/SUB)",
|
||||
"streamType": "MP4",
|
||||
"quality": "1080p",
|
||||
@@ -17,4 +17,4 @@
|
||||
"supportsMojuru": true,
|
||||
"supportsSora": true,
|
||||
"supportsLuna": true
|
||||
}
|
||||
}
|
||||
+235
-123
@@ -313,7 +313,7 @@ async function sendLog(message) {
|
||||
// EDITING THIS FILE COULD BREAK THE UPDATER AND CAUSE ISSUES WITH THE EXTRACTOR
|
||||
|
||||
/* {GE START} */
|
||||
/* {VERSION: 1.1.8} */
|
||||
/* {VERSION: 1.2.0} */
|
||||
|
||||
/**
|
||||
* @name global_extractor.js
|
||||
@@ -321,8 +321,8 @@ async function sendLog(message) {
|
||||
* @author Cufiy
|
||||
* @url https://github.com/JMcrafter26/sora-global-extractor
|
||||
* @license CUSTOM LICENSE - see https://github.com/JMcrafter26/sora-global-extractor/blob/main/LICENSE
|
||||
* @date 2025-11-05 15:44:57
|
||||
* @version 1.1.8
|
||||
* @date 2026-01-03 19:28:28
|
||||
* @version 1.2.0
|
||||
* @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
|
||||
*/
|
||||
@@ -332,12 +332,20 @@ function globalExtractor(providers) {
|
||||
for (const [url, provider] of Object.entries(providers)) {
|
||||
try {
|
||||
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
|
||||
if (streamUrl && typeof streamUrl === "string" && (streamUrl.startsWith("http"))) {
|
||||
if (
|
||||
streamUrl &&
|
||||
typeof streamUrl === "string" &&
|
||||
streamUrl.startsWith("http")
|
||||
) {
|
||||
return streamUrl;
|
||||
// if its an array, get the value that starts with http
|
||||
} else if (Array.isArray(streamUrl)) {
|
||||
const httpStream = streamUrl.find(url => url.startsWith("http"));
|
||||
const httpStream = streamUrl.find((url) => url.startsWith("http"));
|
||||
if (httpStream) {
|
||||
return httpStream;
|
||||
}
|
||||
@@ -345,7 +353,6 @@ function globalExtractor(providers) {
|
||||
// check if it's a valid stream URL
|
||||
return null;
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
// 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
|
||||
{
|
||||
"streams": [
|
||||
"FileMoon",
|
||||
"https://filemoon.example/stream1.m3u8",
|
||||
"StreamWish",
|
||||
"https://streamwish.example/stream2.m3u8",
|
||||
"Okru",
|
||||
"https://okru.example/stream3.m3u8",
|
||||
"MP4",
|
||||
"https://mp4upload.example/stream4.mp4",
|
||||
"Default",
|
||||
"https://default.example/stream5.m3u8"
|
||||
{
|
||||
"title": "FileMoon",
|
||||
"streamUrl": "https://filemoon.example/stream1.m3u8",
|
||||
},
|
||||
{
|
||||
"title": "StreamWish",
|
||||
"streamUrl": "https://streamwish.example/stream2.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.startsWith("direct-")) {
|
||||
const directName = provider.slice(7); // remove "direct-" prefix
|
||||
if (directName && directName.length > 0) {
|
||||
streams.push(directName, url);
|
||||
} else {
|
||||
streams.push("Direct", url); // fallback to "Direct" if no name is provided
|
||||
}
|
||||
const title = (directName && directName.length > 0) ? directName : "Direct";
|
||||
streams.push({
|
||||
title: title,
|
||||
streamUrl: url
|
||||
});
|
||||
continue; // skip to the next provider
|
||||
}
|
||||
if (provider.startsWith("direct")) {
|
||||
provider = provider.slice(7); // remove "direct-" prefix
|
||||
if (provider && provider.length > 0) {
|
||||
streams.push(provider, url);
|
||||
} else {
|
||||
streams.push("Direct", url); // fallback to "Direct" if no name is provided
|
||||
}
|
||||
const title = (provider && provider.length > 0) ? provider : "Direct";
|
||||
streams.push({
|
||||
title: title,
|
||||
streamUrl: url
|
||||
});
|
||||
continue; // skip to the next provider
|
||||
}
|
||||
|
||||
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`);
|
||||
continue;
|
||||
}
|
||||
let streamUrl = await extractStreamUrlByProvider(url, provider);
|
||||
|
||||
if (streamUrl && Array.isArray(streamUrl)) {
|
||||
const httpStream = streamUrl.find(url => url.startsWith("http"));
|
||||
let result = await extractStreamUrlByProvider(url, provider);
|
||||
let streamUrl = null;
|
||||
let headers = null;
|
||||
|
||||
// Check if result is an object with streamUrl and optional headers
|
||||
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) {
|
||||
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 (
|
||||
!streamUrl ||
|
||||
typeof streamUrl !== "string" ||
|
||||
@@ -430,22 +461,29 @@ async function multiExtractor(providers) {
|
||||
provider = customName;
|
||||
}
|
||||
|
||||
let title;
|
||||
if (providersCount[provider]) {
|
||||
providersCount[provider]++;
|
||||
streams.push(
|
||||
provider.charAt(0).toUpperCase() +
|
||||
title = provider.charAt(0).toUpperCase() +
|
||||
provider.slice(1) +
|
||||
"-" +
|
||||
(providersCount[provider] - 1), // add a number to the provider name
|
||||
streamUrl
|
||||
);
|
||||
(providersCount[provider] - 1); // add a number to the provider name
|
||||
} else {
|
||||
providersCount[provider] = 1;
|
||||
streams.push(
|
||||
provider.charAt(0).toUpperCase() + provider.slice(1),
|
||||
streamUrl
|
||||
);
|
||||
title = provider.charAt(0).toUpperCase() + provider.slice(1);
|
||||
}
|
||||
|
||||
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) {
|
||||
// Ignore the error and try the next provider
|
||||
}
|
||||
@@ -456,73 +494,98 @@ async function multiExtractor(providers) {
|
||||
async function extractStreamUrlByProvider(url, provider) {
|
||||
if (eval(`typeof ${provider}Extractor`) !== "function") {
|
||||
// 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;
|
||||
}
|
||||
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 = {
|
||||
"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",
|
||||
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8",
|
||||
"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-Language": "en-US,en;q=0.5",
|
||||
"Referer": url,
|
||||
"Connection": "keep-alive",
|
||||
"x-Requested-With": "XMLHttpRequest"
|
||||
"x-Requested-With": "XMLHttpRequest",
|
||||
};
|
||||
if(provider == 'bigwarp') {
|
||||
delete headers["User-Agent"];
|
||||
headers["x-requested-with"] = "XMLHttpRequest";
|
||||
} else if (provider == 'vk') {
|
||||
headers["encoding"] = "windows-1251"; // required
|
||||
} else if (provider == 'sibnet') {
|
||||
headers["encoding"] = "windows-1251"; // required
|
||||
} else if (provider == 'supervideo') {
|
||||
delete headers["User-Agent"];
|
||||
|
||||
switch (provider) {
|
||||
case "bigwarp":
|
||||
delete headers["User-Agent"];
|
||||
break;
|
||||
case "vk":
|
||||
case "sibnet":
|
||||
headers["encoding"] = "windows-1251"; // required
|
||||
break;
|
||||
case "supervideo":
|
||||
case "savefiles":
|
||||
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
|
||||
// and pass the response to the extractor function
|
||||
console.log("Fetching URL: " + url);
|
||||
const response = await soraFetch(url, {
|
||||
headers
|
||||
});
|
||||
headers,
|
||||
});
|
||||
|
||||
console.log("Response: " + response.status);
|
||||
let html = response.text ? await response.text() : response;
|
||||
// if title contains redirect, then get the redirect url
|
||||
const title = html.match(/<title>(.*?)<\/title>/);
|
||||
if (title && title[1].toLowerCase().includes("redirect")) {
|
||||
const redirectUrl = html.match(/<meta http-equiv="refresh" content="0;url=(.*?)"/);
|
||||
const redirectUrl2 = html.match(/window\.location\.href\s*=\s*["'](.*?)["']/);
|
||||
const redirectUrl3 = html.match(/window\.location\.replace\s*\(\s*["'](.*?)["']\s*\)/);
|
||||
if (redirectUrl) {
|
||||
console.log("Redirect URL: " + redirectUrl[1]);
|
||||
url = redirectUrl[1];
|
||||
html = await soraFetch(url, {
|
||||
headers
|
||||
});
|
||||
html = html.text ? await html.text() : html;
|
||||
|
||||
} 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");
|
||||
const matches = [
|
||||
/<meta http-equiv="refresh" content="0;url=(.*?)"/,
|
||||
/window\.location\.href\s*=\s*["'](.*?)["']/,
|
||||
/window\.location\.replace\s*\(\s*["'](.*?)["']\s*\)/,
|
||||
/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];
|
||||
headers['Referer'] = url;
|
||||
headers['Host'] = url.match(/https?:\/\/([^\/]+)/)[1];
|
||||
html = await soraFetch(url, {
|
||||
headers,
|
||||
}).then((res) => res.text());
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// console.log("HTML: " + html);
|
||||
switch (provider) {
|
||||
case "bigwarp":
|
||||
case "bigwarp":
|
||||
try {
|
||||
return await bigwarpExtractor(html, url);
|
||||
} catch (error) {
|
||||
@@ -571,6 +634,20 @@ async function extractStreamUrlByProvider(url, provider) {
|
||||
console.log("Error extracting stream URL from mp4upload:", error);
|
||||
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":
|
||||
try {
|
||||
return await sendvidExtractor(html, url);
|
||||
@@ -585,6 +662,13 @@ async function extractStreamUrlByProvider(url, provider) {
|
||||
console.log("Error extracting stream URL from sibnet:", error);
|
||||
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":
|
||||
try {
|
||||
return await streamtapeExtractor(html, url);
|
||||
@@ -599,13 +683,6 @@ async function extractStreamUrlByProvider(url, provider) {
|
||||
console.log("Error extracting stream URL from streamup:", error);
|
||||
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":
|
||||
try {
|
||||
return await uploadcxExtractor(html, url);
|
||||
@@ -654,7 +731,6 @@ async function extractStreamUrlByProvider(url, provider) {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////
|
||||
// EXTRACTORS //
|
||||
////////////////////////////////////////////////
|
||||
@@ -1141,7 +1217,6 @@ async function megacloudExtractor(html, embedUrl) {
|
||||
* @author Cufiy
|
||||
*/
|
||||
async function mp4uploadExtractor(html, url = null) {
|
||||
// src: "https://a4.mp4upload.com:183/d/xkx3b4etz3b4quuo66rbmyqtjjoivahfxp27f35pti45rzapbvj5xwb4wuqtlpewdz4dirfp/video.mp4"
|
||||
const regex = /src:\s*"([^"]+)"/;
|
||||
const match = html.match(regex);
|
||||
if (match) {
|
||||
@@ -1151,6 +1226,32 @@ async function mp4uploadExtractor(html, url = 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 --- */
|
||||
|
||||
/**
|
||||
@@ -1186,6 +1287,29 @@ async function sibnetExtractor(html, embedUrl) {
|
||||
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 --- */
|
||||
|
||||
/**
|
||||
@@ -1269,26 +1393,6 @@ async function streamupExtractor(data, url = 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 --- */
|
||||
|
||||
/**
|
||||
@@ -1353,7 +1457,7 @@ async function vidmolyExtractor(html, url = null) {
|
||||
const streamUrl = iframeMatch[1].startsWith("//")
|
||||
? "https:" + iframeMatch[1]
|
||||
: iframeMatch[1];
|
||||
const responseTwo = await fetchv2(streamUrl);
|
||||
const responseTwo = await soraFetch(streamUrl);
|
||||
const htmlTwo = await responseTwo.text();
|
||||
const m3u8Match = htmlTwo.match(/sources:\s*\[\{file:"([^"]+\.m3u8)"/);
|
||||
return m3u8Match ? m3u8Match[1] : null;
|
||||
@@ -1479,6 +1583,7 @@ function voeShiftChars(str, shift) {
|
||||
.join("");
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////
|
||||
// PLUGINS //
|
||||
////////////////////////////////////////////////
|
||||
@@ -1496,17 +1601,25 @@ function voeShiftChars(str, shift) {
|
||||
* @returns {Promise<Response|null>} The response from the server, or null if the
|
||||
* request failed.
|
||||
*/
|
||||
async function soraFetch(url, options = { headers: {}, method: 'GET', body: null }) {
|
||||
async function soraFetch(
|
||||
url,
|
||||
options = { headers: {}, method: "GET", body: null }
|
||||
) {
|
||||
try {
|
||||
return await fetchv2(
|
||||
url,
|
||||
options.headers ?? {},
|
||||
options.method ?? "GET",
|
||||
options.body ?? null
|
||||
);
|
||||
} catch (e) {
|
||||
try {
|
||||
return await fetchv2(url, options.headers ?? {}, options.method ?? 'GET', options.body ?? null);
|
||||
} catch(e) {
|
||||
try {
|
||||
return await fetch(url, options);
|
||||
} catch(error) {
|
||||
await console.log('soraFetch error: ' + error.message);
|
||||
return null;
|
||||
}
|
||||
return await fetch(url, options);
|
||||
} catch (error) {
|
||||
await console.log("soraFetch error: " + error.message);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
/***********************************************************
|
||||
* UNPACKER MODULE
|
||||
@@ -1610,6 +1723,5 @@ function unpack(source) {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* {GE END} */
|
||||
|
||||
@@ -2,10 +2,11 @@
|
||||
"sourceName": "AniWorld (ENG SUB)",
|
||||
"iconUrl": "https://git.luna-app.eu/50n50/sources/raw/branch/main/aniworld/aniworld.png",
|
||||
"author": {
|
||||
"name": "Hamzo & Cufiy",
|
||||
"icon": "https://cdn.discordapp.com/avatars/623644371819954226/591ecab10b0b4535e859bb0b9bbe62e5?size=1024"
|
||||
"name": "Cufiy & Hamzo",
|
||||
"icon": "https://files.catbox.moe/ttj4fc.gif",
|
||||
"url": "https://github.com/JMcrafter26"
|
||||
},
|
||||
"version": "0.2.8",
|
||||
"version": "0.3.1",
|
||||
"language": "English (SUB)",
|
||||
"streamType": "HLS",
|
||||
"quality": "720p",
|
||||
@@ -18,4 +19,4 @@
|
||||
"supportsMojuru": true,
|
||||
"supportsSora": true,
|
||||
"supportsLuna": true
|
||||
}
|
||||
}
|
||||
@@ -3,15 +3,16 @@
|
||||
"iconUrl": "https://git.luna-app.eu/50n50/sources/raw/branch/main/aniworld/aniworld.png",
|
||||
"author": {
|
||||
"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)",
|
||||
"streamType": "HLS",
|
||||
"quality": "720p",
|
||||
"baseUrl": "https://vidmoly.to/",
|
||||
"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,
|
||||
"type": "anime",
|
||||
"supportsMojuru": true,
|
||||
|
||||
@@ -2,10 +2,11 @@
|
||||
"sourceName": "AniWorld (GER DUB)",
|
||||
"iconUrl": "https://git.luna-app.eu/50n50/sources/raw/branch/main/aniworld/aniworld.png",
|
||||
"author": {
|
||||
"name": "Hamzo & Cufiy",
|
||||
"icon": "https://cdn.discordapp.com/avatars/623644371819954226/591ecab10b0b4535e859bb0b9bbe62e5?size=1024"
|
||||
"name": "Cufiy & Hamzo",
|
||||
"icon": "https://files.catbox.moe/ttj4fc.gif",
|
||||
"url": "https://github.com/JMcrafter26"
|
||||
},
|
||||
"version": "0.2.8",
|
||||
"version": "0.3.1",
|
||||
"language": "German (DUB)",
|
||||
"streamType": "HLS",
|
||||
"quality": "720p",
|
||||
@@ -18,4 +19,4 @@
|
||||
"supportsMojuru": true,
|
||||
"supportsSora": true,
|
||||
"supportsLuna": true
|
||||
}
|
||||
}
|
||||
@@ -2,10 +2,11 @@
|
||||
"sourceName": "AniWorld (GER SUB)",
|
||||
"iconUrl": "https://git.luna-app.eu/50n50/sources/raw/branch/main/aniworld/aniworld.png",
|
||||
"author": {
|
||||
"name": "Hamzo & Cufiy",
|
||||
"icon": "https://cdn.discordapp.com/avatars/623644371819954226/591ecab10b0b4535e859bb0b9bbe62e5?size=1024"
|
||||
"name": "Cufiy & Hamzo",
|
||||
"icon": "https://files.catbox.moe/ttj4fc.gif",
|
||||
"url": "https://github.com/JMcrafter26"
|
||||
},
|
||||
"version": "0.2.8",
|
||||
"version": "0.3.1",
|
||||
"language": "German (SUB)",
|
||||
"streamType": "HLS",
|
||||
"quality": "720p",
|
||||
@@ -18,4 +19,4 @@
|
||||
"supportsMojuru": true,
|
||||
"supportsSora": true,
|
||||
"supportsLuna": true
|
||||
}
|
||||
}
|
||||
+264
-137
@@ -6,9 +6,9 @@ async function searchResults(keyword) {
|
||||
try {
|
||||
const encodedKeyword = encodeURIComponent(keyword);
|
||||
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());
|
||||
const data = await JSON.parse(responseText);
|
||||
const data = await responseText.json() || await JSON.parse(responseText);
|
||||
console.log("Search API Data: ", data);
|
||||
|
||||
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
|
||||
finishedList.forEach((item, index) => {
|
||||
item.number = index + 1;
|
||||
});
|
||||
// finishedList.forEach((item, index) => {
|
||||
// item.number = index + 1;
|
||||
// });
|
||||
|
||||
return JSON.stringify(finishedList);
|
||||
} catch (error) {
|
||||
@@ -99,6 +99,7 @@ async function extractStreamUrl(url) {
|
||||
try {
|
||||
const baseUrl = "https://aniworld.to";
|
||||
const fetchUrl = `${url}`;
|
||||
sendLog("Fetching URL: " + fetchUrl);
|
||||
const response = await fetch(fetchUrl);
|
||||
const text = response.text ? await response.text() : response;
|
||||
|
||||
@@ -177,9 +178,7 @@ function selectHoster(finishedList) {
|
||||
|
||||
// Define the preferred providers and languages
|
||||
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 providerName of providerList) {
|
||||
@@ -249,23 +248,39 @@ async function fetchSeasonEpisodes(url) {
|
||||
const response = await fetch(fetchUrl);
|
||||
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
|
||||
const regex =
|
||||
/<td class="seasonEpisodeTitle">\s*<a[^>]*href="([^"]+)"[^>]*>.*?<strong>([^<]*)<\/strong>.*?<span>([^<]+)<\/span>.*?<\/a>/g;
|
||||
|
||||
const matches = [];
|
||||
let match;
|
||||
let holderNumber = 0;
|
||||
let number = 0;
|
||||
|
||||
while ((match = regex.exec(text)) !== null) {
|
||||
const [_, link] = match;
|
||||
matches.push({ number: holderNumber, href: `${baseUrl}${link}` });
|
||||
const [_, link, titleRaw, span] = match;
|
||||
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;
|
||||
} catch (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) {
|
||||
// send http://192.168.2.130/sora-module/log.php?action=add&message=message
|
||||
console.log(message);
|
||||
return;
|
||||
// return;
|
||||
|
||||
await fetch('http://192.168.2.130/sora-module/log.php?action=add&message=' + encodeURIComponent(message))
|
||||
.catch(error => {
|
||||
@@ -350,7 +365,7 @@ async function sendLog(message) {
|
||||
// EDITING THIS FILE COULD BREAK THE UPDATER AND CAUSE ISSUES WITH THE EXTRACTOR
|
||||
|
||||
/* {GE START} */
|
||||
/* {VERSION: 1.1.8} */
|
||||
/* {VERSION: 1.2.0} */
|
||||
|
||||
/**
|
||||
* @name global_extractor.js
|
||||
@@ -358,8 +373,8 @@ async function sendLog(message) {
|
||||
* @author Cufiy
|
||||
* @url https://github.com/JMcrafter26/sora-global-extractor
|
||||
* @license CUSTOM LICENSE - see https://github.com/JMcrafter26/sora-global-extractor/blob/main/LICENSE
|
||||
* @date 2025-11-05 15:44:57
|
||||
* @version 1.1.8
|
||||
* @date 2026-01-03 19:28:28
|
||||
* @version 1.2.0
|
||||
* @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
|
||||
*/
|
||||
@@ -369,12 +384,20 @@ function globalExtractor(providers) {
|
||||
for (const [url, provider] of Object.entries(providers)) {
|
||||
try {
|
||||
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
|
||||
if (streamUrl && typeof streamUrl === "string" && (streamUrl.startsWith("http"))) {
|
||||
if (
|
||||
streamUrl &&
|
||||
typeof streamUrl === "string" &&
|
||||
streamUrl.startsWith("http")
|
||||
) {
|
||||
return streamUrl;
|
||||
// if its an array, get the value that starts with http
|
||||
} else if (Array.isArray(streamUrl)) {
|
||||
const httpStream = streamUrl.find(url => url.startsWith("http"));
|
||||
const httpStream = streamUrl.find((url) => url.startsWith("http"));
|
||||
if (httpStream) {
|
||||
return httpStream;
|
||||
}
|
||||
@@ -382,7 +405,6 @@ function globalExtractor(providers) {
|
||||
// check if it's a valid stream URL
|
||||
return null;
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
// 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
|
||||
{
|
||||
"streams": [
|
||||
"FileMoon",
|
||||
"https://filemoon.example/stream1.m3u8",
|
||||
"StreamWish",
|
||||
"https://streamwish.example/stream2.m3u8",
|
||||
"Okru",
|
||||
"https://okru.example/stream3.m3u8",
|
||||
"MP4",
|
||||
"https://mp4upload.example/stream4.mp4",
|
||||
"Default",
|
||||
"https://default.example/stream5.m3u8"
|
||||
{
|
||||
"title": "FileMoon",
|
||||
"streamUrl": "https://filemoon.example/stream1.m3u8",
|
||||
},
|
||||
{
|
||||
"title": "StreamWish",
|
||||
"streamUrl": "https://streamwish.example/stream2.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.startsWith("direct-")) {
|
||||
const directName = provider.slice(7); // remove "direct-" prefix
|
||||
if (directName && directName.length > 0) {
|
||||
streams.push(directName, url);
|
||||
} else {
|
||||
streams.push("Direct", url); // fallback to "Direct" if no name is provided
|
||||
}
|
||||
const title = (directName && directName.length > 0) ? directName : "Direct";
|
||||
streams.push({
|
||||
title: title,
|
||||
streamUrl: url
|
||||
});
|
||||
continue; // skip to the next provider
|
||||
}
|
||||
if (provider.startsWith("direct")) {
|
||||
provider = provider.slice(7); // remove "direct-" prefix
|
||||
if (provider && provider.length > 0) {
|
||||
streams.push(provider, url);
|
||||
} else {
|
||||
streams.push("Direct", url); // fallback to "Direct" if no name is provided
|
||||
}
|
||||
const title = (provider && provider.length > 0) ? provider : "Direct";
|
||||
streams.push({
|
||||
title: title,
|
||||
streamUrl: url
|
||||
});
|
||||
continue; // skip to the next provider
|
||||
}
|
||||
|
||||
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`);
|
||||
continue;
|
||||
}
|
||||
let streamUrl = await extractStreamUrlByProvider(url, provider);
|
||||
|
||||
if (streamUrl && Array.isArray(streamUrl)) {
|
||||
const httpStream = streamUrl.find(url => url.startsWith("http"));
|
||||
let result = await extractStreamUrlByProvider(url, provider);
|
||||
let streamUrl = null;
|
||||
let headers = null;
|
||||
|
||||
// Check if result is an object with streamUrl and optional headers
|
||||
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) {
|
||||
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 (
|
||||
!streamUrl ||
|
||||
typeof streamUrl !== "string" ||
|
||||
@@ -467,22 +513,29 @@ async function multiExtractor(providers) {
|
||||
provider = customName;
|
||||
}
|
||||
|
||||
let title;
|
||||
if (providersCount[provider]) {
|
||||
providersCount[provider]++;
|
||||
streams.push(
|
||||
provider.charAt(0).toUpperCase() +
|
||||
title = provider.charAt(0).toUpperCase() +
|
||||
provider.slice(1) +
|
||||
"-" +
|
||||
(providersCount[provider] - 1), // add a number to the provider name
|
||||
streamUrl
|
||||
);
|
||||
(providersCount[provider] - 1); // add a number to the provider name
|
||||
} else {
|
||||
providersCount[provider] = 1;
|
||||
streams.push(
|
||||
provider.charAt(0).toUpperCase() + provider.slice(1),
|
||||
streamUrl
|
||||
);
|
||||
title = provider.charAt(0).toUpperCase() + provider.slice(1);
|
||||
}
|
||||
|
||||
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) {
|
||||
// Ignore the error and try the next provider
|
||||
}
|
||||
@@ -493,73 +546,98 @@ async function multiExtractor(providers) {
|
||||
async function extractStreamUrlByProvider(url, provider) {
|
||||
if (eval(`typeof ${provider}Extractor`) !== "function") {
|
||||
// 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;
|
||||
}
|
||||
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 = {
|
||||
"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",
|
||||
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8",
|
||||
"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-Language": "en-US,en;q=0.5",
|
||||
"Referer": url,
|
||||
"Connection": "keep-alive",
|
||||
"x-Requested-With": "XMLHttpRequest"
|
||||
"x-Requested-With": "XMLHttpRequest",
|
||||
};
|
||||
if(provider == 'bigwarp') {
|
||||
delete headers["User-Agent"];
|
||||
headers["x-requested-with"] = "XMLHttpRequest";
|
||||
} else if (provider == 'vk') {
|
||||
headers["encoding"] = "windows-1251"; // required
|
||||
} else if (provider == 'sibnet') {
|
||||
headers["encoding"] = "windows-1251"; // required
|
||||
} else if (provider == 'supervideo') {
|
||||
delete headers["User-Agent"];
|
||||
|
||||
switch (provider) {
|
||||
case "bigwarp":
|
||||
delete headers["User-Agent"];
|
||||
break;
|
||||
case "vk":
|
||||
case "sibnet":
|
||||
headers["encoding"] = "windows-1251"; // required
|
||||
break;
|
||||
case "supervideo":
|
||||
case "savefiles":
|
||||
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
|
||||
// and pass the response to the extractor function
|
||||
console.log("Fetching URL: " + url);
|
||||
const response = await soraFetch(url, {
|
||||
headers
|
||||
});
|
||||
headers,
|
||||
});
|
||||
|
||||
console.log("Response: " + response.status);
|
||||
let html = response.text ? await response.text() : response;
|
||||
// if title contains redirect, then get the redirect url
|
||||
const title = html.match(/<title>(.*?)<\/title>/);
|
||||
if (title && title[1].toLowerCase().includes("redirect")) {
|
||||
const redirectUrl = html.match(/<meta http-equiv="refresh" content="0;url=(.*?)"/);
|
||||
const redirectUrl2 = html.match(/window\.location\.href\s*=\s*["'](.*?)["']/);
|
||||
const redirectUrl3 = html.match(/window\.location\.replace\s*\(\s*["'](.*?)["']\s*\)/);
|
||||
if (redirectUrl) {
|
||||
console.log("Redirect URL: " + redirectUrl[1]);
|
||||
url = redirectUrl[1];
|
||||
html = await soraFetch(url, {
|
||||
headers
|
||||
});
|
||||
html = html.text ? await html.text() : html;
|
||||
|
||||
} 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");
|
||||
const matches = [
|
||||
/<meta http-equiv="refresh" content="0;url=(.*?)"/,
|
||||
/window\.location\.href\s*=\s*["'](.*?)["']/,
|
||||
/window\.location\.replace\s*\(\s*["'](.*?)["']\s*\)/,
|
||||
/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];
|
||||
headers['Referer'] = url;
|
||||
headers['Host'] = url.match(/https?:\/\/([^\/]+)/)[1];
|
||||
html = await soraFetch(url, {
|
||||
headers,
|
||||
}).then((res) => res.text());
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// console.log("HTML: " + html);
|
||||
switch (provider) {
|
||||
case "bigwarp":
|
||||
case "bigwarp":
|
||||
try {
|
||||
return await bigwarpExtractor(html, url);
|
||||
} catch (error) {
|
||||
@@ -608,6 +686,20 @@ async function extractStreamUrlByProvider(url, provider) {
|
||||
console.log("Error extracting stream URL from mp4upload:", error);
|
||||
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":
|
||||
try {
|
||||
return await sendvidExtractor(html, url);
|
||||
@@ -622,6 +714,13 @@ async function extractStreamUrlByProvider(url, provider) {
|
||||
console.log("Error extracting stream URL from sibnet:", error);
|
||||
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":
|
||||
try {
|
||||
return await streamtapeExtractor(html, url);
|
||||
@@ -636,13 +735,6 @@ async function extractStreamUrlByProvider(url, provider) {
|
||||
console.log("Error extracting stream URL from streamup:", error);
|
||||
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":
|
||||
try {
|
||||
return await uploadcxExtractor(html, url);
|
||||
@@ -691,7 +783,6 @@ async function extractStreamUrlByProvider(url, provider) {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////
|
||||
// EXTRACTORS //
|
||||
////////////////////////////////////////////////
|
||||
@@ -1178,7 +1269,6 @@ async function megacloudExtractor(html, embedUrl) {
|
||||
* @author Cufiy
|
||||
*/
|
||||
async function mp4uploadExtractor(html, url = null) {
|
||||
// src: "https://a4.mp4upload.com:183/d/xkx3b4etz3b4quuo66rbmyqtjjoivahfxp27f35pti45rzapbvj5xwb4wuqtlpewdz4dirfp/video.mp4"
|
||||
const regex = /src:\s*"([^"]+)"/;
|
||||
const match = html.match(regex);
|
||||
if (match) {
|
||||
@@ -1188,6 +1278,32 @@ async function mp4uploadExtractor(html, url = 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 --- */
|
||||
|
||||
/**
|
||||
@@ -1223,6 +1339,29 @@ async function sibnetExtractor(html, embedUrl) {
|
||||
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 --- */
|
||||
|
||||
/**
|
||||
@@ -1306,26 +1445,6 @@ async function streamupExtractor(data, url = 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 --- */
|
||||
|
||||
/**
|
||||
@@ -1390,7 +1509,7 @@ async function vidmolyExtractor(html, url = null) {
|
||||
const streamUrl = iframeMatch[1].startsWith("//")
|
||||
? "https:" + iframeMatch[1]
|
||||
: iframeMatch[1];
|
||||
const responseTwo = await fetchv2(streamUrl);
|
||||
const responseTwo = await soraFetch(streamUrl);
|
||||
const htmlTwo = await responseTwo.text();
|
||||
const m3u8Match = htmlTwo.match(/sources:\s*\[\{file:"([^"]+\.m3u8)"/);
|
||||
return m3u8Match ? m3u8Match[1] : null;
|
||||
@@ -1516,6 +1635,7 @@ function voeShiftChars(str, shift) {
|
||||
.join("");
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////
|
||||
// PLUGINS //
|
||||
////////////////////////////////////////////////
|
||||
@@ -1533,17 +1653,25 @@ function voeShiftChars(str, shift) {
|
||||
* @returns {Promise<Response|null>} The response from the server, or null if the
|
||||
* request failed.
|
||||
*/
|
||||
async function soraFetch(url, options = { headers: {}, method: 'GET', body: null }) {
|
||||
async function soraFetch(
|
||||
url,
|
||||
options = { headers: {}, method: "GET", body: null }
|
||||
) {
|
||||
try {
|
||||
return await fetchv2(
|
||||
url,
|
||||
options.headers ?? {},
|
||||
options.method ?? "GET",
|
||||
options.body ?? null
|
||||
);
|
||||
} catch (e) {
|
||||
try {
|
||||
return await fetchv2(url, options.headers ?? {}, options.method ?? 'GET', options.body ?? null);
|
||||
} catch(e) {
|
||||
try {
|
||||
return await fetch(url, options);
|
||||
} catch(error) {
|
||||
await console.log('soraFetch error: ' + error.message);
|
||||
return null;
|
||||
}
|
||||
return await fetch(url, options);
|
||||
} catch (error) {
|
||||
await console.log("soraFetch error: " + error.message);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
/***********************************************************
|
||||
* UNPACKER MODULE
|
||||
@@ -1647,6 +1775,5 @@ function unpack(source) {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* {GE END} */
|
||||
/* {GE END} */
|
||||
|
||||
+262
-134
@@ -6,9 +6,9 @@ async function searchResults(keyword) {
|
||||
try {
|
||||
const encodedKeyword = encodeURIComponent(keyword);
|
||||
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());
|
||||
const data = await JSON.parse(responseText);
|
||||
const data = await responseText.json() || await JSON.parse(responseText);
|
||||
console.log("Search API Data: ", data);
|
||||
|
||||
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
|
||||
finishedList.forEach((item, index) => {
|
||||
item.number = index + 1;
|
||||
});
|
||||
// finishedList.forEach((item, index) => {
|
||||
// item.number = index + 1;
|
||||
// });
|
||||
|
||||
return JSON.stringify(finishedList);
|
||||
} catch (error) {
|
||||
@@ -250,23 +250,39 @@ async function fetchSeasonEpisodes(url) {
|
||||
const response = await fetch(fetchUrl);
|
||||
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
|
||||
const regex =
|
||||
/<td class="seasonEpisodeTitle">\s*<a[^>]*href="([^"]+)"[^>]*>.*?<strong>([^<]*)<\/strong>.*?<span>([^<]+)<\/span>.*?<\/a>/g;
|
||||
|
||||
const matches = [];
|
||||
let match;
|
||||
let holderNumber = 0;
|
||||
let number = 0;
|
||||
|
||||
while ((match = regex.exec(text)) !== null) {
|
||||
const [_, link] = match;
|
||||
matches.push({ number: holderNumber, href: `${baseUrl}${link}` });
|
||||
const [_, link, titleRaw, span] = match;
|
||||
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;
|
||||
} catch (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) {
|
||||
// send http://192.168.2.130/sora-module/log.php?action=add&message=message
|
||||
console.log(message);
|
||||
return;
|
||||
// return;
|
||||
|
||||
await fetch('http://192.168.2.130/sora-module/log.php?action=add&message=' + encodeURIComponent(message))
|
||||
.catch(error => {
|
||||
@@ -351,7 +367,7 @@ async function sendLog(message) {
|
||||
// EDITING THIS FILE COULD BREAK THE UPDATER AND CAUSE ISSUES WITH THE EXTRACTOR
|
||||
|
||||
/* {GE START} */
|
||||
/* {VERSION: 1.1.8} */
|
||||
/* {VERSION: 1.2.0} */
|
||||
|
||||
/**
|
||||
* @name global_extractor.js
|
||||
@@ -359,8 +375,8 @@ async function sendLog(message) {
|
||||
* @author Cufiy
|
||||
* @url https://github.com/JMcrafter26/sora-global-extractor
|
||||
* @license CUSTOM LICENSE - see https://github.com/JMcrafter26/sora-global-extractor/blob/main/LICENSE
|
||||
* @date 2025-11-05 15:44:57
|
||||
* @version 1.1.8
|
||||
* @date 2026-01-03 19:28:28
|
||||
* @version 1.2.0
|
||||
* @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
|
||||
*/
|
||||
@@ -370,12 +386,20 @@ function globalExtractor(providers) {
|
||||
for (const [url, provider] of Object.entries(providers)) {
|
||||
try {
|
||||
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
|
||||
if (streamUrl && typeof streamUrl === "string" && (streamUrl.startsWith("http"))) {
|
||||
if (
|
||||
streamUrl &&
|
||||
typeof streamUrl === "string" &&
|
||||
streamUrl.startsWith("http")
|
||||
) {
|
||||
return streamUrl;
|
||||
// if its an array, get the value that starts with http
|
||||
} else if (Array.isArray(streamUrl)) {
|
||||
const httpStream = streamUrl.find(url => url.startsWith("http"));
|
||||
const httpStream = streamUrl.find((url) => url.startsWith("http"));
|
||||
if (httpStream) {
|
||||
return httpStream;
|
||||
}
|
||||
@@ -383,7 +407,6 @@ function globalExtractor(providers) {
|
||||
// check if it's a valid stream URL
|
||||
return null;
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
// 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
|
||||
{
|
||||
"streams": [
|
||||
"FileMoon",
|
||||
"https://filemoon.example/stream1.m3u8",
|
||||
"StreamWish",
|
||||
"https://streamwish.example/stream2.m3u8",
|
||||
"Okru",
|
||||
"https://okru.example/stream3.m3u8",
|
||||
"MP4",
|
||||
"https://mp4upload.example/stream4.mp4",
|
||||
"Default",
|
||||
"https://default.example/stream5.m3u8"
|
||||
{
|
||||
"title": "FileMoon",
|
||||
"streamUrl": "https://filemoon.example/stream1.m3u8",
|
||||
},
|
||||
{
|
||||
"title": "StreamWish",
|
||||
"streamUrl": "https://streamwish.example/stream2.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.startsWith("direct-")) {
|
||||
const directName = provider.slice(7); // remove "direct-" prefix
|
||||
if (directName && directName.length > 0) {
|
||||
streams.push(directName, url);
|
||||
} else {
|
||||
streams.push("Direct", url); // fallback to "Direct" if no name is provided
|
||||
}
|
||||
const title = (directName && directName.length > 0) ? directName : "Direct";
|
||||
streams.push({
|
||||
title: title,
|
||||
streamUrl: url
|
||||
});
|
||||
continue; // skip to the next provider
|
||||
}
|
||||
if (provider.startsWith("direct")) {
|
||||
provider = provider.slice(7); // remove "direct-" prefix
|
||||
if (provider && provider.length > 0) {
|
||||
streams.push(provider, url);
|
||||
} else {
|
||||
streams.push("Direct", url); // fallback to "Direct" if no name is provided
|
||||
}
|
||||
const title = (provider && provider.length > 0) ? provider : "Direct";
|
||||
streams.push({
|
||||
title: title,
|
||||
streamUrl: url
|
||||
});
|
||||
continue; // skip to the next provider
|
||||
}
|
||||
|
||||
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`);
|
||||
continue;
|
||||
}
|
||||
let streamUrl = await extractStreamUrlByProvider(url, provider);
|
||||
|
||||
if (streamUrl && Array.isArray(streamUrl)) {
|
||||
const httpStream = streamUrl.find(url => url.startsWith("http"));
|
||||
let result = await extractStreamUrlByProvider(url, provider);
|
||||
let streamUrl = null;
|
||||
let headers = null;
|
||||
|
||||
// Check if result is an object with streamUrl and optional headers
|
||||
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) {
|
||||
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 (
|
||||
!streamUrl ||
|
||||
typeof streamUrl !== "string" ||
|
||||
@@ -468,22 +515,29 @@ async function multiExtractor(providers) {
|
||||
provider = customName;
|
||||
}
|
||||
|
||||
let title;
|
||||
if (providersCount[provider]) {
|
||||
providersCount[provider]++;
|
||||
streams.push(
|
||||
provider.charAt(0).toUpperCase() +
|
||||
title = provider.charAt(0).toUpperCase() +
|
||||
provider.slice(1) +
|
||||
"-" +
|
||||
(providersCount[provider] - 1), // add a number to the provider name
|
||||
streamUrl
|
||||
);
|
||||
(providersCount[provider] - 1); // add a number to the provider name
|
||||
} else {
|
||||
providersCount[provider] = 1;
|
||||
streams.push(
|
||||
provider.charAt(0).toUpperCase() + provider.slice(1),
|
||||
streamUrl
|
||||
);
|
||||
title = provider.charAt(0).toUpperCase() + provider.slice(1);
|
||||
}
|
||||
|
||||
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) {
|
||||
// Ignore the error and try the next provider
|
||||
}
|
||||
@@ -494,73 +548,98 @@ async function multiExtractor(providers) {
|
||||
async function extractStreamUrlByProvider(url, provider) {
|
||||
if (eval(`typeof ${provider}Extractor`) !== "function") {
|
||||
// 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;
|
||||
}
|
||||
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 = {
|
||||
"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",
|
||||
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8",
|
||||
"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-Language": "en-US,en;q=0.5",
|
||||
"Referer": url,
|
||||
"Connection": "keep-alive",
|
||||
"x-Requested-With": "XMLHttpRequest"
|
||||
"x-Requested-With": "XMLHttpRequest",
|
||||
};
|
||||
if(provider == 'bigwarp') {
|
||||
delete headers["User-Agent"];
|
||||
headers["x-requested-with"] = "XMLHttpRequest";
|
||||
} else if (provider == 'vk') {
|
||||
headers["encoding"] = "windows-1251"; // required
|
||||
} else if (provider == 'sibnet') {
|
||||
headers["encoding"] = "windows-1251"; // required
|
||||
} else if (provider == 'supervideo') {
|
||||
delete headers["User-Agent"];
|
||||
|
||||
switch (provider) {
|
||||
case "bigwarp":
|
||||
delete headers["User-Agent"];
|
||||
break;
|
||||
case "vk":
|
||||
case "sibnet":
|
||||
headers["encoding"] = "windows-1251"; // required
|
||||
break;
|
||||
case "supervideo":
|
||||
case "savefiles":
|
||||
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
|
||||
// and pass the response to the extractor function
|
||||
console.log("Fetching URL: " + url);
|
||||
const response = await soraFetch(url, {
|
||||
headers
|
||||
});
|
||||
headers,
|
||||
});
|
||||
|
||||
console.log("Response: " + response.status);
|
||||
let html = response.text ? await response.text() : response;
|
||||
// if title contains redirect, then get the redirect url
|
||||
const title = html.match(/<title>(.*?)<\/title>/);
|
||||
if (title && title[1].toLowerCase().includes("redirect")) {
|
||||
const redirectUrl = html.match(/<meta http-equiv="refresh" content="0;url=(.*?)"/);
|
||||
const redirectUrl2 = html.match(/window\.location\.href\s*=\s*["'](.*?)["']/);
|
||||
const redirectUrl3 = html.match(/window\.location\.replace\s*\(\s*["'](.*?)["']\s*\)/);
|
||||
if (redirectUrl) {
|
||||
console.log("Redirect URL: " + redirectUrl[1]);
|
||||
url = redirectUrl[1];
|
||||
html = await soraFetch(url, {
|
||||
headers
|
||||
});
|
||||
html = html.text ? await html.text() : html;
|
||||
|
||||
} 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");
|
||||
const matches = [
|
||||
/<meta http-equiv="refresh" content="0;url=(.*?)"/,
|
||||
/window\.location\.href\s*=\s*["'](.*?)["']/,
|
||||
/window\.location\.replace\s*\(\s*["'](.*?)["']\s*\)/,
|
||||
/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];
|
||||
headers['Referer'] = url;
|
||||
headers['Host'] = url.match(/https?:\/\/([^\/]+)/)[1];
|
||||
html = await soraFetch(url, {
|
||||
headers,
|
||||
}).then((res) => res.text());
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// console.log("HTML: " + html);
|
||||
switch (provider) {
|
||||
case "bigwarp":
|
||||
case "bigwarp":
|
||||
try {
|
||||
return await bigwarpExtractor(html, url);
|
||||
} catch (error) {
|
||||
@@ -609,6 +688,20 @@ async function extractStreamUrlByProvider(url, provider) {
|
||||
console.log("Error extracting stream URL from mp4upload:", error);
|
||||
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":
|
||||
try {
|
||||
return await sendvidExtractor(html, url);
|
||||
@@ -623,6 +716,13 @@ async function extractStreamUrlByProvider(url, provider) {
|
||||
console.log("Error extracting stream URL from sibnet:", error);
|
||||
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":
|
||||
try {
|
||||
return await streamtapeExtractor(html, url);
|
||||
@@ -637,13 +737,6 @@ async function extractStreamUrlByProvider(url, provider) {
|
||||
console.log("Error extracting stream URL from streamup:", error);
|
||||
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":
|
||||
try {
|
||||
return await uploadcxExtractor(html, url);
|
||||
@@ -692,7 +785,6 @@ async function extractStreamUrlByProvider(url, provider) {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////
|
||||
// EXTRACTORS //
|
||||
////////////////////////////////////////////////
|
||||
@@ -1179,7 +1271,6 @@ async function megacloudExtractor(html, embedUrl) {
|
||||
* @author Cufiy
|
||||
*/
|
||||
async function mp4uploadExtractor(html, url = null) {
|
||||
// src: "https://a4.mp4upload.com:183/d/xkx3b4etz3b4quuo66rbmyqtjjoivahfxp27f35pti45rzapbvj5xwb4wuqtlpewdz4dirfp/video.mp4"
|
||||
const regex = /src:\s*"([^"]+)"/;
|
||||
const match = html.match(regex);
|
||||
if (match) {
|
||||
@@ -1189,6 +1280,32 @@ async function mp4uploadExtractor(html, url = 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 --- */
|
||||
|
||||
/**
|
||||
@@ -1224,6 +1341,29 @@ async function sibnetExtractor(html, embedUrl) {
|
||||
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 --- */
|
||||
|
||||
/**
|
||||
@@ -1307,26 +1447,6 @@ async function streamupExtractor(data, url = 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 --- */
|
||||
|
||||
/**
|
||||
@@ -1391,7 +1511,7 @@ async function vidmolyExtractor(html, url = null) {
|
||||
const streamUrl = iframeMatch[1].startsWith("//")
|
||||
? "https:" + iframeMatch[1]
|
||||
: iframeMatch[1];
|
||||
const responseTwo = await fetchv2(streamUrl);
|
||||
const responseTwo = await soraFetch(streamUrl);
|
||||
const htmlTwo = await responseTwo.text();
|
||||
const m3u8Match = htmlTwo.match(/sources:\s*\[\{file:"([^"]+\.m3u8)"/);
|
||||
return m3u8Match ? m3u8Match[1] : null;
|
||||
@@ -1517,6 +1637,7 @@ function voeShiftChars(str, shift) {
|
||||
.join("");
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////
|
||||
// PLUGINS //
|
||||
////////////////////////////////////////////////
|
||||
@@ -1534,17 +1655,25 @@ function voeShiftChars(str, shift) {
|
||||
* @returns {Promise<Response|null>} The response from the server, or null if the
|
||||
* request failed.
|
||||
*/
|
||||
async function soraFetch(url, options = { headers: {}, method: 'GET', body: null }) {
|
||||
async function soraFetch(
|
||||
url,
|
||||
options = { headers: {}, method: "GET", body: null }
|
||||
) {
|
||||
try {
|
||||
return await fetchv2(
|
||||
url,
|
||||
options.headers ?? {},
|
||||
options.method ?? "GET",
|
||||
options.body ?? null
|
||||
);
|
||||
} catch (e) {
|
||||
try {
|
||||
return await fetchv2(url, options.headers ?? {}, options.method ?? 'GET', options.body ?? null);
|
||||
} catch(e) {
|
||||
try {
|
||||
return await fetch(url, options);
|
||||
} catch(error) {
|
||||
await console.log('soraFetch error: ' + error.message);
|
||||
return null;
|
||||
}
|
||||
return await fetch(url, options);
|
||||
} catch (error) {
|
||||
await console.log("soraFetch error: " + error.message);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
/***********************************************************
|
||||
* UNPACKER MODULE
|
||||
@@ -1648,6 +1777,5 @@ function unpack(source) {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* {GE END} */
|
||||
/* {GE END} */
|
||||
|
||||
+263
-134
@@ -6,9 +6,9 @@ async function searchResults(keyword) {
|
||||
try {
|
||||
const encodedKeyword = encodeURIComponent(keyword);
|
||||
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());
|
||||
const data = await JSON.parse(responseText);
|
||||
const data = await responseText.json() || await JSON.parse(responseText);
|
||||
console.log("Search API Data: ", data);
|
||||
|
||||
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
|
||||
finishedList.forEach((item, index) => {
|
||||
item.number = index + 1;
|
||||
});
|
||||
// finishedList.forEach((item, index) => {
|
||||
// item.number = index + 1;
|
||||
// });
|
||||
|
||||
return JSON.stringify(finishedList);
|
||||
} catch (error) {
|
||||
@@ -99,6 +99,7 @@ async function extractStreamUrl(url) {
|
||||
try {
|
||||
const baseUrl = "https://aniworld.to";
|
||||
const fetchUrl = `${url}`;
|
||||
sendLog("Fetching URL: " + fetchUrl);
|
||||
const response = await fetch(fetchUrl);
|
||||
const text = response.text ? await response.text() : response;
|
||||
|
||||
@@ -249,23 +250,39 @@ async function fetchSeasonEpisodes(url) {
|
||||
const response = await fetch(fetchUrl);
|
||||
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
|
||||
const regex =
|
||||
/<td class="seasonEpisodeTitle">\s*<a[^>]*href="([^"]+)"[^>]*>.*?<strong>([^<]*)<\/strong>.*?<span>([^<]+)<\/span>.*?<\/a>/g;
|
||||
|
||||
const matches = [];
|
||||
let match;
|
||||
let holderNumber = 0;
|
||||
let number = 0;
|
||||
|
||||
while ((match = regex.exec(text)) !== null) {
|
||||
const [_, link] = match;
|
||||
matches.push({ number: holderNumber, href: `${baseUrl}${link}` });
|
||||
const [_, link, titleRaw, span] = match;
|
||||
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;
|
||||
} catch (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) {
|
||||
// send http://192.168.2.130/sora-module/log.php?action=add&message=message
|
||||
console.log(message);
|
||||
return;
|
||||
// return;
|
||||
|
||||
await fetch('http://192.168.2.130/sora-module/log.php?action=add&message=' + encodeURIComponent(message))
|
||||
.catch(error => {
|
||||
@@ -350,7 +367,7 @@ async function sendLog(message) {
|
||||
// EDITING THIS FILE COULD BREAK THE UPDATER AND CAUSE ISSUES WITH THE EXTRACTOR
|
||||
|
||||
/* {GE START} */
|
||||
/* {VERSION: 1.1.8} */
|
||||
/* {VERSION: 1.2.0} */
|
||||
|
||||
/**
|
||||
* @name global_extractor.js
|
||||
@@ -358,8 +375,8 @@ async function sendLog(message) {
|
||||
* @author Cufiy
|
||||
* @url https://github.com/JMcrafter26/sora-global-extractor
|
||||
* @license CUSTOM LICENSE - see https://github.com/JMcrafter26/sora-global-extractor/blob/main/LICENSE
|
||||
* @date 2025-11-05 15:44:57
|
||||
* @version 1.1.8
|
||||
* @date 2026-01-03 19:28:28
|
||||
* @version 1.2.0
|
||||
* @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
|
||||
*/
|
||||
@@ -369,12 +386,20 @@ function globalExtractor(providers) {
|
||||
for (const [url, provider] of Object.entries(providers)) {
|
||||
try {
|
||||
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
|
||||
if (streamUrl && typeof streamUrl === "string" && (streamUrl.startsWith("http"))) {
|
||||
if (
|
||||
streamUrl &&
|
||||
typeof streamUrl === "string" &&
|
||||
streamUrl.startsWith("http")
|
||||
) {
|
||||
return streamUrl;
|
||||
// if its an array, get the value that starts with http
|
||||
} else if (Array.isArray(streamUrl)) {
|
||||
const httpStream = streamUrl.find(url => url.startsWith("http"));
|
||||
const httpStream = streamUrl.find((url) => url.startsWith("http"));
|
||||
if (httpStream) {
|
||||
return httpStream;
|
||||
}
|
||||
@@ -382,7 +407,6 @@ function globalExtractor(providers) {
|
||||
// check if it's a valid stream URL
|
||||
return null;
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
// 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
|
||||
{
|
||||
"streams": [
|
||||
"FileMoon",
|
||||
"https://filemoon.example/stream1.m3u8",
|
||||
"StreamWish",
|
||||
"https://streamwish.example/stream2.m3u8",
|
||||
"Okru",
|
||||
"https://okru.example/stream3.m3u8",
|
||||
"MP4",
|
||||
"https://mp4upload.example/stream4.mp4",
|
||||
"Default",
|
||||
"https://default.example/stream5.m3u8"
|
||||
{
|
||||
"title": "FileMoon",
|
||||
"streamUrl": "https://filemoon.example/stream1.m3u8",
|
||||
},
|
||||
{
|
||||
"title": "StreamWish",
|
||||
"streamUrl": "https://streamwish.example/stream2.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.startsWith("direct-")) {
|
||||
const directName = provider.slice(7); // remove "direct-" prefix
|
||||
if (directName && directName.length > 0) {
|
||||
streams.push(directName, url);
|
||||
} else {
|
||||
streams.push("Direct", url); // fallback to "Direct" if no name is provided
|
||||
}
|
||||
const title = (directName && directName.length > 0) ? directName : "Direct";
|
||||
streams.push({
|
||||
title: title,
|
||||
streamUrl: url
|
||||
});
|
||||
continue; // skip to the next provider
|
||||
}
|
||||
if (provider.startsWith("direct")) {
|
||||
provider = provider.slice(7); // remove "direct-" prefix
|
||||
if (provider && provider.length > 0) {
|
||||
streams.push(provider, url);
|
||||
} else {
|
||||
streams.push("Direct", url); // fallback to "Direct" if no name is provided
|
||||
}
|
||||
const title = (provider && provider.length > 0) ? provider : "Direct";
|
||||
streams.push({
|
||||
title: title,
|
||||
streamUrl: url
|
||||
});
|
||||
continue; // skip to the next provider
|
||||
}
|
||||
|
||||
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`);
|
||||
continue;
|
||||
}
|
||||
let streamUrl = await extractStreamUrlByProvider(url, provider);
|
||||
|
||||
if (streamUrl && Array.isArray(streamUrl)) {
|
||||
const httpStream = streamUrl.find(url => url.startsWith("http"));
|
||||
let result = await extractStreamUrlByProvider(url, provider);
|
||||
let streamUrl = null;
|
||||
let headers = null;
|
||||
|
||||
// Check if result is an object with streamUrl and optional headers
|
||||
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) {
|
||||
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 (
|
||||
!streamUrl ||
|
||||
typeof streamUrl !== "string" ||
|
||||
@@ -467,22 +515,29 @@ async function multiExtractor(providers) {
|
||||
provider = customName;
|
||||
}
|
||||
|
||||
let title;
|
||||
if (providersCount[provider]) {
|
||||
providersCount[provider]++;
|
||||
streams.push(
|
||||
provider.charAt(0).toUpperCase() +
|
||||
title = provider.charAt(0).toUpperCase() +
|
||||
provider.slice(1) +
|
||||
"-" +
|
||||
(providersCount[provider] - 1), // add a number to the provider name
|
||||
streamUrl
|
||||
);
|
||||
(providersCount[provider] - 1); // add a number to the provider name
|
||||
} else {
|
||||
providersCount[provider] = 1;
|
||||
streams.push(
|
||||
provider.charAt(0).toUpperCase() + provider.slice(1),
|
||||
streamUrl
|
||||
);
|
||||
title = provider.charAt(0).toUpperCase() + provider.slice(1);
|
||||
}
|
||||
|
||||
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) {
|
||||
// Ignore the error and try the next provider
|
||||
}
|
||||
@@ -493,73 +548,98 @@ async function multiExtractor(providers) {
|
||||
async function extractStreamUrlByProvider(url, provider) {
|
||||
if (eval(`typeof ${provider}Extractor`) !== "function") {
|
||||
// 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;
|
||||
}
|
||||
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 = {
|
||||
"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",
|
||||
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8",
|
||||
"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-Language": "en-US,en;q=0.5",
|
||||
"Referer": url,
|
||||
"Connection": "keep-alive",
|
||||
"x-Requested-With": "XMLHttpRequest"
|
||||
"x-Requested-With": "XMLHttpRequest",
|
||||
};
|
||||
if(provider == 'bigwarp') {
|
||||
delete headers["User-Agent"];
|
||||
headers["x-requested-with"] = "XMLHttpRequest";
|
||||
} else if (provider == 'vk') {
|
||||
headers["encoding"] = "windows-1251"; // required
|
||||
} else if (provider == 'sibnet') {
|
||||
headers["encoding"] = "windows-1251"; // required
|
||||
} else if (provider == 'supervideo') {
|
||||
delete headers["User-Agent"];
|
||||
|
||||
switch (provider) {
|
||||
case "bigwarp":
|
||||
delete headers["User-Agent"];
|
||||
break;
|
||||
case "vk":
|
||||
case "sibnet":
|
||||
headers["encoding"] = "windows-1251"; // required
|
||||
break;
|
||||
case "supervideo":
|
||||
case "savefiles":
|
||||
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
|
||||
// and pass the response to the extractor function
|
||||
console.log("Fetching URL: " + url);
|
||||
const response = await soraFetch(url, {
|
||||
headers
|
||||
});
|
||||
headers,
|
||||
});
|
||||
|
||||
console.log("Response: " + response.status);
|
||||
let html = response.text ? await response.text() : response;
|
||||
// if title contains redirect, then get the redirect url
|
||||
const title = html.match(/<title>(.*?)<\/title>/);
|
||||
if (title && title[1].toLowerCase().includes("redirect")) {
|
||||
const redirectUrl = html.match(/<meta http-equiv="refresh" content="0;url=(.*?)"/);
|
||||
const redirectUrl2 = html.match(/window\.location\.href\s*=\s*["'](.*?)["']/);
|
||||
const redirectUrl3 = html.match(/window\.location\.replace\s*\(\s*["'](.*?)["']\s*\)/);
|
||||
if (redirectUrl) {
|
||||
console.log("Redirect URL: " + redirectUrl[1]);
|
||||
url = redirectUrl[1];
|
||||
html = await soraFetch(url, {
|
||||
headers
|
||||
});
|
||||
html = html.text ? await html.text() : html;
|
||||
|
||||
} 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");
|
||||
const matches = [
|
||||
/<meta http-equiv="refresh" content="0;url=(.*?)"/,
|
||||
/window\.location\.href\s*=\s*["'](.*?)["']/,
|
||||
/window\.location\.replace\s*\(\s*["'](.*?)["']\s*\)/,
|
||||
/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];
|
||||
headers['Referer'] = url;
|
||||
headers['Host'] = url.match(/https?:\/\/([^\/]+)/)[1];
|
||||
html = await soraFetch(url, {
|
||||
headers,
|
||||
}).then((res) => res.text());
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// console.log("HTML: " + html);
|
||||
switch (provider) {
|
||||
case "bigwarp":
|
||||
case "bigwarp":
|
||||
try {
|
||||
return await bigwarpExtractor(html, url);
|
||||
} catch (error) {
|
||||
@@ -608,6 +688,20 @@ async function extractStreamUrlByProvider(url, provider) {
|
||||
console.log("Error extracting stream URL from mp4upload:", error);
|
||||
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":
|
||||
try {
|
||||
return await sendvidExtractor(html, url);
|
||||
@@ -622,6 +716,13 @@ async function extractStreamUrlByProvider(url, provider) {
|
||||
console.log("Error extracting stream URL from sibnet:", error);
|
||||
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":
|
||||
try {
|
||||
return await streamtapeExtractor(html, url);
|
||||
@@ -636,13 +737,6 @@ async function extractStreamUrlByProvider(url, provider) {
|
||||
console.log("Error extracting stream URL from streamup:", error);
|
||||
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":
|
||||
try {
|
||||
return await uploadcxExtractor(html, url);
|
||||
@@ -691,7 +785,6 @@ async function extractStreamUrlByProvider(url, provider) {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////
|
||||
// EXTRACTORS //
|
||||
////////////////////////////////////////////////
|
||||
@@ -1178,7 +1271,6 @@ async function megacloudExtractor(html, embedUrl) {
|
||||
* @author Cufiy
|
||||
*/
|
||||
async function mp4uploadExtractor(html, url = null) {
|
||||
// src: "https://a4.mp4upload.com:183/d/xkx3b4etz3b4quuo66rbmyqtjjoivahfxp27f35pti45rzapbvj5xwb4wuqtlpewdz4dirfp/video.mp4"
|
||||
const regex = /src:\s*"([^"]+)"/;
|
||||
const match = html.match(regex);
|
||||
if (match) {
|
||||
@@ -1188,6 +1280,32 @@ async function mp4uploadExtractor(html, url = 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 --- */
|
||||
|
||||
/**
|
||||
@@ -1223,6 +1341,29 @@ async function sibnetExtractor(html, embedUrl) {
|
||||
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 --- */
|
||||
|
||||
/**
|
||||
@@ -1306,26 +1447,6 @@ async function streamupExtractor(data, url = 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 --- */
|
||||
|
||||
/**
|
||||
@@ -1390,7 +1511,7 @@ async function vidmolyExtractor(html, url = null) {
|
||||
const streamUrl = iframeMatch[1].startsWith("//")
|
||||
? "https:" + iframeMatch[1]
|
||||
: iframeMatch[1];
|
||||
const responseTwo = await fetchv2(streamUrl);
|
||||
const responseTwo = await soraFetch(streamUrl);
|
||||
const htmlTwo = await responseTwo.text();
|
||||
const m3u8Match = htmlTwo.match(/sources:\s*\[\{file:"([^"]+\.m3u8)"/);
|
||||
return m3u8Match ? m3u8Match[1] : null;
|
||||
@@ -1516,6 +1637,7 @@ function voeShiftChars(str, shift) {
|
||||
.join("");
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////
|
||||
// PLUGINS //
|
||||
////////////////////////////////////////////////
|
||||
@@ -1533,17 +1655,25 @@ function voeShiftChars(str, shift) {
|
||||
* @returns {Promise<Response|null>} The response from the server, or null if the
|
||||
* request failed.
|
||||
*/
|
||||
async function soraFetch(url, options = { headers: {}, method: 'GET', body: null }) {
|
||||
async function soraFetch(
|
||||
url,
|
||||
options = { headers: {}, method: "GET", body: null }
|
||||
) {
|
||||
try {
|
||||
return await fetchv2(
|
||||
url,
|
||||
options.headers ?? {},
|
||||
options.method ?? "GET",
|
||||
options.body ?? null
|
||||
);
|
||||
} catch (e) {
|
||||
try {
|
||||
return await fetchv2(url, options.headers ?? {}, options.method ?? 'GET', options.body ?? null);
|
||||
} catch(e) {
|
||||
try {
|
||||
return await fetch(url, options);
|
||||
} catch(error) {
|
||||
await console.log('soraFetch error: ' + error.message);
|
||||
return null;
|
||||
}
|
||||
return await fetch(url, options);
|
||||
} catch (error) {
|
||||
await console.log("soraFetch error: " + error.message);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
/***********************************************************
|
||||
* UNPACKER MODULE
|
||||
@@ -1647,6 +1777,5 @@ function unpack(source) {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* {GE END} */
|
||||
/* {GE END} */
|
||||
|
||||
+235
-123
@@ -147,7 +147,7 @@ function cleanHtmlSymbols(string) {
|
||||
// EDITING THIS FILE COULD BREAK THE UPDATER AND CAUSE ISSUES WITH THE EXTRACTOR
|
||||
|
||||
/* {GE START} */
|
||||
/* {VERSION: 1.1.8} */
|
||||
/* {VERSION: 1.2.0} */
|
||||
|
||||
/**
|
||||
* @name global_extractor.js
|
||||
@@ -155,8 +155,8 @@ function cleanHtmlSymbols(string) {
|
||||
* @author Cufiy
|
||||
* @url https://github.com/JMcrafter26/sora-global-extractor
|
||||
* @license CUSTOM LICENSE - see https://github.com/JMcrafter26/sora-global-extractor/blob/main/LICENSE
|
||||
* @date 2025-11-05 15:44:57
|
||||
* @version 1.1.8
|
||||
* @date 2026-01-03 19:28:28
|
||||
* @version 1.2.0
|
||||
* @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
|
||||
*/
|
||||
@@ -166,12 +166,20 @@ function globalExtractor(providers) {
|
||||
for (const [url, provider] of Object.entries(providers)) {
|
||||
try {
|
||||
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
|
||||
if (streamUrl && typeof streamUrl === "string" && (streamUrl.startsWith("http"))) {
|
||||
if (
|
||||
streamUrl &&
|
||||
typeof streamUrl === "string" &&
|
||||
streamUrl.startsWith("http")
|
||||
) {
|
||||
return streamUrl;
|
||||
// if its an array, get the value that starts with http
|
||||
} else if (Array.isArray(streamUrl)) {
|
||||
const httpStream = streamUrl.find(url => url.startsWith("http"));
|
||||
const httpStream = streamUrl.find((url) => url.startsWith("http"));
|
||||
if (httpStream) {
|
||||
return httpStream;
|
||||
}
|
||||
@@ -179,7 +187,6 @@ function globalExtractor(providers) {
|
||||
// check if it's a valid stream URL
|
||||
return null;
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
// 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
|
||||
{
|
||||
"streams": [
|
||||
"FileMoon",
|
||||
"https://filemoon.example/stream1.m3u8",
|
||||
"StreamWish",
|
||||
"https://streamwish.example/stream2.m3u8",
|
||||
"Okru",
|
||||
"https://okru.example/stream3.m3u8",
|
||||
"MP4",
|
||||
"https://mp4upload.example/stream4.mp4",
|
||||
"Default",
|
||||
"https://default.example/stream5.m3u8"
|
||||
{
|
||||
"title": "FileMoon",
|
||||
"streamUrl": "https://filemoon.example/stream1.m3u8",
|
||||
},
|
||||
{
|
||||
"title": "StreamWish",
|
||||
"streamUrl": "https://streamwish.example/stream2.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.startsWith("direct-")) {
|
||||
const directName = provider.slice(7); // remove "direct-" prefix
|
||||
if (directName && directName.length > 0) {
|
||||
streams.push(directName, url);
|
||||
} else {
|
||||
streams.push("Direct", url); // fallback to "Direct" if no name is provided
|
||||
}
|
||||
const title = (directName && directName.length > 0) ? directName : "Direct";
|
||||
streams.push({
|
||||
title: title,
|
||||
streamUrl: url
|
||||
});
|
||||
continue; // skip to the next provider
|
||||
}
|
||||
if (provider.startsWith("direct")) {
|
||||
provider = provider.slice(7); // remove "direct-" prefix
|
||||
if (provider && provider.length > 0) {
|
||||
streams.push(provider, url);
|
||||
} else {
|
||||
streams.push("Direct", url); // fallback to "Direct" if no name is provided
|
||||
}
|
||||
const title = (provider && provider.length > 0) ? provider : "Direct";
|
||||
streams.push({
|
||||
title: title,
|
||||
streamUrl: url
|
||||
});
|
||||
continue; // skip to the next provider
|
||||
}
|
||||
|
||||
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`);
|
||||
continue;
|
||||
}
|
||||
let streamUrl = await extractStreamUrlByProvider(url, provider);
|
||||
|
||||
if (streamUrl && Array.isArray(streamUrl)) {
|
||||
const httpStream = streamUrl.find(url => url.startsWith("http"));
|
||||
let result = await extractStreamUrlByProvider(url, provider);
|
||||
let streamUrl = null;
|
||||
let headers = null;
|
||||
|
||||
// Check if result is an object with streamUrl and optional headers
|
||||
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) {
|
||||
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 (
|
||||
!streamUrl ||
|
||||
typeof streamUrl !== "string" ||
|
||||
@@ -264,22 +295,29 @@ async function multiExtractor(providers) {
|
||||
provider = customName;
|
||||
}
|
||||
|
||||
let title;
|
||||
if (providersCount[provider]) {
|
||||
providersCount[provider]++;
|
||||
streams.push(
|
||||
provider.charAt(0).toUpperCase() +
|
||||
title = provider.charAt(0).toUpperCase() +
|
||||
provider.slice(1) +
|
||||
"-" +
|
||||
(providersCount[provider] - 1), // add a number to the provider name
|
||||
streamUrl
|
||||
);
|
||||
(providersCount[provider] - 1); // add a number to the provider name
|
||||
} else {
|
||||
providersCount[provider] = 1;
|
||||
streams.push(
|
||||
provider.charAt(0).toUpperCase() + provider.slice(1),
|
||||
streamUrl
|
||||
);
|
||||
title = provider.charAt(0).toUpperCase() + provider.slice(1);
|
||||
}
|
||||
|
||||
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) {
|
||||
// Ignore the error and try the next provider
|
||||
}
|
||||
@@ -290,73 +328,98 @@ async function multiExtractor(providers) {
|
||||
async function extractStreamUrlByProvider(url, provider) {
|
||||
if (eval(`typeof ${provider}Extractor`) !== "function") {
|
||||
// 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;
|
||||
}
|
||||
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 = {
|
||||
"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",
|
||||
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8",
|
||||
"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-Language": "en-US,en;q=0.5",
|
||||
"Referer": url,
|
||||
"Connection": "keep-alive",
|
||||
"x-Requested-With": "XMLHttpRequest"
|
||||
"x-Requested-With": "XMLHttpRequest",
|
||||
};
|
||||
if(provider == 'bigwarp') {
|
||||
delete headers["User-Agent"];
|
||||
headers["x-requested-with"] = "XMLHttpRequest";
|
||||
} else if (provider == 'vk') {
|
||||
headers["encoding"] = "windows-1251"; // required
|
||||
} else if (provider == 'sibnet') {
|
||||
headers["encoding"] = "windows-1251"; // required
|
||||
} else if (provider == 'supervideo') {
|
||||
delete headers["User-Agent"];
|
||||
|
||||
switch (provider) {
|
||||
case "bigwarp":
|
||||
delete headers["User-Agent"];
|
||||
break;
|
||||
case "vk":
|
||||
case "sibnet":
|
||||
headers["encoding"] = "windows-1251"; // required
|
||||
break;
|
||||
case "supervideo":
|
||||
case "savefiles":
|
||||
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
|
||||
// and pass the response to the extractor function
|
||||
console.log("Fetching URL: " + url);
|
||||
const response = await soraFetch(url, {
|
||||
headers
|
||||
});
|
||||
headers,
|
||||
});
|
||||
|
||||
console.log("Response: " + response.status);
|
||||
let html = response.text ? await response.text() : response;
|
||||
// if title contains redirect, then get the redirect url
|
||||
const title = html.match(/<title>(.*?)<\/title>/);
|
||||
if (title && title[1].toLowerCase().includes("redirect")) {
|
||||
const redirectUrl = html.match(/<meta http-equiv="refresh" content="0;url=(.*?)"/);
|
||||
const redirectUrl2 = html.match(/window\.location\.href\s*=\s*["'](.*?)["']/);
|
||||
const redirectUrl3 = html.match(/window\.location\.replace\s*\(\s*["'](.*?)["']\s*\)/);
|
||||
if (redirectUrl) {
|
||||
console.log("Redirect URL: " + redirectUrl[1]);
|
||||
url = redirectUrl[1];
|
||||
html = await soraFetch(url, {
|
||||
headers
|
||||
});
|
||||
html = html.text ? await html.text() : html;
|
||||
|
||||
} 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");
|
||||
const matches = [
|
||||
/<meta http-equiv="refresh" content="0;url=(.*?)"/,
|
||||
/window\.location\.href\s*=\s*["'](.*?)["']/,
|
||||
/window\.location\.replace\s*\(\s*["'](.*?)["']\s*\)/,
|
||||
/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];
|
||||
headers['Referer'] = url;
|
||||
headers['Host'] = url.match(/https?:\/\/([^\/]+)/)[1];
|
||||
html = await soraFetch(url, {
|
||||
headers,
|
||||
}).then((res) => res.text());
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// console.log("HTML: " + html);
|
||||
switch (provider) {
|
||||
case "bigwarp":
|
||||
case "bigwarp":
|
||||
try {
|
||||
return await bigwarpExtractor(html, url);
|
||||
} catch (error) {
|
||||
@@ -405,6 +468,20 @@ async function extractStreamUrlByProvider(url, provider) {
|
||||
console.log("Error extracting stream URL from mp4upload:", error);
|
||||
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":
|
||||
try {
|
||||
return await sendvidExtractor(html, url);
|
||||
@@ -419,6 +496,13 @@ async function extractStreamUrlByProvider(url, provider) {
|
||||
console.log("Error extracting stream URL from sibnet:", error);
|
||||
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":
|
||||
try {
|
||||
return await streamtapeExtractor(html, url);
|
||||
@@ -433,13 +517,6 @@ async function extractStreamUrlByProvider(url, provider) {
|
||||
console.log("Error extracting stream URL from streamup:", error);
|
||||
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":
|
||||
try {
|
||||
return await uploadcxExtractor(html, url);
|
||||
@@ -488,7 +565,6 @@ async function extractStreamUrlByProvider(url, provider) {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////
|
||||
// EXTRACTORS //
|
||||
////////////////////////////////////////////////
|
||||
@@ -975,7 +1051,6 @@ async function megacloudExtractor(html, embedUrl) {
|
||||
* @author Cufiy
|
||||
*/
|
||||
async function mp4uploadExtractor(html, url = null) {
|
||||
// src: "https://a4.mp4upload.com:183/d/xkx3b4etz3b4quuo66rbmyqtjjoivahfxp27f35pti45rzapbvj5xwb4wuqtlpewdz4dirfp/video.mp4"
|
||||
const regex = /src:\s*"([^"]+)"/;
|
||||
const match = html.match(regex);
|
||||
if (match) {
|
||||
@@ -985,6 +1060,32 @@ async function mp4uploadExtractor(html, url = 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 --- */
|
||||
|
||||
/**
|
||||
@@ -1020,6 +1121,29 @@ async function sibnetExtractor(html, embedUrl) {
|
||||
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 --- */
|
||||
|
||||
/**
|
||||
@@ -1103,26 +1227,6 @@ async function streamupExtractor(data, url = 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 --- */
|
||||
|
||||
/**
|
||||
@@ -1187,7 +1291,7 @@ async function vidmolyExtractor(html, url = null) {
|
||||
const streamUrl = iframeMatch[1].startsWith("//")
|
||||
? "https:" + iframeMatch[1]
|
||||
: iframeMatch[1];
|
||||
const responseTwo = await fetchv2(streamUrl);
|
||||
const responseTwo = await soraFetch(streamUrl);
|
||||
const htmlTwo = await responseTwo.text();
|
||||
const m3u8Match = htmlTwo.match(/sources:\s*\[\{file:"([^"]+\.m3u8)"/);
|
||||
return m3u8Match ? m3u8Match[1] : null;
|
||||
@@ -1313,6 +1417,7 @@ function voeShiftChars(str, shift) {
|
||||
.join("");
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////
|
||||
// PLUGINS //
|
||||
////////////////////////////////////////////////
|
||||
@@ -1330,17 +1435,25 @@ function voeShiftChars(str, shift) {
|
||||
* @returns {Promise<Response|null>} The response from the server, or null if the
|
||||
* request failed.
|
||||
*/
|
||||
async function soraFetch(url, options = { headers: {}, method: 'GET', body: null }) {
|
||||
async function soraFetch(
|
||||
url,
|
||||
options = { headers: {}, method: "GET", body: null }
|
||||
) {
|
||||
try {
|
||||
return await fetchv2(
|
||||
url,
|
||||
options.headers ?? {},
|
||||
options.method ?? "GET",
|
||||
options.body ?? null
|
||||
);
|
||||
} catch (e) {
|
||||
try {
|
||||
return await fetchv2(url, options.headers ?? {}, options.method ?? 'GET', options.body ?? null);
|
||||
} catch(e) {
|
||||
try {
|
||||
return await fetch(url, options);
|
||||
} catch(error) {
|
||||
await console.log('soraFetch error: ' + error.message);
|
||||
return null;
|
||||
}
|
||||
return await fetch(url, options);
|
||||
} catch (error) {
|
||||
await console.log("soraFetch error: " + error.message);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
/***********************************************************
|
||||
* UNPACKER MODULE
|
||||
@@ -1444,6 +1557,5 @@ function unpack(source) {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* {GE END} */
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
"name": "50/50",
|
||||
"icon": "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQ3122kQwublLkZ6rf1fEpUP79BxZOFmH9BSA&s"
|
||||
},
|
||||
"version": "1.0.2",
|
||||
"version": "1.0.3",
|
||||
"language": "Hindi",
|
||||
"streamType": "HLS",
|
||||
"quality": "1080p",
|
||||
@@ -19,4 +19,4 @@
|
||||
"supportsMojuru": true,
|
||||
"supportsSora": true,
|
||||
"supportsLuna": true
|
||||
}
|
||||
}
|
||||
@@ -5,7 +5,7 @@
|
||||
"name": "50/50 & Cufiy",
|
||||
"icon": "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQ3122kQwublLkZ6rf1fEpUP79BxZOFmH9BSA&s"
|
||||
},
|
||||
"version": "1.1.3",
|
||||
"version": "1.1.4",
|
||||
"language": "German (SUB)",
|
||||
"streamType": "HLS",
|
||||
"quality": "1080p",
|
||||
@@ -17,4 +17,4 @@
|
||||
"supportsMojuru": true,
|
||||
"supportsSora": true,
|
||||
"supportsLuna": true
|
||||
}
|
||||
}
|
||||
@@ -5,7 +5,7 @@
|
||||
"name": "50/50 & Cufiy",
|
||||
"icon": "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQ3122kQwublLkZ6rf1fEpUP79BxZOFmH9BSA&s"
|
||||
},
|
||||
"version": "1.1.3",
|
||||
"version": "1.1.4",
|
||||
"language": "German (DUB)",
|
||||
"streamType": "HLS",
|
||||
"quality": "1080p",
|
||||
@@ -17,4 +17,4 @@
|
||||
"supportsMojuru": true,
|
||||
"supportsSora": true,
|
||||
"supportsLuna": true
|
||||
}
|
||||
}
|
||||
@@ -5,7 +5,7 @@
|
||||
"name": "50/50 & Cufiy",
|
||||
"icon": "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQ3122kQwublLkZ6rf1fEpUP79BxZOFmH9BSA&s"
|
||||
},
|
||||
"version": "1.1.3",
|
||||
"version": "1.1.4",
|
||||
"language": "English (SUB)",
|
||||
"streamType": "HLS",
|
||||
"quality": "1080p",
|
||||
@@ -17,4 +17,4 @@
|
||||
"supportsMojuru": true,
|
||||
"supportsSora": true,
|
||||
"supportsLuna": true
|
||||
}
|
||||
}
|
||||
+235
-123
@@ -190,7 +190,7 @@ async function sendLog(message) {
|
||||
// EDITING THIS FILE COULD BREAK THE UPDATER AND CAUSE ISSUES WITH THE EXTRACTOR
|
||||
|
||||
/* {GE START} */
|
||||
/* {VERSION: 1.1.8} */
|
||||
/* {VERSION: 1.2.0} */
|
||||
|
||||
/**
|
||||
* @name global_extractor.js
|
||||
@@ -198,8 +198,8 @@ async function sendLog(message) {
|
||||
* @author Cufiy
|
||||
* @url https://github.com/JMcrafter26/sora-global-extractor
|
||||
* @license CUSTOM LICENSE - see https://github.com/JMcrafter26/sora-global-extractor/blob/main/LICENSE
|
||||
* @date 2025-11-05 15:44:57
|
||||
* @version 1.1.8
|
||||
* @date 2026-01-03 19:28:28
|
||||
* @version 1.2.0
|
||||
* @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
|
||||
*/
|
||||
@@ -209,12 +209,20 @@ function globalExtractor(providers) {
|
||||
for (const [url, provider] of Object.entries(providers)) {
|
||||
try {
|
||||
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
|
||||
if (streamUrl && typeof streamUrl === "string" && (streamUrl.startsWith("http"))) {
|
||||
if (
|
||||
streamUrl &&
|
||||
typeof streamUrl === "string" &&
|
||||
streamUrl.startsWith("http")
|
||||
) {
|
||||
return streamUrl;
|
||||
// if its an array, get the value that starts with http
|
||||
} else if (Array.isArray(streamUrl)) {
|
||||
const httpStream = streamUrl.find(url => url.startsWith("http"));
|
||||
const httpStream = streamUrl.find((url) => url.startsWith("http"));
|
||||
if (httpStream) {
|
||||
return httpStream;
|
||||
}
|
||||
@@ -222,7 +230,6 @@ function globalExtractor(providers) {
|
||||
// check if it's a valid stream URL
|
||||
return null;
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
// 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
|
||||
{
|
||||
"streams": [
|
||||
"FileMoon",
|
||||
"https://filemoon.example/stream1.m3u8",
|
||||
"StreamWish",
|
||||
"https://streamwish.example/stream2.m3u8",
|
||||
"Okru",
|
||||
"https://okru.example/stream3.m3u8",
|
||||
"MP4",
|
||||
"https://mp4upload.example/stream4.mp4",
|
||||
"Default",
|
||||
"https://default.example/stream5.m3u8"
|
||||
{
|
||||
"title": "FileMoon",
|
||||
"streamUrl": "https://filemoon.example/stream1.m3u8",
|
||||
},
|
||||
{
|
||||
"title": "StreamWish",
|
||||
"streamUrl": "https://streamwish.example/stream2.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.startsWith("direct-")) {
|
||||
const directName = provider.slice(7); // remove "direct-" prefix
|
||||
if (directName && directName.length > 0) {
|
||||
streams.push(directName, url);
|
||||
} else {
|
||||
streams.push("Direct", url); // fallback to "Direct" if no name is provided
|
||||
}
|
||||
const title = (directName && directName.length > 0) ? directName : "Direct";
|
||||
streams.push({
|
||||
title: title,
|
||||
streamUrl: url
|
||||
});
|
||||
continue; // skip to the next provider
|
||||
}
|
||||
if (provider.startsWith("direct")) {
|
||||
provider = provider.slice(7); // remove "direct-" prefix
|
||||
if (provider && provider.length > 0) {
|
||||
streams.push(provider, url);
|
||||
} else {
|
||||
streams.push("Direct", url); // fallback to "Direct" if no name is provided
|
||||
}
|
||||
const title = (provider && provider.length > 0) ? provider : "Direct";
|
||||
streams.push({
|
||||
title: title,
|
||||
streamUrl: url
|
||||
});
|
||||
continue; // skip to the next provider
|
||||
}
|
||||
|
||||
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`);
|
||||
continue;
|
||||
}
|
||||
let streamUrl = await extractStreamUrlByProvider(url, provider);
|
||||
|
||||
if (streamUrl && Array.isArray(streamUrl)) {
|
||||
const httpStream = streamUrl.find(url => url.startsWith("http"));
|
||||
let result = await extractStreamUrlByProvider(url, provider);
|
||||
let streamUrl = null;
|
||||
let headers = null;
|
||||
|
||||
// Check if result is an object with streamUrl and optional headers
|
||||
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) {
|
||||
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 (
|
||||
!streamUrl ||
|
||||
typeof streamUrl !== "string" ||
|
||||
@@ -307,22 +338,29 @@ async function multiExtractor(providers) {
|
||||
provider = customName;
|
||||
}
|
||||
|
||||
let title;
|
||||
if (providersCount[provider]) {
|
||||
providersCount[provider]++;
|
||||
streams.push(
|
||||
provider.charAt(0).toUpperCase() +
|
||||
title = provider.charAt(0).toUpperCase() +
|
||||
provider.slice(1) +
|
||||
"-" +
|
||||
(providersCount[provider] - 1), // add a number to the provider name
|
||||
streamUrl
|
||||
);
|
||||
(providersCount[provider] - 1); // add a number to the provider name
|
||||
} else {
|
||||
providersCount[provider] = 1;
|
||||
streams.push(
|
||||
provider.charAt(0).toUpperCase() + provider.slice(1),
|
||||
streamUrl
|
||||
);
|
||||
title = provider.charAt(0).toUpperCase() + provider.slice(1);
|
||||
}
|
||||
|
||||
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) {
|
||||
// Ignore the error and try the next provider
|
||||
}
|
||||
@@ -333,73 +371,98 @@ async function multiExtractor(providers) {
|
||||
async function extractStreamUrlByProvider(url, provider) {
|
||||
if (eval(`typeof ${provider}Extractor`) !== "function") {
|
||||
// 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;
|
||||
}
|
||||
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 = {
|
||||
"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",
|
||||
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8",
|
||||
"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-Language": "en-US,en;q=0.5",
|
||||
"Referer": url,
|
||||
"Connection": "keep-alive",
|
||||
"x-Requested-With": "XMLHttpRequest"
|
||||
"x-Requested-With": "XMLHttpRequest",
|
||||
};
|
||||
if(provider == 'bigwarp') {
|
||||
delete headers["User-Agent"];
|
||||
headers["x-requested-with"] = "XMLHttpRequest";
|
||||
} else if (provider == 'vk') {
|
||||
headers["encoding"] = "windows-1251"; // required
|
||||
} else if (provider == 'sibnet') {
|
||||
headers["encoding"] = "windows-1251"; // required
|
||||
} else if (provider == 'supervideo') {
|
||||
delete headers["User-Agent"];
|
||||
|
||||
switch (provider) {
|
||||
case "bigwarp":
|
||||
delete headers["User-Agent"];
|
||||
break;
|
||||
case "vk":
|
||||
case "sibnet":
|
||||
headers["encoding"] = "windows-1251"; // required
|
||||
break;
|
||||
case "supervideo":
|
||||
case "savefiles":
|
||||
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
|
||||
// and pass the response to the extractor function
|
||||
console.log("Fetching URL: " + url);
|
||||
const response = await soraFetch(url, {
|
||||
headers
|
||||
});
|
||||
headers,
|
||||
});
|
||||
|
||||
console.log("Response: " + response.status);
|
||||
let html = response.text ? await response.text() : response;
|
||||
// if title contains redirect, then get the redirect url
|
||||
const title = html.match(/<title>(.*?)<\/title>/);
|
||||
if (title && title[1].toLowerCase().includes("redirect")) {
|
||||
const redirectUrl = html.match(/<meta http-equiv="refresh" content="0;url=(.*?)"/);
|
||||
const redirectUrl2 = html.match(/window\.location\.href\s*=\s*["'](.*?)["']/);
|
||||
const redirectUrl3 = html.match(/window\.location\.replace\s*\(\s*["'](.*?)["']\s*\)/);
|
||||
if (redirectUrl) {
|
||||
console.log("Redirect URL: " + redirectUrl[1]);
|
||||
url = redirectUrl[1];
|
||||
html = await soraFetch(url, {
|
||||
headers
|
||||
});
|
||||
html = html.text ? await html.text() : html;
|
||||
|
||||
} 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");
|
||||
const matches = [
|
||||
/<meta http-equiv="refresh" content="0;url=(.*?)"/,
|
||||
/window\.location\.href\s*=\s*["'](.*?)["']/,
|
||||
/window\.location\.replace\s*\(\s*["'](.*?)["']\s*\)/,
|
||||
/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];
|
||||
headers['Referer'] = url;
|
||||
headers['Host'] = url.match(/https?:\/\/([^\/]+)/)[1];
|
||||
html = await soraFetch(url, {
|
||||
headers,
|
||||
}).then((res) => res.text());
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// console.log("HTML: " + html);
|
||||
switch (provider) {
|
||||
case "bigwarp":
|
||||
case "bigwarp":
|
||||
try {
|
||||
return await bigwarpExtractor(html, url);
|
||||
} catch (error) {
|
||||
@@ -448,6 +511,20 @@ async function extractStreamUrlByProvider(url, provider) {
|
||||
console.log("Error extracting stream URL from mp4upload:", error);
|
||||
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":
|
||||
try {
|
||||
return await sendvidExtractor(html, url);
|
||||
@@ -462,6 +539,13 @@ async function extractStreamUrlByProvider(url, provider) {
|
||||
console.log("Error extracting stream URL from sibnet:", error);
|
||||
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":
|
||||
try {
|
||||
return await streamtapeExtractor(html, url);
|
||||
@@ -476,13 +560,6 @@ async function extractStreamUrlByProvider(url, provider) {
|
||||
console.log("Error extracting stream URL from streamup:", error);
|
||||
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":
|
||||
try {
|
||||
return await uploadcxExtractor(html, url);
|
||||
@@ -531,7 +608,6 @@ async function extractStreamUrlByProvider(url, provider) {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////
|
||||
// EXTRACTORS //
|
||||
////////////////////////////////////////////////
|
||||
@@ -1018,7 +1094,6 @@ async function megacloudExtractor(html, embedUrl) {
|
||||
* @author Cufiy
|
||||
*/
|
||||
async function mp4uploadExtractor(html, url = null) {
|
||||
// src: "https://a4.mp4upload.com:183/d/xkx3b4etz3b4quuo66rbmyqtjjoivahfxp27f35pti45rzapbvj5xwb4wuqtlpewdz4dirfp/video.mp4"
|
||||
const regex = /src:\s*"([^"]+)"/;
|
||||
const match = html.match(regex);
|
||||
if (match) {
|
||||
@@ -1028,6 +1103,32 @@ async function mp4uploadExtractor(html, url = 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 --- */
|
||||
|
||||
/**
|
||||
@@ -1063,6 +1164,29 @@ async function sibnetExtractor(html, embedUrl) {
|
||||
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 --- */
|
||||
|
||||
/**
|
||||
@@ -1146,26 +1270,6 @@ async function streamupExtractor(data, url = 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 --- */
|
||||
|
||||
/**
|
||||
@@ -1230,7 +1334,7 @@ async function vidmolyExtractor(html, url = null) {
|
||||
const streamUrl = iframeMatch[1].startsWith("//")
|
||||
? "https:" + iframeMatch[1]
|
||||
: iframeMatch[1];
|
||||
const responseTwo = await fetchv2(streamUrl);
|
||||
const responseTwo = await soraFetch(streamUrl);
|
||||
const htmlTwo = await responseTwo.text();
|
||||
const m3u8Match = htmlTwo.match(/sources:\s*\[\{file:"([^"]+\.m3u8)"/);
|
||||
return m3u8Match ? m3u8Match[1] : null;
|
||||
@@ -1356,6 +1460,7 @@ function voeShiftChars(str, shift) {
|
||||
.join("");
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////
|
||||
// PLUGINS //
|
||||
////////////////////////////////////////////////
|
||||
@@ -1373,17 +1478,25 @@ function voeShiftChars(str, shift) {
|
||||
* @returns {Promise<Response|null>} The response from the server, or null if the
|
||||
* request failed.
|
||||
*/
|
||||
async function soraFetch(url, options = { headers: {}, method: 'GET', body: null }) {
|
||||
async function soraFetch(
|
||||
url,
|
||||
options = { headers: {}, method: "GET", body: null }
|
||||
) {
|
||||
try {
|
||||
return await fetchv2(
|
||||
url,
|
||||
options.headers ?? {},
|
||||
options.method ?? "GET",
|
||||
options.body ?? null
|
||||
);
|
||||
} catch (e) {
|
||||
try {
|
||||
return await fetchv2(url, options.headers ?? {}, options.method ?? 'GET', options.body ?? null);
|
||||
} catch(e) {
|
||||
try {
|
||||
return await fetch(url, options);
|
||||
} catch(error) {
|
||||
await console.log('soraFetch error: ' + error.message);
|
||||
return null;
|
||||
}
|
||||
return await fetch(url, options);
|
||||
} catch (error) {
|
||||
await console.log("soraFetch error: " + error.message);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
/***********************************************************
|
||||
* UNPACKER MODULE
|
||||
@@ -1487,6 +1600,5 @@ function unpack(source) {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* {GE END} */
|
||||
|
||||
+235
-123
@@ -191,7 +191,7 @@ async function sendLog(message) {
|
||||
// EDITING THIS FILE COULD BREAK THE UPDATER AND CAUSE ISSUES WITH THE EXTRACTOR
|
||||
|
||||
/* {GE START} */
|
||||
/* {VERSION: 1.1.8} */
|
||||
/* {VERSION: 1.2.0} */
|
||||
|
||||
/**
|
||||
* @name global_extractor.js
|
||||
@@ -199,8 +199,8 @@ async function sendLog(message) {
|
||||
* @author Cufiy
|
||||
* @url https://github.com/JMcrafter26/sora-global-extractor
|
||||
* @license CUSTOM LICENSE - see https://github.com/JMcrafter26/sora-global-extractor/blob/main/LICENSE
|
||||
* @date 2025-11-05 15:44:57
|
||||
* @version 1.1.8
|
||||
* @date 2026-01-03 19:28:28
|
||||
* @version 1.2.0
|
||||
* @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
|
||||
*/
|
||||
@@ -210,12 +210,20 @@ function globalExtractor(providers) {
|
||||
for (const [url, provider] of Object.entries(providers)) {
|
||||
try {
|
||||
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
|
||||
if (streamUrl && typeof streamUrl === "string" && (streamUrl.startsWith("http"))) {
|
||||
if (
|
||||
streamUrl &&
|
||||
typeof streamUrl === "string" &&
|
||||
streamUrl.startsWith("http")
|
||||
) {
|
||||
return streamUrl;
|
||||
// if its an array, get the value that starts with http
|
||||
} else if (Array.isArray(streamUrl)) {
|
||||
const httpStream = streamUrl.find(url => url.startsWith("http"));
|
||||
const httpStream = streamUrl.find((url) => url.startsWith("http"));
|
||||
if (httpStream) {
|
||||
return httpStream;
|
||||
}
|
||||
@@ -223,7 +231,6 @@ function globalExtractor(providers) {
|
||||
// check if it's a valid stream URL
|
||||
return null;
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
// 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
|
||||
{
|
||||
"streams": [
|
||||
"FileMoon",
|
||||
"https://filemoon.example/stream1.m3u8",
|
||||
"StreamWish",
|
||||
"https://streamwish.example/stream2.m3u8",
|
||||
"Okru",
|
||||
"https://okru.example/stream3.m3u8",
|
||||
"MP4",
|
||||
"https://mp4upload.example/stream4.mp4",
|
||||
"Default",
|
||||
"https://default.example/stream5.m3u8"
|
||||
{
|
||||
"title": "FileMoon",
|
||||
"streamUrl": "https://filemoon.example/stream1.m3u8",
|
||||
},
|
||||
{
|
||||
"title": "StreamWish",
|
||||
"streamUrl": "https://streamwish.example/stream2.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.startsWith("direct-")) {
|
||||
const directName = provider.slice(7); // remove "direct-" prefix
|
||||
if (directName && directName.length > 0) {
|
||||
streams.push(directName, url);
|
||||
} else {
|
||||
streams.push("Direct", url); // fallback to "Direct" if no name is provided
|
||||
}
|
||||
const title = (directName && directName.length > 0) ? directName : "Direct";
|
||||
streams.push({
|
||||
title: title,
|
||||
streamUrl: url
|
||||
});
|
||||
continue; // skip to the next provider
|
||||
}
|
||||
if (provider.startsWith("direct")) {
|
||||
provider = provider.slice(7); // remove "direct-" prefix
|
||||
if (provider && provider.length > 0) {
|
||||
streams.push(provider, url);
|
||||
} else {
|
||||
streams.push("Direct", url); // fallback to "Direct" if no name is provided
|
||||
}
|
||||
const title = (provider && provider.length > 0) ? provider : "Direct";
|
||||
streams.push({
|
||||
title: title,
|
||||
streamUrl: url
|
||||
});
|
||||
continue; // skip to the next provider
|
||||
}
|
||||
|
||||
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`);
|
||||
continue;
|
||||
}
|
||||
let streamUrl = await extractStreamUrlByProvider(url, provider);
|
||||
|
||||
if (streamUrl && Array.isArray(streamUrl)) {
|
||||
const httpStream = streamUrl.find(url => url.startsWith("http"));
|
||||
let result = await extractStreamUrlByProvider(url, provider);
|
||||
let streamUrl = null;
|
||||
let headers = null;
|
||||
|
||||
// Check if result is an object with streamUrl and optional headers
|
||||
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) {
|
||||
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 (
|
||||
!streamUrl ||
|
||||
typeof streamUrl !== "string" ||
|
||||
@@ -308,22 +339,29 @@ async function multiExtractor(providers) {
|
||||
provider = customName;
|
||||
}
|
||||
|
||||
let title;
|
||||
if (providersCount[provider]) {
|
||||
providersCount[provider]++;
|
||||
streams.push(
|
||||
provider.charAt(0).toUpperCase() +
|
||||
title = provider.charAt(0).toUpperCase() +
|
||||
provider.slice(1) +
|
||||
"-" +
|
||||
(providersCount[provider] - 1), // add a number to the provider name
|
||||
streamUrl
|
||||
);
|
||||
(providersCount[provider] - 1); // add a number to the provider name
|
||||
} else {
|
||||
providersCount[provider] = 1;
|
||||
streams.push(
|
||||
provider.charAt(0).toUpperCase() + provider.slice(1),
|
||||
streamUrl
|
||||
);
|
||||
title = provider.charAt(0).toUpperCase() + provider.slice(1);
|
||||
}
|
||||
|
||||
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) {
|
||||
// Ignore the error and try the next provider
|
||||
}
|
||||
@@ -334,73 +372,98 @@ async function multiExtractor(providers) {
|
||||
async function extractStreamUrlByProvider(url, provider) {
|
||||
if (eval(`typeof ${provider}Extractor`) !== "function") {
|
||||
// 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;
|
||||
}
|
||||
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 = {
|
||||
"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",
|
||||
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8",
|
||||
"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-Language": "en-US,en;q=0.5",
|
||||
"Referer": url,
|
||||
"Connection": "keep-alive",
|
||||
"x-Requested-With": "XMLHttpRequest"
|
||||
"x-Requested-With": "XMLHttpRequest",
|
||||
};
|
||||
if(provider == 'bigwarp') {
|
||||
delete headers["User-Agent"];
|
||||
headers["x-requested-with"] = "XMLHttpRequest";
|
||||
} else if (provider == 'vk') {
|
||||
headers["encoding"] = "windows-1251"; // required
|
||||
} else if (provider == 'sibnet') {
|
||||
headers["encoding"] = "windows-1251"; // required
|
||||
} else if (provider == 'supervideo') {
|
||||
delete headers["User-Agent"];
|
||||
|
||||
switch (provider) {
|
||||
case "bigwarp":
|
||||
delete headers["User-Agent"];
|
||||
break;
|
||||
case "vk":
|
||||
case "sibnet":
|
||||
headers["encoding"] = "windows-1251"; // required
|
||||
break;
|
||||
case "supervideo":
|
||||
case "savefiles":
|
||||
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
|
||||
// and pass the response to the extractor function
|
||||
console.log("Fetching URL: " + url);
|
||||
const response = await soraFetch(url, {
|
||||
headers
|
||||
});
|
||||
headers,
|
||||
});
|
||||
|
||||
console.log("Response: " + response.status);
|
||||
let html = response.text ? await response.text() : response;
|
||||
// if title contains redirect, then get the redirect url
|
||||
const title = html.match(/<title>(.*?)<\/title>/);
|
||||
if (title && title[1].toLowerCase().includes("redirect")) {
|
||||
const redirectUrl = html.match(/<meta http-equiv="refresh" content="0;url=(.*?)"/);
|
||||
const redirectUrl2 = html.match(/window\.location\.href\s*=\s*["'](.*?)["']/);
|
||||
const redirectUrl3 = html.match(/window\.location\.replace\s*\(\s*["'](.*?)["']\s*\)/);
|
||||
if (redirectUrl) {
|
||||
console.log("Redirect URL: " + redirectUrl[1]);
|
||||
url = redirectUrl[1];
|
||||
html = await soraFetch(url, {
|
||||
headers
|
||||
});
|
||||
html = html.text ? await html.text() : html;
|
||||
|
||||
} 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");
|
||||
const matches = [
|
||||
/<meta http-equiv="refresh" content="0;url=(.*?)"/,
|
||||
/window\.location\.href\s*=\s*["'](.*?)["']/,
|
||||
/window\.location\.replace\s*\(\s*["'](.*?)["']\s*\)/,
|
||||
/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];
|
||||
headers['Referer'] = url;
|
||||
headers['Host'] = url.match(/https?:\/\/([^\/]+)/)[1];
|
||||
html = await soraFetch(url, {
|
||||
headers,
|
||||
}).then((res) => res.text());
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// console.log("HTML: " + html);
|
||||
switch (provider) {
|
||||
case "bigwarp":
|
||||
case "bigwarp":
|
||||
try {
|
||||
return await bigwarpExtractor(html, url);
|
||||
} catch (error) {
|
||||
@@ -449,6 +512,20 @@ async function extractStreamUrlByProvider(url, provider) {
|
||||
console.log("Error extracting stream URL from mp4upload:", error);
|
||||
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":
|
||||
try {
|
||||
return await sendvidExtractor(html, url);
|
||||
@@ -463,6 +540,13 @@ async function extractStreamUrlByProvider(url, provider) {
|
||||
console.log("Error extracting stream URL from sibnet:", error);
|
||||
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":
|
||||
try {
|
||||
return await streamtapeExtractor(html, url);
|
||||
@@ -477,13 +561,6 @@ async function extractStreamUrlByProvider(url, provider) {
|
||||
console.log("Error extracting stream URL from streamup:", error);
|
||||
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":
|
||||
try {
|
||||
return await uploadcxExtractor(html, url);
|
||||
@@ -532,7 +609,6 @@ async function extractStreamUrlByProvider(url, provider) {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////
|
||||
// EXTRACTORS //
|
||||
////////////////////////////////////////////////
|
||||
@@ -1019,7 +1095,6 @@ async function megacloudExtractor(html, embedUrl) {
|
||||
* @author Cufiy
|
||||
*/
|
||||
async function mp4uploadExtractor(html, url = null) {
|
||||
// src: "https://a4.mp4upload.com:183/d/xkx3b4etz3b4quuo66rbmyqtjjoivahfxp27f35pti45rzapbvj5xwb4wuqtlpewdz4dirfp/video.mp4"
|
||||
const regex = /src:\s*"([^"]+)"/;
|
||||
const match = html.match(regex);
|
||||
if (match) {
|
||||
@@ -1029,6 +1104,32 @@ async function mp4uploadExtractor(html, url = 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 --- */
|
||||
|
||||
/**
|
||||
@@ -1064,6 +1165,29 @@ async function sibnetExtractor(html, embedUrl) {
|
||||
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 --- */
|
||||
|
||||
/**
|
||||
@@ -1147,26 +1271,6 @@ async function streamupExtractor(data, url = 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 --- */
|
||||
|
||||
/**
|
||||
@@ -1231,7 +1335,7 @@ async function vidmolyExtractor(html, url = null) {
|
||||
const streamUrl = iframeMatch[1].startsWith("//")
|
||||
? "https:" + iframeMatch[1]
|
||||
: iframeMatch[1];
|
||||
const responseTwo = await fetchv2(streamUrl);
|
||||
const responseTwo = await soraFetch(streamUrl);
|
||||
const htmlTwo = await responseTwo.text();
|
||||
const m3u8Match = htmlTwo.match(/sources:\s*\[\{file:"([^"]+\.m3u8)"/);
|
||||
return m3u8Match ? m3u8Match[1] : null;
|
||||
@@ -1357,6 +1461,7 @@ function voeShiftChars(str, shift) {
|
||||
.join("");
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////
|
||||
// PLUGINS //
|
||||
////////////////////////////////////////////////
|
||||
@@ -1374,17 +1479,25 @@ function voeShiftChars(str, shift) {
|
||||
* @returns {Promise<Response|null>} The response from the server, or null if the
|
||||
* request failed.
|
||||
*/
|
||||
async function soraFetch(url, options = { headers: {}, method: 'GET', body: null }) {
|
||||
async function soraFetch(
|
||||
url,
|
||||
options = { headers: {}, method: "GET", body: null }
|
||||
) {
|
||||
try {
|
||||
return await fetchv2(
|
||||
url,
|
||||
options.headers ?? {},
|
||||
options.method ?? "GET",
|
||||
options.body ?? null
|
||||
);
|
||||
} catch (e) {
|
||||
try {
|
||||
return await fetchv2(url, options.headers ?? {}, options.method ?? 'GET', options.body ?? null);
|
||||
} catch(e) {
|
||||
try {
|
||||
return await fetch(url, options);
|
||||
} catch(error) {
|
||||
await console.log('soraFetch error: ' + error.message);
|
||||
return null;
|
||||
}
|
||||
return await fetch(url, options);
|
||||
} catch (error) {
|
||||
await console.log("soraFetch error: " + error.message);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
/***********************************************************
|
||||
* UNPACKER MODULE
|
||||
@@ -1488,6 +1601,5 @@ function unpack(source) {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* {GE END} */
|
||||
+235
-123
@@ -190,7 +190,7 @@ async function sendLog(message) {
|
||||
// EDITING THIS FILE COULD BREAK THE UPDATER AND CAUSE ISSUES WITH THE EXTRACTOR
|
||||
|
||||
/* {GE START} */
|
||||
/* {VERSION: 1.1.8} */
|
||||
/* {VERSION: 1.2.0} */
|
||||
|
||||
/**
|
||||
* @name global_extractor.js
|
||||
@@ -198,8 +198,8 @@ async function sendLog(message) {
|
||||
* @author Cufiy
|
||||
* @url https://github.com/JMcrafter26/sora-global-extractor
|
||||
* @license CUSTOM LICENSE - see https://github.com/JMcrafter26/sora-global-extractor/blob/main/LICENSE
|
||||
* @date 2025-11-05 15:44:57
|
||||
* @version 1.1.8
|
||||
* @date 2026-01-03 19:28:28
|
||||
* @version 1.2.0
|
||||
* @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
|
||||
*/
|
||||
@@ -209,12 +209,20 @@ function globalExtractor(providers) {
|
||||
for (const [url, provider] of Object.entries(providers)) {
|
||||
try {
|
||||
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
|
||||
if (streamUrl && typeof streamUrl === "string" && (streamUrl.startsWith("http"))) {
|
||||
if (
|
||||
streamUrl &&
|
||||
typeof streamUrl === "string" &&
|
||||
streamUrl.startsWith("http")
|
||||
) {
|
||||
return streamUrl;
|
||||
// if its an array, get the value that starts with http
|
||||
} else if (Array.isArray(streamUrl)) {
|
||||
const httpStream = streamUrl.find(url => url.startsWith("http"));
|
||||
const httpStream = streamUrl.find((url) => url.startsWith("http"));
|
||||
if (httpStream) {
|
||||
return httpStream;
|
||||
}
|
||||
@@ -222,7 +230,6 @@ function globalExtractor(providers) {
|
||||
// check if it's a valid stream URL
|
||||
return null;
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
// 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
|
||||
{
|
||||
"streams": [
|
||||
"FileMoon",
|
||||
"https://filemoon.example/stream1.m3u8",
|
||||
"StreamWish",
|
||||
"https://streamwish.example/stream2.m3u8",
|
||||
"Okru",
|
||||
"https://okru.example/stream3.m3u8",
|
||||
"MP4",
|
||||
"https://mp4upload.example/stream4.mp4",
|
||||
"Default",
|
||||
"https://default.example/stream5.m3u8"
|
||||
{
|
||||
"title": "FileMoon",
|
||||
"streamUrl": "https://filemoon.example/stream1.m3u8",
|
||||
},
|
||||
{
|
||||
"title": "StreamWish",
|
||||
"streamUrl": "https://streamwish.example/stream2.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.startsWith("direct-")) {
|
||||
const directName = provider.slice(7); // remove "direct-" prefix
|
||||
if (directName && directName.length > 0) {
|
||||
streams.push(directName, url);
|
||||
} else {
|
||||
streams.push("Direct", url); // fallback to "Direct" if no name is provided
|
||||
}
|
||||
const title = (directName && directName.length > 0) ? directName : "Direct";
|
||||
streams.push({
|
||||
title: title,
|
||||
streamUrl: url
|
||||
});
|
||||
continue; // skip to the next provider
|
||||
}
|
||||
if (provider.startsWith("direct")) {
|
||||
provider = provider.slice(7); // remove "direct-" prefix
|
||||
if (provider && provider.length > 0) {
|
||||
streams.push(provider, url);
|
||||
} else {
|
||||
streams.push("Direct", url); // fallback to "Direct" if no name is provided
|
||||
}
|
||||
const title = (provider && provider.length > 0) ? provider : "Direct";
|
||||
streams.push({
|
||||
title: title,
|
||||
streamUrl: url
|
||||
});
|
||||
continue; // skip to the next provider
|
||||
}
|
||||
|
||||
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`);
|
||||
continue;
|
||||
}
|
||||
let streamUrl = await extractStreamUrlByProvider(url, provider);
|
||||
|
||||
if (streamUrl && Array.isArray(streamUrl)) {
|
||||
const httpStream = streamUrl.find(url => url.startsWith("http"));
|
||||
let result = await extractStreamUrlByProvider(url, provider);
|
||||
let streamUrl = null;
|
||||
let headers = null;
|
||||
|
||||
// Check if result is an object with streamUrl and optional headers
|
||||
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) {
|
||||
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 (
|
||||
!streamUrl ||
|
||||
typeof streamUrl !== "string" ||
|
||||
@@ -307,22 +338,29 @@ async function multiExtractor(providers) {
|
||||
provider = customName;
|
||||
}
|
||||
|
||||
let title;
|
||||
if (providersCount[provider]) {
|
||||
providersCount[provider]++;
|
||||
streams.push(
|
||||
provider.charAt(0).toUpperCase() +
|
||||
title = provider.charAt(0).toUpperCase() +
|
||||
provider.slice(1) +
|
||||
"-" +
|
||||
(providersCount[provider] - 1), // add a number to the provider name
|
||||
streamUrl
|
||||
);
|
||||
(providersCount[provider] - 1); // add a number to the provider name
|
||||
} else {
|
||||
providersCount[provider] = 1;
|
||||
streams.push(
|
||||
provider.charAt(0).toUpperCase() + provider.slice(1),
|
||||
streamUrl
|
||||
);
|
||||
title = provider.charAt(0).toUpperCase() + provider.slice(1);
|
||||
}
|
||||
|
||||
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) {
|
||||
// Ignore the error and try the next provider
|
||||
}
|
||||
@@ -333,73 +371,98 @@ async function multiExtractor(providers) {
|
||||
async function extractStreamUrlByProvider(url, provider) {
|
||||
if (eval(`typeof ${provider}Extractor`) !== "function") {
|
||||
// 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;
|
||||
}
|
||||
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 = {
|
||||
"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",
|
||||
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8",
|
||||
"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-Language": "en-US,en;q=0.5",
|
||||
"Referer": url,
|
||||
"Connection": "keep-alive",
|
||||
"x-Requested-With": "XMLHttpRequest"
|
||||
"x-Requested-With": "XMLHttpRequest",
|
||||
};
|
||||
if(provider == 'bigwarp') {
|
||||
delete headers["User-Agent"];
|
||||
headers["x-requested-with"] = "XMLHttpRequest";
|
||||
} else if (provider == 'vk') {
|
||||
headers["encoding"] = "windows-1251"; // required
|
||||
} else if (provider == 'sibnet') {
|
||||
headers["encoding"] = "windows-1251"; // required
|
||||
} else if (provider == 'supervideo') {
|
||||
delete headers["User-Agent"];
|
||||
|
||||
switch (provider) {
|
||||
case "bigwarp":
|
||||
delete headers["User-Agent"];
|
||||
break;
|
||||
case "vk":
|
||||
case "sibnet":
|
||||
headers["encoding"] = "windows-1251"; // required
|
||||
break;
|
||||
case "supervideo":
|
||||
case "savefiles":
|
||||
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
|
||||
// and pass the response to the extractor function
|
||||
console.log("Fetching URL: " + url);
|
||||
const response = await soraFetch(url, {
|
||||
headers
|
||||
});
|
||||
headers,
|
||||
});
|
||||
|
||||
console.log("Response: " + response.status);
|
||||
let html = response.text ? await response.text() : response;
|
||||
// if title contains redirect, then get the redirect url
|
||||
const title = html.match(/<title>(.*?)<\/title>/);
|
||||
if (title && title[1].toLowerCase().includes("redirect")) {
|
||||
const redirectUrl = html.match(/<meta http-equiv="refresh" content="0;url=(.*?)"/);
|
||||
const redirectUrl2 = html.match(/window\.location\.href\s*=\s*["'](.*?)["']/);
|
||||
const redirectUrl3 = html.match(/window\.location\.replace\s*\(\s*["'](.*?)["']\s*\)/);
|
||||
if (redirectUrl) {
|
||||
console.log("Redirect URL: " + redirectUrl[1]);
|
||||
url = redirectUrl[1];
|
||||
html = await soraFetch(url, {
|
||||
headers
|
||||
});
|
||||
html = html.text ? await html.text() : html;
|
||||
|
||||
} 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");
|
||||
const matches = [
|
||||
/<meta http-equiv="refresh" content="0;url=(.*?)"/,
|
||||
/window\.location\.href\s*=\s*["'](.*?)["']/,
|
||||
/window\.location\.replace\s*\(\s*["'](.*?)["']\s*\)/,
|
||||
/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];
|
||||
headers['Referer'] = url;
|
||||
headers['Host'] = url.match(/https?:\/\/([^\/]+)/)[1];
|
||||
html = await soraFetch(url, {
|
||||
headers,
|
||||
}).then((res) => res.text());
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// console.log("HTML: " + html);
|
||||
switch (provider) {
|
||||
case "bigwarp":
|
||||
case "bigwarp":
|
||||
try {
|
||||
return await bigwarpExtractor(html, url);
|
||||
} catch (error) {
|
||||
@@ -448,6 +511,20 @@ async function extractStreamUrlByProvider(url, provider) {
|
||||
console.log("Error extracting stream URL from mp4upload:", error);
|
||||
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":
|
||||
try {
|
||||
return await sendvidExtractor(html, url);
|
||||
@@ -462,6 +539,13 @@ async function extractStreamUrlByProvider(url, provider) {
|
||||
console.log("Error extracting stream URL from sibnet:", error);
|
||||
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":
|
||||
try {
|
||||
return await streamtapeExtractor(html, url);
|
||||
@@ -476,13 +560,6 @@ async function extractStreamUrlByProvider(url, provider) {
|
||||
console.log("Error extracting stream URL from streamup:", error);
|
||||
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":
|
||||
try {
|
||||
return await uploadcxExtractor(html, url);
|
||||
@@ -531,7 +608,6 @@ async function extractStreamUrlByProvider(url, provider) {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////
|
||||
// EXTRACTORS //
|
||||
////////////////////////////////////////////////
|
||||
@@ -1018,7 +1094,6 @@ async function megacloudExtractor(html, embedUrl) {
|
||||
* @author Cufiy
|
||||
*/
|
||||
async function mp4uploadExtractor(html, url = null) {
|
||||
// src: "https://a4.mp4upload.com:183/d/xkx3b4etz3b4quuo66rbmyqtjjoivahfxp27f35pti45rzapbvj5xwb4wuqtlpewdz4dirfp/video.mp4"
|
||||
const regex = /src:\s*"([^"]+)"/;
|
||||
const match = html.match(regex);
|
||||
if (match) {
|
||||
@@ -1028,6 +1103,32 @@ async function mp4uploadExtractor(html, url = 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 --- */
|
||||
|
||||
/**
|
||||
@@ -1063,6 +1164,29 @@ async function sibnetExtractor(html, embedUrl) {
|
||||
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 --- */
|
||||
|
||||
/**
|
||||
@@ -1146,26 +1270,6 @@ async function streamupExtractor(data, url = 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 --- */
|
||||
|
||||
/**
|
||||
@@ -1230,7 +1334,7 @@ async function vidmolyExtractor(html, url = null) {
|
||||
const streamUrl = iframeMatch[1].startsWith("//")
|
||||
? "https:" + iframeMatch[1]
|
||||
: iframeMatch[1];
|
||||
const responseTwo = await fetchv2(streamUrl);
|
||||
const responseTwo = await soraFetch(streamUrl);
|
||||
const htmlTwo = await responseTwo.text();
|
||||
const m3u8Match = htmlTwo.match(/sources:\s*\[\{file:"([^"]+\.m3u8)"/);
|
||||
return m3u8Match ? m3u8Match[1] : null;
|
||||
@@ -1356,6 +1460,7 @@ function voeShiftChars(str, shift) {
|
||||
.join("");
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////
|
||||
// PLUGINS //
|
||||
////////////////////////////////////////////////
|
||||
@@ -1373,17 +1478,25 @@ function voeShiftChars(str, shift) {
|
||||
* @returns {Promise<Response|null>} The response from the server, or null if the
|
||||
* request failed.
|
||||
*/
|
||||
async function soraFetch(url, options = { headers: {}, method: 'GET', body: null }) {
|
||||
async function soraFetch(
|
||||
url,
|
||||
options = { headers: {}, method: "GET", body: null }
|
||||
) {
|
||||
try {
|
||||
return await fetchv2(
|
||||
url,
|
||||
options.headers ?? {},
|
||||
options.method ?? "GET",
|
||||
options.body ?? null
|
||||
);
|
||||
} catch (e) {
|
||||
try {
|
||||
return await fetchv2(url, options.headers ?? {}, options.method ?? 'GET', options.body ?? null);
|
||||
} catch(e) {
|
||||
try {
|
||||
return await fetch(url, options);
|
||||
} catch(error) {
|
||||
await console.log('soraFetch error: ' + error.message);
|
||||
return null;
|
||||
}
|
||||
return await fetch(url, options);
|
||||
} catch (error) {
|
||||
await console.log("soraFetch error: " + error.message);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
/***********************************************************
|
||||
* UNPACKER MODULE
|
||||
@@ -1487,6 +1600,5 @@ function unpack(source) {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* {GE END} */
|
||||
+2
-2
@@ -5,7 +5,7 @@
|
||||
"name": "Cufiy",
|
||||
"icon": "https://files.catbox.moe/ttj4fc.gif"
|
||||
},
|
||||
"version": "0.3.16",
|
||||
"version": "0.3.17",
|
||||
"language": "English (DUB)",
|
||||
"streamType": "HLS",
|
||||
"quality": "720p",
|
||||
@@ -17,4 +17,4 @@
|
||||
"type": "shows",
|
||||
"supportsSora": true,
|
||||
"supportsLuna": true
|
||||
}
|
||||
}
|
||||
+235
-123
@@ -376,7 +376,7 @@ function base64Decode(str) {
|
||||
// EDITING THIS FILE COULD BREAK THE UPDATER AND CAUSE ISSUES WITH THE EXTRACTOR
|
||||
|
||||
/* {GE START} */
|
||||
/* {VERSION: 1.1.8} */
|
||||
/* {VERSION: 1.2.0} */
|
||||
|
||||
/**
|
||||
* @name global_extractor.js
|
||||
@@ -384,8 +384,8 @@ function base64Decode(str) {
|
||||
* @author Cufiy
|
||||
* @url https://github.com/JMcrafter26/sora-global-extractor
|
||||
* @license CUSTOM LICENSE - see https://github.com/JMcrafter26/sora-global-extractor/blob/main/LICENSE
|
||||
* @date 2025-11-05 15:44:57
|
||||
* @version 1.1.8
|
||||
* @date 2026-01-03 19:28:28
|
||||
* @version 1.2.0
|
||||
* @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
|
||||
*/
|
||||
@@ -395,12 +395,20 @@ function globalExtractor(providers) {
|
||||
for (const [url, provider] of Object.entries(providers)) {
|
||||
try {
|
||||
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
|
||||
if (streamUrl && typeof streamUrl === "string" && (streamUrl.startsWith("http"))) {
|
||||
if (
|
||||
streamUrl &&
|
||||
typeof streamUrl === "string" &&
|
||||
streamUrl.startsWith("http")
|
||||
) {
|
||||
return streamUrl;
|
||||
// if its an array, get the value that starts with http
|
||||
} else if (Array.isArray(streamUrl)) {
|
||||
const httpStream = streamUrl.find(url => url.startsWith("http"));
|
||||
const httpStream = streamUrl.find((url) => url.startsWith("http"));
|
||||
if (httpStream) {
|
||||
return httpStream;
|
||||
}
|
||||
@@ -408,7 +416,6 @@ function globalExtractor(providers) {
|
||||
// check if it's a valid stream URL
|
||||
return null;
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
// 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
|
||||
{
|
||||
"streams": [
|
||||
"FileMoon",
|
||||
"https://filemoon.example/stream1.m3u8",
|
||||
"StreamWish",
|
||||
"https://streamwish.example/stream2.m3u8",
|
||||
"Okru",
|
||||
"https://okru.example/stream3.m3u8",
|
||||
"MP4",
|
||||
"https://mp4upload.example/stream4.mp4",
|
||||
"Default",
|
||||
"https://default.example/stream5.m3u8"
|
||||
{
|
||||
"title": "FileMoon",
|
||||
"streamUrl": "https://filemoon.example/stream1.m3u8",
|
||||
},
|
||||
{
|
||||
"title": "StreamWish",
|
||||
"streamUrl": "https://streamwish.example/stream2.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.startsWith("direct-")) {
|
||||
const directName = provider.slice(7); // remove "direct-" prefix
|
||||
if (directName && directName.length > 0) {
|
||||
streams.push(directName, url);
|
||||
} else {
|
||||
streams.push("Direct", url); // fallback to "Direct" if no name is provided
|
||||
}
|
||||
const title = (directName && directName.length > 0) ? directName : "Direct";
|
||||
streams.push({
|
||||
title: title,
|
||||
streamUrl: url
|
||||
});
|
||||
continue; // skip to the next provider
|
||||
}
|
||||
if (provider.startsWith("direct")) {
|
||||
provider = provider.slice(7); // remove "direct-" prefix
|
||||
if (provider && provider.length > 0) {
|
||||
streams.push(provider, url);
|
||||
} else {
|
||||
streams.push("Direct", url); // fallback to "Direct" if no name is provided
|
||||
}
|
||||
const title = (provider && provider.length > 0) ? provider : "Direct";
|
||||
streams.push({
|
||||
title: title,
|
||||
streamUrl: url
|
||||
});
|
||||
continue; // skip to the next provider
|
||||
}
|
||||
|
||||
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`);
|
||||
continue;
|
||||
}
|
||||
let streamUrl = await extractStreamUrlByProvider(url, provider);
|
||||
|
||||
if (streamUrl && Array.isArray(streamUrl)) {
|
||||
const httpStream = streamUrl.find(url => url.startsWith("http"));
|
||||
let result = await extractStreamUrlByProvider(url, provider);
|
||||
let streamUrl = null;
|
||||
let headers = null;
|
||||
|
||||
// Check if result is an object with streamUrl and optional headers
|
||||
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) {
|
||||
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 (
|
||||
!streamUrl ||
|
||||
typeof streamUrl !== "string" ||
|
||||
@@ -493,22 +524,29 @@ async function multiExtractor(providers) {
|
||||
provider = customName;
|
||||
}
|
||||
|
||||
let title;
|
||||
if (providersCount[provider]) {
|
||||
providersCount[provider]++;
|
||||
streams.push(
|
||||
provider.charAt(0).toUpperCase() +
|
||||
title = provider.charAt(0).toUpperCase() +
|
||||
provider.slice(1) +
|
||||
"-" +
|
||||
(providersCount[provider] - 1), // add a number to the provider name
|
||||
streamUrl
|
||||
);
|
||||
(providersCount[provider] - 1); // add a number to the provider name
|
||||
} else {
|
||||
providersCount[provider] = 1;
|
||||
streams.push(
|
||||
provider.charAt(0).toUpperCase() + provider.slice(1),
|
||||
streamUrl
|
||||
);
|
||||
title = provider.charAt(0).toUpperCase() + provider.slice(1);
|
||||
}
|
||||
|
||||
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) {
|
||||
// Ignore the error and try the next provider
|
||||
}
|
||||
@@ -519,73 +557,98 @@ async function multiExtractor(providers) {
|
||||
async function extractStreamUrlByProvider(url, provider) {
|
||||
if (eval(`typeof ${provider}Extractor`) !== "function") {
|
||||
// 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;
|
||||
}
|
||||
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 = {
|
||||
"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",
|
||||
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8",
|
||||
"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-Language": "en-US,en;q=0.5",
|
||||
"Referer": url,
|
||||
"Connection": "keep-alive",
|
||||
"x-Requested-With": "XMLHttpRequest"
|
||||
"x-Requested-With": "XMLHttpRequest",
|
||||
};
|
||||
if(provider == 'bigwarp') {
|
||||
delete headers["User-Agent"];
|
||||
headers["x-requested-with"] = "XMLHttpRequest";
|
||||
} else if (provider == 'vk') {
|
||||
headers["encoding"] = "windows-1251"; // required
|
||||
} else if (provider == 'sibnet') {
|
||||
headers["encoding"] = "windows-1251"; // required
|
||||
} else if (provider == 'supervideo') {
|
||||
delete headers["User-Agent"];
|
||||
|
||||
switch (provider) {
|
||||
case "bigwarp":
|
||||
delete headers["User-Agent"];
|
||||
break;
|
||||
case "vk":
|
||||
case "sibnet":
|
||||
headers["encoding"] = "windows-1251"; // required
|
||||
break;
|
||||
case "supervideo":
|
||||
case "savefiles":
|
||||
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
|
||||
// and pass the response to the extractor function
|
||||
console.log("Fetching URL: " + url);
|
||||
const response = await soraFetch(url, {
|
||||
headers
|
||||
});
|
||||
headers,
|
||||
});
|
||||
|
||||
console.log("Response: " + response.status);
|
||||
let html = response.text ? await response.text() : response;
|
||||
// if title contains redirect, then get the redirect url
|
||||
const title = html.match(/<title>(.*?)<\/title>/);
|
||||
if (title && title[1].toLowerCase().includes("redirect")) {
|
||||
const redirectUrl = html.match(/<meta http-equiv="refresh" content="0;url=(.*?)"/);
|
||||
const redirectUrl2 = html.match(/window\.location\.href\s*=\s*["'](.*?)["']/);
|
||||
const redirectUrl3 = html.match(/window\.location\.replace\s*\(\s*["'](.*?)["']\s*\)/);
|
||||
if (redirectUrl) {
|
||||
console.log("Redirect URL: " + redirectUrl[1]);
|
||||
url = redirectUrl[1];
|
||||
html = await soraFetch(url, {
|
||||
headers
|
||||
});
|
||||
html = html.text ? await html.text() : html;
|
||||
|
||||
} 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");
|
||||
const matches = [
|
||||
/<meta http-equiv="refresh" content="0;url=(.*?)"/,
|
||||
/window\.location\.href\s*=\s*["'](.*?)["']/,
|
||||
/window\.location\.replace\s*\(\s*["'](.*?)["']\s*\)/,
|
||||
/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];
|
||||
headers['Referer'] = url;
|
||||
headers['Host'] = url.match(/https?:\/\/([^\/]+)/)[1];
|
||||
html = await soraFetch(url, {
|
||||
headers,
|
||||
}).then((res) => res.text());
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// console.log("HTML: " + html);
|
||||
switch (provider) {
|
||||
case "bigwarp":
|
||||
case "bigwarp":
|
||||
try {
|
||||
return await bigwarpExtractor(html, url);
|
||||
} catch (error) {
|
||||
@@ -634,6 +697,20 @@ async function extractStreamUrlByProvider(url, provider) {
|
||||
console.log("Error extracting stream URL from mp4upload:", error);
|
||||
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":
|
||||
try {
|
||||
return await sendvidExtractor(html, url);
|
||||
@@ -648,6 +725,13 @@ async function extractStreamUrlByProvider(url, provider) {
|
||||
console.log("Error extracting stream URL from sibnet:", error);
|
||||
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":
|
||||
try {
|
||||
return await streamtapeExtractor(html, url);
|
||||
@@ -662,13 +746,6 @@ async function extractStreamUrlByProvider(url, provider) {
|
||||
console.log("Error extracting stream URL from streamup:", error);
|
||||
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":
|
||||
try {
|
||||
return await uploadcxExtractor(html, url);
|
||||
@@ -717,7 +794,6 @@ async function extractStreamUrlByProvider(url, provider) {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////
|
||||
// EXTRACTORS //
|
||||
////////////////////////////////////////////////
|
||||
@@ -1204,7 +1280,6 @@ async function megacloudExtractor(html, embedUrl) {
|
||||
* @author Cufiy
|
||||
*/
|
||||
async function mp4uploadExtractor(html, url = null) {
|
||||
// src: "https://a4.mp4upload.com:183/d/xkx3b4etz3b4quuo66rbmyqtjjoivahfxp27f35pti45rzapbvj5xwb4wuqtlpewdz4dirfp/video.mp4"
|
||||
const regex = /src:\s*"([^"]+)"/;
|
||||
const match = html.match(regex);
|
||||
if (match) {
|
||||
@@ -1214,6 +1289,32 @@ async function mp4uploadExtractor(html, url = 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 --- */
|
||||
|
||||
/**
|
||||
@@ -1249,6 +1350,29 @@ async function sibnetExtractor(html, embedUrl) {
|
||||
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 --- */
|
||||
|
||||
/**
|
||||
@@ -1332,26 +1456,6 @@ async function streamupExtractor(data, url = 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 --- */
|
||||
|
||||
/**
|
||||
@@ -1416,7 +1520,7 @@ async function vidmolyExtractor(html, url = null) {
|
||||
const streamUrl = iframeMatch[1].startsWith("//")
|
||||
? "https:" + iframeMatch[1]
|
||||
: iframeMatch[1];
|
||||
const responseTwo = await fetchv2(streamUrl);
|
||||
const responseTwo = await soraFetch(streamUrl);
|
||||
const htmlTwo = await responseTwo.text();
|
||||
const m3u8Match = htmlTwo.match(/sources:\s*\[\{file:"([^"]+\.m3u8)"/);
|
||||
return m3u8Match ? m3u8Match[1] : null;
|
||||
@@ -1542,6 +1646,7 @@ function voeShiftChars(str, shift) {
|
||||
.join("");
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////
|
||||
// PLUGINS //
|
||||
////////////////////////////////////////////////
|
||||
@@ -1559,17 +1664,25 @@ function voeShiftChars(str, shift) {
|
||||
* @returns {Promise<Response|null>} The response from the server, or null if the
|
||||
* request failed.
|
||||
*/
|
||||
async function soraFetch(url, options = { headers: {}, method: 'GET', body: null }) {
|
||||
async function soraFetch(
|
||||
url,
|
||||
options = { headers: {}, method: "GET", body: null }
|
||||
) {
|
||||
try {
|
||||
return await fetchv2(
|
||||
url,
|
||||
options.headers ?? {},
|
||||
options.method ?? "GET",
|
||||
options.body ?? null
|
||||
);
|
||||
} catch (e) {
|
||||
try {
|
||||
return await fetchv2(url, options.headers ?? {}, options.method ?? 'GET', options.body ?? null);
|
||||
} catch(e) {
|
||||
try {
|
||||
return await fetch(url, options);
|
||||
} catch(error) {
|
||||
await console.log('soraFetch error: ' + error.message);
|
||||
return null;
|
||||
}
|
||||
return await fetch(url, options);
|
||||
} catch (error) {
|
||||
await console.log("soraFetch error: " + error.message);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
/***********************************************************
|
||||
* UNPACKER MODULE
|
||||
@@ -1673,7 +1786,6 @@ function unpack(source) {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* {GE END} */
|
||||
|
||||
|
||||
+2
-2
@@ -5,7 +5,7 @@
|
||||
"name": "Hamzo & Cufiy",
|
||||
"icon": "https://cdn.discordapp.com/avatars/623644371819954226/591ecab10b0b4535e859bb0b9bbe62e5?size=1024"
|
||||
},
|
||||
"version": "0.3.16",
|
||||
"version": "0.3.17",
|
||||
"language": "German (DUB)",
|
||||
"streamType": "HLS",
|
||||
"quality": "720p",
|
||||
@@ -17,4 +17,4 @@
|
||||
"type": "shows",
|
||||
"supportsSora": true,
|
||||
"supportsLuna": true
|
||||
}
|
||||
}
|
||||
+235
-123
@@ -376,7 +376,7 @@ function base64Decode(str) {
|
||||
// EDITING THIS FILE COULD BREAK THE UPDATER AND CAUSE ISSUES WITH THE EXTRACTOR
|
||||
|
||||
/* {GE START} */
|
||||
/* {VERSION: 1.1.8} */
|
||||
/* {VERSION: 1.2.0} */
|
||||
|
||||
/**
|
||||
* @name global_extractor.js
|
||||
@@ -384,8 +384,8 @@ function base64Decode(str) {
|
||||
* @author Cufiy
|
||||
* @url https://github.com/JMcrafter26/sora-global-extractor
|
||||
* @license CUSTOM LICENSE - see https://github.com/JMcrafter26/sora-global-extractor/blob/main/LICENSE
|
||||
* @date 2025-11-05 15:44:57
|
||||
* @version 1.1.8
|
||||
* @date 2026-01-03 19:28:28
|
||||
* @version 1.2.0
|
||||
* @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
|
||||
*/
|
||||
@@ -395,12 +395,20 @@ function globalExtractor(providers) {
|
||||
for (const [url, provider] of Object.entries(providers)) {
|
||||
try {
|
||||
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
|
||||
if (streamUrl && typeof streamUrl === "string" && (streamUrl.startsWith("http"))) {
|
||||
if (
|
||||
streamUrl &&
|
||||
typeof streamUrl === "string" &&
|
||||
streamUrl.startsWith("http")
|
||||
) {
|
||||
return streamUrl;
|
||||
// if its an array, get the value that starts with http
|
||||
} else if (Array.isArray(streamUrl)) {
|
||||
const httpStream = streamUrl.find(url => url.startsWith("http"));
|
||||
const httpStream = streamUrl.find((url) => url.startsWith("http"));
|
||||
if (httpStream) {
|
||||
return httpStream;
|
||||
}
|
||||
@@ -408,7 +416,6 @@ function globalExtractor(providers) {
|
||||
// check if it's a valid stream URL
|
||||
return null;
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
// 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
|
||||
{
|
||||
"streams": [
|
||||
"FileMoon",
|
||||
"https://filemoon.example/stream1.m3u8",
|
||||
"StreamWish",
|
||||
"https://streamwish.example/stream2.m3u8",
|
||||
"Okru",
|
||||
"https://okru.example/stream3.m3u8",
|
||||
"MP4",
|
||||
"https://mp4upload.example/stream4.mp4",
|
||||
"Default",
|
||||
"https://default.example/stream5.m3u8"
|
||||
{
|
||||
"title": "FileMoon",
|
||||
"streamUrl": "https://filemoon.example/stream1.m3u8",
|
||||
},
|
||||
{
|
||||
"title": "StreamWish",
|
||||
"streamUrl": "https://streamwish.example/stream2.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.startsWith("direct-")) {
|
||||
const directName = provider.slice(7); // remove "direct-" prefix
|
||||
if (directName && directName.length > 0) {
|
||||
streams.push(directName, url);
|
||||
} else {
|
||||
streams.push("Direct", url); // fallback to "Direct" if no name is provided
|
||||
}
|
||||
const title = (directName && directName.length > 0) ? directName : "Direct";
|
||||
streams.push({
|
||||
title: title,
|
||||
streamUrl: url
|
||||
});
|
||||
continue; // skip to the next provider
|
||||
}
|
||||
if (provider.startsWith("direct")) {
|
||||
provider = provider.slice(7); // remove "direct-" prefix
|
||||
if (provider && provider.length > 0) {
|
||||
streams.push(provider, url);
|
||||
} else {
|
||||
streams.push("Direct", url); // fallback to "Direct" if no name is provided
|
||||
}
|
||||
const title = (provider && provider.length > 0) ? provider : "Direct";
|
||||
streams.push({
|
||||
title: title,
|
||||
streamUrl: url
|
||||
});
|
||||
continue; // skip to the next provider
|
||||
}
|
||||
|
||||
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`);
|
||||
continue;
|
||||
}
|
||||
let streamUrl = await extractStreamUrlByProvider(url, provider);
|
||||
|
||||
if (streamUrl && Array.isArray(streamUrl)) {
|
||||
const httpStream = streamUrl.find(url => url.startsWith("http"));
|
||||
let result = await extractStreamUrlByProvider(url, provider);
|
||||
let streamUrl = null;
|
||||
let headers = null;
|
||||
|
||||
// Check if result is an object with streamUrl and optional headers
|
||||
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) {
|
||||
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 (
|
||||
!streamUrl ||
|
||||
typeof streamUrl !== "string" ||
|
||||
@@ -493,22 +524,29 @@ async function multiExtractor(providers) {
|
||||
provider = customName;
|
||||
}
|
||||
|
||||
let title;
|
||||
if (providersCount[provider]) {
|
||||
providersCount[provider]++;
|
||||
streams.push(
|
||||
provider.charAt(0).toUpperCase() +
|
||||
title = provider.charAt(0).toUpperCase() +
|
||||
provider.slice(1) +
|
||||
"-" +
|
||||
(providersCount[provider] - 1), // add a number to the provider name
|
||||
streamUrl
|
||||
);
|
||||
(providersCount[provider] - 1); // add a number to the provider name
|
||||
} else {
|
||||
providersCount[provider] = 1;
|
||||
streams.push(
|
||||
provider.charAt(0).toUpperCase() + provider.slice(1),
|
||||
streamUrl
|
||||
);
|
||||
title = provider.charAt(0).toUpperCase() + provider.slice(1);
|
||||
}
|
||||
|
||||
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) {
|
||||
// Ignore the error and try the next provider
|
||||
}
|
||||
@@ -519,73 +557,98 @@ async function multiExtractor(providers) {
|
||||
async function extractStreamUrlByProvider(url, provider) {
|
||||
if (eval(`typeof ${provider}Extractor`) !== "function") {
|
||||
// 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;
|
||||
}
|
||||
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 = {
|
||||
"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",
|
||||
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8",
|
||||
"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-Language": "en-US,en;q=0.5",
|
||||
"Referer": url,
|
||||
"Connection": "keep-alive",
|
||||
"x-Requested-With": "XMLHttpRequest"
|
||||
"x-Requested-With": "XMLHttpRequest",
|
||||
};
|
||||
if(provider == 'bigwarp') {
|
||||
delete headers["User-Agent"];
|
||||
headers["x-requested-with"] = "XMLHttpRequest";
|
||||
} else if (provider == 'vk') {
|
||||
headers["encoding"] = "windows-1251"; // required
|
||||
} else if (provider == 'sibnet') {
|
||||
headers["encoding"] = "windows-1251"; // required
|
||||
} else if (provider == 'supervideo') {
|
||||
delete headers["User-Agent"];
|
||||
|
||||
switch (provider) {
|
||||
case "bigwarp":
|
||||
delete headers["User-Agent"];
|
||||
break;
|
||||
case "vk":
|
||||
case "sibnet":
|
||||
headers["encoding"] = "windows-1251"; // required
|
||||
break;
|
||||
case "supervideo":
|
||||
case "savefiles":
|
||||
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
|
||||
// and pass the response to the extractor function
|
||||
console.log("Fetching URL: " + url);
|
||||
const response = await soraFetch(url, {
|
||||
headers
|
||||
});
|
||||
headers,
|
||||
});
|
||||
|
||||
console.log("Response: " + response.status);
|
||||
let html = response.text ? await response.text() : response;
|
||||
// if title contains redirect, then get the redirect url
|
||||
const title = html.match(/<title>(.*?)<\/title>/);
|
||||
if (title && title[1].toLowerCase().includes("redirect")) {
|
||||
const redirectUrl = html.match(/<meta http-equiv="refresh" content="0;url=(.*?)"/);
|
||||
const redirectUrl2 = html.match(/window\.location\.href\s*=\s*["'](.*?)["']/);
|
||||
const redirectUrl3 = html.match(/window\.location\.replace\s*\(\s*["'](.*?)["']\s*\)/);
|
||||
if (redirectUrl) {
|
||||
console.log("Redirect URL: " + redirectUrl[1]);
|
||||
url = redirectUrl[1];
|
||||
html = await soraFetch(url, {
|
||||
headers
|
||||
});
|
||||
html = html.text ? await html.text() : html;
|
||||
|
||||
} 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");
|
||||
const matches = [
|
||||
/<meta http-equiv="refresh" content="0;url=(.*?)"/,
|
||||
/window\.location\.href\s*=\s*["'](.*?)["']/,
|
||||
/window\.location\.replace\s*\(\s*["'](.*?)["']\s*\)/,
|
||||
/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];
|
||||
headers['Referer'] = url;
|
||||
headers['Host'] = url.match(/https?:\/\/([^\/]+)/)[1];
|
||||
html = await soraFetch(url, {
|
||||
headers,
|
||||
}).then((res) => res.text());
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// console.log("HTML: " + html);
|
||||
switch (provider) {
|
||||
case "bigwarp":
|
||||
case "bigwarp":
|
||||
try {
|
||||
return await bigwarpExtractor(html, url);
|
||||
} catch (error) {
|
||||
@@ -634,6 +697,20 @@ async function extractStreamUrlByProvider(url, provider) {
|
||||
console.log("Error extracting stream URL from mp4upload:", error);
|
||||
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":
|
||||
try {
|
||||
return await sendvidExtractor(html, url);
|
||||
@@ -648,6 +725,13 @@ async function extractStreamUrlByProvider(url, provider) {
|
||||
console.log("Error extracting stream URL from sibnet:", error);
|
||||
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":
|
||||
try {
|
||||
return await streamtapeExtractor(html, url);
|
||||
@@ -662,13 +746,6 @@ async function extractStreamUrlByProvider(url, provider) {
|
||||
console.log("Error extracting stream URL from streamup:", error);
|
||||
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":
|
||||
try {
|
||||
return await uploadcxExtractor(html, url);
|
||||
@@ -717,7 +794,6 @@ async function extractStreamUrlByProvider(url, provider) {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////
|
||||
// EXTRACTORS //
|
||||
////////////////////////////////////////////////
|
||||
@@ -1204,7 +1280,6 @@ async function megacloudExtractor(html, embedUrl) {
|
||||
* @author Cufiy
|
||||
*/
|
||||
async function mp4uploadExtractor(html, url = null) {
|
||||
// src: "https://a4.mp4upload.com:183/d/xkx3b4etz3b4quuo66rbmyqtjjoivahfxp27f35pti45rzapbvj5xwb4wuqtlpewdz4dirfp/video.mp4"
|
||||
const regex = /src:\s*"([^"]+)"/;
|
||||
const match = html.match(regex);
|
||||
if (match) {
|
||||
@@ -1214,6 +1289,32 @@ async function mp4uploadExtractor(html, url = 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 --- */
|
||||
|
||||
/**
|
||||
@@ -1249,6 +1350,29 @@ async function sibnetExtractor(html, embedUrl) {
|
||||
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 --- */
|
||||
|
||||
/**
|
||||
@@ -1332,26 +1456,6 @@ async function streamupExtractor(data, url = 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 --- */
|
||||
|
||||
/**
|
||||
@@ -1416,7 +1520,7 @@ async function vidmolyExtractor(html, url = null) {
|
||||
const streamUrl = iframeMatch[1].startsWith("//")
|
||||
? "https:" + iframeMatch[1]
|
||||
: iframeMatch[1];
|
||||
const responseTwo = await fetchv2(streamUrl);
|
||||
const responseTwo = await soraFetch(streamUrl);
|
||||
const htmlTwo = await responseTwo.text();
|
||||
const m3u8Match = htmlTwo.match(/sources:\s*\[\{file:"([^"]+\.m3u8)"/);
|
||||
return m3u8Match ? m3u8Match[1] : null;
|
||||
@@ -1542,6 +1646,7 @@ function voeShiftChars(str, shift) {
|
||||
.join("");
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////
|
||||
// PLUGINS //
|
||||
////////////////////////////////////////////////
|
||||
@@ -1559,17 +1664,25 @@ function voeShiftChars(str, shift) {
|
||||
* @returns {Promise<Response|null>} The response from the server, or null if the
|
||||
* request failed.
|
||||
*/
|
||||
async function soraFetch(url, options = { headers: {}, method: 'GET', body: null }) {
|
||||
async function soraFetch(
|
||||
url,
|
||||
options = { headers: {}, method: "GET", body: null }
|
||||
) {
|
||||
try {
|
||||
return await fetchv2(
|
||||
url,
|
||||
options.headers ?? {},
|
||||
options.method ?? "GET",
|
||||
options.body ?? null
|
||||
);
|
||||
} catch (e) {
|
||||
try {
|
||||
return await fetchv2(url, options.headers ?? {}, options.method ?? 'GET', options.body ?? null);
|
||||
} catch(e) {
|
||||
try {
|
||||
return await fetch(url, options);
|
||||
} catch(error) {
|
||||
await console.log('soraFetch error: ' + error.message);
|
||||
return null;
|
||||
}
|
||||
return await fetch(url, options);
|
||||
} catch (error) {
|
||||
await console.log("soraFetch error: " + error.message);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
/***********************************************************
|
||||
* UNPACKER MODULE
|
||||
@@ -1673,7 +1786,6 @@ function unpack(source) {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* {GE END} */
|
||||
|
||||
|
||||
Reference in New Issue
Block a user