@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.
- package/README.md +1 -3
- package/package.json +1 -1
- package/src/config.js +26 -47
- package/src/sdk.d.ts +1 -1
- package/src/sources/1embed.js +34 -11
- package/src/sources/anihq.js +14 -2
- package/src/sources/anineko.js +122 -15
- package/src/sources/flaxmovies.js +21 -9
- package/src/sources/fsonic.js +2 -2
- package/src/sources/fsonline.js +2 -2
- package/src/sources/lmscript.js +43 -0
- package/src/sources/videasy.js +6 -12
- package/src/sources/vidvault.js +114 -0
- package/src/sources/vidzee.js +73 -87
- package/src/sources/vixsrc.js +28 -14
- package/src/utils/helpers.js +56 -20
- package/src/sources/goated.js +0 -262
- package/src/sources/nebula.js +0 -62
package/README.md
CHANGED
|
@@ -13,8 +13,6 @@ pnpm install @vyla-entertainment/sdk
|
|
|
13
13
|
|
|
14
14
|
View our full documentation at https://docs.vyla.cc/sdk
|
|
15
15
|
|
|
16
|
-
```
|
|
17
|
-
|
|
18
16
|
# [DMCA](https://docs.vyla.cc/misc/dmca)
|
|
19
17
|
|
|
20
18
|
If your source is listed here, I sincerely apologize. I understand that its inclusion may be costing you a lot of money, and that was never my intention. Please contact me as soon as possible, and I'll remove it ASAP.
|
|
@@ -30,4 +28,4 @@ Original work and core authorship:
|
|
|
30
28
|
[@GavinGoGaming](https://github.com/GavinGoGaming)
|
|
31
29
|
|
|
32
30
|
Full credits and acknowledgements:
|
|
33
|
-
https://docs.vyla.cc/misc/credit
|
|
31
|
+
https://docs.vyla.cc/misc/credit
|
package/package.json
CHANGED
package/src/config.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// Currently active sources reported on
|
|
1
|
+
// Currently active sources reported on 9:22:41 PM PST 7/30/2026
|
|
2
2
|
|
|
3
3
|
// None Anime Tested: 936075
|
|
4
4
|
// Anime Tested: 37854
|
|
@@ -135,7 +135,7 @@ export const SOURCES = [
|
|
|
135
135
|
jitter: 500,
|
|
136
136
|
retries: 2,
|
|
137
137
|
multiUrl: true,
|
|
138
|
-
|
|
138
|
+
skipProxy: true,
|
|
139
139
|
cdnHeaders: [{
|
|
140
140
|
pattern: /flix2watch\.pro/i,
|
|
141
141
|
headers: {
|
|
@@ -161,18 +161,6 @@ export const SOURCES = [
|
|
|
161
161
|
},
|
|
162
162
|
},
|
|
163
163
|
|
|
164
|
-
{
|
|
165
|
-
key: 'goated',
|
|
166
|
-
label: 'Goated',
|
|
167
|
-
sourceFile: 'goated',
|
|
168
|
-
proxyParam: 'gt',
|
|
169
|
-
timeout: 25000,
|
|
170
|
-
jitter: 500,
|
|
171
|
-
retries: 2,
|
|
172
|
-
multiUrl: true,
|
|
173
|
-
skipProxy: true,
|
|
174
|
-
},
|
|
175
|
-
|
|
176
164
|
{
|
|
177
165
|
key: 'fsonic',
|
|
178
166
|
label: 'Fsonic',
|
|
@@ -216,6 +204,17 @@ export const SOURCES = [
|
|
|
216
204
|
retries: 1,
|
|
217
205
|
},
|
|
218
206
|
|
|
207
|
+
{
|
|
208
|
+
key: 'lmscript',
|
|
209
|
+
label: 'LMScript',
|
|
210
|
+
sourceFile: 'lmscript',
|
|
211
|
+
proxyParam: 'lmsc',
|
|
212
|
+
timeout: 20000,
|
|
213
|
+
jitter: 500,
|
|
214
|
+
retries: 2,
|
|
215
|
+
multiUrl: true,
|
|
216
|
+
},
|
|
217
|
+
|
|
219
218
|
{
|
|
220
219
|
key: 'lookmovie',
|
|
221
220
|
label: 'LookMovie',
|
|
@@ -252,38 +251,6 @@ export const SOURCES = [
|
|
|
252
251
|
skipProxy: true
|
|
253
252
|
},
|
|
254
253
|
|
|
255
|
-
{
|
|
256
|
-
key: 'nebula',
|
|
257
|
-
label: 'Nebula',
|
|
258
|
-
sourceFile: 'nebula',
|
|
259
|
-
proxyParam: 'nb',
|
|
260
|
-
timeout: 25000,
|
|
261
|
-
jitter: 500,
|
|
262
|
-
retries: 1,
|
|
263
|
-
multiUrl: true,
|
|
264
|
-
skipProxy: true,
|
|
265
|
-
},
|
|
266
|
-
|
|
267
|
-
{
|
|
268
|
-
key: 'nekowatch-sub',
|
|
269
|
-
sourceFile: 'nekowatch',
|
|
270
|
-
label: 'NekoWatch (Sub)',
|
|
271
|
-
proxyParam: 'nwsub',
|
|
272
|
-
timeout: 30000,
|
|
273
|
-
jitter: 500,
|
|
274
|
-
retries: 2
|
|
275
|
-
},
|
|
276
|
-
|
|
277
|
-
{
|
|
278
|
-
key: 'nekowatch-dub',
|
|
279
|
-
sourceFile: 'nekowatch',
|
|
280
|
-
label: 'NekoWatch (Dub)',
|
|
281
|
-
proxyParam: 'nwdub',
|
|
282
|
-
timeout: 30000,
|
|
283
|
-
jitter: 500,
|
|
284
|
-
retries: 2
|
|
285
|
-
},
|
|
286
|
-
|
|
287
254
|
{
|
|
288
255
|
key: 'opstream',
|
|
289
256
|
label: 'OpStream',
|
|
@@ -332,6 +299,7 @@ export const SOURCES = [
|
|
|
332
299
|
timeout: 20000,
|
|
333
300
|
jitter: 500,
|
|
334
301
|
retries: 2,
|
|
302
|
+
skipProxy: true
|
|
335
303
|
},
|
|
336
304
|
|
|
337
305
|
{
|
|
@@ -485,6 +453,17 @@ export const SOURCES = [
|
|
|
485
453
|
multiUrl: true
|
|
486
454
|
},
|
|
487
455
|
|
|
456
|
+
{
|
|
457
|
+
key: 'vidvault',
|
|
458
|
+
label: 'VidVault',
|
|
459
|
+
sourceFile: 'vidvault',
|
|
460
|
+
proxyParam: 'vv',
|
|
461
|
+
timeout: 25000,
|
|
462
|
+
jitter: 500,
|
|
463
|
+
retries: 2,
|
|
464
|
+
multiUrl: true,
|
|
465
|
+
},
|
|
466
|
+
|
|
488
467
|
{
|
|
489
468
|
key: 'vidzee',
|
|
490
469
|
label: 'VidZee',
|
|
@@ -494,13 +473,13 @@ export const SOURCES = [
|
|
|
494
473
|
sourcesTimeout: 10000,
|
|
495
474
|
jitter: 400,
|
|
496
475
|
retries: 3,
|
|
497
|
-
disabled: true, // Temporarily disabled because their site is down, all streams are returning 403's
|
|
498
476
|
verifyHeaders: {
|
|
499
477
|
Accept: '/',
|
|
500
478
|
'Accept-Language': 'en-US,en;q=0.9',
|
|
501
479
|
Referer: 'https://player.vidzee.wtf',
|
|
502
480
|
Origin: 'https://player.vidzee.wtf',
|
|
503
481
|
},
|
|
482
|
+
disabled: true // Disabled temporarily due to decryption issues
|
|
504
483
|
},
|
|
505
484
|
|
|
506
485
|
{
|
package/src/sdk.d.ts
CHANGED
|
@@ -95,7 +95,7 @@ export default class VylaSDK {
|
|
|
95
95
|
s?: number | null,
|
|
96
96
|
e?: number | null,
|
|
97
97
|
clientIP?: string | null
|
|
98
|
-
): Promise<{allUrls: StreamResult[]}>;
|
|
98
|
+
): Promise<StreamResult | { allUrls: StreamResult[] }>;
|
|
99
99
|
|
|
100
100
|
/**
|
|
101
101
|
* Get subtitles for a movie or TV episode.
|
package/src/sources/1embed.js
CHANGED
|
@@ -3,13 +3,11 @@ import { USER_AGENT } from '../utils/helpers.js';
|
|
|
3
3
|
const BASE_URL = "https://1embed.cc";
|
|
4
4
|
|
|
5
5
|
const PROVIDERS = [
|
|
6
|
-
{ id: "
|
|
7
|
-
{ id: "
|
|
8
|
-
{ id: "
|
|
9
|
-
{ id: "
|
|
10
|
-
{ id: "
|
|
11
|
-
{ id: "czo", name: "1Embed - Cinezo" },
|
|
12
|
-
{ id: "fas", name: "1Embed - VidFast" }
|
|
6
|
+
{ id: "BUKE", name: "1Embed - BUKE" },
|
|
7
|
+
{ id: "YOTA", name: "1Embed - YOTA" },
|
|
8
|
+
{ id: "NEPT", name: "1Embed - NEPT" },
|
|
9
|
+
{ id: "AZURE", name: "1Embed - AZURE" },
|
|
10
|
+
{ id: "YORU", name: "1Embed - YORU" }
|
|
13
11
|
];
|
|
14
12
|
|
|
15
13
|
let cachedToken = null;
|
|
@@ -28,6 +26,30 @@ async function getToken() {
|
|
|
28
26
|
} catch { return null; }
|
|
29
27
|
}
|
|
30
28
|
|
|
29
|
+
function decodeBsUrl(url) {
|
|
30
|
+
if (!url) return url;
|
|
31
|
+
let target = url;
|
|
32
|
+
if (url.includes('d=')) {
|
|
33
|
+
try {
|
|
34
|
+
const parts = url.split(/[?&]/);
|
|
35
|
+
for (const part of parts) {
|
|
36
|
+
if (part.startsWith('d=')) {
|
|
37
|
+
target = decodeURIComponent(part.substring(2));
|
|
38
|
+
break;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
} catch { }
|
|
42
|
+
}
|
|
43
|
+
if (target.startsWith('bs_')) {
|
|
44
|
+
try {
|
|
45
|
+
const decoded = Buffer.from(target.slice(3), 'base64').toString('utf-8');
|
|
46
|
+
const reversed = decoded.split('').reverse().join('');
|
|
47
|
+
if (reversed.startsWith('http')) return reversed;
|
|
48
|
+
} catch { }
|
|
49
|
+
}
|
|
50
|
+
return url;
|
|
51
|
+
}
|
|
52
|
+
|
|
31
53
|
async function fetchProviderStream(provider, type, id, s, e, token) {
|
|
32
54
|
try {
|
|
33
55
|
const referer = type === "movie" ? `${BASE_URL}/embed/movie/${id}` : `${BASE_URL}/embed/tv/${id}/${s}/${e}`;
|
|
@@ -51,11 +73,12 @@ async function fetchProviderStream(provider, type, id, s, e, token) {
|
|
|
51
73
|
const results = [];
|
|
52
74
|
for (const src of rawSources) {
|
|
53
75
|
if (typeof src !== 'object' || !src) continue;
|
|
54
|
-
let
|
|
55
|
-
if (typeof
|
|
76
|
+
let rawUrl = src.url || src.file || src.link;
|
|
77
|
+
if (typeof rawUrl !== 'string') continue;
|
|
78
|
+
let streamUrl = decodeBsUrl(rawUrl);
|
|
56
79
|
if (streamUrl.startsWith('/')) streamUrl = `${BASE_URL}${streamUrl}`;
|
|
57
80
|
const isWorker = streamUrl.includes('omena-puu') || streamUrl.includes('nocach') || streamUrl.includes('?p=');
|
|
58
|
-
const isHls = src.type === 'hls' || streamUrl.includes('m3u8') || isWorker;
|
|
81
|
+
const isHls = src.type === 'hls' || src.type === 'm3u8' || streamUrl.includes('m3u8') || isWorker;
|
|
59
82
|
if (isHls && !streamUrl.includes('m3u8')) streamUrl += streamUrl.includes('?') ? '&format=.m3u8' : '?format=.m3u8';
|
|
60
83
|
const srcHeaders = (src.headers && typeof src.headers === 'object') ? src.headers : {};
|
|
61
84
|
results.push({
|
|
@@ -80,7 +103,7 @@ export async function getStream(args) {
|
|
|
80
103
|
let targets = PROVIDERS;
|
|
81
104
|
if (server && server !== 'all') {
|
|
82
105
|
const cleanName = server.replace('1Embed - ', '').toLowerCase();
|
|
83
|
-
targets = PROVIDERS.filter(p => p.id === cleanName || p.name.toLowerCase().includes(cleanName));
|
|
106
|
+
targets = PROVIDERS.filter(p => p.id.toLowerCase() === cleanName || p.name.toLowerCase().includes(cleanName));
|
|
84
107
|
if (!targets.length) targets = PROVIDERS;
|
|
85
108
|
}
|
|
86
109
|
const settled = await Promise.allSettled(targets.map(p => fetchProviderStream(p, type, id, s, e, token)));
|
package/src/sources/anihq.js
CHANGED
|
@@ -2,15 +2,27 @@ import { getTmdbInfo, USER_AGENT } from '../utils/helpers.js';
|
|
|
2
2
|
|
|
3
3
|
async function extractVoe(voeUrl) {
|
|
4
4
|
try {
|
|
5
|
-
|
|
5
|
+
let res = await fetch(voeUrl, {
|
|
6
6
|
headers: {
|
|
7
7
|
'User-Agent': USER_AGENT,
|
|
8
8
|
'Referer': 'https://anihq.cc/'
|
|
9
9
|
}
|
|
10
10
|
});
|
|
11
11
|
|
|
12
|
+
let html = await res.text();
|
|
13
|
+
|
|
14
|
+
const redirectMatch = html.match(/window\.location\.href\s*=\s*['"](https?:\/\/[^'"]+)['"]/);
|
|
15
|
+
if (redirectMatch) {
|
|
16
|
+
res = await fetch(redirectMatch[1], {
|
|
17
|
+
headers: {
|
|
18
|
+
'User-Agent': USER_AGENT,
|
|
19
|
+
'Referer': 'https://anihq.cc/'
|
|
20
|
+
}
|
|
21
|
+
});
|
|
22
|
+
html = await res.text();
|
|
23
|
+
}
|
|
24
|
+
|
|
12
25
|
const mirrorUrl = res.url;
|
|
13
|
-
const html = await res.text();
|
|
14
26
|
|
|
15
27
|
const scriptMatch = html.match(/<script type="application\/json">\["(.*?)"\]<\/script>/);
|
|
16
28
|
if (!scriptMatch) return null;
|
package/src/sources/anineko.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { getTmdbInfo } from '../utils/helpers.js';
|
|
1
|
+
import { getTmdbInfo, USER_AGENT, fetchText } from '../utils/helpers.js';
|
|
2
2
|
|
|
3
3
|
const BASE_URL = "https://anineko.to";
|
|
4
4
|
|
|
@@ -10,7 +10,10 @@ function decodeEntities(str) {
|
|
|
10
10
|
|
|
11
11
|
async function search(query) {
|
|
12
12
|
try {
|
|
13
|
-
const html = await fetchText(`${BASE_URL}/browser?keyword=${encodeURIComponent(query)}`, {
|
|
13
|
+
const html = await fetchText(`${BASE_URL}/browser?keyword=${encodeURIComponent(query)}`, {
|
|
14
|
+
headers: { 'User-Agent': USER_AGENT },
|
|
15
|
+
signal: AbortSignal.timeout(6000)
|
|
16
|
+
});
|
|
14
17
|
const results = [];
|
|
15
18
|
let idx = 0;
|
|
16
19
|
while ((idx = html.indexOf('nv-anime-thumb', idx)) !== -1) {
|
|
@@ -27,40 +30,144 @@ async function search(query) {
|
|
|
27
30
|
}
|
|
28
31
|
idx += 14;
|
|
29
32
|
}
|
|
33
|
+
if (results.length === 0) {
|
|
34
|
+
const linkMatches = [...html.matchAll(/href=["']\/watch\/([^"']+)["']/gi)];
|
|
35
|
+
for (const match of linkMatches) {
|
|
36
|
+
if (!results.find(r => r.slug === match[1])) {
|
|
37
|
+
const linkIdx = html.indexOf(match[0]);
|
|
38
|
+
const titleStart = html.lastIndexOf('>', linkIdx);
|
|
39
|
+
const titleEnd = html.indexOf('<', linkIdx);
|
|
40
|
+
if (titleStart !== -1 && titleEnd !== -1 && titleEnd > linkIdx) {
|
|
41
|
+
const title = html.slice(titleStart + 1, Math.min(titleEnd, linkIdx + 200)).trim();
|
|
42
|
+
if (title.length > 2 && title.length < 100) {
|
|
43
|
+
results.push({ slug: match[1], text: title });
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
30
49
|
return results;
|
|
31
50
|
} catch { return []; }
|
|
32
51
|
}
|
|
33
52
|
|
|
34
53
|
async function extractHls(embedUrl) {
|
|
35
54
|
try {
|
|
36
|
-
const html = await fetchText(embedUrl, {
|
|
37
|
-
|
|
55
|
+
const html = await fetchText(embedUrl, {
|
|
56
|
+
headers: {
|
|
57
|
+
'User-Agent': USER_AGENT,
|
|
58
|
+
Referer: `${BASE_URL}/`
|
|
59
|
+
},
|
|
60
|
+
signal: AbortSignal.timeout(6000)
|
|
61
|
+
});
|
|
62
|
+
const m = html.match(/const\s+src\s*=\s*["'](https?:\/\/[^"']+\.m3u8[^"']*)["']/i) ||
|
|
63
|
+
html.match(/file\s*:\s*["'](https?:\/\/[^"']+\.m3u8[^"']*)["']/i) ||
|
|
64
|
+
html.match(/["'](https?:\/\/[^"']+\/master\.m3u8[^"']*)["']/i) ||
|
|
65
|
+
html.match(/["'](https?:\/\/[^"']+\.m3u8[^"']*)["']/i) ||
|
|
66
|
+
html.match(/src:\s*["'](https?:\/\/[^"']+\.m3u8[^"']*)["']/i);
|
|
38
67
|
if (m) return decodeEntities(m[1]);
|
|
39
68
|
} catch { }
|
|
40
69
|
return null;
|
|
41
70
|
}
|
|
42
71
|
|
|
43
|
-
export async function getStream({ id, s, e, audio }) {
|
|
72
|
+
export async function getStream({ id, s, e, audio, tmdbApiKey }) {
|
|
44
73
|
try {
|
|
45
|
-
const info = await getTmdbInfo(id, s ? 'tv' : 'movie', s);
|
|
74
|
+
const info = await getTmdbInfo(tmdbApiKey, id, s ? 'tv' : 'movie', s);
|
|
46
75
|
if (!info || !info.isAnime) return null;
|
|
47
76
|
let seriesSlug = null;
|
|
48
77
|
for (const title of info.titles) {
|
|
49
|
-
const
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
78
|
+
const potentialSlug = title.toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-+|-+$/g, '');
|
|
79
|
+
if (!potentialSlug) continue;
|
|
80
|
+
try {
|
|
81
|
+
const checkHtml = await fetchText(`${BASE_URL}/watch/${potentialSlug}/ep-${e || 1}`, {
|
|
82
|
+
headers: { 'User-Agent': USER_AGENT, Referer: `${BASE_URL}/watch/${potentialSlug}` },
|
|
83
|
+
signal: AbortSignal.timeout(4000)
|
|
84
|
+
});
|
|
85
|
+
if (checkHtml && checkHtml.includes('nv-watch-page')) {
|
|
86
|
+
seriesSlug = potentialSlug;
|
|
87
|
+
break;
|
|
88
|
+
}
|
|
89
|
+
} catch {}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
if (!seriesSlug) {
|
|
93
|
+
for (const title of info.titles) {
|
|
94
|
+
const results = await search(title);
|
|
95
|
+
const targetClean = cleanTitle(title);
|
|
96
|
+
|
|
97
|
+
const exactSlugMatch = results.find(r => {
|
|
98
|
+
const slugClean = cleanTitle(r.slug);
|
|
99
|
+
return slugClean === targetClean || r.slug === targetClean;
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
if (exactSlugMatch) {
|
|
103
|
+
seriesSlug = exactSlugMatch.slug;
|
|
104
|
+
break;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
const sortedResults = [...results].sort((a, b) => a.slug.length - b.slug.length);
|
|
108
|
+
|
|
109
|
+
for (const r of sortedResults) {
|
|
110
|
+
const resultClean = cleanTitle(r.text);
|
|
111
|
+
const isExactMatch = resultClean === targetClean;
|
|
112
|
+
const isCloseMatch = r.text.toLowerCase().includes(title.toLowerCase());
|
|
113
|
+
if (isExactMatch || isCloseMatch) {
|
|
114
|
+
seriesSlug = r.slug;
|
|
115
|
+
break;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
if (seriesSlug) break;
|
|
119
|
+
}
|
|
53
120
|
}
|
|
54
121
|
if (!seriesSlug && info.titles.length) {
|
|
55
122
|
const fSearch = await search(info.titles[0]);
|
|
56
|
-
|
|
123
|
+
const sortedResults = [...fSearch].sort((a, b) => a.slug.length - b.slug.length);
|
|
124
|
+
if (sortedResults.length) seriesSlug = sortedResults[0].slug;
|
|
57
125
|
}
|
|
58
126
|
if (!seriesSlug) return null;
|
|
59
|
-
const watchHtml = await fetchText(`${BASE_URL}/watch/${seriesSlug}/ep-${e || 1}`, {
|
|
127
|
+
const watchHtml = await fetchText(`${BASE_URL}/watch/${seriesSlug}/ep-${e || 1}`, {
|
|
128
|
+
headers: {
|
|
129
|
+
'User-Agent': USER_AGENT,
|
|
130
|
+
Referer: `${BASE_URL}/watch/${seriesSlug}`
|
|
131
|
+
},
|
|
132
|
+
signal: AbortSignal.timeout(6000)
|
|
133
|
+
});
|
|
60
134
|
const byAudio = { sub: [], dub: [] };
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
135
|
+
|
|
136
|
+
const iframeMatch = watchHtml.match(/<iframe[^>]*src=["']([^"']+)["'][^>]*>/i);
|
|
137
|
+
if (iframeMatch) {
|
|
138
|
+
const iframeSrc = decodeEntities(iframeMatch[1]);
|
|
139
|
+
const hls = await extractHls(iframeSrc);
|
|
140
|
+
if (hls) {
|
|
141
|
+
const audioType = audio === 'dub' ? 'dub' : 'sub';
|
|
142
|
+
byAudio[audioType].push(iframeSrc);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
if (byAudio.sub.length === 0 && byAudio.dub.length === 0) {
|
|
147
|
+
const tabMatches = [...watchHtml.matchAll(/<button[^>]*class=["'][^"']*nv-server-tab[^"']*["'][^>]*data-id=["']([^"']+)["'][^>]*>/gi)];
|
|
148
|
+
const tabMap = {};
|
|
149
|
+
for (const match of tabMatches) {
|
|
150
|
+
const tabId = match[1].toLowerCase();
|
|
151
|
+
const tabClassMatch = match[0].match(/class=["'][^"']*(tab_\d+)[^"']/i);
|
|
152
|
+
if (tabClassMatch) {
|
|
153
|
+
const tabClass = tabClassMatch[1];
|
|
154
|
+
tabMap[tabClass] = tabId.includes('dub') ? 'dub' : 'sub';
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
const btnRegex = /<button\s+[^>]*class=["'][^"']*server-video[^"']*["'][^>]*>/gi;
|
|
159
|
+
const btnMatches = [...watchHtml.matchAll(btnRegex)];
|
|
160
|
+
for (const match of btnMatches) {
|
|
161
|
+
const btnHtml = match[0];
|
|
162
|
+
const videoMatch = btnHtml.match(/data-video=["']([^"']+)["']/i);
|
|
163
|
+
const tabMatch = btnHtml.match(/data-tab=["']([^"']+)["']/i);
|
|
164
|
+
if (videoMatch && tabMatch) {
|
|
165
|
+
const videoUrl = decodeEntities(videoMatch[1]);
|
|
166
|
+
const tabClass = tabMatch[1];
|
|
167
|
+
const audioType = tabMap[tabClass] || 'sub';
|
|
168
|
+
byAudio[audioType].push(videoUrl);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
64
171
|
}
|
|
65
172
|
const audiosToTry = audio === "all" ? ["sub", "dub"] : (audio === "dub" ? ["dub", "sub"] : ["sub", "dub"]);
|
|
66
173
|
const allUrls = [];
|
|
@@ -1,14 +1,26 @@
|
|
|
1
1
|
import { fetchJson } from '../utils/helpers.js';
|
|
2
2
|
|
|
3
|
-
const API_BASE = 'https://itjiocunahckqxcnzpoy.supabase.co/functions/v1';
|
|
4
|
-
const APIKEY = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6Iml0amlvY3VuYWhja3F4Y256cG95Iiwicm9sZSI6ImFub24iLCJpYXQiOjE3NzczMzM1MjQsImV4cCI6MjA5MjkwOTUyNH0.9x9ykdHAzrv_GSvnawPeQaOxQeh3sZg0QAh4u9VOF4M';
|
|
5
|
-
const HEADERS = { 'apikey': APIKEY, 'authorization': `Bearer ${APIKEY}`, 'content-type': 'application/json', 'origin': 'https://flaxmovies.xyz', 'referer': 'https://flaxmovies.xyz/' };
|
|
6
|
-
|
|
7
3
|
export async function getStream({ id, s, e }) {
|
|
8
4
|
try {
|
|
9
|
-
const
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
5
|
+
const url = s && e
|
|
6
|
+
? `https://sparkling-mode-a1ed.fillatame.workers.dev/?tmdb_id=${id}&season=${s}&episode=${e}`
|
|
7
|
+
: `https://sparkling-mode-a1ed.fillatame.workers.dev/?tmdb_id=${id}`;
|
|
8
|
+
const data = await fetchJson(url, { signal: AbortSignal.timeout(15000) });
|
|
9
|
+
if (!data || !Array.isArray(data.streams)) return null;
|
|
10
|
+
const allUrls = [];
|
|
11
|
+
for (const stream of data.streams) {
|
|
12
|
+
const streamUrl = stream.url || stream.link;
|
|
13
|
+
if (!streamUrl) continue;
|
|
14
|
+
const isHls = stream.format === 'HLS' || stream.isM3U8 === true || streamUrl.includes('m3u8');
|
|
15
|
+
allUrls.push({
|
|
16
|
+
url: streamUrl,
|
|
17
|
+
server: stream.provider || 'FlaxMovies',
|
|
18
|
+
quality: stream.resolution || 'Auto',
|
|
19
|
+
type: isHls ? 'hls' : 'mp4'
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
return allUrls.length ? { allUrls } : null;
|
|
23
|
+
} catch {
|
|
24
|
+
return null;
|
|
25
|
+
}
|
|
14
26
|
}
|
package/src/sources/fsonic.js
CHANGED
|
@@ -4,10 +4,10 @@ const BASE_URL = 'https://www.fsonic.net';
|
|
|
4
4
|
const FSHARE_BASE = 'https://fsharetv.co';
|
|
5
5
|
const HEADERS = { 'User-Agent': USER_AGENT, 'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8', 'Accept-Language': 'en-US,en;q=0.9' };
|
|
6
6
|
|
|
7
|
-
export async function getStream({ id, s }) {
|
|
7
|
+
export async function getStream({ id, s, tmdbApiKey }) {
|
|
8
8
|
if (s != null) return null;
|
|
9
9
|
try {
|
|
10
|
-
const info = await getTmdbInfo(id, 'movie');
|
|
10
|
+
const info = await getTmdbInfo(tmdbApiKey, id, 'movie');
|
|
11
11
|
if (!info?.titles?.length) return null;
|
|
12
12
|
let watchSlug = null;
|
|
13
13
|
for (const title of info.titles) {
|
package/src/sources/fsonline.js
CHANGED
|
@@ -4,10 +4,10 @@ const ORIGIN = 'https://www3.fsonline.app';
|
|
|
4
4
|
const AJAX_URL = `${ORIGIN}/wp-admin/admin-ajax.php`;
|
|
5
5
|
const HEADERS = { 'User-Agent': USER_AGENT, 'Origin': ORIGIN, 'Referer': `${ORIGIN}/` };
|
|
6
6
|
|
|
7
|
-
export async function getStream({ id, s, e }) {
|
|
7
|
+
export async function getStream({ id, s, e, tmdbApiKey }) {
|
|
8
8
|
try {
|
|
9
9
|
const isTv = s != null && e != null;
|
|
10
|
-
const info = await getTmdbInfo(id, isTv ? 'tv' : 'movie');
|
|
10
|
+
const info = await getTmdbInfo(tmdbApiKey, id, isTv ? 'tv' : 'movie');
|
|
11
11
|
if (!info?.titles?.length) return null;
|
|
12
12
|
const searchHtml = await fetchText(`${ORIGIN}/?s=${encodeURIComponent(info.titles[0] + (info.year ? ' ' + info.year : ''))}`, { headers: HEADERS });
|
|
13
13
|
const typeFolder = isTv ? 'seriale' : 'film';
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { getTmdbInfo } from '../utils/helpers.js';
|
|
2
|
+
|
|
3
|
+
export async function getStream(args) {
|
|
4
|
+
const { id, s, e, tmdbApiKey } = args;
|
|
5
|
+
const type = (s != null && e != null) ? 'tv' : 'movie';
|
|
6
|
+
|
|
7
|
+
try {
|
|
8
|
+
const tmdbInfo = await getTmdbInfo(tmdbApiKey, id, type, s);
|
|
9
|
+
const name = tmdbInfo.titles[0] || '';
|
|
10
|
+
if (!name) return null;
|
|
11
|
+
|
|
12
|
+
if (type === 'tv') {
|
|
13
|
+
return null;
|
|
14
|
+
} else {
|
|
15
|
+
const url = `https://lmscript.xyz/v1/movies?filters[q]=${encodeURIComponent(name)}&expand=streams`;
|
|
16
|
+
const response = await fetch(url);
|
|
17
|
+
const data = await response.json();
|
|
18
|
+
|
|
19
|
+
const movie = data.items?.find(item => `${item.tmdb_prefix}` === `${id}` || `${item.tmdb_id}` === `${id}`);
|
|
20
|
+
if (!movie) return null;
|
|
21
|
+
|
|
22
|
+
const streams = movie.streams || {};
|
|
23
|
+
const SOURCE_ORDER = ["1080p", "720p", "480p", "360p"];
|
|
24
|
+
const sortedStreams = Object.keys(streams).sort((a, b) => SOURCE_ORDER.indexOf(a) - SOURCE_ORDER.indexOf(b));
|
|
25
|
+
|
|
26
|
+
if (!sortedStreams.length) return null;
|
|
27
|
+
|
|
28
|
+
const allUrls = sortedStreams.map(quality => {
|
|
29
|
+
const streamUrl = streams[quality];
|
|
30
|
+
return {
|
|
31
|
+
url: streamUrl,
|
|
32
|
+
quality,
|
|
33
|
+
type: streamUrl.includes('.m3u8') ? 'hls' : 'mp4',
|
|
34
|
+
headers: {}
|
|
35
|
+
};
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
return { allUrls };
|
|
39
|
+
}
|
|
40
|
+
} catch (err) {
|
|
41
|
+
return null;
|
|
42
|
+
}
|
|
43
|
+
}
|
package/src/sources/videasy.js
CHANGED
|
@@ -1,28 +1,24 @@
|
|
|
1
1
|
import { getTmdbInfo, fetchJson, fetchText, USER_AGENT } from '../utils/helpers.js';
|
|
2
2
|
|
|
3
|
-
const
|
|
3
|
+
const SPEEDRACE_BASE = "https://api.speedracelight.com";
|
|
4
4
|
const HEADERS = { "Accept": "*/*", "Origin": "https://player.videasy.to", "Referer": "https://player.videasy.to/", "User-Agent": USER_AGENT };
|
|
5
5
|
const SERVERS = [
|
|
6
|
-
{ id: 'jett', name: 'Jett' },
|
|
7
6
|
{ id: 'cdn', name: 'Yoru' },
|
|
8
|
-
{ id: 'tejo', name: 'Tejo' },
|
|
9
|
-
{ id: 'neon2', name: 'Neon' },
|
|
10
|
-
{ id: 'ym', name: 'Sage' },
|
|
11
|
-
{ id: 'downloader2', name: 'Cypher' },
|
|
12
7
|
{ id: 'm4uhd', name: 'Breach' },
|
|
8
|
+
{ id: 'vsrc', name: 'Neon' },
|
|
13
9
|
{ id: 'hdmovie', name: 'Vyse' },
|
|
14
10
|
{ id: 'meine', name: 'Killjoy' },
|
|
15
11
|
{ id: 'lamovie', name: 'Omen' },
|
|
16
12
|
{ id: 'superflix', name: 'Raze' }
|
|
17
13
|
];
|
|
18
14
|
|
|
19
|
-
export async function getStream({ id, s, e, server }) {
|
|
15
|
+
export async function getStream({ id, s, e, server, tmdbApiKey }) {
|
|
20
16
|
try {
|
|
21
17
|
const isTv = s != null && e != null;
|
|
22
|
-
const info = await getTmdbInfo(id, isTv ? 'tv' : 'movie');
|
|
18
|
+
const info = await getTmdbInfo(tmdbApiKey, id, isTv ? 'tv' : 'movie');
|
|
23
19
|
if (!info?.titles?.length) return null;
|
|
24
20
|
|
|
25
|
-
const seedData = await fetchJson(`${
|
|
21
|
+
const seedData = await fetchJson(`${SPEEDRACE_BASE}/seed?mediaId=${id}`, { headers: HEADERS, signal: AbortSignal.timeout(5000) });
|
|
26
22
|
const seed = seedData?.seed;
|
|
27
23
|
if (!seed) return null;
|
|
28
24
|
|
|
@@ -36,9 +32,7 @@ export async function getStream({ id, s, e, server }) {
|
|
|
36
32
|
}
|
|
37
33
|
|
|
38
34
|
const settled = await Promise.allSettled(targets.map(async srv => {
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
let url = `${WINGS_BASE}/${srv.id}/sources-with-title?title=${encTitle}&mediaType=${isTv ? 'tv' : 'movie'}&year=${info.year || ''}&tmdbId=${id}&imdbId=${info.imdbId || "tt0000000"}&enc=2&seed=${seed}`;
|
|
35
|
+
let url = `${SPEEDRACE_BASE}/${srv.id}/sources-with-title?title=${encTitle}&mediaType=${isTv ? 'tv' : 'movie'}&year=${info.year || ''}&tmdbId=${id}&imdbId=${info.imdbId || "tt0000000"}&enc=2&seed=${seed}`;
|
|
42
36
|
if (isTv) url += `&episodeId=${e}&seasonId=${s}`;
|
|
43
37
|
|
|
44
38
|
const encText = await fetchText(url, { headers: HEADERS, signal: AbortSignal.timeout(10000) });
|