From dc9bfd773ff246ac6892af9ec4801bec051793cd Mon Sep 17 00:00:00 2001 From: Claudemirovsky <63046606+Claudemirovsky@users.noreply.github.com> Date: Tue, 8 Aug 2023 19:30:40 -0300 Subject: [PATCH] fix(en/aniwatch): Fix video extractor + general refactor (#2028) Hates cloudflare Hates the antichrist Hates obfuscated javascript Hates kotlin Hates aniwatch Hates the femboi role --- src/en/aniwatch/build.gradle | 12 +- .../animeextension/en/zoro/AniWatch.kt | 400 ++++++++---------- .../animeextension/en/zoro/dto/AniWatchDto.kt | 23 + .../en/zoro/extractors/AniWatchExtractor.kt | 76 ++-- .../animeextension/en/zoro/utils/Decryptor.kt | 74 ---- .../en/zoro/utils/FindPassword.kt | 64 --- .../animeextension/en/zoro/utils/JSONUtil.kt | 76 ---- 7 files changed, 247 insertions(+), 478 deletions(-) create mode 100644 src/en/aniwatch/src/eu/kanade/tachiyomi/animeextension/en/zoro/dto/AniWatchDto.kt delete mode 100644 src/en/aniwatch/src/eu/kanade/tachiyomi/animeextension/en/zoro/utils/Decryptor.kt delete mode 100644 src/en/aniwatch/src/eu/kanade/tachiyomi/animeextension/en/zoro/utils/FindPassword.kt delete mode 100644 src/en/aniwatch/src/eu/kanade/tachiyomi/animeextension/en/zoro/utils/JSONUtil.kt diff --git a/src/en/aniwatch/build.gradle b/src/en/aniwatch/build.gradle index 62a946f61..0d044b8a1 100644 --- a/src/en/aniwatch/build.gradle +++ b/src/en/aniwatch/build.gradle @@ -1,17 +1,21 @@ -apply plugin: 'com.android.application' -apply plugin: 'kotlin-android' +plugins { + alias(libs.plugins.android.application) + alias(libs.plugins.kotlin.android) + alias(libs.plugins.kotlin.serialization) +} ext { extName = 'AniWatch.to' pkgNameSuffix = 'en.zoro' extClass = '.AniWatch' - extVersionCode = 32 + extVersionCode = 33 libVersion = '13' } dependencies { implementation(project(':lib-streamtape-extractor')) - implementation(project(':lib-streamsb-extractor')) + implementation(project(':lib-cryptoaes')) + implementation(project(':lib-playlist-utils')) } apply from: "$rootDir/common.gradle" diff --git a/src/en/aniwatch/src/eu/kanade/tachiyomi/animeextension/en/zoro/AniWatch.kt b/src/en/aniwatch/src/eu/kanade/tachiyomi/animeextension/en/zoro/AniWatch.kt index d69a5b3f6..86860ba66 100644 --- a/src/en/aniwatch/src/eu/kanade/tachiyomi/animeextension/en/zoro/AniWatch.kt +++ b/src/en/aniwatch/src/eu/kanade/tachiyomi/animeextension/en/zoro/AniWatch.kt @@ -5,8 +5,8 @@ import android.content.SharedPreferences import androidx.preference.ListPreference import androidx.preference.PreferenceScreen import androidx.preference.SwitchPreferenceCompat +import eu.kanade.tachiyomi.animeextension.en.zoro.dto.VideoDto import eu.kanade.tachiyomi.animeextension.en.zoro.extractors.AniWatchExtractor -import eu.kanade.tachiyomi.animeextension.en.zoro.utils.JSONUtil import eu.kanade.tachiyomi.animesource.ConfigurableAnimeSource import eu.kanade.tachiyomi.animesource.model.AnimeFilterList import eu.kanade.tachiyomi.animesource.model.AnimesPage @@ -15,7 +15,7 @@ import eu.kanade.tachiyomi.animesource.model.SEpisode import eu.kanade.tachiyomi.animesource.model.Track import eu.kanade.tachiyomi.animesource.model.Video import eu.kanade.tachiyomi.animesource.online.ParsedAnimeHttpSource -import eu.kanade.tachiyomi.lib.streamsbextractor.StreamSBExtractor +import eu.kanade.tachiyomi.lib.playlistutils.PlaylistUtils import eu.kanade.tachiyomi.lib.streamtapeextractor.StreamTapeExtractor import eu.kanade.tachiyomi.network.GET import eu.kanade.tachiyomi.network.asObservableSuccess @@ -23,14 +23,12 @@ import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.async import kotlinx.coroutines.awaitAll import kotlinx.coroutines.runBlocking +import kotlinx.serialization.Serializable +import kotlinx.serialization.decodeFromString import kotlinx.serialization.json.Json -import kotlinx.serialization.json.JsonObject -import kotlinx.serialization.json.jsonArray -import kotlinx.serialization.json.jsonObject -import kotlinx.serialization.json.jsonPrimitive import okhttp3.Headers import okhttp3.HttpUrl -import okhttp3.HttpUrl.Companion.toHttpUrlOrNull +import okhttp3.HttpUrl.Companion.toHttpUrl import okhttp3.OkHttpClient import okhttp3.Request import okhttp3.Response @@ -59,6 +57,8 @@ class AniWatch : ConfigurableAnimeSource, ParsedAnimeHttpSource() { private val json: Json by injectLazy() + private val ajaxRoute by lazy { if (baseUrl == "https://kaido.to") "" else "/v2" } + private val preferences: SharedPreferences by lazy { Injekt.get().getSharedPreferences("source_$id", 0x0000) } @@ -77,120 +77,169 @@ class AniWatch : ConfigurableAnimeSource, ParsedAnimeHttpSource() { override fun popularAnimeNextPageSelector(): String = "li.page-item a[title=Next]" + // =============================== Latest =============================== + override fun latestUpdatesNextPageSelector() = popularAnimeNextPageSelector() + + override fun latestUpdatesFromElement(element: Element) = popularAnimeFromElement(element) + + override fun latestUpdatesRequest(page: Int) = GET("$baseUrl/top-airing") + + override fun latestUpdatesSelector() = popularAnimeSelector() + + // =============================== Search =============================== + override fun searchAnimeFromElement(element: Element) = popularAnimeFromElement(element) + + override fun searchAnimeNextPageSelector() = popularAnimeNextPageSelector() + + override fun searchAnimeSelector() = popularAnimeSelector() + + override fun fetchSearchAnime(page: Int, query: String, filters: AnimeFilterList): Observable { + return if (query.startsWith(PREFIX_SEARCH)) { + val slug = query.removePrefix(PREFIX_SEARCH) + client.newCall(GET("$baseUrl/$slug")) + .asObservableSuccess() + .map(::searchAnimeBySlugParse) + } else { + super.fetchSearchAnime(page, query, filters) + } + } + + private fun searchAnimeBySlugParse(response: Response): AnimesPage { + val details = animeDetailsParse(response) + return AnimesPage(listOf(details), false) + } + + override fun searchAnimeRequest(page: Int, query: String, filters: AnimeFilterList): Request { + val params = AniWatchFilters.getSearchParameters(filters) + val endpoint = if (query.isEmpty()) "filter" else "search" + val url = "$baseUrl/$endpoint".toHttpUrl().newBuilder() + .addQueryParameter("page", page.toString()) + .addIfNotBlank("keyword", query) + .addIfNotBlank("type", params.type) + .addIfNotBlank("status", params.status) + .addIfNotBlank("rated", params.rated) + .addIfNotBlank("score", params.score) + .addIfNotBlank("season", params.season) + .addIfNotBlank("language", params.language) + .addIfNotBlank("sort", params.sort) + .addIfNotBlank("sy", params.start_year) + .addIfNotBlank("sm", params.start_month) + .addIfNotBlank("sd", params.start_day) + .addIfNotBlank("ey", params.end_year) + .addIfNotBlank("em", params.end_month) + .addIfNotBlank("ed", params.end_day) + .addIfNotBlank("genres", params.genres) + .build() + + return GET(url.toString()) + } + + override fun getFilterList() = AniWatchFilters.FILTER_LIST + + // =========================== Anime Details ============================ + override fun animeDetailsParse(document: Document) = SAnime.create().apply { + val info = document.selectFirst("div.anisc-info")!! + val detail = document.selectFirst("div.anisc-detail")!! + thumbnail_url = document.selectFirst("div.anisc-poster img")!!.attr("src") + title = detail.selectFirst("h2")!!.attr("data-jname") + author = info.getInfo("Studios:") + status = parseStatus(info.getInfo("Status:")) + genre = info.getInfo("Genres:", isList = true) + + description = buildString { + info.getInfo("Overview:")?.also { append(it + "\n") } + + detail.select("div.film-stats div.tick-dub").eachText().also { + append("\nLanguage: " + it.joinToString()) + } + + info.getInfo("Aired:", full = true)?.also(::append) + info.getInfo("Premiered:", full = true)?.also(::append) + info.getInfo("Synonyms:", full = true)?.also(::append) + info.getInfo("Japanese:", full = true)?.also(::append) + } + } + // ============================== Episodes ============================== override fun episodeListSelector() = "ul#episode_page li a" override fun episodeListRequest(anime: SAnime): Request { val id = anime.url.substringAfterLast("-") val referer = Headers.headersOf("Referer", baseUrl + anime.url) - val ajaxRoute = if (baseUrl == "https://kaido.to") "" else "/v2" - return GET("$baseUrl/ajax$ajaxRoute/episode/list/$id", referer) } override fun episodeListParse(response: Response): List { - val data = response.body.string() - .substringAfter("\"html\":\"") - .substringBefore("