@vyla-entertainment/sdk 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. package/LICENSE +407 -0
  2. package/README.md +44 -0
  3. package/package.json +31 -0
  4. package/src/config.js +519 -0
  5. package/src/downloads/downloads.js +8 -0
  6. package/src/downloads/sources/trendimovies.js +63 -0
  7. package/src/health.js +38 -0
  8. package/src/sdk.js +65 -0
  9. package/src/sources/123anime.js +57 -0
  10. package/src/sources/1embed.js +92 -0
  11. package/src/sources/anihq.js +112 -0
  12. package/src/sources/anineko.js +81 -0
  13. package/src/sources/anipm.js +70 -0
  14. package/src/sources/flaxmovies.js +14 -0
  15. package/src/sources/fsharetv.js +44 -0
  16. package/src/sources/fsonic.js +47 -0
  17. package/src/sources/fsonline.js +46 -0
  18. package/src/sources/goated.js +262 -0
  19. package/src/sources/hexa.js +82 -0
  20. package/src/sources/kisskh.js +60 -0
  21. package/src/sources/lookmovie.js +78 -0
  22. package/src/sources/mapple.js +115 -0
  23. package/src/sources/meowtv.js +111 -0
  24. package/src/sources/nebula.js +62 -0
  25. package/src/sources/nekowatch.js +57 -0
  26. package/src/sources/opstream.js +123 -0
  27. package/src/sources/peachify.js +78 -0
  28. package/src/sources/pengu.js +45 -0
  29. package/src/sources/purstream.js +104 -0
  30. package/src/sources/vidapi.js +115 -0
  31. package/src/sources/vidcore.js +43 -0
  32. package/src/sources/videasy.js +98 -0
  33. package/src/sources/vidfast.js +53 -0
  34. package/src/sources/vidlink.js +28 -0
  35. package/src/sources/vidnest.js +77 -0
  36. package/src/sources/vidrift.js +95 -0
  37. package/src/sources/vidrock.js +145 -0
  38. package/src/sources/vidsync.js +38 -0
  39. package/src/sources/vidup.js +79 -0
  40. package/src/sources/vidzee.js +120 -0
  41. package/src/sources/vixsrc.js +98 -0
  42. package/src/sources/xpass.js +40 -0
  43. package/src/subtitles.js +127 -0
  44. package/src/utils/helpers.js +194 -0
@@ -0,0 +1,38 @@
1
+ import { fetchJson, fetchText, USER_AGENT, getTmdbInfo } from '../utils/helpers.js';
2
+
3
+ const API_BASE = "https://enc-dec.app/api";
4
+ const DOMAIN = "https://vidsync.xyz";
5
+ const HEADERS = { "Accept": "*/*", "Origin": DOMAIN, "Referer": `${DOMAIN}/`, "User-Agent": USER_AGENT, "X-Requested-With": "XMLHttpRequest" };
6
+ const SERVERS = ["cinevault", "cinedub", "cinebox", "cineflix", "cinevip", "cinecloud", "cine4k"];
7
+
8
+ export async function getStream({ id, s, e, server }) {
9
+ try {
10
+ const isTv = s != null && e != null;
11
+ const type = isTv ? 'tv' : 'movie';
12
+ const info = await getTmdbInfo(id, type);
13
+ if (!info?.titles?.length) return null;
14
+ const turnstileJson = await fetchJson(`${API_BASE}/enc-vidsync`, { signal: AbortSignal.timeout(5000) });
15
+ if (turnstileJson.status !== 200 || !turnstileJson.result?.token) return null;
16
+ const turnstileToken = turnstileJson.result.token;
17
+ let targets = SERVERS;
18
+ if (server && server !== 'all') {
19
+ const clean = server.replace('VidSync - ', '');
20
+ targets = SERVERS.includes(clean) ? [clean] : SERVERS;
21
+ }
22
+ const encTitle = encodeURIComponent(info.titles[0]).replace(/%20/g, '+');
23
+ const results = await Promise.allSettled(targets.map(async srv => {
24
+ let url = `${DOMAIN}/api/stream/fetch?title=${encTitle}&type=${type}&releaseYear=${info.year}&mediaId=${id}&serverName=${srv}`;
25
+ if (isTv) url += `&season=${s}&episode=${e}`;
26
+ const encryptedText = await fetchText(url, { headers: { ...HEADERS, "X-Cf-Turnstile": turnstileToken }, signal: AbortSignal.timeout(10000) });
27
+ const decJson = await fetchJson(`${API_BASE}/dec-vidsync`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ text: encryptedText, id: String(id) }), signal: AbortSignal.timeout(10000) });
28
+ if (decJson.status !== 200 || !decJson.result) throw new Error();
29
+ const streams = Array.isArray(decJson.result) ? decJson.result : [decJson.result];
30
+ return streams.map(st => ({ url: st.url || st.file, server: `VidSync - ${srv}`, quality: st.quality || "Auto", type: (st.url || st.file || "").includes(".m3u8") ? "hls" : "mp4", headers: { ...HEADERS, "Origin": DOMAIN }, skipProxy: false, skipVerify: true, skipHlsCheck: true }));
31
+ }));
32
+ const allUrls = [];
33
+ for (const r of results) if (r.status === 'fulfilled') allUrls.push(...r.value);
34
+ return allUrls.length ? { allUrls } : null;
35
+ } catch { return null; }
36
+ }
37
+
38
+ export async function getSources() { return SERVERS.map(s => `VidSync - ${s}`); }
@@ -0,0 +1,79 @@
1
+ import { fetchJson, fetchText, USER_AGENT } from '../utils/helpers.js';
2
+
3
+ const API_BASE = 'https://enc-dec.app/api';
4
+ const DOMAIN = 'https://vidup.to';
5
+ const HEADERS = { 'User-Agent': USER_AGENT, 'Referer': `${DOMAIN}/`, 'X-Requested-With': 'XMLHttpRequest' };
6
+
7
+ async function getVidupMeta(id, s, e) {
8
+ try {
9
+ const embedUrl = s != null && e != null ? `${DOMAIN}/tv/${id}/${s}/${e}/` : `${DOMAIN}/movie/${id}/`;
10
+ const html = await fetchText(embedUrl, { headers: { 'User-Agent': USER_AGENT } });
11
+
12
+ const match = html.match(/\\"(?:en|token)\\":\\"(.*?)\\"/) || html.match(/"(?:en|token)":"(.*?)"/);
13
+ if (!match?.[1]) return null;
14
+
15
+ const encData = await fetchJson(`${API_BASE}/enc-vidup?text=${encodeURIComponent(match[1])}`);
16
+ if (encData.status !== 200 || !encData.result) return null;
17
+
18
+ const { servers: serversUrl, stream: streamUrl, token } = encData.result;
19
+ const reqHeaders = { ...HEADERS, 'X-CSRF-Token': token };
20
+
21
+ const serversEncrypted = await fetchText(serversUrl, { method: 'POST', headers: reqHeaders });
22
+ const decServersData = await fetchJson(`${API_BASE}/dec-vidup`, {
23
+ method: 'POST',
24
+ headers: { 'Content-Type': 'application/json' },
25
+ body: JSON.stringify({ text: serversEncrypted })
26
+ });
27
+
28
+ if (decServersData.status !== 200 || !decServersData.result) return null;
29
+ return { servers: decServersData.result, streamUrl, reqHeaders };
30
+ } catch { return null; }
31
+ }
32
+
33
+ export async function getStream({ id, s, e, server }) {
34
+ const meta = await getVidupMeta(id, s, e);
35
+ if (!meta?.servers?.length) return null;
36
+
37
+ const { servers, streamUrl, reqHeaders } = meta;
38
+ let targets = servers;
39
+
40
+ if (server && server !== 'all') {
41
+ const cleanName = server.replace('VidUp - ', '');
42
+ targets = servers.filter(srv => srv.name === cleanName);
43
+ if (!targets.length) targets = servers;
44
+ }
45
+
46
+ const results = await Promise.allSettled(targets.map(async srv => {
47
+ const streamEncrypted = await fetchText(`${streamUrl}/${srv.data}`, { method: 'POST', headers: reqHeaders });
48
+ const decStreamData = await fetchJson(`${API_BASE}/dec-vidup`, {
49
+ method: 'POST',
50
+ headers: { 'Content-Type': 'application/json' },
51
+ body: JSON.stringify({ text: streamEncrypted })
52
+ });
53
+
54
+ if (decStreamData.status !== 200 || !decStreamData.result?.url) throw new Error();
55
+
56
+ return {
57
+ url: decStreamData.result.url,
58
+ server: `VidUp - ${srv.name || 'Server'}`,
59
+ type: decStreamData.result.url.includes('.m3u8') ? 'hls' : 'mp4',
60
+ subtitles: (decStreamData.result.captions || []).map(c => ({
61
+ url: c.file,
62
+ lang: c.label || 'Unknown'
63
+ })),
64
+ headers: { ...HEADERS, 'Origin': DOMAIN }
65
+ };
66
+ }));
67
+
68
+ const allUrls = [];
69
+ for (const r of results) {
70
+ if (r.status === 'fulfilled') allUrls.push(r.value);
71
+ }
72
+
73
+ return allUrls.length ? { allUrls } : null;
74
+ }
75
+
76
+ export async function getSources(args) {
77
+ const meta = await getVidupMeta(args.id, args.s, args.e);
78
+ return meta?.servers ? meta.servers.map(srv => `VidUp - ${srv.name}`) : [];
79
+ }
@@ -0,0 +1,120 @@
1
+ import { webcrypto } from 'node:crypto';
2
+ import { fetchJson, USER_AGENT } from '../utils/helpers.js';
3
+
4
+ const PLAYER_URL = 'https://player.vidzee.wtf';
5
+ const CORE_URL = 'https://core.vidzee.wtf';
6
+
7
+ let cachedKey = null, cachedKeyTs = 0;
8
+
9
+ async function getDecKey(headers) {
10
+ const now = Date.now();
11
+ if (cachedKey && now - cachedKeyTs < 300000) return cachedKey;
12
+
13
+ const res = await fetch(`${CORE_URL}/api-key`, {
14
+ headers,
15
+ signal: AbortSignal.timeout(7000)
16
+ });
17
+ if (!res.ok) throw new Error(`API key fetch failed: ${res.status}`);
18
+
19
+ const e = await res.text();
20
+ const t = Buffer.from(e.replace(/\s+/g, ''), 'base64');
21
+
22
+ const s = new Uint8Array(t.length - 28 + 16);
23
+ s.set(t.subarray(28), 0);
24
+ s.set(t.subarray(12, 28), t.length - 28);
25
+
26
+ const keyMat = await webcrypto.subtle.digest('SHA-256', new TextEncoder().encode('c4a8f1d7e2b9a6c3d0f5e8a1b7c4d9e2'));
27
+ const key = await webcrypto.subtle.importKey('raw', keyMat, { name: 'AES-GCM' }, false, ['decrypt']);
28
+
29
+ try {
30
+ const decrypted = await webcrypto.subtle.decrypt({
31
+ name: 'AES-GCM',
32
+ iv: t.subarray(0, 12),
33
+ tagLength: 128
34
+ }, key, s);
35
+
36
+ cachedKey = new TextDecoder().decode(decrypted);
37
+ cachedKeyTs = now;
38
+ return cachedKey;
39
+ } catch (err) {
40
+ throw new Error('Failed to decrypt Vidzee master key');
41
+ }
42
+ }
43
+
44
+ function decrypt(enc, key) {
45
+ try {
46
+ const dec = Buffer.from(enc, 'base64').toString('utf8');
47
+ const sep = dec.indexOf(':');
48
+ if (sep === -1) return '';
49
+
50
+ const ivBase64 = dec.slice(0, sep);
51
+ const ciphertext = dec.slice(sep + 1);
52
+ const keyPadded = key.padEnd(32, '\0');
53
+
54
+ const decrypted = CryptoJS.AES.decrypt(
55
+ ciphertext,
56
+ CryptoJS.enc.Utf8.parse(keyPadded),
57
+ {
58
+ iv: CryptoJS.enc.Base64.parse(ivBase64),
59
+ mode: CryptoJS.mode.CBC,
60
+ padding: CryptoJS.pad.Pkcs7
61
+ }
62
+ );
63
+
64
+ return decrypted.toString(CryptoJS.enc.Utf8) || '';
65
+ } catch (err) {
66
+ return '';
67
+ }
68
+ }
69
+
70
+ export async function getStream({ id, s, e, clientIP }) {
71
+ const headers = {
72
+ 'User-Agent': USER_AGENT,
73
+ 'Accept': 'application/json',
74
+ 'Referer': PLAYER_URL,
75
+ 'Origin': PLAYER_URL,
76
+ ...(clientIP && { 'X-Forwarded-For': clientIP })
77
+ };
78
+
79
+ try {
80
+ const decKey = await getDecKey(headers);
81
+
82
+ const serversToTry = [3, 0, 1, 2, 4, 5, 6, 8];
83
+
84
+ for (const sr of serversToTry) {
85
+ try {
86
+ const url = `${PLAYER_URL}/api/server?id=${id}&sr=${sr}${s ? `&ss=${s}&ep=${e || 1}` : ''}`;
87
+ const data = await fetchJson(url, { headers, signal: AbortSignal.timeout(5000) });
88
+
89
+ if (data?.url?.length) {
90
+ for (const entry of data.url) {
91
+ if (!entry.link) continue;
92
+
93
+ const decrypted = decrypt(entry.link, decKey);
94
+ if (decrypted?.startsWith('http')) {
95
+
96
+ return {
97
+ allUrls: [
98
+ {
99
+ url: decrypted,
100
+ skipHlsCheck: true,
101
+ headers: {
102
+ 'User-Agent': USER_AGENT,
103
+ 'Referer': PLAYER_URL,
104
+ 'Origin': PLAYER_URL
105
+ }
106
+ }
107
+ ]
108
+ };
109
+ }
110
+ }
111
+ }
112
+ } catch (err) {
113
+ continue;
114
+ }
115
+ }
116
+ return null;
117
+ } catch (err) {
118
+ return null;
119
+ }
120
+ }
@@ -0,0 +1,98 @@
1
+ import { fetchText, USER_AGENT } from '../utils/helpers.js';
2
+
3
+ const BASE_URL = 'https://vixsrc.to';
4
+
5
+ const HEADERS = {
6
+ 'User-Agent': USER_AGENT,
7
+ 'Accept': 'application/json, text/javascript, */*; q=0.01',
8
+ 'Accept-Language': 'en-US,en;q=0.9',
9
+ 'Referer': `${BASE_URL}/`,
10
+ 'Origin': BASE_URL
11
+ };
12
+
13
+ function extract(regex, text) {
14
+ const m = text?.match(regex);
15
+ if (!m) return null;
16
+ return m[1].replace(/\\/g, '');
17
+ }
18
+
19
+ export async function getStream({ id, s, e, absoluteBase }) {
20
+ try {
21
+ const apiUrl = s && e
22
+ ? `${BASE_URL}/api/tv/${id}/${s}/${e}`
23
+ : `${BASE_URL}/api/movie/${id}`;
24
+
25
+ const apiProxy = absoluteBase
26
+ ? `${absoluteBase.replace(/^https:\/\/(localhost|127\.0\.0\.1)/, 'http://$1')}/api?url=${encodeURIComponent(apiUrl)}&proxyHeaders=${encodeURIComponent(JSON.stringify(HEADERS))}`
27
+ : apiUrl;
28
+
29
+ const apiText = await fetchText(apiProxy).catch(() => null);
30
+ const apiData = JSON.parse(apiText || '{}');
31
+ if (!apiData.src) return null;
32
+
33
+ const embedUrl = apiData.src.startsWith('http') ? apiData.src : `${BASE_URL}${apiData.src}`;
34
+ const embedProxy = absoluteBase
35
+ ? `${absoluteBase.replace(/^https:\/\/(localhost|127\.0\.0\.1)/, 'http://$1')}/api?url=${encodeURIComponent(embedUrl)}&proxyHeaders=${encodeURIComponent(JSON.stringify(HEADERS))}`
36
+ : embedUrl;
37
+
38
+ const html = await fetchText(embedProxy).catch(() => null);
39
+ if (!html) return null;
40
+
41
+ const token = extract(/token["']\s*:\s*["']([^"']+)/, html);
42
+ const expires = extract(/expires["']\s*:\s*["']([^"']+)/, html);
43
+ let playlist = extract(/url["']\s*:\s*["']([^"']+)/, html);
44
+ if (!token || !expires || !playlist) return null;
45
+
46
+ playlist = playlist.replace(/\\/g, '');
47
+ const lang = extract(/lang(?:uage)?["']\s*:\s*["']([a-z]{2,5})/i, html) || 'en';
48
+ const rawMasterUrl = `${playlist}${playlist.includes('?') ? '&' : '?'}token=${token}&expires=${expires}&h=1&lang=${lang}`;
49
+
50
+ const masterProxy = absoluteBase
51
+ ? `${absoluteBase.replace(/^https:\/\/(localhost|127\.0\.0\.1)/, 'http://$1')}/api?url=${encodeURIComponent(rawMasterUrl)}&proxyHeaders=${encodeURIComponent(JSON.stringify(HEADERS))}`
52
+ : rawMasterUrl;
53
+
54
+ const playlistText = await fetchText(rawMasterUrl, { headers: HEADERS }).catch(() => null);
55
+ if (!playlistText?.includes('#EXTM3U')) return null;
56
+
57
+ let finalUrl = null;
58
+ let bestHeight = 0;
59
+ const lines = playlistText.split('\n');
60
+
61
+ for (let i = 0; i < lines.length; i++) {
62
+ const line = lines[i];
63
+ if (!line.startsWith('#EXT-X-STREAM-INF')) continue;
64
+ const height = parseInt(line.match(/RESOLUTION=\d+x(\d+)/)?.[1] || '0', 10);
65
+ let next = lines[i + 1]?.trim();
66
+ if (!next || next.startsWith('#')) continue;
67
+
68
+ if (height > bestHeight) {
69
+ bestHeight = height;
70
+ finalUrl = next;
71
+ }
72
+ }
73
+
74
+ if (!finalUrl) finalUrl = rawMasterUrl;
75
+
76
+ if (!finalUrl.startsWith('http')) {
77
+ const base = rawMasterUrl.split('/playlist/')[0];
78
+ finalUrl = finalUrl.startsWith('/') ? `${base}${finalUrl}` : `${base}/${finalUrl}`;
79
+ }
80
+
81
+ const alreadyProxied = /^https?:\/\/(localhost|127\.0\.0\.1)(:\d+)?\/api\?url=/.test(finalUrl);
82
+
83
+ return {
84
+ allUrls: [
85
+ {
86
+ url: finalUrl,
87
+ skipProxy: alreadyProxied,
88
+ headers: {
89
+ ...HEADERS,
90
+ 'Referer': `${BASE_URL}/`
91
+ }
92
+ }
93
+ ]
94
+ };
95
+ } catch (err) {
96
+ return null;
97
+ }
98
+ }
@@ -0,0 +1,40 @@
1
+ import { fetchJson, fetchText, USER_AGENT } from '../utils/helpers.js';
2
+
3
+ const BASE = "https://play.xpass.top";
4
+ const HEADERS = { "accept": "*/*", "accept-language": "en-US,en;q=0.9", "cache-control": "no-cache", "pragma": "no-cache", "User-Agent": USER_AGENT, "Origin": BASE, "Referrer": `${BASE}/`, "Cookie": "auth_token=de21073d24bca9b50f189b402ac870734cf945f2085cb7e1a4fc453fcfe4f57e" };
5
+
6
+ export async function getStream({ id, s, e, server }) {
7
+ const isTv = s && e && s !== 'null' && e !== 'null';
8
+ const hdrs = { ...HEADERS, referer: `${BASE}/e/${isTv ? 'tv' : 'movie'}/${id}?autostart=true` };
9
+ try {
10
+ let sources = [];
11
+ if (!isTv) {
12
+ const text = await fetchText(`${BASE}/e/movie/${id}?autostart=true`, { headers: hdrs, signal: AbortSignal.timeout(7000) });
13
+ const match = text.match(/var backups=(\[[\s\S]*?\])/);
14
+ if (match) sources = JSON.parse(match[1]);
15
+ } else {
16
+ sources = await fetchJson(`${BASE}/data/tv/${id}/${s}/${e}?autostart=true&force=true`, { headers: hdrs, signal: AbortSignal.timeout(7000) });
17
+ }
18
+ if (!sources?.length) return null;
19
+ if (server && server !== 'all') {
20
+ const exact = sources.find(src => src.name === server.replace('XPass - ', ''));
21
+ if (exact) sources = [exact]; else return null;
22
+ }
23
+ const allUrls = [];
24
+ for (const source of sources) {
25
+ if (!source.url) continue;
26
+ try {
27
+ const mdata = await fetchJson(`${BASE}${source.url}`, { headers: hdrs, signal: AbortSignal.timeout(5000) });
28
+ if (!mdata?.playlist?.[0]?.sources) continue;
29
+ const targetStream = mdata.playlist[0].sources.find(st => st.type === 'hls') || mdata.playlist[0].sources[0];
30
+ if (targetStream?.file) allUrls.push({ server: `XPass - ${source.name}`, type: targetStream.type === 'hls' ? 'hls' : 'mp4', url: targetStream.file, headers: { "Origin": HEADERS.Origin, "Referer": HEADERS.Referrer, "User-Agent": HEADERS["User-Agent"] } });
31
+ } catch { }
32
+ }
33
+ return allUrls.length ? { allUrls } : null;
34
+ } catch { return null; }
35
+ }
36
+
37
+ export async function getSources(args) {
38
+ const stream = await getStream(args);
39
+ return stream?.allUrls ? stream.allUrls.map(u => u.server) : [];
40
+ }
@@ -0,0 +1,127 @@
1
+ import { getUA } from './utils/helpers.js';
2
+
3
+ const SUBTITLE_BASES = [
4
+ 'https://sub.vdrk.site/v1',
5
+ 'https://sub.vdrk.site/v2',
6
+ 'https://fed-subs.pstream.mov'
7
+ ];
8
+ const VYLIAN_MESSAGES = [
9
+ "Thanks for using Vyla!"
10
+ ];
11
+
12
+ function generateVylianVtt() {
13
+ let vttContent = 'WEBVTT\n\n';
14
+
15
+ let currentTime = 1;
16
+ while (currentTime <= 7200) {
17
+ const duration = Math.floor(Math.random() * 4) + 1;
18
+ const message = VYLIAN_MESSAGES[Math.floor(Math.random() * VYLIAN_MESSAGES.length)];
19
+
20
+ const startTime = formatVttTime(currentTime);
21
+ const endTime = formatVttTime(currentTime + duration);
22
+
23
+ vttContent += `${startTime} --> ${endTime}\n${message}\n\n`;
24
+
25
+ currentTime += duration + Math.floor(Math.random() * 60) + 30;
26
+ }
27
+
28
+ return vttContent;
29
+ }
30
+
31
+ function formatVttTime(seconds) {
32
+ const hours = Math.floor(seconds / 3600);
33
+ const minutes = Math.floor((seconds % 3600) / 60);
34
+ const secs = seconds % 60;
35
+ return `${String(hours).padStart(2, '0')}:${String(minutes).padStart(2, '0')}:${String(secs).padStart(2, '0')}.000`;
36
+ }
37
+
38
+ export async function fetchSubtitles(paths = []) {
39
+ try {
40
+ const results = await Promise.all(
41
+ paths.map(async ({ base, path }) => {
42
+ try {
43
+ const res = await fetch(`${base}${path}`, {
44
+ headers: { 'User-Agent': getUA() },
45
+ signal: AbortSignal.timeout(5000),
46
+ });
47
+
48
+ if (!res.ok) {
49
+ res.body?.cancel();
50
+ return [];
51
+ }
52
+
53
+ const data = await res.json();
54
+
55
+ if (base.includes('/v2')) {
56
+ return Array.isArray(data)
57
+ ? data.map(x => ({
58
+ label: x.label,
59
+ file: x.file || x.url,
60
+ type: 'vtt',
61
+ source: 'v2'
62
+ }))
63
+ : [];
64
+ }
65
+
66
+ if (base.includes('fed-subs.pstream.mov')) {
67
+ if (!data?.subtitles || typeof data.subtitles !== 'object') return [];
68
+
69
+ return Object.entries(data.subtitles)
70
+ .map(([language, sub]) => {
71
+ if (!sub?.subtitle_link) return null;
72
+ const ext = sub.subtitle_link.split('.').pop()?.toLowerCase();
73
+ return {
74
+ label: sub.subtitle_name || language,
75
+ file: sub.subtitle_link,
76
+ type: ext === 'vtt' ? 'vtt' : 'srt',
77
+ source: 'febbox'
78
+ };
79
+ })
80
+ .filter(Boolean);
81
+ }
82
+
83
+ const v1 = Array.isArray(data) ? data : [];
84
+ return v1.map(x => ({
85
+ label: x.label,
86
+ file: x.file || x.url,
87
+ type: 'vtt',
88
+ source: 'v1'
89
+ }));
90
+ } catch {
91
+ return [];
92
+ }
93
+ })
94
+ );
95
+
96
+ const val = results.flat();
97
+
98
+ val.push({
99
+ label: 'Vylian',
100
+ file: 'data:text/vtt;base64,' + Buffer.from(generateVylianVtt()).toString('base64'),
101
+ type: 'vtt',
102
+ source: 'easter-egg'
103
+ });
104
+
105
+ return val;
106
+ } catch {
107
+ return [];
108
+ }
109
+ }
110
+
111
+ export async function getSubPathsMovie(id) {
112
+ return [
113
+ { base: SUBTITLE_BASES[0], path: `/movie/${id}` },
114
+ { base: SUBTITLE_BASES[1], path: `/movie/${id}` },
115
+ { base: SUBTITLE_BASES[2], path: `/movie/tt${id}` }
116
+ ];
117
+ }
118
+
119
+ export async function getSubPathsTv(id, season, episode) {
120
+ return [
121
+ { base: SUBTITLE_BASES[0], path: `/tv/${id}/${season}/${episode}` },
122
+ { base: SUBTITLE_BASES[1], path: `/tv/${id}/${season}/${episode}` },
123
+ { base: SUBTITLE_BASES[2], path: `/tv/tt${id}/${season}/${episode}` }
124
+ ];
125
+ }
126
+
127
+ export { SUBTITLE_BASES };