From 39ac1f81a6ef8ee1b274e9155f1bce6366463b12 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edgar=20Mej=C3=ADa?= Date: Sun, 28 Mar 2021 14:19:11 -0600 Subject: [PATCH] Fix LectorManga HTTP error 403, Add MangaMx genre filter and config screen (#6323) * Fix LectorManga HTTP error 403 * Add genres filter, config +18 filter. * Fix tachiyomiorg/tachiyomi-extensions#6325 --- src/es/lectormanga/build.gradle | 2 +- .../extension/es/lectormanga/LectorManga.kt | 10 + src/es/mangamx/build.gradle | 2 +- .../tachiyomi/extension/es/mangamx/MangaMx.kt | 202 +++++++++++++----- src/es/tumangaonline/build.gradle | 2 +- .../es/tumangaonline/TuMangaOnline.kt | 4 +- 6 files changed, 163 insertions(+), 59 deletions(-) diff --git a/src/es/lectormanga/build.gradle b/src/es/lectormanga/build.gradle index 5472594c7..daac6ae53 100755 --- a/src/es/lectormanga/build.gradle +++ b/src/es/lectormanga/build.gradle @@ -5,7 +5,7 @@ ext { extName = 'LectorManga' pkgNameSuffix = 'es.lectormanga' extClass = '.LectorManga' - extVersionCode = 15 + extVersionCode = 16 libVersion = '1.2' } diff --git a/src/es/lectormanga/src/eu/kanade/tachiyomi/extension/es/lectormanga/LectorManga.kt b/src/es/lectormanga/src/eu/kanade/tachiyomi/extension/es/lectormanga/LectorManga.kt index 5810cc06b..d79fca478 100755 --- a/src/es/lectormanga/src/eu/kanade/tachiyomi/extension/es/lectormanga/LectorManga.kt +++ b/src/es/lectormanga/src/eu/kanade/tachiyomi/extension/es/lectormanga/LectorManga.kt @@ -17,6 +17,7 @@ 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 okhttp3.Headers import okhttp3.HttpUrl import okhttp3.OkHttpClient import okhttp3.Request @@ -39,6 +40,15 @@ class LectorManga : ConfigurableSource, ParsedHttpSource() { override val supportsLatest = true + private val userAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 " + + "(KHTML, like Gecko) Chrome/80.0.3987.132 Safari/537.36" + + override fun headersBuilder(): Headers.Builder { + return Headers.Builder() + .add("User-Agent", userAgent) + .add("Referer", "$baseUrl/") + } + private val imageCDNUrl = "https://img1.followmanga.com" private val preferences: SharedPreferences by lazy { diff --git a/src/es/mangamx/build.gradle b/src/es/mangamx/build.gradle index 71fc99c70..d73ae5f31 100644 --- a/src/es/mangamx/build.gradle +++ b/src/es/mangamx/build.gradle @@ -5,7 +5,7 @@ ext { extName = 'MangaMx' pkgNameSuffix = 'es.mangamx' extClass = '.MangaMx' - extVersionCode = 9 + extVersionCode = 10 libVersion = '1.2' } diff --git a/src/es/mangamx/src/eu/kanade/tachiyomi/extension/es/mangamx/MangaMx.kt b/src/es/mangamx/src/eu/kanade/tachiyomi/extension/es/mangamx/MangaMx.kt index ddc564d3f..2532f6ac4 100644 --- a/src/es/mangamx/src/eu/kanade/tachiyomi/extension/es/mangamx/MangaMx.kt +++ b/src/es/mangamx/src/eu/kanade/tachiyomi/extension/es/mangamx/MangaMx.kt @@ -1,6 +1,10 @@ package eu.kanade.tachiyomi.extension.es.mangamx +import android.app.Application +import android.content.SharedPreferences import android.net.Uri +import android.support.v7.preference.CheckBoxPreference +import android.support.v7.preference.PreferenceScreen import android.util.Base64 import com.github.salomonbrys.kotson.get import com.github.salomonbrys.kotson.string @@ -8,6 +12,7 @@ import com.google.gson.JsonElement import com.google.gson.JsonParser import eu.kanade.tachiyomi.network.GET import eu.kanade.tachiyomi.network.POST +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 @@ -21,29 +26,36 @@ 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.nio.charset.Charset import java.text.SimpleDateFormat import java.util.Locale -open class MangaMx : ParsedHttpSource() { - - // Info +open class MangaMx : ConfigurableSource, ParsedHttpSource() { override val name = "MangaMx" + override val baseUrl = "https://manga-mx.com" + override val lang = "es" + override val supportsLatest = true + override val client = network.cloudflareClient private var csrfToken = "" - // Popular - - override fun popularMangaRequest(page: Int) = - GET("$baseUrl/directorio?filtro=visitas&p=$page", headers) + override fun popularMangaRequest(page: Int) = GET( + "$baseUrl/directorio?genero=false" + + "&estado=false&filtro=visitas&tipo=false&adulto=${if (hideNSFWContent()) "0" else "false"}&orden=desc&p=$page", + headers + ) override fun popularMangaNextPageSelector() = ".page-item a[rel=next]" + override fun popularMangaSelector() = "#article-div a" + override fun popularMangaFromElement(element: Element): SManga = SManga.create().apply { setUrlWithoutDomain(element.attr("href")) thumbnail_url = element.select("img").attr("data-src") @@ -65,11 +77,12 @@ open class MangaMx : ParsedHttpSource() { return MangasPage(mangas, hasNextPage) } - // Latest - override fun latestUpdatesRequest(page: Int) = GET("$baseUrl/recientes?p=$page", headers) + override fun latestUpdatesNextPageSelector(): String? = popularMangaNextPageSelector() + override fun latestUpdatesSelector() = "div._1bJU3" + override fun latestUpdatesFromElement(element: Element): SManga = SManga.create().apply { thumbnail_url = element.select("img").attr("data-src") element.select("div a").apply { @@ -78,8 +91,6 @@ open class MangaMx : ParsedHttpSource() { } } - // Search - override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request { if (query.isNotBlank()) { val formBody = FormBody.Builder() @@ -91,28 +102,32 @@ open class MangaMx : ParsedHttpSource() { return POST("$baseUrl/buscar", searchHeaders, formBody) } else { val uri = Uri.parse("$baseUrl/directorio").buildUpon() - // Append uri filters + + uri.appendQueryParameter( + "adulto", + if (hideNSFWContent()) { "0" } else { "1" } + ) + for (filter in filters) { when (filter) { is StatusFilter -> uri.appendQueryParameter( filter.name.toLowerCase(Locale.ROOT), statusArray[filter.state].second ) - is FilterFilter -> uri.appendQueryParameter( - filter.name.toLowerCase(Locale.ROOT), - filterArray[filter.state].second - ) + is SortBy -> { + uri.appendQueryParameter("filtro", sortables[filter.state!!.index].second) + uri.appendQueryParameter( + "orden", + if (filter.state!!.ascending) { "asc" } else { "desc" } + ) + } is TypeFilter -> uri.appendQueryParameter( filter.name.toLowerCase(Locale.ROOT), typedArray[filter.state].second ) - is AdultFilter -> uri.appendQueryParameter( - filter.name.toLowerCase(Locale.ROOT), - adultArray[filter.state].second - ) - is OrderFilter -> uri.appendQueryParameter( - filter.name.toLowerCase(Locale.ROOT), - orderArray[filter.state].second + is GenreFilter -> uri.appendQueryParameter( + "genero", + genresArray[filter.state].second ) } } @@ -122,8 +137,11 @@ open class MangaMx : ParsedHttpSource() { } override fun searchMangaNextPageSelector(): String? = popularMangaNextPageSelector() + override fun searchMangaSelector(): String = popularMangaSelector() + override fun searchMangaFromElement(element: Element): SManga = popularMangaFromElement(element) + override fun searchMangaParse(response: Response): MangasPage { if (!response.isSuccessful) throw Exception("Búsqueda fallida ${response.code()}") if ("directorio" in response.request().url().toString()) { @@ -154,8 +172,6 @@ open class MangaMx : ParsedHttpSource() { thumbnail_url = jsonElement["img"].string.replace("/thumb", "/cover") } - // Details - override fun mangaDetailsParse(document: Document): SManga { val manga = SManga.create() manga.thumbnail_url = document.select("img[src*=cover]").attr("abs:src") @@ -175,8 +191,6 @@ open class MangaMx : ParsedHttpSource() { return manga } - // Chapters - override fun chapterListSelector(): String = "div#c_list a" override fun chapterFromElement(element: Element): SChapter = SChapter.create().apply { @@ -190,13 +204,10 @@ open class MangaMx : ParsedHttpSource() { return SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.US).parse(date)?.time ?: 0 } - // Pages - override fun pageListParse(document: Document): List { val encoded = document.select("script:containsData(unicap)").firstOrNull() ?.data()?.substringAfter("'")?.substringBefore("'")?.reversed() ?: throw Exception("unicap not found") - val drop = encoded.length % 4 val decoded = Base64.decode(encoded.dropLast(drop), Base64.DEFAULT).toString(Charset.defaultCharset()) val path = decoded.substringBefore("||") @@ -207,43 +218,35 @@ open class MangaMx : ParsedHttpSource() { override fun imageUrlParse(document: Document) = throw Exception("Not Used") - // Filters - override fun getFilterList() = FilterList( - Filter.Header("NOTA: ¡Ignorado si usa la búsqueda de texto!"), + Filter.Header("NOTA: Se ignoran si se usa el buscador"), Filter.Separator(), + SortBy("Ordenar por", sortables), StatusFilter("Estado", statusArray), - FilterFilter("Filtro", filterArray), TypeFilter("Tipo", typedArray), - AdultFilter("Adulto", adultArray), - OrderFilter("Orden", orderArray) + GenreFilter("Géneros", genresArray) ) private class StatusFilter(name: String, values: Array>) : Filter.Select(name, values.map { it.first }.toTypedArray()) - private class FilterFilter(name: String, values: Array>) : - Filter.Select(name, values.map { it.first }.toTypedArray()) - private class TypeFilter(name: String, values: Array>) : Filter.Select(name, values.map { it.first }.toTypedArray()) - private class AdultFilter(name: String, values: Array>) : + private class GenreFilter(name: String, values: Array>) : Filter.Select(name, values.map { it.first }.toTypedArray()) - private class OrderFilter(name: String, values: Array>) : - Filter.Select(name, values.map { it.first }.toTypedArray()) + class SortBy(name: String, values: Array>) : Filter.Sort( + name, values.map { it.first }.toTypedArray(), + Selection(0, false) + ) private val statusArray = arrayOf( Pair("Estado", "false"), Pair("En desarrollo", "1"), Pair("Completo", "0") ) - private val filterArray = arrayOf( - Pair("Visitas", "visitas"), - Pair("Recientes", "id"), - Pair("Alfabético", "nombre") - ) + private val typedArray = arrayOf( Pair("Todo", "false"), Pair("Mangas", "0"), @@ -252,13 +255,104 @@ open class MangaMx : ParsedHttpSource() { Pair("Manhuas", "3"), Pair("Novelas", "4") ) - private val adultArray = arrayOf( - Pair("Filtro adulto", "false"), - Pair("No mostrar +18", "0"), - Pair("Mostrar +18", "1") + + private val sortables = arrayOf( + Pair("Visitas", "visitas"), + Pair("Recientes", "id"), + Pair("Alfabético", "nombre"), ) - private val orderArray = arrayOf( - Pair("Descendente", "desc"), - Pair("Ascendente", "asc") + + /** + * Url: https://manga-mx.com/directorio/ + * Last check: 27/03/2021 + * JS script: Array.from(document.querySelectorAll('select[name="genero"] option')) + * .map(a => `Pair("${a.innerText}", "${a.value}")`).join(',\n') + */ + private val genresArray = arrayOf( + Pair("Todos", "false"), + Pair("Comedia", "1"), + Pair("Drama", "2"), + Pair("Acción", "3"), + Pair("Escolar", "4"), + Pair("Romance", "5"), + Pair("Ecchi", "6"), + Pair("Aventura", "7"), + Pair("Shōnen", "8"), + Pair("Shōjo", "9"), + Pair("Deportes", "10"), + Pair("Psicológico", "11"), + Pair("Fantasía", "12"), + Pair("Mecha", "13"), + Pair("Gore", "14"), + Pair("Yaoi", "15"), + Pair("Yuri", "16"), + Pair("Misterio", "17"), + Pair("Sobrenatural", "18"), + Pair("Seinen", "19"), + Pair("Ficción", "20"), + Pair("Harem", "21"), + Pair("Webtoon", "25"), + Pair("Histórico", "27"), + Pair("Músical", "30"), + Pair("Ciencia ficción", "31"), + Pair("Shōjo-ai", "32"), + Pair("Josei", "33"), + Pair("Magia", "34"), + Pair("Artes Marciales", "35"), + Pair("Horror", "36"), + Pair("Demonios", "37"), + Pair("Supervivencia", "38"), + Pair("Recuentos de la vida", "39"), + Pair("Shōnen ai", "40"), + Pair("Militar", "41"), + Pair("Eroge", "42"), + Pair("Isekai", "43") ) + + private val preferences: SharedPreferences by lazy { + Injekt.get().getSharedPreferences("source_$id", 0x0000) + } + + override fun setupPreferenceScreen(screen: androidx.preference.PreferenceScreen) { + + val contentPref = androidx.preference.CheckBoxPreference(screen.context).apply { + key = CONTENT_PREF + title = CONTENT_PREF_TITLE + summary = CONTENT_PREF_SUMMARY + setDefaultValue(CONTENT_PREF_DEFAULT_VALUE) + + setOnPreferenceChangeListener { _, newValue -> + val checkValue = newValue as Boolean + preferences.edit().putBoolean(CONTENT_PREF, checkValue).commit() + } + } + + screen.addPreference(contentPref) + } + + override fun setupPreferenceScreen(screen: PreferenceScreen) { + + val contentPref = CheckBoxPreference(screen.context).apply { + key = CONTENT_PREF + title = CONTENT_PREF_TITLE + summary = CONTENT_PREF_SUMMARY + setDefaultValue(CONTENT_PREF_DEFAULT_VALUE) + + setOnPreferenceChangeListener { _, newValue -> + val checkValue = newValue as Boolean + preferences.edit().putBoolean(CONTENT_PREF, checkValue).commit() + } + } + + screen.addPreference(contentPref) + } + + private fun hideNSFWContent(): Boolean = preferences.getBoolean(CONTENT_PREF, CONTENT_PREF_DEFAULT_VALUE) + + companion object { + private const val CONTENT_PREF = "showNSFWContent" + private const val CONTENT_PREF_TITLE = "Ocultar contenido +18" + private const val CONTENT_PREF_SUMMARY = "Ocultar el contenido erótico en explorar y buscar, no funciona en los mangas recientes." + private const val CONTENT_PREF_DEFAULT_VALUE = false + } } diff --git a/src/es/tumangaonline/build.gradle b/src/es/tumangaonline/build.gradle index 2abc02d6f..3edb99872 100644 --- a/src/es/tumangaonline/build.gradle +++ b/src/es/tumangaonline/build.gradle @@ -5,7 +5,7 @@ ext { extName = 'TuMangaOnline' pkgNameSuffix = 'es.tumangaonline' extClass = '.TuMangaOnline' - extVersionCode = 32 + extVersionCode = 33 libVersion = '1.2' } diff --git a/src/es/tumangaonline/src/eu/kanade/tachiyomi/extension/es/tumangaonline/TuMangaOnline.kt b/src/es/tumangaonline/src/eu/kanade/tachiyomi/extension/es/tumangaonline/TuMangaOnline.kt index e8c15d951..cb9b95da6 100644 --- a/src/es/tumangaonline/src/eu/kanade/tachiyomi/extension/es/tumangaonline/TuMangaOnline.kt +++ b/src/es/tumangaonline/src/eu/kanade/tachiyomi/extension/es/tumangaonline/TuMangaOnline.kt @@ -140,8 +140,8 @@ class TuMangaOnline : ConfigurableSource, ParsedHttpSource() { is GenreList -> { filter.state.forEach { genre -> when (genre.state) { - Filter.TriState.STATE_INCLUDE -> url.addQueryParameter("exclude_genders[]", genre.id) - Filter.TriState.STATE_EXCLUDE -> url.addQueryParameter("genders[]", genre.id) + Filter.TriState.STATE_INCLUDE -> url.addQueryParameter("genders[]", genre.id) + Filter.TriState.STATE_EXCLUDE -> url.addQueryParameter("exclude_genders[]", genre.id) } } }