@vyla-entertainment/sdk 1.10.0 → 1.12.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 +2 -2
- package/src/config.js +212 -98
- package/src/downloads/downloads.js +51 -10
- package/src/sdk.d.ts +0 -1
- package/src/sdk.js +1 -1
- package/src/sources/111477.js +101 -0
- package/src/sources/4khdhub.js +324 -0
- package/src/sources/bcine.js +202 -0
- package/src/sources/cinesrc.js +212 -0
- package/src/sources/cinesu.js +148 -0
- package/src/sources/downloadeverything.js +338 -0
- package/src/sources/meowtv.js +42 -3
- package/src/sources/movienight.js +117 -0
- package/src/sources/movy.js +282 -0
- package/src/sources/pstream.js +68 -0
- package/src/sources/purstream.js +2 -2
- package/src/sources/streamaggregator.js +127 -0
- package/src/sources/vadapav.js +85 -0
- package/src/sources/vidvault.js +131 -57
- package/src/sources/vuflix.js +249 -0
- package/src/sources/xdownloader.js +129 -0
- package/src/subtitles.js +35 -40
- package/src/downloads/sources/trendimovies.js +0 -63
- package/src/sources/anipm.js +0 -70
- package/src/sources/mapple.js +0 -151
- package/src/sources/megasource.js +0 -47
- package/src/sources/nova.js +0 -46
- package/src/sources/videasy.js +0 -94
- package/src/sources/vidrift.js +0 -95
- package/src/sources/vidsync.js +0 -38
- package/src/sources/xpass.js +0 -40
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
const BASE_URL = 'https://www.films365.org';
|
|
2
|
+
const HEADERS = {
|
|
3
|
+
Authorization: 'Bearer 79a02956be35835728a044b11e2ae793149d45fb2c89cb6d029ec01aac19bfdb',
|
|
4
|
+
'Content-Type': 'application/json',
|
|
5
|
+
'User-Agent': 'MovieDownloader/1.0',
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
async function resolveTitle(sdk, id, isTv) {
|
|
9
|
+
const apiKey = sdk?.tmdbApiKey || process.env.TMDB_API_KEY;
|
|
10
|
+
if (!apiKey) return null;
|
|
11
|
+
const type = isTv ? 'tv' : 'movie';
|
|
12
|
+
try {
|
|
13
|
+
const res = await fetch(`https://api.themoviedb.org/3/${type}/${id}?api_key=${apiKey}`, {
|
|
14
|
+
signal: AbortSignal.timeout(6000),
|
|
15
|
+
});
|
|
16
|
+
if (!res.ok) return null;
|
|
17
|
+
const data = await res.json();
|
|
18
|
+
return isTv ? data.name : data.title;
|
|
19
|
+
} catch {
|
|
20
|
+
return null;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export async function getStream({ id, s, e, sdk }) {
|
|
25
|
+
const isTv = Boolean(s && e);
|
|
26
|
+
const targetType = isTv ? 'tv' : 'movie';
|
|
27
|
+
|
|
28
|
+
const title = await resolveTitle(sdk, id, isTv);
|
|
29
|
+
const query = title || String(id);
|
|
30
|
+
|
|
31
|
+
const searchRes = await fetch(`${BASE_URL}/api/mobile/search`, {
|
|
32
|
+
method: 'POST',
|
|
33
|
+
headers: HEADERS,
|
|
34
|
+
body: JSON.stringify({ query }),
|
|
35
|
+
signal: AbortSignal.timeout(8000),
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
if (!searchRes.ok) return null;
|
|
39
|
+
const searchJson = await searchRes.json();
|
|
40
|
+
const resultsObj = searchJson?.results;
|
|
41
|
+
if (!resultsObj) return null;
|
|
42
|
+
|
|
43
|
+
const items = resultsObj.all || (targetType === 'tv' ? resultsObj.tvs : resultsObj.movies) || [];
|
|
44
|
+
if (!Array.isArray(items) || items.length === 0) return null;
|
|
45
|
+
|
|
46
|
+
const cleanSearchTitle = query.toLowerCase().replace(/[^a-z0-9]/g, '');
|
|
47
|
+
let matchedItem = null;
|
|
48
|
+
|
|
49
|
+
for (const item of items) {
|
|
50
|
+
const itemType = String(item?.type || '').toLowerCase();
|
|
51
|
+
if (itemType && itemType !== targetType) continue;
|
|
52
|
+
|
|
53
|
+
const itemTitle = String(item?.title || '');
|
|
54
|
+
const cleanItemTitle = itemTitle.toLowerCase().replace(/[^a-z0-9]/g, '');
|
|
55
|
+
|
|
56
|
+
if (cleanItemTitle === cleanSearchTitle || cleanItemTitle.includes(cleanSearchTitle)) {
|
|
57
|
+
matchedItem = item;
|
|
58
|
+
break;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
if (!matchedItem) {
|
|
63
|
+
matchedItem = items.find(i => String(i?.type || '').toLowerCase() === targetType) || items[0];
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const itemId = matchedItem?.id || matchedItem?.tmdbId;
|
|
67
|
+
if (!itemId) return null;
|
|
68
|
+
|
|
69
|
+
const detailsRes = await fetch(`${BASE_URL}/api/mobile/details?id=${itemId}&type=${targetType}`, {
|
|
70
|
+
headers: HEADERS,
|
|
71
|
+
signal: AbortSignal.timeout(8000),
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
if (!detailsRes.ok) return null;
|
|
75
|
+
const detailsJson = await detailsRes.json();
|
|
76
|
+
const data = detailsJson?.data;
|
|
77
|
+
if (!data) return null;
|
|
78
|
+
|
|
79
|
+
const spoken = Array.isArray(data.spokenLanguages)
|
|
80
|
+
? data.spokenLanguages.map(l => String(l).trim()).filter(Boolean)
|
|
81
|
+
: [];
|
|
82
|
+
const langSuffix = spoken.length > 0 ? ` · ${spoken.join(', ')}` : '';
|
|
83
|
+
|
|
84
|
+
const allUrls = [];
|
|
85
|
+
|
|
86
|
+
if (!isTv) {
|
|
87
|
+
const streamUrl = data.downloadUrl || data.videoUrl;
|
|
88
|
+
if (streamUrl && typeof streamUrl === 'string') {
|
|
89
|
+
allUrls.push({
|
|
90
|
+
url: streamUrl.trim(),
|
|
91
|
+
label: `X-Downloader${langSuffix}`,
|
|
92
|
+
headers: {
|
|
93
|
+
'User-Agent': HEADERS['User-Agent'],
|
|
94
|
+
},
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
} else {
|
|
98
|
+
const seasonNum = Number(s);
|
|
99
|
+
const episodeNum = Number(e);
|
|
100
|
+
const seasons = Array.isArray(data.seasons) ? data.seasons : [];
|
|
101
|
+
const targetSeason = seasons.find(sn => Number(sn.seasonNumber) === seasonNum);
|
|
102
|
+
|
|
103
|
+
if (targetSeason) {
|
|
104
|
+
const episodes = Array.isArray(targetSeason.episodes) ? targetSeason.episodes : [];
|
|
105
|
+
const targetEp = episodes.find(ep => Number(ep.episodeNumber) === episodeNum);
|
|
106
|
+
|
|
107
|
+
if (targetEp) {
|
|
108
|
+
const streamUrl = targetEp.downloadUrl || targetEp.videoUrl;
|
|
109
|
+
if (streamUrl && typeof streamUrl === 'string') {
|
|
110
|
+
allUrls.push({
|
|
111
|
+
url: streamUrl.trim(),
|
|
112
|
+
label: `X-Downloader (S${seasonNum}E${episodeNum})${langSuffix}`,
|
|
113
|
+
headers: {
|
|
114
|
+
'User-Agent': HEADERS['User-Agent'],
|
|
115
|
+
},
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
if (allUrls.length === 0) return null;
|
|
123
|
+
|
|
124
|
+
return {
|
|
125
|
+
url: allUrls[0].url,
|
|
126
|
+
allUrls,
|
|
127
|
+
headers: allUrls[0].headers,
|
|
128
|
+
};
|
|
129
|
+
}
|
package/src/subtitles.js
CHANGED
|
@@ -3,36 +3,24 @@ import { getUA } from './utils/helpers.js';
|
|
|
3
3
|
const SUBTITLE_BASES = [
|
|
4
4
|
'https://sub.vdrk.site/v1',
|
|
5
5
|
'https://sub.vdrk.site/v2',
|
|
6
|
-
'https://
|
|
6
|
+
'https://opensubtitles-v3.strem.io'
|
|
7
7
|
];
|
|
8
|
-
const VYLIAN_MESSAGES = [
|
|
9
|
-
"Thanks for using Vyla!"
|
|
10
|
-
];
|
|
11
|
-
|
|
12
|
-
function generateVylianVtt() {
|
|
13
|
-
let vttContent = 'WEBVTT\n\n';
|
|
14
|
-
|
|
15
|
-
let currentTime = 1;
|
|
16
|
-
while (currentTime <= 7200) {
|
|
17
|
-
const duration = Math.floor(Math.random() * 4) + 1;
|
|
18
|
-
const message = VYLIAN_MESSAGES[Math.floor(Math.random() * VYLIAN_MESSAGES.length)];
|
|
19
|
-
|
|
20
|
-
const startTime = formatVttTime(currentTime);
|
|
21
|
-
const endTime = formatVttTime(currentTime + duration);
|
|
22
8
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
9
|
+
async function resolveImdbId(id, isTv) {
|
|
10
|
+
if (typeof id === 'string' && id.startsWith('tt')) return id;
|
|
11
|
+
const apiKey = process.env.TMDB_API_KEY;
|
|
12
|
+
if (!apiKey) return `tt${id}`;
|
|
13
|
+
const type = isTv ? 'tv' : 'movie';
|
|
14
|
+
try {
|
|
15
|
+
const res = await fetch(`https://api.themoviedb.org/3/${type}/${id}?api_key=${apiKey}&append_to_response=external_ids`, {
|
|
16
|
+
signal: AbortSignal.timeout(5000),
|
|
17
|
+
});
|
|
18
|
+
if (!res.ok) return `tt${id}`;
|
|
19
|
+
const data = await res.json();
|
|
20
|
+
return data.imdb_id || data.external_ids?.imdb_id || `tt${id}`;
|
|
21
|
+
} catch {
|
|
22
|
+
return `tt${id}`;
|
|
26
23
|
}
|
|
27
|
-
|
|
28
|
-
return vttContent;
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
function formatVttTime(seconds) {
|
|
32
|
-
const hours = Math.floor(seconds / 3600);
|
|
33
|
-
const minutes = Math.floor((seconds % 3600) / 60);
|
|
34
|
-
const secs = seconds % 60;
|
|
35
|
-
return `${String(hours).padStart(2, '0')}:${String(minutes).padStart(2, '0')}:${String(secs).padStart(2, '0')}.000`;
|
|
36
24
|
}
|
|
37
25
|
|
|
38
26
|
export async function fetchSubtitles(paths = []) {
|
|
@@ -42,7 +30,7 @@ export async function fetchSubtitles(paths = []) {
|
|
|
42
30
|
try {
|
|
43
31
|
const res = await fetch(`${base}${path}`, {
|
|
44
32
|
headers: { 'User-Agent': getUA() },
|
|
45
|
-
signal: AbortSignal.timeout(
|
|
33
|
+
signal: AbortSignal.timeout(6000),
|
|
46
34
|
});
|
|
47
35
|
|
|
48
36
|
if (!res.ok) {
|
|
@@ -63,6 +51,20 @@ export async function fetchSubtitles(paths = []) {
|
|
|
63
51
|
: [];
|
|
64
52
|
}
|
|
65
53
|
|
|
54
|
+
if (base.includes('opensubtitles-v3.strem.io')) {
|
|
55
|
+
if (!Array.isArray(data?.subtitles)) return [];
|
|
56
|
+
return data.subtitles.map(sub => {
|
|
57
|
+
if (!sub?.url) return null;
|
|
58
|
+
const isSrt = sub.url.toLowerCase().endsWith('.srt');
|
|
59
|
+
return {
|
|
60
|
+
label: sub.lang ? sub.lang.toUpperCase() : 'OpenSubtitles',
|
|
61
|
+
file: sub.url,
|
|
62
|
+
type: isSrt ? 'srt' : 'vtt',
|
|
63
|
+
source: 'opensubtitles'
|
|
64
|
+
};
|
|
65
|
+
}).filter(Boolean);
|
|
66
|
+
}
|
|
67
|
+
|
|
66
68
|
if (base.includes('fed-subs.pstream.mov')) {
|
|
67
69
|
if (!data?.subtitles || typeof data.subtitles !== 'object') return [];
|
|
68
70
|
|
|
@@ -93,34 +95,27 @@ export async function fetchSubtitles(paths = []) {
|
|
|
93
95
|
})
|
|
94
96
|
);
|
|
95
97
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
val.push({
|
|
99
|
-
label: 'Vylian',
|
|
100
|
-
file: 'data:text/vtt;base64,' + Buffer.from(generateVylianVtt()).toString('base64'),
|
|
101
|
-
type: 'vtt',
|
|
102
|
-
source: 'easter-egg'
|
|
103
|
-
});
|
|
104
|
-
|
|
105
|
-
return val;
|
|
98
|
+
return results.flat();
|
|
106
99
|
} catch {
|
|
107
100
|
return [];
|
|
108
101
|
}
|
|
109
102
|
}
|
|
110
103
|
|
|
111
104
|
export async function getSubPathsMovie(id) {
|
|
105
|
+
const imdbId = await resolveImdbId(id, false);
|
|
112
106
|
return [
|
|
113
107
|
{ base: SUBTITLE_BASES[0], path: `/movie/${id}` },
|
|
114
108
|
{ base: SUBTITLE_BASES[1], path: `/movie/${id}` },
|
|
115
|
-
{ base: SUBTITLE_BASES[2], path: `/movie
|
|
109
|
+
{ base: SUBTITLE_BASES[2], path: `/subtitles/movie/${imdbId}.json` }
|
|
116
110
|
];
|
|
117
111
|
}
|
|
118
112
|
|
|
119
113
|
export async function getSubPathsTv(id, season, episode) {
|
|
114
|
+
const imdbId = await resolveImdbId(id, true);
|
|
120
115
|
return [
|
|
121
116
|
{ base: SUBTITLE_BASES[0], path: `/tv/${id}/${season}/${episode}` },
|
|
122
117
|
{ base: SUBTITLE_BASES[1], path: `/tv/${id}/${season}/${episode}` },
|
|
123
|
-
{ base: SUBTITLE_BASES[2], path: `/
|
|
118
|
+
{ base: SUBTITLE_BASES[2], path: `/subtitles/series/${imdbId}:${season}:${episode}.json` }
|
|
124
119
|
];
|
|
125
120
|
}
|
|
126
121
|
|
|
@@ -1,63 +0,0 @@
|
|
|
1
|
-
const BASE = "https://trendimovies.com";
|
|
2
|
-
|
|
3
|
-
const HEADERS = {
|
|
4
|
-
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36",
|
|
5
|
-
Accept: "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
|
|
6
|
-
"Accept-Language": "en-US,en;q=0.9",
|
|
7
|
-
"Referer": "https://trendimovies.com/",
|
|
8
|
-
};
|
|
9
|
-
|
|
10
|
-
async function fetchPage(path) {
|
|
11
|
-
const res = await fetch(`${BASE}${path}`, { headers: HEADERS });
|
|
12
|
-
if (!res.ok) throw new Error(`trendimovies ${res.status}`);
|
|
13
|
-
return res.text();
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
function parseLinks(html) {
|
|
17
|
-
const m = html.match(/DownloadSection[^>]*props="([^"]+)"/s) ||
|
|
18
|
-
html.match(/props="([^"]+)"[^>]*ssr[^>]*client="load"[^>]*opts="[^"]*DownloadSection/s);
|
|
19
|
-
if (!m) {
|
|
20
|
-
const alt = html.match(/"DownloadSection"/);
|
|
21
|
-
if (!alt) return [];
|
|
22
|
-
const propsMatch = html.match(/props="({[^"]*DownloadSection[^"]*})"/s);
|
|
23
|
-
if (!propsMatch) return [];
|
|
24
|
-
return extractLinks(propsMatch[1].replace(/"/g, '"').replace(/'/g, "'"));
|
|
25
|
-
}
|
|
26
|
-
return extractLinks(m[1].replace(/"/g, '"').replace(/'/g, "'"));
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
function extractLinks(propsJson) {
|
|
30
|
-
try {
|
|
31
|
-
const parsed = JSON.parse(propsJson);
|
|
32
|
-
const rawLinks = parsed?.links?.[1];
|
|
33
|
-
if (!Array.isArray(rawLinks)) return [];
|
|
34
|
-
return rawLinks
|
|
35
|
-
.map(item => item?.[1])
|
|
36
|
-
.filter(l => l && typeof l === "object")
|
|
37
|
-
.map(l => ({
|
|
38
|
-
url: unwrap(l.url),
|
|
39
|
-
quality: unwrap(l.quality) || "HD",
|
|
40
|
-
size: unwrap(l.file_size) || null,
|
|
41
|
-
type: "mkv",
|
|
42
|
-
active: unwrap(l.is_active) !== false,
|
|
43
|
-
}))
|
|
44
|
-
.filter(l => l.url && l.active);
|
|
45
|
-
} catch {
|
|
46
|
-
return [];
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
function unwrap(v) {
|
|
51
|
-
if (Array.isArray(v) && v.length === 2 && (v[0] === 0 || v[0] === 1)) return v[1];
|
|
52
|
-
return v;
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
export async function getDownloads(tmdbId) {
|
|
56
|
-
const html = await fetchPage(`/movie/${tmdbId}`);
|
|
57
|
-
return parseLinks(html);
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
export async function getDownloadsTv(tmdbId, season, episode) {
|
|
61
|
-
const html = await fetchPage(`/tv/${tmdbId}/season/${season}/episode/${episode}`);
|
|
62
|
-
return parseLinks(html);
|
|
63
|
-
}
|
package/src/sources/anipm.js
DELETED
|
@@ -1,70 +0,0 @@
|
|
|
1
|
-
import { getTmdbInfo, fetchJson } from '../utils/helpers.js';
|
|
2
|
-
|
|
3
|
-
const BASE_URL = "https://ani.pm";
|
|
4
|
-
|
|
5
|
-
function cleanTitle(t) { return t ? t.toLowerCase().replace(/[^a-z0-9]/g, '') : ''; }
|
|
6
|
-
|
|
7
|
-
async function searchCatalog(query) {
|
|
8
|
-
try {
|
|
9
|
-
const data = await fetchJson(`${BASE_URL}/api/anime/catalog?q=${encodeURIComponent(query)}&page=1`, { signal: AbortSignal.timeout(6000) });
|
|
10
|
-
return Array.isArray(data?.items) ? data.items : [];
|
|
11
|
-
} catch { return []; }
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
async function resolveEntry(titles, year) {
|
|
15
|
-
const orderedTitles = titles.length > 1 ? [...titles].reverse() : titles;
|
|
16
|
-
let best = null, bestScore = -1;
|
|
17
|
-
for (const title of orderedTitles) {
|
|
18
|
-
const items = await searchCatalog(title);
|
|
19
|
-
const targetClean = cleanTitle(title);
|
|
20
|
-
for (const item of items) {
|
|
21
|
-
const itemTitles = [item.title, item.native].filter(Boolean).map(cleanTitle);
|
|
22
|
-
let score = 0;
|
|
23
|
-
if (itemTitles.some(t => t === targetClean)) score += 5;
|
|
24
|
-
else if (itemTitles.some(t => t.includes(targetClean) || targetClean.includes(t))) score += 3;
|
|
25
|
-
else continue;
|
|
26
|
-
if (item.type === 'TV') score += 4;
|
|
27
|
-
else if (item.type === 'Movie' || item.type === 'Special' || item.type === 'ONA') score -= 2;
|
|
28
|
-
if (year && item.year) {
|
|
29
|
-
const diff = Math.abs(item.year - year);
|
|
30
|
-
if (diff === 0) score += 3;
|
|
31
|
-
else if (diff === 1) score += 1;
|
|
32
|
-
else if (diff > 2) score -= 3;
|
|
33
|
-
}
|
|
34
|
-
if (score > bestScore) { bestScore = score; best = item; }
|
|
35
|
-
}
|
|
36
|
-
if (bestScore >= 9) break;
|
|
37
|
-
}
|
|
38
|
-
return best;
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
export async function getStream({ id, s, e, audio }) {
|
|
42
|
-
try {
|
|
43
|
-
const info = await getTmdbInfo(id, s ? 'tv' : 'movie', s);
|
|
44
|
-
if (!info || !info.isAnime) return null;
|
|
45
|
-
const entry = await resolveEntry(info.titles, info.year);
|
|
46
|
-
if (!entry) return null;
|
|
47
|
-
const episodeNum = e || 1;
|
|
48
|
-
const params = new URLSearchParams({ title: entry.title || '', ep: String(episodeNum) });
|
|
49
|
-
if (entry.year) params.set('year', entry.year);
|
|
50
|
-
if (entry.anilistId) params.set('anilistId', entry.anilistId);
|
|
51
|
-
if (entry.malId) params.set('malId', entry.malId);
|
|
52
|
-
const servers = await fetchJson(`${BASE_URL}/api/anime/src/servers?${params}`, { signal: AbortSignal.timeout(10000) });
|
|
53
|
-
if (!servers) return null;
|
|
54
|
-
const audiosToTry = audio === "all" ? ["sub", "dub"] : (audio === "dub" ? ["dub", "sub"] : ["sub", "dub"]);
|
|
55
|
-
for (const aud of audiosToTry) {
|
|
56
|
-
const list = servers[aud];
|
|
57
|
-
if (!Array.isArray(list) || !list.length) continue;
|
|
58
|
-
const allUrls = list.filter(srv => (srv.kind === "hls" || srv.kind === "file") && srv.url)
|
|
59
|
-
.sort((a, b) => (b.priority || 0) - (a.priority || 0))
|
|
60
|
-
.map(srv => ({ url: srv.url.startsWith('/') ? `${BASE_URL}${srv.url}` : srv.url, type: srv.kind === "hls" ? "hls" : "mp4", audio: aud, server: srv.provider ? `AniPM-${srv.provider}` : "AniPM", headers: { Referer: `${BASE_URL}/`, Origin: BASE_URL }, skipProxy: false }));
|
|
61
|
-
if (allUrls.length) return { allUrls };
|
|
62
|
-
}
|
|
63
|
-
return null;
|
|
64
|
-
} catch { return null; }
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
export async function getSources(args) {
|
|
68
|
-
const stream = await getStream(args);
|
|
69
|
-
return stream?.allUrls ? [...new Set(stream.allUrls.map(u => u.server))] : [];
|
|
70
|
-
}
|
package/src/sources/mapple.js
DELETED
|
@@ -1,151 +0,0 @@
|
|
|
1
|
-
import { USER_AGENT } from '../utils/helpers.js';
|
|
2
|
-
|
|
3
|
-
const BASE_URL = "https://mapple.club";
|
|
4
|
-
const PROXY_HEADERS = { Referer: `${BASE_URL}/`, Origin: BASE_URL, "User-Agent": USER_AGENT };
|
|
5
|
-
|
|
6
|
-
const K = new Uint32Array([0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5, 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967, 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3, 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2]);
|
|
7
|
-
const W_BUF = new Uint32Array(64);
|
|
8
|
-
|
|
9
|
-
function sha256Into(buf, total, out) {
|
|
10
|
-
let h0 = 0x6a09e667, h1 = 0xbb67ae85, h2 = 0x3c6ef372, h3 = 0xa54ff53a, h4 = 0x510e527f, h5 = 0x9b05688c, h6 = 0x1f83d9ab, h7 = 0x5be0cd19;
|
|
11
|
-
const dv = new DataView(buf.buffer, buf.byteOffset, total);
|
|
12
|
-
for (let off = 0; off < total; off += 64) {
|
|
13
|
-
for (let i = 0; i < 16; i++) W_BUF[i] = dv.getUint32(off + i * 4, false);
|
|
14
|
-
for (let i = 16; i < 64; i++) {
|
|
15
|
-
const a = W_BUF[i - 15], b = W_BUF[i - 2];
|
|
16
|
-
const s0 = ((a >>> 7) | (a << 25)) ^ ((a >>> 18) | (a << 14)) ^ (a >>> 3);
|
|
17
|
-
const s1 = ((b >>> 17) | (b << 15)) ^ ((b >>> 19) | (b << 13)) ^ (b >>> 10);
|
|
18
|
-
W_BUF[i] = (W_BUF[i - 16] + s0 + W_BUF[i - 7] + s1) | 0;
|
|
19
|
-
}
|
|
20
|
-
let a = h0, b = h1, c = h2, d = h3, e = h4, f = h5, g = h6, h = h7;
|
|
21
|
-
for (let i = 0; i < 64; i++) {
|
|
22
|
-
const S1 = ((e >>> 6) | (e << 26)) ^ ((e >>> 11) | (e << 21)) ^ ((e >>> 25) | (e << 7));
|
|
23
|
-
const ch = (e & f) ^ (~e & g);
|
|
24
|
-
const t1 = (h + S1 + ch + K[i] + W_BUF[i]) | 0;
|
|
25
|
-
const S0 = ((a >>> 2) | (a << 30)) ^ ((a >>> 13) | (a << 19)) ^ ((a >>> 22) | (a << 10));
|
|
26
|
-
const maj = (a & b) ^ (a & c) ^ (b & c);
|
|
27
|
-
const t2 = (S0 + maj) | 0;
|
|
28
|
-
h = g; g = f; f = e; e = (d + t1) | 0; d = c; c = b; b = a; a = (t1 + t2) | 0;
|
|
29
|
-
}
|
|
30
|
-
h0 = (h0 + a) | 0; h1 = (h1 + b) | 0; h2 = (h2 + c) | 0; h3 = (h3 + d) | 0; h4 = (h4 + e) | 0; h5 = (h5 + f) | 0; h6 = (h6 + g) | 0; h7 = (h7 + h) | 0;
|
|
31
|
-
}
|
|
32
|
-
const odv = new DataView(out.buffer, out.byteOffset, 32);
|
|
33
|
-
odv.setUint32(0, h0 >>> 0, false); odv.setUint32(4, h1 >>> 0, false); odv.setUint32(8, h2 >>> 0, false); odv.setUint32(12, h3 >>> 0, false); odv.setUint32(16, h4 >>> 0, false); odv.setUint32(20, h5 >>> 0, false); odv.setUint32(24, h6 >>> 0, false); odv.setUint32(28, h7 >>> 0, false);
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
function solvePoW(challenge, difficulty) {
|
|
37
|
-
const encoder = new TextEncoder();
|
|
38
|
-
const cBuf = encoder.encode(challenge);
|
|
39
|
-
const maxLen = cBuf.length + 16;
|
|
40
|
-
const total = (maxLen + 1 + 8 + 63) & ~63;
|
|
41
|
-
const buf = new Uint8Array(total);
|
|
42
|
-
buf.set(cBuf);
|
|
43
|
-
const hash = new Uint8Array(32);
|
|
44
|
-
const maskBytes = Math.floor(difficulty / 8);
|
|
45
|
-
const maskBits = difficulty % 8;
|
|
46
|
-
const finalMask = maskBits > 0 ? (0xff << (8 - maskBits)) & 0xff : 0;
|
|
47
|
-
const dv = new DataView(buf.buffer);
|
|
48
|
-
for (let nonce = 0; nonce < 10000000; nonce++) {
|
|
49
|
-
const str = nonce.toString();
|
|
50
|
-
const len = cBuf.length + str.length;
|
|
51
|
-
for (let i = 0; i < str.length; i++) buf[cBuf.length + i] = str.charCodeAt(i);
|
|
52
|
-
buf[len] = 0x80;
|
|
53
|
-
for (let i = len + 1; i < total - 8; i++) buf[i] = 0;
|
|
54
|
-
dv.setUint32(total - 4, len * 8, false);
|
|
55
|
-
dv.setUint32(total - 8, 0, false);
|
|
56
|
-
sha256Into(buf, total, hash);
|
|
57
|
-
let ok = true;
|
|
58
|
-
for (let i = 0; i < maskBytes; i++) if (hash[i] !== 0) { ok = false; break; }
|
|
59
|
-
if (ok && (finalMask === 0 || (hash[maskBytes] & finalMask) === 0)) return { found: true, nonce: str };
|
|
60
|
-
}
|
|
61
|
-
return { found: false };
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
const SERVER_LIST = [{ id: "mapple", name: "Mapple" }, { id: "s1", name: "Nexus" }, { id: "s2", name: "Cipher" }, { id: "s3", name: "Pulse" }, { id: "s4", name: "Vertex" }, { id: "s10", name: "Chimp" }];
|
|
65
|
-
|
|
66
|
-
export async function getStream(args) {
|
|
67
|
-
const { id, s, e, server } = args;
|
|
68
|
-
const isTv = s != null && e != null;
|
|
69
|
-
const mediaType = isTv ? "tv" : "movie";
|
|
70
|
-
const tvSlug = isTv ? `${s}-${e}` : "";
|
|
71
|
-
const pageUrl = isTv ? `${BASE_URL}/watch/tv/${id}/${s}/${e}` : `${BASE_URL}/watch/movie/${id}`;
|
|
72
|
-
try {
|
|
73
|
-
const pageRes = await fetch(pageUrl, { headers: PROXY_HEADERS, signal: AbortSignal.timeout(10000) });
|
|
74
|
-
if (!pageRes.ok) return null;
|
|
75
|
-
const html = await pageRes.text();
|
|
76
|
-
const reqMatch = html.match(/window\.__REQUEST_TOKEN__\s*=\s*"([^"]+)"/);
|
|
77
|
-
if (!reqMatch) return null;
|
|
78
|
-
const requestToken = reqMatch[1];
|
|
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
|
-
});
|
|
91
|
-
const initData = await initRes.json();
|
|
92
|
-
let streamToken = initData.token;
|
|
93
|
-
if (initData.requiresPow && initData.pow) {
|
|
94
|
-
const powRes = solvePoW(initData.pow.challenge, initData.pow.difficulty);
|
|
95
|
-
if (powRes.found) {
|
|
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
|
-
});
|
|
102
|
-
streamToken = (await solveRes.json()).token;
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
if (!streamToken) return null;
|
|
106
|
-
let targets = SERVER_LIST;
|
|
107
|
-
if (server && server !== "all") {
|
|
108
|
-
const clean = server.replace("Mapple - ", "");
|
|
109
|
-
targets = SERVER_LIST.filter(sv => sv.name === clean);
|
|
110
|
-
if (!targets.length) targets = SERVER_LIST;
|
|
111
|
-
}
|
|
112
|
-
const allUrls = [];
|
|
113
|
-
for (const srv of targets) {
|
|
114
|
-
try {
|
|
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
|
-
});
|
|
137
|
-
const streamData = await streamRes.json();
|
|
138
|
-
if (streamData.success && streamData.data?.stream_url) {
|
|
139
|
-
let fileUrl = streamData.data.stream_url;
|
|
140
|
-
if (fileUrl.includes('omena-puu') || fileUrl.includes('nocach')) fileUrl += fileUrl.includes('?') ? '&format=.m3u8' : '?format=.m3u8';
|
|
141
|
-
allUrls.push({ url: fileUrl, server: `Mapple - ${srv.name}`, headers: PROXY_HEADERS, skipProxy: false, skipVerify: true, skipHlsCheck: true });
|
|
142
|
-
}
|
|
143
|
-
} catch { }
|
|
144
|
-
}
|
|
145
|
-
return allUrls.length ? { allUrls } : null;
|
|
146
|
-
} catch { return null; }
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
export async function getSources() { return SERVER_LIST.map(s => `Mapple - ${s.name}`); }
|
|
150
|
-
export const SKIP_VERIFY = true;
|
|
151
|
-
export const MULTI_URL = true;
|
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
import { fetchJson } from '../utils/helpers.js';
|
|
2
|
-
|
|
3
|
-
const CONFIGURATION = 'W3siaWQiOiJkZWZhdWx0IiwibmFtZSI6Ik1lZ2FTb3VyY2UgZGVmYXVsdCIsInVybCI6Imh0dHBzOi8vZ2l0aHViLmNvbS96b3JldS9tZWdhc291cmNlX3NjcmFwZXJzL3Jhdy9yZWZzL2hlYWRzL21haW4vZGVmYXVsdF9zY3JhcGVyLnB5IiwiZGVzY3JpcHRpb24iOiJEZWZhdWx0IHNjcmFwZXIgaG9zdGVkIG9uIEdpdEh1Yi4ifV0';
|
|
4
|
-
const BASE_URL = `https://megasource.wasmer.app/${CONFIGURATION}`;
|
|
5
|
-
|
|
6
|
-
async function getImdbId(tmdbApiKey, tmdbId, s, e) {
|
|
7
|
-
const key = tmdbApiKey;
|
|
8
|
-
if (!key) return null;
|
|
9
|
-
try {
|
|
10
|
-
const url = s != null && e != null
|
|
11
|
-
? `https://api.themoviedb.org/3/tv/${tmdbId}/external_ids?api_key=${key}`
|
|
12
|
-
: `https://api.themoviedb.org/3/movie/${tmdbId}?api_key=${key}`;
|
|
13
|
-
const res = await fetchJson(url);
|
|
14
|
-
return res?.imdb_id || null;
|
|
15
|
-
} catch { return null; }
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
export async function getStream({ id, s, e, sdk }) {
|
|
19
|
-
const tmdbApiKey = sdk?.tmdbApiKey || null;
|
|
20
|
-
if (!tmdbApiKey) return null;
|
|
21
|
-
try {
|
|
22
|
-
const imdbId = await getImdbId(tmdbApiKey, id, s, e);
|
|
23
|
-
if (!imdbId) return null;
|
|
24
|
-
|
|
25
|
-
const path = s != null && e != null
|
|
26
|
-
? `/stream/series/${imdbId}:${s}:${e}.json`
|
|
27
|
-
: `/stream/movie/${imdbId}.json`;
|
|
28
|
-
|
|
29
|
-
const res = await fetchJson(`${BASE_URL}${path}`);
|
|
30
|
-
if (!res?.streams?.length) return null;
|
|
31
|
-
|
|
32
|
-
const allUrls = res.streams.map(stream => {
|
|
33
|
-
if (!stream.url) return null;
|
|
34
|
-
const headers = stream.behaviorHints?.proxyHeaders?.request || {};
|
|
35
|
-
return {
|
|
36
|
-
url: stream.url,
|
|
37
|
-
server: `MegaSource - ${stream.title ? ` ${stream.title}` : ''}`,
|
|
38
|
-
headers: Object.keys(headers).length > 0 ? headers : undefined,
|
|
39
|
-
skipProxy: Object.keys(headers).length > 0 && stream.behaviorHints?.notWebReady === false
|
|
40
|
-
};
|
|
41
|
-
}).filter(Boolean);
|
|
42
|
-
|
|
43
|
-
return allUrls.length ? { allUrls } : null;
|
|
44
|
-
} catch {
|
|
45
|
-
return null;
|
|
46
|
-
}
|
|
47
|
-
}
|
package/src/sources/nova.js
DELETED
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
import { fetchJson } from '../utils/helpers.js';
|
|
2
|
-
|
|
3
|
-
const BASE_URL = `https://nova-streamz.vercel.app`;
|
|
4
|
-
|
|
5
|
-
async function getImdbId(tmdbApiKey, tmdbId, s, e) {
|
|
6
|
-
const key = tmdbApiKey;
|
|
7
|
-
if (!key) return null;
|
|
8
|
-
try {
|
|
9
|
-
const url = s != null && e != null
|
|
10
|
-
? `https://api.themoviedb.org/3/tv/${tmdbId}/external_ids?api_key=${key}`
|
|
11
|
-
: `https://api.themoviedb.org/3/movie/${tmdbId}?api_key=${key}`;
|
|
12
|
-
const res = await fetchJson(url);
|
|
13
|
-
return res?.imdb_id || null;
|
|
14
|
-
} catch { return null; }
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
export async function getStream({ id, s, e, sdk }) {
|
|
18
|
-
const tmdbApiKey = sdk?.tmdbApiKey || null;
|
|
19
|
-
if (!tmdbApiKey) return null;
|
|
20
|
-
try {
|
|
21
|
-
const imdbId = await getImdbId(tmdbApiKey, id, s, e);
|
|
22
|
-
if (!imdbId) return null;
|
|
23
|
-
|
|
24
|
-
const path = s != null && e != null
|
|
25
|
-
? `/stream/series/${imdbId}:${s}:${e}.json`
|
|
26
|
-
: `/stream/movie/${imdbId}.json`;
|
|
27
|
-
|
|
28
|
-
const res = await fetchJson(`${BASE_URL}${path}`);
|
|
29
|
-
if (!res?.streams?.length) return null;
|
|
30
|
-
|
|
31
|
-
const allUrls = res.streams.map(stream => {
|
|
32
|
-
if (!stream.url) return null;
|
|
33
|
-
const headers = stream.behaviorHints?.proxyHeaders?.request || {};
|
|
34
|
-
return {
|
|
35
|
-
url: stream.url,
|
|
36
|
-
server: `Nova - ${stream.name.replace('Nova ', '') || 'Server'}${stream.title ? ` ${stream.title.split(' | ')[0] || ''}` : ''}`,
|
|
37
|
-
headers: Object.keys(headers).length > 0 ? headers : undefined,
|
|
38
|
-
skipProxy: Object.keys(headers).length > 0 && stream.behaviorHints?.notWebReady === false
|
|
39
|
-
};
|
|
40
|
-
}).filter(Boolean);
|
|
41
|
-
|
|
42
|
-
return allUrls.length ? { allUrls } : null;
|
|
43
|
-
} catch {
|
|
44
|
-
return null;
|
|
45
|
-
}
|
|
46
|
-
}
|