added braflix and zenox

This commit is contained in:
2026-11-01 00:26:29 -07:00
parent 8717fc8da3
commit b5aee7dae4
3 changed files with 226 additions and 0 deletions

View File

@@ -94,6 +94,25 @@ export const SOURCES = [
}],
},
{
key: 'braflix',
label: 'Braflix',
sourceFile: 'braflix',
proxyParam: 'bf',
timeout: 25000,
jitter: 500,
retries: 2,
multiUrl: true,
skipProxy: true,
cdnHeaders: [{
pattern: /braflix\.dev/i,
headers: {
Referer: 'https://braflix.dev/',
Origin: 'https://braflix.dev',
},
}],
},
{
key: 'castle',
label: 'Castle',
@@ -725,6 +744,25 @@ export const SOURCES = [
multiUrl: true,
},
{
key: 'zenox',
label: 'Zenox',
sourceFile: 'zenox',
proxyParam: 'zx',
timeout: 25000,
jitter: 500,
retries: 2,
multiUrl: true,
skipProxy: true,
cdnHeaders: [{
pattern: /zenox\.lol|hls-m3u8\.lol/i,
headers: {
Referer: 'https://zenox.lol/',
Origin: 'https://zenox.lol',
},
}],
},
];
export const HEALTH_PROBE_ID = '155';

80
src/sources/braflix.js Normal file
View File

@@ -0,0 +1,80 @@
const BASE_URL = 'https://braflix.dev';
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 = {
Origin: BASE_URL,
Referer: `${BASE_URL}/`,
'User-Agent': UA,
Accept: 'application/json, text/plain, */*',
};
const PLAYBACK_HEADERS = {
Origin: BASE_URL,
Referer: `${BASE_URL}/`,
};
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 targetSeason = isTv ? (s || 1) : 1;
const targetEpisode = isTv ? (e || 1) : 1;
const path = isTv
? `/api/play/tv/${idNum}/${targetSeason}/${targetEpisode}`
: `/api/play/movie/${idNum}`;
try {
const res = await fetch(`${BASE_URL}${path}`, {
headers: HEADERS,
signal: AbortSignal.timeout(10000),
});
if (!res.ok) return null;
const data = await res.json();
if (data?.status !== 'ready' || !data.result?.file?.stream) {
return null;
}
const rawStreamPath = data.result.file.stream;
const fullStreamUrl = rawStreamPath.startsWith('http')
? rawStreamPath
: `${BASE_URL}${rawStreamPath}`;
const qualityRaw = String(data.result.source?.quality || '1080,720,480');
const qualityParts = qualityRaw.split(',').map(q => q.trim()).filter(Boolean);
const maxQ = qualityParts.reduce((acc, curr) => {
const num = parseInt(curr, 10);
return num > acc ? num : acc;
}, 0);
const quality = maxQ > 0 ? `${maxQ}p` : 'Auto';
const qualitySummary = qualityParts.length > 0
? qualityParts.map(q => `${q}p`).join('/')
: 'DASH';
const streamType = fullStreamUrl.includes('.mpd')
? 'dash'
: fullStreamUrl.includes('.m3u8')
? 'hls'
: 'dash';
const allUrls = [{
url: fullStreamUrl,
server: `Braflix - DASH (${qualitySummary})`,
quality,
type: streamType,
headers: PLAYBACK_HEADERS,
}];
return {
url: fullStreamUrl,
headers: PLAYBACK_HEADERS,
allUrls,
};
} catch {
return null;
}
}

108
src/sources/zenox.js Normal file
View File

@@ -0,0 +1,108 @@
const BASE_URL = 'https://zenox.lol';
const API_URL = `${BASE_URL}/api/v1/playback/session`;
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 = {
'Content-Type': 'application/json',
Origin: BASE_URL,
Referer: `${BASE_URL}/`,
'User-Agent': UA,
};
const PLAYBACK_HEADERS = {
Origin: BASE_URL,
Referer: `${BASE_URL}/`,
};
async function fetchServerStream(payload, srv) {
try {
const res = await fetch(API_URL, {
method: 'POST',
headers: HEADERS,
body: JSON.stringify({
...payload,
serverIndex: srv.index,
serverName: srv.name,
}),
signal: AbortSignal.timeout(6000),
});
if (!res.ok) return null;
const data = await res.json();
if (
!data?.success ||
!data?.streamUrl ||
data.streamUrl.includes('/primary/') ||
data.serverName?.toLowerCase() === 'primary'
) {
return null;
}
const streamUrl = data.streamUrl.startsWith('http')
? data.streamUrl
: `${BASE_URL}${data.streamUrl}`;
return {
url: streamUrl,
server: `Zenox - ${data.serverName || srv.name}`,
quality: 'Auto',
type: streamUrl.includes('.m3u8') ? 'hls' : (streamUrl.includes('.mpd') ? 'dash' : 'mp4'),
headers: PLAYBACK_HEADERS,
};
} catch {
return null;
}
}
export async function getStream(args) {
const { id, s, e, title, year, imdbId } = args || {};
const idNum = parseInt(id, 10);
if (!idNum) return null;
const isTv = s != null && e != null;
const payload = {
mediaId: idNum,
mediaType: isTv ? 'tv' : 'movie',
season: isTv ? (parseInt(s, 10) || 1) : 1,
episode: isTv ? (parseInt(e, 10) || 1) : 1,
title: title || '',
year: String(year || ''),
imdbId: imdbId || '',
autoUseCommunity: false,
isManualSwitch: true,
};
const activeServers = [
{ name: 'Vbot', index: 1 },
{ name: 'Temporary', index: 1 },
];
const results = await Promise.allSettled(
activeServers.map(srv => fetchServerStream(payload, srv))
);
const allUrls = [];
const seen = new Set();
for (const r of results) {
if (r.status === 'fulfilled' && r.value?.url && !seen.has(r.value.url)) {
seen.add(r.value.url);
allUrls.push(r.value);
}
}
if (!allUrls.length) {
const delta = await fetchServerStream(payload, { name: 'Delta', index: 0 });
if (delta?.url && !seen.has(delta.url)) {
seen.add(delta.url);
allUrls.push(delta);
}
}
if (!allUrls.length) return null;
return {
url: allUrls[0].url,
headers: allUrls[0].headers,
allUrls,
};
}