From c13e0bbd7edcb32c5f6e1d6d75cb045bffc907a6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=CA=9C=C9=AA=C9=B4=E1=B4=87=C9=AA=20N=E1=B4=8F=E1=B4=9C?= =?UTF-8?q?=E1=B4=A2=E1=B4=87=C9=B4?= <157171073+Shineii86@users.noreply.github.com> Date: Tue, 9 Jun 2026 14:16:33 +0530 Subject: [PATCH] feat: v1.1.0 - Swagger docs, mappings, Docker, premium landing page - Swagger UI interactive docs at /docs - OpenAPI 3.0 spec at /openapi.json - mappings field in episodes response (anilistId, malId, kitsuId) - Docker support (Dockerfile + .dockerignore) - Premium landing page with try-it buttons (Walter-style) - Health endpoint with providers list and endpoint count --- .dockerignore | 5 + CHANGELOG.md | 10 ++ Dockerfile | 12 +++ README.md | 37 ++++++- public/index.html | 222 +++++++++++++++++++++++++++++++++------ public/openapi.json | 247 ++++++++++++++++++++++++++++++++++++++++++++ server.js | 33 +++++- src/helpers/pipe.js | 9 +- 8 files changed, 538 insertions(+), 37 deletions(-) create mode 100644 .dockerignore create mode 100644 Dockerfile create mode 100644 public/openapi.json 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 @@ - MiruroAPI - Free Anime Streaming API - + MiruroAPI — Free Anime Streaming API + +
-
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
+
+ +

MiruroAPI

+
Decrypted, bypassed, and reverse-engineered anime streaming API
+
v1.1.0 — Full Data, Streaming & Pagination
+
+ +
+ 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.
+ + + + + + + + + +
ParamValues
genreAction, Romance, Comedy, Drama, Fantasy, Sci-Fi, etc.
tagIsekai, Time Skip, Reincarnation, etc.
year2025, 2024, etc.
seasonWINTER · SPRING · SUMMER · FALL
formatTV · MOVIE · OVA · ONA · SPECIAL
statusRELEASING · FINISHED · NOT_YET_RELEASED · CANCELLED
sortSCORE_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") {