f977zone
v1.0.0
Published
Zero-dependency scraping modules for game-forum cataloging — thread import and the forum's SAM catalog API. Framework-agnostic; works in Node.js >= 18.17, Bun, or any JS runtime with global fetch.
Maintainers
Readme
f977zone
Zero-dependency scraping modules for game-forum cataloging — thread import and the forum's SAM catalog API. Minimal by design: pure fetch, JSON, and hardened HTML parsing — nothing else.
These modules handle the hard parts for you:
- forum — XenForo thread import: title cleanup, overview, developer/version/OS fields, engine & platform detection, genre spoilers, download links (allowlisted hosters), hotlinked screenshots
- sam — the forum's own "Latest Games & Updates" catalog API: the full game list (30 items/page) with cover + screens, no login required
Every function returns normalized, sanitized data — safe to render in your own UI.
Features
- 🔌 Zero dependencies — pure Node.js
fetch(the thread parser is regex-based; no cheerio/jsdom). Runs on Node ≥ 18.17, Bun, Deno, and serverless runtimes - 🧩 Modular — import one module or all of them
- 🔐 Security built-in — download links allowlisted by hoster, URL scheme filtering, HTML stripping, SSRF-safe URL helper
- 🛡️ Hardened parsing — survives markup changes, dedupes, discards ads/tracking, strips category prefixes
- 🌐 Configurable — user agent + session cookie via env vars
- 📦 Tree-shakeable ESM — import only what you need
Supported sources
| Codename | Type | Auth | Export path |
| --- | --- | --- | --- |
| forum | Game forum (XenForo thread import) | session cookie (optional) | f977zone/f95zone |
| sam | Forum catalog API (latest_data.php?cmd=list) | none | f977zone/sam |
Actual base URLs live in src/*.js (as exported constants F95_BASE / SAM_BASE) — see .env.example for the cookie setup.
Requirements
- Node.js ≥ 18.17 (global
fetchrequired) — or any runtime that providesfetch - forum source (optional): a session cookie (
F95_COOKIE) for login-gated content (genre spoilers, download buttons)
Installation
npm install github:bagasss859-sketch/f977zoneNo build step, no transitive dependencies.
Quick start
import { f95ImportFromUrl } from 'f977zone';
import { fetchSamPage, fetchSamMeta } from 'f977zone/sam';
// 1. Import a single thread (needs F95_COOKIE for full data)
const game = await f95ImportFromUrl('https://f95zone.to/threads/game-name.123456/');
console.log(game.title, '|', game.developer, '|', game.downloads.length, 'links');
// 2. Browse the whole catalog — no login
const meta = await fetchSamMeta(); // tag/prefix names
const { items, count } = await fetchSamPage('games', 1);
console.log(count, 'games — first:', items[0]?.title);Configuration
Credentials — where to get the secret
F95_COOKIE (optional but recommended)
Some forum content (genre spoilers, download buttons) is hidden from guests:
- Log in to the forum with a regular account.
- Press
F12→ Network → open any thread page. - Under Request Headers, copy the entire
Cookievalue (starts withxf_csrf=...; xf_session=...). - Paste it into
.envasF95_COOKIE="xf_csrf=...; xf_session=...".
Cookies expire — refresh when imports report missing data. The SAM catalog API (
f977zone/sam) works without any login.
Environment variables
| Variable | Default | Description |
| --- | --- | --- |
| F95_COOKIE | — | forum session cookie (xf_csrf=...; xf_session=...) |
| F95_USER_AGENT | Chrome UA | forum user agent |
How the scraping works
forum (src/f95zone.js) — XenForo thread import
- Fetches the thread page with browser-mirroring headers (
User-Agent,Accept-Language,Referer) plus the optional session cookie. - Isolates the first post (
bbWrapperblock) — everything else (sidebar, replies, ads) is discarded. - Title cleanup: strips the
| F95zonesuffix and layered category prefixes (VN - Ren'Py -,Android - ... -), then decodes HTML entities. - Field extraction from the post text:
Overview:description, then labeled lines (Developer / Version / OS / Language / Censored) with a label-anchored regex. - Engine & platform detection by keyword (Ren'Py, RPG Maker, Unity, ... → Windows/Android/Mac/Linux/iOS; defaults to Windows).
- Genres: spoiler
bbCodeBlock-content(needs login) → inlineGenre: x, y→ bracketed title parts as fallback. - Download links: anchors to allowlisted hosters (Mega, Google Drive, MediaFire, PixelDrain, WorkUpload, GoFile, 1Fichier, UploadHaven, KatFile, RapidGator, NitroFlare, ...) plus forum attachment/thread-download links. Labels come from anchor text (e.g.
MEGA) or the hoster name. - Screenshots: hotlinked — the full-size URL is derived from the
/thumb/URL; banners/logos/gifs are filtered out (max 10). Cover fromog:image, falling back to the first screenshot. login_requiredflag set when the page says links are hidden from guests.
sam (src/sam.js) — catalog API
The forum's own "Latest Games & Updates" app exposes latest_data.php?cmd=list — a JSON API returning the entire game catalog (26k+ items across categories) sorted by date, 30 per page, with cover + screenshots. No login. Metadata (tag/prefix names) is injected into the app's HTML as var latestUpdates = {...}; — parsed once and used to decorate items (tagName(), prefixName(), prefixIsEngine()).
API reference
forum — f977zone/f95zone
| Function | Description |
| --- | --- |
| f95ImportFromUrl(url) | Full thread import → game data (or { error }) |
| f95ParseThread(html, threadUrl) | Parse thread HTML → normalized game data |
| f95Fetch(url, timeoutMs) | Browser-mirroring fetch (cookie + UA) → { html?, error?, finalUrl? }, never throws |
| f95GetFirstPost(html) | Extract the first post body (bbWrapper) |
| f95HtmlToText(html) | HTML → clean text |
| f95FieldValue(text, label) | Value after a label, e.g. Developer: |
| f95GetAttachments(postHtml) | All attachment image URLs |
| f95ThumbToFull(url) | Thumb URL → full-size URL |
| f95DetectEngine(title, text) | Engine detection (Ren'Py, RPG Maker, Unity, ...) |
| f95DetectPlatforms(osText, titleText) | Platform detection (Windows/Android/Mac/Linux/iOS) |
| f95ExtractScreenshots(postHtml) | Full-size screenshot hotlinks (max 10) |
| f95ExtractDownloads(postHtml) | Download links (allowlisted hosters) |
| isF95ThreadUrl(url) | Thread URL validation |
sam — f977zone/sam
| Function | Description |
| --- | --- |
| fetchSamMeta() | Catalog metadata (tags/prefixes) from the app page |
| fetchSamPage(cat, page) | One catalog page (30 items) — no login |
| totalPages(count) | Page count for a category |
| tagName(meta, id) / prefixName(meta, id) | Decorate items with tag/prefix names |
| prefixIsEngine(meta, id) | Is the prefix an engine? |
| slugFromTitle(title) / threadUrlFromId(id, title) | Placeholder thread URL helpers |
Security
- Download links are allowlisted by hoster; forum-internal download/attachment URLs are the only non-hoster exception.
safeHttpUrl()forceshttp(s)://and rejectsjavascript:,data:,vbscript:— applied to covers/attachments before they leave the module.stripHtml()removes tags/scripts/styles from external text (titles, overviews, labels).- Screenshots are hotlinked, never downloaded by this library.
import { safeHttpUrl, stripHtml, isSafeExternalUrl } from 'f977zone';Error handling
f95Fetch never throws — it returns { error }. f95ImportFromUrl returns { error } with a descriptive message (invalid URL, fetch failure, or login-required). Other helpers throw plain Errors on invalid input. Network timeouts abort via AbortSignal.timeout (default 25 s).
Examples
Plain Node script — catalog → detail
import { fetchSamPage, fetchSamMeta, f95Fetch, f95ParseThread, threadUrlFromId } from 'f977zone';
const meta = await fetchSamMeta();
const { items } = await fetchSamPage('games', 1);
const first = items[0];
// Fetch the thread and parse it
const url = threadUrlFromId(first.thread_id, first.title);
const res = await f95Fetch(url);
if (res.html) {
const data = f95ParseThread(res.html, url);
console.log(data.title, '|', data.developer, '|', data.downloads.length, 'links');
}Testing
npm testtest/import.test.mjs— all exports/subpaths resolvetest/f95.test.mjs— offline thread-parser tests (HTML fixture, no network)
Disclaimer
This library is for educational and personal-use purposes only. The scraped sources are third-party websites; this project is not affiliated with, endorsed by, or connected to any of them. You are responsible for:
- Complying with the terms of service of the sites you scrape
- Complying with the laws of your jurisdiction regarding adult content
- Respecting rate limits — cache responses on your side; the built-in helpers keep request volume low
License
MIT © Bagas
