@vyla-entertainment/sdk 1.5.0 → 1.6.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/.env.example ADDED
@@ -0,0 +1 @@
1
+ TMDB_API_KEY=your_tmdb_api_key_here
package/README.md CHANGED
@@ -11,36 +11,8 @@ npm install @vyla-entertainment/sdk
11
11
  pnpm install @vyla-entertainment/sdk
12
12
  ```
13
13
 
14
- ## Usage
15
- Start by creating a VylaSDK element with your TMDB api key.
16
- ```js
17
- import VylaSDK from "@vyla-entertainment/sdk";
18
-
19
- const sdk = new VylaSDK({
20
- tmdbApiKey: ...
21
- });
22
- ```
23
-
24
- You can now use this to access Vyla's content.
25
- ```js
26
- // Get sources
27
- // Args: excludeDisabled
28
- sdk.getSources(false); -> {key, timeout, label, ...}[]
29
-
30
- // Get subtitles
31
- // Args: tmdb id, season?, episode?
32
- await sdk.getSubtitles('0000000', null, null); -> {label, file, type, source}[]
33
-
34
- // Probe & test a source
35
- // Args: source key
36
- await sdk.probeSource('nebula'); -> {ok, ms}
37
-
38
- // Probe & test all sources
39
- await sdk.probeAllSources(); -> {'key': {ok, ms}, ...}
14
+ View our full documentation at https://docs.vyla.cc/sdk
40
15
 
41
- // Get HLS streams
42
- // Args: source key, tmdb id, season?, episode?, clientIP?
43
- await sdk.getStream('nebula', '0000000', null, null, null) -> {allUrls: {url, quality, label?, server?, type?, skipProxy?, skipVerify?, headers? ...}[]}
44
16
  ```
45
17
 
46
18
  # [DMCA](https://docs.vyla.cc/misc/dmca)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vyla-entertainment/sdk",
3
- "version": "1.5.0",
3
+ "version": "1.6.0",
4
4
  "scripts": {
5
5
  "test": "node test/index.js"
6
6
  },
package/src/config.js CHANGED
@@ -1,3 +1,10 @@
1
+ // Currently active sources reported on 7:47:50 PM PST 7/6/2026
2
+
3
+ // None Anime Tested: 936075
4
+ // Anime Tested: 37854
5
+
6
+ // Fshare/Fsonic Tested: 155 ( they don't have Michael )
7
+
1
8
  export const SOURCES = [
2
9
  {
3
10
  key: '1embed',
@@ -107,6 +114,18 @@ export const SOURCES = [
107
114
  },
108
115
  },
109
116
 
117
+ {
118
+ key: 'cinejoy',
119
+ label: 'Cinejoy',
120
+ sourceFile: 'cinejoy',
121
+ proxyParam: 'cj',
122
+ timeout: 30000,
123
+ jitter: 500,
124
+ retries: 1,
125
+ multiUrl: true,
126
+ skipProxy: true,
127
+ },
128
+
110
129
  {
111
130
  key: 'flaxmovies',
112
131
  label: 'FlaxMovies',
@@ -512,6 +531,7 @@ export const SOURCES = [
512
531
  retries: 2,
513
532
  skipProxy: true,
514
533
  },
534
+
515
535
  ];
516
536
 
517
537
  export const HEALTH_PROBE_ID = '155';
@@ -1,8 +1,12 @@
1
1
  import { getDownloads, getDownloadsTv } from "./sources/trendimovies.js";
2
2
 
3
3
  export async function fetchDownloads(id, s = null, e = null) {
4
- const res = s != null && e != null
5
- ? await getDownloadsTv(id, s, e)
6
- : await getDownloads(id);
7
- return res;
4
+ try {
5
+ const res = s != null && e != null
6
+ ? await getDownloadsTv(id, s, e)
7
+ : await getDownloads(id);
8
+ return res || [];
9
+ } catch {
10
+ return [];
11
+ }
8
12
  }
@@ -0,0 +1,210 @@
1
+ import crypto from "crypto";
2
+
3
+ export const ID = "cinejoy";
4
+ export const DOMAIN = "https://cinejoy.to";
5
+ export const API_DOMAIN = "https://api.shegu.st";
6
+ export const ENC_DEC_API = "https://enc-dec.app/api";
7
+ export const RETURN_ALL_URLS = true;
8
+
9
+ const UA = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/137.0.0.0 Safari/537.36";
10
+
11
+ function countLeadingZeroBits(buffer) {
12
+ let count = 0;
13
+ for (const value of buffer) {
14
+ if (value === 0) {
15
+ count += 8;
16
+ continue;
17
+ }
18
+ count += 8 - value.toString(2).length;
19
+ break;
20
+ }
21
+ return count;
22
+ }
23
+
24
+ function solveChallenge(data) {
25
+ const saltStr = `pow2-salt|${data.s}|${data.b}`;
26
+ const salt = crypto.createHash("sha256").update(saltStr).digest();
27
+ const n = data.n;
28
+ const r = data.r;
29
+ const p = data.p;
30
+ const d = data.d;
31
+ const b = data.b;
32
+ const s = data.s;
33
+
34
+ for (let counter = 0; counter < 1000000; counter++) {
35
+ const payload = `pow2|${b}|${s}|${counter}`;
36
+ const result = crypto.scryptSync(payload, salt, 32, {
37
+ N: n,
38
+ r: r,
39
+ p: p,
40
+ maxmem: 128 * n * r * 2
41
+ });
42
+
43
+ if (countLeadingZeroBits(result) >= d) {
44
+ const solved = { ...data, c: counter };
45
+ return Buffer.from(JSON.stringify(solved)).toString("base64");
46
+ }
47
+ }
48
+ throw new Error("Challenge solve failed");
49
+ }
50
+
51
+ async function getTmdbMetadata(tmdbId, isTv) {
52
+ const apiKey = process.env.TMDB_API_KEY;
53
+ if (!apiKey) return null;
54
+ const type = isTv ? "tv" : "movie";
55
+ try {
56
+ const url = `https://api.themoviedb.org/3/${type}/${tmdbId}?api_key=${apiKey}&append_to_response=external_ids`;
57
+ const res = await fetch(url, { signal: AbortSignal.timeout(10000) });
58
+ if (!res.ok) return null;
59
+ const data = await res.json();
60
+ const title = isTv ? data.name : data.title;
61
+ const year = isTv ? (data.first_air_date ? data.first_air_date.split("-")[0] : "") : (data.release_date ? data.release_date.split("-")[0] : "");
62
+ const imdbId = data.external_ids?.imdb_id || "";
63
+ return { title, year, imdbId };
64
+ } catch (err) {
65
+ return null;
66
+ }
67
+ }
68
+
69
+ export async function getStream({ id, s, e, clientIP }) {
70
+ try {
71
+ const isTv = s && e && s !== "null" && e !== "null";
72
+ const meta = await getTmdbMetadata(id, isTv);
73
+ if (!meta) return null;
74
+
75
+ let servers = [];
76
+ try {
77
+ const serverRes = await fetch(`${API_DOMAIN}/servers`, {
78
+ headers: {
79
+ "Origin": DOMAIN,
80
+ "Referer": DOMAIN + "/",
81
+ "User-Agent": UA
82
+ },
83
+ signal: AbortSignal.timeout(15000)
84
+ });
85
+ if (serverRes.ok) {
86
+ const data = await serverRes.json();
87
+ servers = data.servers || [];
88
+ }
89
+ } catch (err) { }
90
+
91
+ const activeServers = servers
92
+ .filter(srv => srv.status === "ok" && srv.name)
93
+ .map(srv => srv.name);
94
+
95
+ if (activeServers.length === 0) return null;
96
+
97
+ const allUrls = [];
98
+
99
+ for (const serverName of activeServers) {
100
+ try {
101
+ const queryParts = [
102
+ `title=${encodeURIComponent(meta.title)}`,
103
+ `type=${isTv ? "series" : "movie"}`,
104
+ `year=${meta.year}`,
105
+ `imdb=${meta.imdbId}`,
106
+ `tmdb=${id}`,
107
+ `server=${serverName}`
108
+ ];
109
+ if (isTv) {
110
+ queryParts.push(`season=${s}`, `episode=${e}`);
111
+ }
112
+
113
+ const plainUrl = `${API_DOMAIN}/?${queryParts.join("&")}`;
114
+
115
+ const encRes = await fetch(`${ENC_DEC_API}/enc-cinejoy?url=${encodeURIComponent(plainUrl)}`, {
116
+ headers: { "User-Agent": UA },
117
+ signal: AbortSignal.timeout(15000)
118
+ });
119
+ if (!encRes.ok) continue;
120
+ const encData = await encRes.json();
121
+ if (encData.status !== 200) continue;
122
+
123
+ const rid = encData.result;
124
+
125
+ const challengeRes = await fetch(`${API_DOMAIN}/challenge?rid=${rid}`, {
126
+ headers: {
127
+ "Origin": DOMAIN,
128
+ "Referer": DOMAIN + "/",
129
+ "User-Agent": UA
130
+ },
131
+ signal: AbortSignal.timeout(30000)
132
+ });
133
+ if (!challengeRes.ok) continue;
134
+
135
+ const challengeData = await challengeRes.json();
136
+
137
+ const solved = solveChallenge(challengeData);
138
+
139
+ const streamRes = await fetch(`${API_DOMAIN}/${rid}`, {
140
+ headers: {
141
+ "Origin": DOMAIN,
142
+ "Referer": DOMAIN + "/",
143
+ "User-Agent": UA,
144
+ "X-At": solved
145
+ },
146
+ signal: AbortSignal.timeout(30000)
147
+ });
148
+
149
+ if (!streamRes.ok) continue;
150
+
151
+ const encryptedText = await streamRes.text();
152
+
153
+ const decRes = await fetch(`${ENC_DEC_API}/dec-cinejoy`, {
154
+ method: "POST",
155
+ headers: {
156
+ "Content-Type": "application/json",
157
+ "User-Agent": UA
158
+ },
159
+ body: JSON.stringify({ text: encryptedText }),
160
+ signal: AbortSignal.timeout(15000)
161
+ });
162
+ if (!decRes.ok) continue;
163
+ const decData = await decRes.json();
164
+ if (decData.status !== 200) continue;
165
+
166
+ let decryptedText = decData.result;
167
+
168
+ if (!decryptedText) continue;
169
+
170
+ if (typeof decryptedText === "string") {
171
+ try {
172
+ decryptedText = JSON.parse(decryptedText);
173
+ } catch (e) { }
174
+ }
175
+
176
+ let streamUrl = "";
177
+ if (decryptedText && typeof decryptedText === "object") {
178
+ if (decryptedText.stream && decryptedText.stream[0]) {
179
+ streamUrl = decryptedText.stream[0].playlist || decryptedText.stream[0].url || "";
180
+ } else {
181
+ streamUrl = decryptedText.url || decryptedText.playlist || decryptedText.file || "";
182
+ }
183
+ } else if (typeof decryptedText === "string") {
184
+ streamUrl = decryptedText.trim();
185
+ }
186
+
187
+ if (streamUrl.startsWith("http")) {
188
+ allUrls.push({
189
+ url: streamUrl,
190
+ headers: {
191
+ "User-Agent": UA,
192
+ "Referer": DOMAIN + "/",
193
+ "Origin": DOMAIN
194
+ },
195
+ server: serverName,
196
+ quality: "Auto",
197
+ type: "hls",
198
+ skipVerify: true,
199
+ skipProxy: true
200
+ });
201
+ }
202
+ } catch (err) { }
203
+ }
204
+
205
+ if (allUrls.length > 0) {
206
+ return { ...allUrls[0], allUrls };
207
+ }
208
+ } catch (err) { }
209
+ return null;
210
+ }