@vyla-entertainment/sdk 1.6.0 → 1.9.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,262 +0,0 @@
1
- import crypto from "crypto";
2
-
3
- export const ID = "goated";
4
- export const DOMAIN = "https://goated.cx";
5
- export const RETURN_ALL_URLS = true;
6
-
7
- const UA = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36";
8
-
9
- const enc = new TextEncoder();
10
- const info = enc.encode("goated-api-v1");
11
- const salt = enc.encode("goated-stream-salt");
12
-
13
- const generateRandomIP = () => {
14
- const p = () => Math.floor(Math.random() * 255);
15
- let first = p();
16
- while ([0, 10, 127, 169, 172, 192].includes(first)) first = p();
17
- return `${first}.${p()}.${p()}.${p()}`;
18
- };
19
-
20
- let cachedSession = null;
21
- let sessionExpiresAt = 0;
22
- let cachedProviders = null;
23
- let providersExpiresAt = 0;
24
-
25
- async function getValidSession(sessionIp) {
26
- const now = Date.now();
27
- if (cachedSession && now < sessionExpiresAt) {
28
- return cachedSession;
29
- }
30
- try {
31
- const chalRes = await fetch(`${DOMAIN}/api/auth/challenge`, {
32
- headers: {
33
- "User-Agent": UA,
34
- "X-Forwarded-For": sessionIp,
35
- "X-Real-IP": sessionIp,
36
- Origin: DOMAIN,
37
- Referer: DOMAIN + "/",
38
- },
39
- signal: AbortSignal.timeout(7000),
40
- });
41
-
42
- if (!chalRes.ok) throw new Error();
43
- const chalData = await chalRes.json();
44
- const { challenge, difficulty } = chalData;
45
- const targetPrefix = "0".repeat(Math.ceil((difficulty || 16) / 4));
46
- let nonceStr = "";
47
-
48
- for (let i = 0; i < 5000000; i++) {
49
- const nonce = Math.random().toString(36).substring(2, 10);
50
- const hash = crypto.createHash("sha256").update(`${challenge}:${nonce}`).digest("hex");
51
- if (hash.startsWith(targetPrefix)) {
52
- nonceStr = nonce;
53
- break;
54
- }
55
- }
56
-
57
- if (!nonceStr) throw new Error();
58
-
59
- const res = await fetch(`${DOMAIN}/api/auth/session`, {
60
- method: "POST",
61
- headers: {
62
- "Content-Type": "application/json",
63
- "User-Agent": UA,
64
- "X-Forwarded-For": sessionIp,
65
- "X-Real-IP": sessionIp,
66
- Origin: DOMAIN,
67
- Referer: DOMAIN + "/",
68
- },
69
- body: JSON.stringify({ challenge, nonce: nonceStr }),
70
- signal: AbortSignal.timeout(7000),
71
- });
72
-
73
- if (!res.ok) throw new Error();
74
- const data = await res.json();
75
- if (!data.token) throw new Error();
76
-
77
- cachedSession = data.token;
78
- sessionExpiresAt = now + (data.expiresIn || 600) * 1000 - 30000;
79
- return cachedSession;
80
- } catch (e) {
81
- throw new Error();
82
- }
83
- }
84
-
85
- async function getProviders() {
86
- const now = Date.now();
87
- if (cachedProviders && now < providersExpiresAt) return cachedProviders;
88
- try {
89
- const res = await fetch(`${DOMAIN}/api/providers`, {
90
- headers: { "User-Agent": UA },
91
- signal: AbortSignal.timeout(5000)
92
- });
93
- if (!res.ok) return [];
94
- const data = await res.json();
95
- cachedProviders = data.providers || [];
96
- providersExpiresAt = now + 3600 * 1000;
97
- return cachedProviders;
98
- } catch (e) {
99
- return [];
100
- }
101
- }
102
-
103
- async function deriveKey(tokenString) {
104
- const rawKey = await crypto.subtle.importKey("raw", enc.encode(tokenString), "HKDF", false, ["deriveKey"]);
105
- return crypto.subtle.deriveKey(
106
- { name: "HKDF", hash: "SHA-256", salt, info },
107
- rawKey,
108
- { name: "AES-GCM", length: 256 },
109
- false,
110
- ["encrypt", "decrypt"],
111
- );
112
- }
113
-
114
- async function encryptParams(payloadObj, key) {
115
- const iv = crypto.getRandomValues(new Uint8Array(12));
116
- const data = enc.encode(JSON.stringify(payloadObj));
117
- const ct = await crypto.subtle.encrypt({ name: "AES-GCM", iv }, key, data);
118
- const combined = new Uint8Array(iv.length + ct.byteLength);
119
- combined.set(iv);
120
- combined.set(new Uint8Array(ct), iv.length);
121
- return Buffer.from(combined).toString("base64");
122
- }
123
-
124
- async function decryptUrl(b64Url, key) {
125
- const raw = Buffer.from(b64Url, "base64");
126
- const iv = raw.slice(0, 12);
127
- const ct = raw.slice(12);
128
- try {
129
- const plain = await crypto.subtle.decrypt({ name: "AES-GCM", iv }, key, ct);
130
- return Buffer.from(plain).toString("utf8");
131
- } catch {
132
- return null;
133
- }
134
- }
135
-
136
- async function decryptDeep(obj, key) {
137
- if (!obj || typeof obj !== "object") return obj;
138
- if (Array.isArray(obj)) return Promise.all(obj.map(x => decryptDeep(x, key)));
139
-
140
- const out = {};
141
- for (const [k, v] of Object.entries(obj)) {
142
- if ((k === "url" || k === "file") && typeof v === "string" && v.length > 40) {
143
- try {
144
- const dec = await decryptUrl(v, key);
145
- out[k] = dec ? dec : v;
146
- } catch {
147
- out[k] = v;
148
- }
149
- } else if (k === "links" && Array.isArray(v)) {
150
- out[k] = await Promise.all(v.map(async x => {
151
- if (typeof x === "string" && x.length > 40) {
152
- try {
153
- const dec = await decryptUrl(x, key);
154
- return dec ? dec : x;
155
- } catch {
156
- return x;
157
- }
158
- }
159
- return x;
160
- }));
161
- } else {
162
- out[k] = await decryptDeep(v, key);
163
- }
164
- }
165
- return out;
166
- }
167
-
168
- export async function getStream({ id, s, e, clientIP }) {
169
- try {
170
- const sessionIp = generateRandomIP();
171
- const sessionJwt = await getValidSession(sessionIp);
172
- const actualKey = await deriveKey(sessionJwt);
173
-
174
- const isTv = s && e && s !== "null" && e !== "null";
175
- const contentType = isTv ? "tv" : "movies";
176
- const refererPath = isTv ? `tv/${id}-${s}-${e}/play` : `movie/${id}/play`;
177
-
178
- let providers = await getProviders();
179
- let activeProviders = providers.filter(p => p.active && (p.endpoints || []).includes(contentType));
180
- if (activeProviders.length === 0) activeProviders = [{ name: null }];
181
-
182
- const fetchProviderStream = async (provider) => {
183
- try {
184
- const payload = isTv
185
- ? { type: "tv", id: id.toString(), season: s.toString(), episode: e.toString() }
186
- : { type: "movie", id: id.toString() };
187
-
188
- if (provider.name) payload.provider = provider.name;
189
-
190
- const dParam = await encryptParams(payload, actualKey);
191
-
192
- const streamRes = await fetch(`${DOMAIN}/api/stream?d=${encodeURIComponent(dParam)}`, {
193
- headers: {
194
- Authorization: `Bearer ${sessionJwt}`,
195
- "User-Agent": UA,
196
- "X-Forwarded-For": sessionIp,
197
- "X-Real-IP": sessionIp,
198
- Origin: DOMAIN,
199
- Referer: `${DOMAIN}/${refererPath}`,
200
- },
201
- signal: AbortSignal.timeout(10000)
202
- });
203
-
204
- if (!streamRes.ok) return [];
205
- let rawData = await streamRes.json();
206
-
207
- if (rawData.enc) {
208
- const raw = Buffer.from(rawData.enc, "base64");
209
- const decIv = raw.slice(0, 12);
210
- const decCt = raw.slice(12);
211
- const decrypted = await crypto.subtle.decrypt({ name: "AES-GCM", iv: decIv }, actualKey, decCt);
212
- rawData = JSON.parse(Buffer.from(decrypted).toString("utf-8"));
213
- }
214
-
215
- const streamData = await decryptDeep(rawData, actualKey);
216
-
217
- if (!streamData.ok || !streamData.streams || streamData.streams.length === 0) return [];
218
-
219
- const validStreams = [];
220
- for (const stream of streamData.streams) {
221
- if (!stream.url) continue;
222
- if (stream.quality && stream.quality.toUpperCase() !== "AUTO" && streamData.streams.some(st => st.quality && st.quality.toUpperCase() === "AUTO")) continue;
223
-
224
- let finalUrl = stream.url;
225
- if (finalUrl.startsWith("/api/")) finalUrl = `${DOMAIN}${finalUrl}`;
226
-
227
- validStreams.push({
228
- url: finalUrl,
229
- headers: {
230
- "User-Agent": UA,
231
- Referer: DOMAIN + "/",
232
- Origin: DOMAIN,
233
- },
234
- server: stream.source || stream.server || provider.nickname || provider.name || "Goated",
235
- quality: stream.quality || "Auto",
236
- type: finalUrl.includes(".mp4") ? "mp4" : "hls",
237
- skipVerify: true,
238
- skipProxy: true,
239
- });
240
- }
241
- return validStreams;
242
- } catch (err) {
243
- return [];
244
- }
245
- };
246
-
247
- const promises = activeProviders.map(p => fetchProviderStream(p));
248
- const results = await Promise.allSettled(promises);
249
-
250
- const allUrls = [];
251
- for (const res of results) {
252
- if (res.status === "fulfilled" && res.value.length > 0) {
253
- allUrls.push(...res.value);
254
- }
255
- }
256
-
257
- if (allUrls.length > 0) {
258
- return { ...allUrls[0], allUrls };
259
- }
260
- } catch (err) { }
261
- return null;
262
- }
@@ -1,62 +0,0 @@
1
- import { fetchJson } from '../utils/helpers.js';
2
-
3
- const BASE_URL = 'https://nebula.work.gd/private/7f6b9d179ba7ecd77a39520e';
4
-
5
- async function getImdbId(tmdbApiKey, tmdbId, s, e) {
6
- const key = tmdbApiKey;
7
- if (!key) return null;
8
- try {
9
- const type = s != null && e != null ? 'tv' : 'movie';
10
- const url = `https://api.themoviedb.org/3/${type}/${tmdbId}/external_ids?api_key=${key}`;
11
- const res = await fetchJson(url);
12
- return res?.imdb_id || null;
13
- } catch(err) {
14
- return null;
15
- }
16
- }
17
-
18
- export async function getStream({ id, s, e, tmdbApiKey }) {
19
- try {
20
- const imdbId = await getImdbId(tmdbApiKey, id, s, e);
21
- if (!imdbId) return null;
22
-
23
- const path = s != null && e != null
24
- ? `/stream/series/${imdbId}:${s}:${e}.json`
25
- : `/stream/movie/${imdbId}.json`;
26
-
27
- const response = await fetch(`${BASE_URL}${path}`, {
28
- headers: {
29
- 'Accept': 'application/json'
30
- },
31
- signal: AbortSignal.timeout(15000)
32
- });
33
-
34
- if (!response.ok) return null;
35
-
36
- const data = await response.json();
37
- if (!data?.streams?.length) return null;
38
-
39
- const allUrls = data.streams
40
- .filter(stream => stream.url && stream.url.trim() !== '' && !stream.url.includes('hubcloud'))
41
- .map(stream => {
42
- const headers = stream.behaviorHints?.proxyHeaders?.request || {};
43
-
44
- let serverName = stream.name || 'Unknown';
45
- if (serverName.includes('\n')) serverName = serverName.split('\n')[0];
46
-
47
- const filename = stream.filename || stream.behaviorHints?.filename;
48
- if (filename) serverName += ` - ${filename}`;
49
-
50
- return {
51
- url: stream.url,
52
- server: `Nebula - ${serverName}`,
53
- type: stream.url.includes('.m3u8') ? 'hls' : 'mp4',
54
- headers: Object.keys(headers).length > 0 ? headers : undefined,
55
- skipProxy: stream.behaviorHints?.notWebReady === false
56
- };
57
- }).filter(Boolean);
58
- return allUrls.length ? { allUrls } : null;
59
- } catch (err) {
60
- return null;
61
- }
62
- }