Files
justanime.fun/src/extractors/animeInfo.extractor.js
2026-03-01 12:39:28 +05:30

195 lines
6.6 KiB
JavaScript

import axios from "axios";
import * as cheerio from "cheerio";
import formatTitle from "../helper/formatTitle.helper.js";
import { v1_base_url } from "../utils/base_v1.js";
import extractRecommendedData from "./recommend.extractor.js";
import extractRelatedData from "./related.extractor.js";
import extractPopularData from "./popular.extractor.js";
async function extractAnimeInfo(id) {
try {
const [resp, characterData] = await Promise.all([
axios.get(`https://${v1_base_url}/${id}`),
axios.get(
`https://${v1_base_url}/ajax/character/list/${id.split("-").pop()}`
),
]);
const characterHtml = characterData.data?.html || "";
const $1 = cheerio.load(characterHtml);
const $ = cheerio.load(resp.data);
const data_id = id.split("-").pop();
const titleElement = $("#ani_detail .film-name");
const showType = $("#ani_detail .prebreadcrumb ol li")
.eq(1)
.find("a")
.text()
.trim();
const posterElement = $("#ani_detail .film-poster");
const tvInfoElement = $("#ani_detail .film-stats");
const tvInfo = {};
tvInfoElement.find(".tick-item, span.item").each((_, element) => {
const el = $(element);
const text = el.text().trim();
if (el.hasClass("tick-quality")) tvInfo.quality = text;
else if (el.hasClass("tick-sub")) tvInfo.sub = text;
else if (el.hasClass("tick-dub")) tvInfo.dub = text;
else if (el.hasClass("tick-eps")) tvInfo.eps = text;
else if (el.hasClass("tick-pg")) tvInfo.rating = text;
else if (el.is("span.item")) {
if (!tvInfo.showType) tvInfo.showType = text;
else if (!tvInfo.duration) tvInfo.duration = text;
}
});
const element = $(
"#ani_detail > .ani_detail-stage > .container > .anis-content > .anisc-info-wrap > .anisc-info > .item"
);
const overviewElement = $("#ani_detail .film-description .text");
const title = titleElement.text().trim();
const japanese_title = titleElement.attr("data-jname");
const synonyms = $('.item.item-title:has(.item-head:contains("Synonyms")) .name').text().trim();
const poster = posterElement.find("img").attr("src");
const syncDataScript = $("#syncData").html();
let anilistId = null;
let malId = null;
if (syncDataScript) {
try {
const syncData = JSON.parse(syncDataScript);
anilistId = syncData.anilist_id || null;
malId = syncData.mal_id || null;
} catch (error) {
console.error("Error parsing syncData:", error);
}
}
const animeInfo = {};
element.each((_, el) => {
const key = $(el).find(".item-head").text().trim().replace(":", "");
const value =
key === "Genres" || key === "Producers"
? $(el)
.find("a")
.map((_, a) => $(a).text().split(" ").join("-").trim())
.get()
: $(el).find(".name").text().split(" ").join("-").trim();
animeInfo[key] = value;
});
const trailers = [];
$('.block_area-promotions-list .screen-items .item').each((_, element) => {
const el = $(element);
const title = el.attr('data-title');
const url = el.attr('data-src');
if (url) {
const fullUrl = url.startsWith('//') ? `https:${url}` : url;
let videoId = null;
const match = fullUrl.match(/\/embed\/([^?&]+)/);
if (match && match[1]) {
videoId = match[1];
}
trailers.push({
title: title || null,
url: fullUrl,
thumbnail: videoId ? `https://img.youtube.com/vi/${videoId}/hqdefault.jpg` : null
});
}
});
animeInfo.trailers = trailers;
const season_id = formatTitle(title, data_id);
animeInfo["Overview"] = overviewElement.text().trim();
animeInfo["tvInfo"] = tvInfo;
let adultContent = false;
const tickRateText = $(".tick-rate", posterElement).text().trim();
if (tickRateText.includes("18+")) {
adultContent = true;
}
const [recommended_data, related_data, popular_data] = await Promise.all([
extractRecommendedData($),
extractRelatedData($),
extractPopularData($),
]);
let charactersVoiceActors = [];
if (characterHtml) {
charactersVoiceActors = $1(".bac-list-wrap .bac-item")
.map((index, el) => {
const character = {
id:
$1(el)
.find(".per-info.ltr .pi-avatar")
.attr("href")
?.split("/")[2] || "",
poster:
$1(el).find(".per-info.ltr .pi-avatar img").attr("data-src") ||
"",
name: $1(el).find(".per-info.ltr .pi-detail a").text(),
cast: $1(el).find(".per-info.ltr .pi-detail .pi-cast").text(),
};
let voiceActors = [];
const rtlVoiceActors = $1(el).find(".per-info.rtl");
const xxVoiceActors = $1(el).find(
".per-info.per-info-xx .pix-list .pi-avatar"
);
if (rtlVoiceActors.length > 0) {
voiceActors = rtlVoiceActors
.map((_, actorEl) => ({
id: $1(actorEl).find("a").attr("href")?.split("/").pop() || "",
poster: $1(actorEl).find("img").attr("data-src") || "",
name:
$1(actorEl).find(".pi-detail .pi-name a").text().trim() || "",
}))
.get();
} else if (xxVoiceActors.length > 0) {
voiceActors = xxVoiceActors
.map((_, actorEl) => ({
id: $1(actorEl).attr("href")?.split("/").pop() || "",
poster: $1(actorEl).find("img").attr("data-src") || "",
name: $1(actorEl).attr("title") || "",
}))
.get();
}
if (voiceActors.length === 0) {
voiceActors = $1(el)
.find(".per-info.per-info-xx .pix-list .pi-avatar")
.map((_, actorEl) => ({
id: $1(actorEl).attr("href")?.split("/")[2] || "",
poster: $1(actorEl).find("img").attr("data-src") || "",
name: $1(actorEl).attr("title") || "",
}))
.get();
}
return { character, voiceActors };
})
.get();
}
return {
adultContent,
data_id,
id: season_id,
anilistId,
malId,
title,
japanese_title,
synonyms,
poster,
showType,
animeInfo,
charactersVoiceActors,
recommended_data,
related_data,
popular_data,
};
} catch (e) {
console.error("Error extracting anime info:", e);
}
}
export default extractAnimeInfo;