fix: critical fixes for 14 broken endpoints

- Fixed ALL CSS selectors to match actual anikototv.to HTML
- Fixed search results returning empty data array
- Fixed anime info returning empty fields
- Fixed episode list returning 0 episodes
- Fixed most-popular returning empty data
- Fixed random endpoint 500 error
- Fixed filter endpoint 500 error
- Fixed suggestions returning empty array
- Added /api/genre/:name and /api/type/:name routes
- Fixed watch page server/episode extraction
- Fixed rating field to return clean value
This commit is contained in:
Sʜɪɴᴇɪ Nᴏᴜᴢᴇɴ
2026-06-08 15:54:22 +05:30
parent 8e201ccb10
commit 8269dcea8b
12 changed files with 202 additions and 198 deletions
+15
View File
@@ -5,6 +5,21 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [1.4.0] - 2026-06-08
### Fixed
- Fixed ALL CSS selectors to match actual anikototv.to HTML structure
- Fixed search results (was returning empty data array)
- Fixed anime info page (was returning empty fields)
- Fixed episode list (was returning 0 episodes)
- Fixed most-popular (was returning empty data array)
- Fixed random endpoint (was returning 500 error)
- Fixed filter endpoint (was returning 500 error)
- Fixed suggestions (was returning empty array)
- Added /api/genre/:name, /api/type/:name routes (was returning 404)
- Fixed watch page server/episode extraction
- Fixed rating field to return clean value
## [1.3.0] - 2026-06-08 ## [1.3.0] - 2026-06-08
### Added ### Added
+41 -68
View File
@@ -9,89 +9,62 @@ const extractAnimeInfo = async (slug) => {
const { data } = await axios.get(url, { headers }); const { data } = await axios.get(url, { headers });
const $ = cheerio.load(data); const $ = cheerio.load(data);
const title = $(".anisc-detail .film-name, .film-name h1").text().trim() || ""; const title = $("h1[itemprop='name'].title.d-title").text().trim() || "";
const japaneseTitle = $(".anisc-detail .film-name .alternate-title, .film-name small").text().trim() || ""; const japaneseTitle = $("h1[itemprop='name'].title.d-title").attr("data-jp") || "";
const poster = $(".film-poster img, .anisc-poster img").attr("src") || ""; const altNames = $(".names.font-italic").text().trim() || "";
const type = $(".film-info .row .col1 .item:first-child .name").text().trim() || ""; const poster = $("img[itemprop='image']").attr("src") || "";
const rating = $(".film-info .row .col1 .item:nth-child(2) .name").text().trim() || ""; const synopsis = $(".synopsis .content").text().trim() || "";
const quality = $(".film-info .row .col1 .item:nth-child(3) .name").text().trim() || ""; const rating = $("#w-rating .score .value").text().trim() || "";
const ratingValue = $("#w-rating span[itemprop='ratingValue']").text().trim() || "";
const reviewCount = $("#w-rating span[itemprop='reviewCount']").text().trim() || "";
const animeId = parseInt($("#watch-main").attr("data-id")) || 0;
const animeInfo = {}; const type = $(".bmeta .meta:first-child > div:nth-child(1) span").text().trim() || "";
$(".anisc-info .item").each((i, el) => { const premiered = $(".bmeta .meta:first-child > div:nth-child(2) span").text().trim() || "";
const label = $(el).find(".name").text().trim().replace(":", ""); const aired = $(".bmeta .meta:first-child > div:nth-child(3) span").text().trim() || "";
const value = $(el).find(".text").text().trim(); const status = $(".bmeta .meta:first-child > div:nth-child(4) span a").text().trim() || "";
if (label && value) { const malScore = $(".bmeta .meta:nth-child(2) > div:nth-child(1) span").text().trim() || "";
animeInfo[label] = value; const duration = $(".bmeta .meta:nth-child(2) > div:nth-child(2) span").text().trim() || "";
} const episodes = $(".bmeta .meta:nth-child(2) > div:nth-child(3) span").text().trim() || "";
const studios = [];
$(".bmeta .meta:nth-child(2) > div:nth-child(4) span a[itemprop='director'] span[itemprop='name']").each((i, el) => {
studios.push($(el).text().trim());
}); });
const overview = $(".anisc-detail .description, .film-description .text").text().trim() || ""; const producers = [];
$(".bmeta .meta:nth-child(2) > div:nth-child(5) span a[itemprop='director'] span[itemprop='name']").each((i, el) => {
producers.push($(el).text().trim());
});
const genres = []; const genres = [];
$(".anisc-info .item:last-child a, .genres a").each((i, el) => { $(".bmeta .meta:first-child > div:nth-child(5) span a[href*='/genre/']").each((i, el) => {
const genre = $(el).text().trim(); genres.push($(el).text().trim());
if (genre) genres.push(genre);
}); });
const episodes = []; const backgroundImage = $("#player").css("background-image")?.match(/url\(['"]?(.+?)['"]?\)/)?.[1] || "";
$(".episodes-list .ep-item, .ss-list a").each((i, el) => {
const epId = $(el).attr("data-id") || $(el).attr("href")?.split("/").pop() || "";
const epNumber = parseInt($(el).find(".ep-number, .ep-num").text().trim()) || i + 1;
const title = $(el).find(".ep-title, .ep-name").text().trim() || "";
if (epId) {
episodes.push({
id: epId,
episode_no: epNumber,
title
});
}
});
const relatedData = [];
$(".related-content .film-detail, .film-list-block .film-detail").each((i, el) => {
const relSlug = $(el).find("a").attr("href")?.split("/watch/").pop() || "";
const relPoster = $(el).find("img").attr("src") || "";
const relTitle = $(el).find(".film-name a").text().trim() || "";
if (relSlug) {
relatedData.push({
slug: relSlug,
poster: relPoster,
title: relTitle
});
}
});
const recommendedData = [];
$(".recommend-content .film-detail, .film-list-block .film-detail").each((i, el) => {
const recSlug = $(el).find("a").attr("href")?.split("/watch/").pop() || "";
const recPoster = $(el).find("img").attr("src") || "";
const recTitle = $(el).find(".film-name a").text().trim() || "";
if (recSlug) {
recommendedData.push({
slug: recSlug,
poster: recPoster,
title: recTitle
});
}
});
return { return {
slug, slug,
animeId,
title, title,
japaneseTitle, japaneseTitle,
altNames,
poster, poster,
backgroundImage,
synopsis,
type, type,
rating, premiered,
quality, aired,
overview, status,
animeInfo, malScore,
genres, duration,
episodes, episodes,
relatedData, studios,
recommendedData producers,
genres,
rating: rating || ratingValue,
reviewCount
}; };
} catch (error) { } catch (error) {
throw error; throw error;
+9 -8
View File
@@ -1,5 +1,4 @@
import * as cheerio from "cheerio"; import * as cheerio from "cheerio";
import axios from "axios";
import { headers } from "../configs/header.config.js"; import { headers } from "../configs/header.config.js";
import { URLS } from "../configs/dataUrl.js"; import { URLS } from "../configs/dataUrl.js";
import { countPages } from "../helper/countPages.helper.js"; import { countPages } from "../helper/countPages.helper.js";
@@ -17,25 +16,27 @@ const extractCategory = async (category, page = 1) => {
} else { } else {
url = `${URLS.home}/${category}`; url = `${URLS.home}/${category}`;
} }
const $ = await extractPages(url, page); const $ = await extractPages(url, page);
const totalPages = countPages($); const totalPages = countPages($);
const results = []; const results = [];
$(".film_list-wrap .flw-item, .film-detail").each((i, el) => { $("#list-items > .item").each((i, el) => {
const slug = $(el).find("a").attr("href")?.split("/watch/").pop() || ""; const slug = $(el).find("a").attr("href")?.split("/watch/").pop() || "";
const poster = $(el).find("img").attr("src") || ""; const poster = $(el).find(".ani.poster.tip > a > img").attr("src") || "";
const title = $(el).find(".film-name a, .name.d-title").text().trim() || ""; const title = $(el).find(".info .b1 a.name.d-title").text().trim() || "";
const japaneseTitle = $(el).find(".name.d-title").attr("data-jp") || ""; const japaneseTitle = $(el).find(".info .b1 a.name.d-title").attr("data-jp") || "";
const animeId = $(el).find(".ani.poster.tip").attr("data-tip") || "";
const sub = parseInt($(el).find(".ep-status.sub span").text().trim()) || 0; const sub = parseInt($(el).find(".ep-status.sub span").text().trim()) || 0;
const dub = parseInt($(el).find(".ep-status.dub span").text().trim()) || 0; const dub = parseInt($(el).find(".ep-status.dub span").text().trim()) || 0;
const total = parseInt($(el).find(".ep-status.total span").text().trim()) || 0; const total = parseInt($(el).find(".ep-status.total span").text().trim()) || 0;
const type = $(el).find(".meta .inner .right, .fdi-item:nth-child(2)").text().trim() || ""; const type = $(el).find(".info .meta .m-item:nth-child(2) label").text().trim() || "";
const rating = $(el).find(".rating, .fdi-item:nth-child(3)").text().trim() || ""; const rating = $(el).find(".info .meta .m-item.rated span").text().trim() || "";
if (slug) { if (slug) {
results.push({ results.push({
slug, slug,
animeId,
poster, poster,
title, title,
japaneseTitle, japaneseTitle,
+20 -14
View File
@@ -9,24 +9,30 @@ const extractEpisodeList = async (slug) => {
const { data } = await axios.get(url, { headers }); const { data } = await axios.get(url, { headers });
const $ = cheerio.load(data); const $ = cheerio.load(data);
const episodes = []; const animeId = parseInt($("#watch-main").attr("data-id")) || 0;
$(".episodes-list .ep-item, .ss-list a").each((i, el) => {
const epId = $(el).attr("data-id") || $(el).attr("href")?.split("/ep-").pop() || "";
const epNumber = parseInt($(el).find(".ep-number, .ep-num").text().trim()) || i + 1;
const title = $(el).find(".ep-title, .ep-name").text().trim() || "";
const filler = $(el).hasClass("filler") || false;
if (epId) { const episodes = [];
episodes.push({ $("#w-episodes a[data-num], #w-episodes a[data-slug]").each((i, el) => {
id: epId, const epId = $(el).attr("data-id") || $(el).attr("data-ep-id") || "";
episode_no: epNumber, const epNum = parseInt($(el).attr("data-num")) || i + 1;
title, const epSlug = $(el).attr("data-slug") || "";
filler const epTitle = $(el).find(".ep-title, .ep-name").text().trim() || "";
}); const isActive = $(el).hasClass("active") || false;
} const href = $(el).attr("href") || "";
episodes.push({
id: epId,
episode_no: epNum,
slug: epSlug,
title: epTitle,
active: isActive,
href
});
}); });
return { return {
animeId,
slug,
totalEpisodes: episodes.length, totalEpisodes: episodes.length,
episodes episodes
}; };
+10 -15
View File
@@ -51,26 +51,22 @@ const extractFilter = async (params) => {
years.forEach(y => queryParams.append("year[]", y.trim())); years.forEach(y => queryParams.append("year[]", y.trim()));
} }
const url = `${URLS.search}?${queryParams.toString()}`; const filterUrl = `${URLS.search}?${queryParams.toString()}`;
const $ = await extractPages(url, params.page || 1); const $ = await extractPages(filterUrl, params.page || 1);
const totalPages = countPages($); const totalPages = countPages($);
const results = []; const results = [];
$("#list-items .item, .film_list-wrap .flw-item").each((i, el) => { $("#list-items > .item").each((i, el) => {
const slug = $(el).find("a").attr("href")?.split("/watch/").pop() || ""; const slug = $(el).find("a").attr("href")?.split("/watch/").pop() || "";
const poster = $(el).find(".poster img, .ani.poster img").attr("src") || ""; const poster = $(el).find(".ani.poster.tip > a > img").attr("src") || "";
const title = $(el).find(".name.d-title").text().trim() || ""; const title = $(el).find(".info .b1 a.name.d-title").text().trim() || "";
const japaneseTitle = $(el).find(".name.d-title").attr("data-jp") || ""; const japaneseTitle = $(el).find(".info .b1 a.name.d-title").attr("data-jp") || "";
const animeId = $(el).find(".poster").attr("data-tip") || ""; const animeId = $(el).find(".ani.poster.tip").attr("data-tip") || "";
const sub = parseInt($(el).find(".ep-status.sub span").text().trim()) || 0; const sub = parseInt($(el).find(".ep-status.sub span").text().trim()) || 0;
const dub = parseInt($(el).find(".ep-status.dub span").text().trim()) || 0; const dub = parseInt($(el).find(".ep-status.dub span").text().trim()) || 0;
const total = parseInt($(el).find(".ep-status.total span").text().trim()) || 0; const total = parseInt($(el).find(".ep-status.total span").text().trim()) || 0;
const type = $(el).find(".meta .right, .m-item:last-child label").text().trim() || ""; const type = $(el).find(".info .meta .m-item:nth-child(2) label").text().trim() || "";
const rating = $(el).find(".m-item.rated span, .rating").text().trim() || ""; const rating = $(el).find(".info .meta .m-item.rated span").text().trim() || "";
const genres = [];
$(el).find(".genre a").each((j, g) => {
genres.push($(g).text().trim());
});
if (slug) { if (slug) {
results.push({ results.push({
@@ -83,8 +79,7 @@ const extractFilter = async (params) => {
dub, dub,
total, total,
type, type,
rating, rating
genres
}); });
} }
}); });
+8 -7
View File
@@ -1,5 +1,4 @@
import * as cheerio from "cheerio"; import * as cheerio from "cheerio";
import axios from "axios";
import { headers } from "../configs/header.config.js"; import { headers } from "../configs/header.config.js";
import { URLS } from "../configs/dataUrl.js"; import { URLS } from "../configs/dataUrl.js";
import { countPages } from "../helper/countPages.helper.js"; import { countPages } from "../helper/countPages.helper.js";
@@ -11,20 +10,22 @@ const extractPopular = async (page = 1) => {
const totalPages = countPages($); const totalPages = countPages($);
const results = []; const results = [];
$(".film_list-wrap .flw-item, .film-detail").each((i, el) => { $("#list-items > .item").each((i, el) => {
const slug = $(el).find("a").attr("href")?.split("/watch/").pop() || ""; const slug = $(el).find("a").attr("href")?.split("/watch/").pop() || "";
const poster = $(el).find("img").attr("src") || ""; const poster = $(el).find(".ani.poster.tip > a > img").attr("src") || "";
const title = $(el).find(".film-name a, .name.d-title").text().trim() || ""; const title = $(el).find(".info .b1 a.name.d-title").text().trim() || "";
const japaneseTitle = $(el).find(".name.d-title").attr("data-jp") || ""; const japaneseTitle = $(el).find(".info .b1 a.name.d-title").attr("data-jp") || "";
const animeId = $(el).find(".ani.poster.tip").attr("data-tip") || "";
const sub = parseInt($(el).find(".ep-status.sub span").text().trim()) || 0; const sub = parseInt($(el).find(".ep-status.sub span").text().trim()) || 0;
const dub = parseInt($(el).find(".ep-status.dub span").text().trim()) || 0; const dub = parseInt($(el).find(".ep-status.dub span").text().trim()) || 0;
const total = parseInt($(el).find(".ep-status.total span").text().trim()) || 0; const total = parseInt($(el).find(".ep-status.total span").text().trim()) || 0;
const type = $(el).find(".meta .inner .right, .fdi-item:nth-child(2)").text().trim() || ""; const type = $(el).find(".info .meta .m-item:nth-child(2) label").text().trim() || "";
const rating = $(el).find(".rating, .fdi-item:nth-child(3)").text().trim() || ""; const rating = $(el).find(".info .meta .m-item.rated span").text().trim() || "";
if (slug) { if (slug) {
results.push({ results.push({
slug, slug,
animeId,
poster, poster,
title, title,
japaneseTitle, japaneseTitle,
+22 -15
View File
@@ -5,34 +5,41 @@ import { URLS } from "../configs/dataUrl.js";
const extractRandom = async () => { const extractRandom = async () => {
try { try {
const { data } = await axios.get(URLS.random, { const { data, request, response } = await axios.get(URLS.random, {
headers, headers,
maxRedirects: 5 maxRedirects: 5,
validateStatus: (status) => status < 400
}); });
const finalUrl = request?.responseURL || URLS.random;
const slug = finalUrl.split("/watch/").pop() || "";
const $ = cheerio.load(data); const $ = cheerio.load(data);
const slug = window.location.pathname.split("/watch/").pop() || ""; const title = $("h1[itemprop='name'].title.d-title").text().trim() || "";
const title = $(".film-name h1, .anisc-detail .film-name").text().trim() || ""; const japaneseTitle = $("h1[itemprop='name'].title.d-title").attr("data-jp") || "";
const japaneseTitle = $(".film-name .alternate-title, .anisc-detail .film-name small").text().trim() || ""; const poster = $("img[itemprop='image']").attr("src") || "";
const poster = $(".film-poster img").attr("src") || ""; const type = $(".bmeta .meta:first-child > div:nth-child(1) span").text().trim() || "";
const type = $(".film-info .row .col1 .item:first-child .name").text().trim() || ""; const synopsis = $(".synopsis .content").text().trim() || "";
const rating = $("#w-rating .score .value").text().trim() || "";
const animeId = parseInt($("#watch-main").attr("data-id")) || 0;
const animeInfo = {}; const genres = [];
$(".anisc-info .item").each((i, el) => { $(".bmeta .meta:first-child > div:nth-child(5) span a[href*='/genre/']").each((i, el) => {
const label = $(el).find(".name").text().trim().replace(":", ""); genres.push($(el).text().trim());
const value = $(el).find(".text").text().trim();
if (label && value) {
animeInfo[label] = value;
}
}); });
return { return {
slug, slug,
animeId,
title, title,
japaneseTitle, japaneseTitle,
poster, poster,
type, type,
animeInfo synopsis,
rating,
genres,
url: finalUrl
}; };
} catch (error) { } catch (error) {
throw error; throw error;
+21 -13
View File
@@ -12,20 +12,26 @@ const extractSearchResults = async (keyword, page = 1) => {
const totalPages = countPages($); const totalPages = countPages($);
const results = []; const results = [];
$(".film_list-wrap .flw-item, .film-detail").each((i, el) => { $("#list-items > .item").each((i, el) => {
const slug = $(el).find("a").attr("href")?.split("/watch/").pop() || ""; const slug = $(el).find("a").attr("href")?.split("/watch/").pop() || "";
const poster = $(el).find("img").attr("src") || ""; const poster = $(el).find(".ani.poster.tip > a > img").attr("src") || "";
const title = $(el).find(".film-name a, .name.d-title").text().trim() || ""; const title = $(el).find(".info .b1 a.name.d-title").text().trim() || "";
const japaneseTitle = $(el).find(".name.d-title").attr("data-jp") || ""; const japaneseTitle = $(el).find(".info .b1 a.name.d-title").attr("data-jp") || "";
const animeId = $(el).find(".ani.poster.tip").attr("data-tip") || "";
const sub = parseInt($(el).find(".ep-status.sub span").text().trim()) || 0; const sub = parseInt($(el).find(".ep-status.sub span").text().trim()) || 0;
const dub = parseInt($(el).find(".ep-status.dub span").text().trim()) || 0; const dub = parseInt($(el).find(".ep-status.dub span").text().trim()) || 0;
const total = parseInt($(el).find(".ep-status.total span").text().trim()) || 0; const total = parseInt($(el).find(".ep-status.total span").text().trim()) || 0;
const type = $(el).find(".meta .inner .right, .fdi-item:nth-child(2)").text().trim() || ""; const type = $(el).find(".info .meta .m-item:nth-child(2) label").text().trim() || "";
const rating = $(el).find(".rating, .fdi-item:nth-child(3)").text().trim() || ""; const rating = $(el).find(".info .meta .m-item.rated span").text().trim() || "";
const genres = [];
$(el).find(".info .b1 .genre a").each((j, g) => {
genres.push($(g).text().trim());
});
if (slug) { if (slug) {
results.push({ results.push({
slug, slug,
animeId,
poster, poster,
title, title,
japaneseTitle, japaneseTitle,
@@ -33,7 +39,8 @@ const extractSearchResults = async (keyword, page = 1) => {
dub, dub,
total, total,
type, type,
rating rating,
genres
}); });
} }
}); });
@@ -51,17 +58,18 @@ const extractSearchSuggestions = async (keyword) => {
const $ = cheerio.load(data); const $ = cheerio.load(data);
const suggestions = []; const suggestions = [];
$(".search-suggest .film-detail, .nav-item a").each((i, el) => { $("#list-items > .item").each((i, el) => {
const slug = $(el).attr("href")?.split("/watch/").pop() || ""; const slug = $(el).find("a").attr("href")?.split("/watch/").pop() || "";
const poster = $(el).find("img").attr("src") || ""; const poster = $(el).find(".ani.poster.tip > a > img").attr("src") || "";
const title = $(el).find(".film-name, .name").text().trim() || ""; const title = $(el).find(".info .b1 a.name.d-title").text().trim() || "";
const type = $(el).find(".info .meta .m-item:nth-child(2) label").text().trim() || "";
if (slug) { if (slug) {
suggestions.push({ slug, poster, title }); suggestions.push({ slug, poster, title, type });
} }
}); });
return suggestions; return suggestions.slice(0, 10);
} catch (error) { } catch (error) {
throw error; throw error;
} }
+10 -7
View File
@@ -10,18 +10,21 @@ const extractSuggestions = async (keyword) => {
const $ = cheerio.load(data); const $ = cheerio.load(data);
const suggestions = []; const suggestions = [];
$(".search-suggest .film-detail, .nav-item a").each((i, el) => { $("#list-items > .item").each((i, el) => {
const slug = $(el).attr("href")?.split("/watch/").pop() || ""; const slug = $(el).find("a").attr("href")?.split("/watch/").pop() || "";
const poster = $(el).find("img").attr("src") || ""; const poster = $(el).find(".ani.poster.tip > a > img").attr("src") || "";
const title = $(el).find(".film-name, .name").text().trim() || ""; const title = $(el).find(".info .b1 a.name.d-title").text().trim() || "";
const type = $(el).find(".fdi-item:first-child").text().trim() || ""; const japaneseTitle = $(el).find(".info .b1 a.name.d-title").attr("data-jp") || "";
const type = $(el).find(".info .meta .m-item:nth-child(2) label").text().trim() || "";
const sub = parseInt($(el).find(".ep-status.sub span").text().trim()) || 0;
const dub = parseInt($(el).find(".ep-status.dub span").text().trim()) || 0;
if (slug) { if (slug) {
suggestions.push({ slug, poster, title, type }); suggestions.push({ slug, poster, title, japaneseTitle, type, sub, dub });
} }
}); });
return suggestions; return suggestions.slice(0, 10);
} catch (error) { } catch (error) {
throw error; throw error;
} }
+26 -42
View File
@@ -9,57 +9,38 @@ const extractWatchPage = async (slug, ep) => {
const { data } = await axios.get(url, { headers }); const { data } = await axios.get(url, { headers });
const $ = cheerio.load(data); const $ = cheerio.load(data);
const animeId = parseInt($("#watch-main").attr("data-id")) || parseInt($("div[data-id]").attr("data-id")) || 0; const animeId = parseInt($("#watch-main").attr("data-id")) || 0;
const animeUrl = $("#watch-main").attr("data-url") || ""; const animeUrl = $("#watch-main").attr("data-url") || "";
const title = $(".heading-name a").text().trim() || slug; const title = $("h1[itemprop='name'].title.d-title").text().trim() || slug;
const japaneseTitle = $(".heading-name .alternate-title").text().trim() || ""; const japaneseTitle = $("h1[itemprop='name'].title.d-title").attr("data-jp") || "";
const episodeNumber = parseInt(ep) || 0; const episodeNumber = parseInt(ep) || 0;
const synopsis = $(".synopsis .content, .film-description .text").text().trim() || ""; const synopsis = $(".synopsis .content").text().trim() || "";
const rating = $("#w-rating .score .value").text().trim() || "";
const poster = $("img[itemprop='image']").attr("src") || "";
const backgroundImage = $("#player").css("background-image")?.match(/url\(['"]?(.+?)['"]?\)/)?.[1] || "";
const animeInfo = {}; const type = $(".bmeta .meta:first-child > div:nth-child(1) span").text().trim() || "";
$(".bmeta .meta div").each((i, el) => { const status = $(".bmeta .meta:first-child > div:nth-child(4) span a").text().trim() || "";
const label = $(el).find("strong, b").text().trim().replace(":", ""); const malScore = $(".bmeta .meta:nth-child(2) > div:nth-child(1) span").text().trim() || "";
const value = $(el).find("span, a").text().trim(); const duration = $(".bmeta .meta:nth-child(2) > div:nth-child(2) span").text().trim() || "";
if (label && value) { const episodes = $(".bmeta .meta:nth-child(2) > div:nth-child(3) span").text().trim() || "";
animeInfo[label] = value;
}
});
const genres = []; const genres = [];
$(".bmeta .meta div:contains('Genres') a").each((i, el) => { $(".bmeta .meta:first-child > div:nth-child(5) span a[href*='/genre/']").each((i, el) => {
const genre = $(el).text().trim(); genres.push($(el).text().trim());
if (genre) genres.push(genre);
}); });
const rating = $(".brating .score .value, #w-rating .score").text().trim() || ""; const studios = [];
const MALScore = $(".bmeta .meta div:contains('MAL') span").text().trim() || ""; $(".bmeta .meta:nth-child(2) > div:nth-child(4) span a[itemprop='director'] span[itemprop='name']").each((i, el) => {
studios.push($(el).text().trim());
const poster = $(".poster img[itemprop='image'], .binfo .poster img").attr("src") || ""; });
const backgroundImage = $("#player").css("background-image")?.match(/url\(['"]?(.+?)['"]?\)/)?.[1] || "";
const nextEpisodeDate = $(".next-episode, .alert.next-episode").text().trim() || ""; const nextEpisodeDate = $(".next-episode, .alert.next-episode").text().trim() || "";
const nextEpisodeTimestamp = parseInt($(".count-down").attr("data-target")) || 0; const nextEpisodeTimestamp = parseInt($(".count-down").attr("data-target")) || 0;
const episodeList = [];
$("#w-episodes .ep-item, .episodes-list a, .ss-list a").each((i, el) => {
const epId = $(el).attr("data-id") || $(el).attr("data-ep-id") || "";
const epNum = parseInt($(el).find(".ep-number, .ep-num").text().trim()) || i + 1;
const epTitle = $(el).find(".ep-title, .ep-name").text().trim() || "";
const isActive = $(el).hasClass("active") || $(el).hasClass("selected") || false;
if (epId || epNum) {
episodeList.push({
id: epId,
episode_no: epNum,
title: epTitle,
active: isActive
});
}
});
const servers = []; const servers = [];
$("#w-servers .servers .type li, #w-servers li").each((i, el) => { $("#w-servers .servers .type li, #w-servers li[data-link-id]").each((i, el) => {
const linkId = $(el).attr("data-link-id") || ""; const linkId = $(el).attr("data-link-id") || "";
const epId = $(el).attr("data-ep-id") || ""; const epId = $(el).attr("data-ep-id") || "";
const cmId = $(el).attr("data-cmid") || ""; const cmId = $(el).attr("data-cmid") || "";
@@ -80,7 +61,7 @@ const extractWatchPage = async (slug, ep) => {
}); });
const trending = []; const trending = [];
$(".w-side-section .scaff.side.items a.item").each((i, el) => { $(".w-side-section:first .scaff.side.items a.item, #watch-order a.item").each((i, el) => {
const trendSlug = $(el).attr("href")?.split("/watch/").pop() || ""; const trendSlug = $(el).attr("href")?.split("/watch/").pop() || "";
const trendPoster = $(el).find(".poster img").attr("src") || ""; const trendPoster = $(el).find(".poster img").attr("src") || "";
const trendTitle = $(el).find(".name").text().trim() || ""; const trendTitle = $(el).find(".name").text().trim() || "";
@@ -123,15 +104,18 @@ const extractWatchPage = async (slug, ep) => {
japaneseTitle, japaneseTitle,
episodeNumber, episodeNumber,
synopsis, synopsis,
animeInfo, type,
status,
malScore,
duration,
episodes,
studios,
genres, genres,
rating, rating,
MALScore,
poster, poster,
backgroundImage, backgroundImage,
nextEpisodeDate, nextEpisodeDate,
nextEpisodeTimestamp, nextEpisodeTimestamp,
episodeList,
servers, servers,
trending, trending,
recommended recommended
-8
View File
@@ -214,14 +214,6 @@ const createApiRoutes = (app, jsonResponse, jsonError) => {
} }
}); });
app.get("/api/status/:status", async (req, res, next) => {
try {
await getStatus(req, res, next);
} catch (error) {
jsonError(res, error.message);
}
});
categoryRoutes(app, jsonResponse, jsonError); categoryRoutes(app, jsonResponse, jsonError);
}; };
+20 -1
View File
@@ -1,7 +1,26 @@
import { getCategory } from "../controllers/category.controller.js"; import { getCategory } from "../controllers/category.controller.js";
const categoryRoutes = (app, jsonResponse, jsonError) => { const categoryRoutes = (app, jsonResponse, jsonError) => {
app.get("/api/:category", async (req, res, next) => { app.get("/api/genre/:genre", async (req, res, next) => {
req.params.category = `genre/${req.params.genre}`;
try {
await getCategory(req, res, next);
} catch (error) {
jsonError(res, error.message);
}
});
app.get("/api/type/:type", async (req, res, next) => {
req.params.category = `type/${req.params.type}`;
try {
await getCategory(req, res, next);
} catch (error) {
jsonError(res, error.message);
}
});
app.get("/api/status/:status", async (req, res, next) => {
req.params.category = `status/${req.params.status}`;
try { try {
await getCategory(req, res, next); await getCategory(req, res, next);
} catch (error) { } catch (error) {