Skip to content

fix(madara): list coin-locked chapters for Tangerine Archive (verified URLs) - #8

Closed
RibatTRW wants to merge 2 commits into
fm/lnreader-plugins-2555-tangerinefrom
fm/tangerine-premium-nav-api
Closed

RibatTRW wants to merge 2 commits into
fm/lnreader-plugins-2555-tangerinefrom
fm/tangerine-premium-nav-api

Conversation

@RibatTRW

@RibatTRW RibatTRW commented Sep 23, 2026 •

Copy link
Copy Markdown
Owner

Stacked candidate fix for the premium (coin-locked) chapter listing gap in lnreader#2564 — base branch is deliberately fm/lnreader-plugins-2555-tangerine, not the default branch.

What changed

plugins/multisrc/madara/template.ts (additive, flag-gated), plugins/multisrc/madara/sources.json (tangerinearchive only, plugin version 2.2.0 → 2.2.1) and a README note for the new option:

  1. New listLockedChapters source option, off by default and set only for tangerinearchive, so every other Madara source takes the unchanged code path.
  2. parseNovel lists coin-locked rows. They carry href="#", which the template's chapterUrl != '#' guard dropped, so Garden of May showed 160 of its 212 chapters. They are now listed with the URL the site actually serves them at, <novelPath>chapter-<n>/ (HTTP 200 with the lock notice in place of the body).
    • The plain slug is accepted without any extra request only when the row title is exactly Chapter <n> and that number is unique in the novel — the site's own free rows confirm the convention (3065/3083 exact across all 16 series; every exception is a suffixed, decimal or duplicated title).
    • Any other title (subtitles, emoji, decimals, repeated numbers) does not slugify the way it reads — Chapter 176 🔞 is /chapter-176-%f0%9f%94%9e/ while Chapter 244 - 🔞 is /chapter-244/ — so its candidate slugs are fetched and accepted only when the page's own post id (#wp-manga-current-chap[data-id]) matches the row's data-chapter-<id> class. If no candidate matches, the row is skipped rather than listed with a URL that serves a different chapter or the series page. Across all 16 series: 784 of 785 locked rows are listed; 1 (villain-let-me-touch-you id 16701, a duplicate Chapter 166 whose slug no candidate matches) is deliberately skipped.
    • Locked rows get releaseTime: null: their date cell holds an unlock countdown (Unlocked in 4 weeks / TBA), which parseData would have turned into a bogus past date.
  3. parseChapter reports locked/unavailable chapters instead of returning a blank chapter (same flag): a coin-locked page throws This chapter is locked on Tangerine Archive (premium, 10 coins). Its text is only served to readers who unlocked it, so it becomes readable here once the site unlocks it., and a chapter URL the site no longer resolves — an unknown slug silently falls back to the series page — throws This chapter is not available on Tangerine Archive anymore. instead of returning ''.
  4. plugins/multisrc/madara/README.md documents the option alongside the generator's other source options.

Verification

Live, anonymous, read-only (no cookies, no login, no purchase, never the buy endpoint). npm run build:compile, npm run build:multisrc, prettier and eslint plugins/multisrc/madara/template.ts pass. npm run check:plugin -- 'plugins/english/TangerineArchive[madara].ts' passes (popularNovels 12, searchNovels PASS, parseNovel 173 chapters, parseChapter 14497 chars).

A custom probe compares parseNovel against an independent read of the live ajax chapter list:

series live rows locked rows listed locked listed
garden-of-may 212 52 212 52
holy-night-my-husband-is-definitely-a-paladin 464 105 464 105
it-seems-like-the-infamous-trash-can-is-right-here 264 34 264 34
i-thought-i-was-the-monster-dukes-fake-tranquilizer 173 120 173 120
villain-let-me-touch-you 191 46 190 45 (1 unresolvable duplicate skipped)

For garden-of-may every one of the 52 derived locked URLs was re-fetched: 52/52 returned HTTP 200 with #wp-manga-current-chap, i.e. they are real chapter pages, not the series-page fallback. parseChapter was exercised on: free chapter-160 (6538 chars of prose), locked chapter-161 and chapter-212, villain-let-me-touch-you/chapter-176-%f0%9f%94%9e/ (the emoji-suffixed slug), villain-let-me-touch-you/chapter-166/ (the duplicate-number owner) — all four throw the locked error — and garden-of-may/chapter-9999/, which throws the not-available error.

Shared-template guard: plugins/english/SleepyTranslations[madara].ts (does not set the flag) still passes all four live checks.

Probed and ruled out (anonymous)

POST /series/<slug>/ajax/chapters/ already returns every row including premium, with the premium data-chapter-<id> class — the gap was only the missing URL. The reading-nav endpoint (POST admin-ajax.php action=manga_get_reading_nav&manga=<id>&chapter=<slug>&volume_id=0&style=list&type=content) returns all 212/464 <option>s but no URLs: premium options carry data-redirect="#" and every option's value is the currently-open chapter slug, and its id sequence is identical to the ajax list's, so it adds nothing. Also ruled out: /wp-json/wp/v2/search → [] and no chapter CPT in REST, ?s=…&post_type=wp-manga-chapter returns no chapters, /sitemap.xml lists only the 997 free chapters, the series /feed/ returns the HTML page, /?p=<chapter id> → 404, and the coin plugin's JS only branches on data-redirect == '#'. No endpoint exposes a locked chapter's URL, so derivation plus post-id verification is the only route; the site's own free rows and the 52/52 re-fetch above are the evidence that the derived URLs are real.

Not tested / known limits

  • No entitled-session test. Locked bodies are gated server-side — the anonymous page contains only the lock notice, and there is no anonymous route to the prose — so no locked chapter text was read and none is claimed. Locked chapters are listed and labelled, and opening one reports the lock honestly. Verifying an entitled read would need an owner-created test account with a coin balance, which LNReader plugins cannot log in with in any case.
  • Locked rows use the template's existing 🔒 name prefix. Richer labels (coin price, unlock countdown) were deliberately left out of this diff, as was enabling hasLocked: true (the existing "Hide locked chapters" switch) — easy to add if wanted.
  • The pre-existing releaseTime: 'LL' bug for free chapters (dayjs().format('LL') without the localizedFormat plugin) is untouched; it affects every Madara source and is not introduced here.

Authored by an AI agent (pi crewmate) on behalf of the task that requested it; reviewed evidence is in this description and reproducible with the commands above.

pi crewmate added 2 commits September 23, 2026 20:04
Tangerine Archive's chapter list returns every row, but coin-locked rows
carry href="#" and were dropped by the template's `chapterUrl != '#'`
guard, so a 212-chapter novel showed only its 160 free chapters.

Locked rows are now listed with the URL the site actually serves them at
(`<novelPath>chapter-<n>/`, n from the row title, which returns 200 with a
lock notice instead of the body), behind a new `listLockedChapters` source
option that only tangerinearchive sets, so other Madara sources take the
unchanged path.

The plain slug is used as-is only when the title is exactly "Chapter <n>"
and that number is unique in the novel; the site's own free rows confirm
the convention (3065/3083 exact, every exception a suffixed, decimal or
duplicated title). Other titles do not slugify the way they read - a
trailing emoji is sometimes part of the slug and sometimes not - so their
candidate slugs are verified against the chapter page's own post id
(`#wp-manga-current-chap[data-id]`) and a row is skipped rather than listed
with a URL that serves a different chapter or the series page. Of the 785
locked rows across all 16 series, 784 are listed and 1 (a duplicate-number
row whose slug no candidate matches) is skipped.

parseChapter now reports locked chapters with a clear error instead of an
empty chapter, and reports an unknown chapter URL (which silently falls
back to the series page) instead of returning an empty chapter.

Verified anonymously and read-only against the live site: parseNovel
returns 212/212 chapters for garden-of-may (52 locked, all 52 derived URLs
resolve to a real chapter page), 464/464 for holy-night (105 locked),
264/264 for it-seems-like (34 locked), 173/173 for
i-thought-i-was-the-monster-dukes (120 locked) and 190/191 for
villain-let-me-touch-you (45 locked, 1 unresolvable row skipped). Free
chapters still parse (chapter-160 -> 6538 chars). No entitled-session
test: locked bodies are server-gated, so no locked prose was read.

Co-Authored-By: pi crewmate <crewmate@pi.local>
Co-Authored-By: pi crewmate <crewmate@pi.local>
@RibatTRW RibatTRW closed this Sep 23, 2026
@RibatTRW
RibatTRW deleted the fm/tangerine-premium-nav-api branch September 23, 2026 13:00
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