Skip to content

feat(madara): list coin-locked chapters with an explicit lock state (Tangerine Archive) - #7

Closed
RibatTRW wants to merge 1 commit into
fm/lnreader-plugins-2555-tangerinefrom
fm/tangerine-premium-lock-state
Closed

RibatTRW wants to merge 1 commit into
fm/lnreader-plugins-2555-tangerinefrom
fm/tangerine-premium-lock-state

Conversation

@RibatTRW

Copy link
Copy Markdown
Owner

Stacked on lnreader#2564 (base fm/lnreader-plugins-2555-tangerine). Fixes the gap that PR's body documents: premium (coin-locked) chapters are now listed and labelled, and open with an honest locked error instead of disappearing from the list or coming back blank.

What changed

plugins/multisrc/madara/template.ts + plugins/multisrc/madara/sources.json (generated plugin not committed).

New opt-in source option listLockedChapters, off by default, so every other Madara source takes the previous code path:

  • parseNovel reads the source's locked-row markup (premium, premium-block, content-blocked, coin-N) plus the release span (Unlocked in N days|weeks|months / TBA) into an explicit per-chapter state: free, coin-locked(N) or scheduled(countdown|TBA).
  • Locked rows are listed with the state in the name and the permalink the source serves for them (series/<slug>/chapter-<n>/), e.g. 🔒 Chapter 212 (10 coins, TBA), 🔒 Chapter 161 (10 coins, unlocks in 1 day). They were previously dropped because their href is #.
  • Locked rows get releaseTime: null rather than a fabricated date (parseData("Unlocked in 4 weeks") would have reported a date four weeks in the past).
  • Rows that derive the same URL (the source reuses a chapter number) are skipped rather than listed twice.
  • parseChapter decides on the fetched page, not on the listing: a gated body (.reading-content .content-blocked / .premium-block) throws This chapter is locked on Tangerine Archive (scheduled: 10 coins, unlocks in 1 day). Its text is only available after unlocking it on the site. Price comes from the page's coin-N class; the countdown (listing-only information) is added when the plugin has just listed the novel.
  • Two cases where the derived URL is not the row's chapter are reported explicitly instead of silently returning the wrong text: the URL is not a chapter page at all (the source keeps a few locked chapters under a different permalink, e.g. emoji slugs) → did not return a chapter page; the URL resolves to a different chapter id (duplicate numbers) → its page could not be identified.
  • tangerinearchive opts in, also gains the existing "Hide locked chapters" switch, and bumps to 2.2.1.

Probed live (anonymous, read-only, no login/cookies/purchase)

  • POST /series/<slug>/ajax/chapters/ for 7 series: locked rows are uniform — premium coin-N data-chapter-<id> premium-block, href="#", span.coin badge, TBA or Unlocked in ..., every one with a leading Chapter <n> in the title.
  • The derived permalink was verified for all 227 premium rows of 4 series (garden-of-may 52, guidelines 40, holy-night 105, i-give-up 30): 227/227 returned HTTP 200, the lock placeholder, and a #wp-manga-current-chap data-id equal to that row's own data-chapter-<id>.
  • Free chapters unchanged: real prose, 6.5k–7k chars after the plugin's normalisation.
  • Plugin counts equal the live ajax list: garden-of-may 212 (52 locked), guidelines 341 (40), holy-night 464 (105), i-give-up 222 (30), villain-let-me-touch-you 190 (45 + 1 duplicate-number row skipped), deal-breaker 125 (0), i-became-a-barbarians-bride 185 (0).
  • Source-side quirks reproduced and handled: holy-night/…/chapter-359/ is listed as free but its page is gated (now reported as locked); villain-let-me-touch-you has Chapter 176 🔞 (chapter-176/ falls back to the series page; the real slug carries the emoji) and a duplicate Chapter 166 (ids 16701/16700; chapter-166/ resolves to 16700).
  • Dead ends (all anonymous): /api/chapters/<id> 404, ?p=<chapter id> 404, action=manga_get_chapters → 0, action=manga_get_reading_nav options carry data-redirect="#" for premium rows, ?style=list unchanged, sitemaps list free chapters only, /wp-json/wp/v2/types has no chapter CPT.

Validation

  • npm run build:multisrc, npm run build:compile, eslint and prettier on the changed files: pass.
  • npm run check:plugin -- 'plugins/english/TangerineArchive[madara].ts': popularNovels 12, searchNovels PASS, parseNovel 173 chapters, parseChapter 14497 chars — all PASS.
  • Shared-template guard with the option off: SleepyTranslations[madara], LightNovelHeaven[madara] and WuxiaWorldSite[madara] all PASS. Foxaholic/BoxNovel were INCONCLUSIVE (Cloudflare 403) and ZetroTranslation's popularNovels FAIL is pre-existing (its site now serves a "Zetro is moving" page).
  • Custom live probe against the generated plugin: 25/25 checks pass (the counts above, the locked errors incl. countdown/TBA, three free chapters, and the three source quirks).

What was NOT tested / is not in scope

  • No entitled session: no login, no cookies, no coin purchase, no buy endpoint call with parameters. The coin-lock is untouched. Locked prose is not obtainable anonymously — a locked page renders only <div class="… content-blocked premium-block">This chapter is locked!</div> in .reading-content, the prose is absent from the DOM (0 text blocks > 200 chars on a locked page vs 2 on a free one). This PR neither includes, unlocks nor redistributes gated text: it lists and labels locked chapters and reports them honestly when opened. Reading one still requires unlocking it on the site.
  • LNReader plugins cannot log in to WordPress, so an entitled session could not be reused by the plugin anyway.
  • Reader-app rendering of the thrown lock error (only the plugin-level message is verified here).

Authored with AI assistance (see the Co-authored-by trailer) — please weight the review accordingly.

Tangerine Archive marks coin-locked chapters in its ajax chapter list as
`li.wp-manga-chapter.premium.coin-N.data-chapter-<id>.premium-block` with
`href="#"` plus either an "Unlocked in ..." countdown or `TBA`, so the
`href != '#'` guard dropped every locked chapter from the listing: readers
saw a chapter list with holes and, reaching such a chapter another way, a
blank chapter body.

The Madara template now reads that markup into an explicit per-chapter
access state (free / coin-locked(N) / scheduled(countdown|TBA)), lists
locked chapters with the state in the name and a derived permalink
(`series/<slug>/chapter-<n>/`), and makes `parseChapter` fail loudly
instead of returning an empty body. The fetched page stays the ground
truth for whether a chapter is gated, so a chapter that unlocks between
listing and reading still reads normally, and a page that is not the
chapter the listing points at (the source keeps a few locked chapters
under a different permalink, e.g. emoji slugs or duplicate numbers) is
reported as such rather than shown as somebody else's chapter or as the
series page. Locked rows get no fabricated release date: their countdown
or `TBA` is already in the name.

Everything is opt-in through the new additive `listLockedChapters` option,
so other Madara sources are unaffected; tangerinearchive opts in, gains
the existing "Hide locked chapters" switch, and bumps to 2.2.1. No gated
text is fetched, unlocked or bypassed: locked chapters are only listed and
labelled.

Co-authored-by: firstmate-crewmate <crewmate@firstmate.local>
@RibatTRW RibatTRW closed this Sep 23, 2026
@RibatTRW
RibatTRW deleted the fm/tangerine-premium-lock-state 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