SinMH: upload date logic, fixes, move YKMH source (#12189)

* SinMH: upload date logic and cleanup

* Fix chapter and page list parse

* SinMH: move, rename and fix Manhuadui -> YKMH

* Fetch categories on popular/latest manga parse

* Change SChapter List sorting

* [skip ci] refine filter prompt
This commit is contained in:
kasperskier 2022-06-15 22:10:49 +08:00 committed by GitHub
parent 08fd67eff6
commit d7f162058f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
21 changed files with 130 additions and 277 deletions

View File

@ -0,0 +1,10 @@
package eu.kanade.tachiyomi.extension.zh.gufengmh
import eu.kanade.tachiyomi.multisrc.sinmh.SinMH
class Gufengmh : SinMH("古风漫画网", "https://www.gufengmh9.com") {
override val dateSelector = ".pic_zi:nth-of-type(4) > dd"
override fun chapterListSelector() = ".list li > a"
}

View File

@ -1,23 +1,15 @@
package eu.kanade.tachiyomi.extension.zh.imitui 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.source.model.Page import eu.kanade.tachiyomi.source.model.Page
import eu.kanade.tachiyomi.source.model.SChapter
import eu.kanade.tachiyomi.source.model.SManga
import org.jsoup.nodes.Document import org.jsoup.nodes.Document
class Imitui : SinMH("爱米推漫画", "https://www.imitui.com") { class Imitui : SinMH("爱米推漫画", "https://www.imitui.com") {
private val mobileUrl = "https://m.imitui.com"
override fun chapterListRequest(manga: SManga) = GET(mobileUrl + manga.url, headers)
override fun pageListRequest(chapter: SChapter) = GET(mobileUrl + chapter.url, headers)
override fun pageListParse(document: Document): List<Page> { override fun pageListParse(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()
val prefix = document.location().removeSuffix(".html") val prefix = document.location().removeSuffix(".html")
return (0 until pageCount).map { Page(it, "$prefix-${it + 1}.html") } return (0 until pageCount).map { Page(it, url = "$prefix-${it + 1}.html") }
} }
override fun imageUrlParse(document: Document): String = override fun imageUrlParse(document: Document): String =

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 100 KiB

View File

@ -0,0 +1,31 @@
package eu.kanade.tachiyomi.extension.zh.manhuadui
import eu.kanade.tachiyomi.multisrc.sinmh.SinMH
import eu.kanade.tachiyomi.source.model.SChapter
import eu.kanade.tachiyomi.source.model.SManga
import org.jsoup.nodes.Document
class YKMH : SinMH("优酷漫画", "http://www.ykmh.com") {
override val id = 1637952806167036168
override val mobileUrl = "http://wap.ykmh.com"
override val comicItemSelector = "li.list-comic"
override val comicItemTitleSelector = "h3 > a, p > a"
// DMZJ style
override fun mangaDetailsParse(document: Document) = SManga.create().apply {
title = document.selectFirst("h1").text()
val details = document.selectFirst("ul.comic_deCon_liO").children()
author = details[0].selectFirst("a").text()
status = when (details[1].selectFirst("a").text()) {
"连载中" -> SManga.ONGOING
"已完结" -> SManga.COMPLETED
else -> SManga.UNKNOWN
}
genre = (details[2].select("a") + details[3].select("a")).joinToString(", ") { it.text() }
description = document.selectFirst("p.comic_deCon_d").text()
thumbnail_url = document.selectFirst("div.comic_i_img > img").attr("src")
}
override fun List<SChapter>.sortedDescending() = this
}

View File

@ -1,9 +1,10 @@
package eu.kanade.tachiyomi.multisrc.sinmh package eu.kanade.tachiyomi.multisrc.sinmh
import android.util.Log import eu.kanade.tachiyomi.AppInfo
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.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
@ -13,7 +14,8 @@ import okhttp3.Headers
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 import java.text.SimpleDateFormat
import java.util.Locale
/** /**
* 圣樱漫画CMS https://gitee.com/shenl/SinMH-2.0-Guide * 圣樱漫画CMS https://gitee.com/shenl/SinMH-2.0-Guide
@ -26,6 +28,7 @@ abstract class SinMH(
override val lang: String = "zh", override val lang: String = "zh",
) : ParsedHttpSource() { ) : ParsedHttpSource() {
override val baseUrl = _baseUrl override val baseUrl = _baseUrl
protected open val mobileUrl = _baseUrl.replace("www", "m")
override val supportsLatest = true override val supportsLatest = true
override fun headersBuilder(): Headers.Builder = Headers.Builder() override fun headersBuilder(): Headers.Builder = Headers.Builder()
@ -34,13 +37,12 @@ abstract class SinMH(
protected open val nextPageSelector = "ul.pagination > li.next:not(.disabled)" protected open val nextPageSelector = "ul.pagination > li.next:not(.disabled)"
protected open val comicItemSelector = "#contList > li" protected open val comicItemSelector = "#contList > li"
protected open val comicItemTitleSelector = "p > a" protected open val comicItemTitleSelector = "p > a"
protected open fun mangaFromElement(element: Element) = protected open fun mangaFromElement(element: Element) = SManga.create().apply {
SManga.create().apply { val titleElement = element.selectFirst(comicItemTitleSelector)
val titleElement = element.select(comicItemTitleSelector) title = titleElement.text()
title = titleElement.text() setUrlWithoutDomain(titleElement.attr("abs:href"))
setUrlWithoutDomain(titleElement.attr("abs:href")) thumbnail_url = element.selectFirst("img").attr("abs:src")
thumbnail_url = element.select("img").attr("abs:src") }
}
// Popular // Popular
@ -49,6 +51,14 @@ abstract class SinMH(
override fun popularMangaSelector() = comicItemSelector override fun popularMangaSelector() = comicItemSelector
override fun popularMangaFromElement(element: Element) = mangaFromElement(element) override fun popularMangaFromElement(element: Element) = mangaFromElement(element)
override fun popularMangaParse(response: Response): MangasPage {
val document = response.asJsoup()
parseCategories(document)
val mangas = document.select(popularMangaSelector()).map(::popularMangaFromElement)
val hasNextPage = popularMangaNextPageSelector()?.let { document.selectFirst(it) } != null
return MangasPage(mangas, hasNextPage)
}
// Latest // Latest
override fun latestUpdatesRequest(page: Int) = GET("$baseUrl/list/update/?page=$page", headers) override fun latestUpdatesRequest(page: Int) = GET("$baseUrl/list/update/?page=$page", headers)
@ -56,6 +66,14 @@ abstract class SinMH(
override fun latestUpdatesSelector() = comicItemSelector override fun latestUpdatesSelector() = comicItemSelector
override fun latestUpdatesFromElement(element: Element) = mangaFromElement(element) override fun latestUpdatesFromElement(element: Element) = mangaFromElement(element)
override fun latestUpdatesParse(response: Response): MangasPage {
val document = response.asJsoup()
parseCategories(document)
val mangas = document.select(latestUpdatesSelector()).map(::latestUpdatesFromElement)
val hasNextPage = latestUpdatesNextPageSelector()?.let { document.selectFirst(it) } != null
return MangasPage(mangas, hasNextPage)
}
// Search // Search
override fun searchMangaNextPageSelector(): String? = nextPageSelector override fun searchMangaNextPageSelector(): String? = nextPageSelector
@ -65,34 +83,48 @@ abstract class SinMH(
if (query.isNotBlank()) { if (query.isNotBlank()) {
GET("$baseUrl/search/?keywords=$query&page=$page", headers) GET("$baseUrl/search/?keywords=$query&page=$page", headers)
} else { } else {
val categories = filters.filterIsInstance<UriPartFilter>() val categories = filters.filterIsInstance<UriPartFilter>().map { it.toUriPart() }
.joinToString("-", transform = UriPartFilter::toUriPart) + "-" .filter { it.isNotEmpty() }.joinToString("-") + "-"
GET("$baseUrl/list/$categories/", headers) GET("$baseUrl/list/$categories/", headers)
} }
// Details // Details
override fun mangaDetailsParse(document: Document) = SManga.create().apply { override fun mangaDetailsParse(document: Document) = SManga.create().apply {
title = document.select(".book-title > h1 > span").text() title = document.selectFirst(".book-title > h1 > span").text()
author = document.select(".detail-list strong:contains(作者) + a").text() author = document.selectFirst(".detail-list strong:contains(作者) + a").text()
description = document.select("#intro-all").text().trim() description = document.selectFirst("#intro-all").text().trim()
.removePrefix("漫画简介:").trim() .removePrefix("漫画简介:").trim()
.removePrefix("漫画简介:").trim() // some sources have double prefix .removePrefix("漫画简介:").trim() // some sources have double prefix
genre = document.select(".detail-list strong:contains(类型) + a").text() + ", " + genre = document.selectFirst(".detail-list strong:contains(类型) + a").text() + ", " +
document.select(".breadcrumb-bar a[href*=/list/]").joinToString(", ") { it.text() } document.select(".breadcrumb-bar a[href*=/list/]").joinToString(", ") { it.text() }
status = when (document.select(".detail-list strong:contains(状态) + a").text()) { status = when (document.selectFirst(".detail-list strong:contains(状态) + a").text()) {
"连载中" -> SManga.ONGOING "连载中" -> SManga.ONGOING
"已完结" -> SManga.COMPLETED "已完结" -> SManga.COMPLETED
else -> SManga.UNKNOWN else -> SManga.UNKNOWN
} }
thumbnail_url = document.select("p.cover > img").attr("abs:src") thumbnail_url = document.selectFirst("p.cover > img").attr("abs:src")
// TODO: can use 更新时间2022-05-23 to set default upload date
} }
// Chapters // Chapters
override fun chapterListSelector() = ".chapter-body li > a:not([href*=/comic/app/])" override fun chapterListRequest(manga: SManga) = GET(mobileUrl + manga.url, headers)
override fun chapterListParse(response: Response) = super.chapterListParse(response).reversed()
protected open val dateSelector = ".date"
protected open fun List<SChapter>.sortedDescending() = this.asReversed()
override fun chapterListParse(response: Response): List<SChapter> {
val document = response.asJsoup()
return document.select(chapterListSelector()).map { chapterFromElement(it) }.sortedDescending().apply {
if (isNewDateLogic) {
val date = document.selectFirst(dateSelector).textNodes().last().text()
this[0].date_upload = DATE_FORMAT.parse(date)?.time ?: 0L
}
}
}
override fun chapterListSelector() = ".chapter-body li > a:not([href^=/comic/app/])"
override fun chapterFromElement(element: Element) = SChapter.create().apply { override fun chapterFromElement(element: Element) = SChapter.create().apply {
setUrlWithoutDomain(element.attr("abs:href")) setUrlWithoutDomain(element.attr("abs:href"))
name = element.text() name = element.text()
@ -100,13 +132,23 @@ abstract class SinMH(
// Pages // Pages
override fun pageListRequest(chapter: SChapter) = GET(mobileUrl + chapter.url, headers)
override fun pageListParse(document: Document): List<Page> { override fun pageListParse(document: Document): List<Page> {
val script = document.select("script:containsData(chapterImages)").html() val script = document.selectFirst("body > script").html()
val images = script.substringAfter("chapterImages = [\"").substringBefore("\"]").split("\",\"") val images = script.substringAfter("chapterImages = [\"").substringBefore("\"]").split("\",\"")
val path = script.substringAfter("chapterPath = \"").substringBefore("\";") val path = script.substringAfter("chapterPath = \"").substringBefore("\";")
// assume cover images are on the same server // assume cover images are on the page image server
val server = script.substringAfter("pageImage = \"").substringBefore("/images/cover") val server = script.substringAfter("pageImage = \"").substringBefore("/images/cover")
return images.mapIndexed { i, image -> Page(i, "", "$server/$path/$image") } return images.mapIndexed { i, image ->
val unescapedImage = image.replace("""\/""", "/")
val imageUrl = if (unescapedImage.startsWith("/")) {
"$server$unescapedImage"
} else {
"$server/$path$unescapedImage"
}
Page(i, imageUrl = imageUrl)
}
} }
override fun imageUrlParse(document: Document): String = throw UnsupportedOperationException("Not Used.") override fun imageUrlParse(document: Document): String = throw UnsupportedOperationException("Not Used.")
@ -121,26 +163,11 @@ abstract class SinMH(
} }
private lateinit var categories: List<Category> private lateinit var categories: List<Category>
private var isFetchingCategories = false
private fun tryFetchCategories() { protected open fun parseCategories(document: Document) {
if (isFetchingCategories) return if (::categories.isInitialized) return
isFetchingCategories = true categories = document.selectFirst(".filter-nav").children().map { element ->
thread { val name = element.selectFirst("label").text()
try {
fetchCategories()
} catch (e: Exception) {
Log.e("SinMH", "Failed to fetch categories ($e)")
} finally {
isFetchingCategories = false
}
}
}
protected open fun fetchCategories() {
val document = client.newCall(GET("$baseUrl/list/", headers)).execute().asJsoup()
categories = document.select(".page-main .filter-nav > .filter-item").map { element ->
val name = element.select("label").text()
val tags = element.select("a") val tags = element.select("a")
val values = tags.map { it.text() }.toTypedArray() val values = tags.map { it.text() }.toTypedArray()
val uriParts = tags.map { it.attr("href").removePrefix("/list/").removeSuffix("/") }.toTypedArray() val uriParts = tags.map { it.attr("href").removePrefix("/list/").removeSuffix("/") }.toTypedArray()
@ -152,10 +179,17 @@ abstract class SinMH(
if (::categories.isInitialized) FilterList( if (::categories.isInitialized) FilterList(
Filter.Header("如果使用文本搜索,将会忽略分类筛选"), Filter.Header("如果使用文本搜索,将会忽略分类筛选"),
*categories.map(Category::toUriPartFilter).toTypedArray() *categories.map(Category::toUriPartFilter).toTypedArray()
) else { ) else FilterList(
tryFetchCategories() Filter.Header("点击“重置”即可刷新分类,如果失败,"),
FilterList( Filter.Header("请尝试重新从图源列表点击进入图源"),
Filter.Header("分类尚未获取,请返回上一页后重试") )
)
} private val DATE_FORMAT = SimpleDateFormat("yyyy-MM-dd", Locale.ENGLISH)
private val isNewDateLogic = run {
val commitCount = AppInfo.getVersionName().substringAfter('-', "")
if (commitCount.isNotEmpty()) // Preview
commitCount.toInt() >= 4442
else // Stable
AppInfo.getVersionCode() >= 81
}
} }

View File

@ -6,7 +6,7 @@ import generator.ThemeSourceGenerator
class SinMHGenerator : ThemeSourceGenerator { class SinMHGenerator : ThemeSourceGenerator {
override val themeClass = "SinMH" override val themeClass = "SinMH"
override val themePkg = "sinmh" override val themePkg = "sinmh"
override val baseVersionCode = 3 override val baseVersionCode = 4
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",
@ -15,7 +15,11 @@ class SinMHGenerator : ThemeSourceGenerator {
SingleLang( SingleLang(
name = "Imitui Manhua", baseUrl = "https://www.imitui.com", lang = "zh", name = "Imitui Manhua", baseUrl = "https://www.imitui.com", lang = "zh",
className = "Imitui", sourceName = "爱米推漫画", overrideVersionCode = 2 className = "Imitui", sourceName = "爱米推漫画", overrideVersionCode = 2
) ),
SingleLang(
name = "YKMH", baseUrl = "http://www.ykmh.com", lang = "zh", className = "YKMH",
pkgName = "manhuadui", sourceName = "优酷漫画", overrideVersionCode = 17
),
) )
companion object { companion object {

View File

@ -1,2 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest package="eu.kanade.tachiyomi.extension" />

View File

@ -1,11 +0,0 @@
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
ext {
extName = 'Manhuadui'
pkgNameSuffix = 'zh.manhuadui'
extClass = '.Manhuadui'
extVersionCode = 17
}
apply from: "$rootDir/common.gradle"

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 56 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 460 KiB

View File

@ -1,205 +0,0 @@
package eu.kanade.tachiyomi.extension.zh.manhuadui
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.Page
import eu.kanade.tachiyomi.source.model.SChapter
import eu.kanade.tachiyomi.source.model.SManga
import eu.kanade.tachiyomi.source.online.ParsedHttpSource
import okhttp3.HttpUrl.Companion.toHttpUrlOrNull
import okhttp3.OkHttpClient
import okhttp3.Request
import okhttp3.Response
import org.jsoup.nodes.Document
import org.jsoup.nodes.Element
class Manhuadui : ParsedHttpSource() {
override val name = "漫画堆"
override val baseUrl = "https://ykmh.com"
override val lang = "zh"
override val supportsLatest = true
// Servers can be found from resHost in baseUrl/js/config.js
private val imageServer = arrayOf("https://js.tingliu.cc")
override val client: OkHttpClient = super.client.newBuilder()
.followRedirects(true)
.build()
override fun popularMangaSelector() = "li.list-comic"
override fun searchMangaSelector() = popularMangaSelector()
override fun latestUpdatesSelector() = popularMangaSelector()
override fun chapterListSelector() = "ul[id^=chapter-list] > li a"
override fun searchMangaNextPageSelector() = "li.next a"
override fun popularMangaNextPageSelector() = searchMangaNextPageSelector()
override fun latestUpdatesNextPageSelector() = searchMangaNextPageSelector()
override fun popularMangaRequest(page: Int) = GET("$baseUrl/list_$page/", headers)
override fun latestUpdatesRequest(page: Int) = GET("$baseUrl/update/$page/", headers)
override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request {
return if (query != "") {
val url = "$baseUrl/search/?keywords=$query&page=$page".toHttpUrlOrNull()?.newBuilder()
GET(url.toString(), headers)
} else {
val params = filters.map {
if (it is UriPartFilter) {
it.toUriPart()
} else ""
}.filter { it != "" }.joinToString("-")
val url = when {
params.isEmpty() -> "$baseUrl/list_$page/".toHttpUrlOrNull()?.newBuilder()
else -> "$baseUrl/list/$params/$page/".toHttpUrlOrNull()?.newBuilder()
}
GET(url.toString(), headers)
}
}
override fun popularMangaFromElement(element: Element) = mangaFromElement(element)
override fun latestUpdatesFromElement(element: Element) = mangaFromElement(element)
private fun mangaFromElement(element: Element): SManga {
val manga = SManga.create()
element.select("a.comic_img").first().let {
manga.setUrlWithoutDomain(it.attr("href"))
manga.title = it.select("img").attr("alt").trim()
manga.thumbnail_url = if (it.select("img").attr("src").trim().indexOf("http") == -1)
"https:${it.select("img").attr("src").trim()}"
else it.select("img").attr("src").trim()
}
manga.author = element.select("span.comic_list_det > p").first()?.text()?.substring(3)
return manga
}
override fun searchMangaFromElement(element: Element): SManga {
val manga = SManga.create()
val els = element.select("a.image-link")
if (els.size == 0) {
element.select("li.list-comic").first().let {
manga.setUrlWithoutDomain(it.select("a").attr("href"))
manga.title = it.select("span").attr("title").trim()
manga.thumbnail_url = it.select("a > img").attr("src").trim()
manga.author = it.select("span > p").first().text().split("")[1].trim()
}
} else {
element.select("a.image-link").first().let {
manga.setUrlWithoutDomain(it.attr("href"))
manga.title = it.attr("title").trim()
manga.thumbnail_url = it.select("img").attr("src").trim()
}
manga.author = element.select("p.auth").text().trim()
}
return manga
}
override fun chapterFromElement(element: Element): SChapter {
val chapter = SChapter.create()
chapter.setUrlWithoutDomain(element.attr("href"))
chapter.name = element.select("span:nth-child(2)").text().trim()
return chapter
}
override fun mangaDetailsParse(document: Document): SManga {
val manga = SManga.create()
manga.description = document.select("p.comic_deCon_d").text().trim()
manga.thumbnail_url = document.select("div.comic_i_img > img").attr("src")
manga.status = when (document.select("ul.comic_deCon_liO a")[1].attr("href")) {
"/list/lianzai/" -> SManga.ONGOING
"/list/wanjie/" -> SManga.COMPLETED
else -> SManga.UNKNOWN
}
manga.genre = document.select("ul.comic_deCon_liO li:nth-child(4) a").eachText().joinToString(", ")
return manga
}
override fun chapterListRequest(manga: SManga) = GET(baseUrl.replace("www", "m") + manga.url)
override fun chapterListParse(response: Response): List<SChapter> {
return super.chapterListParse(response).asReversed()
}
private val chapterImagesRegex = Regex("""var chapterImages =\s*\["(.*?)"\];""")
private val imgCodeCleanupRegex = Regex("""[\[\]"\\]""")
override fun pageListParse(document: Document): List<Page> {
val html = document.html()
val imgCodeStr = chapterImagesRegex.find(html)?.groups?.get(1)?.value ?: throw Exception("imgCodeStr not found")
val imgCode = imgCodeStr
.replace(imgCodeCleanupRegex, "")
.replace("%", "%25")
return imgCode.split(",").mapIndexed { i, imgStr ->
// The way image urls are processed by the website can be found in
// getChapterImage() in baseUrl/js/common.js
if (imgStr.startsWith("http://images.dmzj.com")) {
Page(i, "", "${imageServer[0]}/showImage.php?url=$imgStr")
} else {
Page(i, "", if (imgStr.indexOf("http") == -1) "${imageServer[0]}$imgStr" else imgStr)
}
}
}
override fun imageUrlParse(document: Document) = throw UnsupportedOperationException("Not used")
override fun getFilterList() = FilterList(
CategoryGroup(),
RegionGroup(),
GenreGroup(),
ProgressGroup()
)
private class CategoryGroup : UriPartFilter(
"按类型",
arrayOf(
Pair("全部", ""),
Pair("儿童漫画", "ertong"),
Pair("少年漫画", "shaonian"),
Pair("少女漫画", "shaonv"),
Pair("青年漫画", "qingnian")
)
)
private class ProgressGroup : UriPartFilter(
"按进度",
arrayOf(
Pair("全部", ""),
Pair("已完结", "wanjie"),
Pair("连载中", "lianzai")
)
)
private class RegionGroup : UriPartFilter(
"按地区",
arrayOf(
Pair("全部", ""),
Pair("日本", "riben"),
Pair("大陆", "dalu"),
Pair("香港", "hongkong"),
Pair("台湾", "taiwan"),
Pair("欧美", "oumei"),
Pair("韩国", "hanguo"),
Pair("其他", "qita")
)
)
private class GenreGroup : UriPartFilter(
"按剧情",
arrayOf(
Pair("全部", ""),
Pair("热血", "rexue"),
Pair("冒险", "maoxian"),
Pair("玄幻", "xuanhuan"),
Pair("搞笑", "gaoxiao"),
Pair("恋爱", "lianai"),
Pair("宠物", "chongwu"),
Pair("新作", "xinzuo")
)
)
private open class UriPartFilter(
displayName: String,
val vals: Array<Pair<String, String>>,
defaultValue: Int = 0
) :
Filter.Select<String>(displayName, vals.map { it.first }.toTypedArray(), defaultValue) {
open fun toUriPart() = vals[state].second
}
}