@vyla-entertainment/sdk 1.0.0 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.env.example +1 -0
- package/.github/workflows/publish.yml +38 -0
- package/.gitlab-ci.yml +22 -0
- package/LICENSE +0 -0
- package/README.md +13 -26
- package/package.json +5 -3
- package/src/config.js +63 -77
- package/src/downloads/downloads.js +8 -4
- package/src/downloads/sources/trendimovies.js +0 -0
- package/src/health.js +0 -0
- package/src/sdk.d.ts +123 -0
- package/src/sdk.js +0 -0
- package/src/sources/123anime.js +0 -0
- package/src/sources/anihq.js +14 -2
- package/src/sources/anineko.js +122 -15
- package/src/sources/anipm.js +0 -0
- package/src/sources/cinejoy.js +210 -0
- package/src/sources/flaxmovies.js +114 -10
- package/src/sources/frame.js +88 -0
- package/src/sources/fsharetv.js +0 -0
- package/src/sources/fsonic.js +2 -2
- package/src/sources/fsonline.js +2 -2
- package/src/sources/hexa.js +0 -0
- package/src/sources/kisskh.js +0 -0
- package/src/sources/lmscript.js +43 -0
- package/src/sources/lookmovie.js +0 -0
- package/src/sources/mapple.js +40 -4
- package/src/sources/meowtv.js +1 -9
- package/src/sources/peestream.js +122 -0
- package/src/sources/pengu.js +0 -0
- package/src/sources/purstream.js +0 -0
- package/src/sources/vidapi.js +0 -0
- package/src/sources/vidcore.js +0 -0
- package/src/sources/videasy.js +6 -12
- package/src/sources/vidfast.js +0 -0
- package/src/sources/vidlink.js +0 -0
- package/src/sources/vidnest.js +0 -0
- package/src/sources/vidrift.js +0 -0
- package/src/sources/vidrock.js +0 -0
- package/src/sources/vidsync.js +0 -0
- package/src/sources/vidup.js +0 -0
- package/src/sources/vidvault.js +114 -0
- package/src/sources/vidzee.js +73 -87
- package/src/sources/vixsrc.js +28 -14
- package/src/sources/xpass.js +0 -0
- package/src/subtitles.js +0 -0
- package/src/utils/helpers.js +56 -20
- package/src/sources/1embed.js +0 -92
- package/src/sources/goated.js +0 -262
- package/src/sources/nebula.js +0 -62
- package/src/sources/nekowatch.js +0 -57
- package/src/sources/opstream.js +0 -123
- package/src/sources/peachify.js +0 -78
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { getTmdbInfo } from '../utils/helpers.js';
|
|
2
|
+
|
|
3
|
+
export async function getStream(args) {
|
|
4
|
+
const { id, s, e, tmdbApiKey } = args;
|
|
5
|
+
const type = (s != null && e != null) ? 'tv' : 'movie';
|
|
6
|
+
|
|
7
|
+
try {
|
|
8
|
+
const tmdbInfo = await getTmdbInfo(tmdbApiKey, id, type, s);
|
|
9
|
+
const name = tmdbInfo.titles[0] || '';
|
|
10
|
+
if (!name) return null;
|
|
11
|
+
|
|
12
|
+
if (type === 'tv') {
|
|
13
|
+
return null;
|
|
14
|
+
} else {
|
|
15
|
+
const url = `https://lmscript.xyz/v1/movies?filters[q]=${encodeURIComponent(name)}&expand=streams`;
|
|
16
|
+
const response = await fetch(url);
|
|
17
|
+
const data = await response.json();
|
|
18
|
+
|
|
19
|
+
const movie = data.items?.find(item => `${item.tmdb_prefix}` === `${id}` || `${item.tmdb_id}` === `${id}`);
|
|
20
|
+
if (!movie) return null;
|
|
21
|
+
|
|
22
|
+
const streams = movie.streams || {};
|
|
23
|
+
const SOURCE_ORDER = ["1080p", "720p", "480p", "360p"];
|
|
24
|
+
const sortedStreams = Object.keys(streams).sort((a, b) => SOURCE_ORDER.indexOf(a) - SOURCE_ORDER.indexOf(b));
|
|
25
|
+
|
|
26
|
+
if (!sortedStreams.length) return null;
|
|
27
|
+
|
|
28
|
+
const allUrls = sortedStreams.map(quality => {
|
|
29
|
+
const streamUrl = streams[quality];
|
|
30
|
+
return {
|
|
31
|
+
url: streamUrl,
|
|
32
|
+
quality,
|
|
33
|
+
type: streamUrl.includes('.m3u8') ? 'hls' : 'mp4',
|
|
34
|
+
headers: {}
|
|
35
|
+
};
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
return { allUrls };
|
|
39
|
+
}
|
|
40
|
+
} catch (err) {
|
|
41
|
+
return null;
|
|
42
|
+
}
|
|
43
|
+
}
|
package/src/sources/lookmovie.js
CHANGED
|
File without changes
|
package/src/sources/mapple.js
CHANGED
|
@@ -76,13 +76,29 @@ export async function getStream(args) {
|
|
|
76
76
|
const reqMatch = html.match(/window\.__REQUEST_TOKEN__\s*=\s*"([^"]+)"/);
|
|
77
77
|
if (!reqMatch) return null;
|
|
78
78
|
const requestToken = reqMatch[1];
|
|
79
|
-
const
|
|
79
|
+
const cookies = pageRes.headers.getSetCookie();
|
|
80
|
+
const cookieHeader = cookies.map(c => c.split(';')[0]).join('; ');
|
|
81
|
+
|
|
82
|
+
const initHeaders = { "Content-Type": "application/json", ...PROXY_HEADERS, Referer: pageUrl };
|
|
83
|
+
if (cookieHeader) initHeaders.Cookie = cookieHeader;
|
|
84
|
+
|
|
85
|
+
const initRes = await fetch(`${BASE_URL}/api/playback-init`, {
|
|
86
|
+
method: "POST",
|
|
87
|
+
headers: initHeaders,
|
|
88
|
+
body: JSON.stringify({ mediaId: Number(id), mediaType, requestToken }),
|
|
89
|
+
signal: AbortSignal.timeout(10000)
|
|
90
|
+
});
|
|
80
91
|
const initData = await initRes.json();
|
|
81
92
|
let streamToken = initData.token;
|
|
82
93
|
if (initData.requiresPow && initData.pow) {
|
|
83
94
|
const powRes = solvePoW(initData.pow.challenge, initData.pow.difficulty);
|
|
84
95
|
if (powRes.found) {
|
|
85
|
-
const solveRes = await fetch(`${BASE_URL}/api/playback-init`, {
|
|
96
|
+
const solveRes = await fetch(`${BASE_URL}/api/playback-init`, {
|
|
97
|
+
method: "POST",
|
|
98
|
+
headers: initHeaders,
|
|
99
|
+
body: JSON.stringify({ mediaId: Number(id), mediaType, requestToken, pow: { challengeId: initData.pow.challengeId, nonce: powRes.nonce } }),
|
|
100
|
+
signal: AbortSignal.timeout(10000)
|
|
101
|
+
});
|
|
86
102
|
streamToken = (await solveRes.json()).token;
|
|
87
103
|
}
|
|
88
104
|
}
|
|
@@ -96,8 +112,28 @@ export async function getStream(args) {
|
|
|
96
112
|
const allUrls = [];
|
|
97
113
|
for (const srv of targets) {
|
|
98
114
|
try {
|
|
99
|
-
const
|
|
100
|
-
|
|
115
|
+
const encryptRes = await fetch(`${BASE_URL}/api/encrypt`, {
|
|
116
|
+
method: "POST",
|
|
117
|
+
headers: initHeaders,
|
|
118
|
+
body: JSON.stringify({
|
|
119
|
+
data: {
|
|
120
|
+
mediaId: Number(id),
|
|
121
|
+
mediaType,
|
|
122
|
+
tv_slug: tvSlug,
|
|
123
|
+
source: srv.id,
|
|
124
|
+
apikey: "mptv_sk_a8f29c4e7b3d1f"
|
|
125
|
+
}
|
|
126
|
+
}),
|
|
127
|
+
signal: AbortSignal.timeout(8000)
|
|
128
|
+
});
|
|
129
|
+
const encryptData = await encryptRes.json();
|
|
130
|
+
if (!encryptData.encrypted) continue;
|
|
131
|
+
|
|
132
|
+
const streamUrl = `${BASE_URL}/api/stream-encrypted?data=${encodeURIComponent(encryptData.encrypted)}&requestToken=${encodeURIComponent(requestToken)}&token=${encodeURIComponent(streamToken)}`;
|
|
133
|
+
const streamRes = await fetch(streamUrl, {
|
|
134
|
+
headers: { ...PROXY_HEADERS, Referer: pageUrl, ...(cookieHeader ? { Cookie: cookieHeader } : {}) },
|
|
135
|
+
signal: AbortSignal.timeout(8000)
|
|
136
|
+
});
|
|
101
137
|
const streamData = await streamRes.json();
|
|
102
138
|
if (streamData.success && streamData.data?.stream_url) {
|
|
103
139
|
let fileUrl = streamData.data.stream_url;
|
package/src/sources/meowtv.js
CHANGED
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import { USER_AGENT, fetchJson, getTmdbInfo } from '../utils/helpers.js';
|
|
2
|
+
|
|
3
|
+
const BASE_URL = 'https://providers.peestream.in';
|
|
4
|
+
|
|
5
|
+
export async function getStream(args) {
|
|
6
|
+
const { id, s, e, server, tmdbApiKey } = args;
|
|
7
|
+
const isTv = s != null && e != null;
|
|
8
|
+
const type = isTv ? 'tv' : 'movie';
|
|
9
|
+
|
|
10
|
+
let title = args.title || '';
|
|
11
|
+
let releaseYear = args.year || '';
|
|
12
|
+
let imdbId = args.imdbId || '';
|
|
13
|
+
|
|
14
|
+
if (!title) {
|
|
15
|
+
try {
|
|
16
|
+
const info = await getTmdbInfo(id, type, s, tmdbApiKey);
|
|
17
|
+
if (info) {
|
|
18
|
+
title = info.title || '';
|
|
19
|
+
if (info.year) releaseYear = info.year;
|
|
20
|
+
if (info.imdbId) imdbId = info.imdbId;
|
|
21
|
+
}
|
|
22
|
+
} catch { }
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const queryParams = new URLSearchParams({
|
|
26
|
+
type,
|
|
27
|
+
tmdbId: String(id),
|
|
28
|
+
title: title || String(id)
|
|
29
|
+
});
|
|
30
|
+
if (releaseYear) queryParams.set('releaseYear', String(releaseYear));
|
|
31
|
+
if (imdbId) queryParams.set('imdbId', String(imdbId));
|
|
32
|
+
if (isTv) {
|
|
33
|
+
queryParams.set('season', String(s));
|
|
34
|
+
queryParams.set('episode', String(e));
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const allUrls = [];
|
|
38
|
+
|
|
39
|
+
try {
|
|
40
|
+
const scrapeUrl = `${BASE_URL}/scrape?${queryParams.toString()}`;
|
|
41
|
+
const res = await fetch(scrapeUrl, {
|
|
42
|
+
headers: {
|
|
43
|
+
'User-Agent': USER_AGENT,
|
|
44
|
+
'Accept': 'text/event-stream',
|
|
45
|
+
'Referer': `${BASE_URL}/`
|
|
46
|
+
},
|
|
47
|
+
signal: AbortSignal.timeout(15000)
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
if (res.ok) {
|
|
51
|
+
const text = await res.text();
|
|
52
|
+
const events = text.split('\n\n');
|
|
53
|
+
for (const ev of events) {
|
|
54
|
+
if (ev.includes('event: completed')) {
|
|
55
|
+
const dataMatch = ev.match(/data:\s*(.+)/);
|
|
56
|
+
if (dataMatch) {
|
|
57
|
+
try {
|
|
58
|
+
const parsed = JSON.parse(dataMatch[1].trim());
|
|
59
|
+
const stream = parsed.stream;
|
|
60
|
+
const streamUrl = stream?.playlist || stream?.url || stream?.file;
|
|
61
|
+
if (streamUrl) {
|
|
62
|
+
allUrls.push({
|
|
63
|
+
url: streamUrl,
|
|
64
|
+
server: `PeeStream - ${parsed.sourceId || 'Poseidon'}`,
|
|
65
|
+
quality: 'Auto',
|
|
66
|
+
type: stream.type === 'hls' || streamUrl.includes('.m3u8') ? 'hls' : 'mp4',
|
|
67
|
+
headers: stream.headers || {
|
|
68
|
+
'User-Agent': USER_AGENT
|
|
69
|
+
}
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
} catch { }
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
} catch { }
|
|
78
|
+
|
|
79
|
+
if (!allUrls.length) {
|
|
80
|
+
try {
|
|
81
|
+
const searchUrl = `${BASE_URL}/api/search?q=${encodeURIComponent(title || id)}&type=${type}&tmdbId=${id}${isTv ? `&season=${s}&episode=${e}` : ''}`;
|
|
82
|
+
const data = await fetchJson(searchUrl, {
|
|
83
|
+
headers: {
|
|
84
|
+
'User-Agent': USER_AGENT,
|
|
85
|
+
'Accept': 'application/json',
|
|
86
|
+
'Referer': `${BASE_URL}/`
|
|
87
|
+
},
|
|
88
|
+
signal: AbortSignal.timeout(10000)
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
if (data && Array.isArray(data.results)) {
|
|
92
|
+
for (const result of data.results) {
|
|
93
|
+
const pName = result.providerName || result.provider || 'PeeStream';
|
|
94
|
+
for (const stream of result.streams || []) {
|
|
95
|
+
if (!stream || !stream.url) continue;
|
|
96
|
+
allUrls.push({
|
|
97
|
+
url: stream.url,
|
|
98
|
+
server: `PeeStream - ${stream.name || pName}`,
|
|
99
|
+
quality: stream.quality || 'Auto',
|
|
100
|
+
type: stream.type === 'm3u8' || stream.url.includes('.m3u8') ? 'hls' : 'mp4',
|
|
101
|
+
headers: stream.headers || {
|
|
102
|
+
'User-Agent': USER_AGENT
|
|
103
|
+
}
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
} catch { }
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
if (server && server !== 'all') {
|
|
112
|
+
const clean = server.toLowerCase().replace('peestream - ', '');
|
|
113
|
+
const filtered = allUrls.filter(u => u.server.toLowerCase().includes(clean));
|
|
114
|
+
if (filtered.length) return { allUrls: filtered };
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
return allUrls.length ? { allUrls } : null;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
export async function getSources() {
|
|
121
|
+
return ['PeeStream - vaplayer'];
|
|
122
|
+
}
|
package/src/sources/pengu.js
CHANGED
|
File without changes
|
package/src/sources/purstream.js
CHANGED
|
File without changes
|
package/src/sources/vidapi.js
CHANGED
|
File without changes
|
package/src/sources/vidcore.js
CHANGED
|
File without changes
|
package/src/sources/videasy.js
CHANGED
|
@@ -1,28 +1,24 @@
|
|
|
1
1
|
import { getTmdbInfo, fetchJson, fetchText, USER_AGENT } from '../utils/helpers.js';
|
|
2
2
|
|
|
3
|
-
const
|
|
3
|
+
const SPEEDRACE_BASE = "https://api.speedracelight.com";
|
|
4
4
|
const HEADERS = { "Accept": "*/*", "Origin": "https://player.videasy.to", "Referer": "https://player.videasy.to/", "User-Agent": USER_AGENT };
|
|
5
5
|
const SERVERS = [
|
|
6
|
-
{ id: 'jett', name: 'Jett' },
|
|
7
6
|
{ id: 'cdn', name: 'Yoru' },
|
|
8
|
-
{ id: 'tejo', name: 'Tejo' },
|
|
9
|
-
{ id: 'neon2', name: 'Neon' },
|
|
10
|
-
{ id: 'ym', name: 'Sage' },
|
|
11
|
-
{ id: 'downloader2', name: 'Cypher' },
|
|
12
7
|
{ id: 'm4uhd', name: 'Breach' },
|
|
8
|
+
{ id: 'vsrc', name: 'Neon' },
|
|
13
9
|
{ id: 'hdmovie', name: 'Vyse' },
|
|
14
10
|
{ id: 'meine', name: 'Killjoy' },
|
|
15
11
|
{ id: 'lamovie', name: 'Omen' },
|
|
16
12
|
{ id: 'superflix', name: 'Raze' }
|
|
17
13
|
];
|
|
18
14
|
|
|
19
|
-
export async function getStream({ id, s, e, server }) {
|
|
15
|
+
export async function getStream({ id, s, e, server, tmdbApiKey }) {
|
|
20
16
|
try {
|
|
21
17
|
const isTv = s != null && e != null;
|
|
22
|
-
const info = await getTmdbInfo(id, isTv ? 'tv' : 'movie');
|
|
18
|
+
const info = await getTmdbInfo(tmdbApiKey, id, isTv ? 'tv' : 'movie');
|
|
23
19
|
if (!info?.titles?.length) return null;
|
|
24
20
|
|
|
25
|
-
const seedData = await fetchJson(`${
|
|
21
|
+
const seedData = await fetchJson(`${SPEEDRACE_BASE}/seed?mediaId=${id}`, { headers: HEADERS, signal: AbortSignal.timeout(5000) });
|
|
26
22
|
const seed = seedData?.seed;
|
|
27
23
|
if (!seed) return null;
|
|
28
24
|
|
|
@@ -36,9 +32,7 @@ export async function getStream({ id, s, e, server }) {
|
|
|
36
32
|
}
|
|
37
33
|
|
|
38
34
|
const settled = await Promise.allSettled(targets.map(async srv => {
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
let url = `${WINGS_BASE}/${srv.id}/sources-with-title?title=${encTitle}&mediaType=${isTv ? 'tv' : 'movie'}&year=${info.year || ''}&tmdbId=${id}&imdbId=${info.imdbId || "tt0000000"}&enc=2&seed=${seed}`;
|
|
35
|
+
let url = `${SPEEDRACE_BASE}/${srv.id}/sources-with-title?title=${encTitle}&mediaType=${isTv ? 'tv' : 'movie'}&year=${info.year || ''}&tmdbId=${id}&imdbId=${info.imdbId || "tt0000000"}&enc=2&seed=${seed}`;
|
|
42
36
|
if (isTv) url += `&episodeId=${e}&seasonId=${s}`;
|
|
43
37
|
|
|
44
38
|
const encText = await fetchText(url, { headers: HEADERS, signal: AbortSignal.timeout(10000) });
|
package/src/sources/vidfast.js
CHANGED
|
File without changes
|
package/src/sources/vidlink.js
CHANGED
|
File without changes
|
package/src/sources/vidnest.js
CHANGED
|
File without changes
|
package/src/sources/vidrift.js
CHANGED
|
File without changes
|
package/src/sources/vidrock.js
CHANGED
|
File without changes
|
package/src/sources/vidsync.js
CHANGED
|
File without changes
|
package/src/sources/vidup.js
CHANGED
|
File without changes
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import { USER_AGENT } from '../utils/helpers.js';
|
|
2
|
+
|
|
3
|
+
const BASE_URL = "https://vidvault.ru";
|
|
4
|
+
const HEADERS = {
|
|
5
|
+
"Referer": `${BASE_URL}/`,
|
|
6
|
+
"Origin": BASE_URL,
|
|
7
|
+
"User-Agent": USER_AGENT,
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
async function getToken() {
|
|
11
|
+
const res = await fetch(`${BASE_URL}/api/get-token`, {
|
|
12
|
+
method: "GET",
|
|
13
|
+
headers: HEADERS
|
|
14
|
+
});
|
|
15
|
+
const data = await res.json();
|
|
16
|
+
return data.t;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function getCaptionType(url) {
|
|
20
|
+
return url.toLowerCase().includes(".vtt") ? "vtt" : "srt";
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export async function getStream(args) {
|
|
24
|
+
const { id, s, e } = args;
|
|
25
|
+
const type = (s != null && e != null) ? "tv" : "movie";
|
|
26
|
+
|
|
27
|
+
try {
|
|
28
|
+
const token = await getToken();
|
|
29
|
+
|
|
30
|
+
const payload = { type, tmdbId: String(id) };
|
|
31
|
+
if (type === "tv") {
|
|
32
|
+
payload.season = String(s);
|
|
33
|
+
payload.episode = String(e);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const res = await fetch(`${BASE_URL}/api/download-proxy`, {
|
|
37
|
+
method: "POST",
|
|
38
|
+
headers: {
|
|
39
|
+
...HEADERS,
|
|
40
|
+
"Content-Type": "application/json",
|
|
41
|
+
"x-request-token": token
|
|
42
|
+
},
|
|
43
|
+
body: JSON.stringify(payload)
|
|
44
|
+
});
|
|
45
|
+
const data = await res.json();
|
|
46
|
+
|
|
47
|
+
const allUrls = [];
|
|
48
|
+
const subtitles = [];
|
|
49
|
+
|
|
50
|
+
const mp4 = data?.mp4Data;
|
|
51
|
+
if (mp4) {
|
|
52
|
+
const country = mp4.country || "";
|
|
53
|
+
const lanName = mp4.lanName || "";
|
|
54
|
+
const detailPath = mp4.detailPath || "";
|
|
55
|
+
|
|
56
|
+
const downloads = mp4.downloadInfo?.data?.downloads || [];
|
|
57
|
+
for (const d of downloads) {
|
|
58
|
+
if (!d?.url) continue;
|
|
59
|
+
allUrls.push({
|
|
60
|
+
quality: `${d.resolution}p ${detailPath} ${country} ${lanName}`.trim(),
|
|
61
|
+
url: d.url,
|
|
62
|
+
type: "mp4",
|
|
63
|
+
headers: HEADERS
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const srtCaptions = mp4.downloadInfo?.data?.captions || [];
|
|
68
|
+
for (const c of srtCaptions) {
|
|
69
|
+
if (!c?.url) continue;
|
|
70
|
+
subtitles.push({
|
|
71
|
+
label: c.lanName || c.lan || "Unknown",
|
|
72
|
+
type: getCaptionType(c.url),
|
|
73
|
+
language: c.lan || "",
|
|
74
|
+
url: c.url
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
const mkvFiles = data?.mkvData?.files || [];
|
|
80
|
+
for (const f of mkvFiles) {
|
|
81
|
+
if (!f?.url) continue;
|
|
82
|
+
allUrls.push({
|
|
83
|
+
quality: `MKV ${f.size || ""}`.trim(),
|
|
84
|
+
url: f.url,
|
|
85
|
+
type: "mp4",
|
|
86
|
+
headers: HEADERS
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const mkvV2 = data?.mkvV2Data;
|
|
91
|
+
if (mkvV2?.url) {
|
|
92
|
+
allUrls.push({
|
|
93
|
+
quality: `${mkvV2.quality || ""} ${mkvV2.language || ""} ${mkvV2.country || ""} (${mkvV2.size || ""})`.trim(),
|
|
94
|
+
url: mkvV2.url,
|
|
95
|
+
type: "mp4",
|
|
96
|
+
headers: HEADERS
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
const mkvV3 = data?.mkvV3Data;
|
|
101
|
+
if (mkvV3?.url) {
|
|
102
|
+
allUrls.push({
|
|
103
|
+
quality: `${mkvV3.quality || ""} ${mkvV3.language || ""} ${mkvV3.country || ""} (${mkvV3.size || ""})`.trim(),
|
|
104
|
+
url: mkvV3.url,
|
|
105
|
+
type: "mp4",
|
|
106
|
+
headers: HEADERS
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
return allUrls.length ? { allUrls, subtitles } : null;
|
|
111
|
+
} catch (err) {
|
|
112
|
+
return null;
|
|
113
|
+
}
|
|
114
|
+
}
|
package/src/sources/vidzee.js
CHANGED
|
@@ -1,70 +1,47 @@
|
|
|
1
|
-
|
|
1
|
+
// i can't fix this, i'll wait for someone else to fix this lol
|
|
2
|
+
|
|
3
|
+
import crypto from 'node:crypto';
|
|
2
4
|
import { fetchJson, USER_AGENT } from '../utils/helpers.js';
|
|
3
5
|
|
|
4
|
-
const PLAYER_URL = 'https://player.vidzee.wtf';
|
|
5
6
|
const CORE_URL = 'https://core.vidzee.wtf';
|
|
7
|
+
const PLAYER_URL = 'https://player.vidzee.wtf';
|
|
8
|
+
const KEY = 'c4a8f1d7e2b9a6c3d0f5e8a1b7c4d9e2';
|
|
6
9
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
const now = Date.now();
|
|
11
|
-
if (cachedKey && now - cachedKeyTs < 300000) return cachedKey;
|
|
12
|
-
|
|
13
|
-
const res = await fetch(`${CORE_URL}/api-key`, {
|
|
14
|
-
headers,
|
|
15
|
-
signal: AbortSignal.timeout(7000)
|
|
16
|
-
});
|
|
17
|
-
if (!res.ok) throw new Error(`API key fetch failed: ${res.status}`);
|
|
18
|
-
|
|
19
|
-
const e = await res.text();
|
|
20
|
-
const t = Buffer.from(e.replace(/\s+/g, ''), 'base64');
|
|
21
|
-
|
|
22
|
-
const s = new Uint8Array(t.length - 28 + 16);
|
|
23
|
-
s.set(t.subarray(28), 0);
|
|
24
|
-
s.set(t.subarray(12, 28), t.length - 28);
|
|
25
|
-
|
|
26
|
-
const keyMat = await webcrypto.subtle.digest('SHA-256', new TextEncoder().encode('c4a8f1d7e2b9a6c3d0f5e8a1b7c4d9e2'));
|
|
27
|
-
const key = await webcrypto.subtle.importKey('raw', keyMat, { name: 'AES-GCM' }, false, ['decrypt']);
|
|
10
|
+
function decryptAes(encryptedBase64) {
|
|
11
|
+
const keyBuf = Buffer.from(KEY, 'utf8');
|
|
12
|
+
const ciphertext = Buffer.from(encryptedBase64, 'base64');
|
|
28
13
|
|
|
29
14
|
try {
|
|
30
|
-
const
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
cachedKeyTs = now;
|
|
38
|
-
return cachedKey;
|
|
39
|
-
} catch (err) {
|
|
40
|
-
throw new Error('Failed to decrypt Vidzee master key');
|
|
41
|
-
}
|
|
42
|
-
}
|
|
15
|
+
const decipher = crypto.createDecipheriv('aes-256-ecb', keyBuf, null);
|
|
16
|
+
decipher.setAutoPadding(true);
|
|
17
|
+
let dec = decipher.update(ciphertext);
|
|
18
|
+
dec = Buffer.concat([dec, decipher.final()]);
|
|
19
|
+
const text = dec.toString('utf8');
|
|
20
|
+
if (text.startsWith('http') || text.includes('{')) return text;
|
|
21
|
+
} catch { }
|
|
43
22
|
|
|
44
|
-
function decrypt(enc, key) {
|
|
45
23
|
try {
|
|
46
|
-
const
|
|
47
|
-
const
|
|
48
|
-
|
|
24
|
+
const iv = Buffer.alloc(16, 0);
|
|
25
|
+
const decipher = crypto.createDecipheriv('aes-256-cbc', keyBuf, iv);
|
|
26
|
+
decipher.setAutoPadding(true);
|
|
27
|
+
let dec = decipher.update(ciphertext);
|
|
28
|
+
dec = Buffer.concat([dec, decipher.final()]);
|
|
29
|
+
const text = dec.toString('utf8');
|
|
30
|
+
if (text.startsWith('http') || text.includes('{')) return text;
|
|
31
|
+
} catch { }
|
|
49
32
|
|
|
50
|
-
|
|
51
|
-
const
|
|
52
|
-
const
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
);
|
|
63
|
-
|
|
64
|
-
return decrypted.toString(CryptoJS.enc.Utf8) || '';
|
|
65
|
-
} catch (err) {
|
|
66
|
-
return '';
|
|
67
|
-
}
|
|
33
|
+
try {
|
|
34
|
+
const iv = ciphertext.subarray(0, 16);
|
|
35
|
+
const data = ciphertext.subarray(16);
|
|
36
|
+
const decipher = crypto.createDecipheriv('aes-256-cbc', keyBuf, iv);
|
|
37
|
+
decipher.setAutoPadding(true);
|
|
38
|
+
let dec = decipher.update(data);
|
|
39
|
+
dec = Buffer.concat([dec, decipher.final()]);
|
|
40
|
+
const text = dec.toString('utf8');
|
|
41
|
+
if (text.startsWith('http') || text.includes('{')) return text;
|
|
42
|
+
} catch { }
|
|
43
|
+
|
|
44
|
+
return null;
|
|
68
45
|
}
|
|
69
46
|
|
|
70
47
|
export async function getStream({ id, s, e, clientIP }) {
|
|
@@ -77,44 +54,53 @@ export async function getStream({ id, s, e, clientIP }) {
|
|
|
77
54
|
};
|
|
78
55
|
|
|
79
56
|
try {
|
|
80
|
-
const
|
|
81
|
-
|
|
82
|
-
const
|
|
57
|
+
const isTv = s != null && e != null;
|
|
58
|
+
const servers = ['tik', 'ipcloud', 'v6:Hindi'];
|
|
59
|
+
const results = [];
|
|
83
60
|
|
|
84
|
-
for (const sr of
|
|
61
|
+
for (const sr of servers) {
|
|
85
62
|
try {
|
|
86
|
-
const url =
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
63
|
+
const url = isTv
|
|
64
|
+
? `${CORE_URL}/streams/tv/${id}/${s}/${e}?s=${encodeURIComponent(sr)}&e=${e}`
|
|
65
|
+
: `${CORE_URL}/streams/movie/${id}?s=${encodeURIComponent(sr)}&e=1`;
|
|
66
|
+
|
|
67
|
+
const data = await fetchJson(url, { headers, signal: AbortSignal.timeout(6000) });
|
|
68
|
+
|
|
69
|
+
if (data && data.c) {
|
|
70
|
+
const decryptedText = decryptAes(data.c);
|
|
71
|
+
if (decryptedText) {
|
|
72
|
+
let streamUrl = decryptedText;
|
|
73
|
+
let extraHeaders = {};
|
|
74
|
+
|
|
75
|
+
if (decryptedText.startsWith('{')) {
|
|
76
|
+
try {
|
|
77
|
+
const parsed = JSON.parse(decryptedText);
|
|
78
|
+
streamUrl = parsed.url || parsed.link || parsed.file;
|
|
79
|
+
if (parsed.headers) extraHeaders = parsed.headers;
|
|
80
|
+
} catch { }
|
|
81
|
+
}
|
|
95
82
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
};
|
|
83
|
+
if (streamUrl && streamUrl.startsWith('http')) {
|
|
84
|
+
results.push({
|
|
85
|
+
url: streamUrl,
|
|
86
|
+
server: `VidZee - ${sr}`,
|
|
87
|
+
skipHlsCheck: true,
|
|
88
|
+
headers: {
|
|
89
|
+
'User-Agent': USER_AGENT,
|
|
90
|
+
'Referer': PLAYER_URL,
|
|
91
|
+
'Origin': PLAYER_URL,
|
|
92
|
+
...extraHeaders
|
|
93
|
+
}
|
|
94
|
+
});
|
|
109
95
|
}
|
|
110
96
|
}
|
|
111
97
|
}
|
|
112
98
|
} catch (err) {
|
|
113
|
-
continue;
|
|
114
99
|
}
|
|
115
100
|
}
|
|
116
|
-
|
|
117
|
-
|
|
101
|
+
|
|
102
|
+
return results.length ? { allUrls: results } : null;
|
|
103
|
+
} catch {
|
|
118
104
|
return null;
|
|
119
105
|
}
|
|
120
106
|
}
|