Document the unified httpclient.Client design, FlareSolverr integration, cookie jar sharing, and Kotlin vs Go mapping.
24 KiB
Executable File
Phase 4 — Standalone Sources
Complete port checklist. Check a box when the source passes a basic smoke test (popular/latest list returns ≥1 result, or detail+pages resolve for a known URL).
After implementing each source: mark its checkbox [x] in this doc and add it to the import list in cmd/server/main.go.
Important: Source Types
Base Sources (lib-multisrc)
Shared implementations in:
/Users/achmad/Documents/Belajar/Android/extensions-source/lib-multisrc//Users/achmad/Documents/Belajar/Web/goyomi/sources/base/
These are the core implementations that standalone sources may use.
Standalone Sources (all/ and en/)
Individual extensions in:
/Users/achmad/Documents/Belajar/Android/extensions-source/src/all//Users/achmad/Documents/Belajar/Android/extensions-source/src/en/
These may or may not use a base source from lib-multisrc — and even when they do, they often add their own
implementation on top (overriding methods, adding extra parsing, custom filters, etc.).
When porting, check if the Kotlin source extends a base class (e.g., MangaReader, Madara, ComicGeek) —
if so, use the corresponding Go base implementation as the foundation, then layer the source-specific overrides.
Otherwise, implement fully standalone.
Reference:
/Users/achmad/Documents/Belajar/Android/extensions-source/src/all//Users/achmad/Documents/Belajar/Android/extensions-source/src/en/
HTTP Client Architecture
Cookie Jar
The httpclient.Client creates a cookiejar.Jar for every instance. All cookies from
direct HTTP responses and from FlareSolverr are stored in this shared jar. Sources can
read cookies from the jar using the Cookie(name, host) method:
// After a request, check the jar for a specific cookie (like Kotlin's
// client.cookieJar.loadForRequest()):
value := client.Cookie("mhub_access", "mangahub.io")
Response Headers from FlareSolverr
When Do() falls back to FlareSolverr, the doFS() method now properly propagates
the actual response headers from FlareSolverr (including Set-Cookie) instead of
copying request headers into the response. Cookies from FS are also explicitly added
as Set-Cookie headers and fed into the shared cookie jar.
Single Unified Client
All sources share one unified HTTP client (internal/httpclient.Client) that handles both
direct requests and Cloudflare/DDoS bypass transparently:
httpclient.DefaultClient()
├── Direct HTTP (net/http + cookie jar + rate limiter)
└── FlareSolverr fallback (auto-detected from FLARESOLVERR_URL env var)
The Do(req) method implements adaptive logic matching the Kotlin cloudflareClient:
- Try direct — normal HTTP request with shared cookie jar + rate limiting
- If 403/503 (Cloudflare/DDoS challenge) — falls back to FlareSolverr raw mode
- FlareSolverr solves the challenge — returns actual server response + cookies
- Cookies fed into shared jar — subsequent requests to the same host skip FS
- Chrome HTML wrapper stripped — FlareSolverr wraps responses in
<html><head>...<meta...></head><body><pre>body</pre></body></html>— the client strips this wrapper so callers receive the actual server body (JSON or HTML).
Kotlin vs Go Mapping
Kotlin (cloudflareClient) |
Go (httpclient.Client) |
|---|---|
OkHttpClient with Cloudflare interceptor |
net/http.Client with FlareSolverr fallback |
| Intercepts 503 → solves via WebView → retries with cookies | Tries direct first → on 403/503 → FS raw → strips wrapper |
| Returns raw server response body | Returns stripped FS body or direct body |
| Shared across all sources | DefaultClient() singleton shared across all sources |
How Sources Use the Client
import "goyomi/internal/httpclient"
// Default: use shared singleton (preferred for most sources)
func (s *Source) fetch(ctx context.Context) error {
req, _ := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
req.Header.Set("Accept", "application/json")
resp, err := httpclient.DefaultClient().Do(req)
// ...
}
// Custom rate limit: create a dedicated client
func New() *Source {
c := httpclient.NewClient(httpclient.WithRateLimit(5, 10))
return &Source{client: c}
}
// Custom headers: set on the request (no need for a custom client)
func (s *Source) fetch(ctx context.Context) error {
req, _ := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
req.Header.Set("Accept", "text/css") // DDOS-Guard bypass
resp, err := httpclient.DefaultClient().Do(req)
}
FlareSolverr Integration
FlareSolverr is auto-configured from the FLARESOLVERR_URL environment variable.
If unset, the client works in direct-only mode (no Cloudflare bypass).
The flare package (internal/httpclient/flare/) is a backward-compatible shim —
all it does is alias httpclient.Client and httpclient.NewClient. Sources that
imported flare before continue to compile without changes.
Design Decisions
- Why a singleton? — Shared cookie jar means cookies from FlareSolverr (solved challenges) benefit all sources. Shared rate limiter prevents hammering the same host.
- Why strip FS wrapper? — FlareSolverr routes requests through Chrome, which wraps
JSON responses in
<html><head>...<pre>JSON</pre>.... Without stripping, JSON parsers fail. For HTML responses, Chrome adds<meta charset>tags but the page content remains parseable by goquery. - Why direct first? — Most sites don't have Cloudflare. Direct requests are faster (no Chrome overhead) and return clean response bodies. FS is only invoked on actual challenge responses (403/503).
- Why not Chrome rendering? — The Kotlin
cloudflareClientdoes NOT render through Chrome. It intercepts 503, solves the challenge, and returns the raw server response. Our approach matches this behavior.
Detailed implementation notes for complex sources are in the Notes section at the bottom.
sources/all/ — 125 sources
all/ahottieall/akumaall/allporncomicscoall/asmhentaiall/baobuaall/beauty3600000all/buonduaall/comicfuryall/comicgrowlall/comickliveall/comicskingdom⚠️ no Kotlin reference found — skipall/comicsvalleyall/comikeyall/commitstripall/coomerall/coronaexall/cosplayteleall/cubariall/danbooru⚠️ see notesall/deviantartall/dragonballmultiverseall/e621⚠️ see notesall/elitebabesall/everiacluball/everiaclubcomall/femjoyhunterall/foamgirlall/foolslidecustomizableall/fourkhdall/ftvhunterall/fuwayomi⚠️ skip — server-side Fuwayomi integration, not a scraperall/globalcomix⚠️ see notesall/grabberzoneall/hdoujinall/hennojinall/hentai3all/hentaicosplayall/hentaienvyall/hentaieraall/hentaifoxall/hentaihandall/hentairoxall/hentaizapall/hniscantradall/holonometriaall/honeytoonall/imhentaiall/izneoall/jjcosall/joymiihuball/junmeituall/kemono⚠️ see notesall/kiutakuall/kodokustudioall/koharuall/komga⚠️ see notesall/lanraragiall/leagueoflegendsall/lunaranimeall/luscious⚠️ see notesall/magicaltranslatorsall/manga18meall/mangaballall/mangacrazyall/mangadex⚠️ see notesall/mangadraftall/mangafireall/mangaforfreeall/mangaplus⚠️ see notesall/mangapluscreatorsall/mangataroall/mangatoonall/mangaupall/mangoall/manhuarmall/manhwa18ccall/manhwa18netall/manhwa18uncensoredall/manhwaclubnetall/manhwadashrawall/mayotuneall/metarthunterall/miauscanall/misskonall/mitakuall/myreadingmangaall/namicomiall/nhentaicom⚠️ see notesall/nhentaixxxall/niaddall/ninemangaall/novelcoolall/ososedkiall/pandachaikaall/peppercarrotall/photos18all/pixiv⚠️ see notesall/playmatehunterall/pornpicsall/projectsukiall/qtoonall/rokuhentaiall/sakuramanhwaall/sandraandwooall/seraphicdeviltryall/simplycosplayall/simplyhentaiall/stashapp⚠️ see notesall/taddyinkall/tappytoonall/thelibraryofoharaall/thunderscansall/toomicsall/twicomiall/uncensoredmanhwaall/vinnieVeritasall/webtoons⚠️ see notesall/xarthunterall/xasiatalbumsall/xgmnall/xinmeituluall/xiutakuall/xkcdall/yabaiall/yaoimangaonlineall/yellownoteall/yskcomics
sources/en/ — 430 sources
en/akaicomicen/alandalen/allanime⚠️ see notesen/allporncomicen/allporncomicioen/anisascansen/apcomicsen/aquamangaen/arcrelight→ base: MangAdventureen/arenascansen/armageddonen/artlapsaen/arvencomicsen/arvenscansen/aryascansen/asiatoonen/asmotoonen/assortedscansen/asurascans⚠️ see notesen/athreascansen/atsumaru→ standalone HttpSourceen/aurora→ standalone HttpSourceen/azcomic→ standalone HttpSourceen/azukien/baektoonsen/bakkin⚠️ see notesen/bakkinselfhosteden/batcaveen/battleinfivesecondsaftermeetingen/beehentai→ base: MadThemeen/bookwalker⚠️ see notesen/boratscans→ base: Madaraen/boxmanhwa→ base: MadThemeen/broccolisoup→ standalone HttpSourceen/bunmanga→ base: Madaraen/buttsmithy→ standalone HttpSourceen/clonemanga→ standalone HttpSourceen/clowncorps→ standalone HttpSourceen/cmanhuaen/cocomicen/coffeemanga→ base: Madara (complex)en/collectedcuriosen/comicasuraen/comiccxen/comichubfreeen/comickfanen/comickibaen/comiclanden/comicslanden/comixen/crowscansen/cucumbermanga→ base: Madaraen/culturedworksen/cutiecomicsen/dankefurslesenen/darklegacycomicsen/darkscansen/darkscienceen/darthsdroidsen/deathtollscansen/decadencescans→ base: Madaraen/dflowscansen/digitalcomicmuseumen/divascansen/doujinioen/doujinsen/dragonteaen/drakescansen/dynastyen/eggporncomicsen/egscomicsen/eighteenporncomicen/eightmusesen/elanschoolen/elftoonen/epicmangaen/erisscansen/ero18xen/erofusen/erosscansen/evascansen/evilflowersen/existentialcomicsen/explosmen/ezmangaen/fablescansen/fairyscansen/firescans→ base: Madara (complex)en/flamecomicsen/frierenonlineen/gakamangasen/galaxydegenscansen/galaxymangaen/gedecomixen/gingertoonen/godaen/gourmetscansen/greedscansen/grimscansen/grrlpoweren/gunnerkriggcourten/guya⚠️ see notesen/gwtben/hachirumien/hadesscansen/harimangaen/hentai3zccen/hentai4freeen/hentaidexen/hentaihereen/hentaikunen/hentainexusen/hentairead→ base: Madara (complex)en/hentaireadioen/hentaiscoen/hentaixcomicen/hentaixdickgirlen/hentaixyurien/hentaraen/heytoonen/hijalascansen/hiperdexen/hiveworksen/hm2den/honkaiimpacten/hotcomicsen/hyakuroen/infernalvoidscansen/infinityscansen/irovedouten/isekaiscantopen/jinmangasen/jnovelen/kaganeen/kaizenscanen/kaliscancom→ base: MadThemeen/kaliscanio→ base: MadThemeen/kaliscanmeen/kappabeasten/kaynscansen/keenspoten/kenscansen/kewnscansen/killsixbilliondemonsen/kingcomixen/kingofshojoen/kissmangainen/kmangaen/kodanshaen/ksgroupscansen/kunmangaen/kuramangaen/lagoonscansen/leslievictimsen/lhtranslationen/likemangaen/likemangainen/lilymangaen/linkmanga→ base: Madaraen/loadingartisten/luascansen/luminaretranslationsen/lunatoonsen/madaradexen/madarascansen/madokamien/magusmangaen/mahouirexnohentaikarteen/manga18cluben/manga18freeen/manga18fxen/manga18xen/mangabaten/mangablazeen/mangabolten/mangabtten/mangabuddy→ base: MadThemeen/mangabuddyme→ base: MadThemeen/mangaclashen/mangaclouden/mangacute→ base: MadThemeen/mangadassen/mangadeen/mangademonen/mangadiaen/mangadistricten/mangadotneten/mangadramaen/mangafab→ base: MadThemeen/mangaforest→ base: MadThemeen/mangaforfreecomen/mangafoxen/mangafoxfun→ base: MangaHuben/mangafreaken/mangafreeen/mangaggen/mangagoen/mangagofunen/mangaheen/mangahenen/mangahentaien/mangahereen/mangahereonl→ base: MangaHuben/mangahubio→ base: MangaHuben/mangakaen/mangakakaloten/mangakakalotfun→ base: MangaHuben/mangakatanaen/mangakissen/mangamaniacsen/mangamoen/mangamoben/mangamonk→ base: MadThemeen/manganel→ base: MangaHuben/manganeloen/manganowen/mangaonlinefun→ base: MangaHuben/mangaowlioen/mangapandaonl→ base: MangaHuben/mangapillen/mangapumaen/mangarawcluben/mangareaden/mangareaderccen/mangareadersite→ base: MangaHuben/mangareadorgen/mangasaga→ base: MadThemeen/mangasecten/mangaspin→ base: MadThemeen/mangasushien/mangatellersen/mangatoday→ base: MangaHuben/mangatownen/mangatrenden/mangatxen/mangaxyz→ base: MadThemeen/manhuafasten/manhuafastneten/manhuahoten/manhuanexten/manhuanow→ base: MadThemeen/manhuaplusen/manhuaplusorgen/manhuarushen/manhuascanusen/manhuasite→ base: MadThemeen/manhuatopen/manhuausen/manhuazongheen/manhwa18en/manhwa18orgen/manhwa68en/manhwabuddyen/manhwaclanen/manhwacomicsen/manhwadenen/manhwageten/manhwahuben/manhwajoyen/manhwalikeen/manhwaloveren/manhwamanhuaen/manhwareaden/manhwareadsen/manhwatoonen/manhwatopen/manhwaxen/manhwaxxlen/manhwazen/manhwazoneen/mantaen/megatokyoen/mehgazoneen/meitoonen/mgjinxen/mgreadioen/milftoonen/mistscansen/mlbbloreen/monochromecustomen/monochromescansen/multpornen/murimscanen/myhentaicomicsen/myhentaigalleryen/necroscansen/newmanhwaen/nexcomicen/nikatoonsen/nineanimeen/ninehentaien/ninekonen/novel24hen/novelcrowen/noxenscansen/nuxscansen/nyanukafeen/nyrascansen/nyxscansen/octopusmangaen/oglafen/ohjoysextoyen/omegascansen/onemangaco→ base: MangaHuben/onemangainfo→ base: MangaHuben/onepunchmanonlineen/onlythebesthentaien/ootsen/oppaistreamen/orchisasiaen/orionscansen/paradisescansen/paragonscansen/paritehaberen/patchfridayen/pawmangaen/petrotechsocietyen/philiascansen/plutoscansen/pmscansen/porncomixen/qiscansen/questionablecontenten/ragescansen/randowizen/ravenscansen/razureen/rdscansen/readallcomicscomen/readattackontitanshingekinokyojinmangaen/readberserkmangaen/readblackclovermangaonlineen/readbokunoheroacademiamyheroacademiamangaen/readchainsawmanmangaonlineen/readcomiconlineen/readcomicsonlineen/readfairytailedenszeromangaonlineen/readhaikyuumangaonlineen/readjujutsukaisenmangaonlineen/readkingdommangaonlineen/readnanatsunotaizai7deadlysinsmangaonlineen/readnarutoborutosamurai8mangaonlineen/readonepiecemangaonlineen/readonepunchmanmangaonlinetwoen/readsololevelingmangamanhwaonlineen/readtokyoghoulretokyoghoulmangaonlineen/readvagabondmangaen/reallifecomicsen/reimangaen/renascansen/resetscansen/restscansen/retsuen/revivalscansen/rinkocomicsen/ritharscansen/rizzcomicen/rizzcomicunoriginalen/rokaricomicsen/roliascanen/rosesquadscansen/ryumangaen/s2mangaen/sanascansen/saturdaymorningbreakfastcomicsen/schlockmercenaryen/setsuscansen/shibamangaen/shojoscansen/sirenscansen/skymangaen/sleepytranslations→ base: Madaraen/solarandsundryen/spmanhwaen/spyfakkuen/stonescapeen/sunshinebutterflyscansen/supermegaen/suryascansen/swordscomicen/tapasticen/tcbscansen/tcbscansunoriginalen/teamshadowien/templescanen/theblanken/theduckwebcomicsen/thepropertyofhateen/timelesstoonsen/todaymangaen/toon18en/toongoden/toonilyen/toonilymeen/toonitubeen/toonizyen/topmanhuaen/topmanhuafanen/topmanhuaneten/tritiniascansen/utoonen/valirscansen/vanillascansen/vgpersonen/vizshonenjumpen/voycemeen/vyvymangaen/vyvymangaorgen/warforrayubaen/wearehungeren/webcomicsen/webdexscansen/webnovelen/webtoonscanen/webtoonxyzen/weebcentralen/whalemangaen/witchscansen/woopreaden/writerscansen/wuxiaworlden/xlecxen/xomangaen/xoxocomicsen/yakshacomicsen/yaoihoten/yaoihuben/yaoiscanen/yaoitoonen/yoraien/zazamangaen/zinchanmangaen/zinchanmangacomen/zinmangaen/zinmanganet
Notes — Complex Sources
HTTP Client Selection
All sources use the same unified client — httpclient.DefaultClient(). You no longer need
to decide between direct HTTP and FlareSolverr; the client handles both automatically.
In Kotlin, override val client = network.cloudflareClient signals the source needs
Cloudflare bypass — but in Go, the adaptive Do() method provides it transparently.
all/mangadex ⚠️
- Rate limit: 5 req/s (
golang.org/x/time/rate) GetPopularManga—GET /manga?order[rating]=desc&includes[]=cover_art&limit=20&offset={(n-1)*20}GetLatestUpdates—GET /manga?order[updatedAt]=desc&includes[]=cover_artGetSearchManga— tag filters (includedTags[],excludedTags[]), demographic, content rating, status, sortGetMangaDetails—GET /manga/{id}?includes[]=cover_art&includes[]=author&includes[]=artist; cover URL from relationshipsGetChapterList—GET /manga/{id}/feed?translatedLanguage[]=en&limit=500; paginate until all chapters fetchedGetPageList—GET /at-home/server/{chapterId}→{baseUrl}/{quality}/{hash}/{filename}; quality =dataordata-saverGetFilterList—GET /manga/tag; cache tag list- Language configurable via
MANGADEX_LANGenv var (defaulten)
all/nhentaicom ⚠️
- FlareSolverr required
GetPageList— extract JSON blob fromdocument.getElementById(...)in<script>tag; reconstruct URLs as{imageServer}/galleries/{mediaId}/{pageNum}.{ext}
all/kemono ⚠️
- FlareSolverr required
- Service + creator = manga; posts = chapters; attachment files = pages
- File URLs: prefix relative paths with
{base}/data GetChapterList— paginate in 50-post increments via?o={offset}
all/komga ⚠️
- Self-hosted;
KOMGA_BASE_URL,KOMGA_USERNAME,KOMGA_PASSWORDenv vars; Basic Auth - Series = manga; Books = chapters; pages via
GET /api/v1/books/{bookId}/pages/{n}/thumbnail
all/e621 ⚠️
- Optional Basic Auth (
E621_USERNAME,E621_API_KEY); required for Gold content - Pools = manga; posts = pages;
GET /pools.json,GET /posts.json?tags=pool:{id}
all/danbooru ⚠️
- Optional Basic Auth; free accounts limited to 2 tags per search
- Pools = manga; pool post_ids = pages; image URL from
file_urlfield
all/pixiv ⚠️
- Auth:
PIXIV_PHPSESSIDenv var → Cookie header GetPageList—GET /ajax/illust/{illustId}/pages; extracturls.original- Must set
Referer: https://www.pixiv.net/on all image requests or get 403
all/luscious ⚠️
- GraphQL POST to
https://members.luscious.net/graphql/playground - Albums = manga;
SearchAlbumPicturespaginated for page list
all/mangaplus ⚠️
- JSON endpoint:
GET https://jumpg-webapi.tokyo-cdn.com/api/title_detail?title_id={id} GetPageList— XOR page URL decryption using per-pageencryptionKey(hex string); implement inGetImageURL- Required header:
Origin: https://mangaplus.shueisha.co.jp
all/stashapp ⚠️
- Self-hosted;
STASHAPP_BASE_URL,STASHAPP_API_KEYenv vars - GraphQL POST to
{base}/graphql;ApiKeyheader
all/globalcomix ⚠️
- Signed CDN URLs with short expiry — do NOT cache
image_urlin DB; always fetch fresh page URLs
all/webtoons ⚠️
- HMAC-SHA256
Sec-Webtoon-Client-Dataheader:HMAC-SHA256(fixedKey, "{url}_{timestamp}")→ base64; fixed key in extension source - Web API:
https://global.apis.naver.com/webtoonSvc/v1/...
en/allanime ⚠️
- GraphQL POST to
https://api.allanime.day/api - Episode-based chapters;
episodeStringused as chapter number - Multiple CDN quality tiers in page response; pick highest
en/asurascans ⚠️
- FlareSolverr required
- Page selector varies by layout version; check extension source for current selector
en/bakkin ⚠️
- No list/search; all manga from a single JSON URL; enumerate from object keys
GetSearchMangadoes client-side title filtering only
en/bookwalker ⚠️
- DRM-protected; metadata only
GetPageListreturns empty slice; pages not accessible without purchase
en/guya ⚠️
GET {base}/api/get_all_series/returns all manga at once; no pagination- Scanlation group filter applied client-side in response parsing