const axios = require('axios'); const cheerio = require('cheerio'); class MAL { topAnime = async function () { try { const { data } = await axios.get('https://myanimelist.net/topanime.php'); const $ = cheerio.load(data); const animeList = []; $('.ranking-list').each((_, element) => { const rank = $(element).find('.rank').text().trim(); const title = $(element).find('.title h3 a').text().trim(); const url = $(element).find('.title h3 a').attr('href'); const score = $(element).find('.score span').text().trim(); const cover = $(element).find('.title img').attr('data-src'); const type = $(element).find('.information').text().split('\n')[1].trim(); const release = $(element).find('.information').text().split('\n')[2].trim(); const members = $(element).find('.information').text().split('\n')[3].trim(); animeList.push({ rank, title, score, type, release, members, cover, url }); }); return animeList; } catch (error) { throw new Error(error.message); } } seasonalAnime = async function (season, type) { try { const valid = { seasons: ['fall', 'spring', 'winter', 'summer'], types: { 'tv-new': 'TV (New)', 'tv-continuing': 'TV (Continuing)', 'ona': 'ONA', 'ova': 'OVA', 'movie': 'Movie', 'special': 'Special' } }; if (!valid.types[type]) throw new Error(`Available types: ${Object.keys(valid.types).join(', ')}`); if (!valid.seasons.includes(season)) throw new Error(`Available seasons: ${valid.seasons.join(', ')}`); const { data } = await axios.get(`https://myanimelist.net/anime/season/2024/${season}`); const $ = cheerio.load(data); const animeList = []; $('.seasonal-anime-list').each((_, list) => { const typeTxt = $(list).find('.anime-header').text().trim(); $(list).find('.js-seasonal-anime').each((_, element) => { const title = $(element).find('.h2_anime_title > a').text().trim(); const url = $(element).find('.h2_anime_title > a').attr('href'); const cover = $(element).find('.image > a > img').attr('src') || $(element).find('.image > a > img').attr('data-src'); const score = $(element).find('.js-score').text().trim(); const members = $(element).find('.js-members').text().trim(); const formattedMembers = Number(members.replace(/\D/g, '')).toLocaleString('en-US'); const infoDiv = $(element).find('.info'); const releaseDate = infoDiv.find('.item:first-child').text().trim(); const totalEps = infoDiv.find('.item:nth-child(2) span:first-child').text().trim(); const duration = infoDiv.find('.item:nth-child(2) span:nth-child(2)').text().trim(); const totalEpsWithDuration = `${totalEps}, ${duration}`; const synopsis = $(element).find('.synopsis p').text().trim(); const studio = $(element).find('.property:contains("Studio") .item').text().trim(); const source = $(element).find('.property:contains("Source") .item').text().trim(); const themes = $(element).find('.property:contains("Themes") .item').map((_, theme) => $(theme).text().trim()).get().join(', '); const genres = $(element).find('.genres .genre a').map((_, g) => $(g).text().trim()).get().join(', '); animeList.push({ title, type: typeTxt || 'Unknown', url, cover, stats: { score: score || 'N/A', members: formattedMembers || 'N/A' }, details: { releaseDate: releaseDate || 'Unknown', totalEpisodes: totalEpsWithDuration || 'Unknown', studio: studio || 'Unknown', source: source || 'Unknown' }, tags: { themes: themes || 'None', genres: genres || 'None' }, synopsis: synopsis }); }); }); return animeList.filter(obj => obj.type === valid.types[type]); } catch (error) { throw new Error(error.message); } } animeSearch = async function (query) { try { if (!query) throw new Error('Query is required'); const { data } = await axios.get(`https://myanimelist.net/anime.php?q=${query}&cat=anime`); const $ = cheerio.load(data); const animeList = []; $('table tbody tr').each((_, element) => { const cover = $(element).find('td:nth-child(1) img').attr('data-src') || $(element).find('td:nth-child(1) img').attr('src'); const title = $(element).find('td:nth-child(2) strong').text().trim(); const url = $(element).find('td:nth-child(2) a').attr('href'); const type = $(element).find('td:nth-child(3)').text().trim(); const episodes = $(element).find('td:nth-child(4)').text().trim(); const score = $(element).find('td:nth-child(5)').text().trim(); const description = $(element).find('td:nth-child(2) .pt4').text().replace('read more.', '').trim() || 'No Desc' if (title && url) { animeList.push({ title, description, type, episodes, score, cover, url }); } }); return animeList; } catch (error) { throw new Error(error.message); } } mangaSearch = async function (query) { try { if (!query) throw new Error('Query is required'); const { data } = await axios.get(`https://myanimelist.net/manga.php?q=${query}&cat=manga`); const $ = cheerio.load(data); const animeList = []; $('table tbody tr').each((_, element) => { const cover = $(element).find('td:nth-child(1) img').attr('data-src') || $(element).find('td:nth-child(1) img').attr('src'); const title = $(element).find('td:nth-child(2) strong').text().trim(); const url = $(element).find('td:nth-child(2) a').attr('href'); const type = $(element).find('td:nth-child(3)').text().trim(); const vol = $(element).find('td:nth-child(4)').text().trim(); const score = $(element).find('td:nth-child(5)').text().trim(); const description = $(element).find('td:nth-child(2) .pt4').text().replace('read more.', '').trim() || 'No Desc' if (title && url) { animeList.push({ title, description, type, vol, score, cover, url }); } }); return animeList; } catch (error) { throw new Error(error.message); } } charaSearch = async function (query) { try { if (!query) throw new Error('Query is required'); const { data } = await axios.get(`https://myanimelist.net/character.php?q=${query}&cat=character`); const $ = cheerio.load(data); const characterData = []; $('table tbody tr').each((_, element) => { const cover = $(element).find('td .picSurround img').attr('data-src') || $(element).find('td .picSurround img').attr('src'); const nameElement = $(element).find('td:nth-child(2) a'); const name = nameElement.text().trim(); const url = nameElement.attr('href') || ''; const animeList = []; const mangaList = []; $(element).find('td small a[href*="/anime/"]').each((_, anime) => { animeList.push({ title: $(anime).text().trim(), url: `https://myanimelist.net${$(anime).attr('href')}` }); }); $(element).find('td small a[href*="/manga/"]').each((_, manga) => { mangaList.push({ title: $(manga).text().trim(), url: `https://myanimelist.net${$(manga).attr('href')}` }); }); if (name && url) { characterData.push({ name, anime: animeList, manga: mangaList, cover, url }); } }); return characterData; } catch (error) { throw new Error(error.message); } } animeDetail = async function (url) { try { if (!url.includes('myanimelist.net/anime')) throw new Error('Invalid url'); const { data } = await axios.get(url); const $ = cheerio.load(data); const title = $("h1.title-name").text().trim(); const cover = $(".leftside img").attr("data-src"); const synopsis = $(".js-scrollfix-bottom-rel").find("p").first().text().trim(); const background = $('td.pb24:contains("Background")').contents().map(function() { if (this.type === 'text') { return $(this).text(); } else if (this.name === 'i') { return $(this).text(); } }).get().join('').trim(); const alternativeTitles = { synonyms: $('.spaceit_pad:contains("Synonyms")').contents().not('span').text().trim(), japanese: $('.spaceit_pad:contains("Japanese")').contents().not('span').text().trim(), english: $('.spaceit_pad:contains("English")').contents().not('span').text().trim(), }; const information = { type: $('.spaceit_pad:contains("Type") a').text().trim(), episodes: $('.spaceit_pad:contains("Episodes")').contents().not('span').text().trim(), status: $('.spaceit_pad:contains("Status")').contents().not('span').text().trim(), aired: $('.spaceit_pad:contains("Aired")').contents().not('span').text().trim(), premiered: $('.spaceit_pad:contains("Premiered")').contents().not('span').text().trim(), broadcast: $('.spaceit_pad:contains("Broadcast")').contents().not('span').text().trim(), producers: $("span:contains('Producers:')").nextAll("a").map((i, el) => $(el).text().trim()).get().join(', ') || 'Unknown', licensors: $("span:contains('Licensors:')").nextAll("a").map((i, el) => $(el).text().trim()).get().join(', ') || 'Unknown', studios: $("span:contains('Studios:')").nextAll("a").map((i, el) => $(el).text().trim()).get().join(', ') || 'Unknown', source: $('.spaceit_pad:contains("Source")').contents().not('span').text().trim(), genres: $("span:contains('Genres:')").nextAll("a").map((i, el) => $(el).text().trim()).get().join(', ') || 'None', themes: $("span:contains('Themes:')").nextAll("a").map((i, el) => $(el).text().trim()).get().join(', ') || 'None', demographic: $("span:contains('Demographic:')").nextAll("a").map((i, el) => $(el).text().trim()).get().join(', ') || 'None', duration: $('.spaceit_pad:contains("Duration")').contents().not('span').text().trim(), rating: $('.spaceit_pad:contains("Rating")').contents().not('span').text().trim(), }; const element = $('.spaceit_pad').filter((_, el) => { return $(el).find('span.dark_text').text().trim() === 'Ranked:'; }); const rankedText = element.contents().filter((_, el) => el.type === 'text').text().trim(); const statistics = { score: $('span[itemprop="ratingValue"]').text().trim(), ranked: rankedText, popularity: $('.spaceit_pad:contains("Popularity")').contents().not('span').text().trim(), members: $('.spaceit_pad:contains("Members")').contents().not('span').text().trim(), favorites: $('.spaceit_pad:contains("Favorites")').contents().not('span').text().trim(), }; const externalLinks = $(".external_urls a").map((i, el) => { const name = $(el).find(".caption").text().trim(); const url = $(el).attr("href"); if (name && url) { return { name, url }; } }).get() return { title, synopsis, background, alternativeTitles, information, statistics, externalLinks, cover, url: url }; } catch (error) { throw new Error(error.message); } } mangaDetail = async function (url) { try { if (!url.includes('myanimelist.net/manga')) throw new Error('Invalid url'); const { data } = await axios.get(url); const $ = cheerio.load(data); const title = $('span.h1-title span[itemprop="name"]').text().trim(); const cover = $(".leftside img").attr("data-src"); const synopsis = $('span[itemprop="description"]').text().trim(); const background = $('td.pb24:contains("Background")').contents().map(function() { if (this.type === 'text') { return $(this).text(); } else if (this.name === 'i') { return $(this).text(); } }).get().join('').trim(); const alternativeTitles = { synonyms: $('.spaceit_pad:contains("Synonyms")').contents().not('span').text().trim(), japanese: $('.spaceit_pad:contains("Japanese")').contents().not('span').text().trim(), english: $('.spaceit_pad:contains("English")').contents().not('span').text().trim(), }; const information = { type: $('.spaceit_pad:contains("Type") a').text().trim(), volumes: $('.spaceit_pad:contains("Volumes")').contents().not('span').text().trim(), chapters: $('.spaceit_pad:contains("Chapters")').contents().not('span').text().trim(), status: $('.spaceit_pad:contains("Status")').contents().not('span').text().trim(), published: $('.spaceit_pad:contains("Published")').contents().not('span').text().trim(), genres: $("span:contains('Genres:')").nextAll("a").map((i, el) => $(el).text().trim()).get().join(', ') || 'None', themes: $("span:contains('Themes:')").nextAll("a").map((i, el) => $(el).text().trim()).get().join(', ') || 'None', demographic: $("span:contains('Demographic:')").nextAll("a").map((i, el) => $(el).text().trim()).get().join(', ') || 'None', serialization: $("span:contains('Serialization:')").nextAll("a").map((i, el) => $(el).text().trim()).get().join(', ') || 'None', authors: $("span:contains('Authors:')").nextAll("a").map((i, el) => $(el).text().trim()).get().join(', ') || 'Unknown', }; const element = $('.spaceit_pad').filter((_, el) => { return $(el).find('span.dark_text').text().trim() === 'Ranked:'; }); const rankedText = element.contents().filter((_, el) => el.type === 'text').text().trim(); const statistics = { score: $('span[itemprop="ratingValue"]').text().trim(), ranked: rankedText, popularity: $('.spaceit_pad:contains("Popularity")').contents().not('span').text().trim(), members: $('.spaceit_pad:contains("Members")').contents().not('span').text().trim(), favorites: $('.spaceit_pad:contains("Favorites")').contents().not('span').text().trim(), }; const externalLinks = $(".external_urls a").map((i, el) => { const name = $(el).find(".caption").text().trim(); const url = $(el).attr("href"); if (name && url) { return { name, url }; } }).get() return { title, synopsis, background, alternativeTitles, information, statistics, externalLinks, cover, url: url }; } catch (error) { throw new Error(error.message); } } charaDetail = async function (url) { try { if (!url.includes('myanimelist.net/character')) throw new Error('Invalid url'); const { data } = await axios.get(url); const $ = cheerio.load(data); const name = $('h2.normal_header').first().text().trim(); const description = $('h2.normal_header').closest('td').clone().children().remove().end().text().trim(); const cover = $('img.portrait-225x350').attr('data-src') || $('img.portrait-225x350').attr('src'); const animeography = []; $('td.borderClass').each((i, el) => { const animeTitle = $(el).find('a[href*="https://myanimelist.net/anime/"]').text().trim() const animeLink = $(el).find('a[href*="https://myanimelist.net/anime/"]').attr('href') if (animeTitle && animeLink) { animeography.push({ title: animeTitle, url: animeLink }); } }); const mangaography = []; $('td.borderClass').each((i, el) => { const mangaTitle = $(el).find('a[href*="https://myanimelist.net/manga/"]').text().trim() const mangaLink = $(el).find('a[href*="https://myanimelist.net/manga/"]').attr('href') if (mangaTitle && mangaLink) { mangaography.push({ title: mangaTitle, url: mangaLink }); } }); const voiceActors = []; $('div.voice_actor').find('tr').each((i, el) => { const vaName = $(el).find('td:nth-child(2) a').text().trim(); const vaImage = $(el).find('td:nth-child(1) img').attr('data-src') || $(el).find('td:nth-child(1) img').attr('src'); const vaRole = $(el).find('td:nth-child(3)').text().trim(); if (vaName && vaImage) { voiceActors.push({ name: vaName, role: vaRole, image: vaImage }); } }); return { name, description, cover, animeography, mangaography, voiceActors }; } catch (error) { throw new Error(error.message); } }; } // Usage: const m = new MAL(); const resp = await m.topAnime(); console.log(resp);