Skip to content

fix(english): handle Cloudflare-gated Fenrir Realm chapters - #2703

Open
RibatTRW wants to merge 4 commits into
lnreader:masterfrom
RibatTRW:fm/lnreader-fenrir-empty-chapter-2672
Open

RibatTRW wants to merge 4 commits into
lnreader:masterfrom
RibatTRW:fm/lnreader-fenrir-empty-chapter-2672

Conversation

@RibatTRW

@RibatTRW RibatTRW commented Oct 18, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Fenrir Realm now puts a Cloudflare Turnstile "Confirm you're human to continue reading" check in front of every free chapter. Without a verified session, the chapter API (/api/new/v2/chapters/{id}) still answers HTTP 200 but with content: "" and content_format: "locked", so the plugin silently returned an empty chapter (#2672). I sampled the first and last free chapter of 8 popular novels and all were locked, so this affects every novel, not only the one in the issue. Paid chapters are unaffected (they return content_format: "text" with a teaser).

The site's reader unlocks a chapter by solving Turnstile and calling POST /api/read-token, which sets a short-lived (~10 min), chapter-scoped fr_read cookie. With a live session, the same endpoint mints a token for the next chapter without a new challenge; without one it returns 403.

A second problem hid this: chapter slugs from the API now include the volume (vol1/1), so the plugin's chapter paths became novel/vol1/vol1/1 and the WebView URL was a 404. Users sent to WebView could not reach the page that shows the check.

The plugin does not try to solve or bypass Turnstile. The user passes the check once in WebView, and the plugin then uses that session like the site's own reader does.

What Changed

  • parseChapter: when the API returns content_format: "locked", call POST /api/read-token for the chapter with the existing (WebView-verified) session and refetch the chapter with the minted token. If it is still locked, throw "Fenrir Realm requires a human verification check to read this chapter. Open it in WebView, complete the check, then try again." instead of returning an empty chapter. The error carries the real read-token HTTP status (403 without a session).
  • Chapter API responses with 401/403/429/503 throw an error carrying that status; other non-OK responses fall back to the existing HTML scrape.
  • popularNovels: a non-OK list response now throws the existing "open it in WebView" error with the real HTTP status attached.
  • resolveUrl drops a repeated volume segment (novel/vol1/vol1/1 → novel/vol1/1), so "Open in WebView" lands on the real chapter page. Stored chapter paths are unchanged, so existing libraries keep their read progress. The HTML fallback uses resolveUrl too.
  • The SvelteKit __data.json fallback also builds its URL from resolveUrl, so both fallbacks request the same corrected chapter page.
  • Version 1.1.3 → 1.1.4.

Closes #2672

This PR was written with an AI agent (Claude Code); please weight your review accordingly.

Testing

  • Real site, verified session: I passed the check in a real browser, then ran the bundled plugin with a cookie jar that behaves like the app's shared WebView/OkHttp jar. The issue's chapter (Vol 1 Chapter 1), Vol 1 Chapter 2 and Chapter 264 all returned full text: locked → POST /api/read-token 200 → content.
  • Real site, no session or expired session: POST /api/read-token 403 → the verify-in-WebView error.
  • Live check: npm run check:plugin (local) passed popularNovels, searchNovels and parseNovel. parseChapter was INCONCLUSIVE with HTTP 403, as expected without a verified session. Because the thrown errors carry the site's real HTTP status, scripts/live-check-plugin.js classifies the 403 as an anti-bot block through its existing status check. The checker and workflow are not modified. On the GitHub runner the site also answers the series list with 403, which the check now reports as INCONCLUSIVE instead of FAIL.
  • Not tested in the LNReader app. This relies on the app's plugin fetch sharing WebView cookies, which is the standard Android behaviour that other Cloudflare-gated sources depend on.

Plugin playground

The issue's chapter with no verified session now shows a clear error instead of an empty chapter:

Plugin playground: Parse Chapter without a verified session shows the verification error

The same chapter after passing the check in a browser. The playground proxy does not store Set-Cookie, so I gave it the session cookies as the app's cookie jar holds them after the plugin mints this chapter's read token:

Plugin playground: Parse Chapter with a verified session returns the chapter text

Novel details and the chapter list, with Vol 1 Chapter 1 from the issue:

Plugin playground: Parse Novel for the issue's novel

Plugin playground: chapter list including Vol 1 Chapter 1

Popular listing:

Plugin playground: popular novels

The corrected WebView URL (…/vol1/1) opens the real chapter page on the site; before this change it was …/vol1/vol1/1, a 404:

Fenrir Realm chapter page opened from the plugin's corrected WebView URL

Pipeline

Updates from git push no-mistakes

✅ **intent** - passed

✅ No issues found.

✅ **Rebase** - passed

✅ No issues found.

✅ **Review** - passed

✅ No issues found.

✅ **Test** - passed

✅ No issues found.

  • Live validation: ✅ go - 2 of 3 scenarios driven live against the product
Scenario Result Live Evidence
List, search and novel parsing work against the live site ✅ pass live live-check output: popularNovels 20 novels, searchNovels 1 result, parseNovel 896 chapters
Chapter fetch behind Cloudflare/verification reports a real 403 as INCONCLUSIVE rather than FAIL ✅ pass live live-check output: parseChapter INCONCLUSIVE, HTTP 403
__data.json fallback URL no longer repeats the volume segment ⏸️ untested no The site returns 403 on chapter requests from here, so the fallback could not be exercised end to end.
  • npm ci
  • node scripts/live-check-plugin.js plugins/english/fenrirrealm.ts
  • Reproduced the path-correction regex used by resolveUrl for the __data.json URL
✅ **Document** - passed

✅ No issues found.

✅ **Lint** - passed

✅ No issues found.

✅ **Push** - passed

✅ No issues found.

Fenrir Realm now gates free chapters behind a Cloudflare Turnstile check and
the chapter API returns empty content with content_format "locked" until the
reader has a verified session, so the plugin silently returned empty chapters.

- When a chapter is locked, mint a read token with the existing (WebView
  verified) session and refetch it, as the site's own reader does.
- Otherwise throw a clear error asking to complete the check in WebView
  instead of returning an empty chapter, carrying the read-token HTTP status
  (403 without a session); also throw on non-2xx chapter responses.
- Volume chapter slugs now include the volume, so resolveUrl collapses the
  repeated volume segment; the WebView URL no longer 404s.
@greptile-apps

greptile-apps Bot commented Oct 18, 2026 •

Copy link
Copy Markdown

RetriggerView in GreptileConfidence Score: 4/5

[Medium impact] Adds verification handling for gated chapters in a content plugin.

The PR appears safe to merge, with a non-blocking gap in the chapter URL correction.

Findings

  1. P2 Chapter fallback keeps wrong URL ▶

Summary

Updates the Fenrir Realm plugin to version 1.1.4.

  • Fenrir Realm can retry verification-gated chapters with a read token.
  • Fenrir Realm chapter pages use a cleaned volume path.

RibatTRW acknowledges that playground screenshots are missing and must still be supplied.

Reviews (1) · Last reviewed commit: "no-mistakes(review): fix(english/fenrirr..." · Reviewed by Greptile


// Fallback or old method
const url = `${this.site}/series/${chapterPath.split('~~')[0]}`;
const url = this.resolveUrl(chapterPath);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Chapter fallback keeps wrong URL

The URL correction only reaches the HTML request. For novel/vol1/vol1/1~~123, that request now uses the corrected path. But if the page has no matching paragraphs, jsonUrl still requests the duplicated path and the chapter can return empty. Build jsonUrl from the corrected url so both fallbacks use the same chapter page.

Knowledge Base Used: English source plugins

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in e0f3c39: the SvelteKit __data.json fallback now builds its URL from this.resolveUrl(chapterPath), so it gets the same duplicated-volume-segment correction as the HTML fallback and both request the same chapter page (e.g. …/vol1/1/__data.json instead of …/vol1/vol1/1/__data.json).

…pter`. On the runner, the series list request came back as a non-2xx block page, and the code replaced it with a generic error that had no status. The live check couldn't classify that, so it reported FAIL. I changed `popularNovels` in `plugins/english/fenrirrealm.ts` so that a non-OK response now throws the same user-facing "open it in WebView" message with `error.status` set to the real HTTP status. A JSON parse failure on a 2xx response still throws the same message with no status, so a genuine parsing problem still reports as a failure. I didn't edit `scripts/live-check-plugin.js` or the workflow. The `parseChapter` paths already carried real statuses. The read-token 403 and the chapter API's 401/403/429/503 responses both set `error.status`. In the earlier run `parseChapter` never ran, because the check stopped at `popularNovels`. Prettier and ESLint pass. A local `node scripts/live-check-plugin.js plugins/english/fenrirrealm.ts` gave PASS for `popularNovels`, `searchNovels` and `parseNovel`, and INCONCLUSIVE for `parseChapter` with HTTP 403. Locally the list request wasn't blocked, so I couldn't reproduce the runner's `popularNovels` failure. I haven't confirmed that the runner's response was a 403 or 503. If it was a different non-2xx status, the check will still report FAIL. I made no version change here; the file is at 1.1.4
…llback

The __data.json fallback still built its URL from the raw chapter path, so
volume chapters requested novel/vol1/vol1/1. Build it from resolveUrl so it
hits the same chapter page as the HTML fallback.
@RibatTRW RibatTRW changed the title fix(english): handle verification-gated Fenrir Realm chapters fix(english): handle Cloudflare-gated Fenrir Realm chapters Oct 18, 2026

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

1 participant