@vyla-entertainment/sdk 1.10.0 → 1.12.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.
- package/package.json +2 -2
- package/src/config.js +212 -98
- package/src/downloads/downloads.js +51 -10
- package/src/sdk.d.ts +0 -1
- package/src/sdk.js +1 -1
- package/src/sources/111477.js +101 -0
- package/src/sources/4khdhub.js +324 -0
- package/src/sources/bcine.js +202 -0
- package/src/sources/cinesrc.js +212 -0
- package/src/sources/cinesu.js +148 -0
- package/src/sources/downloadeverything.js +338 -0
- package/src/sources/meowtv.js +42 -3
- package/src/sources/movienight.js +117 -0
- package/src/sources/movy.js +282 -0
- package/src/sources/pstream.js +68 -0
- package/src/sources/purstream.js +2 -2
- package/src/sources/streamaggregator.js +127 -0
- package/src/sources/vadapav.js +85 -0
- package/src/sources/vidvault.js +131 -57
- package/src/sources/vuflix.js +249 -0
- package/src/sources/xdownloader.js +129 -0
- package/src/subtitles.js +35 -40
- package/src/downloads/sources/trendimovies.js +0 -63
- package/src/sources/anipm.js +0 -70
- package/src/sources/mapple.js +0 -151
- package/src/sources/megasource.js +0 -47
- package/src/sources/nova.js +0 -46
- package/src/sources/videasy.js +0 -94
- package/src/sources/vidrift.js +0 -95
- package/src/sources/vidsync.js +0 -38
- package/src/sources/xpass.js +0 -40
|
@@ -0,0 +1,324 @@
|
|
|
1
|
+
import { getUA } from '../utils/helpers.js';
|
|
2
|
+
|
|
3
|
+
const INITIAL_BASE_URL = 'https://4khdhub.one';
|
|
4
|
+
let cachedBaseUrl = null;
|
|
5
|
+
let cachedBaseUrlExpiry = 0;
|
|
6
|
+
|
|
7
|
+
function rot13(str) {
|
|
8
|
+
return str.replace(/[a-zA-Z]/g, (c) => {
|
|
9
|
+
const code = c.charCodeAt(0);
|
|
10
|
+
if (code >= 65 && code <= 90) {
|
|
11
|
+
return String.fromCharCode(((code - 65 + 13) % 26) + 65);
|
|
12
|
+
}
|
|
13
|
+
return String.fromCharCode(((code - 97 + 13) % 26) + 97);
|
|
14
|
+
});
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function levenshtein(a, b) {
|
|
18
|
+
if (a === b) return 0;
|
|
19
|
+
if (!a.length) return b.length;
|
|
20
|
+
if (!b.length) return a.length;
|
|
21
|
+
const la = a.length;
|
|
22
|
+
const lb = b.length;
|
|
23
|
+
const d = Array.from({ length: la + 1 }, () => new Array(lb + 1).fill(0));
|
|
24
|
+
for (let i = 0; i <= la; i++) d[i][0] = i;
|
|
25
|
+
for (let j = 0; j <= lb; j++) d[0][j] = j;
|
|
26
|
+
for (let i = 1; i <= la; i++) {
|
|
27
|
+
for (let j = 1; j <= lb; j++) {
|
|
28
|
+
const cost = a[i - 1] === b[j - 1] ? 0 : 1;
|
|
29
|
+
d[i][j] = Math.min(d[i - 1][j] + 1, d[i][j - 1] + 1, d[i - 1][j - 1] + cost);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
return d[la][lb];
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
async function getBaseUrl(ua) {
|
|
36
|
+
if (cachedBaseUrl && Date.now() < cachedBaseUrlExpiry) {
|
|
37
|
+
return cachedBaseUrl;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
let currentUrl = INITIAL_BASE_URL;
|
|
41
|
+
for (let i = 0; i < 10; i++) {
|
|
42
|
+
try {
|
|
43
|
+
const res = await fetch(currentUrl, {
|
|
44
|
+
method: 'HEAD',
|
|
45
|
+
headers: { 'User-Agent': ua },
|
|
46
|
+
redirect: 'manual',
|
|
47
|
+
signal: AbortSignal.timeout(10000),
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
if (res.status >= 300 && res.status < 400) {
|
|
51
|
+
const location = res.headers.get('location');
|
|
52
|
+
if (location) {
|
|
53
|
+
currentUrl = new URL(location, currentUrl).toString();
|
|
54
|
+
continue;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
break;
|
|
58
|
+
} catch {
|
|
59
|
+
break;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const parsed = new URL(currentUrl);
|
|
64
|
+
cachedBaseUrl = `${parsed.protocol}//${parsed.host}`;
|
|
65
|
+
cachedBaseUrlExpiry = Date.now() + 3600000;
|
|
66
|
+
return cachedBaseUrl;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
async function resolveMetadata(sdk, id, isTv) {
|
|
70
|
+
const apiKey = sdk?.tmdbApiKey || process.env.TMDB_API_KEY;
|
|
71
|
+
if (!apiKey) return null;
|
|
72
|
+
const type = isTv ? 'tv' : 'movie';
|
|
73
|
+
try {
|
|
74
|
+
const res = await fetch(`https://api.themoviedb.org/3/${type}/${id}?api_key=${apiKey}`, {
|
|
75
|
+
signal: AbortSignal.timeout(8000),
|
|
76
|
+
});
|
|
77
|
+
if (!res.ok) return null;
|
|
78
|
+
const data = await res.json();
|
|
79
|
+
const title = isTv ? data.name : data.title;
|
|
80
|
+
const releaseDate = isTv ? data.first_air_date : data.release_date;
|
|
81
|
+
const year = releaseDate ? parseInt(releaseDate.split('-')[0], 10) : null;
|
|
82
|
+
return { title, year };
|
|
83
|
+
} catch {
|
|
84
|
+
return null;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function sanitizeStreamUrl(rawUrl) {
|
|
89
|
+
try {
|
|
90
|
+
const parsed = new URL(rawUrl);
|
|
91
|
+
parsed.pathname = parsed.pathname.replaceAll('::', '%3A%3A');
|
|
92
|
+
return parsed.toString();
|
|
93
|
+
} catch {
|
|
94
|
+
return rawUrl.replaceAll('::', '%3A%3A');
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
async function validateStreamUrl(url, ua) {
|
|
99
|
+
try {
|
|
100
|
+
const res = await fetch(url, {
|
|
101
|
+
headers: {
|
|
102
|
+
'User-Agent': ua,
|
|
103
|
+
Range: 'bytes=0-1024',
|
|
104
|
+
},
|
|
105
|
+
signal: AbortSignal.timeout(4000),
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
const contentType = (res.headers.get('content-type') || '').toLowerCase();
|
|
109
|
+
const isSuccess = (res.status >= 200 && res.status < 300) || res.status === 206;
|
|
110
|
+
const isHtmlOrJsonError = contentType.includes('text/html') || contentType.includes('application/json');
|
|
111
|
+
|
|
112
|
+
if (!isSuccess || isHtmlOrJsonError) return false;
|
|
113
|
+
return true;
|
|
114
|
+
} catch {
|
|
115
|
+
return true;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
async function resolveRedirectUrl(redirectUrl, ua) {
|
|
120
|
+
try {
|
|
121
|
+
const res = await fetch(redirectUrl, {
|
|
122
|
+
headers: { 'User-Agent': ua },
|
|
123
|
+
signal: AbortSignal.timeout(15000),
|
|
124
|
+
});
|
|
125
|
+
if (!res.ok) return null;
|
|
126
|
+
const html = await res.text();
|
|
127
|
+
|
|
128
|
+
let nextUrl = '';
|
|
129
|
+
const downloadMatch = html.match(/<a id="download" href="(.*?)"/);
|
|
130
|
+
if (downloadMatch) {
|
|
131
|
+
nextUrl = downloadMatch[1];
|
|
132
|
+
} else {
|
|
133
|
+
const varMatch = html.match(/var url = '(.*?)';/);
|
|
134
|
+
if (varMatch) {
|
|
135
|
+
nextUrl = varMatch[1];
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
if (!nextUrl) {
|
|
140
|
+
const match = html.match(/'o','(.*?)'/);
|
|
141
|
+
if (!match) return null;
|
|
142
|
+
const rawData = match[1];
|
|
143
|
+
const step1 = Buffer.from(rawData, 'base64').toString('utf8');
|
|
144
|
+
const step2 = Buffer.from(step1, 'base64').toString('utf8');
|
|
145
|
+
const step3 = rot13(step2);
|
|
146
|
+
const step4 = Buffer.from(step3, 'base64').toString('utf8');
|
|
147
|
+
const data = JSON.parse(step4);
|
|
148
|
+
return Buffer.from(data.o, 'base64').toString('utf8');
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
const interRes = await fetch(nextUrl, {
|
|
152
|
+
headers: { 'User-Agent': ua },
|
|
153
|
+
signal: AbortSignal.timeout(15000),
|
|
154
|
+
});
|
|
155
|
+
if (!interRes.ok) return null;
|
|
156
|
+
const interHtml = await interRes.text();
|
|
157
|
+
|
|
158
|
+
const finalMatch = interHtml.match(/<a href="([^"]+)"[^>]*class="[^"]*btn-success/);
|
|
159
|
+
if (finalMatch) {
|
|
160
|
+
return finalMatch[1];
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
return null;
|
|
164
|
+
} catch {
|
|
165
|
+
return null;
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
async function extractSourceResults(itemHtml, ua) {
|
|
170
|
+
const sizeMatch = itemHtml.match(/([\d.]+ ?[GM]B)/);
|
|
171
|
+
const sizeStr = sizeMatch ? sizeMatch[1] : null;
|
|
172
|
+
|
|
173
|
+
const heightMatch = itemHtml.match(/\d{3,}p/);
|
|
174
|
+
const heightStr = heightMatch ? heightMatch[0] : null;
|
|
175
|
+
|
|
176
|
+
const fileTitleMatch = itemHtml.match(/class="(?:file-title|episode-file-title)"[^>]*>([^<]+)/);
|
|
177
|
+
const fileTitle = fileTitleMatch ? fileTitleMatch[1].trim() : 'Unknown';
|
|
178
|
+
|
|
179
|
+
const displayParts = [fileTitle];
|
|
180
|
+
if (heightStr) displayParts.push(heightStr);
|
|
181
|
+
if (sizeStr) displayParts.push(sizeStr);
|
|
182
|
+
|
|
183
|
+
let hubCloudUrl = null;
|
|
184
|
+
let hubDriveUrl = null;
|
|
185
|
+
|
|
186
|
+
const linkRegex = /<a\s+[^>]*href="([^"]+)"[^>]*>([\s\S]*?)<\/a>/gi;
|
|
187
|
+
let match;
|
|
188
|
+
while ((match = linkRegex.exec(itemHtml)) !== null) {
|
|
189
|
+
const href = match[1];
|
|
190
|
+
const text = match[2];
|
|
191
|
+
if (text.includes('HubCloud') && !hubCloudUrl) {
|
|
192
|
+
hubCloudUrl = href;
|
|
193
|
+
} else if (text.includes('HubDrive') && !hubDriveUrl) {
|
|
194
|
+
hubDriveUrl = href;
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
const redirectUrl = hubCloudUrl || hubDriveUrl;
|
|
199
|
+
if (!redirectUrl) return null;
|
|
200
|
+
|
|
201
|
+
const resolvedUrl = await resolveRedirectUrl(redirectUrl, ua);
|
|
202
|
+
if (!resolvedUrl) return null;
|
|
203
|
+
|
|
204
|
+
const safeUrl = sanitizeStreamUrl(resolvedUrl);
|
|
205
|
+
const isValid = await validateStreamUrl(safeUrl, ua);
|
|
206
|
+
if (!isValid) return null;
|
|
207
|
+
|
|
208
|
+
return {
|
|
209
|
+
url: safeUrl,
|
|
210
|
+
label: displayParts.join(' · '),
|
|
211
|
+
headers: {
|
|
212
|
+
'User-Agent': ua,
|
|
213
|
+
},
|
|
214
|
+
};
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
export async function getStream({ id, s, e, sdk }) {
|
|
218
|
+
const isTv = Boolean(s && e);
|
|
219
|
+
const ua = getUA();
|
|
220
|
+
|
|
221
|
+
const meta = await resolveMetadata(sdk, id, isTv);
|
|
222
|
+
if (!meta?.title) return null;
|
|
223
|
+
|
|
224
|
+
const baseUrl = await getBaseUrl(ua);
|
|
225
|
+
const searchUrl = `${baseUrl}/?s=${encodeURIComponent(meta.title)}`;
|
|
226
|
+
|
|
227
|
+
const searchRes = await fetch(searchUrl, {
|
|
228
|
+
headers: { 'User-Agent': ua },
|
|
229
|
+
signal: AbortSignal.timeout(15000),
|
|
230
|
+
});
|
|
231
|
+
if (!searchRes.ok) return null;
|
|
232
|
+
const searchHtml = await searchRes.text();
|
|
233
|
+
|
|
234
|
+
const cardRegex = /<a\s+([^>]*\bclass="[^"]*movie-card[^"]*"[^>]*)>([\s\S]*?)<\/a>/gi;
|
|
235
|
+
const formatFilter = isTv ? 'Series' : 'Movies';
|
|
236
|
+
let targetPageUrl = null;
|
|
237
|
+
|
|
238
|
+
let cardMatch;
|
|
239
|
+
while ((cardMatch = cardRegex.exec(searchHtml)) !== null) {
|
|
240
|
+
const attributes = cardMatch[1];
|
|
241
|
+
const inner = cardMatch[2];
|
|
242
|
+
|
|
243
|
+
const hrefMatch = attributes.match(/href="([^"]+)"/);
|
|
244
|
+
if (!hrefMatch) continue;
|
|
245
|
+
const href = hrefMatch[1];
|
|
246
|
+
|
|
247
|
+
const formatMatches = [...inner.matchAll(/class="movie-card-format"[^>]*>([\s\S]*?)<\//gi)];
|
|
248
|
+
const formatTexts = formatMatches.map(m => m[1].trim());
|
|
249
|
+
if (!formatTexts.some(t => t.includes(formatFilter))) continue;
|
|
250
|
+
|
|
251
|
+
if (meta.year != null) {
|
|
252
|
+
const metaMatch = inner.match(/class="movie-card-meta"[^>]*>([\s\S]*?)<\//i);
|
|
253
|
+
const yearMatch = metaMatch ? metaMatch[1].match(/\d{4}/) : null;
|
|
254
|
+
if (yearMatch) {
|
|
255
|
+
const cardYear = parseInt(yearMatch[0], 10);
|
|
256
|
+
if (Math.abs(cardYear - meta.year) > 1) continue;
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
const titleMatch = inner.match(/class="movie-card-title"[^>]*>([\s\S]*?)<\//i);
|
|
261
|
+
let cardTitle = titleMatch ? titleMatch[1] : '';
|
|
262
|
+
cardTitle = cardTitle.replace(/\[.*?\]/g, '').trim();
|
|
263
|
+
|
|
264
|
+
const diff = levenshtein(cardTitle.toLowerCase(), meta.title.toLowerCase());
|
|
265
|
+
const isMatch = diff < 5 || (cardTitle.toLowerCase().includes(meta.title.toLowerCase()) && diff < 16);
|
|
266
|
+
if (!isMatch) continue;
|
|
267
|
+
|
|
268
|
+
targetPageUrl = new URL(href, baseUrl).toString();
|
|
269
|
+
break;
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
if (!targetPageUrl) return null;
|
|
273
|
+
|
|
274
|
+
const pageRes = await fetch(targetPageUrl, {
|
|
275
|
+
headers: { 'User-Agent': ua },
|
|
276
|
+
signal: AbortSignal.timeout(15000),
|
|
277
|
+
});
|
|
278
|
+
if (!pageRes.ok) return null;
|
|
279
|
+
const pageHtml = await pageRes.text();
|
|
280
|
+
|
|
281
|
+
const matchedHtmlBlocks = [];
|
|
282
|
+
|
|
283
|
+
if (isTv) {
|
|
284
|
+
const seasonStr = String(s).padStart(2, '0');
|
|
285
|
+
const episodeStr = String(e).padStart(2, '0');
|
|
286
|
+
|
|
287
|
+
const epItemRegex = /class="[^"]*episode-item[^"]*"[^>]*>([\s\S]*?)(?=(?:class="[^"]*episode-item"|<\/main>|$))/gi;
|
|
288
|
+
let epMatch;
|
|
289
|
+
while ((epMatch = epItemRegex.exec(pageHtml)) !== null) {
|
|
290
|
+
const epBlock = epMatch[1];
|
|
291
|
+
const titleMatch = epBlock.match(/class="episode-title"[^>]*>([\s\S]*?)<\//i);
|
|
292
|
+
const titleText = titleMatch ? titleMatch[1] : '';
|
|
293
|
+
if (!titleText.includes(`S${seasonStr}`)) continue;
|
|
294
|
+
|
|
295
|
+
const dlItemRegex = /class="[^"]*episode-download-item[^"]*"[^>]*>([\s\S]*?)(?=(?:class="[^"]*episode-download-item"|<\/div>\s*<\/div>|$))/gi;
|
|
296
|
+
let dlMatch;
|
|
297
|
+
while ((dlMatch = dlItemRegex.exec(epBlock)) !== null) {
|
|
298
|
+
const dlBlock = dlMatch[1];
|
|
299
|
+
if (dlBlock.includes(`Episode-${episodeStr}`)) {
|
|
300
|
+
matchedHtmlBlocks.push(dlBlock);
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
} else {
|
|
305
|
+
const dlItemRegex = /class="[^"]*download-item[^"]*"[^>]*>([\s\S]*?)(?=(?:class="[^"]*download-item"|<\/main>|$))/gi;
|
|
306
|
+
let dlMatch;
|
|
307
|
+
while ((dlMatch = dlItemRegex.exec(pageHtml)) !== null) {
|
|
308
|
+
matchedHtmlBlocks.push(dlMatch[1]);
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
if (matchedHtmlBlocks.length === 0) return null;
|
|
313
|
+
|
|
314
|
+
const results = await Promise.all(matchedHtmlBlocks.map((block) => extractSourceResults(block, ua)));
|
|
315
|
+
const allUrls = results.filter(Boolean);
|
|
316
|
+
|
|
317
|
+
if (allUrls.length === 0) return null;
|
|
318
|
+
|
|
319
|
+
return {
|
|
320
|
+
url: allUrls[0].url,
|
|
321
|
+
allUrls,
|
|
322
|
+
headers: allUrls[0].headers,
|
|
323
|
+
};
|
|
324
|
+
}
|
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
import { USER_AGENT } from '../utils/helpers.js';
|
|
2
|
+
|
|
3
|
+
const _sInit = [23, 27, 30, 31, 86, 28, 19, 86, 9, 18, 19, 21, 26, 27, 86, 120, 114, 120, 116, 86, 9, 18, 19, 21, 26, 27, 122, 89, 25, 23];
|
|
4
|
+
const _vParam = `_v=${(_sInit[15] ^ 0x5a)}${(_sInit[16] ^ 0x5a)}${(_sInit[17] ^ 0x5a)}${(_sInit[18] ^ 0x5a)}`;
|
|
5
|
+
|
|
6
|
+
const nD = '4860ac8bfddb';
|
|
7
|
+
const aD = '224eff10e662e9635c9f671cf46351dcd69af42b1edd56f5e5fa21751f44b9c8';
|
|
8
|
+
const Ls = [17, 91, 203, 44, 8, 177, 62, 239, 119, 3, 154, 81, 28, 210, 101, 7];
|
|
9
|
+
const wa = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_';
|
|
10
|
+
|
|
11
|
+
const SERVERS = [
|
|
12
|
+
{ id: 'NIGHT', endpoint: '/server/night' },
|
|
13
|
+
{ id: 'EMP', endpoint: '/server/emp' },
|
|
14
|
+
{ id: 'MAIN', endpoint: '/server/vidsrc' }
|
|
15
|
+
];
|
|
16
|
+
|
|
17
|
+
const CORS_PROXY = 'https://eve.pstream.cfd';
|
|
18
|
+
let cachedToken = null;
|
|
19
|
+
let tokenExpiry = 0;
|
|
20
|
+
|
|
21
|
+
function ab(e) {
|
|
22
|
+
let t = e >>> 0;
|
|
23
|
+
t ^= t >>> 16;
|
|
24
|
+
t = Math.imul(t, 2146121005) >>> 0;
|
|
25
|
+
t ^= t >>> 15;
|
|
26
|
+
t = Math.imul(t, 2221713035) >>> 0;
|
|
27
|
+
return (t ^ (t >>> 16)) >>> 0;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function sD(e) {
|
|
31
|
+
const t = new TextEncoder().encode(aD);
|
|
32
|
+
const r = Math.max(32, Math.min(128, e + 17));
|
|
33
|
+
const n = new Uint8Array(r);
|
|
34
|
+
let a = 2166136261;
|
|
35
|
+
for (let s = 0; s < r; s += 1) {
|
|
36
|
+
a ^= t[s % t.length] ?? s;
|
|
37
|
+
a = ab((a + Ls[s % Ls.length] + ((2654435761 * s) >>> 0)) >>> 0);
|
|
38
|
+
n[s] = a & 255;
|
|
39
|
+
}
|
|
40
|
+
return n;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function iD(e) {
|
|
44
|
+
let t = '';
|
|
45
|
+
for (let r = 0; r < e.length; r += 3) {
|
|
46
|
+
const n = e[r];
|
|
47
|
+
const a = e[r + 1];
|
|
48
|
+
const s = e[r + 2];
|
|
49
|
+
t += wa[n >>> 2];
|
|
50
|
+
t += wa[((3 & n) << 4) | ((a ?? 0) >>> 4)];
|
|
51
|
+
if (a === undefined) break;
|
|
52
|
+
t += wa[((15 & a) << 2) | ((s ?? 0) >>> 6)];
|
|
53
|
+
if (s === undefined) break;
|
|
54
|
+
t += wa[63 & s];
|
|
55
|
+
}
|
|
56
|
+
return t;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function generateDirectHlsUrl(id, s, e) {
|
|
60
|
+
const isTv = s != null && e != null;
|
|
61
|
+
const tmdbId = Math.floor(Number(id));
|
|
62
|
+
const season = isTv ? Math.floor(Number(s || 1)) : 0;
|
|
63
|
+
const episode = isTv ? Math.floor(Number(e || 1)) : 0;
|
|
64
|
+
|
|
65
|
+
const str = `${nD}:${isTv ? 's' : 'm'}:${tmdbId}:${season}:${episode}`;
|
|
66
|
+
const a = new TextEncoder().encode(str);
|
|
67
|
+
const sArr = sD(a.length);
|
|
68
|
+
const i = new Uint8Array(a.length + 2);
|
|
69
|
+
i[0] = a.length & 255;
|
|
70
|
+
i[1] = (a.length >>> 8) & 255;
|
|
71
|
+
let o = (2654435769 ^ a.length) >>> 0;
|
|
72
|
+
for (let l = 0; l < a.length; l += 1) {
|
|
73
|
+
o = ab((o + sArr[l % sArr.length] + Ls[l % Ls.length] + l) >>> 0);
|
|
74
|
+
i[l + 2] = (a[l] ^ (255 & o)) ^ sArr[(7 * l + 3) % sArr.length];
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
return `https://glendale-plumbing.com/c/v1/${iD(i)}/master.m3u8`;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
async function fetchInternalToken() {
|
|
81
|
+
if (cachedToken && Date.now() < tokenExpiry) return cachedToken;
|
|
82
|
+
try {
|
|
83
|
+
const res = await fetch('https://1embed.cc/api/token', {
|
|
84
|
+
headers: {
|
|
85
|
+
'User-Agent': USER_AGENT,
|
|
86
|
+
'Referer': 'https://1embed.cc/',
|
|
87
|
+
'Accept': 'application/json, text/plain, */*'
|
|
88
|
+
},
|
|
89
|
+
signal: AbortSignal.timeout(4000)
|
|
90
|
+
});
|
|
91
|
+
if (res.ok) {
|
|
92
|
+
const data = await res.json();
|
|
93
|
+
if (data?.token) {
|
|
94
|
+
cachedToken = data.token;
|
|
95
|
+
tokenExpiry = Date.now() + 1800 * 1000;
|
|
96
|
+
return cachedToken;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
} catch { }
|
|
100
|
+
|
|
101
|
+
try {
|
|
102
|
+
const proxiedUrl = `${CORS_PROXY}/?destination=${encodeURIComponent('https://1embed.cc/api/token')}`;
|
|
103
|
+
const res = await fetch(proxiedUrl, {
|
|
104
|
+
headers: {
|
|
105
|
+
'X-Referer': 'https://1embed.cc/',
|
|
106
|
+
'X-User-Agent': USER_AGENT
|
|
107
|
+
},
|
|
108
|
+
signal: AbortSignal.timeout(4000)
|
|
109
|
+
});
|
|
110
|
+
if (res.ok) {
|
|
111
|
+
const data = await res.json();
|
|
112
|
+
if (data?.token) {
|
|
113
|
+
cachedToken = data.token;
|
|
114
|
+
tokenExpiry = Date.now() + 1800 * 1000;
|
|
115
|
+
return cachedToken;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
} catch { }
|
|
119
|
+
|
|
120
|
+
return '';
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
export async function getStream(args) {
|
|
124
|
+
const { id, s, e, clientIP } = args;
|
|
125
|
+
const isTv = s != null && e != null;
|
|
126
|
+
const isMovie = !isTv;
|
|
127
|
+
const allUrls = [];
|
|
128
|
+
|
|
129
|
+
const directHlsUrl = generateDirectHlsUrl(id, s, e);
|
|
130
|
+
const masterUrlWithMark = `${directHlsUrl}?${_vParam}`;
|
|
131
|
+
|
|
132
|
+
allUrls.push({
|
|
133
|
+
url: masterUrlWithMark,
|
|
134
|
+
server: 'Bcine (Direct Master)',
|
|
135
|
+
quality: 'Auto',
|
|
136
|
+
type: 'hls',
|
|
137
|
+
headers: {
|
|
138
|
+
'User-Agent': USER_AGENT,
|
|
139
|
+
'Referer': 'https://bcine.ru/',
|
|
140
|
+
'Origin': 'https://bcine.ru',
|
|
141
|
+
...(clientIP && { 'X-Forwarded-For': clientIP })
|
|
142
|
+
}
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
const token = await fetchInternalToken();
|
|
146
|
+
|
|
147
|
+
await Promise.allSettled(
|
|
148
|
+
SERVERS.map(async (server) => {
|
|
149
|
+
const query = isTv
|
|
150
|
+
? `id=${id}?type=tv&s=${s || 1}&e=${e || 1}`
|
|
151
|
+
: `id=${id}?type=movie`;
|
|
152
|
+
|
|
153
|
+
const stParam = token ? `&_st=${token}` : '';
|
|
154
|
+
const targetUrl = `https://1embed.cc${server.endpoint}/${query}${stParam}`;
|
|
155
|
+
|
|
156
|
+
const headers = {
|
|
157
|
+
'User-Agent': USER_AGENT,
|
|
158
|
+
'Referer': 'https://bcine.ru/',
|
|
159
|
+
'Accept': 'application/json, text/plain, */*',
|
|
160
|
+
...(token && { 'X-Stream-Token': token }),
|
|
161
|
+
...(clientIP && { 'X-Forwarded-For': clientIP })
|
|
162
|
+
};
|
|
163
|
+
|
|
164
|
+
let res = await fetch(targetUrl, { headers, signal: AbortSignal.timeout(5000) }).catch(() => null);
|
|
165
|
+
if (!res || !res.ok) {
|
|
166
|
+
const proxiedUrl = `${CORS_PROXY}/?destination=${encodeURIComponent(targetUrl)}`;
|
|
167
|
+
res = await fetch(proxiedUrl, {
|
|
168
|
+
headers: {
|
|
169
|
+
'X-Referer': 'https://bcine.ru/',
|
|
170
|
+
'X-User-Agent': USER_AGENT
|
|
171
|
+
},
|
|
172
|
+
signal: AbortSignal.timeout(5000)
|
|
173
|
+
}).catch(() => null);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
if (res && res.ok) {
|
|
177
|
+
try {
|
|
178
|
+
const data = await res.json();
|
|
179
|
+
if (data && data.success !== false) {
|
|
180
|
+
const streamUrl = data.streams?.proxy_m3u8 || data.streams?.raw_m3u8 || data.streams?.m3u8 || data.streamUrl;
|
|
181
|
+
if (streamUrl && typeof streamUrl === 'string') {
|
|
182
|
+
const urlWithMark = streamUrl.includes('?') ? `${streamUrl}&${_vParam}` : `${streamUrl}?${_vParam}`;
|
|
183
|
+
allUrls.push({
|
|
184
|
+
url: urlWithMark,
|
|
185
|
+
server: `Bcine (${data.selectedSource || data.provider || server.id})`,
|
|
186
|
+
quality: 'Auto',
|
|
187
|
+
type: streamUrl.includes('.m3u8') ? 'hls' : 'mp4',
|
|
188
|
+
headers: {
|
|
189
|
+
'User-Agent': USER_AGENT,
|
|
190
|
+
'Referer': 'https://bcine.ru/',
|
|
191
|
+
'Origin': 'https://bcine.ru'
|
|
192
|
+
}
|
|
193
|
+
});
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
} catch { }
|
|
197
|
+
}
|
|
198
|
+
})
|
|
199
|
+
);
|
|
200
|
+
|
|
201
|
+
return allUrls.length ? { allUrls } : null;
|
|
202
|
+
}
|