@vyla-entertainment/sdk 1.20.0 → 1.21.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 (52) hide show
  1. package/package.json +3 -2
  2. package/src/config.js +10 -85
  3. package/src/sdk.d.ts +94 -121
  4. package/src/sdk.js +82 -78
  5. package/src/sources/111477.js +88 -100
  6. package/src/sources/123anime.js +120 -56
  7. package/src/sources/3eyedraven.js +82 -135
  8. package/src/sources/cinecat.js +37 -18
  9. package/src/sources/cinemaarmy.js +179 -180
  10. package/src/sources/cinesrc.js +401 -397
  11. package/src/sources/cno.js +101 -94
  12. package/src/sources/dulo.js +134 -149
  13. package/src/sources/flaxmovies.js +114 -105
  14. package/src/sources/frost.js +38 -29
  15. package/src/sources/fsonline.js +95 -47
  16. package/src/sources/hdhub.js +43 -36
  17. package/src/sources/hexa.js +60 -53
  18. package/src/sources/hyper.js +42 -35
  19. package/src/sources/kickassanime.js +117 -124
  20. package/src/sources/kisskh.js +114 -59
  21. package/src/sources/lmscript.js +72 -70
  22. package/src/sources/lookmovie.js +80 -79
  23. package/src/sources/movenerds.js +74 -124
  24. package/src/sources/movy.js +197 -209
  25. package/src/sources/net79.js +59 -55
  26. package/src/sources/peestream.js +126 -138
  27. package/src/sources/pengu.js +46 -49
  28. package/src/sources/pstream.js +73 -67
  29. package/src/sources/purstream.js +96 -104
  30. package/src/sources/rivestream.js +182 -176
  31. package/src/sources/rivo.js +58 -53
  32. package/src/sources/streamaggregator.js +114 -128
  33. package/src/sources/thugtv.js +70 -62
  34. package/src/sources/vidapi.js +122 -114
  35. package/src/sources/vidcore.js +14 -122
  36. package/src/sources/vidfast.js +14 -122
  37. package/src/sources/vidlink.js +78 -27
  38. package/src/sources/vidup.js +14 -122
  39. package/src/sources/vidvault.js +133 -187
  40. package/src/sources/vidy.js +192 -187
  41. package/src/sources/vidzee.js +189 -183
  42. package/src/sources/vixsrc.js +123 -113
  43. package/src/sources/watchout.js +60 -46
  44. package/src/sources/xdownloader.js +127 -128
  45. package/src/sources/yapgrid.js +68 -62
  46. package/src/sources/yastream.js +35 -29
  47. package/src/subtitles.js +105 -122
  48. package/src/utils/helpers.js +586 -247
  49. package/src/sources/bcine.js +0 -202
  50. package/src/sources/cinezo.js +0 -55
  51. package/src/sources/vidora.js +0 -62
  52. package/src/sources/vidrock.js +0 -145
@@ -1,106 +1,115 @@
1
- import { fetchJson } from '../utils/helpers.js';
2
-
3
- export async function getStream({ id, s, e }) {
4
- const isTv = s != null && e != null;
5
- const allUrls = [];
6
-
7
- const endpoints = [
8
- async () => {
9
- const url = isTv
10
- ? `https://themoviebox.uk/api/resolve?type=tv&id=${id}&season=${s}&episode=${e}`
11
- : `https://themoviebox.uk/api/resolve?type=movie&id=${id}`;
12
- const res = await fetchJson(url);
13
- if (res?.success && Array.isArray(res.sources)) {
14
- for (const item of res.sources) {
15
- const streamUrl = item.url || item.original_url;
16
- if (streamUrl) {
17
- allUrls.push({
18
- url: streamUrl,
19
- server: `TheMovieBox - ${item.server || item.provider || 'Server'}`,
20
- headers: {
21
- Referer: 'https://themoviebox.uk/',
22
- Origin: 'https://themoviebox.uk'
23
- }
24
- });
25
- }
26
- }
27
- }
28
- },
29
-
30
- async () => {
31
- const url = isTv
32
- ? `https://pro2.nabilekson.workers.dev/tv/${id}/${s}/${e}`
33
- : `https://pro2.nabilekson.workers.dev/movie/${id}`;
34
- const res = await fetchJson(url);
35
- if (res?.success && Array.isArray(res.primarySources)) {
36
- for (const item of res.primarySources) {
37
- const streamUrl = item.url || item.originalUrl;
38
- if (streamUrl) {
39
- allUrls.push({
40
- url: streamUrl,
41
- server: `CineBros - ${item.name || item.quality || 'Server'}`,
42
- headers: item.headers || {
43
- Referer: 'https://pro2.nabilekson.workers.dev/',
44
- Origin: 'https://pro2.nabilekson.workers.dev'
45
- }
46
- });
47
- }
48
- }
49
- }
50
- },
51
-
52
- async () => {
53
- const url = isTv
54
- ? `https://praxi.nabilekson.workers.dev/tv/${id}/${s}/${e}`
55
- : `https://praxi.nabilekson.workers.dev/movie/${id}`;
56
- const res = await fetchJson(url);
57
- if (res?.success && Array.isArray(res.primarySources)) {
58
- for (const item of res.primarySources) {
59
- const streamUrl = item.url || item.originalUrl;
60
- if (streamUrl) {
61
- allUrls.push({
62
- url: streamUrl,
63
- server: `Praxi - ${item.name || item.providerName || 'Server'}`,
64
- headers: item.headers || {
65
- Referer: 'https://praxi.nabilekson.workers.dev/',
66
- Origin: 'https://praxi.nabilekson.workers.dev'
67
- }
68
- });
69
- }
70
- }
71
- }
72
- },
73
-
74
- async () => {
75
- const url = isTv
76
- ? `https://freakyniki.elaxo.lol/?tmdb_id=${id}&season=${s}&episode=${e}`
77
- : `https://freakyniki.elaxo.lol/?tmdb_id=${id}`;
78
- const res = await fetchJson(url);
79
- if (Array.isArray(res?.streams)) {
80
- for (const item of res.streams) {
81
- const streamUrl = item.url || item.raw_url;
82
- if (streamUrl) {
83
- allUrls.push({
84
- url: streamUrl,
85
- server: `FreakyNiki - ${item.server || item.provider || 'Server'}`,
86
- headers: {
87
- Referer: 'https://freakyniki.elaxo.lol/',
88
- Origin: 'https://freakyniki.elaxo.lol'
89
- }
90
- });
91
- }
92
- }
93
- }
94
- }
95
- ];
96
-
97
- await Promise.allSettled(endpoints.map(fn => fn().catch(() => { })));
98
-
99
- if (!allUrls.length) return null;
100
-
101
- return {
102
- url: allUrls[0].url,
103
- headers: allUrls[0].headers,
104
- allUrls
105
- };
1
+ import { fetchJson } from '../utils/helpers.js';
2
+
3
+ export async function getStream(args) {
4
+ const { id, s, e } = args || {};
5
+ const isTv = s != null && e != null;
6
+ const allUrls = [];
7
+
8
+ const endpoints = [
9
+ async () => {
10
+ const url = isTv
11
+ ? `https://themoviebox.uk/api/resolve?type=tv&id=${id}&season=${s}&episode=${e}`
12
+ : `https://themoviebox.uk/api/resolve?type=movie&id=${id}`;
13
+ const res = await fetchJson(url);
14
+ if (res?.success && Array.isArray(res.sources)) {
15
+ for (const item of res.sources) {
16
+ const streamUrl = item.url || item.original_url;
17
+ if (streamUrl) {
18
+ allUrls.push({
19
+ url: streamUrl,
20
+ server: `TheMovieBox - ${item.server || item.provider || 'Server'}`,
21
+ quality: 'Auto',
22
+ type: streamUrl.includes('.m3u8') ? 'hls' : 'mp4',
23
+ headers: {
24
+ Referer: 'https://themoviebox.uk/',
25
+ Origin: 'https://themoviebox.uk',
26
+ },
27
+ });
28
+ }
29
+ }
30
+ }
31
+ },
32
+
33
+ async () => {
34
+ const url = isTv
35
+ ? `https://pro2.nabilekson.workers.dev/tv/${id}/${s}/${e}`
36
+ : `https://pro2.nabilekson.workers.dev/movie/${id}`;
37
+ const res = await fetchJson(url);
38
+ if (res?.success && Array.isArray(res.primarySources)) {
39
+ for (const item of res.primarySources) {
40
+ const streamUrl = item.url || item.originalUrl;
41
+ if (streamUrl) {
42
+ allUrls.push({
43
+ url: streamUrl,
44
+ server: `CineBros - ${item.name || item.quality || 'Server'}`,
45
+ quality: item.quality || 'Auto',
46
+ type: streamUrl.includes('.m3u8') ? 'hls' : 'mp4',
47
+ headers: item.headers || {
48
+ Referer: 'https://pro2.nabilekson.workers.dev/',
49
+ Origin: 'https://pro2.nabilekson.workers.dev',
50
+ },
51
+ });
52
+ }
53
+ }
54
+ }
55
+ },
56
+
57
+ async () => {
58
+ const url = isTv
59
+ ? `https://praxi.nabilekson.workers.dev/tv/${id}/${s}/${e}`
60
+ : `https://praxi.nabilekson.workers.dev/movie/${id}`;
61
+ const res = await fetchJson(url);
62
+ if (res?.success && Array.isArray(res.primarySources)) {
63
+ for (const item of res.primarySources) {
64
+ const streamUrl = item.url || item.originalUrl;
65
+ if (streamUrl) {
66
+ allUrls.push({
67
+ url: streamUrl,
68
+ server: `Praxi - ${item.name || item.providerName || 'Server'}`,
69
+ quality: 'Auto',
70
+ type: streamUrl.includes('.m3u8') ? 'hls' : 'mp4',
71
+ headers: item.headers || {
72
+ Referer: 'https://praxi.nabilekson.workers.dev/',
73
+ Origin: 'https://praxi.nabilekson.workers.dev',
74
+ },
75
+ });
76
+ }
77
+ }
78
+ }
79
+ },
80
+
81
+ async () => {
82
+ const url = isTv
83
+ ? `https://freakyniki.elaxo.lol/?tmdb_id=${id}&season=${s}&episode=${e}`
84
+ : `https://freakyniki.elaxo.lol/?tmdb_id=${id}`;
85
+ const res = await fetchJson(url);
86
+ if (Array.isArray(res?.streams)) {
87
+ for (const item of res.streams) {
88
+ const streamUrl = item.url || item.raw_url;
89
+ if (streamUrl) {
90
+ allUrls.push({
91
+ url: streamUrl,
92
+ server: `FreakyNiki - ${item.server || item.provider || 'Server'}`,
93
+ quality: 'Auto',
94
+ type: streamUrl.includes('.m3u8') ? 'hls' : 'mp4',
95
+ headers: {
96
+ Referer: 'https://freakyniki.elaxo.lol/',
97
+ Origin: 'https://freakyniki.elaxo.lol',
98
+ },
99
+ });
100
+ }
101
+ }
102
+ }
103
+ },
104
+ ];
105
+
106
+ await Promise.allSettled(endpoints.map(fn => fn().catch(() => { })));
107
+
108
+ if (!allUrls.length) return null;
109
+
110
+ return {
111
+ url: allUrls[0].url,
112
+ headers: allUrls[0].headers,
113
+ allUrls,
114
+ };
106
115
  }
@@ -1,30 +1,39 @@
1
- import { fetchJson, resolveImdbId } from '../utils/helpers.js';
2
-
3
- export async function getStream({ id, s, e, tmdbApiKey, sdk }) {
4
- const isTv = s != null && e != null;
5
- const type = isTv ? 'series' : 'movie';
6
- const imdb = await resolveImdbId(id, isTv, tmdbApiKey || sdk?.tmdbApiKey);
7
- if (!imdb) return null;
8
-
9
- const url = isTv
10
- ? `https://froststream.cloutteam.com/stream/series/${imdb}:${s}:${e}.json`
11
- : `https://froststream.cloutteam.com/stream/movie/${imdb}.json`;
12
-
13
- const res = await fetchJson(url).catch(() => null);
14
- if (!res || !Array.isArray(res.streams) || !res.streams.length) return null;
15
-
16
- const allUrls = res.streams.filter(st => st.url).map(st => ({
17
- url: st.url,
18
- server: `Frost - ${st.name || 'Server'} ${st.title ? `- ${st.title}` : ''}`,
19
- headers: st.behaviorHints?.proxyHeaders?.request || undefined,
20
- skipProxy: st.behaviorHints?.notWebReady === false
21
- }));
22
-
23
- if (!allUrls.length) return null;
24
-
25
- return {
26
- url: allUrls[0].url,
27
- headers: allUrls[0].headers,
28
- allUrls
29
- };
1
+ import { fetchJson, resolveImdbId, resolveTmdbKey } from '../utils/helpers.js';
2
+
3
+ const BASE_URL = 'https://froststream.cloutteam.com';
4
+
5
+ export async function getStream(args) {
6
+ const { id, s, e, sdk } = args || {};
7
+ const isTv = s != null && e != null;
8
+ const type = isTv ? 'series' : 'movie';
9
+ const apiKey = resolveTmdbKey(args, sdk);
10
+
11
+ const imdb = await resolveImdbId(id, isTv, apiKey);
12
+ if (!imdb) return null;
13
+
14
+ const url = isTv
15
+ ? `${BASE_URL}/stream/series/${imdb}:${s}:${e}.json`
16
+ : `${BASE_URL}/stream/movie/${imdb}.json`;
17
+
18
+ const res = await fetchJson(url).catch(() => null);
19
+ if (!res || !Array.isArray(res.streams) || !res.streams.length) return null;
20
+
21
+ const allUrls = res.streams
22
+ .filter(st => st.url)
23
+ .map(st => ({
24
+ url: st.url,
25
+ server: `Frost - ${st.name || 'Server'}${st.title ? ` - ${st.title}` : ''}`,
26
+ quality: 'Auto',
27
+ type: st.url.includes('.m3u8') ? 'hls' : 'mp4',
28
+ headers: st.behaviorHints?.proxyHeaders?.request || undefined,
29
+ skipProxy: st.behaviorHints?.notWebReady === false,
30
+ }));
31
+
32
+ if (!allUrls.length) return null;
33
+
34
+ return {
35
+ url: allUrls[0].url,
36
+ headers: allUrls[0].headers,
37
+ allUrls,
38
+ };
30
39
  }
@@ -1,48 +1,96 @@
1
- import { fetchText, USER_AGENT, getTmdbInfo } from '../utils/helpers.js';
2
-
3
- const ORIGIN = 'https://www3.fsonline.app';
4
- const AJAX_URL = `${ORIGIN}/wp-admin/admin-ajax.php`;
5
- const HEADERS = { 'User-Agent': USER_AGENT, 'Origin': ORIGIN, 'Referer': `${ORIGIN}/` };
6
-
7
- export async function getStream({ id, s, e, sdk }) {
8
- const tmdbApiKey = sdk?.tmdbApiKey || null;
9
- if (!tmdbApiKey) return null;
10
- try {
11
- const isTv = s != null && e != null;
12
- const info = await getTmdbInfo(tmdbApiKey, id, isTv ? 'tv' : 'movie');
13
- if (!info?.titles?.length) return null;
14
- const searchHtml = await fetchText(`${ORIGIN}/?s=${encodeURIComponent(info.titles[0] + (info.year ? ' ' + info.year : ''))}`, { headers: HEADERS });
15
- const typeFolder = isTv ? 'seriale' : 'film';
16
- const linkMatch = searchHtml.match(new RegExp(`href=["'](https?://www3\\.fsonline\\.app/${typeFolder}/([^"'/]+)/)["']`, 'i'));
17
- if (!linkMatch) return null;
18
- const targetPageUrl = isTv ? `${ORIGIN}/episoade/${linkMatch[2].replace(/-\d{4}$/, '')}-sezonul-${s}-episodul-${e}/` : linkMatch[1];
19
- const pageHtml = await fetchText(targetPageUrl, { headers: HEADERS });
20
- const movieId = (pageHtml.match(/movie-id=['"]([^'"]+)['"]/) || pageHtml.match(/movie-id=([^ >]+)/))?.[1];
21
- if (!movieId) return null;
22
- const ajaxHtml = await fetchText(AJAX_URL, { method: 'POST', headers: { ...HEADERS, 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8', 'X-Requested-With': 'XMLHttpRequest', 'Referer': targetPageUrl }, body: `action=lazy_player&movieID=${movieId}` });
23
- const results = [];
24
- let idx = 0;
25
- while ((idx = ajaxHtml.indexOf('data-vs="', idx)) !== -1) {
26
- const embedStart = idx + 9;
27
- const embedEnd = ajaxHtml.indexOf('"', embedStart);
28
- const embedUrl = ajaxHtml.slice(embedStart, embedEnd);
29
- const spanStart = ajaxHtml.indexOf('<span>', embedEnd);
30
- const spanEnd = ajaxHtml.indexOf('</span>', spanStart);
31
- const serverLabel = ajaxHtml.slice(spanStart + 6, spanEnd).trim().toLowerCase();
32
- if (serverLabel.includes('filesun')) {
33
- try {
34
- const rHtml = await fetchText(embedUrl, { headers: { 'Referer': ORIGIN, 'User-Agent': USER_AGENT } });
35
- const m3u8Match = rHtml.match(/file:\s*["'](https?:\/\/[^"']+\.m3u8[^"']*)["']/) || rHtml.match(/["']?file["']?:\s*["'](https?:\/\/[^"']+)["']/);
36
- if (m3u8Match) results.push({ url: m3u8Match[1].replace(/\\\//g, '/'), server: 'FSOnline - FileSuN', headers: { 'Referer': embedUrl, 'Origin': 'https://player.fsonline.app', 'User-Agent': USER_AGENT } });
37
- } catch { }
38
- }
39
- idx = spanEnd;
40
- }
41
- return results.length ? { allUrls: results } : null;
42
- } catch { return null; }
43
- }
44
-
45
- export async function getSources(args) {
46
- const res = await getStream(args);
47
- return res?.allUrls ? [...new Set(res.allUrls.map(u => u.server))] : [];
1
+ import { fetchText, getTmdbMetadata, resolveTmdbKey, USER_AGENT } from '../utils/helpers.js';
2
+
3
+ const ORIGIN = 'https://www3.fsonline.app';
4
+ const AJAX_URL = `${ORIGIN}/wp-admin/admin-ajax.php`;
5
+ const HEADERS = {
6
+ 'User-Agent': USER_AGENT,
7
+ Origin: ORIGIN,
8
+ Referer: `${ORIGIN}/`,
9
+ };
10
+
11
+ export async function getStream(args) {
12
+ const { id, s, e, sdk } = args || {};
13
+ const isTv = s != null && e != null;
14
+ const apiKey = resolveTmdbKey(args, sdk);
15
+ if (!apiKey) return null;
16
+
17
+ try {
18
+ const meta = await getTmdbMetadata(id, isTv, apiKey);
19
+ if (!meta?.titles?.length) return null;
20
+
21
+ const query = meta.titles[0] + (meta.year ? ` ${meta.year}` : '');
22
+ const searchHtml = await fetchText(`${ORIGIN}/?s=${encodeURIComponent(query)}`, { headers: HEADERS });
23
+ const typeFolder = isTv ? 'seriale' : 'film';
24
+ const linkMatch = searchHtml.match(new RegExp(`href=["'](https?://www3\\.fsonline\\.app/${typeFolder}/([^"'/]+)/)["']`, 'i'));
25
+ if (!linkMatch) return null;
26
+
27
+ const targetPageUrl = isTv
28
+ ? `${ORIGIN}/episoade/${linkMatch[2].replace(/-\d{4}$/, '')}-sezonul-${s}-episodul-${e}/`
29
+ : linkMatch[1];
30
+
31
+ const pageHtml = await fetchText(targetPageUrl, { headers: HEADERS });
32
+ const movieId = (pageHtml.match(/movie-id=['"]([^'"]+)['"]/) || pageHtml.match(/movie-id=([^ >]+)/))?.[1];
33
+ if (!movieId) return null;
34
+
35
+ const ajaxHtml = await fetchText(AJAX_URL, {
36
+ method: 'POST',
37
+ headers: {
38
+ ...HEADERS,
39
+ 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',
40
+ 'X-Requested-With': 'XMLHttpRequest',
41
+ Referer: targetPageUrl,
42
+ },
43
+ body: `action=lazy_player&movieID=${movieId}`,
44
+ });
45
+
46
+ const allUrls = [];
47
+ let idx = 0;
48
+ while ((idx = ajaxHtml.indexOf('data-vs="', idx)) !== -1) {
49
+ const embedStart = idx + 9;
50
+ const embedEnd = ajaxHtml.indexOf('"', embedStart);
51
+ const embedUrl = ajaxHtml.slice(embedStart, embedEnd);
52
+ const spanStart = ajaxHtml.indexOf('<span>', embedEnd);
53
+ const spanEnd = ajaxHtml.indexOf('</span>', spanStart);
54
+ const serverLabel = ajaxHtml.slice(spanStart + 6, spanEnd).trim().toLowerCase();
55
+
56
+ if (serverLabel.includes('filesun')) {
57
+ try {
58
+ const rHtml = await fetchText(embedUrl, {
59
+ headers: { Referer: ORIGIN, 'User-Agent': USER_AGENT },
60
+ });
61
+ const m3u8Match = rHtml.match(/file:\s*["'](https?:\/\/[^"']+\.m3u8[^"']*)["']/) || rHtml.match(/["']?file["']?:\s*["'](https?:\/\/[^"']+)["']/);
62
+ if (m3u8Match) {
63
+ const streamUrl = m3u8Match[1].replace(/\\\//g, '/');
64
+ allUrls.push({
65
+ url: streamUrl,
66
+ server: 'FSOnline - FileSuN',
67
+ quality: 'Auto',
68
+ type: 'hls',
69
+ headers: {
70
+ Referer: embedUrl,
71
+ Origin: 'https://player.fsonline.app',
72
+ 'User-Agent': USER_AGENT,
73
+ },
74
+ });
75
+ }
76
+ } catch { }
77
+ }
78
+ idx = spanEnd;
79
+ }
80
+
81
+ if (!allUrls.length) return null;
82
+
83
+ return {
84
+ url: allUrls[0].url,
85
+ headers: allUrls[0].headers,
86
+ allUrls,
87
+ };
88
+ } catch {
89
+ return null;
90
+ }
91
+ }
92
+
93
+ export async function getSources(args) {
94
+ const stream = await getStream(args);
95
+ return stream?.allUrls ? [...new Set(stream.allUrls.map(u => u.server))] : [];
48
96
  }
@@ -1,36 +1,43 @@
1
- import { fetchJson, resolveImdbId } from '../utils/helpers.js';
2
-
3
- export async function getStream({ id, s, e, tmdbApiKey, sdk }) {
4
- const isTv = s != null && e != null;
5
- const type = isTv ? 'series' : 'movie';
6
- const imdb = await resolveImdbId(id, isTv, tmdbApiKey || sdk?.tmdbApiKey);
7
- if (!imdb) return null;
8
-
9
- const CONFIG = btoa(JSON.stringify({ "torbox": "unset", "qualities": "2160p,1080p,720p", "sort": "desc", "catalogs": "featured,popular" }));
10
- const url = `https://hdhub.thevolecitor.qzz.io/${CONFIG}/stream/${type}/${imdb}${isTv ? `:${s}:${e}` : ""}.json`;
11
-
12
- const res = await fetchJson(url).catch(() => null);
13
- if (!res || !Array.isArray(res.streams) || !res.streams.length) return null;
14
-
15
- const validStreams = res.streams.filter(st => {
16
- if (!st.url || !st.url.trim()) return false;
17
- const lower = st.url.toLowerCase();
18
- if (lower.includes('hubcloud') || lower.includes('pixeldrain.dev') || lower.endsWith('.zip')) return false;
19
- return true;
20
- });
21
-
22
- if (!validStreams.length) return null;
23
-
24
- const allUrls = validStreams.map(st => ({
25
- url: st.url,
26
- server: `HDHub - ${st.name || 'Server'}`,
27
- headers: st.behaviorHints?.proxyHeaders?.request || undefined,
28
- skipProxy: st.behaviorHints?.notWebReady === false
29
- }));
30
-
31
- return {
32
- url: allUrls[0].url,
33
- headers: allUrls[0].headers,
34
- allUrls
35
- };
36
- }
1
+ import { fetchJson, resolveImdbId, resolveTmdbKey } from '../utils/helpers.js';
2
+
3
+ const BASE_URL = 'https://hdhub.thevolecitor.qzz.io';
4
+
5
+ export async function getStream(args) {
6
+ const { id, s, e, sdk } = args || {};
7
+ const isTv = s != null && e != null;
8
+ const type = isTv ? 'series' : 'movie';
9
+ const apiKey = resolveTmdbKey(args, sdk);
10
+
11
+ const imdb = await resolveImdbId(id, isTv, apiKey);
12
+ if (!imdb) return null;
13
+
14
+ const config = btoa(JSON.stringify({ torbox: 'unset', qualities: '2160p,1080p,720p', sort: 'desc', catalogs: 'featured,popular' }));
15
+ const url = `${BASE_URL}/${config}/stream/${type}/${imdb}${isTv ? `:${s}:${e}` : ''}.json`;
16
+
17
+ const res = await fetchJson(url).catch(() => null);
18
+ if (!res || !Array.isArray(res.streams) || !res.streams.length) return null;
19
+
20
+ const validStreams = res.streams.filter(st => {
21
+ if (!st.url || !st.url.trim()) return false;
22
+ const lower = st.url.toLowerCase();
23
+ if (lower.includes('hubcloud') || lower.includes('pixeldrain.dev') || lower.endsWith('.zip')) return false;
24
+ return true;
25
+ });
26
+
27
+ if (!validStreams.length) return null;
28
+
29
+ const allUrls = validStreams.map(st => ({
30
+ url: st.url,
31
+ server: `HDHub - ${st.name || 'Server'}`,
32
+ quality: 'Auto',
33
+ type: st.url.includes('.m3u8') ? 'hls' : 'mp4',
34
+ headers: st.behaviorHints?.proxyHeaders?.request || undefined,
35
+ skipProxy: st.behaviorHints?.notWebReady === false,
36
+ }));
37
+
38
+ return {
39
+ url: allUrls[0].url,
40
+ headers: allUrls[0].headers,
41
+ allUrls,
42
+ };
43
+ }