Passing (7): manhuanext, manhwa68, manhuaus, shibamanga,
toonizy, ksgroupscans, mangaclash
Site issue (1): gingertoon — httpcloak fingerprint detected by site,
returns empty manga list vs FS returns real content
The challenge detection only needs a sample (32KB), but when the body
is not a challenge, the full body must be returned. The previous code
discarded everything after 32KB, causing Madara parsers to find 0
manga items in truncated HTML.
Some anti-bot challenge pages return HTTP 200 instead of 403/503,
bypassing the FS fallback. Read response body on 200, check for
challenge indicators, and fall through to FlareSolverr if detected.
hentai4free: Use search-based URLs for popular/latest with m_orderby
param (views/latest), matching Kotlin's popularMangaRequest override
nyanukafe: Set custom selectors for popular listing and detail fields
- Use httpcloak.Session (Chrome JA3/JA4 fingerprint) as primary transport
- Adaptive: direct request via httpcloak first; FlareSolverr fallback on 403/503
- FS cookies fed into httpcloak session so subsequent requests reuse
cf_clearance (Chrome fingerprint + cookie = no re-challenge)
- FlareSolverr timeout increased to 120s for slow challenges
- Sanitize FS cookie values (strip quotes/newlines to avoid Go cookie warnings)
- Remove go-cfscraper dependency (pure JS solver was fragile)
Re-enables the direct-then-FS approach removed earlier due to TLS
fingerprint mismatch. With FS sessions configured, the fallback path
is fast after the first request per domain (Chrome caches cf_clearance).
Non-Cloudflare sites still benefit from direct HTTP speed.
go test -json implicitly sets testing.Verbose()=true, so the old guard
always printed manga lists through the script. Switched to an env var
(SOURCETEST_VERBOSE=1) set by the script only when -v is passed.
- Guard isCloudflareChallenge with directStatus >= 400 to prevent
overriding status to 0 when no direct request was made
- When FS returns challenge page without a prior direct status,
return an error instead of silently passing HTTP 0
- Restore default FS session ID to 'goyomi' — without a session,
each request spawns a new Chrome, causing timeouts under load
- Add Message field to FlareSolverrResponse for better error reporting
- Document FLARESOLVERR_SESSION env var: shared session = fast after
1st request, but serializes. Set empty for parallel (resource-heavy).
- guya: align JSON structs with current API (seriesListEntry, seriesDetail),
use Cover/Author/Artist fields, fix chapter date parsing via release_date
- iken: update DTO fields to match API (postTitle, featuredImage, etc.),
add JSON-vs-HTML detection, map seriesStatus to source.Status constants
- kemono: set Accept: text/css header for DDOS-Guard bypass
- madtheme: use .book-detailed-item selector, fix pagination detection,
use 'updated_at' sort for latest updates
- mangahub: check cookie jar in addition to Set-Cookie headers, add
retry-once logic for API key expiry (matching Kotlin interceptor)
When -v is passed, test-sources.sh passes it through to go test -v.
sourcetest.Run uses testing.Verbose() to print the full manga list
from GetPopularManga and GetLatestUpdates, showing title + URL.
The domain aquareader.net now redirects to aquareader.org.
Direct access to aquareader.net hits a JS-based tracking intermediary (mks98.com)
via FlareSolverr, while aquareader.org works reliably.
- Fix GraphQL x param: was "POPULAR"/"LATEST", must be per-site source ID
(e.g. "m01"); order type moved to separate mod param
- Add mhub_access cookie acquisition (x-mhub-access header required on all
API calls); cached 10 min, retried with ?reloadKey=1 on failure
- Fix image URLs: construct as imgx.mghcdn.com/{p}{image} from pages JSON
- Fix thumbnail URLs: add thumb.mghcdn.com/ CDN prefix
- Fix hasNext: use len(rows)==30 instead of non-existent count field
- Fix chapter URL format: /{slug}/chapter-{num} matching Kotlin
- Fix page URL parsing to match new chapter URL format
- Add artist and alternativeTitle fields to manga details
- Fix status parsing: "ongoing"/"completed" string values
- Switch from parameterized GQL variables to direct string interpolation
- Add MangaSource field to Config; update all 11 wrapper sources with
their correct per-site source IDs
Based on Kotlin reference verification:
- madtheme, mangabox, mangacatalog, mangahub → flare
Also updated phase4-standalone.md with project requirement for
checking HTTP client type when porting sources from Kotlin
- Remove global ProxyEnabled() logic from httpclient
- Each source now explicitly chooses client at import time:
- flare client: for JS-rendering/cloudflare sources
- normal httpclient: for REST API sources
- Updated 29 base sources based on Kotlin reference (network.cloudflareClient)
- Add FLARESOLVERR_LOG_LEVEL and FLARESOLVERR_PROXY to .env.example
- Change default port to 3300 in dev.env
- Make compose-dev.yml use ADDR env var for host port
- Enable FLARESOLVERR_PROXY by default in dev.env for testing
- Add config table for storing FlareSolverr proxy setting
- Add HTTP endpoints to get/set proxy status (GET/POST /api/config/flaresolverr)
- Refactor httpclient to support proxy mode (requests go through FlareSolverr)
- Add verbose logging for debugging
- Add POST support to FlareSolverr client
Usage:
GET /api/config/flaresolverr - returns {flaresolverr_proxy: bool}
POST /api/config/flaresolverr - body: {enabled: true/false}