From 4c38b2413ef84d8c2369ded1c3ecd1018c770ed1 Mon Sep 17 00:00:00 2001 From: Claudemirovsky <63046606+Claudemirovsky@users.noreply.github.com> Date: Wed, 15 Mar 2023 06:21:41 -0300 Subject: [PATCH] Anitube: Fix video extractor (#1393) * Fix anitube extractor * General refactor * Bump version --- src/pt/anitube/build.gradle | 3 +- .../animeextension/pt/anitube/Anitube.kt | 202 ++++++++---------- .../pt/anitube/extractors/AnitubeExtractor.kt | 16 +- 3 files changed, 101 insertions(+), 120 deletions(-) diff --git a/src/pt/anitube/build.gradle b/src/pt/anitube/build.gradle index ab76deb64..14650bac5 100644 --- a/src/pt/anitube/build.gradle +++ b/src/pt/anitube/build.gradle @@ -5,9 +5,8 @@ ext { extName = 'Anitube' pkgNameSuffix = 'pt.anitube' extClass = '.Anitube' - extVersionCode = 8 + extVersionCode = 9 libVersion = '13' } - apply from: "$rootDir/common.gradle" diff --git a/src/pt/anitube/src/eu/kanade/tachiyomi/animeextension/pt/anitube/Anitube.kt b/src/pt/anitube/src/eu/kanade/tachiyomi/animeextension/pt/anitube/Anitube.kt index 2fdce5979..dbfc5667a 100644 --- a/src/pt/anitube/src/eu/kanade/tachiyomi/animeextension/pt/anitube/Anitube.kt +++ b/src/pt/anitube/src/eu/kanade/tachiyomi/animeextension/pt/anitube/Anitube.kt @@ -13,21 +13,15 @@ import eu.kanade.tachiyomi.animesource.model.SEpisode import eu.kanade.tachiyomi.animesource.model.Video import eu.kanade.tachiyomi.animesource.online.ParsedAnimeHttpSource import eu.kanade.tachiyomi.network.GET -import eu.kanade.tachiyomi.network.asObservableSuccess import eu.kanade.tachiyomi.util.asJsoup -import kotlinx.serialization.json.Json -import okhttp3.Headers import okhttp3.OkHttpClient import okhttp3.Request import okhttp3.Response import org.jsoup.nodes.Document import org.jsoup.nodes.Element -import rx.Observable import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.api.get -import uy.kohesive.injekt.injectLazy import java.lang.Exception -import java.text.ParseException import java.text.SimpleDateFormat import java.util.Locale @@ -43,27 +37,25 @@ class Anitube : ConfigurableAnimeSource, ParsedAnimeHttpSource() { override val client: OkHttpClient = network.cloudflareClient - private val json: Json by injectLazy() - private val preferences: SharedPreferences by lazy { Injekt.get().getSharedPreferences("source_$id", 0x0000) } - override fun headersBuilder(): Headers.Builder = Headers.Builder() + override fun headersBuilder() = super.headersBuilder() .add("Referer", baseUrl) .add("Accept-Language", ACCEPT_LANGUAGE) - // Popular + // ============================== Popular =============================== override fun popularAnimeSelector(): String = "div.lista_de_animes div.ani_loop_item_img > a" override fun popularAnimeRequest(page: Int): Request = GET("$baseUrl/anime/page/$page") override fun popularAnimeFromElement(element: Element): SAnime { - val anime: SAnime = SAnime.create() - anime.setUrlWithoutDomain(element.attr("href")) - val img = element.selectFirst("img")!! - anime.title = img.attr("title") - anime.thumbnail_url = img.attr("src") - return anime + return SAnime.create().apply { + setUrlWithoutDomain(element.attr("href")) + val img = element.selectFirst("img")!! + title = img.attr("title") + thumbnail_url = img.attr("src") + } } override fun popularAnimeNextPageSelector(): String = "a.page-numbers:contains(Próximo)" @@ -77,17 +69,19 @@ class Anitube : ConfigurableAnimeSource, ParsedAnimeHttpSource() { return AnimesPage(animes, hasNextPage) } - // Episodes + // ============================== Episodes ============================== override fun episodeListSelector(): String = "div.animepag_episodios_container > div.animepag_episodios_item > a" private fun getAllEps(response: Response): List { - val doc = if (response.request.url.toString().contains("/video/")) { - getRealDoc(response.asJsoup()) - } else { response.asJsoup() } + val doc = response.asJsoup().let { + if (response.request.url.toString().contains("/video/")) { + getRealDoc(it) + } else { it } + } val epElementList = doc.select(episodeListSelector()) val epList = mutableListOf() - epList.addAll(epElementList.map { episodeFromElement(it) }) + epList.addAll(epElementList.map(::episodeFromElement)) if (hasNextPage(doc)) { val next = doc.selectFirst(popularAnimeNextPageSelector())!!.attr("href") val request = GET(baseUrl + next) @@ -96,31 +90,34 @@ class Anitube : ConfigurableAnimeSource, ParsedAnimeHttpSource() { } return epList } + override fun episodeListParse(response: Response): List { return getAllEps(response).reversed() } override fun episodeFromElement(element: Element): SEpisode { - val episode = SEpisode.create() - episode.setUrlWithoutDomain(element.attr("href")) - episode.episode_number = try { - element.selectFirst("div.animepag_episodios_item_views")!! + return SEpisode.create().apply { + setUrlWithoutDomain(element.attr("href")) + episode_number = runCatching { + element.selectFirst("div.animepag_episodios_item_views")!! + .text() + .substringAfter(" ") + .toFloat() + }.getOrDefault(0F) + name = element.selectFirst("div.animepag_episodios_item_nome")!!.text() + date_upload = element.selectFirst("div.animepag_episodios_item_date")!! .text() - .substringAfter(" ").toFloat() - } catch (e: NumberFormatException) { 0F } - episode.name = element.selectFirst("div.animepag_episodios_item_nome")!!.text() - episode.date_upload = element.selectFirst("div.animepag_episodios_item_date")!! - .text().toDate() - return episode + .toDate() + } } - // Video links + // ============================ Video Links ============================= override fun videoListParse(response: Response) = AnitubeExtractor.getVideoList(response) override fun videoListSelector() = throw Exception("not used") override fun videoFromElement(element: Element) = throw Exception("not used") override fun videoUrlParse(document: Document) = throw Exception("not used") - // Search + // =============================== Search =============================== override fun searchAnimeFromElement(element: Element) = throw Exception("not used") override fun searchAnimeNextPageSelector() = throw Exception("not used") @@ -128,23 +125,15 @@ class Anitube : ConfigurableAnimeSource, ParsedAnimeHttpSource() { override fun searchAnimeParse(response: Response): AnimesPage = popularAnimeParse(response) - override fun fetchSearchAnime(page: Int, query: String, filters: AnimeFilterList): Observable { - val params = AnitubeFilters.getSearchParameters(filters) - return client.newCall(searchAnimeRequest(page, query, params)) - .asObservableSuccess() - .map { response -> - searchAnimeParse(response) - } - } + override fun getFilterList(): AnimeFilterList = AnitubeFilters.filterList - override fun searchAnimeRequest(page: Int, query: String, filters: AnimeFilterList) = throw Exception("not used") - - private fun searchAnimeRequest(page: Int, query: String, filters: AnitubeFilters.FilterSearchParams): Request { + override fun searchAnimeRequest(page: Int, query: String, filters: AnimeFilterList): Request { return if (query.isBlank()) { - val season = filters.season - val genre = filters.genre - val year = filters.year - val char = filters.initialChar + val params = AnitubeFilters.getSearchParameters(filters) + val season = params.season + val genre = params.genre + val year = params.year + val char = params.initialChar when { !season.isBlank() -> GET("$baseUrl/temporada/$season/$year") !genre.isBlank() -> GET("$baseUrl/genero/$genre/page/$page/${char.replace("todos", "")}") @@ -155,43 +144,44 @@ class Anitube : ConfigurableAnimeSource, ParsedAnimeHttpSource() { } } - // Anime Details + // =========================== Anime Details ============================ override fun animeDetailsParse(document: Document): SAnime { - val anime = SAnime.create() val doc = getRealDoc(document) - val content = doc.selectFirst("div.anime_container_content")!! - val infos = content.selectFirst("div.anime_infos")!! + return SAnime.create().apply { + setUrlWithoutDomain(doc.location()) + val content = doc.selectFirst("div.anime_container_content")!! + val infos = content.selectFirst("div.anime_infos")!! - anime.title = doc.selectFirst("div.anime_container_titulo")!!.text() - anime.thumbnail_url = content.selectFirst("img")!!.attr("src") - anime.genre = infos.getInfo("Gêneros") - anime.author = infos.getInfo("Autor") - anime.artist = infos.getInfo("Estúdio") - anime.status = parseStatus(infos.getInfo("Status")) + title = doc.selectFirst("div.anime_container_titulo")!!.text() + thumbnail_url = content.selectFirst("img")!!.attr("src") + genre = infos.getInfo("Gêneros") + author = infos.getInfo("Autor") + artist = infos.getInfo("Estúdio") + status = parseStatus(infos.getInfo("Status")) - var desc = doc.selectFirst("div.sinopse_container_content")!!.text() + "\n" - infos.getInfo("Ano")?.let { desc += "\nAno: $it" } - infos.getInfo("Direção")?.let { desc += "\nDireção: $it" } - infos.getInfo("Episódios")?.let { desc += "\nEpisódios: $it" } - infos.getInfo("Temporada")?.let { desc += "\nTemporada: $it" } - infos.getInfo("Alternativo")?.let { desc += "\nTítulo alternativo: $it" } - anime.description = desc + val infoItems = listOf("Ano", "Direção", "Episódios", "Temporada", "Título Alternativo") - return anime + description = buildString { + append(doc.selectFirst("div.sinopse_container_content")!!.text() + "\n") + infoItems.forEach { item -> + infos.getInfo(item)?.let { append("\n$item: $it") } + } + } + } } - // Latest + // =============================== Latest =============================== override fun latestUpdatesNextPageSelector(): String = popularAnimeNextPageSelector() override fun latestUpdatesSelector(): String = "div.mContainer_content.threeItensPerContent > div.epi_loop_item" override fun latestUpdatesFromElement(element: Element): SAnime { - val anime = SAnime.create() - val img = element.selectFirst("img")!! - anime.setUrlWithoutDomain(element.selectFirst("a")!!.attr("href")) - anime.title = img.attr("title") - anime.thumbnail_url = img.attr("src") - return anime + return SAnime.create().apply { + val img = element.selectFirst("img")!! + setUrlWithoutDomain(element.selectFirst("a")!!.attr("href")) + title = img.attr("title") + thumbnail_url = img.attr("src") + } } override fun latestUpdatesRequest(page: Int): Request = GET("$baseUrl/?page=$page") @@ -205,14 +195,14 @@ class Anitube : ConfigurableAnimeSource, ParsedAnimeHttpSource() { return AnimesPage(animes, hasNextPage) } - // Settings + // ============================== Settings ============================== override fun setupPreferenceScreen(screen: PreferenceScreen) { val videoQualityPref = ListPreference(screen.context).apply { - key = "preferred_quality" - title = "Qualidade preferida" - entries = QUALITIES - entryValues = QUALITIES - setDefaultValue(QUALITIES.last()) + key = PREF_QUALITY_KEY + title = PREF_QUALITY_TITLE + entries = PREF_QUALITY_VALUES + entryValues = PREF_QUALITY_VALUES + setDefaultValue(PREF_QUALITY_DEFAULT) summary = "%s" setOnPreferenceChangeListener { _, newValue -> val selected = newValue as String @@ -224,10 +214,7 @@ class Anitube : ConfigurableAnimeSource, ParsedAnimeHttpSource() { screen.addPreference(videoQualityPref) } - // Filters - override fun getFilterList(): AnimeFilterList = AnitubeFilters.filterList - - // New functions + // ============================= Utilities ============================== private fun getRealDoc(document: Document): Document { val menu = document.selectFirst("div.controles_ep > a[href] > i.spr.listaEP") if (menu != null) { @@ -252,10 +239,10 @@ class Anitube : ConfigurableAnimeSource, ParsedAnimeHttpSource() { val pagination = document.selectFirst("div.pagination") val items = pagination?.select("a.page-numbers") if (pagination == null || items!!.size < 2) return false - val currentPage: Int = pagination.selectFirst("a.page-numbers.current") + val currentPage = pagination.selectFirst("a.page-numbers.current") ?.attr("href") ?.toPageNum() ?: 1 - val lastPage: Int = items[items.lastIndex - 1] + val lastPage = items[items.lastIndex - 1] .attr("href") .toPageNum() return currentPage != lastPage @@ -269,47 +256,38 @@ class Anitube : ConfigurableAnimeSource, ParsedAnimeHttpSource() { } catch (e: NumberFormatException) { 1 } private fun Element.getInfo(key: String): String? { - val elementB: Element? = this.selectFirst("b:contains($key)") - val parent = elementB?.parent() - val elementsA = parent?.select("a") - val text = if (elementsA?.size == 0) { - parent.text()?.replace(elementB.html(), "")?.trim() + val parent = selectFirst("b:contains($key)")?.parent() + val genres = parent?.select("a") + val text = if (genres?.size == 0) { + parent.ownText() } else { - elementsA?.joinToString(", ") { it.text() } + genres?.joinToString(", ") { it.text() } } if (text == "") return null return text } override fun List