A self-hostable, multi-provider anime scraper + web UI that extracts m3u8 (HLS) streams, MP4 streams, and WebVTT subtitles from multiple upstream anime sites, enriched with AniList metadata.
Built with Next.js 16 Β· TypeScript Β· Tailwind CSS Β· shadcn/ui Β· hls.js. Deploys to Vercel in one click or runs anywhere with Docker.
β οΈ Educational project. Streams are proxied from upstream providers for personal use only. The maintainer does not host any media content. Support the official release when available in your region.
- ποΈ Multi-provider architecture β switch between upstreams from the UI with one click
- Animetsu β
animetsu.liveΒ· soft sub Β· 4 servers (kite / dio / sage / meg) Β· HLS m3u8 - Anikuro β
anikuro.ruΒ· aggregates 11 upstreams (animeverse / animegg / anikoto / animepahe / reanime / animedao / anidb / animedunya / animeverse / allani / senshi / animix) Β· MP4 + HLS
- Animetsu β
- π Search β instant debounced search across the active provider's catalog
- π¬ Universal media player β hls.js for HLS, native HTML5 for MP4, with quality switcher and VTT subtitle selector
π °οΈ Sub / Dub toggle β switch between subtitled and dubbed sources per episode- βοΈ Skip intro / outro β auto-detected skip markers surface as in-player buttons
- π§ AniList enrichment β characters, studios, recommendations, YouTube trailer, next-airing countdown
- π₯ Trending now β pulled from the AniList GraphQL API on the home page
- π Recently released β live from animetsu.live
- π‘οΈ Cloudflare-friendly β built-in CORS proxy rewrites all upstream URIs through your own domain, with retry + fallback logic for 403/429/503 challenges, and per-upstream Referer support
- π³ One-command Docker β
docker compose upand you're done - β² One-click Vercel β pure Node.js runtime, no native deps
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Browser (you) β
β ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β / β Home (search Β· trending Β· recent) β β
β β [Provider switcher: Animetsu β Anikuro] β β
β β β click anime β β
β β /?anime=<id> β Details (info Β· episodes Β· trailer) β β
β β β click episode β β
β β /?watch=<id>&ep=<n> β MediaPlayer (HLS or MP4) β β
β ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
βββββββββββββββββββββββββββββββββ¬ββββββββββββββββββββββββββββββββββ
β fetch (same origin, ?provider=β¦)
βΌ
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Next.js (your domain) β
β β
β /api/scrape/providers β list of registered providers β
β /api/scrape/search ββ β
β /api/scrape/info β β
β /api/scrape/episodes β β dispatches to the active β
β /api/scrape/servers β provider (animetsu or anikuro) β
β /api/scrape/sources ββ β
β β
β /api/scrape/anilist β AniList GraphQL (enrichment) β
β /api/scrape/recent β animetsu recent releases β
β β
β /api/proxy/m3u8?url=β¦&referer=β¦ β
β β Handles both HLS playlists and MP4 streams β
β β - HLS: rewrites relative URIs through itself β
β β - MP4: passes through with Range support β
β β - Sets the right Referer per upstream β
β βΌ β
β Provider abstraction: β
β ββββββββββββββββ ββββββββββββββββ β
β β Animetsu β β Anikuro β β
β β /v2/api/ β β /api/v1/ β β
β β anime/* β β anime/* β β
β β β β β β
β β β³ kite β β β³ 11 upstreams β
β β β³ dio β β tried in parallel β
β β β³ sage β β MP4 preferred β
β β β³ meg β β β
β ββββββββββββββββ ββββββββββββββββ β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
| Path | Purpose |
|---|---|
src/lib/providers/types.ts |
Unified Provider interface β every backend implements this |
src/lib/providers/index.ts |
Provider registry β single source of truth |
src/lib/providers/animetsu.ts |
Animetsu adapter (wraps the animetsu client) |
src/lib/providers/anikuro.ts |
Anikuro adapter (multi-provider fan-out, MP4 preference) |
src/lib/animetsu/client.ts |
Raw HTTP client for animetsu.live |
src/lib/anilist/client.ts |
AniList GraphQL client (cached, rate-limit-friendly) |
src/app/api/scrape/* |
API routes β all accept `?provider=animetsu |
src/app/api/proxy/m3u8/route.ts |
Universal CORS proxy (HLS + MP4 + VTT, per-upstream Referer) |
src/components/animetsu/media-player.tsx |
Universal player β HLS via hls.js, MP4 via native <video> |
src/app/page.tsx |
Single-page UI with provider switcher |
git clone <this-repo> anime-scraper
cd anime-scraper
bun install # or: npm install / pnpm install
cp .env.example .env.local # optional β defaults already work
bun run dev # β http://localhost:3000- Push this repo to GitHub.
- Go to vercel.com/new and import the repo.
- Vercel auto-detects Next.js β no build config needed.
- (Optional) Set environment variables from
.env.examplein Project β Settings β Environment Variables. - Click Deploy. Done.
vercel.json already bumps the /api/proxy/m3u8 function to 60 s maxDuration + 1 GB RAM so it can stream long episodes without timing out.
docker compose up -d --build
# β http://localhost:3000The Dockerfile uses Next.js standalone output β the final image is ~150 MB and runs as a non-root user.
| Env var | Default | Purpose |
|---|---|---|
ANIMETSU_API_BASE |
https://animetsu.live/v2/api/anime |
Override the animetsu JSON API base |
SWIFTSTREAM_PROXY |
https://swiftstream.top/proxy |
Override the animetsu m3u8 / subtitle proxy |
ANIKURO_BASE |
https://anikuro.ru |
Override the anikuro API base |
ANIKURO_PROXY |
https://proxy.anikuro.ru |
Override the anikuro MP4 / m3u8 proxy |
FALLBACK_PROXY |
(empty) | Optional cors-anywhere-style proxy used when Cloudflare returns a 403/429/503 |
All routes are GET. Responses are JSON unless noted. All /api/scrape/* routes accept a ?provider=animetsu|anikuro query param (defaults to animetsu).
Returns the list of registered providers.
Returns results[] with the provider-specific id, title, cover image, year, score, etc.
Returns the full anime info, optionally merged with AniList data when enrich=1 (default).
Returns the list of episodes.
Returns the available streaming servers for that episode.
- Animetsu:
kite(default, soft sub) Β·dioΒ·sageΒ·meg - Anikuro:
animeverse(default, MP4) Β·animeggΒ·anikotoΒ·animepaheΒ·reanimeΒ·animedaoΒ·animeggΒ·anidbΒ·animedunyaΒ·animeverseΒ·allaniΒ·senshiΒ·animix
Returns a player-ready payload:
{
"sources": [
{ "url": "/api/proxy/m3u8?url=β¦", "type": "master", "quality": "auto", "isMaster": true },
{ "url": "/api/proxy/m3u8?url=β¦", "type": "hls", "quality": "1080p" },
// OR
{ "url": "https://proxy.anikuro.ru/β¦", "type": "mp4", "quality": "720p" }
],
"subtitles": [{ "lang": "English", "url": "/api/proxy/m3u8?format=vtt&url=β¦" }],
"skips": { "intro": { "start": 0, "end": 0 }, "outro": { "start": 0, "end": 0 } },
"server": "animeverse",
"provider": "anikuro",
"qualities": [{ "label": "1080p", "resolution": "1920x1080", "url": "β¦" }]
}For anikuro, if server is omitted or set to auto/default, the provider fans out to a curated subset of upstreams in parallel and returns the best playable source (preferring MP4 over HLS).
Universal CORS proxy for upstream m3u8 / MP4 / VTT URLs. Auto-detects content type:
application/vnd.apple.mpegurlcontent β rewrites all relative URIs in the playlist back through/api/proxy/m3u8(preserving the&referer=if provided)video/mp4content β streamed through with Range supporttext/vttcontent β passed through withtext/vtt; charset=utf-8- Binary segments (TS / fMP4) β streamed through with upstream content-type
The optional ?referer= param sets the Referer header sent to the upstream β required for anikuro HLS streams that come from referer-locked CDNs.
Direct AniList GraphQL passthrough. Cached for 30 min.
The provider abstraction makes it trivial to add a new upstream site:
- Create
src/lib/providers/<name>.tsand implement theProviderinterface:export const myProvider: Provider = { meta: { id: "mine", label: "Mine", /* β¦ */ }, async search(query) { /* β¦ */ return []; }, async getInfo(id) { /* β¦ */ return null; }, async getEpisodes(id) { /* β¦ */ return []; }, async getServers(id, ep) { /* β¦ */ return []; }, // optional async getSources(opts) { /* β¦ */ return { sources, subtitles, server, provider: "mine" }; }, };
- Register it in
src/lib/providers/index.ts:export const providers: Record<ProviderId, Provider> = { animetsu: animetsuProvider, anikuro: anikuroProvider, mine: myProvider, // β add here };
- Add the provider id to the
ProviderIdunion type insrc/lib/providers/types.ts.
The UI and API routes will pick it up automatically β no other changes needed.
- The animetsu.live frontend is a Vite SPA. Its main bundle reveals the API base:
window.b = https://animetsu.live/v2and an axios instance atole = ${b}/api. - All API calls are routed through
${ole}/anime/<key>. The interesting keys are:search/?query=<q>info/<id>eps/<id>servers/<id>/<ep>oppai/<id>/<ep>?server=<s>&source_type=sub|dubβ returns{ sources, subs, skips }
- The
sources[].urlis a relative path like/oppai/kite/<token>. Whenneed_proxy === true, the host ishttps://swiftstream.top/proxy. - The master playlist contains relative token paths for each quality (360p / 720p / 1080p).
- Subtitles come back as full
https://swiftstream.top/proxy/oppai/kite/<token>URLs in WebVTT format.
- Anikuro.ru is a Next.js-style site with a clean JSON API at
/api/v1/*that aggregates 11 upstream anime providers. - Endpoints used:
discovery/search?query=<q>β searchanime/<id>β info (id is the AniList id)anime/<id>/episodesβ episode listsources/<provider>/<animeId>:<epNum>β stream sources for a specific upstream provider
- Stream URLs come back pre-wrapped through
https://proxy.anikuro.ru/<base64>.m3u8|referer?proxy=0where the base64 decodes to<streamUrl>|<upstreamReferer>. - As of 2026-06, anikuro's m3u8 proxy returns HTTP 500, but the MP4 proxy works with Range support. The provider prefers MP4 sources (animeverse, animegg) and falls back to HLS (anikoto, animix) routed through our own
/api/proxy/m3u8with the upstream Referer set. - When
server=auto(the default), the provider fans out to a curated subset of 4 upstreams in parallel (~600 ms total), then falls back to the remaining 7 if no hit was found.
| Layer | Choice | Why |
|---|---|---|
| Framework | Next.js 16 (App Router) | One codebase for API + UI, deploys to Vercel out of the box |
| Language | TypeScript 5 | Strict typing for upstream payloads |
| UI | Tailwind CSS 4 + shadcn/ui | Fast, accessible, themeable |
| Player | hls.js + native HTML5 <video> |
HLS for adaptive streaming, native for MP4 |
| State | React hooks (no global store needed) | App is a single-page flow |
| Caching | In-memory LRU + Cache-Control headers |
No external cache infra required |
| Container | Docker (Node 22-alpine, standalone) | ~150 MB final image |
| Host | Vercel or any Node host | Pure JS β no native deps |
MIT β see LICENSE. The project is provided for educational purposes. Use responsibly and in accordance with the laws of your jurisdiction.
{ "providers": [ { "id": "animetsu", "label": "Animetsu", "description": "β¦", "defaultServer": "kite", "supportsDub": true }, { "id": "anikuro", "label": "Anikuro", "description": "β¦", "defaultServer": "animeverse", "supportsDub": true } ] }