bani / server.js
Reaperxxxx's picture
Update server.js
9694c50 verified
Raw
History Blame Contribute Delete
4.9 kB
const express = require("express");
const axios = require("axios");
const cheerio = require("cheerio");
const stringSimilarity = require("string-similarity");
const app = express();
const PORT = 7860;
const baseURL = "https://www.tokyoinsider.com/anime/";
app.get("/anime/:name/:episode", async (req, res) => {
const animeName = decodeURIComponent(req.params.name).replace(/\s+/g, " ").trim();
const episodeNumber = req.params.episode;
const firstLetter = animeName[0].toUpperCase();
const categoryURL = `${baseURL}${firstLetter}`;
console.log(`Fetching: ${categoryURL}`);
try {
let response = await axios.get(categoryURL);
let html = response.data;
let $ = cheerio.load(html);
let animeLinks = {};
$("a[href*='/anime/']").each((i, link) => {
let title = $(link).text().replace(/\s+/g, " ").trim();
let href = $(link).attr("href");
if (title && href) {
animeLinks[title] = `https://www.tokyoinsider.com${href}`;
}
});
if (Object.keys(animeLinks).length === 0) {
return res.status(404).json({ error: "No anime found." });
}
let bestMatch = findClosestMatch(animeName, Object.keys(animeLinks));
if (bestMatch) {
let baseAnimeURL = animeLinks[bestMatch].replace(/\/episode\/\d+$/, "");
let episodeURL = `${baseAnimeURL}/episode/${episodeNumber}`;
console.log(`Anime Found: ${bestMatch}`);
console.log(`Episode URL: ${episodeURL}`);
let downloads = await getEpisodeDownloads(episodeURL);
if (downloads.length === 0) {
console.log("No downloads found, retrying alternative URL...");
let alternativeURL = `${baseAnimeURL}/ep/${episodeNumber}`;
downloads = await getEpisodeDownloads(alternativeURL);
if (downloads.length === 0) {
return res.status(404).json({ error: "No download links found." });
}
episodeURL = alternativeURL;
}
let categorizedDownloads = categorizeDownloads(downloads);
let responseObj = {
anime: bestMatch,
episode: episodeNumber,
episodeURL,
...categorizedDownloads
};
return res.json(responseObj);
} else {
return res.status(404).json({ error: "No close match found." });
}
} catch (error) {
console.error("Error:", error.message);
return res.status(500).json({ error: "Server error." });
}
});
async function getEpisodeDownloads(episodeURL) {
console.log(`Fetching Episode Page: ${episodeURL}`);
try {
let response = await axios.get(episodeURL);
let html = response.data;
let $ = cheerio.load(html);
let downloads = [];
$(".c_h2, .c_h2b").each((i, div) => {
let linkElement = $(div).find("a[href*='media.tokyoinsider.com']");
let infoElement = $(div).find(".finfo");
if (linkElement.length > 0 && infoElement.length > 0) {
let title = linkElement.text().trim();
let url = linkElement.attr("href");
let sizeText = infoElement.find("b").eq(0).text();
let downloadsCount = infoElement.find("b").eq(1).text();
let uploader = infoElement.find("b").eq(2).text();
let addedOn = infoElement.find("b").eq(3).text();
let size = parseSize(sizeText);
if (size > 0) {
downloads.push({ title, url, size, downloadsCount, uploader, addedOn });
}
}
});
return downloads;
} catch (error) {
console.error("Error fetching episode page:", error.message);
return [];
}
}
function categorizeDownloads(downloads) {
let resolutions = { "360p": null, "720p": null, "1080p": null };
downloads.forEach(d => {
if (d.size >= 25 && d.size < 100 && !resolutions["360p"]) {
resolutions["360p"] = d;
} else if (d.size >= 100 && d.size < 400 && !resolutions["720p"]) {
resolutions["720p"] = d;
} else if (d.size >= 400 && !resolutions["1080p"]) {
resolutions["1080p"] = d;
}
});
return resolutions;
}
function parseSize(sizeText) {
let size = parseFloat(sizeText);
if (sizeText.includes("GB")) {
size *= 1024;
}
return isNaN(size) ? 0 : size;
}
function findClosestMatch(query, animeList) {
let bestMatch = stringSimilarity.findBestMatch(query, animeList);
return bestMatch.bestMatch.rating > 0.5 ? bestMatch.bestMatch.target : null;
}
app.listen(PORT, () => {
console.log(`Server running on http://localhost:${PORT}`);
});