@vyla-entertainment/sdk 1.20.0 → 1.21.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 +3 -2
- package/src/config.js +10 -85
- package/src/sdk.d.ts +94 -121
- package/src/sdk.js +82 -78
- package/src/sources/111477.js +88 -100
- package/src/sources/123anime.js +120 -56
- package/src/sources/3eyedraven.js +82 -135
- package/src/sources/cinecat.js +37 -18
- package/src/sources/cinemaarmy.js +179 -180
- package/src/sources/cinesrc.js +401 -397
- package/src/sources/cno.js +101 -94
- package/src/sources/dulo.js +134 -149
- package/src/sources/flaxmovies.js +114 -105
- package/src/sources/frost.js +38 -29
- package/src/sources/fsonline.js +95 -47
- package/src/sources/hdhub.js +43 -36
- package/src/sources/hexa.js +60 -53
- package/src/sources/hyper.js +42 -35
- package/src/sources/kickassanime.js +117 -124
- package/src/sources/kisskh.js +114 -59
- package/src/sources/lmscript.js +72 -70
- package/src/sources/lookmovie.js +80 -79
- package/src/sources/movenerds.js +74 -124
- package/src/sources/movy.js +197 -209
- package/src/sources/net79.js +59 -55
- package/src/sources/peestream.js +126 -138
- package/src/sources/pengu.js +46 -49
- package/src/sources/pstream.js +73 -67
- package/src/sources/purstream.js +96 -104
- package/src/sources/rivestream.js +182 -176
- package/src/sources/rivo.js +58 -53
- package/src/sources/streamaggregator.js +114 -128
- package/src/sources/thugtv.js +70 -62
- package/src/sources/vidapi.js +122 -114
- package/src/sources/vidcore.js +14 -122
- package/src/sources/vidfast.js +14 -122
- package/src/sources/vidlink.js +78 -27
- package/src/sources/vidup.js +14 -122
- package/src/sources/vidvault.js +133 -187
- package/src/sources/vidy.js +192 -187
- package/src/sources/vidzee.js +189 -183
- package/src/sources/vixsrc.js +123 -113
- package/src/sources/watchout.js +60 -46
- package/src/sources/xdownloader.js +127 -128
- package/src/sources/yapgrid.js +68 -62
- package/src/sources/yastream.js +35 -29
- package/src/subtitles.js +105 -122
- package/src/utils/helpers.js +586 -247
- package/src/sources/bcine.js +0 -202
- package/src/sources/cinezo.js +0 -55
- package/src/sources/vidora.js +0 -62
- package/src/sources/vidrock.js +0 -145
package/src/sources/hexa.js
CHANGED
|
@@ -1,54 +1,61 @@
|
|
|
1
|
-
import { fetchJson, fetchText } from '../utils/helpers.js';
|
|
2
|
-
|
|
3
|
-
const
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
const
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
const
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
const
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
1
|
+
import { fetchJson, fetchText, getRandomHex } from '../utils/helpers.js';
|
|
2
|
+
|
|
3
|
+
const API_BASE = 'https://enc-dec.app/api';
|
|
4
|
+
const BASE_URL = 'https://hexa.su';
|
|
5
|
+
const TMDB_PROXY_BASE = 'https://theemoviedb.hexa.su';
|
|
6
|
+
|
|
7
|
+
export async function getStream(args) {
|
|
8
|
+
const { id, s, e } = args || {};
|
|
9
|
+
const isTv = s != null && e != null;
|
|
10
|
+
const key = getRandomHex(32);
|
|
11
|
+
|
|
12
|
+
const headers = {
|
|
13
|
+
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/137.0.0.0 Safari/537.36',
|
|
14
|
+
Referer: `${BASE_URL}/`,
|
|
15
|
+
Accept: 'text/plain',
|
|
16
|
+
'X-Fingerprint-Lite': 'e9136c41504646444',
|
|
17
|
+
'X-Api-Key': key,
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
const encHexaRes = await fetchJson(`${API_BASE}/enc-hexa`);
|
|
21
|
+
if (!encHexaRes || encHexaRes.status !== 200) return null;
|
|
22
|
+
headers['X-Cap-Token'] = encHexaRes.result.token;
|
|
23
|
+
|
|
24
|
+
const url = isTv
|
|
25
|
+
? `${TMDB_PROXY_BASE}/api/tmdb/tv/${id}/season/${s}/episode/${e}/images`
|
|
26
|
+
: `${TMDB_PROXY_BASE}/api/tmdb/movie/${id}/images`;
|
|
27
|
+
|
|
28
|
+
const encrypted = await fetchText(url, { headers });
|
|
29
|
+
if (!encrypted) return null;
|
|
30
|
+
|
|
31
|
+
const decRes = await fetchJson(`${API_BASE}/dec-hexa`, {
|
|
32
|
+
method: 'POST',
|
|
33
|
+
headers: { 'Content-Type': 'application/json' },
|
|
34
|
+
body: JSON.stringify({ text: encrypted, key }),
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
if (!decRes || decRes.status !== 200 || !decRes.result) return null;
|
|
38
|
+
|
|
39
|
+
const data = decRes.result;
|
|
40
|
+
const streamUrl = data.url || data.file || (Array.isArray(data.sources) ? data.sources[0]?.url : null);
|
|
41
|
+
if (!streamUrl) return null;
|
|
42
|
+
|
|
43
|
+
const streamHeaders = {
|
|
44
|
+
Referer: `${BASE_URL}/`,
|
|
45
|
+
Origin: BASE_URL,
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
const allUrls = [{
|
|
49
|
+
url: streamUrl,
|
|
50
|
+
server: 'Hexa',
|
|
51
|
+
quality: 'Auto',
|
|
52
|
+
type: streamUrl.includes('.m3u8') ? 'hls' : 'mp4',
|
|
53
|
+
headers: streamHeaders,
|
|
54
|
+
}];
|
|
55
|
+
|
|
56
|
+
return {
|
|
57
|
+
url: streamUrl,
|
|
58
|
+
headers: streamHeaders,
|
|
59
|
+
allUrls,
|
|
60
|
+
};
|
|
54
61
|
}
|
package/src/sources/hyper.js
CHANGED
|
@@ -1,36 +1,43 @@
|
|
|
1
|
-
import { fetchJson } from '../utils/helpers.js';
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
const
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
1
|
+
import { fetchJson } from '../utils/helpers.js';
|
|
2
|
+
|
|
3
|
+
const BASE_URL = 'https://hyper.stravo.site';
|
|
4
|
+
const DEFAULT_ORIGIN = 'https://mzfi.me';
|
|
5
|
+
|
|
6
|
+
export async function getStream(args) {
|
|
7
|
+
const { id, s, e } = args || {};
|
|
8
|
+
const isTv = s != null && e != null;
|
|
9
|
+
|
|
10
|
+
const path = isTv
|
|
11
|
+
? `/local/stream/series/tmdb:${id}:${s}:${e}.json`
|
|
12
|
+
: `/local/stream/movie/tmdb:${id}.json`;
|
|
13
|
+
|
|
14
|
+
const url = `${BASE_URL}${path}`;
|
|
15
|
+
const res = await fetchJson(url).catch(() => null);
|
|
16
|
+
if (!res || !Array.isArray(res.streams)) return null;
|
|
17
|
+
|
|
18
|
+
const validStreams = res.streams.filter(item => {
|
|
19
|
+
if (!item.url || item.externalUrl) return false;
|
|
20
|
+
if (typeof item.title === 'string' && item.title.includes('🔒')) return false;
|
|
21
|
+
return true;
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
if (!validStreams.length) return null;
|
|
25
|
+
|
|
26
|
+
const allUrls = validStreams.map(item => ({
|
|
27
|
+
url: item.url,
|
|
28
|
+
server: `Hyper - ${item.title?.replace(/[^\x20-\x7E]+/g, '').trim() || 'Server'}`,
|
|
29
|
+
quality: 'Auto',
|
|
30
|
+
type: item.url.includes('.m3u8') ? 'hls' : 'mp4',
|
|
31
|
+
headers: item.behaviorHints?.proxyHeaders?.request || {
|
|
32
|
+
Referer: `${DEFAULT_ORIGIN}/`,
|
|
33
|
+
Accept: '*/*',
|
|
34
|
+
},
|
|
35
|
+
skipProxy: item.behaviorHints?.notWebReady === false,
|
|
36
|
+
}));
|
|
37
|
+
|
|
38
|
+
return {
|
|
39
|
+
url: allUrls[0].url,
|
|
40
|
+
headers: allUrls[0].headers,
|
|
41
|
+
allUrls,
|
|
42
|
+
};
|
|
36
43
|
}
|
|
@@ -1,125 +1,118 @@
|
|
|
1
|
-
import { fetchJson, USER_AGENT } from '../utils/helpers.js';
|
|
2
|
-
|
|
3
|
-
const
|
|
4
|
-
const HLS_BASE =
|
|
5
|
-
const HEADERS = {
|
|
6
|
-
|
|
7
|
-
async function kaaSearch(query) {
|
|
8
|
-
const res = await fetchJson(`${
|
|
9
|
-
method:
|
|
10
|
-
headers: { ...HEADERS,
|
|
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(`${
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
async function kaaEpisodePage(showSlug, ep) {
|
|
21
|
-
return fetchJson(`${
|
|
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
|
|
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(`${
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
export async function getStream(
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
const
|
|
80
|
-
if (
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
const
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
} catch (err) {
|
|
119
|
-
return null;
|
|
120
|
-
}
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
export async function getSources() {
|
|
124
|
-
return ['KickAssAnime'];
|
|
1
|
+
import { fetchJson, getTmdbMetadata, resolveTmdbKey, USER_AGENT } from '../utils/helpers.js';
|
|
2
|
+
|
|
3
|
+
const BASE_URL = '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_URL}/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_URL}/api/show/${showSlug}`, { headers: HEADERS }).catch(() => null);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
async function kaaEpisodePage(showSlug, ep) {
|
|
21
|
+
return fetchJson(`${BASE_URL}/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_URL}/api/show/${showSlug}/episode/${fullEpSlug}`, { headers: HEADERS }).catch(() => null);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export async function getStream(args) {
|
|
50
|
+
const { id, s, e, sdk } = args || {};
|
|
51
|
+
const isTv = s != null && e != null;
|
|
52
|
+
const apiKey = resolveTmdbKey(args, sdk);
|
|
53
|
+
const epNum = e ? Number(e) : 1;
|
|
54
|
+
|
|
55
|
+
try {
|
|
56
|
+
const meta = await getTmdbMetadata(id, isTv, apiKey);
|
|
57
|
+
const titles = meta?.titles || [];
|
|
58
|
+
if (!titles.length) return null;
|
|
59
|
+
|
|
60
|
+
let showSlug = null;
|
|
61
|
+
for (const title of titles) {
|
|
62
|
+
const results = await kaaSearch(title);
|
|
63
|
+
if (results.length) {
|
|
64
|
+
showSlug = results[0].slug;
|
|
65
|
+
break;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
if (!showSlug) return null;
|
|
70
|
+
|
|
71
|
+
const showInfo = await kaaShowInfo(showSlug);
|
|
72
|
+
let fullEpSlug = null;
|
|
73
|
+
|
|
74
|
+
if (showInfo?.type === 'movie') {
|
|
75
|
+
const m = (showInfo.watch_uri || '').match(/\/(ep-(\d+)-([a-f0-9]+))$/i);
|
|
76
|
+
if (m) fullEpSlug = m[1];
|
|
77
|
+
} else {
|
|
78
|
+
const episodes = await kaaAllEpisodes(showSlug);
|
|
79
|
+
const epItem = episodes.find(item => item.episode_number === epNum);
|
|
80
|
+
if (epItem) fullEpSlug = `ep-${epItem.episode_number}-${epItem.slug}`;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
if (!fullEpSlug) return null;
|
|
84
|
+
|
|
85
|
+
const episodeData = await kaaEpisodeServers(showSlug, fullEpSlug);
|
|
86
|
+
const servers = Array.isArray(episodeData?.servers) ? episodeData.servers : [];
|
|
87
|
+
|
|
88
|
+
const allUrls = [];
|
|
89
|
+
for (const srv of servers) {
|
|
90
|
+
if (!srv.src) continue;
|
|
91
|
+
const m = srv.src.match(/[?&]id=([^&]+)/);
|
|
92
|
+
if (m) {
|
|
93
|
+
allUrls.push({
|
|
94
|
+
url: `${HLS_BASE}/${m[1]}/master.m3u8`,
|
|
95
|
+
server: `KickAssAnime (${srv.name || 'Server'})`,
|
|
96
|
+
quality: 'Auto',
|
|
97
|
+
type: 'hls',
|
|
98
|
+
headers: HEADERS,
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
if (!allUrls.length) return null;
|
|
104
|
+
|
|
105
|
+
return {
|
|
106
|
+
url: allUrls[0].url,
|
|
107
|
+
headers: allUrls[0].headers,
|
|
108
|
+
allUrls,
|
|
109
|
+
};
|
|
110
|
+
} catch {
|
|
111
|
+
return null;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
export async function getSources(args) {
|
|
116
|
+
const stream = await getStream(args);
|
|
117
|
+
return stream?.allUrls ? [...new Set(stream.allUrls.map(u => u.server))] : ['KickAssAnime'];
|
|
125
118
|
}
|
package/src/sources/kisskh.js
CHANGED
|
@@ -1,60 +1,115 @@
|
|
|
1
|
-
import { fetchJson,
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
const
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
1
|
+
import { cleanTitle, fetchJson, getTmdbMetadata, resolveTmdbKey, USER_AGENT } from '../utils/helpers.js';
|
|
2
|
+
|
|
3
|
+
const ENC_API = 'https://enc-dec.app/api';
|
|
4
|
+
const BASE_URL = 'https://kisskh.do';
|
|
5
|
+
|
|
6
|
+
const HEADERS = { 'User-Agent': USER_AGENT, Accept: 'application/json' };
|
|
7
|
+
|
|
8
|
+
async function encKey(episodeId, type) {
|
|
9
|
+
try {
|
|
10
|
+
const data = await fetchJson(`${ENC_API}/enc-kisskh?text=${episodeId}&type=${type}`, { signal: AbortSignal.timeout(6000) });
|
|
11
|
+
return data?.status === 200 ? data.result : null;
|
|
12
|
+
} catch {
|
|
13
|
+
return null;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export async function getStream(args) {
|
|
18
|
+
const { id, s, e, sdk } = args || {};
|
|
19
|
+
const isTv = s != null && e != null;
|
|
20
|
+
const apiKey = resolveTmdbKey(args, sdk);
|
|
21
|
+
|
|
22
|
+
try {
|
|
23
|
+
const meta = await getTmdbMetadata(id, isTv, apiKey);
|
|
24
|
+
if (!meta?.titles?.length) return null;
|
|
25
|
+
|
|
26
|
+
let drama = null;
|
|
27
|
+
let bestScore = -1;
|
|
28
|
+
const searchTitles = meta.titles.length > 1 ? [...meta.titles].reverse() : meta.titles;
|
|
29
|
+
|
|
30
|
+
for (const title of searchTitles) {
|
|
31
|
+
const results = await fetchJson(`${BASE_URL}/api/DramaList/Search?q=${encodeURIComponent(title)}`, {
|
|
32
|
+
headers: HEADERS,
|
|
33
|
+
signal: AbortSignal.timeout(6000),
|
|
34
|
+
}).catch(() => []);
|
|
35
|
+
|
|
36
|
+
const targetClean = cleanTitle(title);
|
|
37
|
+
for (const item of results) {
|
|
38
|
+
const parts = (item.title || '').split(/\s*-\s*/).map(cleanTitle);
|
|
39
|
+
let score = 0;
|
|
40
|
+
if (parts.some(p => p === targetClean)) score += 5;
|
|
41
|
+
else if (parts.some(p => p.includes(targetClean) || targetClean.includes(p))) score += 3;
|
|
42
|
+
else continue;
|
|
43
|
+
if (score > bestScore) {
|
|
44
|
+
bestScore = score;
|
|
45
|
+
drama = item;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
if (drama && bestScore >= 5) break;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
if (!drama) return null;
|
|
52
|
+
|
|
53
|
+
const detail = await fetchJson(`${BASE_URL}/api/DramaList/Drama/${drama.id}`, {
|
|
54
|
+
headers: HEADERS,
|
|
55
|
+
signal: AbortSignal.timeout(8000),
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
const episodeId = detail?.episodes?.find(ep => Math.floor(ep.number) === Number(e || 1))?.id;
|
|
59
|
+
if (!episodeId) return null;
|
|
60
|
+
|
|
61
|
+
const vidKey = await encKey(episodeId, 'vid');
|
|
62
|
+
if (!vidKey) return null;
|
|
63
|
+
|
|
64
|
+
const videoData = await fetchJson(`${BASE_URL}/api/DramaList/Episode/${episodeId}.png?err=false&ts=&time=&kkey=${vidKey}`, {
|
|
65
|
+
headers: HEADERS,
|
|
66
|
+
signal: AbortSignal.timeout(10000),
|
|
67
|
+
});
|
|
68
|
+
if (!videoData?.Video) return null;
|
|
69
|
+
|
|
70
|
+
let subtitles = [];
|
|
71
|
+
const subKey = await encKey(episodeId, 'sub');
|
|
72
|
+
if (subKey) {
|
|
73
|
+
try {
|
|
74
|
+
const subList = await fetchJson(`${BASE_URL}/api/Sub/${episodeId}?kkey=${subKey}`, {
|
|
75
|
+
headers: HEADERS,
|
|
76
|
+
signal: AbortSignal.timeout(8000),
|
|
77
|
+
});
|
|
78
|
+
if (Array.isArray(subList)) {
|
|
79
|
+
subtitles = subList.map(sub => ({
|
|
80
|
+
url: sub.src,
|
|
81
|
+
label: sub.label,
|
|
82
|
+
lang: sub.land,
|
|
83
|
+
default: Boolean(sub.default),
|
|
84
|
+
}));
|
|
85
|
+
}
|
|
86
|
+
} catch { }
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
const streamUrl = videoData.Video;
|
|
90
|
+
const allUrls = [{
|
|
91
|
+
url: streamUrl,
|
|
92
|
+
type: streamUrl.includes('.m3u8') ? 'hls' : 'mp4',
|
|
93
|
+
audio: 'sub',
|
|
94
|
+
server: 'KissKH',
|
|
95
|
+
quality: 'Auto',
|
|
96
|
+
headers: HEADERS,
|
|
97
|
+
skipProxy: false,
|
|
98
|
+
subtitles,
|
|
99
|
+
}];
|
|
100
|
+
|
|
101
|
+
return {
|
|
102
|
+
url: streamUrl,
|
|
103
|
+
headers: HEADERS,
|
|
104
|
+
allUrls,
|
|
105
|
+
subtitles,
|
|
106
|
+
};
|
|
107
|
+
} catch {
|
|
108
|
+
return null;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
export async function getSources(args) {
|
|
113
|
+
const stream = await getStream(args);
|
|
114
|
+
return stream?.allUrls ? [...new Set(stream.allUrls.map(u => u.server))] : ['KissKH'];
|
|
60
115
|
}
|