forked from 50n50/sources
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 is contained in:
@@ -5,7 +5,7 @@
|
|||||||
"name": "50/50 & Cufiy",
|
"name": "50/50 & Cufiy",
|
||||||
"icon": "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQ3122kQwublLkZ6rf1fEpUP79BxZOFmH9BSA&s"
|
"icon": "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQ3122kQwublLkZ6rf1fEpUP79BxZOFmH9BSA&s"
|
||||||
},
|
},
|
||||||
"version": "1.2.13",
|
"version": "1.2.14",
|
||||||
"language": "German (DUB/SUB)",
|
"language": "German (DUB/SUB)",
|
||||||
"streamType": "MP4",
|
"streamType": "MP4",
|
||||||
"quality": "1080p",
|
"quality": "1080p",
|
||||||
@@ -17,4 +17,4 @@
|
|||||||
"supportsMojuru": true,
|
"supportsMojuru": true,
|
||||||
"supportsSora": true,
|
"supportsSora": true,
|
||||||
"supportsLuna": 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
|
// EDITING THIS FILE COULD BREAK THE UPDATER AND CAUSE ISSUES WITH THE EXTRACTOR
|
||||||
|
|
||||||
/* {GE START} */
|
/* {GE START} */
|
||||||
/* {VERSION: 1.1.8} */
|
/* {VERSION: 1.2.0} */
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @name global_extractor.js
|
* @name global_extractor.js
|
||||||
@@ -321,8 +321,8 @@ async function sendLog(message) {
|
|||||||
* @author Cufiy
|
* @author Cufiy
|
||||||
* @url https://github.com/JMcrafter26/sora-global-extractor
|
* @url https://github.com/JMcrafter26/sora-global-extractor
|
||||||
* @license CUSTOM LICENSE - see https://github.com/JMcrafter26/sora-global-extractor/blob/main/LICENSE
|
* @license CUSTOM LICENSE - see https://github.com/JMcrafter26/sora-global-extractor/blob/main/LICENSE
|
||||||
* @date 2025-11-05 15:44:57
|
* @date 2026-01-03 19:28:28
|
||||||
* @version 1.1.8
|
* @version 1.2.0
|
||||||
* @note This file was generated automatically.
|
* @note This file was generated automatically.
|
||||||
* The global extractor comes with an auto-updating feature, so you can always get the latest version. https://github.com/JMcrafter26/sora-global-extractor#-auto-updater
|
* The global extractor comes with an auto-updating feature, so you can always get the latest version. https://github.com/JMcrafter26/sora-global-extractor#-auto-updater
|
||||||
*/
|
*/
|
||||||
@@ -332,12 +332,20 @@ function globalExtractor(providers) {
|
|||||||
for (const [url, provider] of Object.entries(providers)) {
|
for (const [url, provider] of Object.entries(providers)) {
|
||||||
try {
|
try {
|
||||||
const streamUrl = extractStreamUrlByProvider(url, provider);
|
const streamUrl = extractStreamUrlByProvider(url, provider);
|
||||||
|
// check if streamUrl is an object with streamUrl property
|
||||||
|
if (streamUrl && typeof streamUrl === "object" && !Array.isArray(streamUrl) && streamUrl.streamUrl) {
|
||||||
|
return streamUrl.streamUrl;
|
||||||
|
}
|
||||||
// check if streamUrl is not null, a string, and starts with http or https
|
// check if streamUrl is not null, a string, and starts with http or https
|
||||||
if (streamUrl && typeof streamUrl === "string" && (streamUrl.startsWith("http"))) {
|
if (
|
||||||
|
streamUrl &&
|
||||||
|
typeof streamUrl === "string" &&
|
||||||
|
streamUrl.startsWith("http")
|
||||||
|
) {
|
||||||
return streamUrl;
|
return streamUrl;
|
||||||
// if its an array, get the value that starts with http
|
// if its an array, get the value that starts with http
|
||||||
} else if (Array.isArray(streamUrl)) {
|
} else if (Array.isArray(streamUrl)) {
|
||||||
const httpStream = streamUrl.find(url => url.startsWith("http"));
|
const httpStream = streamUrl.find((url) => url.startsWith("http"));
|
||||||
if (httpStream) {
|
if (httpStream) {
|
||||||
return httpStream;
|
return httpStream;
|
||||||
}
|
}
|
||||||
@@ -345,7 +353,6 @@ function globalExtractor(providers) {
|
|||||||
// check if it's a valid stream URL
|
// check if it's a valid stream URL
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
// Ignore the error and try the next provider
|
// Ignore the error and try the next provider
|
||||||
}
|
}
|
||||||
@@ -357,16 +364,30 @@ async function multiExtractor(providers) {
|
|||||||
/* this scheme should be returned as a JSON object
|
/* this scheme should be returned as a JSON object
|
||||||
{
|
{
|
||||||
"streams": [
|
"streams": [
|
||||||
"FileMoon",
|
{
|
||||||
"https://filemoon.example/stream1.m3u8",
|
"title": "FileMoon",
|
||||||
"StreamWish",
|
"streamUrl": "https://filemoon.example/stream1.m3u8",
|
||||||
"https://streamwish.example/stream2.m3u8",
|
},
|
||||||
"Okru",
|
{
|
||||||
"https://okru.example/stream3.m3u8",
|
"title": "StreamWish",
|
||||||
"MP4",
|
"streamUrl": "https://streamwish.example/stream2.m3u8",
|
||||||
"https://mp4upload.example/stream4.mp4",
|
},
|
||||||
"Default",
|
{
|
||||||
"https://default.example/stream5.m3u8"
|
"title": "Okru",
|
||||||
|
"streamUrl": "https://okru.example/stream3.m3u8",
|
||||||
|
"headers": { // Optional headers for the stream
|
||||||
|
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.3",
|
||||||
|
"Referer": "https://okru.example/",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "MP4",
|
||||||
|
"streamUrl": "https://mp4upload.example/stream4.mp4",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Default",
|
||||||
|
"streamUrl": "https://default.example/stream5.m3u8"
|
||||||
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
@@ -378,20 +399,21 @@ async function multiExtractor(providers) {
|
|||||||
// if provider starts with "direct-", then add the url to the streams array directly
|
// if provider starts with "direct-", then add the url to the streams array directly
|
||||||
if (provider.startsWith("direct-")) {
|
if (provider.startsWith("direct-")) {
|
||||||
const directName = provider.slice(7); // remove "direct-" prefix
|
const directName = provider.slice(7); // remove "direct-" prefix
|
||||||
if (directName && directName.length > 0) {
|
const title = (directName && directName.length > 0) ? directName : "Direct";
|
||||||
streams.push(directName, url);
|
streams.push({
|
||||||
} else {
|
title: title,
|
||||||
streams.push("Direct", url); // fallback to "Direct" if no name is provided
|
streamUrl: url
|
||||||
}
|
});
|
||||||
continue; // skip to the next provider
|
continue; // skip to the next provider
|
||||||
}
|
}
|
||||||
if (provider.startsWith("direct")) {
|
if (provider.startsWith("direct")) {
|
||||||
provider = provider.slice(7); // remove "direct-" prefix
|
provider = provider.slice(7); // remove "direct-" prefix
|
||||||
if (provider && provider.length > 0) {
|
const title = (provider && provider.length > 0) ? provider : "Direct";
|
||||||
streams.push(provider, url);
|
streams.push({
|
||||||
} else {
|
title: title,
|
||||||
streams.push("Direct", url); // fallback to "Direct" if no name is provided
|
streamUrl: url
|
||||||
}
|
});
|
||||||
|
continue; // skip to the next provider
|
||||||
}
|
}
|
||||||
|
|
||||||
let customName = null; // to store the custom name if provided
|
let customName = null; // to store the custom name if provided
|
||||||
@@ -408,15 +430,24 @@ async function multiExtractor(providers) {
|
|||||||
console.log(`Skipping ${provider} as it has already 3 streams`);
|
console.log(`Skipping ${provider} as it has already 3 streams`);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
let streamUrl = await extractStreamUrlByProvider(url, provider);
|
let result = await extractStreamUrlByProvider(url, provider);
|
||||||
|
let streamUrl = null;
|
||||||
if (streamUrl && Array.isArray(streamUrl)) {
|
let headers = null;
|
||||||
const httpStream = streamUrl.find(url => url.startsWith("http"));
|
|
||||||
|
// 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) {
|
if (httpStream) {
|
||||||
streamUrl = httpStream;
|
streamUrl = httpStream;
|
||||||
}
|
}
|
||||||
|
} else if (result && typeof result === "string") {
|
||||||
|
streamUrl = result;
|
||||||
}
|
}
|
||||||
// check if provider is already in streams, if it is, add a number to it
|
|
||||||
|
// check if streamUrl is valid
|
||||||
if (
|
if (
|
||||||
!streamUrl ||
|
!streamUrl ||
|
||||||
typeof streamUrl !== "string" ||
|
typeof streamUrl !== "string" ||
|
||||||
@@ -430,22 +461,29 @@ async function multiExtractor(providers) {
|
|||||||
provider = customName;
|
provider = customName;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let title;
|
||||||
if (providersCount[provider]) {
|
if (providersCount[provider]) {
|
||||||
providersCount[provider]++;
|
providersCount[provider]++;
|
||||||
streams.push(
|
title = provider.charAt(0).toUpperCase() +
|
||||||
provider.charAt(0).toUpperCase() +
|
|
||||||
provider.slice(1) +
|
provider.slice(1) +
|
||||||
"-" +
|
"-" +
|
||||||
(providersCount[provider] - 1), // add a number to the provider name
|
(providersCount[provider] - 1); // add a number to the provider name
|
||||||
streamUrl
|
|
||||||
);
|
|
||||||
} else {
|
} else {
|
||||||
providersCount[provider] = 1;
|
providersCount[provider] = 1;
|
||||||
streams.push(
|
title = provider.charAt(0).toUpperCase() + provider.slice(1);
|
||||||
provider.charAt(0).toUpperCase() + provider.slice(1),
|
|
||||||
streamUrl
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const streamObject = {
|
||||||
|
title: title,
|
||||||
|
streamUrl: streamUrl
|
||||||
|
};
|
||||||
|
|
||||||
|
// Add headers if they exist
|
||||||
|
if (headers && typeof headers === "object" && Object.keys(headers).length > 0) {
|
||||||
|
streamObject.headers = headers;
|
||||||
|
}
|
||||||
|
|
||||||
|
streams.push(streamObject);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
// Ignore the error and try the next provider
|
// Ignore the error and try the next provider
|
||||||
}
|
}
|
||||||
@@ -456,73 +494,98 @@ async function multiExtractor(providers) {
|
|||||||
async function extractStreamUrlByProvider(url, provider) {
|
async function extractStreamUrlByProvider(url, provider) {
|
||||||
if (eval(`typeof ${provider}Extractor`) !== "function") {
|
if (eval(`typeof ${provider}Extractor`) !== "function") {
|
||||||
// skip if the extractor is not defined
|
// skip if the extractor is not defined
|
||||||
console.log(`Extractor for provider ${provider} is not defined, skipping...`);
|
console.log(
|
||||||
|
`Extractor for provider ${provider} is not defined, skipping...`
|
||||||
|
);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
let uas = [
|
||||||
|
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.3",
|
||||||
|
"Mozilla/5.0 (iPhone; CPU iPhone OS 18_1_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/18.1.1 Mobile/15E148 Safari/604.1",
|
||||||
|
"Mozilla/5.0 (Linux; Android 10; SM-G973F) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Mobile Safari/537.36",
|
||||||
|
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.3",
|
||||||
|
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.1.2 Safari/605.1.15",
|
||||||
|
"Mozilla/5.0 (Linux; Android 11; Pixel 4 XL) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Mobile Safari/537.36",
|
||||||
|
];
|
||||||
let headers = {
|
let headers = {
|
||||||
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.3",
|
"User-Agent": uas[(url.length + provider.length) % uas.length], // use a different user agent based on the url and provider
|
||||||
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8",
|
"Accept":
|
||||||
|
"text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8",
|
||||||
"Accept-Language": "en-US,en;q=0.5",
|
"Accept-Language": "en-US,en;q=0.5",
|
||||||
"Referer": url,
|
"Referer": url,
|
||||||
"Connection": "keep-alive",
|
"Connection": "keep-alive",
|
||||||
"x-Requested-With": "XMLHttpRequest"
|
"x-Requested-With": "XMLHttpRequest",
|
||||||
};
|
};
|
||||||
if(provider == 'bigwarp') {
|
|
||||||
delete headers["User-Agent"];
|
switch (provider) {
|
||||||
headers["x-requested-with"] = "XMLHttpRequest";
|
case "bigwarp":
|
||||||
} else if (provider == 'vk') {
|
delete headers["User-Agent"];
|
||||||
headers["encoding"] = "windows-1251"; // required
|
break;
|
||||||
} else if (provider == 'sibnet') {
|
case "vk":
|
||||||
headers["encoding"] = "windows-1251"; // required
|
case "sibnet":
|
||||||
} else if (provider == 'supervideo') {
|
headers["encoding"] = "windows-1251"; // required
|
||||||
delete headers["User-Agent"];
|
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
|
// fetch the url
|
||||||
// and pass the response to the extractor function
|
// and pass the response to the extractor function
|
||||||
console.log("Fetching URL: " + url);
|
console.log("Fetching URL: " + url);
|
||||||
const response = await soraFetch(url, {
|
const response = await soraFetch(url, {
|
||||||
headers
|
headers,
|
||||||
});
|
});
|
||||||
|
|
||||||
console.log("Response: " + response.status);
|
console.log("Response: " + response.status);
|
||||||
let html = response.text ? await response.text() : response;
|
let html = response.text ? await response.text() : response;
|
||||||
// if title contains redirect, then get the redirect url
|
// if title contains redirect, then get the redirect url
|
||||||
const title = html.match(/<title>(.*?)<\/title>/);
|
const title = html.match(/<title>(.*?)<\/title>/);
|
||||||
if (title && title[1].toLowerCase().includes("redirect")) {
|
if (title && title[1].toLowerCase().includes("redirect")) {
|
||||||
const redirectUrl = html.match(/<meta http-equiv="refresh" content="0;url=(.*?)"/);
|
const matches = [
|
||||||
const redirectUrl2 = html.match(/window\.location\.href\s*=\s*["'](.*?)["']/);
|
/<meta http-equiv="refresh" content="0;url=(.*?)"/,
|
||||||
const redirectUrl3 = html.match(/window\.location\.replace\s*\(\s*["'](.*?)["']\s*\)/);
|
/window\.location\.href\s*=\s*["'](.*?)["']/,
|
||||||
if (redirectUrl) {
|
/window\.location\.replace\s*\(\s*["'](.*?)["']\s*\)/,
|
||||||
console.log("Redirect URL: " + redirectUrl[1]);
|
/window\.location\s*=\s*["'](.*?)["']/,
|
||||||
url = redirectUrl[1];
|
/window\.location\.assign\s*\(\s*["'](.*?)["']\s*\)/,
|
||||||
html = await soraFetch(url, {
|
/top\.location\s*=\s*["'](.*?)["']/,
|
||||||
headers
|
/top\.location\.replace\s*\(\s*["'](.*?)["']\s*\)/,
|
||||||
});
|
];
|
||||||
html = html.text ? await html.text() : html;
|
for (const match of matches) {
|
||||||
|
const redirectUrl = html.match(match);
|
||||||
} else if (redirectUrl2) {
|
if (redirectUrl && redirectUrl[1] && typeof redirectUrl[1] === "string" && redirectUrl[1].startsWith("http")) {
|
||||||
console.log("Redirect URL 2: " + redirectUrl2[1]);
|
console.log("Redirect URL found: " + redirectUrl[1]);
|
||||||
url = redirectUrl2[1];
|
url = redirectUrl[1];
|
||||||
html = await soraFetch(url, {
|
headers['Referer'] = url;
|
||||||
headers
|
headers['Host'] = url.match(/https?:\/\/([^\/]+)/)[1];
|
||||||
});
|
html = await soraFetch(url, {
|
||||||
html = html.text ? await html.text() : html;
|
headers,
|
||||||
} else if (redirectUrl3) {
|
}).then((res) => res.text());
|
||||||
console.log("Redirect URL 3: " + redirectUrl3[1]);
|
break;
|
||||||
url = redirectUrl3[1];
|
}
|
||||||
html = await soraFetch(url, {
|
|
||||||
headers
|
|
||||||
});
|
|
||||||
html = html.text ? await html.text() : html;
|
|
||||||
} else {
|
|
||||||
console.log("No redirect URL found");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// console.log("HTML: " + html);
|
// console.log("HTML: " + html);
|
||||||
switch (provider) {
|
switch (provider) {
|
||||||
case "bigwarp":
|
case "bigwarp":
|
||||||
try {
|
try {
|
||||||
return await bigwarpExtractor(html, url);
|
return await bigwarpExtractor(html, url);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@@ -571,6 +634,20 @@ async function extractStreamUrlByProvider(url, provider) {
|
|||||||
console.log("Error extracting stream URL from mp4upload:", error);
|
console.log("Error extracting stream URL from mp4upload:", error);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
case "oneupload":
|
||||||
|
try {
|
||||||
|
return await oneuploadExtractor(html, url);
|
||||||
|
} catch (error) {
|
||||||
|
console.log("Error extracting stream URL from oneupload:", error);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
case "packer":
|
||||||
|
try {
|
||||||
|
return await packerExtractor(html, url);
|
||||||
|
} catch (error) {
|
||||||
|
console.log("Error extracting stream URL from packer:", error);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
case "sendvid":
|
case "sendvid":
|
||||||
try {
|
try {
|
||||||
return await sendvidExtractor(html, url);
|
return await sendvidExtractor(html, url);
|
||||||
@@ -585,6 +662,13 @@ async function extractStreamUrlByProvider(url, provider) {
|
|||||||
console.log("Error extracting stream URL from sibnet:", error);
|
console.log("Error extracting stream URL from sibnet:", error);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
case "smoothpre":
|
||||||
|
try {
|
||||||
|
return await smoothpreExtractor(html, url);
|
||||||
|
} catch (error) {
|
||||||
|
console.log("Error extracting stream URL from smoothpre:", error);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
case "streamtape":
|
case "streamtape":
|
||||||
try {
|
try {
|
||||||
return await streamtapeExtractor(html, url);
|
return await streamtapeExtractor(html, url);
|
||||||
@@ -599,13 +683,6 @@ async function extractStreamUrlByProvider(url, provider) {
|
|||||||
console.log("Error extracting stream URL from streamup:", error);
|
console.log("Error extracting stream URL from streamup:", error);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
case "supervideo":
|
|
||||||
try {
|
|
||||||
return await supervideoExtractor(html, url);
|
|
||||||
} catch (error) {
|
|
||||||
console.log("Error extracting stream URL from supervideo:", error);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
case "uploadcx":
|
case "uploadcx":
|
||||||
try {
|
try {
|
||||||
return await uploadcxExtractor(html, url);
|
return await uploadcxExtractor(html, url);
|
||||||
@@ -654,7 +731,6 @@ async function extractStreamUrlByProvider(url, provider) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
////////////////////////////////////////////////
|
////////////////////////////////////////////////
|
||||||
// EXTRACTORS //
|
// EXTRACTORS //
|
||||||
////////////////////////////////////////////////
|
////////////////////////////////////////////////
|
||||||
@@ -1141,7 +1217,6 @@ async function megacloudExtractor(html, embedUrl) {
|
|||||||
* @author Cufiy
|
* @author Cufiy
|
||||||
*/
|
*/
|
||||||
async function mp4uploadExtractor(html, url = null) {
|
async function mp4uploadExtractor(html, url = null) {
|
||||||
// src: "https://a4.mp4upload.com:183/d/xkx3b4etz3b4quuo66rbmyqtjjoivahfxp27f35pti45rzapbvj5xwb4wuqtlpewdz4dirfp/video.mp4"
|
|
||||||
const regex = /src:\s*"([^"]+)"/;
|
const regex = /src:\s*"([^"]+)"/;
|
||||||
const match = html.match(regex);
|
const match = html.match(regex);
|
||||||
if (match) {
|
if (match) {
|
||||||
@@ -1151,6 +1226,32 @@ async function mp4uploadExtractor(html, url = null) {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
/* --- oneupload --- */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @name oneuploadExtractor
|
||||||
|
* @author 50/50
|
||||||
|
*/
|
||||||
|
async function oneuploadExtractor(data, url = null) {
|
||||||
|
const match = data.match(/sources:\s*\[\{file:"([^"]+)"\}\]/);
|
||||||
|
const fileUrl = match ? match[1] : null;
|
||||||
|
return fileUrl;
|
||||||
|
}
|
||||||
|
/* --- packer --- */
|
||||||
|
|
||||||
|
/* {REQUIRED PLUGINS: unbaser} */
|
||||||
|
/**
|
||||||
|
* @name packerExtractor
|
||||||
|
* @author 50/50
|
||||||
|
*/
|
||||||
|
async function packerExtractor(data, url = null) {
|
||||||
|
const obfuscatedScript = data.match(/<script[^>]*>\s*(eval\(function\(p,a,c,k,e,d.*?\)[\s\S]*?)<\/script>/);
|
||||||
|
const unpackedScript = unpack(obfuscatedScript[1]);
|
||||||
|
const m3u8Match = unpackedScript.match(/"hls2"\s*:\s*"([^"]+)"/);
|
||||||
|
const m3u8Url = m3u8Match[1];
|
||||||
|
return m3u8Url;
|
||||||
|
}
|
||||||
|
|
||||||
/* --- sendvid --- */
|
/* --- sendvid --- */
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1186,6 +1287,29 @@ async function sibnetExtractor(html, embedUrl) {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
/* --- smoothpre --- */
|
||||||
|
|
||||||
|
/* {REQUIRED PLUGINS: unbaser} */
|
||||||
|
/**
|
||||||
|
* @name SmoothPre Extractor
|
||||||
|
* @author 50/50
|
||||||
|
*/
|
||||||
|
async function smoothpreExtractor(data, url = null) {
|
||||||
|
console.log("Using SmoothPre Extractor");
|
||||||
|
console.log("Data Length: " + data.length);
|
||||||
|
const obfuscatedScript = data.match(/<script[^>]*>\s*(eval\(function\(p,a,c,k,e,d.*?\)[\s\S]*?)<\/script>/);
|
||||||
|
if (!obfuscatedScript || !obfuscatedScript[1]) {
|
||||||
|
console.log("No obfuscated script found");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
const unpackedScript = unpack(obfuscatedScript[1]);
|
||||||
|
|
||||||
|
const hls2Match = unpackedScript.match(/"hls2"\s*:\s*"([^"]+)"/);
|
||||||
|
const hls2Url = hls2Match ? hls2Match[1] : null;
|
||||||
|
return hls2Url;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/* --- streamtape --- */
|
/* --- streamtape --- */
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1269,26 +1393,6 @@ async function streamupExtractor(data, url = null) {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/* --- supervideo --- */
|
|
||||||
|
|
||||||
/* {REQUIRED PLUGINS: unbaser} */
|
|
||||||
/**
|
|
||||||
* @name SuperVideo Extractor
|
|
||||||
* @author 50/50
|
|
||||||
*/
|
|
||||||
async function supervideoExtractor(data, url = null) {
|
|
||||||
const obfuscatedScript = data.match(/<script[^>]*>\s*(eval\(function\(p,a,c,k,e,d.*?\)[\s\S]*?)<\/script>/);
|
|
||||||
const unpackedScript = unpack(obfuscatedScript[1]);
|
|
||||||
const regex = /file:\s*"([^"]+\.m3u8)"/;
|
|
||||||
const match = regex.exec(unpackedScript);
|
|
||||||
if (match) {
|
|
||||||
const fileUrl = match[1];
|
|
||||||
console.log("File URL:" + fileUrl);
|
|
||||||
return fileUrl;
|
|
||||||
}
|
|
||||||
return "No stream found";
|
|
||||||
}
|
|
||||||
|
|
||||||
/* --- uploadcx --- */
|
/* --- uploadcx --- */
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1353,7 +1457,7 @@ async function vidmolyExtractor(html, url = null) {
|
|||||||
const streamUrl = iframeMatch[1].startsWith("//")
|
const streamUrl = iframeMatch[1].startsWith("//")
|
||||||
? "https:" + iframeMatch[1]
|
? "https:" + iframeMatch[1]
|
||||||
: iframeMatch[1];
|
: iframeMatch[1];
|
||||||
const responseTwo = await fetchv2(streamUrl);
|
const responseTwo = await soraFetch(streamUrl);
|
||||||
const htmlTwo = await responseTwo.text();
|
const htmlTwo = await responseTwo.text();
|
||||||
const m3u8Match = htmlTwo.match(/sources:\s*\[\{file:"([^"]+\.m3u8)"/);
|
const m3u8Match = htmlTwo.match(/sources:\s*\[\{file:"([^"]+\.m3u8)"/);
|
||||||
return m3u8Match ? m3u8Match[1] : null;
|
return m3u8Match ? m3u8Match[1] : null;
|
||||||
@@ -1479,6 +1583,7 @@ function voeShiftChars(str, shift) {
|
|||||||
.join("");
|
.join("");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
////////////////////////////////////////////////
|
////////////////////////////////////////////////
|
||||||
// PLUGINS //
|
// PLUGINS //
|
||||||
////////////////////////////////////////////////
|
////////////////////////////////////////////////
|
||||||
@@ -1496,17 +1601,25 @@ function voeShiftChars(str, shift) {
|
|||||||
* @returns {Promise<Response|null>} The response from the server, or null if the
|
* @returns {Promise<Response|null>} The response from the server, or null if the
|
||||||
* request failed.
|
* request failed.
|
||||||
*/
|
*/
|
||||||
async function soraFetch(url, options = { headers: {}, method: 'GET', body: null }) {
|
async function soraFetch(
|
||||||
|
url,
|
||||||
|
options = { headers: {}, method: "GET", body: null }
|
||||||
|
) {
|
||||||
|
try {
|
||||||
|
return await fetchv2(
|
||||||
|
url,
|
||||||
|
options.headers ?? {},
|
||||||
|
options.method ?? "GET",
|
||||||
|
options.body ?? null
|
||||||
|
);
|
||||||
|
} catch (e) {
|
||||||
try {
|
try {
|
||||||
return await fetchv2(url, options.headers ?? {}, options.method ?? 'GET', options.body ?? null);
|
return await fetch(url, options);
|
||||||
} catch(e) {
|
} catch (error) {
|
||||||
try {
|
await console.log("soraFetch error: " + error.message);
|
||||||
return await fetch(url, options);
|
return null;
|
||||||
} catch(error) {
|
|
||||||
await console.log('soraFetch error: ' + error.message);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
/***********************************************************
|
/***********************************************************
|
||||||
* UNPACKER MODULE
|
* UNPACKER MODULE
|
||||||
@@ -1610,6 +1723,5 @@ function unpack(source) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* {GE END} */
|
/* {GE END} */
|
||||||
|
|||||||
@@ -2,10 +2,11 @@
|
|||||||
"sourceName": "AniWorld (ENG SUB)",
|
"sourceName": "AniWorld (ENG SUB)",
|
||||||
"iconUrl": "https://git.luna-app.eu/50n50/sources/raw/branch/main/aniworld/aniworld.png",
|
"iconUrl": "https://git.luna-app.eu/50n50/sources/raw/branch/main/aniworld/aniworld.png",
|
||||||
"author": {
|
"author": {
|
||||||
"name": "Hamzo & Cufiy",
|
"name": "Cufiy & Hamzo",
|
||||||
"icon": "https://cdn.discordapp.com/avatars/623644371819954226/591ecab10b0b4535e859bb0b9bbe62e5?size=1024"
|
"icon": "https://files.catbox.moe/ttj4fc.gif",
|
||||||
|
"url": "https://github.com/JMcrafter26"
|
||||||
},
|
},
|
||||||
"version": "0.2.8",
|
"version": "0.3.1",
|
||||||
"language": "English (SUB)",
|
"language": "English (SUB)",
|
||||||
"streamType": "HLS",
|
"streamType": "HLS",
|
||||||
"quality": "720p",
|
"quality": "720p",
|
||||||
@@ -18,4 +19,4 @@
|
|||||||
"supportsMojuru": true,
|
"supportsMojuru": true,
|
||||||
"supportsSora": true,
|
"supportsSora": true,
|
||||||
"supportsLuna": true
|
"supportsLuna": true
|
||||||
}
|
}
|
||||||
@@ -3,15 +3,16 @@
|
|||||||
"iconUrl": "https://git.luna-app.eu/50n50/sources/raw/branch/main/aniworld/aniworld.png",
|
"iconUrl": "https://git.luna-app.eu/50n50/sources/raw/branch/main/aniworld/aniworld.png",
|
||||||
"author": {
|
"author": {
|
||||||
"name": "Cufiy",
|
"name": "Cufiy",
|
||||||
"icon": "https://cdn.discordapp.com/avatars/623644371819954226/591ecab10b0b4535e859bb0b9bbe62e5?size=1024"
|
"icon": "https://files.catbox.moe/ttj4fc.gif",
|
||||||
|
"url": "https://github.com/JMcrafter26"
|
||||||
},
|
},
|
||||||
"version": "0.2.52",
|
"version": "0.3.194",
|
||||||
"language": "German (DUB)",
|
"language": "German (DUB)",
|
||||||
"streamType": "HLS",
|
"streamType": "HLS",
|
||||||
"quality": "720p",
|
"quality": "720p",
|
||||||
"baseUrl": "https://vidmoly.to/",
|
"baseUrl": "https://vidmoly.to/",
|
||||||
"searchBaseUrl": "https://aniworld.to/ajax/seriesSearch?keyword=%s",
|
"searchBaseUrl": "https://aniworld.to/ajax/seriesSearch?keyword=%s",
|
||||||
"scriptUrl": "http://192.168.2.130/sora-module-repos/sources/aniworld/v2/AniWorldGerDub_v2.js",
|
"scriptUrl": "http://192.168.2.130/sora-module-repos/sources-fork/aniworld/v2/AniWorldGerDub_v2.js",
|
||||||
"asyncJS": true,
|
"asyncJS": true,
|
||||||
"type": "anime",
|
"type": "anime",
|
||||||
"supportsMojuru": true,
|
"supportsMojuru": true,
|
||||||
|
|||||||
@@ -2,10 +2,11 @@
|
|||||||
"sourceName": "AniWorld (GER DUB)",
|
"sourceName": "AniWorld (GER DUB)",
|
||||||
"iconUrl": "https://git.luna-app.eu/50n50/sources/raw/branch/main/aniworld/aniworld.png",
|
"iconUrl": "https://git.luna-app.eu/50n50/sources/raw/branch/main/aniworld/aniworld.png",
|
||||||
"author": {
|
"author": {
|
||||||
"name": "Hamzo & Cufiy",
|
"name": "Cufiy & Hamzo",
|
||||||
"icon": "https://cdn.discordapp.com/avatars/623644371819954226/591ecab10b0b4535e859bb0b9bbe62e5?size=1024"
|
"icon": "https://files.catbox.moe/ttj4fc.gif",
|
||||||
|
"url": "https://github.com/JMcrafter26"
|
||||||
},
|
},
|
||||||
"version": "0.2.8",
|
"version": "0.3.1",
|
||||||
"language": "German (DUB)",
|
"language": "German (DUB)",
|
||||||
"streamType": "HLS",
|
"streamType": "HLS",
|
||||||
"quality": "720p",
|
"quality": "720p",
|
||||||
@@ -18,4 +19,4 @@
|
|||||||
"supportsMojuru": true,
|
"supportsMojuru": true,
|
||||||
"supportsSora": true,
|
"supportsSora": true,
|
||||||
"supportsLuna": true
|
"supportsLuna": true
|
||||||
}
|
}
|
||||||
@@ -2,10 +2,11 @@
|
|||||||
"sourceName": "AniWorld (GER SUB)",
|
"sourceName": "AniWorld (GER SUB)",
|
||||||
"iconUrl": "https://git.luna-app.eu/50n50/sources/raw/branch/main/aniworld/aniworld.png",
|
"iconUrl": "https://git.luna-app.eu/50n50/sources/raw/branch/main/aniworld/aniworld.png",
|
||||||
"author": {
|
"author": {
|
||||||
"name": "Hamzo & Cufiy",
|
"name": "Cufiy & Hamzo",
|
||||||
"icon": "https://cdn.discordapp.com/avatars/623644371819954226/591ecab10b0b4535e859bb0b9bbe62e5?size=1024"
|
"icon": "https://files.catbox.moe/ttj4fc.gif",
|
||||||
|
"url": "https://github.com/JMcrafter26"
|
||||||
},
|
},
|
||||||
"version": "0.2.8",
|
"version": "0.3.1",
|
||||||
"language": "German (SUB)",
|
"language": "German (SUB)",
|
||||||
"streamType": "HLS",
|
"streamType": "HLS",
|
||||||
"quality": "720p",
|
"quality": "720p",
|
||||||
@@ -18,4 +19,4 @@
|
|||||||
"supportsMojuru": true,
|
"supportsMojuru": true,
|
||||||
"supportsSora": true,
|
"supportsSora": true,
|
||||||
"supportsLuna": true
|
"supportsLuna": true
|
||||||
}
|
}
|
||||||
+264
-137
@@ -6,9 +6,9 @@ async function searchResults(keyword) {
|
|||||||
try {
|
try {
|
||||||
const encodedKeyword = encodeURIComponent(keyword);
|
const encodedKeyword = encodeURIComponent(keyword);
|
||||||
const searchApiUrl = `https://aniworld.to/ajax/seriesSearch?keyword=${encodedKeyword}`;
|
const searchApiUrl = `https://aniworld.to/ajax/seriesSearch?keyword=${encodedKeyword}`;
|
||||||
const responseText = await fetch(searchApiUrl);
|
const responseText = await soraFetch(searchApiUrl);
|
||||||
// console.log("Search API Response: " + await responseText.text());
|
// console.log("Search API Response: " + await responseText.text());
|
||||||
const data = await JSON.parse(responseText);
|
const data = await responseText.json() || await JSON.parse(responseText);
|
||||||
console.log("Search API Data: ", data);
|
console.log("Search API Data: ", data);
|
||||||
|
|
||||||
const transformedResults = data.map((anime) => ({
|
const transformedResults = data.map((anime) => ({
|
||||||
@@ -84,9 +84,9 @@ async function extractEpisodes(url) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Replace the field "number" with the current index of each item, starting from 1
|
// Replace the field "number" with the current index of each item, starting from 1
|
||||||
finishedList.forEach((item, index) => {
|
// finishedList.forEach((item, index) => {
|
||||||
item.number = index + 1;
|
// item.number = index + 1;
|
||||||
});
|
// });
|
||||||
|
|
||||||
return JSON.stringify(finishedList);
|
return JSON.stringify(finishedList);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@@ -99,6 +99,7 @@ async function extractStreamUrl(url) {
|
|||||||
try {
|
try {
|
||||||
const baseUrl = "https://aniworld.to";
|
const baseUrl = "https://aniworld.to";
|
||||||
const fetchUrl = `${url}`;
|
const fetchUrl = `${url}`;
|
||||||
|
sendLog("Fetching URL: " + fetchUrl);
|
||||||
const response = await fetch(fetchUrl);
|
const response = await fetch(fetchUrl);
|
||||||
const text = response.text ? await response.text() : response;
|
const text = response.text ? await response.text() : response;
|
||||||
|
|
||||||
@@ -177,9 +178,7 @@ function selectHoster(finishedList) {
|
|||||||
|
|
||||||
// Define the preferred providers and languages
|
// Define the preferred providers and languages
|
||||||
const providerList = ["VOE", "Filemoon", "SpeedFiles", "Vidmoly", "DoodStream", "Vidoza", "mp4upload"];
|
const providerList = ["VOE", "Filemoon", "SpeedFiles", "Vidmoly", "DoodStream", "Vidoza", "mp4upload"];
|
||||||
const languageList = ["mit Untertitel Englisch", "mit Untertitel Deutsch", "Deutsch"];
|
const languageList = ["mit Untertitel Englisch", "Englisch", "mit Untertitel Deutsch", "Deutsch"];
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
for (const language of languageList) {
|
for (const language of languageList) {
|
||||||
for (const providerName of providerList) {
|
for (const providerName of providerList) {
|
||||||
@@ -249,23 +248,39 @@ async function fetchSeasonEpisodes(url) {
|
|||||||
const response = await fetch(fetchUrl);
|
const response = await fetch(fetchUrl);
|
||||||
const text = response.text ? await response.text() : response;
|
const text = response.text ? await response.text() : response;
|
||||||
|
|
||||||
|
// if is filme, e.g. https://aniworld.to/anime/stream/jujutsu-kaisen/filme
|
||||||
|
let isFilme = false;
|
||||||
|
if (url.endsWith("/filme") || url.includes("/filme/")) {
|
||||||
|
isFilme = true;
|
||||||
|
}
|
||||||
|
|
||||||
// Updated regex to allow empty <strong> content
|
// Updated regex to allow empty <strong> content
|
||||||
const regex =
|
const regex =
|
||||||
/<td class="seasonEpisodeTitle">\s*<a[^>]*href="([^"]+)"[^>]*>.*?<strong>([^<]*)<\/strong>.*?<span>([^<]+)<\/span>.*?<\/a>/g;
|
/<td class="seasonEpisodeTitle">\s*<a[^>]*href="([^"]+)"[^>]*>.*?<strong>([^<]*)<\/strong>.*?<span>([^<]+)<\/span>.*?<\/a>/g;
|
||||||
|
|
||||||
const matches = [];
|
const matches = [];
|
||||||
let match;
|
let match;
|
||||||
let holderNumber = 0;
|
let number = 0;
|
||||||
|
|
||||||
while ((match = regex.exec(text)) !== null) {
|
while ((match = regex.exec(text)) !== null) {
|
||||||
const [_, link] = match;
|
const [_, link, titleRaw, span] = match;
|
||||||
matches.push({ number: holderNumber, href: `${baseUrl}${link}` });
|
number += 1;
|
||||||
|
// sendLog("Episode found:", { number, link, title, span });
|
||||||
|
|
||||||
|
let title = titleRaw.trim() || span.trim();
|
||||||
|
if (isFilme) {
|
||||||
|
title = `[FILM] ${title || span.trim() || "Untitled"}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
matches.push({ number, href: `${baseUrl}${link}`, title});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sendLog("Season Episodes:" + JSON.stringify(matches));
|
||||||
|
|
||||||
return matches;
|
return matches;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
sendLog("FetchSeasonEpisodes helper function error:" + error);
|
sendLog("FetchSeasonEpisodes helper function error:" + error);
|
||||||
return [{ number: "0", href: "https://error.org" }];
|
return [{ number: "0", href: "https://error.org", title: "Error" }];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -337,7 +352,7 @@ function base64Decode(str) {
|
|||||||
async function sendLog(message) {
|
async function sendLog(message) {
|
||||||
// send http://192.168.2.130/sora-module/log.php?action=add&message=message
|
// send http://192.168.2.130/sora-module/log.php?action=add&message=message
|
||||||
console.log(message);
|
console.log(message);
|
||||||
return;
|
// return;
|
||||||
|
|
||||||
await fetch('http://192.168.2.130/sora-module/log.php?action=add&message=' + encodeURIComponent(message))
|
await fetch('http://192.168.2.130/sora-module/log.php?action=add&message=' + encodeURIComponent(message))
|
||||||
.catch(error => {
|
.catch(error => {
|
||||||
@@ -350,7 +365,7 @@ async function sendLog(message) {
|
|||||||
// EDITING THIS FILE COULD BREAK THE UPDATER AND CAUSE ISSUES WITH THE EXTRACTOR
|
// EDITING THIS FILE COULD BREAK THE UPDATER AND CAUSE ISSUES WITH THE EXTRACTOR
|
||||||
|
|
||||||
/* {GE START} */
|
/* {GE START} */
|
||||||
/* {VERSION: 1.1.8} */
|
/* {VERSION: 1.2.0} */
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @name global_extractor.js
|
* @name global_extractor.js
|
||||||
@@ -358,8 +373,8 @@ async function sendLog(message) {
|
|||||||
* @author Cufiy
|
* @author Cufiy
|
||||||
* @url https://github.com/JMcrafter26/sora-global-extractor
|
* @url https://github.com/JMcrafter26/sora-global-extractor
|
||||||
* @license CUSTOM LICENSE - see https://github.com/JMcrafter26/sora-global-extractor/blob/main/LICENSE
|
* @license CUSTOM LICENSE - see https://github.com/JMcrafter26/sora-global-extractor/blob/main/LICENSE
|
||||||
* @date 2025-11-05 15:44:57
|
* @date 2026-01-03 19:28:28
|
||||||
* @version 1.1.8
|
* @version 1.2.0
|
||||||
* @note This file was generated automatically.
|
* @note This file was generated automatically.
|
||||||
* The global extractor comes with an auto-updating feature, so you can always get the latest version. https://github.com/JMcrafter26/sora-global-extractor#-auto-updater
|
* The global extractor comes with an auto-updating feature, so you can always get the latest version. https://github.com/JMcrafter26/sora-global-extractor#-auto-updater
|
||||||
*/
|
*/
|
||||||
@@ -369,12 +384,20 @@ function globalExtractor(providers) {
|
|||||||
for (const [url, provider] of Object.entries(providers)) {
|
for (const [url, provider] of Object.entries(providers)) {
|
||||||
try {
|
try {
|
||||||
const streamUrl = extractStreamUrlByProvider(url, provider);
|
const streamUrl = extractStreamUrlByProvider(url, provider);
|
||||||
|
// check if streamUrl is an object with streamUrl property
|
||||||
|
if (streamUrl && typeof streamUrl === "object" && !Array.isArray(streamUrl) && streamUrl.streamUrl) {
|
||||||
|
return streamUrl.streamUrl;
|
||||||
|
}
|
||||||
// check if streamUrl is not null, a string, and starts with http or https
|
// check if streamUrl is not null, a string, and starts with http or https
|
||||||
if (streamUrl && typeof streamUrl === "string" && (streamUrl.startsWith("http"))) {
|
if (
|
||||||
|
streamUrl &&
|
||||||
|
typeof streamUrl === "string" &&
|
||||||
|
streamUrl.startsWith("http")
|
||||||
|
) {
|
||||||
return streamUrl;
|
return streamUrl;
|
||||||
// if its an array, get the value that starts with http
|
// if its an array, get the value that starts with http
|
||||||
} else if (Array.isArray(streamUrl)) {
|
} else if (Array.isArray(streamUrl)) {
|
||||||
const httpStream = streamUrl.find(url => url.startsWith("http"));
|
const httpStream = streamUrl.find((url) => url.startsWith("http"));
|
||||||
if (httpStream) {
|
if (httpStream) {
|
||||||
return httpStream;
|
return httpStream;
|
||||||
}
|
}
|
||||||
@@ -382,7 +405,6 @@ function globalExtractor(providers) {
|
|||||||
// check if it's a valid stream URL
|
// check if it's a valid stream URL
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
// Ignore the error and try the next provider
|
// Ignore the error and try the next provider
|
||||||
}
|
}
|
||||||
@@ -394,16 +416,30 @@ async function multiExtractor(providers) {
|
|||||||
/* this scheme should be returned as a JSON object
|
/* this scheme should be returned as a JSON object
|
||||||
{
|
{
|
||||||
"streams": [
|
"streams": [
|
||||||
"FileMoon",
|
{
|
||||||
"https://filemoon.example/stream1.m3u8",
|
"title": "FileMoon",
|
||||||
"StreamWish",
|
"streamUrl": "https://filemoon.example/stream1.m3u8",
|
||||||
"https://streamwish.example/stream2.m3u8",
|
},
|
||||||
"Okru",
|
{
|
||||||
"https://okru.example/stream3.m3u8",
|
"title": "StreamWish",
|
||||||
"MP4",
|
"streamUrl": "https://streamwish.example/stream2.m3u8",
|
||||||
"https://mp4upload.example/stream4.mp4",
|
},
|
||||||
"Default",
|
{
|
||||||
"https://default.example/stream5.m3u8"
|
"title": "Okru",
|
||||||
|
"streamUrl": "https://okru.example/stream3.m3u8",
|
||||||
|
"headers": { // Optional headers for the stream
|
||||||
|
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.3",
|
||||||
|
"Referer": "https://okru.example/",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "MP4",
|
||||||
|
"streamUrl": "https://mp4upload.example/stream4.mp4",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Default",
|
||||||
|
"streamUrl": "https://default.example/stream5.m3u8"
|
||||||
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
@@ -415,20 +451,21 @@ async function multiExtractor(providers) {
|
|||||||
// if provider starts with "direct-", then add the url to the streams array directly
|
// if provider starts with "direct-", then add the url to the streams array directly
|
||||||
if (provider.startsWith("direct-")) {
|
if (provider.startsWith("direct-")) {
|
||||||
const directName = provider.slice(7); // remove "direct-" prefix
|
const directName = provider.slice(7); // remove "direct-" prefix
|
||||||
if (directName && directName.length > 0) {
|
const title = (directName && directName.length > 0) ? directName : "Direct";
|
||||||
streams.push(directName, url);
|
streams.push({
|
||||||
} else {
|
title: title,
|
||||||
streams.push("Direct", url); // fallback to "Direct" if no name is provided
|
streamUrl: url
|
||||||
}
|
});
|
||||||
continue; // skip to the next provider
|
continue; // skip to the next provider
|
||||||
}
|
}
|
||||||
if (provider.startsWith("direct")) {
|
if (provider.startsWith("direct")) {
|
||||||
provider = provider.slice(7); // remove "direct-" prefix
|
provider = provider.slice(7); // remove "direct-" prefix
|
||||||
if (provider && provider.length > 0) {
|
const title = (provider && provider.length > 0) ? provider : "Direct";
|
||||||
streams.push(provider, url);
|
streams.push({
|
||||||
} else {
|
title: title,
|
||||||
streams.push("Direct", url); // fallback to "Direct" if no name is provided
|
streamUrl: url
|
||||||
}
|
});
|
||||||
|
continue; // skip to the next provider
|
||||||
}
|
}
|
||||||
|
|
||||||
let customName = null; // to store the custom name if provided
|
let customName = null; // to store the custom name if provided
|
||||||
@@ -445,15 +482,24 @@ async function multiExtractor(providers) {
|
|||||||
console.log(`Skipping ${provider} as it has already 3 streams`);
|
console.log(`Skipping ${provider} as it has already 3 streams`);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
let streamUrl = await extractStreamUrlByProvider(url, provider);
|
let result = await extractStreamUrlByProvider(url, provider);
|
||||||
|
let streamUrl = null;
|
||||||
if (streamUrl && Array.isArray(streamUrl)) {
|
let headers = null;
|
||||||
const httpStream = streamUrl.find(url => url.startsWith("http"));
|
|
||||||
|
// 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) {
|
if (httpStream) {
|
||||||
streamUrl = httpStream;
|
streamUrl = httpStream;
|
||||||
}
|
}
|
||||||
|
} else if (result && typeof result === "string") {
|
||||||
|
streamUrl = result;
|
||||||
}
|
}
|
||||||
// check if provider is already in streams, if it is, add a number to it
|
|
||||||
|
// check if streamUrl is valid
|
||||||
if (
|
if (
|
||||||
!streamUrl ||
|
!streamUrl ||
|
||||||
typeof streamUrl !== "string" ||
|
typeof streamUrl !== "string" ||
|
||||||
@@ -467,22 +513,29 @@ async function multiExtractor(providers) {
|
|||||||
provider = customName;
|
provider = customName;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let title;
|
||||||
if (providersCount[provider]) {
|
if (providersCount[provider]) {
|
||||||
providersCount[provider]++;
|
providersCount[provider]++;
|
||||||
streams.push(
|
title = provider.charAt(0).toUpperCase() +
|
||||||
provider.charAt(0).toUpperCase() +
|
|
||||||
provider.slice(1) +
|
provider.slice(1) +
|
||||||
"-" +
|
"-" +
|
||||||
(providersCount[provider] - 1), // add a number to the provider name
|
(providersCount[provider] - 1); // add a number to the provider name
|
||||||
streamUrl
|
|
||||||
);
|
|
||||||
} else {
|
} else {
|
||||||
providersCount[provider] = 1;
|
providersCount[provider] = 1;
|
||||||
streams.push(
|
title = provider.charAt(0).toUpperCase() + provider.slice(1);
|
||||||
provider.charAt(0).toUpperCase() + provider.slice(1),
|
|
||||||
streamUrl
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const streamObject = {
|
||||||
|
title: title,
|
||||||
|
streamUrl: streamUrl
|
||||||
|
};
|
||||||
|
|
||||||
|
// Add headers if they exist
|
||||||
|
if (headers && typeof headers === "object" && Object.keys(headers).length > 0) {
|
||||||
|
streamObject.headers = headers;
|
||||||
|
}
|
||||||
|
|
||||||
|
streams.push(streamObject);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
// Ignore the error and try the next provider
|
// Ignore the error and try the next provider
|
||||||
}
|
}
|
||||||
@@ -493,73 +546,98 @@ async function multiExtractor(providers) {
|
|||||||
async function extractStreamUrlByProvider(url, provider) {
|
async function extractStreamUrlByProvider(url, provider) {
|
||||||
if (eval(`typeof ${provider}Extractor`) !== "function") {
|
if (eval(`typeof ${provider}Extractor`) !== "function") {
|
||||||
// skip if the extractor is not defined
|
// skip if the extractor is not defined
|
||||||
console.log(`Extractor for provider ${provider} is not defined, skipping...`);
|
console.log(
|
||||||
|
`Extractor for provider ${provider} is not defined, skipping...`
|
||||||
|
);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
let uas = [
|
||||||
|
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.3",
|
||||||
|
"Mozilla/5.0 (iPhone; CPU iPhone OS 18_1_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/18.1.1 Mobile/15E148 Safari/604.1",
|
||||||
|
"Mozilla/5.0 (Linux; Android 10; SM-G973F) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Mobile Safari/537.36",
|
||||||
|
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.3",
|
||||||
|
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.1.2 Safari/605.1.15",
|
||||||
|
"Mozilla/5.0 (Linux; Android 11; Pixel 4 XL) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Mobile Safari/537.36",
|
||||||
|
];
|
||||||
let headers = {
|
let headers = {
|
||||||
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.3",
|
"User-Agent": uas[(url.length + provider.length) % uas.length], // use a different user agent based on the url and provider
|
||||||
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8",
|
"Accept":
|
||||||
|
"text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8",
|
||||||
"Accept-Language": "en-US,en;q=0.5",
|
"Accept-Language": "en-US,en;q=0.5",
|
||||||
"Referer": url,
|
"Referer": url,
|
||||||
"Connection": "keep-alive",
|
"Connection": "keep-alive",
|
||||||
"x-Requested-With": "XMLHttpRequest"
|
"x-Requested-With": "XMLHttpRequest",
|
||||||
};
|
};
|
||||||
if(provider == 'bigwarp') {
|
|
||||||
delete headers["User-Agent"];
|
switch (provider) {
|
||||||
headers["x-requested-with"] = "XMLHttpRequest";
|
case "bigwarp":
|
||||||
} else if (provider == 'vk') {
|
delete headers["User-Agent"];
|
||||||
headers["encoding"] = "windows-1251"; // required
|
break;
|
||||||
} else if (provider == 'sibnet') {
|
case "vk":
|
||||||
headers["encoding"] = "windows-1251"; // required
|
case "sibnet":
|
||||||
} else if (provider == 'supervideo') {
|
headers["encoding"] = "windows-1251"; // required
|
||||||
delete headers["User-Agent"];
|
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
|
// fetch the url
|
||||||
// and pass the response to the extractor function
|
// and pass the response to the extractor function
|
||||||
console.log("Fetching URL: " + url);
|
console.log("Fetching URL: " + url);
|
||||||
const response = await soraFetch(url, {
|
const response = await soraFetch(url, {
|
||||||
headers
|
headers,
|
||||||
});
|
});
|
||||||
|
|
||||||
console.log("Response: " + response.status);
|
console.log("Response: " + response.status);
|
||||||
let html = response.text ? await response.text() : response;
|
let html = response.text ? await response.text() : response;
|
||||||
// if title contains redirect, then get the redirect url
|
// if title contains redirect, then get the redirect url
|
||||||
const title = html.match(/<title>(.*?)<\/title>/);
|
const title = html.match(/<title>(.*?)<\/title>/);
|
||||||
if (title && title[1].toLowerCase().includes("redirect")) {
|
if (title && title[1].toLowerCase().includes("redirect")) {
|
||||||
const redirectUrl = html.match(/<meta http-equiv="refresh" content="0;url=(.*?)"/);
|
const matches = [
|
||||||
const redirectUrl2 = html.match(/window\.location\.href\s*=\s*["'](.*?)["']/);
|
/<meta http-equiv="refresh" content="0;url=(.*?)"/,
|
||||||
const redirectUrl3 = html.match(/window\.location\.replace\s*\(\s*["'](.*?)["']\s*\)/);
|
/window\.location\.href\s*=\s*["'](.*?)["']/,
|
||||||
if (redirectUrl) {
|
/window\.location\.replace\s*\(\s*["'](.*?)["']\s*\)/,
|
||||||
console.log("Redirect URL: " + redirectUrl[1]);
|
/window\.location\s*=\s*["'](.*?)["']/,
|
||||||
url = redirectUrl[1];
|
/window\.location\.assign\s*\(\s*["'](.*?)["']\s*\)/,
|
||||||
html = await soraFetch(url, {
|
/top\.location\s*=\s*["'](.*?)["']/,
|
||||||
headers
|
/top\.location\.replace\s*\(\s*["'](.*?)["']\s*\)/,
|
||||||
});
|
];
|
||||||
html = html.text ? await html.text() : html;
|
for (const match of matches) {
|
||||||
|
const redirectUrl = html.match(match);
|
||||||
} else if (redirectUrl2) {
|
if (redirectUrl && redirectUrl[1] && typeof redirectUrl[1] === "string" && redirectUrl[1].startsWith("http")) {
|
||||||
console.log("Redirect URL 2: " + redirectUrl2[1]);
|
console.log("Redirect URL found: " + redirectUrl[1]);
|
||||||
url = redirectUrl2[1];
|
url = redirectUrl[1];
|
||||||
html = await soraFetch(url, {
|
headers['Referer'] = url;
|
||||||
headers
|
headers['Host'] = url.match(/https?:\/\/([^\/]+)/)[1];
|
||||||
});
|
html = await soraFetch(url, {
|
||||||
html = html.text ? await html.text() : html;
|
headers,
|
||||||
} else if (redirectUrl3) {
|
}).then((res) => res.text());
|
||||||
console.log("Redirect URL 3: " + redirectUrl3[1]);
|
break;
|
||||||
url = redirectUrl3[1];
|
}
|
||||||
html = await soraFetch(url, {
|
|
||||||
headers
|
|
||||||
});
|
|
||||||
html = html.text ? await html.text() : html;
|
|
||||||
} else {
|
|
||||||
console.log("No redirect URL found");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// console.log("HTML: " + html);
|
// console.log("HTML: " + html);
|
||||||
switch (provider) {
|
switch (provider) {
|
||||||
case "bigwarp":
|
case "bigwarp":
|
||||||
try {
|
try {
|
||||||
return await bigwarpExtractor(html, url);
|
return await bigwarpExtractor(html, url);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@@ -608,6 +686,20 @@ async function extractStreamUrlByProvider(url, provider) {
|
|||||||
console.log("Error extracting stream URL from mp4upload:", error);
|
console.log("Error extracting stream URL from mp4upload:", error);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
case "oneupload":
|
||||||
|
try {
|
||||||
|
return await oneuploadExtractor(html, url);
|
||||||
|
} catch (error) {
|
||||||
|
console.log("Error extracting stream URL from oneupload:", error);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
case "packer":
|
||||||
|
try {
|
||||||
|
return await packerExtractor(html, url);
|
||||||
|
} catch (error) {
|
||||||
|
console.log("Error extracting stream URL from packer:", error);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
case "sendvid":
|
case "sendvid":
|
||||||
try {
|
try {
|
||||||
return await sendvidExtractor(html, url);
|
return await sendvidExtractor(html, url);
|
||||||
@@ -622,6 +714,13 @@ async function extractStreamUrlByProvider(url, provider) {
|
|||||||
console.log("Error extracting stream URL from sibnet:", error);
|
console.log("Error extracting stream URL from sibnet:", error);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
case "smoothpre":
|
||||||
|
try {
|
||||||
|
return await smoothpreExtractor(html, url);
|
||||||
|
} catch (error) {
|
||||||
|
console.log("Error extracting stream URL from smoothpre:", error);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
case "streamtape":
|
case "streamtape":
|
||||||
try {
|
try {
|
||||||
return await streamtapeExtractor(html, url);
|
return await streamtapeExtractor(html, url);
|
||||||
@@ -636,13 +735,6 @@ async function extractStreamUrlByProvider(url, provider) {
|
|||||||
console.log("Error extracting stream URL from streamup:", error);
|
console.log("Error extracting stream URL from streamup:", error);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
case "supervideo":
|
|
||||||
try {
|
|
||||||
return await supervideoExtractor(html, url);
|
|
||||||
} catch (error) {
|
|
||||||
console.log("Error extracting stream URL from supervideo:", error);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
case "uploadcx":
|
case "uploadcx":
|
||||||
try {
|
try {
|
||||||
return await uploadcxExtractor(html, url);
|
return await uploadcxExtractor(html, url);
|
||||||
@@ -691,7 +783,6 @@ async function extractStreamUrlByProvider(url, provider) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
////////////////////////////////////////////////
|
////////////////////////////////////////////////
|
||||||
// EXTRACTORS //
|
// EXTRACTORS //
|
||||||
////////////////////////////////////////////////
|
////////////////////////////////////////////////
|
||||||
@@ -1178,7 +1269,6 @@ async function megacloudExtractor(html, embedUrl) {
|
|||||||
* @author Cufiy
|
* @author Cufiy
|
||||||
*/
|
*/
|
||||||
async function mp4uploadExtractor(html, url = null) {
|
async function mp4uploadExtractor(html, url = null) {
|
||||||
// src: "https://a4.mp4upload.com:183/d/xkx3b4etz3b4quuo66rbmyqtjjoivahfxp27f35pti45rzapbvj5xwb4wuqtlpewdz4dirfp/video.mp4"
|
|
||||||
const regex = /src:\s*"([^"]+)"/;
|
const regex = /src:\s*"([^"]+)"/;
|
||||||
const match = html.match(regex);
|
const match = html.match(regex);
|
||||||
if (match) {
|
if (match) {
|
||||||
@@ -1188,6 +1278,32 @@ async function mp4uploadExtractor(html, url = null) {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
/* --- oneupload --- */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @name oneuploadExtractor
|
||||||
|
* @author 50/50
|
||||||
|
*/
|
||||||
|
async function oneuploadExtractor(data, url = null) {
|
||||||
|
const match = data.match(/sources:\s*\[\{file:"([^"]+)"\}\]/);
|
||||||
|
const fileUrl = match ? match[1] : null;
|
||||||
|
return fileUrl;
|
||||||
|
}
|
||||||
|
/* --- packer --- */
|
||||||
|
|
||||||
|
/* {REQUIRED PLUGINS: unbaser} */
|
||||||
|
/**
|
||||||
|
* @name packerExtractor
|
||||||
|
* @author 50/50
|
||||||
|
*/
|
||||||
|
async function packerExtractor(data, url = null) {
|
||||||
|
const obfuscatedScript = data.match(/<script[^>]*>\s*(eval\(function\(p,a,c,k,e,d.*?\)[\s\S]*?)<\/script>/);
|
||||||
|
const unpackedScript = unpack(obfuscatedScript[1]);
|
||||||
|
const m3u8Match = unpackedScript.match(/"hls2"\s*:\s*"([^"]+)"/);
|
||||||
|
const m3u8Url = m3u8Match[1];
|
||||||
|
return m3u8Url;
|
||||||
|
}
|
||||||
|
|
||||||
/* --- sendvid --- */
|
/* --- sendvid --- */
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1223,6 +1339,29 @@ async function sibnetExtractor(html, embedUrl) {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
/* --- smoothpre --- */
|
||||||
|
|
||||||
|
/* {REQUIRED PLUGINS: unbaser} */
|
||||||
|
/**
|
||||||
|
* @name SmoothPre Extractor
|
||||||
|
* @author 50/50
|
||||||
|
*/
|
||||||
|
async function smoothpreExtractor(data, url = null) {
|
||||||
|
console.log("Using SmoothPre Extractor");
|
||||||
|
console.log("Data Length: " + data.length);
|
||||||
|
const obfuscatedScript = data.match(/<script[^>]*>\s*(eval\(function\(p,a,c,k,e,d.*?\)[\s\S]*?)<\/script>/);
|
||||||
|
if (!obfuscatedScript || !obfuscatedScript[1]) {
|
||||||
|
console.log("No obfuscated script found");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
const unpackedScript = unpack(obfuscatedScript[1]);
|
||||||
|
|
||||||
|
const hls2Match = unpackedScript.match(/"hls2"\s*:\s*"([^"]+)"/);
|
||||||
|
const hls2Url = hls2Match ? hls2Match[1] : null;
|
||||||
|
return hls2Url;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/* --- streamtape --- */
|
/* --- streamtape --- */
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1306,26 +1445,6 @@ async function streamupExtractor(data, url = null) {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/* --- supervideo --- */
|
|
||||||
|
|
||||||
/* {REQUIRED PLUGINS: unbaser} */
|
|
||||||
/**
|
|
||||||
* @name SuperVideo Extractor
|
|
||||||
* @author 50/50
|
|
||||||
*/
|
|
||||||
async function supervideoExtractor(data, url = null) {
|
|
||||||
const obfuscatedScript = data.match(/<script[^>]*>\s*(eval\(function\(p,a,c,k,e,d.*?\)[\s\S]*?)<\/script>/);
|
|
||||||
const unpackedScript = unpack(obfuscatedScript[1]);
|
|
||||||
const regex = /file:\s*"([^"]+\.m3u8)"/;
|
|
||||||
const match = regex.exec(unpackedScript);
|
|
||||||
if (match) {
|
|
||||||
const fileUrl = match[1];
|
|
||||||
console.log("File URL:" + fileUrl);
|
|
||||||
return fileUrl;
|
|
||||||
}
|
|
||||||
return "No stream found";
|
|
||||||
}
|
|
||||||
|
|
||||||
/* --- uploadcx --- */
|
/* --- uploadcx --- */
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1390,7 +1509,7 @@ async function vidmolyExtractor(html, url = null) {
|
|||||||
const streamUrl = iframeMatch[1].startsWith("//")
|
const streamUrl = iframeMatch[1].startsWith("//")
|
||||||
? "https:" + iframeMatch[1]
|
? "https:" + iframeMatch[1]
|
||||||
: iframeMatch[1];
|
: iframeMatch[1];
|
||||||
const responseTwo = await fetchv2(streamUrl);
|
const responseTwo = await soraFetch(streamUrl);
|
||||||
const htmlTwo = await responseTwo.text();
|
const htmlTwo = await responseTwo.text();
|
||||||
const m3u8Match = htmlTwo.match(/sources:\s*\[\{file:"([^"]+\.m3u8)"/);
|
const m3u8Match = htmlTwo.match(/sources:\s*\[\{file:"([^"]+\.m3u8)"/);
|
||||||
return m3u8Match ? m3u8Match[1] : null;
|
return m3u8Match ? m3u8Match[1] : null;
|
||||||
@@ -1516,6 +1635,7 @@ function voeShiftChars(str, shift) {
|
|||||||
.join("");
|
.join("");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
////////////////////////////////////////////////
|
////////////////////////////////////////////////
|
||||||
// PLUGINS //
|
// PLUGINS //
|
||||||
////////////////////////////////////////////////
|
////////////////////////////////////////////////
|
||||||
@@ -1533,17 +1653,25 @@ function voeShiftChars(str, shift) {
|
|||||||
* @returns {Promise<Response|null>} The response from the server, or null if the
|
* @returns {Promise<Response|null>} The response from the server, or null if the
|
||||||
* request failed.
|
* request failed.
|
||||||
*/
|
*/
|
||||||
async function soraFetch(url, options = { headers: {}, method: 'GET', body: null }) {
|
async function soraFetch(
|
||||||
|
url,
|
||||||
|
options = { headers: {}, method: "GET", body: null }
|
||||||
|
) {
|
||||||
|
try {
|
||||||
|
return await fetchv2(
|
||||||
|
url,
|
||||||
|
options.headers ?? {},
|
||||||
|
options.method ?? "GET",
|
||||||
|
options.body ?? null
|
||||||
|
);
|
||||||
|
} catch (e) {
|
||||||
try {
|
try {
|
||||||
return await fetchv2(url, options.headers ?? {}, options.method ?? 'GET', options.body ?? null);
|
return await fetch(url, options);
|
||||||
} catch(e) {
|
} catch (error) {
|
||||||
try {
|
await console.log("soraFetch error: " + error.message);
|
||||||
return await fetch(url, options);
|
return null;
|
||||||
} catch(error) {
|
|
||||||
await console.log('soraFetch error: ' + error.message);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
/***********************************************************
|
/***********************************************************
|
||||||
* UNPACKER MODULE
|
* UNPACKER MODULE
|
||||||
@@ -1647,6 +1775,5 @@ function unpack(source) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* {GE END} */
|
/* {GE END} */
|
||||||
|
|||||||
+262
-134
@@ -6,9 +6,9 @@ async function searchResults(keyword) {
|
|||||||
try {
|
try {
|
||||||
const encodedKeyword = encodeURIComponent(keyword);
|
const encodedKeyword = encodeURIComponent(keyword);
|
||||||
const searchApiUrl = `https://aniworld.to/ajax/seriesSearch?keyword=${encodedKeyword}`;
|
const searchApiUrl = `https://aniworld.to/ajax/seriesSearch?keyword=${encodedKeyword}`;
|
||||||
const responseText = await fetch(searchApiUrl);
|
const responseText = await soraFetch(searchApiUrl);
|
||||||
// console.log("Search API Response: " + await responseText.text());
|
// console.log("Search API Response: " + await responseText.text());
|
||||||
const data = await JSON.parse(responseText);
|
const data = await responseText.json() || await JSON.parse(responseText);
|
||||||
console.log("Search API Data: ", data);
|
console.log("Search API Data: ", data);
|
||||||
|
|
||||||
const transformedResults = data.map((anime) => ({
|
const transformedResults = data.map((anime) => ({
|
||||||
@@ -84,9 +84,9 @@ async function extractEpisodes(url) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Replace the field "number" with the current index of each item, starting from 1
|
// Replace the field "number" with the current index of each item, starting from 1
|
||||||
finishedList.forEach((item, index) => {
|
// finishedList.forEach((item, index) => {
|
||||||
item.number = index + 1;
|
// item.number = index + 1;
|
||||||
});
|
// });
|
||||||
|
|
||||||
return JSON.stringify(finishedList);
|
return JSON.stringify(finishedList);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@@ -250,23 +250,39 @@ async function fetchSeasonEpisodes(url) {
|
|||||||
const response = await fetch(fetchUrl);
|
const response = await fetch(fetchUrl);
|
||||||
const text = response.text ? await response.text() : response;
|
const text = response.text ? await response.text() : response;
|
||||||
|
|
||||||
|
// if is filme, e.g. https://aniworld.to/anime/stream/jujutsu-kaisen/filme
|
||||||
|
let isFilme = false;
|
||||||
|
if (url.endsWith("/filme") || url.includes("/filme/")) {
|
||||||
|
isFilme = true;
|
||||||
|
}
|
||||||
|
|
||||||
// Updated regex to allow empty <strong> content
|
// Updated regex to allow empty <strong> content
|
||||||
const regex =
|
const regex =
|
||||||
/<td class="seasonEpisodeTitle">\s*<a[^>]*href="([^"]+)"[^>]*>.*?<strong>([^<]*)<\/strong>.*?<span>([^<]+)<\/span>.*?<\/a>/g;
|
/<td class="seasonEpisodeTitle">\s*<a[^>]*href="([^"]+)"[^>]*>.*?<strong>([^<]*)<\/strong>.*?<span>([^<]+)<\/span>.*?<\/a>/g;
|
||||||
|
|
||||||
const matches = [];
|
const matches = [];
|
||||||
let match;
|
let match;
|
||||||
let holderNumber = 0;
|
let number = 0;
|
||||||
|
|
||||||
while ((match = regex.exec(text)) !== null) {
|
while ((match = regex.exec(text)) !== null) {
|
||||||
const [_, link] = match;
|
const [_, link, titleRaw, span] = match;
|
||||||
matches.push({ number: holderNumber, href: `${baseUrl}${link}` });
|
number += 1;
|
||||||
|
// sendLog("Episode found:", { number, link, title, span });
|
||||||
|
|
||||||
|
let title = titleRaw.trim() || span.trim();
|
||||||
|
if (isFilme) {
|
||||||
|
title = `[FILM] ${title || span.trim() || "Untitled"}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
matches.push({ number, href: `${baseUrl}${link}`, title});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sendLog("Season Episodes:" + JSON.stringify(matches));
|
||||||
|
|
||||||
return matches;
|
return matches;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
sendLog("FetchSeasonEpisodes helper function error:" + error);
|
sendLog("FetchSeasonEpisodes helper function error:" + error);
|
||||||
return [{ number: "0", href: "https://error.org" }];
|
return [{ number: "0", href: "https://error.org", title: "Error" }];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -338,7 +354,7 @@ function base64Decode(str) {
|
|||||||
async function sendLog(message) {
|
async function sendLog(message) {
|
||||||
// send http://192.168.2.130/sora-module/log.php?action=add&message=message
|
// send http://192.168.2.130/sora-module/log.php?action=add&message=message
|
||||||
console.log(message);
|
console.log(message);
|
||||||
return;
|
// return;
|
||||||
|
|
||||||
await fetch('http://192.168.2.130/sora-module/log.php?action=add&message=' + encodeURIComponent(message))
|
await fetch('http://192.168.2.130/sora-module/log.php?action=add&message=' + encodeURIComponent(message))
|
||||||
.catch(error => {
|
.catch(error => {
|
||||||
@@ -351,7 +367,7 @@ async function sendLog(message) {
|
|||||||
// EDITING THIS FILE COULD BREAK THE UPDATER AND CAUSE ISSUES WITH THE EXTRACTOR
|
// EDITING THIS FILE COULD BREAK THE UPDATER AND CAUSE ISSUES WITH THE EXTRACTOR
|
||||||
|
|
||||||
/* {GE START} */
|
/* {GE START} */
|
||||||
/* {VERSION: 1.1.8} */
|
/* {VERSION: 1.2.0} */
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @name global_extractor.js
|
* @name global_extractor.js
|
||||||
@@ -359,8 +375,8 @@ async function sendLog(message) {
|
|||||||
* @author Cufiy
|
* @author Cufiy
|
||||||
* @url https://github.com/JMcrafter26/sora-global-extractor
|
* @url https://github.com/JMcrafter26/sora-global-extractor
|
||||||
* @license CUSTOM LICENSE - see https://github.com/JMcrafter26/sora-global-extractor/blob/main/LICENSE
|
* @license CUSTOM LICENSE - see https://github.com/JMcrafter26/sora-global-extractor/blob/main/LICENSE
|
||||||
* @date 2025-11-05 15:44:57
|
* @date 2026-01-03 19:28:28
|
||||||
* @version 1.1.8
|
* @version 1.2.0
|
||||||
* @note This file was generated automatically.
|
* @note This file was generated automatically.
|
||||||
* The global extractor comes with an auto-updating feature, so you can always get the latest version. https://github.com/JMcrafter26/sora-global-extractor#-auto-updater
|
* The global extractor comes with an auto-updating feature, so you can always get the latest version. https://github.com/JMcrafter26/sora-global-extractor#-auto-updater
|
||||||
*/
|
*/
|
||||||
@@ -370,12 +386,20 @@ function globalExtractor(providers) {
|
|||||||
for (const [url, provider] of Object.entries(providers)) {
|
for (const [url, provider] of Object.entries(providers)) {
|
||||||
try {
|
try {
|
||||||
const streamUrl = extractStreamUrlByProvider(url, provider);
|
const streamUrl = extractStreamUrlByProvider(url, provider);
|
||||||
|
// check if streamUrl is an object with streamUrl property
|
||||||
|
if (streamUrl && typeof streamUrl === "object" && !Array.isArray(streamUrl) && streamUrl.streamUrl) {
|
||||||
|
return streamUrl.streamUrl;
|
||||||
|
}
|
||||||
// check if streamUrl is not null, a string, and starts with http or https
|
// check if streamUrl is not null, a string, and starts with http or https
|
||||||
if (streamUrl && typeof streamUrl === "string" && (streamUrl.startsWith("http"))) {
|
if (
|
||||||
|
streamUrl &&
|
||||||
|
typeof streamUrl === "string" &&
|
||||||
|
streamUrl.startsWith("http")
|
||||||
|
) {
|
||||||
return streamUrl;
|
return streamUrl;
|
||||||
// if its an array, get the value that starts with http
|
// if its an array, get the value that starts with http
|
||||||
} else if (Array.isArray(streamUrl)) {
|
} else if (Array.isArray(streamUrl)) {
|
||||||
const httpStream = streamUrl.find(url => url.startsWith("http"));
|
const httpStream = streamUrl.find((url) => url.startsWith("http"));
|
||||||
if (httpStream) {
|
if (httpStream) {
|
||||||
return httpStream;
|
return httpStream;
|
||||||
}
|
}
|
||||||
@@ -383,7 +407,6 @@ function globalExtractor(providers) {
|
|||||||
// check if it's a valid stream URL
|
// check if it's a valid stream URL
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
// Ignore the error and try the next provider
|
// Ignore the error and try the next provider
|
||||||
}
|
}
|
||||||
@@ -395,16 +418,30 @@ async function multiExtractor(providers) {
|
|||||||
/* this scheme should be returned as a JSON object
|
/* this scheme should be returned as a JSON object
|
||||||
{
|
{
|
||||||
"streams": [
|
"streams": [
|
||||||
"FileMoon",
|
{
|
||||||
"https://filemoon.example/stream1.m3u8",
|
"title": "FileMoon",
|
||||||
"StreamWish",
|
"streamUrl": "https://filemoon.example/stream1.m3u8",
|
||||||
"https://streamwish.example/stream2.m3u8",
|
},
|
||||||
"Okru",
|
{
|
||||||
"https://okru.example/stream3.m3u8",
|
"title": "StreamWish",
|
||||||
"MP4",
|
"streamUrl": "https://streamwish.example/stream2.m3u8",
|
||||||
"https://mp4upload.example/stream4.mp4",
|
},
|
||||||
"Default",
|
{
|
||||||
"https://default.example/stream5.m3u8"
|
"title": "Okru",
|
||||||
|
"streamUrl": "https://okru.example/stream3.m3u8",
|
||||||
|
"headers": { // Optional headers for the stream
|
||||||
|
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.3",
|
||||||
|
"Referer": "https://okru.example/",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "MP4",
|
||||||
|
"streamUrl": "https://mp4upload.example/stream4.mp4",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Default",
|
||||||
|
"streamUrl": "https://default.example/stream5.m3u8"
|
||||||
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
@@ -416,20 +453,21 @@ async function multiExtractor(providers) {
|
|||||||
// if provider starts with "direct-", then add the url to the streams array directly
|
// if provider starts with "direct-", then add the url to the streams array directly
|
||||||
if (provider.startsWith("direct-")) {
|
if (provider.startsWith("direct-")) {
|
||||||
const directName = provider.slice(7); // remove "direct-" prefix
|
const directName = provider.slice(7); // remove "direct-" prefix
|
||||||
if (directName && directName.length > 0) {
|
const title = (directName && directName.length > 0) ? directName : "Direct";
|
||||||
streams.push(directName, url);
|
streams.push({
|
||||||
} else {
|
title: title,
|
||||||
streams.push("Direct", url); // fallback to "Direct" if no name is provided
|
streamUrl: url
|
||||||
}
|
});
|
||||||
continue; // skip to the next provider
|
continue; // skip to the next provider
|
||||||
}
|
}
|
||||||
if (provider.startsWith("direct")) {
|
if (provider.startsWith("direct")) {
|
||||||
provider = provider.slice(7); // remove "direct-" prefix
|
provider = provider.slice(7); // remove "direct-" prefix
|
||||||
if (provider && provider.length > 0) {
|
const title = (provider && provider.length > 0) ? provider : "Direct";
|
||||||
streams.push(provider, url);
|
streams.push({
|
||||||
} else {
|
title: title,
|
||||||
streams.push("Direct", url); // fallback to "Direct" if no name is provided
|
streamUrl: url
|
||||||
}
|
});
|
||||||
|
continue; // skip to the next provider
|
||||||
}
|
}
|
||||||
|
|
||||||
let customName = null; // to store the custom name if provided
|
let customName = null; // to store the custom name if provided
|
||||||
@@ -446,15 +484,24 @@ async function multiExtractor(providers) {
|
|||||||
console.log(`Skipping ${provider} as it has already 3 streams`);
|
console.log(`Skipping ${provider} as it has already 3 streams`);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
let streamUrl = await extractStreamUrlByProvider(url, provider);
|
let result = await extractStreamUrlByProvider(url, provider);
|
||||||
|
let streamUrl = null;
|
||||||
if (streamUrl && Array.isArray(streamUrl)) {
|
let headers = null;
|
||||||
const httpStream = streamUrl.find(url => url.startsWith("http"));
|
|
||||||
|
// 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) {
|
if (httpStream) {
|
||||||
streamUrl = httpStream;
|
streamUrl = httpStream;
|
||||||
}
|
}
|
||||||
|
} else if (result && typeof result === "string") {
|
||||||
|
streamUrl = result;
|
||||||
}
|
}
|
||||||
// check if provider is already in streams, if it is, add a number to it
|
|
||||||
|
// check if streamUrl is valid
|
||||||
if (
|
if (
|
||||||
!streamUrl ||
|
!streamUrl ||
|
||||||
typeof streamUrl !== "string" ||
|
typeof streamUrl !== "string" ||
|
||||||
@@ -468,22 +515,29 @@ async function multiExtractor(providers) {
|
|||||||
provider = customName;
|
provider = customName;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let title;
|
||||||
if (providersCount[provider]) {
|
if (providersCount[provider]) {
|
||||||
providersCount[provider]++;
|
providersCount[provider]++;
|
||||||
streams.push(
|
title = provider.charAt(0).toUpperCase() +
|
||||||
provider.charAt(0).toUpperCase() +
|
|
||||||
provider.slice(1) +
|
provider.slice(1) +
|
||||||
"-" +
|
"-" +
|
||||||
(providersCount[provider] - 1), // add a number to the provider name
|
(providersCount[provider] - 1); // add a number to the provider name
|
||||||
streamUrl
|
|
||||||
);
|
|
||||||
} else {
|
} else {
|
||||||
providersCount[provider] = 1;
|
providersCount[provider] = 1;
|
||||||
streams.push(
|
title = provider.charAt(0).toUpperCase() + provider.slice(1);
|
||||||
provider.charAt(0).toUpperCase() + provider.slice(1),
|
|
||||||
streamUrl
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const streamObject = {
|
||||||
|
title: title,
|
||||||
|
streamUrl: streamUrl
|
||||||
|
};
|
||||||
|
|
||||||
|
// Add headers if they exist
|
||||||
|
if (headers && typeof headers === "object" && Object.keys(headers).length > 0) {
|
||||||
|
streamObject.headers = headers;
|
||||||
|
}
|
||||||
|
|
||||||
|
streams.push(streamObject);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
// Ignore the error and try the next provider
|
// Ignore the error and try the next provider
|
||||||
}
|
}
|
||||||
@@ -494,73 +548,98 @@ async function multiExtractor(providers) {
|
|||||||
async function extractStreamUrlByProvider(url, provider) {
|
async function extractStreamUrlByProvider(url, provider) {
|
||||||
if (eval(`typeof ${provider}Extractor`) !== "function") {
|
if (eval(`typeof ${provider}Extractor`) !== "function") {
|
||||||
// skip if the extractor is not defined
|
// skip if the extractor is not defined
|
||||||
console.log(`Extractor for provider ${provider} is not defined, skipping...`);
|
console.log(
|
||||||
|
`Extractor for provider ${provider} is not defined, skipping...`
|
||||||
|
);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
let uas = [
|
||||||
|
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.3",
|
||||||
|
"Mozilla/5.0 (iPhone; CPU iPhone OS 18_1_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/18.1.1 Mobile/15E148 Safari/604.1",
|
||||||
|
"Mozilla/5.0 (Linux; Android 10; SM-G973F) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Mobile Safari/537.36",
|
||||||
|
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.3",
|
||||||
|
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.1.2 Safari/605.1.15",
|
||||||
|
"Mozilla/5.0 (Linux; Android 11; Pixel 4 XL) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Mobile Safari/537.36",
|
||||||
|
];
|
||||||
let headers = {
|
let headers = {
|
||||||
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.3",
|
"User-Agent": uas[(url.length + provider.length) % uas.length], // use a different user agent based on the url and provider
|
||||||
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8",
|
"Accept":
|
||||||
|
"text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8",
|
||||||
"Accept-Language": "en-US,en;q=0.5",
|
"Accept-Language": "en-US,en;q=0.5",
|
||||||
"Referer": url,
|
"Referer": url,
|
||||||
"Connection": "keep-alive",
|
"Connection": "keep-alive",
|
||||||
"x-Requested-With": "XMLHttpRequest"
|
"x-Requested-With": "XMLHttpRequest",
|
||||||
};
|
};
|
||||||
if(provider == 'bigwarp') {
|
|
||||||
delete headers["User-Agent"];
|
switch (provider) {
|
||||||
headers["x-requested-with"] = "XMLHttpRequest";
|
case "bigwarp":
|
||||||
} else if (provider == 'vk') {
|
delete headers["User-Agent"];
|
||||||
headers["encoding"] = "windows-1251"; // required
|
break;
|
||||||
} else if (provider == 'sibnet') {
|
case "vk":
|
||||||
headers["encoding"] = "windows-1251"; // required
|
case "sibnet":
|
||||||
} else if (provider == 'supervideo') {
|
headers["encoding"] = "windows-1251"; // required
|
||||||
delete headers["User-Agent"];
|
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
|
// fetch the url
|
||||||
// and pass the response to the extractor function
|
// and pass the response to the extractor function
|
||||||
console.log("Fetching URL: " + url);
|
console.log("Fetching URL: " + url);
|
||||||
const response = await soraFetch(url, {
|
const response = await soraFetch(url, {
|
||||||
headers
|
headers,
|
||||||
});
|
});
|
||||||
|
|
||||||
console.log("Response: " + response.status);
|
console.log("Response: " + response.status);
|
||||||
let html = response.text ? await response.text() : response;
|
let html = response.text ? await response.text() : response;
|
||||||
// if title contains redirect, then get the redirect url
|
// if title contains redirect, then get the redirect url
|
||||||
const title = html.match(/<title>(.*?)<\/title>/);
|
const title = html.match(/<title>(.*?)<\/title>/);
|
||||||
if (title && title[1].toLowerCase().includes("redirect")) {
|
if (title && title[1].toLowerCase().includes("redirect")) {
|
||||||
const redirectUrl = html.match(/<meta http-equiv="refresh" content="0;url=(.*?)"/);
|
const matches = [
|
||||||
const redirectUrl2 = html.match(/window\.location\.href\s*=\s*["'](.*?)["']/);
|
/<meta http-equiv="refresh" content="0;url=(.*?)"/,
|
||||||
const redirectUrl3 = html.match(/window\.location\.replace\s*\(\s*["'](.*?)["']\s*\)/);
|
/window\.location\.href\s*=\s*["'](.*?)["']/,
|
||||||
if (redirectUrl) {
|
/window\.location\.replace\s*\(\s*["'](.*?)["']\s*\)/,
|
||||||
console.log("Redirect URL: " + redirectUrl[1]);
|
/window\.location\s*=\s*["'](.*?)["']/,
|
||||||
url = redirectUrl[1];
|
/window\.location\.assign\s*\(\s*["'](.*?)["']\s*\)/,
|
||||||
html = await soraFetch(url, {
|
/top\.location\s*=\s*["'](.*?)["']/,
|
||||||
headers
|
/top\.location\.replace\s*\(\s*["'](.*?)["']\s*\)/,
|
||||||
});
|
];
|
||||||
html = html.text ? await html.text() : html;
|
for (const match of matches) {
|
||||||
|
const redirectUrl = html.match(match);
|
||||||
} else if (redirectUrl2) {
|
if (redirectUrl && redirectUrl[1] && typeof redirectUrl[1] === "string" && redirectUrl[1].startsWith("http")) {
|
||||||
console.log("Redirect URL 2: " + redirectUrl2[1]);
|
console.log("Redirect URL found: " + redirectUrl[1]);
|
||||||
url = redirectUrl2[1];
|
url = redirectUrl[1];
|
||||||
html = await soraFetch(url, {
|
headers['Referer'] = url;
|
||||||
headers
|
headers['Host'] = url.match(/https?:\/\/([^\/]+)/)[1];
|
||||||
});
|
html = await soraFetch(url, {
|
||||||
html = html.text ? await html.text() : html;
|
headers,
|
||||||
} else if (redirectUrl3) {
|
}).then((res) => res.text());
|
||||||
console.log("Redirect URL 3: " + redirectUrl3[1]);
|
break;
|
||||||
url = redirectUrl3[1];
|
}
|
||||||
html = await soraFetch(url, {
|
|
||||||
headers
|
|
||||||
});
|
|
||||||
html = html.text ? await html.text() : html;
|
|
||||||
} else {
|
|
||||||
console.log("No redirect URL found");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// console.log("HTML: " + html);
|
// console.log("HTML: " + html);
|
||||||
switch (provider) {
|
switch (provider) {
|
||||||
case "bigwarp":
|
case "bigwarp":
|
||||||
try {
|
try {
|
||||||
return await bigwarpExtractor(html, url);
|
return await bigwarpExtractor(html, url);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@@ -609,6 +688,20 @@ async function extractStreamUrlByProvider(url, provider) {
|
|||||||
console.log("Error extracting stream URL from mp4upload:", error);
|
console.log("Error extracting stream URL from mp4upload:", error);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
case "oneupload":
|
||||||
|
try {
|
||||||
|
return await oneuploadExtractor(html, url);
|
||||||
|
} catch (error) {
|
||||||
|
console.log("Error extracting stream URL from oneupload:", error);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
case "packer":
|
||||||
|
try {
|
||||||
|
return await packerExtractor(html, url);
|
||||||
|
} catch (error) {
|
||||||
|
console.log("Error extracting stream URL from packer:", error);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
case "sendvid":
|
case "sendvid":
|
||||||
try {
|
try {
|
||||||
return await sendvidExtractor(html, url);
|
return await sendvidExtractor(html, url);
|
||||||
@@ -623,6 +716,13 @@ async function extractStreamUrlByProvider(url, provider) {
|
|||||||
console.log("Error extracting stream URL from sibnet:", error);
|
console.log("Error extracting stream URL from sibnet:", error);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
case "smoothpre":
|
||||||
|
try {
|
||||||
|
return await smoothpreExtractor(html, url);
|
||||||
|
} catch (error) {
|
||||||
|
console.log("Error extracting stream URL from smoothpre:", error);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
case "streamtape":
|
case "streamtape":
|
||||||
try {
|
try {
|
||||||
return await streamtapeExtractor(html, url);
|
return await streamtapeExtractor(html, url);
|
||||||
@@ -637,13 +737,6 @@ async function extractStreamUrlByProvider(url, provider) {
|
|||||||
console.log("Error extracting stream URL from streamup:", error);
|
console.log("Error extracting stream URL from streamup:", error);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
case "supervideo":
|
|
||||||
try {
|
|
||||||
return await supervideoExtractor(html, url);
|
|
||||||
} catch (error) {
|
|
||||||
console.log("Error extracting stream URL from supervideo:", error);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
case "uploadcx":
|
case "uploadcx":
|
||||||
try {
|
try {
|
||||||
return await uploadcxExtractor(html, url);
|
return await uploadcxExtractor(html, url);
|
||||||
@@ -692,7 +785,6 @@ async function extractStreamUrlByProvider(url, provider) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
////////////////////////////////////////////////
|
////////////////////////////////////////////////
|
||||||
// EXTRACTORS //
|
// EXTRACTORS //
|
||||||
////////////////////////////////////////////////
|
////////////////////////////////////////////////
|
||||||
@@ -1179,7 +1271,6 @@ async function megacloudExtractor(html, embedUrl) {
|
|||||||
* @author Cufiy
|
* @author Cufiy
|
||||||
*/
|
*/
|
||||||
async function mp4uploadExtractor(html, url = null) {
|
async function mp4uploadExtractor(html, url = null) {
|
||||||
// src: "https://a4.mp4upload.com:183/d/xkx3b4etz3b4quuo66rbmyqtjjoivahfxp27f35pti45rzapbvj5xwb4wuqtlpewdz4dirfp/video.mp4"
|
|
||||||
const regex = /src:\s*"([^"]+)"/;
|
const regex = /src:\s*"([^"]+)"/;
|
||||||
const match = html.match(regex);
|
const match = html.match(regex);
|
||||||
if (match) {
|
if (match) {
|
||||||
@@ -1189,6 +1280,32 @@ async function mp4uploadExtractor(html, url = null) {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
/* --- oneupload --- */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @name oneuploadExtractor
|
||||||
|
* @author 50/50
|
||||||
|
*/
|
||||||
|
async function oneuploadExtractor(data, url = null) {
|
||||||
|
const match = data.match(/sources:\s*\[\{file:"([^"]+)"\}\]/);
|
||||||
|
const fileUrl = match ? match[1] : null;
|
||||||
|
return fileUrl;
|
||||||
|
}
|
||||||
|
/* --- packer --- */
|
||||||
|
|
||||||
|
/* {REQUIRED PLUGINS: unbaser} */
|
||||||
|
/**
|
||||||
|
* @name packerExtractor
|
||||||
|
* @author 50/50
|
||||||
|
*/
|
||||||
|
async function packerExtractor(data, url = null) {
|
||||||
|
const obfuscatedScript = data.match(/<script[^>]*>\s*(eval\(function\(p,a,c,k,e,d.*?\)[\s\S]*?)<\/script>/);
|
||||||
|
const unpackedScript = unpack(obfuscatedScript[1]);
|
||||||
|
const m3u8Match = unpackedScript.match(/"hls2"\s*:\s*"([^"]+)"/);
|
||||||
|
const m3u8Url = m3u8Match[1];
|
||||||
|
return m3u8Url;
|
||||||
|
}
|
||||||
|
|
||||||
/* --- sendvid --- */
|
/* --- sendvid --- */
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1224,6 +1341,29 @@ async function sibnetExtractor(html, embedUrl) {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
/* --- smoothpre --- */
|
||||||
|
|
||||||
|
/* {REQUIRED PLUGINS: unbaser} */
|
||||||
|
/**
|
||||||
|
* @name SmoothPre Extractor
|
||||||
|
* @author 50/50
|
||||||
|
*/
|
||||||
|
async function smoothpreExtractor(data, url = null) {
|
||||||
|
console.log("Using SmoothPre Extractor");
|
||||||
|
console.log("Data Length: " + data.length);
|
||||||
|
const obfuscatedScript = data.match(/<script[^>]*>\s*(eval\(function\(p,a,c,k,e,d.*?\)[\s\S]*?)<\/script>/);
|
||||||
|
if (!obfuscatedScript || !obfuscatedScript[1]) {
|
||||||
|
console.log("No obfuscated script found");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
const unpackedScript = unpack(obfuscatedScript[1]);
|
||||||
|
|
||||||
|
const hls2Match = unpackedScript.match(/"hls2"\s*:\s*"([^"]+)"/);
|
||||||
|
const hls2Url = hls2Match ? hls2Match[1] : null;
|
||||||
|
return hls2Url;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/* --- streamtape --- */
|
/* --- streamtape --- */
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1307,26 +1447,6 @@ async function streamupExtractor(data, url = null) {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/* --- supervideo --- */
|
|
||||||
|
|
||||||
/* {REQUIRED PLUGINS: unbaser} */
|
|
||||||
/**
|
|
||||||
* @name SuperVideo Extractor
|
|
||||||
* @author 50/50
|
|
||||||
*/
|
|
||||||
async function supervideoExtractor(data, url = null) {
|
|
||||||
const obfuscatedScript = data.match(/<script[^>]*>\s*(eval\(function\(p,a,c,k,e,d.*?\)[\s\S]*?)<\/script>/);
|
|
||||||
const unpackedScript = unpack(obfuscatedScript[1]);
|
|
||||||
const regex = /file:\s*"([^"]+\.m3u8)"/;
|
|
||||||
const match = regex.exec(unpackedScript);
|
|
||||||
if (match) {
|
|
||||||
const fileUrl = match[1];
|
|
||||||
console.log("File URL:" + fileUrl);
|
|
||||||
return fileUrl;
|
|
||||||
}
|
|
||||||
return "No stream found";
|
|
||||||
}
|
|
||||||
|
|
||||||
/* --- uploadcx --- */
|
/* --- uploadcx --- */
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1391,7 +1511,7 @@ async function vidmolyExtractor(html, url = null) {
|
|||||||
const streamUrl = iframeMatch[1].startsWith("//")
|
const streamUrl = iframeMatch[1].startsWith("//")
|
||||||
? "https:" + iframeMatch[1]
|
? "https:" + iframeMatch[1]
|
||||||
: iframeMatch[1];
|
: iframeMatch[1];
|
||||||
const responseTwo = await fetchv2(streamUrl);
|
const responseTwo = await soraFetch(streamUrl);
|
||||||
const htmlTwo = await responseTwo.text();
|
const htmlTwo = await responseTwo.text();
|
||||||
const m3u8Match = htmlTwo.match(/sources:\s*\[\{file:"([^"]+\.m3u8)"/);
|
const m3u8Match = htmlTwo.match(/sources:\s*\[\{file:"([^"]+\.m3u8)"/);
|
||||||
return m3u8Match ? m3u8Match[1] : null;
|
return m3u8Match ? m3u8Match[1] : null;
|
||||||
@@ -1517,6 +1637,7 @@ function voeShiftChars(str, shift) {
|
|||||||
.join("");
|
.join("");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
////////////////////////////////////////////////
|
////////////////////////////////////////////////
|
||||||
// PLUGINS //
|
// PLUGINS //
|
||||||
////////////////////////////////////////////////
|
////////////////////////////////////////////////
|
||||||
@@ -1534,17 +1655,25 @@ function voeShiftChars(str, shift) {
|
|||||||
* @returns {Promise<Response|null>} The response from the server, or null if the
|
* @returns {Promise<Response|null>} The response from the server, or null if the
|
||||||
* request failed.
|
* request failed.
|
||||||
*/
|
*/
|
||||||
async function soraFetch(url, options = { headers: {}, method: 'GET', body: null }) {
|
async function soraFetch(
|
||||||
|
url,
|
||||||
|
options = { headers: {}, method: "GET", body: null }
|
||||||
|
) {
|
||||||
|
try {
|
||||||
|
return await fetchv2(
|
||||||
|
url,
|
||||||
|
options.headers ?? {},
|
||||||
|
options.method ?? "GET",
|
||||||
|
options.body ?? null
|
||||||
|
);
|
||||||
|
} catch (e) {
|
||||||
try {
|
try {
|
||||||
return await fetchv2(url, options.headers ?? {}, options.method ?? 'GET', options.body ?? null);
|
return await fetch(url, options);
|
||||||
} catch(e) {
|
} catch (error) {
|
||||||
try {
|
await console.log("soraFetch error: " + error.message);
|
||||||
return await fetch(url, options);
|
return null;
|
||||||
} catch(error) {
|
|
||||||
await console.log('soraFetch error: ' + error.message);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
/***********************************************************
|
/***********************************************************
|
||||||
* UNPACKER MODULE
|
* UNPACKER MODULE
|
||||||
@@ -1648,6 +1777,5 @@ function unpack(source) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* {GE END} */
|
/* {GE END} */
|
||||||
|
|||||||
+263
-134
@@ -6,9 +6,9 @@ async function searchResults(keyword) {
|
|||||||
try {
|
try {
|
||||||
const encodedKeyword = encodeURIComponent(keyword);
|
const encodedKeyword = encodeURIComponent(keyword);
|
||||||
const searchApiUrl = `https://aniworld.to/ajax/seriesSearch?keyword=${encodedKeyword}`;
|
const searchApiUrl = `https://aniworld.to/ajax/seriesSearch?keyword=${encodedKeyword}`;
|
||||||
const responseText = await fetch(searchApiUrl);
|
const responseText = await soraFetch(searchApiUrl);
|
||||||
// console.log("Search API Response: " + await responseText.text());
|
// console.log("Search API Response: " + await responseText.text());
|
||||||
const data = await JSON.parse(responseText);
|
const data = await responseText.json() || await JSON.parse(responseText);
|
||||||
console.log("Search API Data: ", data);
|
console.log("Search API Data: ", data);
|
||||||
|
|
||||||
const transformedResults = data.map((anime) => ({
|
const transformedResults = data.map((anime) => ({
|
||||||
@@ -84,9 +84,9 @@ async function extractEpisodes(url) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Replace the field "number" with the current index of each item, starting from 1
|
// Replace the field "number" with the current index of each item, starting from 1
|
||||||
finishedList.forEach((item, index) => {
|
// finishedList.forEach((item, index) => {
|
||||||
item.number = index + 1;
|
// item.number = index + 1;
|
||||||
});
|
// });
|
||||||
|
|
||||||
return JSON.stringify(finishedList);
|
return JSON.stringify(finishedList);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@@ -99,6 +99,7 @@ async function extractStreamUrl(url) {
|
|||||||
try {
|
try {
|
||||||
const baseUrl = "https://aniworld.to";
|
const baseUrl = "https://aniworld.to";
|
||||||
const fetchUrl = `${url}`;
|
const fetchUrl = `${url}`;
|
||||||
|
sendLog("Fetching URL: " + fetchUrl);
|
||||||
const response = await fetch(fetchUrl);
|
const response = await fetch(fetchUrl);
|
||||||
const text = response.text ? await response.text() : response;
|
const text = response.text ? await response.text() : response;
|
||||||
|
|
||||||
@@ -249,23 +250,39 @@ async function fetchSeasonEpisodes(url) {
|
|||||||
const response = await fetch(fetchUrl);
|
const response = await fetch(fetchUrl);
|
||||||
const text = response.text ? await response.text() : response;
|
const text = response.text ? await response.text() : response;
|
||||||
|
|
||||||
|
// if is filme, e.g. https://aniworld.to/anime/stream/jujutsu-kaisen/filme
|
||||||
|
let isFilme = false;
|
||||||
|
if (url.endsWith("/filme") || url.includes("/filme/")) {
|
||||||
|
isFilme = true;
|
||||||
|
}
|
||||||
|
|
||||||
// Updated regex to allow empty <strong> content
|
// Updated regex to allow empty <strong> content
|
||||||
const regex =
|
const regex =
|
||||||
/<td class="seasonEpisodeTitle">\s*<a[^>]*href="([^"]+)"[^>]*>.*?<strong>([^<]*)<\/strong>.*?<span>([^<]+)<\/span>.*?<\/a>/g;
|
/<td class="seasonEpisodeTitle">\s*<a[^>]*href="([^"]+)"[^>]*>.*?<strong>([^<]*)<\/strong>.*?<span>([^<]+)<\/span>.*?<\/a>/g;
|
||||||
|
|
||||||
const matches = [];
|
const matches = [];
|
||||||
let match;
|
let match;
|
||||||
let holderNumber = 0;
|
let number = 0;
|
||||||
|
|
||||||
while ((match = regex.exec(text)) !== null) {
|
while ((match = regex.exec(text)) !== null) {
|
||||||
const [_, link] = match;
|
const [_, link, titleRaw, span] = match;
|
||||||
matches.push({ number: holderNumber, href: `${baseUrl}${link}` });
|
number += 1;
|
||||||
|
// sendLog("Episode found:", { number, link, title, span });
|
||||||
|
|
||||||
|
let title = titleRaw.trim() || span.trim();
|
||||||
|
if (isFilme) {
|
||||||
|
title = `[FILM] ${title || span.trim() || "Untitled"}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
matches.push({ number, href: `${baseUrl}${link}`, title});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sendLog("Season Episodes:" + JSON.stringify(matches));
|
||||||
|
|
||||||
return matches;
|
return matches;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
sendLog("FetchSeasonEpisodes helper function error:" + error);
|
sendLog("FetchSeasonEpisodes helper function error:" + error);
|
||||||
return [{ number: "0", href: "https://error.org" }];
|
return [{ number: "0", href: "https://error.org", title: "Error" }];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -337,7 +354,7 @@ function base64Decode(str) {
|
|||||||
async function sendLog(message) {
|
async function sendLog(message) {
|
||||||
// send http://192.168.2.130/sora-module/log.php?action=add&message=message
|
// send http://192.168.2.130/sora-module/log.php?action=add&message=message
|
||||||
console.log(message);
|
console.log(message);
|
||||||
return;
|
// return;
|
||||||
|
|
||||||
await fetch('http://192.168.2.130/sora-module/log.php?action=add&message=' + encodeURIComponent(message))
|
await fetch('http://192.168.2.130/sora-module/log.php?action=add&message=' + encodeURIComponent(message))
|
||||||
.catch(error => {
|
.catch(error => {
|
||||||
@@ -350,7 +367,7 @@ async function sendLog(message) {
|
|||||||
// EDITING THIS FILE COULD BREAK THE UPDATER AND CAUSE ISSUES WITH THE EXTRACTOR
|
// EDITING THIS FILE COULD BREAK THE UPDATER AND CAUSE ISSUES WITH THE EXTRACTOR
|
||||||
|
|
||||||
/* {GE START} */
|
/* {GE START} */
|
||||||
/* {VERSION: 1.1.8} */
|
/* {VERSION: 1.2.0} */
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @name global_extractor.js
|
* @name global_extractor.js
|
||||||
@@ -358,8 +375,8 @@ async function sendLog(message) {
|
|||||||
* @author Cufiy
|
* @author Cufiy
|
||||||
* @url https://github.com/JMcrafter26/sora-global-extractor
|
* @url https://github.com/JMcrafter26/sora-global-extractor
|
||||||
* @license CUSTOM LICENSE - see https://github.com/JMcrafter26/sora-global-extractor/blob/main/LICENSE
|
* @license CUSTOM LICENSE - see https://github.com/JMcrafter26/sora-global-extractor/blob/main/LICENSE
|
||||||
* @date 2025-11-05 15:44:57
|
* @date 2026-01-03 19:28:28
|
||||||
* @version 1.1.8
|
* @version 1.2.0
|
||||||
* @note This file was generated automatically.
|
* @note This file was generated automatically.
|
||||||
* The global extractor comes with an auto-updating feature, so you can always get the latest version. https://github.com/JMcrafter26/sora-global-extractor#-auto-updater
|
* The global extractor comes with an auto-updating feature, so you can always get the latest version. https://github.com/JMcrafter26/sora-global-extractor#-auto-updater
|
||||||
*/
|
*/
|
||||||
@@ -369,12 +386,20 @@ function globalExtractor(providers) {
|
|||||||
for (const [url, provider] of Object.entries(providers)) {
|
for (const [url, provider] of Object.entries(providers)) {
|
||||||
try {
|
try {
|
||||||
const streamUrl = extractStreamUrlByProvider(url, provider);
|
const streamUrl = extractStreamUrlByProvider(url, provider);
|
||||||
|
// check if streamUrl is an object with streamUrl property
|
||||||
|
if (streamUrl && typeof streamUrl === "object" && !Array.isArray(streamUrl) && streamUrl.streamUrl) {
|
||||||
|
return streamUrl.streamUrl;
|
||||||
|
}
|
||||||
// check if streamUrl is not null, a string, and starts with http or https
|
// check if streamUrl is not null, a string, and starts with http or https
|
||||||
if (streamUrl && typeof streamUrl === "string" && (streamUrl.startsWith("http"))) {
|
if (
|
||||||
|
streamUrl &&
|
||||||
|
typeof streamUrl === "string" &&
|
||||||
|
streamUrl.startsWith("http")
|
||||||
|
) {
|
||||||
return streamUrl;
|
return streamUrl;
|
||||||
// if its an array, get the value that starts with http
|
// if its an array, get the value that starts with http
|
||||||
} else if (Array.isArray(streamUrl)) {
|
} else if (Array.isArray(streamUrl)) {
|
||||||
const httpStream = streamUrl.find(url => url.startsWith("http"));
|
const httpStream = streamUrl.find((url) => url.startsWith("http"));
|
||||||
if (httpStream) {
|
if (httpStream) {
|
||||||
return httpStream;
|
return httpStream;
|
||||||
}
|
}
|
||||||
@@ -382,7 +407,6 @@ function globalExtractor(providers) {
|
|||||||
// check if it's a valid stream URL
|
// check if it's a valid stream URL
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
// Ignore the error and try the next provider
|
// Ignore the error and try the next provider
|
||||||
}
|
}
|
||||||
@@ -394,16 +418,30 @@ async function multiExtractor(providers) {
|
|||||||
/* this scheme should be returned as a JSON object
|
/* this scheme should be returned as a JSON object
|
||||||
{
|
{
|
||||||
"streams": [
|
"streams": [
|
||||||
"FileMoon",
|
{
|
||||||
"https://filemoon.example/stream1.m3u8",
|
"title": "FileMoon",
|
||||||
"StreamWish",
|
"streamUrl": "https://filemoon.example/stream1.m3u8",
|
||||||
"https://streamwish.example/stream2.m3u8",
|
},
|
||||||
"Okru",
|
{
|
||||||
"https://okru.example/stream3.m3u8",
|
"title": "StreamWish",
|
||||||
"MP4",
|
"streamUrl": "https://streamwish.example/stream2.m3u8",
|
||||||
"https://mp4upload.example/stream4.mp4",
|
},
|
||||||
"Default",
|
{
|
||||||
"https://default.example/stream5.m3u8"
|
"title": "Okru",
|
||||||
|
"streamUrl": "https://okru.example/stream3.m3u8",
|
||||||
|
"headers": { // Optional headers for the stream
|
||||||
|
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.3",
|
||||||
|
"Referer": "https://okru.example/",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "MP4",
|
||||||
|
"streamUrl": "https://mp4upload.example/stream4.mp4",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Default",
|
||||||
|
"streamUrl": "https://default.example/stream5.m3u8"
|
||||||
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
@@ -415,20 +453,21 @@ async function multiExtractor(providers) {
|
|||||||
// if provider starts with "direct-", then add the url to the streams array directly
|
// if provider starts with "direct-", then add the url to the streams array directly
|
||||||
if (provider.startsWith("direct-")) {
|
if (provider.startsWith("direct-")) {
|
||||||
const directName = provider.slice(7); // remove "direct-" prefix
|
const directName = provider.slice(7); // remove "direct-" prefix
|
||||||
if (directName && directName.length > 0) {
|
const title = (directName && directName.length > 0) ? directName : "Direct";
|
||||||
streams.push(directName, url);
|
streams.push({
|
||||||
} else {
|
title: title,
|
||||||
streams.push("Direct", url); // fallback to "Direct" if no name is provided
|
streamUrl: url
|
||||||
}
|
});
|
||||||
continue; // skip to the next provider
|
continue; // skip to the next provider
|
||||||
}
|
}
|
||||||
if (provider.startsWith("direct")) {
|
if (provider.startsWith("direct")) {
|
||||||
provider = provider.slice(7); // remove "direct-" prefix
|
provider = provider.slice(7); // remove "direct-" prefix
|
||||||
if (provider && provider.length > 0) {
|
const title = (provider && provider.length > 0) ? provider : "Direct";
|
||||||
streams.push(provider, url);
|
streams.push({
|
||||||
} else {
|
title: title,
|
||||||
streams.push("Direct", url); // fallback to "Direct" if no name is provided
|
streamUrl: url
|
||||||
}
|
});
|
||||||
|
continue; // skip to the next provider
|
||||||
}
|
}
|
||||||
|
|
||||||
let customName = null; // to store the custom name if provided
|
let customName = null; // to store the custom name if provided
|
||||||
@@ -445,15 +484,24 @@ async function multiExtractor(providers) {
|
|||||||
console.log(`Skipping ${provider} as it has already 3 streams`);
|
console.log(`Skipping ${provider} as it has already 3 streams`);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
let streamUrl = await extractStreamUrlByProvider(url, provider);
|
let result = await extractStreamUrlByProvider(url, provider);
|
||||||
|
let streamUrl = null;
|
||||||
if (streamUrl && Array.isArray(streamUrl)) {
|
let headers = null;
|
||||||
const httpStream = streamUrl.find(url => url.startsWith("http"));
|
|
||||||
|
// 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) {
|
if (httpStream) {
|
||||||
streamUrl = httpStream;
|
streamUrl = httpStream;
|
||||||
}
|
}
|
||||||
|
} else if (result && typeof result === "string") {
|
||||||
|
streamUrl = result;
|
||||||
}
|
}
|
||||||
// check if provider is already in streams, if it is, add a number to it
|
|
||||||
|
// check if streamUrl is valid
|
||||||
if (
|
if (
|
||||||
!streamUrl ||
|
!streamUrl ||
|
||||||
typeof streamUrl !== "string" ||
|
typeof streamUrl !== "string" ||
|
||||||
@@ -467,22 +515,29 @@ async function multiExtractor(providers) {
|
|||||||
provider = customName;
|
provider = customName;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let title;
|
||||||
if (providersCount[provider]) {
|
if (providersCount[provider]) {
|
||||||
providersCount[provider]++;
|
providersCount[provider]++;
|
||||||
streams.push(
|
title = provider.charAt(0).toUpperCase() +
|
||||||
provider.charAt(0).toUpperCase() +
|
|
||||||
provider.slice(1) +
|
provider.slice(1) +
|
||||||
"-" +
|
"-" +
|
||||||
(providersCount[provider] - 1), // add a number to the provider name
|
(providersCount[provider] - 1); // add a number to the provider name
|
||||||
streamUrl
|
|
||||||
);
|
|
||||||
} else {
|
} else {
|
||||||
providersCount[provider] = 1;
|
providersCount[provider] = 1;
|
||||||
streams.push(
|
title = provider.charAt(0).toUpperCase() + provider.slice(1);
|
||||||
provider.charAt(0).toUpperCase() + provider.slice(1),
|
|
||||||
streamUrl
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const streamObject = {
|
||||||
|
title: title,
|
||||||
|
streamUrl: streamUrl
|
||||||
|
};
|
||||||
|
|
||||||
|
// Add headers if they exist
|
||||||
|
if (headers && typeof headers === "object" && Object.keys(headers).length > 0) {
|
||||||
|
streamObject.headers = headers;
|
||||||
|
}
|
||||||
|
|
||||||
|
streams.push(streamObject);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
// Ignore the error and try the next provider
|
// Ignore the error and try the next provider
|
||||||
}
|
}
|
||||||
@@ -493,73 +548,98 @@ async function multiExtractor(providers) {
|
|||||||
async function extractStreamUrlByProvider(url, provider) {
|
async function extractStreamUrlByProvider(url, provider) {
|
||||||
if (eval(`typeof ${provider}Extractor`) !== "function") {
|
if (eval(`typeof ${provider}Extractor`) !== "function") {
|
||||||
// skip if the extractor is not defined
|
// skip if the extractor is not defined
|
||||||
console.log(`Extractor for provider ${provider} is not defined, skipping...`);
|
console.log(
|
||||||
|
`Extractor for provider ${provider} is not defined, skipping...`
|
||||||
|
);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
let uas = [
|
||||||
|
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.3",
|
||||||
|
"Mozilla/5.0 (iPhone; CPU iPhone OS 18_1_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/18.1.1 Mobile/15E148 Safari/604.1",
|
||||||
|
"Mozilla/5.0 (Linux; Android 10; SM-G973F) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Mobile Safari/537.36",
|
||||||
|
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.3",
|
||||||
|
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.1.2 Safari/605.1.15",
|
||||||
|
"Mozilla/5.0 (Linux; Android 11; Pixel 4 XL) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Mobile Safari/537.36",
|
||||||
|
];
|
||||||
let headers = {
|
let headers = {
|
||||||
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.3",
|
"User-Agent": uas[(url.length + provider.length) % uas.length], // use a different user agent based on the url and provider
|
||||||
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8",
|
"Accept":
|
||||||
|
"text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8",
|
||||||
"Accept-Language": "en-US,en;q=0.5",
|
"Accept-Language": "en-US,en;q=0.5",
|
||||||
"Referer": url,
|
"Referer": url,
|
||||||
"Connection": "keep-alive",
|
"Connection": "keep-alive",
|
||||||
"x-Requested-With": "XMLHttpRequest"
|
"x-Requested-With": "XMLHttpRequest",
|
||||||
};
|
};
|
||||||
if(provider == 'bigwarp') {
|
|
||||||
delete headers["User-Agent"];
|
switch (provider) {
|
||||||
headers["x-requested-with"] = "XMLHttpRequest";
|
case "bigwarp":
|
||||||
} else if (provider == 'vk') {
|
delete headers["User-Agent"];
|
||||||
headers["encoding"] = "windows-1251"; // required
|
break;
|
||||||
} else if (provider == 'sibnet') {
|
case "vk":
|
||||||
headers["encoding"] = "windows-1251"; // required
|
case "sibnet":
|
||||||
} else if (provider == 'supervideo') {
|
headers["encoding"] = "windows-1251"; // required
|
||||||
delete headers["User-Agent"];
|
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
|
// fetch the url
|
||||||
// and pass the response to the extractor function
|
// and pass the response to the extractor function
|
||||||
console.log("Fetching URL: " + url);
|
console.log("Fetching URL: " + url);
|
||||||
const response = await soraFetch(url, {
|
const response = await soraFetch(url, {
|
||||||
headers
|
headers,
|
||||||
});
|
});
|
||||||
|
|
||||||
console.log("Response: " + response.status);
|
console.log("Response: " + response.status);
|
||||||
let html = response.text ? await response.text() : response;
|
let html = response.text ? await response.text() : response;
|
||||||
// if title contains redirect, then get the redirect url
|
// if title contains redirect, then get the redirect url
|
||||||
const title = html.match(/<title>(.*?)<\/title>/);
|
const title = html.match(/<title>(.*?)<\/title>/);
|
||||||
if (title && title[1].toLowerCase().includes("redirect")) {
|
if (title && title[1].toLowerCase().includes("redirect")) {
|
||||||
const redirectUrl = html.match(/<meta http-equiv="refresh" content="0;url=(.*?)"/);
|
const matches = [
|
||||||
const redirectUrl2 = html.match(/window\.location\.href\s*=\s*["'](.*?)["']/);
|
/<meta http-equiv="refresh" content="0;url=(.*?)"/,
|
||||||
const redirectUrl3 = html.match(/window\.location\.replace\s*\(\s*["'](.*?)["']\s*\)/);
|
/window\.location\.href\s*=\s*["'](.*?)["']/,
|
||||||
if (redirectUrl) {
|
/window\.location\.replace\s*\(\s*["'](.*?)["']\s*\)/,
|
||||||
console.log("Redirect URL: " + redirectUrl[1]);
|
/window\.location\s*=\s*["'](.*?)["']/,
|
||||||
url = redirectUrl[1];
|
/window\.location\.assign\s*\(\s*["'](.*?)["']\s*\)/,
|
||||||
html = await soraFetch(url, {
|
/top\.location\s*=\s*["'](.*?)["']/,
|
||||||
headers
|
/top\.location\.replace\s*\(\s*["'](.*?)["']\s*\)/,
|
||||||
});
|
];
|
||||||
html = html.text ? await html.text() : html;
|
for (const match of matches) {
|
||||||
|
const redirectUrl = html.match(match);
|
||||||
} else if (redirectUrl2) {
|
if (redirectUrl && redirectUrl[1] && typeof redirectUrl[1] === "string" && redirectUrl[1].startsWith("http")) {
|
||||||
console.log("Redirect URL 2: " + redirectUrl2[1]);
|
console.log("Redirect URL found: " + redirectUrl[1]);
|
||||||
url = redirectUrl2[1];
|
url = redirectUrl[1];
|
||||||
html = await soraFetch(url, {
|
headers['Referer'] = url;
|
||||||
headers
|
headers['Host'] = url.match(/https?:\/\/([^\/]+)/)[1];
|
||||||
});
|
html = await soraFetch(url, {
|
||||||
html = html.text ? await html.text() : html;
|
headers,
|
||||||
} else if (redirectUrl3) {
|
}).then((res) => res.text());
|
||||||
console.log("Redirect URL 3: " + redirectUrl3[1]);
|
break;
|
||||||
url = redirectUrl3[1];
|
}
|
||||||
html = await soraFetch(url, {
|
|
||||||
headers
|
|
||||||
});
|
|
||||||
html = html.text ? await html.text() : html;
|
|
||||||
} else {
|
|
||||||
console.log("No redirect URL found");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// console.log("HTML: " + html);
|
// console.log("HTML: " + html);
|
||||||
switch (provider) {
|
switch (provider) {
|
||||||
case "bigwarp":
|
case "bigwarp":
|
||||||
try {
|
try {
|
||||||
return await bigwarpExtractor(html, url);
|
return await bigwarpExtractor(html, url);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@@ -608,6 +688,20 @@ async function extractStreamUrlByProvider(url, provider) {
|
|||||||
console.log("Error extracting stream URL from mp4upload:", error);
|
console.log("Error extracting stream URL from mp4upload:", error);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
case "oneupload":
|
||||||
|
try {
|
||||||
|
return await oneuploadExtractor(html, url);
|
||||||
|
} catch (error) {
|
||||||
|
console.log("Error extracting stream URL from oneupload:", error);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
case "packer":
|
||||||
|
try {
|
||||||
|
return await packerExtractor(html, url);
|
||||||
|
} catch (error) {
|
||||||
|
console.log("Error extracting stream URL from packer:", error);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
case "sendvid":
|
case "sendvid":
|
||||||
try {
|
try {
|
||||||
return await sendvidExtractor(html, url);
|
return await sendvidExtractor(html, url);
|
||||||
@@ -622,6 +716,13 @@ async function extractStreamUrlByProvider(url, provider) {
|
|||||||
console.log("Error extracting stream URL from sibnet:", error);
|
console.log("Error extracting stream URL from sibnet:", error);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
case "smoothpre":
|
||||||
|
try {
|
||||||
|
return await smoothpreExtractor(html, url);
|
||||||
|
} catch (error) {
|
||||||
|
console.log("Error extracting stream URL from smoothpre:", error);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
case "streamtape":
|
case "streamtape":
|
||||||
try {
|
try {
|
||||||
return await streamtapeExtractor(html, url);
|
return await streamtapeExtractor(html, url);
|
||||||
@@ -636,13 +737,6 @@ async function extractStreamUrlByProvider(url, provider) {
|
|||||||
console.log("Error extracting stream URL from streamup:", error);
|
console.log("Error extracting stream URL from streamup:", error);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
case "supervideo":
|
|
||||||
try {
|
|
||||||
return await supervideoExtractor(html, url);
|
|
||||||
} catch (error) {
|
|
||||||
console.log("Error extracting stream URL from supervideo:", error);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
case "uploadcx":
|
case "uploadcx":
|
||||||
try {
|
try {
|
||||||
return await uploadcxExtractor(html, url);
|
return await uploadcxExtractor(html, url);
|
||||||
@@ -691,7 +785,6 @@ async function extractStreamUrlByProvider(url, provider) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
////////////////////////////////////////////////
|
////////////////////////////////////////////////
|
||||||
// EXTRACTORS //
|
// EXTRACTORS //
|
||||||
////////////////////////////////////////////////
|
////////////////////////////////////////////////
|
||||||
@@ -1178,7 +1271,6 @@ async function megacloudExtractor(html, embedUrl) {
|
|||||||
* @author Cufiy
|
* @author Cufiy
|
||||||
*/
|
*/
|
||||||
async function mp4uploadExtractor(html, url = null) {
|
async function mp4uploadExtractor(html, url = null) {
|
||||||
// src: "https://a4.mp4upload.com:183/d/xkx3b4etz3b4quuo66rbmyqtjjoivahfxp27f35pti45rzapbvj5xwb4wuqtlpewdz4dirfp/video.mp4"
|
|
||||||
const regex = /src:\s*"([^"]+)"/;
|
const regex = /src:\s*"([^"]+)"/;
|
||||||
const match = html.match(regex);
|
const match = html.match(regex);
|
||||||
if (match) {
|
if (match) {
|
||||||
@@ -1188,6 +1280,32 @@ async function mp4uploadExtractor(html, url = null) {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
/* --- oneupload --- */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @name oneuploadExtractor
|
||||||
|
* @author 50/50
|
||||||
|
*/
|
||||||
|
async function oneuploadExtractor(data, url = null) {
|
||||||
|
const match = data.match(/sources:\s*\[\{file:"([^"]+)"\}\]/);
|
||||||
|
const fileUrl = match ? match[1] : null;
|
||||||
|
return fileUrl;
|
||||||
|
}
|
||||||
|
/* --- packer --- */
|
||||||
|
|
||||||
|
/* {REQUIRED PLUGINS: unbaser} */
|
||||||
|
/**
|
||||||
|
* @name packerExtractor
|
||||||
|
* @author 50/50
|
||||||
|
*/
|
||||||
|
async function packerExtractor(data, url = null) {
|
||||||
|
const obfuscatedScript = data.match(/<script[^>]*>\s*(eval\(function\(p,a,c,k,e,d.*?\)[\s\S]*?)<\/script>/);
|
||||||
|
const unpackedScript = unpack(obfuscatedScript[1]);
|
||||||
|
const m3u8Match = unpackedScript.match(/"hls2"\s*:\s*"([^"]+)"/);
|
||||||
|
const m3u8Url = m3u8Match[1];
|
||||||
|
return m3u8Url;
|
||||||
|
}
|
||||||
|
|
||||||
/* --- sendvid --- */
|
/* --- sendvid --- */
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1223,6 +1341,29 @@ async function sibnetExtractor(html, embedUrl) {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
/* --- smoothpre --- */
|
||||||
|
|
||||||
|
/* {REQUIRED PLUGINS: unbaser} */
|
||||||
|
/**
|
||||||
|
* @name SmoothPre Extractor
|
||||||
|
* @author 50/50
|
||||||
|
*/
|
||||||
|
async function smoothpreExtractor(data, url = null) {
|
||||||
|
console.log("Using SmoothPre Extractor");
|
||||||
|
console.log("Data Length: " + data.length);
|
||||||
|
const obfuscatedScript = data.match(/<script[^>]*>\s*(eval\(function\(p,a,c,k,e,d.*?\)[\s\S]*?)<\/script>/);
|
||||||
|
if (!obfuscatedScript || !obfuscatedScript[1]) {
|
||||||
|
console.log("No obfuscated script found");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
const unpackedScript = unpack(obfuscatedScript[1]);
|
||||||
|
|
||||||
|
const hls2Match = unpackedScript.match(/"hls2"\s*:\s*"([^"]+)"/);
|
||||||
|
const hls2Url = hls2Match ? hls2Match[1] : null;
|
||||||
|
return hls2Url;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/* --- streamtape --- */
|
/* --- streamtape --- */
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1306,26 +1447,6 @@ async function streamupExtractor(data, url = null) {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/* --- supervideo --- */
|
|
||||||
|
|
||||||
/* {REQUIRED PLUGINS: unbaser} */
|
|
||||||
/**
|
|
||||||
* @name SuperVideo Extractor
|
|
||||||
* @author 50/50
|
|
||||||
*/
|
|
||||||
async function supervideoExtractor(data, url = null) {
|
|
||||||
const obfuscatedScript = data.match(/<script[^>]*>\s*(eval\(function\(p,a,c,k,e,d.*?\)[\s\S]*?)<\/script>/);
|
|
||||||
const unpackedScript = unpack(obfuscatedScript[1]);
|
|
||||||
const regex = /file:\s*"([^"]+\.m3u8)"/;
|
|
||||||
const match = regex.exec(unpackedScript);
|
|
||||||
if (match) {
|
|
||||||
const fileUrl = match[1];
|
|
||||||
console.log("File URL:" + fileUrl);
|
|
||||||
return fileUrl;
|
|
||||||
}
|
|
||||||
return "No stream found";
|
|
||||||
}
|
|
||||||
|
|
||||||
/* --- uploadcx --- */
|
/* --- uploadcx --- */
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1390,7 +1511,7 @@ async function vidmolyExtractor(html, url = null) {
|
|||||||
const streamUrl = iframeMatch[1].startsWith("//")
|
const streamUrl = iframeMatch[1].startsWith("//")
|
||||||
? "https:" + iframeMatch[1]
|
? "https:" + iframeMatch[1]
|
||||||
: iframeMatch[1];
|
: iframeMatch[1];
|
||||||
const responseTwo = await fetchv2(streamUrl);
|
const responseTwo = await soraFetch(streamUrl);
|
||||||
const htmlTwo = await responseTwo.text();
|
const htmlTwo = await responseTwo.text();
|
||||||
const m3u8Match = htmlTwo.match(/sources:\s*\[\{file:"([^"]+\.m3u8)"/);
|
const m3u8Match = htmlTwo.match(/sources:\s*\[\{file:"([^"]+\.m3u8)"/);
|
||||||
return m3u8Match ? m3u8Match[1] : null;
|
return m3u8Match ? m3u8Match[1] : null;
|
||||||
@@ -1516,6 +1637,7 @@ function voeShiftChars(str, shift) {
|
|||||||
.join("");
|
.join("");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
////////////////////////////////////////////////
|
////////////////////////////////////////////////
|
||||||
// PLUGINS //
|
// PLUGINS //
|
||||||
////////////////////////////////////////////////
|
////////////////////////////////////////////////
|
||||||
@@ -1533,17 +1655,25 @@ function voeShiftChars(str, shift) {
|
|||||||
* @returns {Promise<Response|null>} The response from the server, or null if the
|
* @returns {Promise<Response|null>} The response from the server, or null if the
|
||||||
* request failed.
|
* request failed.
|
||||||
*/
|
*/
|
||||||
async function soraFetch(url, options = { headers: {}, method: 'GET', body: null }) {
|
async function soraFetch(
|
||||||
|
url,
|
||||||
|
options = { headers: {}, method: "GET", body: null }
|
||||||
|
) {
|
||||||
|
try {
|
||||||
|
return await fetchv2(
|
||||||
|
url,
|
||||||
|
options.headers ?? {},
|
||||||
|
options.method ?? "GET",
|
||||||
|
options.body ?? null
|
||||||
|
);
|
||||||
|
} catch (e) {
|
||||||
try {
|
try {
|
||||||
return await fetchv2(url, options.headers ?? {}, options.method ?? 'GET', options.body ?? null);
|
return await fetch(url, options);
|
||||||
} catch(e) {
|
} catch (error) {
|
||||||
try {
|
await console.log("soraFetch error: " + error.message);
|
||||||
return await fetch(url, options);
|
return null;
|
||||||
} catch(error) {
|
|
||||||
await console.log('soraFetch error: ' + error.message);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
/***********************************************************
|
/***********************************************************
|
||||||
* UNPACKER MODULE
|
* 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
|
// EDITING THIS FILE COULD BREAK THE UPDATER AND CAUSE ISSUES WITH THE EXTRACTOR
|
||||||
|
|
||||||
/* {GE START} */
|
/* {GE START} */
|
||||||
/* {VERSION: 1.1.8} */
|
/* {VERSION: 1.2.0} */
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @name global_extractor.js
|
* @name global_extractor.js
|
||||||
@@ -155,8 +155,8 @@ function cleanHtmlSymbols(string) {
|
|||||||
* @author Cufiy
|
* @author Cufiy
|
||||||
* @url https://github.com/JMcrafter26/sora-global-extractor
|
* @url https://github.com/JMcrafter26/sora-global-extractor
|
||||||
* @license CUSTOM LICENSE - see https://github.com/JMcrafter26/sora-global-extractor/blob/main/LICENSE
|
* @license CUSTOM LICENSE - see https://github.com/JMcrafter26/sora-global-extractor/blob/main/LICENSE
|
||||||
* @date 2025-11-05 15:44:57
|
* @date 2026-01-03 19:28:28
|
||||||
* @version 1.1.8
|
* @version 1.2.0
|
||||||
* @note This file was generated automatically.
|
* @note This file was generated automatically.
|
||||||
* The global extractor comes with an auto-updating feature, so you can always get the latest version. https://github.com/JMcrafter26/sora-global-extractor#-auto-updater
|
* The global extractor comes with an auto-updating feature, so you can always get the latest version. https://github.com/JMcrafter26/sora-global-extractor#-auto-updater
|
||||||
*/
|
*/
|
||||||
@@ -166,12 +166,20 @@ function globalExtractor(providers) {
|
|||||||
for (const [url, provider] of Object.entries(providers)) {
|
for (const [url, provider] of Object.entries(providers)) {
|
||||||
try {
|
try {
|
||||||
const streamUrl = extractStreamUrlByProvider(url, provider);
|
const streamUrl = extractStreamUrlByProvider(url, provider);
|
||||||
|
// check if streamUrl is an object with streamUrl property
|
||||||
|
if (streamUrl && typeof streamUrl === "object" && !Array.isArray(streamUrl) && streamUrl.streamUrl) {
|
||||||
|
return streamUrl.streamUrl;
|
||||||
|
}
|
||||||
// check if streamUrl is not null, a string, and starts with http or https
|
// check if streamUrl is not null, a string, and starts with http or https
|
||||||
if (streamUrl && typeof streamUrl === "string" && (streamUrl.startsWith("http"))) {
|
if (
|
||||||
|
streamUrl &&
|
||||||
|
typeof streamUrl === "string" &&
|
||||||
|
streamUrl.startsWith("http")
|
||||||
|
) {
|
||||||
return streamUrl;
|
return streamUrl;
|
||||||
// if its an array, get the value that starts with http
|
// if its an array, get the value that starts with http
|
||||||
} else if (Array.isArray(streamUrl)) {
|
} else if (Array.isArray(streamUrl)) {
|
||||||
const httpStream = streamUrl.find(url => url.startsWith("http"));
|
const httpStream = streamUrl.find((url) => url.startsWith("http"));
|
||||||
if (httpStream) {
|
if (httpStream) {
|
||||||
return httpStream;
|
return httpStream;
|
||||||
}
|
}
|
||||||
@@ -179,7 +187,6 @@ function globalExtractor(providers) {
|
|||||||
// check if it's a valid stream URL
|
// check if it's a valid stream URL
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
// Ignore the error and try the next provider
|
// Ignore the error and try the next provider
|
||||||
}
|
}
|
||||||
@@ -191,16 +198,30 @@ async function multiExtractor(providers) {
|
|||||||
/* this scheme should be returned as a JSON object
|
/* this scheme should be returned as a JSON object
|
||||||
{
|
{
|
||||||
"streams": [
|
"streams": [
|
||||||
"FileMoon",
|
{
|
||||||
"https://filemoon.example/stream1.m3u8",
|
"title": "FileMoon",
|
||||||
"StreamWish",
|
"streamUrl": "https://filemoon.example/stream1.m3u8",
|
||||||
"https://streamwish.example/stream2.m3u8",
|
},
|
||||||
"Okru",
|
{
|
||||||
"https://okru.example/stream3.m3u8",
|
"title": "StreamWish",
|
||||||
"MP4",
|
"streamUrl": "https://streamwish.example/stream2.m3u8",
|
||||||
"https://mp4upload.example/stream4.mp4",
|
},
|
||||||
"Default",
|
{
|
||||||
"https://default.example/stream5.m3u8"
|
"title": "Okru",
|
||||||
|
"streamUrl": "https://okru.example/stream3.m3u8",
|
||||||
|
"headers": { // Optional headers for the stream
|
||||||
|
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.3",
|
||||||
|
"Referer": "https://okru.example/",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "MP4",
|
||||||
|
"streamUrl": "https://mp4upload.example/stream4.mp4",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Default",
|
||||||
|
"streamUrl": "https://default.example/stream5.m3u8"
|
||||||
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
@@ -212,20 +233,21 @@ async function multiExtractor(providers) {
|
|||||||
// if provider starts with "direct-", then add the url to the streams array directly
|
// if provider starts with "direct-", then add the url to the streams array directly
|
||||||
if (provider.startsWith("direct-")) {
|
if (provider.startsWith("direct-")) {
|
||||||
const directName = provider.slice(7); // remove "direct-" prefix
|
const directName = provider.slice(7); // remove "direct-" prefix
|
||||||
if (directName && directName.length > 0) {
|
const title = (directName && directName.length > 0) ? directName : "Direct";
|
||||||
streams.push(directName, url);
|
streams.push({
|
||||||
} else {
|
title: title,
|
||||||
streams.push("Direct", url); // fallback to "Direct" if no name is provided
|
streamUrl: url
|
||||||
}
|
});
|
||||||
continue; // skip to the next provider
|
continue; // skip to the next provider
|
||||||
}
|
}
|
||||||
if (provider.startsWith("direct")) {
|
if (provider.startsWith("direct")) {
|
||||||
provider = provider.slice(7); // remove "direct-" prefix
|
provider = provider.slice(7); // remove "direct-" prefix
|
||||||
if (provider && provider.length > 0) {
|
const title = (provider && provider.length > 0) ? provider : "Direct";
|
||||||
streams.push(provider, url);
|
streams.push({
|
||||||
} else {
|
title: title,
|
||||||
streams.push("Direct", url); // fallback to "Direct" if no name is provided
|
streamUrl: url
|
||||||
}
|
});
|
||||||
|
continue; // skip to the next provider
|
||||||
}
|
}
|
||||||
|
|
||||||
let customName = null; // to store the custom name if provided
|
let customName = null; // to store the custom name if provided
|
||||||
@@ -242,15 +264,24 @@ async function multiExtractor(providers) {
|
|||||||
console.log(`Skipping ${provider} as it has already 3 streams`);
|
console.log(`Skipping ${provider} as it has already 3 streams`);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
let streamUrl = await extractStreamUrlByProvider(url, provider);
|
let result = await extractStreamUrlByProvider(url, provider);
|
||||||
|
let streamUrl = null;
|
||||||
if (streamUrl && Array.isArray(streamUrl)) {
|
let headers = null;
|
||||||
const httpStream = streamUrl.find(url => url.startsWith("http"));
|
|
||||||
|
// 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) {
|
if (httpStream) {
|
||||||
streamUrl = httpStream;
|
streamUrl = httpStream;
|
||||||
}
|
}
|
||||||
|
} else if (result && typeof result === "string") {
|
||||||
|
streamUrl = result;
|
||||||
}
|
}
|
||||||
// check if provider is already in streams, if it is, add a number to it
|
|
||||||
|
// check if streamUrl is valid
|
||||||
if (
|
if (
|
||||||
!streamUrl ||
|
!streamUrl ||
|
||||||
typeof streamUrl !== "string" ||
|
typeof streamUrl !== "string" ||
|
||||||
@@ -264,22 +295,29 @@ async function multiExtractor(providers) {
|
|||||||
provider = customName;
|
provider = customName;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let title;
|
||||||
if (providersCount[provider]) {
|
if (providersCount[provider]) {
|
||||||
providersCount[provider]++;
|
providersCount[provider]++;
|
||||||
streams.push(
|
title = provider.charAt(0).toUpperCase() +
|
||||||
provider.charAt(0).toUpperCase() +
|
|
||||||
provider.slice(1) +
|
provider.slice(1) +
|
||||||
"-" +
|
"-" +
|
||||||
(providersCount[provider] - 1), // add a number to the provider name
|
(providersCount[provider] - 1); // add a number to the provider name
|
||||||
streamUrl
|
|
||||||
);
|
|
||||||
} else {
|
} else {
|
||||||
providersCount[provider] = 1;
|
providersCount[provider] = 1;
|
||||||
streams.push(
|
title = provider.charAt(0).toUpperCase() + provider.slice(1);
|
||||||
provider.charAt(0).toUpperCase() + provider.slice(1),
|
|
||||||
streamUrl
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const streamObject = {
|
||||||
|
title: title,
|
||||||
|
streamUrl: streamUrl
|
||||||
|
};
|
||||||
|
|
||||||
|
// Add headers if they exist
|
||||||
|
if (headers && typeof headers === "object" && Object.keys(headers).length > 0) {
|
||||||
|
streamObject.headers = headers;
|
||||||
|
}
|
||||||
|
|
||||||
|
streams.push(streamObject);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
// Ignore the error and try the next provider
|
// Ignore the error and try the next provider
|
||||||
}
|
}
|
||||||
@@ -290,73 +328,98 @@ async function multiExtractor(providers) {
|
|||||||
async function extractStreamUrlByProvider(url, provider) {
|
async function extractStreamUrlByProvider(url, provider) {
|
||||||
if (eval(`typeof ${provider}Extractor`) !== "function") {
|
if (eval(`typeof ${provider}Extractor`) !== "function") {
|
||||||
// skip if the extractor is not defined
|
// skip if the extractor is not defined
|
||||||
console.log(`Extractor for provider ${provider} is not defined, skipping...`);
|
console.log(
|
||||||
|
`Extractor for provider ${provider} is not defined, skipping...`
|
||||||
|
);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
let uas = [
|
||||||
|
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.3",
|
||||||
|
"Mozilla/5.0 (iPhone; CPU iPhone OS 18_1_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/18.1.1 Mobile/15E148 Safari/604.1",
|
||||||
|
"Mozilla/5.0 (Linux; Android 10; SM-G973F) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Mobile Safari/537.36",
|
||||||
|
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.3",
|
||||||
|
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.1.2 Safari/605.1.15",
|
||||||
|
"Mozilla/5.0 (Linux; Android 11; Pixel 4 XL) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Mobile Safari/537.36",
|
||||||
|
];
|
||||||
let headers = {
|
let headers = {
|
||||||
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.3",
|
"User-Agent": uas[(url.length + provider.length) % uas.length], // use a different user agent based on the url and provider
|
||||||
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8",
|
"Accept":
|
||||||
|
"text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8",
|
||||||
"Accept-Language": "en-US,en;q=0.5",
|
"Accept-Language": "en-US,en;q=0.5",
|
||||||
"Referer": url,
|
"Referer": url,
|
||||||
"Connection": "keep-alive",
|
"Connection": "keep-alive",
|
||||||
"x-Requested-With": "XMLHttpRequest"
|
"x-Requested-With": "XMLHttpRequest",
|
||||||
};
|
};
|
||||||
if(provider == 'bigwarp') {
|
|
||||||
delete headers["User-Agent"];
|
switch (provider) {
|
||||||
headers["x-requested-with"] = "XMLHttpRequest";
|
case "bigwarp":
|
||||||
} else if (provider == 'vk') {
|
delete headers["User-Agent"];
|
||||||
headers["encoding"] = "windows-1251"; // required
|
break;
|
||||||
} else if (provider == 'sibnet') {
|
case "vk":
|
||||||
headers["encoding"] = "windows-1251"; // required
|
case "sibnet":
|
||||||
} else if (provider == 'supervideo') {
|
headers["encoding"] = "windows-1251"; // required
|
||||||
delete headers["User-Agent"];
|
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
|
// fetch the url
|
||||||
// and pass the response to the extractor function
|
// and pass the response to the extractor function
|
||||||
console.log("Fetching URL: " + url);
|
console.log("Fetching URL: " + url);
|
||||||
const response = await soraFetch(url, {
|
const response = await soraFetch(url, {
|
||||||
headers
|
headers,
|
||||||
});
|
});
|
||||||
|
|
||||||
console.log("Response: " + response.status);
|
console.log("Response: " + response.status);
|
||||||
let html = response.text ? await response.text() : response;
|
let html = response.text ? await response.text() : response;
|
||||||
// if title contains redirect, then get the redirect url
|
// if title contains redirect, then get the redirect url
|
||||||
const title = html.match(/<title>(.*?)<\/title>/);
|
const title = html.match(/<title>(.*?)<\/title>/);
|
||||||
if (title && title[1].toLowerCase().includes("redirect")) {
|
if (title && title[1].toLowerCase().includes("redirect")) {
|
||||||
const redirectUrl = html.match(/<meta http-equiv="refresh" content="0;url=(.*?)"/);
|
const matches = [
|
||||||
const redirectUrl2 = html.match(/window\.location\.href\s*=\s*["'](.*?)["']/);
|
/<meta http-equiv="refresh" content="0;url=(.*?)"/,
|
||||||
const redirectUrl3 = html.match(/window\.location\.replace\s*\(\s*["'](.*?)["']\s*\)/);
|
/window\.location\.href\s*=\s*["'](.*?)["']/,
|
||||||
if (redirectUrl) {
|
/window\.location\.replace\s*\(\s*["'](.*?)["']\s*\)/,
|
||||||
console.log("Redirect URL: " + redirectUrl[1]);
|
/window\.location\s*=\s*["'](.*?)["']/,
|
||||||
url = redirectUrl[1];
|
/window\.location\.assign\s*\(\s*["'](.*?)["']\s*\)/,
|
||||||
html = await soraFetch(url, {
|
/top\.location\s*=\s*["'](.*?)["']/,
|
||||||
headers
|
/top\.location\.replace\s*\(\s*["'](.*?)["']\s*\)/,
|
||||||
});
|
];
|
||||||
html = html.text ? await html.text() : html;
|
for (const match of matches) {
|
||||||
|
const redirectUrl = html.match(match);
|
||||||
} else if (redirectUrl2) {
|
if (redirectUrl && redirectUrl[1] && typeof redirectUrl[1] === "string" && redirectUrl[1].startsWith("http")) {
|
||||||
console.log("Redirect URL 2: " + redirectUrl2[1]);
|
console.log("Redirect URL found: " + redirectUrl[1]);
|
||||||
url = redirectUrl2[1];
|
url = redirectUrl[1];
|
||||||
html = await soraFetch(url, {
|
headers['Referer'] = url;
|
||||||
headers
|
headers['Host'] = url.match(/https?:\/\/([^\/]+)/)[1];
|
||||||
});
|
html = await soraFetch(url, {
|
||||||
html = html.text ? await html.text() : html;
|
headers,
|
||||||
} else if (redirectUrl3) {
|
}).then((res) => res.text());
|
||||||
console.log("Redirect URL 3: " + redirectUrl3[1]);
|
break;
|
||||||
url = redirectUrl3[1];
|
}
|
||||||
html = await soraFetch(url, {
|
|
||||||
headers
|
|
||||||
});
|
|
||||||
html = html.text ? await html.text() : html;
|
|
||||||
} else {
|
|
||||||
console.log("No redirect URL found");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// console.log("HTML: " + html);
|
// console.log("HTML: " + html);
|
||||||
switch (provider) {
|
switch (provider) {
|
||||||
case "bigwarp":
|
case "bigwarp":
|
||||||
try {
|
try {
|
||||||
return await bigwarpExtractor(html, url);
|
return await bigwarpExtractor(html, url);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@@ -405,6 +468,20 @@ async function extractStreamUrlByProvider(url, provider) {
|
|||||||
console.log("Error extracting stream URL from mp4upload:", error);
|
console.log("Error extracting stream URL from mp4upload:", error);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
case "oneupload":
|
||||||
|
try {
|
||||||
|
return await oneuploadExtractor(html, url);
|
||||||
|
} catch (error) {
|
||||||
|
console.log("Error extracting stream URL from oneupload:", error);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
case "packer":
|
||||||
|
try {
|
||||||
|
return await packerExtractor(html, url);
|
||||||
|
} catch (error) {
|
||||||
|
console.log("Error extracting stream URL from packer:", error);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
case "sendvid":
|
case "sendvid":
|
||||||
try {
|
try {
|
||||||
return await sendvidExtractor(html, url);
|
return await sendvidExtractor(html, url);
|
||||||
@@ -419,6 +496,13 @@ async function extractStreamUrlByProvider(url, provider) {
|
|||||||
console.log("Error extracting stream URL from sibnet:", error);
|
console.log("Error extracting stream URL from sibnet:", error);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
case "smoothpre":
|
||||||
|
try {
|
||||||
|
return await smoothpreExtractor(html, url);
|
||||||
|
} catch (error) {
|
||||||
|
console.log("Error extracting stream URL from smoothpre:", error);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
case "streamtape":
|
case "streamtape":
|
||||||
try {
|
try {
|
||||||
return await streamtapeExtractor(html, url);
|
return await streamtapeExtractor(html, url);
|
||||||
@@ -433,13 +517,6 @@ async function extractStreamUrlByProvider(url, provider) {
|
|||||||
console.log("Error extracting stream URL from streamup:", error);
|
console.log("Error extracting stream URL from streamup:", error);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
case "supervideo":
|
|
||||||
try {
|
|
||||||
return await supervideoExtractor(html, url);
|
|
||||||
} catch (error) {
|
|
||||||
console.log("Error extracting stream URL from supervideo:", error);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
case "uploadcx":
|
case "uploadcx":
|
||||||
try {
|
try {
|
||||||
return await uploadcxExtractor(html, url);
|
return await uploadcxExtractor(html, url);
|
||||||
@@ -488,7 +565,6 @@ async function extractStreamUrlByProvider(url, provider) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
////////////////////////////////////////////////
|
////////////////////////////////////////////////
|
||||||
// EXTRACTORS //
|
// EXTRACTORS //
|
||||||
////////////////////////////////////////////////
|
////////////////////////////////////////////////
|
||||||
@@ -975,7 +1051,6 @@ async function megacloudExtractor(html, embedUrl) {
|
|||||||
* @author Cufiy
|
* @author Cufiy
|
||||||
*/
|
*/
|
||||||
async function mp4uploadExtractor(html, url = null) {
|
async function mp4uploadExtractor(html, url = null) {
|
||||||
// src: "https://a4.mp4upload.com:183/d/xkx3b4etz3b4quuo66rbmyqtjjoivahfxp27f35pti45rzapbvj5xwb4wuqtlpewdz4dirfp/video.mp4"
|
|
||||||
const regex = /src:\s*"([^"]+)"/;
|
const regex = /src:\s*"([^"]+)"/;
|
||||||
const match = html.match(regex);
|
const match = html.match(regex);
|
||||||
if (match) {
|
if (match) {
|
||||||
@@ -985,6 +1060,32 @@ async function mp4uploadExtractor(html, url = null) {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
/* --- oneupload --- */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @name oneuploadExtractor
|
||||||
|
* @author 50/50
|
||||||
|
*/
|
||||||
|
async function oneuploadExtractor(data, url = null) {
|
||||||
|
const match = data.match(/sources:\s*\[\{file:"([^"]+)"\}\]/);
|
||||||
|
const fileUrl = match ? match[1] : null;
|
||||||
|
return fileUrl;
|
||||||
|
}
|
||||||
|
/* --- packer --- */
|
||||||
|
|
||||||
|
/* {REQUIRED PLUGINS: unbaser} */
|
||||||
|
/**
|
||||||
|
* @name packerExtractor
|
||||||
|
* @author 50/50
|
||||||
|
*/
|
||||||
|
async function packerExtractor(data, url = null) {
|
||||||
|
const obfuscatedScript = data.match(/<script[^>]*>\s*(eval\(function\(p,a,c,k,e,d.*?\)[\s\S]*?)<\/script>/);
|
||||||
|
const unpackedScript = unpack(obfuscatedScript[1]);
|
||||||
|
const m3u8Match = unpackedScript.match(/"hls2"\s*:\s*"([^"]+)"/);
|
||||||
|
const m3u8Url = m3u8Match[1];
|
||||||
|
return m3u8Url;
|
||||||
|
}
|
||||||
|
|
||||||
/* --- sendvid --- */
|
/* --- sendvid --- */
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1020,6 +1121,29 @@ async function sibnetExtractor(html, embedUrl) {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
/* --- smoothpre --- */
|
||||||
|
|
||||||
|
/* {REQUIRED PLUGINS: unbaser} */
|
||||||
|
/**
|
||||||
|
* @name SmoothPre Extractor
|
||||||
|
* @author 50/50
|
||||||
|
*/
|
||||||
|
async function smoothpreExtractor(data, url = null) {
|
||||||
|
console.log("Using SmoothPre Extractor");
|
||||||
|
console.log("Data Length: " + data.length);
|
||||||
|
const obfuscatedScript = data.match(/<script[^>]*>\s*(eval\(function\(p,a,c,k,e,d.*?\)[\s\S]*?)<\/script>/);
|
||||||
|
if (!obfuscatedScript || !obfuscatedScript[1]) {
|
||||||
|
console.log("No obfuscated script found");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
const unpackedScript = unpack(obfuscatedScript[1]);
|
||||||
|
|
||||||
|
const hls2Match = unpackedScript.match(/"hls2"\s*:\s*"([^"]+)"/);
|
||||||
|
const hls2Url = hls2Match ? hls2Match[1] : null;
|
||||||
|
return hls2Url;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/* --- streamtape --- */
|
/* --- streamtape --- */
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1103,26 +1227,6 @@ async function streamupExtractor(data, url = null) {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/* --- supervideo --- */
|
|
||||||
|
|
||||||
/* {REQUIRED PLUGINS: unbaser} */
|
|
||||||
/**
|
|
||||||
* @name SuperVideo Extractor
|
|
||||||
* @author 50/50
|
|
||||||
*/
|
|
||||||
async function supervideoExtractor(data, url = null) {
|
|
||||||
const obfuscatedScript = data.match(/<script[^>]*>\s*(eval\(function\(p,a,c,k,e,d.*?\)[\s\S]*?)<\/script>/);
|
|
||||||
const unpackedScript = unpack(obfuscatedScript[1]);
|
|
||||||
const regex = /file:\s*"([^"]+\.m3u8)"/;
|
|
||||||
const match = regex.exec(unpackedScript);
|
|
||||||
if (match) {
|
|
||||||
const fileUrl = match[1];
|
|
||||||
console.log("File URL:" + fileUrl);
|
|
||||||
return fileUrl;
|
|
||||||
}
|
|
||||||
return "No stream found";
|
|
||||||
}
|
|
||||||
|
|
||||||
/* --- uploadcx --- */
|
/* --- uploadcx --- */
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1187,7 +1291,7 @@ async function vidmolyExtractor(html, url = null) {
|
|||||||
const streamUrl = iframeMatch[1].startsWith("//")
|
const streamUrl = iframeMatch[1].startsWith("//")
|
||||||
? "https:" + iframeMatch[1]
|
? "https:" + iframeMatch[1]
|
||||||
: iframeMatch[1];
|
: iframeMatch[1];
|
||||||
const responseTwo = await fetchv2(streamUrl);
|
const responseTwo = await soraFetch(streamUrl);
|
||||||
const htmlTwo = await responseTwo.text();
|
const htmlTwo = await responseTwo.text();
|
||||||
const m3u8Match = htmlTwo.match(/sources:\s*\[\{file:"([^"]+\.m3u8)"/);
|
const m3u8Match = htmlTwo.match(/sources:\s*\[\{file:"([^"]+\.m3u8)"/);
|
||||||
return m3u8Match ? m3u8Match[1] : null;
|
return m3u8Match ? m3u8Match[1] : null;
|
||||||
@@ -1313,6 +1417,7 @@ function voeShiftChars(str, shift) {
|
|||||||
.join("");
|
.join("");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
////////////////////////////////////////////////
|
////////////////////////////////////////////////
|
||||||
// PLUGINS //
|
// PLUGINS //
|
||||||
////////////////////////////////////////////////
|
////////////////////////////////////////////////
|
||||||
@@ -1330,17 +1435,25 @@ function voeShiftChars(str, shift) {
|
|||||||
* @returns {Promise<Response|null>} The response from the server, or null if the
|
* @returns {Promise<Response|null>} The response from the server, or null if the
|
||||||
* request failed.
|
* request failed.
|
||||||
*/
|
*/
|
||||||
async function soraFetch(url, options = { headers: {}, method: 'GET', body: null }) {
|
async function soraFetch(
|
||||||
|
url,
|
||||||
|
options = { headers: {}, method: "GET", body: null }
|
||||||
|
) {
|
||||||
|
try {
|
||||||
|
return await fetchv2(
|
||||||
|
url,
|
||||||
|
options.headers ?? {},
|
||||||
|
options.method ?? "GET",
|
||||||
|
options.body ?? null
|
||||||
|
);
|
||||||
|
} catch (e) {
|
||||||
try {
|
try {
|
||||||
return await fetchv2(url, options.headers ?? {}, options.method ?? 'GET', options.body ?? null);
|
return await fetch(url, options);
|
||||||
} catch(e) {
|
} catch (error) {
|
||||||
try {
|
await console.log("soraFetch error: " + error.message);
|
||||||
return await fetch(url, options);
|
return null;
|
||||||
} catch(error) {
|
|
||||||
await console.log('soraFetch error: ' + error.message);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
/***********************************************************
|
/***********************************************************
|
||||||
* UNPACKER MODULE
|
* UNPACKER MODULE
|
||||||
@@ -1444,6 +1557,5 @@ function unpack(source) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* {GE END} */
|
/* {GE END} */
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
"name": "50/50",
|
"name": "50/50",
|
||||||
"icon": "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQ3122kQwublLkZ6rf1fEpUP79BxZOFmH9BSA&s"
|
"icon": "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQ3122kQwublLkZ6rf1fEpUP79BxZOFmH9BSA&s"
|
||||||
},
|
},
|
||||||
"version": "1.0.2",
|
"version": "1.0.3",
|
||||||
"language": "Hindi",
|
"language": "Hindi",
|
||||||
"streamType": "HLS",
|
"streamType": "HLS",
|
||||||
"quality": "1080p",
|
"quality": "1080p",
|
||||||
@@ -19,4 +19,4 @@
|
|||||||
"supportsMojuru": true,
|
"supportsMojuru": true,
|
||||||
"supportsSora": true,
|
"supportsSora": true,
|
||||||
"supportsLuna": true
|
"supportsLuna": true
|
||||||
}
|
}
|
||||||
@@ -5,7 +5,7 @@
|
|||||||
"name": "50/50 & Cufiy",
|
"name": "50/50 & Cufiy",
|
||||||
"icon": "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQ3122kQwublLkZ6rf1fEpUP79BxZOFmH9BSA&s"
|
"icon": "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQ3122kQwublLkZ6rf1fEpUP79BxZOFmH9BSA&s"
|
||||||
},
|
},
|
||||||
"version": "1.1.3",
|
"version": "1.1.4",
|
||||||
"language": "German (SUB)",
|
"language": "German (SUB)",
|
||||||
"streamType": "HLS",
|
"streamType": "HLS",
|
||||||
"quality": "1080p",
|
"quality": "1080p",
|
||||||
@@ -17,4 +17,4 @@
|
|||||||
"supportsMojuru": true,
|
"supportsMojuru": true,
|
||||||
"supportsSora": true,
|
"supportsSora": true,
|
||||||
"supportsLuna": true
|
"supportsLuna": true
|
||||||
}
|
}
|
||||||
@@ -5,7 +5,7 @@
|
|||||||
"name": "50/50 & Cufiy",
|
"name": "50/50 & Cufiy",
|
||||||
"icon": "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQ3122kQwublLkZ6rf1fEpUP79BxZOFmH9BSA&s"
|
"icon": "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQ3122kQwublLkZ6rf1fEpUP79BxZOFmH9BSA&s"
|
||||||
},
|
},
|
||||||
"version": "1.1.3",
|
"version": "1.1.4",
|
||||||
"language": "German (DUB)",
|
"language": "German (DUB)",
|
||||||
"streamType": "HLS",
|
"streamType": "HLS",
|
||||||
"quality": "1080p",
|
"quality": "1080p",
|
||||||
@@ -17,4 +17,4 @@
|
|||||||
"supportsMojuru": true,
|
"supportsMojuru": true,
|
||||||
"supportsSora": true,
|
"supportsSora": true,
|
||||||
"supportsLuna": true
|
"supportsLuna": true
|
||||||
}
|
}
|
||||||
@@ -5,7 +5,7 @@
|
|||||||
"name": "50/50 & Cufiy",
|
"name": "50/50 & Cufiy",
|
||||||
"icon": "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQ3122kQwublLkZ6rf1fEpUP79BxZOFmH9BSA&s"
|
"icon": "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQ3122kQwublLkZ6rf1fEpUP79BxZOFmH9BSA&s"
|
||||||
},
|
},
|
||||||
"version": "1.1.3",
|
"version": "1.1.4",
|
||||||
"language": "English (SUB)",
|
"language": "English (SUB)",
|
||||||
"streamType": "HLS",
|
"streamType": "HLS",
|
||||||
"quality": "1080p",
|
"quality": "1080p",
|
||||||
@@ -17,4 +17,4 @@
|
|||||||
"supportsMojuru": true,
|
"supportsMojuru": true,
|
||||||
"supportsSora": true,
|
"supportsSora": true,
|
||||||
"supportsLuna": 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
|
// EDITING THIS FILE COULD BREAK THE UPDATER AND CAUSE ISSUES WITH THE EXTRACTOR
|
||||||
|
|
||||||
/* {GE START} */
|
/* {GE START} */
|
||||||
/* {VERSION: 1.1.8} */
|
/* {VERSION: 1.2.0} */
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @name global_extractor.js
|
* @name global_extractor.js
|
||||||
@@ -198,8 +198,8 @@ async function sendLog(message) {
|
|||||||
* @author Cufiy
|
* @author Cufiy
|
||||||
* @url https://github.com/JMcrafter26/sora-global-extractor
|
* @url https://github.com/JMcrafter26/sora-global-extractor
|
||||||
* @license CUSTOM LICENSE - see https://github.com/JMcrafter26/sora-global-extractor/blob/main/LICENSE
|
* @license CUSTOM LICENSE - see https://github.com/JMcrafter26/sora-global-extractor/blob/main/LICENSE
|
||||||
* @date 2025-11-05 15:44:57
|
* @date 2026-01-03 19:28:28
|
||||||
* @version 1.1.8
|
* @version 1.2.0
|
||||||
* @note This file was generated automatically.
|
* @note This file was generated automatically.
|
||||||
* The global extractor comes with an auto-updating feature, so you can always get the latest version. https://github.com/JMcrafter26/sora-global-extractor#-auto-updater
|
* The global extractor comes with an auto-updating feature, so you can always get the latest version. https://github.com/JMcrafter26/sora-global-extractor#-auto-updater
|
||||||
*/
|
*/
|
||||||
@@ -209,12 +209,20 @@ function globalExtractor(providers) {
|
|||||||
for (const [url, provider] of Object.entries(providers)) {
|
for (const [url, provider] of Object.entries(providers)) {
|
||||||
try {
|
try {
|
||||||
const streamUrl = extractStreamUrlByProvider(url, provider);
|
const streamUrl = extractStreamUrlByProvider(url, provider);
|
||||||
|
// check if streamUrl is an object with streamUrl property
|
||||||
|
if (streamUrl && typeof streamUrl === "object" && !Array.isArray(streamUrl) && streamUrl.streamUrl) {
|
||||||
|
return streamUrl.streamUrl;
|
||||||
|
}
|
||||||
// check if streamUrl is not null, a string, and starts with http or https
|
// check if streamUrl is not null, a string, and starts with http or https
|
||||||
if (streamUrl && typeof streamUrl === "string" && (streamUrl.startsWith("http"))) {
|
if (
|
||||||
|
streamUrl &&
|
||||||
|
typeof streamUrl === "string" &&
|
||||||
|
streamUrl.startsWith("http")
|
||||||
|
) {
|
||||||
return streamUrl;
|
return streamUrl;
|
||||||
// if its an array, get the value that starts with http
|
// if its an array, get the value that starts with http
|
||||||
} else if (Array.isArray(streamUrl)) {
|
} else if (Array.isArray(streamUrl)) {
|
||||||
const httpStream = streamUrl.find(url => url.startsWith("http"));
|
const httpStream = streamUrl.find((url) => url.startsWith("http"));
|
||||||
if (httpStream) {
|
if (httpStream) {
|
||||||
return httpStream;
|
return httpStream;
|
||||||
}
|
}
|
||||||
@@ -222,7 +230,6 @@ function globalExtractor(providers) {
|
|||||||
// check if it's a valid stream URL
|
// check if it's a valid stream URL
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
// Ignore the error and try the next provider
|
// Ignore the error and try the next provider
|
||||||
}
|
}
|
||||||
@@ -234,16 +241,30 @@ async function multiExtractor(providers) {
|
|||||||
/* this scheme should be returned as a JSON object
|
/* this scheme should be returned as a JSON object
|
||||||
{
|
{
|
||||||
"streams": [
|
"streams": [
|
||||||
"FileMoon",
|
{
|
||||||
"https://filemoon.example/stream1.m3u8",
|
"title": "FileMoon",
|
||||||
"StreamWish",
|
"streamUrl": "https://filemoon.example/stream1.m3u8",
|
||||||
"https://streamwish.example/stream2.m3u8",
|
},
|
||||||
"Okru",
|
{
|
||||||
"https://okru.example/stream3.m3u8",
|
"title": "StreamWish",
|
||||||
"MP4",
|
"streamUrl": "https://streamwish.example/stream2.m3u8",
|
||||||
"https://mp4upload.example/stream4.mp4",
|
},
|
||||||
"Default",
|
{
|
||||||
"https://default.example/stream5.m3u8"
|
"title": "Okru",
|
||||||
|
"streamUrl": "https://okru.example/stream3.m3u8",
|
||||||
|
"headers": { // Optional headers for the stream
|
||||||
|
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.3",
|
||||||
|
"Referer": "https://okru.example/",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "MP4",
|
||||||
|
"streamUrl": "https://mp4upload.example/stream4.mp4",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Default",
|
||||||
|
"streamUrl": "https://default.example/stream5.m3u8"
|
||||||
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
@@ -255,20 +276,21 @@ async function multiExtractor(providers) {
|
|||||||
// if provider starts with "direct-", then add the url to the streams array directly
|
// if provider starts with "direct-", then add the url to the streams array directly
|
||||||
if (provider.startsWith("direct-")) {
|
if (provider.startsWith("direct-")) {
|
||||||
const directName = provider.slice(7); // remove "direct-" prefix
|
const directName = provider.slice(7); // remove "direct-" prefix
|
||||||
if (directName && directName.length > 0) {
|
const title = (directName && directName.length > 0) ? directName : "Direct";
|
||||||
streams.push(directName, url);
|
streams.push({
|
||||||
} else {
|
title: title,
|
||||||
streams.push("Direct", url); // fallback to "Direct" if no name is provided
|
streamUrl: url
|
||||||
}
|
});
|
||||||
continue; // skip to the next provider
|
continue; // skip to the next provider
|
||||||
}
|
}
|
||||||
if (provider.startsWith("direct")) {
|
if (provider.startsWith("direct")) {
|
||||||
provider = provider.slice(7); // remove "direct-" prefix
|
provider = provider.slice(7); // remove "direct-" prefix
|
||||||
if (provider && provider.length > 0) {
|
const title = (provider && provider.length > 0) ? provider : "Direct";
|
||||||
streams.push(provider, url);
|
streams.push({
|
||||||
} else {
|
title: title,
|
||||||
streams.push("Direct", url); // fallback to "Direct" if no name is provided
|
streamUrl: url
|
||||||
}
|
});
|
||||||
|
continue; // skip to the next provider
|
||||||
}
|
}
|
||||||
|
|
||||||
let customName = null; // to store the custom name if provided
|
let customName = null; // to store the custom name if provided
|
||||||
@@ -285,15 +307,24 @@ async function multiExtractor(providers) {
|
|||||||
console.log(`Skipping ${provider} as it has already 3 streams`);
|
console.log(`Skipping ${provider} as it has already 3 streams`);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
let streamUrl = await extractStreamUrlByProvider(url, provider);
|
let result = await extractStreamUrlByProvider(url, provider);
|
||||||
|
let streamUrl = null;
|
||||||
if (streamUrl && Array.isArray(streamUrl)) {
|
let headers = null;
|
||||||
const httpStream = streamUrl.find(url => url.startsWith("http"));
|
|
||||||
|
// 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) {
|
if (httpStream) {
|
||||||
streamUrl = httpStream;
|
streamUrl = httpStream;
|
||||||
}
|
}
|
||||||
|
} else if (result && typeof result === "string") {
|
||||||
|
streamUrl = result;
|
||||||
}
|
}
|
||||||
// check if provider is already in streams, if it is, add a number to it
|
|
||||||
|
// check if streamUrl is valid
|
||||||
if (
|
if (
|
||||||
!streamUrl ||
|
!streamUrl ||
|
||||||
typeof streamUrl !== "string" ||
|
typeof streamUrl !== "string" ||
|
||||||
@@ -307,22 +338,29 @@ async function multiExtractor(providers) {
|
|||||||
provider = customName;
|
provider = customName;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let title;
|
||||||
if (providersCount[provider]) {
|
if (providersCount[provider]) {
|
||||||
providersCount[provider]++;
|
providersCount[provider]++;
|
||||||
streams.push(
|
title = provider.charAt(0).toUpperCase() +
|
||||||
provider.charAt(0).toUpperCase() +
|
|
||||||
provider.slice(1) +
|
provider.slice(1) +
|
||||||
"-" +
|
"-" +
|
||||||
(providersCount[provider] - 1), // add a number to the provider name
|
(providersCount[provider] - 1); // add a number to the provider name
|
||||||
streamUrl
|
|
||||||
);
|
|
||||||
} else {
|
} else {
|
||||||
providersCount[provider] = 1;
|
providersCount[provider] = 1;
|
||||||
streams.push(
|
title = provider.charAt(0).toUpperCase() + provider.slice(1);
|
||||||
provider.charAt(0).toUpperCase() + provider.slice(1),
|
|
||||||
streamUrl
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const streamObject = {
|
||||||
|
title: title,
|
||||||
|
streamUrl: streamUrl
|
||||||
|
};
|
||||||
|
|
||||||
|
// Add headers if they exist
|
||||||
|
if (headers && typeof headers === "object" && Object.keys(headers).length > 0) {
|
||||||
|
streamObject.headers = headers;
|
||||||
|
}
|
||||||
|
|
||||||
|
streams.push(streamObject);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
// Ignore the error and try the next provider
|
// Ignore the error and try the next provider
|
||||||
}
|
}
|
||||||
@@ -333,73 +371,98 @@ async function multiExtractor(providers) {
|
|||||||
async function extractStreamUrlByProvider(url, provider) {
|
async function extractStreamUrlByProvider(url, provider) {
|
||||||
if (eval(`typeof ${provider}Extractor`) !== "function") {
|
if (eval(`typeof ${provider}Extractor`) !== "function") {
|
||||||
// skip if the extractor is not defined
|
// skip if the extractor is not defined
|
||||||
console.log(`Extractor for provider ${provider} is not defined, skipping...`);
|
console.log(
|
||||||
|
`Extractor for provider ${provider} is not defined, skipping...`
|
||||||
|
);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
let uas = [
|
||||||
|
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.3",
|
||||||
|
"Mozilla/5.0 (iPhone; CPU iPhone OS 18_1_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/18.1.1 Mobile/15E148 Safari/604.1",
|
||||||
|
"Mozilla/5.0 (Linux; Android 10; SM-G973F) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Mobile Safari/537.36",
|
||||||
|
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.3",
|
||||||
|
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.1.2 Safari/605.1.15",
|
||||||
|
"Mozilla/5.0 (Linux; Android 11; Pixel 4 XL) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Mobile Safari/537.36",
|
||||||
|
];
|
||||||
let headers = {
|
let headers = {
|
||||||
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.3",
|
"User-Agent": uas[(url.length + provider.length) % uas.length], // use a different user agent based on the url and provider
|
||||||
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8",
|
"Accept":
|
||||||
|
"text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8",
|
||||||
"Accept-Language": "en-US,en;q=0.5",
|
"Accept-Language": "en-US,en;q=0.5",
|
||||||
"Referer": url,
|
"Referer": url,
|
||||||
"Connection": "keep-alive",
|
"Connection": "keep-alive",
|
||||||
"x-Requested-With": "XMLHttpRequest"
|
"x-Requested-With": "XMLHttpRequest",
|
||||||
};
|
};
|
||||||
if(provider == 'bigwarp') {
|
|
||||||
delete headers["User-Agent"];
|
switch (provider) {
|
||||||
headers["x-requested-with"] = "XMLHttpRequest";
|
case "bigwarp":
|
||||||
} else if (provider == 'vk') {
|
delete headers["User-Agent"];
|
||||||
headers["encoding"] = "windows-1251"; // required
|
break;
|
||||||
} else if (provider == 'sibnet') {
|
case "vk":
|
||||||
headers["encoding"] = "windows-1251"; // required
|
case "sibnet":
|
||||||
} else if (provider == 'supervideo') {
|
headers["encoding"] = "windows-1251"; // required
|
||||||
delete headers["User-Agent"];
|
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
|
// fetch the url
|
||||||
// and pass the response to the extractor function
|
// and pass the response to the extractor function
|
||||||
console.log("Fetching URL: " + url);
|
console.log("Fetching URL: " + url);
|
||||||
const response = await soraFetch(url, {
|
const response = await soraFetch(url, {
|
||||||
headers
|
headers,
|
||||||
});
|
});
|
||||||
|
|
||||||
console.log("Response: " + response.status);
|
console.log("Response: " + response.status);
|
||||||
let html = response.text ? await response.text() : response;
|
let html = response.text ? await response.text() : response;
|
||||||
// if title contains redirect, then get the redirect url
|
// if title contains redirect, then get the redirect url
|
||||||
const title = html.match(/<title>(.*?)<\/title>/);
|
const title = html.match(/<title>(.*?)<\/title>/);
|
||||||
if (title && title[1].toLowerCase().includes("redirect")) {
|
if (title && title[1].toLowerCase().includes("redirect")) {
|
||||||
const redirectUrl = html.match(/<meta http-equiv="refresh" content="0;url=(.*?)"/);
|
const matches = [
|
||||||
const redirectUrl2 = html.match(/window\.location\.href\s*=\s*["'](.*?)["']/);
|
/<meta http-equiv="refresh" content="0;url=(.*?)"/,
|
||||||
const redirectUrl3 = html.match(/window\.location\.replace\s*\(\s*["'](.*?)["']\s*\)/);
|
/window\.location\.href\s*=\s*["'](.*?)["']/,
|
||||||
if (redirectUrl) {
|
/window\.location\.replace\s*\(\s*["'](.*?)["']\s*\)/,
|
||||||
console.log("Redirect URL: " + redirectUrl[1]);
|
/window\.location\s*=\s*["'](.*?)["']/,
|
||||||
url = redirectUrl[1];
|
/window\.location\.assign\s*\(\s*["'](.*?)["']\s*\)/,
|
||||||
html = await soraFetch(url, {
|
/top\.location\s*=\s*["'](.*?)["']/,
|
||||||
headers
|
/top\.location\.replace\s*\(\s*["'](.*?)["']\s*\)/,
|
||||||
});
|
];
|
||||||
html = html.text ? await html.text() : html;
|
for (const match of matches) {
|
||||||
|
const redirectUrl = html.match(match);
|
||||||
} else if (redirectUrl2) {
|
if (redirectUrl && redirectUrl[1] && typeof redirectUrl[1] === "string" && redirectUrl[1].startsWith("http")) {
|
||||||
console.log("Redirect URL 2: " + redirectUrl2[1]);
|
console.log("Redirect URL found: " + redirectUrl[1]);
|
||||||
url = redirectUrl2[1];
|
url = redirectUrl[1];
|
||||||
html = await soraFetch(url, {
|
headers['Referer'] = url;
|
||||||
headers
|
headers['Host'] = url.match(/https?:\/\/([^\/]+)/)[1];
|
||||||
});
|
html = await soraFetch(url, {
|
||||||
html = html.text ? await html.text() : html;
|
headers,
|
||||||
} else if (redirectUrl3) {
|
}).then((res) => res.text());
|
||||||
console.log("Redirect URL 3: " + redirectUrl3[1]);
|
break;
|
||||||
url = redirectUrl3[1];
|
}
|
||||||
html = await soraFetch(url, {
|
|
||||||
headers
|
|
||||||
});
|
|
||||||
html = html.text ? await html.text() : html;
|
|
||||||
} else {
|
|
||||||
console.log("No redirect URL found");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// console.log("HTML: " + html);
|
// console.log("HTML: " + html);
|
||||||
switch (provider) {
|
switch (provider) {
|
||||||
case "bigwarp":
|
case "bigwarp":
|
||||||
try {
|
try {
|
||||||
return await bigwarpExtractor(html, url);
|
return await bigwarpExtractor(html, url);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@@ -448,6 +511,20 @@ async function extractStreamUrlByProvider(url, provider) {
|
|||||||
console.log("Error extracting stream URL from mp4upload:", error);
|
console.log("Error extracting stream URL from mp4upload:", error);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
case "oneupload":
|
||||||
|
try {
|
||||||
|
return await oneuploadExtractor(html, url);
|
||||||
|
} catch (error) {
|
||||||
|
console.log("Error extracting stream URL from oneupload:", error);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
case "packer":
|
||||||
|
try {
|
||||||
|
return await packerExtractor(html, url);
|
||||||
|
} catch (error) {
|
||||||
|
console.log("Error extracting stream URL from packer:", error);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
case "sendvid":
|
case "sendvid":
|
||||||
try {
|
try {
|
||||||
return await sendvidExtractor(html, url);
|
return await sendvidExtractor(html, url);
|
||||||
@@ -462,6 +539,13 @@ async function extractStreamUrlByProvider(url, provider) {
|
|||||||
console.log("Error extracting stream URL from sibnet:", error);
|
console.log("Error extracting stream URL from sibnet:", error);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
case "smoothpre":
|
||||||
|
try {
|
||||||
|
return await smoothpreExtractor(html, url);
|
||||||
|
} catch (error) {
|
||||||
|
console.log("Error extracting stream URL from smoothpre:", error);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
case "streamtape":
|
case "streamtape":
|
||||||
try {
|
try {
|
||||||
return await streamtapeExtractor(html, url);
|
return await streamtapeExtractor(html, url);
|
||||||
@@ -476,13 +560,6 @@ async function extractStreamUrlByProvider(url, provider) {
|
|||||||
console.log("Error extracting stream URL from streamup:", error);
|
console.log("Error extracting stream URL from streamup:", error);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
case "supervideo":
|
|
||||||
try {
|
|
||||||
return await supervideoExtractor(html, url);
|
|
||||||
} catch (error) {
|
|
||||||
console.log("Error extracting stream URL from supervideo:", error);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
case "uploadcx":
|
case "uploadcx":
|
||||||
try {
|
try {
|
||||||
return await uploadcxExtractor(html, url);
|
return await uploadcxExtractor(html, url);
|
||||||
@@ -531,7 +608,6 @@ async function extractStreamUrlByProvider(url, provider) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
////////////////////////////////////////////////
|
////////////////////////////////////////////////
|
||||||
// EXTRACTORS //
|
// EXTRACTORS //
|
||||||
////////////////////////////////////////////////
|
////////////////////////////////////////////////
|
||||||
@@ -1018,7 +1094,6 @@ async function megacloudExtractor(html, embedUrl) {
|
|||||||
* @author Cufiy
|
* @author Cufiy
|
||||||
*/
|
*/
|
||||||
async function mp4uploadExtractor(html, url = null) {
|
async function mp4uploadExtractor(html, url = null) {
|
||||||
// src: "https://a4.mp4upload.com:183/d/xkx3b4etz3b4quuo66rbmyqtjjoivahfxp27f35pti45rzapbvj5xwb4wuqtlpewdz4dirfp/video.mp4"
|
|
||||||
const regex = /src:\s*"([^"]+)"/;
|
const regex = /src:\s*"([^"]+)"/;
|
||||||
const match = html.match(regex);
|
const match = html.match(regex);
|
||||||
if (match) {
|
if (match) {
|
||||||
@@ -1028,6 +1103,32 @@ async function mp4uploadExtractor(html, url = null) {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
/* --- oneupload --- */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @name oneuploadExtractor
|
||||||
|
* @author 50/50
|
||||||
|
*/
|
||||||
|
async function oneuploadExtractor(data, url = null) {
|
||||||
|
const match = data.match(/sources:\s*\[\{file:"([^"]+)"\}\]/);
|
||||||
|
const fileUrl = match ? match[1] : null;
|
||||||
|
return fileUrl;
|
||||||
|
}
|
||||||
|
/* --- packer --- */
|
||||||
|
|
||||||
|
/* {REQUIRED PLUGINS: unbaser} */
|
||||||
|
/**
|
||||||
|
* @name packerExtractor
|
||||||
|
* @author 50/50
|
||||||
|
*/
|
||||||
|
async function packerExtractor(data, url = null) {
|
||||||
|
const obfuscatedScript = data.match(/<script[^>]*>\s*(eval\(function\(p,a,c,k,e,d.*?\)[\s\S]*?)<\/script>/);
|
||||||
|
const unpackedScript = unpack(obfuscatedScript[1]);
|
||||||
|
const m3u8Match = unpackedScript.match(/"hls2"\s*:\s*"([^"]+)"/);
|
||||||
|
const m3u8Url = m3u8Match[1];
|
||||||
|
return m3u8Url;
|
||||||
|
}
|
||||||
|
|
||||||
/* --- sendvid --- */
|
/* --- sendvid --- */
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1063,6 +1164,29 @@ async function sibnetExtractor(html, embedUrl) {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
/* --- smoothpre --- */
|
||||||
|
|
||||||
|
/* {REQUIRED PLUGINS: unbaser} */
|
||||||
|
/**
|
||||||
|
* @name SmoothPre Extractor
|
||||||
|
* @author 50/50
|
||||||
|
*/
|
||||||
|
async function smoothpreExtractor(data, url = null) {
|
||||||
|
console.log("Using SmoothPre Extractor");
|
||||||
|
console.log("Data Length: " + data.length);
|
||||||
|
const obfuscatedScript = data.match(/<script[^>]*>\s*(eval\(function\(p,a,c,k,e,d.*?\)[\s\S]*?)<\/script>/);
|
||||||
|
if (!obfuscatedScript || !obfuscatedScript[1]) {
|
||||||
|
console.log("No obfuscated script found");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
const unpackedScript = unpack(obfuscatedScript[1]);
|
||||||
|
|
||||||
|
const hls2Match = unpackedScript.match(/"hls2"\s*:\s*"([^"]+)"/);
|
||||||
|
const hls2Url = hls2Match ? hls2Match[1] : null;
|
||||||
|
return hls2Url;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/* --- streamtape --- */
|
/* --- streamtape --- */
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1146,26 +1270,6 @@ async function streamupExtractor(data, url = null) {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/* --- supervideo --- */
|
|
||||||
|
|
||||||
/* {REQUIRED PLUGINS: unbaser} */
|
|
||||||
/**
|
|
||||||
* @name SuperVideo Extractor
|
|
||||||
* @author 50/50
|
|
||||||
*/
|
|
||||||
async function supervideoExtractor(data, url = null) {
|
|
||||||
const obfuscatedScript = data.match(/<script[^>]*>\s*(eval\(function\(p,a,c,k,e,d.*?\)[\s\S]*?)<\/script>/);
|
|
||||||
const unpackedScript = unpack(obfuscatedScript[1]);
|
|
||||||
const regex = /file:\s*"([^"]+\.m3u8)"/;
|
|
||||||
const match = regex.exec(unpackedScript);
|
|
||||||
if (match) {
|
|
||||||
const fileUrl = match[1];
|
|
||||||
console.log("File URL:" + fileUrl);
|
|
||||||
return fileUrl;
|
|
||||||
}
|
|
||||||
return "No stream found";
|
|
||||||
}
|
|
||||||
|
|
||||||
/* --- uploadcx --- */
|
/* --- uploadcx --- */
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1230,7 +1334,7 @@ async function vidmolyExtractor(html, url = null) {
|
|||||||
const streamUrl = iframeMatch[1].startsWith("//")
|
const streamUrl = iframeMatch[1].startsWith("//")
|
||||||
? "https:" + iframeMatch[1]
|
? "https:" + iframeMatch[1]
|
||||||
: iframeMatch[1];
|
: iframeMatch[1];
|
||||||
const responseTwo = await fetchv2(streamUrl);
|
const responseTwo = await soraFetch(streamUrl);
|
||||||
const htmlTwo = await responseTwo.text();
|
const htmlTwo = await responseTwo.text();
|
||||||
const m3u8Match = htmlTwo.match(/sources:\s*\[\{file:"([^"]+\.m3u8)"/);
|
const m3u8Match = htmlTwo.match(/sources:\s*\[\{file:"([^"]+\.m3u8)"/);
|
||||||
return m3u8Match ? m3u8Match[1] : null;
|
return m3u8Match ? m3u8Match[1] : null;
|
||||||
@@ -1356,6 +1460,7 @@ function voeShiftChars(str, shift) {
|
|||||||
.join("");
|
.join("");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
////////////////////////////////////////////////
|
////////////////////////////////////////////////
|
||||||
// PLUGINS //
|
// PLUGINS //
|
||||||
////////////////////////////////////////////////
|
////////////////////////////////////////////////
|
||||||
@@ -1373,17 +1478,25 @@ function voeShiftChars(str, shift) {
|
|||||||
* @returns {Promise<Response|null>} The response from the server, or null if the
|
* @returns {Promise<Response|null>} The response from the server, or null if the
|
||||||
* request failed.
|
* request failed.
|
||||||
*/
|
*/
|
||||||
async function soraFetch(url, options = { headers: {}, method: 'GET', body: null }) {
|
async function soraFetch(
|
||||||
|
url,
|
||||||
|
options = { headers: {}, method: "GET", body: null }
|
||||||
|
) {
|
||||||
|
try {
|
||||||
|
return await fetchv2(
|
||||||
|
url,
|
||||||
|
options.headers ?? {},
|
||||||
|
options.method ?? "GET",
|
||||||
|
options.body ?? null
|
||||||
|
);
|
||||||
|
} catch (e) {
|
||||||
try {
|
try {
|
||||||
return await fetchv2(url, options.headers ?? {}, options.method ?? 'GET', options.body ?? null);
|
return await fetch(url, options);
|
||||||
} catch(e) {
|
} catch (error) {
|
||||||
try {
|
await console.log("soraFetch error: " + error.message);
|
||||||
return await fetch(url, options);
|
return null;
|
||||||
} catch(error) {
|
|
||||||
await console.log('soraFetch error: ' + error.message);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
/***********************************************************
|
/***********************************************************
|
||||||
* UNPACKER MODULE
|
* UNPACKER MODULE
|
||||||
@@ -1487,6 +1600,5 @@ function unpack(source) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* {GE END} */
|
/* {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
|
// EDITING THIS FILE COULD BREAK THE UPDATER AND CAUSE ISSUES WITH THE EXTRACTOR
|
||||||
|
|
||||||
/* {GE START} */
|
/* {GE START} */
|
||||||
/* {VERSION: 1.1.8} */
|
/* {VERSION: 1.2.0} */
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @name global_extractor.js
|
* @name global_extractor.js
|
||||||
@@ -199,8 +199,8 @@ async function sendLog(message) {
|
|||||||
* @author Cufiy
|
* @author Cufiy
|
||||||
* @url https://github.com/JMcrafter26/sora-global-extractor
|
* @url https://github.com/JMcrafter26/sora-global-extractor
|
||||||
* @license CUSTOM LICENSE - see https://github.com/JMcrafter26/sora-global-extractor/blob/main/LICENSE
|
* @license CUSTOM LICENSE - see https://github.com/JMcrafter26/sora-global-extractor/blob/main/LICENSE
|
||||||
* @date 2025-11-05 15:44:57
|
* @date 2026-01-03 19:28:28
|
||||||
* @version 1.1.8
|
* @version 1.2.0
|
||||||
* @note This file was generated automatically.
|
* @note This file was generated automatically.
|
||||||
* The global extractor comes with an auto-updating feature, so you can always get the latest version. https://github.com/JMcrafter26/sora-global-extractor#-auto-updater
|
* The global extractor comes with an auto-updating feature, so you can always get the latest version. https://github.com/JMcrafter26/sora-global-extractor#-auto-updater
|
||||||
*/
|
*/
|
||||||
@@ -210,12 +210,20 @@ function globalExtractor(providers) {
|
|||||||
for (const [url, provider] of Object.entries(providers)) {
|
for (const [url, provider] of Object.entries(providers)) {
|
||||||
try {
|
try {
|
||||||
const streamUrl = extractStreamUrlByProvider(url, provider);
|
const streamUrl = extractStreamUrlByProvider(url, provider);
|
||||||
|
// check if streamUrl is an object with streamUrl property
|
||||||
|
if (streamUrl && typeof streamUrl === "object" && !Array.isArray(streamUrl) && streamUrl.streamUrl) {
|
||||||
|
return streamUrl.streamUrl;
|
||||||
|
}
|
||||||
// check if streamUrl is not null, a string, and starts with http or https
|
// check if streamUrl is not null, a string, and starts with http or https
|
||||||
if (streamUrl && typeof streamUrl === "string" && (streamUrl.startsWith("http"))) {
|
if (
|
||||||
|
streamUrl &&
|
||||||
|
typeof streamUrl === "string" &&
|
||||||
|
streamUrl.startsWith("http")
|
||||||
|
) {
|
||||||
return streamUrl;
|
return streamUrl;
|
||||||
// if its an array, get the value that starts with http
|
// if its an array, get the value that starts with http
|
||||||
} else if (Array.isArray(streamUrl)) {
|
} else if (Array.isArray(streamUrl)) {
|
||||||
const httpStream = streamUrl.find(url => url.startsWith("http"));
|
const httpStream = streamUrl.find((url) => url.startsWith("http"));
|
||||||
if (httpStream) {
|
if (httpStream) {
|
||||||
return httpStream;
|
return httpStream;
|
||||||
}
|
}
|
||||||
@@ -223,7 +231,6 @@ function globalExtractor(providers) {
|
|||||||
// check if it's a valid stream URL
|
// check if it's a valid stream URL
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
// Ignore the error and try the next provider
|
// Ignore the error and try the next provider
|
||||||
}
|
}
|
||||||
@@ -235,16 +242,30 @@ async function multiExtractor(providers) {
|
|||||||
/* this scheme should be returned as a JSON object
|
/* this scheme should be returned as a JSON object
|
||||||
{
|
{
|
||||||
"streams": [
|
"streams": [
|
||||||
"FileMoon",
|
{
|
||||||
"https://filemoon.example/stream1.m3u8",
|
"title": "FileMoon",
|
||||||
"StreamWish",
|
"streamUrl": "https://filemoon.example/stream1.m3u8",
|
||||||
"https://streamwish.example/stream2.m3u8",
|
},
|
||||||
"Okru",
|
{
|
||||||
"https://okru.example/stream3.m3u8",
|
"title": "StreamWish",
|
||||||
"MP4",
|
"streamUrl": "https://streamwish.example/stream2.m3u8",
|
||||||
"https://mp4upload.example/stream4.mp4",
|
},
|
||||||
"Default",
|
{
|
||||||
"https://default.example/stream5.m3u8"
|
"title": "Okru",
|
||||||
|
"streamUrl": "https://okru.example/stream3.m3u8",
|
||||||
|
"headers": { // Optional headers for the stream
|
||||||
|
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.3",
|
||||||
|
"Referer": "https://okru.example/",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "MP4",
|
||||||
|
"streamUrl": "https://mp4upload.example/stream4.mp4",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Default",
|
||||||
|
"streamUrl": "https://default.example/stream5.m3u8"
|
||||||
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
@@ -256,20 +277,21 @@ async function multiExtractor(providers) {
|
|||||||
// if provider starts with "direct-", then add the url to the streams array directly
|
// if provider starts with "direct-", then add the url to the streams array directly
|
||||||
if (provider.startsWith("direct-")) {
|
if (provider.startsWith("direct-")) {
|
||||||
const directName = provider.slice(7); // remove "direct-" prefix
|
const directName = provider.slice(7); // remove "direct-" prefix
|
||||||
if (directName && directName.length > 0) {
|
const title = (directName && directName.length > 0) ? directName : "Direct";
|
||||||
streams.push(directName, url);
|
streams.push({
|
||||||
} else {
|
title: title,
|
||||||
streams.push("Direct", url); // fallback to "Direct" if no name is provided
|
streamUrl: url
|
||||||
}
|
});
|
||||||
continue; // skip to the next provider
|
continue; // skip to the next provider
|
||||||
}
|
}
|
||||||
if (provider.startsWith("direct")) {
|
if (provider.startsWith("direct")) {
|
||||||
provider = provider.slice(7); // remove "direct-" prefix
|
provider = provider.slice(7); // remove "direct-" prefix
|
||||||
if (provider && provider.length > 0) {
|
const title = (provider && provider.length > 0) ? provider : "Direct";
|
||||||
streams.push(provider, url);
|
streams.push({
|
||||||
} else {
|
title: title,
|
||||||
streams.push("Direct", url); // fallback to "Direct" if no name is provided
|
streamUrl: url
|
||||||
}
|
});
|
||||||
|
continue; // skip to the next provider
|
||||||
}
|
}
|
||||||
|
|
||||||
let customName = null; // to store the custom name if provided
|
let customName = null; // to store the custom name if provided
|
||||||
@@ -286,15 +308,24 @@ async function multiExtractor(providers) {
|
|||||||
console.log(`Skipping ${provider} as it has already 3 streams`);
|
console.log(`Skipping ${provider} as it has already 3 streams`);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
let streamUrl = await extractStreamUrlByProvider(url, provider);
|
let result = await extractStreamUrlByProvider(url, provider);
|
||||||
|
let streamUrl = null;
|
||||||
if (streamUrl && Array.isArray(streamUrl)) {
|
let headers = null;
|
||||||
const httpStream = streamUrl.find(url => url.startsWith("http"));
|
|
||||||
|
// 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) {
|
if (httpStream) {
|
||||||
streamUrl = httpStream;
|
streamUrl = httpStream;
|
||||||
}
|
}
|
||||||
|
} else if (result && typeof result === "string") {
|
||||||
|
streamUrl = result;
|
||||||
}
|
}
|
||||||
// check if provider is already in streams, if it is, add a number to it
|
|
||||||
|
// check if streamUrl is valid
|
||||||
if (
|
if (
|
||||||
!streamUrl ||
|
!streamUrl ||
|
||||||
typeof streamUrl !== "string" ||
|
typeof streamUrl !== "string" ||
|
||||||
@@ -308,22 +339,29 @@ async function multiExtractor(providers) {
|
|||||||
provider = customName;
|
provider = customName;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let title;
|
||||||
if (providersCount[provider]) {
|
if (providersCount[provider]) {
|
||||||
providersCount[provider]++;
|
providersCount[provider]++;
|
||||||
streams.push(
|
title = provider.charAt(0).toUpperCase() +
|
||||||
provider.charAt(0).toUpperCase() +
|
|
||||||
provider.slice(1) +
|
provider.slice(1) +
|
||||||
"-" +
|
"-" +
|
||||||
(providersCount[provider] - 1), // add a number to the provider name
|
(providersCount[provider] - 1); // add a number to the provider name
|
||||||
streamUrl
|
|
||||||
);
|
|
||||||
} else {
|
} else {
|
||||||
providersCount[provider] = 1;
|
providersCount[provider] = 1;
|
||||||
streams.push(
|
title = provider.charAt(0).toUpperCase() + provider.slice(1);
|
||||||
provider.charAt(0).toUpperCase() + provider.slice(1),
|
|
||||||
streamUrl
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const streamObject = {
|
||||||
|
title: title,
|
||||||
|
streamUrl: streamUrl
|
||||||
|
};
|
||||||
|
|
||||||
|
// Add headers if they exist
|
||||||
|
if (headers && typeof headers === "object" && Object.keys(headers).length > 0) {
|
||||||
|
streamObject.headers = headers;
|
||||||
|
}
|
||||||
|
|
||||||
|
streams.push(streamObject);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
// Ignore the error and try the next provider
|
// Ignore the error and try the next provider
|
||||||
}
|
}
|
||||||
@@ -334,73 +372,98 @@ async function multiExtractor(providers) {
|
|||||||
async function extractStreamUrlByProvider(url, provider) {
|
async function extractStreamUrlByProvider(url, provider) {
|
||||||
if (eval(`typeof ${provider}Extractor`) !== "function") {
|
if (eval(`typeof ${provider}Extractor`) !== "function") {
|
||||||
// skip if the extractor is not defined
|
// skip if the extractor is not defined
|
||||||
console.log(`Extractor for provider ${provider} is not defined, skipping...`);
|
console.log(
|
||||||
|
`Extractor for provider ${provider} is not defined, skipping...`
|
||||||
|
);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
let uas = [
|
||||||
|
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.3",
|
||||||
|
"Mozilla/5.0 (iPhone; CPU iPhone OS 18_1_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/18.1.1 Mobile/15E148 Safari/604.1",
|
||||||
|
"Mozilla/5.0 (Linux; Android 10; SM-G973F) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Mobile Safari/537.36",
|
||||||
|
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.3",
|
||||||
|
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.1.2 Safari/605.1.15",
|
||||||
|
"Mozilla/5.0 (Linux; Android 11; Pixel 4 XL) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Mobile Safari/537.36",
|
||||||
|
];
|
||||||
let headers = {
|
let headers = {
|
||||||
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.3",
|
"User-Agent": uas[(url.length + provider.length) % uas.length], // use a different user agent based on the url and provider
|
||||||
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8",
|
"Accept":
|
||||||
|
"text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8",
|
||||||
"Accept-Language": "en-US,en;q=0.5",
|
"Accept-Language": "en-US,en;q=0.5",
|
||||||
"Referer": url,
|
"Referer": url,
|
||||||
"Connection": "keep-alive",
|
"Connection": "keep-alive",
|
||||||
"x-Requested-With": "XMLHttpRequest"
|
"x-Requested-With": "XMLHttpRequest",
|
||||||
};
|
};
|
||||||
if(provider == 'bigwarp') {
|
|
||||||
delete headers["User-Agent"];
|
switch (provider) {
|
||||||
headers["x-requested-with"] = "XMLHttpRequest";
|
case "bigwarp":
|
||||||
} else if (provider == 'vk') {
|
delete headers["User-Agent"];
|
||||||
headers["encoding"] = "windows-1251"; // required
|
break;
|
||||||
} else if (provider == 'sibnet') {
|
case "vk":
|
||||||
headers["encoding"] = "windows-1251"; // required
|
case "sibnet":
|
||||||
} else if (provider == 'supervideo') {
|
headers["encoding"] = "windows-1251"; // required
|
||||||
delete headers["User-Agent"];
|
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
|
// fetch the url
|
||||||
// and pass the response to the extractor function
|
// and pass the response to the extractor function
|
||||||
console.log("Fetching URL: " + url);
|
console.log("Fetching URL: " + url);
|
||||||
const response = await soraFetch(url, {
|
const response = await soraFetch(url, {
|
||||||
headers
|
headers,
|
||||||
});
|
});
|
||||||
|
|
||||||
console.log("Response: " + response.status);
|
console.log("Response: " + response.status);
|
||||||
let html = response.text ? await response.text() : response;
|
let html = response.text ? await response.text() : response;
|
||||||
// if title contains redirect, then get the redirect url
|
// if title contains redirect, then get the redirect url
|
||||||
const title = html.match(/<title>(.*?)<\/title>/);
|
const title = html.match(/<title>(.*?)<\/title>/);
|
||||||
if (title && title[1].toLowerCase().includes("redirect")) {
|
if (title && title[1].toLowerCase().includes("redirect")) {
|
||||||
const redirectUrl = html.match(/<meta http-equiv="refresh" content="0;url=(.*?)"/);
|
const matches = [
|
||||||
const redirectUrl2 = html.match(/window\.location\.href\s*=\s*["'](.*?)["']/);
|
/<meta http-equiv="refresh" content="0;url=(.*?)"/,
|
||||||
const redirectUrl3 = html.match(/window\.location\.replace\s*\(\s*["'](.*?)["']\s*\)/);
|
/window\.location\.href\s*=\s*["'](.*?)["']/,
|
||||||
if (redirectUrl) {
|
/window\.location\.replace\s*\(\s*["'](.*?)["']\s*\)/,
|
||||||
console.log("Redirect URL: " + redirectUrl[1]);
|
/window\.location\s*=\s*["'](.*?)["']/,
|
||||||
url = redirectUrl[1];
|
/window\.location\.assign\s*\(\s*["'](.*?)["']\s*\)/,
|
||||||
html = await soraFetch(url, {
|
/top\.location\s*=\s*["'](.*?)["']/,
|
||||||
headers
|
/top\.location\.replace\s*\(\s*["'](.*?)["']\s*\)/,
|
||||||
});
|
];
|
||||||
html = html.text ? await html.text() : html;
|
for (const match of matches) {
|
||||||
|
const redirectUrl = html.match(match);
|
||||||
} else if (redirectUrl2) {
|
if (redirectUrl && redirectUrl[1] && typeof redirectUrl[1] === "string" && redirectUrl[1].startsWith("http")) {
|
||||||
console.log("Redirect URL 2: " + redirectUrl2[1]);
|
console.log("Redirect URL found: " + redirectUrl[1]);
|
||||||
url = redirectUrl2[1];
|
url = redirectUrl[1];
|
||||||
html = await soraFetch(url, {
|
headers['Referer'] = url;
|
||||||
headers
|
headers['Host'] = url.match(/https?:\/\/([^\/]+)/)[1];
|
||||||
});
|
html = await soraFetch(url, {
|
||||||
html = html.text ? await html.text() : html;
|
headers,
|
||||||
} else if (redirectUrl3) {
|
}).then((res) => res.text());
|
||||||
console.log("Redirect URL 3: " + redirectUrl3[1]);
|
break;
|
||||||
url = redirectUrl3[1];
|
}
|
||||||
html = await soraFetch(url, {
|
|
||||||
headers
|
|
||||||
});
|
|
||||||
html = html.text ? await html.text() : html;
|
|
||||||
} else {
|
|
||||||
console.log("No redirect URL found");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// console.log("HTML: " + html);
|
// console.log("HTML: " + html);
|
||||||
switch (provider) {
|
switch (provider) {
|
||||||
case "bigwarp":
|
case "bigwarp":
|
||||||
try {
|
try {
|
||||||
return await bigwarpExtractor(html, url);
|
return await bigwarpExtractor(html, url);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@@ -449,6 +512,20 @@ async function extractStreamUrlByProvider(url, provider) {
|
|||||||
console.log("Error extracting stream URL from mp4upload:", error);
|
console.log("Error extracting stream URL from mp4upload:", error);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
case "oneupload":
|
||||||
|
try {
|
||||||
|
return await oneuploadExtractor(html, url);
|
||||||
|
} catch (error) {
|
||||||
|
console.log("Error extracting stream URL from oneupload:", error);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
case "packer":
|
||||||
|
try {
|
||||||
|
return await packerExtractor(html, url);
|
||||||
|
} catch (error) {
|
||||||
|
console.log("Error extracting stream URL from packer:", error);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
case "sendvid":
|
case "sendvid":
|
||||||
try {
|
try {
|
||||||
return await sendvidExtractor(html, url);
|
return await sendvidExtractor(html, url);
|
||||||
@@ -463,6 +540,13 @@ async function extractStreamUrlByProvider(url, provider) {
|
|||||||
console.log("Error extracting stream URL from sibnet:", error);
|
console.log("Error extracting stream URL from sibnet:", error);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
case "smoothpre":
|
||||||
|
try {
|
||||||
|
return await smoothpreExtractor(html, url);
|
||||||
|
} catch (error) {
|
||||||
|
console.log("Error extracting stream URL from smoothpre:", error);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
case "streamtape":
|
case "streamtape":
|
||||||
try {
|
try {
|
||||||
return await streamtapeExtractor(html, url);
|
return await streamtapeExtractor(html, url);
|
||||||
@@ -477,13 +561,6 @@ async function extractStreamUrlByProvider(url, provider) {
|
|||||||
console.log("Error extracting stream URL from streamup:", error);
|
console.log("Error extracting stream URL from streamup:", error);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
case "supervideo":
|
|
||||||
try {
|
|
||||||
return await supervideoExtractor(html, url);
|
|
||||||
} catch (error) {
|
|
||||||
console.log("Error extracting stream URL from supervideo:", error);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
case "uploadcx":
|
case "uploadcx":
|
||||||
try {
|
try {
|
||||||
return await uploadcxExtractor(html, url);
|
return await uploadcxExtractor(html, url);
|
||||||
@@ -532,7 +609,6 @@ async function extractStreamUrlByProvider(url, provider) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
////////////////////////////////////////////////
|
////////////////////////////////////////////////
|
||||||
// EXTRACTORS //
|
// EXTRACTORS //
|
||||||
////////////////////////////////////////////////
|
////////////////////////////////////////////////
|
||||||
@@ -1019,7 +1095,6 @@ async function megacloudExtractor(html, embedUrl) {
|
|||||||
* @author Cufiy
|
* @author Cufiy
|
||||||
*/
|
*/
|
||||||
async function mp4uploadExtractor(html, url = null) {
|
async function mp4uploadExtractor(html, url = null) {
|
||||||
// src: "https://a4.mp4upload.com:183/d/xkx3b4etz3b4quuo66rbmyqtjjoivahfxp27f35pti45rzapbvj5xwb4wuqtlpewdz4dirfp/video.mp4"
|
|
||||||
const regex = /src:\s*"([^"]+)"/;
|
const regex = /src:\s*"([^"]+)"/;
|
||||||
const match = html.match(regex);
|
const match = html.match(regex);
|
||||||
if (match) {
|
if (match) {
|
||||||
@@ -1029,6 +1104,32 @@ async function mp4uploadExtractor(html, url = null) {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
/* --- oneupload --- */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @name oneuploadExtractor
|
||||||
|
* @author 50/50
|
||||||
|
*/
|
||||||
|
async function oneuploadExtractor(data, url = null) {
|
||||||
|
const match = data.match(/sources:\s*\[\{file:"([^"]+)"\}\]/);
|
||||||
|
const fileUrl = match ? match[1] : null;
|
||||||
|
return fileUrl;
|
||||||
|
}
|
||||||
|
/* --- packer --- */
|
||||||
|
|
||||||
|
/* {REQUIRED PLUGINS: unbaser} */
|
||||||
|
/**
|
||||||
|
* @name packerExtractor
|
||||||
|
* @author 50/50
|
||||||
|
*/
|
||||||
|
async function packerExtractor(data, url = null) {
|
||||||
|
const obfuscatedScript = data.match(/<script[^>]*>\s*(eval\(function\(p,a,c,k,e,d.*?\)[\s\S]*?)<\/script>/);
|
||||||
|
const unpackedScript = unpack(obfuscatedScript[1]);
|
||||||
|
const m3u8Match = unpackedScript.match(/"hls2"\s*:\s*"([^"]+)"/);
|
||||||
|
const m3u8Url = m3u8Match[1];
|
||||||
|
return m3u8Url;
|
||||||
|
}
|
||||||
|
|
||||||
/* --- sendvid --- */
|
/* --- sendvid --- */
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1064,6 +1165,29 @@ async function sibnetExtractor(html, embedUrl) {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
/* --- smoothpre --- */
|
||||||
|
|
||||||
|
/* {REQUIRED PLUGINS: unbaser} */
|
||||||
|
/**
|
||||||
|
* @name SmoothPre Extractor
|
||||||
|
* @author 50/50
|
||||||
|
*/
|
||||||
|
async function smoothpreExtractor(data, url = null) {
|
||||||
|
console.log("Using SmoothPre Extractor");
|
||||||
|
console.log("Data Length: " + data.length);
|
||||||
|
const obfuscatedScript = data.match(/<script[^>]*>\s*(eval\(function\(p,a,c,k,e,d.*?\)[\s\S]*?)<\/script>/);
|
||||||
|
if (!obfuscatedScript || !obfuscatedScript[1]) {
|
||||||
|
console.log("No obfuscated script found");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
const unpackedScript = unpack(obfuscatedScript[1]);
|
||||||
|
|
||||||
|
const hls2Match = unpackedScript.match(/"hls2"\s*:\s*"([^"]+)"/);
|
||||||
|
const hls2Url = hls2Match ? hls2Match[1] : null;
|
||||||
|
return hls2Url;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/* --- streamtape --- */
|
/* --- streamtape --- */
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1147,26 +1271,6 @@ async function streamupExtractor(data, url = null) {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/* --- supervideo --- */
|
|
||||||
|
|
||||||
/* {REQUIRED PLUGINS: unbaser} */
|
|
||||||
/**
|
|
||||||
* @name SuperVideo Extractor
|
|
||||||
* @author 50/50
|
|
||||||
*/
|
|
||||||
async function supervideoExtractor(data, url = null) {
|
|
||||||
const obfuscatedScript = data.match(/<script[^>]*>\s*(eval\(function\(p,a,c,k,e,d.*?\)[\s\S]*?)<\/script>/);
|
|
||||||
const unpackedScript = unpack(obfuscatedScript[1]);
|
|
||||||
const regex = /file:\s*"([^"]+\.m3u8)"/;
|
|
||||||
const match = regex.exec(unpackedScript);
|
|
||||||
if (match) {
|
|
||||||
const fileUrl = match[1];
|
|
||||||
console.log("File URL:" + fileUrl);
|
|
||||||
return fileUrl;
|
|
||||||
}
|
|
||||||
return "No stream found";
|
|
||||||
}
|
|
||||||
|
|
||||||
/* --- uploadcx --- */
|
/* --- uploadcx --- */
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1231,7 +1335,7 @@ async function vidmolyExtractor(html, url = null) {
|
|||||||
const streamUrl = iframeMatch[1].startsWith("//")
|
const streamUrl = iframeMatch[1].startsWith("//")
|
||||||
? "https:" + iframeMatch[1]
|
? "https:" + iframeMatch[1]
|
||||||
: iframeMatch[1];
|
: iframeMatch[1];
|
||||||
const responseTwo = await fetchv2(streamUrl);
|
const responseTwo = await soraFetch(streamUrl);
|
||||||
const htmlTwo = await responseTwo.text();
|
const htmlTwo = await responseTwo.text();
|
||||||
const m3u8Match = htmlTwo.match(/sources:\s*\[\{file:"([^"]+\.m3u8)"/);
|
const m3u8Match = htmlTwo.match(/sources:\s*\[\{file:"([^"]+\.m3u8)"/);
|
||||||
return m3u8Match ? m3u8Match[1] : null;
|
return m3u8Match ? m3u8Match[1] : null;
|
||||||
@@ -1357,6 +1461,7 @@ function voeShiftChars(str, shift) {
|
|||||||
.join("");
|
.join("");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
////////////////////////////////////////////////
|
////////////////////////////////////////////////
|
||||||
// PLUGINS //
|
// PLUGINS //
|
||||||
////////////////////////////////////////////////
|
////////////////////////////////////////////////
|
||||||
@@ -1374,17 +1479,25 @@ function voeShiftChars(str, shift) {
|
|||||||
* @returns {Promise<Response|null>} The response from the server, or null if the
|
* @returns {Promise<Response|null>} The response from the server, or null if the
|
||||||
* request failed.
|
* request failed.
|
||||||
*/
|
*/
|
||||||
async function soraFetch(url, options = { headers: {}, method: 'GET', body: null }) {
|
async function soraFetch(
|
||||||
|
url,
|
||||||
|
options = { headers: {}, method: "GET", body: null }
|
||||||
|
) {
|
||||||
|
try {
|
||||||
|
return await fetchv2(
|
||||||
|
url,
|
||||||
|
options.headers ?? {},
|
||||||
|
options.method ?? "GET",
|
||||||
|
options.body ?? null
|
||||||
|
);
|
||||||
|
} catch (e) {
|
||||||
try {
|
try {
|
||||||
return await fetchv2(url, options.headers ?? {}, options.method ?? 'GET', options.body ?? null);
|
return await fetch(url, options);
|
||||||
} catch(e) {
|
} catch (error) {
|
||||||
try {
|
await console.log("soraFetch error: " + error.message);
|
||||||
return await fetch(url, options);
|
return null;
|
||||||
} catch(error) {
|
|
||||||
await console.log('soraFetch error: ' + error.message);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
/***********************************************************
|
/***********************************************************
|
||||||
* UNPACKER MODULE
|
* UNPACKER MODULE
|
||||||
@@ -1488,6 +1601,5 @@ function unpack(source) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* {GE END} */
|
/* {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
|
// EDITING THIS FILE COULD BREAK THE UPDATER AND CAUSE ISSUES WITH THE EXTRACTOR
|
||||||
|
|
||||||
/* {GE START} */
|
/* {GE START} */
|
||||||
/* {VERSION: 1.1.8} */
|
/* {VERSION: 1.2.0} */
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @name global_extractor.js
|
* @name global_extractor.js
|
||||||
@@ -198,8 +198,8 @@ async function sendLog(message) {
|
|||||||
* @author Cufiy
|
* @author Cufiy
|
||||||
* @url https://github.com/JMcrafter26/sora-global-extractor
|
* @url https://github.com/JMcrafter26/sora-global-extractor
|
||||||
* @license CUSTOM LICENSE - see https://github.com/JMcrafter26/sora-global-extractor/blob/main/LICENSE
|
* @license CUSTOM LICENSE - see https://github.com/JMcrafter26/sora-global-extractor/blob/main/LICENSE
|
||||||
* @date 2025-11-05 15:44:57
|
* @date 2026-01-03 19:28:28
|
||||||
* @version 1.1.8
|
* @version 1.2.0
|
||||||
* @note This file was generated automatically.
|
* @note This file was generated automatically.
|
||||||
* The global extractor comes with an auto-updating feature, so you can always get the latest version. https://github.com/JMcrafter26/sora-global-extractor#-auto-updater
|
* The global extractor comes with an auto-updating feature, so you can always get the latest version. https://github.com/JMcrafter26/sora-global-extractor#-auto-updater
|
||||||
*/
|
*/
|
||||||
@@ -209,12 +209,20 @@ function globalExtractor(providers) {
|
|||||||
for (const [url, provider] of Object.entries(providers)) {
|
for (const [url, provider] of Object.entries(providers)) {
|
||||||
try {
|
try {
|
||||||
const streamUrl = extractStreamUrlByProvider(url, provider);
|
const streamUrl = extractStreamUrlByProvider(url, provider);
|
||||||
|
// check if streamUrl is an object with streamUrl property
|
||||||
|
if (streamUrl && typeof streamUrl === "object" && !Array.isArray(streamUrl) && streamUrl.streamUrl) {
|
||||||
|
return streamUrl.streamUrl;
|
||||||
|
}
|
||||||
// check if streamUrl is not null, a string, and starts with http or https
|
// check if streamUrl is not null, a string, and starts with http or https
|
||||||
if (streamUrl && typeof streamUrl === "string" && (streamUrl.startsWith("http"))) {
|
if (
|
||||||
|
streamUrl &&
|
||||||
|
typeof streamUrl === "string" &&
|
||||||
|
streamUrl.startsWith("http")
|
||||||
|
) {
|
||||||
return streamUrl;
|
return streamUrl;
|
||||||
// if its an array, get the value that starts with http
|
// if its an array, get the value that starts with http
|
||||||
} else if (Array.isArray(streamUrl)) {
|
} else if (Array.isArray(streamUrl)) {
|
||||||
const httpStream = streamUrl.find(url => url.startsWith("http"));
|
const httpStream = streamUrl.find((url) => url.startsWith("http"));
|
||||||
if (httpStream) {
|
if (httpStream) {
|
||||||
return httpStream;
|
return httpStream;
|
||||||
}
|
}
|
||||||
@@ -222,7 +230,6 @@ function globalExtractor(providers) {
|
|||||||
// check if it's a valid stream URL
|
// check if it's a valid stream URL
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
// Ignore the error and try the next provider
|
// Ignore the error and try the next provider
|
||||||
}
|
}
|
||||||
@@ -234,16 +241,30 @@ async function multiExtractor(providers) {
|
|||||||
/* this scheme should be returned as a JSON object
|
/* this scheme should be returned as a JSON object
|
||||||
{
|
{
|
||||||
"streams": [
|
"streams": [
|
||||||
"FileMoon",
|
{
|
||||||
"https://filemoon.example/stream1.m3u8",
|
"title": "FileMoon",
|
||||||
"StreamWish",
|
"streamUrl": "https://filemoon.example/stream1.m3u8",
|
||||||
"https://streamwish.example/stream2.m3u8",
|
},
|
||||||
"Okru",
|
{
|
||||||
"https://okru.example/stream3.m3u8",
|
"title": "StreamWish",
|
||||||
"MP4",
|
"streamUrl": "https://streamwish.example/stream2.m3u8",
|
||||||
"https://mp4upload.example/stream4.mp4",
|
},
|
||||||
"Default",
|
{
|
||||||
"https://default.example/stream5.m3u8"
|
"title": "Okru",
|
||||||
|
"streamUrl": "https://okru.example/stream3.m3u8",
|
||||||
|
"headers": { // Optional headers for the stream
|
||||||
|
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.3",
|
||||||
|
"Referer": "https://okru.example/",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "MP4",
|
||||||
|
"streamUrl": "https://mp4upload.example/stream4.mp4",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Default",
|
||||||
|
"streamUrl": "https://default.example/stream5.m3u8"
|
||||||
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
@@ -255,20 +276,21 @@ async function multiExtractor(providers) {
|
|||||||
// if provider starts with "direct-", then add the url to the streams array directly
|
// if provider starts with "direct-", then add the url to the streams array directly
|
||||||
if (provider.startsWith("direct-")) {
|
if (provider.startsWith("direct-")) {
|
||||||
const directName = provider.slice(7); // remove "direct-" prefix
|
const directName = provider.slice(7); // remove "direct-" prefix
|
||||||
if (directName && directName.length > 0) {
|
const title = (directName && directName.length > 0) ? directName : "Direct";
|
||||||
streams.push(directName, url);
|
streams.push({
|
||||||
} else {
|
title: title,
|
||||||
streams.push("Direct", url); // fallback to "Direct" if no name is provided
|
streamUrl: url
|
||||||
}
|
});
|
||||||
continue; // skip to the next provider
|
continue; // skip to the next provider
|
||||||
}
|
}
|
||||||
if (provider.startsWith("direct")) {
|
if (provider.startsWith("direct")) {
|
||||||
provider = provider.slice(7); // remove "direct-" prefix
|
provider = provider.slice(7); // remove "direct-" prefix
|
||||||
if (provider && provider.length > 0) {
|
const title = (provider && provider.length > 0) ? provider : "Direct";
|
||||||
streams.push(provider, url);
|
streams.push({
|
||||||
} else {
|
title: title,
|
||||||
streams.push("Direct", url); // fallback to "Direct" if no name is provided
|
streamUrl: url
|
||||||
}
|
});
|
||||||
|
continue; // skip to the next provider
|
||||||
}
|
}
|
||||||
|
|
||||||
let customName = null; // to store the custom name if provided
|
let customName = null; // to store the custom name if provided
|
||||||
@@ -285,15 +307,24 @@ async function multiExtractor(providers) {
|
|||||||
console.log(`Skipping ${provider} as it has already 3 streams`);
|
console.log(`Skipping ${provider} as it has already 3 streams`);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
let streamUrl = await extractStreamUrlByProvider(url, provider);
|
let result = await extractStreamUrlByProvider(url, provider);
|
||||||
|
let streamUrl = null;
|
||||||
if (streamUrl && Array.isArray(streamUrl)) {
|
let headers = null;
|
||||||
const httpStream = streamUrl.find(url => url.startsWith("http"));
|
|
||||||
|
// 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) {
|
if (httpStream) {
|
||||||
streamUrl = httpStream;
|
streamUrl = httpStream;
|
||||||
}
|
}
|
||||||
|
} else if (result && typeof result === "string") {
|
||||||
|
streamUrl = result;
|
||||||
}
|
}
|
||||||
// check if provider is already in streams, if it is, add a number to it
|
|
||||||
|
// check if streamUrl is valid
|
||||||
if (
|
if (
|
||||||
!streamUrl ||
|
!streamUrl ||
|
||||||
typeof streamUrl !== "string" ||
|
typeof streamUrl !== "string" ||
|
||||||
@@ -307,22 +338,29 @@ async function multiExtractor(providers) {
|
|||||||
provider = customName;
|
provider = customName;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let title;
|
||||||
if (providersCount[provider]) {
|
if (providersCount[provider]) {
|
||||||
providersCount[provider]++;
|
providersCount[provider]++;
|
||||||
streams.push(
|
title = provider.charAt(0).toUpperCase() +
|
||||||
provider.charAt(0).toUpperCase() +
|
|
||||||
provider.slice(1) +
|
provider.slice(1) +
|
||||||
"-" +
|
"-" +
|
||||||
(providersCount[provider] - 1), // add a number to the provider name
|
(providersCount[provider] - 1); // add a number to the provider name
|
||||||
streamUrl
|
|
||||||
);
|
|
||||||
} else {
|
} else {
|
||||||
providersCount[provider] = 1;
|
providersCount[provider] = 1;
|
||||||
streams.push(
|
title = provider.charAt(0).toUpperCase() + provider.slice(1);
|
||||||
provider.charAt(0).toUpperCase() + provider.slice(1),
|
|
||||||
streamUrl
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const streamObject = {
|
||||||
|
title: title,
|
||||||
|
streamUrl: streamUrl
|
||||||
|
};
|
||||||
|
|
||||||
|
// Add headers if they exist
|
||||||
|
if (headers && typeof headers === "object" && Object.keys(headers).length > 0) {
|
||||||
|
streamObject.headers = headers;
|
||||||
|
}
|
||||||
|
|
||||||
|
streams.push(streamObject);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
// Ignore the error and try the next provider
|
// Ignore the error and try the next provider
|
||||||
}
|
}
|
||||||
@@ -333,73 +371,98 @@ async function multiExtractor(providers) {
|
|||||||
async function extractStreamUrlByProvider(url, provider) {
|
async function extractStreamUrlByProvider(url, provider) {
|
||||||
if (eval(`typeof ${provider}Extractor`) !== "function") {
|
if (eval(`typeof ${provider}Extractor`) !== "function") {
|
||||||
// skip if the extractor is not defined
|
// skip if the extractor is not defined
|
||||||
console.log(`Extractor for provider ${provider} is not defined, skipping...`);
|
console.log(
|
||||||
|
`Extractor for provider ${provider} is not defined, skipping...`
|
||||||
|
);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
let uas = [
|
||||||
|
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.3",
|
||||||
|
"Mozilla/5.0 (iPhone; CPU iPhone OS 18_1_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/18.1.1 Mobile/15E148 Safari/604.1",
|
||||||
|
"Mozilla/5.0 (Linux; Android 10; SM-G973F) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Mobile Safari/537.36",
|
||||||
|
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.3",
|
||||||
|
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.1.2 Safari/605.1.15",
|
||||||
|
"Mozilla/5.0 (Linux; Android 11; Pixel 4 XL) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Mobile Safari/537.36",
|
||||||
|
];
|
||||||
let headers = {
|
let headers = {
|
||||||
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.3",
|
"User-Agent": uas[(url.length + provider.length) % uas.length], // use a different user agent based on the url and provider
|
||||||
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8",
|
"Accept":
|
||||||
|
"text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8",
|
||||||
"Accept-Language": "en-US,en;q=0.5",
|
"Accept-Language": "en-US,en;q=0.5",
|
||||||
"Referer": url,
|
"Referer": url,
|
||||||
"Connection": "keep-alive",
|
"Connection": "keep-alive",
|
||||||
"x-Requested-With": "XMLHttpRequest"
|
"x-Requested-With": "XMLHttpRequest",
|
||||||
};
|
};
|
||||||
if(provider == 'bigwarp') {
|
|
||||||
delete headers["User-Agent"];
|
switch (provider) {
|
||||||
headers["x-requested-with"] = "XMLHttpRequest";
|
case "bigwarp":
|
||||||
} else if (provider == 'vk') {
|
delete headers["User-Agent"];
|
||||||
headers["encoding"] = "windows-1251"; // required
|
break;
|
||||||
} else if (provider == 'sibnet') {
|
case "vk":
|
||||||
headers["encoding"] = "windows-1251"; // required
|
case "sibnet":
|
||||||
} else if (provider == 'supervideo') {
|
headers["encoding"] = "windows-1251"; // required
|
||||||
delete headers["User-Agent"];
|
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
|
// fetch the url
|
||||||
// and pass the response to the extractor function
|
// and pass the response to the extractor function
|
||||||
console.log("Fetching URL: " + url);
|
console.log("Fetching URL: " + url);
|
||||||
const response = await soraFetch(url, {
|
const response = await soraFetch(url, {
|
||||||
headers
|
headers,
|
||||||
});
|
});
|
||||||
|
|
||||||
console.log("Response: " + response.status);
|
console.log("Response: " + response.status);
|
||||||
let html = response.text ? await response.text() : response;
|
let html = response.text ? await response.text() : response;
|
||||||
// if title contains redirect, then get the redirect url
|
// if title contains redirect, then get the redirect url
|
||||||
const title = html.match(/<title>(.*?)<\/title>/);
|
const title = html.match(/<title>(.*?)<\/title>/);
|
||||||
if (title && title[1].toLowerCase().includes("redirect")) {
|
if (title && title[1].toLowerCase().includes("redirect")) {
|
||||||
const redirectUrl = html.match(/<meta http-equiv="refresh" content="0;url=(.*?)"/);
|
const matches = [
|
||||||
const redirectUrl2 = html.match(/window\.location\.href\s*=\s*["'](.*?)["']/);
|
/<meta http-equiv="refresh" content="0;url=(.*?)"/,
|
||||||
const redirectUrl3 = html.match(/window\.location\.replace\s*\(\s*["'](.*?)["']\s*\)/);
|
/window\.location\.href\s*=\s*["'](.*?)["']/,
|
||||||
if (redirectUrl) {
|
/window\.location\.replace\s*\(\s*["'](.*?)["']\s*\)/,
|
||||||
console.log("Redirect URL: " + redirectUrl[1]);
|
/window\.location\s*=\s*["'](.*?)["']/,
|
||||||
url = redirectUrl[1];
|
/window\.location\.assign\s*\(\s*["'](.*?)["']\s*\)/,
|
||||||
html = await soraFetch(url, {
|
/top\.location\s*=\s*["'](.*?)["']/,
|
||||||
headers
|
/top\.location\.replace\s*\(\s*["'](.*?)["']\s*\)/,
|
||||||
});
|
];
|
||||||
html = html.text ? await html.text() : html;
|
for (const match of matches) {
|
||||||
|
const redirectUrl = html.match(match);
|
||||||
} else if (redirectUrl2) {
|
if (redirectUrl && redirectUrl[1] && typeof redirectUrl[1] === "string" && redirectUrl[1].startsWith("http")) {
|
||||||
console.log("Redirect URL 2: " + redirectUrl2[1]);
|
console.log("Redirect URL found: " + redirectUrl[1]);
|
||||||
url = redirectUrl2[1];
|
url = redirectUrl[1];
|
||||||
html = await soraFetch(url, {
|
headers['Referer'] = url;
|
||||||
headers
|
headers['Host'] = url.match(/https?:\/\/([^\/]+)/)[1];
|
||||||
});
|
html = await soraFetch(url, {
|
||||||
html = html.text ? await html.text() : html;
|
headers,
|
||||||
} else if (redirectUrl3) {
|
}).then((res) => res.text());
|
||||||
console.log("Redirect URL 3: " + redirectUrl3[1]);
|
break;
|
||||||
url = redirectUrl3[1];
|
}
|
||||||
html = await soraFetch(url, {
|
|
||||||
headers
|
|
||||||
});
|
|
||||||
html = html.text ? await html.text() : html;
|
|
||||||
} else {
|
|
||||||
console.log("No redirect URL found");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// console.log("HTML: " + html);
|
// console.log("HTML: " + html);
|
||||||
switch (provider) {
|
switch (provider) {
|
||||||
case "bigwarp":
|
case "bigwarp":
|
||||||
try {
|
try {
|
||||||
return await bigwarpExtractor(html, url);
|
return await bigwarpExtractor(html, url);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@@ -448,6 +511,20 @@ async function extractStreamUrlByProvider(url, provider) {
|
|||||||
console.log("Error extracting stream URL from mp4upload:", error);
|
console.log("Error extracting stream URL from mp4upload:", error);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
case "oneupload":
|
||||||
|
try {
|
||||||
|
return await oneuploadExtractor(html, url);
|
||||||
|
} catch (error) {
|
||||||
|
console.log("Error extracting stream URL from oneupload:", error);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
case "packer":
|
||||||
|
try {
|
||||||
|
return await packerExtractor(html, url);
|
||||||
|
} catch (error) {
|
||||||
|
console.log("Error extracting stream URL from packer:", error);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
case "sendvid":
|
case "sendvid":
|
||||||
try {
|
try {
|
||||||
return await sendvidExtractor(html, url);
|
return await sendvidExtractor(html, url);
|
||||||
@@ -462,6 +539,13 @@ async function extractStreamUrlByProvider(url, provider) {
|
|||||||
console.log("Error extracting stream URL from sibnet:", error);
|
console.log("Error extracting stream URL from sibnet:", error);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
case "smoothpre":
|
||||||
|
try {
|
||||||
|
return await smoothpreExtractor(html, url);
|
||||||
|
} catch (error) {
|
||||||
|
console.log("Error extracting stream URL from smoothpre:", error);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
case "streamtape":
|
case "streamtape":
|
||||||
try {
|
try {
|
||||||
return await streamtapeExtractor(html, url);
|
return await streamtapeExtractor(html, url);
|
||||||
@@ -476,13 +560,6 @@ async function extractStreamUrlByProvider(url, provider) {
|
|||||||
console.log("Error extracting stream URL from streamup:", error);
|
console.log("Error extracting stream URL from streamup:", error);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
case "supervideo":
|
|
||||||
try {
|
|
||||||
return await supervideoExtractor(html, url);
|
|
||||||
} catch (error) {
|
|
||||||
console.log("Error extracting stream URL from supervideo:", error);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
case "uploadcx":
|
case "uploadcx":
|
||||||
try {
|
try {
|
||||||
return await uploadcxExtractor(html, url);
|
return await uploadcxExtractor(html, url);
|
||||||
@@ -531,7 +608,6 @@ async function extractStreamUrlByProvider(url, provider) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
////////////////////////////////////////////////
|
////////////////////////////////////////////////
|
||||||
// EXTRACTORS //
|
// EXTRACTORS //
|
||||||
////////////////////////////////////////////////
|
////////////////////////////////////////////////
|
||||||
@@ -1018,7 +1094,6 @@ async function megacloudExtractor(html, embedUrl) {
|
|||||||
* @author Cufiy
|
* @author Cufiy
|
||||||
*/
|
*/
|
||||||
async function mp4uploadExtractor(html, url = null) {
|
async function mp4uploadExtractor(html, url = null) {
|
||||||
// src: "https://a4.mp4upload.com:183/d/xkx3b4etz3b4quuo66rbmyqtjjoivahfxp27f35pti45rzapbvj5xwb4wuqtlpewdz4dirfp/video.mp4"
|
|
||||||
const regex = /src:\s*"([^"]+)"/;
|
const regex = /src:\s*"([^"]+)"/;
|
||||||
const match = html.match(regex);
|
const match = html.match(regex);
|
||||||
if (match) {
|
if (match) {
|
||||||
@@ -1028,6 +1103,32 @@ async function mp4uploadExtractor(html, url = null) {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
/* --- oneupload --- */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @name oneuploadExtractor
|
||||||
|
* @author 50/50
|
||||||
|
*/
|
||||||
|
async function oneuploadExtractor(data, url = null) {
|
||||||
|
const match = data.match(/sources:\s*\[\{file:"([^"]+)"\}\]/);
|
||||||
|
const fileUrl = match ? match[1] : null;
|
||||||
|
return fileUrl;
|
||||||
|
}
|
||||||
|
/* --- packer --- */
|
||||||
|
|
||||||
|
/* {REQUIRED PLUGINS: unbaser} */
|
||||||
|
/**
|
||||||
|
* @name packerExtractor
|
||||||
|
* @author 50/50
|
||||||
|
*/
|
||||||
|
async function packerExtractor(data, url = null) {
|
||||||
|
const obfuscatedScript = data.match(/<script[^>]*>\s*(eval\(function\(p,a,c,k,e,d.*?\)[\s\S]*?)<\/script>/);
|
||||||
|
const unpackedScript = unpack(obfuscatedScript[1]);
|
||||||
|
const m3u8Match = unpackedScript.match(/"hls2"\s*:\s*"([^"]+)"/);
|
||||||
|
const m3u8Url = m3u8Match[1];
|
||||||
|
return m3u8Url;
|
||||||
|
}
|
||||||
|
|
||||||
/* --- sendvid --- */
|
/* --- sendvid --- */
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1063,6 +1164,29 @@ async function sibnetExtractor(html, embedUrl) {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
/* --- smoothpre --- */
|
||||||
|
|
||||||
|
/* {REQUIRED PLUGINS: unbaser} */
|
||||||
|
/**
|
||||||
|
* @name SmoothPre Extractor
|
||||||
|
* @author 50/50
|
||||||
|
*/
|
||||||
|
async function smoothpreExtractor(data, url = null) {
|
||||||
|
console.log("Using SmoothPre Extractor");
|
||||||
|
console.log("Data Length: " + data.length);
|
||||||
|
const obfuscatedScript = data.match(/<script[^>]*>\s*(eval\(function\(p,a,c,k,e,d.*?\)[\s\S]*?)<\/script>/);
|
||||||
|
if (!obfuscatedScript || !obfuscatedScript[1]) {
|
||||||
|
console.log("No obfuscated script found");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
const unpackedScript = unpack(obfuscatedScript[1]);
|
||||||
|
|
||||||
|
const hls2Match = unpackedScript.match(/"hls2"\s*:\s*"([^"]+)"/);
|
||||||
|
const hls2Url = hls2Match ? hls2Match[1] : null;
|
||||||
|
return hls2Url;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/* --- streamtape --- */
|
/* --- streamtape --- */
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1146,26 +1270,6 @@ async function streamupExtractor(data, url = null) {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/* --- supervideo --- */
|
|
||||||
|
|
||||||
/* {REQUIRED PLUGINS: unbaser} */
|
|
||||||
/**
|
|
||||||
* @name SuperVideo Extractor
|
|
||||||
* @author 50/50
|
|
||||||
*/
|
|
||||||
async function supervideoExtractor(data, url = null) {
|
|
||||||
const obfuscatedScript = data.match(/<script[^>]*>\s*(eval\(function\(p,a,c,k,e,d.*?\)[\s\S]*?)<\/script>/);
|
|
||||||
const unpackedScript = unpack(obfuscatedScript[1]);
|
|
||||||
const regex = /file:\s*"([^"]+\.m3u8)"/;
|
|
||||||
const match = regex.exec(unpackedScript);
|
|
||||||
if (match) {
|
|
||||||
const fileUrl = match[1];
|
|
||||||
console.log("File URL:" + fileUrl);
|
|
||||||
return fileUrl;
|
|
||||||
}
|
|
||||||
return "No stream found";
|
|
||||||
}
|
|
||||||
|
|
||||||
/* --- uploadcx --- */
|
/* --- uploadcx --- */
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1230,7 +1334,7 @@ async function vidmolyExtractor(html, url = null) {
|
|||||||
const streamUrl = iframeMatch[1].startsWith("//")
|
const streamUrl = iframeMatch[1].startsWith("//")
|
||||||
? "https:" + iframeMatch[1]
|
? "https:" + iframeMatch[1]
|
||||||
: iframeMatch[1];
|
: iframeMatch[1];
|
||||||
const responseTwo = await fetchv2(streamUrl);
|
const responseTwo = await soraFetch(streamUrl);
|
||||||
const htmlTwo = await responseTwo.text();
|
const htmlTwo = await responseTwo.text();
|
||||||
const m3u8Match = htmlTwo.match(/sources:\s*\[\{file:"([^"]+\.m3u8)"/);
|
const m3u8Match = htmlTwo.match(/sources:\s*\[\{file:"([^"]+\.m3u8)"/);
|
||||||
return m3u8Match ? m3u8Match[1] : null;
|
return m3u8Match ? m3u8Match[1] : null;
|
||||||
@@ -1356,6 +1460,7 @@ function voeShiftChars(str, shift) {
|
|||||||
.join("");
|
.join("");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
////////////////////////////////////////////////
|
////////////////////////////////////////////////
|
||||||
// PLUGINS //
|
// PLUGINS //
|
||||||
////////////////////////////////////////////////
|
////////////////////////////////////////////////
|
||||||
@@ -1373,17 +1478,25 @@ function voeShiftChars(str, shift) {
|
|||||||
* @returns {Promise<Response|null>} The response from the server, or null if the
|
* @returns {Promise<Response|null>} The response from the server, or null if the
|
||||||
* request failed.
|
* request failed.
|
||||||
*/
|
*/
|
||||||
async function soraFetch(url, options = { headers: {}, method: 'GET', body: null }) {
|
async function soraFetch(
|
||||||
|
url,
|
||||||
|
options = { headers: {}, method: "GET", body: null }
|
||||||
|
) {
|
||||||
|
try {
|
||||||
|
return await fetchv2(
|
||||||
|
url,
|
||||||
|
options.headers ?? {},
|
||||||
|
options.method ?? "GET",
|
||||||
|
options.body ?? null
|
||||||
|
);
|
||||||
|
} catch (e) {
|
||||||
try {
|
try {
|
||||||
return await fetchv2(url, options.headers ?? {}, options.method ?? 'GET', options.body ?? null);
|
return await fetch(url, options);
|
||||||
} catch(e) {
|
} catch (error) {
|
||||||
try {
|
await console.log("soraFetch error: " + error.message);
|
||||||
return await fetch(url, options);
|
return null;
|
||||||
} catch(error) {
|
|
||||||
await console.log('soraFetch error: ' + error.message);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
/***********************************************************
|
/***********************************************************
|
||||||
* UNPACKER MODULE
|
* UNPACKER MODULE
|
||||||
@@ -1487,6 +1600,5 @@ function unpack(source) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* {GE END} */
|
/* {GE END} */
|
||||||
+2
-2
@@ -5,7 +5,7 @@
|
|||||||
"name": "Cufiy",
|
"name": "Cufiy",
|
||||||
"icon": "https://files.catbox.moe/ttj4fc.gif"
|
"icon": "https://files.catbox.moe/ttj4fc.gif"
|
||||||
},
|
},
|
||||||
"version": "0.3.16",
|
"version": "0.3.17",
|
||||||
"language": "English (DUB)",
|
"language": "English (DUB)",
|
||||||
"streamType": "HLS",
|
"streamType": "HLS",
|
||||||
"quality": "720p",
|
"quality": "720p",
|
||||||
@@ -17,4 +17,4 @@
|
|||||||
"type": "shows",
|
"type": "shows",
|
||||||
"supportsSora": true,
|
"supportsSora": true,
|
||||||
"supportsLuna": 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
|
// EDITING THIS FILE COULD BREAK THE UPDATER AND CAUSE ISSUES WITH THE EXTRACTOR
|
||||||
|
|
||||||
/* {GE START} */
|
/* {GE START} */
|
||||||
/* {VERSION: 1.1.8} */
|
/* {VERSION: 1.2.0} */
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @name global_extractor.js
|
* @name global_extractor.js
|
||||||
@@ -384,8 +384,8 @@ function base64Decode(str) {
|
|||||||
* @author Cufiy
|
* @author Cufiy
|
||||||
* @url https://github.com/JMcrafter26/sora-global-extractor
|
* @url https://github.com/JMcrafter26/sora-global-extractor
|
||||||
* @license CUSTOM LICENSE - see https://github.com/JMcrafter26/sora-global-extractor/blob/main/LICENSE
|
* @license CUSTOM LICENSE - see https://github.com/JMcrafter26/sora-global-extractor/blob/main/LICENSE
|
||||||
* @date 2025-11-05 15:44:57
|
* @date 2026-01-03 19:28:28
|
||||||
* @version 1.1.8
|
* @version 1.2.0
|
||||||
* @note This file was generated automatically.
|
* @note This file was generated automatically.
|
||||||
* The global extractor comes with an auto-updating feature, so you can always get the latest version. https://github.com/JMcrafter26/sora-global-extractor#-auto-updater
|
* The global extractor comes with an auto-updating feature, so you can always get the latest version. https://github.com/JMcrafter26/sora-global-extractor#-auto-updater
|
||||||
*/
|
*/
|
||||||
@@ -395,12 +395,20 @@ function globalExtractor(providers) {
|
|||||||
for (const [url, provider] of Object.entries(providers)) {
|
for (const [url, provider] of Object.entries(providers)) {
|
||||||
try {
|
try {
|
||||||
const streamUrl = extractStreamUrlByProvider(url, provider);
|
const streamUrl = extractStreamUrlByProvider(url, provider);
|
||||||
|
// check if streamUrl is an object with streamUrl property
|
||||||
|
if (streamUrl && typeof streamUrl === "object" && !Array.isArray(streamUrl) && streamUrl.streamUrl) {
|
||||||
|
return streamUrl.streamUrl;
|
||||||
|
}
|
||||||
// check if streamUrl is not null, a string, and starts with http or https
|
// check if streamUrl is not null, a string, and starts with http or https
|
||||||
if (streamUrl && typeof streamUrl === "string" && (streamUrl.startsWith("http"))) {
|
if (
|
||||||
|
streamUrl &&
|
||||||
|
typeof streamUrl === "string" &&
|
||||||
|
streamUrl.startsWith("http")
|
||||||
|
) {
|
||||||
return streamUrl;
|
return streamUrl;
|
||||||
// if its an array, get the value that starts with http
|
// if its an array, get the value that starts with http
|
||||||
} else if (Array.isArray(streamUrl)) {
|
} else if (Array.isArray(streamUrl)) {
|
||||||
const httpStream = streamUrl.find(url => url.startsWith("http"));
|
const httpStream = streamUrl.find((url) => url.startsWith("http"));
|
||||||
if (httpStream) {
|
if (httpStream) {
|
||||||
return httpStream;
|
return httpStream;
|
||||||
}
|
}
|
||||||
@@ -408,7 +416,6 @@ function globalExtractor(providers) {
|
|||||||
// check if it's a valid stream URL
|
// check if it's a valid stream URL
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
// Ignore the error and try the next provider
|
// Ignore the error and try the next provider
|
||||||
}
|
}
|
||||||
@@ -420,16 +427,30 @@ async function multiExtractor(providers) {
|
|||||||
/* this scheme should be returned as a JSON object
|
/* this scheme should be returned as a JSON object
|
||||||
{
|
{
|
||||||
"streams": [
|
"streams": [
|
||||||
"FileMoon",
|
{
|
||||||
"https://filemoon.example/stream1.m3u8",
|
"title": "FileMoon",
|
||||||
"StreamWish",
|
"streamUrl": "https://filemoon.example/stream1.m3u8",
|
||||||
"https://streamwish.example/stream2.m3u8",
|
},
|
||||||
"Okru",
|
{
|
||||||
"https://okru.example/stream3.m3u8",
|
"title": "StreamWish",
|
||||||
"MP4",
|
"streamUrl": "https://streamwish.example/stream2.m3u8",
|
||||||
"https://mp4upload.example/stream4.mp4",
|
},
|
||||||
"Default",
|
{
|
||||||
"https://default.example/stream5.m3u8"
|
"title": "Okru",
|
||||||
|
"streamUrl": "https://okru.example/stream3.m3u8",
|
||||||
|
"headers": { // Optional headers for the stream
|
||||||
|
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.3",
|
||||||
|
"Referer": "https://okru.example/",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "MP4",
|
||||||
|
"streamUrl": "https://mp4upload.example/stream4.mp4",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Default",
|
||||||
|
"streamUrl": "https://default.example/stream5.m3u8"
|
||||||
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
@@ -441,20 +462,21 @@ async function multiExtractor(providers) {
|
|||||||
// if provider starts with "direct-", then add the url to the streams array directly
|
// if provider starts with "direct-", then add the url to the streams array directly
|
||||||
if (provider.startsWith("direct-")) {
|
if (provider.startsWith("direct-")) {
|
||||||
const directName = provider.slice(7); // remove "direct-" prefix
|
const directName = provider.slice(7); // remove "direct-" prefix
|
||||||
if (directName && directName.length > 0) {
|
const title = (directName && directName.length > 0) ? directName : "Direct";
|
||||||
streams.push(directName, url);
|
streams.push({
|
||||||
} else {
|
title: title,
|
||||||
streams.push("Direct", url); // fallback to "Direct" if no name is provided
|
streamUrl: url
|
||||||
}
|
});
|
||||||
continue; // skip to the next provider
|
continue; // skip to the next provider
|
||||||
}
|
}
|
||||||
if (provider.startsWith("direct")) {
|
if (provider.startsWith("direct")) {
|
||||||
provider = provider.slice(7); // remove "direct-" prefix
|
provider = provider.slice(7); // remove "direct-" prefix
|
||||||
if (provider && provider.length > 0) {
|
const title = (provider && provider.length > 0) ? provider : "Direct";
|
||||||
streams.push(provider, url);
|
streams.push({
|
||||||
} else {
|
title: title,
|
||||||
streams.push("Direct", url); // fallback to "Direct" if no name is provided
|
streamUrl: url
|
||||||
}
|
});
|
||||||
|
continue; // skip to the next provider
|
||||||
}
|
}
|
||||||
|
|
||||||
let customName = null; // to store the custom name if provided
|
let customName = null; // to store the custom name if provided
|
||||||
@@ -471,15 +493,24 @@ async function multiExtractor(providers) {
|
|||||||
console.log(`Skipping ${provider} as it has already 3 streams`);
|
console.log(`Skipping ${provider} as it has already 3 streams`);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
let streamUrl = await extractStreamUrlByProvider(url, provider);
|
let result = await extractStreamUrlByProvider(url, provider);
|
||||||
|
let streamUrl = null;
|
||||||
if (streamUrl && Array.isArray(streamUrl)) {
|
let headers = null;
|
||||||
const httpStream = streamUrl.find(url => url.startsWith("http"));
|
|
||||||
|
// 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) {
|
if (httpStream) {
|
||||||
streamUrl = httpStream;
|
streamUrl = httpStream;
|
||||||
}
|
}
|
||||||
|
} else if (result && typeof result === "string") {
|
||||||
|
streamUrl = result;
|
||||||
}
|
}
|
||||||
// check if provider is already in streams, if it is, add a number to it
|
|
||||||
|
// check if streamUrl is valid
|
||||||
if (
|
if (
|
||||||
!streamUrl ||
|
!streamUrl ||
|
||||||
typeof streamUrl !== "string" ||
|
typeof streamUrl !== "string" ||
|
||||||
@@ -493,22 +524,29 @@ async function multiExtractor(providers) {
|
|||||||
provider = customName;
|
provider = customName;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let title;
|
||||||
if (providersCount[provider]) {
|
if (providersCount[provider]) {
|
||||||
providersCount[provider]++;
|
providersCount[provider]++;
|
||||||
streams.push(
|
title = provider.charAt(0).toUpperCase() +
|
||||||
provider.charAt(0).toUpperCase() +
|
|
||||||
provider.slice(1) +
|
provider.slice(1) +
|
||||||
"-" +
|
"-" +
|
||||||
(providersCount[provider] - 1), // add a number to the provider name
|
(providersCount[provider] - 1); // add a number to the provider name
|
||||||
streamUrl
|
|
||||||
);
|
|
||||||
} else {
|
} else {
|
||||||
providersCount[provider] = 1;
|
providersCount[provider] = 1;
|
||||||
streams.push(
|
title = provider.charAt(0).toUpperCase() + provider.slice(1);
|
||||||
provider.charAt(0).toUpperCase() + provider.slice(1),
|
|
||||||
streamUrl
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const streamObject = {
|
||||||
|
title: title,
|
||||||
|
streamUrl: streamUrl
|
||||||
|
};
|
||||||
|
|
||||||
|
// Add headers if they exist
|
||||||
|
if (headers && typeof headers === "object" && Object.keys(headers).length > 0) {
|
||||||
|
streamObject.headers = headers;
|
||||||
|
}
|
||||||
|
|
||||||
|
streams.push(streamObject);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
// Ignore the error and try the next provider
|
// Ignore the error and try the next provider
|
||||||
}
|
}
|
||||||
@@ -519,73 +557,98 @@ async function multiExtractor(providers) {
|
|||||||
async function extractStreamUrlByProvider(url, provider) {
|
async function extractStreamUrlByProvider(url, provider) {
|
||||||
if (eval(`typeof ${provider}Extractor`) !== "function") {
|
if (eval(`typeof ${provider}Extractor`) !== "function") {
|
||||||
// skip if the extractor is not defined
|
// skip if the extractor is not defined
|
||||||
console.log(`Extractor for provider ${provider} is not defined, skipping...`);
|
console.log(
|
||||||
|
`Extractor for provider ${provider} is not defined, skipping...`
|
||||||
|
);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
let uas = [
|
||||||
|
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.3",
|
||||||
|
"Mozilla/5.0 (iPhone; CPU iPhone OS 18_1_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/18.1.1 Mobile/15E148 Safari/604.1",
|
||||||
|
"Mozilla/5.0 (Linux; Android 10; SM-G973F) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Mobile Safari/537.36",
|
||||||
|
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.3",
|
||||||
|
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.1.2 Safari/605.1.15",
|
||||||
|
"Mozilla/5.0 (Linux; Android 11; Pixel 4 XL) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Mobile Safari/537.36",
|
||||||
|
];
|
||||||
let headers = {
|
let headers = {
|
||||||
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.3",
|
"User-Agent": uas[(url.length + provider.length) % uas.length], // use a different user agent based on the url and provider
|
||||||
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8",
|
"Accept":
|
||||||
|
"text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8",
|
||||||
"Accept-Language": "en-US,en;q=0.5",
|
"Accept-Language": "en-US,en;q=0.5",
|
||||||
"Referer": url,
|
"Referer": url,
|
||||||
"Connection": "keep-alive",
|
"Connection": "keep-alive",
|
||||||
"x-Requested-With": "XMLHttpRequest"
|
"x-Requested-With": "XMLHttpRequest",
|
||||||
};
|
};
|
||||||
if(provider == 'bigwarp') {
|
|
||||||
delete headers["User-Agent"];
|
switch (provider) {
|
||||||
headers["x-requested-with"] = "XMLHttpRequest";
|
case "bigwarp":
|
||||||
} else if (provider == 'vk') {
|
delete headers["User-Agent"];
|
||||||
headers["encoding"] = "windows-1251"; // required
|
break;
|
||||||
} else if (provider == 'sibnet') {
|
case "vk":
|
||||||
headers["encoding"] = "windows-1251"; // required
|
case "sibnet":
|
||||||
} else if (provider == 'supervideo') {
|
headers["encoding"] = "windows-1251"; // required
|
||||||
delete headers["User-Agent"];
|
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
|
// fetch the url
|
||||||
// and pass the response to the extractor function
|
// and pass the response to the extractor function
|
||||||
console.log("Fetching URL: " + url);
|
console.log("Fetching URL: " + url);
|
||||||
const response = await soraFetch(url, {
|
const response = await soraFetch(url, {
|
||||||
headers
|
headers,
|
||||||
});
|
});
|
||||||
|
|
||||||
console.log("Response: " + response.status);
|
console.log("Response: " + response.status);
|
||||||
let html = response.text ? await response.text() : response;
|
let html = response.text ? await response.text() : response;
|
||||||
// if title contains redirect, then get the redirect url
|
// if title contains redirect, then get the redirect url
|
||||||
const title = html.match(/<title>(.*?)<\/title>/);
|
const title = html.match(/<title>(.*?)<\/title>/);
|
||||||
if (title && title[1].toLowerCase().includes("redirect")) {
|
if (title && title[1].toLowerCase().includes("redirect")) {
|
||||||
const redirectUrl = html.match(/<meta http-equiv="refresh" content="0;url=(.*?)"/);
|
const matches = [
|
||||||
const redirectUrl2 = html.match(/window\.location\.href\s*=\s*["'](.*?)["']/);
|
/<meta http-equiv="refresh" content="0;url=(.*?)"/,
|
||||||
const redirectUrl3 = html.match(/window\.location\.replace\s*\(\s*["'](.*?)["']\s*\)/);
|
/window\.location\.href\s*=\s*["'](.*?)["']/,
|
||||||
if (redirectUrl) {
|
/window\.location\.replace\s*\(\s*["'](.*?)["']\s*\)/,
|
||||||
console.log("Redirect URL: " + redirectUrl[1]);
|
/window\.location\s*=\s*["'](.*?)["']/,
|
||||||
url = redirectUrl[1];
|
/window\.location\.assign\s*\(\s*["'](.*?)["']\s*\)/,
|
||||||
html = await soraFetch(url, {
|
/top\.location\s*=\s*["'](.*?)["']/,
|
||||||
headers
|
/top\.location\.replace\s*\(\s*["'](.*?)["']\s*\)/,
|
||||||
});
|
];
|
||||||
html = html.text ? await html.text() : html;
|
for (const match of matches) {
|
||||||
|
const redirectUrl = html.match(match);
|
||||||
} else if (redirectUrl2) {
|
if (redirectUrl && redirectUrl[1] && typeof redirectUrl[1] === "string" && redirectUrl[1].startsWith("http")) {
|
||||||
console.log("Redirect URL 2: " + redirectUrl2[1]);
|
console.log("Redirect URL found: " + redirectUrl[1]);
|
||||||
url = redirectUrl2[1];
|
url = redirectUrl[1];
|
||||||
html = await soraFetch(url, {
|
headers['Referer'] = url;
|
||||||
headers
|
headers['Host'] = url.match(/https?:\/\/([^\/]+)/)[1];
|
||||||
});
|
html = await soraFetch(url, {
|
||||||
html = html.text ? await html.text() : html;
|
headers,
|
||||||
} else if (redirectUrl3) {
|
}).then((res) => res.text());
|
||||||
console.log("Redirect URL 3: " + redirectUrl3[1]);
|
break;
|
||||||
url = redirectUrl3[1];
|
}
|
||||||
html = await soraFetch(url, {
|
|
||||||
headers
|
|
||||||
});
|
|
||||||
html = html.text ? await html.text() : html;
|
|
||||||
} else {
|
|
||||||
console.log("No redirect URL found");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// console.log("HTML: " + html);
|
// console.log("HTML: " + html);
|
||||||
switch (provider) {
|
switch (provider) {
|
||||||
case "bigwarp":
|
case "bigwarp":
|
||||||
try {
|
try {
|
||||||
return await bigwarpExtractor(html, url);
|
return await bigwarpExtractor(html, url);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@@ -634,6 +697,20 @@ async function extractStreamUrlByProvider(url, provider) {
|
|||||||
console.log("Error extracting stream URL from mp4upload:", error);
|
console.log("Error extracting stream URL from mp4upload:", error);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
case "oneupload":
|
||||||
|
try {
|
||||||
|
return await oneuploadExtractor(html, url);
|
||||||
|
} catch (error) {
|
||||||
|
console.log("Error extracting stream URL from oneupload:", error);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
case "packer":
|
||||||
|
try {
|
||||||
|
return await packerExtractor(html, url);
|
||||||
|
} catch (error) {
|
||||||
|
console.log("Error extracting stream URL from packer:", error);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
case "sendvid":
|
case "sendvid":
|
||||||
try {
|
try {
|
||||||
return await sendvidExtractor(html, url);
|
return await sendvidExtractor(html, url);
|
||||||
@@ -648,6 +725,13 @@ async function extractStreamUrlByProvider(url, provider) {
|
|||||||
console.log("Error extracting stream URL from sibnet:", error);
|
console.log("Error extracting stream URL from sibnet:", error);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
case "smoothpre":
|
||||||
|
try {
|
||||||
|
return await smoothpreExtractor(html, url);
|
||||||
|
} catch (error) {
|
||||||
|
console.log("Error extracting stream URL from smoothpre:", error);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
case "streamtape":
|
case "streamtape":
|
||||||
try {
|
try {
|
||||||
return await streamtapeExtractor(html, url);
|
return await streamtapeExtractor(html, url);
|
||||||
@@ -662,13 +746,6 @@ async function extractStreamUrlByProvider(url, provider) {
|
|||||||
console.log("Error extracting stream URL from streamup:", error);
|
console.log("Error extracting stream URL from streamup:", error);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
case "supervideo":
|
|
||||||
try {
|
|
||||||
return await supervideoExtractor(html, url);
|
|
||||||
} catch (error) {
|
|
||||||
console.log("Error extracting stream URL from supervideo:", error);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
case "uploadcx":
|
case "uploadcx":
|
||||||
try {
|
try {
|
||||||
return await uploadcxExtractor(html, url);
|
return await uploadcxExtractor(html, url);
|
||||||
@@ -717,7 +794,6 @@ async function extractStreamUrlByProvider(url, provider) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
////////////////////////////////////////////////
|
////////////////////////////////////////////////
|
||||||
// EXTRACTORS //
|
// EXTRACTORS //
|
||||||
////////////////////////////////////////////////
|
////////////////////////////////////////////////
|
||||||
@@ -1204,7 +1280,6 @@ async function megacloudExtractor(html, embedUrl) {
|
|||||||
* @author Cufiy
|
* @author Cufiy
|
||||||
*/
|
*/
|
||||||
async function mp4uploadExtractor(html, url = null) {
|
async function mp4uploadExtractor(html, url = null) {
|
||||||
// src: "https://a4.mp4upload.com:183/d/xkx3b4etz3b4quuo66rbmyqtjjoivahfxp27f35pti45rzapbvj5xwb4wuqtlpewdz4dirfp/video.mp4"
|
|
||||||
const regex = /src:\s*"([^"]+)"/;
|
const regex = /src:\s*"([^"]+)"/;
|
||||||
const match = html.match(regex);
|
const match = html.match(regex);
|
||||||
if (match) {
|
if (match) {
|
||||||
@@ -1214,6 +1289,32 @@ async function mp4uploadExtractor(html, url = null) {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
/* --- oneupload --- */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @name oneuploadExtractor
|
||||||
|
* @author 50/50
|
||||||
|
*/
|
||||||
|
async function oneuploadExtractor(data, url = null) {
|
||||||
|
const match = data.match(/sources:\s*\[\{file:"([^"]+)"\}\]/);
|
||||||
|
const fileUrl = match ? match[1] : null;
|
||||||
|
return fileUrl;
|
||||||
|
}
|
||||||
|
/* --- packer --- */
|
||||||
|
|
||||||
|
/* {REQUIRED PLUGINS: unbaser} */
|
||||||
|
/**
|
||||||
|
* @name packerExtractor
|
||||||
|
* @author 50/50
|
||||||
|
*/
|
||||||
|
async function packerExtractor(data, url = null) {
|
||||||
|
const obfuscatedScript = data.match(/<script[^>]*>\s*(eval\(function\(p,a,c,k,e,d.*?\)[\s\S]*?)<\/script>/);
|
||||||
|
const unpackedScript = unpack(obfuscatedScript[1]);
|
||||||
|
const m3u8Match = unpackedScript.match(/"hls2"\s*:\s*"([^"]+)"/);
|
||||||
|
const m3u8Url = m3u8Match[1];
|
||||||
|
return m3u8Url;
|
||||||
|
}
|
||||||
|
|
||||||
/* --- sendvid --- */
|
/* --- sendvid --- */
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1249,6 +1350,29 @@ async function sibnetExtractor(html, embedUrl) {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
/* --- smoothpre --- */
|
||||||
|
|
||||||
|
/* {REQUIRED PLUGINS: unbaser} */
|
||||||
|
/**
|
||||||
|
* @name SmoothPre Extractor
|
||||||
|
* @author 50/50
|
||||||
|
*/
|
||||||
|
async function smoothpreExtractor(data, url = null) {
|
||||||
|
console.log("Using SmoothPre Extractor");
|
||||||
|
console.log("Data Length: " + data.length);
|
||||||
|
const obfuscatedScript = data.match(/<script[^>]*>\s*(eval\(function\(p,a,c,k,e,d.*?\)[\s\S]*?)<\/script>/);
|
||||||
|
if (!obfuscatedScript || !obfuscatedScript[1]) {
|
||||||
|
console.log("No obfuscated script found");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
const unpackedScript = unpack(obfuscatedScript[1]);
|
||||||
|
|
||||||
|
const hls2Match = unpackedScript.match(/"hls2"\s*:\s*"([^"]+)"/);
|
||||||
|
const hls2Url = hls2Match ? hls2Match[1] : null;
|
||||||
|
return hls2Url;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/* --- streamtape --- */
|
/* --- streamtape --- */
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1332,26 +1456,6 @@ async function streamupExtractor(data, url = null) {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/* --- supervideo --- */
|
|
||||||
|
|
||||||
/* {REQUIRED PLUGINS: unbaser} */
|
|
||||||
/**
|
|
||||||
* @name SuperVideo Extractor
|
|
||||||
* @author 50/50
|
|
||||||
*/
|
|
||||||
async function supervideoExtractor(data, url = null) {
|
|
||||||
const obfuscatedScript = data.match(/<script[^>]*>\s*(eval\(function\(p,a,c,k,e,d.*?\)[\s\S]*?)<\/script>/);
|
|
||||||
const unpackedScript = unpack(obfuscatedScript[1]);
|
|
||||||
const regex = /file:\s*"([^"]+\.m3u8)"/;
|
|
||||||
const match = regex.exec(unpackedScript);
|
|
||||||
if (match) {
|
|
||||||
const fileUrl = match[1];
|
|
||||||
console.log("File URL:" + fileUrl);
|
|
||||||
return fileUrl;
|
|
||||||
}
|
|
||||||
return "No stream found";
|
|
||||||
}
|
|
||||||
|
|
||||||
/* --- uploadcx --- */
|
/* --- uploadcx --- */
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1416,7 +1520,7 @@ async function vidmolyExtractor(html, url = null) {
|
|||||||
const streamUrl = iframeMatch[1].startsWith("//")
|
const streamUrl = iframeMatch[1].startsWith("//")
|
||||||
? "https:" + iframeMatch[1]
|
? "https:" + iframeMatch[1]
|
||||||
: iframeMatch[1];
|
: iframeMatch[1];
|
||||||
const responseTwo = await fetchv2(streamUrl);
|
const responseTwo = await soraFetch(streamUrl);
|
||||||
const htmlTwo = await responseTwo.text();
|
const htmlTwo = await responseTwo.text();
|
||||||
const m3u8Match = htmlTwo.match(/sources:\s*\[\{file:"([^"]+\.m3u8)"/);
|
const m3u8Match = htmlTwo.match(/sources:\s*\[\{file:"([^"]+\.m3u8)"/);
|
||||||
return m3u8Match ? m3u8Match[1] : null;
|
return m3u8Match ? m3u8Match[1] : null;
|
||||||
@@ -1542,6 +1646,7 @@ function voeShiftChars(str, shift) {
|
|||||||
.join("");
|
.join("");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
////////////////////////////////////////////////
|
////////////////////////////////////////////////
|
||||||
// PLUGINS //
|
// PLUGINS //
|
||||||
////////////////////////////////////////////////
|
////////////////////////////////////////////////
|
||||||
@@ -1559,17 +1664,25 @@ function voeShiftChars(str, shift) {
|
|||||||
* @returns {Promise<Response|null>} The response from the server, or null if the
|
* @returns {Promise<Response|null>} The response from the server, or null if the
|
||||||
* request failed.
|
* request failed.
|
||||||
*/
|
*/
|
||||||
async function soraFetch(url, options = { headers: {}, method: 'GET', body: null }) {
|
async function soraFetch(
|
||||||
|
url,
|
||||||
|
options = { headers: {}, method: "GET", body: null }
|
||||||
|
) {
|
||||||
|
try {
|
||||||
|
return await fetchv2(
|
||||||
|
url,
|
||||||
|
options.headers ?? {},
|
||||||
|
options.method ?? "GET",
|
||||||
|
options.body ?? null
|
||||||
|
);
|
||||||
|
} catch (e) {
|
||||||
try {
|
try {
|
||||||
return await fetchv2(url, options.headers ?? {}, options.method ?? 'GET', options.body ?? null);
|
return await fetch(url, options);
|
||||||
} catch(e) {
|
} catch (error) {
|
||||||
try {
|
await console.log("soraFetch error: " + error.message);
|
||||||
return await fetch(url, options);
|
return null;
|
||||||
} catch(error) {
|
|
||||||
await console.log('soraFetch error: ' + error.message);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
/***********************************************************
|
/***********************************************************
|
||||||
* UNPACKER MODULE
|
* UNPACKER MODULE
|
||||||
@@ -1673,7 +1786,6 @@ function unpack(source) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* {GE END} */
|
/* {GE END} */
|
||||||
|
|
||||||
|
|||||||
+2
-2
@@ -5,7 +5,7 @@
|
|||||||
"name": "Hamzo & Cufiy",
|
"name": "Hamzo & Cufiy",
|
||||||
"icon": "https://cdn.discordapp.com/avatars/623644371819954226/591ecab10b0b4535e859bb0b9bbe62e5?size=1024"
|
"icon": "https://cdn.discordapp.com/avatars/623644371819954226/591ecab10b0b4535e859bb0b9bbe62e5?size=1024"
|
||||||
},
|
},
|
||||||
"version": "0.3.16",
|
"version": "0.3.17",
|
||||||
"language": "German (DUB)",
|
"language": "German (DUB)",
|
||||||
"streamType": "HLS",
|
"streamType": "HLS",
|
||||||
"quality": "720p",
|
"quality": "720p",
|
||||||
@@ -17,4 +17,4 @@
|
|||||||
"type": "shows",
|
"type": "shows",
|
||||||
"supportsSora": true,
|
"supportsSora": true,
|
||||||
"supportsLuna": 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
|
// EDITING THIS FILE COULD BREAK THE UPDATER AND CAUSE ISSUES WITH THE EXTRACTOR
|
||||||
|
|
||||||
/* {GE START} */
|
/* {GE START} */
|
||||||
/* {VERSION: 1.1.8} */
|
/* {VERSION: 1.2.0} */
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @name global_extractor.js
|
* @name global_extractor.js
|
||||||
@@ -384,8 +384,8 @@ function base64Decode(str) {
|
|||||||
* @author Cufiy
|
* @author Cufiy
|
||||||
* @url https://github.com/JMcrafter26/sora-global-extractor
|
* @url https://github.com/JMcrafter26/sora-global-extractor
|
||||||
* @license CUSTOM LICENSE - see https://github.com/JMcrafter26/sora-global-extractor/blob/main/LICENSE
|
* @license CUSTOM LICENSE - see https://github.com/JMcrafter26/sora-global-extractor/blob/main/LICENSE
|
||||||
* @date 2025-11-05 15:44:57
|
* @date 2026-01-03 19:28:28
|
||||||
* @version 1.1.8
|
* @version 1.2.0
|
||||||
* @note This file was generated automatically.
|
* @note This file was generated automatically.
|
||||||
* The global extractor comes with an auto-updating feature, so you can always get the latest version. https://github.com/JMcrafter26/sora-global-extractor#-auto-updater
|
* The global extractor comes with an auto-updating feature, so you can always get the latest version. https://github.com/JMcrafter26/sora-global-extractor#-auto-updater
|
||||||
*/
|
*/
|
||||||
@@ -395,12 +395,20 @@ function globalExtractor(providers) {
|
|||||||
for (const [url, provider] of Object.entries(providers)) {
|
for (const [url, provider] of Object.entries(providers)) {
|
||||||
try {
|
try {
|
||||||
const streamUrl = extractStreamUrlByProvider(url, provider);
|
const streamUrl = extractStreamUrlByProvider(url, provider);
|
||||||
|
// check if streamUrl is an object with streamUrl property
|
||||||
|
if (streamUrl && typeof streamUrl === "object" && !Array.isArray(streamUrl) && streamUrl.streamUrl) {
|
||||||
|
return streamUrl.streamUrl;
|
||||||
|
}
|
||||||
// check if streamUrl is not null, a string, and starts with http or https
|
// check if streamUrl is not null, a string, and starts with http or https
|
||||||
if (streamUrl && typeof streamUrl === "string" && (streamUrl.startsWith("http"))) {
|
if (
|
||||||
|
streamUrl &&
|
||||||
|
typeof streamUrl === "string" &&
|
||||||
|
streamUrl.startsWith("http")
|
||||||
|
) {
|
||||||
return streamUrl;
|
return streamUrl;
|
||||||
// if its an array, get the value that starts with http
|
// if its an array, get the value that starts with http
|
||||||
} else if (Array.isArray(streamUrl)) {
|
} else if (Array.isArray(streamUrl)) {
|
||||||
const httpStream = streamUrl.find(url => url.startsWith("http"));
|
const httpStream = streamUrl.find((url) => url.startsWith("http"));
|
||||||
if (httpStream) {
|
if (httpStream) {
|
||||||
return httpStream;
|
return httpStream;
|
||||||
}
|
}
|
||||||
@@ -408,7 +416,6 @@ function globalExtractor(providers) {
|
|||||||
// check if it's a valid stream URL
|
// check if it's a valid stream URL
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
// Ignore the error and try the next provider
|
// Ignore the error and try the next provider
|
||||||
}
|
}
|
||||||
@@ -420,16 +427,30 @@ async function multiExtractor(providers) {
|
|||||||
/* this scheme should be returned as a JSON object
|
/* this scheme should be returned as a JSON object
|
||||||
{
|
{
|
||||||
"streams": [
|
"streams": [
|
||||||
"FileMoon",
|
{
|
||||||
"https://filemoon.example/stream1.m3u8",
|
"title": "FileMoon",
|
||||||
"StreamWish",
|
"streamUrl": "https://filemoon.example/stream1.m3u8",
|
||||||
"https://streamwish.example/stream2.m3u8",
|
},
|
||||||
"Okru",
|
{
|
||||||
"https://okru.example/stream3.m3u8",
|
"title": "StreamWish",
|
||||||
"MP4",
|
"streamUrl": "https://streamwish.example/stream2.m3u8",
|
||||||
"https://mp4upload.example/stream4.mp4",
|
},
|
||||||
"Default",
|
{
|
||||||
"https://default.example/stream5.m3u8"
|
"title": "Okru",
|
||||||
|
"streamUrl": "https://okru.example/stream3.m3u8",
|
||||||
|
"headers": { // Optional headers for the stream
|
||||||
|
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.3",
|
||||||
|
"Referer": "https://okru.example/",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "MP4",
|
||||||
|
"streamUrl": "https://mp4upload.example/stream4.mp4",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Default",
|
||||||
|
"streamUrl": "https://default.example/stream5.m3u8"
|
||||||
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
@@ -441,20 +462,21 @@ async function multiExtractor(providers) {
|
|||||||
// if provider starts with "direct-", then add the url to the streams array directly
|
// if provider starts with "direct-", then add the url to the streams array directly
|
||||||
if (provider.startsWith("direct-")) {
|
if (provider.startsWith("direct-")) {
|
||||||
const directName = provider.slice(7); // remove "direct-" prefix
|
const directName = provider.slice(7); // remove "direct-" prefix
|
||||||
if (directName && directName.length > 0) {
|
const title = (directName && directName.length > 0) ? directName : "Direct";
|
||||||
streams.push(directName, url);
|
streams.push({
|
||||||
} else {
|
title: title,
|
||||||
streams.push("Direct", url); // fallback to "Direct" if no name is provided
|
streamUrl: url
|
||||||
}
|
});
|
||||||
continue; // skip to the next provider
|
continue; // skip to the next provider
|
||||||
}
|
}
|
||||||
if (provider.startsWith("direct")) {
|
if (provider.startsWith("direct")) {
|
||||||
provider = provider.slice(7); // remove "direct-" prefix
|
provider = provider.slice(7); // remove "direct-" prefix
|
||||||
if (provider && provider.length > 0) {
|
const title = (provider && provider.length > 0) ? provider : "Direct";
|
||||||
streams.push(provider, url);
|
streams.push({
|
||||||
} else {
|
title: title,
|
||||||
streams.push("Direct", url); // fallback to "Direct" if no name is provided
|
streamUrl: url
|
||||||
}
|
});
|
||||||
|
continue; // skip to the next provider
|
||||||
}
|
}
|
||||||
|
|
||||||
let customName = null; // to store the custom name if provided
|
let customName = null; // to store the custom name if provided
|
||||||
@@ -471,15 +493,24 @@ async function multiExtractor(providers) {
|
|||||||
console.log(`Skipping ${provider} as it has already 3 streams`);
|
console.log(`Skipping ${provider} as it has already 3 streams`);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
let streamUrl = await extractStreamUrlByProvider(url, provider);
|
let result = await extractStreamUrlByProvider(url, provider);
|
||||||
|
let streamUrl = null;
|
||||||
if (streamUrl && Array.isArray(streamUrl)) {
|
let headers = null;
|
||||||
const httpStream = streamUrl.find(url => url.startsWith("http"));
|
|
||||||
|
// 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) {
|
if (httpStream) {
|
||||||
streamUrl = httpStream;
|
streamUrl = httpStream;
|
||||||
}
|
}
|
||||||
|
} else if (result && typeof result === "string") {
|
||||||
|
streamUrl = result;
|
||||||
}
|
}
|
||||||
// check if provider is already in streams, if it is, add a number to it
|
|
||||||
|
// check if streamUrl is valid
|
||||||
if (
|
if (
|
||||||
!streamUrl ||
|
!streamUrl ||
|
||||||
typeof streamUrl !== "string" ||
|
typeof streamUrl !== "string" ||
|
||||||
@@ -493,22 +524,29 @@ async function multiExtractor(providers) {
|
|||||||
provider = customName;
|
provider = customName;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let title;
|
||||||
if (providersCount[provider]) {
|
if (providersCount[provider]) {
|
||||||
providersCount[provider]++;
|
providersCount[provider]++;
|
||||||
streams.push(
|
title = provider.charAt(0).toUpperCase() +
|
||||||
provider.charAt(0).toUpperCase() +
|
|
||||||
provider.slice(1) +
|
provider.slice(1) +
|
||||||
"-" +
|
"-" +
|
||||||
(providersCount[provider] - 1), // add a number to the provider name
|
(providersCount[provider] - 1); // add a number to the provider name
|
||||||
streamUrl
|
|
||||||
);
|
|
||||||
} else {
|
} else {
|
||||||
providersCount[provider] = 1;
|
providersCount[provider] = 1;
|
||||||
streams.push(
|
title = provider.charAt(0).toUpperCase() + provider.slice(1);
|
||||||
provider.charAt(0).toUpperCase() + provider.slice(1),
|
|
||||||
streamUrl
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const streamObject = {
|
||||||
|
title: title,
|
||||||
|
streamUrl: streamUrl
|
||||||
|
};
|
||||||
|
|
||||||
|
// Add headers if they exist
|
||||||
|
if (headers && typeof headers === "object" && Object.keys(headers).length > 0) {
|
||||||
|
streamObject.headers = headers;
|
||||||
|
}
|
||||||
|
|
||||||
|
streams.push(streamObject);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
// Ignore the error and try the next provider
|
// Ignore the error and try the next provider
|
||||||
}
|
}
|
||||||
@@ -519,73 +557,98 @@ async function multiExtractor(providers) {
|
|||||||
async function extractStreamUrlByProvider(url, provider) {
|
async function extractStreamUrlByProvider(url, provider) {
|
||||||
if (eval(`typeof ${provider}Extractor`) !== "function") {
|
if (eval(`typeof ${provider}Extractor`) !== "function") {
|
||||||
// skip if the extractor is not defined
|
// skip if the extractor is not defined
|
||||||
console.log(`Extractor for provider ${provider} is not defined, skipping...`);
|
console.log(
|
||||||
|
`Extractor for provider ${provider} is not defined, skipping...`
|
||||||
|
);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
let uas = [
|
||||||
|
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.3",
|
||||||
|
"Mozilla/5.0 (iPhone; CPU iPhone OS 18_1_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/18.1.1 Mobile/15E148 Safari/604.1",
|
||||||
|
"Mozilla/5.0 (Linux; Android 10; SM-G973F) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Mobile Safari/537.36",
|
||||||
|
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.3",
|
||||||
|
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.1.2 Safari/605.1.15",
|
||||||
|
"Mozilla/5.0 (Linux; Android 11; Pixel 4 XL) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Mobile Safari/537.36",
|
||||||
|
];
|
||||||
let headers = {
|
let headers = {
|
||||||
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.3",
|
"User-Agent": uas[(url.length + provider.length) % uas.length], // use a different user agent based on the url and provider
|
||||||
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8",
|
"Accept":
|
||||||
|
"text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8",
|
||||||
"Accept-Language": "en-US,en;q=0.5",
|
"Accept-Language": "en-US,en;q=0.5",
|
||||||
"Referer": url,
|
"Referer": url,
|
||||||
"Connection": "keep-alive",
|
"Connection": "keep-alive",
|
||||||
"x-Requested-With": "XMLHttpRequest"
|
"x-Requested-With": "XMLHttpRequest",
|
||||||
};
|
};
|
||||||
if(provider == 'bigwarp') {
|
|
||||||
delete headers["User-Agent"];
|
switch (provider) {
|
||||||
headers["x-requested-with"] = "XMLHttpRequest";
|
case "bigwarp":
|
||||||
} else if (provider == 'vk') {
|
delete headers["User-Agent"];
|
||||||
headers["encoding"] = "windows-1251"; // required
|
break;
|
||||||
} else if (provider == 'sibnet') {
|
case "vk":
|
||||||
headers["encoding"] = "windows-1251"; // required
|
case "sibnet":
|
||||||
} else if (provider == 'supervideo') {
|
headers["encoding"] = "windows-1251"; // required
|
||||||
delete headers["User-Agent"];
|
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
|
// fetch the url
|
||||||
// and pass the response to the extractor function
|
// and pass the response to the extractor function
|
||||||
console.log("Fetching URL: " + url);
|
console.log("Fetching URL: " + url);
|
||||||
const response = await soraFetch(url, {
|
const response = await soraFetch(url, {
|
||||||
headers
|
headers,
|
||||||
});
|
});
|
||||||
|
|
||||||
console.log("Response: " + response.status);
|
console.log("Response: " + response.status);
|
||||||
let html = response.text ? await response.text() : response;
|
let html = response.text ? await response.text() : response;
|
||||||
// if title contains redirect, then get the redirect url
|
// if title contains redirect, then get the redirect url
|
||||||
const title = html.match(/<title>(.*?)<\/title>/);
|
const title = html.match(/<title>(.*?)<\/title>/);
|
||||||
if (title && title[1].toLowerCase().includes("redirect")) {
|
if (title && title[1].toLowerCase().includes("redirect")) {
|
||||||
const redirectUrl = html.match(/<meta http-equiv="refresh" content="0;url=(.*?)"/);
|
const matches = [
|
||||||
const redirectUrl2 = html.match(/window\.location\.href\s*=\s*["'](.*?)["']/);
|
/<meta http-equiv="refresh" content="0;url=(.*?)"/,
|
||||||
const redirectUrl3 = html.match(/window\.location\.replace\s*\(\s*["'](.*?)["']\s*\)/);
|
/window\.location\.href\s*=\s*["'](.*?)["']/,
|
||||||
if (redirectUrl) {
|
/window\.location\.replace\s*\(\s*["'](.*?)["']\s*\)/,
|
||||||
console.log("Redirect URL: " + redirectUrl[1]);
|
/window\.location\s*=\s*["'](.*?)["']/,
|
||||||
url = redirectUrl[1];
|
/window\.location\.assign\s*\(\s*["'](.*?)["']\s*\)/,
|
||||||
html = await soraFetch(url, {
|
/top\.location\s*=\s*["'](.*?)["']/,
|
||||||
headers
|
/top\.location\.replace\s*\(\s*["'](.*?)["']\s*\)/,
|
||||||
});
|
];
|
||||||
html = html.text ? await html.text() : html;
|
for (const match of matches) {
|
||||||
|
const redirectUrl = html.match(match);
|
||||||
} else if (redirectUrl2) {
|
if (redirectUrl && redirectUrl[1] && typeof redirectUrl[1] === "string" && redirectUrl[1].startsWith("http")) {
|
||||||
console.log("Redirect URL 2: " + redirectUrl2[1]);
|
console.log("Redirect URL found: " + redirectUrl[1]);
|
||||||
url = redirectUrl2[1];
|
url = redirectUrl[1];
|
||||||
html = await soraFetch(url, {
|
headers['Referer'] = url;
|
||||||
headers
|
headers['Host'] = url.match(/https?:\/\/([^\/]+)/)[1];
|
||||||
});
|
html = await soraFetch(url, {
|
||||||
html = html.text ? await html.text() : html;
|
headers,
|
||||||
} else if (redirectUrl3) {
|
}).then((res) => res.text());
|
||||||
console.log("Redirect URL 3: " + redirectUrl3[1]);
|
break;
|
||||||
url = redirectUrl3[1];
|
}
|
||||||
html = await soraFetch(url, {
|
|
||||||
headers
|
|
||||||
});
|
|
||||||
html = html.text ? await html.text() : html;
|
|
||||||
} else {
|
|
||||||
console.log("No redirect URL found");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// console.log("HTML: " + html);
|
// console.log("HTML: " + html);
|
||||||
switch (provider) {
|
switch (provider) {
|
||||||
case "bigwarp":
|
case "bigwarp":
|
||||||
try {
|
try {
|
||||||
return await bigwarpExtractor(html, url);
|
return await bigwarpExtractor(html, url);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@@ -634,6 +697,20 @@ async function extractStreamUrlByProvider(url, provider) {
|
|||||||
console.log("Error extracting stream URL from mp4upload:", error);
|
console.log("Error extracting stream URL from mp4upload:", error);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
case "oneupload":
|
||||||
|
try {
|
||||||
|
return await oneuploadExtractor(html, url);
|
||||||
|
} catch (error) {
|
||||||
|
console.log("Error extracting stream URL from oneupload:", error);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
case "packer":
|
||||||
|
try {
|
||||||
|
return await packerExtractor(html, url);
|
||||||
|
} catch (error) {
|
||||||
|
console.log("Error extracting stream URL from packer:", error);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
case "sendvid":
|
case "sendvid":
|
||||||
try {
|
try {
|
||||||
return await sendvidExtractor(html, url);
|
return await sendvidExtractor(html, url);
|
||||||
@@ -648,6 +725,13 @@ async function extractStreamUrlByProvider(url, provider) {
|
|||||||
console.log("Error extracting stream URL from sibnet:", error);
|
console.log("Error extracting stream URL from sibnet:", error);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
case "smoothpre":
|
||||||
|
try {
|
||||||
|
return await smoothpreExtractor(html, url);
|
||||||
|
} catch (error) {
|
||||||
|
console.log("Error extracting stream URL from smoothpre:", error);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
case "streamtape":
|
case "streamtape":
|
||||||
try {
|
try {
|
||||||
return await streamtapeExtractor(html, url);
|
return await streamtapeExtractor(html, url);
|
||||||
@@ -662,13 +746,6 @@ async function extractStreamUrlByProvider(url, provider) {
|
|||||||
console.log("Error extracting stream URL from streamup:", error);
|
console.log("Error extracting stream URL from streamup:", error);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
case "supervideo":
|
|
||||||
try {
|
|
||||||
return await supervideoExtractor(html, url);
|
|
||||||
} catch (error) {
|
|
||||||
console.log("Error extracting stream URL from supervideo:", error);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
case "uploadcx":
|
case "uploadcx":
|
||||||
try {
|
try {
|
||||||
return await uploadcxExtractor(html, url);
|
return await uploadcxExtractor(html, url);
|
||||||
@@ -717,7 +794,6 @@ async function extractStreamUrlByProvider(url, provider) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
////////////////////////////////////////////////
|
////////////////////////////////////////////////
|
||||||
// EXTRACTORS //
|
// EXTRACTORS //
|
||||||
////////////////////////////////////////////////
|
////////////////////////////////////////////////
|
||||||
@@ -1204,7 +1280,6 @@ async function megacloudExtractor(html, embedUrl) {
|
|||||||
* @author Cufiy
|
* @author Cufiy
|
||||||
*/
|
*/
|
||||||
async function mp4uploadExtractor(html, url = null) {
|
async function mp4uploadExtractor(html, url = null) {
|
||||||
// src: "https://a4.mp4upload.com:183/d/xkx3b4etz3b4quuo66rbmyqtjjoivahfxp27f35pti45rzapbvj5xwb4wuqtlpewdz4dirfp/video.mp4"
|
|
||||||
const regex = /src:\s*"([^"]+)"/;
|
const regex = /src:\s*"([^"]+)"/;
|
||||||
const match = html.match(regex);
|
const match = html.match(regex);
|
||||||
if (match) {
|
if (match) {
|
||||||
@@ -1214,6 +1289,32 @@ async function mp4uploadExtractor(html, url = null) {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
/* --- oneupload --- */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @name oneuploadExtractor
|
||||||
|
* @author 50/50
|
||||||
|
*/
|
||||||
|
async function oneuploadExtractor(data, url = null) {
|
||||||
|
const match = data.match(/sources:\s*\[\{file:"([^"]+)"\}\]/);
|
||||||
|
const fileUrl = match ? match[1] : null;
|
||||||
|
return fileUrl;
|
||||||
|
}
|
||||||
|
/* --- packer --- */
|
||||||
|
|
||||||
|
/* {REQUIRED PLUGINS: unbaser} */
|
||||||
|
/**
|
||||||
|
* @name packerExtractor
|
||||||
|
* @author 50/50
|
||||||
|
*/
|
||||||
|
async function packerExtractor(data, url = null) {
|
||||||
|
const obfuscatedScript = data.match(/<script[^>]*>\s*(eval\(function\(p,a,c,k,e,d.*?\)[\s\S]*?)<\/script>/);
|
||||||
|
const unpackedScript = unpack(obfuscatedScript[1]);
|
||||||
|
const m3u8Match = unpackedScript.match(/"hls2"\s*:\s*"([^"]+)"/);
|
||||||
|
const m3u8Url = m3u8Match[1];
|
||||||
|
return m3u8Url;
|
||||||
|
}
|
||||||
|
|
||||||
/* --- sendvid --- */
|
/* --- sendvid --- */
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1249,6 +1350,29 @@ async function sibnetExtractor(html, embedUrl) {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
/* --- smoothpre --- */
|
||||||
|
|
||||||
|
/* {REQUIRED PLUGINS: unbaser} */
|
||||||
|
/**
|
||||||
|
* @name SmoothPre Extractor
|
||||||
|
* @author 50/50
|
||||||
|
*/
|
||||||
|
async function smoothpreExtractor(data, url = null) {
|
||||||
|
console.log("Using SmoothPre Extractor");
|
||||||
|
console.log("Data Length: " + data.length);
|
||||||
|
const obfuscatedScript = data.match(/<script[^>]*>\s*(eval\(function\(p,a,c,k,e,d.*?\)[\s\S]*?)<\/script>/);
|
||||||
|
if (!obfuscatedScript || !obfuscatedScript[1]) {
|
||||||
|
console.log("No obfuscated script found");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
const unpackedScript = unpack(obfuscatedScript[1]);
|
||||||
|
|
||||||
|
const hls2Match = unpackedScript.match(/"hls2"\s*:\s*"([^"]+)"/);
|
||||||
|
const hls2Url = hls2Match ? hls2Match[1] : null;
|
||||||
|
return hls2Url;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/* --- streamtape --- */
|
/* --- streamtape --- */
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1332,26 +1456,6 @@ async function streamupExtractor(data, url = null) {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/* --- supervideo --- */
|
|
||||||
|
|
||||||
/* {REQUIRED PLUGINS: unbaser} */
|
|
||||||
/**
|
|
||||||
* @name SuperVideo Extractor
|
|
||||||
* @author 50/50
|
|
||||||
*/
|
|
||||||
async function supervideoExtractor(data, url = null) {
|
|
||||||
const obfuscatedScript = data.match(/<script[^>]*>\s*(eval\(function\(p,a,c,k,e,d.*?\)[\s\S]*?)<\/script>/);
|
|
||||||
const unpackedScript = unpack(obfuscatedScript[1]);
|
|
||||||
const regex = /file:\s*"([^"]+\.m3u8)"/;
|
|
||||||
const match = regex.exec(unpackedScript);
|
|
||||||
if (match) {
|
|
||||||
const fileUrl = match[1];
|
|
||||||
console.log("File URL:" + fileUrl);
|
|
||||||
return fileUrl;
|
|
||||||
}
|
|
||||||
return "No stream found";
|
|
||||||
}
|
|
||||||
|
|
||||||
/* --- uploadcx --- */
|
/* --- uploadcx --- */
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1416,7 +1520,7 @@ async function vidmolyExtractor(html, url = null) {
|
|||||||
const streamUrl = iframeMatch[1].startsWith("//")
|
const streamUrl = iframeMatch[1].startsWith("//")
|
||||||
? "https:" + iframeMatch[1]
|
? "https:" + iframeMatch[1]
|
||||||
: iframeMatch[1];
|
: iframeMatch[1];
|
||||||
const responseTwo = await fetchv2(streamUrl);
|
const responseTwo = await soraFetch(streamUrl);
|
||||||
const htmlTwo = await responseTwo.text();
|
const htmlTwo = await responseTwo.text();
|
||||||
const m3u8Match = htmlTwo.match(/sources:\s*\[\{file:"([^"]+\.m3u8)"/);
|
const m3u8Match = htmlTwo.match(/sources:\s*\[\{file:"([^"]+\.m3u8)"/);
|
||||||
return m3u8Match ? m3u8Match[1] : null;
|
return m3u8Match ? m3u8Match[1] : null;
|
||||||
@@ -1542,6 +1646,7 @@ function voeShiftChars(str, shift) {
|
|||||||
.join("");
|
.join("");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
////////////////////////////////////////////////
|
////////////////////////////////////////////////
|
||||||
// PLUGINS //
|
// PLUGINS //
|
||||||
////////////////////////////////////////////////
|
////////////////////////////////////////////////
|
||||||
@@ -1559,17 +1664,25 @@ function voeShiftChars(str, shift) {
|
|||||||
* @returns {Promise<Response|null>} The response from the server, or null if the
|
* @returns {Promise<Response|null>} The response from the server, or null if the
|
||||||
* request failed.
|
* request failed.
|
||||||
*/
|
*/
|
||||||
async function soraFetch(url, options = { headers: {}, method: 'GET', body: null }) {
|
async function soraFetch(
|
||||||
|
url,
|
||||||
|
options = { headers: {}, method: "GET", body: null }
|
||||||
|
) {
|
||||||
|
try {
|
||||||
|
return await fetchv2(
|
||||||
|
url,
|
||||||
|
options.headers ?? {},
|
||||||
|
options.method ?? "GET",
|
||||||
|
options.body ?? null
|
||||||
|
);
|
||||||
|
} catch (e) {
|
||||||
try {
|
try {
|
||||||
return await fetchv2(url, options.headers ?? {}, options.method ?? 'GET', options.body ?? null);
|
return await fetch(url, options);
|
||||||
} catch(e) {
|
} catch (error) {
|
||||||
try {
|
await console.log("soraFetch error: " + error.message);
|
||||||
return await fetch(url, options);
|
return null;
|
||||||
} catch(error) {
|
|
||||||
await console.log('soraFetch error: ' + error.message);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
/***********************************************************
|
/***********************************************************
|
||||||
* UNPACKER MODULE
|
* UNPACKER MODULE
|
||||||
@@ -1673,7 +1786,6 @@ function unpack(source) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* {GE END} */
|
/* {GE END} */
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user