mirror of
https://github.com/Shineii86/MiruroAPI.git
synced 2026-10-22 10:15:10 +00:00
- Full index.html rewrite with AniKotoAPI-style features: * Canvas particle effects (cyan particles) * Scroll-reveal animations (IntersectionObserver) * Animated stat counters (count up on scroll) * Infinite scrolling marquee (8 feature badges) * Live interactive API console (5 presets: Search, Trending, Episodes, Info, Health) * Sidebar endpoint explorer (16 endpoints, multi-language tabs: cURL/JS/Python) * 4 interactive playground cards (Search, Info, Episodes, Trending) * Custom scrollbar, JetBrains Mono font * 4-column grid footer with API/Resources/Legal * Fixed header with scroll-aware glass blur * Ambient orb gradient animations * CTA section with gradient overlay - Added public/404.html (glitch animation error page) - Added public/manifest.json (PWA, theme: #38bdf8) - Added public/robots.txt (Allow /, Disallow /api) - Added public/sitemap.xml (4 URLs) - Added public/tos.html (13 sections) - Added public/privacy.html (8 sections) - Added .env.example, CONTRIBUTING.md, LICENSE, render.yaml, test.js - Added docs/ (5 files: index, endpoints, streaming, examples, architecture) - Updated server.js: clean URL routes (/tos, /privacy), 404.html fallback - Updated package.json: devDependencies nodemon, repository, dev script - All branding: AniKotoAPI → MiruroAPI, cyan color scheme (#38bdf8)
239 lines
5.6 KiB
Markdown
239 lines
5.6 KiB
Markdown
# Streaming Flow Guide
|
|
|
|
This guide explains how to get streaming URLs from MiruroAPI in 3 steps.
|
|
|
|
## Overview
|
|
|
|
```
|
|
Step 1: Get Episodes → /api/episodes/:anilistId
|
|
Step 2: Get Stream URLs → /api/watch/:provider/:anilistId/:category/:slug
|
|
Step 3: Play M3U8 → Use HLS player
|
|
```
|
|
|
|
---
|
|
|
|
## Step 1: Get Episodes
|
|
|
|
Each anime has episodes from multiple providers. Fetch the episode list with the AniList ID.
|
|
|
|
**Request:**
|
|
|
|
```bash
|
|
curl "https://mirurotvapi.vercel.app/api/episodes/20"
|
|
```
|
|
|
|
**Response:**
|
|
|
|
```json
|
|
{
|
|
"success": true,
|
|
"results": {
|
|
"providers": {
|
|
"kiwi": {
|
|
"meta": { "id": "1571", "title": "Naruto", "type": "TV" },
|
|
"episodes": {
|
|
"sub": [
|
|
{
|
|
"id": "watch/kiwi/20/sub/animepahe-1",
|
|
"number": 1,
|
|
"title": "Enter: Naruto Uzumaki!",
|
|
"image": "https://image.tmdb.org/t/p/original/...",
|
|
"airDate": "2002-10-03",
|
|
"audio": "sub",
|
|
"filler": false
|
|
}
|
|
]
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
> **Key field:** `id` in each episode — Pass this to the next step.
|
|
|
|
---
|
|
|
|
## Step 2: Get Stream URLs
|
|
|
|
Use the episode `id` from Step 1 to get M3U8 streaming URLs.
|
|
|
|
**Request:**
|
|
|
|
```bash
|
|
curl "https://mirurotvapi.vercel.app/api/watch/kiwi/20/sub/animepahe-1"
|
|
```
|
|
|
|
**Response:**
|
|
|
|
```json
|
|
{
|
|
"success": true,
|
|
"results": {
|
|
"streams": [
|
|
{
|
|
"url": "https://vault-01.uwucdn.top/stream/.../uwu.m3u8",
|
|
"type": "hls",
|
|
"quality": "360p",
|
|
"resolution": { "width": 640, "height": 360 },
|
|
"codec": "h264",
|
|
"audio": "sub",
|
|
"fansub": "df68",
|
|
"isActive": false,
|
|
"referer": "https://kwik.cx/e/..."
|
|
}
|
|
],
|
|
"download": "https://pahe.win/LJmbA"
|
|
}
|
|
}
|
|
```
|
|
|
|
> **Done!** Use `streams[0].url` in your HLS video player.
|
|
|
|
---
|
|
|
|
## Step 3: Play M3U8
|
|
|
|
### HLS.js (Browser)
|
|
|
|
```html
|
|
<script src="https://cdn.jsdelivr.net/npm/hls.js@latest"></script>
|
|
<video id="player" controls></video>
|
|
|
|
<script>
|
|
const url = 'https://vault-01.uwucdn.top/stream/.../uwu.m3u8';
|
|
const video = document.getElementById('player');
|
|
|
|
if (Hls.isSupported()) {
|
|
const hls = new Hls();
|
|
hls.loadSource(url);
|
|
hls.attachMedia(video);
|
|
} else if (video.canPlayType('application/vnd.apple.mpegurl')) {
|
|
video.src = url; // Native HLS (Safari)
|
|
}
|
|
</script>
|
|
```
|
|
|
|
---
|
|
|
|
## JavaScript Example
|
|
|
|
```javascript
|
|
async function getStreamUrl(anilistId) {
|
|
// Step 1: Get episodes
|
|
const episodesRes = await fetch(`https://mirurotvapi.vercel.app/api/episodes/${anilistId}`);
|
|
const episodesData = await episodesRes.json();
|
|
const providers = episodesData.results.providers;
|
|
|
|
// Pick first provider with sub episodes
|
|
const provider = Object.keys(providers)[0];
|
|
const episodes = providers[provider].episodes.sub;
|
|
|
|
if (!episodes || episodes.length === 0) {
|
|
throw new Error('No episodes found');
|
|
}
|
|
|
|
// Use first episode's id
|
|
const episodeId = episodes[0].id; // "watch/kiwi/20/sub/animepahe-1"
|
|
const parts = episodeId.split('/');
|
|
const category = parts[3]; // "sub"
|
|
const slug = parts[4]; // "animepahe-1"
|
|
|
|
// Step 2: Get stream URLs
|
|
const streamRes = await fetch(`https://mirurotvapi.vercel.app/api/watch/${provider}/${anilistId}/${category}/${slug}`);
|
|
const streamData = await streamRes.json();
|
|
|
|
return streamData.results.streams[0].url; // "https://...m3u8"
|
|
}
|
|
|
|
// Usage
|
|
getStreamUrl(20)
|
|
.then(url => console.log('Stream URL:', url))
|
|
.catch(err => console.error('Error:', err));
|
|
```
|
|
|
|
---
|
|
|
|
## Python Example
|
|
|
|
```python
|
|
import requests
|
|
|
|
def get_stream_url(anilist_id):
|
|
base = "https://mirurotvapi.vercel.app/api"
|
|
|
|
# Step 1: Get episodes
|
|
episodes_res = requests.get(f"{base}/episodes/{anilist_id}")
|
|
episodes_data = episodes_res.json()
|
|
providers = episodes_data['results']['providers']
|
|
|
|
# Pick first provider
|
|
provider = list(providers.keys())[0]
|
|
episodes = providers[provider]['episodes']['sub']
|
|
|
|
if not episodes:
|
|
raise Exception("No episodes found")
|
|
|
|
episode_id = episodes[0]['id'] # "watch/kiwi/20/sub/animepahe-1"
|
|
parts = episode_id.split('/')
|
|
category = parts[3]
|
|
slug = parts[4]
|
|
|
|
# Step 2: Get stream URLs
|
|
stream_res = requests.get(f"{base}/watch/{provider}/{anilist_id}/{category}/{slug}")
|
|
stream_data = stream_res.json()
|
|
|
|
return stream_data['results']['streams'][0]['url']
|
|
|
|
# Usage
|
|
url = get_stream_url(20)
|
|
print(f"Stream URL: {url}")
|
|
```
|
|
|
|
---
|
|
|
|
## Sub & Dub
|
|
|
|
Providers return both `sub` and `dub` episode lists:
|
|
|
|
```javascript
|
|
const eps = await fetch("/api/episodes/20").then(r => r.json());
|
|
const kiwi = eps.results.providers.kiwi.episodes;
|
|
|
|
// Get sub episodes
|
|
const subEps = kiwi.sub; // [{ id: "watch/kiwi/20/sub/animepahe-1", ... }]
|
|
|
|
// Get dub episodes (if available)
|
|
const dubEps = kiwi.dub; // [{ id: "watch/kiwi/20/dub/...", ... }]
|
|
```
|
|
|
|
---
|
|
|
|
## Available Providers
|
|
|
|
| Provider | Sub | Dub | Download | Skip Times |
|
|
|:---|:---:|:---:|:---:|:---:|
|
|
| kiwi | ✅ | ❌ | ✅ | ❌ |
|
|
| pewe | ✅ | ❌ | ❌ | ❌ |
|
|
| bee | ✅ | ✅ | ❌ | ❌ |
|
|
| bonk | ✅ | ✅ | ✅ | ✅ |
|
|
| bun | ✅ | ✅ | ❌ | ❌ |
|
|
| ally | ✅ | ❌ | ✅ | ❌ |
|
|
| nun | ✅ | ❌ | ❌ | ❌ |
|
|
| twin | ✅ | ✅ | ❌ | ❌ |
|
|
| cog | ✅ | ❌ | ❌ | ❌ |
|
|
| moo | ✅ | ❌ | ✅ | ❌ |
|
|
| hop | ❌ | ✅ | ❌ | ❌ |
|
|
| telli | ✅ | ❌ | ❌ | ❌ |
|
|
|
|
---
|
|
|
|
## Troubleshooting
|
|
|
|
| Issue | Solution |
|
|
|-------|----------|
|
|
| "No episodes found" | Check the AniList ID is correct (use `/api/search` first) |
|
|
| "No providers" | The anime may not be available on any provider |
|
|
| Stream URL 403 | Some servers require specific referrer headers |
|
|
| CORS error | Use a proxy or access from server-side |
|