@vyla-entertainment/sdk 1.11.0 → 1.13.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/package.json +2 -2
- package/src/config.js +80 -245
- package/src/downloads/downloads.js +51 -10
- package/src/sdk.d.ts +0 -1
- package/src/sdk.js +1 -1
- package/src/sources/111477.js +101 -0
- package/src/sources/dulo.js +150 -0
- package/src/sources/movienight.js +117 -0
- package/src/sources/movy.js +282 -0
- package/src/sources/purstream.js +2 -2
- package/src/sources/streamaggregator.js +129 -0
- package/src/sources/vidvault.js +131 -57
- package/src/sources/xdownloader.js +129 -0
- package/src/subtitles.js +35 -40
- package/src/downloads/sources/trendimovies.js +0 -63
- package/src/sources/anipm.js +0 -70
- package/src/sources/cinejoy.js +0 -195
- package/src/sources/cinesrc.js +0 -212
- package/src/sources/cinesu.js +0 -148
- package/src/sources/frame.js +0 -88
- package/src/sources/fsharetv.js +0 -44
- package/src/sources/fsonic.js +0 -49
- package/src/sources/hexa.js +0 -82
- package/src/sources/luna.js +0 -125
- package/src/sources/mapple.js +0 -151
- package/src/sources/megasource.js +0 -47
- package/src/sources/meowtv.js +0 -103
- package/src/sources/nova.js +0 -46
- package/src/sources/peestream.js +0 -122
- package/src/sources/vidcore.js +0 -43
- package/src/sources/videasy.js +0 -94
- package/src/sources/vidfast.js +0 -53
- package/src/sources/xpass.js +0 -40
- package/src/sources/zxcstream.js +0 -141
package/src/sources/cinesu.js
DELETED
|
@@ -1,148 +0,0 @@
|
|
|
1
|
-
import { USER_AGENT } from '../utils/helpers.js';
|
|
2
|
-
|
|
3
|
-
const BASE_DOMAIN = 'https://glendale-plumbing.com';
|
|
4
|
-
const REFERER = 'https://cine.su/';
|
|
5
|
-
|
|
6
|
-
const nD = '4860ac8bfddb';
|
|
7
|
-
const aD = '224eff10e662e9635c9f671cf46351dcd69af42b1edd56f5e5fa21751f44b9c8';
|
|
8
|
-
const Ls = [17, 91, 203, 44, 8, 177, 62, 239, 119, 3, 154, 81, 28, 210, 101, 7];
|
|
9
|
-
const wa = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_';
|
|
10
|
-
|
|
11
|
-
function ab(e) {
|
|
12
|
-
let t = e >>> 0;
|
|
13
|
-
t ^= t >>> 16;
|
|
14
|
-
t = Math.imul(t, 2146121005) >>> 0;
|
|
15
|
-
t ^= t >>> 15;
|
|
16
|
-
t = Math.imul(t, 2221713035) >>> 0;
|
|
17
|
-
return (t ^ (t >>> 16)) >>> 0;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
function sD(e) {
|
|
21
|
-
const t = new TextEncoder().encode(aD);
|
|
22
|
-
const r = Math.max(32, Math.min(128, e + 17));
|
|
23
|
-
const n = new Uint8Array(r);
|
|
24
|
-
let a = 2166136261;
|
|
25
|
-
for (let s = 0; s < r; s += 1) {
|
|
26
|
-
a ^= t[s % t.length] ?? s;
|
|
27
|
-
a = ab((a + Ls[s % Ls.length] + ((2654435761 * s) >>> 0)) >>> 0);
|
|
28
|
-
n[s] = a & 255;
|
|
29
|
-
}
|
|
30
|
-
return n;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
function iD(e) {
|
|
34
|
-
let t = '';
|
|
35
|
-
for (let r = 0; r < e.length; r += 3) {
|
|
36
|
-
const n = e[r];
|
|
37
|
-
const a = e[r + 1];
|
|
38
|
-
const s = e[r + 2];
|
|
39
|
-
t += wa[n >>> 2];
|
|
40
|
-
t += wa[((3 & n) << 4) | ((a ?? 0) >>> 4)];
|
|
41
|
-
if (a === undefined) break;
|
|
42
|
-
t += wa[((15 & a) << 2) | ((s ?? 0) >>> 6)];
|
|
43
|
-
if (s === undefined) break;
|
|
44
|
-
t += wa[63 & s];
|
|
45
|
-
}
|
|
46
|
-
return t;
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
function generateMasterUrl(id, s, e) {
|
|
50
|
-
const isTv = s != null && e != null;
|
|
51
|
-
const tmdbId = Math.floor(Number(id));
|
|
52
|
-
const season = isTv ? Math.floor(Number(s || 1)) : 0;
|
|
53
|
-
const episode = isTv ? Math.floor(Number(e || 1)) : 0;
|
|
54
|
-
|
|
55
|
-
const str = `${nD}:${isTv ? 's' : 'm'}:${tmdbId}:${season}:${episode}`;
|
|
56
|
-
const a = new TextEncoder().encode(str);
|
|
57
|
-
const sArr = sD(a.length);
|
|
58
|
-
const i = new Uint8Array(a.length + 2);
|
|
59
|
-
i[0] = a.length & 255;
|
|
60
|
-
i[1] = (a.length >>> 8) & 255;
|
|
61
|
-
let o = (2654435769 ^ a.length) >>> 0;
|
|
62
|
-
for (let l = 0; l < a.length; l += 1) {
|
|
63
|
-
o = ab((o + sArr[l % sArr.length] + Ls[l % Ls.length] + l) >>> 0);
|
|
64
|
-
i[l + 2] = (a[l] ^ (255 & o)) ^ sArr[(7 * l + 3) % sArr.length];
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
return `${BASE_DOMAIN}/c/v1/${iD(i)}/master.m3u8`;
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
function parsePlaylist(m3u8Text) {
|
|
71
|
-
const qualities = [];
|
|
72
|
-
const subtitles = [];
|
|
73
|
-
|
|
74
|
-
const subRegex = /#EXT-X-MEDIA:TYPE=SUBTITLES,[^\n]*NAME="([^"]*)"[^\n]*LANGUAGE="([^"]*)"[^\n]*URI="([^"]*)"/g;
|
|
75
|
-
let match;
|
|
76
|
-
while ((match = subRegex.exec(m3u8Text)) !== null) {
|
|
77
|
-
let uri = match[3];
|
|
78
|
-
if (uri.startsWith('/')) uri = `${BASE_DOMAIN}${uri}`;
|
|
79
|
-
subtitles.push({ label: match[1], language: match[2], file: uri, type: 'vtt' });
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
const streamRegex = /#EXT-X-STREAM-INF:[^\n]*RESOLUTION=\d+x(\d+)[^\n]*\r?\n([^\n#][^\n]*)/g;
|
|
83
|
-
while ((match = streamRegex.exec(m3u8Text)) !== null) {
|
|
84
|
-
let streamPath = match[2].trim();
|
|
85
|
-
if (streamPath.startsWith('/')) streamPath = `${BASE_DOMAIN}${streamPath}`;
|
|
86
|
-
qualities.push({ quality: `${match[1]}p`, url: streamPath });
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
return { qualities, subtitles };
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
export async function getStream(args) {
|
|
93
|
-
const { id, s, e, clientIP } = args;
|
|
94
|
-
const rawMasterUrl = generateMasterUrl(id, s, e);
|
|
95
|
-
|
|
96
|
-
const headers = {
|
|
97
|
-
'User-Agent': USER_AGENT,
|
|
98
|
-
'Referer': REFERER,
|
|
99
|
-
'Origin': 'https://cine.su',
|
|
100
|
-
...(clientIP && { 'X-Forwarded-For': clientIP })
|
|
101
|
-
};
|
|
102
|
-
|
|
103
|
-
try {
|
|
104
|
-
const res = await fetch(rawMasterUrl, {
|
|
105
|
-
headers,
|
|
106
|
-
signal: AbortSignal.timeout(5000)
|
|
107
|
-
});
|
|
108
|
-
|
|
109
|
-
if (res.ok) {
|
|
110
|
-
const text = await res.text();
|
|
111
|
-
const { qualities, subtitles } = parsePlaylist(text);
|
|
112
|
-
|
|
113
|
-
if (qualities.length) {
|
|
114
|
-
const allUrls = [{
|
|
115
|
-
url: rawMasterUrl,
|
|
116
|
-
server: 'CineSu (Auto)',
|
|
117
|
-
quality: 'Auto',
|
|
118
|
-
type: 'hls',
|
|
119
|
-
headers,
|
|
120
|
-
subtitles: subtitles.length ? subtitles : undefined
|
|
121
|
-
}];
|
|
122
|
-
|
|
123
|
-
for (const q of qualities) {
|
|
124
|
-
allUrls.push({
|
|
125
|
-
url: q.url,
|
|
126
|
-
server: `CineSu (${q.quality})`,
|
|
127
|
-
quality: q.quality,
|
|
128
|
-
type: 'hls',
|
|
129
|
-
headers,
|
|
130
|
-
subtitles: subtitles.length ? subtitles : undefined
|
|
131
|
-
});
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
return { allUrls };
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
|
-
} catch { }
|
|
138
|
-
|
|
139
|
-
return {
|
|
140
|
-
allUrls: [{
|
|
141
|
-
url: rawMasterUrl,
|
|
142
|
-
server: 'CineSu',
|
|
143
|
-
quality: 'Auto',
|
|
144
|
-
type: 'hls',
|
|
145
|
-
headers
|
|
146
|
-
}]
|
|
147
|
-
};
|
|
148
|
-
}
|
package/src/sources/frame.js
DELETED
|
@@ -1,88 +0,0 @@
|
|
|
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/fsharetv.js
DELETED
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
import { fetchText, fetchJson, USER_AGENT, getTmdbInfo } from '../utils/helpers.js';
|
|
2
|
-
|
|
3
|
-
const BASE_URL = 'https://fsharetv.cc';
|
|
4
|
-
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', 'Referer': `${BASE_URL}/` };
|
|
5
|
-
const API_HEADERS = { ...HEADERS, 'Accept': 'application/json, */*; q=0.01', 'X-Requested-With': 'XMLHttpRequest' };
|
|
6
|
-
|
|
7
|
-
export async function getStream({ id, s }) {
|
|
8
|
-
if (s != null) return null;
|
|
9
|
-
try {
|
|
10
|
-
const info = await getTmdbInfo(id, 'movie');
|
|
11
|
-
if (!info?.imdbId) return null;
|
|
12
|
-
const html = await fetchText(`${BASE_URL}/movie/${info.imdbId}`, { headers: HEADERS, signal: AbortSignal.timeout(8000) });
|
|
13
|
-
let watchPath = null;
|
|
14
|
-
const match = html.match(/href="(\/w\/[^"]+)"/);
|
|
15
|
-
if (match) watchPath = match[1];
|
|
16
|
-
if (!watchPath) return null;
|
|
17
|
-
const watchHtml = await fetchText(`${BASE_URL}${watchPath}`, { headers: HEADERS, signal: AbortSignal.timeout(8000) });
|
|
18
|
-
let sourceId = null;
|
|
19
|
-
const sidMatch = watchHtml.match(/(?:source_id|file_id|setSource)[\s=:\('"]+([^'"\)]+)/i);
|
|
20
|
-
if (sidMatch) sourceId = sidMatch[1];
|
|
21
|
-
if (!sourceId) return null;
|
|
22
|
-
const json = await fetchJson(`${BASE_URL}/api/file/${sourceId}/source?trailer=Png81APqcxU&type=watch`, { headers: { ...API_HEADERS, 'Referer': `${BASE_URL}${watchPath}` }, signal: AbortSignal.timeout(8000) });
|
|
23
|
-
if (json.status !== 'ok') return null;
|
|
24
|
-
const groups = [];
|
|
25
|
-
if (Array.isArray(json.data?.file?.sources)) groups.push(json.data.file.sources);
|
|
26
|
-
if (Array.isArray(json.data?.file?.alternatives)) json.data.file.alternatives.forEach(g => Array.isArray(g) && groups.push(g));
|
|
27
|
-
const out = [], seen = new Set();
|
|
28
|
-
for (const group of groups) {
|
|
29
|
-
for (const item of group.filter(st => st?.src).sort((a, b) => parseInt(b.quality) - parseInt(a.quality))) {
|
|
30
|
-
const url = item.src.startsWith('http') ? item.src : `${BASE_URL}${item.src}`;
|
|
31
|
-
if (seen.has(url)) continue;
|
|
32
|
-
seen.add(url);
|
|
33
|
-
out.push({ url, quality: parseInt(item.quality) || 0, label: item.label || null });
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
out.sort((a, b) => b.quality - a.quality);
|
|
37
|
-
return out.length ? { allUrls: out.map(u => ({ url: u.url, headers: HEADERS, skipProxy: false })) } : null;
|
|
38
|
-
} catch { return null; }
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
export async function getSources(args) {
|
|
42
|
-
const stream = await getStream(args);
|
|
43
|
-
return stream?.allUrls ? stream.allUrls.map(u => u.url) : [];
|
|
44
|
-
}
|
package/src/sources/fsonic.js
DELETED
|
@@ -1,49 +0,0 @@
|
|
|
1
|
-
import { fetchText, fetchJson, USER_AGENT, getTmdbInfo } from '../utils/helpers.js';
|
|
2
|
-
|
|
3
|
-
const BASE_URL = 'https://www.fsonic.net';
|
|
4
|
-
const FSHARE_BASE = 'https://fsharetv.co';
|
|
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
|
-
|
|
7
|
-
export async function getStream({ id, s, sdk }) {
|
|
8
|
-
const tmdbApiKey = sdk?.tmdbApiKey || null;
|
|
9
|
-
if (!tmdbApiKey) return null;
|
|
10
|
-
if (s != null) return null;
|
|
11
|
-
try {
|
|
12
|
-
const info = await getTmdbInfo(tmdbApiKey, id, 'movie');
|
|
13
|
-
if (!info?.titles?.length) return null;
|
|
14
|
-
let watchSlug = null;
|
|
15
|
-
for (const title of info.titles) {
|
|
16
|
-
try {
|
|
17
|
-
const html = await fetchText(`${BASE_URL}/movie/search/${encodeURIComponent(title)}`, { headers: HEADERS, signal: AbortSignal.timeout(8000) });
|
|
18
|
-
let idx = 0, found = null;
|
|
19
|
-
const yearStr = String(info.year);
|
|
20
|
-
while ((idx = html.indexOf('href="/watch/', idx)) !== -1) {
|
|
21
|
-
const start = idx + 6;
|
|
22
|
-
const end = html.indexOf('"', start);
|
|
23
|
-
const link = html.slice(start, end);
|
|
24
|
-
if (!found) found = link;
|
|
25
|
-
if (link.includes(yearStr)) { found = link; break; }
|
|
26
|
-
idx = end;
|
|
27
|
-
}
|
|
28
|
-
if (found) { watchSlug = found; break; }
|
|
29
|
-
} catch { }
|
|
30
|
-
}
|
|
31
|
-
if (!watchSlug) return null;
|
|
32
|
-
const wHtml = await fetchText(`${BASE_URL}${watchSlug}`, { headers: HEADERS, signal: AbortSignal.timeout(8000) });
|
|
33
|
-
let token = null, trailer = null;
|
|
34
|
-
const match = wHtml.match(/init\('([^']+)',\s*(?:'[^']*',\s*)?'([^']+)'/);
|
|
35
|
-
if (match) { token = match[1]; trailer = match[2]; }
|
|
36
|
-
if (!token) return null;
|
|
37
|
-
const json = await fetchJson(`${BASE_URL}/api/source/${token}?trailer=${trailer}&type=watch`, { headers: { ...HEADERS, 'Accept': 'application/json, text/plain, */*', 'Referer': `${BASE_URL}${watchSlug}` }, signal: AbortSignal.timeout(8000) });
|
|
38
|
-
if (json.status !== 'ok') return null;
|
|
39
|
-
const allGroups = [];
|
|
40
|
-
if (json.data?.file?.sources?.length) allGroups.push(json.data.file.sources);
|
|
41
|
-
if (json.data?.file?.alternatives) for (const g of json.data.file.alternatives) if (g?.length) allGroups.push(g);
|
|
42
|
-
const urls = new Set();
|
|
43
|
-
for (const group of allGroups) {
|
|
44
|
-
const best = [...group].filter(st => st?.src).sort((a, b) => parseInt(b.quality) - parseInt(a.quality))[0];
|
|
45
|
-
if (best) urls.add(best.src.startsWith('http') ? best.src : `${FSHARE_BASE}${best.src}`);
|
|
46
|
-
}
|
|
47
|
-
return urls.size ? { allUrls: [...urls].map(url => ({ url, headers: { ...HEADERS, 'Referer': `${FSHARE_BASE}/` }, skipProxy: false })) } : null;
|
|
48
|
-
} catch { return null; }
|
|
49
|
-
}
|
package/src/sources/hexa.js
DELETED
|
@@ -1,82 +0,0 @@
|
|
|
1
|
-
import crypto from 'node:crypto';
|
|
2
|
-
import { fetchJson, fetchText, USER_AGENT } from '../utils/helpers.js';
|
|
3
|
-
|
|
4
|
-
const API_BASE = 'https://enc-dec.app/api';
|
|
5
|
-
const DOMAINS = ['hexa.su', 'flixer.su'];
|
|
6
|
-
|
|
7
|
-
async function getChallengeToken() {
|
|
8
|
-
try {
|
|
9
|
-
const data = await fetchJson(`${API_BASE}/enc-hexa`, { signal: AbortSignal.timeout(6000) });
|
|
10
|
-
return data?.status === 200 ? data.result.token : null;
|
|
11
|
-
} catch { return null; }
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
export async function getStream({ id, s, e, server }) {
|
|
15
|
-
try {
|
|
16
|
-
const apiKey = crypto.randomBytes(32).toString('hex');
|
|
17
|
-
const capToken = await getChallengeToken();
|
|
18
|
-
if (!capToken) return null;
|
|
19
|
-
|
|
20
|
-
const isTv = s != null && e != null;
|
|
21
|
-
let decrypted = null;
|
|
22
|
-
|
|
23
|
-
for (const domain of DOMAINS) {
|
|
24
|
-
try {
|
|
25
|
-
const url = isTv
|
|
26
|
-
? `https://theemoviedb.${domain}/api/tmdb/tv/${id}/season/${s}/episode/${e}/images`
|
|
27
|
-
: `https://theemoviedb.${domain}/api/tmdb/movie/${id}/images`;
|
|
28
|
-
|
|
29
|
-
const encrypted = await fetchText(url, {
|
|
30
|
-
headers: {
|
|
31
|
-
'User-Agent': USER_AGENT,
|
|
32
|
-
'Referer': `https://${domain}/`,
|
|
33
|
-
'Accept': 'text/plain',
|
|
34
|
-
'X-Fingerprint-Lite': 'e9136c41504646444',
|
|
35
|
-
'X-Api-Key': apiKey,
|
|
36
|
-
'X-Cap-Token': capToken
|
|
37
|
-
},
|
|
38
|
-
signal: AbortSignal.timeout(10000)
|
|
39
|
-
});
|
|
40
|
-
|
|
41
|
-
if (!encrypted) continue;
|
|
42
|
-
|
|
43
|
-
const decData = await fetchJson(`${API_BASE}/dec-hexa`, {
|
|
44
|
-
method: 'POST',
|
|
45
|
-
headers: { 'Content-Type': 'application/json' },
|
|
46
|
-
body: JSON.stringify({ text: encrypted, key: apiKey }),
|
|
47
|
-
signal: AbortSignal.timeout(8000)
|
|
48
|
-
});
|
|
49
|
-
|
|
50
|
-
if (decData?.status === 200 && decData.result?.sources?.length) {
|
|
51
|
-
decrypted = decData.result;
|
|
52
|
-
break;
|
|
53
|
-
}
|
|
54
|
-
} catch { continue; }
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
if (!decrypted?.sources?.length) return null;
|
|
58
|
-
|
|
59
|
-
let sources = decrypted.sources.map(src => ({
|
|
60
|
-
url: src.url,
|
|
61
|
-
server: `Hexa - ${src.name || src.server || 'Server'}`,
|
|
62
|
-
type: src.url.includes('.m3u8') ? 'hls' : 'mp4',
|
|
63
|
-
quality: src.quality || 'Auto',
|
|
64
|
-
subtitles: (decrypted.subtitles || []).map(sub => ({
|
|
65
|
-
url: sub.url,
|
|
66
|
-
lang: sub.label || sub.lang || 'Unknown'
|
|
67
|
-
})),
|
|
68
|
-
skipProxy: false
|
|
69
|
-
}));
|
|
70
|
-
|
|
71
|
-
if (server && server !== 'all') {
|
|
72
|
-
const cleanName = server.replace('Hexa - ', '');
|
|
73
|
-
sources = sources.filter(u => u.server.includes(cleanName));
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
return sources.length ? { allUrls: sources } : null;
|
|
77
|
-
} catch { return null; }
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
export async function getSources() {
|
|
81
|
-
return ['Hexa'];
|
|
82
|
-
}
|
package/src/sources/luna.js
DELETED
|
@@ -1,125 +0,0 @@
|
|
|
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
|
-
}
|