@vyla-entertainment/sdk 1.9.0 → 1.10.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 (40) hide show
  1. package/.github/workflows/publish.yml +38 -38
  2. package/.gitlab-ci.yml +16 -0
  3. package/README.md +9 -13
  4. package/package.json +33 -32
  5. package/src/config.js +86 -118
  6. package/src/health.js +2 -2
  7. package/src/sdk.d.ts +122 -122
  8. package/src/sdk.js +7 -4
  9. package/src/sources/anineko.js +3 -1
  10. package/src/sources/cinejoy.js +194 -209
  11. package/src/sources/flaxmovies.js +113 -21
  12. package/src/sources/frame.js +88 -0
  13. package/src/sources/fsonic.js +3 -1
  14. package/src/sources/fsonline.js +3 -1
  15. package/src/sources/lmscript.js +42 -42
  16. package/src/sources/lookmovie.js +3 -1
  17. package/src/sources/luna.js +125 -0
  18. package/src/sources/mapple.js +40 -4
  19. package/src/sources/megasource.js +47 -0
  20. package/src/sources/meowtv.js +0 -1
  21. package/src/sources/nova.js +46 -0
  22. package/src/sources/peestream.js +122 -0
  23. package/src/sources/pengu.js +8 -3
  24. package/src/sources/rivestream.js +179 -0
  25. package/src/sources/videasy.js +3 -1
  26. package/src/sources/vidgod.js +147 -0
  27. package/src/sources/vidnest.js +3 -1
  28. package/src/sources/vidvault.js +113 -113
  29. package/src/sources/vidzee.js +154 -199
  30. package/src/subtitles.js +18 -0
  31. package/src/utils/helpers.js +2 -2
  32. package/src/sources/1embed.js +0 -115
  33. package/src/sources/anikoto.js +0 -198
  34. package/src/sources/anizone.js +0 -109
  35. package/src/sources/cinesu.js +0 -94
  36. package/src/sources/kickassanime.js +0 -107
  37. package/src/sources/nekowatch.js +0 -57
  38. package/src/sources/opstream.js +0 -123
  39. package/src/sources/peachify.js +0 -78
  40. package/src/sources/reanime.js +0 -324
@@ -1,26 +1,118 @@
1
- import { fetchJson } from '../utils/helpers.js';
1
+ import { USER_AGENT, fetchJson } from '../utils/helpers.js';
2
2
 
3
- export async function getStream({ id, s, e }) {
3
+ const BASE_URL = 'https://flaxmovies.xyz';
4
+
5
+ async function getWorkerUrls(embedUrl) {
6
+ const foundUrls = [];
4
7
  try {
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
- });
8
+ const res = await fetch(embedUrl, {
9
+ headers: {
10
+ 'User-Agent': USER_AGENT,
11
+ 'Referer': `${BASE_URL}/`,
12
+ 'Origin': BASE_URL
13
+ },
14
+ signal: AbortSignal.timeout(8000)
15
+ });
16
+ if (res.ok) {
17
+ const html = await res.text();
18
+ const directMatch = html.match(/WORKER_URL\s*[:=]\s*["'](https?:\/\/[^"']+)["']/i);
19
+ if (directMatch && directMatch[1]) {
20
+ foundUrls.push(directMatch[1].replace(/\/+$/, ''));
21
+ }
22
+
23
+ const scriptMatches = [...html.matchAll(/<script[^>]+src=["']([^"']+)["']/gi)];
24
+ for (const sm of scriptMatches) {
25
+ let scriptUrl = sm[1];
26
+ if (scriptUrl.startsWith('//')) scriptUrl = `https:${scriptUrl}`;
27
+ else if (scriptUrl.startsWith('/')) scriptUrl = `${BASE_URL}${scriptUrl}`;
28
+ else if (!scriptUrl.startsWith('http')) scriptUrl = `${BASE_URL}/${scriptUrl}`;
29
+
30
+ if (scriptUrl.includes('jsdelivr') || scriptUrl.includes('google') || scriptUrl.includes('cloudflare')) continue;
31
+
32
+ try {
33
+ const sRes = await fetch(scriptUrl, {
34
+ headers: { 'User-Agent': USER_AGENT, 'Referer': embedUrl },
35
+ signal: AbortSignal.timeout(6000)
36
+ });
37
+ if (sRes.ok) {
38
+ const sCode = await sRes.text();
39
+ const m = sCode.match(/WORKER_URL\s*[:=]\s*["'](https?:\/\/[^"']+)["']/i);
40
+ if (m && m[1]) {
41
+ const wUrl = m[1].replace(/\/+$/, '');
42
+ if (!foundUrls.includes(wUrl)) foundUrls.push(wUrl);
43
+ }
44
+ }
45
+ } catch { }
46
+ }
21
47
  }
22
- return allUrls.length ? { allUrls } : null;
23
- } catch {
24
- return null;
48
+ } catch { }
49
+
50
+ const defaults = ['https://freakyniki.elaxo.lol', 'https://vidlove.nabilekson.workers.dev'];
51
+ for (const d of defaults) {
52
+ if (!foundUrls.includes(d)) foundUrls.push(d);
53
+ }
54
+ return foundUrls;
55
+ }
56
+
57
+ export async function getStream(args) {
58
+ const { id, s, e, server } = args;
59
+ const isTv = s != null && e != null;
60
+ const embedUrl = isTv
61
+ ? `${BASE_URL}/embed/tv/${id}/${s}/${e}`
62
+ : `${BASE_URL}/embed/movie/${id}`;
63
+
64
+ const workerUrls = await getWorkerUrls(embedUrl);
65
+ const query = isTv
66
+ ? `tmdb_id=${id}&tmdbId=${id}&season=${s}&episode=${e}`
67
+ : `tmdb_id=${id}&tmdbId=${id}`;
68
+
69
+ for (const worker of workerUrls) {
70
+ try {
71
+ const url = `${worker}/?${query}`;
72
+ const data = await fetchJson(url, {
73
+ headers: {
74
+ 'User-Agent': USER_AGENT,
75
+ 'Referer': `${BASE_URL}/`,
76
+ 'Origin': BASE_URL,
77
+ 'Accept': 'application/json, text/plain, */*'
78
+ },
79
+ signal: AbortSignal.timeout(12000)
80
+ });
81
+
82
+ if (data && Array.isArray(data.streams) && data.streams.length > 0) {
83
+ let allUrls = data.streams
84
+ .filter(st => st && typeof st.url === 'string')
85
+ .map(st => {
86
+ const isHls = (st.format || '').toLowerCase().includes('hls') || st.url.includes('.m3u8');
87
+ return {
88
+ url: st.url,
89
+ server: `FlaxMovies - ${st.provider || 'Default'}`,
90
+ quality: st.resolution || 'Auto',
91
+ type: isHls ? 'hls' : 'mp4',
92
+ headers: {
93
+ 'User-Agent': USER_AGENT,
94
+ 'Referer': `${BASE_URL}/`,
95
+ 'Origin': BASE_URL
96
+ }
97
+ };
98
+ });
99
+
100
+ if (server && server !== 'all') {
101
+ const clean = server.toLowerCase().replace('flaxmovies - ', '');
102
+ const filtered = allUrls.filter(u => u.server.toLowerCase().includes(clean));
103
+ if (filtered.length) allUrls = filtered;
104
+ }
105
+
106
+ if (allUrls.length) {
107
+ return { allUrls };
108
+ }
109
+ }
110
+ } catch { }
25
111
  }
112
+
113
+ return null;
114
+ }
115
+
116
+ export async function getSources() {
117
+ return ['FlaxMovies - Airflix', 'FlaxMovies - Xpass-VIP 1'];
26
118
  }
@@ -0,0 +1,88 @@
1
+ import { USER_AGENT, fetchJson } from '../utils/helpers.js';
2
+
3
+ const BASE_URL = 'https://api.peestream.in';
4
+
5
+ const PROVIDERS = [
6
+ { id: 'vaplayer', name: 'FRAME Zephyr' },
7
+ { id: 'castle', name: 'FRAME Atlas' },
8
+ { id: 'hera', name: 'FRAME Luna' },
9
+ { id: 'multivid', name: 'FRAME Volt' },
10
+ { id: 'netmirror', name: 'FRAME Echo' },
11
+ { id: 'vidsuper-castle', name: 'FRAME Rift' },
12
+ { id: 'vidsuper-vixsrc', name: 'FRAME Quill' }
13
+ ];
14
+
15
+ async function fetchProviderStreams(provider, type, id, s, e) {
16
+ try {
17
+ const queryParams = new URLSearchParams({
18
+ q: String(id),
19
+ type,
20
+ provider: provider.id
21
+ });
22
+ if (type === 'tv') {
23
+ queryParams.set('season', String(s));
24
+ queryParams.set('episode', String(e));
25
+ }
26
+
27
+ const url = `${BASE_URL}/api/search?${queryParams.toString()}`;
28
+ const data = await fetchJson(url, {
29
+ headers: {
30
+ 'User-Agent': USER_AGENT,
31
+ 'Accept': 'application/json',
32
+ 'Referer': 'https://peestream.in/'
33
+ },
34
+ signal: AbortSignal.timeout(12000)
35
+ });
36
+
37
+ if (!data || !Array.isArray(data.results) || !data.results.length) return [];
38
+
39
+ const streams = [];
40
+ for (const result of data.results) {
41
+ for (const stream of result.streams || []) {
42
+ if (!stream || !stream.url) continue;
43
+ streams.push({
44
+ url: stream.url,
45
+ server: provider.name,
46
+ quality: stream.quality || 'Auto',
47
+ type: stream.type === 'm3u8' || stream.url.includes('.m3u8') ? 'hls' : 'mp4',
48
+ headers: stream.headers || {
49
+ 'User-Agent': USER_AGENT
50
+ }
51
+ });
52
+ }
53
+ }
54
+ return streams;
55
+ } catch {
56
+ return [];
57
+ }
58
+ }
59
+
60
+ export async function getStream(args) {
61
+ const { id, s, e, server } = args;
62
+ const isTv = s != null && e != null;
63
+ const type = isTv ? 'tv' : 'movie';
64
+
65
+ let targets = PROVIDERS;
66
+ if (server && server !== 'all') {
67
+ const clean = server.toLowerCase().replace('frame ', '').trim();
68
+ targets = PROVIDERS.filter(p => p.id.toLowerCase() === clean || p.name.toLowerCase().includes(clean));
69
+ if (!targets.length) targets = PROVIDERS;
70
+ }
71
+
72
+ const settled = await Promise.allSettled(
73
+ targets.map(p => fetchProviderStreams(p, type, id, s, e))
74
+ );
75
+
76
+ const allUrls = [];
77
+ for (const r of settled) {
78
+ if (r.status === 'fulfilled' && Array.isArray(r.value)) {
79
+ allUrls.push(...r.value);
80
+ }
81
+ }
82
+
83
+ return allUrls.length ? { allUrls } : null;
84
+ }
85
+
86
+ export async function getSources() {
87
+ return PROVIDERS.map(p => p.name);
88
+ }
@@ -4,7 +4,9 @@ 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, tmdbApiKey }) {
7
+ export async function getStream({ id, s, sdk }) {
8
+ const tmdbApiKey = sdk?.tmdbApiKey || null;
9
+ if (!tmdbApiKey) return null;
8
10
  if (s != null) return null;
9
11
  try {
10
12
  const info = await getTmdbInfo(tmdbApiKey, id, 'movie');
@@ -4,7 +4,9 @@ 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, tmdbApiKey }) {
7
+ export async function getStream({ id, s, e, sdk }) {
8
+ const tmdbApiKey = sdk?.tmdbApiKey || null;
9
+ if (!tmdbApiKey) return null;
8
10
  try {
9
11
  const isTv = s != null && e != null;
10
12
  const info = await getTmdbInfo(tmdbApiKey, id, isTv ? 'tv' : 'movie');
@@ -1,43 +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
- }
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
43
  }
@@ -47,7 +47,9 @@ function getEpisodeIdFast(html, s, e) {
47
47
  return am ? am[1] : null;
48
48
  }
49
49
 
50
- export async function getStream({ id, s, e, tmdbApiKey, clientIP }) {
50
+ export async function getStream({ id, s, e, sdk, clientIP }) {
51
+ const tmdbApiKey = sdk?.tmdbApiKey || null;
52
+ if (!tmdbApiKey) return null;
51
53
  try {
52
54
  const tmdbKey = tmdbApiKey;
53
55
  if (!tmdbKey) return null;
@@ -0,0 +1,125 @@
1
+ import { USER_AGENT, fetchText, getTmdbInfo, tmdbToAnilist } from '../utils/helpers.js';
2
+
3
+ const ACTION_FETCH_SOURCES = 'afb0491c5516f9fff5fcb464d627638df76062f8';
4
+ const WATCH_URL = 'https://luna-stream.me/anime/watch/21/gogoanime/1';
5
+
6
+ const PROVIDERS = [
7
+ { id: 'megaplay', name: 'Helios' },
8
+ { id: 'anibd', name: 'Nova' },
9
+ { id: 'pahe', name: 'Polaris' },
10
+ { id: 'gogoanime', name: 'Quasar' },
11
+ { id: 'zoro', name: 'Zenith' },
12
+ { id: 'animepahe', name: 'Vega' },
13
+ { id: 'animekai', name: 'Cosmos' },
14
+ { id: 'crysoline', name: 'CrysOline' },
15
+ { id: 'orion', name: 'Orion' },
16
+ { id: 'pulsar', name: 'Pulsar' }
17
+ ];
18
+
19
+ function parseRscResponse(text) {
20
+ for (const line of text.split('\n')) {
21
+ if (line.startsWith('1:')) {
22
+ try {
23
+ return JSON.parse(line.slice(2));
24
+ } catch { }
25
+ }
26
+ }
27
+ return null;
28
+ }
29
+
30
+ function cleanUrl(rawUrl) {
31
+ if (!rawUrl) return '';
32
+ return rawUrl.replace(/^https:\/\/api\.luna-stream\.mehttps:\/\/api\.luna-stream\.me/, 'https://api.luna-stream.me');
33
+ }
34
+
35
+ async function fetchProviderStreams(anilistId, epNum, provider, subtype) {
36
+ try {
37
+ const text = await fetchText(WATCH_URL, {
38
+ method: 'POST',
39
+ headers: {
40
+ 'User-Agent': USER_AGENT,
41
+ 'Next-Action': ACTION_FETCH_SOURCES,
42
+ 'Content-Type': 'text/plain;charset=UTF-8',
43
+ 'Accept': 'text/x-component',
44
+ 'Referer': WATCH_URL,
45
+ 'Origin': 'https://luna-stream.me'
46
+ },
47
+ body: JSON.stringify([anilistId, provider.id, String(epNum), epNum, subtype, null]),
48
+ signal: AbortSignal.timeout(10000)
49
+ });
50
+
51
+ if (!text) return [];
52
+
53
+ const parsed = parseRscResponse(text);
54
+ if (!parsed || !Array.isArray(parsed.sources)) return [];
55
+
56
+ const streams = [];
57
+ for (const s of parsed.sources) {
58
+ if (!s || !s.url) continue;
59
+
60
+ const url = cleanUrl(s.url);
61
+ const format = String(s.type || '').toLowerCase();
62
+ const isM3U8 = format === 'hls' || format === 'm3u8' || url.includes('.m3u8') || url.includes('.txt');
63
+
64
+ streams.push({
65
+ url,
66
+ server: `Luna (${provider.name})`,
67
+ quality: String(s.quality || 'Auto'),
68
+ type: isM3U8 ? 'hls' : 'mp4',
69
+ headers: {
70
+ 'User-Agent': USER_AGENT,
71
+ 'Referer': 'https://luna-stream.me/'
72
+ }
73
+ });
74
+ }
75
+
76
+ return streams;
77
+ } catch {
78
+ return [];
79
+ }
80
+ }
81
+
82
+ export async function getStream(args) {
83
+ const { id, s, e, ep, audio, server, tmdbApiKey } = args;
84
+ const isTv = s != null && e != null;
85
+ const mediaType = isTv ? 'tv' : 'movie';
86
+
87
+ let anilistId = null;
88
+ try {
89
+ const info = await getTmdbInfo(tmdbApiKey, id, mediaType, s);
90
+ anilistId = await tmdbToAnilist(id, mediaType, s, info?.titles || [], info?.year);
91
+ } catch { }
92
+
93
+ if (!anilistId) {
94
+ anilistId = Number(id);
95
+ }
96
+
97
+ if (!anilistId || isNaN(anilistId)) return null;
98
+
99
+ const epNum = Number(e || ep || 1);
100
+ const resolvedSubtype = audio === 'dub' ? 'dub' : 'sub';
101
+
102
+ let targets = PROVIDERS;
103
+ if (server && server !== 'all') {
104
+ const clean = server.toLowerCase().replace('luna (', '').replace(')', '').replace('luna', '').trim();
105
+ targets = PROVIDERS.filter(p => p.id.toLowerCase() === clean || p.name.toLowerCase().includes(clean));
106
+ if (!targets.length) targets = PROVIDERS;
107
+ }
108
+
109
+ const settled = await Promise.allSettled(
110
+ targets.map(p => fetchProviderStreams(anilistId, epNum, p, resolvedSubtype))
111
+ );
112
+
113
+ const allUrls = [];
114
+ for (const r of settled) {
115
+ if (r.status === 'fulfilled' && Array.isArray(r.value)) {
116
+ allUrls.push(...r.value);
117
+ }
118
+ }
119
+
120
+ return allUrls.length ? { allUrls } : null;
121
+ }
122
+
123
+ export async function getSources() {
124
+ return PROVIDERS.map(p => `Luna (${p.name})`);
125
+ }
@@ -76,13 +76,29 @@ export async function getStream(args) {
76
76
  const reqMatch = html.match(/window\.__REQUEST_TOKEN__\s*=\s*"([^"]+)"/);
77
77
  if (!reqMatch) return null;
78
78
  const requestToken = reqMatch[1];
79
- const initRes = await fetch(`${BASE_URL}/api/playback-init`, { method: "POST", headers: { "Content-Type": "application/json", ...PROXY_HEADERS, Referer: pageUrl }, body: JSON.stringify({ mediaId: Number(id), mediaType, requestToken }), signal: AbortSignal.timeout(10000) });
79
+ const cookies = pageRes.headers.getSetCookie();
80
+ const cookieHeader = cookies.map(c => c.split(';')[0]).join('; ');
81
+
82
+ const initHeaders = { "Content-Type": "application/json", ...PROXY_HEADERS, Referer: pageUrl };
83
+ if (cookieHeader) initHeaders.Cookie = cookieHeader;
84
+
85
+ const initRes = await fetch(`${BASE_URL}/api/playback-init`, {
86
+ method: "POST",
87
+ headers: initHeaders,
88
+ body: JSON.stringify({ mediaId: Number(id), mediaType, requestToken }),
89
+ signal: AbortSignal.timeout(10000)
90
+ });
80
91
  const initData = await initRes.json();
81
92
  let streamToken = initData.token;
82
93
  if (initData.requiresPow && initData.pow) {
83
94
  const powRes = solvePoW(initData.pow.challenge, initData.pow.difficulty);
84
95
  if (powRes.found) {
85
- const solveRes = await fetch(`${BASE_URL}/api/playback-init`, { method: "POST", headers: { "Content-Type": "application/json", ...PROXY_HEADERS, Referer: pageUrl }, body: JSON.stringify({ mediaId: Number(id), mediaType, requestToken, pow: { challengeId: initData.pow.challengeId, nonce: powRes.nonce } }), signal: AbortSignal.timeout(10000) });
96
+ const solveRes = await fetch(`${BASE_URL}/api/playback-init`, {
97
+ method: "POST",
98
+ headers: initHeaders,
99
+ body: JSON.stringify({ mediaId: Number(id), mediaType, requestToken, pow: { challengeId: initData.pow.challengeId, nonce: powRes.nonce } }),
100
+ signal: AbortSignal.timeout(10000)
101
+ });
86
102
  streamToken = (await solveRes.json()).token;
87
103
  }
88
104
  }
@@ -96,8 +112,28 @@ export async function getStream(args) {
96
112
  const allUrls = [];
97
113
  for (const srv of targets) {
98
114
  try {
99
- const streamUrl = `${BASE_URL}/api/stream?mediaId=${id}&mediaType=${mediaType}&tv_slug=${tvSlug}&source=${srv.id}&apikey=mptv_sk_a8f29c4e7b3d1f&requestToken=${requestToken}&token=${streamToken}`;
100
- const streamRes = await fetch(streamUrl, { headers: { ...PROXY_HEADERS, Referer: pageUrl }, signal: AbortSignal.timeout(8000) });
115
+ const encryptRes = await fetch(`${BASE_URL}/api/encrypt`, {
116
+ method: "POST",
117
+ headers: initHeaders,
118
+ body: JSON.stringify({
119
+ data: {
120
+ mediaId: Number(id),
121
+ mediaType,
122
+ tv_slug: tvSlug,
123
+ source: srv.id,
124
+ apikey: "mptv_sk_a8f29c4e7b3d1f"
125
+ }
126
+ }),
127
+ signal: AbortSignal.timeout(8000)
128
+ });
129
+ const encryptData = await encryptRes.json();
130
+ if (!encryptData.encrypted) continue;
131
+
132
+ const streamUrl = `${BASE_URL}/api/stream-encrypted?data=${encodeURIComponent(encryptData.encrypted)}&requestToken=${encodeURIComponent(requestToken)}&token=${encodeURIComponent(streamToken)}`;
133
+ const streamRes = await fetch(streamUrl, {
134
+ headers: { ...PROXY_HEADERS, Referer: pageUrl, ...(cookieHeader ? { Cookie: cookieHeader } : {}) },
135
+ signal: AbortSignal.timeout(8000)
136
+ });
101
137
  const streamData = await streamRes.json();
102
138
  if (streamData.success && streamData.data?.stream_url) {
103
139
  let fileUrl = streamData.data.stream_url;
@@ -0,0 +1,47 @@
1
+ import { fetchJson } from '../utils/helpers.js';
2
+
3
+ const CONFIGURATION = 'W3siaWQiOiJkZWZhdWx0IiwibmFtZSI6Ik1lZ2FTb3VyY2UgZGVmYXVsdCIsInVybCI6Imh0dHBzOi8vZ2l0aHViLmNvbS96b3JldS9tZWdhc291cmNlX3NjcmFwZXJzL3Jhdy9yZWZzL2hlYWRzL21haW4vZGVmYXVsdF9zY3JhcGVyLnB5IiwiZGVzY3JpcHRpb24iOiJEZWZhdWx0IHNjcmFwZXIgaG9zdGVkIG9uIEdpdEh1Yi4ifV0';
4
+ const BASE_URL = `https://megasource.wasmer.app/${CONFIGURATION}`;
5
+
6
+ async function getImdbId(tmdbApiKey, tmdbId, s, e) {
7
+ const key = tmdbApiKey;
8
+ if (!key) return null;
9
+ try {
10
+ const url = s != null && e != null
11
+ ? `https://api.themoviedb.org/3/tv/${tmdbId}/external_ids?api_key=${key}`
12
+ : `https://api.themoviedb.org/3/movie/${tmdbId}?api_key=${key}`;
13
+ const res = await fetchJson(url);
14
+ return res?.imdb_id || null;
15
+ } catch { return null; }
16
+ }
17
+
18
+ export async function getStream({ id, s, e, sdk }) {
19
+ const tmdbApiKey = sdk?.tmdbApiKey || null;
20
+ if (!tmdbApiKey) return null;
21
+ try {
22
+ const imdbId = await getImdbId(tmdbApiKey, id, s, e);
23
+ if (!imdbId) return null;
24
+
25
+ const path = s != null && e != null
26
+ ? `/stream/series/${imdbId}:${s}:${e}.json`
27
+ : `/stream/movie/${imdbId}.json`;
28
+
29
+ const res = await fetchJson(`${BASE_URL}${path}`);
30
+ if (!res?.streams?.length) return null;
31
+
32
+ const allUrls = res.streams.map(stream => {
33
+ if (!stream.url) return null;
34
+ const headers = stream.behaviorHints?.proxyHeaders?.request || {};
35
+ return {
36
+ url: stream.url,
37
+ server: `MegaSource - ${stream.title ? ` ${stream.title}` : ''}`,
38
+ headers: Object.keys(headers).length > 0 ? headers : undefined,
39
+ skipProxy: Object.keys(headers).length > 0 && stream.behaviorHints?.notWebReady === false
40
+ };
41
+ }).filter(Boolean);
42
+
43
+ return allUrls.length ? { allUrls } : null;
44
+ } catch {
45
+ return null;
46
+ }
47
+ }
@@ -13,7 +13,6 @@ const HEADERS = {
13
13
  };
14
14
 
15
15
  const SERVERS = [
16
- 'tik',
17
16
  'hindiv3'
18
17
  ];
19
18
 
@@ -0,0 +1,46 @@
1
+ import { fetchJson } from '../utils/helpers.js';
2
+
3
+ const BASE_URL = `https://nova-streamz.vercel.app`;
4
+
5
+ async function getImdbId(tmdbApiKey, tmdbId, s, e) {
6
+ const key = tmdbApiKey;
7
+ if (!key) return null;
8
+ try {
9
+ const url = s != null && e != null
10
+ ? `https://api.themoviedb.org/3/tv/${tmdbId}/external_ids?api_key=${key}`
11
+ : `https://api.themoviedb.org/3/movie/${tmdbId}?api_key=${key}`;
12
+ const res = await fetchJson(url);
13
+ return res?.imdb_id || null;
14
+ } catch { return null; }
15
+ }
16
+
17
+ export async function getStream({ id, s, e, sdk }) {
18
+ const tmdbApiKey = sdk?.tmdbApiKey || null;
19
+ if (!tmdbApiKey) return null;
20
+ try {
21
+ const imdbId = await getImdbId(tmdbApiKey, id, s, e);
22
+ if (!imdbId) return null;
23
+
24
+ const path = s != null && e != null
25
+ ? `/stream/series/${imdbId}:${s}:${e}.json`
26
+ : `/stream/movie/${imdbId}.json`;
27
+
28
+ const res = await fetchJson(`${BASE_URL}${path}`);
29
+ if (!res?.streams?.length) return null;
30
+
31
+ const allUrls = res.streams.map(stream => {
32
+ if (!stream.url) return null;
33
+ const headers = stream.behaviorHints?.proxyHeaders?.request || {};
34
+ return {
35
+ url: stream.url,
36
+ server: `Nova - ${stream.name.replace('Nova ', '') || 'Server'}${stream.title ? ` ${stream.title.split(' | ')[0] || ''}` : ''}`,
37
+ headers: Object.keys(headers).length > 0 ? headers : undefined,
38
+ skipProxy: Object.keys(headers).length > 0 && stream.behaviorHints?.notWebReady === false
39
+ };
40
+ }).filter(Boolean);
41
+
42
+ return allUrls.length ? { allUrls } : null;
43
+ } catch {
44
+ return null;
45
+ }
46
+ }