better-ani-scraped 1.5.0 → 1.5.2

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
@@ -27,7 +27,7 @@ const crunchyroll = new AnimeScraper('crunchyroll') //for Crunchyroll
27
27
 
28
28
  ## `AnimeScraper("animesama")` methods
29
29
 
30
- - [searchAnime](#animesamasearchanimequery-limit--10-wantedlanguages--vostfr-vf-vastfr-wantedtypes--anime-film)
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
33
  - [getEmbed](#animesamagetembedseasonurl-hostpriority--sibnet-vidmoly)
@@ -37,7 +37,7 @@ const crunchyroll = new AnimeScraper('crunchyroll') //for Crunchyroll
37
37
  - [getLatestEpisodes](#animesamagetlatestepisodeslanguagefilter--null)
38
38
  - [getRandomAnime](#animesamagetrandomanimewantedlanguages--vostfr-vf-vastfr-wantedtypes--anime-film-maxattempts--null-attempt--0)
39
39
 
40
- ### `animesama.searchAnime(query, limit = 10, wantedLanguages = ["vostfr", "vf", "vastfr"], wantedTypes = ["Anime", "Film"])`
40
+ ### `animesama.searchAnime(query, limit = 10, wantedLanguages = ["vostfr", "vf", "vastfr"], wantedTypes = ["Anime", "Film"], page = null)`
41
41
  Searches for anime titles that match the given query.
42
42
 
43
43
  - **Parameters:**
@@ -45,6 +45,7 @@ Searches for anime titles that match the given query.
45
45
  - `limit` *(number)*: Maximum number of results to return (default: 10).
46
46
  - `wantedLanguages` *(string[])*: Array of wanted languages.
47
47
  - `wantedTypes` *(string[])*: Array of wanted types.
48
+ - `page` *(number)*: The catalog page number.
48
49
  - **Returns:**
49
50
  An array of anime objects:
50
51
  ```js
@@ -169,8 +170,13 @@ Fetches the full anime catalog, optionally including season information.
169
170
  ```js
170
171
  [
171
172
  {
172
- title: string,
173
173
  url: string,
174
+ title: string,
175
+ altTitles: string[],
176
+ cover: string,
177
+ genres: string[],
178
+ types: string[],
179
+ languages: string[],
174
180
  }
175
181
  ...
176
182
  ]
@@ -302,7 +308,7 @@ Extracts information from all episodes of a season of an anime.
302
308
  ```
303
309
  ---
304
310
 
305
- ## Utility functions
311
+ ## Utility functions
306
312
 
307
313
  - [getVideoUrlFromEmbed](#getvideourlfromembedsource-embedurl)
308
314
 
@@ -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 search = await animesama.searchAnime("86", 3, ["vostfr", "vf", "vastfr"], ["Anime", "Film"]);
6
+ const search = await animesama.searchAnime("a", 100, ["vostfr", "vf", "vastfr"], ["Anime", "Film"], 2);
7
7
  console.log("Search Results:", search);
8
8
  };
9
9
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "better-ani-scraped",
3
- "version": "1.5.0",
3
+ "version": "1.5.2",
4
4
  "description": "Scrape anime data from different sources (only anime-sama.fr for the moment)",
5
5
  "main": "index.js",
6
6
  "scripts": {
@@ -47,67 +47,73 @@ export async function searchAnime(
47
47
  query,
48
48
  limit = 10,
49
49
  wantedLanguages = ["vostfr", "vf", "vastfr"],
50
- wantedTypes = ["Anime", "Film"]
50
+ wantedTypes = ["Anime", "Film"],
51
+ page = null
51
52
  ) {
52
- const url = `${CATALOGUE_URL}/?search=${encodeURIComponent(
53
- query
54
- )}`;
55
53
  const isWanted = (text, list) =>
56
54
  list.some(item => text.toLowerCase().includes(item.toLowerCase()));
57
- const res = await axios.get(url, { headers: getHeaders(CATALOGUE_URL) });
58
- const $ = cheerio.load(res.data);
55
+
59
56
  const results = [];
60
57
 
61
- $("a.flex.divide-x").each((i, el) => {
62
- if (i >= limit) return false;
58
+ const fetchPage = async (pageNum) => {
59
+ const url =
60
+ pageNum === 1
61
+ ? `${CATALOGUE_URL}/?search=${encodeURIComponent(query)}`
62
+ : `${CATALOGUE_URL}/?search=${encodeURIComponent(query)}&page=${pageNum}`;
63
63
 
64
- const anchor = $(el);
65
- const link = anchor.attr("href");
66
- const title = anchor.find("h1").first().text().trim();
67
- const altRaw = anchor
68
- .find("p.text-xs.opacity-40.italic")
69
- .first()
70
- .text()
71
- .trim();
72
- const cover = anchor.find("img").first().attr("src");
64
+ const res = await axios.get(url, { headers: getHeaders(CATALOGUE_URL) });
65
+ const $ = cheerio.load(res.data);
73
66
 
74
- const tagText = anchor.find("p").filter((_, p) =>
75
- isWanted($(p).text(), wantedTypes)
76
- ).first().text();
67
+ const containers = $("a.flex.divide-x");
77
68
 
78
- const languageText = anchor.find("p").filter((_, p) =>
79
- isWanted($(p).text(), wantedLanguages)
80
- ).first().text();
69
+ containers.each((_, el) => {
70
+ if (results.length >= limit) return false;
81
71
 
82
- const altTitles = altRaw
83
- ? altRaw
84
- .split(",")
85
- .map((t) => t.trim())
86
- .filter(Boolean)
87
- : [];
72
+ const anchor = $(el);
73
+ const link = anchor.attr("href");
74
+ const title = anchor.find("h1").first().text().trim();
75
+ const altRaw = anchor.find("p.text-xs.opacity-40.italic").first().text().trim();
76
+ const cover = anchor.find("img").first().attr("src");
88
77
 
89
- const genreRaw = anchor
90
- .find("p.text-xs.font-medium.text-gray-300")
91
- .first()
92
- .text()
93
- .trim();
94
- const genres = genreRaw
95
- ? genreRaw
96
- .split(",")
97
- .map((g) => g.trim())
98
- .filter(Boolean)
99
- : [];
78
+ const tagText = anchor.find("p").filter((_, p) =>
79
+ isWanted($(p).text(), wantedTypes)
80
+ ).first().text();
100
81
 
101
- if (title && link && tagText && languageText) {
102
- results.push({
103
- title,
104
- altTitles,
105
- genres,
106
- url: link.startsWith("http") ? link : `${CATALOGUE_URL}${link}`,
107
- cover,
108
- });
82
+ const languageText = anchor.find("p").filter((_, p) =>
83
+ isWanted($(p).text(), wantedLanguages)
84
+ ).first().text();
85
+
86
+ const altTitles = altRaw
87
+ ? altRaw.split(",").map((t) => t.trim()).filter(Boolean)
88
+ : [];
89
+
90
+ const genreRaw = anchor.find("p.text-xs.font-medium.text-gray-300").first().text().trim();
91
+ const genres = genreRaw
92
+ ? genreRaw.split(",").map((g) => g.trim()).filter(Boolean)
93
+ : [];
94
+
95
+ if (title && link && tagText && languageText) {
96
+ results.push({
97
+ title,
98
+ altTitles,
99
+ genres,
100
+ url: link.startsWith("http") ? link : `${CATALOGUE_URL}${link}`,
101
+ cover,
102
+ });
103
+ }
104
+ });
105
+
106
+ return containers.length > 0;
107
+ };
108
+
109
+ if (page) {
110
+ await fetchPage(page);
111
+ } else {
112
+ let currentPage = 1;
113
+ while (await fetchPage(currentPage++) && results.length < limit) {
114
+ await new Promise((res) => setTimeout(res, 300));
109
115
  }
110
- });
116
+ }
111
117
 
112
118
  return results;
113
119
  }
@@ -343,31 +349,45 @@ export async function getAllAnime(
343
349
  const url = pageNum === 1 ? CATALOGUE_URL : `${CATALOGUE_URL}?page=${pageNum}`;
344
350
  const res = await axios.get(url, { headers: getHeaders(CATALOGUE_URL) });
345
351
  const $ = cheerio.load(res.data);
346
-
352
+
347
353
  const containers = $("div.shrink-0.m-3.rounded.border-2");
348
-
354
+
349
355
  containers.each((_, el) => {
350
356
  const anchor = $(el).find("a");
351
357
  const title = anchor.find("h1").text().trim();
352
358
  const link = anchor.attr("href");
353
-
354
- const tagText = anchor.find("p").filter((_, p) =>
355
- isWanted($(p).text(), wantedTypes)
356
- ).first().text();
357
-
358
- const languageText = anchor.find("p").filter((_, p) =>
359
- isWanted($(p).text(), wantedLanguages)
360
- ).first().text();
361
-
362
- if (title && link && tagText && languageText) {
359
+ const img = anchor.find("img").attr("src");
360
+
361
+ const paragraphs = anchor.find("p").toArray().map(p => $(p).text().trim());
362
+
363
+ const altTitles = paragraphs[0] ? paragraphs[0].split(',').map(name => name.trim()) : [];
364
+ const genres = paragraphs[1] ? paragraphs[1].split(',').map(genre => genre.trim()) : [];
365
+ const type = paragraphs[2] ? paragraphs[2].split(',').map(t => t.trim()) : [];
366
+ const language = paragraphs[3] ? paragraphs[3].split(',').map(lang => lang.trim()) : [];
367
+ const filteredTypes = type.filter(t => isWanted(t, wantedTypes));
368
+ const filteredLanguages = language.filter(lang => isWanted(lang, wantedLanguages));
369
+ if (
370
+ title &&
371
+ link &&
372
+ filteredTypes.length > 0 &&
373
+ filteredLanguages.length > 0
374
+ ) {
363
375
  const fullUrl = link.startsWith("http") ? link : `${BASE_URL}${link}`;
364
- animeLinks.push({ title, url: fullUrl });
376
+ animeLinks.push({
377
+ url: fullUrl,
378
+ title,
379
+ altTitles,
380
+ cover: img,
381
+ genres,
382
+ types: filteredTypes,
383
+ languages: filteredLanguages,
384
+ });
365
385
  }
366
386
  });
367
-
387
+
368
388
  return containers.length > 0;
369
389
  };
370
-
390
+
371
391
  const enrichWithSeasons = async (list) => {
372
392
  for (const anime of list) {
373
393
  try {
@@ -392,7 +412,6 @@ export async function getAllAnime(
392
412
  await new Promise(r => setTimeout(r, 300));
393
413
  }
394
414
 
395
- // Dédupliquer les URLs
396
415
  const uniqueLinks = [...new Map(animeLinks.map(item => [item.url, item])).values()];
397
416
  if (get_seasons) await enrichWithSeasons(uniqueLinks);
398
417
 
@@ -400,7 +419,7 @@ export async function getAllAnime(
400
419
  return true;
401
420
  }
402
421
  } catch (err) {
403
- console.error("🔥 Erreur surpuissante détectée :", err.message);
422
+ console.error("error :", err.message);
404
423
  return false;
405
424
  }
406
425
  }