mirror of
https://github.com/Shineii86/AniKotoAPI.git
synced 2026-10-22 02:45:10 +00:00
fix: Full diagnostic sweep — bugs, dead code, unused imports, docs accuracy
Code fixes: - seasons/watchOrder: Remove dead ternary (both branches identical) - mirror.helper: Remove redundant ternary (both branches identical) - topAnimeRankings: Fix JSDoc (day/week/month, not top/newest) - apiRoutes: Fix proxy domain validation (exact match, not includes) - apiRoutes: Fix proxy .m3u8 check (pathname, not hostname) - pagination: Fix totalItems calculation (was overestimating) - server.js: Remove dead has404File variable - server.js: Remove duplicate /api/cache/stats route - server.js: Remove unused getCacheStats import Unused imports removed: - animeInfo: URLS from dataUrl.js - episodeList: headers from header.config.js - episodeListAjax: axios - trending: URLS from dataUrl.js - trendingSidebar: URLS from dataUrl.js - upcomingAnime: parseListItems - watchPage: URLS, BASE_URL from dataUrl.js Docs fixes: - README: 33->38 endpoints, 24->28 controllers, 26->28 extractors - README: Cache TTL 5min->per-endpoint, Timeout 15s->30s - docs/index.md: 33->38 endpoints - architecture.md: LRU cache strategy, remove stale cookie-parser - agents: compression version, extractor/controller counts - agents: domain typo, stale CORS dep, version badge
This commit is contained in:
@@ -138,7 +138,7 @@ flowchart TD
|
||||
### ⚡ Core
|
||||
- **Real-time scraping** from anikototv.to
|
||||
- **Smart caching** with LRU and configurable TTL per endpoint
|
||||
- **33 RESTful endpoints** covering all data
|
||||
- **38 RESTful endpoints** covering all data
|
||||
- **AJAX episode loading** for accurate data
|
||||
- **Mapper API integration** for extra servers
|
||||
- **Graceful error handling** per endpoint
|
||||
@@ -320,7 +320,7 @@ AniKotoAPI/
|
||||
│ │ ├── 📄 header.config.js # 📋 Request headers
|
||||
│ │ └── 📄 ids.config.js # 🏷️ Genre/Type/Status/Source/Season ID mappings
|
||||
│ │
|
||||
│ ├── 📂 controllers/ # 🎮 Route handlers (24 files)
|
||||
│ ├── 📂 controllers/ # 🎮 Route handlers (28 files)
|
||||
│ │ ├── 📄 homeInfo.controller.js
|
||||
│ │ ├── 📄 animeInfo.controller.js
|
||||
│ │ ├── 📄 search.controller.js
|
||||
@@ -332,7 +332,7 @@ AniKotoAPI/
|
||||
│ │ ├── 📄 watchPage.controller.js
|
||||
│ │ └── 📄 ... (15 more)
|
||||
│ │
|
||||
│ ├── 📂 extractors/ # 🔍 HTML scrapers (26 files)
|
||||
│ ├── 📂 extractors/ # 🔍 HTML scrapers (28 files)
|
||||
│ │ ├── 📄 homeInfo.extractor.js # 🏠 Home page data
|
||||
│ │ ├── 📄 search.extractor.js # 🔍 Search results
|
||||
│ │ ├── 📄 animeInfo.extractor.js # 📺 Anime details
|
||||
@@ -2298,8 +2298,8 @@ CMD ["node", "server.js"]
|
||||
|:---|:---|
|
||||
| ⚡ Cached response | ~200ms |
|
||||
| 🔄 Fresh fetch | ~1-3s |
|
||||
| 💾 Cache TTL | 5 minutes |
|
||||
| ⏱️ Timeout per request | 15 seconds |
|
||||
| 💾 Cache TTL | 3 min–1 hour (per-endpoint) |
|
||||
| ⏱️ Timeout per request | 30 seconds |
|
||||
| 🎬 Total anime indexed | 10,000+ |
|
||||
| 📦 Total codebase | ~120KB |
|
||||
|
||||
@@ -2390,7 +2390,7 @@ Yes! CORS is enabled for all origins (<code>*</code>). Just make fetch requests
|
||||
<details>
|
||||
<summary><b>🔄 How often does the data refresh?</b></summary>
|
||||
<br/>
|
||||
The cache TTL is 5 minutes. After that, the next request triggers a fresh scrape from anikototv.to. This keeps data relatively fresh while reducing load on the source site.
|
||||
The cache uses an LRU strategy with per-endpoint TTLs ranging from 3 minutes (search, stream) to 1 hour (schedule). After TTL expiry, the next request triggers a fresh scrape from anikototv.to.
|
||||
</details>
|
||||
|
||||
<details>
|
||||
|
||||
@@ -12,7 +12,7 @@ You are **Backend Architect** for AniKotoAPI, a free REST API scraping anime dat
|
||||
## Your Identity
|
||||
|
||||
- **Project**: AniKotoAPI v2.2.0 — https://github.com/Shineii86/AniKotoAPI
|
||||
- **Stack**: Node.js (ESM modules), Express 4.21, Cheerio 1.0, Axios 1.8, compression 1.8
|
||||
- **Stack**: Node.js (ESM modules), Express 4.21, Cheerio 1.0, Axios 1.8, compression 1.7
|
||||
- **Deployment**: Vercel serverless (read-only filesystem, `/tmp` only writable)
|
||||
- **Data Source**: anikototv.to (5 mirror domains: anikototv.to, anikoto.cz, anikoto.me, anikoto.net, anikototv.se)
|
||||
|
||||
@@ -30,8 +30,8 @@ Client → Vercel Edge/Express → Rate Limiter → CORS + Security Headers →
|
||||
- `src/helper/mirror.helper.js` — Multi-mirror fallback with health checks, session caching
|
||||
- `src/helper/extractPages.helper.js` — Page fetching using `fetchWithMirror()`
|
||||
- `src/helper/parseListItem.helper.js` — Shared list item parser (DRY across extractors)
|
||||
- `src/extractors/` — 26 HTML scrapers using Cheerio selectors
|
||||
- `src/controllers/` — 26 route handlers
|
||||
- `src/extractors/` — 28 HTML scrapers using Cheerio selectors
|
||||
- `src/controllers/` — 28 route handlers
|
||||
- `src/configs/ids.config.js` — Genre/Type/Status/Rating/Sort/Source/Season ID mappings
|
||||
|
||||
### Endpoints (38 total)
|
||||
|
||||
@@ -91,7 +91,7 @@ git remote set-url origin https://github.com/Shineii86/AniKotoAPI.git
|
||||
|
||||
### Version Bump Checklist
|
||||
1. [ ] Update `package.json` version
|
||||
2. [ ] Update README.md badge (`Version-2.0.0`)
|
||||
2. [ ] Update README.md badge (`Version-2.2.0`)
|
||||
3. [ ] Add CHANGELOG.md entry (prepend at top)
|
||||
4. [ ] Update OpenAPI spec version in `apiRoutes.js`
|
||||
5. [ ] Update endpoint count if changed
|
||||
|
||||
@@ -38,7 +38,7 @@ You are **Security Architect** for AniKotoAPI, responsible for securing a public
|
||||
```javascript
|
||||
// ALLOWED_ORIGINS env var (comma-separated)
|
||||
// Default: allow all origins
|
||||
// Only GET method allowed
|
||||
// GET and POST methods allowed
|
||||
// Handles OPTIONS preflight
|
||||
```
|
||||
|
||||
@@ -99,8 +99,7 @@ You are **Security Architect** for AniKotoAPI, responsible for securing a public
|
||||
| express | 4.21 | Check for known CVEs |
|
||||
| axios | 1.8 | Check for SSRF bugs |
|
||||
| cheerio | 1.0-rc.12 | Check for prototype pollution |
|
||||
| cors | 2.8 | Check for bypass issues |
|
||||
| compression | 1.8 | Check for BREACH attacks |
|
||||
| compression | 1.7 | Check for BREACH attacks |
|
||||
| dotenv | 16.4 | Check for env injection |
|
||||
|
||||
## What You Do
|
||||
|
||||
@@ -19,7 +19,7 @@ You are **Technical Writer** for AniKotoAPI, responsible for maintaining all doc
|
||||
|
||||
```
|
||||
AniKotoAPI/
|
||||
├── README.md # Main docs (2200+ lines)
|
||||
├── README.md # Main docs (2600+ lines)
|
||||
├── CHANGELOG.md # Version history (append only)
|
||||
├── CONTRIBUTING.md # Contribution guidelines
|
||||
├── docs/
|
||||
@@ -27,7 +27,8 @@ AniKotoAPI/
|
||||
│ ├── endpoints.md # Full API reference (38 endpoints)
|
||||
│ ├── streaming.md # 3-step streaming flow guide
|
||||
│ ├── examples.md # cURL, JS, Python examples
|
||||
│ └── architecture.md # Project structure, tech stack
|
||||
│ ├── architecture.md # Project structure, tech stack
|
||||
│ └── testing.md # Integration test suite & benchmarks
|
||||
└── public/
|
||||
├── index.html # Landing page
|
||||
├── tos.html # Terms of Service
|
||||
|
||||
@@ -178,8 +178,8 @@ Client Response
|
||||
|
||||
## Caching Strategy
|
||||
|
||||
- **Type:** In-memory Map
|
||||
- **TTL:** 5 minutes (300,000ms)
|
||||
- **Type:** LRU Cache (Map-based with TTL expiration)
|
||||
- **TTL:** Per-endpoint (3 min for search/stream, 5 min for home/trending, 10 min for info, 1 hour for schedule)
|
||||
- **Key:** Full request URL
|
||||
- **Behavior:** First request fetches from source, subsequent requests served from cache
|
||||
- **Reset:** TTL resets on each access
|
||||
@@ -252,6 +252,5 @@ Error responses:
|
||||
| express | ^4.21.0 | Web framework |
|
||||
| cheerio | ^1.0.0-rc.12 | HTML parsing |
|
||||
| axios | ^1.8.0 | HTTP requests |
|
||||
| cookie-parser | ^1.4.7 | Cookie parsing |
|
||||
| dotenv | ^16.4.0 | Environment variables |
|
||||
| compression | ^1.7.4 | Response compression |
|
||||
|
||||
+1
-1
@@ -57,7 +57,7 @@ curl "https://anikototvapi.vercel.app/api/info?id=naruto-eybxz"
|
||||
|
||||
## Features
|
||||
|
||||
- **33 Endpoints** — Home, search, info, episodes, streaming, schedule, seasons, watch order, download, and more
|
||||
- **38 Endpoints** — Home, search, info, episodes, streaming, schedule, seasons, watch order, download, and more
|
||||
- **No API Key** — Just make requests, no registration needed
|
||||
- **LRU Cache** — Configurable TTL per endpoint type (3min to 60min)
|
||||
- **Multi-Mirror Fallback** — Automatic failover across 5 mirror domains
|
||||
|
||||
@@ -25,7 +25,6 @@ import crypto from "crypto";
|
||||
import { fileURLToPath } from "url";
|
||||
import { dirname } from "path";
|
||||
import { createApiRoutes } from "./src/routes/apiRoutes.js";
|
||||
import { getCacheStats } from "./src/helper/cache.helper.js";
|
||||
import { addCreatorInfo } from "./src/middleware/creatorInfo.js";
|
||||
|
||||
dotenv.config();
|
||||
@@ -39,7 +38,6 @@ const PORT = process.env.PORT || 4444;
|
||||
const __filename = fileURLToPath(import.meta.url);
|
||||
const __dirname = dirname(__filename);
|
||||
const publicDir = path.join(process.cwd(), "public");
|
||||
const has404File = fs.existsSync(path.join(publicDir, "404.html"));
|
||||
const allowedOrigins = process.env.ALLOWED_ORIGINS?.split(",");
|
||||
|
||||
// ---- FEATURE: Response compression ----
|
||||
@@ -188,11 +186,6 @@ app.use((req, res, next) => {
|
||||
next();
|
||||
});
|
||||
|
||||
// ---- FEATURE: Cache stats endpoint ----
|
||||
app.get("/api/cache/stats", (req, res) => {
|
||||
res.json({ success: true, results: getCacheStats() });
|
||||
});
|
||||
|
||||
// ---- FEATURE: Creator info middleware (injects attribution into all responses) ----
|
||||
app.use(addCreatorInfo);
|
||||
|
||||
|
||||
@@ -17,7 +17,6 @@
|
||||
*/
|
||||
|
||||
import * as cheerio from "cheerio";
|
||||
import { URLS } from "../configs/dataUrl.js";
|
||||
import { fetchWithMirror } from "../helper/mirror.helper.js";
|
||||
|
||||
// ══════════════════════════════════════════════════════════════
|
||||
|
||||
@@ -17,8 +17,6 @@
|
||||
*/
|
||||
|
||||
import * as cheerio from "cheerio";
|
||||
import { headers } from "../configs/header.config.js";
|
||||
import { URLS, BASE_URL } from "../configs/dataUrl.js";
|
||||
import { fetchWithMirror } from "../helper/mirror.helper.js";
|
||||
|
||||
// ══════════════════════════════════════════════════════════════
|
||||
|
||||
@@ -17,7 +17,6 @@
|
||||
*/
|
||||
|
||||
import * as cheerio from "cheerio";
|
||||
import axios from "axios";
|
||||
import { headers } from "../configs/header.config.js";
|
||||
import { fetchWithMirror } from "../helper/mirror.helper.js";
|
||||
|
||||
|
||||
@@ -40,7 +40,7 @@ import { fetchWithMirror } from "../helper/mirror.helper.js";
|
||||
*/
|
||||
const extractSeasons = async (slugOrId) => {
|
||||
try {
|
||||
const path = /^\d+$/.test(slugOrId) ? `/watch/${slugOrId}` : `/watch/${slugOrId}`;
|
||||
const path = `/watch/${slugOrId}`;
|
||||
const { data } = await fetchWithMirror(path);
|
||||
|
||||
const html = typeof data === "string" ? data : String(data);
|
||||
|
||||
@@ -28,7 +28,7 @@ import { fetchWithMirror } from "../helper/mirror.helper.js";
|
||||
* Fetches and parses the top anime rankings from the homepage.
|
||||
* Supports switching between "top" (all-time) and "newest" rankings.
|
||||
*
|
||||
* @param {string} sort - Ranking sort mode: "top" or "newest" (default: "top")
|
||||
* @param {string} sort - Ranking sort mode: "day", "week", or "month" (default: "day")
|
||||
* @returns {Promise<Array<Object>>} Array of ranked anime objects
|
||||
* @returns {number} return[].rank - Ranking position (1-9)
|
||||
* @returns {string} return[].slug - URL slug for the anime
|
||||
|
||||
@@ -16,7 +16,6 @@
|
||||
*/
|
||||
|
||||
import * as cheerio from "cheerio";
|
||||
import { URLS } from "../configs/dataUrl.js";
|
||||
import { fetchWithMirror } from "../helper/mirror.helper.js";
|
||||
|
||||
// ══════════════════════════════════════════════════════════════
|
||||
|
||||
@@ -16,7 +16,6 @@
|
||||
*/
|
||||
|
||||
import * as cheerio from "cheerio";
|
||||
import { URLS } from "../configs/dataUrl.js";
|
||||
import { fetchWithMirror } from "../helper/mirror.helper.js";
|
||||
|
||||
// ══════════════════════════════════════════════════════════════
|
||||
|
||||
@@ -17,7 +17,6 @@
|
||||
|
||||
import * as cheerio from "cheerio";
|
||||
import { fetchWithMirror } from "../helper/mirror.helper.js";
|
||||
import { parseListItems } from "../helper/parseListItem.helper.js";
|
||||
|
||||
// ══════════════════════════════════════════════════════════════
|
||||
// UPCOMING ANIME EXTRACTION
|
||||
|
||||
@@ -40,7 +40,7 @@ import { fetchWithMirror } from "../helper/mirror.helper.js";
|
||||
*/
|
||||
const extractWatchOrder = async (slugOrId) => {
|
||||
try {
|
||||
const path = /^\d+$/.test(slugOrId) ? `/watch/${slugOrId}` : `/watch/${slugOrId}`;
|
||||
const path = `/watch/${slugOrId}`;
|
||||
const { data } = await fetchWithMirror(path);
|
||||
|
||||
const html = typeof data === "string" ? data : String(data);
|
||||
|
||||
@@ -16,7 +16,6 @@
|
||||
*/
|
||||
|
||||
import * as cheerio from "cheerio";
|
||||
import { URLS, BASE_URL } from "../configs/dataUrl.js";
|
||||
import { fetchWithMirror } from "../helper/mirror.helper.js";
|
||||
|
||||
// ══════════════════════════════════════════════════════════════
|
||||
|
||||
@@ -201,7 +201,7 @@ async function fetchWithMirror(path, options = {}) {
|
||||
console.log(`[MIRROR] Switched to: ${mirror}`);
|
||||
}
|
||||
return {
|
||||
data: returnType === "text" ? response.data : response.data,
|
||||
data: response.data,
|
||||
mirror
|
||||
};
|
||||
}
|
||||
|
||||
@@ -45,7 +45,7 @@
|
||||
const addPaginationMeta = (data, currentPage, itemsPerPage = 30) => {
|
||||
const totalPages = data.totalPages || 1;
|
||||
const page = parseInt(currentPage) || 1;
|
||||
const totalItems = data.data ? data.data.length * totalPages : 0;
|
||||
const totalItems = data.data ? data.data.length + (totalPages - 1) * itemsPerPage : 0;
|
||||
|
||||
return {
|
||||
...data,
|
||||
|
||||
@@ -238,7 +238,7 @@ app.use((req, res, next) => {
|
||||
// NOTE: Validate URL is from known streaming domains
|
||||
const allowedDomains = ["vidtube.site", "vidplay.site", "megaplay.buzz", "megaplay-1.buzz", "cdn.anipixcdn.co"];
|
||||
const urlObj = new URL(url);
|
||||
const isAllowed = allowedDomains.some(d => urlObj.hostname.includes(d)) || urlObj.hostname.endsWith(".m3u8");
|
||||
const isAllowed = allowedDomains.some(d => urlObj.hostname === d || urlObj.hostname.endsWith(`.${d}`)) || urlObj.pathname.endsWith(".m3u8");
|
||||
|
||||
if (!isAllowed) {
|
||||
return res.status(403).json({ success: false, message: "Domain not allowed for proxy" });
|
||||
|
||||
Reference in New Issue
Block a user