mirror of
https://gitlab.com/vyla-entertainment/TV.git
synced 2026-11-09 17:47:20 -05:00
115 lines
3.7 KiB
JavaScript
115 lines
3.7 KiB
JavaScript
import { fetchJson, USER_AGENT } from '../utils/helpers.js';
|
|
|
|
const API_BASE = 'https://streamdata.vaplayer.ru/api.php';
|
|
const EMBED_ORIGIN = 'https://nextgencloudfabric.com';
|
|
|
|
export const VERIFY_HEADERS = {
|
|
'User-Agent': USER_AGENT,
|
|
'Referer': `${EMBED_ORIGIN}/`,
|
|
'Origin': EMBED_ORIGIN
|
|
};
|
|
|
|
function extractStreamUrls(obj, urls = new Set()) {
|
|
if (!obj) return urls;
|
|
if (typeof obj === 'string') {
|
|
if (obj.startsWith('http') && (obj.includes('.m3u8') || obj.includes('.mp4') || obj.includes('.mpd'))) {
|
|
urls.add(obj);
|
|
}
|
|
} else if (Array.isArray(obj)) {
|
|
for (const item of obj) extractStreamUrls(item, urls);
|
|
} else if (typeof obj === 'object') {
|
|
for (const val of Object.values(obj)) {
|
|
extractStreamUrls(val, urls);
|
|
}
|
|
}
|
|
return urls;
|
|
}
|
|
|
|
export async function getStream({ id, s, e }) {
|
|
try {
|
|
const isTv = s != null && e != null;
|
|
const type = isTv ? 'tv' : 'movie';
|
|
|
|
const url = `${API_BASE}?tmdb=${id}&type=${type}${isTv ? `&season=${s}&episode=${e}` : ''}`;
|
|
|
|
const data = await fetchJson(url, {
|
|
headers: {
|
|
'Accept': '*/*',
|
|
'Origin': EMBED_ORIGIN,
|
|
'Referer': `${EMBED_ORIGIN}/`,
|
|
'User-Agent': USER_AGENT
|
|
},
|
|
signal: AbortSignal.timeout(10000)
|
|
});
|
|
|
|
const streamUrls = Array.from(extractStreamUrls(data));
|
|
if (!streamUrls.length) return null;
|
|
|
|
let subs = [];
|
|
if (data.subtitles && Array.isArray(data.subtitles)) {
|
|
subs = data.subtitles;
|
|
} else if (data.captions && Array.isArray(data.captions)) {
|
|
subs = data.captions;
|
|
}
|
|
|
|
const subtitles = subs.map(sub => ({
|
|
url: sub.url || sub.file || sub.src,
|
|
lang: sub.display || sub.label || sub.language || sub.lang || 'Unknown'
|
|
})).filter(s => s.url);
|
|
|
|
const allUrls = streamUrls.map(streamUrl => {
|
|
let type = 'mp4';
|
|
if (streamUrl.includes('.mpd')) type = 'dash';
|
|
else if (streamUrl.includes('.m3u8')) type = 'hls';
|
|
|
|
return {
|
|
url: streamUrl,
|
|
server: 'VidAPI',
|
|
quality: 'Auto',
|
|
type,
|
|
headers: VERIFY_HEADERS,
|
|
subtitles: subtitles.length > 0 ? subtitles : undefined,
|
|
skipProxy: false
|
|
};
|
|
});
|
|
|
|
return allUrls.length ? { allUrls } : null;
|
|
} catch {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
export async function getSources() {
|
|
return ['VidAPI'];
|
|
}
|
|
|
|
export async function proxyStream({ url, res, proxyUtils: { fetchUpstream, rewriteM3u8 } }) {
|
|
try {
|
|
const upstream = await fetchUpstream(url, 0, VERIFY_HEADERS);
|
|
if (!upstream) return res.end();
|
|
|
|
const ct = (upstream.headers?.['content-type'] || '').toLowerCase();
|
|
const isM3u8 = ct.includes('mpegurl') || ct.includes('m3u8') || /\.m3u8?(\?|$)/i.test(url);
|
|
|
|
if (isM3u8) {
|
|
const chunks = [];
|
|
for await (const c of upstream) chunks.push(c);
|
|
let body = Buffer.concat(chunks).toString('utf8');
|
|
|
|
res.setHeader('Content-Type', 'application/vnd.apple.mpegurl');
|
|
res.setHeader('Access-Control-Allow-Origin', '*');
|
|
|
|
return res.end(rewriteM3u8(body, url, '&va=1&tt=1'));
|
|
}
|
|
|
|
res.setHeader('Content-Type', ct || 'application/octet-stream');
|
|
res.setHeader('Access-Control-Allow-Origin', '*');
|
|
res.setHeader('Cache-Control', 'public, max-age=3600');
|
|
upstream.pipe(res);
|
|
} catch {
|
|
if (!res.headersSent) {
|
|
res.statusCode = 502;
|
|
res.end();
|
|
}
|
|
}
|
|
} |