diff --git a/src/pt/anidong/src/eu/kanade/tachiyomi/animeextension/pt/anidong/AniDong.kt b/src/pt/anidong/src/eu/kanade/tachiyomi/animeextension/pt/anidong/AniDong.kt index da8f6d7ac5..943d357d24 100644 --- a/src/pt/anidong/src/eu/kanade/tachiyomi/animeextension/pt/anidong/AniDong.kt +++ b/src/pt/anidong/src/eu/kanade/tachiyomi/animeextension/pt/anidong/AniDong.kt @@ -13,7 +13,6 @@ import eu.kanade.tachiyomi.network.GET import eu.kanade.tachiyomi.network.POST import eu.kanade.tachiyomi.network.asObservableSuccess import eu.kanade.tachiyomi.util.asJsoup -import kotlinx.serialization.decodeFromString import kotlinx.serialization.json.Json import okhttp3.FormBody import okhttp3.Request @@ -43,6 +42,10 @@ class AniDong : ParsedAnimeHttpSource() { } // ============================== Popular =============================== + override fun popularAnimeRequest(page: Int) = GET(baseUrl) + + override fun popularAnimeSelector() = "article.top10_animes_item > a" + override fun popularAnimeFromElement(element: Element) = SAnime.create().apply { setUrlWithoutDomain(element.attr("href")) title = element.attr("title") @@ -50,42 +53,90 @@ class AniDong : ParsedAnimeHttpSource() { } override fun popularAnimeNextPageSelector() = null - override fun popularAnimeRequest(page: Int) = GET(baseUrl) - override fun popularAnimeSelector() = "article.top10_animes_item > a" - // ============================== Episodes ============================== - override fun episodeFromElement(element: Element): SEpisode { - throw UnsupportedOperationException("Not used.") + // =============================== Latest =============================== + override fun latestUpdatesRequest(page: Int) = GET("$baseUrl/lancamentos/page/$page/") + + override fun latestUpdatesSelector() = "article.main_content_article > a" + + override fun latestUpdatesFromElement(element: Element) = popularAnimeFromElement(element) + + override fun latestUpdatesNextPageSelector() = "div.paginacao > a.next" + + // =============================== Search =============================== + override fun fetchSearchAnime(page: Int, query: String, filters: AnimeFilterList): Observable { + return if (query.startsWith(PREFIX_SEARCH)) { // URL intent handler + val id = query.removePrefix(PREFIX_SEARCH) + client.newCall(GET("$baseUrl/anime/$id")) + .asObservableSuccess() + .map(::searchAnimeByIdParse) + } else { + super.fetchSearchAnime(page, query, filters) + } } - override fun episodeListSelector(): String { - throw UnsupportedOperationException("Not used.") + private fun searchAnimeByIdParse(response: Response): AnimesPage { + val details = animeDetailsParse(response.use { it.asJsoup() }) + return AnimesPage(listOf(details), false) } - override fun episodeListParse(response: Response): List { - val doc = getRealDoc(response.asJsoup()) + override fun getFilterList() = AniDongFilters.FILTER_LIST + + private val nonce by lazy { + client.newCall(GET("$baseUrl/?js_global=1&ver=6.2.2")).execute() + .use { it.body.string() } + .substringAfter("search_nonce") + .substringAfter("'") + .substringBefore("'") + } + + override fun searchAnimeRequest(page: Int, query: String, filters: AnimeFilterList): Request { + val params = AniDongFilters.getSearchParameters(filters) - val id = doc.selectFirst("link[rel=shortlink]")!!.attr("href").substringAfter("=") val body = FormBody.Builder() - .add("action", "show_videos") - .add("anime_id", id) - .build() + .add("letra", "") + .add("action", "show_animes_ajax") + .add("nome", query) + .add("status", params.status) + .add("formato", params.format) + .add("search_nonce", nonce) + .add("paged", page.toString()) + .apply { + params.genres.forEach { add("generos[]", it) } + }.build() - val res = client.newCall(POST("$baseUrl/api", headers = apiHeaders, body = body)).execute() - val data = json.decodeFromString(res.body.string()) + return POST("$baseUrl/wp-admin/admin-ajax.php", headers = apiHeaders, body = body) + } - return buildList { - data.episodes.forEach { add(episodeFromObject(it, "Episódio")) } - data.movies.forEach { add(episodeFromObject(it, "Filme")) } - data.ovas.forEach { add(episodeFromObject(it, "OVA")) } - sortByDescending { it.episode_number } + override fun searchAnimeParse(response: Response): AnimesPage { + val searchData: SearchResultDto = response.use { it.body.string() } + .takeIf { it.trim() != "402" } + ?.let(json::decodeFromString) + ?: return AnimesPage(emptyList(), false) + + val animes = searchData.animes.map { + SAnime.create().apply { + setUrlWithoutDomain(it.url) + title = it.title + thumbnail_url = it.thumbnail_url + } } + + val hasNextPage = searchData.pages > 1 && searchData.animes.size == 10 + + return AnimesPage(animes, hasNextPage) } - private fun episodeFromObject(episode: EpisodeDto, prefix: String) = SEpisode.create().apply { - setUrlWithoutDomain(episode.epi_url) - episode_number = episode.epi_num.toFloatOrNull() ?: 0F - name = "$prefix ${episode.epi_num}" + override fun searchAnimeSelector(): String { + throw UnsupportedOperationException("Not used.") + } + + override fun searchAnimeFromElement(element: Element): SAnime { + throw UnsupportedOperationException("Not used.") + } + + override fun searchAnimeNextPageSelector(): String? { + throw UnsupportedOperationException("Not used.") } // =========================== Anime Details ============================ @@ -95,9 +146,9 @@ class AniDong : ParsedAnimeHttpSource() { setUrlWithoutDomain(doc.location()) title = infos.selectFirst("div > h3")!!.ownText() - thumbnail_url = infos.selectFirst("img")!!.attr("src") + thumbnail_url = infos.selectFirst("img")?.attr("src") genre = infos.select("div[itemprop=genre] a").eachText().joinToString() - artist = infos.selectFirst("div[itemprop=productionCompany]")!!.text() + artist = infos.selectFirst("div[itemprop=productionCompany]")?.text() status = doc.selectFirst("div:contains(Status) span")?.text().let { when { @@ -109,19 +160,55 @@ class AniDong : ParsedAnimeHttpSource() { } description = buildString { - infos.selectFirst("div.anime_name + div.anime_info")?.text()?.let { + infos.selectFirst("div.anime_name + div.anime_info")?.text()?.also { append("Nomes alternativos: $it\n") } - doc.selectFirst("div[itemprop=description]")?.text()?.let { + doc.selectFirst("div[itemprop=description]")?.text()?.also { append("\n$it") } } } + // ============================== Episodes ============================== + override fun episodeListSelector(): String { + throw UnsupportedOperationException("Not used.") + } + + override fun episodeFromElement(element: Element): SEpisode { + throw UnsupportedOperationException("Not used.") + } + + override fun episodeListParse(response: Response): List { + val doc = getRealDoc(response.use { it.asJsoup() }) + + val id = doc.selectFirst("link[rel=shortlink]")!!.attr("href").substringAfter("=") + val body = FormBody.Builder() + .add("action", "show_videos") + .add("anime_id", id) + .build() + + val res = client.newCall(POST("$baseUrl/api", apiHeaders, body)).execute() + .use { it.body.string() } + val data = json.decodeFromString(res) + + return buildList { + data.episodes.forEach { add(episodeFromObject(it, "Episódio")) } + data.movies.forEach { add(episodeFromObject(it, "Filme")) } + data.ovas.forEach { add(episodeFromObject(it, "OVA")) } + sortByDescending { it.episode_number } + } + } + + private fun episodeFromObject(episode: EpisodeDto, prefix: String) = SEpisode.create().apply { + setUrlWithoutDomain(episode.epi_url) + episode_number = episode.epi_num.toFloatOrNull() ?: 0F + name = "$prefix ${episode.epi_num}" + } + // ============================ Video Links ============================= override fun videoListParse(response: Response): List