Add TuMangas.net (#3748)

* Add TuMangas.net

* use build

Co-authored-by: AwkwardPeak7 <48650614+AwkwardPeak7@users.noreply.github.com>

* Apply suggestions from code review

Co-authored-by: AwkwardPeak7 <48650614+AwkwardPeak7@users.noreply.github.com>

* to lazy to wake up

---------

Co-authored-by: AwkwardPeak7 <48650614+AwkwardPeak7@users.noreply.github.com>
This commit is contained in:
bapeey 2024-06-26 07:36:14 -05:00 committed by GitHub
parent 0e298c9d78
commit 38e8dc2fa2
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 190 additions and 0 deletions

View File

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

View File

@ -0,0 +1,120 @@
package eu.kanade.tachiyomi.extension.es.tumangasnet
import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.network.interceptor.rateLimitHost
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.online.ParsedHttpSource
import okhttp3.HttpUrl.Companion.toHttpUrl
import okhttp3.Request
import org.jsoup.nodes.Document
import org.jsoup.nodes.Element
class TuMangasNet : ParsedHttpSource() {
override val name = "TuMangas.net"
override val baseUrl = "https://tumangas.net"
override val lang = "es"
override val supportsLatest = true
override val client = network.cloudflareClient.newBuilder()
.rateLimitHost(baseUrl.toHttpUrl(), 3, 1)
.build()
override fun headersBuilder() = super.headersBuilder()
.add("Referer", "$baseUrl/")
override fun popularMangaRequest(page: Int) = GET("$baseUrl/biblioteca-manga?page=$page", headers)
override fun popularMangaNextPageSelector() = searchMangaNextPageSelector()
override fun popularMangaSelector() = searchMangaSelector()
override fun popularMangaFromElement(element: Element) = searchMangaFromElement(element)
override fun latestUpdatesRequest(page: Int) = GET(baseUrl, headers)
override fun latestUpdatesNextPageSelector() = null
override fun latestUpdatesSelector() = "ul.episodes article.episode"
override fun latestUpdatesFromElement(element: Element) = SManga.create().apply {
setUrlWithoutDomain(
element.selectFirst("a")!!.attr("href")
.substringBeforeLast("-")
.replace("/leer-manga/", "/manga/"),
)
title = element.selectFirst(".title")!!.text().substringBeforeLast("Ep.").trim()
thumbnail_url = element.selectFirst("figure > img")?.attr("src")
}
override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request {
if (query.isNotBlank()) {
val url = "$baseUrl/biblioteca-manga".toHttpUrl().newBuilder()
.addQueryParameter("buscar", query)
.addQueryParameter("page", page.toString())
.build()
return GET(url, headers)
}
val url = "$baseUrl/tag".toHttpUrl().newBuilder()
filters.forEach { filter ->
when (filter) {
is GenreFilter -> {
url.addPathSegment(filter.toUriPart())
}
else -> {}
}
}
url.addQueryParameter("page", page.toString())
return GET(url.build(), headers)
}
override fun searchMangaNextPageSelector() = "ul.pagination li.page-item a[rel=next]"
override fun searchMangaSelector() = "ul.animes article.anime"
override fun searchMangaFromElement(element: Element) = SManga.create().apply {
setUrlWithoutDomain(element.selectFirst("a")!!.attr("href"))
title = element.selectFirst(".title")!!.text()
thumbnail_url = element.selectFirst("figure > img")?.attr("src")
}
override fun getFilterList() = FilterList(
Filter.Header("NOTA: Los filtros no funcionan en la búsqueda por texto."),
GenreFilter(),
)
override fun mangaDetailsParse(document: Document) = SManga.create().apply {
document.selectFirst("article.anime-single")!!.let { element ->
title = element.selectFirst(".title")!!.text()
genre = element.select("p.genres > span").joinToString { it.text() }
description = element.selectFirst(".sinopsis")?.text()
thumbnail_url = element.selectFirst("div.thumb figure > img")?.attr("abs:src")
}
}
override fun chapterListSelector() = "ul.episodes-list > li"
override fun chapterFromElement(element: Element) = SChapter.create().apply {
setUrlWithoutDomain(element.selectFirst("a")!!.attr("abs:href"))
name = element.selectFirst("a > span")!!.text()
}
override fun pageListParse(document: Document): List<Page> {
return document.select("div#chapter_imgs img[src]").mapIndexed { i, img ->
Page(i, imageUrl = img.attr("abs:src"))
}
}
override fun imageUrlParse(document: Document) = throw UnsupportedOperationException()
}

View File

@ -0,0 +1,62 @@
package eu.kanade.tachiyomi.extension.es.tumangasnet
import eu.kanade.tachiyomi.source.model.Filter
class GenreFilter() : UriPartFilter(
"Género",
arrayOf(
Pair("Acción", "accion"),
Pair("Aventura", "aventura"),
Pair("Comedia", "comedia"),
Pair("Drama", "drama"),
Pair("Recuentos de la vida", "recuentos-de-la-vida"),
Pair("Ecchi", "ecchi"),
Pair("Fantasia", "fantasia"),
Pair("Magia", "magia"),
Pair("Sobrenatural", "sobrenatural"),
Pair("Horror", "horror"),
Pair("Misterio", "misterio"),
Pair("Psicológico", "psicologico"),
Pair("Romance", "romance"),
Pair("Ciencia Ficción", "ciencia-ficcion"),
Pair("Thriller", "thriller"),
Pair("Deporte", "deporte"),
Pair("Girls Love", "girls-love"),
Pair("Boys Love", "boys-love"),
Pair("Harem", "harem"),
Pair("Mecha", "mecha"),
Pair("Supervivencia", "supervivencia"),
Pair("Reencarnación", "reencarnacion"),
Pair("Gore", "gore"),
Pair("Apocalíptico", "apocaliptico"),
Pair("Tragedia", "tragedia"),
Pair("Vida Escolar", "vida-escolar"),
Pair("Historia", "historia"),
Pair("Militar", "militar"),
Pair("Policiaco", "policiaco"),
Pair("Crimen", "crimen"),
Pair("Superpoderes", "superpoderes"),
Pair("Vampiros", "vampiros"),
Pair("Artes Marciales", "artes-marciales"),
Pair("Samurái", "samurai"),
Pair("Género Bender", "genero-bender"),
Pair("Realidad Virtual", "realidad-virtual"),
Pair("Ciberpunk", "ciberpunk"),
Pair("Musica", "musica"),
Pair("Parodia", "parodia"),
Pair("Animación", "animacion"),
Pair("Demonios", "demonios"),
Pair("Familia", "familia"),
Pair("Extranjero", "extranjero"),
Pair("Niños", "ninos"),
Pair("Realidad", "realidad"),
Pair("Telenovela", "telenovela"),
Pair("Guerra", "guerra"),
Pair("Oeste", "oeste"),
),
)
open class UriPartFilter(displayName: String, val vals: Array<Pair<String, String>>) :
Filter.Select<String>(displayName, vals.map { it.first }.toTypedArray()) {
fun toUriPart() = vals[state].second
}