diff --git a/src/all/baobua/build.gradle b/src/all/baobua/build.gradle new file mode 100644 index 000000000..302f91502 --- /dev/null +++ b/src/all/baobua/build.gradle @@ -0,0 +1,8 @@ +ext { + extName = 'BaoBua' + extClass = '.BaoBua' + extVersionCode = 1 + isNsfw = true +} + +apply from: "$rootDir/common.gradle" diff --git a/src/all/baobua/res/mipmap-hdpi/ic_launcher.png b/src/all/baobua/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 000000000..f6fb7f7ec Binary files /dev/null and b/src/all/baobua/res/mipmap-hdpi/ic_launcher.png differ diff --git a/src/all/baobua/res/mipmap-mdpi/ic_launcher.png b/src/all/baobua/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 000000000..8bd7b76e2 Binary files /dev/null and b/src/all/baobua/res/mipmap-mdpi/ic_launcher.png differ diff --git a/src/all/baobua/res/mipmap-xhdpi/ic_launcher.png b/src/all/baobua/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 000000000..f31eeb435 Binary files /dev/null and b/src/all/baobua/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/src/all/baobua/res/mipmap-xxhdpi/ic_launcher.png b/src/all/baobua/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 000000000..8a983368e Binary files /dev/null and b/src/all/baobua/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/src/all/baobua/res/mipmap-xxxhdpi/ic_launcher.png b/src/all/baobua/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 000000000..d32f4fa4c Binary files /dev/null and b/src/all/baobua/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/src/all/baobua/src/eu/kanade/tachiyomi/extension/all/baobua/BaoBua.kt b/src/all/baobua/src/eu/kanade/tachiyomi/extension/all/baobua/BaoBua.kt new file mode 100644 index 000000000..0e5dd2683 --- /dev/null +++ b/src/all/baobua/src/eu/kanade/tachiyomi/extension/all/baobua/BaoBua.kt @@ -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() + 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 { + 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) + } +} diff --git a/src/all/baobua/src/eu/kanade/tachiyomi/extension/all/baobua/SimpleParsedHttpSource.kt b/src/all/baobua/src/eu/kanade/tachiyomi/extension/all/baobua/SimpleParsedHttpSource.kt new file mode 100644 index 000000000..43cd69aa5 --- /dev/null +++ b/src/all/baobua/src/eu/kanade/tachiyomi/extension/all/baobua/SimpleParsedHttpSource.kt @@ -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 +} diff --git a/src/all/baobua/src/eu/kanade/tachiyomi/extension/all/baobua/SourceCategorySelector.kt b/src/all/baobua/src/eu/kanade/tachiyomi/extension/all/baobua/SourceCategorySelector.kt new file mode 100644 index 000000000..7e8b505da --- /dev/null +++ b/src/all/baobua/src/eu/kanade/tachiyomi/extension/all/baobua/SourceCategorySelector.kt @@ -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, +) : Filter.Select(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) + } + } +}