mirror of
https://github.com/Shineii86/AniKotoAPI.git
synced 2026-10-22 10:55:10 +00:00
docs: Complete documentation overhaul with live API data + bug fixes
Documentation (5800+ lines total): - endpoints.md: All 43 endpoints documented with real response examples, param tables, response patterns, common workflows, error formats - streaming.md: Complete streaming workflow guide with HLS.js/Plyr/Video.js integration, M3U8 proxy explanation, error handling - examples.md: Working code examples in cURL, JS fetch, Python requests, Node.js axios, HTML/JS player integration (2240 lines) - architecture.md: Full project structure, request flow diagrams, caching strategy, mirror failover, security, deployment notes - index.md: Overview with quick start, feature highlights, endpoint tables Bug fixes: - episodeList.extractor: Parse JSON response before Cheerio (was passing raw JSON string as HTML, causing escaped quotes in IDs) - episodeListAjax.extractor: Same JSON parsing fix - topAnimeRankings.controller: Fix JSDoc and default sort from top to day (actual values are day/week/month, not top/newest) - apiRoutes.js: Fix OpenAPI spec sort description for top-rankings Tests: 35/35 passing
This commit is contained in:
+871
-231
File diff suppressed because it is too large
Load Diff
+280
-26
@@ -6,35 +6,42 @@ Base URL: `https://anikototvapi.vercel.app/api`
|
|||||||
|
|
||||||
## Pagination
|
## Pagination
|
||||||
|
|
||||||
All list endpoints include pagination metadata in the response:
|
Most list endpoints return a `totalPages` field for pagination:
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"success": true,
|
"success": true,
|
||||||
"results": {
|
"results": {
|
||||||
"data": [...],
|
"totalPages": 296,
|
||||||
|
"data": [...]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Use the `page` query parameter to navigate: `?page=2`
|
||||||
|
|
||||||
|
**Only `/api/filter` returns the full pagination object:**
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"results": {
|
||||||
|
"totalPages": 1,
|
||||||
|
"data": [],
|
||||||
"pagination": {
|
"pagination": {
|
||||||
"currentPage": 1,
|
"currentPage": 1,
|
||||||
"totalPages": 294,
|
"totalPages": 1,
|
||||||
"totalItems": 8820,
|
"totalItems": 0,
|
||||||
"itemsPerPage": 30,
|
"itemsPerPage": 30,
|
||||||
"hasNext": true,
|
"hasNext": false,
|
||||||
"hasPrev": false
|
"hasPrev": false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
| Field | Type | Description |
|
**Endpoints with `totalPages` pagination:** `/api/search`, `/api/most-popular`, `/api/new-release`, `/api/newly-added`, `/api/latest-updated`, `/api/az-list/:letter`, `/api/filter`, `/api/genre/:name`, `/api/type/:name`, `/api/status/:name`
|
||||||
|:---|:---|:---|
|
|
||||||
| `currentPage` | `number` | Current page number (1-indexed) |
|
|
||||||
| `totalPages` | `number` | Total number of pages available |
|
|
||||||
| `totalItems` | `number` | Estimated total items across all pages |
|
|
||||||
| `itemsPerPage` | `number` | Number of items per page (default: 30) |
|
|
||||||
| `hasNext` | `boolean` | Whether there is a next page |
|
|
||||||
| `hasPrev` | `boolean` | Whether there is a previous page |
|
|
||||||
|
|
||||||
**Endpoints with pagination:** `/api/search`, `/api/most-popular`, `/api/new-release`, `/api/newly-added`, `/api/latest-updated`, `/api/az-list/:letter`, `/api/filter`, `/api/genre/:name`, `/api/type/:name`, `/api/status/:name`
|
**Endpoints with flat arrays (no pagination):** `/api/spotlight`, `/api/trending`, `/api/top-ten`, `/api/suggestions`, `/api/recently-updated`, `/api/completed`, `/api/upcoming`, `/api/top-rankings`, `/api/mirrors`, `/api/schedule`
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -136,12 +143,14 @@ Get detailed info about an anime.
|
|||||||
|
|
||||||
| Param | Type | Required | Description |
|
| Param | Type | Required | Description |
|
||||||
|-------|------|----------|-------------|
|
|-------|------|----------|-------------|
|
||||||
| `id` | string | Yes | Anime slug (e.g., `road-of-naruto-ggjw8`) |
|
| `id` | string | Yes | Anime slug with random hash (e.g., `naruto-shippuuden-movie-6-road-to-ninja-w2wqq`) |
|
||||||
|
|
||||||
|
> **Important:** Slugs now include random hashes (e.g., `one-piece-odmau`). Get valid slugs from `/search` or `/most-popular` results.
|
||||||
|
|
||||||
**Request:**
|
**Request:**
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
curl "https://anikototvapi.vercel.app/api/info?id=road-of-naruto-ggjw8"
|
curl "https://anikototvapi.vercel.app/api/info?id=naruto-shippuuden-movie-6-road-to-ninja-w2wqq"
|
||||||
```
|
```
|
||||||
|
|
||||||
**Response:**
|
**Response:**
|
||||||
@@ -150,13 +159,26 @@ curl "https://anikototvapi.vercel.app/api/info?id=road-of-naruto-ggjw8"
|
|||||||
{
|
{
|
||||||
"success": true,
|
"success": true,
|
||||||
"results": {
|
"results": {
|
||||||
"title": "Road of Naruto",
|
"slug": "naruto-shippuuden-movie-6-road-to-ninja-w2wqq",
|
||||||
"type": "ONA",
|
"animeId": 786,
|
||||||
|
"title": "Naruto: Shippuuden Movie 6: Road to Ninja",
|
||||||
|
"japaneseTitle": "Naruto: Shippuuden Movie 6 - Road to Ninja",
|
||||||
|
"altNames": "Naruto: Shippuuden Movie 6 - Road to Ninja;Naruto Movie 9",
|
||||||
|
"poster": "https://cdn.anipixcdn.co/thumbnail/43dd49b4fdb9bede653e94468ff8df1e.jpg",
|
||||||
|
"backgroundImage": "'",
|
||||||
|
"synopsis": "Returning home to Konohagakure, the young ninja celebrate defeating a group of supposed Akatsuki...",
|
||||||
|
"type": "Movie",
|
||||||
|
"premiered": "Summer 2012",
|
||||||
|
"aired": "Jul 28, 2012",
|
||||||
"status": "Finished Airing",
|
"status": "Finished Airing",
|
||||||
"totalEpisodes": null,
|
"malScore": "7.84",
|
||||||
"synopsis": "In celebration of 20 years of Naruto, Studio Pierrot posted an anniversary PV on their YouTube channel...",
|
"duration": "1 hr 39 min",
|
||||||
"malId": "53236",
|
"episodes": "1",
|
||||||
"genres": ["Action", "Fantasy", "Shounen"]
|
"studios": ["Pierrot"],
|
||||||
|
"producers": ["TV Tokyo", "Shueisha"],
|
||||||
|
"genres": ["Action", "Comedy", "Supernatural"],
|
||||||
|
"rating": "PG-13",
|
||||||
|
"reviewCount": "0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
@@ -687,17 +709,36 @@ Returns paginated list of upcoming anime with sub/dub counts, type, and rating.
|
|||||||
|
|
||||||
## GET /top-rankings
|
## GET /top-rankings
|
||||||
|
|
||||||
Get top-ranked anime (source site's top section).
|
Get top-ranked anime with sort options.
|
||||||
|
|
||||||
| Param | Type | Required | Description |
|
| Param | Type | Required | Description |
|
||||||
|-------|------|----------|-------------|
|
|-------|------|----------|-------------|
|
||||||
|
| `sort` | string | No | Sort mode: `day` (default), `week`, or `month` |
|
||||||
| `page` | number | No | Page number (default: 1) |
|
| `page` | number | No | Page number (default: 1) |
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
curl "https://anikototvapi.vercel.app/api/top-rankings"
|
curl "https://anikototvapi.vercel.app/api/top-rankings?sort=week"
|
||||||
```
|
```
|
||||||
|
|
||||||
Returns paginated list with rank numbers extracted from CSS classes.
|
**Response:**
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"success": true,
|
||||||
|
"results": [
|
||||||
|
{
|
||||||
|
"rank": 1,
|
||||||
|
"slug": "demon-slayer-kimetsu-no-yaiba-infinity-castle-4tmun",
|
||||||
|
"poster": "https://cdn.anipixcdn.co/thumbnail/...",
|
||||||
|
"title": "Demon Slayer: Kimetsu no Yaiba Infinity Castle",
|
||||||
|
"japaneseTitle": "Kimetsu no Yaiba Movie: Mugen Jou-hen",
|
||||||
|
"sub": 1,
|
||||||
|
"dub": 1,
|
||||||
|
"type": "Movie"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -730,7 +771,9 @@ Get completed anime sorted by score.
|
|||||||
curl "https://anikototvapi.vercel.app/api/completed"
|
curl "https://anikototvapi.vercel.app/api/completed"
|
||||||
```
|
```
|
||||||
|
|
||||||
Returns paginated list of finished anime from `section.top-table[data-name='completed']`.
|
Returns flat array of finished anime.
|
||||||
|
|
||||||
|
> **Note:** The `type` field in completed results contains the **air date** (e.g., `"2016-05-14"`), not the anime type (TV/Movie/etc).
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -796,3 +839,214 @@ curl "https://anikototvapi.vercel.app/api/stream/ts-proxy?url=https://example.co
|
|||||||
```
|
```
|
||||||
|
|
||||||
Returns binary video segment with `Content-Type: video/mp2t` and `Access-Control-Allow-Origin: *`.
|
Returns binary video segment with `Content-Type: video/mp2t` and `Access-Control-Allow-Origin: *`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## System Endpoints
|
||||||
|
|
||||||
|
### GET /health
|
||||||
|
|
||||||
|
Health check endpoint. No parameters required.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl "https://anikototvapi.vercel.app/api/health"
|
||||||
|
```
|
||||||
|
|
||||||
|
**Response:**
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"success": true,
|
||||||
|
"results": {
|
||||||
|
"status": "healthy",
|
||||||
|
"version": "2.2.0",
|
||||||
|
"uptime": "0h 1m 21s",
|
||||||
|
"uptimeSeconds": 81,
|
||||||
|
"timestamp": "2026-07-30T02:20:46.153Z",
|
||||||
|
"node": "v26.4.0",
|
||||||
|
"memory": {
|
||||||
|
"used": "99MB",
|
||||||
|
"total": "150MB"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### GET /stats
|
||||||
|
|
||||||
|
API statistics endpoint. No parameters required.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl "https://anikototvapi.vercel.app/api/stats"
|
||||||
|
```
|
||||||
|
|
||||||
|
**Response:**
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"success": true,
|
||||||
|
"results": {
|
||||||
|
"uptime": "81s",
|
||||||
|
"requests": {
|
||||||
|
"total": 37,
|
||||||
|
"errors": 9,
|
||||||
|
"successRate": "75.7%"
|
||||||
|
},
|
||||||
|
"cache": {
|
||||||
|
"type": "in-memory",
|
||||||
|
"ttl": "5 minutes",
|
||||||
|
"description": "Map-based cache with TTL expiration"
|
||||||
|
},
|
||||||
|
"endpoints": 38,
|
||||||
|
"timestamp": "2026-07-30T02:20:46.205Z"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### GET /cache/stats
|
||||||
|
|
||||||
|
Cache statistics endpoint. No parameters required.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl "https://anikototvapi.vercel.app/api/cache/stats"
|
||||||
|
```
|
||||||
|
|
||||||
|
**Response:**
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"success": true,
|
||||||
|
"results": {
|
||||||
|
"hits": 0,
|
||||||
|
"misses": 28,
|
||||||
|
"sets": 26,
|
||||||
|
"deletes": 0,
|
||||||
|
"size": 26,
|
||||||
|
"hitRate": "0.00%"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### GET /mirrors
|
||||||
|
|
||||||
|
Mirror status endpoint. No parameters required.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl "https://anikototvapi.vercel.app/api/mirrors"
|
||||||
|
```
|
||||||
|
|
||||||
|
**Response:**
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"success": true,
|
||||||
|
"results": [
|
||||||
|
{
|
||||||
|
"name": "Primary",
|
||||||
|
"url": "https://anikototv.to",
|
||||||
|
"priority": 1,
|
||||||
|
"status": "online",
|
||||||
|
"latency": "6115ms",
|
||||||
|
"isCurrent": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Regional CZ",
|
||||||
|
"url": "https://anikoto.cz",
|
||||||
|
"priority": 2,
|
||||||
|
"status": "online",
|
||||||
|
"latency": "672ms",
|
||||||
|
"isCurrent": false
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### POST /mirrors/reset
|
||||||
|
|
||||||
|
Reset mirror cache. No parameters required.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl -X POST "https://anikototvapi.vercel.app/api/mirrors/reset"
|
||||||
|
```
|
||||||
|
|
||||||
|
**Response:**
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"success": true,
|
||||||
|
"message": "Mirror cache reset"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### GET /openapi
|
||||||
|
|
||||||
|
OpenAPI 3.0.3 specification. No parameters required.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl "https://anikototvapi.vercel.app/api/openapi"
|
||||||
|
```
|
||||||
|
|
||||||
|
Returns the full OpenAPI spec document with all endpoint definitions, tags, and server information.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Response Patterns
|
||||||
|
|
||||||
|
| Pattern | Format | Endpoints |
|
||||||
|
|:---|:---|:---|
|
||||||
|
| **Flat array** | `results: [...]` | `/spotlight`, `/trending`, `/top-ten`, `/suggestions`, `/recently-updated`, `/completed`, `/upcoming`, `/top-rankings`, `/mirrors`, `/schedule` |
|
||||||
|
| **Paginated data** | `results: { totalPages, data: [...] }` | `/search`, `/most-popular`, `/new-release`, `/newly-added`, `/latest-updated`, `/genre/:name`, `/type/:name`, `/status/:name`, `/az-list/:letter` |
|
||||||
|
| **Full pagination** | `results: { totalPages, data: [...], pagination: {...} }` | `/filter` only |
|
||||||
|
| **Three-period** | `results: { today/week/month: [...] }` or `{ day/week/month: [...] }` | `/top-ten`, `/trending-sidebar` |
|
||||||
|
| **Single object** | `results: {...}` | `/home`, `/info`, `/episodes`, `/episodes-ajax`, `/random`, `/health`, `/stats`, `/cache/stats`, `/stream-qualities` |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Common Workflows
|
||||||
|
|
||||||
|
### Streaming Workflow
|
||||||
|
|
||||||
|
```
|
||||||
|
1. GET /episodes/{animeId} → Get episode list with server_ids
|
||||||
|
2. GET /servers?ids={ids} → Get available servers with link_ids
|
||||||
|
3. GET /stream?id={link_id} → Get embed URL + skip data
|
||||||
|
4. GET /stream/resolve?id={link_id} → Get actual m3u8/mp4 URL
|
||||||
|
5. GET /stream/qualities?url={m3u8} → Get available qualities
|
||||||
|
6. GET /stream/proxy?url={m3u8} → Get CORS-free proxied playlist
|
||||||
|
```
|
||||||
|
|
||||||
|
### Discovery Workflow
|
||||||
|
|
||||||
|
```
|
||||||
|
1. GET / → Homepage: spotlights, trending, genres
|
||||||
|
2. GET /search?q= → Search by keyword
|
||||||
|
3. GET /info?id= → Get anime details
|
||||||
|
4. GET /episodes/ → Get episode list
|
||||||
|
5. GET /seasons/ → Get all seasons
|
||||||
|
6. GET /watch-order/ → Get watch order for franchises
|
||||||
|
```
|
||||||
|
|
||||||
|
### Filter & Browse Workflow
|
||||||
|
|
||||||
|
```
|
||||||
|
1. GET /genre/{name} → Browse by genre
|
||||||
|
2. GET /type/{name} → Browse by type (TV, Movie, OVA)
|
||||||
|
3. GET /status/{name} → Browse by status (airing, completed)
|
||||||
|
4. GET /filter?... → Advanced multi-parameter filter
|
||||||
|
5. GET /az-list/{letter} → Browse alphabetically
|
||||||
|
6. GET /top-rankings?sort=day → Top ranked anime
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
// ══════ END: endpoints.md ══════
|
||||||
|
|||||||
+2043
-269
File diff suppressed because it is too large
Load Diff
+405
@@ -0,0 +1,405 @@
|
|||||||
|
# // === HEADER ===
|
||||||
|
# AniKotoAPI v2.2.0
|
||||||
|
# Free REST API for Anime Data
|
||||||
|
# Scraping anikototv.to with Cheerio
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|

|
||||||
|

|
||||||
|

|
||||||
|

|
||||||
|
|
||||||
|
> A fast, free REST API that scrapes anime data from **anikototv.to**.
|
||||||
|
> Built with Node.js, Cheerio, and deployed on Vercel serverless functions.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## // === QUICK START ===
|
||||||
|
|
||||||
|
### Get started in 3 steps:
|
||||||
|
|
||||||
|
**Step 1** — Make your first request:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl https://anikototvapi.vercel.app/api/
|
||||||
|
```
|
||||||
|
|
||||||
|
**Step 2** — Search for an anime:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl https://anikototvapi.vercel.app/api/search?q=naruto
|
||||||
|
```
|
||||||
|
|
||||||
|
**Step 3** — Get anime info:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl https://anikototvapi.vercel.app/api/info?link=/anime/naruto
|
||||||
|
```
|
||||||
|
|
||||||
|
That's it. No API keys required.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## // === FEATURES ===
|
||||||
|
|
||||||
|
| Feature | Description |
|
||||||
|
|---|---|
|
||||||
|
| **38+ Endpoints** | Full coverage of anime data, streaming, and discovery |
|
||||||
|
| **28 HTML Scrapers** | Cheerio-based scrapers for robust data extraction |
|
||||||
|
| **28 Route Handlers** | Organized controllers with clean separation of concerns |
|
||||||
|
| **5 Mirror Domains** | Automatic failover across multiple source domains |
|
||||||
|
| **LRU Cache** | Per-endpoint TTL caching for performance |
|
||||||
|
| **Rate Limiting** | 100 requests per 15 minutes per IP |
|
||||||
|
| **GZIP Compression** | Compressed responses for faster transfers |
|
||||||
|
| **Vercel Deployment** | Zero-config serverless deployment |
|
||||||
|
| **OpenAPI Spec** | Auto-generated API documentation at `/api/openapi` |
|
||||||
|
| **Health Checks** | System status and cache monitoring endpoints |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## // === ENDPOINTS OVERVIEW ===
|
||||||
|
|
||||||
|
### Home
|
||||||
|
|
||||||
|
| Method | Endpoint | Description |
|
||||||
|
|--------|----------|-------------|
|
||||||
|
| GET | `/api/` | Homepage data: featured, spotlight, sidebar content |
|
||||||
|
|
||||||
|
### Anime
|
||||||
|
|
||||||
|
| Method | Endpoint | Description |
|
||||||
|
|--------|----------|-------------|
|
||||||
|
| GET | `/api/info` | Full anime info, episodes, relations, recommendations |
|
||||||
|
| GET | `/api/seasons/:id` | Season/episode details for an anime |
|
||||||
|
| GET | `/api/watch-order/:id` | Canonical watch order for a series |
|
||||||
|
|
||||||
|
### Search
|
||||||
|
|
||||||
|
| Method | Endpoint | Description |
|
||||||
|
|--------|----------|-------------|
|
||||||
|
| GET | `/api/search` | Search anime by title |
|
||||||
|
| GET | `/api/search/suggest` | Autocomplete suggestions |
|
||||||
|
| GET | `/api/suggestions` | Trending/general suggestions |
|
||||||
|
|
||||||
|
### Episodes
|
||||||
|
|
||||||
|
| Method | Endpoint | Description |
|
||||||
|
|--------|----------|-------------|
|
||||||
|
| GET | `/api/episodes/:id` | Episode list for an anime |
|
||||||
|
| GET | `/api/episodes-ajax/:id` | Lazy-loaded episode pagination |
|
||||||
|
|
||||||
|
### Streaming
|
||||||
|
|
||||||
|
| Method | Endpoint | Description |
|
||||||
|
|--------|----------|-------------|
|
||||||
|
| GET | `/api/stream` | Master stream URL for an episode |
|
||||||
|
| GET | `/api/servers` | Available servers for an episode |
|
||||||
|
| GET | `/api/stream/resolve` | Resolve a streaming URL |
|
||||||
|
| GET | `/api/stream/qualities` | Available quality variants |
|
||||||
|
| GET | `/api/stream/proxy` | Proxied video stream |
|
||||||
|
| GET | `/api/stream/ts-proxy` | Proxied TS segment stream |
|
||||||
|
| GET | `/api/watch` | Watch page with stream data |
|
||||||
|
| GET | `/api/download` | Download link for an episode |
|
||||||
|
| GET | `/api/mapper-servers` | Server name mappings |
|
||||||
|
|
||||||
|
### Discovery
|
||||||
|
|
||||||
|
| Method | Endpoint | Description |
|
||||||
|
|--------|----------|-------------|
|
||||||
|
| GET | `/api/spotlight` | Featured/spotlight anime |
|
||||||
|
| GET | `/api/trending` | Currently trending anime |
|
||||||
|
| GET | `/api/top-ten` | Top 10 rankings |
|
||||||
|
| GET | `/api/random` | Random anime recommendation |
|
||||||
|
| GET | `/api/most-popular` | Most popular anime |
|
||||||
|
| GET | `/api/trending-sidebar` | Trending sidebar widget data |
|
||||||
|
|
||||||
|
### Releases
|
||||||
|
|
||||||
|
| Method | Endpoint | Description |
|
||||||
|
|--------|----------|-------------|
|
||||||
|
| GET | `/api/new-release` | Recently released episodes |
|
||||||
|
| GET | `/api/newly-added` | Newly added anime |
|
||||||
|
| GET | `/api/latest-updated` | Latest updated anime |
|
||||||
|
| GET | `/api/recently-updated` | Recently updated entries |
|
||||||
|
| GET | `/api/completed` | Completed anime |
|
||||||
|
| GET | `/api/upcoming` | Upcoming anime |
|
||||||
|
|
||||||
|
### Rankings
|
||||||
|
|
||||||
|
| Method | Endpoint | Description |
|
||||||
|
|--------|----------|-------------|
|
||||||
|
| GET | `/api/top-rankings` | Top ranked anime by category |
|
||||||
|
|
||||||
|
### Categories
|
||||||
|
|
||||||
|
| Method | Endpoint | Description |
|
||||||
|
|--------|----------|-------------|
|
||||||
|
| GET | `/api/genre/:name` | Anime by genre |
|
||||||
|
| GET | `/api/type/:name` | Anime by type (TV, Movie, OVA, etc.) |
|
||||||
|
| GET | `/api/status/:name` | Anime by status (Airing, Completed, etc.) |
|
||||||
|
| GET | `/api/az-list/:letter` | Alphabetical listing |
|
||||||
|
| GET | `/api/filter` | Multi-criteria filter |
|
||||||
|
|
||||||
|
### Schedule
|
||||||
|
|
||||||
|
| Method | Endpoint | Description |
|
||||||
|
|--------|----------|-------------|
|
||||||
|
| GET | `/api/schedule` | Anime release schedule |
|
||||||
|
|
||||||
|
### System
|
||||||
|
|
||||||
|
| Method | Endpoint | Description |
|
||||||
|
|--------|----------|-------------|
|
||||||
|
| GET | `/api/health` | Health check / uptime |
|
||||||
|
| GET | `/api/stats` | API statistics |
|
||||||
|
| GET | `/api/cache/stats` | Cache hit/miss statistics |
|
||||||
|
| GET | `/api/mirrors` | List available mirror domains |
|
||||||
|
| POST | `/api/mirrors/reset` | Reset failover mirror state |
|
||||||
|
| GET | `/api/openapi` | OpenAPI/Swagger spec |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## // === ARCHITECTURE ===
|
||||||
|
|
||||||
|
```
|
||||||
|
AniKotoAPI/
|
||||||
|
├── api/ # Vercel serverless functions
|
||||||
|
│ ├── index.js # Home endpoint
|
||||||
|
│ ├── info.js # Anime info
|
||||||
|
│ ├── search.js # Search
|
||||||
|
│ ├── stream.js # Streaming
|
||||||
|
│ └── ... # 38+ route handlers
|
||||||
|
├── scrapers/ # Cheerio HTML scrapers
|
||||||
|
│ ├── home.js
|
||||||
|
│ ├── info.js
|
||||||
|
│ ├── search.js
|
||||||
|
│ └── ... # 28 scrapers
|
||||||
|
├── utils/ # Shared utilities
|
||||||
|
│ ├── cache.js # LRU cache with TTL
|
||||||
|
│ ├── rateLimit.js # IP-based rate limiter
|
||||||
|
│ ├── mirrors.js # Mirror failover logic
|
||||||
|
│ └── compress.js # GZIP compression
|
||||||
|
├── docs/ # Documentation
|
||||||
|
│ ├── index.md # This file
|
||||||
|
│ ├── endpoints.md # Full API reference
|
||||||
|
│ ├── streaming.md # Streaming workflow
|
||||||
|
│ ├── examples.md # Code examples
|
||||||
|
│ ├── architecture.md # Design patterns
|
||||||
|
│ └── testing.md # Test suite docs
|
||||||
|
├── package.json
|
||||||
|
└── vercel.json
|
||||||
|
```
|
||||||
|
|
||||||
|
### Key Design Patterns
|
||||||
|
|
||||||
|
- **Scraping Layer**: 28 Cheerio-based scrapers parse HTML from anikototv.to, isolating page-specific logic from route handlers.
|
||||||
|
- **Controller Layer**: 28 route handlers map HTTP requests to scraper calls, handle validation, and format responses.
|
||||||
|
- **Cache Layer**: LRU cache with per-endpoint TTL reduces redundant scraping. Hot endpoints cache for 2–5 minutes; cold endpoints for 10+ minutes.
|
||||||
|
- **Mirror Layer**: 5 source domains with automatic failover. If one domain is slow or down, requests transparently fall back to the next mirror.
|
||||||
|
- **Rate Limiter**: Token bucket algorithm, 100 requests per 15-minute window per client IP.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## // === GETTING STARTED ===
|
||||||
|
|
||||||
|
### Using cURL
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Get homepage data
|
||||||
|
curl https://anikototvapi.vercel.app/api/
|
||||||
|
|
||||||
|
# Search for an anime
|
||||||
|
curl "https://anikototvapi.vercel.app/api/search?q=one+piece"
|
||||||
|
|
||||||
|
# Get anime details
|
||||||
|
curl "https://anikototvapi.vercel.app/api/info?link=/anime/one-piece"
|
||||||
|
|
||||||
|
# Get streaming servers
|
||||||
|
curl "https://anikototvapi.vercel.app/api/servers?link=/one-piece-episode-1"
|
||||||
|
|
||||||
|
# Get trending anime
|
||||||
|
curl https://anikototvapi.vercel.app/api/trending
|
||||||
|
```
|
||||||
|
|
||||||
|
### Using JavaScript (fetch)
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
const BASE = "https://anikototvapi.vercel.app/api";
|
||||||
|
|
||||||
|
// Search
|
||||||
|
const res = await fetch(`${BASE}/search?q=naruto`);
|
||||||
|
const data = await res.json();
|
||||||
|
console.log(data.results);
|
||||||
|
```
|
||||||
|
|
||||||
|
### Using Python (requests)
|
||||||
|
|
||||||
|
```python
|
||||||
|
import requests
|
||||||
|
|
||||||
|
BASE = "https://anikototvapi.vercel.app/api"
|
||||||
|
|
||||||
|
# Search
|
||||||
|
r = requests.get(f"{BASE}/search?q=naruto")
|
||||||
|
print(r.json()["results"])
|
||||||
|
```
|
||||||
|
|
||||||
|
### Using Node.js (axios)
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
const axios = require("axios");
|
||||||
|
const BASE = "https://anikototvapi.vercel.app/api";
|
||||||
|
|
||||||
|
async function searchAnime(query) {
|
||||||
|
const { data } = await axios.get(`${BASE}/search`, {
|
||||||
|
params: { q: query }
|
||||||
|
});
|
||||||
|
return data.results;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## // === RATE LIMITING ===
|
||||||
|
|
||||||
|
- **Limit**: 100 requests per 15-minute window
|
||||||
|
- **Scope**: Per client IP address
|
||||||
|
- **Headers**: Rate limit info returned in response headers
|
||||||
|
- **Exceeded**: Returns `429 Too Many Requests`
|
||||||
|
|
||||||
|
If you hit the rate limit, wait for the window to reset before retrying.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## // === MIRROR FAILOVER ===
|
||||||
|
|
||||||
|
AniKotoAPI maintains 5 mirror domains. When a request fails on the primary domain, the system automatically retries on the next available mirror.
|
||||||
|
|
||||||
|
```
|
||||||
|
Primary → Mirror 1 → Mirror 2 → Mirror 3 → Mirror 4 → Mirror 5
|
||||||
|
```
|
||||||
|
|
||||||
|
Use `/api/mirrors` to check current mirror status, or `POST /api/mirrors/reset` to reset the failover state.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## // === CACHING ===
|
||||||
|
|
||||||
|
The API uses an LRU (Least Recently Used) cache with per-endpoint TTL:
|
||||||
|
|
||||||
|
| Endpoint Category | TTL |
|
||||||
|
|-------------------|-----|
|
||||||
|
| Home / Spotlight | 2 minutes |
|
||||||
|
| Search | 3 minutes |
|
||||||
|
| Anime Info | 5 minutes |
|
||||||
|
| Streaming | 1 minute |
|
||||||
|
| Rankings / Lists | 10 minutes |
|
||||||
|
|
||||||
|
Cache stats available at `/api/cache/stats`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## // === ERROR RESPONSES ===
|
||||||
|
|
||||||
|
All errors return JSON with a consistent structure:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"error": true,
|
||||||
|
"message": "Description of what went wrong",
|
||||||
|
"status": 404
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
| Status | Meaning |
|
||||||
|
|--------|---------|
|
||||||
|
| 400 | Bad request / missing parameters |
|
||||||
|
| 404 | Resource not found |
|
||||||
|
| 429 | Rate limit exceeded |
|
||||||
|
| 500 | Internal server error |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## // === DOCUMENTATION ===
|
||||||
|
|
||||||
|
| File | Description |
|
||||||
|
|------|-------------|
|
||||||
|
| [endpoints.md](./endpoints.md) | Full API reference for all 38+ endpoints |
|
||||||
|
| [streaming.md](./streaming.md) | Complete streaming workflow guide |
|
||||||
|
| [examples.md](./examples.md) | Working code examples in cURL, JS, Python |
|
||||||
|
| [architecture.md](./architecture.md) | Project structure and design patterns |
|
||||||
|
| [testing.md](./testing.md) | Integration test suite documentation |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## // === CONTRIBUTING ===
|
||||||
|
|
||||||
|
Contributions are welcome. To contribute:
|
||||||
|
|
||||||
|
1. Fork the repository from [GitHub](https://github.com/Shineii86/AniKotoAPI)
|
||||||
|
2. Create a feature branch: `git checkout -b feature/my-feature`
|
||||||
|
3. Make your changes and test them
|
||||||
|
4. Commit with a clear message
|
||||||
|
5. Push and open a Pull Request
|
||||||
|
|
||||||
|
### Development Setup
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git clone https://github.com/Shineii86/AniKotoAPI.git
|
||||||
|
cd AniKotoAPI
|
||||||
|
npm install
|
||||||
|
npm run dev
|
||||||
|
```
|
||||||
|
|
||||||
|
### Guidelines
|
||||||
|
|
||||||
|
- Follow existing code style and patterns
|
||||||
|
- Add scrapers in `scrapers/` for new pages
|
||||||
|
- Add route handlers in `api/` for new endpoints
|
||||||
|
- Update documentation when adding features
|
||||||
|
- Test all changes before submitting
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## // === LICENSE ===
|
||||||
|
|
||||||
|
This project is licensed under the **MIT License**.
|
||||||
|
|
||||||
|
```
|
||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2024 Shinei Nouzen
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## // === LINKS ===
|
||||||
|
|
||||||
|
- **Live API**: https://anikototvapi.vercel.app/api
|
||||||
|
- **GitHub**: https://github.com/Shineii86/AniKotoAPI
|
||||||
|
- **OpenAPI Spec**: https://anikototvapi.vercel.app/api/openapi
|
||||||
|
- **Health Check**: https://anikototvapi.vercel.app/api/health
|
||||||
|
- **Author**: [Shinei Nouzen](https://github.com/Shineii86)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
// ══════ END: index.md ══════
|
||||||
+819
-274
File diff suppressed because it is too large
Load Diff
@@ -5,7 +5,7 @@
|
|||||||
*
|
*
|
||||||
* @description
|
* @description
|
||||||
* Controller for top anime rankings. Returns ranked anime list
|
* Controller for top anime rankings. Returns ranked anime list
|
||||||
* with support for top (all-time) and newest sort modes.
|
* with support for day, week, and month sort modes.
|
||||||
*
|
*
|
||||||
* @exports
|
* @exports
|
||||||
* getTopAnimeRankings - Express route handler for GET /api/top-rankings
|
* getTopAnimeRankings - Express route handler for GET /api/top-rankings
|
||||||
@@ -28,18 +28,18 @@ import { getCache, setCache, TTL } from "../helper/cache.helper.js";
|
|||||||
* with support for top (all-time) and newest sort modes.
|
* with support for top (all-time) and newest sort modes.
|
||||||
*
|
*
|
||||||
* @param {object} req - Express request object
|
* @param {object} req - Express request object
|
||||||
* @param {string} [req.query.sort="top"] - Sort mode: "top" or "newest"
|
* @param {string} [req.query.sort="day"] - Sort mode: "day", "week", or "month"
|
||||||
* @param {object} res - Express response object
|
* @param {object} res - Express response object
|
||||||
* @param {Function} next - Express next middleware function
|
* @param {Function} next - Express next middleware function
|
||||||
* @returns {void} Sends JSON response with ranked anime list
|
* @returns {void} Sends JSON response with ranked anime list
|
||||||
*
|
*
|
||||||
* @example
|
* @example
|
||||||
* GET /api/top-rankings?sort=top
|
* GET /api/top-rankings?sort=day
|
||||||
* Response: { success: true, results: [{ rank: 1, title: "...", ... }] }
|
* Response: { success: true, results: [{ rank: 1, title: "...", ... }] }
|
||||||
*/
|
*/
|
||||||
const getTopAnimeRankings = async (req, res, next) => {
|
const getTopAnimeRankings = async (req, res, next) => {
|
||||||
try {
|
try {
|
||||||
const { sort = "top" } = req.query;
|
const { sort = "day" } = req.query;
|
||||||
const cacheKey = `top_rankings_${sort}`;
|
const cacheKey = `top_rankings_${sort}`;
|
||||||
const cached = getCache(cacheKey);
|
const cached = getCache(cacheKey);
|
||||||
if (cached) {
|
if (cached) {
|
||||||
|
|||||||
@@ -61,7 +61,8 @@ const extractEpisodeList = async (slugOrId) => {
|
|||||||
headers: { "X-Requested-With": "XMLHttpRequest" }
|
headers: { "X-Requested-With": "XMLHttpRequest" }
|
||||||
});
|
});
|
||||||
|
|
||||||
const ajaxHtml = typeof ajaxRaw === "string" ? ajaxRaw : (ajaxRaw?.result || "");
|
const ajaxParsed = typeof ajaxRaw === "string" ? JSON.parse(ajaxRaw) : ajaxRaw;
|
||||||
|
const ajaxHtml = ajaxParsed?.result || "";
|
||||||
const $ep = cheerio.load(ajaxHtml);
|
const $ep = cheerio.load(ajaxHtml);
|
||||||
|
|
||||||
const episodes = [];
|
const episodes = [];
|
||||||
|
|||||||
@@ -55,7 +55,8 @@ const extractEpisodeListAjax = async (animeId, style = "", vrf = "") => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// NOTE: Response is JSON {status, result} where result is HTML
|
// NOTE: Response is JSON {status, result} where result is HTML
|
||||||
const html = typeof raw === "string" ? raw : (raw?.result || "");
|
const parsed = typeof raw === "string" ? JSON.parse(raw) : raw;
|
||||||
|
const html = parsed?.result || "";
|
||||||
const $ = cheerio.load(html);
|
const $ = cheerio.load(html);
|
||||||
|
|
||||||
// NOTE: Extract filter options (Sub/Dub, episode ranges)
|
// NOTE: Extract filter options (Sub/Dub, episode ranges)
|
||||||
|
|||||||
@@ -661,7 +661,7 @@ app.get("/api/openapi", (req, res) => {
|
|||||||
"/az-list/{letter}": { get: { summary: "A-Z listing", tags: ["Discovery"] } },
|
"/az-list/{letter}": { get: { summary: "A-Z listing", tags: ["Discovery"] } },
|
||||||
"/suggestions": { get: { summary: "Anime suggestions", tags: ["Search"] } },
|
"/suggestions": { get: { summary: "Anime suggestions", tags: ["Search"] } },
|
||||||
"/upcoming": { get: { summary: "Upcoming anime", tags: ["Discovery"] } },
|
"/upcoming": { get: { summary: "Upcoming anime", tags: ["Discovery"] } },
|
||||||
"/top-rankings": { get: { summary: "Top anime rankings (top/newest)", tags: ["Rankings"] } },
|
"/top-rankings": { get: { summary: "Top anime rankings (day/week/month)", tags: ["Rankings"] } },
|
||||||
"/recently-updated": { get: { summary: "Recently updated (all/dub/sub tabs)", tags: ["Releases"] } },
|
"/recently-updated": { get: { summary: "Recently updated (all/dub/sub tabs)", tags: ["Releases"] } },
|
||||||
"/completed": { get: { summary: "Completed anime series", tags: ["Rankings"] } },
|
"/completed": { get: { summary: "Completed anime series", tags: ["Rankings"] } },
|
||||||
},
|
},
|
||||||
|
|||||||
Reference in New Issue
Block a user