fix(madara): list Tangerine Archive premium chapters and fail loudly on locked bodies (replica + hardening) - #11
Closed
RibatTRW wants to merge 1 commit into
Conversation
…on locked bodies Premium (coin-locked) chapter rows in the ajax chapter list carry href="#", so they were dropped from novel.chapters and their real (guessable) URLs returned an empty body. With the new premiumChapterUrls option (enabled only for tangerinearchive), locked rows are listed with their reconstructed chapter-<n> path and parseChapter throws a descriptive error instead of a blank chapter: - reconstruct from the first number in the row's anchor text, keeping a non-ASCII token that is part of the site's slug (Chapter 176 -> chapter-176 with the percent-encoded emoji) but not a " - title" suffix, and turning "27.2" into "27-2"; a row with no number is skipped; - resolve duplicate-number collisions at list time: only rows whose derived URL resolves to their own post id are kept (villain-let-me-touch-you Chapter 166 has two posts on one slug), so a derived URL can never be listed as a different chapter; - parseChapter: a page without a chapter container throws a distinct "did not return a chapter page" error (series-page soft-404), a gated page throws "Premium chapter locked: costs N coins ...", and an empty chapter page throws a "no readable text" error; - releaseTime is null for reconstructed rows (the site's countdown/TBA is not a publication date); - hasLocked gives users the existing "Hide locked chapters" switch. Verified live, anonymous and read-only: 672/672 premium rows across 19 series resolve to their own gated chapter page; 156/156 free pages still parse; duplicate/emoji/title-suffix/soft-404 cases exercised. Sources without the flag are unchanged (SleepyTranslations, Lovelyblossoms check:plugin PASS). Co-authored-by: firstmate-crewmate <crewmate@firstmate.local>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Stacked candidate fix for lnreader#2564
Base is deliberately
fm/lnreader-plugins-2555-tangerine, the head branch of PR lnreader#2564 — not a default branch. Merge only after (or together with) that PR.This is the wave-2 replica of the
fm/tangerine-premium-fail-loudlane (PR #2 in this fork): same fail-loud approach, rebuilt on PR 2564's head with the hardening that lane lacked. It fixes exactly the gap PR 2564 documented: premium (coin-locked) chapters were dropped from the chapter list because their ajax rows carryhref="#", andparseChapteron their real URLs returned an empty body.What changed (
plugins/multisrc/madara/{template.ts,sources.json,README.md})New
premiumChapterUrlsoption, off by default and enabled only fortangerinearchive(versionIncrements: 1→ plugin 2.2.1). Sources without the flag take the previous code path unchanged.premium-block,href="#") gets its realchapter-<n>path reconstructed from the first number in the row's anchor text. The rule also covers the site's odd slugs: a non-ASCII token directly after the number is part of the slug (Chapter 176 🔞→chapter-176-%f0%9f%94%9e) while a- titlesuffix is not (Chapter 242 - 🔞→chapter-242), and27.2becomes27-2. A row with no number is skipped rather than listed with a guessed URL.Chapter 166on one slug, and in principle a free row could own the same slug). Only those ambiguous rows are resolved with one extra fetch, and a row is kept only when the page's#wp-manga-current-chap[data-id]is its owndata-chapter-<id>; the other row is dropped. A derived URL can therefore never be listed as a different chapter.parseChapterfail-loud (flag-gated): a fetched page with no chapter container (the series-page soft-404) throwsChapter content unavailable: … did not return a chapter page …; a gated page throwsPremium chapter locked: costs N coins and requires a site account to unlock.(price read from the gate block, so it also covers rows listed free whose page is still gated); an empty chapter page throws a distinct "no readable text" error. Free chapters parse exactly as before.releaseTimeisnullfor reconstructed rows (the site's "Unlocked in 4 weeks"/TBA is not a publication date; the template'sparseDatawould otherwise report a past date).hasLocked: trueexposes the existing "Hide locked chapters" switch (default off ⇒ locked rows are shown).What was probed (all anonymous, read-only; no cookies, no login, buy endpoint never called)
wp-manga-sitemap.xml,POST /series/<slug>/ajax/chapters/each → 3359 rows = 2687 free + 672 premium. The rule reproduces 2669/2687 free rows' real hrefs exactly, 17 differ only by percent-encoding case (same URL), 1 is the WP duplicate-slugchapter-55_1.data-idand the lock block; the 1 exception is the documented duplicateChapter 166(id 16701, resolves to sibling 16700), which this PR drops from the list. 0 soft-404s, 0 free-prose-under-premium.chapter-0, emoji and_1slugs) all carry#wp-manga-current-chap+.reading-contentand parse prose (median 4465 chars)..scratch/probe-premium.mjs, 44/44): for 6 series the plugin's chapter count equals the live ajax list minus resolved collisions; every premium path equals the independently derived set; emoji rows 175/176 keep their emoji slug; villainChapter 166lists only the row that owns the slug; free garden-160/villain-144 parse 6538/7859 chars; locked garden-161/212 and the villain emoji slug throw the 10-coin message; the plain villain-176 URL (series-page soft-404) and a deliberately wrong/chapter-999/throw the distinct "did not return a chapter page" error;hideLockedon/off gives 160/212 chapters.npm run build:multisrc,npm run build:compile,npm run check:pluginon the generated Tangerine plugin (PASS: 12 novels, 173 chapters, 14497 chars), prettier/eslint clean on the three changed files. Repo-wide eslint is unchanged from the base commit (79 problems both sides); the 10 prettier warnings are pre-existing files untouched here.SleepyTranslations[madara](44 chapters, 9872 chars) andLovelyblossoms[madara](5 chapters, 15373 chars) — both PASS.What was NOT tested
it-seems-like-…/chapter-231,i-thought-…/chapter-53) and garden-of-maychapter-161still reports "Unlocked in 1 day". A read-only flip watcher is armed to capture a flip if one lands.Authored with AI assistance (firstmate crewmate) — reviewers should weight accordingly.