@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.
Files changed (58) hide show
  1. package/package.json +3 -2
  2. package/src/config.js +99 -67
  3. package/src/health.js +60 -20
  4. package/src/sdk.d.ts +94 -121
  5. package/src/sdk.js +82 -78
  6. package/src/sources/111477.js +88 -100
  7. package/src/sources/123anime.js +120 -56
  8. package/src/sources/3eyedraven.js +82 -135
  9. package/src/sources/4movies.js +77 -0
  10. package/src/sources/aether.js +99 -0
  11. package/src/sources/castle.js +415 -0
  12. package/src/sources/cinecat.js +37 -18
  13. package/src/sources/cinemaarmy.js +179 -180
  14. package/src/sources/cinesrc.js +401 -397
  15. package/src/sources/cno.js +101 -94
  16. package/src/sources/dulo.js +134 -149
  17. package/src/sources/flaxmovies.js +114 -105
  18. package/src/sources/framextv.js +126 -0
  19. package/src/sources/frost.js +38 -29
  20. package/src/sources/fsonline.js +95 -47
  21. package/src/sources/hdhub.js +43 -36
  22. package/src/sources/hexa.js +60 -53
  23. package/src/sources/hyper.js +42 -35
  24. package/src/sources/kickassanime.js +117 -124
  25. package/src/sources/kisskh.js +114 -59
  26. package/src/sources/lmscript.js +72 -70
  27. package/src/sources/lookmovie.js +80 -79
  28. package/src/sources/meowtv.js +140 -0
  29. package/src/sources/movenerds.js +74 -124
  30. package/src/sources/movy.js +197 -209
  31. package/src/sources/net79.js +59 -55
  32. package/src/sources/peestream.js +126 -138
  33. package/src/sources/pengu.js +46 -49
  34. package/src/sources/pstream.js +73 -67
  35. package/src/sources/purstream.js +96 -104
  36. package/src/sources/rivestream.js +182 -176
  37. package/src/sources/rivo.js +58 -53
  38. package/src/sources/streamaggregator.js +114 -128
  39. package/src/sources/thugtv.js +70 -62
  40. package/src/sources/vidapi.js +122 -114
  41. package/src/sources/vidcore.js +14 -122
  42. package/src/sources/vidfast.js +14 -122
  43. package/src/sources/vidlink.js +78 -27
  44. package/src/sources/vidora.js +74 -42
  45. package/src/sources/vidup.js +14 -122
  46. package/src/sources/vidvault.js +133 -187
  47. package/src/sources/vidy.js +192 -187
  48. package/src/sources/vidzee.js +189 -183
  49. package/src/sources/vixsrc.js +123 -113
  50. package/src/sources/watchout.js +60 -46
  51. package/src/sources/xdownloader.js +127 -128
  52. package/src/sources/yapgrid.js +68 -62
  53. package/src/sources/yastream.js +35 -29
  54. package/src/subtitles.js +105 -122
  55. package/src/utils/helpers.js +586 -247
  56. package/src/sources/bcine.js +0 -202
  57. package/src/sources/cinezo.js +0 -55
  58. 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
- return await mod.getStream(streamArgs);
59
- }
60
- async getSubtitles(id, s = null, e = null) {
61
- const paths = s != null && e != null
62
- ? await getSubPathsTv(id, s, e)
63
- : await getSubPathsMovie(id);
64
- return await fetchSubtitles(paths);
65
- }
66
- async getDownloads(id, s = null, e = null) {
67
- return await fetchDownloads(this, id, s, e);
68
- }
69
- async searchManga(query, page = 1, perPage = 12) {
70
- return await searchManga(query, page, perPage);
71
- }
72
- async getManga(id) {
73
- return await getMangaDetails(id);
74
- }
75
- async getMangaChapter(mangaId, chapterId) {
76
- return await getChapterPages(mangaId, chapterId);
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
+ }
@@ -1,101 +1,89 @@
1
- import { getUA } from '../utils/helpers.js';
2
-
3
- const SERVICE_ORIGIN = 'https://st.111477.xyz';
4
-
5
- function generateManifestBaseUrl(tmdbKey) {
6
- let config = 'https://a.111477.xyz/::sort=file-desc::limit=3';
7
- if (tmdbKey) {
8
- config += `::tmdb=${tmdbKey}`;
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 resolveImdbId(sdk, id, isTv) {
19
- const apiKey = sdk?.tmdbApiKey || process.env.TMDB_API_KEY;
20
- if (!apiKey) return null;
21
- const type = isTv ? 'tv' : 'movie';
22
- try {
23
- const res = await fetch(`https://api.themoviedb.org/3/${type}/${id}?api_key=${apiKey}&append_to_response=external_ids`, {
24
- signal: AbortSignal.timeout(6000),
25
- });
26
- if (!res.ok) return null;
27
- const data = await res.json();
28
- return data.imdb_id || data.external_ids?.imdb_id || null;
29
- } catch {
30
- return null;
31
- }
32
- }
33
-
34
- export async function getStream({ id, s, e, sdk }) {
35
- const isTv = Boolean(s && e);
36
- const ua = getUA();
37
- const apiKey = sdk?.tmdbApiKey || process.env.TMDB_API_KEY;
38
-
39
- const imdbId = await resolveImdbId(sdk, id, isTv);
40
- const targetIds = [];
41
-
42
- if (imdbId && imdbId.startsWith('tt')) {
43
- targetIds.push(imdbId);
44
- }
45
- targetIds.push(`tmdb:${id}`);
46
-
47
- const addonBase = generateManifestBaseUrl(apiKey);
48
- const seenUrls = new Set();
49
- const allUrls = [];
50
-
51
- const defaultHeaders = {
52
- 'User-Agent': ua,
53
- Accept: 'application/json, text/plain, */*',
54
- };
55
-
56
- for (const targetId of targetIds) {
57
- const endpoint = isTv
58
- ? `${addonBase}/stream/series/${targetId}:${s || 1}:${e || 1}.json`
59
- : `${addonBase}/stream/movie/${targetId}.json`;
60
-
61
- try {
62
- const res = await fetch(endpoint, {
63
- headers: defaultHeaders,
64
- signal: AbortSignal.timeout(12000),
65
- });
66
-
67
- if (!res.ok) continue;
68
- const data = await res.json();
69
-
70
- if (Array.isArray(data?.streams) && data.streams.length > 0) {
71
- for (const item of data.streams) {
72
- if (!item || typeof item !== 'object') continue;
73
- const url = String(item.url || '').trim();
74
- if (!url || !url.startsWith('http') || seenUrls.has(url)) continue;
75
-
76
- seenUrls.add(url);
77
-
78
- const rawTitle = item.title ? String(item.title).trim() : '';
79
- const rawName = item.name ? String(item.name).trim() : '111477';
80
- const label = rawTitle || rawName;
81
-
82
- allUrls.push({
83
- url,
84
- label,
85
- headers: defaultHeaders,
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
  }
@@ -1,57 +1,121 @@
1
- import { getTmdbInfo, fetchJson, USER_AGENT } from '../utils/helpers.js';
2
-
3
- async function resolveSlug(id, s, e, info) {
4
- const titles = info?.titles || [];
5
- const keywords = [];
6
- for (const title of titles) {
7
- const t = title.toLowerCase().replace(/[^a-z0-9\s]/g, ' ').replace(/\s+/g, ' ').trim();
8
- if (!keywords.includes(t)) keywords.push(t);
9
- const words = title.split(/[\s:]+/).filter(w => w.length > 2);
10
- if (words.length >= 2) {
11
- const short = words.slice(0, 3).join(' ').toLowerCase();
12
- if (!keywords.includes(short)) keywords.push(short);
13
- }
14
- }
15
- const slugCandidates = [];
16
- for (const keyword of keywords) {
17
- try {
18
- const data = await fetchJson(`https://123animehub.cc/ajax/film/search?keyword=${encodeURIComponent(keyword)}&_=${Date.now()}`, { headers: { 'X-Requested-With': 'XMLHttpRequest', 'Referer': 'https://123animehub.cc', 'Accept': 'application/json', 'User-Agent': USER_AGENT }, signal: AbortSignal.timeout(8000) });
19
- let html = data.html || '', idx = 0;
20
- while ((idx = html.indexOf('href="/anime/', idx)) !== -1) {
21
- const start = idx + 13;
22
- const end = html.indexOf('"', start);
23
- const slug = html.slice(start, end);
24
- if (!slugCandidates.includes(slug)) slugCandidates.push(slug);
25
- idx = end;
26
- }
27
- } catch { }
28
- if (slugCandidates.length) break;
29
- }
30
- for (const slug of slugCandidates) {
31
- try {
32
- const data = await fetchJson(`https://123animehub.cc/ajax/episode/info?epr=${encodeURIComponent(s ? `${slug}/${s}/${e}` : `${slug}/1`)}&ts=1&_=${Date.now()}`, { headers: { 'Referer': `https://123animehub.cc/anime/${slug}`, 'X-Requested-With': 'XMLHttpRequest', 'Accept': 'application/json, text/javascript, */*; q=0.01', 'User-Agent': USER_AGENT }, signal: AbortSignal.timeout(10000) });
33
- if (data?.target) return data.target;
34
- } catch { }
35
- }
36
- return null;
37
- }
38
-
39
- export async function getStream({ id, s, e }) {
40
- if (!s) return null;
41
- const info = await getTmdbInfo(id, 'tv', s);
42
- if (!info.isAnime) return null;
43
- const target = await resolveSlug(id, s, e, info);
44
- if (!target) return null;
45
- const embedMatch = target.match(/\/embed-[^/]+\/([A-Za-z0-9+/=]+)$/);
46
- if (!embedMatch) return null;
47
- const origin = new URL(target).origin;
48
- try {
49
- const embedRes = await fetch(target, { headers: { 'User-Agent': USER_AGENT }, signal: AbortSignal.timeout(10000) });
50
- if (!embedRes.ok) return null;
51
- const setCookie = embedRes.headers.get('set-cookie');
52
- const data = await fetchJson(`${origin}/hs/getSources?id=${embedMatch[1]}`, { headers: { 'Referer': target, 'Accept': '*/*', 'User-Agent': USER_AGENT, ...(setCookie ? { 'Cookie': setCookie.split(';')[0].trim() } : {}) }, signal: AbortSignal.timeout(10000) });
53
- const src = typeof data.sources === 'string' && data.sources.length ? data.sources : (Array.isArray(data.sources) && data.sources.length ? (data.sources[0].file || data.sources[0].src || data.sources[0].url) : null);
54
- if (src) return { url: src, headers: { 'Referer': 'https://play2.echovideo.ru/', 'Origin': 'https://play2.echovideo.ru' } };
55
- } catch { }
56
- return null;
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
  }