@vyla-entertainment/sdk 1.20.0 → 1.22.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 +99 -67
- package/src/health.js +60 -20
- 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/4movies.js +77 -0
- package/src/sources/aether.js +99 -0
- package/src/sources/castle.js +415 -0
- 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/framextv.js +126 -0
- 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/meowtv.js +140 -0
- 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/vidora.js +74 -42
- 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/vidrock.js +0 -145
package/src/sdk.js
CHANGED
|
@@ -1,78 +1,82 @@
|
|
|
1
|
-
import SOURCES, { SOURCE_MAP } from "./config.js";
|
|
2
|
-
import { fetchDownloads } from "./downloads/downloads.js";
|
|
3
|
-
import { probeSource } from "./health.js";
|
|
4
|
-
import { getChapterPages, getMangaDetails, searchManga } from "./manga/atsu.js";
|
|
5
|
-
import { fetchSubtitles, getSubPathsMovie, getSubPathsTv } from "./subtitles.js";
|
|
6
|
-
import { createStreamArgs } from "./utils/helpers.js";
|
|
7
|
-
|
|
8
|
-
const ALL_SOURCE_MODULES = Object.fromEntries(
|
|
9
|
-
await Promise.all(SOURCES.map(async cfg => [cfg.key, await import(`./sources/${cfg.sourceFile}.js`)]))
|
|
10
|
-
);
|
|
11
|
-
|
|
12
|
-
const SOURCE_MODULES = Object.fromEntries(
|
|
13
|
-
Object.entries(ALL_SOURCE_MODULES).filter(([key]) => !SOURCE_MAP[key]?.disabled)
|
|
14
|
-
);
|
|
15
|
-
|
|
16
|
-
export default class VylaSDK {
|
|
17
|
-
tmdbApiKey = null;
|
|
18
|
-
penguManifest = null;
|
|
19
|
-
constructor({ tmdbApiKey, penguManifest } = {}) {
|
|
20
|
-
this.tmdbApiKey = tmdbApiKey || null;
|
|
21
|
-
this.penguManifest = penguManifest || null;
|
|
22
|
-
}
|
|
23
|
-
getSources(excludeDisabled = false) {
|
|
24
|
-
if (excludeDisabled) {
|
|
25
|
-
return SOURCES.filter(cfg => !cfg.disabled);
|
|
26
|
-
}
|
|
27
|
-
return SOURCES;
|
|
28
|
-
}
|
|
29
|
-
async probeAllSources() {
|
|
30
|
-
const active = SOURCES.filter(cfg => !cfg.disabled);
|
|
31
|
-
|
|
32
|
-
const results = await Promise.allSettled(
|
|
33
|
-
active.map(async cfg => await probeSource(cfg, SOURCE_MODULES[cfg.key]))
|
|
34
|
-
);
|
|
35
|
-
|
|
36
|
-
const sources = Object.fromEntries(
|
|
37
|
-
active.map((cfg, i) => [
|
|
38
|
-
cfg.key,
|
|
39
|
-
results[i].status === 'fulfilled' ? results[i].value : { ok: false, ms: null },
|
|
40
|
-
])
|
|
41
|
-
);
|
|
42
|
-
|
|
43
|
-
return sources;
|
|
44
|
-
}
|
|
45
|
-
async probeSource(key) {
|
|
46
|
-
const cfg = SOURCES.find(cfg => cfg.key === key);
|
|
47
|
-
if (!cfg) throw new Error(`Source with key "${key}" not found`);
|
|
48
|
-
const mod = SOURCE_MODULES[key];
|
|
49
|
-
if (!mod) throw new Error(`Source module for key "${key}" not found`);
|
|
50
|
-
return await probeSource(this, cfg, mod);
|
|
51
|
-
}
|
|
52
|
-
async getStream(key, id, s = null, e = null, clientIP = null) {
|
|
53
|
-
const cfg = SOURCES.find(cfg => cfg.key === key);
|
|
54
|
-
if (!cfg) throw new Error(`Source with key "${key}" not found`);
|
|
55
|
-
const mod = SOURCE_MODULES[key];
|
|
56
|
-
if (!mod) throw new Error(`Source module for key "${key}" not found`);
|
|
57
|
-
const streamArgs = await createStreamArgs(cfg, this, id, s, e, clientIP);
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
}
|
|
1
|
+
import SOURCES, { SOURCE_MAP } from "./config.js";
|
|
2
|
+
import { fetchDownloads } from "./downloads/downloads.js";
|
|
3
|
+
import { probeSource } from "./health.js";
|
|
4
|
+
import { getChapterPages, getMangaDetails, searchManga } from "./manga/atsu.js";
|
|
5
|
+
import { fetchSubtitles, getSubPathsMovie, getSubPathsTv } from "./subtitles.js";
|
|
6
|
+
import { createStreamArgs } from "./utils/helpers.js";
|
|
7
|
+
|
|
8
|
+
const ALL_SOURCE_MODULES = Object.fromEntries(
|
|
9
|
+
await Promise.all(SOURCES.map(async cfg => [cfg.key, await import(`./sources/${cfg.sourceFile}.js`)]))
|
|
10
|
+
);
|
|
11
|
+
|
|
12
|
+
const SOURCE_MODULES = Object.fromEntries(
|
|
13
|
+
Object.entries(ALL_SOURCE_MODULES).filter(([key]) => !SOURCE_MAP[key]?.disabled)
|
|
14
|
+
);
|
|
15
|
+
|
|
16
|
+
export default class VylaSDK {
|
|
17
|
+
tmdbApiKey = null;
|
|
18
|
+
penguManifest = null;
|
|
19
|
+
constructor({ tmdbApiKey, penguManifest } = {}) {
|
|
20
|
+
this.tmdbApiKey = tmdbApiKey || null;
|
|
21
|
+
this.penguManifest = penguManifest || null;
|
|
22
|
+
}
|
|
23
|
+
getSources(excludeDisabled = false) {
|
|
24
|
+
if (excludeDisabled) {
|
|
25
|
+
return SOURCES.filter(cfg => !cfg.disabled);
|
|
26
|
+
}
|
|
27
|
+
return SOURCES;
|
|
28
|
+
}
|
|
29
|
+
async probeAllSources() {
|
|
30
|
+
const active = SOURCES.filter(cfg => !cfg.disabled);
|
|
31
|
+
|
|
32
|
+
const results = await Promise.allSettled(
|
|
33
|
+
active.map(async cfg => await probeSource(this, cfg, SOURCE_MODULES[cfg.key]))
|
|
34
|
+
);
|
|
35
|
+
|
|
36
|
+
const sources = Object.fromEntries(
|
|
37
|
+
active.map((cfg, i) => [
|
|
38
|
+
cfg.key,
|
|
39
|
+
results[i].status === 'fulfilled' ? results[i].value : { ok: false, ms: null },
|
|
40
|
+
])
|
|
41
|
+
);
|
|
42
|
+
|
|
43
|
+
return sources;
|
|
44
|
+
}
|
|
45
|
+
async probeSource(key) {
|
|
46
|
+
const cfg = SOURCES.find(cfg => cfg.key === key);
|
|
47
|
+
if (!cfg) throw new Error(`Source with key "${key}" not found`);
|
|
48
|
+
const mod = SOURCE_MODULES[key];
|
|
49
|
+
if (!mod) throw new Error(`Source module for key "${key}" not found`);
|
|
50
|
+
return await probeSource(this, cfg, mod);
|
|
51
|
+
}
|
|
52
|
+
async getStream(key, id, s = null, e = null, clientIP = null) {
|
|
53
|
+
const cfg = SOURCES.find(cfg => cfg.key === key);
|
|
54
|
+
if (!cfg) throw new Error(`Source with key "${key}" not found`);
|
|
55
|
+
const mod = SOURCE_MODULES[key];
|
|
56
|
+
if (!mod) throw new Error(`Source module for key "${key}" not found`);
|
|
57
|
+
const streamArgs = await createStreamArgs(cfg, this, id, s, e, clientIP);
|
|
58
|
+
const result = await mod.getStream(streamArgs);
|
|
59
|
+
if (result && typeof result === 'object') {
|
|
60
|
+
delete result.subtitles;
|
|
61
|
+
}
|
|
62
|
+
return result;
|
|
63
|
+
}
|
|
64
|
+
async getSubtitles(id, s = null, e = null) {
|
|
65
|
+
const paths = s != null && e != null
|
|
66
|
+
? await getSubPathsTv(id, s, e)
|
|
67
|
+
: await getSubPathsMovie(id);
|
|
68
|
+
return await fetchSubtitles(paths);
|
|
69
|
+
}
|
|
70
|
+
async getDownloads(id, s = null, e = null) {
|
|
71
|
+
return await fetchDownloads(this, id, s, e);
|
|
72
|
+
}
|
|
73
|
+
async searchManga(query, page = 1, perPage = 12) {
|
|
74
|
+
return await searchManga(query, page, perPage);
|
|
75
|
+
}
|
|
76
|
+
async getManga(id) {
|
|
77
|
+
return await getMangaDetails(id);
|
|
78
|
+
}
|
|
79
|
+
async getMangaChapter(mangaId, chapterId) {
|
|
80
|
+
return await getChapterPages(mangaId, chapterId);
|
|
81
|
+
}
|
|
82
|
+
}
|
package/src/sources/111477.js
CHANGED
|
@@ -1,101 +1,89 @@
|
|
|
1
|
-
import { getUA } from '../utils/helpers.js';
|
|
2
|
-
|
|
3
|
-
const SERVICE_ORIGIN = 'https://st.111477.xyz';
|
|
4
|
-
|
|
5
|
-
function generateManifestBaseUrl(
|
|
6
|
-
let config = 'https://a.111477.xyz/::sort=file-desc::limit=3';
|
|
7
|
-
if (
|
|
8
|
-
config += `::tmdb=${
|
|
9
|
-
}
|
|
10
|
-
const b64 = Buffer.from(config, 'utf8')
|
|
11
|
-
.toString('base64')
|
|
12
|
-
.replace(/\+/g, '-')
|
|
13
|
-
.replace(/\//g, '_')
|
|
14
|
-
.replace(/=+$/, '');
|
|
15
|
-
return `${SERVICE_ORIGIN}/config/${b64}`;
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
async function
|
|
19
|
-
const
|
|
20
|
-
|
|
21
|
-
const
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
const
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
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
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
if (allUrls.length > 0) break;
|
|
90
|
-
}
|
|
91
|
-
} catch { }
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
if (allUrls.length === 0) return null;
|
|
95
|
-
|
|
96
|
-
return {
|
|
97
|
-
url: allUrls[0].url,
|
|
98
|
-
allUrls,
|
|
99
|
-
headers: allUrls[0].headers,
|
|
100
|
-
};
|
|
1
|
+
import { getUA, resolveImdbId, resolveTmdbKey } from '../utils/helpers.js';
|
|
2
|
+
|
|
3
|
+
const SERVICE_ORIGIN = 'https://st.111477.xyz';
|
|
4
|
+
|
|
5
|
+
function generateManifestBaseUrl(apiKey) {
|
|
6
|
+
let config = 'https://a.111477.xyz/::sort=file-desc::limit=3';
|
|
7
|
+
if (apiKey) {
|
|
8
|
+
config += `::tmdb=${apiKey}`;
|
|
9
|
+
}
|
|
10
|
+
const b64 = Buffer.from(config, 'utf8')
|
|
11
|
+
.toString('base64')
|
|
12
|
+
.replace(/\+/g, '-')
|
|
13
|
+
.replace(/\//g, '_')
|
|
14
|
+
.replace(/=+$/, '');
|
|
15
|
+
return `${SERVICE_ORIGIN}/config/${b64}`;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export async function getStream(args) {
|
|
19
|
+
const { id, s, e, sdk } = args || {};
|
|
20
|
+
const isTv = s != null && e != null;
|
|
21
|
+
const apiKey = resolveTmdbKey(args, sdk);
|
|
22
|
+
const ua = getUA();
|
|
23
|
+
|
|
24
|
+
const imdbId = await resolveImdbId(id, isTv, apiKey);
|
|
25
|
+
const targetIds = [];
|
|
26
|
+
|
|
27
|
+
if (imdbId && imdbId.startsWith('tt')) {
|
|
28
|
+
targetIds.push(imdbId);
|
|
29
|
+
}
|
|
30
|
+
targetIds.push(`tmdb:${id}`);
|
|
31
|
+
|
|
32
|
+
const addonBase = generateManifestBaseUrl(apiKey);
|
|
33
|
+
const seenUrls = new Set();
|
|
34
|
+
const allUrls = [];
|
|
35
|
+
|
|
36
|
+
const headers = {
|
|
37
|
+
'User-Agent': ua,
|
|
38
|
+
Accept: 'application/json, text/plain, */*',
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
for (const targetId of targetIds) {
|
|
42
|
+
const endpoint = isTv
|
|
43
|
+
? `${addonBase}/stream/series/${targetId}:${s || 1}:${e || 1}.json`
|
|
44
|
+
: `${addonBase}/stream/movie/${targetId}.json`;
|
|
45
|
+
|
|
46
|
+
try {
|
|
47
|
+
const res = await fetch(endpoint, {
|
|
48
|
+
headers,
|
|
49
|
+
signal: AbortSignal.timeout(12000),
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
if (!res.ok) continue;
|
|
53
|
+
const data = await res.json();
|
|
54
|
+
|
|
55
|
+
if (Array.isArray(data?.streams) && data.streams.length > 0) {
|
|
56
|
+
for (const item of data.streams) {
|
|
57
|
+
if (!item || typeof item !== 'object') continue;
|
|
58
|
+
const streamUrl = String(item.url || '').trim();
|
|
59
|
+
if (!streamUrl || !streamUrl.startsWith('http') || seenUrls.has(streamUrl)) continue;
|
|
60
|
+
|
|
61
|
+
seenUrls.add(streamUrl);
|
|
62
|
+
|
|
63
|
+
const rawTitle = item.title ? String(item.title).trim() : '';
|
|
64
|
+
const rawName = item.name ? String(item.name).trim() : '111477';
|
|
65
|
+
const label = rawTitle || rawName;
|
|
66
|
+
|
|
67
|
+
allUrls.push({
|
|
68
|
+
url: streamUrl,
|
|
69
|
+
label,
|
|
70
|
+
server: label,
|
|
71
|
+
quality: 'Auto',
|
|
72
|
+
type: streamUrl.includes('.m3u8') ? 'hls' : 'mp4',
|
|
73
|
+
headers,
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
if (allUrls.length > 0) break;
|
|
78
|
+
}
|
|
79
|
+
} catch { }
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
if (allUrls.length === 0) return null;
|
|
83
|
+
|
|
84
|
+
return {
|
|
85
|
+
url: allUrls[0].url,
|
|
86
|
+
headers: allUrls[0].headers,
|
|
87
|
+
allUrls,
|
|
88
|
+
};
|
|
101
89
|
}
|
package/src/sources/123anime.js
CHANGED
|
@@ -1,57 +1,121 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
const
|
|
10
|
-
if (
|
|
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
|
-
|
|
1
|
+
import { fetchJson, getTmdbMetadata, resolveTmdbKey, USER_AGENT } from '../utils/helpers.js';
|
|
2
|
+
|
|
3
|
+
const BASE_URL = 'https://123animehub.cc';
|
|
4
|
+
const CDN_ORIGIN = 'https://play2.echovideo.ru';
|
|
5
|
+
|
|
6
|
+
async function resolveSlug(id, s, e, titles) {
|
|
7
|
+
const keywords = [];
|
|
8
|
+
for (const title of titles) {
|
|
9
|
+
const t = title.toLowerCase().replace(/[^a-z0-9\s]/g, ' ').replace(/\s+/g, ' ').trim();
|
|
10
|
+
if (!keywords.includes(t)) keywords.push(t);
|
|
11
|
+
const words = title.split(/[\s:]+/).filter(w => w.length > 2);
|
|
12
|
+
if (words.length >= 2) {
|
|
13
|
+
const short = words.slice(0, 3).join(' ').toLowerCase();
|
|
14
|
+
if (!keywords.includes(short)) keywords.push(short);
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const slugCandidates = [];
|
|
19
|
+
for (const keyword of keywords) {
|
|
20
|
+
try {
|
|
21
|
+
const data = await fetchJson(`${BASE_URL}/ajax/film/search?keyword=${encodeURIComponent(keyword)}&_=${Date.now()}`, {
|
|
22
|
+
headers: {
|
|
23
|
+
'X-Requested-With': 'XMLHttpRequest',
|
|
24
|
+
Referer: `${BASE_URL}/`,
|
|
25
|
+
Accept: 'application/json',
|
|
26
|
+
'User-Agent': USER_AGENT,
|
|
27
|
+
},
|
|
28
|
+
signal: AbortSignal.timeout(8000),
|
|
29
|
+
});
|
|
30
|
+
let html = data.html || '';
|
|
31
|
+
let idx = 0;
|
|
32
|
+
while ((idx = html.indexOf('href="/anime/', idx)) !== -1) {
|
|
33
|
+
const start = idx + 13;
|
|
34
|
+
const end = html.indexOf('"', start);
|
|
35
|
+
const slug = html.slice(start, end);
|
|
36
|
+
if (!slugCandidates.includes(slug)) slugCandidates.push(slug);
|
|
37
|
+
idx = end;
|
|
38
|
+
}
|
|
39
|
+
} catch { }
|
|
40
|
+
if (slugCandidates.length) break;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
for (const slug of slugCandidates) {
|
|
44
|
+
try {
|
|
45
|
+
const data = await fetchJson(`${BASE_URL}/ajax/episode/info?epr=${encodeURIComponent(s ? `${slug}/${s}/${e}` : `${slug}/1`)}&ts=1&_=${Date.now()}`, {
|
|
46
|
+
headers: {
|
|
47
|
+
Referer: `${BASE_URL}/anime/${slug}`,
|
|
48
|
+
'X-Requested-With': 'XMLHttpRequest',
|
|
49
|
+
Accept: 'application/json, text/javascript, */*; q=0.01',
|
|
50
|
+
'User-Agent': USER_AGENT,
|
|
51
|
+
},
|
|
52
|
+
signal: AbortSignal.timeout(10000),
|
|
53
|
+
});
|
|
54
|
+
if (data?.target) return data.target;
|
|
55
|
+
} catch { }
|
|
56
|
+
}
|
|
57
|
+
return null;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export async function getStream(args) {
|
|
61
|
+
const { id, s, e, sdk } = args || {};
|
|
62
|
+
const isTv = s != null && e != null;
|
|
63
|
+
if (!s) return null;
|
|
64
|
+
|
|
65
|
+
const apiKey = resolveTmdbKey(args, sdk);
|
|
66
|
+
const meta = await getTmdbMetadata(id, isTv, apiKey);
|
|
67
|
+
if (!meta?.isAnime) return null;
|
|
68
|
+
|
|
69
|
+
const target = await resolveSlug(id, s, e, meta.titles || []);
|
|
70
|
+
if (!target) return null;
|
|
71
|
+
|
|
72
|
+
const embedMatch = target.match(/\/embed-[^/]+\/([A-Za-z0-9+/=]+)$/);
|
|
73
|
+
if (!embedMatch) return null;
|
|
74
|
+
|
|
75
|
+
const origin = new URL(target).origin;
|
|
76
|
+
try {
|
|
77
|
+
const embedRes = await fetch(target, {
|
|
78
|
+
headers: { 'User-Agent': USER_AGENT },
|
|
79
|
+
signal: AbortSignal.timeout(10000),
|
|
80
|
+
});
|
|
81
|
+
if (!embedRes.ok) return null;
|
|
82
|
+
|
|
83
|
+
const setCookie = embedRes.headers.get('set-cookie');
|
|
84
|
+
const data = await fetchJson(`${origin}/hs/getSources?id=${embedMatch[1]}`, {
|
|
85
|
+
headers: {
|
|
86
|
+
Referer: target,
|
|
87
|
+
Accept: '*/*',
|
|
88
|
+
'User-Agent': USER_AGENT,
|
|
89
|
+
...(setCookie ? { Cookie: setCookie.split(';')[0].trim() } : {}),
|
|
90
|
+
},
|
|
91
|
+
signal: AbortSignal.timeout(10000),
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
const streamUrl = typeof data.sources === 'string' && data.sources.length
|
|
95
|
+
? data.sources
|
|
96
|
+
: (Array.isArray(data.sources) && data.sources.length ? (data.sources[0].file || data.sources[0].src || data.sources[0].url) : null);
|
|
97
|
+
|
|
98
|
+
if (!streamUrl) return null;
|
|
99
|
+
|
|
100
|
+
const headers = {
|
|
101
|
+
Referer: `${CDN_ORIGIN}/`,
|
|
102
|
+
Origin: CDN_ORIGIN,
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
const allUrls = [{
|
|
106
|
+
url: streamUrl,
|
|
107
|
+
server: '123Anime',
|
|
108
|
+
quality: 'Auto',
|
|
109
|
+
type: streamUrl.includes('.m3u8') ? 'hls' : 'mp4',
|
|
110
|
+
headers,
|
|
111
|
+
}];
|
|
112
|
+
|
|
113
|
+
return {
|
|
114
|
+
url: streamUrl,
|
|
115
|
+
headers,
|
|
116
|
+
allUrls,
|
|
117
|
+
};
|
|
118
|
+
} catch {
|
|
119
|
+
return null;
|
|
120
|
+
}
|
|
57
121
|
}
|