@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/watchout.js
CHANGED
|
@@ -1,46 +1,60 @@
|
|
|
1
|
-
import { fetchJson, fetchText } from '../utils/helpers.js';
|
|
2
|
-
|
|
3
|
-
const API_URL =
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
if (type === 'movie') {
|
|
19
|
-
const data = await fetchText(targetUrl).catch(() => null);
|
|
20
|
-
if (!data || !data.includes('://')) return null;
|
|
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
|
-
|
|
1
|
+
import { fetchJson, fetchText } from '../utils/helpers.js';
|
|
2
|
+
|
|
3
|
+
const API_URL = 'https://raw.githubusercontent.com/Watchout2025/api/refs/heads/main/hls/';
|
|
4
|
+
const HEADERS = {
|
|
5
|
+
Origin: 'https://vidout.pages.dev',
|
|
6
|
+
Referer: 'https://vidout.pages.dev',
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
export async function getStream(args) {
|
|
10
|
+
const { id, s, e } = args || {};
|
|
11
|
+
const isTv = s != null && e != null;
|
|
12
|
+
const type = isTv ? 'tv' : 'movie';
|
|
13
|
+
|
|
14
|
+
const targetUrl = isTv
|
|
15
|
+
? `${API_URL}tv/${id}/S${s}.json`
|
|
16
|
+
: `${API_URL}movie/${id}`;
|
|
17
|
+
|
|
18
|
+
if (type === 'movie') {
|
|
19
|
+
const data = await fetchText(targetUrl).catch(() => null);
|
|
20
|
+
if (!data || !data.includes('://')) return null;
|
|
21
|
+
|
|
22
|
+
const streamUrl = data.trim();
|
|
23
|
+
const isHls = streamUrl.includes('.m3u8') || streamUrl.includes('.txt') || streamUrl.includes('master') || !streamUrl.includes('.mp4');
|
|
24
|
+
const allUrls = [{
|
|
25
|
+
url: streamUrl,
|
|
26
|
+
server: 'Watchout - Direct',
|
|
27
|
+
quality: 'Auto',
|
|
28
|
+
type: isHls ? 'hls' : 'mp4',
|
|
29
|
+
headers: HEADERS,
|
|
30
|
+
}];
|
|
31
|
+
|
|
32
|
+
return {
|
|
33
|
+
url: streamUrl,
|
|
34
|
+
headers: HEADERS,
|
|
35
|
+
allUrls,
|
|
36
|
+
};
|
|
37
|
+
} else {
|
|
38
|
+
let data = await fetchJson(targetUrl).catch(() => null);
|
|
39
|
+
if (!data) {
|
|
40
|
+
data = await fetchJson(`${API_URL}tv/${id}/s${s}.json`).catch(() => null);
|
|
41
|
+
}
|
|
42
|
+
const streamUrl = data?.[String(e)] || null;
|
|
43
|
+
if (!streamUrl) return null;
|
|
44
|
+
|
|
45
|
+
const isHls = streamUrl.includes('.m3u8') || streamUrl.includes('.txt') || streamUrl.includes('master') || !streamUrl.includes('.mp4');
|
|
46
|
+
const allUrls = [{
|
|
47
|
+
url: streamUrl,
|
|
48
|
+
server: 'Watchout - Direct',
|
|
49
|
+
quality: 'Auto',
|
|
50
|
+
type: isHls ? 'hls' : 'mp4',
|
|
51
|
+
headers: HEADERS,
|
|
52
|
+
}];
|
|
53
|
+
|
|
54
|
+
return {
|
|
55
|
+
url: streamUrl,
|
|
56
|
+
headers: HEADERS,
|
|
57
|
+
allUrls,
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
}
|
|
@@ -1,129 +1,128 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
const
|
|
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
|
-
}
|
|
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
|
-
|
|
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
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
};
|
|
1
|
+
import { getTmdbMetadata, resolveTmdbKey } from '../utils/helpers.js';
|
|
2
|
+
|
|
3
|
+
const BASE_URL = 'https://www.films365.org';
|
|
4
|
+
const HEADERS = {
|
|
5
|
+
Authorization: 'Bearer 79a02956be35835728a044b11e2ae793149d45fb2c89cb6d029ec01aac19bfdb',
|
|
6
|
+
'Content-Type': 'application/json',
|
|
7
|
+
'User-Agent': 'MovieDownloader/1.0',
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
export async function getStream(args) {
|
|
11
|
+
const { id, s, e, sdk } = args || {};
|
|
12
|
+
const isTv = s != null && e != null;
|
|
13
|
+
const targetType = isTv ? 'tv' : 'movie';
|
|
14
|
+
const apiKey = resolveTmdbKey(args, sdk);
|
|
15
|
+
|
|
16
|
+
const meta = await getTmdbMetadata(id, isTv, apiKey);
|
|
17
|
+
const title = meta?.title;
|
|
18
|
+
const query = title || String(id);
|
|
19
|
+
|
|
20
|
+
try {
|
|
21
|
+
const searchRes = await fetch(`${BASE_URL}/api/mobile/search`, {
|
|
22
|
+
method: 'POST',
|
|
23
|
+
headers: HEADERS,
|
|
24
|
+
body: JSON.stringify({ query }),
|
|
25
|
+
signal: AbortSignal.timeout(8000),
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
if (!searchRes.ok) return null;
|
|
29
|
+
const searchJson = await searchRes.json();
|
|
30
|
+
const resultsObj = searchJson?.results;
|
|
31
|
+
if (!resultsObj) return null;
|
|
32
|
+
|
|
33
|
+
const items = resultsObj.all || (targetType === 'tv' ? resultsObj.tvs : resultsObj.movies) || [];
|
|
34
|
+
if (!Array.isArray(items) || items.length === 0) return null;
|
|
35
|
+
|
|
36
|
+
const cleanSearchTitle = query.toLowerCase().replace(/[^a-z0-9]/g, '');
|
|
37
|
+
let matchedItem = null;
|
|
38
|
+
|
|
39
|
+
for (const item of items) {
|
|
40
|
+
const itemType = String(item?.type || '').toLowerCase();
|
|
41
|
+
if (itemType && itemType !== targetType) continue;
|
|
42
|
+
|
|
43
|
+
const itemTitle = String(item?.title || '');
|
|
44
|
+
const cleanItemTitle = itemTitle.toLowerCase().replace(/[^a-z0-9]/g, '');
|
|
45
|
+
|
|
46
|
+
if (cleanItemTitle === cleanSearchTitle || cleanItemTitle.includes(cleanSearchTitle)) {
|
|
47
|
+
matchedItem = item;
|
|
48
|
+
break;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
if (!matchedItem) {
|
|
53
|
+
matchedItem = items.find(i => String(i?.type || '').toLowerCase() === targetType) || items[0];
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const itemId = matchedItem?.id || matchedItem?.tmdbId;
|
|
57
|
+
if (!itemId) return null;
|
|
58
|
+
|
|
59
|
+
const detailsRes = await fetch(`${BASE_URL}/api/mobile/details?id=${itemId}&type=${targetType}`, {
|
|
60
|
+
headers: HEADERS,
|
|
61
|
+
signal: AbortSignal.timeout(8000),
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
if (!detailsRes.ok) return null;
|
|
65
|
+
const detailsJson = await detailsRes.json();
|
|
66
|
+
const data = detailsJson?.data;
|
|
67
|
+
if (!data) return null;
|
|
68
|
+
|
|
69
|
+
const spoken = Array.isArray(data.spokenLanguages)
|
|
70
|
+
? data.spokenLanguages.map(l => String(l).trim()).filter(Boolean)
|
|
71
|
+
: [];
|
|
72
|
+
const langSuffix = spoken.length > 0 ? ` · ${spoken.join(', ')}` : '';
|
|
73
|
+
|
|
74
|
+
const allUrls = [];
|
|
75
|
+
|
|
76
|
+
if (!isTv) {
|
|
77
|
+
const streamUrl = data.downloadUrl || data.videoUrl;
|
|
78
|
+
if (streamUrl && typeof streamUrl === 'string') {
|
|
79
|
+
allUrls.push({
|
|
80
|
+
url: streamUrl.trim(),
|
|
81
|
+
label: `X-Downloader${langSuffix}`,
|
|
82
|
+
server: `X-Downloader${langSuffix}`,
|
|
83
|
+
quality: 'Auto',
|
|
84
|
+
type: streamUrl.includes('.m3u8') ? 'hls' : 'mp4',
|
|
85
|
+
headers: {
|
|
86
|
+
'User-Agent': HEADERS['User-Agent'],
|
|
87
|
+
},
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
} else {
|
|
91
|
+
const seasonNum = Number(s);
|
|
92
|
+
const episodeNum = Number(e);
|
|
93
|
+
const seasons = Array.isArray(data.seasons) ? data.seasons : [];
|
|
94
|
+
const targetSeason = seasons.find(sn => Number(sn.seasonNumber) === seasonNum);
|
|
95
|
+
|
|
96
|
+
if (targetSeason) {
|
|
97
|
+
const episodes = Array.isArray(targetSeason.episodes) ? targetSeason.episodes : [];
|
|
98
|
+
const targetEp = episodes.find(ep => Number(ep.episodeNumber) === episodeNum);
|
|
99
|
+
|
|
100
|
+
if (targetEp) {
|
|
101
|
+
const streamUrl = targetEp.downloadUrl || targetEp.videoUrl;
|
|
102
|
+
if (streamUrl && typeof streamUrl === 'string') {
|
|
103
|
+
allUrls.push({
|
|
104
|
+
url: streamUrl.trim(),
|
|
105
|
+
label: `X-Downloader (S${seasonNum}E${episodeNum})${langSuffix}`,
|
|
106
|
+
server: `X-Downloader (S${seasonNum}E${episodeNum})${langSuffix}`,
|
|
107
|
+
quality: 'Auto',
|
|
108
|
+
type: streamUrl.includes('.m3u8') ? 'hls' : 'mp4',
|
|
109
|
+
headers: {
|
|
110
|
+
'User-Agent': HEADERS['User-Agent'],
|
|
111
|
+
},
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
if (allUrls.length === 0) return null;
|
|
119
|
+
|
|
120
|
+
return {
|
|
121
|
+
url: allUrls[0].url,
|
|
122
|
+
headers: allUrls[0].headers,
|
|
123
|
+
allUrls,
|
|
124
|
+
};
|
|
125
|
+
} catch {
|
|
126
|
+
return null;
|
|
127
|
+
}
|
|
129
128
|
}
|
package/src/sources/yapgrid.js
CHANGED
|
@@ -1,63 +1,69 @@
|
|
|
1
|
-
import { fetchJson } from '../utils/helpers.js';
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
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
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
1
|
+
import { fetchJson } from '../utils/helpers.js';
|
|
2
|
+
|
|
3
|
+
const BASE_URL = 'https://yapgrid.com';
|
|
4
|
+
const EMBED_BASE = 'https://embedwave.cc';
|
|
5
|
+
|
|
6
|
+
export async function getStream(args) {
|
|
7
|
+
const { id, s, e } = args || {};
|
|
8
|
+
const isTv = s != null && e != null;
|
|
9
|
+
|
|
10
|
+
const targetEmbedUrl = isTv
|
|
11
|
+
? `${EMBED_BASE}/embed/tv/${id}/${s}/${e}`
|
|
12
|
+
: `${EMBED_BASE}/embed/movie/${id}`;
|
|
13
|
+
|
|
14
|
+
const headers = {
|
|
15
|
+
Accept: 'application/json',
|
|
16
|
+
'Content-Type': 'application/json',
|
|
17
|
+
Origin: BASE_URL,
|
|
18
|
+
Referer: isTv ? `${BASE_URL}/embed/tv/${id}/${s}/${e}` : `${BASE_URL}/embed/movie/${id}`,
|
|
19
|
+
'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',
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
const payload = {
|
|
23
|
+
url: targetEmbedUrl,
|
|
24
|
+
source: 'embedwave',
|
|
25
|
+
strictSource: true,
|
|
26
|
+
waitMs: 5000,
|
|
27
|
+
nativeHls: false,
|
|
28
|
+
cacheOnly: true,
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
const res = await fetchJson(`${BASE_URL}/player/api/extract`, {
|
|
32
|
+
method: 'POST',
|
|
33
|
+
headers,
|
|
34
|
+
body: JSON.stringify(payload),
|
|
35
|
+
}).catch(() => null);
|
|
36
|
+
|
|
37
|
+
if (!res?.success || !Array.isArray(res.found) || !res.found.length) return null;
|
|
38
|
+
|
|
39
|
+
const allUrls = [];
|
|
40
|
+
|
|
41
|
+
for (const item of res.found) {
|
|
42
|
+
if (!item.url) continue;
|
|
43
|
+
|
|
44
|
+
let streamUrl = item.url;
|
|
45
|
+
if (streamUrl.startsWith('/')) {
|
|
46
|
+
streamUrl = `${BASE_URL}${streamUrl}`;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
allUrls.push({
|
|
50
|
+
url: streamUrl,
|
|
51
|
+
server: `YapGrid - ${item.nxshaProvider || item.provider || 'Server'}`,
|
|
52
|
+
quality: 'Auto',
|
|
53
|
+
type: item.type === 'master' ? 'hls' : (streamUrl.includes('.m3u8') ? 'hls' : 'mp4'),
|
|
54
|
+
headers: {
|
|
55
|
+
Referer: `${BASE_URL}/`,
|
|
56
|
+
Origin: BASE_URL,
|
|
57
|
+
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36',
|
|
58
|
+
},
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
if (!allUrls.length) return null;
|
|
63
|
+
|
|
64
|
+
return {
|
|
65
|
+
url: allUrls[0].url,
|
|
66
|
+
headers: allUrls[0].headers,
|
|
67
|
+
allUrls,
|
|
68
|
+
};
|
|
63
69
|
}
|
package/src/sources/yastream.js
CHANGED
|
@@ -1,30 +1,36 @@
|
|
|
1
|
-
import { fetchJson } from '../utils/helpers.js';
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
const
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
const
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
1
|
+
import { fetchJson } from '../utils/helpers.js';
|
|
2
|
+
|
|
3
|
+
const BASE_URL = 'https://yastream.tamthai.de';
|
|
4
|
+
|
|
5
|
+
export async function getStream(args) {
|
|
6
|
+
const { id, s, e } = args || {};
|
|
7
|
+
const isTv = s != null && e != null;
|
|
8
|
+
|
|
9
|
+
const path = isTv
|
|
10
|
+
? `/stream/series/tmdb:${id}:${s}:${e}.json`
|
|
11
|
+
: `/stream/movie/tmdb:${id}.json`;
|
|
12
|
+
|
|
13
|
+
const url = `${BASE_URL}${path}`;
|
|
14
|
+
const res = await fetchJson(url).catch(() => null);
|
|
15
|
+
if (!res || !Array.isArray(res.streams) || !res.streams.length) return null;
|
|
16
|
+
|
|
17
|
+
const allUrls = res.streams.map(item => {
|
|
18
|
+
if (!item.url) return null;
|
|
19
|
+
return {
|
|
20
|
+
url: item.url,
|
|
21
|
+
server: `YaStream - ${item.name?.replace(/\n/g, ' ') || 'Server'}`,
|
|
22
|
+
quality: 'Auto',
|
|
23
|
+
type: item.url.includes('.m3u8') ? 'hls' : 'mp4',
|
|
24
|
+
headers: item.behaviorHints?.proxyHeaders?.request || undefined,
|
|
25
|
+
skipProxy: item.behaviorHints?.notWebReady === false,
|
|
26
|
+
};
|
|
27
|
+
}).filter(Boolean);
|
|
28
|
+
|
|
29
|
+
if (!allUrls.length) return null;
|
|
30
|
+
|
|
31
|
+
return {
|
|
32
|
+
url: allUrls[0].url,
|
|
33
|
+
headers: allUrls[0].headers,
|
|
34
|
+
allUrls,
|
|
35
|
+
};
|
|
30
36
|
}
|