@vyla-entertainment/sdk 1.9.0 → 1.11.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/.github/workflows/publish.yml +38 -38
- package/.gitlab-ci.yml +16 -0
- package/README.md +9 -13
- package/package.json +33 -32
- package/src/config.js +140 -116
- package/src/health.js +2 -2
- package/src/sdk.d.ts +122 -122
- package/src/sdk.js +7 -4
- package/src/sources/anineko.js +3 -1
- package/src/sources/bcine.js +202 -0
- package/src/sources/cinejoy.js +194 -209
- package/src/sources/cinesrc.js +212 -0
- package/src/sources/cinesu.js +147 -93
- package/src/sources/flaxmovies.js +113 -21
- package/src/sources/frame.js +88 -0
- package/src/sources/fsonic.js +3 -1
- package/src/sources/fsonline.js +3 -1
- package/src/sources/lmscript.js +42 -42
- package/src/sources/lookmovie.js +3 -1
- package/src/sources/luna.js +125 -0
- package/src/sources/mapple.js +40 -4
- package/src/sources/megasource.js +47 -0
- package/src/sources/meowtv.js +0 -1
- package/src/sources/nova.js +46 -0
- package/src/sources/peestream.js +122 -0
- package/src/sources/pengu.js +8 -3
- package/src/sources/pstream.js +68 -0
- package/src/sources/rivestream.js +179 -0
- package/src/sources/videasy.js +3 -1
- package/src/sources/vidgod.js +147 -0
- package/src/sources/vidnest.js +3 -1
- package/src/sources/vidvault.js +113 -113
- package/src/sources/vidzee.js +154 -199
- package/src/sources/zxcstream.js +141 -0
- package/src/subtitles.js +18 -0
- package/src/utils/helpers.js +2 -2
- package/src/sources/1embed.js +0 -115
- package/src/sources/anikoto.js +0 -198
- package/src/sources/anizone.js +0 -109
- package/src/sources/kickassanime.js +0 -107
- package/src/sources/nekowatch.js +0 -57
- package/src/sources/opstream.js +0 -123
- package/src/sources/peachify.js +0 -78
- package/src/sources/reanime.js +0 -324
- package/src/sources/vidrift.js +0 -95
- package/src/sources/vidsync.js +0 -38
package/src/sources/opstream.js
DELETED
|
@@ -1,123 +0,0 @@
|
|
|
1
|
-
import { getTmdbInfo, USER_AGENT } from '../utils/helpers.js';
|
|
2
|
-
|
|
3
|
-
const BASE_URL = 'https://opstream.fun';
|
|
4
|
-
const API_BASE = `${BASE_URL}/api/resolve`;
|
|
5
|
-
|
|
6
|
-
const API_HEADERS = {
|
|
7
|
-
'User-Agent': USER_AGENT,
|
|
8
|
-
'Accept': 'application/x-ndjson; charset=utf-8',
|
|
9
|
-
'Referer': `${BASE_URL}/`,
|
|
10
|
-
'Origin': BASE_URL,
|
|
11
|
-
};
|
|
12
|
-
|
|
13
|
-
const STREAM_HEADERS = {
|
|
14
|
-
'User-Agent': USER_AGENT,
|
|
15
|
-
'Referer': `${BASE_URL}/`,
|
|
16
|
-
'Origin': BASE_URL,
|
|
17
|
-
};
|
|
18
|
-
|
|
19
|
-
export async function getStream({ id, s, e }) {
|
|
20
|
-
try {
|
|
21
|
-
const isTv = s != null && e != null;
|
|
22
|
-
const info = await getTmdbInfo(id, isTv ? 'tv' : 'movie');
|
|
23
|
-
|
|
24
|
-
if (!info?.titles?.length || !info.imdbId) return null;
|
|
25
|
-
|
|
26
|
-
const params = new URLSearchParams({
|
|
27
|
-
type: isTv ? 'tv' : 'movie',
|
|
28
|
-
tmdbId: id,
|
|
29
|
-
title: info.titles[0],
|
|
30
|
-
year: info.year || '',
|
|
31
|
-
imdbId: info.imdbId,
|
|
32
|
-
progress: '1'
|
|
33
|
-
});
|
|
34
|
-
|
|
35
|
-
if (isTv) {
|
|
36
|
-
params.append('season', s);
|
|
37
|
-
params.append('episode', e);
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
const res = await fetch(`${API_BASE}?${params}`, {
|
|
41
|
-
headers: API_HEADERS,
|
|
42
|
-
signal: AbortSignal.timeout(15000)
|
|
43
|
-
});
|
|
44
|
-
|
|
45
|
-
if (!res.ok) return null;
|
|
46
|
-
|
|
47
|
-
const text = await res.text();
|
|
48
|
-
const lines = text.split('\n');
|
|
49
|
-
|
|
50
|
-
let streamData = null;
|
|
51
|
-
for (const line of lines) {
|
|
52
|
-
const trimmed = line.trim();
|
|
53
|
-
if (!trimmed) continue;
|
|
54
|
-
try {
|
|
55
|
-
const json = JSON.parse(trimmed);
|
|
56
|
-
if (json.t === 'done' && json.data) {
|
|
57
|
-
streamData = json.data;
|
|
58
|
-
break;
|
|
59
|
-
}
|
|
60
|
-
} catch { continue; }
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
if (!streamData) return null;
|
|
64
|
-
|
|
65
|
-
const subtitles = (streamData.captions || []).map(c => ({
|
|
66
|
-
url: c.url.startsWith('http') ? c.url : `${BASE_URL}${c.url}`,
|
|
67
|
-
lang: c.label || 'Unknown'
|
|
68
|
-
}));
|
|
69
|
-
|
|
70
|
-
const allUrls = [];
|
|
71
|
-
|
|
72
|
-
if (streamData.qualities && streamData.qualities.length > 0) {
|
|
73
|
-
for (const q of streamData.qualities) {
|
|
74
|
-
let finalUrl = q.url;
|
|
75
|
-
if (!finalUrl.startsWith('http')) {
|
|
76
|
-
finalUrl = `${BASE_URL}${finalUrl}`;
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
const isDash = finalUrl.includes('.mpd');
|
|
80
|
-
const isHls = finalUrl.includes('.m3u8');
|
|
81
|
-
|
|
82
|
-
allUrls.push({
|
|
83
|
-
url: finalUrl,
|
|
84
|
-
server: `OpStream - ${q.label}`,
|
|
85
|
-
type: isDash ? 'dash' : (isHls ? 'hls' : 'mp4'),
|
|
86
|
-
headers: STREAM_HEADERS,
|
|
87
|
-
subtitles,
|
|
88
|
-
skipProxy: true
|
|
89
|
-
});
|
|
90
|
-
}
|
|
91
|
-
} else if (streamData.url) {
|
|
92
|
-
let finalUrl = streamData.url;
|
|
93
|
-
if (finalUrl.includes('?u=')) {
|
|
94
|
-
try {
|
|
95
|
-
const uParam = new URL(finalUrl, BASE_URL).searchParams.get('u');
|
|
96
|
-
if (uParam) finalUrl = Buffer.from(uParam, 'base64').toString('utf8');
|
|
97
|
-
} catch {}
|
|
98
|
-
} else if (!finalUrl.startsWith('http')) {
|
|
99
|
-
finalUrl = `${BASE_URL}${finalUrl}`;
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
const isDash = streamData.kind === 'dash' || finalUrl.includes('.mpd');
|
|
103
|
-
const isHls = streamData.kind === 'hls' || finalUrl.includes('.m3u8');
|
|
104
|
-
|
|
105
|
-
allUrls.push({
|
|
106
|
-
url: finalUrl,
|
|
107
|
-
server: 'OpStream',
|
|
108
|
-
type: isDash ? 'dash' : (isHls ? 'hls' : 'mp4'),
|
|
109
|
-
headers: STREAM_HEADERS,
|
|
110
|
-
subtitles,
|
|
111
|
-
skipProxy: true
|
|
112
|
-
});
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
if (allUrls.length === 0) return null;
|
|
116
|
-
|
|
117
|
-
return { allUrls };
|
|
118
|
-
} catch { return null; }
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
export async function getSources() {
|
|
122
|
-
return ['OpStream'];
|
|
123
|
-
}
|
package/src/sources/peachify.js
DELETED
|
@@ -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
|
-
}
|
package/src/sources/reanime.js
DELETED
|
@@ -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
|
-
}
|
package/src/sources/vidrift.js
DELETED
|
@@ -1,95 +0,0 @@
|
|
|
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
|
-
}
|
package/src/sources/vidsync.js
DELETED
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
import { fetchJson, fetchText, USER_AGENT, getTmdbInfo } from '../utils/helpers.js';
|
|
2
|
-
|
|
3
|
-
const API_BASE = "https://enc-dec.app/api";
|
|
4
|
-
const DOMAIN = "https://vidsync.xyz";
|
|
5
|
-
const HEADERS = { "Accept": "*/*", "Origin": DOMAIN, "Referer": `${DOMAIN}/`, "User-Agent": USER_AGENT, "X-Requested-With": "XMLHttpRequest" };
|
|
6
|
-
const SERVERS = ["cinevault", "cinedub", "cinebox", "cineflix", "cinevip", "cinecloud", "cine4k"];
|
|
7
|
-
|
|
8
|
-
export async function getStream({ id, s, e, server }) {
|
|
9
|
-
try {
|
|
10
|
-
const isTv = s != null && e != null;
|
|
11
|
-
const type = isTv ? 'tv' : 'movie';
|
|
12
|
-
const info = await getTmdbInfo(id, type);
|
|
13
|
-
if (!info?.titles?.length) return null;
|
|
14
|
-
const turnstileJson = await fetchJson(`${API_BASE}/enc-vidsync`, { signal: AbortSignal.timeout(5000) });
|
|
15
|
-
if (turnstileJson.status !== 200 || !turnstileJson.result?.token) return null;
|
|
16
|
-
const turnstileToken = turnstileJson.result.token;
|
|
17
|
-
let targets = SERVERS;
|
|
18
|
-
if (server && server !== 'all') {
|
|
19
|
-
const clean = server.replace('VidSync - ', '');
|
|
20
|
-
targets = SERVERS.includes(clean) ? [clean] : SERVERS;
|
|
21
|
-
}
|
|
22
|
-
const encTitle = encodeURIComponent(info.titles[0]).replace(/%20/g, '+');
|
|
23
|
-
const results = await Promise.allSettled(targets.map(async srv => {
|
|
24
|
-
let url = `${DOMAIN}/api/stream/fetch?title=${encTitle}&type=${type}&releaseYear=${info.year}&mediaId=${id}&serverName=${srv}`;
|
|
25
|
-
if (isTv) url += `&season=${s}&episode=${e}`;
|
|
26
|
-
const encryptedText = await fetchText(url, { headers: { ...HEADERS, "X-Cf-Turnstile": turnstileToken }, signal: AbortSignal.timeout(10000) });
|
|
27
|
-
const decJson = await fetchJson(`${API_BASE}/dec-vidsync`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ text: encryptedText, id: String(id) }), signal: AbortSignal.timeout(10000) });
|
|
28
|
-
if (decJson.status !== 200 || !decJson.result) throw new Error();
|
|
29
|
-
const streams = Array.isArray(decJson.result) ? decJson.result : [decJson.result];
|
|
30
|
-
return streams.map(st => ({ url: st.url || st.file, server: `VidSync - ${srv}`, quality: st.quality || "Auto", type: (st.url || st.file || "").includes(".m3u8") ? "hls" : "mp4", headers: { ...HEADERS, "Origin": DOMAIN }, skipProxy: false, skipVerify: true, skipHlsCheck: true }));
|
|
31
|
-
}));
|
|
32
|
-
const allUrls = [];
|
|
33
|
-
for (const r of results) if (r.status === 'fulfilled') allUrls.push(...r.value);
|
|
34
|
-
return allUrls.length ? { allUrls } : null;
|
|
35
|
-
} catch { return null; }
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
export async function getSources() { return SERVERS.map(s => `VidSync - ${s}`); }
|