Skip to content

API Reference

type ContentLanguage = 'sub' | 'dub' | 'raw';
ValueMeaning
'sub'Original audio with subtitles
'dub'Dubbed audio (usually English)
'raw'Original audio, no subtitles
type MediaCatalogType = 'ANIME' | 'MOVIE' | 'TV' | 'MANGA' | 'LIVE_TV';

Supported types include 'ANIME' and 'MANGA'.

Returned by provider.search().

interface IMediaSearchResult {
id: string; // opaque, pass to fetchContentUnits
title: string;
thumbnailUrl?: string;
catalogType: MediaCatalogType;
providerId: string; // matches provider.id
availableLanguages?: ContentLanguage[]; // omitted if unknown
}

Returned by provider.fetchContentUnits(). Represents a single episode or chapter in a unified, language-agnostic list: the caller picks a translation at resolveStream time.

interface IContentUnit {
id: string; // opaque, pass to resolveStream
title: string;
number: number; // episode/chapter number (may be fractional, e.g. 1.5)
availableLanguages?: ContentLanguage[]; // e.g. ['sub', 'dub']: omitted if unknown
availableSubtitles?: ISubtitleAvailability[]; // optional, list-time metadata
availableQualities?: IVideoPayload['quality'][]; // optional
}
interface ISubtitleAvailability {
language: string; // BCP-47 (e.g. 'en', 'pt-BR')
label: string; // human-readable ('English')
format?: 'vtt' | 'srt' | 'ass';
}
interface ISubtitleTrack extends ISubtitleAvailability {
url: string;
}

ISubtitleAvailability is the metadata-only shape used in list contexts; ISubtitleTrack adds the URL once resolved.

Returned by the optional provider.fetchUnitTracks(). Lets a UI introspect tracks without paying the cost of a full stream resolution.

interface IUnitTracks {
subtitles: ISubtitleTrack[];
qualities: IVideoPayload['quality'][];
headers?: Record<string, string>; // forwarded to the subtitle/stream fetcher when present
}

A single playable stream.

interface IVideoPayload {
sourceUrl: string;
isHLS: boolean; // true → HLS manifest (.m3u8)
quality: '1080p' | '720p' | '480p' | '360p' | 'auto';
language?: ContentLanguage;
headers?: Record<string, string>; // forward to fetch/player (Referer, User-Agent)
subtitles?: ISubtitleTrack[]; // external VTT tracks when the provider has them
}

Minimal pluggable cache contract: bring whatever store you want (Map, Redis, SQLite, edge KV). Both methods may be sync or async; the SDK awaits either way.

interface SdkCache {
get(key: string): unknown | Promise<unknown>;
set(key: string, value: unknown): void | Promise<void>;
}

Keys are namespaced strings produced by startServer: search:<providerId>:<query>, content:<providerId>:<mediaId>, stream:<providerId>:<unitId>:<lang>, tracks:<providerId>:<unitId>:<lang>. get returns undefined for a miss; any other value (including null) is treated as a hit. Inspect the key prefix in your set to apply different TTLs per endpoint or refuse to cache (e.g. stream: if your provider hands out signed expiring URLs).

interface IMangaPayload {
imageUrls: string[];
headers?: Record<string, string>;
}

Returned by provider.resolveStream(). A discriminated union: always check type before accessing the payload.

type ResolvedMediaStream =
| { type: 'video'; streams: IVideoPayload[] }
| { type: 'manga'; pages: IMangaPayload };

Anime providers return type: 'video'; manga providers return type: 'manga'. The streams array on video results is sorted best-first by the provider.


The shared HTTP transport. All providers and extractors accept one via constructor.

class HttpClient {
constructor(config?: HttpClientConfig);
}
interface HttpClientConfig {
timeoutMs?: number; // default: 10000 (10s)
proxyUrl?: string; // e.g. 'https://proxy.example.com'
proxyType?: 'prepend' | 'query'; // default: 'prepend'
proxyQueryParam?: string; // used when proxyType='query'; default: 'url'
defaultHeaders?: Record<string, string>;
}
get(url: string, options?: RequestInit): Promise<Response>
post(url: string, body?: any, options?: RequestInit): Promise<Response>
request(url: string, options?: RequestInit): Promise<Response>
// Convenience mutators (affect defaultHeaders)
setCookie(name: string, value: string): void
setUserAgent(userAgent: string): void
// Proxy internals (used by HlsUtils)
getProxyUrl(): string | undefined
getProxyType(): 'prepend' | 'query'
getProxyQueryParam(): string
getDefaultHeaders(): Record<string, string>
requestUrl(url: string): string // applies proxy rewriting to a URL

prepend: strips the protocol from the target and prepends the proxy base:

proxyUrl: 'https://proxy.example.com'
target: 'https://cdn.site.com/video.m3u8'
result: 'https://proxy.example.com/cdn.site.com/video.m3u8'

query: passes the target as a query param:

proxyUrl: 'https://proxy.example.com/fetch'
proxyQueryParam: 'url'
result: 'https://proxy.example.com/fetch?url=https%3A%2F%2Fcdn.site.com%2Fvideo.m3u8'

When running in Node and fetch throws (timeout, TLS error, anti-bot rejection), HttpClient automatically retries the request using curl via child_process.execSync. The fallback synthesises a standard Response-shaped object, so callers see no difference. Cookie persistence uses a per-client temp file.


abstract class BaseProvider {
abstract readonly id: string;
abstract readonly supportedTypes: MediaCatalogType[];
constructor(protected http: HttpClient);
abstract search(query: string): Promise<IMediaSearchResult[]>;
// Language-agnostic: returns one unified list. Each unit advertises its
// available translations via `availableLanguages`.
abstract fetchContentUnits(mediaId: string): Promise<IContentUnit[]>;
abstract resolveStream(
unitId: string,
language?: ContentLanguage
): Promise<ResolvedMediaStream>;
// Optional: list the subtitle/quality tracks for a single unit without
// resolving the playable stream. Implement when the provider exposes a
// cheap metadata endpoint.
fetchUnitTracks?(
unitId: string,
language?: ContentLanguage
): Promise<IUnitTracks>;
}

abstract class BaseExtractor {
abstract readonly id: string;
constructor(protected http: HttpClient);
abstract extract(embedUrl: string): Promise<IVideoPayload[]>;
}

Extractors return an empty array (never throw) when they cannot handle the given URL.


Extracts googlevideo.com MP4 URLs from Blogger video embeds.

class BloggerExtractor extends BaseExtractor {
readonly id = 'blogger';
static matches(url: string): boolean; // true for blogger.com/video.g?token=...
extract(embedUrl: string): Promise<IVideoPayload[]>;
}

Flow:

  1. Fetch the embed page; extract FdrFJe (session ID) and cfb2h (build hash).
  2. POST to /_/BloggerVideoPlayerUi/data/batchexecute with the WcwnYd RPC.
  3. Parse the response: find the stream array inside the wrb.fr / WcwnYd envelope; prefer itag=22 (720p) over itag=18 (360p).
  4. Falls back to a raw regex scan for googlevideo.com URLs if structured parsing yields nothing.

Returned URLs require { Referer: 'https://www.blogger.com/' } headers.

Extracts direct MP4 URLs from mp4upload.com embed pages.

class Mp4UploadExtractor extends BaseExtractor {
readonly id = 'mp4upload';
static matches(url: string): boolean; // true for mp4upload.com URLs
extract(embedUrl: string): Promise<IVideoPayload[]>;
}

Parses the player.src({ src: "https://...video.mp4" }) line from the embed HTML. Returned URLs require { Referer: 'https://mp4upload.com/' }.

Best-effort extractor that scans any embed page for a .m3u8 or .mp4 URL.

class GenericHlsExtractor extends BaseExtractor {
readonly id = 'generic-hls';
extract(embedUrl: string): Promise<IVideoPayload[]>;
}

Prefers .m3u8 over .mp4. Handles JSON-embedded URLs with escaped slashes. Cannot decrypt obfuscated players (filemoon, streamwish, ok.ru). Returns [] if nothing matches.

Handles vidstreaming.io / gogoplay embeds with AES-CBC decryption.

class VidstreamingExtractor extends BaseExtractor {
readonly id = 'vidstreaming';
extract(embedUrl: string): Promise<IVideoPayload[]>;
}

Flow:

  1. Fetch the embed page. Extract encryption key, IV, and decryption key from container-N / videocontent-N class names.
  2. Decrypt the data-value attribute with AES-CBC using the encryption key.
  3. Encrypt the id query param with AES-CBC.
  4. GET {host}/encrypt-ajax.php?{decrypted_data}&id={encrypted_id}&alias={contentId} with X-Requested-With: XMLHttpRequest.
  5. Decrypt the data field of the JSON response with AES-CBC using the decryption key.
  6. Parse the resulting JSON: source and source_bk arrays each contain { file, label } entries.

class HlsUtils {
static rewriteManifest(manifestText: string, playlistUrl: string, httpClient: HttpClient): string;
}

Rewrites every URI in an .m3u8 manifest to route through the HttpClient’s configured proxy:

  • Plain URI lines (chunks, sub-playlists)
  • URI="..." attributes inside #EXT-X-KEY, #EXT-X-MAP, and similar tags
  • Relative URIs are first resolved to absolute using playlistUrl

Returns the manifest unchanged if no proxy is configured on the client.


class DomRegistry {
static register(customParser: IDomParser): void;
static getParser(): IDomParser;
static parse(html: string): IDomElement;
}

Global singleton parser used by all providers that scrape HTML. The default is BrowserDomParser, which wraps globalThis.DOMParser.

linkedom is registered automatically on import in Node environments: no setup is needed. DomRegistry.register() is only needed when you want to swap in a fully custom parser.

Custom parser:

import { DomRegistry, IDomParser, IDomElement } from 'anime-sdk';
class MyParser implements IDomParser {
parse(html: string): IDomElement {
/* ... */
}
}
DomRegistry.register(new MyParser());
interface IDomParser {
parse(html: string): IDomElement;
}
interface IDomElement {
querySelector(selector: string): IDomElement | null;
querySelectorAll(selector: string): IDomElement[];
getAttribute(name: string): string | null;
readonly textContent: string | null;
readonly outerHTML: string;
readonly innerHTML: string;
}

function startServer(options: ServerOptions): http.Server;
interface ServerOptions {
providers: BaseProvider[];
port?: number; // default: 3000
auth?: { token: string };
proxy?: boolean; // enable /proxy and rewrite stream + subtitle URLs through it
cache?: SdkCache; // memoize /search, /content, /stream, /tracks
}

Launches a Node http.Server. Returns the server instance so you can call server.close(). All requests must be GET; all responses are JSON. See HTTP Server for full route documentation and Stream Proxy for the proxy behaviour.

Routes: /search, /content, /stream, /tracks, /proxy (when proxy: true), and download routes /download/video, /download/video/progress, /download/video/file, /download/manga/page, /download/manga/chapter, /download/manga/chapter/progress, /download/manga/chapter/file.


Functions for saving resolved streams to disk. Requires Node 20+; ffmpeg on PATH for HLS video downloads only.

function downloadVideo(
streams: IVideoPayload | IVideoPayload[],
outputPath: string,
options?: DownloadVideoOptions,
): Promise<DownloadVideoResult>;

Downloads an anime episode to a .mp4 file. Accepts a single stream or an array: if an array, each candidate is tried in order until one succeeds. Throws with a combined error message if all fail.

  • HLS streams: downloads segments, strips PNG-wrapped bytes, concatenates into a .ts file, then calls ffmpeg -c copy to mux to MP4.
  • Direct MP4: streams to disk via fetch.
interface DownloadVideoOptions {
onProgress?: (info: { phase: string; detail?: string }) => void;
timeoutMs?: number; // default 300_000 (5 min)
}
interface DownloadVideoResult {
outputPath: string;
stream: IVideoPayload; // the candidate that succeeded
fileSize: number; // bytes
}

Progress phase values: 'resolving''downloading''muxing''complete'.

function downloadMangaPage(
pages: IMangaPayload,
pageIndex: number,
outputDir: string,
options?: DownloadMangaPageOptions,
): Promise<DownloadMangaPageResult>;

Downloads a single manga page. The filename is auto-generated as page_<NNN><ext> where the extension is inferred from the Content-Type header.

interface DownloadMangaPageOptions {
headers?: Record<string, string>; // override the headers on IMangaPayload
timeoutMs?: number; // default 30_000
}
interface DownloadMangaPageResult {
outputPath: string;
pageIndex: number;
fileSize: number;
contentType: string;
}
function downloadMangaChapter(
pages: IMangaPayload,
outputPath: string,
options?: DownloadMangaChapterOptions,
): Promise<DownloadMangaChapterResult>;

Downloads all pages and packages them as an uncompressed .zip archive (STORE method: images are already compressed). No external dependencies.

interface DownloadMangaChapterOptions {
onProgress?: (info: { downloaded: number; total: number }) => void;
timeoutMs?: number; // per page; default 30_000
}
interface DownloadMangaChapterResult {
outputPath: string;
pageCount: number;
fileSize: number; // total ZIP size in bytes
}

Low-level utilities exported for custom pipelines:

// Parse variant playlist URLs from a master playlist
parseHlsMaster(content: string, baseUrl: string): string[]
// Parse segment descriptors from a media playlist
parseHlsSegments(content: string, baseUrl: string): Array<{ url: string; duration: number }>
// Infer file extension from Content-Type
detectImageExtension(contentType: string): string
// CRC-32 checksum (used by the ZIP writer)
crc32(buf: Buffer): number
// Build an uncompressed ZIP buffer
createZipBuffer(entries: Array<{ filename: string; data: Buffer }>): Buffer

Exported helpers used internally by AnimeParadiseProvider and startServer; available for custom providers and self-hosted setups.

// Normalise an arbitrary array of {src|url, label, type|format} entries into
// ISubtitleTrack[]. Non-http(s) entries (Drive IDs, etc.) are dropped.
normalizeSubtitleEntries(entries: unknown): ISubtitleTrack[]
// Wrap a subtitle URL through the SDK's /proxy endpoint. Forces
// Content-Type: text/vtt on VTT files so browsers parse them correctly.
proxifySubtitleUrl(
proxyBase: string,
track: ISubtitleTrack,
options?: { headers?: Record<string, string>; contentType?: string }
): string
// BCP-47 inference from a human label (best-effort).
labelToBcp47(label: string): string

Exported for use in custom providers.

// AES-CBC decrypt: returns plaintext string
aesDecrypt(ciphertextBase64: string, keyStr: string, ivStr: string): Promise<string>
// AES-CBC encrypt: returns base64 string
aesEncrypt(plaintext: string, keyStr: string, ivStr: string): Promise<string>
// SHA-256 hash
sha256(text: string): Promise<Uint8Array>
// AES-CTR decrypt (used by AllmangaProvider)
aesDecryptCtr(ciphertext: Uint8Array, key: Uint8Array, iv: Uint8Array): Promise<Uint8Array>

All functions use globalThis.crypto.subtle (available in Node 20+ and all modern browsers).