@vyla-entertainment/sdk 1.6.0 → 1.8.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.
@@ -0,0 +1,114 @@
1
+ import { USER_AGENT } from '../utils/helpers.js';
2
+
3
+ const BASE_URL = "https://vidvault.ru";
4
+ const HEADERS = {
5
+ "Referer": `${BASE_URL}/`,
6
+ "Origin": BASE_URL,
7
+ "User-Agent": USER_AGENT,
8
+ };
9
+
10
+ async function getToken() {
11
+ const res = await fetch(`${BASE_URL}/api/get-token`, {
12
+ method: "GET",
13
+ headers: HEADERS
14
+ });
15
+ const data = await res.json();
16
+ return data.t;
17
+ }
18
+
19
+ function getCaptionType(url) {
20
+ return url.toLowerCase().includes(".vtt") ? "vtt" : "srt";
21
+ }
22
+
23
+ export async function getStream(args) {
24
+ const { id, s, e } = args;
25
+ const type = (s != null && e != null) ? "tv" : "movie";
26
+
27
+ try {
28
+ const token = await getToken();
29
+
30
+ const payload = { type, tmdbId: String(id) };
31
+ if (type === "tv") {
32
+ payload.season = String(s);
33
+ payload.episode = String(e);
34
+ }
35
+
36
+ const res = await fetch(`${BASE_URL}/api/download-proxy`, {
37
+ method: "POST",
38
+ headers: {
39
+ ...HEADERS,
40
+ "Content-Type": "application/json",
41
+ "x-request-token": token
42
+ },
43
+ body: JSON.stringify(payload)
44
+ });
45
+ const data = await res.json();
46
+
47
+ const allUrls = [];
48
+ const subtitles = [];
49
+
50
+ const mp4 = data?.mp4Data;
51
+ if (mp4) {
52
+ const country = mp4.country || "";
53
+ const lanName = mp4.lanName || "";
54
+ const detailPath = mp4.detailPath || "";
55
+
56
+ const downloads = mp4.downloadInfo?.data?.downloads || [];
57
+ for (const d of downloads) {
58
+ if (!d?.url) continue;
59
+ allUrls.push({
60
+ quality: `${d.resolution}p ${detailPath} ${country} ${lanName}`.trim(),
61
+ url: d.url,
62
+ type: "mp4",
63
+ headers: HEADERS
64
+ });
65
+ }
66
+
67
+ const srtCaptions = mp4.downloadInfo?.data?.captions || [];
68
+ for (const c of srtCaptions) {
69
+ if (!c?.url) continue;
70
+ subtitles.push({
71
+ label: c.lanName || c.lan || "Unknown",
72
+ type: getCaptionType(c.url),
73
+ language: c.lan || "",
74
+ url: c.url
75
+ });
76
+ }
77
+ }
78
+
79
+ const mkvFiles = data?.mkvData?.files || [];
80
+ for (const f of mkvFiles) {
81
+ if (!f?.url) continue;
82
+ allUrls.push({
83
+ quality: `MKV ${f.size || ""}`.trim(),
84
+ url: f.url,
85
+ type: "mp4",
86
+ headers: HEADERS
87
+ });
88
+ }
89
+
90
+ const mkvV2 = data?.mkvV2Data;
91
+ if (mkvV2?.url) {
92
+ allUrls.push({
93
+ quality: `${mkvV2.quality || ""} ${mkvV2.language || ""} ${mkvV2.country || ""} (${mkvV2.size || ""})`.trim(),
94
+ url: mkvV2.url,
95
+ type: "mp4",
96
+ headers: HEADERS
97
+ });
98
+ }
99
+
100
+ const mkvV3 = data?.mkvV3Data;
101
+ if (mkvV3?.url) {
102
+ allUrls.push({
103
+ quality: `${mkvV3.quality || ""} ${mkvV3.language || ""} ${mkvV3.country || ""} (${mkvV3.size || ""})`.trim(),
104
+ url: mkvV3.url,
105
+ type: "mp4",
106
+ headers: HEADERS
107
+ });
108
+ }
109
+
110
+ return allUrls.length ? { allUrls, subtitles } : null;
111
+ } catch (err) {
112
+ return null;
113
+ }
114
+ }
@@ -1,70 +1,47 @@
1
- import { webcrypto } from 'node:crypto';
1
+ // i can't fix this, i'll wait for someone else to fix this lol
2
+
3
+ import crypto from 'node:crypto';
2
4
  import { fetchJson, USER_AGENT } from '../utils/helpers.js';
3
5
 
4
- const PLAYER_URL = 'https://player.vidzee.wtf';
5
6
  const CORE_URL = 'https://core.vidzee.wtf';
7
+ const PLAYER_URL = 'https://player.vidzee.wtf';
8
+ const KEY = 'c4a8f1d7e2b9a6c3d0f5e8a1b7c4d9e2';
6
9
 
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']);
10
+ function decryptAes(encryptedBase64) {
11
+ const keyBuf = Buffer.from(KEY, 'utf8');
12
+ const ciphertext = Buffer.from(encryptedBase64, 'base64');
28
13
 
29
14
  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
- }
15
+ const decipher = crypto.createDecipheriv('aes-256-ecb', keyBuf, null);
16
+ decipher.setAutoPadding(true);
17
+ let dec = decipher.update(ciphertext);
18
+ dec = Buffer.concat([dec, decipher.final()]);
19
+ const text = dec.toString('utf8');
20
+ if (text.startsWith('http') || text.includes('{')) return text;
21
+ } catch { }
43
22
 
44
- function decrypt(enc, key) {
45
23
  try {
46
- const dec = Buffer.from(enc, 'base64').toString('utf8');
47
- const sep = dec.indexOf(':');
48
- if (sep === -1) return '';
24
+ const iv = Buffer.alloc(16, 0);
25
+ const decipher = crypto.createDecipheriv('aes-256-cbc', keyBuf, iv);
26
+ decipher.setAutoPadding(true);
27
+ let dec = decipher.update(ciphertext);
28
+ dec = Buffer.concat([dec, decipher.final()]);
29
+ const text = dec.toString('utf8');
30
+ if (text.startsWith('http') || text.includes('{')) return text;
31
+ } catch { }
49
32
 
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
- }
33
+ try {
34
+ const iv = ciphertext.subarray(0, 16);
35
+ const data = ciphertext.subarray(16);
36
+ const decipher = crypto.createDecipheriv('aes-256-cbc', keyBuf, iv);
37
+ decipher.setAutoPadding(true);
38
+ let dec = decipher.update(data);
39
+ dec = Buffer.concat([dec, decipher.final()]);
40
+ const text = dec.toString('utf8');
41
+ if (text.startsWith('http') || text.includes('{')) return text;
42
+ } catch { }
43
+
44
+ return null;
68
45
  }
69
46
 
70
47
  export async function getStream({ id, s, e, clientIP }) {
@@ -77,44 +54,53 @@ export async function getStream({ id, s, e, clientIP }) {
77
54
  };
78
55
 
79
56
  try {
80
- const decKey = await getDecKey(headers);
81
-
82
- const serversToTry = [3, 0, 1, 2, 4, 5, 6, 8];
57
+ const isTv = s != null && e != null;
58
+ const servers = ['tik', 'ipcloud', 'v6:Hindi'];
59
+ const results = [];
83
60
 
84
- for (const sr of serversToTry) {
61
+ for (const sr of servers) {
85
62
  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')) {
63
+ const url = isTv
64
+ ? `${CORE_URL}/streams/tv/${id}/${s}/${e}?s=${encodeURIComponent(sr)}&e=${e}`
65
+ : `${CORE_URL}/streams/movie/${id}?s=${encodeURIComponent(sr)}&e=1`;
66
+
67
+ const data = await fetchJson(url, { headers, signal: AbortSignal.timeout(6000) });
68
+
69
+ if (data && data.c) {
70
+ const decryptedText = decryptAes(data.c);
71
+ if (decryptedText) {
72
+ let streamUrl = decryptedText;
73
+ let extraHeaders = {};
74
+
75
+ if (decryptedText.startsWith('{')) {
76
+ try {
77
+ const parsed = JSON.parse(decryptedText);
78
+ streamUrl = parsed.url || parsed.link || parsed.file;
79
+ if (parsed.headers) extraHeaders = parsed.headers;
80
+ } catch { }
81
+ }
95
82
 
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
- };
83
+ if (streamUrl && streamUrl.startsWith('http')) {
84
+ results.push({
85
+ url: streamUrl,
86
+ server: `VidZee - ${sr}`,
87
+ skipHlsCheck: true,
88
+ headers: {
89
+ 'User-Agent': USER_AGENT,
90
+ 'Referer': PLAYER_URL,
91
+ 'Origin': PLAYER_URL,
92
+ ...extraHeaders
93
+ }
94
+ });
109
95
  }
110
96
  }
111
97
  }
112
98
  } catch (err) {
113
- continue;
114
99
  }
115
100
  }
116
- return null;
117
- } catch (err) {
101
+
102
+ return results.length ? { allUrls: results } : null;
103
+ } catch {
118
104
  return null;
119
105
  }
120
106
  }
@@ -13,7 +13,11 @@ const HEADERS = {
13
13
  function extract(regex, text) {
14
14
  const m = text?.match(regex);
15
15
  if (!m) return null;
16
- return m[1].replace(/\\/g, '');
16
+ let val = m[1];
17
+ val = val.replace(/\\u0026/gi, '&');
18
+ val = val.replace(/\\\//g, '/');
19
+ val = val.replace(/\\/g, '');
20
+ return val;
17
21
  }
18
22
 
19
23
  export async function getStream({ id, s, e, absoluteBase }) {
@@ -22,36 +26,40 @@ export async function getStream({ id, s, e, absoluteBase }) {
22
26
  ? `${BASE_URL}/api/tv/${id}/${s}/${e}`
23
27
  : `${BASE_URL}/api/movie/${id}`;
24
28
 
25
- const apiProxy = absoluteBase
29
+ const apiProxy = absoluteBase && absoluteBase !== '/'
26
30
  ? `${absoluteBase.replace(/^https:\/\/(localhost|127\.0\.0\.1)/, 'http://$1')}/api?url=${encodeURIComponent(apiUrl)}&proxyHeaders=${encodeURIComponent(JSON.stringify(HEADERS))}`
27
31
  : apiUrl;
28
32
 
29
- const apiText = await fetchText(apiProxy).catch(() => null);
33
+ const apiText = await fetchText(apiProxy, { headers: HEADERS }).catch(() => null);
30
34
  const apiData = JSON.parse(apiText || '{}');
31
35
  if (!apiData.src) return null;
32
36
 
33
37
  const embedUrl = apiData.src.startsWith('http') ? apiData.src : `${BASE_URL}${apiData.src}`;
34
- const embedProxy = absoluteBase
38
+ const embedProxy = absoluteBase && absoluteBase !== '/'
35
39
  ? `${absoluteBase.replace(/^https:\/\/(localhost|127\.0\.0\.1)/, 'http://$1')}/api?url=${encodeURIComponent(embedUrl)}&proxyHeaders=${encodeURIComponent(JSON.stringify(HEADERS))}`
36
40
  : embedUrl;
37
41
 
38
- const html = await fetchText(embedProxy).catch(() => null);
42
+ const html = await fetchText(embedProxy, { headers: HEADERS }).catch(() => null);
39
43
  if (!html) return null;
40
44
 
41
45
  const token = extract(/token["']\s*:\s*["']([^"']+)/, html);
42
46
  const expires = extract(/expires["']\s*:\s*["']([^"']+)/, html);
43
47
  let playlist = extract(/url["']\s*:\s*["']([^"']+)/, html);
48
+
44
49
  if (!token || !expires || !playlist) return null;
45
50
 
46
- playlist = playlist.replace(/\\/g, '');
51
+ if (playlist.startsWith('/')) {
52
+ playlist = `${BASE_URL}${playlist}`;
53
+ }
54
+
47
55
  const lang = extract(/lang(?:uage)?["']\s*:\s*["']([a-z]{2,5})/i, html) || 'en';
48
56
  const rawMasterUrl = `${playlist}${playlist.includes('?') ? '&' : '?'}token=${token}&expires=${expires}&h=1&lang=${lang}`;
49
57
 
50
- const masterProxy = absoluteBase
51
- ? `${absoluteBase.replace(/^https:\/\/(localhost|127\.0\.0\.1)/, 'http://$1')}/api?url=${encodeURIComponent(rawMasterUrl)}&proxyHeaders=${encodeURIComponent(JSON.stringify(HEADERS))}`
58
+ const masterProxy = absoluteBase && absoluteBase !== '/'
59
+ ? `${absoluteBase.replace(/^https:\/\/(localhost|127\.0\.0\.1)/, 'http://$1')}/api?url=${encodeURIComponent(rawMasterUrl)}&proxyHeaders=${encodeURIComponent(JSON.stringify({ ...HEADERS, Referer: embedUrl }))}`
52
60
  : rawMasterUrl;
53
61
 
54
- const playlistText = await fetchText(rawMasterUrl, { headers: HEADERS }).catch(() => null);
62
+ const playlistText = await fetchText(masterProxy, { headers: { ...HEADERS, 'Referer': embedUrl } }).catch(() => null);
55
63
  if (!playlistText?.includes('#EXTM3U')) return null;
56
64
 
57
65
  let finalUrl = null;
@@ -74,17 +82,23 @@ export async function getStream({ id, s, e, absoluteBase }) {
74
82
  if (!finalUrl) finalUrl = rawMasterUrl;
75
83
 
76
84
  if (!finalUrl.startsWith('http')) {
77
- const base = rawMasterUrl.split('/playlist/')[0];
78
- finalUrl = finalUrl.startsWith('/') ? `${base}${finalUrl}` : `${base}/${finalUrl}`;
85
+ const baseMatch = rawMasterUrl.match(/^(https?:\/\/[^\/]+)/);
86
+ const base = baseMatch ? baseMatch[1] : BASE_URL;
87
+ if (finalUrl.startsWith('/')) {
88
+ finalUrl = `${base}${finalUrl}`;
89
+ } else {
90
+ const pathParts = rawMasterUrl.split('/');
91
+ pathParts.pop();
92
+ finalUrl = `${pathParts.join('/')}/${finalUrl}`;
93
+ }
79
94
  }
80
95
 
81
- const alreadyProxied = /^https?:\/\/(localhost|127\.0\.0\.1)(:\d+)?\/api\?url=/.test(finalUrl);
82
-
83
96
  return {
84
97
  allUrls: [
85
98
  {
86
99
  url: finalUrl,
87
- skipProxy: alreadyProxied,
100
+ server: 'VixSrc',
101
+ skipProxy: true,
88
102
  headers: {
89
103
  ...HEADERS,
90
104
  'Referer': `${BASE_URL}/`
@@ -67,8 +67,7 @@ function cacheGet(cache, key) {
67
67
 
68
68
  function cacheSet(cache, key, val, ttl) { cache.set(key, { val, ts: Date.now(), ttl }); }
69
69
 
70
- export async function createStreamArgs(source, tmdbApiKey, id, s, e, clientIP=null) {
71
- const getAbsoluteBase = host => (host.startsWith('localhost') || host.startsWith('127.0.0.1')) ? `http://${host}` : `https://${host}`;
70
+ export async function createStreamArgs(source, tmdbApiKey, id, s, e, clientIP = null) {
72
71
  const audio = /dub$/.test(source.key) ? 'dub' : 'sub';
73
72
  return {
74
73
  id: id,
@@ -83,13 +82,25 @@ export async function createStreamArgs(source, tmdbApiKey, id, s, e, clientIP=nu
83
82
  }
84
83
 
85
84
  export async function validateTmdbId(tmdbApiKey, tmdbId, mediaType = 'movie') {
86
- const key = `${tmdbId}-${mediaType}`;
85
+ let apiKey = tmdbApiKey;
86
+ let id = tmdbId;
87
+ let type = mediaType;
88
+ if (id === 'movie' || id === 'tv' || !id) {
89
+ if (apiKey && apiKey !== 'movie' && apiKey !== 'tv') {
90
+ type = id || 'movie';
91
+ id = apiKey;
92
+ apiKey = process.env.TMDB_API_KEY;
93
+ }
94
+ }
95
+ if (!apiKey) {
96
+ apiKey = process.env.TMDB_API_KEY;
97
+ }
98
+ const key = `${id}-${type}`;
87
99
  const cached = cacheGet(tmdbValidationCache, key);
88
100
  if (cached !== undefined) return cached;
89
- const k = tmdbApiKey;
90
- if (!k) return { valid: false, error: 'TMDB API key not configured' };
101
+ if (!apiKey) return { valid: false, error: 'TMDB API key not configured' };
91
102
  try {
92
- const res = await fetch(`https://api.themoviedb.org/3/${mediaType}/${tmdbId}?api_key=${k}`, { signal: AbortSignal.timeout(5000) });
103
+ const res = await fetch(`https://api.themoviedb.org/3/${type}/${id}?api_key=${apiKey}`, { signal: AbortSignal.timeout(5000) });
93
104
  if (res.ok) {
94
105
  const data = await res.json();
95
106
  const isValid = !!(data?.id && (data?.title || data?.name));
@@ -108,15 +119,30 @@ export async function validateTmdbId(tmdbApiKey, tmdbId, mediaType = 'movie') {
108
119
  }
109
120
 
110
121
  export async function getTmdbInfo(tmdbApiKey, tmdbId, mediaType, season) {
111
- const key = `${tmdbId}-${mediaType}-${season || ''}`;
122
+ let apiKey = tmdbApiKey;
123
+ let id = tmdbId;
124
+ let type = mediaType;
125
+ let s = season;
126
+ if (apiKey === 'movie' || apiKey === 'tv') {
127
+ type = apiKey;
128
+ id = tmdbId;
129
+ apiKey = process.env.TMDB_API_KEY;
130
+ } else if (id === 'movie' || id === 'tv') {
131
+ type = id;
132
+ id = apiKey;
133
+ apiKey = process.env.TMDB_API_KEY;
134
+ }
135
+ if (!apiKey) {
136
+ apiKey = process.env.TMDB_API_KEY;
137
+ }
138
+ const key = `${id}-${type}-${s || ''}`;
112
139
  const cached = cacheGet(tmdbInfoCache, key);
113
140
  if (cached !== undefined) return cached;
114
- const k = tmdbApiKey;
115
- if (!k) return { isAnime: false, titles: [], year: null, imdbId: null };
141
+ if (!apiKey) return { isAnime: false, titles: [], year: null, imdbId: null };
116
142
  try {
117
143
  const [mainRes, seasonRes] = await Promise.all([
118
- fetch(`https://api.themoviedb.org/3/${mediaType}/${tmdbId}?api_key=${k}&append_to_response=external_ids`, { signal: AbortSignal.timeout(5000) }),
119
- season && mediaType === 'tv' ? fetch(`https://api.themoviedb.org/3/tv/${tmdbId}/season/${season}?api_key=${k}`, { signal: AbortSignal.timeout(5000) }) : Promise.resolve(null),
144
+ fetch(`https://api.themoviedb.org/3/${type}/${id}?api_key=${apiKey}&append_to_response=external_ids`, { signal: AbortSignal.timeout(5000) }),
145
+ s && type === 'tv' ? fetch(`https://api.themoviedb.org/3/tv/${id}/season/${s}?api_key=${apiKey}`, { signal: AbortSignal.timeout(5000) }) : Promise.resolve(null),
120
146
  ]);
121
147
  let mainData = null, seasonData = null;
122
148
  if (mainRes.ok) mainData = await mainRes.json(); else mainRes.body?.cancel();
@@ -125,10 +151,10 @@ export async function getTmdbInfo(tmdbApiKey, tmdbId, mediaType, season) {
125
151
  const isAnime = genres.some(g => g.id === 16) && ((mainData?.origin_country || []).includes('JP') || mainData?.original_language === 'ja');
126
152
  const titles = [];
127
153
  if (seasonData?.name) titles.push(seasonData.name);
128
- const t = mainData?.title || mainData?.name || '';
154
+ const titleText = mainData?.title || mainData?.name || '';
129
155
  const ot = mainData?.original_title || mainData?.original_name || '';
130
- if (t) titles.push(t);
131
- if (ot && ot !== t) titles.push(ot);
156
+ if (titleText) titles.push(titleText);
157
+ if (ot && ot !== titleText) titles.push(ot);
132
158
  const dateStr = seasonData?.air_date || mainData?.release_date || mainData?.first_air_date || '';
133
159
  const result = { isAnime, titles: [...new Set(titles.filter(Boolean))], year: dateStr ? parseInt(dateStr.slice(0, 4), 10) : null, imdbId: mainData?.imdb_id || mainData?.external_ids?.imdb_id || null };
134
160
  cacheSet(tmdbInfoCache, key, result, 600000);
@@ -178,16 +204,26 @@ export async function tmdbToAnilist(tmdbId, mediaType, season, titles = [], year
178
204
  }
179
205
 
180
206
  export async function imdbToTmdb(tmdbApiKey, imdbId, mediaType = 'movie') {
181
- const key = `imdb-${imdbId}`;
207
+ let apiKey = tmdbApiKey;
208
+ let id = imdbId;
209
+ let type = mediaType;
210
+ if (id === 'movie' || id === 'tv') {
211
+ type = id;
212
+ id = apiKey;
213
+ apiKey = process.env.TMDB_API_KEY;
214
+ }
215
+ if (!apiKey) {
216
+ apiKey = process.env.TMDB_API_KEY;
217
+ }
218
+ const key = `imdb-${id}`;
182
219
  const cached = cacheGet(tmdbInfoCache, key);
183
220
  if (cached !== undefined) return cached;
184
- const k = tmdbApiKey;
185
- if (!k) return null;
221
+ if (!apiKey) return null;
186
222
  try {
187
- const data = await fetchJson(`https://api.themoviedb.org/3/find/${imdbId}?api_key=${k}&external_source=imdb_id`, { signal: AbortSignal.timeout(5000) });
223
+ const data = await fetchJson(`https://api.themoviedb.org/3/find/${id}?api_key=${apiKey}&external_source=imdb_id`, { signal: AbortSignal.timeout(5000) });
188
224
  let result = null;
189
- if (mediaType === 'movie' && data.movie_results?.length) result = data.movie_results[0].id;
190
- else if (mediaType === 'tv' && data.tv_results?.length) result = data.tv_results[0].id;
225
+ if (type === 'movie' && data.movie_results?.length) result = data.movie_results[0].id;
226
+ else if (type === 'tv' && data.tv_results?.length) result = data.tv_results[0].id;
191
227
  if (result) cacheSet(tmdbInfoCache, key, result, 600000);
192
228
  return result;
193
229
  } catch { return null; }