diff --git a/.dockerignore b/.dockerignore
new file mode 100644
index 0000000..bbe7da0
--- /dev/null
+++ b/.dockerignore
@@ -0,0 +1,5 @@
+node_modules
+npm-debug.log
+.env
+.vercel
+.git
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 13bde2e..d0c885d 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,15 @@
# Changelog
+## v1.1.0
+- Added Swagger UI interactive docs at /docs
+- Added OpenAPI 3.0 spec at /openapi.json
+- Added mappings field in episodes response (anilistId, malId, kitsuId)
+- Added Docker support (Dockerfile + .dockerignore)
+- Improved landing page with premium design (Walter-style)
+- Added "Try it" buttons on all endpoints
+- Health endpoint now includes version, providers list, and endpoint count
+- Version bump to 1.1.0
+
## v1.0.0
- Initial release
- AniList GraphQL integration for anime metadata
diff --git a/Dockerfile b/Dockerfile
new file mode 100644
index 0000000..0e1d931
--- /dev/null
+++ b/Dockerfile
@@ -0,0 +1,12 @@
+FROM node:20-alpine
+
+WORKDIR /app
+
+COPY package*.json ./
+RUN npm install --production
+
+COPY . .
+
+EXPOSE 3000
+
+CMD ["node", "server.js"]
diff --git a/README.md b/README.md
index af2bd84..c090ef9 100644
--- a/README.md
+++ b/README.md
@@ -5,6 +5,8 @@ Free RESTful API for anime streaming data. Powered by AniList GraphQL and Miruro
## Live
- **API**: `https://miruroapi-zeta.vercel.app`
+- **Docs**: `https://miruroapi-zeta.vercel.app/docs` (Swagger UI)
+- **OpenAPI**: `https://miruroapi-zeta.vercel.app/openapi.json`
- **GitHub**: `https://github.com/Shineii86/MiruroAPI`
## Features
@@ -13,29 +15,49 @@ Free RESTful API for anime streaming data. Powered by AniList GraphQL and Miruro
- AniList GraphQL for metadata (search, trending, info, filter, schedule)
- 12 streaming providers (kiwi, pewe, bee, bonk, bun, ally, nun, twin, cog, moo, hop, telli)
- M3U8 streaming URLs with subtitles and skip timestamps
-- In-memory caching
+- In-memory caching with TTL
- CORS enabled
+- Swagger UI interactive docs
+- Docker support
+- Health endpoint
## Endpoints
+### Search & Discovery
+
| Endpoint | Description |
|---|---|
| `GET /api/search?query=naruto` | Search anime by name |
| `GET /api/suggestions?query=naruto` | Autocomplete suggestions |
+| `GET /api/filter?genre=Action` | Advanced filter |
+
+### Collections
+
+| Endpoint | Description |
+|---|---|
| `GET /api/trending` | Trending anime |
| `GET /api/popular` | Most popular anime |
| `GET /api/upcoming` | Upcoming anime |
| `GET /api/recent` | Currently airing anime |
| `GET /api/spotlight` | Spotlight/featured anime |
| `GET /api/schedule` | Airing schedule |
-| `GET /api/filter?genre=Action` | Advanced filter |
+
+### Anime Details
+
+| Endpoint | Description |
+|---|---|
| `GET /api/info/:id` | Full anime info by AniList ID |
| `GET /api/anime/:id/characters` | Characters with voice actors |
| `GET /api/anime/:id/relations` | Related anime |
| `GET /api/anime/:id/recommendations` | Recommendations |
+
+### Streaming
+
+| Endpoint | Description |
+|---|---|
| `GET /api/episodes/:id` | Episode list from all providers |
-| `GET /api/sources?episodeId=...&provider=...&anilistId=...` | Streaming sources |
-| `GET /api/watch/:provider/:anilistId/:category/:slug` | Simple streaming sources |
+| `GET /api/sources?episodeId=...&provider=...&anilistId=...` | Streaming sources (detailed) |
+| `GET /api/watch/:provider/:anilistId/:category/:slug` | Streaming sources (simple) |
## Streaming Flow
@@ -50,6 +72,13 @@ npm install
npm start
```
+## Docker
+
+```bash
+docker build -t miruroapi .
+docker run -p 3000:3000 miruroapi
+```
+
## License
MIT
diff --git a/public/index.html b/public/index.html
index c7f2588..b1b008b 100644
--- a/public/index.html
+++ b/public/index.html
@@ -3,43 +3,203 @@
-
v1.0.0
-
MiruroAPI
-
Free RESTful API for anime streaming data. Powered by AniList GraphQL and Miruro streaming providers.
-
-
Endpoints
-
GET /api/search?query=naruto
-
GET /api/trending
-
GET /api/popular
-
GET /api/upcoming
-
GET /api/recent
-
GET /api/spotlight
-
GET /api/schedule
-
GET /api/filter?genre=Action
-
GET /api/info/:id
-
GET /api/episodes/:id
-
GET /api/watch/:provider/:id/:category/:slug
-
GET /api/health
+
+
+
+ 16 Endpoints — Powered by AniList GraphQL + Miruro streaming pipe. M3U8 URLs, subtitles, intro/outro timestamps.
+
+
+
+
Search & Discovery
+
+
+
GET /api/search?query={name}
+
Full-text anime search with rich metadata — title, cover, genres, studios, scores, airing status. 20+ fields per result.
+
Try it
+
+
+
+
GET /api/suggestions?query={name} NEW
+
Lightweight autocomplete for dropdowns — id, title, poster, format, status, year. Max 8 results.
+
Try it
+
+
+
+
GET /api/filter NEW
+
Advanced filter by genre, tag, year, season, format, status, sort. All params optional.
+
+ | Param | Values |
+ | genre | Action, Romance, Comedy, Drama, Fantasy, Sci-Fi, etc. |
+ | tag | Isekai, Time Skip, Reincarnation, etc. |
+ | year | 2025, 2024, etc. |
+ | season | WINTER · SPRING · SUMMER · FALL |
+ | format | TV · MOVIE · OVA · ONA · SPECIAL |
+ | status | RELEASING · FINISHED · NOT_YET_RELEASED · CANCELLED |
+ | sort | SCORE_DESC · POPULARITY_DESC · TRENDING_DESC · START_DATE_DESC |
+
+
Try it
+
+
+
+
Collections (All Paginated)
+
+
+
GET /api/trending
+
Currently trending anime across the community.
+
Try it
+
+
+
+
GET /api/popular
+
Most popular anime of all time by total user count.
+
Try it
+
+
+
+
GET /api/upcoming
+
Most anticipated anime that haven't aired yet.
+
Try it
+
+
+
+
GET /api/recent
+
Currently airing / this season's anime.
+
Try it
+
+
+
+
GET /api/spotlight HOT
+
Top 10 trending + popular anime. Perfect for hero banners.
+
Try it
+
+
+
+
GET /api/schedule
+
Upcoming episodes with UNIX timestamps and full anime metadata.
+
Try it
+
+
+
+
Anime Details
+
+
+
GET /api/info/{anilist_id}
+
Complete anime page — everything AniList has to offer. Characters (25), staff, relations, recommendations, trailer, stats, external links.
+
Try it (One Piece)
+
+
+
+
GET /api/anime/{id}/characters
+
Paginated character list with Japanese voice actors, images, roles.
+
Try it (Naruto)
+
+
+
+
GET /api/anime/{id}/relations
+
Sequels, prequels, side stories, spin-offs, source material.
+
Try it
+
+
+
+
GET /api/anime/{id}/recommendations
+
Community recommendations sorted by highest rating.
+
Try it
+
+
+
+
Streaming (3-Step Flow)
+
+
+ How streaming works: To get a video URL, follow these 3 steps in order. Each step's output feeds into the next.
+
+
+
+
1GET /api/episodes/{anilist_id}
+
Get all episodes from multiple providers (kiwi, bee, bonk, ally, moo, etc.) organized by sub/dub. Returns episode IDs for Step 2.
+
{
+ "providers": {
+ "kiwi": { "episodes": { "sub": [{ "id": "watch/kiwi/20/sub/animepahe-1", "number": 1, ... }] } },
+ "bee": { ... },
+ "bonk": { ... }
+ }
+}
+
Try it (Naruto)
+
+
+
+
2 /api/watch/{provider}/{anilistId}/{category}/{slug} RECOMMENDED
+
Take the id from Step 1 and use it as the URL. Returns M3U8 streams, subtitles, intro/outro timestamps.
+
{
+ "streams": [
+ { "url": "https://.../master.m3u8", "type": "hls", "quality": "1080p" }
+ ],
+ "subtitles": [ { "file": "...", "label": "English" } ],
+ "intro": { "start": 0, "end": 90 },
+ "outro": { "start": 1300, "end": 1420 }
+}
+
Try it
+
+
+
+
3 Play the stream
+
Feed streams[0].url into any HLS player (Video.js, hls.js, VLC, mpv). Use intro/outro timestamps for skip buttons.
+
+
+
diff --git a/public/openapi.json b/public/openapi.json
new file mode 100644
index 0000000..a8a82e0
--- /dev/null
+++ b/public/openapi.json
@@ -0,0 +1,247 @@
+{
+ "openapi": "3.0.3",
+ "info": {
+ "title": "MiruroAPI",
+ "description": "Free RESTful API for anime streaming data. Powered by AniList GraphQL and Miruro streaming providers.",
+ "version": "1.1.0",
+ "contact": {
+ "name": "Shineii86",
+ "url": "https://github.com/Shineii86/MiruroAPI"
+ },
+ "license": {
+ "name": "MIT"
+ }
+ },
+ "servers": [
+ {
+ "url": "https://miruroapi-zeta.vercel.app",
+ "description": "Production"
+ }
+ ],
+ "paths": {
+ "/api/health": {
+ "get": {
+ "tags": ["System"],
+ "summary": "Health check",
+ "operationId": "health",
+ "responses": {
+ "200": {
+ "description": "API is healthy",
+ "content": { "application/json": { "schema": { "type": "object" } } }
+ }
+ }
+ }
+ },
+ "/api/search": {
+ "get": {
+ "tags": ["Search & Discovery"],
+ "summary": "Search anime by name",
+ "operationId": "searchAnime",
+ "parameters": [
+ { "name": "query", "in": "query", "required": true, "schema": { "type": "string" }, "description": "Search query" },
+ { "name": "page", "in": "query", "schema": { "type": "integer", "default": 1 } },
+ { "name": "per_page", "in": "query", "schema": { "type": "integer", "default": 20, "maximum": 50 } }
+ ],
+ "responses": {
+ "200": { "description": "Search results with pagination" }
+ }
+ }
+ },
+ "/api/suggestions": {
+ "get": {
+ "tags": ["Search & Discovery"],
+ "summary": "Autocomplete suggestions",
+ "operationId": "getSuggestions",
+ "parameters": [
+ { "name": "query", "in": "query", "required": true, "schema": { "type": "string" } }
+ ],
+ "responses": {
+ "200": { "description": "Lightweight suggestions (max 8)" }
+ }
+ }
+ },
+ "/api/filter": {
+ "get": {
+ "tags": ["Search & Discovery"],
+ "summary": "Advanced filter / browse",
+ "operationId": "filterAnime",
+ "parameters": [
+ { "name": "genre", "in": "query", "schema": { "type": "string" }, "description": "e.g. Action, Romance" },
+ { "name": "tag", "in": "query", "schema": { "type": "string" }, "description": "e.g. Isekai, Time Skip" },
+ { "name": "year", "in": "query", "schema": { "type": "integer" } },
+ { "name": "season", "in": "query", "schema": { "type": "string", "enum": ["WINTER", "SPRING", "SUMMER", "FALL"] } },
+ { "name": "format", "in": "query", "schema": { "type": "string", "enum": ["TV", "MOVIE", "OVA", "ONA", "SPECIAL", "MUSIC"] } },
+ { "name": "status", "in": "query", "schema": { "type": "string", "enum": ["RELEASING", "FINISHED", "NOT_YET_RELEASED", "CANCELLED", "HIATUS"] } },
+ { "name": "sort", "in": "query", "schema": { "type": "string", "enum": ["SCORE_DESC", "POPULARITY_DESC", "TRENDING_DESC", "START_DATE_DESC", "FAVOURITES_DESC", "UPDATED_AT_DESC"], "default": "POPULARITY_DESC" } },
+ { "name": "page", "in": "query", "schema": { "type": "integer", "default": 1 } },
+ { "name": "per_page", "in": "query", "schema": { "type": "integer", "default": 20, "maximum": 50 } }
+ ],
+ "responses": {
+ "200": { "description": "Filtered results with pagination" }
+ }
+ }
+ },
+ "/api/trending": {
+ "get": {
+ "tags": ["Collections"],
+ "summary": "Trending anime",
+ "operationId": "getTrending",
+ "parameters": [
+ { "name": "page", "in": "query", "schema": { "type": "integer", "default": 1 } },
+ { "name": "per_page", "in": "query", "schema": { "type": "integer", "default": 20 } }
+ ],
+ "responses": { "200": { "description": "Trending anime list" } }
+ }
+ },
+ "/api/popular": {
+ "get": {
+ "tags": ["Collections"],
+ "summary": "Most popular anime",
+ "operationId": "getPopular",
+ "parameters": [
+ { "name": "page", "in": "query", "schema": { "type": "integer", "default": 1 } },
+ { "name": "per_page", "in": "query", "schema": { "type": "integer", "default": 20 } }
+ ],
+ "responses": { "200": { "description": "Popular anime list" } }
+ }
+ },
+ "/api/upcoming": {
+ "get": {
+ "tags": ["Collections"],
+ "summary": "Upcoming anime",
+ "operationId": "getUpcoming",
+ "parameters": [
+ { "name": "page", "in": "query", "schema": { "type": "integer", "default": 1 } },
+ { "name": "per_page", "in": "query", "schema": { "type": "integer", "default": 20 } }
+ ],
+ "responses": { "200": { "description": "Upcoming anime list" } }
+ }
+ },
+ "/api/recent": {
+ "get": {
+ "tags": ["Collections"],
+ "summary": "Recently updated anime",
+ "operationId": "getRecent",
+ "parameters": [
+ { "name": "page", "in": "query", "schema": { "type": "integer", "default": 1 } },
+ { "name": "per_page", "in": "query", "schema": { "type": "integer", "default": 20 } }
+ ],
+ "responses": { "200": { "description": "Recent anime list" } }
+ }
+ },
+ "/api/spotlight": {
+ "get": {
+ "tags": ["Collections"],
+ "summary": "Spotlight / featured anime",
+ "operationId": "getSpotlight",
+ "responses": { "200": { "description": "Top 10 trending + popular" } }
+ }
+ },
+ "/api/schedule": {
+ "get": {
+ "tags": ["Collections"],
+ "summary": "Airing schedule",
+ "operationId": "getSchedule",
+ "parameters": [
+ { "name": "page", "in": "query", "schema": { "type": "integer", "default": 1 } },
+ { "name": "per_page", "in": "query", "schema": { "type": "integer", "default": 20 } }
+ ],
+ "responses": { "200": { "description": "Upcoming episodes with timestamps" } }
+ }
+ },
+ "/api/info/{id}": {
+ "get": {
+ "tags": ["Anime Details"],
+ "summary": "Full anime info",
+ "operationId": "getAnimeInfo",
+ "parameters": [
+ { "name": "id", "in": "path", "required": true, "schema": { "type": "integer" }, "description": "AniList anime ID" }
+ ],
+ "responses": { "200": { "description": "Complete anime metadata, characters, relations, recommendations" } }
+ }
+ },
+ "/api/anime/{id}/characters": {
+ "get": {
+ "tags": ["Anime Details"],
+ "summary": "Characters with voice actors",
+ "operationId": "getCharacters",
+ "parameters": [
+ { "name": "id", "in": "path", "required": true, "schema": { "type": "integer" } },
+ { "name": "page", "in": "query", "schema": { "type": "integer", "default": 1 } },
+ { "name": "per_page", "in": "query", "schema": { "type": "integer", "default": 25 } }
+ ],
+ "responses": { "200": { "description": "Paginated character list" } }
+ }
+ },
+ "/api/anime/{id}/relations": {
+ "get": {
+ "tags": ["Anime Details"],
+ "summary": "Related anime",
+ "operationId": "getRelations",
+ "parameters": [
+ { "name": "id", "in": "path", "required": true, "schema": { "type": "integer" } }
+ ],
+ "responses": { "200": { "description": "Sequels, prequels, side stories, etc." } }
+ }
+ },
+ "/api/anime/{id}/recommendations": {
+ "get": {
+ "tags": ["Anime Details"],
+ "summary": "Community recommendations",
+ "operationId": "getRecommendations",
+ "parameters": [
+ { "name": "id", "in": "path", "required": true, "schema": { "type": "integer" } },
+ { "name": "page", "in": "query", "schema": { "type": "integer", "default": 1 } },
+ { "name": "per_page", "in": "query", "schema": { "type": "integer", "default": 10 } }
+ ],
+ "responses": { "200": { "description": "Recommended anime sorted by rating" } }
+ }
+ },
+ "/api/episodes/{id}": {
+ "get": {
+ "tags": ["Streaming"],
+ "summary": "Episode list from all providers",
+ "operationId": "getEpisodes",
+ "parameters": [
+ { "name": "id", "in": "path", "required": true, "schema": { "type": "integer" }, "description": "AniList anime ID" }
+ ],
+ "responses": { "200": { "description": "Episodes organized by provider and audio type (sub/dub)" } }
+ }
+ },
+ "/api/sources": {
+ "get": {
+ "tags": ["Streaming"],
+ "summary": "Streaming sources (detailed)",
+ "operationId": "getSources",
+ "parameters": [
+ { "name": "episodeId", "in": "query", "required": true, "schema": { "type": "string" } },
+ { "name": "provider", "in": "query", "required": true, "schema": { "type": "string" } },
+ { "name": "anilistId", "in": "query", "required": true, "schema": { "type": "integer" } },
+ { "name": "category", "in": "query", "schema": { "type": "string", "enum": ["sub", "dub"], "default": "sub" } }
+ ],
+ "responses": { "200": { "description": "M3U8 streams, subtitles, intro/outro timestamps" } }
+ }
+ },
+ "/api/watch/{provider}/{anilistId}/{category}/{slug}": {
+ "get": {
+ "tags": ["Streaming"],
+ "summary": "Streaming sources (simple slug-based)",
+ "operationId": "getWatchSources",
+ "parameters": [
+ { "name": "provider", "in": "path", "required": true, "schema": { "type": "string" } },
+ { "name": "anilistId", "in": "path", "required": true, "schema": { "type": "integer" } },
+ { "name": "category", "in": "path", "required": true, "schema": { "type": "string", "enum": ["sub", "dub"] } },
+ { "name": "slug", "in": "path", "required": true, "schema": { "type": "string" }, "description": "e.g. animepahe-1" }
+ ],
+ "responses": { "200": { "description": "M3U8 streams, subtitles, intro/outro timestamps" } }
+ }
+ }
+ },
+ "tags": [
+ { "name": "System", "description": "Health & status" },
+ { "name": "Search & Discovery", "description": "Search, suggestions, and advanced filtering" },
+ { "name": "Collections", "description": "Trending, popular, upcoming, recent, spotlight, schedule" },
+ { "name": "Anime Details", "description": "Full anime info, characters, relations, recommendations" },
+ { "name": "Streaming", "description": "Episode lists and M3U8 streaming sources" }
+ ]
+}
diff --git a/server.js b/server.js
index 70e87f7..1e094c8 100644
--- a/server.js
+++ b/server.js
@@ -43,12 +43,43 @@ app.use(express.static(path.join(__dirname, "public")));
app.get("/api/health", (req, res) => {
res.json({
status: "ok",
- version: "1.0.0",
+ version: "1.1.0",
timestamp: new Date().toISOString(),
uptime: process.uptime(),
+ endpoints: 16,
+ providers: ["kiwi", "pewe", "bee", "bonk", "bun", "ally", "nun", "twin", "cog", "moo", "hop", "telli"],
});
});
+// Swagger UI page
+app.get("/docs", (req, res) => {
+ res.send(`
+
+
+
+
+
MiruroAPI — Swagger UI
+
+
+
+
+
+
+
+
+`);
+});
+
// Routes
const apiRoutes = require("./src/routes/apiRoutes");
app.use("/api", apiRoutes);
diff --git a/src/helpers/pipe.js b/src/helpers/pipe.js
index 3b84dc4..33212fc 100644
--- a/src/helpers/pipe.js
+++ b/src/helpers/pipe.js
@@ -92,7 +92,14 @@ async function fetchRawEpisodes(anilistId) {
async function getEpisodes(anilistId) {
const data = await fetchRawEpisodes(anilistId);
- return injectSourceSlugs(data, anilistId);
+ const result = injectSourceSlugs(data, anilistId);
+ // Ensure mappings field is present (like Walter's API)
+ if (!result.mappings) {
+ result.mappings = { anilistId };
+ if (result.malId) result.mappings.malId = result.malId;
+ if (result.kitsuId) result.mappings.kitsuId = result.kitsuId;
+ }
+ return result;
}
async function getSources(episodeId, provider, anilistId, category = "sub") {