Skip to content

fix(madara): list Tangerine Archive premium chapters with verified URLs - #10

Closed
RibatTRW wants to merge 1 commit into
fm/lnreader-plugins-2555-tangerinefrom
fm/tangerine-replica-name-meta
Closed

RibatTRW wants to merge 1 commit into
fm/lnreader-plugins-2555-tangerinefrom
fm/tangerine-replica-name-meta

Conversation

@RibatTRW

Copy link
Copy Markdown
Owner

Stacked candidate fix for lnreader#2564 (Tangerine Archive premium/coin-locked chapters). Base is deliberately fm/lnreader-plugins-2555-tangerine, not a default branch.

Wave-2 replica of the tangerine-premium-name-meta lane. This is an independent implementation with the hardening the replica brief asked for; it is not a copy of PR 1. Every probe was anonymous and read-only: no login, cookies, nonces, purchase calls, or access-control defeat.

What changed

plugins/multisrc/madara/template.ts — new flag-gated listLockedChapters option, off by default:

  • parseNovel: keeps premium-block rows and appends the site's own unlock state to the name, e.g. Chapter 161 [Premium - 10 coins - Unlocked in 1 day], Chapter 411 [Premium - 16 coins - TBA]; rebuilds the real (still server-gated) URL from the chapter name; sets releaseTime: null for those rows (countdown/TBA is not a publication date).
  • URL rebuild rule measured against the site's own free-row hrefs (3288/3289 exact): leading number, dots to dashes, an emoji token right after the number is kept (Chapter 141 🔞 → chapter-141-%F0%9F%94%9E), a - subtitle suffix is dropped (Chapter 166 - Secret → chapter-166).
  • Rebuilt paths carry the listing row's own post id (?chapter-id=<id>). The site ignores that query (verified on free and locked pages), but parseChapter uses it to verify that the URL really resolves to that row.
  • parseChapter: throws a clear locked error with the price read from the fetched page's gate block (this also covers rows still listed as free whose page is already gated, e.g. holy-night Chapter 359 → 12 coins); throws a clear "could not find this chapter's page" when a rebuilt URL falls back to the series page (soft 404); throws "resolves to a different chapter" when the expected id mismatches — the honest outcome for the duplicate Chapter 166 pair (ids 16701/16700) in villain-let-me-touch-you.
  • The lock check is gate block present OR (This chapter is locked text present AND no .text-left/.text-right body), so a lock notice can never mask real prose.

plugins/multisrc/madara/sources.json — tangerinearchive opts in (listLockedChapters: true, hasLocked: true, versionIncrements: 1 → plugin 2.2.1). Other Madara sources take the exact old code path.

plugins/multisrc/madara/README.md — documents the option.

What was probed (live, anonymous read-only)

  • npm run build:multisrc, npm run build:compile, npx eslint and npx prettier --check on the changed files: pass.
  • npm run check:plugin -- plugins/english/TangerineArchive[madara].ts: PASS (popularNovels 12 novels, searchNovels, parseNovel 173 chapters, parseChapter 14497 chars).
  • Custom live probe (37/37 checks, exit 0): chapter counts equal the live ajax/chapters/ list — garden-of-may 212 (52 premium + 160 free), villain-let-me-touch-you 191 (46), holy-night 464 (105, including a 16-coin row), it-seems-like, me-the-weakest, deal-breaker (0 premium), i-thought; premium paths and ?chapter-id values equal each row's own data-chapter-<id>; free prose parses (garden ch160 = 6538 chars); locked gom 161/212 throw the 10-coin error; villain emoji chapter-176-%F0%9F%94%9E throws 10-coin; holy-night 359 (list-marked free, gated page) throws 12-coin; duplicate 166 owner row throws the lock error, non-owner row throws the mismatch error; a wrong derived slug throws the not-found error; hideLocked on/off = 160/212 chapters; Chapter 27.2 and Chapter 0 free rows are untouched.
  • Shared-template guard with the flag off: SleepyTranslations[madara] PASS (16 novels / 44 chapters / 9872 chars) and LightNovelHeaven[madara] PASS (12 novels / 798 chapters / 15439 chars).
  • Independent re-verification of the swarm's retrieval sweep: core sitemap 997 URLs and Yoast chapter sitemaps 2289 URLs contain 0 premium paths; /wp-json/ has 222 routes with no chapter content route. New dead ends are logged on the swarm board (CPT archive/feed 404/301, /wp/v2/types and taxonomies, posts-by-id 404, Yoast routes 401, static cache dirs empty/403).

What was NOT tested

  • No entitled session. LNReader plugins cannot log in and no owner-provided test account exists, so the locked prose itself was never read. The prebuilt entitled probe is validated in anonymous/refusal mode (lock mode confirms the gate; it refuses without credentials and without explicit authorization) and is ready to run the moment credentials arrive. No session/nonce was forged or replayed and no coins were spent.
  • No reader-app rendering of the thrown error; only the plugin-level behaviour was verified.
  • The pre-existing releaseTime: 'LL' bug for free rows is untouched and out of scope.

Co-authored by an AI agent (firstmate crewmate) under the owner's authorization on file; review accordingly.

Tangerine Archive renders coin-locked chapters with href="#" and
premium-block/coin-N classes, so the shared template dropped them and the
chapter list understated each novel's real length. Add a flag-gated
listLockedChapters source option (Tangerine Archive only) that:

- keeps premium rows in the list with the site's own unlock state, e.g.
  "Chapter 161 [Premium - 10 coins - Unlocked in 1 day]" or
  "[Premium - 12 coins - TBA]";
- rebuilds the real (still server-gated) URL from the chapter name using a
  rule measured against the site's own free-row hrefs (3288/3289 exact:
  "Chapter 27.2" -> chapter-27-2, "Chapter 141 (emoji)" -> the emoji slug,
  " - subtitle" suffixes dropped);
- carries each row's own post id in the rebuilt path (?chapter-id=<id>),
  which the site ignores but parseChapter uses to verify that the URL
  resolves to that row - so duplicate chapter numbers (villain-let-me-touch-you
  Chapter 166 ids 16701/16700) and any future slug drift fail with an honest
  "resolves to a different chapter" error instead of showing another
  chapter's notice;
- makes parseChapter throw a clear locked-chapter error (price read from the
  fetched page's gate block, covering rows still listed as free whose page is
  already gated, e.g. holy-night Chapter 359) and a clear "page not found"
  error when a rebuilt URL falls back to the series page (soft 404).

Verified live, anonymous read-only: 37/37 probe checks; counts match the ajax
list for garden-of-may 212 (52 premium), villain 191 (46), holy-night 464
(105), it-seems-like, me-the-weakest, deal-breaker and i-thought; free
chapters parse normally; locked rows throw; shared-template guard
SleepyTranslations passes with the flag off; build:compile, build:multisrc,
eslint and prettier pass.

Co-Authored-By: firstmate crewmate (AI agent) <crewmate@local>
@RibatTRW RibatTRW closed this Sep 23, 2026
@RibatTRW
RibatTRW deleted the fm/tangerine-replica-name-meta branch September 24, 2026 01:06
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant