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 {
|
ext {
|
||||||
extName = 'BiliManga'
|
extName = 'BiliManga'
|
||||||
extClass = '.BiliManga'
|
extClass = '.BiliManga'
|
||||||
extVersionCode = 3
|
extVersionCode = 4
|
||||||
isNsfw = true
|
isNsfw = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -16,6 +16,7 @@ import keiyoushi.utils.tryParse
|
|||||||
import okhttp3.HttpUrl.Companion.toHttpUrl
|
import okhttp3.HttpUrl.Companion.toHttpUrl
|
||||||
import okhttp3.Request
|
import okhttp3.Request
|
||||||
import okhttp3.Response
|
import okhttp3.Response
|
||||||
|
import org.jsoup.nodes.Document
|
||||||
import org.jsoup.nodes.Element
|
import org.jsoup.nodes.Element
|
||||||
import org.jsoup.select.Elements
|
import org.jsoup.select.Elements
|
||||||
import java.text.SimpleDateFormat
|
import java.text.SimpleDateFormat
|
||||||
@ -53,13 +54,29 @@ class BiliManga : HttpSource(), ConfigurableSource {
|
|||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
const val PAGE_SIZE = 50
|
|
||||||
val META_REGEX = Regex("連載|完結|收藏|推薦|热度")
|
val META_REGEX = Regex("連載|完結|收藏|推薦|热度")
|
||||||
val DATE_REGEX = Regex("\\d{4}-\\d{1,2}-\\d{1,2}")
|
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 MANGA_ID_REGEX = Regex("/detail/(\\d+)\\.html")
|
||||||
val DATE_FORMAT = SimpleDateFormat("yyyy-MM-dd", Locale.CHINESE)
|
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) {
|
private fun getChapterUrlByContext(i: Int, els: Elements) = when (i) {
|
||||||
0 -> "${els[1].attr("href")}#prev"
|
0 -> "${els[1].attr("href")}#prev"
|
||||||
else -> "${els[i - 1].attr("href")}#next"
|
else -> "${els[i - 1].attr("href")}#next"
|
||||||
@ -72,8 +89,8 @@ class BiliManga : HttpSource(), ConfigurableSource {
|
|||||||
return GET(baseUrl + String.format(suffix, page), headers)
|
return GET(baseUrl + String.format(suffix, page), headers)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun popularMangaParse(response: Response) = response.asJsoup().let {
|
override fun popularMangaParse(response: Response) = response.asJsoup().let { doc ->
|
||||||
val mangas = it.select(".book-layout").map {
|
val mangas = doc.select(".book-layout").map {
|
||||||
SManga.create().apply {
|
SManga.create().apply {
|
||||||
setUrlWithoutDomain(it.absUrl("href"))
|
setUrlWithoutDomain(it.absUrl("href"))
|
||||||
val img = it.selectFirst("img")!!
|
val img = it.selectFirst("img")!!
|
||||||
@ -81,12 +98,13 @@ class BiliManga : HttpSource(), ConfigurableSource {
|
|||||||
title = img.attr("alt")
|
title = img.attr("alt")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
MangasPage(mangas, mangas.size >= PAGE_SIZE)
|
MangasPage(mangas, hasNextPage(doc, mangas.size))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Latest Page
|
// 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)
|
override fun latestUpdatesParse(response: Response) = popularMangaParse(response)
|
||||||
|
|
||||||
@ -94,13 +112,14 @@ class BiliManga : HttpSource(), ConfigurableSource {
|
|||||||
|
|
||||||
override fun getFilterList() = buildFilterList()
|
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 {
|
override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request {
|
||||||
val url = baseUrl.toHttpUrl().newBuilder()
|
val url = baseUrl.toHttpUrl().newBuilder()
|
||||||
if (query.isNotBlank()) {
|
if (query.isNotBlank()) {
|
||||||
url.addPathSegment("search").addPathSegment("${query}_$page.html")
|
url.addPathSegment("search").addPathSegment("${query}_$page.html")
|
||||||
} else {
|
} else {
|
||||||
url.addPathSegment("top").addPathSegment(filters[1].toString())
|
url.addPathSegment("filter")
|
||||||
.addPathSegment("$page.html")
|
.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)
|
return GET(url.build(), headers)
|
||||||
}
|
}
|
||||||
@ -118,13 +137,11 @@ class BiliManga : HttpSource(), ConfigurableSource {
|
|||||||
val doc = response.asJsoup()
|
val doc = response.asJsoup()
|
||||||
val meta = doc.selectFirst(".book-meta")!!.text().split("|")
|
val meta = doc.selectFirst(".book-meta")!!.text().split("|")
|
||||||
val extra = meta.filterNot(META_REGEX::containsMatchIn)
|
val extra = meta.filterNot(META_REGEX::containsMatchIn)
|
||||||
val backupname = doc.selectFirst(".backupname")?.let {
|
val backupname = doc.selectFirst(".backupname")?.let { "【別名:${it.text()}】\n\n" } ?: ""
|
||||||
"\n\n漫畫別名:\n• ${it.text().split("、").joinToString("\n• ")}"
|
|
||||||
}
|
|
||||||
setUrlWithoutDomain(doc.location())
|
setUrlWithoutDomain(doc.location())
|
||||||
title = doc.selectFirst(".book-title")!!.text()
|
title = doc.selectFirst(".book-title")!!.text()
|
||||||
thumbnail_url = doc.selectFirst(".book-cover")!!.attr("src")
|
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()
|
artist = doc.selectFirst(".authorname")?.text()
|
||||||
author = doc.selectFirst(".illname")?.text() ?: artist
|
author = doc.selectFirst(".illname")?.text() ?: artist
|
||||||
status = when (meta.firstOrNull()) {
|
status = when (meta.firstOrNull()) {
|
||||||
@ -138,7 +155,8 @@ class BiliManga : HttpSource(), ConfigurableSource {
|
|||||||
|
|
||||||
// Catalog Page
|
// 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 {
|
override fun chapterListParse(response: Response) = response.asJsoup().let {
|
||||||
val info = it.selectFirst(".chapter-sub-title")!!.text()
|
val info = it.selectFirst(".chapter-sub-title")!!.text()
|
||||||
@ -162,7 +180,7 @@ class BiliManga : HttpSource(), ConfigurableSource {
|
|||||||
|
|
||||||
override fun pageListParse(response: Response) = response.asJsoup().let {
|
override fun pageListParse(response: Response) = response.asJsoup().let {
|
||||||
val images = it.select(".imagecontent")
|
val images = it.select(".imagecontent")
|
||||||
check(images.size > 0) {
|
check(images.isNotEmpty()) {
|
||||||
it.selectFirst("#acontentz")?.let { e ->
|
it.selectFirst("#acontentz")?.let { e ->
|
||||||
if ("電腦端" in e.text()) "不支持電腦端查看,請在高級設置中更換移動端UA標識" else "漫畫可能已下架或需要登錄查看"
|
if ("電腦端" in e.text()) "不支持電腦端查看,請在高級設置中更換移動端UA標識" else "漫畫可能已下架或需要登錄查看"
|
||||||
} ?: "章节鏈接错误"
|
} ?: "章节鏈接错误"
|
||||||
|
@ -4,39 +4,103 @@ import eu.kanade.tachiyomi.source.model.Filter
|
|||||||
import eu.kanade.tachiyomi.source.model.FilterList
|
import eu.kanade.tachiyomi.source.model.FilterList
|
||||||
|
|
||||||
fun buildFilterList() = FilterList(
|
fun buildFilterList() = FilterList(
|
||||||
Filter.Header("篩選條件(搜尋時無效)"),
|
Filter.Header("篩選條件(搜尋關鍵字時無效)"),
|
||||||
RankFilter(),
|
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(
|
arrayOf(
|
||||||
"月點擊榜",
|
"不限", "奇幻", "冒險", "異世界", "龍傲天", "魔法",
|
||||||
"周點擊榜",
|
"仙俠", "戰爭", "熱血", "戰鬥", "競技", "懸疑",
|
||||||
"月推薦榜",
|
"驚悚", "獵奇", "神鬼", "偵探", "校園", "日常",
|
||||||
"周推薦榜",
|
"JK", "JC", "青梅竹馬", "妹妹", "大小姐", "女兒",
|
||||||
"月鮮花榜",
|
"愛情", "耽美", "百合", "NTR", "後宮", "職場",
|
||||||
"周鮮花榜",
|
"經營", "犯罪", "旅行", "群像", "女性視角",
|
||||||
"月雞蛋榜",
|
"歷史", "武俠", "東方", "勵志", "宅系", "科幻",
|
||||||
"周雞蛋榜",
|
"機戰", "遊戲", "異能", "腦洞", "病嬌", "人外",
|
||||||
"最新入庫",
|
"復仇", "鬥智", "惡役", "間諜", "治癒", "歡樂",
|
||||||
"收藏榜",
|
"萌系", "末日", "大逃殺", "音樂", "美食", "性轉",
|
||||||
"新書榜",
|
"偽娘", "穿越", "童話", "轉生", "黑暗", "溫馨",
|
||||||
|
"超自然",
|
||||||
),
|
),
|
||||||
) {
|
) {
|
||||||
override fun toString(): String {
|
override fun toString(): String {
|
||||||
return arrayOf(
|
return arrayOf(
|
||||||
"monthvisit",
|
"0", "1", "2", "3", "4", "5", "6", "7", "8",
|
||||||
"weekvisit",
|
"9", "10", "11", "12", "13", "14", "15", "16",
|
||||||
"monthvote",
|
"17", "18", "19", "20", "21", "22", "23", "24",
|
||||||
"weekvote",
|
"25", "26", "27", "28", "29", "30", "31", "32",
|
||||||
"monthflower",
|
"33", "34", "35", "36", "37", "38", "39", "40",
|
||||||
"weekflower",
|
"41", "42", "43", "44", "45", "46", "47", "48",
|
||||||
"monthegg",
|
"49", "50", "51", "52", "53", "54", "55", "56",
|
||||||
"weekegg",
|
"57", "58", "59", "60", "61", "62", "63", "64",
|
||||||
"postdate",
|
"65",
|
||||||
"goodnum",
|
|
||||||
"newhot",
|
|
||||||
)[state]
|
)[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