Skip to content

feat: add ja.spoilerplus - #684

Open
kinboy56 wants to merge 1 commit into
Aidoku-Community:mainfrom
kinboy56:ja.spoilerplus
Open

feat: add ja.spoilerplus#684
kinboy56 wants to merge 1 commit into
Aidoku-Community:mainfrom
kinboy56:ja.spoilerplus

Conversation

@kinboy56

@kinboy56 kinboy56 commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

Adds a source for https://spoilerplus.tv, a Japanese raw manga site. Fix #17

The site runs the same CMS and image infrastructure as ja.mangarawjp (same
POST /api/v1/get/c endpoint, same img-cdn.stackpathcdn.app CDN, same
page-descrambling scheme), but ships a different theme, so only the request and
descrambling logic are shared in spirit — the HTML parsing is written against
this site's markup.

Notes on the implementation

  • Descrambling. Each page is shuffled into a square grid. The API returns
    the ordering as hex bytes XORed with the site's own domain string, so
    spoilerplus.tv is the key. The order travels to the image through
    PageContext, and PageImageProcessor reassembles the grid on a canvas of
    the original image's dimensions.
  • templates/wpcomics was not used. The theme is NetTruyen-based and its
    default selectors do match, but the template does not forward
    PageImageProcessor, and images here are useless without descrambling.
    Changing the template to support that would risk regressions in the seven
    sources already using it, so the source is written standalone.
  • Keys are stored decoded. The site percent-encodes series slugs but leaves
    chapter segments as raw UTF-8, and a raw UTF-8 path is answered with a 404.
    Since encode_uri also escapes %, passing the mixed form straight through
    turns %20 into %2520. Keys are therefore decoded on the way in (to_key)
    and encoded on the way out (url_for), so both href forms collapse to one
    key.
  • Publication status is read off the labelled row. The info list gives the
    alternative-titles row the same li.row.status classes as the publication
    status row, so selecting by class alone reads the alternative title as the
    status. The row is picked by its 状態 label instead; a regression test
    covers this. An unrecognised value maps to MangaStatus::Unknown rather than
    falling back to Ongoing.
  • Inline script bodies are read with html(). Element::data() returns
    None for every <script> on these pages, and the reader's window.MangaId
    / window.CNumber values are what key the image API.
  • contentRating is 2 (adult). The genre list carries permanent Ecchi
    and エロい categories, consistent with the sibling sites on the same CMS.
  • Search takes precedence over the sort filter. Search has its own endpoint
    that the ordering paths cannot be applied to, so config.hidesFiltersWhileSearching
    is set and a non-empty query wins over whatever sort value is still stored.

Deliberately left out

  • date_uploaded is not set. The site mixes relative (1日前) and absolute
    (2026年07月30日) forms, and only the absolute one is parseable — the same
    reason it is unset in ja.mangarawjp.
  • No genre filter. /genre/<name>/ exists, but the list changes on the site's
    side, so it would need DynamicFilters rather than a hardcoded list. Left
    for a follow-up.

Test plan

  • cargo fmt
  • cargo clippy --release
  • cargo test --release (17 passed)
  • aidoku package && aidoku verify package.aix
  • Verified on device: browsing, ranking, search, details, chapter list and
    descrambled pages.

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.

spoilerplus.tv

1 participant