MCCMS: add filters, fix popular and latest manga, update URL (#12171)

This commit is contained in:
kasperskier 2022-06-12 22:17:49 +08:00 committed by GitHub
parent cff72ef4ce
commit e0b4fcbce8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 109 additions and 19 deletions

View File

@ -1,8 +1,13 @@
package eu.kanade.tachiyomi.extension.zh.haoman6_glens package eu.kanade.tachiyomi.extension.zh.haoman6_glens
import eu.kanade.tachiyomi.multisrc.mccms.MCCMS import eu.kanade.tachiyomi.multisrc.mccms.MCCMS
import eu.kanade.tachiyomi.network.GET
import okhttp3.Response
class Haoman6_glens : MCCMS("好漫6 (g-lens)", "https://www.g-lens.com") { class Haoman6_glens : MCCMS("好漫6 (g-lens)", "https://www.g-lens.com") {
override fun transformTitle(title: String) = title.removeSuffix("_").removeSuffix("-") override fun transformTitle(title: String) = title.removeSuffix("_").removeSuffix("-")
override val lazyLoadImageAttr = "pc-ec" override val lazyLoadImageAttr = "pc-ec"
override fun latestUpdatesRequest(page: Int) = GET("$baseUrl/category/order/addtime", headers)
override fun latestUpdatesParse(response: Response) = searchMangaParse(response)
} }

View File

@ -2,13 +2,12 @@ package eu.kanade.tachiyomi.extension.zh.haoman8
import eu.kanade.tachiyomi.multisrc.mccms.MCCMS import eu.kanade.tachiyomi.multisrc.mccms.MCCMS
import eu.kanade.tachiyomi.network.GET import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.source.model.FilterList
class Haoman8 : MCCMS("好漫8", "https://caiji.haoman8.com") { class Haoman8 : MCCMS("好漫8", "https://caiji.haoman8.com") {
// Search: 此站点nginx配置有问题只能用以下格式搜索第一页 // Search: 此站点nginx配置有问题只能用以下格式搜索第一页
override fun searchMangaRequest(page: Int, query: String, filters: FilterList) = override fun textSearchRequest(page: Int, query: String) =
GET("$baseUrl/index.php/search?key=$query", headers) GET("$baseUrl/index.php/search?key=$query", headers)
override fun searchMangaNextPageSelector(): String? = null override fun searchMangaNextPageSelector(): String? = null

View File

@ -1,12 +1,13 @@
package eu.kanade.tachiyomi.extension.zh.haomanwu package eu.kanade.tachiyomi.extension.zh.haomanwu
import eu.kanade.tachiyomi.multisrc.mccms.MCCMS import eu.kanade.tachiyomi.multisrc.mccms.MCCMS
import eu.kanade.tachiyomi.source.model.FilterList
import eu.kanade.tachiyomi.source.model.Page import eu.kanade.tachiyomi.source.model.Page
import eu.kanade.tachiyomi.source.model.SManga import eu.kanade.tachiyomi.source.model.SManga
import org.jsoup.nodes.Document import org.jsoup.nodes.Document
import org.jsoup.nodes.Element import org.jsoup.nodes.Element
class Haomanwu : MCCMS("好漫屋", "https://app2.haomanwu.com") { class Haomanwu : MCCMS("好漫屋", "https://app2.haoman6.com") {
// Search // Search
@ -24,4 +25,8 @@ class Haomanwu : MCCMS("好漫屋", "https://app2.haomanwu.com") {
} }
return pages return pages
} }
// 分类页面缺失
override fun fetchCategories() = Unit
override fun getFilterList() = FilterList(emptyList())
} }

View File

@ -1,14 +1,19 @@
package eu.kanade.tachiyomi.multisrc.mccms package eu.kanade.tachiyomi.multisrc.mccms
import android.util.Log
import eu.kanade.tachiyomi.network.GET import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.source.model.Filter
import eu.kanade.tachiyomi.source.model.FilterList import eu.kanade.tachiyomi.source.model.FilterList
import eu.kanade.tachiyomi.source.model.MangasPage
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.source.model.SManga import eu.kanade.tachiyomi.source.model.SManga
import eu.kanade.tachiyomi.source.online.ParsedHttpSource import eu.kanade.tachiyomi.source.online.ParsedHttpSource
import eu.kanade.tachiyomi.util.asJsoup
import okhttp3.Response import okhttp3.Response
import org.jsoup.nodes.Document import org.jsoup.nodes.Document
import org.jsoup.nodes.Element import org.jsoup.nodes.Element
import kotlin.concurrent.thread
/** /**
* 漫城CMS http://mccms.cn/ * 漫城CMS http://mccms.cn/
@ -20,19 +25,15 @@ abstract class MCCMS(
) : ParsedHttpSource() { ) : ParsedHttpSource() {
override val supportsLatest: Boolean = true override val supportsLatest: Boolean = true
protected open val nextPageSelector = "div#Pagination a.next"
protected open val comicItemSelector = "div.common-comic-item"
protected open val comicItemTitleSelector = "p.comic__title > a"
protected open fun transformTitle(title: String) = title protected open fun transformTitle(title: String) = title
// Popular // Popular
override fun popularMangaRequest(page: Int) = GET("$baseUrl/category/order/hits/page/$page", headers) override fun popularMangaRequest(page: Int) = GET("$baseUrl/custom/hot", headers)
override fun popularMangaNextPageSelector() = nextPageSelector override fun popularMangaNextPageSelector(): String? = null
override fun popularMangaSelector() = comicItemSelector override fun popularMangaSelector() = ".top-list__box-item"
override fun popularMangaFromElement(element: Element) = SManga.create().apply { override fun popularMangaFromElement(element: Element) = SManga.create().apply {
val titleElement = element.select(comicItemTitleSelector) val titleElement = element.select("p.comic__title > a")
title = transformTitle(titleElement.text().trim()) title = transformTitle(titleElement.text().trim())
setUrlWithoutDomain(titleElement.attr("abs:href")) setUrlWithoutDomain(titleElement.attr("abs:href"))
thumbnail_url = element.select("img").attr("abs:data-original") thumbnail_url = element.select("img").attr("abs:data-original")
@ -40,20 +41,48 @@ abstract class MCCMS(
// Latest // Latest
override fun latestUpdatesRequest(page: Int) = GET("$baseUrl/category/order/addtime/page/$page", headers) override fun latestUpdatesRequest(page: Int) = GET("$baseUrl/custom/update", headers)
override fun latestUpdatesNextPageSelector() = nextPageSelector override fun latestUpdatesNextPageSelector(): String? = null
override fun latestUpdatesSelector() = comicItemSelector override fun latestUpdatesSelector() = "div.common-comic-item"
override fun latestUpdatesFromElement(element: Element) = popularMangaFromElement(element) override fun latestUpdatesFromElement(element: Element) = popularMangaFromElement(element)
// Search // Search
override fun searchMangaRequest(page: Int, query: String, filters: FilterList) = protected open fun textSearchRequest(page: Int, query: String) =
GET("$baseUrl/search/$query/$page", headers) GET("$baseUrl/search/$query/$page", headers)
override fun searchMangaNextPageSelector(): String? = nextPageSelector override fun searchMangaRequest(page: Int, query: String, filters: FilterList) =
override fun searchMangaSelector() = comicItemSelector if (query.isNotBlank()) {
textSearchRequest(page, query)
} else {
val categories = filters.filterIsInstance<UriPartFilter>()
.map { it.toUriPart() }.filter { it.isNotEmpty() }.joinToString("/")
GET("$baseUrl/category/$categories/page/$page", headers)
}
override fun searchMangaNextPageSelector(): String? = "" // empty string means default pagination
override fun searchMangaSelector() = latestUpdatesSelector()
override fun searchMangaFromElement(element: Element) = popularMangaFromElement(element) override fun searchMangaFromElement(element: Element) = popularMangaFromElement(element)
override fun searchMangaParse(response: Response): MangasPage {
val document = response.asJsoup()
val isTextSearch = document.location().contains("search")
val mangas = if (isTextSearch) {
document.select(searchMangaSelector()).map { searchMangaFromElement(it) }
} else {
document.select(latestUpdatesSelector()).map { popularMangaFromElement(it) }
}
val hasNextPage = if (isTextSearch && searchMangaNextPageSelector() != "") {
searchMangaNextPageSelector()?.let { document.selectFirst(it) } != null
} else { // default pagination
val buttons = document.select("#Pagination a")
val count = buttons.size
// Next page != Last page
buttons[count - 1].attr("href") != buttons[count - 2].attr("href")
}
return MangasPage(mangas, hasNextPage)
}
// Details // Details
override fun mangaDetailsParse(document: Document) = SManga.create().apply { override fun mangaDetailsParse(document: Document) = SManga.create().apply {
@ -83,4 +112,56 @@ abstract class MCCMS(
.mapIndexed { i, el -> Page(i, "", el.attr("abs:$lazyLoadImageAttr")) } .mapIndexed { i, el -> Page(i, "", el.attr("abs:$lazyLoadImageAttr")) }
override fun imageUrlParse(document: Document) = throw UnsupportedOperationException("Not used.") override fun imageUrlParse(document: Document) = throw UnsupportedOperationException("Not used.")
protected class UriPartFilter(name: String, values: Array<String>, private val uriParts: Array<String>) :
Filter.Select<String>(name, values) {
fun toUriPart(): String = uriParts[state]
}
protected data class Category(val name: String, val values: Array<String>, val uriParts: Array<String>) {
fun toUriPartFilter() = UriPartFilter(name, values, uriParts)
}
private val sortCategory = Category("排序", arrayOf("热门人气", "更新时间"), arrayOf("order/hits", "order/addtime"))
private lateinit var categories: List<Category>
private var isFetchingCategories = false
private fun tryFetchCategories() {
if (isFetchingCategories) return
isFetchingCategories = true
thread {
try {
fetchCategories()
} catch (e: Exception) {
Log.e("MCCMS/$name", "Failed to fetch categories ($e)")
} finally {
isFetchingCategories = false
}
}
}
protected open fun fetchCategories() {
val document = client.newCall(GET("$baseUrl/category/", headers)).execute().asJsoup()
categories = document.select("div.cate-col").map { element ->
val name = element.select("p.cate-title").text().removeSuffix("")
val tags = element.select("li.cate-item > a")
val values = tags.map { it.text() }.toTypedArray()
val uriParts = tags.map { it.attr("href").removePrefix("/category/") }.toTypedArray()
Category(name, values, uriParts)
}
}
override fun getFilterList(): FilterList {
val result = mutableListOf(
Filter.Header("如果使用文本搜索,将会忽略分类筛选"),
sortCategory.toUriPartFilter(),
)
if (::categories.isInitialized) {
categories.forEach { result.add(it.toUriPartFilter()) }
} else {
tryFetchCategories()
result.add(Filter.Header("其他分类正在获取,请返回上一页后重试"))
}
return FilterList(result)
}
} }

View File

@ -6,14 +6,14 @@ import generator.ThemeSourceGenerator
class MCCMSGenerator : ThemeSourceGenerator { class MCCMSGenerator : ThemeSourceGenerator {
override val themeClass = "MCCMS" override val themeClass = "MCCMS"
override val themePkg = "mccms" override val themePkg = "mccms"
override val baseVersionCode = 1 override val baseVersionCode = 2
override val sources = listOf( override val sources = listOf(
SingleLang( SingleLang(
name = "Haoman6", baseUrl = "https://www.haoman6.com", lang = "zh", name = "Haoman6", baseUrl = "https://www.haoman6.com", lang = "zh",
className = "Haoman6", sourceName = "好漫6", overrideVersionCode = 2 className = "Haoman6", sourceName = "好漫6", overrideVersionCode = 2
), ),
SingleLang( SingleLang(
name = "Haomanwu", baseUrl = "https://app2.haomanwu.com", lang = "zh", name = "Haomanwu", baseUrl = "https://app2.haoman6.com", lang = "zh",
className = "Haomanwu", sourceName = "好漫屋", overrideVersionCode = 3 className = "Haomanwu", sourceName = "好漫屋", overrideVersionCode = 3
), ),
SingleLang( SingleLang(