BiliManga: add more filter options (#10166)
* add more filter options * fix pagination
This commit is contained in:
parent
4587ac2c1d
commit
88407d64af
@ -1,7 +1,7 @@
|
||||
ext {
|
||||
extName = 'BiliManga'
|
||||
extClass = '.BiliManga'
|
||||
extVersionCode = 3
|
||||
extVersionCode = 4
|
||||
isNsfw = true
|
||||
}
|
||||
|
||||
|
@ -16,6 +16,7 @@ import keiyoushi.utils.tryParse
|
||||
import okhttp3.HttpUrl.Companion.toHttpUrl
|
||||
import okhttp3.Request
|
||||
import okhttp3.Response
|
||||
import org.jsoup.nodes.Document
|
||||
import org.jsoup.nodes.Element
|
||||
import org.jsoup.select.Elements
|
||||
import java.text.SimpleDateFormat
|
||||
@ -53,13 +54,29 @@ class BiliManga : HttpSource(), ConfigurableSource {
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val PAGE_SIZE = 50
|
||||
val META_REGEX = Regex("連載|完結|收藏|推薦|热度")
|
||||
val DATE_REGEX = Regex("\\d{4}-\\d{1,2}-\\d{1,2}")
|
||||
val PAGE_REGEX = Regex("第(\\d+)/(\\d+)页")
|
||||
val MANGA_ID_REGEX = Regex("/detail/(\\d+)\\.html")
|
||||
val DATE_FORMAT = SimpleDateFormat("yyyy-MM-dd", Locale.CHINESE)
|
||||
}
|
||||
|
||||
private fun hasNextPage(doc: Document, size: Int): Boolean {
|
||||
val url = doc.location()
|
||||
return when {
|
||||
url.contains("filter") -> {
|
||||
val total = doc.selectFirst("#pagelink > .last")!!.text().toInt()
|
||||
val cur = doc.selectFirst("#pagelink > strong")!!.text().toInt()
|
||||
cur < total
|
||||
}
|
||||
url.contains("search") -> {
|
||||
val find = PAGE_REGEX.find(doc.selectFirst("#pagelink > span")!!.text())!!
|
||||
find.groups[1]!!.value.toInt() < find.groups[1]!!.value.toInt()
|
||||
}
|
||||
else -> size == 50
|
||||
}
|
||||
}
|
||||
|
||||
private fun getChapterUrlByContext(i: Int, els: Elements) = when (i) {
|
||||
0 -> "${els[1].attr("href")}#prev"
|
||||
else -> "${els[i - 1].attr("href")}#next"
|
||||
@ -72,8 +89,8 @@ class BiliManga : HttpSource(), ConfigurableSource {
|
||||
return GET(baseUrl + String.format(suffix, page), headers)
|
||||
}
|
||||
|
||||
override fun popularMangaParse(response: Response) = response.asJsoup().let {
|
||||
val mangas = it.select(".book-layout").map {
|
||||
override fun popularMangaParse(response: Response) = response.asJsoup().let { doc ->
|
||||
val mangas = doc.select(".book-layout").map {
|
||||
SManga.create().apply {
|
||||
setUrlWithoutDomain(it.absUrl("href"))
|
||||
val img = it.selectFirst("img")!!
|
||||
@ -81,12 +98,13 @@ class BiliManga : HttpSource(), ConfigurableSource {
|
||||
title = img.attr("alt")
|
||||
}
|
||||
}
|
||||
MangasPage(mangas, mangas.size >= PAGE_SIZE)
|
||||
MangasPage(mangas, hasNextPage(doc, mangas.size))
|
||||
}
|
||||
|
||||
// Latest Page
|
||||
|
||||
override fun latestUpdatesRequest(page: Int) = GET("$baseUrl/top/lastupdate/$page.html", headers)
|
||||
override fun latestUpdatesRequest(page: Int) =
|
||||
GET("$baseUrl/top/lastupdate/$page.html", headers)
|
||||
|
||||
override fun latestUpdatesParse(response: Response) = popularMangaParse(response)
|
||||
|
||||
@ -94,13 +112,14 @@ class BiliManga : HttpSource(), ConfigurableSource {
|
||||
|
||||
override fun getFilterList() = buildFilterList()
|
||||
|
||||
// https://www.bilimanga.net/filter/lastupdate_1_0_0_0_0_0_0_1_0.html
|
||||
override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request {
|
||||
val url = baseUrl.toHttpUrl().newBuilder()
|
||||
if (query.isNotBlank()) {
|
||||
url.addPathSegment("search").addPathSegment("${query}_$page.html")
|
||||
} else {
|
||||
url.addPathSegment("top").addPathSegment(filters[1].toString())
|
||||
.addPathSegment("$page.html")
|
||||
url.addPathSegment("filter")
|
||||
.addPathSegment("${filters[4]}_${filters[1]}_${filters[7]}_${filters[5]}_${filters[3]}_${filters[2]}_${filters[8]}_${filters[6]}_${page}_0.html")
|
||||
}
|
||||
return GET(url.build(), headers)
|
||||
}
|
||||
@ -118,13 +137,11 @@ class BiliManga : HttpSource(), ConfigurableSource {
|
||||
val doc = response.asJsoup()
|
||||
val meta = doc.selectFirst(".book-meta")!!.text().split("|")
|
||||
val extra = meta.filterNot(META_REGEX::containsMatchIn)
|
||||
val backupname = doc.selectFirst(".backupname")?.let {
|
||||
"\n\n漫畫別名:\n• ${it.text().split("、").joinToString("\n• ")}"
|
||||
}
|
||||
val backupname = doc.selectFirst(".backupname")?.let { "【別名:${it.text()}】\n\n" } ?: ""
|
||||
setUrlWithoutDomain(doc.location())
|
||||
title = doc.selectFirst(".book-title")!!.text()
|
||||
thumbnail_url = doc.selectFirst(".book-cover")!!.attr("src")
|
||||
description = doc.selectFirst("#bookSummary")?.text() + backupname
|
||||
description = backupname + doc.selectFirst("#bookSummary > content")?.wholeText()?.trim()
|
||||
artist = doc.selectFirst(".authorname")?.text()
|
||||
author = doc.selectFirst(".illname")?.text() ?: artist
|
||||
status = when (meta.firstOrNull()) {
|
||||
@ -138,7 +155,8 @@ class BiliManga : HttpSource(), ConfigurableSource {
|
||||
|
||||
// Catalog Page
|
||||
|
||||
override fun chapterListRequest(manga: SManga) = GET("$baseUrl/read/${manga.id}/catalog", headers)
|
||||
override fun chapterListRequest(manga: SManga) =
|
||||
GET("$baseUrl/read/${manga.id}/catalog", headers)
|
||||
|
||||
override fun chapterListParse(response: Response) = response.asJsoup().let {
|
||||
val info = it.selectFirst(".chapter-sub-title")!!.text()
|
||||
@ -162,7 +180,7 @@ class BiliManga : HttpSource(), ConfigurableSource {
|
||||
|
||||
override fun pageListParse(response: Response) = response.asJsoup().let {
|
||||
val images = it.select(".imagecontent")
|
||||
check(images.size > 0) {
|
||||
check(images.isNotEmpty()) {
|
||||
it.selectFirst("#acontentz")?.let { e ->
|
||||
if ("電腦端" in e.text()) "不支持電腦端查看,請在高級設置中更換移動端UA標識" else "漫畫可能已下架或需要登錄查看"
|
||||
} ?: "章节鏈接错误"
|
||||
|
@ -4,39 +4,103 @@ import eu.kanade.tachiyomi.source.model.Filter
|
||||
import eu.kanade.tachiyomi.source.model.FilterList
|
||||
|
||||
fun buildFilterList() = FilterList(
|
||||
Filter.Header("篩選條件(搜尋時無效)"),
|
||||
RankFilter(),
|
||||
Filter.Header("篩選條件(搜尋關鍵字時無效)"),
|
||||
ThemeFilter(), // 1
|
||||
TypeFilter(), // 5
|
||||
RegionFilter(), // 4
|
||||
SortFilter(), // 0
|
||||
AnimeFilter(), // 3
|
||||
NovelFilter(), // 7
|
||||
StatusFilter(), // 2
|
||||
TimeFilter(), // 6
|
||||
)
|
||||
|
||||
class RankFilter : Filter.Select<String>(
|
||||
"排行榜",
|
||||
class ThemeFilter : Filter.Select<String>(
|
||||
"作品主題",
|
||||
arrayOf(
|
||||
"月點擊榜",
|
||||
"周點擊榜",
|
||||
"月推薦榜",
|
||||
"周推薦榜",
|
||||
"月鮮花榜",
|
||||
"周鮮花榜",
|
||||
"月雞蛋榜",
|
||||
"周雞蛋榜",
|
||||
"最新入庫",
|
||||
"收藏榜",
|
||||
"新書榜",
|
||||
"不限", "奇幻", "冒險", "異世界", "龍傲天", "魔法",
|
||||
"仙俠", "戰爭", "熱血", "戰鬥", "競技", "懸疑",
|
||||
"驚悚", "獵奇", "神鬼", "偵探", "校園", "日常",
|
||||
"JK", "JC", "青梅竹馬", "妹妹", "大小姐", "女兒",
|
||||
"愛情", "耽美", "百合", "NTR", "後宮", "職場",
|
||||
"經營", "犯罪", "旅行", "群像", "女性視角",
|
||||
"歷史", "武俠", "東方", "勵志", "宅系", "科幻",
|
||||
"機戰", "遊戲", "異能", "腦洞", "病嬌", "人外",
|
||||
"復仇", "鬥智", "惡役", "間諜", "治癒", "歡樂",
|
||||
"萌系", "末日", "大逃殺", "音樂", "美食", "性轉",
|
||||
"偽娘", "穿越", "童話", "轉生", "黑暗", "溫馨",
|
||||
"超自然",
|
||||
),
|
||||
) {
|
||||
override fun toString(): String {
|
||||
return arrayOf(
|
||||
"monthvisit",
|
||||
"weekvisit",
|
||||
"monthvote",
|
||||
"weekvote",
|
||||
"monthflower",
|
||||
"weekflower",
|
||||
"monthegg",
|
||||
"weekegg",
|
||||
"postdate",
|
||||
"goodnum",
|
||||
"newhot",
|
||||
"0", "1", "2", "3", "4", "5", "6", "7", "8",
|
||||
"9", "10", "11", "12", "13", "14", "15", "16",
|
||||
"17", "18", "19", "20", "21", "22", "23", "24",
|
||||
"25", "26", "27", "28", "29", "30", "31", "32",
|
||||
"33", "34", "35", "36", "37", "38", "39", "40",
|
||||
"41", "42", "43", "44", "45", "46", "47", "48",
|
||||
"49", "50", "51", "52", "53", "54", "55", "56",
|
||||
"57", "58", "59", "60", "61", "62", "63", "64",
|
||||
"65",
|
||||
)[state]
|
||||
}
|
||||
}
|
||||
|
||||
class TypeFilter : Filter.Select<String>(
|
||||
"作品分類",
|
||||
arrayOf(
|
||||
"全部",
|
||||
"奇幻冒險", "戰鬥熱血", "懸疑驚悚", "校園青春",
|
||||
"愛情浪漫", "職場都市", "歷史文化", "科幻未來",
|
||||
"奇異幻想", "治癒溫馨", "末日生存", "其他分類",
|
||||
),
|
||||
) {
|
||||
override fun toString(): String {
|
||||
return arrayOf(
|
||||
"0", "1", "2", "3", "4", "5", "6", "7", "8",
|
||||
"9", "10", "11", "12",
|
||||
)[state]
|
||||
}
|
||||
}
|
||||
|
||||
class RegionFilter : Filter.Select<String>(
|
||||
"作品地區",
|
||||
arrayOf("不限", "日本", "韓國", "港台", "歐美", "大陸"),
|
||||
) {
|
||||
override fun toString() = arrayOf("0", "1", "2", "3", "4", "5")[state]
|
||||
}
|
||||
|
||||
class SortFilter : Filter.Select<String>(
|
||||
"排序方式",
|
||||
arrayOf(
|
||||
"最近更新", "月點擊", "周推薦", "月推薦", "周鮮花",
|
||||
"月鮮花", "字數", "收藏數", "周點擊", "最新入庫",
|
||||
),
|
||||
) {
|
||||
override fun toString(): String {
|
||||
return arrayOf(
|
||||
"lastupdate", "monthvisit", "weekvote", "monthvote", "weekflower",
|
||||
"monthflower", "words", "goodnum", "weekvisit", "postdate",
|
||||
)[state]
|
||||
}
|
||||
}
|
||||
|
||||
class AnimeFilter : Filter.Select<String>("是否動畫", arrayOf("不限", "已動畫化", "未動畫化")) {
|
||||
override fun toString() = arrayOf("0", "1", "2")[state]
|
||||
}
|
||||
|
||||
class NovelFilter : Filter.Select<String>("是否輕改", arrayOf("不限", "輕改漫畫", "普通漫畫")) {
|
||||
override fun toString() = arrayOf("0", "1", "2")[state]
|
||||
}
|
||||
|
||||
class StatusFilter : Filter.Select<String>("連載狀態", arrayOf("不限", "連載", "完結")) {
|
||||
override fun toString() = arrayOf("0", "1", "2")[state]
|
||||
}
|
||||
|
||||
class TimeFilter : Filter.Select<String>(
|
||||
"更新時間",
|
||||
arrayOf("不限", "三日內", "七日內", "半月內", "一月內"),
|
||||
) {
|
||||
override fun toString() = arrayOf("0", "1", "2", "3", "4")[state]
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user