From 73dcd89df2c156667f8cc6a29fb472436cf84f6d Mon Sep 17 00:00:00 2001 From: Claudemirovsky <63046606+Claudemirovsky@users.noreply.github.com> Date: Thu, 21 Sep 2023 20:01:34 -0300 Subject: [PATCH 01/10] fix: Fix popular anime page --- .../animeextension/de/animebase/AnimeBase.kt | 15 +-- .../de/animebase/CookieInterceptor.kt | 103 ------------------ 2 files changed, 4 insertions(+), 114 deletions(-) delete mode 100644 src/de/animebase/src/eu/kanade/tachiyomi/animeextension/de/animebase/CookieInterceptor.kt diff --git a/src/de/animebase/src/eu/kanade/tachiyomi/animeextension/de/animebase/AnimeBase.kt b/src/de/animebase/src/eu/kanade/tachiyomi/animeextension/de/animebase/AnimeBase.kt index dc7c2fbeb5..d89685edc5 100644 --- a/src/de/animebase/src/eu/kanade/tachiyomi/animeextension/de/animebase/AnimeBase.kt +++ b/src/de/animebase/src/eu/kanade/tachiyomi/animeextension/de/animebase/AnimeBase.kt @@ -1,7 +1,6 @@ package eu.kanade.tachiyomi.animeextension.de.animebase import android.app.Application -import android.content.SharedPreferences import androidx.preference.ListPreference import androidx.preference.PreferenceScreen import eu.kanade.tachiyomi.animesource.ConfigurableAnimeSource @@ -35,17 +34,13 @@ class AnimeBase : ConfigurableAnimeSource, ParsedAnimeHttpSource() { override val client: OkHttpClient = network.cloudflareClient - private val preferences: SharedPreferences by lazy { + private val preferences by lazy { Injekt.get().getSharedPreferences("source_$id", 0x0000) } override fun popularAnimeSelector(): String = "div.table-responsive a" - override fun popularAnimeRequest(page: Int): Request { - val cookieInterceptor = client.newBuilder().addInterceptor(CookieInterceptor(baseUrl)).build() - val headers = cookieInterceptor.newCall(GET(baseUrl)).execute().request.headers - return GET("$baseUrl/favorites", headers = headers) - } + override fun popularAnimeRequest(page: Int) = GET("$baseUrl/favorites", headers) override fun popularAnimeFromElement(element: Element): SAnime { val anime = SAnime.create() @@ -162,11 +157,9 @@ class AnimeBase : ConfigurableAnimeSource, ParsedAnimeHttpSource() { override fun searchAnimeSelector(): String = "div.col-lg-9.col-md-8 div.box-body a" override fun searchAnimeRequest(page: Int, query: String, filters: AnimeFilterList): Request { - val cookieInterceptor = client.newBuilder().addInterceptor(CookieInterceptor(baseUrl)).build() - val headers = cookieInterceptor.newCall(GET(baseUrl)).execute().request.headers - val token = client.newCall(GET("$baseUrl/searching", headers = headers)).execute().asJsoup() + val token = client.newCall(GET("$baseUrl/searching", headers)).execute().asJsoup() .select("div.box-body form input[name=\"_token\"]").attr("value") - return POST("$baseUrl/searching", headers = headers, body = "_token=$token&_token=$token&name_serie=$query&jahr=".toRequestBody("application/x-www-form-urlencoded".toMediaType())) + return POST("$baseUrl/searching", headers, body = "_token=$token&_token=$token&name_serie=$query&jahr=".toRequestBody("application/x-www-form-urlencoded".toMediaType())) } // Details diff --git a/src/de/animebase/src/eu/kanade/tachiyomi/animeextension/de/animebase/CookieInterceptor.kt b/src/de/animebase/src/eu/kanade/tachiyomi/animeextension/de/animebase/CookieInterceptor.kt deleted file mode 100644 index 327e4ebdf9..0000000000 --- a/src/de/animebase/src/eu/kanade/tachiyomi/animeextension/de/animebase/CookieInterceptor.kt +++ /dev/null @@ -1,103 +0,0 @@ -package eu.kanade.tachiyomi.animeextension.de.animebase - -import android.annotation.SuppressLint -import android.app.Application -import android.os.Handler -import android.os.Looper -import android.webkit.WebResourceRequest -import android.webkit.WebResourceResponse -import android.webkit.WebView -import android.webkit.WebViewClient -import eu.kanade.tachiyomi.network.GET -import okhttp3.Headers.Companion.toHeaders -import okhttp3.Interceptor -import okhttp3.Request -import okhttp3.Response -import uy.kohesive.injekt.Injekt -import uy.kohesive.injekt.api.get -import java.util.concurrent.CountDownLatch -import java.util.concurrent.TimeUnit - -class CookieInterceptor(private val baseUrl: String) : Interceptor { - - private val context = Injekt.get() - private val handler by lazy { Handler(Looper.getMainLooper()) } - - override fun intercept(chain: Interceptor.Chain): Response { - val originalRequest = chain.request() - - val newRequest = resolveWithWebView(originalRequest) ?: throw Exception("bruh") - - return chain.proceed(newRequest) - } - - @SuppressLint("SetJavaScriptEnabled") - private fun resolveWithWebView(request: Request): Request? { - // We need to lock this thread until the WebView finds the challenge solution url, because - // OkHttp doesn't support asynchronous interceptors. - val latch = CountDownLatch(1) - - var webView: WebView? = null - - val origRequestUrl = request.url.toString() - val headers = request.headers.toMultimap().mapValues { it.value.getOrNull(0) ?: "" }.toMutableMap() - - var newRequest: Request? = null - - handler.post { - val webview = WebView(context) - webView = webview - with(webview.settings) { - javaScriptEnabled = true - domStorageEnabled = true - databaseEnabled = true - useWideViewPort = false - loadWithOverviewMode = false - userAgentString = request.header("User-Agent") - ?: "\"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.150 Safari/537.36 Edg/88.0.705.63\"" - } - - webview.webViewClient = object : WebViewClient() { - override fun shouldInterceptRequest( - view: WebView, - request: WebResourceRequest, - ): WebResourceResponse? { - handler.post { - webView?.clearHistory() - } - if (request.url.toString().contains("/?d=1") && request.url.toString().contains("anime-base")) { - newRequest = GET(baseUrl, request.requestHeaders.toHeaders()) - latch.countDown() - } - if (request.url.toString().contains("favicon.ico") && request.url.toString().contains("anime-base")) { - newRequest = GET(baseUrl, request.requestHeaders.toHeaders()) - latch.countDown() - } - if (request.url.toString().contains("/search") || request.url.toString().contains("/search?d=1")) { - newRequest = GET(request.url.toString(), request.requestHeaders.toHeaders()) - latch.countDown() - } - if (request.url.toString().contains("/favorites") || request.url.toString().contains("/favorites?d=1")) { - newRequest = GET(request.url.toString(), request.requestHeaders.toHeaders()) - latch.countDown() - } - return super.shouldInterceptRequest(view, request) - } - } - - webView?.loadUrl(origRequestUrl, headers) - } - - // Wait a reasonable amount of time to retrieve the solution. The minimum should be - // around 4 seconds but it can take more due to slow networks or server issues. - latch.await(12, TimeUnit.SECONDS) - - handler.post { - webView?.stopLoading() - webView?.destroy() - webView = null - } - - return newRequest - } -} From 427f3e0f8c4eec02cdcedd482ed8d11574f230e4 Mon Sep 17 00:00:00 2001 From: Claudemirovsky <63046606+Claudemirovsky@users.noreply.github.com> Date: Thu, 21 Sep 2023 20:15:49 -0300 Subject: [PATCH 02/10] fix: Fix search anime page --- .../animeextension/de/animebase/AnimeBase.kt | 29 +++++++++++-------- 1 file changed, 17 insertions(+), 12 deletions(-) diff --git a/src/de/animebase/src/eu/kanade/tachiyomi/animeextension/de/animebase/AnimeBase.kt b/src/de/animebase/src/eu/kanade/tachiyomi/animeextension/de/animebase/AnimeBase.kt index d89685edc5..57d9c0bddc 100644 --- a/src/de/animebase/src/eu/kanade/tachiyomi/animeextension/de/animebase/AnimeBase.kt +++ b/src/de/animebase/src/eu/kanade/tachiyomi/animeextension/de/animebase/AnimeBase.kt @@ -12,10 +12,9 @@ import eu.kanade.tachiyomi.animesource.online.ParsedAnimeHttpSource import eu.kanade.tachiyomi.network.GET import eu.kanade.tachiyomi.network.POST import eu.kanade.tachiyomi.util.asJsoup -import okhttp3.MediaType.Companion.toMediaType +import okhttp3.FormBody import okhttp3.OkHttpClient import okhttp3.Request -import okhttp3.RequestBody.Companion.toRequestBody import okhttp3.Response import org.jsoup.nodes.Document import org.jsoup.nodes.Element @@ -142,13 +141,9 @@ class AnimeBase : ConfigurableAnimeSource, ParsedAnimeHttpSource() { override fun searchAnimeFromElement(element: Element): SAnime { val anime = SAnime.create() - if (!element.text().contains("PainterCrowe")) { - anime.setUrlWithoutDomain(element.attr("href")) - anime.thumbnail_url = element.select("div.thumbnail img").attr("src") - anime.title = element.select("div.caption h3").text() - } else { - throw Exception("Keine Ergebnisse gefunden") - } + anime.setUrlWithoutDomain(element.attr("href")) + anime.thumbnail_url = element.select("div.thumbnail img").attr("src") + anime.title = element.select("div.caption h3").text() return anime } @@ -156,10 +151,20 @@ class AnimeBase : ConfigurableAnimeSource, ParsedAnimeHttpSource() { override fun searchAnimeSelector(): String = "div.col-lg-9.col-md-8 div.box-body a" + private val searchToken by lazy { + client.newCall(GET("$baseUrl/searching", headers)).execute() + .use { it.asJsoup() } + .selectFirst("form > input[name=_token]")!! + .attr("value") + } override fun searchAnimeRequest(page: Int, query: String, filters: AnimeFilterList): Request { - val token = client.newCall(GET("$baseUrl/searching", headers)).execute().asJsoup() - .select("div.box-body form input[name=\"_token\"]").attr("value") - return POST("$baseUrl/searching", headers, body = "_token=$token&_token=$token&name_serie=$query&jahr=".toRequestBody("application/x-www-form-urlencoded".toMediaType())) + val body = FormBody.Builder() + .add("_token", searchToken) + .add("_token", searchToken) + .add("name_serie", query) + .add("jahr", "") + .build() + return POST("$baseUrl/searching", headers, body) } // Details From aa17052ea3d7b76260f6d526e9a48cfec4087e96 Mon Sep 17 00:00:00 2001 From: Claudemirovsky <63046606+Claudemirovsky@users.noreply.github.com> Date: Thu, 21 Sep 2023 21:30:39 -0300 Subject: [PATCH 03/10] fix!: Fix episode list (but breaks old links xD) --- .../animeextension/de/animebase/AnimeBase.kt | 62 +++---------------- 1 file changed, 9 insertions(+), 53 deletions(-) diff --git a/src/de/animebase/src/eu/kanade/tachiyomi/animeextension/de/animebase/AnimeBase.kt b/src/de/animebase/src/eu/kanade/tachiyomi/animeextension/de/animebase/AnimeBase.kt index 57d9c0bddc..39ee4ab699 100644 --- a/src/de/animebase/src/eu/kanade/tachiyomi/animeextension/de/animebase/AnimeBase.kt +++ b/src/de/animebase/src/eu/kanade/tachiyomi/animeextension/de/animebase/AnimeBase.kt @@ -52,62 +52,18 @@ class AnimeBase : ConfigurableAnimeSource, ParsedAnimeHttpSource() { override fun popularAnimeNextPageSelector(): String? = null // episodes + override fun episodeListParse(response: Response) = + super.episodeListParse(response).reversed() - override fun episodeListSelector() = throw Exception("not used") + override fun episodeListSelector() = "div.tab-content > div > div.panel" - override fun episodeListParse(response: Response): List { - val document = response.asJsoup() - val episodeList = mutableListOf() - val episodeElement = document.select( - "div.tab-content #gersub div.panel, div.tab-content #filme div.panel button[${ - if (document.select("div.tab-content #filme div.panel button[data-dubbed=\"0\"]").isNullOrEmpty()) { - "data-dubbed=\"1\"" - } else { - "data-dubbed=\"0\"" - } - }][data-hoster=\"1\"], div.tab-content #specials div.panel button[data-dubbed=\"0\"][data-hoster=\"1\"]", - ) - episodeElement.forEach { - val episode = episodeFromElement(it) - episodeList.add(episode) - } - return episodeList.reversed() - } - - override fun episodeFromElement(element: Element): SEpisode { - val episode = SEpisode.create() - val id = element.select("button[data-hoster=\"1\"]").attr("data-serieid") - val epnum = element.select("button[data-hoster=\"1\"]").attr("data-folge") - val host = element.select("button[data-hoster=\"1\"]").attr("data-hoster") - if (element.attr("data-dubbed").contains("1")) { - if (element.attr("data-special").contains("2")) { - episode.episode_number = 1F - episode.name = "Film $epnum" - episode.setUrlWithoutDomain("/episode/$id/$epnum/1/$host/2") - } - } else { - if (element.select("button[data-hoster=\"1\"]").attr("data-special").contains("2")) { - episode.episode_number = 1F - episode.name = "Film ${epnum.toInt() - 1}" - episode.setUrlWithoutDomain("/episode/$id/$epnum/0/$host/2") - } else { - val season = element.select("button[data-hoster=\"1\"]").attr("data-embedcontainer") - .substringAfter("-").substringBefore("-") - episode.name = "Staffel $season Folge $epnum : " + element.select("h3.panel-title").text() - .substringAfter(": ") - .replace("Filler!", "").replace(" ", "") - episode.episode_number = element.select("button[data-hoster=\"1\"]").attr("data-folge").toFloat() - episode.setUrlWithoutDomain("/episode/$id/$epnum/0/$host/0") - } - if (element.select("button[data-hoster=\"1\"]").attr("data-special").contains("1")) { - episode.episode_number = 1F - episode.name = "Special ${epnum.toInt() - 1}" - episode.setUrlWithoutDomain("/episode/$id/$epnum/0/$host/1") - } - } - return episode + override fun episodeFromElement(element: Element) = SEpisode.create().apply { + val epname = element.selectFirst("h3")?.text() ?: "Episode 1" + name = epname + episode_number = epname.substringBefore(":").substringAfter(" ").toFloatOrNull() ?: 0F + val selectorClass = element.classNames().first { it.startsWith("episode-div") } + setUrlWithoutDomain(element.baseUri() + "?selector=div.panel.$selectorClass") } - // Video Extractor override fun videoListParse(response: Response) = From 481a580964a673b163a3d5830f1002a919d96caf Mon Sep 17 00:00:00 2001 From: Claudemirovsky <63046606+Claudemirovsky@users.noreply.github.com> Date: Thu, 21 Sep 2023 23:31:33 -0300 Subject: [PATCH 04/10] refactor: General refactoration --- src/de/animebase/build.gradle | 7 +- .../animeextension/de/animebase/AnimeBase.kt | 178 +++++++++--------- 2 files changed, 93 insertions(+), 92 deletions(-) diff --git a/src/de/animebase/build.gradle b/src/de/animebase/build.gradle index c931cda2af..a57bd29a04 100644 --- a/src/de/animebase/build.gradle +++ b/src/de/animebase/build.gradle @@ -1,6 +1,7 @@ -apply plugin: 'com.android.application' -apply plugin: 'kotlin-android' -apply plugin: 'kotlinx-serialization' +plugins { + alias(libs.plugins.android.application) + alias(libs.plugins.kotlin.android) +} ext { extName = 'Anime-Base' diff --git a/src/de/animebase/src/eu/kanade/tachiyomi/animeextension/de/animebase/AnimeBase.kt b/src/de/animebase/src/eu/kanade/tachiyomi/animeextension/de/animebase/AnimeBase.kt index 39ee4ab699..632cce7a16 100644 --- a/src/de/animebase/src/eu/kanade/tachiyomi/animeextension/de/animebase/AnimeBase.kt +++ b/src/de/animebase/src/eu/kanade/tachiyomi/animeextension/de/animebase/AnimeBase.kt @@ -37,82 +37,36 @@ class AnimeBase : ConfigurableAnimeSource, ParsedAnimeHttpSource() { Injekt.get().getSharedPreferences("source_$id", 0x0000) } - override fun popularAnimeSelector(): String = "div.table-responsive a" + // ============================== Popular =============================== + override fun popularAnimeSelector() = "div.table-responsive a" override fun popularAnimeRequest(page: Int) = GET("$baseUrl/favorites", headers) - override fun popularAnimeFromElement(element: Element): SAnime { - val anime = SAnime.create() - anime.setUrlWithoutDomain(element.attr("href")) - anime.thumbnail_url = element.select("div.thumbnail img").attr("src") - anime.title = element.select("div.thumbnail div.caption h3").text() - return anime + override fun popularAnimeFromElement(element: Element) = SAnime.create().apply { + setUrlWithoutDomain(element.attr("href").replace("/link/", "/anime/")) + thumbnail_url = element.selectFirst("div.thumbnail img")?.absUrl("src") + title = element.selectFirst("div.caption h3")!!.text() } - override fun popularAnimeNextPageSelector(): String? = null + override fun popularAnimeNextPageSelector() = null - // episodes - override fun episodeListParse(response: Response) = - super.episodeListParse(response).reversed() - - override fun episodeListSelector() = "div.tab-content > div > div.panel" - - override fun episodeFromElement(element: Element) = SEpisode.create().apply { - val epname = element.selectFirst("h3")?.text() ?: "Episode 1" - name = epname - episode_number = epname.substringBefore(":").substringAfter(" ").toFloatOrNull() ?: 0F - val selectorClass = element.classNames().first { it.startsWith("episode-div") } - setUrlWithoutDomain(element.baseUri() + "?selector=div.panel.$selectorClass") - } - // Video Extractor - - override fun videoListParse(response: Response) = - throw Exception("This source only uses StreamSB as video hoster, and StreamSB is down.") - - override fun List