A simple, plugin-based engine for scraping media streams and subtitles. Load provider plugins from GitHub, local files, or directly in code — with health tracking, auto-updates, caching, and more built right in. Works in Node.js, browsers, React and React Native.
Guides live in docs/ — the README keeps just the overview and a quick start.
| Guide | What it covers |
|---|---|
| Installation | install + React Native / Expo setup |
| Provider Sources | GitHub, local, and registry sources |
| Creating a Provider Plugin | config.ts / stream.ts / subtitle.ts / index.ts |
| Bundling Providers | bundling plugins into standalone modules |
| Testing Providers | the test-provider CLI |
| Configuration | manager + scrape config, metrics & health |
| Examples | end-to-end usage examples |
React Hook (useSources) |
the React/React Native hook |
| API Reference | full API surface |
New capabilities (see What's new): HTTP hardening · Challenge solver · Source flags · Lazy sources · full changelog
- 🔌 Plugin system — add or remove providers anytime
- 🌍 Runs anywhere — Node.js, browsers, React Native
- 🎯 Pick a provider — scrape from one specific provider by its scheme
- ⚡ Run in parallel — scrape from multiple providers at the same time
- 🏁 Stop early — quit as soon as enough providers have responded
- ⏱️ Timeouts — never wait forever for a slow provider
- 📊 Health tracking — see how each provider is doing (errors, successes)
- 🔴 Auto-disable — bad providers get turned off on their own
- 🔄 Auto-update — remote providers refresh themselves on a timer
- ♻️ Warm Puppeteer pool — reuse browser processes as tabs instead of spawning a browser for every request
- 💾 Built-in cache — save results in memory so you don't repeat work
- 🔁 Retries — automatically retry failed providers
- ✅ Validation — checks that plugins are set up correctly before loading
import { GrabitManager } from "grabit-engine";
// Create the manager with a registry source (simplest approach)
const manager = await GrabitManager.create({
source: {
type: "registry",
name: "my-providers",
providers: {
"my-provider": myProviderModule
}
},
tmdbApiKeys: ["your-tmdb-api-key"]
});
// Scrape streams for a movie — minimal: only tmdbId is required!
// TMDB service auto-fills title, year, duration, imdbId, etc.
const streams = await manager.getStreams({
media: {
type: "movie",
tmdbId: "27205"
},
targetLanguageISO: "en"
});
// Scrape from a specific provider by scheme
const targeted = await manager.getStreamsByScheme("my-provider", request);See Provider Sources for GitHub/local sources and Creating a Provider Plugin to build your own.
Recent additions — each is opt-in and documented in its own guide:
- HTTP hardening —
ctx.xhrgains a cookie jar, plus per-host concurrency,429rate-limit handling, and request coalescing (on by default fromconfig.xhr). Proxy is host config on the manager — a proxy agent or a URL resolver — see Configuration → Proxy. - Challenge solver —
ctx.solveChallenge(url, …)(puppeteer on Node; inject a hidden RN WebView or FlareSolverr viasetChallengeSolver). - Source flags —
xhr.flags: SourceFlag[](replaceshaveCorsPolicy) so a source states exactly how the host must play it. - Lazy sources — return
{ lazy: { id } }and aresolveLazyworker to defer final-URL resolution to play time.
Full details in IMPROVEMENTS.md.
Two ways to drive a real browser from a provider, with different reach:
ctx.puppeteerhands you the live Puppeteerpage(andbrowser) leased from the shared pool. Use it only when you truly need the page object: listening to network requests to capture the media URL, or injecting / interacting directly in the browser. It runs on Node only, so a provider that uses it must setenv: "node"in itsmanifest.jsonentry. Off Node (browser / React Native) the engine only runsenv: "universal"providers, so a node-only provider is correctly skipped there instead of failing at runtime.ctx.solveChallenge(url, requester, opts)returns just{ html, cookies, cookieMap, userAgent }. Use it when you only need the rendered HTML (for example to pass a Cloudflare interstitial and read the DOM). It works everywhere: on Node it drives Puppeteer, and a host can inject an RN hidden WebView or FlareSolverr solver viasetChallengeSolver, so these providers stayenv: "universal".
Rule of thumb: need the page or a network listener, use ctx.puppeteer with env: "node". Only
need the solved HTML, use ctx.solveChallenge with env: "universal".
When reusing a solved result, forward the returned userAgent (and cookies) on later requests:
Cloudflare binds cf_clearance to the exact User-Agent and IP that earned it.
ISC — see LICENSE. For educational / personal use.