feat(BoaBua): add source BaoBua (#8253)
* feat(buondua): add source BaoBua Refs: #1104 * fix(buondua/search): resolve pagination param forwarding chore(buondua/search): clean up URL format and flag * Fix: - Add missing pagination parameter propagation * Maintenance: - Remove redundant trailing "/" in pagination URLs - Set `supportsLatest` to false (default behavior) Closes: #1104 * chore(buondua): remove redundant trim() calls Closes: #1104 * refactor(BaoBua): standardize chapter names and URL handling - Replace date-based chapter names with static "Gallery" value - Remove baseUrl from category URLs (construct dynamically when used) Closes: #1104 * chore(BaoBua): remove unused Closes: #1104 * chore(BaoBua): revert settings.gradle.kts Closes: #1104 * chore(BaoBua): remove unused import Closes: #1104 * chore(BaoBua): remove needless blank line Closes: #1104 * fix(BaoBua): add unselected Category Closes: #1104 * refactor(BaoBua): optimize manga details parsing - Set update_strategy = UpdateStrategy.ONLY_FETCH_ONCE - Remove unused randomua dependency Closes: #1104
This commit is contained in:
parent
40a9d2ec6a
commit
fad76bc4b2
8
src/all/baobua/build.gradle
Normal file
8
src/all/baobua/build.gradle
Normal file
@ -0,0 +1,8 @@
|
||||
ext {
|
||||
extName = 'BaoBua'
|
||||
extClass = '.BaoBua'
|
||||
extVersionCode = 1
|
||||
isNsfw = true
|
||||
}
|
||||
|
||||
apply from: "$rootDir/common.gradle"
|
BIN
src/all/baobua/res/mipmap-hdpi/ic_launcher.png
Normal file
BIN
src/all/baobua/res/mipmap-hdpi/ic_launcher.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 4.5 KiB |
BIN
src/all/baobua/res/mipmap-mdpi/ic_launcher.png
Normal file
BIN
src/all/baobua/res/mipmap-mdpi/ic_launcher.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 2.5 KiB |
BIN
src/all/baobua/res/mipmap-xhdpi/ic_launcher.png
Normal file
BIN
src/all/baobua/res/mipmap-xhdpi/ic_launcher.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 6.3 KiB |
BIN
src/all/baobua/res/mipmap-xxhdpi/ic_launcher.png
Normal file
BIN
src/all/baobua/res/mipmap-xxhdpi/ic_launcher.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 11 KiB |
BIN
src/all/baobua/res/mipmap-xxxhdpi/ic_launcher.png
Normal file
BIN
src/all/baobua/res/mipmap-xxxhdpi/ic_launcher.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 16 KiB |
@ -0,0 +1,109 @@
|
||||
package eu.kanade.tachiyomi.extension.all.baobua
|
||||
|
||||
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.model.UpdateStrategy
|
||||
import eu.kanade.tachiyomi.util.asJsoup
|
||||
import keiyoushi.utils.firstInstance
|
||||
import keiyoushi.utils.tryParse
|
||||
import okhttp3.HttpUrl.Companion.toHttpUrl
|
||||
import okhttp3.Request
|
||||
import org.jsoup.nodes.Document
|
||||
import org.jsoup.nodes.Element
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.Locale
|
||||
|
||||
class BaoBua() : SimpleParsedHttpSource() {
|
||||
|
||||
override val baseUrl = "https://www.baobua.net"
|
||||
override val lang = "all"
|
||||
override val name = "BaoBua"
|
||||
override val supportsLatest = false
|
||||
|
||||
override fun simpleMangaSelector() = "article.post"
|
||||
|
||||
override fun simpleMangaFromElement(element: Element) = SManga.create().apply {
|
||||
setUrlWithoutDomain(element.selectFirst("a.popunder")!!.absUrl("href"))
|
||||
title = element.selectFirst("div.read-title")!!.text()
|
||||
thumbnail_url = element.selectFirst("img")?.absUrl("src")
|
||||
update_strategy = UpdateStrategy.ONLY_FETCH_ONCE
|
||||
}
|
||||
|
||||
override fun simpleNextPageSelector(): String = "nav.pagination a.next"
|
||||
|
||||
// region popular
|
||||
override fun popularMangaRequest(page: Int) = GET("$baseUrl?page=$page", headers)
|
||||
// endregion
|
||||
|
||||
// region latest
|
||||
override fun latestUpdatesRequest(page: Int) = throw UnsupportedOperationException()
|
||||
// endregion
|
||||
|
||||
// region Search
|
||||
override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request {
|
||||
val filter = filters.firstInstance<SourceCategorySelector>()
|
||||
return filter.selectedCategory?.let {
|
||||
GET(it.buildUrl(baseUrl), headers)
|
||||
} ?: run {
|
||||
baseUrl.toHttpUrl().newBuilder()
|
||||
.addEncodedQueryParameter("q", query)
|
||||
.addEncodedQueryParameter("page", page.toString())
|
||||
.build()
|
||||
.let { GET(it, headers) }
|
||||
}
|
||||
}
|
||||
|
||||
// region Details
|
||||
override fun mangaDetailsParse(document: Document): SManga {
|
||||
val trailItemsEl = document.selectFirst("div.breadcrumb-trail > ul.trail-items")!!
|
||||
return SManga.create().apply {
|
||||
title = trailItemsEl.selectFirst("li.trail-end")!!.text()
|
||||
genre = trailItemsEl.select("li:not(.trail-end):not(.trail-begin)").joinToString { it.text() }
|
||||
}
|
||||
}
|
||||
|
||||
override fun chapterListSelector() = "html"
|
||||
|
||||
override fun chapterFromElement(element: Element) = SChapter.create().apply {
|
||||
chapter_number = 0F
|
||||
setUrlWithoutDomain(element.selectFirst("div.breadcrumb-trail li.trail-end > a")!!.absUrl("href"))
|
||||
date_upload = POST_DATE_FORMAT.tryParse(element.selectFirst("span.item-metadata.posts-date")?.text())
|
||||
name = "Gallery"
|
||||
}
|
||||
// endregion
|
||||
|
||||
// region Pages
|
||||
override fun pageListParse(document: Document): List<Page> {
|
||||
val basePageUrl = document.selectFirst("div.breadcrumb-trail li.trail-end > a")!!.absUrl("href")
|
||||
|
||||
val maxPage: Int = document.selectFirst("div.nav-links > a.next.page-numbers")?.text()?.toInt() ?: 1
|
||||
|
||||
var pageIndex = 0
|
||||
return (1..maxPage).flatMap { pageNum ->
|
||||
val doc = if (pageNum == 1) {
|
||||
document
|
||||
} else {
|
||||
client.newCall(GET("$basePageUrl?p=$pageNum", headers)).execute().asJsoup()
|
||||
}
|
||||
|
||||
doc.select("div.entry-content.read-details img.wp-image")
|
||||
.map { Page(pageIndex++, imageUrl = it.absUrl("src")) }
|
||||
}
|
||||
}
|
||||
// endregion
|
||||
|
||||
override fun getFilterList(): FilterList = FilterList(
|
||||
Filter.Header("NOTE: Unable to further search in the category!"),
|
||||
Filter.Separator(),
|
||||
SourceCategorySelector.create(baseUrl),
|
||||
)
|
||||
|
||||
companion object {
|
||||
|
||||
private val POST_DATE_FORMAT = SimpleDateFormat("EEE MMM dd yyyy", Locale.US)
|
||||
}
|
||||
}
|
@ -0,0 +1,44 @@
|
||||
package eu.kanade.tachiyomi.extension.all.baobua
|
||||
|
||||
import eu.kanade.tachiyomi.source.model.SManga
|
||||
import eu.kanade.tachiyomi.source.online.ParsedHttpSource
|
||||
import org.jsoup.nodes.Document
|
||||
import org.jsoup.nodes.Element
|
||||
|
||||
abstract class SimpleParsedHttpSource : ParsedHttpSource() {
|
||||
|
||||
abstract fun simpleMangaSelector(): String
|
||||
|
||||
abstract fun simpleMangaFromElement(element: Element): SManga
|
||||
|
||||
abstract fun simpleNextPageSelector(): String?
|
||||
|
||||
// region popular
|
||||
override fun popularMangaSelector() = simpleMangaSelector()
|
||||
override fun popularMangaNextPageSelector() = simpleNextPageSelector()
|
||||
override fun popularMangaFromElement(element: Element) = simpleMangaFromElement(element)
|
||||
// endregion
|
||||
|
||||
// region last
|
||||
override fun latestUpdatesSelector() =
|
||||
if (supportsLatest) simpleMangaSelector() else throw throw UnsupportedOperationException()
|
||||
|
||||
override fun latestUpdatesFromElement(element: Element) =
|
||||
if (supportsLatest) simpleMangaFromElement(element) else throw throw UnsupportedOperationException()
|
||||
|
||||
override fun latestUpdatesNextPageSelector() =
|
||||
if (supportsLatest) simpleNextPageSelector() else throw throw UnsupportedOperationException()
|
||||
// endregion
|
||||
|
||||
// region search
|
||||
override fun searchMangaSelector() = simpleMangaSelector()
|
||||
override fun searchMangaFromElement(element: Element) = simpleMangaFromElement(element)
|
||||
override fun searchMangaNextPageSelector() = simpleNextPageSelector()
|
||||
// endregion
|
||||
|
||||
override fun chapterListSelector() = simpleMangaSelector()
|
||||
override fun imageUrlParse(document: Document): String {
|
||||
throw UnsupportedOperationException()
|
||||
}
|
||||
// endregion
|
||||
}
|
@ -0,0 +1,50 @@
|
||||
package eu.kanade.tachiyomi.extension.all.baobua
|
||||
|
||||
import eu.kanade.tachiyomi.source.model.Filter
|
||||
import okhttp3.HttpUrl.Companion.toHttpUrl
|
||||
|
||||
data class SourceCategory(private val name: String, var cat: String) {
|
||||
override fun toString() = this.name
|
||||
|
||||
fun buildUrl(baseUrl: String): String {
|
||||
return "$baseUrl/".toHttpUrl().newBuilder()
|
||||
.addEncodedQueryParameter("cat", this.cat)
|
||||
.build()
|
||||
.toString()
|
||||
}
|
||||
}
|
||||
|
||||
class SourceCategorySelector(
|
||||
name: String,
|
||||
categories: List<SourceCategory>,
|
||||
) : Filter.Select<SourceCategory>(name, categories.toTypedArray()) {
|
||||
|
||||
val selectedCategory: SourceCategory?
|
||||
get() = if (state > 0) values[state] else null
|
||||
|
||||
companion object {
|
||||
|
||||
fun create(baseUrl: String): SourceCategorySelector {
|
||||
val options = listOf(
|
||||
SourceCategory("unselected", ""),
|
||||
SourceCategory("大胸美女", "YmpydEtkNzV5NHJKcDJYVGtOVW0yZz09"),
|
||||
SourceCategory("巨乳美女", "Q09EdlMvMHgweERrUitScTFTaDM4Zz09"),
|
||||
SourceCategory("全裸写真", "eXZzejJPNFRVNzJqKzFDUmNzZEU2QT09"),
|
||||
SourceCategory("chinese", "bG9LamJsWWdSbGcyY0FEZytldkhTZz09"),
|
||||
SourceCategory("chinese models", "OCtTSEI2YzRTcWMvWUsyeDM0aHdzdUIwWDlHMERZUEZaVHUwUEVUVWo3QT0"),
|
||||
SourceCategory("korean", "Tm1ydGlaZ1A2YWM3a3BvYWh6L3dIdz09"),
|
||||
SourceCategory("korea", "bzRjeWR0akQrRWpxRE1xOGF6TW5Tdz09"),
|
||||
SourceCategory("korean models", "TGZTVGtwOCtxTW1TQU1KYWhUb01DQT09"),
|
||||
SourceCategory("big boobs", "UmFLQVkvVndGNlpPckwvZkpVaEE4UT09"),
|
||||
SourceCategory("adult", "b2RFSnlwdWxyREMxVmRpcThKVXRLUT09"),
|
||||
SourceCategory("nude-art", "djFqa293VmFZMEJLdDlUWndsMGtldz09"),
|
||||
SourceCategory("Asian adult photo", "SHBGZHFueTVNeUlxVHRLaU53RjU2NS9VcjNxRVg3VnhqTGJoK25YaVQ1UT0"),
|
||||
SourceCategory("cosplay", "OEI2c000ZDBxakwydjZIUVJaRnlMQT09"),
|
||||
SourceCategory("hot", "c3VRb3RJZ2wrU2tTYmpGSUVqMnFndz09"),
|
||||
SourceCategory("big breast", "dkQ3b0RiK0xpZDRlMVNSY3lUNkJXQT09"),
|
||||
)
|
||||
|
||||
return SourceCategorySelector("Category", options)
|
||||
}
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user