@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.
- package/.github/workflows/publish.yml +38 -38
- package/.gitlab-ci.yml +16 -0
- package/README.md +9 -13
- package/package.json +33 -32
- package/src/config.js +86 -118
- package/src/health.js +2 -2
- package/src/sdk.d.ts +122 -122
- package/src/sdk.js +7 -4
- package/src/sources/anineko.js +3 -1
- package/src/sources/cinejoy.js +194 -209
- package/src/sources/flaxmovies.js +113 -21
- package/src/sources/frame.js +88 -0
- package/src/sources/fsonic.js +3 -1
- package/src/sources/fsonline.js +3 -1
- package/src/sources/lmscript.js +42 -42
- package/src/sources/lookmovie.js +3 -1
- package/src/sources/luna.js +125 -0
- package/src/sources/mapple.js +40 -4
- package/src/sources/megasource.js +47 -0
- package/src/sources/meowtv.js +0 -1
- package/src/sources/nova.js +46 -0
- package/src/sources/peestream.js +122 -0
- package/src/sources/pengu.js +8 -3
- package/src/sources/rivestream.js +179 -0
- package/src/sources/videasy.js +3 -1
- package/src/sources/vidgod.js +147 -0
- package/src/sources/vidnest.js +3 -1
- package/src/sources/vidvault.js +113 -113
- package/src/sources/vidzee.js +154 -199
- package/src/subtitles.js +18 -0
- package/src/utils/helpers.js +2 -2
- package/src/sources/1embed.js +0 -115
- package/src/sources/anikoto.js +0 -198
- package/src/sources/anizone.js +0 -109
- package/src/sources/cinesu.js +0 -94
- package/src/sources/kickassanime.js +0 -107
- package/src/sources/nekowatch.js +0 -57
- package/src/sources/opstream.js +0 -123
- package/src/sources/peachify.js +0 -78
- 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
|
-
|
|
3
|
+
const BASE_URL = 'https://flaxmovies.xyz';
|
|
4
|
+
|
|
5
|
+
async function getWorkerUrls(embedUrl) {
|
|
6
|
+
const foundUrls = [];
|
|
4
7
|
try {
|
|
5
|
-
const
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
const
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
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
|
-
|
|
23
|
-
|
|
24
|
-
|
|
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
|
+
}
|
package/src/sources/fsonic.js
CHANGED
|
@@ -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,
|
|
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');
|
package/src/sources/fsonline.js
CHANGED
|
@@ -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,
|
|
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');
|
package/src/sources/lmscript.js
CHANGED
|
@@ -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
|
}
|
package/src/sources/lookmovie.js
CHANGED
|
@@ -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,
|
|
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
|
+
}
|
package/src/sources/mapple.js
CHANGED
|
@@ -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
|
|
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`, {
|
|
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
|
|
100
|
-
|
|
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
|
+
}
|
package/src/sources/meowtv.js
CHANGED
|
@@ -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
|
+
}
|