Gufengmh: prefer PC chapter list page and SinMH cleanups (#12290)

This commit is contained in:
stevenyomi 2022-06-24 17:15:54 +08:00 committed by GitHub
parent afb387df25
commit 7de9719d0a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 67 additions and 23 deletions

View File

@ -1,10 +1,48 @@
package eu.kanade.tachiyomi.extension.zh.gufengmh package eu.kanade.tachiyomi.extension.zh.gufengmh
import eu.kanade.tachiyomi.multisrc.sinmh.SinMH import eu.kanade.tachiyomi.multisrc.sinmh.SinMH
import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.source.model.SChapter
import eu.kanade.tachiyomi.source.model.SManga
import org.jsoup.nodes.Document
import rx.Observable
import rx.Single
class Gufengmh : SinMH("古风漫画网", "https://www.gufengmh9.com") { class Gufengmh : SinMH("古风漫画网", "https://www.gufengmh9.com") {
override val dateSelector = ".pic_zi:nth-of-type(4) > dd" override fun mangaDetailsParse(document: Document): SManga =
super.mangaDetailsParse(document).apply {
if (status == SManga.COMPLETED) return@apply
val firstChapter = document.selectFirst(".chapter-body li > a") ?: return@apply
if (firstChapter.attr("href").startsWith("javascript"))
status = SManga.LICENSED
}
override fun chapterListSelector() = ".list li > a" override fun fetchChapterList(manga: SManga): Observable<List<SChapter>> =
Single.create<List<SChapter>> { subscriber ->
val pcResponse = client.newCall(GET(baseUrl + manga.url, headers)).execute()
val pcResult = chapterListParse(pcResponse, ".chapter-body li > a", "span.sj")
if (pcResult.none { it.url.isEmpty() }) return@create subscriber.onSuccess(pcResult)
// Example: https://www.gufengmh9.com/manhua/niaoling/
val mobileResponse = client.newCall(GET(mobileUrl + manga.url, headers)).execute()
val mobileResult = chapterListParse(mobileResponse, ".list li > a", ".pic_zi:nth-of-type(4) > dd")
val pcAscending = pcResult.asReversed()
val mobileAscending = mobileResult.asReversed()
for ((pcChapter, mobileChapter) in pcAscending zip mobileAscending) {
if (pcChapter.name != mobileChapter.name) return@create subscriber.onSuccess(mobileResult)
pcChapter.url = mobileChapter.url
}
pcAscending.forEachIndexed { i, chapter ->
if (chapter.url.isNotEmpty()) return@forEachIndexed
if (i == 0) return@create subscriber.onSuccess(mobileResult)
val prevUrl = pcAscending[i - 1].url
val response = client.newCall(GET(baseUrl + prevUrl, headers)).execute()
chapter.url = buildString {
append(prevUrl, 0, prevUrl.lastIndexOf('/') + 1)
append(ProgressiveParser(response.body!!.string()).substringBetween("""nextChapterData = {"id":""", ","))
append(".html")
}
}
subscriber.onSuccess(pcResult)
}.toObservable()
} }

View File

@ -2,22 +2,25 @@ package eu.kanade.tachiyomi.extension.zh.imitui
import eu.kanade.tachiyomi.multisrc.sinmh.SinMH import eu.kanade.tachiyomi.multisrc.sinmh.SinMH
import eu.kanade.tachiyomi.network.GET import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.network.asObservableSuccess
import eu.kanade.tachiyomi.source.model.Page import eu.kanade.tachiyomi.source.model.Page
import eu.kanade.tachiyomi.source.model.SChapter import eu.kanade.tachiyomi.source.model.SChapter
import eu.kanade.tachiyomi.util.asJsoup import eu.kanade.tachiyomi.util.asJsoup
import org.jsoup.nodes.Document import org.jsoup.nodes.Document
import rx.Observable import rx.Observable
import rx.Single
class Imitui : SinMH("爱米推漫画", "https://www.imitui.com") { class Imitui : SinMH("爱米推漫画", "https://www.imitui.com") {
override fun chapterListSelector() = ".chapter-body li > a:not([href^=/comic/app/])"
override fun fetchPageList(chapter: SChapter): Observable<List<Page>> = override fun fetchPageList(chapter: SChapter): Observable<List<Page>> =
client.newCall(GET(baseUrl + chapter.url, headers)).asObservableSuccess().map { Single.create<List<Page>> {
val pcResult = pageListParse(it) val pcResponse = client.newCall(GET(baseUrl + chapter.url, headers)).execute()
if (pcResult.isNotEmpty()) return@map pcResult val pcResult = pageListParse(pcResponse.asJsoup())
val response = client.newCall(GET(mobileUrl + chapter.url, headers)).execute() if (pcResult.isNotEmpty()) return@create it.onSuccess(pcResult)
mobilePageListParse(response.asJsoup()) val mobileResponse = client.newCall(GET(mobileUrl + chapter.url, headers)).execute()
} it.onSuccess(mobilePageListParse(mobileResponse.asJsoup()))
}.toObservable()
private fun mobilePageListParse(document: Document): List<Page> { private fun mobilePageListParse(document: Document): List<Page> {
val pageCount = document.select("div.image-content > p").text().removePrefix("1/").toInt() val pageCount = document.select("div.image-content > p").text().removePrefix("1/").toInt()

View File

@ -52,7 +52,7 @@ class WuqiManga : SinMH("57漫画", "http://www.wuqimh.net") {
override val dateSelector = ".cont-list dt:contains(更新于) + dd" override val dateSelector = ".cont-list dt:contains(更新于) + dd"
override val imageHost: String by lazy { override val imageHost: String by lazy {
client.newCall(GET("$baseUrl/templates/wuqi/default/scripts/configs.js", headers)).execute().use { client.newCall(GET("$baseUrl/templates/wuqi/default/scripts/configs.js", headers)).execute().let {
Regex("""\['(.+?)']""").find(it.body!!.string())!!.groupValues[1].run { "http://$this" } Regex("""\['(.+?)']""").find(it.body!!.string())!!.groupValues[1].run { "http://$this" }
} }
} }
@ -73,15 +73,14 @@ class WuqiManga : SinMH("57漫画", "http://www.wuqimh.net") {
return@replace if (key < size) dictionary[key] else this return@replace if (key < size) dictionary[key] else this
} }
}.removeSurrounding("'").split("','") }.removeSurrounding("'").split("','")
return unpacked.filterNot { it.endsWith("/ManHuaKu/222.jpg") }.mapIndexed { i, image -> val list = unpacked.filterNot { it.endsWith("/ManHuaKu/222.jpg") }.map { image ->
val imageUrl = if (image.startsWith("http")) image else imageHost + image if (image.startsWith("http")) image else imageHost + image
Page(i, imageUrl = imageUrl)
}.also { list ->
if (list.isEmpty()) return emptyList()
client.newCall(GET(list[0].imageUrl!!, headers)).execute().use {
if (!it.isSuccessful) throw Exception("该章节的图片加载出错:${it.code}")
}
} }
if (list.isEmpty()) return emptyList()
client.newCall(GET(list[0], headers)).execute().apply { close() }.also {
if (!it.isSuccessful) throw Exception("该章节的图片加载出错:${it.code}")
}
return list.mapIndexed { i, imageUrl -> Page(i, imageUrl = imageUrl) }
} }
override fun parseCategories(document: Document) { override fun parseCategories(document: Document) {

View File

@ -151,8 +151,12 @@ abstract class SinMH(
protected open fun List<SChapter>.sortedDescending() = this.asReversed() protected open fun List<SChapter>.sortedDescending() = this.asReversed()
override fun chapterListParse(response: Response): List<SChapter> { override fun chapterListParse(response: Response): List<SChapter> {
return chapterListParse(response, chapterListSelector(), dateSelector)
}
protected fun chapterListParse(response: Response, listSelector: String, dateSelector: String): List<SChapter> {
val document = response.asJsoup() val document = response.asJsoup()
return document.select(chapterListSelector()).map { chapterFromElement(it) }.sortedDescending().apply { return document.select(listSelector).map { chapterFromElement(it) }.sortedDescending().apply {
if (isNewDateLogic && this.isNotEmpty()) { if (isNewDateLogic && this.isNotEmpty()) {
val date = document.selectFirst(dateSelector).textNodes().last().text() val date = document.selectFirst(dateSelector).textNodes().last().text()
this[0].date_upload = DATE_FORMAT.parse(date)?.time ?: 0L this[0].date_upload = DATE_FORMAT.parse(date)?.time ?: 0L
@ -160,9 +164,9 @@ abstract class SinMH(
} }
} }
override fun chapterListSelector() = ".chapter-body li > a:not([href^=/comic/app/])" override fun chapterListSelector() = ".chapter-body li > a"
override fun chapterFromElement(element: Element) = SChapter.create().apply { override fun chapterFromElement(element: Element) = SChapter.create().apply {
setUrlWithoutDomain(element.attr("href")) runCatching { setUrlWithoutDomain(element.attr("href")) }.onFailure { url = "" }
val children = element.children() val children = element.children()
name = if (children.isEmpty()) element.text() else children[0].text() name = if (children.isEmpty()) element.text() else children[0].text()
} }
@ -172,7 +176,7 @@ abstract class SinMH(
override fun pageListRequest(chapter: SChapter) = GET(mobileUrl + chapter.url, headers) override fun pageListRequest(chapter: SChapter) = GET(mobileUrl + chapter.url, headers)
protected open val imageHost: String by lazy { protected open val imageHost: String by lazy {
client.newCall(GET("$baseUrl/js/config.js", headers)).execute().use { client.newCall(GET("$baseUrl/js/config.js", headers)).execute().let {
Regex("""resHost:.+?"domain":\["(.+?)"""").find(it.body!!.string())!! Regex("""resHost:.+?"domain":\["(.+?)"""").find(it.body!!.string())!!
.groupValues[1].substringAfter(':').run { "https:$this" } .groupValues[1].substringAfter(':').run { "https:$this" }
} }

View File

@ -10,7 +10,7 @@ class SinMHGenerator : ThemeSourceGenerator {
override val sources = listOf( override val sources = listOf(
SingleLang( SingleLang(
name = "Gufeng Manhua", baseUrl = "https://www.gufengmh9.com", lang = "zh", name = "Gufeng Manhua", baseUrl = "https://www.gufengmh9.com", lang = "zh",
className = "Gufengmh", sourceName = "古风漫画网", overrideVersionCode = 5 className = "Gufengmh", sourceName = "古风漫画网", overrideVersionCode = 6
), ),
SingleLang( SingleLang(
name = "Imitui Manhua", baseUrl = "https://www.imitui.com", lang = "zh", name = "Imitui Manhua", baseUrl = "https://www.imitui.com", lang = "zh",