@vyla-entertainment/sdk 1.20.0 → 1.22.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 (58) hide show
  1. package/package.json +3 -2
  2. package/src/config.js +99 -67
  3. package/src/health.js +60 -20
  4. package/src/sdk.d.ts +94 -121
  5. package/src/sdk.js +82 -78
  6. package/src/sources/111477.js +88 -100
  7. package/src/sources/123anime.js +120 -56
  8. package/src/sources/3eyedraven.js +82 -135
  9. package/src/sources/4movies.js +77 -0
  10. package/src/sources/aether.js +99 -0
  11. package/src/sources/castle.js +415 -0
  12. package/src/sources/cinecat.js +37 -18
  13. package/src/sources/cinemaarmy.js +179 -180
  14. package/src/sources/cinesrc.js +401 -397
  15. package/src/sources/cno.js +101 -94
  16. package/src/sources/dulo.js +134 -149
  17. package/src/sources/flaxmovies.js +114 -105
  18. package/src/sources/framextv.js +126 -0
  19. package/src/sources/frost.js +38 -29
  20. package/src/sources/fsonline.js +95 -47
  21. package/src/sources/hdhub.js +43 -36
  22. package/src/sources/hexa.js +60 -53
  23. package/src/sources/hyper.js +42 -35
  24. package/src/sources/kickassanime.js +117 -124
  25. package/src/sources/kisskh.js +114 -59
  26. package/src/sources/lmscript.js +72 -70
  27. package/src/sources/lookmovie.js +80 -79
  28. package/src/sources/meowtv.js +140 -0
  29. package/src/sources/movenerds.js +74 -124
  30. package/src/sources/movy.js +197 -209
  31. package/src/sources/net79.js +59 -55
  32. package/src/sources/peestream.js +126 -138
  33. package/src/sources/pengu.js +46 -49
  34. package/src/sources/pstream.js +73 -67
  35. package/src/sources/purstream.js +96 -104
  36. package/src/sources/rivestream.js +182 -176
  37. package/src/sources/rivo.js +58 -53
  38. package/src/sources/streamaggregator.js +114 -128
  39. package/src/sources/thugtv.js +70 -62
  40. package/src/sources/vidapi.js +122 -114
  41. package/src/sources/vidcore.js +14 -122
  42. package/src/sources/vidfast.js +14 -122
  43. package/src/sources/vidlink.js +78 -27
  44. package/src/sources/vidora.js +74 -42
  45. package/src/sources/vidup.js +14 -122
  46. package/src/sources/vidvault.js +133 -187
  47. package/src/sources/vidy.js +192 -187
  48. package/src/sources/vidzee.js +189 -183
  49. package/src/sources/vixsrc.js +123 -113
  50. package/src/sources/watchout.js +60 -46
  51. package/src/sources/xdownloader.js +127 -128
  52. package/src/sources/yapgrid.js +68 -62
  53. package/src/sources/yastream.js +35 -29
  54. package/src/subtitles.js +105 -122
  55. package/src/utils/helpers.js +586 -247
  56. package/src/sources/bcine.js +0 -202
  57. package/src/sources/cinezo.js +0 -55
  58. package/src/sources/vidrock.js +0 -145
@@ -1,55 +1,87 @@
1
- import { fetchText } from '../utils/helpers.js';
1
+ const BASE_URL = 'https://vidoraplayer.duckdns.org';
2
+ 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';
2
3
 
3
- export async function getStream({ id, s, e }) {
4
- const isTv = s != null && e != null;
5
- const url = isTv
6
- ? `https://vidora-3l2w.onrender.com/api/sources?type=tv&id=${id}&season=${s}&episode=${e}`
7
- : `https://vidora-3l2w.onrender.com/api/sources?type=movie&id=${id}`;
8
-
9
- const headers = {
10
- 'Accept': 'text/event-stream',
11
- 'Referer': 'https://vidora-3l2w.onrender.com/',
12
- 'Origin': 'https://vidora-3l2w.onrender.com',
13
- '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'
14
- };
15
-
16
- const raw = await fetchText(url, { headers }).catch(() => null);
17
- if (!raw) return null;
18
-
19
- const lines = raw.split('\n');
20
- let doneSources = null;
21
- const streamItems = [];
22
-
23
- for (const line of lines) {
24
- const trimmed = line.trim();
25
- if (!trimmed.startsWith('{') || !trimmed.endsWith('}')) continue;
4
+ const HEADERS = {
5
+ 'User-Agent': UA,
6
+ Referer: `${BASE_URL}/`,
7
+ Origin: BASE_URL,
8
+ Accept: 'application/x-ndjson, text/plain, */*',
9
+ 'Accept-Language': 'en-US,en;q=0.9',
10
+ };
26
11
 
12
+ function parseNdjson(text) {
13
+ if (!text) return [];
14
+ const normalized = text.replace(/\}\s*\{/g, '}\n{');
15
+ const lines = normalized.split('\n');
16
+ const items = [];
17
+ for (const raw of lines) {
18
+ const line = raw.trim();
19
+ if (!line) continue;
27
20
  try {
28
- const parsed = JSON.parse(trimmed);
29
- if (parsed.event === 'source' && parsed.source?.url) {
30
- streamItems.push(parsed.source);
31
- } else if (parsed.event === 'done' && Array.isArray(parsed.sources)) {
32
- doneSources = parsed.sources;
33
- }
21
+ items.push(JSON.parse(line));
34
22
  } catch { }
35
23
  }
24
+ return items;
25
+ }
36
26
 
37
- const targetList = (doneSources && doneSources.length > 0) ? doneSources : streamItems;
38
- if (!targetList.length) return null;
27
+ export async function getStream(args) {
28
+ const { id, s, e } = args || {};
29
+ const isTv = s != null && e != null;
30
+
31
+ const query = isTv
32
+ ? `type=tv&id=${id}&season=${s}&episode=${e}`
33
+ : `type=movie&id=${id}`;
34
+
35
+ const urls = [
36
+ `${BASE_URL}/api/sources?${query}`,
37
+ `${BASE_URL}/api/sources?${query}&gen=2`,
38
+ ];
39
+
40
+ const responses = await Promise.all(
41
+ urls.map(url =>
42
+ fetch(url, { headers: HEADERS })
43
+ .then(r => (r.ok ? r.text() : ''))
44
+ .catch(() => '')
45
+ )
46
+ );
39
47
 
40
48
  const allUrls = [];
49
+ const seen = new Set();
50
+
51
+ for (const text of responses) {
52
+ const events = parseNdjson(text);
53
+ for (const item of events) {
54
+ const list = [];
55
+ if (item?.event === 'source' && item?.source) {
56
+ list.push(item.source);
57
+ } else if (item?.event === 'done' && Array.isArray(item?.sources)) {
58
+ list.push(...item.sources);
59
+ } else if (item?.url) {
60
+ list.push(item);
61
+ }
41
62
 
42
- for (const src of targetList) {
43
- if (!src.url || !src.url.startsWith('http')) continue;
63
+ for (const src of list) {
64
+ if (!src?.url) continue;
65
+ const fullUrl = src.url.startsWith('http')
66
+ ? src.url
67
+ : `${BASE_URL}${src.url.startsWith('/') ? '' : '/'}${src.url}`;
44
68
 
45
- allUrls.push({
46
- url: src.url,
47
- server: `Vidora - ${src.label || src.server || src.providerGroup || 'Server'} (${src.quality || 'Auto'})`,
48
- headers: src.headers || {
49
- Referer: 'https://vidora-3l2w.onrender.com/',
50
- Origin: 'https://vidora-3l2w.onrender.com'
69
+ if (!seen.has(fullUrl)) {
70
+ seen.add(fullUrl);
71
+ allUrls.push({
72
+ url: fullUrl,
73
+ server: `Vidora - ${src.label || src.server || src.providerGroup || 'Default'}`,
74
+ quality: src.quality || 'Auto',
75
+ type: src.type || (fullUrl.includes('.m3u8') ? 'hls' : 'mp4'),
76
+ headers: {
77
+ Referer: `${BASE_URL}/`,
78
+ Origin: BASE_URL,
79
+ 'User-Agent': UA,
80
+ },
81
+ });
82
+ }
51
83
  }
52
- });
84
+ }
53
85
  }
54
86
 
55
87
  if (!allUrls.length) return null;
@@ -57,6 +89,6 @@ export async function getStream({ id, s, e }) {
57
89
  return {
58
90
  url: allUrls[0].url,
59
91
  headers: allUrls[0].headers,
60
- allUrls
92
+ allUrls,
61
93
  };
62
94
  }
@@ -1,123 +1,15 @@
1
- import { getUA } from '../utils/helpers.js';
2
-
3
- const BASE_URL = 'https://vidup.to';
4
- const API = 'https://enc-dec.app/api';
5
-
6
- export async function getStream({ id, s, e }) {
7
- const isTv = Boolean(s && e);
8
- const targetUrl = isTv
9
- ? `${BASE_URL}/tv/${id}/${s}/${e}/`
10
- : `${BASE_URL}/movie/${id}`;
11
-
12
- const ua = getUA();
13
- const baseHeaders = {
14
- 'User-Agent': ua,
15
- 'Referer': `${BASE_URL}/`,
16
- 'X-Requested-With': 'XMLHttpRequest',
17
- };
18
-
19
- try {
20
- const pageRes = await fetch(targetUrl, {
21
- headers: { 'User-Agent': ua },
22
- signal: AbortSignal.timeout(10000),
23
- });
24
- if (!pageRes.ok) return null;
25
- const html = await pageRes.text();
26
-
27
- const match = html.match(/\\"(?:en|token)\\":\\"(.*?)\\"/);
28
- if (!match || !match[1]) return null;
29
- const enToken = match[1];
30
-
31
- const s1Res = await fetch(`${API}/enc-vidup?text=${encodeURIComponent(enToken)}&stage=1`, {
32
- signal: AbortSignal.timeout(8000),
33
- });
34
- const s1Data = await s1Res.json();
35
- if (s1Data?.status !== 200 || !s1Data?.result?.stage1) return null;
36
-
37
- const token1 = s1Data.result.token;
38
- const stage1Url = s1Data.result.stage1;
39
-
40
- const post1Res = await fetch(stage1Url, {
41
- method: 'POST',
42
- headers: { ...baseHeaders, 'X-CSRF-Token': token1 },
43
- signal: AbortSignal.timeout(10000),
44
- });
45
- if (!post1Res.ok) return null;
46
- const stage1Text = await post1Res.text();
47
-
48
- const s2Res = await fetch(`${API}/enc-vidup?text=${encodeURIComponent(stage1Text)}&stage=2`, {
49
- signal: AbortSignal.timeout(8000),
50
- });
51
- const s2Data = await s2Res.json();
52
- if (s2Data?.status !== 200 || !s2Data?.result?.servers) return null;
53
-
54
- const { servers: serversUrl, stream: streamBaseUrl, token: token2 } = s2Data.result;
55
- const currentHeaders = { ...baseHeaders, 'X-CSRF-Token': token2 };
56
-
57
- const serversRes = await fetch(serversUrl, {
58
- method: 'POST',
59
- headers: currentHeaders,
60
- signal: AbortSignal.timeout(10000),
61
- });
62
- if (!serversRes.ok) return null;
63
- const serversEncrypted = await serversRes.text();
64
-
65
- const decServersRes = await fetch(`${API}/dec-vidup`, {
66
- method: 'POST',
67
- headers: { 'Content-Type': 'application/json' },
68
- body: JSON.stringify({ text: serversEncrypted }),
69
- signal: AbortSignal.timeout(8000),
70
- });
71
- const decServersData = await decServersRes.json();
72
- if (decServersData?.status !== 200 || !Array.isArray(decServersData.result)) return null;
73
-
74
- const allUrls = [];
75
-
76
- for (const server of decServersData.result) {
77
- if (!server?.data) continue;
78
- try {
79
- const streamUrl = `${streamBaseUrl}/${server.data}`;
80
- const streamEncRes = await fetch(streamUrl, {
81
- method: 'POST',
82
- headers: currentHeaders,
83
- signal: AbortSignal.timeout(8000),
84
- });
85
- if (!streamEncRes.ok) continue;
86
- const streamEncText = await streamEncRes.text();
87
-
88
- const decStreamRes = await fetch(`${API}/dec-vidup`, {
89
- method: 'POST',
90
- headers: { 'Content-Type': 'application/json' },
91
- body: JSON.stringify({ text: streamEncText }),
92
- signal: AbortSignal.timeout(8000),
93
- });
94
- const decStreamData = await decStreamRes.json();
95
- if (decStreamData?.status !== 200 || !decStreamData.result) continue;
96
-
97
- const streamResult = decStreamData.result;
98
- const resultUrl = typeof streamResult === 'string' ? streamResult : streamResult?.url || streamResult?.file;
99
-
100
- if (typeof resultUrl === 'string' && resultUrl.startsWith('http')) {
101
- allUrls.push({
102
- url: resultUrl,
103
- label: server.name || server.title || 'VidUp',
104
- headers: {
105
- 'User-Agent': ua,
106
- 'Referer': `${BASE_URL}/`,
107
- },
108
- });
109
- }
110
- } catch { }
111
- }
112
-
113
- if (allUrls.length === 0) return null;
114
-
115
- return {
116
- url: allUrls[0].url,
117
- allUrls,
118
- headers: allUrls[0].headers,
119
- };
120
- } catch {
121
- return null;
122
- }
1
+ import { scrapeVidcoreFamily } from '../utils/helpers.js';
2
+
3
+ const BASE_URL = 'https://vidup.to';
4
+
5
+ export async function getStream(args) {
6
+ const { id, s, e } = args || {};
7
+ return await scrapeVidcoreFamily({
8
+ id,
9
+ s,
10
+ e,
11
+ baseUrl: BASE_URL,
12
+ apiName: 'vidup',
13
+ serverLabel: 'VidUp',
14
+ });
123
15
  }
@@ -1,188 +1,134 @@
1
- import { USER_AGENT } from '../utils/helpers.js';
2
-
3
- const BASE_URL = 'https://vidvault.to';
4
- const API_BASE = `${BASE_URL}/api`;
5
- const DL_PROXY_BASE = 'https://dl.gemlelispe.workers.dev';
6
- const SUB_PROXY_BASE = 'https://sub.k5s7sjozpn.workers.dev';
7
-
8
- const HEADERS = {
9
- 'Referer': `${BASE_URL}/`,
10
- 'Origin': BASE_URL,
11
- 'User-Agent': USER_AGENT,
12
- 'Accept': 'application/json, text/plain, */*',
13
- 'Accept-Language': 'en-US,en;q=0.9',
14
- };
15
-
16
- function formatBytes(bytes, decimals = 2) {
17
- if (!bytes || bytes <= 0) return '0 B';
18
- const k = 1024;
19
- const sizes = ['B', 'KB', 'MB', 'GB', 'TB'];
20
- const i = Math.min(Math.floor(Math.log(bytes) / Math.log(k)), sizes.length - 1);
21
- return `${(bytes / Math.pow(k, i)).toFixed(decimals)} ${sizes[i]}`;
22
- }
23
-
24
- async function getToken() {
25
- const res = await fetch(`${API_BASE}/get-token`, {
26
- method: 'GET',
27
- headers: HEADERS,
28
- signal: AbortSignal.timeout(10000),
29
- });
30
- if (!res.ok) throw new Error(`Token fetch failed: ${res.status}`);
31
- const data = await res.json();
32
- return data?.t;
33
- }
34
-
35
- function getCaptionType(url) {
36
- return String(url).toLowerCase().includes('.vtt') ? 'vtt' : 'srt';
37
- }
38
-
39
- export async function getStream(args) {
40
- const { id, s, e } = args;
41
- const isTv = s != null && e != null;
42
- const type = isTv ? 'tv' : 'movie';
43
-
44
- try {
45
- const token = await getToken();
46
- if (!token) return null;
47
-
48
- const payload = {
49
- type,
50
- tmdbId: String(id),
51
- };
52
-
53
- if (isTv) {
54
- payload.season = Number(s);
55
- payload.episode = Number(e);
56
- }
57
-
58
- const res = await fetch(`${API_BASE}/download-proxy`, {
59
- method: 'POST',
60
- headers: {
61
- ...HEADERS,
62
- 'Content-Type': 'application/json',
63
- 'x-request-token': token,
64
- },
65
- body: JSON.stringify(payload),
66
- signal: AbortSignal.timeout(15000),
67
- });
68
-
69
- if (!res.ok) return null;
70
- const data = await res.json();
71
-
72
- const allUrls = [];
73
- const subtitles = [];
74
- const titleTag = encodeURIComponent(isTv ? `Show_${id}_S${s}E${e}` : `Movie_${id}`);
75
-
76
- const mp4 = data?.mp4Data;
77
- const downloadData = mp4?.downloadInfo?.data || mp4?.data?.data || mp4?.data || mp4;
78
-
79
- if (downloadData) {
80
- const streams = downloadData.streams || downloadData.downloads || [];
81
- for (const item of streams) {
82
- if (!item?.url) continue;
83
-
84
- const res = item.resolutions || item.resolution || 'Auto';
85
- const sizeStr = item.size ? ` (${typeof item.size === 'number' ? formatBytes(item.size) : item.size})` : '';
86
- const proxiedUrl = `${DL_PROXY_BASE}/${encodeURIComponent(item.url)}?n=${titleTag}`;
87
-
88
- allUrls.push({
89
- url: proxiedUrl,
90
- server: `VidVault - MP4 ${res}p${sizeStr}`.trim(),
91
- quality: typeof res === 'number' ? `${res}p` : res,
92
- type: 'mp4',
93
- headers: HEADERS,
94
- });
95
- }
96
-
97
- const captions = downloadData.captions || [];
98
- for (const cap of captions) {
99
- if (!cap?.url) continue;
100
- const subUrl = `${SUB_PROXY_BASE}/?url=${encodeURIComponent(cap.url)}&title=${titleTag}`;
101
- subtitles.push({
102
- label: cap.lanName || cap.lan || 'Unknown',
103
- type: getCaptionType(cap.url),
104
- language: cap.lan || '',
105
- url: subUrl,
106
- });
107
- }
108
- }
109
-
110
- const mkvFiles = data?.mkvData?.files || [];
111
- for (const file of mkvFiles) {
112
- if (!file?.url) continue;
113
- const sizeStr = file.size ? ` (${typeof file.size === 'number' ? formatBytes(file.size) : file.size})` : '';
114
- allUrls.push({
115
- url: file.url,
116
- server: `VidVault - MKV 480p${sizeStr}`.trim(),
117
- quality: '480p',
118
- type: 'mp4',
119
- headers: HEADERS,
120
- });
121
- }
122
-
123
- const mkvV2 = data?.mkvV2Data;
124
- const mkvV2List = Array.isArray(mkvV2)
125
- ? mkvV2
126
- : Array.isArray(mkvV2?.files)
127
- ? mkvV2.files
128
- : (mkvV2?.url ? [mkvV2] : []);
129
-
130
- for (const file of mkvV2List) {
131
- if (!file?.url) continue;
132
- const quality = file.quality ? String(file.quality).replace(/p$/i, '') : 'Auto';
133
- const sizeStr = file.size ? ` (${typeof file.size === 'number' ? formatBytes(file.size) : file.size})` : '';
134
- const meta = [file.country, file.language].filter(Boolean).join(' • ');
135
- const metaStr = meta ? ` [${meta}]` : '';
136
-
137
- allUrls.push({
138
- url: file.url,
139
- server: `VidVault - MKV v2 ${quality}p${sizeStr}${metaStr}`.trim(),
140
- quality: `${quality}p`,
141
- type: 'mp4',
142
- headers: HEADERS,
143
- });
144
- }
145
-
146
- const mkvV3 = data?.mkvV3Data;
147
- if (mkvV3?.downloads && Array.isArray(mkvV3.downloads)) {
148
- const country = mkvV3.country || '';
149
- const language = mkvV3.language || '';
150
-
151
- for (const down of mkvV3.downloads) {
152
- if (Array.isArray(down?.qualities)) {
153
- for (const qObj of down.qualities) {
154
- for (const ep of qObj?.episodes || []) {
155
- if (!ep?.url) continue;
156
- const quality = qObj.quality ? String(qObj.quality).replace(/p$/i, '') : 'Auto';
157
- const sizeStr = ep.size ? ` (${typeof ep.size === 'number' ? formatBytes(ep.size) : ep.size})` : '';
158
- const meta = [ep.country || country, ep.language || language].filter(Boolean).join(' • ');
159
- const metaStr = meta ? ` [${meta}]` : '';
160
-
161
- allUrls.push({
162
- url: ep.url,
163
- server: `VidVault - MKV v3 ${quality}p${sizeStr}${metaStr}`.trim(),
164
- quality: `${quality}p`,
165
- type: 'mp4',
166
- headers: HEADERS,
167
- });
168
- }
169
- }
170
- } else if (down?.url) {
171
- const quality = down.quality ? String(down.quality).replace(/p$/i, '') : 'Auto';
172
- const sizeStr = down.size ? ` (${typeof down.size === 'number' ? formatBytes(down.size) : down.size})` : '';
173
- allUrls.push({
174
- url: down.url,
175
- server: `VidVault - MKV v3 ${quality}p${sizeStr}`.trim(),
176
- quality: `${quality}p`,
177
- type: 'mp4',
178
- headers: HEADERS,
179
- });
180
- }
181
- }
182
- }
183
-
184
- return allUrls.length ? { allUrls, subtitles: subtitles.length ? subtitles : undefined } : null;
185
- } catch (err) {
186
- return null;
187
- }
1
+ import { formatBytes, USER_AGENT } from '../utils/helpers.js';
2
+
3
+ const BASE_URL = 'https://vidvault.to';
4
+ const API_BASE = `${BASE_URL}/api`;
5
+ const DL_PROXY_BASE = 'https://dl.gemlelispe.workers.dev';
6
+ const SUB_PROXY_BASE = 'https://sub.k5s7sjozpn.workers.dev';
7
+
8
+ const HEADERS = {
9
+ Referer: `${BASE_URL}/`,
10
+ Origin: BASE_URL,
11
+ 'User-Agent': USER_AGENT,
12
+ Accept: 'application/json, text/plain, */*',
13
+ 'Accept-Language': 'en-US,en;q=0.9',
14
+ };
15
+
16
+ async function getToken() {
17
+ const res = await fetch(`${API_BASE}/get-token`, {
18
+ method: 'GET',
19
+ headers: HEADERS,
20
+ signal: AbortSignal.timeout(10000),
21
+ });
22
+ if (!res.ok) throw new Error(`Token fetch failed: ${res.status}`);
23
+ const data = await res.json();
24
+ return data?.t;
25
+ }
26
+
27
+ function getCaptionType(url) {
28
+ return String(url).toLowerCase().includes('.vtt') ? 'vtt' : 'srt';
29
+ }
30
+
31
+ async function fetchJson(url, options = {}) {
32
+ try {
33
+ const res = await fetch(url, options);
34
+ if (!res.ok) return null;
35
+ return await res.json();
36
+ } catch {
37
+ return null;
38
+ }
39
+ }
40
+
41
+ async function resolveDirect(directPath, headers) {
42
+ const fullUrl = directPath.startsWith('http') ? directPath : `${BASE_URL}${directPath}`;
43
+ try {
44
+ const res = await fetch(fullUrl, {
45
+ method: 'HEAD',
46
+ redirect: 'manual',
47
+ headers,
48
+ });
49
+ const loc = res.headers.get('location');
50
+ if (loc) return loc;
51
+ } catch { }
52
+ return fullUrl;
53
+ }
54
+
55
+ export async function getStream(args) {
56
+ const { id, s, e } = args || {};
57
+ const isTv = s != null && e != null;
58
+
59
+ const mediaPath = isTv
60
+ ? `${BASE_URL}/media/tv/${id}/${s}/${e}`
61
+ : `${BASE_URL}/media/movie/${id}`;
62
+
63
+ const [mediaData, tokenData] = await Promise.all([
64
+ fetchJson(mediaPath, { headers: HEADERS }).catch(() => null),
65
+ fetchJson(`${BASE_URL}/api/get-token`, { headers: HEADERS }).catch(() => null),
66
+ ]);
67
+
68
+ const allUrls = [];
69
+ const seen = new Set();
70
+
71
+ if (Array.isArray(mediaData?.direct)) {
72
+ await Promise.all(
73
+ mediaData.direct.map(async (item) => {
74
+ if (!item?.path) return;
75
+ const streamUrl = await resolveDirect(item.path, HEADERS);
76
+ if (streamUrl && !seen.has(streamUrl)) {
77
+ seen.add(streamUrl);
78
+ const format = item.format || (streamUrl.includes('.mkv') ? 'mkv' : 'mp4');
79
+ allUrls.push({
80
+ url: streamUrl,
81
+ server: `VidVault - Direct ${format.toUpperCase()} (${item.quality || item.size || 'Auto'})`,
82
+ quality: item.quality || 'Auto',
83
+ type: format,
84
+ headers: HEADERS,
85
+ });
86
+ }
87
+ })
88
+ );
89
+ }
90
+
91
+ const token = tokenData?.t;
92
+ if (token) {
93
+ const payload = isTv
94
+ ? { type: 'tv', tmdbId: String(id), season: String(s), episode: String(e) }
95
+ : { type: 'movie', tmdbId: String(id) };
96
+
97
+ const proxyData = await fetchJson(`${BASE_URL}/api/download-proxy`, {
98
+ method: 'POST',
99
+ headers: {
100
+ ...HEADERS,
101
+ 'Content-Type': 'application/json',
102
+ 'x-request-token': token,
103
+ },
104
+ body: JSON.stringify(payload),
105
+ }).catch(() => null);
106
+
107
+ const candidates = [
108
+ { data: proxyData?.mp4Data, type: 'mp4', label: 'MP4' },
109
+ { data: proxyData?.mkvData, type: 'mkv', label: 'MKV' },
110
+ { data: proxyData?.mkvV2Data, type: 'mkv', label: 'MKV V2' },
111
+ ];
112
+
113
+ for (const { data, type, label } of candidates) {
114
+ if (data?.url && !seen.has(data.url)) {
115
+ seen.add(data.url);
116
+ allUrls.push({
117
+ url: data.url,
118
+ server: `VidVault - ${label} (${data.quality || data.size || 'Auto'})`,
119
+ quality: data.quality || 'Auto',
120
+ type,
121
+ headers: HEADERS,
122
+ });
123
+ }
124
+ }
125
+ }
126
+
127
+ if (!allUrls.length) return null;
128
+
129
+ return {
130
+ url: allUrls[0].url,
131
+ headers: allUrls[0].headers,
132
+ allUrls,
133
+ };
188
134
  }