Add BrasilHentai (#2918)

* Add BrasilHentai

* Fix popularMangaNextPage

* Fix popularMangaRequest and set rateLimit

* Fix names

* Fix filter

* Fix intent pathSegment

* Remove extra request and add Initilize in popularMangaFromElement

* Update src/pt/brasilhentai/src/eu/kanade/tachiyomi/extension/pt/brasilhentai/BrasilHentai.kt

* Update src/pt/brasilhentai/src/eu/kanade/tachiyomi/extension/pt/brasilhentai/BrasilHentai.kt

---------

Co-authored-by: AwkwardPeak7 <48650614+AwkwardPeak7@users.noreply.github.com>
This commit is contained in:
Chopper 2024-05-13 12:30:02 -03:00 committed by Draff
parent 9fe6e06574
commit 1ef8153eca
9 changed files with 237 additions and 0 deletions

View File

@ -0,0 +1,22 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<application>
<activity
android:name=".pt.brasilhentai.BrasilHentaiUrlActivity"
android:excludeFromRecents="true"
android:exported="true"
android:theme="@android:style/Theme.NoDisplay">
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data
android:host="brasilhentai.com"
android:pathPattern="/..*"
android:scheme="https" />
</intent-filter>
</activity>
</application>
</manifest>

View File

@ -0,0 +1,8 @@
ext {
extName = 'Brasil Hentai'
extClass = '.BrasilHentai'
extVersionCode = 1
isNsfw = true
}
apply from: "$rootDir/common.gradle"

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

View File

@ -0,0 +1,173 @@
package eu.kanade.tachiyomi.extension.pt.brasilhentai
import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.network.asObservableSuccess
import eu.kanade.tachiyomi.network.interceptor.rateLimit
import eu.kanade.tachiyomi.source.model.Filter
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.SChapter
import eu.kanade.tachiyomi.source.model.SManga
import eu.kanade.tachiyomi.source.online.ParsedHttpSource
import eu.kanade.tachiyomi.util.asJsoup
import okhttp3.HttpUrl.Companion.toHttpUrl
import okhttp3.OkHttpClient
import okhttp3.Request
import okhttp3.Response
import org.jsoup.nodes.Document
import org.jsoup.nodes.Element
import rx.Observable
class BrasilHentai : ParsedHttpSource() {
override val name = "Brasil Hentai"
override val baseUrl = "https://brasilhentai.com"
override val lang = "pt-BR"
override val supportsLatest: Boolean = false
override val client: OkHttpClient = network.cloudflareClient.newBuilder()
.rateLimit(4)
.build()
private var categoryFilterOptions: Array<Pair<String, String>> = emptyArray()
override fun chapterFromElement(element: Element) =
throw UnsupportedOperationException()
override fun chapterListSelector() =
throw UnsupportedOperationException()
override fun fetchChapterList(manga: SManga): Observable<List<SChapter>> {
return Observable.just(
listOf(
SChapter.create().apply {
name = "Capítulo único"
url = manga.url
},
),
)
}
override fun imageUrlParse(document: Document) = ""
override fun latestUpdatesFromElement(element: Element) =
throw UnsupportedOperationException()
override fun latestUpdatesNextPageSelector() =
throw UnsupportedOperationException()
override fun latestUpdatesRequest(page: Int) =
throw UnsupportedOperationException()
override fun latestUpdatesSelector() =
throw UnsupportedOperationException()
override fun mangaDetailsParse(document: Document) = SManga.create().apply {
title = document.selectFirst("h1")!!.ownText()
thumbnail_url = document.selectFirst(".entry-content p a img")?.absUrl("src")
}
override fun pageListParse(document: Document): List<Page> {
return document.select(".entry-content p > img").mapIndexed { index, element ->
Page(index, imageUrl = element.absUrl("src"))
}
}
override fun popularMangaFromElement(element: Element) = SManga.create().apply {
val anchor = element.selectFirst("a[title]")
title = anchor!!.attr("title")
thumbnail_url = element.selectFirst("img")?.absUrl("src")
initialized = true
setUrlWithoutDomain(anchor.absUrl("href"))
}
override fun popularMangaNextPageSelector() = ".next.page-numbers"
override fun popularMangaRequest(page: Int) = GET("$baseUrl/page/$page", headers)
override fun popularMangaSelector() = ".content-area article"
override fun popularMangaParse(response: Response): MangasPage {
val document = response.asJsoup()
if (categoryFilterOptions.isEmpty()) {
categoryFilterOptions = parseCategories(document)
}
return MangasPage(
mangas = document.select(popularMangaSelector()).map(::popularMangaFromElement),
hasNextPage = document.selectFirst(popularMangaNextPageSelector()) != null,
)
}
override fun searchMangaFromElement(element: Element) = popularMangaFromElement(element)
override fun searchMangaNextPageSelector() = popularMangaNextPageSelector()
override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request {
val category = filters.filterIsInstance<CategoryFilter>().first()
.selectedValue()
val url = if (category.isEmpty()) {
"$baseUrl/page/$page".toHttpUrl().newBuilder()
.addQueryParameter("s", query)
.build()
} else {
"$baseUrl/category/$category/page/$page/".toHttpUrl()
}
return GET(url, headers)
}
override fun fetchSearchManga(page: Int, query: String, filters: FilterList): Observable<MangasPage> {
return if (query.startsWith(PREFIX_SEARCH)) {
client.newCall(GET("$baseUrl/${query.substringAfter(PREFIX_SEARCH)}", headers))
.asObservableSuccess()
.map { MangasPage(listOf(searchMangaFromElement(it.asJsoup())), false) }
} else {
super.fetchSearchManga(page, query, filters)
}
}
override fun searchMangaSelector() = popularMangaSelector()
override fun getFilterList(): FilterList {
val filters = mutableListOf<Filter<*>>()
filters += if (categoryFilterOptions.isNotEmpty()) {
CategoryFilter("Categoria", categoryFilterOptions)
} else {
Filter.Header("Aperte 'Redefinir' para tentar mostrar as categorias")
}
return FilterList(filters)
}
private fun parseCategories(document: Document): Array<Pair<String, String>> {
return document.select("#categories-2 li a")
.map { element ->
val url = element.absUrl("href")
val category = url.split("/").filter { it.isNotBlank() }.last()
Pair(element.ownText(), category)
}.toTypedArray()
}
class CategoryFilter(
displayName: String,
private val vals: Array<Pair<String, String>>,
) : Filter.Select<String>(
displayName,
vals.map { it.first }.toTypedArray(),
vals.indexOfFirst { it.second.isEmpty() }.takeIf { it != -1 } ?: 0,
) {
fun selectedValue() = vals[state].second
}
private fun categoriesRequest(): Request = GET(baseUrl, headers)
companion object {
val PREFIX_SEARCH: String = "slug:"
}
}

View File

@ -0,0 +1,34 @@
package eu.kanade.tachiyomi.extension.pt.brasilhentai
import android.app.Activity
import android.content.ActivityNotFoundException
import android.content.Intent
import android.os.Bundle
import android.util.Log
import kotlin.system.exitProcess
class BrasilHentaiUrlActivity : Activity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val segment = intent?.data?.lastPathSegment
if (segment != null) {
val mainIntent = Intent().apply {
action = "eu.kanade.tachiyomi.SEARCH"
putExtra("query", "${BrasilHentai.PREFIX_SEARCH}$segment")
putExtra("filter", packageName)
}
try {
startActivity(mainIntent)
} catch (e: ActivityNotFoundException) {
Log.e("BrasilHentaiUrlActivity", e.toString())
}
} else {
Log.e("BrasilHentaiUrlActivity", "could not parse uri from intent $intent")
}
finish()
exitProcess(0)
}
}