anigamer
v0.2.1
Published
Unofficial SDK for ani.gamer.com.tw (bahamut animation) — user data: watch history, cover URLs, cookie auto-rotation
Downloads
445
Maintainers
Readme
anigamer
Unofficial SDK for 巴哈姆特動畫瘋 (ani.gamer.com.tw) — user-data side: watch history, cover URLs, cookie auto-rotation.
Not affiliated with 巴哈姆特 / Gamer Digital Inc. Use at your own risk; respect their TOS.
Why this exists
There's an OSS gap. Existing projects (aniGamerPlus, AniGamerDownloader, anigamerdatabase, bahamut-anime) cover downloading or public catalog data. None give you a typed, dependency-free API for your own account data — what you've watched, when, and their covers.
If you want to build a "what I'm watching" widget, sync to Trakt, feed an LLM your watch history, etc — anigamer does the auth dance and gives you typed entries.
Install
pnpm add anigamerRequires Node 20+ (uses native fetch).
Quick start
import { AniGamer } from 'anigamer';
const client = new AniGamer({
cookie: process.env.BAHAMUT_COOKIE!, // raw cookie string or { name: value } object
});
const page = await client.history(1);
console.log(page.entries); // [{ animeSn, videoSn, title, episode, watchedAt }, ...]
const all = await client.historyAll(); // walks all pages, de-dups
const info = await client.animeInfo(page.entries[0].animeSn);
console.log(info.cover); // og:image URL on p2.bahamut.com.twGetting your cookie
- Log into
ani.gamer.com.tw - Open DevTools → Network → trigger any
api.gamer.com.twrequest - Right-click → Copy as cURL → grab the
-b '...'value - Paste as
BAHAMUT_COOKIEenv (or directly intonew AniGamer({ cookie }))
The 7 required cookies: BAHAID, BAHAHASHID, BAHANICK, BAHALV, BAHAFLT, BAHAENUR, BAHARUNE.
BAHARUNE is a JWT signed by Bahamut with ~14-day expiry.
Cookie auto-rotation
Bahamut periodically rotates BAHARUNE via Set-Cookie on API responses. The SDK captures and merges these into its in-memory jar automatically. To persist the rotated jar across process restarts:
import { readFileSync, writeFileSync, existsSync } from 'node:fs';
import { AniGamer } from 'anigamer';
const COOKIE_FILE = '.anigamer-cookies.json';
const jar = existsSync(COOKIE_FILE) ? JSON.parse(readFileSync(COOKIE_FILE, 'utf8')) : undefined;
const client = new AniGamer({
cookie: jar ?? process.env.BAHAMUT_COOKIE!,
onCookiesRotated: (rotated) => writeFileSync(COOKIE_FILE, JSON.stringify(rotated, null, 2)),
});As long as your process makes Bahamut requests regularly, the JWT keeps renewing — you may never need to re-grab your cookie manually.
JWT expiry check
Inspect the token without making a request:
const status = client.jwtStatus();
if (status?.isExpiringSoon) console.warn('JWT expires at', status.expiresAt);
if (status?.isExpired) console.error('JWT expired — re-grab cookie.');Authentication & session lifetime
This SDK does not — and intentionally will not — perform automated login.
Bahamut's login is gated by Google reCAPTCHA. The only ways to bypass it (paid captcha-solving services, headless browsers with human interaction, token forgery) are fragile, against Bahamut's ToS, and a good way to get a project taken down. So anigamer stays read-only on your own account data, using a cookie you provide. We never log in for you, never automate in-app actions (sign-in, lottery, quiz answering), and never store your password.
What that means for "set it and forget it":
BAHARUNE(the auth JWT) has a fixed expiry of ~14 days.- While your process keeps making requests, Bahamut may rotate the cookie via
Set-Cookie— the SDK captures and persists this automatically (see Cookie auto-rotation). This is best-effort renewal, not guaranteed. - Whether rotation actually extends the session indefinitely is not something we can promise — it depends on Bahamut's server behavior, which is undocumented and may change.
The robust pattern is "runs automatically, warns loudly before it dies":
const status = client.jwtStatus();
if (status?.isExpiringSoon) {
// < 24h left — fire your own alert (Discord / email / log) so you can refresh in time
notify(`Bahamut cookie expires ${status.expiresAt.toISOString()} — re-grab it.`);
}When the cookie does expire, refreshing it is a ~5-minute manual step (log in on the website, copy the cookie again). The SDK gives you the early warning; it can't do the re-login itself.
API
new AniGamer(options)
| option | type | default |
|---------------------|-----------------------------------------|----------------------|
| cookie | string \| Record<string, string> | required |
| timeoutMs | number | 8000 |
| userAgent | string | desktop Chrome UA |
| onCookiesRotated | (jar) => void | — |
| fetch | typeof fetch | globalThis.fetch |
Methods
client.history(page = 1): Promise<HistoryPage>— one page (~24 entries)client.historyAll(opts?): Promise<HistoryEntry[]>— all pages, de-duplicated byanimeSn:videoSnclient.animeInfo(animeSn): Promise<AnimeInfo>— scrape OG meta fromanimeRef.phpclient.cover(animeSn): Promise<string | null>— shortcut foranimeInfo(sn).then(i => i.cover)client.validate(): { ok, missing }— check required cookies presentclient.jwtStatus(): JwtExpiry | null— BAHARUNE expiry without a network call
Standalone helpers
import {
parseCookieString,
serializeCookies,
mergeSetCookies,
decodeJwtPayload,
checkJwtExpiry,
} from 'anigamer';Scope
In scope (v0.x): user-data endpoints behind cookie auth — history, anime info, eventually subscriptions/follows/comments.
Out of scope: video downloading (see aniGamerPlus), public anime catalog data (see bahamut-anime), DRM/m3u8 stream extraction.
License
MIT © timo9378
