@vyla-entertainment/sdk 1.18.0 → 1.20.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/README.md +5 -5
- package/package.json +1 -1
- package/src/config.js +138 -137
- package/src/sources/cno.js +95 -0
- package/src/sources/flaxmovies.js +91 -103
- package/src/sources/frost.js +30 -0
- package/src/sources/hdhub.js +36 -0
- package/src/sources/kickassanime.js +125 -0
- package/src/sources/lmscript.js +53 -25
- package/src/sources/movy.js +161 -233
- package/src/sources/net79.js +56 -0
- package/src/sources/rivo.js +54 -0
- package/src/sources/thugtv.js +56 -55
- package/src/sources/vidora.js +62 -0
- package/src/sources/watchout.js +46 -0
- package/src/sources/yapgrid.js +63 -0
- package/src/utils/helpers.js +17 -0
- package/src/sources/anihq.js +0 -124
- package/src/sources/anineko.js +0 -190
- package/src/sources/movienight.js +0 -117
- package/src/sources/vidgod.js +0 -147
- package/src/sources/vidnest.js +0 -79
package/src/sources/movy.js
CHANGED
|
@@ -1,282 +1,210 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { fetchJson, fetchText, getTmdbInfo } from '../utils/helpers.js';
|
|
2
2
|
|
|
3
|
-
const
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
const SERVERS = [
|
|
9
|
-
{ endpoint: 'miami', name: 'Miami', note: 'Original audio (Up to 4K)' },
|
|
10
|
-
{ endpoint: 'seattle', name: 'Seattle', note: 'Original audio' },
|
|
11
|
-
{ endpoint: 'denver', name: 'Denver', note: 'Original audio' },
|
|
12
|
-
{ endpoint: 'chicago', name: 'Chicago', note: 'Original audio' },
|
|
13
|
-
{ endpoint: 'dallas', name: 'Dallas', note: 'Original audio' },
|
|
14
|
-
{ endpoint: 'atlanta', name: 'Atlanta', note: 'Original audio' },
|
|
15
|
-
{ endpoint: 'houston', name: 'Houston', note: 'Original audio' },
|
|
16
|
-
{ endpoint: 'austin', name: 'Austin', note: 'Original audio' },
|
|
17
|
-
{ endpoint: 'boston', name: 'Boston', note: 'Original audio' },
|
|
18
|
-
{ endpoint: 'munich', name: 'Munich', note: 'German audio', extra: 'language=german' },
|
|
19
|
-
{ endpoint: 'berlin', name: 'Berlin', note: 'German audio' },
|
|
20
|
-
{ endpoint: 'paris', name: 'Paris', note: 'French audio' },
|
|
21
|
-
{ endpoint: 'delhi', name: 'Delhi', note: 'Hindi audio' },
|
|
22
|
-
{ endpoint: 'cancun', name: 'Cancun', note: 'Spanish audio' },
|
|
3
|
+
const K = [
|
|
4
|
+
0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5,
|
|
5
|
+
0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
|
|
6
|
+
0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3,
|
|
7
|
+
0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174
|
|
23
8
|
];
|
|
9
|
+
const MAGIC = [109, 118, 109, 49];
|
|
24
10
|
|
|
25
|
-
const
|
|
11
|
+
const isEven = e => ((e * (e + 1)) & 1) === 0;
|
|
26
12
|
|
|
27
|
-
function
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
return (
|
|
13
|
+
function scramble(e) {
|
|
14
|
+
e >>>= 0;
|
|
15
|
+
e ^= e >>> 16;
|
|
16
|
+
e = Math.imul(e, 0x85ebca6b) >>> 0;
|
|
17
|
+
e ^= e >>> 13;
|
|
18
|
+
e = Math.imul(e, 0xc2b2ae35) >>> 0;
|
|
19
|
+
return (e ^ (e >>> 16)) >>> 0;
|
|
34
20
|
}
|
|
35
21
|
|
|
36
|
-
function
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
return ((
|
|
22
|
+
function rotl(e, a) {
|
|
23
|
+
e >>>= 0;
|
|
24
|
+
a &= 31;
|
|
25
|
+
return a === 0 ? e >>> 0 : ((e << a) | (e >>> (32 - a))) >>> 0;
|
|
40
26
|
}
|
|
41
27
|
|
|
42
|
-
function
|
|
43
|
-
let
|
|
44
|
-
|
|
45
|
-
const
|
|
46
|
-
|
|
28
|
+
function initCipherState(keyStr, seedStr) {
|
|
29
|
+
let keyLen = keyStr.length;
|
|
30
|
+
if (((keyLen * (keyLen + 1)) & 1) === 1) {
|
|
31
|
+
const S = new Array(256);
|
|
32
|
+
for (let i = 0; i < 256; i++) S[i] = i;
|
|
33
|
+
let j = 0;
|
|
34
|
+
for (let i = 0; i < 256; i++) {
|
|
35
|
+
j = (j + S[i] + keyStr.charCodeAt(i % keyStr.length)) & 255;
|
|
36
|
+
const tmp = S[i];
|
|
37
|
+
S[i] = S[j];
|
|
38
|
+
S[j] = tmp;
|
|
39
|
+
}
|
|
40
|
+
let acc = 0x67452301;
|
|
41
|
+
for (let i = 0; i < keyStr.length; i++) {
|
|
42
|
+
acc = rotl((acc ^ Math.imul(keyStr.charCodeAt(i), K[15 & i])) >>> 0, 5);
|
|
43
|
+
}
|
|
44
|
+
return { S, acc: scramble(acc) };
|
|
47
45
|
}
|
|
48
|
-
return _l(t);
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
function _initKeyState(seed, tmdbId) {
|
|
52
|
-
const s = new Array(61).fill(0);
|
|
53
|
-
const isSet = new Array(61).fill(false);
|
|
54
|
-
const idVal = ((tmdbId >>> 0) ^ 0x9e3779b9) >>> 0;
|
|
55
|
-
let r = _l((_fnv1a(seed) ^ _l(idVal)) >>> 0);
|
|
56
46
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
isSet[t] = true;
|
|
62
|
-
r = _l((r + t) >>> 0);
|
|
47
|
+
const S = new Array(61);
|
|
48
|
+
let hash = 0x811c9dc5;
|
|
49
|
+
for (let i = 0; i < keyStr.length; i++) {
|
|
50
|
+
hash = Math.imul(hash ^ keyStr.charCodeAt(i), 0x1000193) >>> 0;
|
|
63
51
|
}
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
52
|
+
let n = scramble(scramble(hash) ^ scramble((seedStr >>> 0) ^ 0x9e3779b9)) >>> 0;
|
|
53
|
+
for (let i = 0; i < 8; i++) {
|
|
54
|
+
if (isEven(i)) {
|
|
55
|
+
const idx = n % 61;
|
|
56
|
+
n = rotl((n + 0x9e3779b9) >>> 0, 7 + (7 & i));
|
|
57
|
+
S[idx] = (n ^ scramble(n)) >>> 0;
|
|
58
|
+
n = scramble((n + idx) >>> 0);
|
|
59
|
+
} else {
|
|
60
|
+
S[i] = K[15 & i];
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
return {
|
|
64
|
+
S,
|
|
65
|
+
acc: scramble(0xa5a5a5a5 ^ n) >>> 0
|
|
66
|
+
};
|
|
67
67
|
}
|
|
68
68
|
|
|
69
|
-
function
|
|
70
|
-
const
|
|
71
|
-
let
|
|
72
|
-
const
|
|
73
|
-
const
|
|
74
|
-
const
|
|
75
|
-
const
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
state.isSet[i] = true;
|
|
83
|
-
state.acc = nState;
|
|
84
|
-
return nState >>> 0;
|
|
69
|
+
function nextKeystreamWord(state, step) {
|
|
70
|
+
const S = state.S;
|
|
71
|
+
let acc = state.acc;
|
|
72
|
+
const idx = acc % 61;
|
|
73
|
+
const hasIdx = 0 - Number(idx in S);
|
|
74
|
+
const sVal = S[idx] >>> 0;
|
|
75
|
+
const c = Math.imul(0x9e3779b9, step + 1) >>> 0;
|
|
76
|
+
let b = ((acc ^ ((sVal ^ c) >>> 0)) >>> 0 | (acc & (sVal ^ c) & hasIdx) >>> 0) >>> 0;
|
|
77
|
+
b = (rotl((b + acc) >>> 0, 31 & idx) ^ rotl(acc, 31 & Math.imul(idx, 7))) >>> 0;
|
|
78
|
+
acc = scramble((b + 0x9e3779b9) >>> 0);
|
|
79
|
+
S[idx] = acc >>> 0;
|
|
80
|
+
state.acc = acc;
|
|
81
|
+
return acc >>> 0;
|
|
85
82
|
}
|
|
86
83
|
|
|
87
|
-
function
|
|
88
|
-
const
|
|
89
|
-
const
|
|
90
|
-
|
|
91
|
-
let
|
|
92
|
-
|
|
93
|
-
while (byteIdx < len) {
|
|
94
|
-
const word = _nextKeystreamWord(state, wordIdx++);
|
|
95
|
-
out[byteIdx++] = word & 0xFF;
|
|
96
|
-
if (byteIdx < len) out[byteIdx++] = (word >>> 8) & 0xFF;
|
|
97
|
-
if (byteIdx < len) out[byteIdx++] = (word >>> 16) & 0xFF;
|
|
98
|
-
if (byteIdx < len) out[byteIdx++] = (word >>> 24) & 0xFF;
|
|
99
|
-
}
|
|
100
|
-
return out;
|
|
84
|
+
function decodeBase64Url(str) {
|
|
85
|
+
const padded = str.replace(/-/g, "+").replace(/_/g, "/").padEnd(4 * Math.ceil(str.length / 4), "=");
|
|
86
|
+
const bin = atob(padded);
|
|
87
|
+
const bytes = new Uint8Array(bin.length);
|
|
88
|
+
for (let i = 0; i < bin.length; i++) bytes[i] = bin.charCodeAt(i);
|
|
89
|
+
return bytes;
|
|
101
90
|
}
|
|
102
91
|
|
|
103
|
-
function
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
if (cipherBytes.length <= MAGIC.length) return null;
|
|
109
|
-
|
|
110
|
-
const ks = _generateKeyStream(seed, tmdbId, cipherBytes.length);
|
|
111
|
-
const decrypted = new Uint8Array(cipherBytes.length);
|
|
112
|
-
for (let i = 0; i < cipherBytes.length; i++) {
|
|
113
|
-
decrypted[i] = cipherBytes[i] ^ ks[i];
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
for (let k = 0; k < MAGIC.length; k++) {
|
|
117
|
-
if (decrypted[k] !== MAGIC[k]) return null;
|
|
118
|
-
}
|
|
92
|
+
function decryptPayload(ciphertext, seed, mediaId) {
|
|
93
|
+
const rawBytes = decodeBase64Url(ciphertext);
|
|
94
|
+
const totalLen = rawBytes.length;
|
|
95
|
+
const state = initCipherState(seed, mediaId);
|
|
96
|
+
const keystream = new Uint8Array(totalLen);
|
|
119
97
|
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
98
|
+
let byteIdx = 0;
|
|
99
|
+
let step = 0;
|
|
100
|
+
while (byteIdx < totalLen) {
|
|
101
|
+
const word = nextKeystreamWord(state, step++);
|
|
102
|
+
keystream[byteIdx++] = 255 & word;
|
|
103
|
+
if (byteIdx < totalLen) keystream[byteIdx++] = (word >>> 8) & 255;
|
|
104
|
+
if (byteIdx < totalLen) keystream[byteIdx++] = (word >>> 16) & 255;
|
|
105
|
+
if (byteIdx < totalLen) keystream[byteIdx++] = (word >>> 24) & 255;
|
|
123
106
|
}
|
|
124
|
-
}
|
|
125
107
|
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
const cached = seedCache.get(tmdbId);
|
|
129
|
-
if (cached && cached.expiresAt > now + 5000) {
|
|
130
|
-
return cached.seed;
|
|
108
|
+
for (let i = 0; i < totalLen; i++) {
|
|
109
|
+
rawBytes[i] ^= keystream[i];
|
|
131
110
|
}
|
|
132
111
|
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
'User-Agent': ua,
|
|
137
|
-
Referer: REFERER,
|
|
138
|
-
Origin: ORIGIN,
|
|
139
|
-
},
|
|
140
|
-
signal: AbortSignal.timeout(8000),
|
|
141
|
-
});
|
|
142
|
-
|
|
143
|
-
if (res.ok) {
|
|
144
|
-
const data = await res.json();
|
|
145
|
-
const seed = data?.seed ? String(data.seed) : '';
|
|
146
|
-
const ttlMs = typeof data?.ttlMs === 'number' ? data.ttlMs : 30000;
|
|
147
|
-
if (seed) {
|
|
148
|
-
seedCache.set(tmdbId, { seed, expiresAt: now + ttlMs });
|
|
149
|
-
return seed;
|
|
150
|
-
}
|
|
112
|
+
for (let i = 0; i < MAGIC.length; i++) {
|
|
113
|
+
if (rawBytes[i] !== MAGIC[i]) {
|
|
114
|
+
throw new Error("decrypt failed: bad seed or tampered payload");
|
|
151
115
|
}
|
|
152
|
-
}
|
|
116
|
+
}
|
|
153
117
|
|
|
154
|
-
|
|
118
|
+
const payload = rawBytes.subarray(MAGIC.length);
|
|
119
|
+
return new TextDecoder("utf-8").decode(payload);
|
|
155
120
|
}
|
|
156
121
|
|
|
157
|
-
async function
|
|
158
|
-
const
|
|
159
|
-
if (!apiKey) return null;
|
|
122
|
+
export async function getStream({ id, s, e, tmdbApiKey, sdk }) {
|
|
123
|
+
const isTv = s != null && e != null;
|
|
160
124
|
const type = isTv ? 'tv' : 'movie';
|
|
161
|
-
|
|
162
|
-
const res = await fetch(`https://api.themoviedb.org/3/${type}/${id}?api_key=${apiKey}&append_to_response=external_ids`, {
|
|
163
|
-
signal: AbortSignal.timeout(6000),
|
|
164
|
-
});
|
|
165
|
-
if (!res.ok) return null;
|
|
166
|
-
const data = await res.json();
|
|
167
|
-
const title = isTv ? data.name : data.title;
|
|
168
|
-
const releaseDate = isTv ? data.first_air_date : data.release_date;
|
|
169
|
-
const year = releaseDate ? parseInt(releaseDate.split('-')[0], 10) : null;
|
|
170
|
-
const imdbId = data.imdb_id || data.external_ids?.imdb_id || null;
|
|
171
|
-
return { title, year, imdbId };
|
|
172
|
-
} catch {
|
|
173
|
-
return null;
|
|
174
|
-
}
|
|
175
|
-
}
|
|
125
|
+
const apiKey = tmdbApiKey || sdk?.tmdbApiKey || process.env.TMDB_API_KEY || '45dbdd5e714da212424bda0336167447';
|
|
176
126
|
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
if (lower.includes('360')) return '360p';
|
|
184
|
-
if (raw) return String(raw);
|
|
185
|
-
return 'Auto';
|
|
186
|
-
}
|
|
127
|
+
const headers = {
|
|
128
|
+
'Accept': '*/*',
|
|
129
|
+
'Origin': 'https://www.movy.sx',
|
|
130
|
+
'Referer': 'https://www.movy.sx/',
|
|
131
|
+
'User-Agent': 'Mozilla/5.0 (iPhone; CPU iPhone OS 18_5 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/18.5 Mobile/15E148 Safari/604.1'
|
|
132
|
+
};
|
|
187
133
|
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
const tmdbId = parseInt(id, 10);
|
|
192
|
-
const ua = getUA();
|
|
134
|
+
let title = '';
|
|
135
|
+
let year = '';
|
|
136
|
+
let imdbId = '';
|
|
193
137
|
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
138
|
+
try {
|
|
139
|
+
const info = await getTmdbInfo(apiKey, id, type, s);
|
|
140
|
+
title = info?.titles?.[0] || '';
|
|
141
|
+
year = info?.year || '';
|
|
142
|
+
imdbId = info?.imdbId || '';
|
|
143
|
+
} catch { }
|
|
198
144
|
|
|
199
|
-
|
|
200
|
-
|
|
145
|
+
if (!title) {
|
|
146
|
+
try {
|
|
147
|
+
const tmdbRes = await fetchJson(`https://api.themoviedb.org/3/${type}/${id}?api_key=${apiKey}&append_to_response=external_ids`);
|
|
148
|
+
title = tmdbRes?.title || tmdbRes?.name || '';
|
|
149
|
+
year = (tmdbRes?.release_date || tmdbRes?.first_air_date || '').split('-')[0] || '';
|
|
150
|
+
imdbId = tmdbRes?.imdb_id || tmdbRes?.external_ids?.imdb_id || '';
|
|
151
|
+
} catch { }
|
|
152
|
+
}
|
|
201
153
|
|
|
202
|
-
|
|
203
|
-
title,
|
|
204
|
-
mediaType,
|
|
205
|
-
tmdbId: String(tmdbId),
|
|
206
|
-
enc: '2',
|
|
207
|
-
seed,
|
|
208
|
-
});
|
|
154
|
+
if (!title) return null;
|
|
209
155
|
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
if (e) params.set('episodeId', String(e));
|
|
214
|
-
}
|
|
215
|
-
if (imdbId) params.set('imdbId', imdbId);
|
|
156
|
+
const seedRes = await fetchJson(`https://api.wecollege.net/seed?mediaId=${id}`, { headers }).catch(() => null);
|
|
157
|
+
const seed = seedRes?.seed;
|
|
158
|
+
if (!seed) return null;
|
|
216
159
|
|
|
217
|
-
const
|
|
218
|
-
const seenUrls = new Set();
|
|
160
|
+
const servers = ['miami', 'boise', 'vegas', 'chicago', 'reno', 'seattle'];
|
|
219
161
|
const allUrls = [];
|
|
220
162
|
|
|
221
|
-
const
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
163
|
+
for (const srv of servers) {
|
|
164
|
+
const params = new URLSearchParams({
|
|
165
|
+
title,
|
|
166
|
+
mediaType: type,
|
|
167
|
+
year: String(year),
|
|
168
|
+
episodeId: String(e || 1),
|
|
169
|
+
seasonId: String(s || 1),
|
|
170
|
+
tmdbId: String(id),
|
|
171
|
+
imdbId: String(imdbId || ''),
|
|
172
|
+
enc: '2',
|
|
173
|
+
seed
|
|
174
|
+
});
|
|
226
175
|
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
if (
|
|
176
|
+
const url = `https://api.wecollege.net/${srv}/sources?${params.toString()}`;
|
|
177
|
+
const rawEnc = await fetchText(url, { headers }).catch(() => null);
|
|
178
|
+
if (!rawEnc || rawEnc.length < 20 || rawEnc.startsWith('{') || rawEnc.startsWith('<')) continue;
|
|
230
179
|
|
|
231
180
|
try {
|
|
232
|
-
const
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
if (!src || typeof src !== 'object') continue;
|
|
249
|
-
const streamUrl = String(src.url || '').trim();
|
|
250
|
-
if (!streamUrl || seenUrls.has(streamUrl)) continue;
|
|
251
|
-
|
|
252
|
-
seenUrls.add(streamUrl);
|
|
253
|
-
|
|
254
|
-
const cleanQuality = formatQuality(src.quality || 'Auto');
|
|
255
|
-
const isSpecificAudio = server.note.toLowerCase().includes('audio') && !server.note.toLowerCase().includes('original');
|
|
256
|
-
const langLabel = isSpecificAudio ? server.note.split(' ')[0] : '';
|
|
257
|
-
const label = isSpecificAudio
|
|
258
|
-
? `${server.name} (${langLabel}) · ${cleanQuality}`
|
|
259
|
-
: `${server.name} · ${cleanQuality}`;
|
|
260
|
-
|
|
261
|
-
allUrls.push({
|
|
262
|
-
url: streamUrl,
|
|
263
|
-
label,
|
|
264
|
-
headers: {
|
|
265
|
-
'User-Agent': ua,
|
|
266
|
-
Referer: REFERER,
|
|
267
|
-
},
|
|
268
|
-
});
|
|
181
|
+
const dec = decryptPayload(rawEnc.trim(), seed, id);
|
|
182
|
+
const parsed = JSON.parse(dec);
|
|
183
|
+
|
|
184
|
+
if (Array.isArray(parsed?.sources)) {
|
|
185
|
+
for (const src of parsed.sources) {
|
|
186
|
+
if (src?.url) {
|
|
187
|
+
allUrls.push({
|
|
188
|
+
url: src.url,
|
|
189
|
+
server: `Movy - ${srv.toUpperCase()} (${src.quality || 'Auto'})`,
|
|
190
|
+
headers: {
|
|
191
|
+
Referer: 'https://www.movy.sx/',
|
|
192
|
+
Origin: 'https://www.movy.sx'
|
|
193
|
+
}
|
|
194
|
+
});
|
|
195
|
+
}
|
|
196
|
+
}
|
|
269
197
|
}
|
|
270
198
|
} catch { }
|
|
271
|
-
});
|
|
272
199
|
|
|
273
|
-
|
|
200
|
+
if (allUrls.length) break;
|
|
201
|
+
}
|
|
274
202
|
|
|
275
|
-
if (allUrls.length
|
|
203
|
+
if (!allUrls.length) return null;
|
|
276
204
|
|
|
277
205
|
return {
|
|
278
206
|
url: allUrls[0].url,
|
|
279
|
-
allUrls,
|
|
280
207
|
headers: allUrls[0].headers,
|
|
208
|
+
allUrls
|
|
281
209
|
};
|
|
282
210
|
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { fetchJson } from '../utils/helpers.js';
|
|
2
|
+
|
|
3
|
+
export async function getStream({ id, s, e }) {
|
|
4
|
+
const isTv = s != null && e != null;
|
|
5
|
+
const url = isTv
|
|
6
|
+
? `https://net79.cc/api/embed-tmdb/${id}?type=tv&se=${s}&ep=${e}`
|
|
7
|
+
: `https://net79.cc/api/embed-tmdb/${id}?type=movie`;
|
|
8
|
+
|
|
9
|
+
const headers = {
|
|
10
|
+
'Accept': 'application/json',
|
|
11
|
+
'Referer': 'https://net79.cc/',
|
|
12
|
+
'Origin': 'https://net79.cc',
|
|
13
|
+
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/137.0.0.0 Safari/537.36'
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
const res = await fetchJson(url, { headers }).catch(() => null);
|
|
17
|
+
if (!res || !res.ok || res.noSource) return null;
|
|
18
|
+
|
|
19
|
+
const allUrls = [];
|
|
20
|
+
|
|
21
|
+
if (Array.isArray(res.streams) && res.streams.length > 0) {
|
|
22
|
+
for (const stream of res.streams) {
|
|
23
|
+
if (stream?.url) {
|
|
24
|
+
allUrls.push({
|
|
25
|
+
url: stream.url,
|
|
26
|
+
quality: stream.resolution ? `${stream.resolution}p` : 'Auto',
|
|
27
|
+
server: `Net79 - ${stream.resolution ? `${stream.resolution}p` : 'MP4'}`,
|
|
28
|
+
type: stream.url.includes('.m3u8') ? 'hls' : 'mp4',
|
|
29
|
+
headers: {
|
|
30
|
+
Referer: 'https://net79.cc/',
|
|
31
|
+
Origin: 'https://net79.cc'
|
|
32
|
+
}
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
} else if (res.mp4) {
|
|
37
|
+
allUrls.push({
|
|
38
|
+
url: res.mp4,
|
|
39
|
+
quality: res.resolution ? `${res.resolution}p` : 'Auto',
|
|
40
|
+
server: `Net79 - ${res.resolution ? `${res.resolution}p` : 'MP4'}`,
|
|
41
|
+
type: res.mp4.includes('.m3u8') ? 'hls' : 'mp4',
|
|
42
|
+
headers: {
|
|
43
|
+
Referer: 'https://net79.cc/',
|
|
44
|
+
Origin: 'https://net79.cc'
|
|
45
|
+
}
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
if (!allUrls.length) return null;
|
|
50
|
+
|
|
51
|
+
return {
|
|
52
|
+
url: allUrls[0].url,
|
|
53
|
+
headers: allUrls[0].headers,
|
|
54
|
+
allUrls
|
|
55
|
+
};
|
|
56
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { fetchJson } from '../utils/helpers.js';
|
|
2
|
+
|
|
3
|
+
export async function getStream({ id, s, e }) {
|
|
4
|
+
const isTv = s != null && e != null;
|
|
5
|
+
const separator = isTv ? '&' : '?';
|
|
6
|
+
const basePath = isTv
|
|
7
|
+
? `https://f.rivo.st/api/source/tv/${id}?s=${s}&e=${e}`
|
|
8
|
+
: `https://f.rivo.st/api/source/movie/${id}`;
|
|
9
|
+
|
|
10
|
+
const headers = {
|
|
11
|
+
'Accept': '*/*',
|
|
12
|
+
'Referer': 'https://f.rivo.st/',
|
|
13
|
+
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/137.0.0.0 Safari/537.36'
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
const serverIndices = Array.from({ length: 26 }, (_, i) => i + 1);
|
|
17
|
+
|
|
18
|
+
const responses = await Promise.allSettled(
|
|
19
|
+
serverIndices.map(serverNum =>
|
|
20
|
+
fetchJson(`${basePath}&server=${serverNum}`, { headers })
|
|
21
|
+
.catch(() => null)
|
|
22
|
+
)
|
|
23
|
+
);
|
|
24
|
+
|
|
25
|
+
const allUrls = [];
|
|
26
|
+
|
|
27
|
+
for (const res of responses) {
|
|
28
|
+
if (res.status !== 'fulfilled' || !res.value) continue;
|
|
29
|
+
const data = res.value;
|
|
30
|
+
|
|
31
|
+
if (Array.isArray(data.sources)) {
|
|
32
|
+
for (const src of data.sources) {
|
|
33
|
+
if (src?.url) {
|
|
34
|
+
allUrls.push({
|
|
35
|
+
url: src.url,
|
|
36
|
+
server: `Rivo - ${data.server || src.subProvider || 'Server'}`,
|
|
37
|
+
headers: {
|
|
38
|
+
Referer: 'https://f.rivo.st/',
|
|
39
|
+
Origin: 'https://f.rivo.st'
|
|
40
|
+
}
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
if (!allUrls.length) return null;
|
|
48
|
+
|
|
49
|
+
return {
|
|
50
|
+
url: allUrls[0].url,
|
|
51
|
+
headers: allUrls[0].headers,
|
|
52
|
+
allUrls
|
|
53
|
+
};
|
|
54
|
+
}
|