diff --git a/src/en/wcofun/build.gradle b/src/en/wcofun/build.gradle index 217d64149..6c2baf205 100644 --- a/src/en/wcofun/build.gradle +++ b/src/en/wcofun/build.gradle @@ -5,7 +5,7 @@ ext { extName = 'Wcofun' pkgNameSuffix = 'en.wcofun' extClass = '.Wcofun' - extVersionCode = 5 + extVersionCode = 6 libVersion = '13' } diff --git a/src/en/wcofun/src/eu/kanade/tachiyomi/animeextension/en/wcofun/RedirectInterceptor.kt b/src/en/wcofun/src/eu/kanade/tachiyomi/animeextension/en/wcofun/RedirectInterceptor.kt new file mode 100644 index 000000000..3177341e2 --- /dev/null +++ b/src/en/wcofun/src/eu/kanade/tachiyomi/animeextension/en/wcofun/RedirectInterceptor.kt @@ -0,0 +1,89 @@ +package eu.kanade.tachiyomi.animeextension.en.wcofun + +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 RedirectInterceptor : 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 + blockNetworkLoads = true + 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? { + if (request.url.toString().contains("https://www.wcofun.net")) { + 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 + } +} diff --git a/src/en/wcofun/src/eu/kanade/tachiyomi/animeextension/en/wcofun/Wcofun.kt b/src/en/wcofun/src/eu/kanade/tachiyomi/animeextension/en/wcofun/Wcofun.kt index e1da0fab7..130e44a44 100644 --- a/src/en/wcofun/src/eu/kanade/tachiyomi/animeextension/en/wcofun/Wcofun.kt +++ b/src/en/wcofun/src/eu/kanade/tachiyomi/animeextension/en/wcofun/Wcofun.kt @@ -36,7 +36,7 @@ class Wcofun : ConfigurableAnimeSource, ParsedAnimeHttpSource() { override val name = "Wcofun" - override val baseUrl = "https://www.wcofun.com/" + override val baseUrl = "https://www.wcofun.net" override val lang = "en" @@ -50,18 +50,23 @@ class Wcofun : ConfigurableAnimeSource, ParsedAnimeHttpSource() { Injekt.get().getSharedPreferences("source_$id", 0x0000) } - override fun popularAnimeSelector(): String = "div.sidebar-titles li a" + override fun popularAnimeSelector(): String = "#sidebar_right2 ul.items li" - override fun popularAnimeRequest(page: Int): Request = GET(baseUrl) + override fun popularAnimeRequest(page: Int): Request { + val interceptor = client.newBuilder().addInterceptor(RedirectInterceptor()).build() + val headers = interceptor.newCall(GET(baseUrl)).execute().request.headers + return GET(baseUrl, headers = headers) + } override fun popularAnimeFromElement(element: Element): SAnime { val anime = SAnime.create() - anime.setUrlWithoutDomain(element.attr("href")) - anime.title = element.text() + anime.thumbnail_url = "https:" + element.select("div.img a img").attr("src") + anime.setUrlWithoutDomain(element.select("div.img a").attr("href")) + anime.title = element.select("div.recent-release-episodes a").text() return anime } - override fun popularAnimeNextPageSelector(): String = "ul.pagination li:last-child:not(.selected)" + override fun popularAnimeNextPageSelector(): String? = null override fun episodeListSelector() = "div.cat-eps a" @@ -176,26 +181,22 @@ class Wcofun : ConfigurableAnimeSource, ParsedAnimeHttpSource() { override fun searchAnimeFromElement(element: Element): SAnime { val anime = SAnime.create() anime.setUrlWithoutDomain(element.attr("href")) - anime.title = element.text() + anime.thumbnail_url = element.select("img").attr("src") + anime.title = element.select("img").attr("alt") + return anime } - override fun searchAnimeNextPageSelector(): String = "ul.pagination-list li:last-child:not(.selected)" + override fun searchAnimeNextPageSelector(): String? = null - override fun searchAnimeSelector(): String = "div#sidebar_right2 li div.recent-release-episodes a, div.ddmcc li a" + override fun searchAnimeSelector(): String = "div#sidebar_right2 li div.img a" override fun searchAnimeRequest(page: Int, query: String, filters: AnimeFilterList): Request { - val filterList = if (filters.isEmpty()) getFilterList() else filters - val genreFilter = filterList.find { it is GenreFilter } as GenreFilter val formBody = FormBody.Builder() .add("catara", query) .add("konuara", "series") .build() - return when { - query.isNotBlank() -> POST("$baseUrl/search", headers, body = formBody) - genreFilter.state != 0 -> GET("$baseUrl/search-by-genre/page/${genreFilter.toUriPart()}") - else -> GET("$baseUrl/") - } + return POST("$baseUrl/search", headers, body = formBody) } override fun animeDetailsParse(document: Document): SAnime { @@ -233,166 +234,4 @@ class Wcofun : ConfigurableAnimeSource, ParsedAnimeHttpSource() { } screen.addPreference(videoQualityPref) } - - // Filters - override fun getFilterList(): AnimeFilterList = AnimeFilterList( - AnimeFilter.Header("Text search ignores filters"), - GenreFilter() - ) - - private class GenreFilter : UriPartFilter( - "Genres", - arrayOf( - Pair("