New source: Procomic (replaces MangaPro/ProChan) - #18067
Conversation
| return SManga.create().also { | ||
| it.url = "/series/$type/$id/$slug" | ||
| } | ||
| } |
There was a problem hiding this comment.
Thanks for the review!
I'll add the domain check. Quick question though — I was planning to use this:
kotlin
if (url.host != baseUrl.toHttpUrl().host) {
return null
}
But Prochan has mirrors, so I'm not sure how to handle those. Is it worth adding a list of allowed mirror domains, or should I just stick with the main one?
Honestly, it's probably not a big deal since the main website is what users actually use — the mirrors are just redirects anyway. But I added it as a mirror because the API requests work through it too.
What do you think? Should I keep it simple with just the main domain, or build out a mirror list?
There was a problem hiding this comment.
it's fine, or if the mirror isn't really used, you can remove the mirror from extension entirely
- Use client.get()/parseAs helpers instead of GET/await - Remove local Json instance, use shared parseAs - Replace SimpleDateFormat/tryParse with Instant.parseOrNull - Use firstInstanceOrNull instead of filterIsInstance - Remove broad try-catch in searchApi - Convert data class DTOs to regular class - Replace Calendar with Year.now() in Filters - Capitalize extension name in build.gradle.kts - Remove unused import - Use baseUrl mirrors DSL with versionId - Fix ambiguous it-shadowing in genre builder
Add network interceptor that decodes image/avif responses via tachiyomi.decoder.ImageDecoder and re-encodes as JPEG, preventing BitmapRegionDecoder crash during tall image splitting on pre-API30.
- Drop `supportsLatest = true` (default in KeiSource) - Remove AVIF-to-JPEG interceptor and ImageDecoder dependency — app handles AVIF natively - Remove empty `configureHeaders` override - Remove `CacheControl.FORCE_NETWORK` from search requests - Add host check in `getMangaByUrl` to reject cross-domain URLs - Store type/slug/seriesId/chapterNumber in `memo` instead of encoding them in URL paths; use `/$id` as the canonical URL - Add `getChapterUrl()` to reconstruct full chapter URLs from memo - Return both manga details and chapters unconditionally in `getMangaDetails`
|
I used the extention for some time reading some Mangas I discovered some bugs I will try to fix
dear reviewer am sorry for wasting your time dealing with this source I will try my best to fix the issue |
| return SManga.create().also { | ||
| it.url = "/series/$type/$id/$slug" | ||
| } | ||
| } |
There was a problem hiding this comment.
it's fine, or if the mirror isn't really used, you can remove the mirror from extension entirely
| val id = segs[off + 2] | ||
| val slug = segs[off + 3] | ||
| return SManga.create().also { | ||
| it.url = "/$id" |
There was a problem hiding this comment.
no need to prefix with "/"
There was a problem hiding this comment.
I’ll remove the prefix as well, and I won’t change the manga URL structure (/series/$type/$id/$slug) since that’s how it’s set on the website. That way, when you open it in the WebView, you can see your manga.
There was a problem hiding this comment.
override getMangaUrl and getChapterUrl for webview urls
| override suspend fun getPageList(chapter: SChapter): List<Page> { | ||
| val response = client.get(getChapterUrl(chapter), headersBuilder().set("rsc", "1").build()) | ||
| return response.extractNextJs<ChapterImages>()?.appImages?.mapIndexed { i, img -> | ||
| Page(i, imageUrl = img.mobile ?: img.desktop ?: "") |
There was a problem hiding this comment.
| Page(i, imageUrl = img.mobile ?: img.desktop ?: "") | |
| Page(i, imageUrl = img.mobile ?: img.desktop!!) |
shouldn't be empty
There was a problem hiding this comment.
thanks, i will apply it the change
| } | ||
| override fun getChapterUrl(chapter: SChapter): String { | ||
| val m = chapter.memo | ||
| return "$baseUrl/series/${m["type"]!!.jsonPrimitive.content}/${m["seriesId"]!!.jsonPrimitive.content}/${m["slug"]!!.jsonPrimitive.content}/${chapter.url.removePrefix("/")}/${m["chapterNumber"]!!.jsonPrimitive.content}" |
There was a problem hiding this comment.
| return "$baseUrl/series/${m["type"]!!.jsonPrimitive.content}/${m["seriesId"]!!.jsonPrimitive.content}/${m["slug"]!!.jsonPrimitive.content}/${chapter.url.removePrefix("/")}/${m["chapterNumber"]!!.jsonPrimitive.content}" | |
| return "$baseUrl/series/${m["type"]!!.string}/${m["seriesId"]!!.string}/${m["slug"]!!.string}/${chapter.url.removePrefix("/")}/${m["chapterNumber"]!!.string}" |
use JsonElement helper utils
There was a problem hiding this comment.
thank u for the review , i didn't know that exist i will use it instead of the current method
|
Correction to my earlier reply where I said the site no longer uses scrambled pages. That was incorrect. Some chapters are still served as scrambled pages, which is what caused the "chapters missing images" bug I reported. This has been fixed in the latest commits. I've verified the fix on a real device, and the affected chapters now load every page correctly. I also discovered that some series are hidden behind the site's "Safe Browsing" setting. Guests can't disable this setting, so That said, the site seems to change every couple of weeks. I suspect they're preparing or promoting their official app and are intentionally making the website harder to scrape. At this point, I'm considering marking this source as a draft because it may not be worth constantly chasing their frequent changes and debugging their ever-changing anti-scraping measures. and thank you AwkwardPeak7 so much for giving me advises and being friendly and patient with me |
| ) | ||
| } | ||
|
|
||
| val rscHeaders by lazy { headersBuilder().add("rsc", "1").build() } |
There was a problem hiding this comment.
| val rscHeaders by lazy { headersBuilder().add("rsc", "1").build() } | |
| val rscHeaders get() = headersBuilder().add("rsc", "1").build() |
| val payload = buildJsonObject { put("url", cdnUrl) }.toJsonString() | ||
| .toRequestBody("application/json".toMediaType()) |
There was a problem hiding this comment.
| val payload = buildJsonObject { put("url", cdnUrl) }.toJsonString() | |
| .toRequestBody("application/json".toMediaType()) | |
| val payload = buildJsonObject { put("url", cdnUrl) }.toJsonRequestBody() |
| val response = client.post("$baseUrl/api/cdn-image/sign", rscheaders, payload) | ||
|
|
||
| if (!response.isSuccessful) { | ||
| response.close() |
There was a problem hiding this comment.
client.post ensures http success
perhaps you can sniff the api of the app and use it ;) |
Complete rewrite of the former MangaPro/ProChan extension as a new KeiSource (libVersion = "1.6"). The site and its API changed significantly — the old codebase was no longer salvageable, so this was built from scratch against the current API.
Changes
In relations:
open #14571 ( Source request (AR): ProChan )
Closes #16185 ( remove mangapro / prochan)
Checklist:
versionCodevalue inbuild.gradle.ktsbaseVersionCodeinbuild.gradle.kts(if updated multisrc theme code)contentWarningconfiguration inbuild.gradle.ktsappropriatelyidif a source's name or language were changedweb_hi_res_512.pngwhen adding a new extension