better-ani-scraped 1.5.2 → 1.6.1
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/DOCUMENTATION.md
CHANGED
|
@@ -30,7 +30,7 @@ const crunchyroll = new AnimeScraper('crunchyroll') //for Crunchyroll
|
|
|
30
30
|
- [searchAnime](#animesamasearchanimequery-limit--10-wantedlanguages--vostfr-vf-vastfr-wantedtypes--anime-film-page--null)
|
|
31
31
|
- [getSeasons](#animesamagetseasonsanimeurl-language--vostfr)
|
|
32
32
|
- [getEpisodeTitles](#animesamagetepisodetitlesseasonurl-customchromiumpath)
|
|
33
|
-
- [getEmbed](#animesamagetembedseasonurl-hostpriority--sibnet-vidmoly)
|
|
33
|
+
- [getEmbed](#animesamagetembedseasonurl-hostpriority--sendvid-sibnet-vidmoly-oneupload)
|
|
34
34
|
- [getAnimeInfo](#animesamagetanimeinfoanimeurl)
|
|
35
35
|
- [getAvailableLanguages](#animesamagetavailablelanguagesseasonurl-wantedlanguages--vostfr-vf-va-vkr-vcn-vqc-vf1-vf2-numberepisodes--false)
|
|
36
36
|
- [getAllAnime](#animesamagetallanimewantedlanguages--vostfr-vf-vastfr-wantedtypes--anime-film-page--null-output--anime_listjson-get_seasons--false)
|
|
@@ -95,7 +95,7 @@ Fetches the names of all episodes in a season
|
|
|
95
95
|
|
|
96
96
|
---
|
|
97
97
|
|
|
98
|
-
### `animesama.getEmbed(seasonUrl, hostPriority = ["sibnet", "vidmoly"])`
|
|
98
|
+
### `animesama.getEmbed(seasonUrl, hostPriority = ["sendvid", "sibnet", "vidmoly", "oneupload"])`
|
|
99
99
|
Retrieves embed URLs for episodes, prioritizing by host.
|
|
100
100
|
|
|
101
101
|
- **Parameters:**
|
|
@@ -108,6 +108,7 @@ Retrieves embed URLs for episodes, prioritizing by host.
|
|
|
108
108
|
{
|
|
109
109
|
title: string,
|
|
110
110
|
url: string,
|
|
111
|
+
host: string,
|
|
111
112
|
}
|
|
112
113
|
...
|
|
113
114
|
]
|
|
@@ -316,10 +317,14 @@ Extracts information from all episodes of a season of an anime.
|
|
|
316
317
|
Retrieves the video URL of the source's embed.
|
|
317
318
|
|
|
318
319
|
- **Parameters:**
|
|
319
|
-
- `source` *(string)*: The embed source (only "sibnet" available at the moment)
|
|
320
|
+
- `source` *(string)*: The embed source (only "sibnet", "sendvid", "vidmoly" and "oneupload" available at the moment)
|
|
320
321
|
- `embedUrl` *(string)*: The embed url of the given source.
|
|
321
322
|
- **Returns:**
|
|
322
|
-
A video URL as a string
|
|
323
|
+
A video URL as a string :
|
|
324
|
+
- `sibnet`: mp4
|
|
325
|
+
- `sendvid`: mp4
|
|
326
|
+
- `vidmoly`: m3u8
|
|
327
|
+
- `oneupload`: m3u8
|
|
323
328
|
|
|
324
329
|
---
|
|
325
330
|
---
|
|
@@ -1,11 +1,22 @@
|
|
|
1
1
|
import { getVideoUrlFromEmbed } from "../../index.js"; // REPLACE BY "from 'better-ani-scraped';"
|
|
2
2
|
|
|
3
3
|
const main = async () => {
|
|
4
|
-
const
|
|
5
|
-
|
|
6
|
-
const
|
|
7
|
-
|
|
4
|
+
const embedUrlSibnet = "https://video.sibnet.ru/shell.php?videoid=4291083";
|
|
5
|
+
const embedUrlSendvid = "https://sendvid.com/embed/4vzpcb0q";
|
|
6
|
+
const embedUrlVidmoly = "https://vidmoly.to/embed-vt374ef2joph.html";
|
|
7
|
+
const embedUrlOneupload = "https://oneupload.net/embed-axdrxh1y3p37.html";
|
|
8
|
+
|
|
9
|
+
const videoUrlSibnet = await getVideoUrlFromEmbed("sibnet", embedUrlSibnet)
|
|
10
|
+
console.log("Video URL Sibnet:", videoUrlSibnet);
|
|
8
11
|
|
|
12
|
+
const videoUrlSendvid = await getVideoUrlFromEmbed("sendvid", embedUrlSendvid)
|
|
13
|
+
console.log("Video URL Sendvid:", videoUrlSendvid);
|
|
14
|
+
|
|
15
|
+
const videoUrlVidmoly = await getVideoUrlFromEmbed("vidmoly", embedUrlVidmoly)
|
|
16
|
+
console.log("Video URL Vidmoly:", videoUrlVidmoly);
|
|
17
|
+
|
|
18
|
+
const videoUrlOneupload = await getVideoUrlFromEmbed("oneupload", embedUrlOneupload)
|
|
19
|
+
console.log("Video URL Oneupload:", videoUrlOneupload);
|
|
9
20
|
};
|
|
10
21
|
|
|
11
22
|
main().catch(console.error);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "better-ani-scraped",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.6.1",
|
|
4
4
|
"description": "Scrape anime data from different sources (only anime-sama.fr for the moment)",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"scripts": {
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
"axios": "^1.8.4",
|
|
22
22
|
"cheerio": "^1.0.0",
|
|
23
23
|
"playwright": "^1.52.0",
|
|
24
|
-
"puppeteer": "^24.
|
|
24
|
+
"puppeteer": "^24.8.1",
|
|
25
25
|
"puppeteer-extra": "^3.3.6",
|
|
26
26
|
"puppeteer-extra-plugin-stealth": "^2.11.2"
|
|
27
27
|
},
|
package/scrapers/animesama.js
CHANGED
|
@@ -215,7 +215,7 @@ export async function getEpisodeTitles(seasonUrl, customChromiumPath) {
|
|
|
215
215
|
}
|
|
216
216
|
}
|
|
217
217
|
|
|
218
|
-
export async function getEmbed(seasonUrl, hostPriority = ["sibnet", "vidmoly"], customChromiumPath) {
|
|
218
|
+
export async function getEmbed(seasonUrl, hostPriority = ["sendvid", "sibnet", "vidmoly", "oneupload"], customChromiumPath) {
|
|
219
219
|
const res = await axios.get(seasonUrl, {
|
|
220
220
|
headers: getHeaders(seasonUrl.split("/").slice(0, 5).join("/")),
|
|
221
221
|
});
|
|
@@ -249,25 +249,35 @@ export async function getEmbed(seasonUrl, hostPriority = ["sibnet", "vidmoly"],
|
|
|
249
249
|
|
|
250
250
|
const maxEpisodes = Math.max(...episodeMatrix.map(arr => arr.length));
|
|
251
251
|
const finalEmbeds = [];
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
252
|
+
for (let i = 0; i < maxEpisodes; i++) {
|
|
253
|
+
let selectedUrl = null;
|
|
254
|
+
let selectedHost = null;
|
|
255
|
+
|
|
256
|
+
for (const host of hostPriority) {
|
|
257
|
+
for (const arr of episodeMatrix) {
|
|
258
|
+
if (i < arr.length && arr[i].includes(host)) {
|
|
259
|
+
selectedUrl = arr[i];
|
|
260
|
+
selectedHost = host;
|
|
261
|
+
break;
|
|
260
262
|
}
|
|
261
|
-
if (selectedUrl) break;
|
|
262
263
|
}
|
|
263
|
-
|
|
264
|
+
if (selectedUrl) break;
|
|
264
265
|
}
|
|
265
266
|
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
267
|
+
finalEmbeds.push({
|
|
268
|
+
url: selectedUrl || null,
|
|
269
|
+
host: selectedHost || null
|
|
270
|
+
});
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
const titles = await getEpisodeTitles(seasonUrl, customChromiumPath);
|
|
274
|
+
|
|
275
|
+
return finalEmbeds.map((embed, i) => ({
|
|
276
|
+
title: titles[i] || null,
|
|
277
|
+
url: embed.url,
|
|
278
|
+
host: embed.host
|
|
279
|
+
}));
|
|
280
|
+
|
|
271
281
|
}
|
|
272
282
|
|
|
273
283
|
export async function getAnimeInfo(animeUrl) {
|
package/utils/dispatcher.js
CHANGED
|
@@ -4,6 +4,12 @@ export async function getVideoUrlFromEmbed(source, embedUrl) {
|
|
|
4
4
|
if (source === "sibnet") {
|
|
5
5
|
return await extractor.getSibnetVideo(embedUrl);
|
|
6
6
|
}
|
|
7
|
+
if (source === "sendvid") {
|
|
8
|
+
return await extractor.getSendvidVideo(embedUrl);
|
|
9
|
+
}
|
|
10
|
+
if (source === "vidmoly" || source === "oneupload" ) {
|
|
11
|
+
return await extractor.getVidmolyOrOneuploadVideo(embedUrl);
|
|
12
|
+
}
|
|
7
13
|
|
|
8
14
|
throw new Error(`Unsupported embed source: ${source}`);
|
|
9
15
|
}
|
package/utils/extractVideoUrl.js
CHANGED
|
@@ -1,67 +1,74 @@
|
|
|
1
1
|
import axios from "axios";
|
|
2
2
|
import * as cheerio from "cheerio";
|
|
3
3
|
|
|
4
|
-
export async function getSibnetVideo(embedUrl) {
|
|
5
|
-
let intermediaries = [];
|
|
6
|
-
let realUrl = "";
|
|
7
|
-
|
|
8
|
-
const getIntermediary = async () => {
|
|
9
|
-
try {
|
|
10
|
-
const { data } = await axios.get(embedUrl, { headers: getHeaders(embedUrl) });
|
|
11
|
-
const $ = cheerio.load(data);
|
|
12
|
-
const script = $("script")
|
|
13
|
-
.toArray()
|
|
14
|
-
.map((s) => $(s).html())
|
|
15
|
-
.find((s) => s.includes("player.src"));
|
|
16
|
-
const match = script?.match(/player\.src\(\[{src:\s*["']([^"']+)["']/);
|
|
17
|
-
if (match) intermediaries.push(`https://video.sibnet.ru${match[1]}`);
|
|
18
|
-
return !!match;
|
|
19
|
-
} catch {
|
|
20
|
-
return false;
|
|
21
|
-
}
|
|
22
|
-
};
|
|
23
4
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
5
|
+
const getHeaders = (referer) => ({
|
|
6
|
+
Accept: "*/*",
|
|
7
|
+
Referer: referer,
|
|
8
|
+
"User-Agent":
|
|
9
|
+
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36",
|
|
10
|
+
});
|
|
11
|
+
export async function getSibnetVideo(embedUrl) {
|
|
12
|
+
try {
|
|
13
|
+
const { data } = await axios.get(embedUrl, {
|
|
14
|
+
headers: getHeaders(embedUrl),
|
|
15
|
+
});
|
|
16
|
+
const $ = cheerio.load(data);
|
|
17
|
+
const script = $("script")
|
|
18
|
+
.toArray()
|
|
19
|
+
.map((s) => $(s).html())
|
|
20
|
+
.find((s) => s.includes("player.src"));
|
|
21
|
+
const match = script?.match(/player\.src\(\[{src:\s*["']([^"']+)["']/);
|
|
22
|
+
if (!match || !match[1]) return null;
|
|
23
|
+
const intermediateUrl = `https://video.sibnet.ru${match[1]}`;
|
|
24
|
+
const res1 = await axios.get(intermediateUrl, {
|
|
25
|
+
headers: getHeaders(embedUrl),
|
|
26
|
+
maxRedirects: 0,
|
|
27
|
+
validateStatus: (s) => s >= 200 && s < 400,
|
|
28
|
+
});
|
|
29
|
+
const redirectUrl = res1.headers.location;
|
|
30
|
+
if (!redirectUrl) return null;
|
|
31
|
+
const finalUrl = redirectUrl.startsWith("http")
|
|
32
|
+
? redirectUrl
|
|
33
|
+
: `https:${redirectUrl}`;
|
|
34
|
+
return finalUrl;
|
|
35
|
+
} catch (err) {
|
|
36
|
+
console.error("Erreur getSibnetVideo:", err.message);
|
|
37
|
+
return null;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
32
40
|
|
|
33
|
-
|
|
34
|
-
|
|
41
|
+
export async function getSendvidVideo(embedUrl) {
|
|
42
|
+
try {
|
|
43
|
+
const { data } = await axios.get(embedUrl, {
|
|
44
|
+
headers: getHeaders(embedUrl),
|
|
45
|
+
});
|
|
46
|
+
const $ = cheerio.load(data);
|
|
47
|
+
const sourceTag = $("video source[type='video/mp4']").attr("src");
|
|
48
|
+
return sourceTag || null;
|
|
49
|
+
} catch (err) {
|
|
50
|
+
return null;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
35
53
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
54
|
+
export async function getVidmolyOrOneuploadVideo(embedUrl) {
|
|
55
|
+
try {
|
|
56
|
+
const { data } = await axios.get(embedUrl, {
|
|
57
|
+
headers: getHeaders(embedUrl),
|
|
58
|
+
});
|
|
59
|
+
const $ = cheerio.load(data);
|
|
60
|
+
const scripts = $("script");
|
|
41
61
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
return !!realUrl;
|
|
49
|
-
} catch {
|
|
50
|
-
return false;
|
|
62
|
+
for (let i = 0; i < scripts.length; i++) {
|
|
63
|
+
const content = $(scripts[i]).html();
|
|
64
|
+
const match = content && content.match(/file\s*:\s*"(https[^"]+\.m3u8[^"]*)"/);
|
|
65
|
+
if (match && match[1]) {
|
|
66
|
+
return match[1];
|
|
67
|
+
}
|
|
51
68
|
}
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
Accept: "*/*",
|
|
58
|
-
Referer: referer,
|
|
59
|
-
Range: "bytes=0-",
|
|
60
|
-
"User-Agent":
|
|
61
|
-
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36",
|
|
62
|
-
});
|
|
63
|
-
|
|
64
|
-
return (await getIntermediary()) && (await followRedirection())
|
|
65
|
-
? realUrl
|
|
66
|
-
: null;
|
|
69
|
+
return null;
|
|
70
|
+
} catch (err) {
|
|
71
|
+
console.error("Erreur getVidmolyVideo:", err.message);
|
|
72
|
+
return null;
|
|
73
|
+
}
|
|
67
74
|
}
|