add mangaonline (#1610)

* Add Manga Online

* Fix note about latest manga parse

* Add new line at the end of build.gradle

* Remove blank line

* Fix global search

* Remove extra URL builder in search

* Remove URLBuilder

* Add try/catch in fetchMangaGenre

* Set latestUpdatesNextPageSelector to null

* Rename constante name and remove breakline in GenreList::selected

* Remove fetchLatestUpdate override

* Replace 'runCatching' for try/catch

* Remove 'headers'

* Remove unused import

* Remove last blank line

* Rename constant variable with uppercase

* small cleanup

* lint

---------

Co-authored-by: Chopper <156493704+ttony2chopper@users.noreply.github.com>
This commit is contained in:
AwkwardPeak7 2024-02-29 18:38:03 +05:00 committed by GitHub
parent b873136c0a
commit 6289e61041
7 changed files with 216 additions and 0 deletions

View File

@ -0,0 +1,12 @@
ext {
extName = 'Manga Online'
extClass = '.MangaOnline'
extVersionCode = 1
isNsfw = true
}
apply from: "$rootDir/common.gradle"
dependencies {
implementation(project(':lib:randomua'))
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

View File

@ -0,0 +1,204 @@
package eu.kanade.tachiyomi.extension.pt.mangaonline
import android.app.Application
import android.content.SharedPreferences
import android.util.Log
import androidx.preference.PreferenceScreen
import eu.kanade.tachiyomi.lib.randomua.addRandomUAPreferenceToScreen
import eu.kanade.tachiyomi.lib.randomua.getPrefCustomUA
import eu.kanade.tachiyomi.lib.randomua.getPrefUAType
import eu.kanade.tachiyomi.lib.randomua.setRandomUserAgent
import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.network.interceptor.rateLimitHost
import eu.kanade.tachiyomi.source.ConfigurableSource
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 kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
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 uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
import java.text.SimpleDateFormat
import java.util.Locale
import java.util.concurrent.TimeUnit
class MangaOnline : ParsedHttpSource(), ConfigurableSource {
override val lang = "pt-BR"
override val supportsLatest = true
override val name = "Manga Online"
override val baseUrl = "https://mangaonline.biz"
private var genresSet: Set<Genre> = emptySet()
private val preferences: SharedPreferences =
Injekt.get<Application>().getSharedPreferences("source_$id", 0x0000)
override val client: OkHttpClient =
network.cloudflareClient.newBuilder()
.setRandomUserAgent(
preferences.getPrefUAType(),
preferences.getPrefCustomUA(),
)
.rateLimitHost(baseUrl.toHttpUrl(), 1, 2, TimeUnit.SECONDS)
.build()
override fun setupPreferenceScreen(screen: PreferenceScreen) {
addRandomUAPreferenceToScreen(screen)
}
override fun chapterFromElement(element: Element) = SChapter.create().apply {
name = element.selectFirst("a")!!.ownText()
date_upload = element.selectFirst("a span.date")?.ownText()!!.toDate()
setUrlWithoutDomain(element.selectFirst("a")!!.absUrl("href"))
}
override fun chapterListSelector() = "div.episodiotitle"
override fun imageUrlParse(document: Document) = ""
override fun latestUpdatesFromElement(element: Element) = SManga.create().apply {
title = element.selectFirst("h3 a")!!.ownText()
.replace("Capítulo\\s+([\\d.]+)".toRegex(), "")
.trim()
thumbnail_url = element.selectFirst("img")?.absUrl("src")
val mangaUrl = element.selectFirst("h3 a")!!.absUrl("href")
.replace("-capitulo-[\\d-]+".toRegex(), "")
.replace("capitulo", "manga")
setUrlWithoutDomain(mangaUrl)
}
override fun latestUpdatesNextPageSelector() = null
override fun latestUpdatesRequest(page: Int) = GET("$baseUrl/capitulo/page/$page")
override fun latestUpdatesParse(response: Response): MangasPage {
val mangesPage = super.latestUpdatesParse(response)
return MangasPage(
mangesPage.mangas.distinctBy { it.title },
mangesPage.hasNextPage,
)
}
override fun latestUpdatesSelector() = popularMangaSelector()
override fun mangaDetailsParse(document: Document) = SManga.create().apply {
val containerInfo = document.selectFirst("div.content > div.sheader")
title = containerInfo!!.selectFirst("h1")!!.ownText()
thumbnail_url = containerInfo.selectFirst("img")?.absUrl("src")
description = containerInfo.selectFirst("p:last-child")?.ownText()
genre = containerInfo.select("div.sgeneros a")
.map { it.ownText() }
.filter { it.length > 1 }
.joinToString()
}
override fun pageListParse(document: Document): List<Page> {
return document.select("img[loading=lazy]").mapIndexed { i, it ->
Page(i, imageUrl = it.absUrl("src"))
}
}
override fun popularMangaFromElement(element: Element) = SManga.create().apply {
title = element.selectFirst("h3 a")!!.ownText()
thumbnail_url = element.selectFirst("img")?.absUrl("src")
setUrlWithoutDomain(element.selectFirst("h3 a")!!.absUrl("href"))
}
override fun popularMangaNextPageSelector() = null
override fun popularMangaRequest(page: Int): Request = GET("$baseUrl/mais-vistos/")
override fun popularMangaSelector() = "div.content .item"
override fun searchMangaFromElement(element: Element) = popularMangaFromElement(element)
override fun searchMangaNextPageSelector() = ".pagination > .current + a"
override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request {
if (query.isNotBlank()) {
val url = "$baseUrl/search".toHttpUrl().newBuilder()
.addPathSegment(query)
.build()
return GET(url)
}
val path = when (val genre = (filters.first() as GenreList).selected) {
Genre.GLOBAL -> "$baseUrl/${genre.id}"
else -> "$baseUrl/genero/${genre.id}"
}
return GET("$path/page/$page")
}
override fun searchMangaSelector() = popularMangaSelector()
override fun getFilterList(): FilterList {
CoroutineScope(Dispatchers.IO).launch {
fetchMangaGenre()
}
genresSet += Genre.GLOBAL
return FilterList(
GenreList(
title = "Gêneros",
genres = genresSet.toTypedArray(),
),
)
}
private fun fetchMangaGenre() {
try {
val request = client
.newCall(GET("$baseUrl/generos/"))
.execute()
val document = request.asJsoup()
genresSet = document.select(".wp-content a").map { element ->
val id = element.absUrl("href")
.split("/")
.last { it.isNotEmpty() }
Genre(element.ownText(), id)
}.toSet()
} catch (e: Exception) {
Log.e("MangaOnline", e.stackTraceToString())
}
}
private fun String.toDate() =
try { dateFormat.parse(trim())!!.time } catch (_: Exception) { 0L }
private val dateFormat = SimpleDateFormat("dd/MM/yyyy", Locale("pt", "BR"))
}
data class Genre(val name: String, val id: String) {
override fun toString() = name
companion object {
val GLOBAL = Genre("Todos", "manga")
}
}
class GenreList(title: String, private val genres: Array<Genre>) : Filter.Select<Genre>(title, genres) {
val selected get() = genres[state]
}