better-ani-scraped 1.6.11 → 1.6.82

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
@@ -36,9 +36,6 @@ const crunchyroll = new AnimeScraper('crunchyroll') //for Crunchyroll
36
36
  - [getAllAnime](#animesamagetallanimewantedlanguages--vostfr-vf-vastfr-wantedtypes--anime-film-page--null-output--anime_listjson-get_seasons--false)
37
37
  - [getLatestEpisodes](#animesamagetlatestepisodeslanguagefilter--null)
38
38
  - [getRandomAnime](#animesamagetrandomanimewantedlanguages--vostfr-vf-vastfr-wantedtypes--anime-film-maxattempts--null-attempt--0)
39
- - [getAllTitleScans](#animesamagetalltitlescansmangaurl-numberimg--false)
40
- - [getImgScans](#animesamagetimgscansmangaurl-wantedchapter)
41
-
42
39
 
43
40
  ### `animesama.searchAnime(query, limit = 10, wantedLanguages = ["vostfr", "vf", "vastfr"], wantedTypes = ["Anime", "Film"], page = null)`
44
41
  Searches for anime titles that match the given query.
@@ -257,40 +254,6 @@ Fetches a random anime from the catalogue.
257
254
 
258
255
  ---
259
256
 
260
- ### `animesama.getAllTitleScans(mangaUrl, numberImg = false)`
261
- Scrapes all the scans of a chapter.
262
-
263
- - **Parameters:**
264
- - `mangaUrl` *(string)*: The manga URL.
265
- - `numberImg` *(boolean)*: If `true`, indicates the number of images in each chapter.
266
- - **Returns:**
267
- An array of chapter titles if *numberImg = false*
268
- Else :
269
- ```js
270
- {
271
- scans :
272
- [
273
- {
274
- title: string,
275
- url: string,
276
- },
277
- ...
278
- ]
279
- mangaTitle: string,
280
- }
281
-
282
- ---
283
-
284
- ### `animesama.getImgScans(mangaUrl, wantedChapter)`
285
- Scrapes all the scans of a chapter.
286
-
287
- - **Parameters:**
288
- - `mangaUrl` *(string)*: The manga URL.
289
- - `wantedChapter` *(int)*: The number of the chapter you want.
290
- - **Returns:**
291
- An array of image URL.
292
-
293
- ---
294
257
 
295
258
  ## `AnimeScraper("animepahe")` methods
296
259
 
@@ -3,7 +3,7 @@ import { AnimeScraper } from "../../index.js"; // REPLACE BY "from 'better-ani-s
3
3
  const main = async () => {
4
4
  const animesama = new AnimeScraper('animesama');
5
5
 
6
- const catalogue = await animesama.getAllAnime(["vostfr", "vf", "vastfr"], ["Anime", "Film", "Scans"], 1);
6
+ const catalogue = await animesama.getAllAnime(["vostfr", "vf", "vastfr"], ["Anime", "Film"], 2);
7
7
  console.log(catalogue)
8
8
  };
9
9
 
@@ -2,9 +2,9 @@ import { AnimeScraper } from "../../index.js"; // REPLACE BY "from 'better-ani-s
2
2
 
3
3
  const main = async () => {
4
4
  const animesama = new AnimeScraper('animesama');
5
- const seasonUrl = "https://anime-sama.fr/catalogue/one-piece/saison11/vostfr";
5
+ const seasonUrl = "https://anime-sama.fr/catalogue/solo-leveling/saison1/vostfr/";
6
6
 
7
- const embeds = await animesama.getEmbed(seasonUrl, ["smoothpre", "movearnpre", "sibnet", "vidmoly", "sendvid"], true, true);
7
+ const embeds = await animesama.getEmbed(seasonUrl, ["sibnet", "vidmoly", "sendvid"], true, true);
8
8
 
9
9
  console.log("Embed Links:", JSON.stringify(embeds, null, 2));
10
10
 
@@ -2,9 +2,9 @@ import { AnimeScraper } from "../../index.js"; // REPLACE BY "from 'better-ani-s
2
2
 
3
3
  const main = async () => {
4
4
  const animesama = new AnimeScraper('animesama');
5
- const animeUrl = "https://anime-sama.fr/catalogue/one-piece";
5
+ const animeUrl = "https://anime-sama.fr/catalogue/sword-art-online";
6
6
 
7
- const seasons = await animesama.getSeasons(animeUrl, ["vostfr", "vf"], ["Anime", "Scans"]);
7
+ const seasons = await animesama.getSeasons(animeUrl, ["vostfr", "vf"]);
8
8
  console.log("Seasons:", seasons);
9
9
  };
10
10
 
@@ -5,8 +5,6 @@ const main = async () => {
5
5
  const embedUrlSendvid = "https://sendvid.com/embed/4vzpcb0q";
6
6
  const embedUrlVidmoly = "https://vidmoly.to/embed-rvqrwg5zk37w.html";
7
7
  const embedUrlOneupload = "https://oneupload.net/embed-axdrxh1y3p37.html";
8
- const embedUrlSmoothpre = "https://smoothpre.com/embed/8294jcf1q8jf";
9
- const embedUrlMovearnpre = "https://movearnpre.com/embed/e3xbkin87yt3";
10
8
 
11
9
  const videoUrlSibnet = await getVideoUrlFromEmbed("sibnet", embedUrlSibnet)
12
10
  console.log("Video URL Sibnet:", videoUrlSibnet);
@@ -19,13 +17,6 @@ const main = async () => {
19
17
 
20
18
  const videoUrlOneupload = await getVideoUrlFromEmbed("oneupload", embedUrlOneupload)
21
19
  console.log("Video URL Oneupload:", videoUrlOneupload);
22
-
23
- const videoUrlSmoothpre = await getVideoUrlFromEmbed("smoothpre", embedUrlSmoothpre)
24
- console.log("Video URL Smoothpre:", videoUrlSmoothpre);
25
-
26
- const videoUrlMovearnpre = await getVideoUrlFromEmbed("movearnpre", embedUrlMovearnpre)
27
- console.log("Video URL Movearnpre:", videoUrlMovearnpre);
28
-
29
20
  };
30
21
 
31
22
  main().catch(console.error);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "better-ani-scraped",
3
- "version": "1.6.11",
3
+ "version": "1.6.82",
4
4
  "description": "Scrape anime data from different sources (only anime-sama.fr, animepahe and crunchyroll for the moment)",
5
5
  "main": "index.js",
6
6
  "scripts": {
@@ -4,7 +4,6 @@ import fs from "fs";
4
4
  import path from 'path';
5
5
  import { exec as execCallback } from 'child_process';
6
6
  import { promisify } from 'util';
7
- import { title } from "process";
8
7
  const execAsync = promisify(execCallback);
9
8
 
10
9
 
@@ -125,26 +124,15 @@ export async function searchAnime(
125
124
  return results;
126
125
  }
127
126
 
128
- export async function getSeasons(animeUrl, languagePriority = ["vostfr", "vf", "va", "vkr", "vcn", "vqc", "vf1", "vf2"], wantedTypes=["Anime", "Kai", "Scans"]) {
127
+ export async function getSeasons(animeUrl, languagePriority = ["vostfr", "vf", "va", "vkr", "vcn", "vqc", "vf1", "vf2"]) {
129
128
  const res = await axios.get(animeUrl, { headers: getHeaders(CATALOGUE_URL) });
130
129
  const html = res.data;
131
- let mainAnimeOnly = html;
132
- if (wantedTypes.length !== 0) {
133
- if (!wantedTypes.includes("Anime")) {
134
- mainAnimeOnly = mainAnimeOnly.replace(/<h2.*?>Anime<\/h2>[\s\S]*?(?=<h2|$)/g, '');
135
- }
136
- if (!wantedTypes.includes("Kai")) {
137
- mainAnimeOnly = mainAnimeOnly.replace(/<h2.*?>Anime Version Kai<\/h2>[\s\S]*?(?=<h2|$)/g, '');
138
- }
139
- if (!wantedTypes.includes("Scans")) {
140
- mainAnimeOnly = mainAnimeOnly.replace(/<h2.*?>Manga<\/h2>[\s\S]*?(?=<h2|$)/g, '');
141
- }
142
- }
143
-
130
+
131
+ const mainAnimeOnly = html.split("Anime Version Kai")[0];
144
132
  const $ = cheerio.load(mainAnimeOnly);
145
133
  const scriptTags = $("script")
146
134
  .toArray()
147
- .filter(script => ["panneauAnime", "panneauScan"].some(str => $(script).html().includes(str)));
135
+ .filter(script => $(script).html().includes("panneauAnime"));
148
136
 
149
137
  const animeName = animeUrl.split("/")[4];
150
138
  let seasons = [];
@@ -152,7 +140,7 @@ export async function getSeasons(animeUrl, languagePriority = ["vostfr", "vf", "
152
140
  for (const language of languagePriority) {
153
141
  seasons = [];
154
142
  let languageAvailable = false;
155
- let scansLanguage = "";
143
+
156
144
  for (let script of scriptTags) {
157
145
  const content = $(script).html();
158
146
 
@@ -160,49 +148,33 @@ export async function getSeasons(animeUrl, languagePriority = ["vostfr", "vf", "
160
148
  .replace(/\/\*[\s\S]*?\*\//g, "")
161
149
  .replace(/\/\/.*$/gm, "");
162
150
 
163
- const matches = [...uncommentedContent.matchAll(/(panneauAnime|panneauScan)\("([^"]+)", "([^"]+)"\);/g)];
151
+ const matches = [...uncommentedContent.matchAll(/panneauAnime\("([^"]+)", "([^"]+)"\);/g)];
164
152
 
165
153
  for (let match of matches) {
166
- const type = match[1];
167
- const title = match[2];
168
- const href = match[3].split("/")[0];
169
-
170
- if (type === "panneauScan") {
171
- let found = false;
172
- for (const lang of languagePriority) {
173
- const fullUrl = `${CATALOGUE_URL}/${animeName}/${href}/${lang}`;
174
- try {
175
- const check = await axios.head(fullUrl, { headers: getHeaders(animeUrl) });
176
- if (check.status === 200) {
177
- seasons.push({ title, url: fullUrl });
178
- scansLanguage = lang
179
- found = true;
180
- break;
181
- }
182
- } catch {}
154
+ const title = match[1];
155
+ const href = match[2].split("/")[0];
156
+ const fullUrl = `${CATALOGUE_URL}/${animeName}/${href}/${language}`;
157
+
158
+ try {
159
+ const check = await axios.head(fullUrl, {
160
+ headers: getHeaders(animeUrl),
161
+ });
162
+ if (check.status === 200) {
163
+ languageAvailable = true;
164
+ seasons.push({ title, url: fullUrl });
183
165
  }
184
- if (found) languageAvailable = true;
185
- } else {
186
- const fullUrl = `${CATALOGUE_URL}/${animeName}/${href}/${language}`;
187
- try {
188
- const check = await axios.head(fullUrl, { headers: getHeaders(animeUrl) });
189
- if (check.status === 200) {
190
- seasons.push({ title, url: fullUrl });
191
- languageAvailable = true;
192
- }
193
- } catch {}
166
+ } catch (err) {
167
+ // Ignore invalid URLs
194
168
  }
195
169
  }
196
170
  }
197
- if (wantedTypes.includes("Scans") && wantedTypes.length==1 && scansLanguage) {
198
- return { scansLanguage, seasons };
199
- }
200
- else if (languageAvailable) {
171
+
172
+ if (languageAvailable) {
201
173
  return { language, seasons };
202
174
  }
203
175
  }
204
176
 
205
- return [];
177
+ return { error: "No language available in : " + languagePriority.join(", ") };
206
178
  }
207
179
 
208
180
 
@@ -284,7 +256,7 @@ export async function getEmbed(
284
256
  .get(scriptUrl, { headers: getHeaders(seasonUrl) })
285
257
  .then((r) => r.data);
286
258
 
287
- const matches = [...episodesJs.toLowerCase().matchAll(/var\s+(eps\d+)\s*=\s*(\[[^\]]+\])/g)];
259
+ const matches = [...episodesJs.matchAll(/var\s+(eps\d+)\s*=\s*(\[[^\]]+\])/g)];
288
260
  if (!matches.length) throw new Error("No episode arrays found");
289
261
 
290
262
  let episodeMatrix = [];
@@ -307,18 +279,18 @@ export async function getEmbed(
307
279
 
308
280
  for (const host of hostPriority) {
309
281
  for (const arr of episodeMatrix) {
310
- if (i < arr.length && arr[i].includes(host.toLowerCase())) {
311
- if (!hosts.includes(host.toLowerCase())) {
282
+ if (i < arr.length && arr[i].includes(host)) {
283
+ if (!hosts.includes(host)) {
312
284
  urls.push(arr[i]);
313
- hosts.push(host.toLowerCase());
285
+ hosts.push(host);
314
286
  }
315
- break;
287
+ break; // une seule URL par host
316
288
  }
317
289
  }
318
290
  }
319
291
 
320
292
  finalEmbeds.push({
321
- title: null,
293
+ title: null, // à remplir plus tard
322
294
  url: urls.length ? urls : null,
323
295
  host: hosts.length ? hosts : null,
324
296
  });
@@ -329,9 +301,9 @@ export async function getEmbed(
329
301
 
330
302
  for (const host of hostPriority) {
331
303
  for (const arr of episodeMatrix) {
332
- if (i < arr.length && arr[i].includes(host.toLowerCase())) {
304
+ if (i < arr.length && arr[i].includes(host)) {
333
305
  selectedUrl = arr[i];
334
- selectedHost = host.toLowerCase();
306
+ selectedHost = host;
335
307
  break;
336
308
  }
337
309
  }
@@ -339,7 +311,7 @@ export async function getEmbed(
339
311
  }
340
312
 
341
313
  finalEmbeds.push({
342
- title: null,
314
+ title: null, // à remplir plus tard
343
315
  url: selectedUrl || null,
344
316
  host: selectedHost || null,
345
317
  });
@@ -445,37 +417,28 @@ export async function getAllAnime(
445
417
  const url = pageNum === 1 ? CATALOGUE_URL : `${CATALOGUE_URL}?page=${pageNum}`;
446
418
  const res = await axios.get(url, { headers: getHeaders(CATALOGUE_URL) });
447
419
  const $ = cheerio.load(res.data);
448
-
420
+
449
421
  const containers = $("div.shrink-0.m-3.rounded.border-2");
450
-
422
+
451
423
  containers.each((_, el) => {
452
424
  const anchor = $(el).find("a");
453
425
  const title = anchor.find("h1").text().trim();
454
426
  const link = anchor.attr("href");
455
427
  const img = anchor.find("img").attr("src");
456
-
428
+
457
429
  const paragraphs = anchor.find("p").toArray().map(p => $(p).text().trim());
458
-
430
+
459
431
  const altTitles = paragraphs[0] ? paragraphs[0].split(',').map(name => name.trim()) : [];
460
432
  const genres = paragraphs[1] ? paragraphs[1].split(',').map(genre => genre.trim()) : [];
461
433
  const type = paragraphs[2] ? paragraphs[2].split(',').map(t => t.trim()) : [];
462
434
  const language = paragraphs[3] ? paragraphs[3].split(',').map(lang => lang.trim()) : [];
463
-
464
- const filteredTypes = wantedTypes.length === 0
465
- ? type
466
- : type.filter(t => isWanted(t, wantedTypes));
467
-
468
- const hasScans = filteredTypes.some(t => t.toLowerCase() === "scans".toLowerCase());
469
-
470
- const filteredLanguages = (wantedLanguages.length === 0 || hasScans)
471
- ? language
472
- : language.filter(lang => isWanted(lang, wantedLanguages));
473
-
435
+ const filteredTypes = type.filter(t => isWanted(t, wantedTypes));
436
+ const filteredLanguages = language.filter(lang => isWanted(lang, wantedLanguages));
474
437
  if (
475
438
  title &&
476
439
  link &&
477
440
  filteredTypes.length > 0 &&
478
- (filteredLanguages.length > 0 || hasScans)
441
+ filteredLanguages.length > 0
479
442
  ) {
480
443
  const fullUrl = link.startsWith("http") ? link : `${BASE_URL}${link}`;
481
444
  animeLinks.push({
@@ -489,10 +452,10 @@ export async function getAllAnime(
489
452
  });
490
453
  }
491
454
  });
492
-
455
+
493
456
  return containers.length > 0;
494
457
  };
495
-
458
+
496
459
  const enrichWithSeasons = async (list) => {
497
460
  for (const anime of list) {
498
461
  try {
@@ -646,32 +609,3 @@ export async function getRandomAnime(
646
609
  return null;
647
610
  }
648
611
  }
649
-
650
-
651
- export async function getAllTitleScans(mangaUrl, numberImg = false) {
652
- const res = await axios.get(mangaUrl, { headers: getHeaders() });
653
- const $ = cheerio.load(res.data);
654
-
655
- const title = encodeURIComponent($("#titreOeuvre").text().trim());
656
- const urlInfo = `https://anime-sama.fr/s2/scans/get_nb_chap_et_img.php?oeuvre=${title}`
657
- const infoPage = await axios.get(urlInfo, { headers: getHeaders() });
658
- const titleChapter = Object.keys(infoPage.data)
659
-
660
- if (numberImg) {
661
- return {scans :infoPage.data, mangaTitle :title}
662
- } else {
663
- return titleChapter
664
- }
665
- }
666
-
667
- export async function getImgScans(mangaUrl, wantedChapter) {
668
- const infoScan = await getAllTitleScans(mangaUrl, true)
669
- const numberImg = infoScan.scans[wantedChapter.toString()];
670
- const mangaTitle = infoScan.mangaTitle
671
- const imgUrls = [];
672
- for (let i = 1; i <= numberImg; i++) {
673
- imgUrls.push(`https://anime-sama.fr/s2/scans/${mangaTitle}/${wantedChapter}/${i}.jpg`);
674
- }
675
-
676
- return imgUrls
677
- }
@@ -104,21 +104,4 @@ export class AnimeScraper {
104
104
  return null;
105
105
  }
106
106
  }
107
-
108
- async getAllTitleScans(mangaUrl, ...rest) {
109
- try {
110
- return await this.source.getAllTitleScans(mangaUrl, ...rest);
111
- } catch (error) {
112
- console.error(`This scraper does not have the getAllTitleScans function implemented or an error happened -> ${error}`);
113
- return null;
114
- }
115
- }
116
- async getImgScans(mangaUrl, ...rest) {
117
- try {
118
- return await this.source.getImgScans(mangaUrl, ...rest);
119
- } catch (error) {
120
- console.error(`This scraper does not have the getImgScans function implemented or an error happened -> ${error}`);
121
- return null;
122
- }
123
- }
124
107
  }
@@ -10,9 +10,6 @@ export async function getVideoUrlFromEmbed(source, embedUrl) {
10
10
  if (source === "vidmoly" || source === "oneupload" ) {
11
11
  return await extractor.getVidmolyOrOneuploadVideo(embedUrl);
12
12
  }
13
- if (source === "movearnpre" || source === "smoothpre" ) {
14
- return await extractor.getMovearnpreOrSmoothpreVideo(embedUrl);
15
- }
16
13
 
17
14
  throw new Error(`Unsupported embed source: ${source}`);
18
15
  }
@@ -60,6 +60,7 @@ export async function getVidmolyOrOneuploadVideo(embedUrl) {
60
60
  const { data } = await axios.get(embedUrl, {
61
61
  headers: getHeaders(embedUrl),
62
62
  });
63
+ console.log(data)
63
64
  const $ = cheerio.load(data);
64
65
  const scripts = $("script");
65
66
 
@@ -76,28 +77,3 @@ export async function getVidmolyOrOneuploadVideo(embedUrl) {
76
77
  return null;
77
78
  }
78
79
  }
79
-
80
- import puppeteer from 'puppeteer';
81
-
82
- export async function getMovearnpreOrSmoothpreVideo(embedUrl) {
83
- const browser = await puppeteer.launch({ headless: true });
84
- const page = await browser.newPage();
85
-
86
- await page.setUserAgent(
87
- "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/135.0.0.0 Safari/537.36"
88
- );
89
-
90
- await page.goto(embedUrl, { waitUntil: 'networkidle2' });
91
-
92
- await page.waitForFunction('typeof jwplayer !== "undefined"');
93
-
94
- const videoUrl = await page.evaluate(() => {
95
- const player = jwplayer();
96
- const sources = player?.getPlaylist()?.[0]?.sources;
97
- return sources?.[0]?.file || null;
98
- });
99
-
100
- await browser.close();
101
- const finalUrl = embedUrl.split("/").slice(0, 3).join("/") + videoUrl
102
- return finalUrl;
103
- }
@@ -1,10 +0,0 @@
1
- import { AnimeScraper } from "../../index.js"; // REPLACE BY "from 'better-ani-scraped';"
2
-
3
- const main = async () => {
4
- const animesama = new AnimeScraper('animesama');
5
- const mangaUrl = "https://anime-sama.fr/catalogue/drcl-midnight-children/scan/vf";
6
- const chapterTitles = await animesama.getAllTitleScans(mangaUrl, true);
7
- console.log("Titles:", chapterTitles);
8
- };
9
-
10
- main().catch(console.error);
@@ -1,10 +0,0 @@
1
- import { AnimeScraper } from "../../index.js"; // REPLACE BY "from 'better-ani-scraped';"
2
-
3
- const main = async () => {
4
- const animesama = new AnimeScraper('animesama');
5
- const scansUrl = "https://anime-sama.fr/catalogue/drcl-midnight-children/scan/vf";
6
- const scansImgUrl = await animesama.getImgScans(scansUrl, 9);
7
- console.log("Image scans:", scansImgUrl);
8
- };
9
-
10
- main().catch(console.error);