mirror of
https://github.com/Shineii86/AniKotoAPI.git
synced 2026-10-22 10:55:10 +00:00
feat: upgrade to v2.0.0 with multi-mirror fallback, new endpoints, and LRU cache
- Add multi-mirror fallback system with 5 mirror domains - Add LRU cache with configurable TTL per endpoint - Add response compression (gzip) - Add Seasons endpoint (/api/seasons/:id) - Add Watch Order endpoint (/api/watch-order/:id) - Add Latest Updated endpoint (/api/latest-updated) - Add Download endpoint (/api/download) - Add cache stats endpoint (/api/cache/stats) - Add mirror status/reset endpoints (/api/mirrors, /api/mirrors/reset) - Add configurable rate limiting with X-RateLimit headers - Add request timeouts and entity too large handling - Update documentation with all new endpoints - Update CHANGELOG.md with v2.0.0 entry - Update package.json version to 2.0.0 - Add 6 AI agent prompts for development
This commit is contained in:
@@ -4,8 +4,9 @@
|
||||
* Repository: https://github.com/Shineii86/AniKotoAPI
|
||||
*
|
||||
* @description
|
||||
* Extracts season information for a specific anime via API.
|
||||
* Returns season data from the internal API endpoint.
|
||||
* Extracts season information for a specific anime from the
|
||||
* anikototv.to AJAX endpoint. Returns all seasons/OVAs/specials
|
||||
* for a given anime series.
|
||||
*
|
||||
* @exports
|
||||
* extractSeasons
|
||||
@@ -15,30 +16,82 @@
|
||||
* ======= • ======= • ======= • ======= • =======• =======
|
||||
*/
|
||||
|
||||
import axios from "axios";
|
||||
import { headers } from "../configs/header.config.js";
|
||||
import { BASE_URL } from "../configs/dataUrl.js";
|
||||
import * as cheerio from "cheerio";
|
||||
import { fetchWithMirror } from "../helper/mirror.helper.js";
|
||||
|
||||
// ══════════════════════════════════════════════════════════════
|
||||
// SEASONS API EXTRACTION
|
||||
// ══════════════════════════════════════════════════════════════
|
||||
|
||||
// ---- FEATURE: Extract season data from internal API ----
|
||||
/**
|
||||
* Fetches season information for a specific anime from the internal API.
|
||||
* Returns an array of season objects or empty array on error.
|
||||
* Fetches and parses season information for an anime.
|
||||
* Scrapes the AJAX endpoint that returns season carousel HTML.
|
||||
*
|
||||
* @param {string} animeId - The anime ID to fetch seasons for
|
||||
* @returns {Promise<Array<Object>>} Array of season objects or empty array
|
||||
* @param {string|number} animeId - The numeric anime ID
|
||||
* @returns {Promise<Object>} Object with animeId, totalSeasons, and seasons array
|
||||
*
|
||||
* @example
|
||||
* const seasons = await extractSeasons("12345");
|
||||
* console.log(seasons.length); // Number of seasons
|
||||
* const seasons = await extractSeasons(7174);
|
||||
* console.log(seasons.totalSeasons); // number of seasons
|
||||
* console.log(seasons.seasons[0].title); // first season title
|
||||
*/
|
||||
const extractSeasons = async (animeId) => {
|
||||
throw new Error("Seasons endpoint not available - source site does not provide this data");
|
||||
try {
|
||||
const { data } = await fetchWithMirror(`/api/seasons/${animeId}`, {
|
||||
headers: { "X-Requested-With": "XMLHttpRequest" }
|
||||
});
|
||||
|
||||
// Handle both string HTML and JSON responses
|
||||
let raw = "";
|
||||
if (typeof data === "string") {
|
||||
try {
|
||||
const json = JSON.parse(data);
|
||||
raw = json.result || data;
|
||||
} catch (e) {
|
||||
raw = data;
|
||||
}
|
||||
} else if (data?.result) {
|
||||
raw = data.result;
|
||||
} else if (data?.data) {
|
||||
raw = data.data;
|
||||
}
|
||||
|
||||
const $ = cheerio.load(raw);
|
||||
|
||||
const seasons = [];
|
||||
|
||||
// Try multiple selectors to handle different HTML structures
|
||||
const selectors = [
|
||||
".swiper-slide.season",
|
||||
".seasons .swiper-slide",
|
||||
"#w-seasons .swiper-slide",
|
||||
".swiper-slide"
|
||||
];
|
||||
|
||||
for (const selector of selectors) {
|
||||
$(selector).each((_, el) => {
|
||||
const link = $(el).find("a").first();
|
||||
const href = link.attr("href") || "";
|
||||
const style = link.attr("style") || "";
|
||||
const poster = style.match(/url\(['"]?(.+?)['"]?\)/)?.[1] || "";
|
||||
const title = $(el).find(".name").text().trim() ||
|
||||
link.text().trim() || "";
|
||||
|
||||
// Extract slug from href
|
||||
const slug = href.split("/watch/").pop() || "";
|
||||
|
||||
if (slug || title) {
|
||||
seasons.push({ title, slug, poster, url: href });
|
||||
}
|
||||
});
|
||||
|
||||
if (seasons.length > 0) break;
|
||||
}
|
||||
|
||||
return {
|
||||
animeId,
|
||||
totalSeasons: seasons.length,
|
||||
seasons
|
||||
};
|
||||
} catch (error) {
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
export { extractSeasons };
|
||||
|
||||
// ══════════════════════════════════════════════════════════════ END: seasons.extractor.js
|
||||
|
||||
Reference in New Issue
Block a user