@vyla-entertainment/sdk 1.9.0 → 1.10.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 (40) hide show
  1. package/.github/workflows/publish.yml +38 -38
  2. package/.gitlab-ci.yml +16 -0
  3. package/README.md +9 -13
  4. package/package.json +33 -32
  5. package/src/config.js +86 -118
  6. package/src/health.js +2 -2
  7. package/src/sdk.d.ts +122 -122
  8. package/src/sdk.js +7 -4
  9. package/src/sources/anineko.js +3 -1
  10. package/src/sources/cinejoy.js +194 -209
  11. package/src/sources/flaxmovies.js +113 -21
  12. package/src/sources/frame.js +88 -0
  13. package/src/sources/fsonic.js +3 -1
  14. package/src/sources/fsonline.js +3 -1
  15. package/src/sources/lmscript.js +42 -42
  16. package/src/sources/lookmovie.js +3 -1
  17. package/src/sources/luna.js +125 -0
  18. package/src/sources/mapple.js +40 -4
  19. package/src/sources/megasource.js +47 -0
  20. package/src/sources/meowtv.js +0 -1
  21. package/src/sources/nova.js +46 -0
  22. package/src/sources/peestream.js +122 -0
  23. package/src/sources/pengu.js +8 -3
  24. package/src/sources/rivestream.js +179 -0
  25. package/src/sources/videasy.js +3 -1
  26. package/src/sources/vidgod.js +147 -0
  27. package/src/sources/vidnest.js +3 -1
  28. package/src/sources/vidvault.js +113 -113
  29. package/src/sources/vidzee.js +154 -199
  30. package/src/subtitles.js +18 -0
  31. package/src/utils/helpers.js +2 -2
  32. package/src/sources/1embed.js +0 -115
  33. package/src/sources/anikoto.js +0 -198
  34. package/src/sources/anizone.js +0 -109
  35. package/src/sources/cinesu.js +0 -94
  36. package/src/sources/kickassanime.js +0 -107
  37. package/src/sources/nekowatch.js +0 -57
  38. package/src/sources/opstream.js +0 -123
  39. package/src/sources/peachify.js +0 -78
  40. package/src/sources/reanime.js +0 -324
@@ -1,78 +0,0 @@
1
- import { fetchJson, USER_AGENT } from '../utils/helpers.js';
2
-
3
- const DEC_API = "https://enc-dec.app/api/dec-peachify";
4
- const ORIGIN = "https://peachify.top";
5
- const HEADERS = { "User-Agent": USER_AGENT, "Origin": ORIGIN, "Referer": `${ORIGIN}/` };
6
-
7
- const SERVERS = [
8
- { label: "Wolf", path: "air", api: "https://usa.eat-peach.sbs" },
9
- { label: "Spider", path: "holly", api: "https://usa.eat-peach.sbs" },
10
- { label: "Iron", path: "moviebox", api: "https://uwu.eat-peach.sbs" },
11
- { label: "Multi", path: "multi", api: "https://usa.eat-peach.sbs" },
12
- { label: "Dark", path: "net", api: "https://uwu.eat-peach.sbs" },
13
- ];
14
-
15
- export async function getStream({ id, s, e, server }) {
16
- try {
17
- const isTv = s != null && e != null;
18
- const segment = isTv ? `tv/${id}/${s}/${e}` : `movie/${id}`;
19
-
20
- let targets = SERVERS;
21
- if (server && server !== 'all') {
22
- const cleanName = server.replace('Peachify - ', '');
23
- targets = SERVERS.filter(srv => srv.label === cleanName);
24
- if (!targets.length) targets = SERVERS;
25
- }
26
-
27
- const settled = await Promise.allSettled(targets.map(async srv => {
28
- const url = `${srv.api}/${srv.path}/${segment}`;
29
- const rawData = await fetchJson(url, { headers: HEADERS, signal: AbortSignal.timeout(10000) });
30
-
31
- if (!rawData?.data) throw new Error();
32
-
33
- const decJson = await fetchJson(DEC_API, {
34
- method: 'POST',
35
- headers: { 'Content-Type': 'application/json' },
36
- body: JSON.stringify({ text: rawData.data }),
37
- signal: AbortSignal.timeout(10000)
38
- });
39
-
40
- if (decJson.status !== 200 || !decJson.result) throw new Error();
41
- const res = decJson.result;
42
-
43
- const sources = Array.isArray(res) ? res : (res.sources || [res]);
44
- const subs = res.subtitles || [];
45
-
46
- return sources.map(source => {
47
- const streamUrl = source.url || source.file || source.link;
48
- if (!streamUrl) return null;
49
-
50
- return {
51
- url: streamUrl,
52
- server: `Peachify - ${srv.label}`,
53
- quality: source.quality || "Auto",
54
- type: streamUrl.includes('.m3u8') ? 'hls' : 'mp4',
55
- headers: { ...HEADERS, "Origin": ORIGIN },
56
- subtitles: subs.map(sub => ({
57
- url: sub.url || sub.file,
58
- lang: sub.language || sub.lang || sub.label || 'Unknown'
59
- })),
60
- skipProxy: false,
61
- skipVerify: true,
62
- skipHlsCheck: true
63
- };
64
- }).filter(Boolean);
65
- }));
66
-
67
- const allUrls = [];
68
- for (const r of settled) {
69
- if (r.status === 'fulfilled' && r.value) allUrls.push(...r.value);
70
- }
71
-
72
- return allUrls.length ? { allUrls } : null;
73
- } catch { return null; }
74
- }
75
-
76
- export async function getSources() {
77
- return SERVERS.map(s => `Peachify - ${s.label}`);
78
- }
@@ -1,324 +0,0 @@
1
- import crypto from 'node:crypto';
2
- import { fetchJson, getTmdbInfo, tmdbToAnilist, USER_AGENT } from '../utils/helpers.js';
3
-
4
- const BASE = "https://reanime.to";
5
- const FLIX = "https://flixcloud.cc";
6
- const HEADERS = { "User-Agent": USER_AGENT, Accept: "application/json, */*" };
7
-
8
- async function sha256hex(s) {
9
- const buf = await crypto.subtle.digest("SHA-256", typeof s === "string" ? new TextEncoder().encode(s) : s);
10
- return Array.from(new Uint8Array(buf)).map((b) => b.toString(16).padStart(2, "0")).join("");
11
- }
12
-
13
- function b64toU8(b64) {
14
- const bin = Buffer.from(b64, 'base64');
15
- return new Uint8Array(bin);
16
- }
17
-
18
- async function deriveFields(seed) {
19
- let e = seed;
20
- for (let i = 0; i < 3; i++) e = await sha256hex(e + i);
21
- let l = e;
22
- for (let i = 0; i < 3; i++) l = await sha256hex(l + i);
23
- return {
24
- keyField: "kf_" + e.substring(8, 16),
25
- ivField: "ivf_" + e.substring(16, 24),
26
- containerName: "cd_" + e.substring(24, 32),
27
- arrayName: "ad_" + e.substring(32, 40),
28
- objectName: "od_" + e.substring(40, 48),
29
- tokenField: e.substring(48, 64) + "_" + e.substring(56, 64),
30
- keyFrag2Field: l.substring(0, 16) + "_" + l.substring(16, 24)
31
- };
32
- }
33
-
34
- function extractSsrObj(html) {
35
- const m = html.match(/\{type:"data",data:(\{)/);
36
- if (!m) return null;
37
- let depth = 0;
38
- const start = html.indexOf("{", m.index + m[0].length - 1);
39
- for (let i = start; i < html.length; i++) {
40
- if (html[i] === "{") depth++;
41
- else if (html[i] === "}") {
42
- if (--depth === 0) return html.slice(start, i + 1);
43
- }
44
- }
45
- return null;
46
- }
47
-
48
- function parseJsLiteral(src) {
49
- let i = 0;
50
- function ws() { while (i < src.length && /\s/.test(src[i])) i++; }
51
- function parseValue() {
52
- ws();
53
- if (src[i] === "{") return parseObject();
54
- if (src[i] === "[") return parseArray();
55
- if (src[i] === '"') return parseDStr();
56
- if (src[i] === "'") return parseSStr();
57
- if (src.startsWith("true", i)) { i += 4; return true; }
58
- if (src.startsWith("false", i)) { i += 5; return false; }
59
- if (src.startsWith("null", i)) { i += 4; return null; }
60
- if (src.startsWith("undefined", i)) { i += 9; return null; }
61
- if (src.startsWith("!0", i)) { i += 2; return true; }
62
- if (src.startsWith("!1", i)) { i += 2; return false; }
63
- const m = src.slice(i).match(/^-?[\d.]+([eE][+-]?\d+)?/);
64
- if (m) { i += m[0].length; return parseFloat(m[0]); }
65
- throw new Error(`Parse error`);
66
- }
67
- function parseDStr() {
68
- let r = ""; i++;
69
- while (i < src.length && src[i] !== '"') {
70
- if (src[i] === "\\") { i++; r += src[i++]; }
71
- else r += src[i++];
72
- }
73
- i++; return r;
74
- }
75
- function parseSStr() {
76
- let r = ""; i++;
77
- while (i < src.length && src[i] !== "'") {
78
- if (src[i] === "\\") { i++; r += src[i++]; }
79
- else r += src[i++];
80
- }
81
- i++; return r;
82
- }
83
- function parseKey() {
84
- ws();
85
- if (src[i] === '"') return parseDStr();
86
- if (src[i] === "'") return parseSStr();
87
- const m = src.slice(i).match(/^[a-zA-Z_$][a-zA-Z0-9_$]*/);
88
- if (m) { i += m[0].length; return m[0]; }
89
- throw new Error(`Bad key`);
90
- }
91
- function parseObject() {
92
- const obj = {}; i++; ws();
93
- while (i < src.length && src[i] !== "}") {
94
- if (src[i] === ",") { i++; ws(); continue; }
95
- const k = parseKey(); ws(); i++;
96
- obj[k] = parseValue(); ws();
97
- }
98
- i++; return obj;
99
- }
100
- function parseArray() {
101
- const arr = []; i++; ws();
102
- while (i < src.length && src[i] !== "]") {
103
- if (src[i] === ",") { i++; ws(); continue; }
104
- arr.push(parseValue()); ws();
105
- }
106
- i++; return arr;
107
- }
108
- return parseValue();
109
- }
110
-
111
- function parseWasmDecrypt(wasmBytes) {
112
- const b = wasmBytes;
113
- let pos = 8;
114
- while (pos < b.length) {
115
- const secId = b[pos++];
116
- let sz = 0, sh = 0, by;
117
- do { by = b[pos++]; sz |= (by & 127) << sh; sh += 7; } while (by & 128);
118
- if (secId === 10) {
119
- pos++;
120
- let sbs = 0, sh2 = 0, by2;
121
- do { by2 = b[pos++]; sbs |= (by2 & 127) << sh2; sh2 += 7; } while (by2 & 128);
122
- pos += sbs; break;
123
- }
124
- pos += sz;
125
- }
126
- let rbs = 0, sh3 = 0, by3;
127
- do { by3 = b[pos++]; rbs |= (by3 & 127) << sh3; sh3 += 7; } while (by3 & 128);
128
- const r = b.slice(pos, pos + rbs);
129
- function leb(arr, i) {
130
- let v = 0, s = 0, b2;
131
- do { b2 = arr[i++]; v |= (b2 & 127) << s; s += 7; } while (b2 & 128);
132
- return [v, i];
133
- }
134
- const XOR_END = [32, 2, 32, 5, 106, 45, 0, 0, 115, 33, 6];
135
- let txStart = -1;
136
- outer: for (let i = 0; i < r.length - XOR_END.length; i++) {
137
- for (let j = 0; j < XOR_END.length; j++) if (r[i + j] !== XOR_END[j]) continue outer;
138
- txStart = i + XOR_END.length; break;
139
- }
140
- if (txStart < 0) return null;
141
- let txEnd = -1, step = 36;
142
- for (let i = txStart; i < r.length - 4; i++) {
143
- if (r[i] === 32 && r[i + 1] === 5 && r[i + 2] === 65) {
144
- const [val, ni] = leb(r, i + 3);
145
- if (r[ni] === 108) { txEnd = i; step = val; break; }
146
- }
147
- }
148
- if (txEnd < 0) return null;
149
- const code = r.slice(txStart, txEnd);
150
- function transform(inputByte) {
151
- let local6 = inputByte & 255;
152
- const stk = []; let i = 0;
153
- while (i < code.length) {
154
- const op = code[i++];
155
- if (op === 32) { const [idx, ni] = leb(code, i); i = ni; stk.push(idx === 6 ? local6 : 0); }
156
- else if (op === 33) { const [idx, ni] = leb(code, i); i = ni; const v = stk.pop(); if (idx === 6) local6 = v & 255; }
157
- else if (op === 65) { const [v, ni] = leb(code, i); i = ni; stk.push(v); }
158
- else if (op === 106) { const b2 = stk.pop(), a = stk.pop(); stk.push(a + b2 & 255); }
159
- else if (op === 107) { const b2 = stk.pop(), a = stk.pop(); stk.push(a - b2 + 256 & 255); }
160
- else if (op === 113) { const b2 = stk.pop(), a = stk.pop(); stk.push(a & b2 & 255); }
161
- else if (op === 114) { const b2 = stk.pop(), a = stk.pop(); stk.push((a | b2) & 255); }
162
- else if (op === 115) { const b2 = stk.pop(), a = stk.pop(); stk.push((a ^ b2) & 255); }
163
- else if (op === 116) { const b2 = stk.pop(), a = stk.pop(); stk.push(a << (b2 & 7) & 255); }
164
- else if (op === 118) { const b2 = stk.pop(), a = stk.pop(); stk.push(a >>> (b2 & 7) & 255); }
165
- }
166
- return local6;
167
- }
168
- return { step, transform };
169
- }
170
-
171
- function runDecrypt(wasmBytes, frag1, kf2, T, seedInt) {
172
- const parsed = parseWasmDecrypt(wasmBytes);
173
- if (!parsed) return null;
174
- const { step, transform } = parsed;
175
- const out = new Uint8Array(frag1.length);
176
- for (let i = 0; i < frag1.length; i++) {
177
- const c = (frag1[i] ^ kf2[i] ^ T[i]) & 255;
178
- out[i] = transform(c) ^ i * step + seedInt & 255;
179
- }
180
- return out;
181
- }
182
-
183
- async function decryptEmbed(html) {
184
- const raw = extractSsrObj(html);
185
- if (!raw) return null;
186
- const data = parseJsLiteral(raw);
187
- const seed = data.obfuscation_seed;
188
- if (!seed) return null;
189
-
190
- const fields = await deriveFields(seed);
191
- const ocd = data.obfuscated_crypto_data;
192
- if (!ocd) return null;
193
-
194
- const container = ocd[fields.containerName];
195
- if (!container) return null;
196
-
197
- const arr = container[fields.arrayName];
198
- if (!arr || !arr[0]) return null;
199
-
200
- const obj = arr[0][fields.objectName];
201
- if (!obj) return null;
202
-
203
- const frag1 = b64toU8(obj[fields.keyField]);
204
- const iv = b64toU8(obj[fields.ivField]);
205
- const kf2raw = data[fields.keyFrag2Field];
206
- if (!kf2raw) return null;
207
-
208
- const kf2 = b64toU8(kf2raw);
209
- const token = data[fields.tokenField];
210
- if (!token) return null;
211
-
212
- const tokData = await fetchJson(`${FLIX}/api/m3u8/${token}`, { headers: { ...HEADERS, Referer: `${BASE}/` } }).catch(() => null);
213
- if (!tokData) return null;
214
-
215
- const vidKey = (await sha256hex(token + "vid")).substring(0, 10);
216
- const keyKey = (await sha256hex(token + "key")).substring(0, 10);
217
- const v_bytes = b64toU8(tokData[vidKey] || "");
218
- const T_bytes = b64toU8(tokData[keyKey] || "");
219
-
220
- if (!v_bytes.length || !T_bytes.length) return null;
221
-
222
- const seedInt = parseInt(seed.substring(0, 8), 16);
223
- const wPayload = b64toU8(data.w_payload ?? "");
224
- if (!wPayload.length) return null;
225
-
226
- const wasmOut = runDecrypt(wPayload, frag1, kf2, T_bytes, seedInt);
227
- if (!wasmOut) return null;
228
-
229
- const keyMat = await crypto.subtle.importKey("raw", wasmOut, { name: "PBKDF2" }, false, ["deriveBits"]);
230
- const derived = new Uint8Array(await crypto.subtle.deriveBits(
231
- { name: "PBKDF2", salt: new TextEncoder().encode(seed), iterations: 1000, hash: "SHA-256" },
232
- keyMat,
233
- 256
234
- ));
235
-
236
- for (let i = 0; i < 32; i++) derived[i] ^= seed.charCodeAt(i % seed.length);
237
-
238
- const aesKeyBytes = new Uint8Array(await crypto.subtle.digest("SHA-256", derived));
239
- const aesKey = await crypto.subtle.importKey("raw", aesKeyBytes, { name: "AES-CBC" }, false, ["decrypt"]);
240
-
241
- const plain = await crypto.subtle.decrypt({ name: "AES-CBC", iv }, aesKey, v_bytes).catch(() => null);
242
- if (!plain) return null;
243
-
244
- const url = new TextDecoder().decode(plain).trim().replace(/\0+$/, "");
245
- if (!url.startsWith("http")) return null;
246
-
247
- return { url };
248
- }
249
-
250
- async function searchReanime(query) {
251
- const data = await fetchJson(`${BASE}/api/v1/search?${new URLSearchParams({ q: query, limit: 10 })}`, { headers: HEADERS }).catch(() => null);
252
- return Array.isArray(data?.results) ? data.results : [];
253
- }
254
-
255
- export async function getStream({ id, s, e, audio, tmdbApiKey }) {
256
- try {
257
- const isTv = !!s;
258
- const mediaType = isTv ? 'tv' : 'movie';
259
- const epNum = e ? Number(e) : 1;
260
- const seasonNum = s ? Number(s) : 1;
261
-
262
- let anilistId = null;
263
- let titles = [];
264
- const tmdbInfo = await getTmdbInfo(tmdbApiKey, id, mediaType, seasonNum);
265
-
266
- if (tmdbInfo) {
267
- titles = tmdbInfo.titles || [];
268
- anilistId = await tmdbToAnilist(id, mediaType, seasonNum, titles, tmdbInfo.year);
269
- }
270
- if (!anilistId) {
271
- anilistId = Number(id);
272
- }
273
-
274
- let animeSlug = null;
275
- for (const title of titles) {
276
- const results = await searchReanime(title);
277
- if (results.length) {
278
- animeSlug = results[0].anime_id;
279
- break;
280
- }
281
- }
282
-
283
- if (!animeSlug) return null;
284
-
285
- const audioPref = audio === 'dub' ? 'dub' : 'sub';
286
- const watchRes = await fetchJson(`${BASE}/api/watch/${animeSlug}/${epNum}`, { headers: HEADERS }).catch(() => null);
287
- const links = [...(watchRes?.episode_links || [])];
288
-
289
- const flixRes = await fetchJson(`${BASE}/api/flix/${anilistId}/${epNum}`, { headers: HEADERS }).catch(() => null);
290
- if (flixRes?.success && Array.isArray(flixRes.servers)) {
291
- const seen = new Set(links.map((s) => s["$id"]));
292
- for (const srv of flixRes.servers) {
293
- if (!seen.has(srv["$id"])) links.push(srv);
294
- }
295
- }
296
-
297
- const audioTypes = audioPref === "sub" ? ["sub", "s-sub"] : ["dub", "s-dub"];
298
- const servers = links.filter((srv) => audioTypes.includes(srv.dataType));
299
- if (!servers.length) return null;
300
-
301
- const urls = [];
302
- for (const srv of servers.slice(0, 3)) {
303
- if (!srv.dataLink) continue;
304
- const res = await fetch(srv.dataLink, { headers: { ...HEADERS, Referer: `${BASE}/` } }).catch(() => null);
305
- if (!res || !res.ok) continue;
306
- const html = await res.text();
307
- const stream = await decryptEmbed(html);
308
- if (stream?.url) {
309
- urls.push({
310
- url: stream.url,
311
- server: `Reanime - ${srv.serverName || 'Server'}`,
312
- });
313
- }
314
- }
315
-
316
- return urls.length ? { allUrls: urls } : null;
317
- } catch (err) {
318
- return null;
319
- }
320
- }
321
-
322
- export async function getSources() {
323
- return ['Reanime'];
324
- }