Readd search to LM (#19480)

Readd search to LM.
This commit is contained in:
Alessandro Jean 2023-12-29 17:52:31 -03:00 committed by GitHub
parent 5c71163ef6
commit 09458dcd86
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 96 additions and 19 deletions

View File

@ -6,7 +6,7 @@ ext {
extName = 'Ler Mangá'
pkgNameSuffix = 'pt.lermanga'
extClass = '.LerManga'
extVersionCode = 3
extVersionCode = 4
isNsfw = true
}

View File

@ -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"))

View File

@ -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,
)