@vyla-entertainment/sdk 1.0.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 (44) hide show
  1. package/LICENSE +407 -0
  2. package/README.md +44 -0
  3. package/package.json +31 -0
  4. package/src/config.js +519 -0
  5. package/src/downloads/downloads.js +8 -0
  6. package/src/downloads/sources/trendimovies.js +63 -0
  7. package/src/health.js +38 -0
  8. package/src/sdk.js +65 -0
  9. package/src/sources/123anime.js +57 -0
  10. package/src/sources/1embed.js +92 -0
  11. package/src/sources/anihq.js +112 -0
  12. package/src/sources/anineko.js +81 -0
  13. package/src/sources/anipm.js +70 -0
  14. package/src/sources/flaxmovies.js +14 -0
  15. package/src/sources/fsharetv.js +44 -0
  16. package/src/sources/fsonic.js +47 -0
  17. package/src/sources/fsonline.js +46 -0
  18. package/src/sources/goated.js +262 -0
  19. package/src/sources/hexa.js +82 -0
  20. package/src/sources/kisskh.js +60 -0
  21. package/src/sources/lookmovie.js +78 -0
  22. package/src/sources/mapple.js +115 -0
  23. package/src/sources/meowtv.js +111 -0
  24. package/src/sources/nebula.js +62 -0
  25. package/src/sources/nekowatch.js +57 -0
  26. package/src/sources/opstream.js +123 -0
  27. package/src/sources/peachify.js +78 -0
  28. package/src/sources/pengu.js +45 -0
  29. package/src/sources/purstream.js +104 -0
  30. package/src/sources/vidapi.js +115 -0
  31. package/src/sources/vidcore.js +43 -0
  32. package/src/sources/videasy.js +98 -0
  33. package/src/sources/vidfast.js +53 -0
  34. package/src/sources/vidlink.js +28 -0
  35. package/src/sources/vidnest.js +77 -0
  36. package/src/sources/vidrift.js +95 -0
  37. package/src/sources/vidrock.js +145 -0
  38. package/src/sources/vidsync.js +38 -0
  39. package/src/sources/vidup.js +79 -0
  40. package/src/sources/vidzee.js +120 -0
  41. package/src/sources/vixsrc.js +98 -0
  42. package/src/sources/xpass.js +40 -0
  43. package/src/subtitles.js +127 -0
  44. package/src/utils/helpers.js +194 -0
@@ -0,0 +1,115 @@
1
+ import { fetchJson, USER_AGENT } from '../utils/helpers.js';
2
+
3
+ const API_BASE = 'https://streamdata.vaplayer.ru/api.php';
4
+ const EMBED_ORIGIN = 'https://nextgencloudfabric.com';
5
+
6
+ export const VERIFY_HEADERS = {
7
+ 'User-Agent': USER_AGENT,
8
+ 'Referer': `${EMBED_ORIGIN}/`,
9
+ 'Origin': EMBED_ORIGIN
10
+ };
11
+
12
+ function extractStreamUrls(obj, urls = new Set()) {
13
+ if (!obj) return urls;
14
+ if (typeof obj === 'string') {
15
+ if (obj.startsWith('http') && (obj.includes('.m3u8') || obj.includes('.mp4') || obj.includes('.mpd'))) {
16
+ urls.add(obj);
17
+ }
18
+ } else if (Array.isArray(obj)) {
19
+ for (const item of obj) extractStreamUrls(item, urls);
20
+ } else if (typeof obj === 'object') {
21
+ for (const val of Object.values(obj)) {
22
+ extractStreamUrls(val, urls);
23
+ }
24
+ }
25
+ return urls;
26
+ }
27
+
28
+ export async function getStream({ id, s, e }) {
29
+ try {
30
+ const isTv = s != null && e != null;
31
+ const type = isTv ? 'tv' : 'movie';
32
+
33
+ const url = `${API_BASE}?tmdb=${id}&type=${type}${isTv ? `&season=${s}&episode=${e}` : ''}`;
34
+
35
+ const data = await fetchJson(url, {
36
+ headers: {
37
+ 'Accept': '*/*',
38
+ 'Origin': EMBED_ORIGIN,
39
+ 'Referer': `${EMBED_ORIGIN}/`,
40
+ 'User-Agent': USER_AGENT
41
+ },
42
+ signal: AbortSignal.timeout(10000)
43
+ });
44
+
45
+ const streamUrls = Array.from(extractStreamUrls(data));
46
+ if (!streamUrls.length) return null;
47
+
48
+ let subs = [];
49
+ if (data.subtitles && Array.isArray(data.subtitles)) {
50
+ subs = data.subtitles;
51
+ } else if (data.captions && Array.isArray(data.captions)) {
52
+ subs = data.captions;
53
+ }
54
+
55
+ const subtitles = subs.map(sub => ({
56
+ url: sub.url || sub.file || sub.src,
57
+ lang: sub.display || sub.label || sub.language || sub.lang || 'Unknown'
58
+ })).filter(s => s.url);
59
+
60
+ const allUrls = streamUrls.map(streamUrl => {
61
+ let type = 'mp4';
62
+ if (streamUrl.includes('.mpd')) type = 'dash';
63
+ else if (streamUrl.includes('.m3u8')) type = 'hls';
64
+
65
+ return {
66
+ url: streamUrl,
67
+ server: 'VidAPI',
68
+ quality: 'Auto',
69
+ type,
70
+ headers: VERIFY_HEADERS,
71
+ subtitles: subtitles.length > 0 ? subtitles : undefined,
72
+ skipProxy: false
73
+ };
74
+ });
75
+
76
+ return allUrls.length ? { allUrls } : null;
77
+ } catch {
78
+ return null;
79
+ }
80
+ }
81
+
82
+ export async function getSources() {
83
+ return ['VidAPI'];
84
+ }
85
+
86
+ export async function proxyStream({ url, res, proxyUtils: { fetchUpstream, rewriteM3u8 } }) {
87
+ try {
88
+ const upstream = await fetchUpstream(url, 0, VERIFY_HEADERS);
89
+ if (!upstream) return res.end();
90
+
91
+ const ct = (upstream.headers?.['content-type'] || '').toLowerCase();
92
+ const isM3u8 = ct.includes('mpegurl') || ct.includes('m3u8') || /\.m3u8?(\?|$)/i.test(url);
93
+
94
+ if (isM3u8) {
95
+ const chunks = [];
96
+ for await (const c of upstream) chunks.push(c);
97
+ let body = Buffer.concat(chunks).toString('utf8');
98
+
99
+ res.setHeader('Content-Type', 'application/vnd.apple.mpegurl');
100
+ res.setHeader('Access-Control-Allow-Origin', '*');
101
+
102
+ return res.end(rewriteM3u8(body, url, '&va=1&tt=1'));
103
+ }
104
+
105
+ res.setHeader('Content-Type', ct || 'application/octet-stream');
106
+ res.setHeader('Access-Control-Allow-Origin', '*');
107
+ res.setHeader('Cache-Control', 'public, max-age=3600');
108
+ upstream.pipe(res);
109
+ } catch {
110
+ if (!res.headersSent) {
111
+ res.statusCode = 502;
112
+ res.end();
113
+ }
114
+ }
115
+ }
@@ -0,0 +1,43 @@
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
+ }
@@ -0,0 +1,98 @@
1
+ import { getTmdbInfo, fetchJson, fetchText, USER_AGENT } from '../utils/helpers.js';
2
+
3
+ const WINGS_BASE = "https://api.wingsdatabase.com";
4
+ const HEADERS = { "Accept": "*/*", "Origin": "https://player.videasy.to", "Referer": "https://player.videasy.to/", "User-Agent": USER_AGENT };
5
+ const SERVERS = [
6
+ { id: 'jett', name: 'Jett' },
7
+ { id: 'cdn', name: 'Yoru' },
8
+ { id: 'tejo', name: 'Tejo' },
9
+ { id: 'neon2', name: 'Neon' },
10
+ { id: 'ym', name: 'Sage' },
11
+ { id: 'downloader2', name: 'Cypher' },
12
+ { id: 'm4uhd', name: 'Breach' },
13
+ { id: 'hdmovie', name: 'Vyse' },
14
+ { id: 'meine', name: 'Killjoy' },
15
+ { id: 'lamovie', name: 'Omen' },
16
+ { id: 'superflix', name: 'Raze' }
17
+ ];
18
+
19
+ export async function getStream({ id, s, e, server }) {
20
+ try {
21
+ const isTv = s != null && e != null;
22
+ const info = await getTmdbInfo(id, isTv ? 'tv' : 'movie');
23
+ if (!info?.titles?.length) return null;
24
+
25
+ const seedData = await fetchJson(`${WINGS_BASE}/seed?mediaId=${id}`, { headers: HEADERS, signal: AbortSignal.timeout(5000) });
26
+ const seed = seedData?.seed;
27
+ if (!seed) return null;
28
+
29
+ const encTitle = encodeURIComponent(encodeURIComponent(info.titles[0]));
30
+ let targets = SERVERS;
31
+
32
+ if (server && server !== 'all') {
33
+ const cleanName = server.replace('VidEasy - ', '');
34
+ targets = SERVERS.filter(sv => sv.name === cleanName);
35
+ if (!targets.length) targets = SERVERS;
36
+ }
37
+
38
+ const settled = await Promise.allSettled(targets.map(async srv => {
39
+ if (srv.id === 'cdn' && isTv) throw new Error();
40
+
41
+ let url = `${WINGS_BASE}/${srv.id}/sources-with-title?title=${encTitle}&mediaType=${isTv ? 'tv' : 'movie'}&year=${info.year || ''}&tmdbId=${id}&imdbId=${info.imdbId || "tt0000000"}&enc=2&seed=${seed}`;
42
+ if (isTv) url += `&episodeId=${e}&seasonId=${s}`;
43
+
44
+ const encText = await fetchText(url, { headers: HEADERS, signal: AbortSignal.timeout(10000) });
45
+ const decJson = await fetchJson("https://enc-dec.app/api/dec-videasy", {
46
+ method: 'POST',
47
+ headers: { 'Content-Type': 'application/json' },
48
+ body: JSON.stringify({ text: encText, id: String(id), seed }),
49
+ signal: AbortSignal.timeout(10000)
50
+ });
51
+
52
+ if (decJson.status !== 200 || !decJson.result) throw new Error();
53
+
54
+ let sourcesArray = [];
55
+ let subsArray = [];
56
+
57
+ if (Array.isArray(decJson.result)) {
58
+ sourcesArray = decJson.result;
59
+ } else if (decJson.result.sources && Array.isArray(decJson.result.sources)) {
60
+ sourcesArray = decJson.result.sources;
61
+ if (decJson.result.subtitles && Array.isArray(decJson.result.subtitles)) {
62
+ subsArray = decJson.result.subtitles.map(sub => ({
63
+ url: sub.url || sub.file,
64
+ lang: sub.language || sub.lang || sub.label || 'Unknown'
65
+ })).filter(sub => sub.url);
66
+ }
67
+ } else {
68
+ sourcesArray = [decJson.result];
69
+ }
70
+
71
+ return sourcesArray.map(res => {
72
+ const streamUrl = res.url || res.file || res.link || res.playlist || res.stream;
73
+ return streamUrl ? {
74
+ url: streamUrl,
75
+ quality: res.quality || "Auto",
76
+ server: `VidEasy - ${srv.name}`,
77
+ type: streamUrl.includes('.m3u8') ? 'hls' : 'mp4',
78
+ headers: HEADERS,
79
+ subtitles: subsArray.length > 0 ? subsArray : undefined,
80
+ skipProxy: false,
81
+ skipVerify: true,
82
+ skipHlsCheck: true
83
+ } : null;
84
+ }).filter(Boolean);
85
+ }));
86
+
87
+ const allUrls = [];
88
+ for (const r of settled) {
89
+ if (r.status === 'fulfilled' && r.value) allUrls.push(...r.value);
90
+ }
91
+
92
+ return allUrls.length ? { allUrls } : null;
93
+ } catch { return null; }
94
+ }
95
+
96
+ export async function getSources() {
97
+ return SERVERS.map(s => `VidEasy - ${s.name}`);
98
+ }
@@ -0,0 +1,53 @@
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
+ }
@@ -0,0 +1,28 @@
1
+ import { fetchJson, USER_AGENT } from '../utils/helpers.js';
2
+
3
+ const BASE = "https://vidlink.pro";
4
+ const HEADERS = { 'User-Agent': USER_AGENT, Origin: BASE, Referer: `${BASE}/` };
5
+
6
+ export async function getStream({ id, s, e }) {
7
+ try {
8
+ const encData = await fetchJson(`https://enc-dec.app/api/enc-vidlink?text=${id}`, { signal: AbortSignal.timeout(6000) });
9
+ if (encData?.status !== 200 || !encData?.result) return null;
10
+ const data = await fetchJson(s ? `${BASE}/api/b/tv/${encData.result}/${s}/${e || 1}` : `${BASE}/api/b/movie/${encData.result}`, { headers: HEADERS, signal: AbortSignal.timeout(10000) });
11
+ const stream = data?.stream;
12
+ if (!stream) return null;
13
+ if (stream.type === 'file' && stream.qualities) {
14
+ let picked = null;
15
+ for (const q of ['1080', '720', '480', '360']) if (stream.qualities[q]?.url) { picked = stream.qualities[q].url; break; }
16
+ if (!picked) picked = stream.qualities[Object.keys(stream.qualities)[0]]?.url;
17
+ if (picked) return { allUrls: [{ url: picked, type: 'mp4', audio: 'sub', server: 'Vidlink', skipHlsCheck: true, skipCache: true }] };
18
+ } else if (stream.playlist) {
19
+ return { allUrls: [{ url: stream.playlist, type: 'hls', audio: 'sub', server: 'Vidlink', skipHlsCheck: true, skipCache: true }] };
20
+ }
21
+ return null;
22
+ } catch { return null; }
23
+ }
24
+
25
+ export async function getSources(args) {
26
+ const stream = await getStream(args);
27
+ return stream?.allUrls ? [...new Set(stream.allUrls.map(u => u.server))] : [];
28
+ }
@@ -0,0 +1,77 @@
1
+ import { fetchJson, fetchText, USER_AGENT } from '../utils/helpers.js';
2
+
3
+ const BASE_URL = 'https://vidnest.fun';
4
+ const API_BASE_URL = 'https://new.vidnest.fun';
5
+ const REQUEST_HEADERS = { 'User-Agent': USER_AGENT, 'Accept': 'application/json, text/javascript, */*; q=0.01', 'Accept-Language': 'en-US,en;q=0.9', 'Referer': `${BASE_URL}/`, 'Origin': BASE_URL };
6
+ const CDN_PROXY_HEADERS = { 'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:137.0) Gecko/20100101 Firefox/137.0', 'accept': '*/*', 'accept-language': 'en-US,en;q=0.5', 'origin': 'https://megaplay.buzz', 'referer': 'https://megaplay.buzz/' };
7
+
8
+ export const CDN_HEADERS = [
9
+ { pattern: /letsgocdn\d+\.shop/i, headers: { 'Referer': BASE_URL, 'Origin': BASE_URL, 'User-Agent': USER_AGENT } },
10
+ { pattern: /cdn\.mewstream\.buzz/i, headers: { 'Referer': BASE_URL, 'Origin': BASE_URL, 'User-Agent': USER_AGENT } }
11
+ ];
12
+
13
+ const ALPHABET = 'RB0fpH8ZEyVLkv7c2i6MAJ5u3IKFDxlS1NTsnGaqmXYdUrtzjwObCgQP94hoeW+/=';
14
+ const REV_MAP = new Uint8Array(256);
15
+ for (let i = 0; i < ALPHABET.length; i++) REV_MAP[ALPHABET.charCodeAt(i)] = i;
16
+
17
+ function decrypt(payload) {
18
+ const len = payload.length;
19
+ const bytes = new Uint8Array(len);
20
+ let j = 0;
21
+ for (let i = 0; i < len; i += 4) {
22
+ const c0 = REV_MAP[payload.charCodeAt(i)] ?? 64;
23
+ const c1 = REV_MAP[payload.charCodeAt(i + 1)] ?? 64;
24
+ const c2 = REV_MAP[payload.charCodeAt(i + 2)] ?? 64;
25
+ const c3 = REV_MAP[payload.charCodeAt(i + 3)] ?? 64;
26
+ bytes[j++] = (c0 << 2) | (c1 >> 4);
27
+ if (c2 !== 64) bytes[j++] = ((c1 & 15) << 4) | (c2 >> 2);
28
+ if (c3 !== 64) bytes[j++] = ((c2 & 3) << 6) | c3;
29
+ }
30
+ return JSON.parse(new TextDecoder().decode(bytes.subarray(0, j)));
31
+ }
32
+
33
+ async function tmdbToAnilist(tmdbId, season) {
34
+ try {
35
+ const data = await fetchJson(`https://api.ani.zip/mappings?tmdb_id=${tmdbId}&type=tv&season=${season || 1}`, { signal: AbortSignal.timeout(6000) });
36
+ return data?.mappings?.[0]?.anilist_id || null;
37
+ } catch { return null; }
38
+ }
39
+
40
+ const SERVER_KEYS = ['hollymoviehd', 'allmovies', 'catflix', 'purstream', 'lamda', 'vidlink', 'klikxxi'];
41
+
42
+ export async function getStream({ id, s, e, tmdbApiKey, audio }) {
43
+ if (s) {
44
+ const k = tmdbApiKey;
45
+ const showData = k ? await fetchJson(`https://api.themoviedb.org/3/tv/${id}?api_key=${k}`, { signal: AbortSignal.timeout(5000) }).catch(() => null) : null;
46
+ if (showData?.genres?.some(g => g.id === 16)) {
47
+ const anilistId = await tmdbToAnilist(id, s);
48
+ if (anilistId) {
49
+ try {
50
+ const json = await fetchJson(`${API_BASE_URL}/hianime/anime/${anilistId}/${e || 1}/${audio === 'dub' ? 'dub' : 'sub'}`, { headers: REQUEST_HEADERS, signal: AbortSignal.timeout(15000) });
51
+ const data = json.encrypted ? decrypt(json.data) : json.data;
52
+ const file = data?.sources?.[0]?.file;
53
+ if (file) return { url: `https://megacloud.animanga.fun/proxy?url=${encodeURIComponent(file)}&headers=${encodeURIComponent(JSON.stringify(CDN_PROXY_HEADERS))}`, headers: REQUEST_HEADERS };
54
+ } catch { }
55
+ }
56
+ }
57
+ }
58
+ if (audio === 'dub') return null;
59
+ const segment = s ? `tv/${id}/${s}/${e || 1}` : `movie/${id}`;
60
+ const settled = await Promise.allSettled(SERVER_KEYS.map(async server => {
61
+ const json = await fetchJson(`${API_BASE_URL}/${server}/${segment}`, { headers: REQUEST_HEADERS, signal: AbortSignal.timeout(10000) });
62
+ const data = json.encrypted ? decrypt(json.data) : json.data;
63
+ const file = data?.sources?.[0]?.file ?? data?.streams?.[0]?.url ?? data?.url?.[0]?.link ?? data?.data?.stream?.playlist;
64
+ if (file) return { url: file, server, headers: REQUEST_HEADERS };
65
+ throw new Error();
66
+ }));
67
+ const allUrls = [];
68
+ for (const r of settled) if (r.status === 'fulfilled') allUrls.push(r.value);
69
+ return allUrls.length ? { allUrls } : null;
70
+ }
71
+
72
+ export async function getSources(args) {
73
+ const res = await getStream(args);
74
+ return res?.allUrls ? res.allUrls.map(u => u.server) : [];
75
+ }
76
+
77
+ export const VERIFY_HEADERS = { ...REQUEST_HEADERS };
@@ -0,0 +1,95 @@
1
+ 'use strict';
2
+
3
+ const BASE = "https://embed.vidrift.in";
4
+
5
+ const DEFAULT_HEADERS = {
6
+ "Accept": "*/*",
7
+ "Accept-Language": "en-US,en;q=0.9",
8
+ "Cache-Control": "no-cache",
9
+ "Pragma": "no-cache",
10
+ "Referer": `${BASE}/`,
11
+ "Origin": BASE,
12
+ "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/150.0.0.0 Safari/537.36 Edg/150.0.0.0"
13
+ };
14
+
15
+ function decodeStreams(encodedStr) {
16
+ try {
17
+ const base64Str = encodedStr.replace(/-/g, '+').replace(/_/g, '/');
18
+
19
+ const binaryStr = typeof atob === 'function'
20
+ ? atob(base64Str)
21
+ : Buffer.from(base64Str, 'base64').toString('binary');
22
+
23
+ const bytes = new Uint8Array(binaryStr.length);
24
+ for (let i = 0; i < binaryStr.length; i++) {
25
+ bytes[i] = binaryStr.charCodeAt(i);
26
+ }
27
+
28
+ const key = bytes[0];
29
+ let decodedStr = '';
30
+ for (let i = 1; i < bytes.length; i++) {
31
+ decodedStr += String.fromCharCode(bytes[i] ^ key);
32
+ }
33
+
34
+ return JSON.parse(decodedStr);
35
+ } catch (e) {
36
+ return [];
37
+ }
38
+ }
39
+
40
+ export async function getStream(args) {
41
+ const { id, s, e, server: serverName } = args;
42
+ const type = s != null && e != null ? "tv" : "movie";
43
+
44
+ const endpoint = type === "tv"
45
+ ? `${BASE}/embed/tv/${id}/${s}/${e}`
46
+ : `${BASE}/embed/movie/${id}`;
47
+
48
+ try {
49
+ const res = await fetch(endpoint, {
50
+ headers: {
51
+ ...DEFAULT_HEADERS,
52
+ "Accept": "text/html",
53
+ },
54
+ signal: AbortSignal.timeout(10000)
55
+ });
56
+
57
+ if (!res.ok) return null;
58
+
59
+ const html = await res.text();
60
+
61
+ const match = html.match(/var\s+_s\s*=\s*['"]([^'"]+)['"]/);
62
+ if (!match) return null;
63
+
64
+ const encodedStreams = match[1];
65
+ const streamsData = decodeStreams(encodedStreams);
66
+
67
+ if (!Array.isArray(streamsData) || !streamsData.length) {
68
+ return null;
69
+ }
70
+
71
+ const allUrls = streamsData.map((stream, index) => ({
72
+ url: stream.url.startsWith("http")
73
+ ? stream.url
74
+ : `${BASE}${stream.url}`,
75
+ server: stream.label || `VidRift - Server ${index + 1}`,
76
+ type: "hls",
77
+ headers: DEFAULT_HEADERS
78
+ }));
79
+
80
+ if (serverName && serverName !== "all") {
81
+ const filtered = allUrls.filter(x => x.server === serverName);
82
+ return filtered.length ? { allUrls: filtered } : null;
83
+ }
84
+
85
+ return { allUrls };
86
+ } catch (err) {
87
+ return null;
88
+ }
89
+ }
90
+
91
+ export async function getSources(args) {
92
+ const res = await getStream(args);
93
+ if (!res?.allUrls) return ["VidRift - Server 1"];
94
+ return res.allUrls.map(x => x.server);
95
+ }
@@ -0,0 +1,145 @@
1
+ import { webcrypto } from 'node:crypto';
2
+ import { fetchJson, USER_AGENT } from '../utils/helpers.js';
3
+
4
+ const GCM_HEX_KEY = '7f3e9c2a8b5d1f4e6a9c3b7d2e5f8a1c4b6d9e2f5a8c1b4d7e9f2a5c8b1d4e7f';
5
+ const BASE_URL = 'https://vidrock.ru/';
6
+ const PROXY_PREFIX = 'https://proxy.vidrock.store/';
7
+
8
+ let cryptoKey = null;
9
+
10
+ function hexToBytes(hex) {
11
+ const bytes = new Uint8Array(hex.length / 2);
12
+
13
+ for (let i = 0; i < bytes.length; i++) {
14
+ bytes[i] = parseInt(hex.slice(i * 2, i * 2 + 2), 16);
15
+ }
16
+
17
+ return bytes;
18
+ }
19
+
20
+ function base64UrlToBytes(value) {
21
+ let base64 = value.replace(/-/g, '+').replace(/_/g, '/');
22
+
23
+ const padding = base64.length % 4;
24
+
25
+ if (padding === 2) {
26
+ base64 += '==';
27
+ } else if (padding === 3) {
28
+ base64 += '=';
29
+ } else if (padding === 1) {
30
+ throw new Error('Invalid base64url');
31
+ }
32
+
33
+ return new Uint8Array(Buffer.from(base64, 'base64'));
34
+ }
35
+
36
+ async function getKey() {
37
+ if (cryptoKey) return cryptoKey;
38
+
39
+ const keyBytes = hexToBytes(GCM_HEX_KEY);
40
+
41
+ cryptoKey = await webcrypto.subtle.importKey(
42
+ 'raw',
43
+ keyBytes,
44
+ 'AES-GCM',
45
+ false,
46
+ ['decrypt']
47
+ );
48
+
49
+ return cryptoKey;
50
+ }
51
+
52
+ async function decryptStreamUrl(value) {
53
+ try {
54
+ const data = base64UrlToBytes(value);
55
+
56
+ if (data.length < 28) {
57
+ throw new Error('Ciphertext too short');
58
+ }
59
+
60
+ const iv = data.slice(0, 12);
61
+ const ciphertext = data.slice(12);
62
+
63
+ const key = await getKey();
64
+
65
+ const decrypted = await webcrypto.subtle.decrypt(
66
+ {
67
+ name: 'AES-GCM',
68
+ iv
69
+ },
70
+ key,
71
+ ciphertext
72
+ );
73
+
74
+ return new TextDecoder().decode(decrypted);
75
+ } catch (error) {
76
+ return null;
77
+ }
78
+ }
79
+
80
+ export async function getStream({ id, s, e }) {
81
+ try {
82
+ const path = s
83
+ ? `tv/${id}/${s}/${e || 1}`
84
+ : `movie/${id}`;
85
+
86
+ const apiData = await fetchJson(`${BASE_URL}api/${path}`, {
87
+ headers: {
88
+ 'User-Agent': USER_AGENT,
89
+ Referer: BASE_URL,
90
+ Origin: BASE_URL
91
+ }
92
+ });
93
+
94
+ if (!apiData || typeof apiData !== 'object') {
95
+ return null;
96
+ }
97
+
98
+ const streams = [];
99
+
100
+ for (const [provider, info] of Object.entries(apiData)) {
101
+ if (!info?.url) {
102
+ continue;
103
+ }
104
+
105
+ const decryptedUrl = await decryptStreamUrl(info.url);
106
+
107
+ if (!decryptedUrl) {
108
+ continue;
109
+ }
110
+
111
+ let url = decryptedUrl;
112
+
113
+ if (url.startsWith(PROXY_PREFIX)) {
114
+ url = decodeURIComponent(
115
+ url.slice(PROXY_PREFIX.length).replace(/^\/+/, '')
116
+ );
117
+ }
118
+
119
+ const isXpass = url.includes('xpass.top');
120
+
121
+ streams.push({
122
+ name: provider,
123
+ url,
124
+ file: url,
125
+ stream: url,
126
+ src: url,
127
+ quality: info.type || 'auto',
128
+ type: info.type || 'hls',
129
+ headers: {
130
+ 'User-Agent': USER_AGENT,
131
+ Referer: isXpass
132
+ ? 'https://play.xpass.top/'
133
+ : BASE_URL,
134
+ Origin: isXpass
135
+ ? 'https://play.xpass.top'
136
+ : 'https://vidrock.ru'
137
+ }
138
+ });
139
+ }
140
+
141
+ return streams.length ? { streams } : null;
142
+ } catch (error) {
143
+ return null;
144
+ }
145
+ }