mirror of
https://gitlab.com/vyla-entertainment/sdk.git
synced 2026-11-09 19:57:23 -05:00
188 lines
7.0 KiB
JavaScript
188 lines
7.0 KiB
JavaScript
import { USER_AGENT } from '../utils/helpers.js';
|
|
|
|
const BASE_URL = 'https://vidvault.to';
|
|
const API_BASE = `${BASE_URL}/api`;
|
|
const DL_PROXY_BASE = 'https://dl.gemlelispe.workers.dev';
|
|
const SUB_PROXY_BASE = 'https://sub.k5s7sjozpn.workers.dev';
|
|
|
|
const HEADERS = {
|
|
'Referer': `${BASE_URL}/`,
|
|
'Origin': BASE_URL,
|
|
'User-Agent': USER_AGENT,
|
|
'Accept': 'application/json, text/plain, */*',
|
|
'Accept-Language': 'en-US,en;q=0.9',
|
|
};
|
|
|
|
function formatBytes(bytes, decimals = 2) {
|
|
if (!bytes || bytes <= 0) return '0 B';
|
|
const k = 1024;
|
|
const sizes = ['B', 'KB', 'MB', 'GB', 'TB'];
|
|
const i = Math.min(Math.floor(Math.log(bytes) / Math.log(k)), sizes.length - 1);
|
|
return `${(bytes / Math.pow(k, i)).toFixed(decimals)} ${sizes[i]}`;
|
|
}
|
|
|
|
async function getToken() {
|
|
const res = await fetch(`${API_BASE}/get-token`, {
|
|
method: 'GET',
|
|
headers: HEADERS,
|
|
signal: AbortSignal.timeout(10000),
|
|
});
|
|
if (!res.ok) throw new Error(`Token fetch failed: ${res.status}`);
|
|
const data = await res.json();
|
|
return data?.t;
|
|
}
|
|
|
|
function getCaptionType(url) {
|
|
return String(url).toLowerCase().includes('.vtt') ? 'vtt' : 'srt';
|
|
}
|
|
|
|
export async function getStream(args) {
|
|
const { id, s, e } = args;
|
|
const isTv = s != null && e != null;
|
|
const type = isTv ? 'tv' : 'movie';
|
|
|
|
try {
|
|
const token = await getToken();
|
|
if (!token) return null;
|
|
|
|
const payload = {
|
|
type,
|
|
tmdbId: String(id),
|
|
};
|
|
|
|
if (isTv) {
|
|
payload.season = Number(s);
|
|
payload.episode = Number(e);
|
|
}
|
|
|
|
const res = await fetch(`${API_BASE}/download-proxy`, {
|
|
method: 'POST',
|
|
headers: {
|
|
...HEADERS,
|
|
'Content-Type': 'application/json',
|
|
'x-request-token': token,
|
|
},
|
|
body: JSON.stringify(payload),
|
|
signal: AbortSignal.timeout(15000),
|
|
});
|
|
|
|
if (!res.ok) return null;
|
|
const data = await res.json();
|
|
|
|
const allUrls = [];
|
|
const subtitles = [];
|
|
const titleTag = encodeURIComponent(isTv ? `Show_${id}_S${s}E${e}` : `Movie_${id}`);
|
|
|
|
const mp4 = data?.mp4Data;
|
|
const downloadData = mp4?.downloadInfo?.data || mp4?.data?.data || mp4?.data || mp4;
|
|
|
|
if (downloadData) {
|
|
const streams = downloadData.streams || downloadData.downloads || [];
|
|
for (const item of streams) {
|
|
if (!item?.url) continue;
|
|
|
|
const res = item.resolutions || item.resolution || 'Auto';
|
|
const sizeStr = item.size ? ` (${typeof item.size === 'number' ? formatBytes(item.size) : item.size})` : '';
|
|
const proxiedUrl = `${DL_PROXY_BASE}/${encodeURIComponent(item.url)}?n=${titleTag}`;
|
|
|
|
allUrls.push({
|
|
url: proxiedUrl,
|
|
server: `VidVault - MP4 ${res}p${sizeStr}`.trim(),
|
|
quality: typeof res === 'number' ? `${res}p` : res,
|
|
type: 'mp4',
|
|
headers: HEADERS,
|
|
});
|
|
}
|
|
|
|
const captions = downloadData.captions || [];
|
|
for (const cap of captions) {
|
|
if (!cap?.url) continue;
|
|
const subUrl = `${SUB_PROXY_BASE}/?url=${encodeURIComponent(cap.url)}&title=${titleTag}`;
|
|
subtitles.push({
|
|
label: cap.lanName || cap.lan || 'Unknown',
|
|
type: getCaptionType(cap.url),
|
|
language: cap.lan || '',
|
|
url: subUrl,
|
|
});
|
|
}
|
|
}
|
|
|
|
const mkvFiles = data?.mkvData?.files || [];
|
|
for (const file of mkvFiles) {
|
|
if (!file?.url) continue;
|
|
const sizeStr = file.size ? ` (${typeof file.size === 'number' ? formatBytes(file.size) : file.size})` : '';
|
|
allUrls.push({
|
|
url: file.url,
|
|
server: `VidVault - MKV 480p${sizeStr}`.trim(),
|
|
quality: '480p',
|
|
type: 'mp4',
|
|
headers: HEADERS,
|
|
});
|
|
}
|
|
|
|
const mkvV2 = data?.mkvV2Data;
|
|
const mkvV2List = Array.isArray(mkvV2)
|
|
? mkvV2
|
|
: Array.isArray(mkvV2?.files)
|
|
? mkvV2.files
|
|
: (mkvV2?.url ? [mkvV2] : []);
|
|
|
|
for (const file of mkvV2List) {
|
|
if (!file?.url) continue;
|
|
const quality = file.quality ? String(file.quality).replace(/p$/i, '') : 'Auto';
|
|
const sizeStr = file.size ? ` (${typeof file.size === 'number' ? formatBytes(file.size) : file.size})` : '';
|
|
const meta = [file.country, file.language].filter(Boolean).join(' • ');
|
|
const metaStr = meta ? ` [${meta}]` : '';
|
|
|
|
allUrls.push({
|
|
url: file.url,
|
|
server: `VidVault - MKV v2 ${quality}p${sizeStr}${metaStr}`.trim(),
|
|
quality: `${quality}p`,
|
|
type: 'mp4',
|
|
headers: HEADERS,
|
|
});
|
|
}
|
|
|
|
const mkvV3 = data?.mkvV3Data;
|
|
if (mkvV3?.downloads && Array.isArray(mkvV3.downloads)) {
|
|
const country = mkvV3.country || '';
|
|
const language = mkvV3.language || '';
|
|
|
|
for (const down of mkvV3.downloads) {
|
|
if (Array.isArray(down?.qualities)) {
|
|
for (const qObj of down.qualities) {
|
|
for (const ep of qObj?.episodes || []) {
|
|
if (!ep?.url) continue;
|
|
const quality = qObj.quality ? String(qObj.quality).replace(/p$/i, '') : 'Auto';
|
|
const sizeStr = ep.size ? ` (${typeof ep.size === 'number' ? formatBytes(ep.size) : ep.size})` : '';
|
|
const meta = [ep.country || country, ep.language || language].filter(Boolean).join(' • ');
|
|
const metaStr = meta ? ` [${meta}]` : '';
|
|
|
|
allUrls.push({
|
|
url: ep.url,
|
|
server: `VidVault - MKV v3 ${quality}p${sizeStr}${metaStr}`.trim(),
|
|
quality: `${quality}p`,
|
|
type: 'mp4',
|
|
headers: HEADERS,
|
|
});
|
|
}
|
|
}
|
|
} else if (down?.url) {
|
|
const quality = down.quality ? String(down.quality).replace(/p$/i, '') : 'Auto';
|
|
const sizeStr = down.size ? ` (${typeof down.size === 'number' ? formatBytes(down.size) : down.size})` : '';
|
|
allUrls.push({
|
|
url: down.url,
|
|
server: `VidVault - MKV v3 ${quality}p${sizeStr}`.trim(),
|
|
quality: `${quality}p`,
|
|
type: 'mp4',
|
|
headers: HEADERS,
|
|
});
|
|
}
|
|
}
|
|
}
|
|
|
|
return allUrls.length ? { allUrls, subtitles: subtitles.length ? subtitles : undefined } : null;
|
|
} catch (err) {
|
|
return null;
|
|
}
|
|
} |