npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

kaizoku-core

v0.3.4

Published

TypeScript scrapers used by [kaizoku.site](https://kaizoku.site): anime, movies/TV, torrents, live TV, plus TMDB / AniList / Kitsu metadata and a handful of video extractors.

Readme

kaizoku-core

TypeScript scrapers used by kaizoku.site: anime, movies/TV, torrents, live TV, plus TMDB / AniList / Kitsu metadata and a handful of video extractors.

The package is isomorphic (fetch only). It does not include a browser, Puppeteer, or a residential proxy. Whether a provider works is mostly a function of where your process egresses from, not of the TypeScript.


Where you should run this

These sites treat well-known datacenter IP ranges as bots. If you deploy the scraper on Vercel, Netlify, AWS Lambda, Cloudflare Pages Functions as the only hop, or similar, expect mass 403s, empty JSON, and Cloudflare challenge HTML. That is normal. It is not a bug in this repo.

Prefer a VPS (Hetzner, OVH, a random small provider, even a $5 box) whose IP is not sitting in a hyperscaler range every WAF already lists. Run Node there, or put this package behind your own API on that machine.

Cloudflare Workers can still work, but Workers share Cloudflare egress. Some origins are fine with that; others 403 anything that looks like CF. For those you add a non-Cloudflare fetch proxy (Cloud Run, Fly, the same VPS) — see Proxies.

Residential or “stealth” VPS egress is what UniqueStream, XAnime, AnimeOnsen, and MKissa usually need. A Worker in iad will not magically pass their checks.

Test from the environment you will actually ship. A provider that works on your laptop can fail on a Worker, and the reverse is also true.


Proxies

configure() talks about three URLs. Two of them fetch pages/APIs while scraping. The third rewrites playback URLs for the video player.

| Option | What it is | When you need it | |---|---|---| | scrapeProxyUrl + scrapeProxyKey | Cloudflare Worker at POST {url}/fetch. Used by httpGet / httpPost (most anime HTML/JSON). | You are on a VPS/Node and want a Worker to solve or absorb Cloudflare challenges (AnimeDunya is the usual case). If this package already runs on Workers, try without it first — a second Worker hop is often redundant. We have not proven every provider without it, so A/B it. | | movieProxyUrl + movieProxyKey | Same /fetch contract, but the host must not be Cloudflare. Used by movieFetch (almost every movie provider, plus AniZone and XAnime). Falls back to scrapeProxyUrl if unset. | You are scraping from Cloudflare Workers (or any CF product) and the origin blocks CF IPs. Point this at Cloud Run, Fly, or a VPS. If you already run on a VPS, leave it unset — movieFetch uses native fetch from that box. | | proxy.proxyUrl | Media proxy. Rewrites Source.proxiedUrl so the browser can play streams that need Referer/Origin. Unrelated to scraping. | Your player cannot hit the CDN directly. If unset, proxiedUrl stays undefined (it will not silently copy url). |

anilistProxyUrl is the same physical fetch proxy as movieProxyUrl in our deploy (FETCH_PROXY_URL). AniList itself is public; the hop exists so GraphQL from a Worker does not trip AniList’s WAF.

Environment aliases the config object already reads:

  • SCRAPE_PROXY_URL / SCRAPE_PROXY_KEY → scrape Worker
  • FETCH_PROXY_URL / FETCH_PROXY_KEY → fetch proxy (movieProxyUrl and, unless overridden, AniList)
  • PROXY_URL → playback proxy.proxyUrl
  • TMDB_API_KEY

AniZone (and similar)

AniZone is not “magic bypass.” Its scrape uses movieFetch. On a VPS that is just fetch. On Workers you must set movieProxyUrl to a non-Cloudflare /fetch service, otherwise Livewire/HTML comes back 403. The hostname is also on the scrape-Worker skip list in src/utils/http.ts, so it will not go through scrapeProxyUrl even if that is set.

If a provider 403s only after you turn on scrapeProxyUrl, check that skip list — some origins reject the Worker’s egress and want your VPS (or the fetch proxy) instead.


Features

  • Movie & TV scrapers (TMDB id): VidNest, VidFast, VidZee, VidUp, Purstream, VidCore, VidRock, Icefy, LM Script (movies only), RiveStream, VidGod, CineJoy, ZxcStream. Downloads: TrendiMovies.
  • Anime scrapers (AniList / MAL / site ids): see the table below. Several are implemented but currently unusable from datacenter IPs.
  • Airing schedule from AniList AiringSchedule, daily/weekly, timezone offset in minutes.
  • AniList & Kitsu search/detail/trending. Cross-site ids via ARM (arm.haglund.dev).
  • Torrents: Torrentio (TMDB → IMDB) and Nyaa (anilist.torrents.nyaa / kitsu.torrents.nyaa).
  • Live TV M3U aggregators.
  • TMDB details, search, auth, lists.
  • AniSkip intro/outro ranges merged into anime VideoStreams.

Removed from this package (do not call them): Peachify, VidLink (as a top-level provider), VidRift, VixSrc, Vidvault, Mapple, Cinesu, Opstream, Gojo, AnimePahe. VidNest may still list a “VidLink” server internally.


Provider status

Status is “does the scraper return playable URLs from a normal VPS / our fetch proxy,” not “the TypeScript compiles.” Origins die without notice.

Movie & TV

| Provider | Status | Notes | |---|---|---| | VidUp | Active | Enc/dec handshake. Solid default. | | VidFast | Active | Same family as VidUp. Movies more reliable than some TV rows. | | VidCore | Active | Encrypted server list via enc-dec.app. | | VidRock | Active | AES-GCM payloads. Occasional title-specific blanks. | | VidZee | Active | WASM decrypt. | | VidNest | Active | Several backup servers (including a VidLink path). Try another server if one 429s. | | Purstream | Active | Title lookup + catalog stream. Uses httpGet. | | Icefy | Active | Direct HLS. | | RiveStream | Active | HLS/MP4 mix. | | LM Script | Active | Movies only — no TV. | | VidGod | Implemented | Hit-or-miss; we disable it in the Kaizoku app until it settles. | | CineJoy | Implemented | WASM. Same: test before you ship it. | | ZxcStream | Implemented | Can return an empty source list. | | TrendiMovies | Active | Downloads, not a watch provider (tmdb.fetchDownloads). |

Anime

| Provider | Status | Egress | Notes | |---|---|---|---| | AniKoto | Active | VPS or Worker | Large library. Servers vidstream-2, hd-1, vidplay-1. Sub/dub via fetchSources(id, 'sub' \| 'dub'). | | MegaPlay | Active | VPS or Worker | AniList id. HMAC-signed CDN URLs; tiktokcdn-style bodies are junk-stripped in the media proxy. | | KickAssAnime | Active | VPS or Worker | Real <show>/<ep> ids. Dub is often an HLS audio track, sometimes a separate dubId. Empty servers on a dub locale ≠ provider down. | | AniBD | Active | VPS or Worker | AniList id, no title mapping. Sub only. | | AniZone | Active | VPS, or Worker + fetch proxy | Multi-audio m3u8. Uses movieFetch. | | 2Dhive | Active | VPS or Worker | MAL id, no title mapping. | | AniKage | Active | VPS or Worker | AniList id. Collects koto/uwu/kiwi/megg/dib/wave. Prefer url over proxiedUrl in the browser. | | AnimeSogo | Active | VPS or Worker | AniKoto-like catalog and MegaPlay extractors. | | AnimeDunya | Active | Scrape Worker helps | MAL id, sub only. Cloudflare-managed challenge — scrapeProxyUrl is the usual fix. | | AnimeParadise | Active | VPS or Worker | HLS + subs. Sub only. Prefer direct url. | | AniNeko | Down | — | Site currently dead. Code still exported. | | AniDB | Maintenance | — | Streams not usable right now. | | AniWaves | Streams broken | — | EchoVideo + numeric HLS is implemented; live playlists currently fail. | | MKissa | Needs VPS / challenge | Residential or challenge-capable VPS | Captcha is not solved. Throws NEED_CAPTCHA. | | AnimeOnsen | Needs VPS | Residential/VPS | Session cookie scrape. Same class of failure as XAnime/UniqueStream from Workers. | | XAnime | Needs VPS | Residential/VPS | GraphQL {v,iv,ct} via movieFetch. CF egress often 403s. | | UniqueStream | Needs VPS | Residential/VPS | AES unwrap (x-am-media-id). | | Senshi | 403 on rewrite | — | Sources come back; playback/proxy rewrite 403s. | | AnimeNoSub | 403 on rewrite | — | Same. | | ReAnime | Unplayable | — | Often a single .ts / not a real #EXTM3U; CDN 403s. | | AnimeGG | Unreliable handoff | — | Sub/dub flags lie; player handoff is messy. | | AnimeUnity | Italian | — | Italian audio/subs. Skip for an EN catalog. | | AllManga | List only | — | Search/episodes work; sources AA_CRYPTO_MISSING. | | Gojo | Removed | — | animetsu.live dead. | | AnimePahe | Removed | — | JS challenge; needs a real browser. |

Audio: how sub/dub is exposed

Getting this wrong is how you ship a Dub button that does nothing.

| Pattern | Providers | How to switch | |---|---|---| | Category argument | AniKoto, MegaPlay, AniNeko, 2Dhive, AnimeSogo, AniKage, AniWaves | fetchSources(id, 'sub' \| 'dub') | | Audio tracks in the manifest | AniZone, KickAssAnime (most shows) | Do not change the URL. Switch audioTrack in the player. | | Separate episode id | KickAssAnime | Episode.dubId, or fetchSources(id, 'dub') | | Sub only | AniBD, AnimeDunya, AnimeParadise, AnimeOnsen | No audio parameter. |

AniBD manifests have no EXT-X-MEDIA:TYPE=AUDIO. KickAssAnime hasDub means “a dub row exists,” not “a dub file plays.”


Installation

npm install kaizoku-core
# or
pnpm add kaizoku-core

Requires a global fetch (Node 18+, Bun, Workers, modern browsers).


Configuration

import { configure } from "kaizoku-core";

configure({
  tmdbApiKey: process.env.TMDB_API_KEY,

  // Playback rewrite for the player (optional)
  proxy: {
    enabled: true,
    proxyUrl: process.env.PROXY_URL,
    generateSourceProxies: true,
  },

  // Cloudflare Worker scrape hop (optional — see Proxies)
  scrapeProxyUrl: process.env.SCRAPE_PROXY_URL,
  scrapeProxyKey: process.env.SCRAPE_PROXY_KEY,

  // Non-Cloudflare fetch hop (optional — see Proxies)
  movieProxyUrl: process.env.FETCH_PROXY_URL,
  movieProxyKey: process.env.FETCH_PROXY_KEY,
  anilistProxyUrl: process.env.FETCH_PROXY_URL,
  anilistProxyKey: process.env.FETCH_PROXY_KEY,
});

Both scrape and fetch proxies expect:

POST /fetch
X-Proxy-Key: <key>
Content-Type: application/json

{ "url": "https://…", "method": "GET", "headers": { }, "body": "…" }

and return the upstream response.

proxy.proxyUrl

Rewrites each stream to your media proxy with Referer/Origin in the payload. Consumers that prefer url over proxiedUrl (AniKage, AnimeParadise, AniZone in the Kaizoku app) skip this on purpose when the CDN allows browser playback.


Quick start

Movies / TV (TMDB id)

import { tmdb } from "kaizoku-core";

const stream = await tmdb.fetchSourcesByProvider("vidup", "27205", "movie");
console.log(stream.sources[0].url);

const all = await tmdb.fetchAllSources("84958", "tv", 1, 1);

Torrents

import { torrentio, nyaa, anilist, kitsu } from "kaizoku-core";

const result = await torrentio.fetchTorrents("550", "movie");
const page = await nyaa.search("Frieren 1080p");
const fromAnilist = await anilist.torrents.nyaa(154587, { episode: 1 });
const fromKitsu = await kitsu.torrents.nyaa(12, { episode: 1 });

Live TV

import { livetv } from "kaizoku-core";

const xumo = await livetv.fetchLiveTV("xumo");
const all = await livetv.fetchLiveTV("all");

TMDB meta

import { tmdb } from "kaizoku-core";

const movie = await tmdb.getMovieDetails("27205");
const account = await tmdb.getAccountDetails(userAccessToken);

AniList / Kitsu / anime scrapers

import { anilist, kitsu, anikoto } from "kaizoku-core";

const trending = await anilist.getTrending(1, 10);
const kitsuTrending = await kitsu.getTrending(1, 10);

const anilistEpisodes = await anilist.fetchEpisodesByProvider("166240", "anikoto");
const stream = await anikoto.fetchSources(anilistEpisodes[0].id, "sub");

Kitsu list helpers use the same names as AniList (getPopularThisSeason, searchAnime, …) but Kitsu field names (canonicalTitle, averageRating, …). getTrending is a single curated list — later pages are empty.

Cross-site ids: fetchArmIds("kitsu", 12) → { anilist, myanimelist, … }. Direct-id scrapers (AniBD, 2Dhive, AniKage, …) use that inside kitsu.fetchEpisodesByProvider / anilist.fetchEpisodesByProvider.

Airing schedule

Uses AniList AiringSchedule, not nextAiringEpisode (that field is only the next episode per show).

import { anilist } from "kaizoku-core";

const today = await anilist.getDailySchedule(new Date(), 180); // UTC+3
const week = await anilist.getWeeklySchedule(new Date(), 180);

The week is one range, bucketed locally.

Episode ids

Episode.id is the provider’s real id when they have one. Synthetic ids re-run fuzzy mapping on every play and can silently switch shows.

| Provider | Episode.id | |---|---| | KickAssAnime | one-piece-0948/ep-1-225ebd | | AniNeko | frieren-beyond-journeys-end/ep-1 | | AniBD | 21eop1web | | 2Dhive, AnimeDunya | <malId>/<episodeNumber> |

No title mapping in the status table means fetchAnimeInfo takes an AniList (or MAL) id and never fuzzy-matches titles. Prefer those when they cover the show.


Build & test

pnpm run build
pnpm run test

Most provider tests hit live sites. They are slow and fail when the origin is having a day. AnimeDunya skips without a scrape proxy:

SCRAPE_PROXY_URL=https://your-scrape-worker.example pnpm test

CI only runs offline suites:

npx vitest run src/tests/astro.test.ts src/tests/proxy.test.ts src/tests/mapping.test.ts

made by pato ❤️

License

MIT © Kaizoku