@vyla-entertainment/sdk 1.12.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.
@@ -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
- }
@@ -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
- }
@@ -1,142 +0,0 @@
1
- import { fetchJson, USER_AGENT } from '../utils/helpers.js';
2
-
3
- const API_BASE = 'https://api.meowtv.ru';
4
- const REFERER = 'https://meowtv.ru/';
5
- const ENC_DEC_API = 'https://enc-dec.app/api/dec-meowtv';
6
-
7
- const BASE_HEADERS = {
8
- 'User-Agent': USER_AGENT,
9
- 'Accept': 'application/json',
10
- 'Referer': REFERER,
11
- 'Origin': 'https://meowtv.ru',
12
- 'Accept-Language': 'en-US,en;q=0.9'
13
- };
14
-
15
- const SERVERS = [
16
- 'dcloud', 'acme', 'tcloud', 'ipcloud', 'turkce', 'hindiv3'
17
- ];
18
-
19
- const TIMEOUT_MS = 15000;
20
-
21
- export const VERIFY_HEADERS = { 'User-Agent': USER_AGENT, 'Referer': REFERER, 'Origin': 'https://meowtv.ru' };
22
-
23
- let cachedTicket = null;
24
- let ticketExpiry = 0;
25
-
26
- /**
27
- * Fetches a stream ticket or returns a cached valid ticket.
28
- */
29
- async function getTicket(forceRefresh = false) {
30
- const now = Date.now();
31
- if (!forceRefresh && cachedTicket && now < ticketExpiry - 10000) {
32
- return cachedTicket;
33
- }
34
-
35
- try {
36
- const res = await fetchJson(`${API_BASE}/streams/ticket`, {
37
- method: 'POST',
38
- headers: {
39
- ...BASE_HEADERS,
40
- 'Content-Type': 'application/json'
41
- },
42
- body: JSON.stringify({})
43
- });
44
-
45
- if (res?.ticket) {
46
- cachedTicket = res.ticket;
47
- ticketExpiry = typeof res.exp === 'number' ? res.exp : now + 60000;
48
- return cachedTicket;
49
- }
50
- } catch {
51
- }
52
-
53
- return null;
54
- }
55
-
56
- async function decryptPayload(payload) {
57
- try {
58
- const response = await fetchJson(ENC_DEC_API, {
59
- method: 'POST',
60
- headers: { 'Content-Type': 'application/json' },
61
- body: JSON.stringify({ data: payload })
62
- });
63
-
64
- if (response?.status !== 200 || !response?.result) {
65
- return null;
66
- }
67
-
68
- return typeof response.result === 'string' ? JSON.parse(response.result) : response.result;
69
- } catch (e) {
70
- return null;
71
- }
72
- }
73
-
74
- export async function getStream({ id, s, e, server }) {
75
- try {
76
- const isTv = !!s;
77
- let targets = SERVERS;
78
- if (server && server !== 'all') {
79
- const cleanName = server.replace('MeowTV - ', '');
80
- targets = SERVERS.includes(cleanName) ? [cleanName] : [targets[0]];
81
- }
82
-
83
- const ticket = await getTicket();
84
- const requestHeaders = {
85
- ...BASE_HEADERS,
86
- ...(ticket ? { 'x-stream-ticket': ticket } : {})
87
- };
88
-
89
- const results = await Promise.all(targets.map(async srv => {
90
- try {
91
- return await Promise.race([
92
- (async () => {
93
- const path = isTv
94
- ? `/streams/tv/${id}/${s}/${e}?s=${encodeURIComponent(srv)}`
95
- : `/streams/movie/${id}?s=${encodeURIComponent(srv)}`;
96
-
97
- const payload = await fetchJson(`${API_BASE}${path}`, {
98
- headers: requestHeaders
99
- });
100
-
101
- if (!payload) return null;
102
-
103
- const data = await decryptPayload(payload);
104
- if (!data) return null;
105
-
106
- const urls = [];
107
- if (data?.url?.startsWith('http')) {
108
- urls.push({
109
- url: data.url,
110
- server: `MeowTV - ${srv}`,
111
- });
112
- }
113
-
114
- if (Array.isArray(data?.streams)) {
115
- for (const stream of data.streams) {
116
- if (stream?.url?.startsWith('http')) {
117
- urls.push({
118
- url: stream.url,
119
- server: `MeowTV - ${srv} (${stream.language || 'Unknown'})`,
120
- });
121
- }
122
- }
123
- }
124
- return urls;
125
- })(),
126
- new Promise((_, reject) => setTimeout(() => reject(new Error('Timeout exceeded')), TIMEOUT_MS))
127
- ]);
128
- } catch (err) {
129
- return null;
130
- }
131
- }));
132
-
133
- const allUrls = results.filter(Boolean).flat();
134
- return allUrls.length ? { allUrls } : null;
135
- } catch (e) {
136
- return null;
137
- }
138
- }
139
-
140
- export async function getSources() {
141
- return SERVERS.map(s => `MeowTV - ${s}`);
142
- }
@@ -1,122 +0,0 @@
1
- import { USER_AGENT, fetchJson, getTmdbInfo } from '../utils/helpers.js';
2
-
3
- const BASE_URL = 'https://providers.peestream.in';
4
-
5
- export async function getStream(args) {
6
- const { id, s, e, server, tmdbApiKey } = args;
7
- const isTv = s != null && e != null;
8
- const type = isTv ? 'tv' : 'movie';
9
-
10
- let title = args.title || '';
11
- let releaseYear = args.year || '';
12
- let imdbId = args.imdbId || '';
13
-
14
- if (!title) {
15
- try {
16
- const info = await getTmdbInfo(id, type, s, tmdbApiKey);
17
- if (info) {
18
- title = info.title || '';
19
- if (info.year) releaseYear = info.year;
20
- if (info.imdbId) imdbId = info.imdbId;
21
- }
22
- } catch { }
23
- }
24
-
25
- const queryParams = new URLSearchParams({
26
- type,
27
- tmdbId: String(id),
28
- title: title || String(id)
29
- });
30
- if (releaseYear) queryParams.set('releaseYear', String(releaseYear));
31
- if (imdbId) queryParams.set('imdbId', String(imdbId));
32
- if (isTv) {
33
- queryParams.set('season', String(s));
34
- queryParams.set('episode', String(e));
35
- }
36
-
37
- const allUrls = [];
38
-
39
- try {
40
- const scrapeUrl = `${BASE_URL}/scrape?${queryParams.toString()}`;
41
- const res = await fetch(scrapeUrl, {
42
- headers: {
43
- 'User-Agent': USER_AGENT,
44
- 'Accept': 'text/event-stream',
45
- 'Referer': `${BASE_URL}/`
46
- },
47
- signal: AbortSignal.timeout(15000)
48
- });
49
-
50
- if (res.ok) {
51
- const text = await res.text();
52
- const events = text.split('\n\n');
53
- for (const ev of events) {
54
- if (ev.includes('event: completed')) {
55
- const dataMatch = ev.match(/data:\s*(.+)/);
56
- if (dataMatch) {
57
- try {
58
- const parsed = JSON.parse(dataMatch[1].trim());
59
- const stream = parsed.stream;
60
- const streamUrl = stream?.playlist || stream?.url || stream?.file;
61
- if (streamUrl) {
62
- allUrls.push({
63
- url: streamUrl,
64
- server: `PeeStream - ${parsed.sourceId || 'Poseidon'}`,
65
- quality: 'Auto',
66
- type: stream.type === 'hls' || streamUrl.includes('.m3u8') ? 'hls' : 'mp4',
67
- headers: stream.headers || {
68
- 'User-Agent': USER_AGENT
69
- }
70
- });
71
- }
72
- } catch { }
73
- }
74
- }
75
- }
76
- }
77
- } catch { }
78
-
79
- if (!allUrls.length) {
80
- try {
81
- const searchUrl = `${BASE_URL}/api/search?q=${encodeURIComponent(title || id)}&type=${type}&tmdbId=${id}${isTv ? `&season=${s}&episode=${e}` : ''}`;
82
- const data = await fetchJson(searchUrl, {
83
- headers: {
84
- 'User-Agent': USER_AGENT,
85
- 'Accept': 'application/json',
86
- 'Referer': `${BASE_URL}/`
87
- },
88
- signal: AbortSignal.timeout(10000)
89
- });
90
-
91
- if (data && Array.isArray(data.results)) {
92
- for (const result of data.results) {
93
- const pName = result.providerName || result.provider || 'PeeStream';
94
- for (const stream of result.streams || []) {
95
- if (!stream || !stream.url) continue;
96
- allUrls.push({
97
- url: stream.url,
98
- server: `PeeStream - ${stream.name || pName}`,
99
- quality: stream.quality || 'Auto',
100
- type: stream.type === 'm3u8' || stream.url.includes('.m3u8') ? 'hls' : 'mp4',
101
- headers: stream.headers || {
102
- 'User-Agent': USER_AGENT
103
- }
104
- });
105
- }
106
- }
107
- }
108
- } catch { }
109
- }
110
-
111
- if (server && server !== 'all') {
112
- const clean = server.toLowerCase().replace('peestream - ', '');
113
- const filtered = allUrls.filter(u => u.server.toLowerCase().includes(clean));
114
- if (filtered.length) return { allUrls: filtered };
115
- }
116
-
117
- return allUrls.length ? { allUrls } : null;
118
- }
119
-
120
- export async function getSources() {
121
- return ['PeeStream - vaplayer'];
122
- }
@@ -1,85 +0,0 @@
1
- import { getUA } from '../utils/helpers.js';
2
-
3
- const ADDON_BASE = 'https://stremio.vadapav.mov';
4
-
5
- async function resolveImdbId(sdk, id, isTv) {
6
- const apiKey = sdk?.tmdbApiKey || process.env.TMDB_API_KEY;
7
- if (!apiKey) return null;
8
- const type = isTv ? 'tv' : 'movie';
9
- try {
10
- const res = await fetch(`https://api.themoviedb.org/3/${type}/${id}?api_key=${apiKey}&append_to_response=external_ids`, {
11
- signal: AbortSignal.timeout(6000),
12
- });
13
- if (!res.ok) return null;
14
- const data = await res.json();
15
- return data.imdb_id || data.external_ids?.imdb_id || null;
16
- } catch {
17
- return null;
18
- }
19
- }
20
-
21
- export async function getStream({ id, s, e, sdk }) {
22
- const isTv = Boolean(s && e);
23
- const ua = getUA();
24
-
25
- const imdbId = await resolveImdbId(sdk, id, isTv);
26
- const targetIds = [];
27
- if (imdbId && imdbId.startsWith('tt')) {
28
- targetIds.push(imdbId);
29
- }
30
- targetIds.push(`tmdb:${id}`);
31
-
32
- const seenUrls = new Set();
33
- const allUrls = [];
34
-
35
- const defaultHeaders = {
36
- 'User-Agent': ua,
37
- Accept: 'application/json, text/plain, */*',
38
- };
39
-
40
- for (const targetId of targetIds) {
41
- const streamPath = isTv
42
- ? `${ADDON_BASE}/stream/series/${targetId}:${s}:${e}.json`
43
- : `${ADDON_BASE}/stream/movie/${targetId}.json`;
44
-
45
- try {
46
- const res = await fetch(streamPath, {
47
- headers: defaultHeaders,
48
- signal: AbortSignal.timeout(12000),
49
- });
50
-
51
- if (!res.ok) continue;
52
- const data = await res.json();
53
-
54
- if (Array.isArray(data?.streams) && data.streams.length > 0) {
55
- for (const item of data.streams) {
56
- if (!item || typeof item !== 'object') continue;
57
- const url = String(item.url || '').trim();
58
- if (!url || !url.startsWith('http') || seenUrls.has(url)) continue;
59
-
60
- seenUrls.add(url);
61
-
62
- const rawTitle = item.title ? String(item.title).trim() : '';
63
- const rawName = item.name ? String(item.name).trim() : 'vadapav.mov';
64
- const label = rawTitle || rawName;
65
-
66
- allUrls.push({
67
- url,
68
- label,
69
- headers: defaultHeaders,
70
- });
71
- }
72
-
73
- if (allUrls.length > 0) break;
74
- }
75
- } catch { }
76
- }
77
-
78
- if (allUrls.length === 0) return null;
79
-
80
- return {
81
- url: allUrls[0].url,
82
- allUrls,
83
- headers: allUrls[0].headers,
84
- };
85
- }
@@ -1,43 +0,0 @@
1
- import { fetchJson, fetchText, USER_AGENT } from '../utils/helpers.js';
2
-
3
- const API_BASE = 'https://enc-dec.app/api';
4
- const HEADERS = { 'User-Agent': USER_AGENT, 'Referer': 'https://vidcore.net/', 'X-Requested-With': 'XMLHttpRequest' };
5
-
6
- async function getDynamicServers(id, s, e) {
7
- try {
8
- const html = await fetchText(s != null && e != null ? `https://vidcore.net/tv/${id}/${s}/${e}/` : `https://vidcore.net/movie/${id}/`, { headers: HEADERS });
9
-
10
- const match = html.match(/\\"(?:en|token)\\":\\"(.*?)\\"/) || html.match(/"(?:en|token)":"(.*?)"/);
11
- if (!match?.[1]) return null;
12
-
13
- const encData = await fetchJson(`${API_BASE}/enc-vidcore?text=${encodeURIComponent(match[1])}`);
14
- if (!encData?.result) return null;
15
- const { servers: serversUrl, stream: streamUrl, token } = encData.result;
16
- const reqHeaders = { ...HEADERS, 'X-CSRF-Token': token };
17
- const serversEncrypted = await fetchText(serversUrl, { method: 'POST', headers: reqHeaders });
18
- const decData = await fetchJson(`${API_BASE}/dec-vidcore`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ text: serversEncrypted }) });
19
- return decData?.result ? { servers: decData.result, streamUrl, reqHeaders } : null;
20
- } catch { return null; }
21
- }
22
-
23
- export async function getStream({ id, s, e }) {
24
- const data = await getDynamicServers(id, s, e);
25
- if (!data?.servers?.length) return null;
26
- for (const srv of data.servers) {
27
- try {
28
- const streamEncrypted = await fetchText(`${data.streamUrl}/${srv.data}`, { method: 'POST', headers: data.reqHeaders });
29
- const decData = await fetchJson(`${API_BASE}/dec-vidcore`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ text: streamEncrypted }) });
30
- const url = decData?.result?.url;
31
- if (url) {
32
- const r = await fetch(url, { headers: { ...HEADERS, 'Range': 'bytes=0-511' }, signal: AbortSignal.timeout(5000) });
33
- if (r.ok || r.status === 206) return { url, subtitles: (decData.result.captions || []).map(c => ({ url: c.file, lang: c.label || 'Unknown' })), headers: { ...HEADERS, 'Origin': 'https://vidcore.net' }, server: srv.name };
34
- }
35
- } catch { }
36
- }
37
- return null;
38
- }
39
-
40
- export async function getSources(args) {
41
- const data = await getDynamicServers(args.id, args.s, args.e);
42
- return data?.servers ? data.servers.map(s => s.name) : [];
43
- }
@@ -1,53 +0,0 @@
1
- import { fetchJson, fetchText, USER_AGENT } from '../utils/helpers.js';
2
-
3
- const API_BASE = 'https://enc-dec.app/api';
4
- const DOMAIN = 'https://vidfast.vc';
5
- const HEADERS = { 'User-Agent': USER_AGENT, 'Referer': `${DOMAIN}/`, 'X-Requested-With': 'XMLHttpRequest' };
6
-
7
- async function getVidfastMeta(id, s, e) {
8
- try {
9
- const embedUrl = s != null && e != null ? `${DOMAIN}/tv/${id}/${s}/${e}/` : `${DOMAIN}/movie/${id}/`;
10
- const html = await fetchText(embedUrl, { headers: { 'User-Agent': USER_AGENT } });
11
-
12
- const match = html.match(/\\"(?:en|token)\\":\\"(.*?)\\"/) || html.match(/"(?:en|token)":"(.*?)"/);
13
- if (!match?.[1]) return null;
14
-
15
- const encData = await fetchJson(`${API_BASE}/enc-vidfast?text=${encodeURIComponent(match[1])}`);
16
- if (encData.status !== 200 || !encData.result) return null;
17
-
18
- const { servers: serversUrl, stream: streamUrl, token } = encData.result;
19
- const reqHeaders = { ...HEADERS, 'X-CSRF-Token': token };
20
-
21
- const serversEncrypted = await fetchText(serversUrl, { method: 'POST', headers: reqHeaders });
22
- const decServersData = await fetchJson(`${API_BASE}/dec-vidfast`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ text: serversEncrypted }) });
23
-
24
- if (decServersData.status !== 200 || !decServersData.result) return null;
25
- return { servers: decServersData.result, streamUrl, reqHeaders };
26
- } catch { return null; }
27
- }
28
-
29
- export async function getStream({ id, s, e, server }) {
30
- const meta = await getVidfastMeta(id, s, e);
31
- if (!meta?.servers?.length) return null;
32
- const { servers, streamUrl, reqHeaders } = meta;
33
- let targets = servers;
34
- if (server && server !== 'all') {
35
- const cleanName = server.replace('VidFast - ', '');
36
- targets = servers.filter(srv => srv.name === cleanName);
37
- if (!targets.length) targets = servers;
38
- }
39
- const results = await Promise.allSettled(targets.map(async srv => {
40
- const streamEncrypted = await fetchText(`${streamUrl}/${srv.data}`, { method: 'POST', headers: reqHeaders });
41
- const decStreamData = await fetchJson(`${API_BASE}/dec-vidfast`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ text: streamEncrypted }) });
42
- if (decStreamData.status !== 200 || !decStreamData.result?.url) throw new Error();
43
- return { url: decStreamData.result.url, server: `VidFast - ${srv.name || 'Server'}`, type: decStreamData.result.url.includes('.m3u8') ? 'hls' : 'mp4', subtitles: (decStreamData.result.captions || []).map(c => ({ url: c.file, lang: c.label || 'Unknown' })), headers: { ...HEADERS, 'Origin': DOMAIN } };
44
- }));
45
- const allUrls = [];
46
- for (const r of results) if (r.status === 'fulfilled') allUrls.push(r.value);
47
- return allUrls.length ? { allUrls } : null;
48
- }
49
-
50
- export async function getSources(args) {
51
- const meta = await getVidfastMeta(args.id, args.s, args.e);
52
- return meta?.servers ? meta.servers.map(srv => `VidFast - ${srv.name}`) : [];
53
- }