@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,101 +1,89 @@
1
- import { getUA } from '../utils/helpers.js';
2
-
3
- const SERVICE_ORIGIN = 'https://st.111477.xyz';
4
-
5
- function generateManifestBaseUrl(tmdbKey) {
6
- let config = 'https://a.111477.xyz/::sort=file-desc::limit=3';
7
- if (tmdbKey) {
8
- config += `::tmdb=${tmdbKey}`;
9
- }
10
- const b64 = Buffer.from(config, 'utf8')
11
- .toString('base64')
12
- .replace(/\+/g, '-')
13
- .replace(/\//g, '_')
14
- .replace(/=+$/, '');
15
- return `${SERVICE_ORIGIN}/config/${b64}`;
16
- }
17
-
18
- async function resolveImdbId(sdk, id, isTv) {
19
- const apiKey = sdk?.tmdbApiKey || process.env.TMDB_API_KEY;
20
- if (!apiKey) return null;
21
- const type = isTv ? 'tv' : 'movie';
22
- try {
23
- const res = await fetch(`https://api.themoviedb.org/3/${type}/${id}?api_key=${apiKey}&append_to_response=external_ids`, {
24
- signal: AbortSignal.timeout(6000),
25
- });
26
- if (!res.ok) return null;
27
- const data = await res.json();
28
- return data.imdb_id || data.external_ids?.imdb_id || null;
29
- } catch {
30
- return null;
31
- }
32
- }
33
-
34
- export async function getStream({ id, s, e, sdk }) {
35
- const isTv = Boolean(s && e);
36
- const ua = getUA();
37
- const apiKey = sdk?.tmdbApiKey || process.env.TMDB_API_KEY;
38
-
39
- const imdbId = await resolveImdbId(sdk, id, isTv);
40
- const targetIds = [];
41
-
42
- if (imdbId && imdbId.startsWith('tt')) {
43
- targetIds.push(imdbId);
44
- }
45
- targetIds.push(`tmdb:${id}`);
46
-
47
- const addonBase = generateManifestBaseUrl(apiKey);
48
- const seenUrls = new Set();
49
- const allUrls = [];
50
-
51
- const defaultHeaders = {
52
- 'User-Agent': ua,
53
- Accept: 'application/json, text/plain, */*',
54
- };
55
-
56
- for (const targetId of targetIds) {
57
- const endpoint = isTv
58
- ? `${addonBase}/stream/series/${targetId}:${s || 1}:${e || 1}.json`
59
- : `${addonBase}/stream/movie/${targetId}.json`;
60
-
61
- try {
62
- const res = await fetch(endpoint, {
63
- headers: defaultHeaders,
64
- signal: AbortSignal.timeout(12000),
65
- });
66
-
67
- if (!res.ok) continue;
68
- const data = await res.json();
69
-
70
- if (Array.isArray(data?.streams) && data.streams.length > 0) {
71
- for (const item of data.streams) {
72
- if (!item || typeof item !== 'object') continue;
73
- const url = String(item.url || '').trim();
74
- if (!url || !url.startsWith('http') || seenUrls.has(url)) continue;
75
-
76
- seenUrls.add(url);
77
-
78
- const rawTitle = item.title ? String(item.title).trim() : '';
79
- const rawName = item.name ? String(item.name).trim() : '111477';
80
- const label = rawTitle || rawName;
81
-
82
- allUrls.push({
83
- url,
84
- label,
85
- headers: defaultHeaders,
86
- });
87
- }
88
-
89
- if (allUrls.length > 0) break;
90
- }
91
- } catch { }
92
- }
93
-
94
- if (allUrls.length === 0) return null;
95
-
96
- return {
97
- url: allUrls[0].url,
98
- allUrls,
99
- headers: allUrls[0].headers,
100
- };
1
+ import { getUA, resolveImdbId, resolveTmdbKey } from '../utils/helpers.js';
2
+
3
+ const SERVICE_ORIGIN = 'https://st.111477.xyz';
4
+
5
+ function generateManifestBaseUrl(apiKey) {
6
+ let config = 'https://a.111477.xyz/::sort=file-desc::limit=3';
7
+ if (apiKey) {
8
+ config += `::tmdb=${apiKey}`;
9
+ }
10
+ const b64 = Buffer.from(config, 'utf8')
11
+ .toString('base64')
12
+ .replace(/\+/g, '-')
13
+ .replace(/\//g, '_')
14
+ .replace(/=+$/, '');
15
+ return `${SERVICE_ORIGIN}/config/${b64}`;
16
+ }
17
+
18
+ export async function getStream(args) {
19
+ const { id, s, e, sdk } = args || {};
20
+ const isTv = s != null && e != null;
21
+ const apiKey = resolveTmdbKey(args, sdk);
22
+ const ua = getUA();
23
+
24
+ const imdbId = await resolveImdbId(id, isTv, apiKey);
25
+ const targetIds = [];
26
+
27
+ if (imdbId && imdbId.startsWith('tt')) {
28
+ targetIds.push(imdbId);
29
+ }
30
+ targetIds.push(`tmdb:${id}`);
31
+
32
+ const addonBase = generateManifestBaseUrl(apiKey);
33
+ const seenUrls = new Set();
34
+ const allUrls = [];
35
+
36
+ const headers = {
37
+ 'User-Agent': ua,
38
+ Accept: 'application/json, text/plain, */*',
39
+ };
40
+
41
+ for (const targetId of targetIds) {
42
+ const endpoint = isTv
43
+ ? `${addonBase}/stream/series/${targetId}:${s || 1}:${e || 1}.json`
44
+ : `${addonBase}/stream/movie/${targetId}.json`;
45
+
46
+ try {
47
+ const res = await fetch(endpoint, {
48
+ headers,
49
+ signal: AbortSignal.timeout(12000),
50
+ });
51
+
52
+ if (!res.ok) continue;
53
+ const data = await res.json();
54
+
55
+ if (Array.isArray(data?.streams) && data.streams.length > 0) {
56
+ for (const item of data.streams) {
57
+ if (!item || typeof item !== 'object') continue;
58
+ const streamUrl = String(item.url || '').trim();
59
+ if (!streamUrl || !streamUrl.startsWith('http') || seenUrls.has(streamUrl)) continue;
60
+
61
+ seenUrls.add(streamUrl);
62
+
63
+ const rawTitle = item.title ? String(item.title).trim() : '';
64
+ const rawName = item.name ? String(item.name).trim() : '111477';
65
+ const label = rawTitle || rawName;
66
+
67
+ allUrls.push({
68
+ url: streamUrl,
69
+ label,
70
+ server: label,
71
+ quality: 'Auto',
72
+ type: streamUrl.includes('.m3u8') ? 'hls' : 'mp4',
73
+ headers,
74
+ });
75
+ }
76
+
77
+ if (allUrls.length > 0) break;
78
+ }
79
+ } catch { }
80
+ }
81
+
82
+ if (allUrls.length === 0) return null;
83
+
84
+ return {
85
+ url: allUrls[0].url,
86
+ headers: allUrls[0].headers,
87
+ allUrls,
88
+ };
101
89
  }
@@ -1,57 +1,121 @@
1
- import { getTmdbInfo, fetchJson, USER_AGENT } from '../utils/helpers.js';
2
-
3
- async function resolveSlug(id, s, e, info) {
4
- const titles = info?.titles || [];
5
- const keywords = [];
6
- for (const title of titles) {
7
- const t = title.toLowerCase().replace(/[^a-z0-9\s]/g, ' ').replace(/\s+/g, ' ').trim();
8
- if (!keywords.includes(t)) keywords.push(t);
9
- const words = title.split(/[\s:]+/).filter(w => w.length > 2);
10
- if (words.length >= 2) {
11
- const short = words.slice(0, 3).join(' ').toLowerCase();
12
- if (!keywords.includes(short)) keywords.push(short);
13
- }
14
- }
15
- const slugCandidates = [];
16
- for (const keyword of keywords) {
17
- try {
18
- const data = await fetchJson(`https://123animehub.cc/ajax/film/search?keyword=${encodeURIComponent(keyword)}&_=${Date.now()}`, { headers: { 'X-Requested-With': 'XMLHttpRequest', 'Referer': 'https://123animehub.cc', 'Accept': 'application/json', 'User-Agent': USER_AGENT }, signal: AbortSignal.timeout(8000) });
19
- let html = data.html || '', idx = 0;
20
- while ((idx = html.indexOf('href="/anime/', idx)) !== -1) {
21
- const start = idx + 13;
22
- const end = html.indexOf('"', start);
23
- const slug = html.slice(start, end);
24
- if (!slugCandidates.includes(slug)) slugCandidates.push(slug);
25
- idx = end;
26
- }
27
- } catch { }
28
- if (slugCandidates.length) break;
29
- }
30
- for (const slug of slugCandidates) {
31
- try {
32
- const data = await fetchJson(`https://123animehub.cc/ajax/episode/info?epr=${encodeURIComponent(s ? `${slug}/${s}/${e}` : `${slug}/1`)}&ts=1&_=${Date.now()}`, { headers: { 'Referer': `https://123animehub.cc/anime/${slug}`, 'X-Requested-With': 'XMLHttpRequest', 'Accept': 'application/json, text/javascript, */*; q=0.01', 'User-Agent': USER_AGENT }, signal: AbortSignal.timeout(10000) });
33
- if (data?.target) return data.target;
34
- } catch { }
35
- }
36
- return null;
37
- }
38
-
39
- export async function getStream({ id, s, e }) {
40
- if (!s) return null;
41
- const info = await getTmdbInfo(id, 'tv', s);
42
- if (!info.isAnime) return null;
43
- const target = await resolveSlug(id, s, e, info);
44
- if (!target) return null;
45
- const embedMatch = target.match(/\/embed-[^/]+\/([A-Za-z0-9+/=]+)$/);
46
- if (!embedMatch) return null;
47
- const origin = new URL(target).origin;
48
- try {
49
- const embedRes = await fetch(target, { headers: { 'User-Agent': USER_AGENT }, signal: AbortSignal.timeout(10000) });
50
- if (!embedRes.ok) return null;
51
- const setCookie = embedRes.headers.get('set-cookie');
52
- const data = await fetchJson(`${origin}/hs/getSources?id=${embedMatch[1]}`, { headers: { 'Referer': target, 'Accept': '*/*', 'User-Agent': USER_AGENT, ...(setCookie ? { 'Cookie': setCookie.split(';')[0].trim() } : {}) }, signal: AbortSignal.timeout(10000) });
53
- const src = typeof data.sources === 'string' && data.sources.length ? data.sources : (Array.isArray(data.sources) && data.sources.length ? (data.sources[0].file || data.sources[0].src || data.sources[0].url) : null);
54
- if (src) return { url: src, headers: { 'Referer': 'https://play2.echovideo.ru/', 'Origin': 'https://play2.echovideo.ru' } };
55
- } catch { }
56
- return null;
1
+ import { fetchJson, getTmdbMetadata, resolveTmdbKey, USER_AGENT } from '../utils/helpers.js';
2
+
3
+ const BASE_URL = 'https://123animehub.cc';
4
+ const CDN_ORIGIN = 'https://play2.echovideo.ru';
5
+
6
+ async function resolveSlug(id, s, e, titles) {
7
+ const keywords = [];
8
+ for (const title of titles) {
9
+ const t = title.toLowerCase().replace(/[^a-z0-9\s]/g, ' ').replace(/\s+/g, ' ').trim();
10
+ if (!keywords.includes(t)) keywords.push(t);
11
+ const words = title.split(/[\s:]+/).filter(w => w.length > 2);
12
+ if (words.length >= 2) {
13
+ const short = words.slice(0, 3).join(' ').toLowerCase();
14
+ if (!keywords.includes(short)) keywords.push(short);
15
+ }
16
+ }
17
+
18
+ const slugCandidates = [];
19
+ for (const keyword of keywords) {
20
+ try {
21
+ const data = await fetchJson(`${BASE_URL}/ajax/film/search?keyword=${encodeURIComponent(keyword)}&_=${Date.now()}`, {
22
+ headers: {
23
+ 'X-Requested-With': 'XMLHttpRequest',
24
+ Referer: `${BASE_URL}/`,
25
+ Accept: 'application/json',
26
+ 'User-Agent': USER_AGENT,
27
+ },
28
+ signal: AbortSignal.timeout(8000),
29
+ });
30
+ let html = data.html || '';
31
+ let idx = 0;
32
+ while ((idx = html.indexOf('href="/anime/', idx)) !== -1) {
33
+ const start = idx + 13;
34
+ const end = html.indexOf('"', start);
35
+ const slug = html.slice(start, end);
36
+ if (!slugCandidates.includes(slug)) slugCandidates.push(slug);
37
+ idx = end;
38
+ }
39
+ } catch { }
40
+ if (slugCandidates.length) break;
41
+ }
42
+
43
+ for (const slug of slugCandidates) {
44
+ try {
45
+ const data = await fetchJson(`${BASE_URL}/ajax/episode/info?epr=${encodeURIComponent(s ? `${slug}/${s}/${e}` : `${slug}/1`)}&ts=1&_=${Date.now()}`, {
46
+ headers: {
47
+ Referer: `${BASE_URL}/anime/${slug}`,
48
+ 'X-Requested-With': 'XMLHttpRequest',
49
+ Accept: 'application/json, text/javascript, */*; q=0.01',
50
+ 'User-Agent': USER_AGENT,
51
+ },
52
+ signal: AbortSignal.timeout(10000),
53
+ });
54
+ if (data?.target) return data.target;
55
+ } catch { }
56
+ }
57
+ return null;
58
+ }
59
+
60
+ export async function getStream(args) {
61
+ const { id, s, e, sdk } = args || {};
62
+ const isTv = s != null && e != null;
63
+ if (!s) return null;
64
+
65
+ const apiKey = resolveTmdbKey(args, sdk);
66
+ const meta = await getTmdbMetadata(id, isTv, apiKey);
67
+ if (!meta?.isAnime) return null;
68
+
69
+ const target = await resolveSlug(id, s, e, meta.titles || []);
70
+ if (!target) return null;
71
+
72
+ const embedMatch = target.match(/\/embed-[^/]+\/([A-Za-z0-9+/=]+)$/);
73
+ if (!embedMatch) return null;
74
+
75
+ const origin = new URL(target).origin;
76
+ try {
77
+ const embedRes = await fetch(target, {
78
+ headers: { 'User-Agent': USER_AGENT },
79
+ signal: AbortSignal.timeout(10000),
80
+ });
81
+ if (!embedRes.ok) return null;
82
+
83
+ const setCookie = embedRes.headers.get('set-cookie');
84
+ const data = await fetchJson(`${origin}/hs/getSources?id=${embedMatch[1]}`, {
85
+ headers: {
86
+ Referer: target,
87
+ Accept: '*/*',
88
+ 'User-Agent': USER_AGENT,
89
+ ...(setCookie ? { Cookie: setCookie.split(';')[0].trim() } : {}),
90
+ },
91
+ signal: AbortSignal.timeout(10000),
92
+ });
93
+
94
+ const streamUrl = typeof data.sources === 'string' && data.sources.length
95
+ ? data.sources
96
+ : (Array.isArray(data.sources) && data.sources.length ? (data.sources[0].file || data.sources[0].src || data.sources[0].url) : null);
97
+
98
+ if (!streamUrl) return null;
99
+
100
+ const headers = {
101
+ Referer: `${CDN_ORIGIN}/`,
102
+ Origin: CDN_ORIGIN,
103
+ };
104
+
105
+ const allUrls = [{
106
+ url: streamUrl,
107
+ server: '123Anime',
108
+ quality: 'Auto',
109
+ type: streamUrl.includes('.m3u8') ? 'hls' : 'mp4',
110
+ headers,
111
+ }];
112
+
113
+ return {
114
+ url: streamUrl,
115
+ headers,
116
+ allUrls,
117
+ };
118
+ } catch {
119
+ return null;
120
+ }
57
121
  }
@@ -1,136 +1,83 @@
1
- import { getUA } from "../utils/helpers.js";
2
-
3
- const BASE_URL = "https://3eyedraven.watch";
4
- const API_BASE = "https://3eyedraven.watch/api";
5
-
6
- function getHeaders() {
7
- return {
8
- "User-Agent": getUA(),
9
- Accept: "application/json",
10
- Referer: `${BASE_URL}/`,
11
- Origin: BASE_URL,
12
- };
13
- }
14
-
15
- function resolveApiKey(args) {
16
- return (
17
- args?.tmdbApiKey ||
18
- args?.sdk?.tmdbApiKey ||
19
- process.env.TMDB_API_KEY ||
20
- null
21
- );
22
- }
23
-
24
- async function getTitle(id, isTv, apiKey) {
25
- if (!apiKey) return null;
26
- const type = isTv ? "tv" : "movie";
27
- try {
28
- const res = await fetch(
29
- `https://api.themoviedb.org/3/${type}/${id}?api_key=${apiKey}`,
30
- { signal: AbortSignal.timeout(5000) }
31
- );
32
- if (!res.ok) return null;
33
- const data = await res.json();
34
- return isTv ? data.name : data.title;
35
- } catch {
36
- return null;
37
- }
38
- }
39
-
40
- function slugify(text) {
41
- return text
42
- .toLowerCase()
43
- .normalize("NFKD")
44
- .replace(/[\u0300-\u036f]/g, "")
45
- .replace(/[^a-z0-9]+/g, "-")
46
- .replace(/^-|-$/g, "");
47
- }
48
-
49
- function absolute(url) {
50
- if (!url) return "";
51
- if (/^https?:\/\//.test(url)) return url;
52
- return `${BASE_URL}${url.startsWith("/") ? "" : "/"}${url}`;
53
- }
54
-
55
- export async function getStream(args) {
56
- const { id, s = null, e = null } = args;
57
- const isTv = s != null && e != null;
58
-
59
- const apiKey = resolveApiKey(args);
60
- const title = await getTitle(id, isTv, apiKey);
61
-
62
- if (!title) {
63
- if (process.env.DEBUG_CAUSE) {
64
- console.error(
65
- `[3eyedraven] Could not resolve title for ${isTv ? "tv" : "movie"} ${id} ` +
66
- `(tmdbApiKey ${apiKey ? "present" : "MISSING"})`
67
- );
68
- }
69
- return null;
70
- }
71
-
72
- const identifier = `${slugify(title)}-${id}`;
73
-
74
- const url = isTv
75
- ? `${API_BASE}/stream/${identifier}?season=${s}&episode=${e}`
76
- : `${API_BASE}/stream/${identifier}`;
77
-
78
- try {
79
- const res = await fetch(url, {
80
- headers: getHeaders(),
81
- signal: AbortSignal.timeout(15000),
82
- });
83
-
84
- if (!res.ok) {
85
- if (process.env.DEBUG_CAUSE) {
86
- console.error(`[3eyedraven] HTTP ${res.status} for ${url}`);
87
- }
88
- return null;
89
- }
90
-
91
- const data = await res.json();
92
- const stream = data?.stream;
93
- if (!stream?.url) {
94
- if (process.env.DEBUG_CAUSE) {
95
- console.error(`[3eyedraven] No stream.url in response for ${url}`);
96
- }
97
- return null;
98
- }
99
-
100
- const streamUrl = absolute(stream.url);
101
- if (!/^https?:\/\//.test(streamUrl)) return null;
102
-
103
- const subtitles = (stream.subtitles || [])
104
- .map(sub => {
105
- const subUrl = absolute(sub.url);
106
- if (!subUrl) return null;
107
- return {
108
- label: sub.label || sub.language || "Subtitle",
109
- file: subUrl,
110
- type: "vtt",
111
- source: "3eyedraven",
112
- };
113
- })
114
- .filter(Boolean);
115
-
116
- return {
117
- url: streamUrl,
118
- headers: {
119
- "User-Agent": getUA(),
120
- Referer: `${BASE_URL}/`,
121
- Origin: BASE_URL,
122
- },
123
- server: "3eyedraven",
124
- quality: "1080p",
125
- type: stream.format === "hls" ? "hls" : "mp4",
126
- subtitles,
127
- skipProxy: true,
128
- };
129
- } catch (err) {
130
- if (process.env.DEBUG_CAUSE) {
131
- console.error(`[3eyedraven] fetch error for ${url}:`, err.message);
132
- if (err.cause) console.error(" cause:", err.cause.code, err.cause.message);
133
- }
134
- return null;
135
- }
1
+ import { getTmdbMetadata, getUA, resolveTmdbKey, slugify } from '../utils/helpers.js';
2
+
3
+ const BASE_URL = 'https://3eyedraven.watch';
4
+ const API_BASE = 'https://3eyedraven.watch/api';
5
+
6
+ function absolute(url) {
7
+ if (!url) return '';
8
+ if (/^https?:\/\//.test(url)) return url;
9
+ return `${BASE_URL}${url.startsWith('/') ? '' : '/'}${url}`;
10
+ }
11
+
12
+ export async function getStream(args) {
13
+ const { id, s, e, sdk } = args || {};
14
+ const isTv = s != null && e != null;
15
+ const apiKey = resolveTmdbKey(args, sdk);
16
+
17
+ const meta = await getTmdbMetadata(id, isTv, apiKey);
18
+ const title = meta?.title;
19
+ if (!title) return null;
20
+
21
+ const identifier = `${slugify(title)}-${id}`;
22
+ const url = isTv
23
+ ? `${API_BASE}/stream/${identifier}?season=${s}&episode=${e}`
24
+ : `${API_BASE}/stream/${identifier}`;
25
+
26
+ const headers = {
27
+ 'User-Agent': getUA(),
28
+ Accept: 'application/json',
29
+ Referer: `${BASE_URL}/`,
30
+ Origin: BASE_URL,
31
+ };
32
+
33
+ try {
34
+ const res = await fetch(url, {
35
+ headers,
36
+ signal: AbortSignal.timeout(15000),
37
+ });
38
+ if (!res.ok) return null;
39
+
40
+ const data = await res.json();
41
+ const stream = data?.stream;
42
+ if (!stream?.url) return null;
43
+
44
+ const streamUrl = absolute(stream.url);
45
+ if (!/^https?:\/\//.test(streamUrl)) return null;
46
+
47
+ const subtitles = (stream.subtitles || [])
48
+ .map(sub => {
49
+ const subUrl = absolute(sub.url);
50
+ if (!subUrl) return null;
51
+ return {
52
+ label: sub.label || sub.language || 'Subtitle',
53
+ file: subUrl,
54
+ type: 'vtt',
55
+ source: '3eyedraven',
56
+ };
57
+ })
58
+ .filter(Boolean);
59
+
60
+ const allUrls = [{
61
+ url: streamUrl,
62
+ headers,
63
+ server: '3eyedraven',
64
+ quality: '1080p',
65
+ type: stream.format === 'hls' ? 'hls' : 'mp4',
66
+ subtitles,
67
+ skipProxy: true,
68
+ }];
69
+
70
+ return {
71
+ url: streamUrl,
72
+ headers,
73
+ server: '3eyedraven',
74
+ quality: '1080p',
75
+ type: stream.format === 'hls' ? 'hls' : 'mp4',
76
+ subtitles,
77
+ skipProxy: true,
78
+ allUrls,
79
+ };
80
+ } catch {
81
+ return null;
82
+ }
136
83
  }
@@ -1,19 +1,38 @@
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 url = isTv
6
- ? `https://zcdn.bluevelvet.space/cdn?tmdb=${id}&type=tv&season=${s}&episode=${e}`
7
- : `https://zcdn.bluevelvet.space/cdn?tmdb=${id}&type=movie`;
8
-
9
- const res = await fetchJson(url);
10
- if (!res || !res.ready || !res.url) return null;
11
-
12
- return {
13
- url: res.url,
14
- headers: {
15
- Referer: 'https://zcdn.bluevelvet.space/',
16
- Origin: 'https://zcdn.bluevelvet.space',
17
- }
18
- };
1
+ import { fetchJson } from '../utils/helpers.js';
2
+
3
+ const BASE_URL = 'https://zcdn.bluevelvet.space';
4
+
5
+ export async function getStream(args) {
6
+ const { id, s, e } = args || {};
7
+ const isTv = s != null && e != null;
8
+
9
+ const url = isTv
10
+ ? `${BASE_URL}/cdn?tmdb=${id}&type=tv&season=${s}&episode=${e}`
11
+ : `${BASE_URL}/cdn?tmdb=${id}&type=movie`;
12
+
13
+ try {
14
+ const res = await fetchJson(url);
15
+ if (!res || !res.ready || !res.url) return null;
16
+
17
+ const headers = {
18
+ Referer: `${BASE_URL}/`,
19
+ Origin: BASE_URL,
20
+ };
21
+
22
+ const allUrls = [{
23
+ url: res.url,
24
+ server: 'CineCat',
25
+ quality: 'Auto',
26
+ type: res.url.includes('.m3u8') ? 'hls' : 'mp4',
27
+ headers,
28
+ }];
29
+
30
+ return {
31
+ url: res.url,
32
+ headers,
33
+ allUrls,
34
+ };
35
+ } catch {
36
+ return null;
37
+ }
19
38
  }