From d6f01fea0bfe6d6f3661b8a357ddf91bc6047635 Mon Sep 17 00:00:00 2001 From: bapeey <90949336+bapeey@users.noreply.github.com> Date: Mon, 26 Feb 2024 07:41:46 -0500 Subject: [PATCH] MangaEsp: Update domain and add alternative names (#1470) * Update domain * Get comics from next data (api has old results) * Add alternative names * Change name creation logic Co-authored-by: AwkwardPeak7 <48650614+AwkwardPeak7@users.noreply.github.com> * Missing replace --------- Co-authored-by: AwkwardPeak7 <48650614+AwkwardPeak7@users.noreply.github.com> --- src/es/mangaesp/build.gradle | 2 +- .../extension/es/mangaesp/MangaEsp.kt | 74 ++++++--------- .../extension/es/mangaesp/MangaEspDto.kt | 93 ++++++++++++------- 3 files changed, 90 insertions(+), 79 deletions(-) diff --git a/src/es/mangaesp/build.gradle b/src/es/mangaesp/build.gradle index 5c587dc3e..a60d2405a 100644 --- a/src/es/mangaesp/build.gradle +++ b/src/es/mangaesp/build.gradle @@ -1,7 +1,7 @@ ext { extName = 'MangaEsp' extClass = '.MangaEsp' - extVersionCode = 1 + extVersionCode = 2 isNsfw = false } diff --git a/src/es/mangaesp/src/eu/kanade/tachiyomi/extension/es/mangaesp/MangaEsp.kt b/src/es/mangaesp/src/eu/kanade/tachiyomi/extension/es/mangaesp/MangaEsp.kt index 15a980ef5..70e3ca30d 100644 --- a/src/es/mangaesp/src/eu/kanade/tachiyomi/extension/es/mangaesp/MangaEsp.kt +++ b/src/es/mangaesp/src/eu/kanade/tachiyomi/extension/es/mangaesp/MangaEsp.kt @@ -29,9 +29,9 @@ class MangaEsp : HttpSource() { override val name = "MangaEsp" - override val baseUrl = "https://mangaesp.co" + override val baseUrl = "https://mangaesp.net" - private val apiBaseUrl = "https://apis.mangaesp.co" + private val apiBaseUrl = "https://apis.mangaesp.net" override val lang = "es" @@ -57,13 +57,7 @@ class MangaEsp : HttpSource() { val topWeekly = responseData.response.topWeekly.flatten().map { it.data } val topMonthly = responseData.response.topMonthly.flatten().map { it.data } - val mangas = (topDaily + topWeekly + topMonthly).distinctBy { it.slug }.map { series -> - SManga.create().apply { - title = series.name - thumbnail_url = series.thumbnail - url = "/ver/${series.slug}" - } - } + val mangas = (topDaily + topWeekly + topMonthly).distinctBy { it.slug }.map { it.toSManga() } return MangasPage(mangas, false) } @@ -73,13 +67,7 @@ class MangaEsp : HttpSource() { override fun latestUpdatesParse(response: Response): MangasPage { val responseData = json.decodeFromString(response.body.string()) - val mangas = responseData.response.map { series -> - SManga.create().apply { - title = series.name - thumbnail_url = series.thumbnail - url = "/ver/${series.slug}" - } - } + val mangas = responseData.response.map { it.toSManga() } return MangasPage(mangas, false) } @@ -100,13 +88,17 @@ class MangaEsp : HttpSource() { } } - override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request = GET("$baseUrl/api/comics", headers) + override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request = GET("$baseUrl/comics", headers) override fun searchMangaParse(response: Response): MangasPage = throw UnsupportedOperationException() private fun searchMangaParse(response: Response, page: Int, query: String, filters: FilterList): MangasPage { - val responseData = json.decodeFromString(response.body.string()) - comicsList = responseData.response.toMutableList() + val document = response.asJsoup() + val script = document.select("script:containsData(self.__next_f.push)").joinToString { it.data() } + val jsonString = MANGA_LIST_REGEX.find(script)?.groupValues?.get(1) + ?: throw Exception("No se pudo encontrar la lista de comics") + val unescapedJson = jsonString.unescape() + comicsList = json.decodeFromString>(unescapedJson).toMutableList() return parseComicsList(page, query, filters) } @@ -118,7 +110,11 @@ class MangaEsp : HttpSource() { if (query.isNotBlank()) { if (query.length < 2) throw Exception("La búsqueda debe tener al menos 2 caracteres") - filteredList.addAll(comicsList.filter { it.name.contains(query, ignoreCase = true) }) + filteredList.addAll( + comicsList.filter { + it.name.contains(query, ignoreCase = true) || it.alternativeName?.contains(query, ignoreCase = true) == true + }, + ) } else { filteredList.addAll(comicsList) } @@ -154,7 +150,7 @@ class MangaEsp : HttpSource() { return MangasPage( filteredList.subList((page - 1) * MANGAS_PER_PAGE, min(page * MANGAS_PER_PAGE, filteredList.size)) - .map { it.toSimpleSManga() }, + .map { it.toSManga() }, hasNextPage, ) } @@ -163,42 +159,24 @@ class MangaEsp : HttpSource() { val responseBody = response.body.string() val mangaDetailsJson = MANGA_DETAILS_REGEX.find(responseBody)?.groupValues?.get(1) ?: throw Exception("No se pudo encontrar los detalles del manga") - val unescapedJson = mangaDetailsJson.replace("\\", "") + val unescapedJson = mangaDetailsJson.unescape() - val series = json.decodeFromString(unescapedJson) - return SManga.create().apply { - title = series.name - thumbnail_url = series.thumbnail - description = series.synopsis - genre = series.genders.joinToString { it.gender.name } - author = series.authors.joinToString { it.author.name } - artist = series.artists.joinToString { it.artist.name } - } + return json.decodeFromString(unescapedJson).toSMangaDetails() } override fun chapterListParse(response: Response): List { val responseBody = response.body.string() val mangaDetailsJson = MANGA_DETAILS_REGEX.find(responseBody)?.groupValues?.get(1) ?: throw Exception("No se pudo encontrar la lista de capítulos") - val unescapedJson = mangaDetailsJson.replace("\\", "") + val unescapedJson = mangaDetailsJson.unescape() val series = json.decodeFromString(unescapedJson) - return series.chapters.map { chapter -> - SChapter.create().apply { - name = if (chapter.name.isNullOrBlank()) { - "Capítulo ${chapter.number.toString().removeSuffix(".0")}" - } else { - "Capítulo ${chapter.number.toString().removeSuffix(".0")} - ${chapter.name}" - } - date_upload = runCatching { dateFormat.parse(chapter.date)?.time }.getOrNull() ?: 0L - url = "/ver/${series.slug}/${chapter.slug}" - } - } + return series.chapters.map { it.toSChapter(series.slug, dateFormat) } } override fun pageListParse(response: Response): List { val document = response.asJsoup() return document.select("main.contenedor.read img").mapIndexed { i, img -> - Page(i, "", img.imgAttr()) + Page(i, imageUrl = img.imgAttr()) } } @@ -254,8 +232,14 @@ class MangaEsp : HttpSource() { else -> attr("abs:src") } + private fun String.unescape(): String { + return UNESCAPE_REGEX.replace(this, "$1") + } + companion object { - private val MANGA_DETAILS_REGEX = """self.__next_f.push\(.*data\\":(\{.*lastChapters.*\}).*numFollow""".toRegex() + private val UNESCAPE_REGEX = """\\(.)""".toRegex() + private val MANGA_LIST_REGEX = """self\.__next_f\.push\(.*data\\":(\[.*trending.*])\}""".toRegex() + private val MANGA_DETAILS_REGEX = """self\.__next_f\.push\(.*data\\":(\{.*lastChapters.*\}).*numFollow""".toRegex() private const val MANGAS_PER_PAGE = 15 } } diff --git a/src/es/mangaesp/src/eu/kanade/tachiyomi/extension/es/mangaesp/MangaEspDto.kt b/src/es/mangaesp/src/eu/kanade/tachiyomi/extension/es/mangaesp/MangaEspDto.kt index 7be67694f..2b72b9647 100644 --- a/src/es/mangaesp/src/eu/kanade/tachiyomi/extension/es/mangaesp/MangaEspDto.kt +++ b/src/es/mangaesp/src/eu/kanade/tachiyomi/extension/es/mangaesp/MangaEspDto.kt @@ -1,91 +1,118 @@ package eu.kanade.tachiyomi.extension.es.mangaesp +import eu.kanade.tachiyomi.source.model.SChapter import eu.kanade.tachiyomi.source.model.SManga import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable +import java.text.SimpleDateFormat @Serializable -data class TopSeriesDto( +class TopSeriesDto( val response: TopSeriesResponseDto, ) @Serializable -data class LastUpdatesDto( +class LastUpdatesDto( val response: List, ) @Serializable -data class ComicsDto( - val response: List, -) - -@Serializable -data class TopSeriesResponseDto( +class TopSeriesResponseDto( @SerialName("mensual") val topMonthly: List>, @SerialName("semanal") val topWeekly: List>, @SerialName("diario") val topDaily: List>, ) @Serializable -data class PayloadSeriesDto( +class PayloadSeriesDto( @SerialName("project") val data: SeriesDto, ) @Serializable -data class SeriesDto( +class SeriesDto( val name: String, + val alternativeName: String? = null, val slug: String, - @SerialName("sinopsis") val synopsis: String? = null, - @SerialName("urlImg") val thumbnail: String? = null, - val isVisible: Boolean, + @SerialName("sinopsis") private val synopsis: String? = null, + @SerialName("urlImg") private val thumbnail: String? = null, @SerialName("actualizacionCap") val lastChapterDate: String? = null, @SerialName("created_at") val createdAt: String? = null, @SerialName("state_id") val status: Int? = 0, - val genders: List = emptyList(), - @SerialName("lastChapters") val chapters: List = emptyList(), - val trending: SeriesTrendingDto? = null, - @SerialName("autors") val authors: List = emptyList(), - val artists: List = emptyList(), + private val genders: List = emptyList(), + @SerialName("lastChapters") val chapters: List = emptyList(), + val trending: TrendingDto? = null, + @SerialName("autors") private val authors: List = emptyList(), + private val artists: List = emptyList(), ) { - fun toSimpleSManga(): SManga { + fun toSManga(): SManga { return SManga.create().apply { title = name thumbnail_url = thumbnail url = "/ver/$slug" } } + + fun toSMangaDetails(): SManga { + return SManga.create().apply { + title = name + thumbnail_url = thumbnail + description = synopsis + if (!alternativeName.isNullOrBlank()) { + if (!description.isNullOrBlank()) description += "\n\n" + description += "Nombres alternativos: $alternativeName" + } + genre = genders.joinToString { it.gender.name } + author = authors.joinToString { it.author.name } + artist = artists.joinToString { it.artist.name } + } + } } @Serializable -data class SeriesTrendingDto( +class TrendingDto( @SerialName("visitas") val views: Int? = 0, ) @Serializable -data class SeriesGenderDto( - val gender: SeriesDetailDataNameDto, +class GenderDto( + val gender: DetailDataNameDto, ) @Serializable -data class SeriesAuthorDto( - @SerialName("autor") val author: SeriesDetailDataNameDto, +class AuthorDto( + @SerialName("autor") val author: DetailDataNameDto, ) @Serializable -data class SeriesArtistDto( - val artist: SeriesDetailDataNameDto, +class ArtistDto( + val artist: DetailDataNameDto, ) @Serializable -data class SeriesDetailDataNameDto( +class DetailDataNameDto( val name: String, ) @Serializable -data class SeriesChapterDto( - @SerialName("num") val number: Float, - val name: String? = null, - val slug: String, - @SerialName("created_at") val date: String, -) +class ChapterDto( + @SerialName("num") private val number: Float, + private val name: String? = null, + private val slug: String, + @SerialName("created_at") private val date: String, +) { + fun toSChapter(seriesSlug: String, dateFormat: SimpleDateFormat): SChapter { + return SChapter.create().apply { + name = "Capítulo ${number.toString().removeSuffix(".0")}" + if (!this@ChapterDto.name.isNullOrBlank()) { + name += " - ${this@ChapterDto.name}" + } + date_upload = try { + dateFormat.parse(date)?.time ?: 0L + } catch (e: Exception) { + 0L + } + url = "/ver/$seriesSlug/$slug" + } + } +}