removed all skipproxies, fixed hexa, and nexa

This commit is contained in:
2026-11-05 18:31:32 -08:00
parent 0454a1e0b0
commit 28d59dd8f0
4 changed files with 382 additions and 75 deletions

View File

@@ -8,7 +8,6 @@ export const SOURCES = [
jitter: 500,
retries: 2,
multiUrl: false,
skipProxy: true,
cdnHeaders: [{
pattern: /3eyedraven\.watch/i,
headers: {
@@ -27,7 +26,6 @@ export const SOURCES = [
jitter: 500,
retries: 2,
multiUrl: true,
skipProxy: true,
cdnHeaders: [{
pattern: /4movie\.in|aapanel\.devcorp\.me/i,
headers: {
@@ -46,7 +44,6 @@ export const SOURCES = [
jitter: 500,
retries: 2,
multiUrl: false,
skipProxy: true,
cdnHeaders: [{
pattern: /hlsx\d+cdn\.|burntburst\d+\.store|echovideo\.ru/i,
headers: {
@@ -65,7 +62,6 @@ export const SOURCES = [
jitter: 500,
retries: 2,
multiUrl: true,
skipProxy: true,
cdnHeaders: [{
pattern: /111477\.xyz/i,
headers: {
@@ -84,7 +80,6 @@ export const SOURCES = [
jitter: 500,
retries: 2,
multiUrl: true,
skipProxy: true,
cdnHeaders: [{
pattern: /raccoonunbutteredbagel\.zip|1x2\.space|kaxate\.fun|mechanicalserenity\.xyz|reelvault\.click|visionaryfounderslab\.site|goldenkite\.top|aether\.ist/i,
headers: {
@@ -103,7 +98,6 @@ export const SOURCES = [
jitter: 500,
retries: 2,
multiUrl: true,
skipProxy: true,
cdnHeaders: [{
pattern: /braflix\.dev/i,
headers: {
@@ -122,7 +116,6 @@ export const SOURCES = [
jitter: 500,
retries: 2,
multiUrl: true,
skipProxy: true,
},
{
@@ -134,7 +127,6 @@ export const SOURCES = [
jitter: 500,
retries: 2,
multiUrl: true,
skipProxy: true,
cdnHeaders: [{
pattern: /cinema\.army/i,
headers: {
@@ -153,7 +145,6 @@ export const SOURCES = [
jitter: 500,
retries: 2,
multiUrl: true,
skipProxy: true,
cdnHeaders: [{
pattern: /cinesrc\.st/i,
headers: {
@@ -172,7 +163,6 @@ export const SOURCES = [
jitter: 300,
retries: 1,
multiUrl: true,
skipProxy: true,
cdnHeaders: [{
pattern: /cno\.to/i,
headers: {
@@ -191,7 +181,6 @@ export const SOURCES = [
jitter: 500,
retries: 2,
multiUrl: true,
skipProxy: true,
cdnHeaders: [{
pattern: /themoviebox\.uk|vidrock\.|orbitingdreams\.xyz|fastcloud\.live|deton444eik\.com/i,
headers: {
@@ -210,7 +199,6 @@ export const SOURCES = [
jitter: 500,
retries: 2,
multiUrl: true,
skipProxy: true,
cdnHeaders: [{
pattern: /fmfau\.com/i,
headers: {
@@ -229,7 +217,6 @@ export const SOURCES = [
jitter: 500,
retries: 2,
multiUrl: true,
skipProxy: true,
cdnHeaders: [{
pattern: /framextv\.cc|streamos\.top|workers\.dev|films365\.org/i,
headers: {
@@ -248,7 +235,6 @@ export const SOURCES = [
jitter: 500,
retries: 2,
multiUrl: true,
skipProxy: true,
},
{
@@ -260,7 +246,6 @@ export const SOURCES = [
retries: 1,
jitter: 0,
multiUrl: true,
skipProxy: true,
},
{
@@ -272,7 +257,6 @@ export const SOURCES = [
jitter: 500,
retries: 2,
multiUrl: true,
skipProxy: true,
},
{
@@ -284,7 +268,6 @@ export const SOURCES = [
jitter: 500,
retries: 2,
multiUrl: true,
skipProxy: true,
cdnHeaders: [{
pattern: /hexa\.su|flixer\.su/i,
headers: {
@@ -303,7 +286,6 @@ export const SOURCES = [
jitter: 500,
retries: 2,
multiUrl: true,
skipProxy: true,
cdnHeaders: [{
pattern: /krussdomi\.com/i,
headers: {
@@ -375,7 +357,6 @@ export const SOURCES = [
jitter: 500,
retries: 2,
multiUrl: true,
skipProxy: true,
cdnHeaders: [{
pattern: /meowtv\.ru/i,
headers: {
@@ -394,7 +375,6 @@ export const SOURCES = [
jitter: 500,
retries: 2,
multiUrl: true,
skipProxy: true,
cdnHeaders: [{
pattern: /movenerds\.space/i,
headers: {
@@ -413,7 +393,6 @@ export const SOURCES = [
jitter: 500,
retries: 2,
multiUrl: true,
skipProxy: true,
cdnHeaders: [{
pattern: /wecollege\.net|movy\.sx|goldenkite\.top/i,
headers: {
@@ -434,6 +413,24 @@ export const SOURCES = [
multiUrl: true,
},
{
key: 'nexa',
label: 'Nexa',
sourceFile: 'nexa',
proxyParam: 'nx',
timeout: 30000,
jitter: 500,
retries: 2,
multiUrl: true,
cdnHeaders: [{
pattern: /nexa\.mov|providers\.nexa\.mov/i,
headers: {
Referer: 'https://nexa.mov/',
Origin: 'https://nexa.mov',
},
}],
},
{
key: 'pengu',
label: 'Pengu',
@@ -443,7 +440,6 @@ export const SOURCES = [
jitter: 500,
retries: 1,
multiUrl: true,
skipProxy: true,
disabled: false,
},
@@ -507,7 +503,6 @@ export const SOURCES = [
jitter: 500,
retries: 2,
multiUrl: true,
skipProxy: true,
cdnHeaders: [{
pattern: /streamaggregator\.in/i,
headers: {
@@ -526,7 +521,6 @@ export const SOURCES = [
jitter: 500,
retries: 2,
multiUrl: true,
skipProxy: true,
cdnHeaders: [{
pattern: /thugtv\.online|fcxmb\.com|hscow\.com/i,
headers: {
@@ -545,7 +539,6 @@ export const SOURCES = [
jitter: 500,
retries: 1,
multiUrl: true,
skipProxy: true,
cdnHeaders: [{
pattern: /torrentio\.to|fcxmb\.com/i,
headers: {
@@ -575,7 +568,6 @@ export const SOURCES = [
jitter: 500,
retries: 2,
multiUrl: true,
skipProxy: true,
cdnHeaders: [{
pattern: /rousav\.tech|vidbolt\.xyz/i,
headers: {
@@ -594,7 +586,6 @@ export const SOURCES = [
jitter: 500,
retries: 1,
multiUrl: true,
skipProxy: true,
cdnHeaders: [{
pattern: /vidcore\.io/i,
headers: {
@@ -635,7 +626,6 @@ export const SOURCES = [
jitter: 500,
retries: 2,
multiUrl: true,
skipProxy: true,
cdnHeaders: [{
pattern: /vidoraplayer\.duckdns\.org/i,
headers: {
@@ -676,7 +666,6 @@ export const SOURCES = [
jitter: 500,
retries: 2,
multiUrl: true,
skipProxy: true,
cdnHeaders: [{
pattern: /wecollege\.net|vidy\.st/i,
headers: {
@@ -712,7 +701,6 @@ export const SOURCES = [
jitter: 0,
retries: 2,
multiUrl: false,
skipProxy: true,
verifyHeaders: {
Accept: 'application/json, text/javascript, /; q=0.01',
'Accept-Language': 'en-US,en;q=0.9',
@@ -730,7 +718,6 @@ export const SOURCES = [
jitter: 500,
retries: 2,
multiUrl: true,
skipProxy: true,
},
{
@@ -742,7 +729,6 @@ export const SOURCES = [
jitter: 500,
retries: 2,
multiUrl: true,
skipProxy: true,
cdnHeaders: [{
pattern: /hscow\.com|downloadwella\.com|willowmovies\.com/i,
headers: {
@@ -761,7 +747,6 @@ export const SOURCES = [
jitter: 500,
retries: 2,
multiUrl: true,
skipProxy: true,
cdnHeaders: [{
pattern: /films365\.org/i,
headers: {
@@ -780,7 +765,6 @@ export const SOURCES = [
jitter: 500,
retries: 2,
multiUrl: true,
skipProxy: true,
cdnHeaders: [{
pattern: /yapgrid\.com|embedwave\.cc/i,
headers: {

View File

@@ -1,61 +1,205 @@
import { fetchJson, fetchText, getRandomHex } from '../utils/helpers.js';
import crypto from 'node:crypto';
const API_BASE = 'https://enc-dec.app/api';
const BASE_URL = 'https://hexa.su';
const TMDB_PROXY_BASE = 'https://theemoviedb.hexa.su';
const BASE_URL = 'https://theemoviedb.hexa.su';
const CAP_URL = 'https://cap.hexa.su/15d2cf0395';
const DEC_API = 'https://enc-dec.app/api';
const UA = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/137.0.0.0 Safari/537.36';
const CHALLENGE_HEADERS = {
'Content-Type': 'application/json',
Accept: 'application/json',
'Accept-Language': 'en-US,en;q=0.9,ru;q=0.8,fr;q=0.7,bg;q=0.6,pl;q=0.5,id;q=0.4,zh-CN;q=0.3,zh;q=0.2,ja;q=0.1,es;q=0.1',
Origin: 'https://hexa.su',
Referer: 'https://hexa.su/',
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/145.0.0.0 Safari/537.36 OPR/129.0.0.0 (Edition std-2)',
};
const PLAYBACK_HEADERS = {
'User-Agent': UA,
Referer: 'https://hexa.su/',
Origin: 'https://hexa.su',
};
let cachedCapToken = null;
let tokenUseCount = 0;
async function fetchCapToken() {
if (cachedCapToken && tokenUseCount < 18) {
tokenUseCount++;
return cachedCapToken;
}
try {
const chalRes = await fetch(`${CAP_URL}/challenge`, {
method: 'POST',
headers: CHALLENGE_HEADERS,
signal: AbortSignal.timeout(8000),
});
if (!chalRes.ok) return null;
const challengeData = await chalRes.json();
const encRes = await fetch(`${DEC_API}/enc-hexa`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'User-Agent': UA,
},
body: JSON.stringify({ challenge_data: challengeData }),
signal: AbortSignal.timeout(8000),
});
if (!encRes.ok) return null;
const encJson = await encRes.json();
if (encJson?.status !== 200 || !encJson.result) return null;
const redeemRes = await fetch(`${CAP_URL}/redeem`, {
method: 'POST',
headers: CHALLENGE_HEADERS,
body: JSON.stringify(encJson.result),
signal: AbortSignal.timeout(8000),
});
if (!redeemRes.ok) return null;
const redeemJson = await redeemRes.json();
if (redeemJson?.token) {
cachedCapToken = redeemJson.token;
tokenUseCount = 1;
return cachedCapToken;
}
} catch {
cachedCapToken = null;
}
return null;
}
function extractStreams(result) {
const list = [];
if (!result) return list;
const data = typeof result === 'string' && (result.startsWith('{') || result.startsWith('['))
? JSON.parse(result)
: result;
if (typeof data === 'string' && /^https?:\/\//.test(data)) {
list.push({
url: data,
server: 'Hexa - Direct',
quality: 'Auto',
type: data.includes('.m3u8') ? 'hls' : (data.includes('.mpd') ? 'dash' : 'mp4'),
headers: PLAYBACK_HEADERS,
});
return list;
}
const rawSources = Array.isArray(data)
? data
: Array.isArray(data?.sources)
? data.sources
: Array.isArray(data?.streams)
? data.streams
: [];
for (const item of rawSources) {
const streamUrl = item?.url || item?.file || item?.src || (typeof item === 'string' ? item : null);
if (!streamUrl || typeof streamUrl !== 'string' || !streamUrl.startsWith('http')) continue;
let quality = item.quality || item.label || 'Auto';
if (/^\d+$/i.test(quality)) quality = `${quality}p`;
list.push({
url: streamUrl,
server: `Hexa - ${item.server || item.name || 'Direct'}`,
quality,
type: item.type || (streamUrl.includes('.m3u8') ? 'hls' : (streamUrl.includes('.mpd') ? 'dash' : 'mp4')),
headers: PLAYBACK_HEADERS,
});
}
if (data?.url && typeof data.url === 'string' && data.url.startsWith('http')) {
list.push({
url: data.url,
server: `Hexa - ${data.server || 'Direct'}`,
quality: data.quality || 'Auto',
type: data.type || (data.url.includes('.m3u8') ? 'hls' : (data.url.includes('.mpd') ? 'dash' : 'mp4')),
headers: PLAYBACK_HEADERS,
});
}
return list;
}
export async function getStream(args) {
const { id, s, e } = args || {};
const idNum = parseInt(id, 10);
if (!idNum) return null;
const isTv = s != null && e != null;
const key = getRandomHex(32);
const path = isTv
? `/api/tmdb/tv/${idNum}/season/${s}/episode/${e}/images`
: `/api/tmdb/movie/${idNum}/images`;
const capToken = await fetchCapToken();
if (!capToken) return null;
const key = crypto.randomBytes(32).toString('hex');
const headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/137.0.0.0 Safari/537.36',
Referer: `${BASE_URL}/`,
'User-Agent': UA,
Referer: 'https://hexa.su/',
Origin: 'https://hexa.su',
Accept: 'text/plain',
'X-Fingerprint-Lite': 'e9136c41504646444',
'X-Cap-Token': capToken,
'X-Api-Key': key,
};
const encHexaRes = await fetchJson(`${API_BASE}/enc-hexa`);
if (!encHexaRes || encHexaRes.status !== 200) return null;
headers['X-Cap-Token'] = encHexaRes.result.token;
try {
const res = await fetch(`${BASE_URL}${path}`, {
headers,
signal: AbortSignal.timeout(12000),
});
const url = isTv
? `${TMDB_PROXY_BASE}/api/tmdb/tv/${id}/season/${s}/episode/${e}/images`
: `${TMDB_PROXY_BASE}/api/tmdb/movie/${id}/images`;
if (!res.ok) {
cachedCapToken = null;
return null;
}
const encrypted = await fetchText(url, { headers });
if (!encrypted) return null;
const encryptedText = (await res.text()).trim();
if (!encryptedText) return null;
const decRes = await fetchJson(`${API_BASE}/dec-hexa`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ text: encrypted, key }),
});
const decRes = await fetch(`${DEC_API}/dec-hexa`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'User-Agent': UA,
},
body: JSON.stringify({ text: encryptedText, key }),
signal: AbortSignal.timeout(10000),
});
if (!decRes || decRes.status !== 200 || !decRes.result) return null;
if (!decRes.ok) return null;
const decJson = await decRes.json();
if (decJson?.status !== 200 || !decJson.result) return null;
const data = decRes.result;
const streamUrl = data.url || data.file || (Array.isArray(data.sources) ? data.sources[0]?.url : null);
if (!streamUrl) return null;
const streams = extractStreams(decJson.result);
if (!streams.length) return null;
const streamHeaders = {
Referer: `${BASE_URL}/`,
Origin: BASE_URL,
};
const allUrls = [];
const seen = new Set();
const allUrls = [{
url: streamUrl,
server: 'Hexa',
quality: 'Auto',
type: streamUrl.includes('.m3u8') ? 'hls' : 'mp4',
headers: streamHeaders,
}];
for (const item of streams) {
if (!item?.url || seen.has(item.url)) continue;
seen.add(item.url);
allUrls.push(item);
}
return {
url: streamUrl,
headers: streamHeaders,
allUrls,
};
if (!allUrls.length) return null;
return {
url: allUrls[0].url,
headers: allUrls[0].headers,
allUrls,
};
} catch {
return null;
}
}

179
src/sources/nexa.js Normal file
View File

@@ -0,0 +1,179 @@
const BASE_URL = 'https://providers.nexa.mov';
const DEC_API = 'https://enc-dec.app/api';
const UA = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/137.0.0.0 Safari/537.36';
const HEADERS = {
'User-Agent': UA,
Referer: 'https://nexa.mov/',
Origin: 'https://nexa.mov',
};
const PLAYBACK_HEADERS = {
'User-Agent': UA,
Referer: 'https://nexa.mov/',
Origin: 'https://nexa.mov',
};
const ANIME_SERVERS = new Set(['kyoto', 'tokyo', 'hokkaido', 'fuji']);
function extractUrls(result, serverName) {
const urls = [];
if (!result) return urls;
const data = typeof result === 'string' && (result.startsWith('{') || result.startsWith('['))
? JSON.parse(result)
: result;
if (typeof data === 'string' && /^https?:\/\//.test(data)) {
urls.push({
url: data,
server: `Nexa - ${serverName}`,
quality: 'Auto',
type: data.includes('.m3u8') ? 'hls' : (data.includes('.mpd') ? 'dash' : 'mp4'),
headers: PLAYBACK_HEADERS,
});
return urls;
}
const list = Array.isArray(data)
? data
: Array.isArray(data?.sources)
? data.sources
: Array.isArray(data?.streams)
? data.streams
: Array.isArray(data?.playlist)
? data.playlist
: [];
for (const item of list) {
const streamUrl = item?.url || item?.file || item?.src || (typeof item === 'string' ? item : null);
if (!streamUrl || typeof streamUrl !== 'string' || !streamUrl.startsWith('http')) continue;
let quality = item.quality || item.label || 'Auto';
if (/^\d+$/i.test(quality)) quality = `${quality}p`;
urls.push({
url: streamUrl,
server: `Nexa - ${item.server || item.name || serverName}`,
quality,
type: item.type || (streamUrl.includes('.m3u8') ? 'hls' : (streamUrl.includes('.mpd') ? 'dash' : 'mp4')),
headers: PLAYBACK_HEADERS,
});
}
if (data?.url && typeof data.url === 'string' && data.url.startsWith('http')) {
urls.push({
url: data.url,
server: `Nexa - ${data.server || serverName}`,
quality: data.quality || 'Auto',
type: data.type || (data.url.includes('.m3u8') ? 'hls' : (data.url.includes('.mpd') ? 'dash' : 'mp4')),
headers: PLAYBACK_HEADERS,
});
}
return urls;
}
async function getAvailableServers() {
try {
const res = await fetch(`${BASE_URL}/v1/status`, {
headers: HEADERS,
signal: AbortSignal.timeout(6000),
});
if (!res.ok) return ['charlotte', 'albuquerque', 'denver'];
const json = await res.json();
const sources = Array.isArray(json?.sources) ? json.sources : [];
const active = sources
.map(s => s?.codename)
.filter(name => name && !ANIME_SERVERS.has(name));
return active.length > 0 ? active : ['charlotte', 'albuquerque', 'denver'];
} catch {
return ['charlotte', 'albuquerque', 'denver'];
}
}
async function resolveServer(server, targetUrl) {
try {
const encRes = await fetch(`${DEC_API}/enc-nexa?url=${encodeURIComponent(targetUrl)}`, {
headers: { 'User-Agent': UA },
signal: AbortSignal.timeout(8000),
});
if (!encRes.ok) return null;
const encJson = await encRes.json();
if (encJson?.status !== 200 || !encJson.result) return null;
const payloadBuffer = Buffer.from(encJson.result, 'base64');
const playlistRes = await fetch(`${BASE_URL}/v1/playlist`, {
method: 'POST',
headers: {
...HEADERS,
'Content-Type': 'application/octet-stream',
},
body: payloadBuffer,
signal: AbortSignal.timeout(10000),
});
if (!playlistRes.ok) return null;
const playlistBytes = await playlistRes.arrayBuffer();
const hexData = Buffer.from(playlistBytes).toString('hex');
if (!hexData) return null;
const decRes = await fetch(`${DEC_API}/dec-nexa`, {
method: 'POST',
headers: {
'User-Agent': UA,
'Content-Type': 'application/json',
},
body: JSON.stringify({ text: hexData, server }),
signal: AbortSignal.timeout(8000),
});
if (!decRes.ok) return null;
const decJson = await decRes.json();
if (decJson?.status !== 200 || !decJson.result) return null;
return { server, result: decJson.result };
} catch {
return null;
}
}
export async function getStream(args) {
const { id, s, e } = args || {};
if (!id) return null;
const isTv = s != null && e != null;
const servers = await getAvailableServers();
const targetServers = servers.slice(0, 3);
const responses = await Promise.allSettled(
targetServers.map(server => {
const url = isTv
? `https://providers.nexa.mov/?platform=tmdb&id=${id}&server=${server}&season=${s}&episode=${e}`
: `https://providers.nexa.mov/?platform=tmdb&id=${id}&server=${server}`;
return resolveServer(server, url);
})
);
const allUrls = [];
const seen = new Set();
for (const res of responses) {
if (res.status !== 'fulfilled' || !res.value?.result) continue;
const { server, result } = res.value;
const found = extractUrls(result, server);
for (const item of found) {
if (!item?.url || seen.has(item.url)) continue;
seen.add(item.url);
allUrls.push(item);
}
}
if (!allUrls.length) return null;
return {
url: allUrls[0].url,
headers: allUrls[0].headers,
allUrls,
};
}

View File

@@ -13,7 +13,7 @@ const CONFIG = {
TV_ID: "37854", // TV TMDB ID to test, for anime I recommend using a random ID like 37854
TV_SEASON: "1", // TV Season to test
TV_EPISODE: "1", // TV Episode to test
STREAM_SOURCE: "", // Source key to test
STREAM_SOURCE: "hexa", // Source key to test
TEST_SUBTITLE: false,
TEST_DOWNLOAD: false,