@vyla-entertainment/sdk 1.6.0 → 1.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +12 -10
- package/package.json +1 -1
- package/src/config.js +116 -48
- package/src/sdk.d.ts +1 -1
- package/src/sources/1embed.js +34 -11
- package/src/sources/anihq.js +14 -2
- package/src/sources/anikoto.js +198 -0
- package/src/sources/anineko.js +122 -15
- package/src/sources/anizone.js +109 -0
- package/src/sources/cinesu.js +94 -0
- package/src/sources/flaxmovies.js +21 -9
- package/src/sources/fsonic.js +2 -2
- package/src/sources/fsonline.js +2 -2
- package/src/sources/kickassanime.js +107 -0
- package/src/sources/lmscript.js +43 -0
- package/src/sources/meowtv.js +1 -8
- package/src/sources/reanime.js +324 -0
- package/src/sources/videasy.js +6 -12
- package/src/sources/vidvault.js +114 -0
- package/src/sources/vidzee.js +210 -101
- package/src/sources/vixsrc.js +28 -14
- package/src/subtitles.js +0 -18
- package/src/utils/helpers.js +56 -20
- package/src/sources/goated.js +0 -262
- package/src/sources/nebula.js +0 -62
package/src/sources/anineko.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { getTmdbInfo } from '../utils/helpers.js';
|
|
1
|
+
import { getTmdbInfo, USER_AGENT, fetchText } from '../utils/helpers.js';
|
|
2
2
|
|
|
3
3
|
const BASE_URL = "https://anineko.to";
|
|
4
4
|
|
|
@@ -10,7 +10,10 @@ function decodeEntities(str) {
|
|
|
10
10
|
|
|
11
11
|
async function search(query) {
|
|
12
12
|
try {
|
|
13
|
-
const html = await fetchText(`${BASE_URL}/browser?keyword=${encodeURIComponent(query)}`, {
|
|
13
|
+
const html = await fetchText(`${BASE_URL}/browser?keyword=${encodeURIComponent(query)}`, {
|
|
14
|
+
headers: { 'User-Agent': USER_AGENT },
|
|
15
|
+
signal: AbortSignal.timeout(6000)
|
|
16
|
+
});
|
|
14
17
|
const results = [];
|
|
15
18
|
let idx = 0;
|
|
16
19
|
while ((idx = html.indexOf('nv-anime-thumb', idx)) !== -1) {
|
|
@@ -27,40 +30,144 @@ async function search(query) {
|
|
|
27
30
|
}
|
|
28
31
|
idx += 14;
|
|
29
32
|
}
|
|
33
|
+
if (results.length === 0) {
|
|
34
|
+
const linkMatches = [...html.matchAll(/href=["']\/watch\/([^"']+)["']/gi)];
|
|
35
|
+
for (const match of linkMatches) {
|
|
36
|
+
if (!results.find(r => r.slug === match[1])) {
|
|
37
|
+
const linkIdx = html.indexOf(match[0]);
|
|
38
|
+
const titleStart = html.lastIndexOf('>', linkIdx);
|
|
39
|
+
const titleEnd = html.indexOf('<', linkIdx);
|
|
40
|
+
if (titleStart !== -1 && titleEnd !== -1 && titleEnd > linkIdx) {
|
|
41
|
+
const title = html.slice(titleStart + 1, Math.min(titleEnd, linkIdx + 200)).trim();
|
|
42
|
+
if (title.length > 2 && title.length < 100) {
|
|
43
|
+
results.push({ slug: match[1], text: title });
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
30
49
|
return results;
|
|
31
50
|
} catch { return []; }
|
|
32
51
|
}
|
|
33
52
|
|
|
34
53
|
async function extractHls(embedUrl) {
|
|
35
54
|
try {
|
|
36
|
-
const html = await fetchText(embedUrl, {
|
|
37
|
-
|
|
55
|
+
const html = await fetchText(embedUrl, {
|
|
56
|
+
headers: {
|
|
57
|
+
'User-Agent': USER_AGENT,
|
|
58
|
+
Referer: `${BASE_URL}/`
|
|
59
|
+
},
|
|
60
|
+
signal: AbortSignal.timeout(6000)
|
|
61
|
+
});
|
|
62
|
+
const m = html.match(/const\s+src\s*=\s*["'](https?:\/\/[^"']+\.m3u8[^"']*)["']/i) ||
|
|
63
|
+
html.match(/file\s*:\s*["'](https?:\/\/[^"']+\.m3u8[^"']*)["']/i) ||
|
|
64
|
+
html.match(/["'](https?:\/\/[^"']+\/master\.m3u8[^"']*)["']/i) ||
|
|
65
|
+
html.match(/["'](https?:\/\/[^"']+\.m3u8[^"']*)["']/i) ||
|
|
66
|
+
html.match(/src:\s*["'](https?:\/\/[^"']+\.m3u8[^"']*)["']/i);
|
|
38
67
|
if (m) return decodeEntities(m[1]);
|
|
39
68
|
} catch { }
|
|
40
69
|
return null;
|
|
41
70
|
}
|
|
42
71
|
|
|
43
|
-
export async function getStream({ id, s, e, audio }) {
|
|
72
|
+
export async function getStream({ id, s, e, audio, tmdbApiKey }) {
|
|
44
73
|
try {
|
|
45
|
-
const info = await getTmdbInfo(id, s ? 'tv' : 'movie', s);
|
|
74
|
+
const info = await getTmdbInfo(tmdbApiKey, id, s ? 'tv' : 'movie', s);
|
|
46
75
|
if (!info || !info.isAnime) return null;
|
|
47
76
|
let seriesSlug = null;
|
|
48
77
|
for (const title of info.titles) {
|
|
49
|
-
const
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
78
|
+
const potentialSlug = title.toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-+|-+$/g, '');
|
|
79
|
+
if (!potentialSlug) continue;
|
|
80
|
+
try {
|
|
81
|
+
const checkHtml = await fetchText(`${BASE_URL}/watch/${potentialSlug}/ep-${e || 1}`, {
|
|
82
|
+
headers: { 'User-Agent': USER_AGENT, Referer: `${BASE_URL}/watch/${potentialSlug}` },
|
|
83
|
+
signal: AbortSignal.timeout(4000)
|
|
84
|
+
});
|
|
85
|
+
if (checkHtml && checkHtml.includes('nv-watch-page')) {
|
|
86
|
+
seriesSlug = potentialSlug;
|
|
87
|
+
break;
|
|
88
|
+
}
|
|
89
|
+
} catch {}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
if (!seriesSlug) {
|
|
93
|
+
for (const title of info.titles) {
|
|
94
|
+
const results = await search(title);
|
|
95
|
+
const targetClean = cleanTitle(title);
|
|
96
|
+
|
|
97
|
+
const exactSlugMatch = results.find(r => {
|
|
98
|
+
const slugClean = cleanTitle(r.slug);
|
|
99
|
+
return slugClean === targetClean || r.slug === targetClean;
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
if (exactSlugMatch) {
|
|
103
|
+
seriesSlug = exactSlugMatch.slug;
|
|
104
|
+
break;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
const sortedResults = [...results].sort((a, b) => a.slug.length - b.slug.length);
|
|
108
|
+
|
|
109
|
+
for (const r of sortedResults) {
|
|
110
|
+
const resultClean = cleanTitle(r.text);
|
|
111
|
+
const isExactMatch = resultClean === targetClean;
|
|
112
|
+
const isCloseMatch = r.text.toLowerCase().includes(title.toLowerCase());
|
|
113
|
+
if (isExactMatch || isCloseMatch) {
|
|
114
|
+
seriesSlug = r.slug;
|
|
115
|
+
break;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
if (seriesSlug) break;
|
|
119
|
+
}
|
|
53
120
|
}
|
|
54
121
|
if (!seriesSlug && info.titles.length) {
|
|
55
122
|
const fSearch = await search(info.titles[0]);
|
|
56
|
-
|
|
123
|
+
const sortedResults = [...fSearch].sort((a, b) => a.slug.length - b.slug.length);
|
|
124
|
+
if (sortedResults.length) seriesSlug = sortedResults[0].slug;
|
|
57
125
|
}
|
|
58
126
|
if (!seriesSlug) return null;
|
|
59
|
-
const watchHtml = await fetchText(`${BASE_URL}/watch/${seriesSlug}/ep-${e || 1}`, {
|
|
127
|
+
const watchHtml = await fetchText(`${BASE_URL}/watch/${seriesSlug}/ep-${e || 1}`, {
|
|
128
|
+
headers: {
|
|
129
|
+
'User-Agent': USER_AGENT,
|
|
130
|
+
Referer: `${BASE_URL}/watch/${seriesSlug}`
|
|
131
|
+
},
|
|
132
|
+
signal: AbortSignal.timeout(6000)
|
|
133
|
+
});
|
|
60
134
|
const byAudio = { sub: [], dub: [] };
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
135
|
+
|
|
136
|
+
const iframeMatch = watchHtml.match(/<iframe[^>]*src=["']([^"']+)["'][^>]*>/i);
|
|
137
|
+
if (iframeMatch) {
|
|
138
|
+
const iframeSrc = decodeEntities(iframeMatch[1]);
|
|
139
|
+
const hls = await extractHls(iframeSrc);
|
|
140
|
+
if (hls) {
|
|
141
|
+
const audioType = audio === 'dub' ? 'dub' : 'sub';
|
|
142
|
+
byAudio[audioType].push(iframeSrc);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
if (byAudio.sub.length === 0 && byAudio.dub.length === 0) {
|
|
147
|
+
const tabMatches = [...watchHtml.matchAll(/<button[^>]*class=["'][^"']*nv-server-tab[^"']*["'][^>]*data-id=["']([^"']+)["'][^>]*>/gi)];
|
|
148
|
+
const tabMap = {};
|
|
149
|
+
for (const match of tabMatches) {
|
|
150
|
+
const tabId = match[1].toLowerCase();
|
|
151
|
+
const tabClassMatch = match[0].match(/class=["'][^"']*(tab_\d+)[^"']/i);
|
|
152
|
+
if (tabClassMatch) {
|
|
153
|
+
const tabClass = tabClassMatch[1];
|
|
154
|
+
tabMap[tabClass] = tabId.includes('dub') ? 'dub' : 'sub';
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
const btnRegex = /<button\s+[^>]*class=["'][^"']*server-video[^"']*["'][^>]*>/gi;
|
|
159
|
+
const btnMatches = [...watchHtml.matchAll(btnRegex)];
|
|
160
|
+
for (const match of btnMatches) {
|
|
161
|
+
const btnHtml = match[0];
|
|
162
|
+
const videoMatch = btnHtml.match(/data-video=["']([^"']+)["']/i);
|
|
163
|
+
const tabMatch = btnHtml.match(/data-tab=["']([^"']+)["']/i);
|
|
164
|
+
if (videoMatch && tabMatch) {
|
|
165
|
+
const videoUrl = decodeEntities(videoMatch[1]);
|
|
166
|
+
const tabClass = tabMatch[1];
|
|
167
|
+
const audioType = tabMap[tabClass] || 'sub';
|
|
168
|
+
byAudio[audioType].push(videoUrl);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
64
171
|
}
|
|
65
172
|
const audiosToTry = audio === "all" ? ["sub", "dub"] : (audio === "dub" ? ["dub", "sub"] : ["sub", "dub"]);
|
|
66
173
|
const allUrls = [];
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import { fetchText, getTmdbInfo, USER_AGENT } from '../utils/helpers.js';
|
|
2
|
+
|
|
3
|
+
const BASE = "https://anizone.to";
|
|
4
|
+
const HEADERS = { "User-Agent": USER_AGENT, Referer: `${BASE}/` };
|
|
5
|
+
|
|
6
|
+
function decodeEntities(encodedString) {
|
|
7
|
+
var translate_re = /&(nbsp|amp|quot|lt|gt);/g;
|
|
8
|
+
var translate = { "nbsp": " ", "amp": "&", "quot": "\"", "lt": "<", "gt": ">" };
|
|
9
|
+
return (encodedString || "").replace(translate_re, function (match, entity) {
|
|
10
|
+
return translate[entity];
|
|
11
|
+
}).replace(/&#(\d+);/g, function (match, dec) {
|
|
12
|
+
return String.fromCharCode(dec);
|
|
13
|
+
});
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function processJsonArg(raw) {
|
|
17
|
+
const PH = "\x01U\x01";
|
|
18
|
+
let s = raw.replace(/\\\\u([0-9a-fA-F]{4})/g, `${PH}$1`);
|
|
19
|
+
s = s.replace(/\\u([0-9a-fA-F]{4})/g, (_, h) => String.fromCharCode(parseInt(h, 16)));
|
|
20
|
+
s = s.replace(/\x01U\x01([0-9a-fA-F]{4})/g, "\\u$1");
|
|
21
|
+
try { return JSON.parse(s); } catch { return {}; }
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function pickTitle(titles) {
|
|
25
|
+
return titles["1"] || titles["5"] || titles["8"] || Object.values(titles)[0] || "";
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function extractSlug(ctx) {
|
|
29
|
+
const m = ctx.match(/href="(?:https:\/\/anizone\.to)?\/anime\/([a-z0-9-]+)"/);
|
|
30
|
+
return m ? m[1] : null;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function extractJsonArg(xdata, key) {
|
|
34
|
+
const re = new RegExp(`${key}:\\s*JSON\\.parse\\('((?:[^'\\\\]|\\\\.)*)'\\)`);
|
|
35
|
+
const m = xdata.match(re);
|
|
36
|
+
return m ? m[1] : null;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
async function search(query) {
|
|
40
|
+
const html = await fetchText(`${BASE}/anime?search=${encodeURIComponent(query)}`, { headers: HEADERS }).catch(() => "");
|
|
41
|
+
const results = [];
|
|
42
|
+
const xdataRe = /x-data="(\{[^"]*anmTitles[^"]*\})"/g;
|
|
43
|
+
let m;
|
|
44
|
+
while ((m = xdataRe.exec(html)) !== null) {
|
|
45
|
+
const ctxStart = Math.max(0, m.index - 300);
|
|
46
|
+
const ctxEnd = Math.min(html.length, m.index + m[0].length + 800);
|
|
47
|
+
const ctx = html.slice(ctxStart, ctxEnd);
|
|
48
|
+
const slug = extractSlug(ctx);
|
|
49
|
+
if (!slug) continue;
|
|
50
|
+
const xdata = decodeEntities(m[1]);
|
|
51
|
+
const raw = extractJsonArg(xdata, "anmTitles");
|
|
52
|
+
if (!raw) continue;
|
|
53
|
+
const titles = processJsonArg(raw);
|
|
54
|
+
const title = pickTitle(titles);
|
|
55
|
+
if (title) results.push({ slug, text: title });
|
|
56
|
+
}
|
|
57
|
+
return results;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
async function scrapeWatch(slug, episodeNum) {
|
|
61
|
+
const html = await fetchText(`${BASE}/anime/${slug}/${episodeNum}`, { headers: HEADERS }).catch(() => "");
|
|
62
|
+
if (!html) return null;
|
|
63
|
+
|
|
64
|
+
const hlsMatch = html.match(/<media-player[^>]+src="([^"]+\.m3u8[^"]*)"/i);
|
|
65
|
+
const hls = hlsMatch ? decodeEntities(hlsMatch[1]) : null;
|
|
66
|
+
|
|
67
|
+
return hls;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export async function getStream({ id, s, e, tmdbApiKey }) {
|
|
71
|
+
try {
|
|
72
|
+
const isTv = !!s;
|
|
73
|
+
const mediaType = isTv ? 'tv' : 'movie';
|
|
74
|
+
const epNum = e ? Number(e) : 1;
|
|
75
|
+
const seasonNum = s ? Number(s) : 1;
|
|
76
|
+
|
|
77
|
+
const tmdbInfo = await getTmdbInfo(tmdbApiKey, id, mediaType, seasonNum);
|
|
78
|
+
const titles = tmdbInfo?.titles || [];
|
|
79
|
+
|
|
80
|
+
let slug = null;
|
|
81
|
+
for (const title of titles) {
|
|
82
|
+
const results = await search(title);
|
|
83
|
+
if (results.length) {
|
|
84
|
+
slug = results[0].slug;
|
|
85
|
+
break;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
if (!slug) return null;
|
|
90
|
+
|
|
91
|
+
const hls = await scrapeWatch(slug, epNum);
|
|
92
|
+
if (!hls) return null;
|
|
93
|
+
|
|
94
|
+
return {
|
|
95
|
+
allUrls: [
|
|
96
|
+
{
|
|
97
|
+
url: hls,
|
|
98
|
+
server: 'AniZone',
|
|
99
|
+
}
|
|
100
|
+
]
|
|
101
|
+
};
|
|
102
|
+
} catch (err) {
|
|
103
|
+
return null;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
export async function getSources() {
|
|
108
|
+
return ['AniZone'];
|
|
109
|
+
}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { USER_AGENT } from '../utils/helpers.js';
|
|
2
|
+
|
|
3
|
+
const lS = [17, 91, 203, 44, 8, 177, 62, 239, 119, 3, 154, 81, 28, 210, 101, 7];
|
|
4
|
+
|
|
5
|
+
function lA(e) {
|
|
6
|
+
let t = e >>> 0;
|
|
7
|
+
t ^= t >>> 16;
|
|
8
|
+
t = Math.imul(t, 0x7feb352d) >>> 0;
|
|
9
|
+
t ^= t >>> 15;
|
|
10
|
+
t = Math.imul(t, 0x846ca68b) >>> 0;
|
|
11
|
+
return (t ^ (t >>> 16)) >>> 0;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function lL(keyStr, targetLen) {
|
|
15
|
+
const encoder = new TextEncoder();
|
|
16
|
+
const i = encoder.encode(keyStr || "dev");
|
|
17
|
+
const rLen = Math.max(32, Math.min(128, targetLen + 17));
|
|
18
|
+
const r = new Uint8Array(rLen);
|
|
19
|
+
let s = 0x811c9dc5;
|
|
20
|
+
for (let e = 0; e < rLen; e += 1) {
|
|
21
|
+
const a = i[e % i.length] ?? e;
|
|
22
|
+
s ^= a;
|
|
23
|
+
s = lA((s + lS[e % lS.length] + ((0x9e3779b1 * e) >>> 0)) >>> 0);
|
|
24
|
+
r[e] = s & 255;
|
|
25
|
+
}
|
|
26
|
+
return r;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function base64UrlEncode(bytes) {
|
|
30
|
+
const chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
|
|
31
|
+
let i = "";
|
|
32
|
+
for (let r = 0; r < bytes.length; r += 3) {
|
|
33
|
+
const s = bytes[r];
|
|
34
|
+
const a = bytes[r + 1];
|
|
35
|
+
const n = bytes[r + 2];
|
|
36
|
+
i += chars[s >>> 2];
|
|
37
|
+
i += chars[((3 & s) << 4) | ((a ?? 0) >>> 4)];
|
|
38
|
+
if (a === undefined) break;
|
|
39
|
+
i += chars[((15 & a) << 2) | ((n ?? 0) >>> 6)];
|
|
40
|
+
if (n === undefined) break;
|
|
41
|
+
i += chars[63 & n];
|
|
42
|
+
}
|
|
43
|
+
return i;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export function generateCineSuUrl(type, tmdbId, season = 1, episode = 1) {
|
|
47
|
+
const kind = type === "show" || type === "tv" ? "show" : "movie";
|
|
48
|
+
const s = kind === "show" ? Math.max(1, Math.floor(season)) : 0;
|
|
49
|
+
const ep = kind === "show" ? Math.max(1, Math.floor(episode)) : 0;
|
|
50
|
+
const char = kind[0];
|
|
51
|
+
|
|
52
|
+
const payloadStr = `4860ac8bfddb:${char}:${Math.floor(Number(tmdbId))}:${s}:${ep}`;
|
|
53
|
+
const encoder = new TextEncoder();
|
|
54
|
+
const d = encoder.encode(payloadStr);
|
|
55
|
+
|
|
56
|
+
const key = "224eff10e662e9635c9f671cf46351dcd69af42b1edd56f5e5fa21751f44b9c8";
|
|
57
|
+
const u = lL(key, d.length);
|
|
58
|
+
|
|
59
|
+
const h = new Uint8Array(d.length + 2);
|
|
60
|
+
h[0] = 255 & d.length;
|
|
61
|
+
h[1] = (d.length >>> 8) & 255;
|
|
62
|
+
|
|
63
|
+
let c = (0x9e3779b9 ^ d.length) >>> 0;
|
|
64
|
+
for (let e = 0; e < d.length; e += 1) {
|
|
65
|
+
c = lA((c + u[e % u.length] + lS[e % lS.length] + e) >>> 0);
|
|
66
|
+
h[e + 2] = d[e] ^ (255 & c) ^ u[(7 * e + 3) % u.length];
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const token = base64UrlEncode(h);
|
|
70
|
+
return `https://glendale-plumbing.com/c/v1/${token}/master.m3u8`;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export async function getStream({ id, s, e }) {
|
|
74
|
+
try {
|
|
75
|
+
const isTv = !!s;
|
|
76
|
+
const mediaType = isTv ? 'tv' : 'movie';
|
|
77
|
+
const m3u8Url = generateCineSuUrl(mediaType, id, s || 1, e || 1);
|
|
78
|
+
|
|
79
|
+
return {
|
|
80
|
+
allUrls: [
|
|
81
|
+
{
|
|
82
|
+
url: m3u8Url,
|
|
83
|
+
server: 'CineSu',
|
|
84
|
+
}
|
|
85
|
+
]
|
|
86
|
+
};
|
|
87
|
+
} catch (err) {
|
|
88
|
+
return null;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
export async function getSources() {
|
|
93
|
+
return ['CineSu'];
|
|
94
|
+
}
|
|
@@ -1,14 +1,26 @@
|
|
|
1
1
|
import { fetchJson } from '../utils/helpers.js';
|
|
2
2
|
|
|
3
|
-
const API_BASE = 'https://itjiocunahckqxcnzpoy.supabase.co/functions/v1';
|
|
4
|
-
const APIKEY = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6Iml0amlvY3VuYWhja3F4Y256cG95Iiwicm9sZSI6ImFub24iLCJpYXQiOjE3NzczMzM1MjQsImV4cCI6MjA5MjkwOTUyNH0.9x9ykdHAzrv_GSvnawPeQaOxQeh3sZg0QAh4u9VOF4M';
|
|
5
|
-
const HEADERS = { 'apikey': APIKEY, 'authorization': `Bearer ${APIKEY}`, 'content-type': 'application/json', 'origin': 'https://flaxmovies.xyz', 'referer': 'https://flaxmovies.xyz/' };
|
|
6
|
-
|
|
7
3
|
export async function getStream({ id, s, e }) {
|
|
8
4
|
try {
|
|
9
|
-
const
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
5
|
+
const url = s && e
|
|
6
|
+
? `https://sparkling-mode-a1ed.fillatame.workers.dev/?tmdb_id=${id}&season=${s}&episode=${e}`
|
|
7
|
+
: `https://sparkling-mode-a1ed.fillatame.workers.dev/?tmdb_id=${id}`;
|
|
8
|
+
const data = await fetchJson(url, { signal: AbortSignal.timeout(15000) });
|
|
9
|
+
if (!data || !Array.isArray(data.streams)) return null;
|
|
10
|
+
const allUrls = [];
|
|
11
|
+
for (const stream of data.streams) {
|
|
12
|
+
const streamUrl = stream.url || stream.link;
|
|
13
|
+
if (!streamUrl) continue;
|
|
14
|
+
const isHls = stream.format === 'HLS' || stream.isM3U8 === true || streamUrl.includes('m3u8');
|
|
15
|
+
allUrls.push({
|
|
16
|
+
url: streamUrl,
|
|
17
|
+
server: stream.provider || 'FlaxMovies',
|
|
18
|
+
quality: stream.resolution || 'Auto',
|
|
19
|
+
type: isHls ? 'hls' : 'mp4'
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
return allUrls.length ? { allUrls } : null;
|
|
23
|
+
} catch {
|
|
24
|
+
return null;
|
|
25
|
+
}
|
|
14
26
|
}
|
package/src/sources/fsonic.js
CHANGED
|
@@ -4,10 +4,10 @@ const BASE_URL = 'https://www.fsonic.net';
|
|
|
4
4
|
const FSHARE_BASE = 'https://fsharetv.co';
|
|
5
5
|
const HEADERS = { 'User-Agent': USER_AGENT, 'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8', 'Accept-Language': 'en-US,en;q=0.9' };
|
|
6
6
|
|
|
7
|
-
export async function getStream({ id, s }) {
|
|
7
|
+
export async function getStream({ id, s, tmdbApiKey }) {
|
|
8
8
|
if (s != null) return null;
|
|
9
9
|
try {
|
|
10
|
-
const info = await getTmdbInfo(id, 'movie');
|
|
10
|
+
const info = await getTmdbInfo(tmdbApiKey, id, 'movie');
|
|
11
11
|
if (!info?.titles?.length) return null;
|
|
12
12
|
let watchSlug = null;
|
|
13
13
|
for (const title of info.titles) {
|
package/src/sources/fsonline.js
CHANGED
|
@@ -4,10 +4,10 @@ const ORIGIN = 'https://www3.fsonline.app';
|
|
|
4
4
|
const AJAX_URL = `${ORIGIN}/wp-admin/admin-ajax.php`;
|
|
5
5
|
const HEADERS = { 'User-Agent': USER_AGENT, 'Origin': ORIGIN, 'Referer': `${ORIGIN}/` };
|
|
6
6
|
|
|
7
|
-
export async function getStream({ id, s, e }) {
|
|
7
|
+
export async function getStream({ id, s, e, tmdbApiKey }) {
|
|
8
8
|
try {
|
|
9
9
|
const isTv = s != null && e != null;
|
|
10
|
-
const info = await getTmdbInfo(id, isTv ? 'tv' : 'movie');
|
|
10
|
+
const info = await getTmdbInfo(tmdbApiKey, id, isTv ? 'tv' : 'movie');
|
|
11
11
|
if (!info?.titles?.length) return null;
|
|
12
12
|
const searchHtml = await fetchText(`${ORIGIN}/?s=${encodeURIComponent(info.titles[0] + (info.year ? ' ' + info.year : ''))}`, { headers: HEADERS });
|
|
13
13
|
const typeFolder = isTv ? 'seriale' : 'film';
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import { fetchJson, getTmdbInfo, USER_AGENT } from '../utils/helpers.js';
|
|
2
|
+
|
|
3
|
+
const BASE = "https://kaa.lt";
|
|
4
|
+
const HLS_BASE = "https://hls.krussdomi.com/manifest";
|
|
5
|
+
const HEADERS = { "User-Agent": USER_AGENT, Accept: "application/json" };
|
|
6
|
+
|
|
7
|
+
async function kaaSearch(query) {
|
|
8
|
+
const res = await fetchJson(`${BASE}/api/fsearch`, {
|
|
9
|
+
method: "POST",
|
|
10
|
+
headers: { ...HEADERS, "Content-Type": "application/json" },
|
|
11
|
+
body: JSON.stringify({ page: 1, query }),
|
|
12
|
+
}).catch(() => null);
|
|
13
|
+
return Array.isArray(res?.result) ? res.result : [];
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
async function kaaShowInfo(showSlug) {
|
|
17
|
+
return fetchJson(`${BASE}/api/show/${showSlug}`, { headers: HEADERS }).catch(() => null);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
async function kaaEpisodePage(showSlug, ep) {
|
|
21
|
+
return fetchJson(`${BASE}/api/show/${showSlug}/episodes?ep=${ep}&lang=ja-JP`, { headers: HEADERS }).catch(() => null);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
async function kaaAllEpisodes(showSlug) {
|
|
25
|
+
const first = await kaaEpisodePage(showSlug, 1);
|
|
26
|
+
if (!first) return [];
|
|
27
|
+
const pages = Array.isArray(first.pages) ? first.pages : [];
|
|
28
|
+
const all = Array.isArray(first.result) ? [...first.result] : [];
|
|
29
|
+
|
|
30
|
+
if (pages.length > 1) {
|
|
31
|
+
const rest = await Promise.all(
|
|
32
|
+
pages.slice(1).map(async (pg) => {
|
|
33
|
+
const startEp = pg.eps?.[0];
|
|
34
|
+
if (!startEp) return [];
|
|
35
|
+
const d = await kaaEpisodePage(showSlug, startEp);
|
|
36
|
+
return Array.isArray(d?.result) ? d.result : [];
|
|
37
|
+
})
|
|
38
|
+
);
|
|
39
|
+
for (const batch of rest) all.push(...batch);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
return all;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
async function kaaEpisodeServers(showSlug, fullEpSlug) {
|
|
46
|
+
return fetchJson(`${BASE}/api/show/${showSlug}/episode/${fullEpSlug}`, { headers: HEADERS }).catch(() => null);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export async function getStream({ id, s, e, tmdbApiKey }) {
|
|
50
|
+
try {
|
|
51
|
+
const isTv = !!s;
|
|
52
|
+
const mediaType = isTv ? 'tv' : 'movie';
|
|
53
|
+
const epNum = e ? Number(e) : 1;
|
|
54
|
+
const seasonNum = s ? Number(s) : 1;
|
|
55
|
+
|
|
56
|
+
const tmdbInfo = await getTmdbInfo(tmdbApiKey, id, mediaType, seasonNum);
|
|
57
|
+
const titles = tmdbInfo?.titles || [];
|
|
58
|
+
|
|
59
|
+
let showSlug = null;
|
|
60
|
+
for (const title of titles) {
|
|
61
|
+
const results = await kaaSearch(title);
|
|
62
|
+
if (results.length) {
|
|
63
|
+
showSlug = results[0].slug;
|
|
64
|
+
break;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
if (!showSlug) return null;
|
|
69
|
+
|
|
70
|
+
const showInfo = await kaaShowInfo(showSlug);
|
|
71
|
+
let fullEpSlug = null;
|
|
72
|
+
|
|
73
|
+
if (showInfo?.type === "movie") {
|
|
74
|
+
const m = (showInfo.watch_uri || "").match(/\/(ep-(\d+)-([a-f0-9]+))$/i);
|
|
75
|
+
if (m) fullEpSlug = m[1];
|
|
76
|
+
} else {
|
|
77
|
+
const episodes = await kaaAllEpisodes(showSlug);
|
|
78
|
+
const epItem = episodes.find((item) => item.episode_number === epNum);
|
|
79
|
+
if (epItem) fullEpSlug = `ep-${epItem.episode_number}-${epItem.slug}`;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
if (!fullEpSlug) return null;
|
|
83
|
+
|
|
84
|
+
const episodeData = await kaaEpisodeServers(showSlug, fullEpSlug);
|
|
85
|
+
const servers = Array.isArray(episodeData?.servers) ? episodeData.servers : [];
|
|
86
|
+
|
|
87
|
+
const urls = [];
|
|
88
|
+
for (const srv of servers) {
|
|
89
|
+
if (!srv.src) continue;
|
|
90
|
+
const m = srv.src.match(/[?&]id=([^&]+)/);
|
|
91
|
+
if (m) {
|
|
92
|
+
urls.push({
|
|
93
|
+
url: `${HLS_BASE}/${m[1]}/master.m3u8`,
|
|
94
|
+
server: `KickAssAnime (${srv.name || 'Server'})`,
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
return urls.length ? { allUrls: urls } : null;
|
|
100
|
+
} catch (err) {
|
|
101
|
+
return null;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
export async function getSources() {
|
|
106
|
+
return ['KickAssAnime'];
|
|
107
|
+
}
|
|
@@ -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/meowtv.js
CHANGED