parent
5c71163ef6
commit
09458dcd86
|
@ -6,7 +6,7 @@ ext {
|
|||
extName = 'Ler Mangá'
|
||||
pkgNameSuffix = 'pt.lermanga'
|
||||
extClass = '.LerManga'
|
||||
extVersionCode = 3
|
||||
extVersionCode = 4
|
||||
isNsfw = true
|
||||
}
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@ package eu.kanade.tachiyomi.extension.pt.lermanga
|
|||
|
||||
import android.util.Base64
|
||||
import eu.kanade.tachiyomi.network.GET
|
||||
import eu.kanade.tachiyomi.network.interceptor.rateLimit
|
||||
import eu.kanade.tachiyomi.network.interceptor.rateLimitHost
|
||||
import eu.kanade.tachiyomi.source.model.FilterList
|
||||
import eu.kanade.tachiyomi.source.model.MangasPage
|
||||
import eu.kanade.tachiyomi.source.model.Page
|
||||
|
@ -12,16 +12,17 @@ import eu.kanade.tachiyomi.source.online.ParsedHttpSource
|
|||
import kotlinx.serialization.decodeFromString
|
||||
import kotlinx.serialization.json.Json
|
||||
import okhttp3.Headers
|
||||
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.injectLazy
|
||||
import java.lang.UnsupportedOperationException
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.Locale
|
||||
import java.util.concurrent.TimeUnit
|
||||
import kotlin.UnsupportedOperationException
|
||||
|
||||
class LerManga : ParsedHttpSource() {
|
||||
|
||||
|
@ -34,7 +35,8 @@ class LerManga : ParsedHttpSource() {
|
|||
override val supportsLatest = true
|
||||
|
||||
override val client: OkHttpClient = network.cloudflareClient.newBuilder()
|
||||
.rateLimit(1, 2, TimeUnit.SECONDS)
|
||||
.rateLimitHost(baseUrl.toHttpUrl(), 1, 1, TimeUnit.SECONDS)
|
||||
.rateLimitHost(IMG_CDN_URL.toHttpUrl(), 1, 2, TimeUnit.SECONDS)
|
||||
.build()
|
||||
|
||||
private val json: Json by injectLazy()
|
||||
|
@ -42,6 +44,11 @@ class LerManga : ParsedHttpSource() {
|
|||
override fun headersBuilder(): Headers.Builder = Headers.Builder()
|
||||
.add("Referer", "$baseUrl/")
|
||||
|
||||
private fun apiHeadersBuilder(): Headers.Builder = headersBuilder()
|
||||
.add("Accept", "application/json")
|
||||
|
||||
private val apiHeaders by lazy { apiHeadersBuilder().build() }
|
||||
|
||||
override fun popularMangaRequest(page: Int): Request {
|
||||
val path = if (page > 1) "page/$page/" else ""
|
||||
return GET("$baseUrl/mangas/$path?orderby=views&order=desc", headers)
|
||||
|
@ -70,20 +77,37 @@ class LerManga : ParsedHttpSource() {
|
|||
override fun latestUpdatesNextPageSelector(): String? = null
|
||||
|
||||
override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request {
|
||||
if (!query.startsWith(PREFIX_SLUG_SEARCH)) {
|
||||
throw Exception(ERROR_NO_SEARCH_AVAILABLE)
|
||||
if (query.startsWith(PREFIX_SLUG_SEARCH)) {
|
||||
val slug = query.removePrefix(PREFIX_SLUG_SEARCH)
|
||||
val tempManga = SManga.create().apply { url = "/mangas/$slug" }
|
||||
|
||||
return mangaDetailsRequest(tempManga)
|
||||
}
|
||||
|
||||
val slug = query.removePrefix(PREFIX_SLUG_SEARCH)
|
||||
val tempManga = SManga.create().apply { url = "/mangas/$slug" }
|
||||
val apiRequest = "$API_BASE_URL/mangas".toHttpUrl().newBuilder()
|
||||
.addQueryParameter("page", page.toString())
|
||||
.addQueryParameter("search", query)
|
||||
.addQueryParameter("_fields", "title,slug")
|
||||
.build()
|
||||
|
||||
return mangaDetailsRequest(tempManga)
|
||||
return GET(apiRequest, apiHeaders)
|
||||
}
|
||||
|
||||
override fun searchMangaParse(response: Response): MangasPage {
|
||||
val manga = mangaDetailsParse(response)
|
||||
if (response.request.url.queryParameter("slug") != null) {
|
||||
val manga = mangaDetailsParse(response)
|
||||
return MangasPage(listOf(manga), hasNextPage = false)
|
||||
}
|
||||
|
||||
return MangasPage(listOf(manga), hasNextPage = false)
|
||||
val result = response.parseAs<List<LmMangaDto>>()
|
||||
val mangaList = result.map(LmMangaDto::toSManga)
|
||||
|
||||
val currentPage = response.request.url.queryParameter("page")
|
||||
.orEmpty().toIntOrNull() ?: 1
|
||||
val lastPage = response.headers["X-Wp-TotalPages"]!!.toInt()
|
||||
val hasNextPage = currentPage < lastPage
|
||||
|
||||
return MangasPage(mangaList, hasNextPage)
|
||||
}
|
||||
|
||||
override fun searchMangaSelector() = throw UnsupportedOperationException("Not used")
|
||||
|
@ -92,17 +116,27 @@ class LerManga : ParsedHttpSource() {
|
|||
|
||||
override fun searchMangaNextPageSelector() = throw UnsupportedOperationException("Not used")
|
||||
|
||||
override fun mangaDetailsParse(document: Document): SManga = SManga.create().apply {
|
||||
val infoElement = document.selectFirst("div.capitulo_recente")!!
|
||||
override fun getMangaUrl(manga: SManga): String = baseUrl + manga.url
|
||||
|
||||
title = document.select("title").text().substringBeforeLast(" - ")
|
||||
genre = infoElement.select("ul.genre-list li a")
|
||||
.joinToString { it.text() }
|
||||
description = infoElement.selectFirst("div.boxAnimeSobreLast p:last-child")!!.ownText()
|
||||
thumbnail_url = infoElement.selectFirst("div.capaMangaInfo img")!!.absUrl("src")
|
||||
setUrlWithoutDomain(document.location())
|
||||
override fun mangaDetailsRequest(manga: SManga): Request {
|
||||
val slug = manga.url.removePrefix("/mangas/").removeSuffix("/")
|
||||
|
||||
val apiRequest = "$API_BASE_URL/mangas".toHttpUrl().newBuilder()
|
||||
.addQueryParameter("slug", slug)
|
||||
.addQueryParameter("limit", "1")
|
||||
.addQueryParameter("_embed", "wp:term")
|
||||
.addQueryParameter("_fields", "title,slug,content,_links.wp:term,_embedded.wp:term")
|
||||
.build()
|
||||
|
||||
return GET(apiRequest, apiHeaders)
|
||||
}
|
||||
|
||||
override fun mangaDetailsParse(response: Response): SManga {
|
||||
return response.parseAs<List<LmMangaDto>>().first().toSManga()
|
||||
}
|
||||
|
||||
override fun mangaDetailsParse(document: Document): SManga = throw UnsupportedOperationException("Not used")
|
||||
|
||||
override fun chapterListSelector() = "div.manga-chapters div.single-chapter"
|
||||
|
||||
override fun chapterFromElement(element: Element): SChapter = SChapter.create().apply {
|
||||
|
@ -143,6 +177,10 @@ class LerManga : ParsedHttpSource() {
|
|||
return GET(page.imageUrl!!, newHeaders)
|
||||
}
|
||||
|
||||
private inline fun <reified T> Response.parseAs(): T = use {
|
||||
json.decodeFromString(it.body.string())
|
||||
}
|
||||
|
||||
private fun Element.srcAttr(): String = when {
|
||||
hasAttr("data-src") -> absUrl("data-src")
|
||||
else -> absUrl("src")
|
||||
|
@ -154,6 +192,8 @@ class LerManga : ParsedHttpSource() {
|
|||
}
|
||||
|
||||
companion object {
|
||||
const val API_BASE_URL = "https://lermanga.org/wp-json/wp/v2"
|
||||
const val IMG_CDN_URL = "https://img.lermanga.org"
|
||||
private val PAGES_VARIABLE_REGEX = "var imagens_cap\\s*=\\s*".toRegex()
|
||||
private val DATE_FORMATTER by lazy {
|
||||
SimpleDateFormat("dd-MM-yyyy", Locale("pt", "BR"))
|
||||
|
|
|
@ -0,0 +1,37 @@
|
|||
package eu.kanade.tachiyomi.extension.pt.lermanga
|
||||
|
||||
import eu.kanade.tachiyomi.source.model.SManga
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
import org.jsoup.Jsoup
|
||||
|
||||
@Serializable
|
||||
data class LmMangaDto(
|
||||
val slug: String,
|
||||
val title: LmContentDto,
|
||||
val content: LmContentDto? = null,
|
||||
@SerialName("_embedded") val embedded: LmEmbedDto? = null,
|
||||
) {
|
||||
|
||||
fun toSManga(): SManga = SManga.create().apply {
|
||||
title = this@LmMangaDto.title.rendered
|
||||
thumbnail_url = "${LerManga.IMG_CDN_URL}/${slug.first().uppercase()}/$slug/capa.jpg"
|
||||
description = content?.rendered?.let { Jsoup.parseBodyFragment(it) }?.text()?.trim()
|
||||
genre = embedded?.wpTerm.orEmpty().flatten()
|
||||
.filter { it.taxonomy == "generomanga" }
|
||||
.joinToString { it.name }
|
||||
url = "/mangas/$slug"
|
||||
}
|
||||
}
|
||||
|
||||
@Serializable
|
||||
data class LmContentDto(val rendered: String)
|
||||
|
||||
@Serializable
|
||||
data class LmEmbedDto(@SerialName("wp:term") val wpTerm: List<List<LmTaxonomyDto>>)
|
||||
|
||||
@Serializable
|
||||
data class LmTaxonomyDto(
|
||||
val name: String,
|
||||
val taxonomy: String,
|
||||
)
|
Loading…
Reference in New Issue