Baozimanhua: various cleanup and fixes (#11991)

This commit is contained in:
kasperskier 2022-05-27 20:03:07 +08:00 committed by GitHub
parent e22adc0fc9
commit d1abd5cf30
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 36 additions and 42 deletions

View File

@ -1,3 +1,9 @@
## 1.2.6 (2022-05-27)
- 整理代码
- 切除部分罕见情况上下端都有的 banner
- 修复部分章节较少的漫画解析章节列表为空的问题
## 1.2.5 (2022-05-17) ## 1.2.5 (2022-05-17)
- 提高 banner 识别容错率,切除噪声更多的 banner - 提高 banner 识别容错率,切除噪声更多的 banner

View File

@ -5,7 +5,7 @@ ext {
extName = 'Baozimanhua' extName = 'Baozimanhua'
pkgNameSuffix = 'zh.baozimanhua' pkgNameSuffix = 'zh.baozimanhua'
extClass = '.Baozimanhua' extClass = '.Baozimanhua'
extVersionCode = 5 extVersionCode = 6
} }
apply from: "$rootDir/common.gradle" apply from: "$rootDir/common.gradle"

View File

@ -8,6 +8,7 @@ import okhttp3.MediaType.Companion.toMediaType
import okhttp3.Response import okhttp3.Response
import okhttp3.ResponseBody.Companion.toResponseBody import okhttp3.ResponseBody.Companion.toResponseBody
import java.io.ByteArrayOutputStream import java.io.ByteArrayOutputStream
import java.util.EnumSet
import kotlin.math.abs import kotlin.math.abs
class BannerInterceptor : Interceptor { class BannerInterceptor : Interceptor {
@ -34,17 +35,13 @@ class BannerInterceptor : Interceptor {
val contentType = body.contentType() val contentType = body.contentType()
val content = body.bytes() val content = body.bytes()
val bitmap = BitmapFactory.decodeByteArray(content, 0, content.size) val bitmap = BitmapFactory.decodeByteArray(content, 0, content.size)
val position = checkBanner(bitmap) val positions = checkBanner(bitmap)
return if (position == null) { return if (positions.isEmpty()) {
response.newBuilder().body(content.toResponseBody(contentType)).build() response.newBuilder().body(content.toResponseBody(contentType)).build()
} else { } else {
val result = Bitmap.createBitmap( val result = Bitmap.createBitmap(
bitmap, 0, bitmap, 0, if (positions.contains(BannerPosition.TOP)) h else 0,
when (position) { bitmap.width, bitmap.height - h * positions.size
BannerPosition.TOP -> h
BannerPosition.BOTTOM -> 0
},
bitmap.width, bitmap.height - h
) )
val output = ByteArrayOutputStream() val output = ByteArrayOutputStream()
result.compress(Bitmap.CompressFormat.JPEG, 90, output) result.compress(Bitmap.CompressFormat.JPEG, 90, output)
@ -53,16 +50,17 @@ class BannerInterceptor : Interceptor {
} }
} }
private fun checkBanner(image: Bitmap): BannerPosition? { private fun checkBanner(image: Bitmap): EnumSet<BannerPosition> {
if (image.width < w || image.height < h) return null val result = EnumSet.noneOf(BannerPosition::class.java)
if ((image.width - w) % 2 != 0) return null if (image.width < w || image.height < h) return result
if ((image.width - w) % 2 != 0) return result
val pad = (image.width - w) / 2 val pad = (image.width - w) / 2
val buf = IntArray(size) val buf = IntArray(size)
image.getPixels(buf, 0, w, pad, 0, w, h) // top image.getPixels(buf, 0, w, pad, 0, w, h) // top
if (isIdentical(bannerBuffer, buf)) return BannerPosition.TOP if (isIdentical(bannerBuffer, buf)) result.add(BannerPosition.TOP)
image.getPixels(buf, 0, w, pad, image.height - h, w, h) // bottom image.getPixels(buf, 0, w, pad, image.height - h, w, h) // bottom
if (isIdentical(bannerBuffer, buf)) return BannerPosition.BOTTOM if (isIdentical(bannerBuffer, buf)) result.add(BannerPosition.BOTTOM)
return null return result
} }
private fun isIdentical(a: IntArray, b: IntArray): Boolean { private fun isIdentical(a: IntArray, b: IntArray): Boolean {

View File

@ -40,17 +40,18 @@ class Baozimanhua : ParsedHttpSource(), ConfigurableSource {
override val supportsLatest = true override val supportsLatest = true
override val client: OkHttpClient = network.cloudflareClient.newBuilder() override val client: OkHttpClient = network.cloudflareClient.newBuilder()
.addNetworkInterceptor(BannerInterceptor()).build() .addInterceptor(BannerInterceptor()).build()
override fun chapterListSelector(): String = "div.pure-g[id^=chapter] > div" override fun chapterListSelector() = throw UnsupportedOperationException("Not used.")
override fun chapterListParse(response: Response): List<SChapter> { override fun chapterListParse(response: Response): List<SChapter> {
val document = response.asJsoup() val document = response.asJsoup()
val chapters = document.select(chapterListSelector()).map { element -> return if (document.select(".l-box > .pure-g").size == 1) { // only latest chapters
chapterFromElement(element) document.select(".l-box > .pure-g > div")
} } else {
// chapters are listed oldest to newest in the source // chapters are listed oldest to newest in the source
return chapters.reversed() document.select(".l-box > .pure-g[id^=chapter] > div").reversed()
}.map { chapterFromElement(it) }
} }
override fun chapterFromElement(element: Element): SChapter { override fun chapterFromElement(element: Element): SChapter {
@ -70,15 +71,12 @@ class Baozimanhua : ParsedHttpSource(), ConfigurableSource {
} }
} }
override fun popularMangaNextPageSelector() = throw java.lang.UnsupportedOperationException("Not used.") override fun popularMangaNextPageSelector(): String? = null
override fun popularMangaRequest(page: Int): Request = GET("$baseUrl/classify", headers) override fun popularMangaRequest(page: Int): Request = GET("$baseUrl/classify?page=$page", headers)
override fun popularMangaParse(response: Response): MangasPage { override fun popularMangaParse(response: Response): MangasPage {
val document = response.asJsoup() val mangas = super.popularMangaParse(response).mangas
val mangas = document.select(popularMangaSelector()).map { element ->
popularMangaFromElement(element)
}
return MangasPage(mangas, mangas.size == 36) return MangasPage(mangas, mangas.size == 36)
} }
@ -88,18 +86,10 @@ class Baozimanhua : ParsedHttpSource(), ConfigurableSource {
return popularMangaFromElement(element) return popularMangaFromElement(element)
} }
override fun latestUpdatesNextPageSelector() = throw java.lang.UnsupportedOperationException("Not used.") override fun latestUpdatesNextPageSelector(): String? = null
override fun latestUpdatesRequest(page: Int): Request = GET("$baseUrl/list/new", headers) override fun latestUpdatesRequest(page: Int): Request = GET("$baseUrl/list/new", headers)
override fun latestUpdatesParse(response: Response): MangasPage {
val document = response.asJsoup()
val mangas = document.select(popularMangaSelector()).map { element ->
popularMangaFromElement(element)
}
return MangasPage(mangas, mangas.size == 12)
}
override fun mangaDetailsParse(document: Document): SManga { override fun mangaDetailsParse(document: Document): SManga {
return SManga.create().apply { return SManga.create().apply {
title = document.select("h1.comics-detail__title").text().trim() title = document.select("h1.comics-detail__title").text().trim()
@ -113,23 +103,23 @@ class Baozimanhua : ParsedHttpSource(), ConfigurableSource {
"已完結" -> SManga.COMPLETED "已完結" -> SManga.COMPLETED
else -> SManga.UNKNOWN else -> SManga.UNKNOWN
} }
// TODO: upload date by selector "em" in format "(2021年06月29日 更新)"
} }
} }
override fun pageListParse(document: Document): List<Page> { override fun pageListParse(document: Document): List<Page> {
val pages = document.select(".comic-contain > .chapter-img > img").mapIndexed { index, element -> return document.select(".comic-contain > .chapter-img > img").mapIndexed { index, element ->
Page(index, imageUrl = element.attr("data-src").trim() + COMIC_IMAGE_SUFFIX) Page(index, imageUrl = element.attr("data-src").trim() + COMIC_IMAGE_SUFFIX)
} }
return pages
} }
override fun imageUrlParse(document: Document) = throw UnsupportedOperationException("Not used.") override fun imageUrlParse(document: Document) = throw UnsupportedOperationException("Not used.")
override fun searchMangaSelector() = throw java.lang.UnsupportedOperationException("Not used.") override fun searchMangaSelector() = throw UnsupportedOperationException("Not used.")
override fun searchMangaFromElement(element: Element) = throw java.lang.UnsupportedOperationException("Not used.") override fun searchMangaFromElement(element: Element) = throw UnsupportedOperationException("Not used.")
override fun searchMangaNextPageSelector() = throw java.lang.UnsupportedOperationException("Not used.") override fun searchMangaNextPageSelector() = throw UnsupportedOperationException("Not used.")
override fun fetchSearchManga(page: Int, query: String, filters: FilterList): Observable<MangasPage> { override fun fetchSearchManga(page: Int, query: String, filters: FilterList): Observable<MangasPage> {
return if (query.startsWith(ID_SEARCH_PREFIX)) { return if (query.startsWith(ID_SEARCH_PREFIX)) {