@vyla-entertainment/sdk 1.16.0 → 1.18.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/package.json +1 -1
- package/src/config.js +123 -0
- package/src/manga/atsu.js +168 -0
- package/src/sdk.js +10 -0
- package/src/sources/cinecat.js +19 -0
- package/src/sources/cinesrc.js +398 -0
- package/src/sources/cinezo.js +55 -0
- package/src/sources/hexa.js +54 -0
- package/src/sources/hyper.js +36 -0
- package/src/sources/peestream.js +139 -0
- package/src/sources/yastream.js +30 -0
package/package.json
CHANGED
package/src/config.js
CHANGED
|
@@ -118,6 +118,25 @@ export const SOURCES = [
|
|
|
118
118
|
}],
|
|
119
119
|
},
|
|
120
120
|
|
|
121
|
+
{
|
|
122
|
+
key: 'cinecat',
|
|
123
|
+
label: 'Cinecat',
|
|
124
|
+
sourceFile: 'cinecat',
|
|
125
|
+
proxyParam: 'ccat',
|
|
126
|
+
timeout: 25000,
|
|
127
|
+
jitter: 500,
|
|
128
|
+
retries: 2,
|
|
129
|
+
multiUrl: false,
|
|
130
|
+
skipProxy: true,
|
|
131
|
+
cdnHeaders: [{
|
|
132
|
+
pattern: /bluevelvet\.space/i,
|
|
133
|
+
headers: {
|
|
134
|
+
Referer: 'https://zcdn.bluevelvet.space/',
|
|
135
|
+
Origin: 'https://zcdn.bluevelvet.space',
|
|
136
|
+
},
|
|
137
|
+
}],
|
|
138
|
+
},
|
|
139
|
+
|
|
121
140
|
{
|
|
122
141
|
key: 'cinemaarmy',
|
|
123
142
|
label: 'CinemaArmy',
|
|
@@ -137,6 +156,43 @@ export const SOURCES = [
|
|
|
137
156
|
}],
|
|
138
157
|
},
|
|
139
158
|
|
|
159
|
+
{
|
|
160
|
+
key: 'cinesrc',
|
|
161
|
+
label: 'CineSrc',
|
|
162
|
+
sourceFile: 'cinesrc',
|
|
163
|
+
proxyParam: 'cs',
|
|
164
|
+
timeout: 35000,
|
|
165
|
+
jitter: 500,
|
|
166
|
+
retries: 2,
|
|
167
|
+
multiUrl: true,
|
|
168
|
+
skipProxy: true,
|
|
169
|
+
cdnHeaders: [{
|
|
170
|
+
pattern: /cinesrc\.st/i,
|
|
171
|
+
headers: {
|
|
172
|
+
Referer: 'https://cinesrc.st/',
|
|
173
|
+
Origin: 'https://cinesrc.st',
|
|
174
|
+
},
|
|
175
|
+
}],
|
|
176
|
+
},
|
|
177
|
+
|
|
178
|
+
{
|
|
179
|
+
key: 'cinezo',
|
|
180
|
+
label: 'Cinezo',
|
|
181
|
+
sourceFile: 'cinezo',
|
|
182
|
+
proxyParam: 'czo',
|
|
183
|
+
timeout: 25000,
|
|
184
|
+
jitter: 500,
|
|
185
|
+
retries: 2,
|
|
186
|
+
multiUrl: true,
|
|
187
|
+
cdnHeaders: [{
|
|
188
|
+
pattern: /yenime\.net|flikhub\.net|hakunaymatata\.com/i,
|
|
189
|
+
headers: {
|
|
190
|
+
Referer: 'https://player.cinezo.live/',
|
|
191
|
+
Origin: 'https://player.cinezo.live',
|
|
192
|
+
},
|
|
193
|
+
}],
|
|
194
|
+
},
|
|
195
|
+
|
|
140
196
|
{
|
|
141
197
|
key: 'dulo',
|
|
142
198
|
label: 'Dulo',
|
|
@@ -187,6 +243,43 @@ export const SOURCES = [
|
|
|
187
243
|
skipProxy: true,
|
|
188
244
|
},
|
|
189
245
|
|
|
246
|
+
{
|
|
247
|
+
key: 'hexa',
|
|
248
|
+
label: 'Hexa',
|
|
249
|
+
sourceFile: 'hexa',
|
|
250
|
+
proxyParam: 'hx',
|
|
251
|
+
timeout: 30000,
|
|
252
|
+
jitter: 500,
|
|
253
|
+
retries: 2,
|
|
254
|
+
multiUrl: true,
|
|
255
|
+
skipProxy: true,
|
|
256
|
+
cdnHeaders: [{
|
|
257
|
+
pattern: /hexa\.su|flixer\.su/i,
|
|
258
|
+
headers: {
|
|
259
|
+
Referer: 'https://hexa.su/',
|
|
260
|
+
Origin: 'https://hexa.su',
|
|
261
|
+
},
|
|
262
|
+
}],
|
|
263
|
+
},
|
|
264
|
+
|
|
265
|
+
{
|
|
266
|
+
key: 'hyper',
|
|
267
|
+
label: 'Hyper',
|
|
268
|
+
sourceFile: 'hyper',
|
|
269
|
+
proxyParam: 'hyp',
|
|
270
|
+
timeout: 25000,
|
|
271
|
+
jitter: 500,
|
|
272
|
+
retries: 2,
|
|
273
|
+
multiUrl: true,
|
|
274
|
+
cdnHeaders: [{
|
|
275
|
+
pattern: /hakunaymatata\.com/i,
|
|
276
|
+
headers: {
|
|
277
|
+
Referer: 'https://mzfi.me/',
|
|
278
|
+
Origin: 'https://mzfi.me',
|
|
279
|
+
},
|
|
280
|
+
}],
|
|
281
|
+
},
|
|
282
|
+
|
|
190
283
|
{
|
|
191
284
|
key: 'kisskh',
|
|
192
285
|
label: 'KissKH',
|
|
@@ -279,6 +372,25 @@ export const SOURCES = [
|
|
|
279
372
|
}],
|
|
280
373
|
},
|
|
281
374
|
|
|
375
|
+
{
|
|
376
|
+
key: 'peestream',
|
|
377
|
+
label: 'PeeStream',
|
|
378
|
+
sourceFile: 'peestream',
|
|
379
|
+
proxyParam: 'pee',
|
|
380
|
+
timeout: 25000,
|
|
381
|
+
jitter: 500,
|
|
382
|
+
retries: 2,
|
|
383
|
+
multiUrl: true,
|
|
384
|
+
skipProxy: true,
|
|
385
|
+
cdnHeaders: [{
|
|
386
|
+
pattern: /peestream\.in/i,
|
|
387
|
+
headers: {
|
|
388
|
+
Referer: 'https://peestream.in/',
|
|
389
|
+
Origin: 'https://peestream.in',
|
|
390
|
+
},
|
|
391
|
+
}],
|
|
392
|
+
},
|
|
393
|
+
|
|
282
394
|
{
|
|
283
395
|
key: 'pengu',
|
|
284
396
|
label: 'Pengu',
|
|
@@ -569,6 +681,17 @@ export const SOURCES = [
|
|
|
569
681
|
}],
|
|
570
682
|
},
|
|
571
683
|
|
|
684
|
+
{
|
|
685
|
+
key: 'yastream',
|
|
686
|
+
label: 'YaStream',
|
|
687
|
+
sourceFile: 'yastream',
|
|
688
|
+
proxyParam: 'ya',
|
|
689
|
+
timeout: 25000,
|
|
690
|
+
jitter: 500,
|
|
691
|
+
retries: 2,
|
|
692
|
+
multiUrl: true,
|
|
693
|
+
},
|
|
694
|
+
|
|
572
695
|
];
|
|
573
696
|
|
|
574
697
|
export const HEALTH_PROBE_ID = '155';
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
import { Session, ClientIdentifier, initTLS, destroyTLS } from "node-tls-client";
|
|
2
|
+
|
|
3
|
+
const BASE_URL = "https://atsu.moe";
|
|
4
|
+
const CDN_URL = "https://cdn.atsu.moe";
|
|
5
|
+
|
|
6
|
+
const UA = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36";
|
|
7
|
+
|
|
8
|
+
const HEADERS = {
|
|
9
|
+
"User-Agent": UA,
|
|
10
|
+
"Accept": "*/*",
|
|
11
|
+
"Accept-Language": "en-US,en;q=0.9",
|
|
12
|
+
"Accept-Encoding": "gzip, deflate, br",
|
|
13
|
+
"Cache-Control": "no-cache",
|
|
14
|
+
"Pragma": "no-cache",
|
|
15
|
+
"Referer": `${BASE_URL}/`,
|
|
16
|
+
"Origin": BASE_URL,
|
|
17
|
+
"Sec-Ch-Ua": '"Chromium";v="131", "Not_A Brand";v="24"',
|
|
18
|
+
"Sec-Ch-Ua-Mobile": "?0",
|
|
19
|
+
"Sec-Ch-Ua-Platform": '"Windows"',
|
|
20
|
+
"Sec-Fetch-Dest": "empty",
|
|
21
|
+
"Sec-Fetch-Mode": "cors",
|
|
22
|
+
"Sec-Fetch-Site": "same-origin",
|
|
23
|
+
"Priority": "u=1, i"
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
let tlsInitialized = false;
|
|
27
|
+
let session = null;
|
|
28
|
+
|
|
29
|
+
async function ensureSession() {
|
|
30
|
+
if (!tlsInitialized) {
|
|
31
|
+
await initTLS();
|
|
32
|
+
tlsInitialized = true;
|
|
33
|
+
}
|
|
34
|
+
if (!session) {
|
|
35
|
+
session = new Session({
|
|
36
|
+
clientIdentifier: ClientIdentifier.chrome_131,
|
|
37
|
+
timeout: 15000,
|
|
38
|
+
insecureSkipVerify: false,
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
return session;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const formatAssetUrl = (url) => {
|
|
45
|
+
if (!url) return null;
|
|
46
|
+
if (url.startsWith("http://") || url.startsWith("https://")) return url;
|
|
47
|
+
if (url.startsWith("/static/")) return `${CDN_URL}${url}`;
|
|
48
|
+
if (url.startsWith("/")) return `${CDN_URL}${url}`;
|
|
49
|
+
return `${CDN_URL}/static/${url}`;
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
async function atsuFetch(url) {
|
|
53
|
+
const sess = await ensureSession();
|
|
54
|
+
const res = await sess.get(url, { headers: HEADERS });
|
|
55
|
+
if (res.status !== 200) {
|
|
56
|
+
throw new Error(`Atsu request failed: ${res.status}`);
|
|
57
|
+
}
|
|
58
|
+
const text = await res.text();
|
|
59
|
+
return JSON.parse(text);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export async function searchManga(query, page = 1, perPage = 12) {
|
|
63
|
+
const params = new URLSearchParams({
|
|
64
|
+
q: query,
|
|
65
|
+
query_by: "title,englishTitle,otherNames,authors,acronyms",
|
|
66
|
+
query_by_weights: "4,3,2,2,1",
|
|
67
|
+
num_typos: "4,3,2,1,0",
|
|
68
|
+
prefix: "true,true,true,true,false",
|
|
69
|
+
include_fields: "id,title,englishTitle,authors,poster,posterSmall,posterMedium,type,medium,isAdult,status,year,mbRating,popularity,dateAdded",
|
|
70
|
+
filter_by: "isAdult:=false && views:>0 && hidden:!=true",
|
|
71
|
+
page: String(page),
|
|
72
|
+
per_page: String(perPage),
|
|
73
|
+
infix: "off,off,fallback,off,off"
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
const data = await atsuFetch(`${BASE_URL}/collections/manga/documents/search?${params.toString()}`);
|
|
77
|
+
const results = (data?.hits || []).map(hit => {
|
|
78
|
+
const doc = hit.document || {};
|
|
79
|
+
return {
|
|
80
|
+
id: doc.id,
|
|
81
|
+
title: doc.title,
|
|
82
|
+
englishTitle: doc.englishTitle || null,
|
|
83
|
+
authors: Array.isArray(doc.authors) ? doc.authors : [],
|
|
84
|
+
type: doc.type,
|
|
85
|
+
medium: doc.medium,
|
|
86
|
+
status: doc.status,
|
|
87
|
+
year: doc.year,
|
|
88
|
+
rating: doc.mbRating ?? null,
|
|
89
|
+
popularity: doc.popularity ?? null,
|
|
90
|
+
poster: formatAssetUrl(doc.poster),
|
|
91
|
+
posterSmall: formatAssetUrl(doc.posterSmall),
|
|
92
|
+
posterMedium: formatAssetUrl(doc.posterMedium)
|
|
93
|
+
};
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
return {
|
|
97
|
+
page: data.page,
|
|
98
|
+
total: data.found,
|
|
99
|
+
results
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
export async function getMangaDetails(id) {
|
|
104
|
+
const data = await atsuFetch(`${BASE_URL}/api/manga/page?id=${encodeURIComponent(id)}`);
|
|
105
|
+
const page = data?.mangaPage;
|
|
106
|
+
if (!page) throw new Error(`Manga with ID "${id}" not found`);
|
|
107
|
+
|
|
108
|
+
return {
|
|
109
|
+
id: page.id,
|
|
110
|
+
title: page.title,
|
|
111
|
+
englishTitle: page.englishTitle || null,
|
|
112
|
+
synopsis: page.synopsis || null,
|
|
113
|
+
status: page.status,
|
|
114
|
+
type: page.type,
|
|
115
|
+
medium: page.medium,
|
|
116
|
+
rating: page.avgRating ?? null,
|
|
117
|
+
views: page.views ?? null,
|
|
118
|
+
authors: Array.isArray(page.authors) ? page.authors.map(a => a.name) : [],
|
|
119
|
+
genres: Array.isArray(page.genres) ? page.genres.map(g => g.name) : [],
|
|
120
|
+
tags: Array.isArray(page.tags) ? page.tags.map(t => t.name) : [],
|
|
121
|
+
poster: formatAssetUrl(page.poster?.largeImage || page.poster?.image),
|
|
122
|
+
totalChapters: page.totalChapterCount || page.chapters?.length || 0,
|
|
123
|
+
chapters: (page.chapters || []).map(ch => ({
|
|
124
|
+
id: ch.id,
|
|
125
|
+
number: ch.number,
|
|
126
|
+
title: ch.title,
|
|
127
|
+
pageCount: ch.pageCount,
|
|
128
|
+
createdAt: ch.createdAt ? new Date(ch.createdAt).toISOString() : null
|
|
129
|
+
}))
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
export async function getChapterPages(mangaId, chapterId) {
|
|
134
|
+
const data = await atsuFetch(
|
|
135
|
+
`${BASE_URL}/api/read/chapter?mangaId=${encodeURIComponent(mangaId)}&chapterId=${encodeURIComponent(chapterId)}`
|
|
136
|
+
);
|
|
137
|
+
const chapter = data?.readChapter;
|
|
138
|
+
if (!chapter) throw new Error(`Chapter "${chapterId}" not found for manga "${mangaId}"`);
|
|
139
|
+
|
|
140
|
+
return {
|
|
141
|
+
id: chapter.id,
|
|
142
|
+
number: chapter.number,
|
|
143
|
+
title: chapter.title,
|
|
144
|
+
scanlatorId: chapter.scanlationMangaId || null,
|
|
145
|
+
pages: (chapter.pages || []).map(p => ({
|
|
146
|
+
number: p.number,
|
|
147
|
+
url: formatAssetUrl(p.image),
|
|
148
|
+
width: p.width,
|
|
149
|
+
height: p.height,
|
|
150
|
+
aspectRatio: p.aspectRatio
|
|
151
|
+
}))
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
export async function cleanup() {
|
|
156
|
+
if (session) {
|
|
157
|
+
try {
|
|
158
|
+
await session.close();
|
|
159
|
+
} catch { }
|
|
160
|
+
session = null;
|
|
161
|
+
}
|
|
162
|
+
if (tlsInitialized) {
|
|
163
|
+
try {
|
|
164
|
+
await destroyTLS();
|
|
165
|
+
} catch { }
|
|
166
|
+
tlsInitialized = false;
|
|
167
|
+
}
|
|
168
|
+
}
|
package/src/sdk.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import SOURCES, { SOURCE_MAP } from "./config.js";
|
|
2
2
|
import { fetchDownloads } from "./downloads/downloads.js";
|
|
3
3
|
import { probeSource } from "./health.js";
|
|
4
|
+
import { getChapterPages, getMangaDetails, searchManga } from "./manga/atsu.js";
|
|
4
5
|
import { fetchSubtitles, getSubPathsMovie, getSubPathsTv } from "./subtitles.js";
|
|
5
6
|
import { createStreamArgs } from "./utils/helpers.js";
|
|
6
7
|
|
|
@@ -65,4 +66,13 @@ export default class VylaSDK {
|
|
|
65
66
|
async getDownloads(id, s = null, e = null) {
|
|
66
67
|
return await fetchDownloads(this, id, s, e);
|
|
67
68
|
}
|
|
69
|
+
async searchManga(query, page = 1, perPage = 12) {
|
|
70
|
+
return await searchManga(query, page, perPage);
|
|
71
|
+
}
|
|
72
|
+
async getManga(id) {
|
|
73
|
+
return await getMangaDetails(id);
|
|
74
|
+
}
|
|
75
|
+
async getMangaChapter(mangaId, chapterId) {
|
|
76
|
+
return await getChapterPages(mangaId, chapterId);
|
|
77
|
+
}
|
|
68
78
|
}
|
|
@@ -0,0 +1,19 @@
|
|
|
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://zcdn.bluevelvet.space/cdn?tmdb=${id}&type=tv&season=${s}&episode=${e}`
|
|
7
|
+
: `https://zcdn.bluevelvet.space/cdn?tmdb=${id}&type=movie`;
|
|
8
|
+
|
|
9
|
+
const res = await fetchJson(url);
|
|
10
|
+
if (!res || !res.ready || !res.url) return null;
|
|
11
|
+
|
|
12
|
+
return {
|
|
13
|
+
url: res.url,
|
|
14
|
+
headers: {
|
|
15
|
+
Referer: 'https://zcdn.bluevelvet.space/',
|
|
16
|
+
Origin: 'https://zcdn.bluevelvet.space',
|
|
17
|
+
}
|
|
18
|
+
};
|
|
19
|
+
}
|
|
@@ -0,0 +1,398 @@
|
|
|
1
|
+
import { fetchJson, fetchText } from '../utils/helpers.js';
|
|
2
|
+
|
|
3
|
+
const API = "https://enc-dec.app/api";
|
|
4
|
+
|
|
5
|
+
async function solveStage1(challenge1, seed) {
|
|
6
|
+
const K = [
|
|
7
|
+
0x46C6C721, 0x4230C311, 0x18223A1D, 0x56389BA7,
|
|
8
|
+
0xD4A87103, 0x07CB9E2F, 0x73085F9D, 0xD2396A1F,
|
|
9
|
+
0x23AE5ADD, 0x4646C48F, 0x0F4E0903, 0x808F5F31,
|
|
10
|
+
0x953D4E07, 0x7E238FFF, 0x634595DF, 0xCB5E4301,
|
|
11
|
+
];
|
|
12
|
+
|
|
13
|
+
const SCHEDULES = [
|
|
14
|
+
[0, 7, 6, 3, 2, 1, 5, 4, 0, 2, 4, 6, 5, 1, 7, 3],
|
|
15
|
+
[5, 2, 1, 3, 0, 4, 6, 7, 4, 1, 0, 7, 3, 6, 5, 2],
|
|
16
|
+
[4, 0, 2, 1, 5, 7, 6, 3, 2, 6, 0, 7, 4, 3, 5, 1],
|
|
17
|
+
[5, 4, 7, 0, 2, 3, 1, 6, 5, 1, 4, 0, 7, 2, 3, 6],
|
|
18
|
+
[4, 7, 0, 2, 1, 6, 5, 3, 6, 1, 3, 2, 0, 7, 4, 5],
|
|
19
|
+
[0, 7, 2, 6, 3, 4, 5, 1, 6, 5, 0, 2, 7, 1, 4, 3],
|
|
20
|
+
[0, 4, 5, 3, 1, 7, 2, 6, 3, 7, 5, 6, 1, 0, 2, 4],
|
|
21
|
+
[7, 6, 0, 4, 2, 5, 1, 3, 7, 5, 3, 4, 2, 6, 0, 1],
|
|
22
|
+
];
|
|
23
|
+
|
|
24
|
+
const PERMUTATIONS = [
|
|
25
|
+
[3, 5, 1, 0, 2, 4],
|
|
26
|
+
[2, 0, 4, 5, 3, 1],
|
|
27
|
+
[1, 4, 5, 3, 0, 2],
|
|
28
|
+
[2, 0, 1, 5, 4, 3],
|
|
29
|
+
[4, 2, 1, 0, 3, 5],
|
|
30
|
+
[5, 0, 2, 1, 3, 4],
|
|
31
|
+
[4, 5, 1, 0, 3, 2],
|
|
32
|
+
[4, 0, 3, 5, 1, 2],
|
|
33
|
+
];
|
|
34
|
+
|
|
35
|
+
const u32 = x => x >>> 0;
|
|
36
|
+
const rotl = (x, n) => u32((x << (n & 31)) | (x >>> (32 - (n & 31))));
|
|
37
|
+
const imul = (a, b) => {
|
|
38
|
+
const ah = (a >>> 16) & 0xffff;
|
|
39
|
+
const al = a & 0xffff;
|
|
40
|
+
const bh = (b >>> 16) & 0xffff;
|
|
41
|
+
const bl = b & 0xffff;
|
|
42
|
+
return u32((ah * bl + al * bh) << 16) + al * bl;
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
const b64dec = s => {
|
|
46
|
+
const bin = atob(s.replace(/-/g, '+').replace(/_/g, '/'));
|
|
47
|
+
const bytes = new Uint8Array(bin.length);
|
|
48
|
+
for (let i = 0; i < bin.length; i++) bytes[i] = bin.charCodeAt(i);
|
|
49
|
+
return bytes;
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
const c = b64dec(challenge1.w);
|
|
53
|
+
const view = new DataView(c.buffer);
|
|
54
|
+
const mode = c[21];
|
|
55
|
+
const diffVal = c[22];
|
|
56
|
+
const var_ = c[23];
|
|
57
|
+
const rnd = c[24];
|
|
58
|
+
const m_b = c[25];
|
|
59
|
+
const s_i = c[26];
|
|
60
|
+
const p_i = c[27];
|
|
61
|
+
|
|
62
|
+
const w0 = view.getUint32(0, true);
|
|
63
|
+
const w1 = view.getUint32(4, true);
|
|
64
|
+
const w2 = view.getUint32(8, true);
|
|
65
|
+
const w3 = view.getUint32(12, true);
|
|
66
|
+
const w = [w0, w1, w2, w3];
|
|
67
|
+
|
|
68
|
+
const size = 1 << m_b;
|
|
69
|
+
const s = new Uint32Array(size);
|
|
70
|
+
let x = u32(w[var_ & 3] ^ K[s_i] ^ imul(K[8 + s_i], var_ + 1));
|
|
71
|
+
let y = u32(w[(var_ + 1) & 3] ^ K[15 - s_i]);
|
|
72
|
+
|
|
73
|
+
for (let i = 0; i < size; i++) {
|
|
74
|
+
x = u32(w[(i + s_i) & 3] + x + imul(K[(i + var_) & 15], i + 1));
|
|
75
|
+
x = u32(x ^ (x << 13));
|
|
76
|
+
x = u32(x ^ (x >>> 17));
|
|
77
|
+
x = u32(x ^ (x << 5));
|
|
78
|
+
y = rotl(imul(K[(i + 9) & 15], i + 3) ^ y ^ x, i + var_ + s_i);
|
|
79
|
+
s[i] = u32(imul(K[(i + 5) & 15], i ^ var_) ^ rotl(w[(i + var_) & 3], i + s_i) ^ x ^ y);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const seedBytes = new Uint8Array(seed.match(/.{1,2}/g).map(byte => parseInt(byte, 16)));
|
|
83
|
+
const msg = new Uint8Array(88);
|
|
84
|
+
msg.set(c.slice(0, 16), 24);
|
|
85
|
+
|
|
86
|
+
const cfg = m_b ^ 0x6B29;
|
|
87
|
+
msg[48] = 4;
|
|
88
|
+
msg[49] = mode;
|
|
89
|
+
msg[50] = var_;
|
|
90
|
+
msg[51] = s_i;
|
|
91
|
+
msg[52] = p_i;
|
|
92
|
+
msg[53] = rnd;
|
|
93
|
+
msg[54] = cfg & 0xff;
|
|
94
|
+
msg[55] = cfg >> 8;
|
|
95
|
+
msg.set(seedBytes, 56);
|
|
96
|
+
|
|
97
|
+
const mask = size - 1;
|
|
98
|
+
const sch = SCHEDULES[s_i];
|
|
99
|
+
const perm = PERMUTATIONS[p_i];
|
|
100
|
+
|
|
101
|
+
let n = 0n;
|
|
102
|
+
while (true) {
|
|
103
|
+
const lo = Number(n & 0xffffffffn);
|
|
104
|
+
const hi = Number(n >> 32n);
|
|
105
|
+
|
|
106
|
+
let ma = u32(w[0] ^ K[var_] ^ lo);
|
|
107
|
+
let mb = u32(rotl(lo, var_ + 7) ^ w[2]);
|
|
108
|
+
let mc = u32(w[1] ^ K[8 + s_i] ^ hi);
|
|
109
|
+
let md = u32(rotl(lo ^ hi, s_i + 11) ^ w[3]);
|
|
110
|
+
let me = u32(K[s_i + var_] ^ lo ^ hi);
|
|
111
|
+
let mf = u32(K[15 - var_] ^ w[s_i & 3]);
|
|
112
|
+
|
|
113
|
+
for (let r = 0; r < rnd; r++) {
|
|
114
|
+
const i1 = u32(rotl(mb, r + 1) ^ imul(K[r & 15], mf) ^ me ^ ma) & mask;
|
|
115
|
+
const v1 = s[i1];
|
|
116
|
+
const i2 = u32(rotl(me, var_ + r + 3) ^ imul(K[(r + 7) & 15], md) ^ mc ^ v1) & mask;
|
|
117
|
+
const op = sch[r];
|
|
118
|
+
|
|
119
|
+
if (op === 0) {
|
|
120
|
+
ma = rotl(u32(ma + md + v1), 5);
|
|
121
|
+
mc = u32(imul(mc ^ mf ^ ma, 1187432225) + mb);
|
|
122
|
+
} else if (op === 1) {
|
|
123
|
+
mb = rotl(mc ^ me ^ mb ^ v1, 11);
|
|
124
|
+
md = u32(imul(mb ^ ma, 1110491921) + md);
|
|
125
|
+
} else if (op === 2) {
|
|
126
|
+
mc = rotl(u32(mc + mb + v1), 17);
|
|
127
|
+
ma = u32(imul(md ^ mf ^ mc, 404896285) ^ ma);
|
|
128
|
+
} else if (op === 3) {
|
|
129
|
+
md = rotl(ma ^ md ^ v1, 23);
|
|
130
|
+
mb = u32(imul(mc ^ me ^ md, 1446550439) + mb);
|
|
131
|
+
} else if (op === 4) {
|
|
132
|
+
ma = u32(imul(ma ^ me ^ v1, -727158525) + rotl(mb, 7));
|
|
133
|
+
md = u32(rotl(u32(mc + mf + ma), 13) ^ md);
|
|
134
|
+
} else if (op === 5) {
|
|
135
|
+
mc = u32(imul(u32(mc + mf + v1), 130784815) ^ rotl(md, 9));
|
|
136
|
+
mb = u32(rotl(ma ^ me ^ mc, 19) + mb);
|
|
137
|
+
} else if (op === 6) {
|
|
138
|
+
mb = u32(imul(mb ^ mf ^ v1, 1929928605) + rotl(ma, 3));
|
|
139
|
+
mc = u32(rotl(u32(md + me + mb), 15) ^ mc);
|
|
140
|
+
} else {
|
|
141
|
+
md = u32(imul(u32(md + me + v1), -767989217) ^ rotl(mc, 21));
|
|
142
|
+
ma = u32(rotl(mb ^ mf ^ md, 27) + ma);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
const v2 = s[i2];
|
|
146
|
+
if (((mf ^ r ^ v1) & 1) === 0) {
|
|
147
|
+
mf = rotl(u32(v2 + mf + ma), r + 5);
|
|
148
|
+
me = u32(imul(K[8 + op], v2 ^ me ^ mf) + md);
|
|
149
|
+
} else {
|
|
150
|
+
me = rotl(v2 ^ me ^ mb, r + 9);
|
|
151
|
+
mf = u32(imul(K[(op + 11) & 15], u32(v2 + mf + me)) ^ mc);
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
const mArr = [ma, mc, mb, md, mf, me];
|
|
156
|
+
const v = perm.map(idx => mArr[idx]);
|
|
157
|
+
|
|
158
|
+
const msgView = new DataView(msg.buffer);
|
|
159
|
+
msgView.setUint32(0, v[0], true);
|
|
160
|
+
msgView.setUint32(4, v[1], true);
|
|
161
|
+
msgView.setUint32(8, v[2], true);
|
|
162
|
+
msgView.setUint32(12, v[3], true);
|
|
163
|
+
msgView.setUint32(16, v[4], true);
|
|
164
|
+
msgView.setUint32(20, v[5], true);
|
|
165
|
+
msgView.setBigUint64(40, n, true);
|
|
166
|
+
|
|
167
|
+
const hashBuf = await crypto.subtle.digest('SHA-256', msg);
|
|
168
|
+
const hashBytes = new Uint8Array(hashBuf);
|
|
169
|
+
|
|
170
|
+
const b = Math.floor(diffVal / 8);
|
|
171
|
+
const rBits = diffVal % 8;
|
|
172
|
+
let ok = true;
|
|
173
|
+
for (let i = 0; i < b; i++) {
|
|
174
|
+
if (hashBytes[i] !== 0) {
|
|
175
|
+
ok = false;
|
|
176
|
+
break;
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
if (ok && (rBits === 0 || (hashBytes[b] >> (8 - rBits)) === 0)) {
|
|
180
|
+
return `m4.${n.toString(16)}`;
|
|
181
|
+
}
|
|
182
|
+
n++;
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
async function solveStage2(data) {
|
|
187
|
+
const target = data.pack[0].split('').reverse().join('');
|
|
188
|
+
const salt = data.pack[3].split('').reverse().join('');
|
|
189
|
+
const r = data.pack[4].split('').reverse().join('');
|
|
190
|
+
|
|
191
|
+
const b64decStr = s => decodeURIComponent(escape(atob(s.replace(/-/g, '+').replace(/_/g, '/'))));
|
|
192
|
+
const body = b64decStr(r.split('.')[1]);
|
|
193
|
+
const payload = b64decStr(body.split('.')[1]);
|
|
194
|
+
const difficulty = JSON.parse(payload).d;
|
|
195
|
+
|
|
196
|
+
const width = Math.floor((difficulty + 3) / 4);
|
|
197
|
+
const limit = 1 << difficulty;
|
|
198
|
+
|
|
199
|
+
for (let counter = 0; counter < limit; counter++) {
|
|
200
|
+
const key = counter.toString(16).padStart(width, '0');
|
|
201
|
+
const msgUint8 = new TextEncoder().encode(salt + key);
|
|
202
|
+
const hashBuffer = await crypto.subtle.digest('SHA-256', msgUint8);
|
|
203
|
+
const hashArray = Array.from(new Uint8Array(hashBuffer));
|
|
204
|
+
const digest = hashArray.map(b => b.toString(16).padStart(2, '0')).join('');
|
|
205
|
+
|
|
206
|
+
if (digest === target) {
|
|
207
|
+
return key;
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
throw new Error('no solution found');
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
export async function getStream({ id, s, e }) {
|
|
214
|
+
const isTv = s != null && e != null;
|
|
215
|
+
const type = isTv ? 'tv' : 'movie';
|
|
216
|
+
const url = isTv
|
|
217
|
+
? `https://cinesrc.st/embed/tv/${id}?s=${s}&e=${e}`
|
|
218
|
+
: `https://cinesrc.st/embed/movie/${id}`;
|
|
219
|
+
|
|
220
|
+
const headers = {
|
|
221
|
+
"Origin": "https://cinesrc.st",
|
|
222
|
+
"Referer": "https://cinesrc.st/",
|
|
223
|
+
"Content-Type": "text/plain;charset=UTF-8",
|
|
224
|
+
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.37 (KHTML, like Gecko) Chrome/137.0.0.0 Safari/537.37"
|
|
225
|
+
};
|
|
226
|
+
|
|
227
|
+
const fields = [type, id, s ? String(s) : null, e ? String(e) : null];
|
|
228
|
+
const encoded = btoa(JSON.stringify(fields).replace(/ /g, '')).replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '');
|
|
229
|
+
|
|
230
|
+
const bootstrapHeaders = { ...headers, "x-cs-q": encoded };
|
|
231
|
+
const bootstrap = await fetchJson("https://cinesrc.st/api/c/bootstrap", {
|
|
232
|
+
method: "POST",
|
|
233
|
+
headers: bootstrapHeaders,
|
|
234
|
+
body: "{}"
|
|
235
|
+
});
|
|
236
|
+
|
|
237
|
+
const cookies = {
|
|
238
|
+
"x-cs-b": "20261014-a",
|
|
239
|
+
"x-cs-q": encoded,
|
|
240
|
+
"x-cs-r": bootstrap.r,
|
|
241
|
+
"x-cs-p": bootstrap.p,
|
|
242
|
+
};
|
|
243
|
+
|
|
244
|
+
const challenge1 = await fetchJson("https://cinesrc.st/api/c/issue", {
|
|
245
|
+
headers: { ...headers, ...cookies }
|
|
246
|
+
});
|
|
247
|
+
|
|
248
|
+
const payload1 = {
|
|
249
|
+
data: {
|
|
250
|
+
url,
|
|
251
|
+
user_agent: headers["User-Agent"],
|
|
252
|
+
challenge_data: challenge1
|
|
253
|
+
},
|
|
254
|
+
type: "seed"
|
|
255
|
+
};
|
|
256
|
+
|
|
257
|
+
const seedRes = await fetchJson(`${API}/enc-cinesrc`, {
|
|
258
|
+
method: "POST",
|
|
259
|
+
headers: { "Content-Type": "application/json" },
|
|
260
|
+
body: JSON.stringify(payload1)
|
|
261
|
+
});
|
|
262
|
+
|
|
263
|
+
const seedData = seedRes.result;
|
|
264
|
+
const solution1 = await solveStage1(challenge1, seedData.seed);
|
|
265
|
+
|
|
266
|
+
const challenge2 = await fetchJson("https://cinesrc.st/api/c/stage2/issue", {
|
|
267
|
+
headers: { ...headers, ...cookies }
|
|
268
|
+
});
|
|
269
|
+
const solution2 = await solveStage2(challenge2);
|
|
270
|
+
|
|
271
|
+
const payload2 = {
|
|
272
|
+
data: {
|
|
273
|
+
url,
|
|
274
|
+
user_agent: headers["User-Agent"],
|
|
275
|
+
challenge_data: {
|
|
276
|
+
stage1: { challenge: challenge1, solution: solution1 },
|
|
277
|
+
stage2: { challenge: challenge2, solution: solution2 }
|
|
278
|
+
},
|
|
279
|
+
state: seedData.state
|
|
280
|
+
},
|
|
281
|
+
type: "encrypt"
|
|
282
|
+
};
|
|
283
|
+
|
|
284
|
+
const encryptRes = await fetchJson(`${API}/enc-cinesrc`, {
|
|
285
|
+
method: "POST",
|
|
286
|
+
headers: { "Content-Type": "application/json" },
|
|
287
|
+
body: JSON.stringify(payload2)
|
|
288
|
+
});
|
|
289
|
+
|
|
290
|
+
const encData = encryptRes.result;
|
|
291
|
+
const token = `${encData.token}::c3::${cookies["x-cs-r"]}`;
|
|
292
|
+
const nextActions = encData.headers;
|
|
293
|
+
|
|
294
|
+
const providersText = await fetchText(url, {
|
|
295
|
+
method: "POST",
|
|
296
|
+
headers: { ...headers, ...cookies, "Next-Action": nextActions.getProviderList },
|
|
297
|
+
body: JSON.stringify([])
|
|
298
|
+
});
|
|
299
|
+
|
|
300
|
+
const lines = providersText.split('\n');
|
|
301
|
+
let providers = null;
|
|
302
|
+
for (const rawLine of lines) {
|
|
303
|
+
const colonIndex = rawLine.indexOf(':');
|
|
304
|
+
if (colonIndex !== -1) {
|
|
305
|
+
const candidate = rawLine.slice(colonIndex + 1).trim();
|
|
306
|
+
if (candidate.startsWith('[') && candidate.endsWith(']')) {
|
|
307
|
+
try {
|
|
308
|
+
providers = JSON.parse(candidate);
|
|
309
|
+
break;
|
|
310
|
+
} catch { }
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
if (!Array.isArray(providers) || providers.length === 0) return null;
|
|
316
|
+
|
|
317
|
+
const allUrls = [];
|
|
318
|
+
|
|
319
|
+
for (const providerObj of providers) {
|
|
320
|
+
const provider = providerObj.id;
|
|
321
|
+
const streamPayload = [
|
|
322
|
+
id,
|
|
323
|
+
isTv ? "show" : type,
|
|
324
|
+
isTv ? String(s) : "$undefined",
|
|
325
|
+
isTv ? String(e) : "$undefined",
|
|
326
|
+
token,
|
|
327
|
+
provider
|
|
328
|
+
];
|
|
329
|
+
|
|
330
|
+
let streamRes;
|
|
331
|
+
try {
|
|
332
|
+
streamRes = await fetchText(url, {
|
|
333
|
+
method: "POST",
|
|
334
|
+
headers: { ...headers, ...cookies, "Next-Action": nextActions.getStream },
|
|
335
|
+
body: JSON.stringify(streamPayload)
|
|
336
|
+
});
|
|
337
|
+
} catch {
|
|
338
|
+
continue;
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
if (!streamRes || streamRes.includes('"status":500') || streamRes.includes('invalid_challenge') || streamRes.length < 10) continue;
|
|
342
|
+
|
|
343
|
+
let decryptRes;
|
|
344
|
+
try {
|
|
345
|
+
decryptRes = await fetchJson(`${API}/dec-cinesrc`, {
|
|
346
|
+
method: "POST",
|
|
347
|
+
headers: { "Content-Type": "application/json" },
|
|
348
|
+
body: JSON.stringify({ text: streamRes, state: encData.state })
|
|
349
|
+
});
|
|
350
|
+
} catch {
|
|
351
|
+
continue;
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
if (decryptRes && decryptRes.status === 200 && decryptRes.result) {
|
|
355
|
+
let resultData = decryptRes.result;
|
|
356
|
+
if (typeof resultData === 'string') {
|
|
357
|
+
try {
|
|
358
|
+
resultData = JSON.parse(resultData);
|
|
359
|
+
} catch { }
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
let streamUrl = null;
|
|
363
|
+
if (Array.isArray(resultData?.url)) {
|
|
364
|
+
streamUrl = resultData.url[0]?.url;
|
|
365
|
+
} else if (typeof resultData?.url === 'string') {
|
|
366
|
+
streamUrl = resultData.url;
|
|
367
|
+
} else if (resultData?.file) {
|
|
368
|
+
streamUrl = resultData.file;
|
|
369
|
+
} else if (Array.isArray(resultData?.sources)) {
|
|
370
|
+
streamUrl = resultData.sources[0]?.url;
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
if (streamUrl) {
|
|
374
|
+
if (streamUrl.startsWith('/')) {
|
|
375
|
+
streamUrl = `https://cinesrc.st${streamUrl}`;
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
allUrls.push({
|
|
379
|
+
url: streamUrl,
|
|
380
|
+
server: `CineSrc - ${providerObj.name || provider}`,
|
|
381
|
+
headers: resultData.headers || {
|
|
382
|
+
Referer: "https://cinesrc.st/",
|
|
383
|
+
Origin: "https://cinesrc.st"
|
|
384
|
+
}
|
|
385
|
+
});
|
|
386
|
+
break;
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
if (!allUrls.length) return null;
|
|
392
|
+
|
|
393
|
+
return {
|
|
394
|
+
url: allUrls[0].url,
|
|
395
|
+
headers: allUrls[0].headers,
|
|
396
|
+
allUrls
|
|
397
|
+
};
|
|
398
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
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 referer = isTv
|
|
6
|
+
? `https://player.cinezo.live/embed/tv/${id}?s=${s}&e=${e}`
|
|
7
|
+
: `https://player.cinezo.live/embed/movie/${id}`;
|
|
8
|
+
|
|
9
|
+
const headers = {
|
|
10
|
+
'Accept': '*/*',
|
|
11
|
+
'Origin': 'https://player.cinezo.live',
|
|
12
|
+
'Referer': referer,
|
|
13
|
+
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/155.0.0.0 Safari/537.36 Edg/155.0.0.0'
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
const hosts = [
|
|
17
|
+
'https://proxy.yenime.net',
|
|
18
|
+
'https://proxy1.flikhub.net'
|
|
19
|
+
];
|
|
20
|
+
|
|
21
|
+
const sources = ['zendaya', 'berlin', 'jennifer', 'scarlet'];
|
|
22
|
+
const allUrls = [];
|
|
23
|
+
|
|
24
|
+
for (const host of hosts) {
|
|
25
|
+
for (const source of sources) {
|
|
26
|
+
const url = isTv
|
|
27
|
+
? `${host}/tv?id=${id}&season=${s}&episode=${e}&mode=json&sources=${source}&hevc=1`
|
|
28
|
+
: `${host}/movie?id=${id}&mode=json&sources=${source}&hevc=1`;
|
|
29
|
+
|
|
30
|
+
try {
|
|
31
|
+
const res = await fetchJson(url, { headers });
|
|
32
|
+
const streamUrl = res?.source?.url;
|
|
33
|
+
if (streamUrl) {
|
|
34
|
+
allUrls.push({
|
|
35
|
+
url: streamUrl,
|
|
36
|
+
server: `Cinezo - ${res.source.label || source}`,
|
|
37
|
+
headers: {
|
|
38
|
+
Referer: 'https://player.cinezo.live/',
|
|
39
|
+
Origin: 'https://player.cinezo.live'
|
|
40
|
+
}
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
} catch { }
|
|
44
|
+
}
|
|
45
|
+
if (allUrls.length) break;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
if (!allUrls.length) return null;
|
|
49
|
+
|
|
50
|
+
return {
|
|
51
|
+
url: allUrls[0].url,
|
|
52
|
+
headers: allUrls[0].headers,
|
|
53
|
+
allUrls
|
|
54
|
+
};
|
|
55
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { fetchJson, fetchText } from '../utils/helpers.js';
|
|
2
|
+
|
|
3
|
+
const API = "https://enc-dec.app/api";
|
|
4
|
+
|
|
5
|
+
function getRandomHex(size = 32) {
|
|
6
|
+
const array = new Uint8Array(size);
|
|
7
|
+
crypto.getRandomValues(array);
|
|
8
|
+
return Array.from(array).map(b => b.toString(16).padStart(2, '0')).join('');
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export async function getStream({ id, s, e }) {
|
|
12
|
+
const isTv = s != null && e != null;
|
|
13
|
+
const key = getRandomHex(32);
|
|
14
|
+
|
|
15
|
+
const headers = {
|
|
16
|
+
"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",
|
|
17
|
+
"Referer": "https://hexa.su/",
|
|
18
|
+
"Accept": "text/plain",
|
|
19
|
+
"X-Fingerprint-Lite": "e9136c41504646444",
|
|
20
|
+
"X-Api-Key": key
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
const encHexaRes = await fetchJson(`${API}/enc-hexa`);
|
|
24
|
+
if (!encHexaRes || encHexaRes.status !== 200) return null;
|
|
25
|
+
headers["X-Cap-Token"] = encHexaRes.result.token;
|
|
26
|
+
|
|
27
|
+
const url = isTv
|
|
28
|
+
? `https://theemoviedb.hexa.su/api/tmdb/tv/${id}/season/${s}/episode/${e}/images`
|
|
29
|
+
: `https://theemoviedb.hexa.su/api/tmdb/movie/${id}/images`;
|
|
30
|
+
|
|
31
|
+
const encrypted = await fetchText(url, { headers });
|
|
32
|
+
if (!encrypted) return null;
|
|
33
|
+
|
|
34
|
+
const decRes = await fetchJson(`${API}/dec-hexa`, {
|
|
35
|
+
method: "POST",
|
|
36
|
+
headers: { "Content-Type": "application/json" },
|
|
37
|
+
body: JSON.stringify({ text: encrypted, key })
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
if (!decRes || decRes.status !== 200 || !decRes.result) return null;
|
|
41
|
+
|
|
42
|
+
const data = decRes.result;
|
|
43
|
+
const streamUrl = data.url || data.file || (Array.isArray(data.sources) ? data.sources[0]?.url : null);
|
|
44
|
+
|
|
45
|
+
if (!streamUrl) return null;
|
|
46
|
+
|
|
47
|
+
return {
|
|
48
|
+
url: streamUrl,
|
|
49
|
+
headers: {
|
|
50
|
+
Referer: "https://hexa.su/",
|
|
51
|
+
Origin: "https://hexa.su"
|
|
52
|
+
}
|
|
53
|
+
};
|
|
54
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
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 path = isTv
|
|
6
|
+
? `/local/stream/series/tmdb:${id}:${s}:${e}.json`
|
|
7
|
+
: `/local/stream/movie/tmdb:${id}.json`;
|
|
8
|
+
|
|
9
|
+
const url = `https://hyper.stravo.site${path}`;
|
|
10
|
+
const res = await fetchJson(url);
|
|
11
|
+
if (!res || !Array.isArray(res.streams)) return null;
|
|
12
|
+
|
|
13
|
+
const validStreams = res.streams.filter(item => {
|
|
14
|
+
if (!item.url || item.externalUrl) return false;
|
|
15
|
+
if (typeof item.title === 'string' && item.title.includes('🔒')) return false;
|
|
16
|
+
return true;
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
if (!validStreams.length) return null;
|
|
20
|
+
|
|
21
|
+
const allUrls = validStreams.map(item => ({
|
|
22
|
+
url: item.url,
|
|
23
|
+
server: `Hyper - ${item.title?.replace(/[^\x20-\x7E]+/g, '').trim() || 'Server'}`,
|
|
24
|
+
headers: item.behaviorHints?.proxyHeaders?.request || {
|
|
25
|
+
Referer: 'https://mzfi.me/',
|
|
26
|
+
Accept: '*/*'
|
|
27
|
+
},
|
|
28
|
+
skipProxy: item.behaviorHints?.notWebReady === false
|
|
29
|
+
}));
|
|
30
|
+
|
|
31
|
+
return {
|
|
32
|
+
url: allUrls[0].url,
|
|
33
|
+
headers: allUrls[0].headers,
|
|
34
|
+
allUrls
|
|
35
|
+
};
|
|
36
|
+
}
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
import { USER_AGENT, fetchJson, getTmdbInfo } from '../utils/helpers.js';
|
|
2
|
+
|
|
3
|
+
const BASE_URL = 'https://providers.peestream.in';
|
|
4
|
+
|
|
5
|
+
const PROVIDERS = {
|
|
6
|
+
purstream: 'Athena',
|
|
7
|
+
vidrock: 'Thor',
|
|
8
|
+
vaplayer: 'Poseidon',
|
|
9
|
+
netmirror: 'Ra'
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
export async function getStream(args) {
|
|
13
|
+
const { id, s, e, server, tmdbApiKey } = args;
|
|
14
|
+
const isTv = s != null && e != null;
|
|
15
|
+
const type = isTv ? 'tv' : 'movie';
|
|
16
|
+
|
|
17
|
+
let title = args.title || '';
|
|
18
|
+
let releaseYear = args.year || '';
|
|
19
|
+
let imdbId = args.imdbId || '';
|
|
20
|
+
let seasonTmdbId = args.seasonTmdbId || '';
|
|
21
|
+
let episodeTmdbId = args.episodeTmdbId || '';
|
|
22
|
+
|
|
23
|
+
try {
|
|
24
|
+
const info = await getTmdbInfo(id, type, s, tmdbApiKey);
|
|
25
|
+
if (info) {
|
|
26
|
+
title = title || info.name || info.title || '';
|
|
27
|
+
|
|
28
|
+
if (!releaseYear) {
|
|
29
|
+
const date = info.release_date || info.first_air_date || '';
|
|
30
|
+
releaseYear = date ? date.split('-')[0] : (info.year || '');
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
if (!imdbId && info.external_ids?.imdb_id) {
|
|
34
|
+
imdbId = info.external_ids.imdb_id;
|
|
35
|
+
} else if (!imdbId && info.imdb_id) {
|
|
36
|
+
imdbId = info.imdb_id;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
if (isTv) {
|
|
40
|
+
if (!seasonTmdbId && Array.isArray(info.seasons)) {
|
|
41
|
+
const matchedSeason = info.seasons.find(
|
|
42
|
+
item => item.season_number === Number(s)
|
|
43
|
+
);
|
|
44
|
+
if (matchedSeason) {
|
|
45
|
+
seasonTmdbId = String(matchedSeason.id);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
if (!episodeTmdbId && tmdbApiKey) {
|
|
50
|
+
try {
|
|
51
|
+
const seasonData = await fetchJson(
|
|
52
|
+
`https://api.themoviedb.org/3/tv/${id}/season/${s}?api_key=${tmdbApiKey}`
|
|
53
|
+
);
|
|
54
|
+
if (seasonData?.episodes) {
|
|
55
|
+
const matchedEp = seasonData.episodes.find(
|
|
56
|
+
ep => ep.episode_number === Number(e)
|
|
57
|
+
);
|
|
58
|
+
if (matchedEp) {
|
|
59
|
+
episodeTmdbId = String(matchedEp.id);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
} catch { }
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
} catch { }
|
|
67
|
+
|
|
68
|
+
const queryParams = new URLSearchParams({
|
|
69
|
+
type,
|
|
70
|
+
tmdbId: String(id),
|
|
71
|
+
title: title || String(id)
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
if (releaseYear) queryParams.set('releaseYear', String(releaseYear));
|
|
75
|
+
if (imdbId) queryParams.set('imdbId', String(imdbId));
|
|
76
|
+
|
|
77
|
+
if (isTv) {
|
|
78
|
+
queryParams.set('seasonNumber', String(s));
|
|
79
|
+
queryParams.set('episodeNumber', String(e));
|
|
80
|
+
if (seasonTmdbId) queryParams.set('seasonTmdbId', String(seasonTmdbId));
|
|
81
|
+
if (episodeTmdbId) queryParams.set('episodeTmdbId', String(episodeTmdbId));
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
const allUrls = [];
|
|
85
|
+
|
|
86
|
+
try {
|
|
87
|
+
const scrapeUrl = `${BASE_URL}/scrape?${queryParams.toString()}`;
|
|
88
|
+
const res = await fetch(scrapeUrl, {
|
|
89
|
+
headers: {
|
|
90
|
+
'User-Agent': USER_AGENT,
|
|
91
|
+
'Accept': 'text/event-stream',
|
|
92
|
+
'Referer': `${BASE_URL}/`
|
|
93
|
+
},
|
|
94
|
+
signal: AbortSignal.timeout(20000)
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
if (res.ok) {
|
|
98
|
+
const text = await res.text();
|
|
99
|
+
const events = text.split('\n\n');
|
|
100
|
+
|
|
101
|
+
for (const ev of events) {
|
|
102
|
+
if (ev.includes('event: completed')) {
|
|
103
|
+
const dataMatch = ev.match(/data:\s*(.+)/);
|
|
104
|
+
if (dataMatch) {
|
|
105
|
+
try {
|
|
106
|
+
const parsed = JSON.parse(dataMatch[1].trim());
|
|
107
|
+
const stream = parsed.stream;
|
|
108
|
+
const streamUrl = stream?.playlist || stream?.url || stream?.file;
|
|
109
|
+
if (streamUrl) {
|
|
110
|
+
const sourceName = PROVIDERS[parsed.sourceId] || parsed.sourceId || 'Poseidon';
|
|
111
|
+
allUrls.push({
|
|
112
|
+
url: streamUrl,
|
|
113
|
+
server: `PeeStream - ${sourceName}`,
|
|
114
|
+
quality: 'Auto',
|
|
115
|
+
type: stream.type === 'hls' || streamUrl.includes('.m3u8') ? 'hls' : 'mp4',
|
|
116
|
+
headers: stream.headers || {
|
|
117
|
+
'User-Agent': USER_AGENT
|
|
118
|
+
}
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
} catch { }
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
} catch { }
|
|
127
|
+
|
|
128
|
+
if (server && server !== 'all') {
|
|
129
|
+
const clean = server.toLowerCase().replace('peestream - ', '').trim();
|
|
130
|
+
const filtered = allUrls.filter(u => u.server.toLowerCase().includes(clean));
|
|
131
|
+
if (filtered.length) return { allUrls: filtered };
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
return allUrls.length ? { allUrls } : null;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
export async function getSources() {
|
|
138
|
+
return Object.values(PROVIDERS).map(name => `PeeStream - ${name}`);
|
|
139
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
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 path = isTv
|
|
6
|
+
? `/stream/series/tmdb:${id}:${s}:${e}.json`
|
|
7
|
+
: `/stream/movie/tmdb:${id}.json`;
|
|
8
|
+
|
|
9
|
+
const url = `https://yastream.tamthai.de${path}`;
|
|
10
|
+
const res = await fetchJson(url);
|
|
11
|
+
if (!res || !Array.isArray(res.streams) || !res.streams.length) return null;
|
|
12
|
+
|
|
13
|
+
const allUrls = res.streams.map(item => {
|
|
14
|
+
if (!item.url) return null;
|
|
15
|
+
return {
|
|
16
|
+
url: item.url,
|
|
17
|
+
server: `YaStream - ${item.name?.replace(/\n/g, ' ') || 'Server'}`,
|
|
18
|
+
headers: item.behaviorHints?.proxyHeaders?.request || undefined,
|
|
19
|
+
skipProxy: item.behaviorHints?.notWebReady === false
|
|
20
|
+
};
|
|
21
|
+
}).filter(Boolean);
|
|
22
|
+
|
|
23
|
+
if (!allUrls.length) return null;
|
|
24
|
+
|
|
25
|
+
return {
|
|
26
|
+
url: allUrls[0].url,
|
|
27
|
+
headers: allUrls[0].headers,
|
|
28
|
+
allUrls
|
|
29
|
+
};
|
|
30
|
+
}
|