Wcofun update & fixes (#904)
This commit is contained in:
@ -5,7 +5,7 @@ ext {
|
|||||||
extName = 'Wcofun'
|
extName = 'Wcofun'
|
||||||
pkgNameSuffix = 'en.wcofun'
|
pkgNameSuffix = 'en.wcofun'
|
||||||
extClass = '.Wcofun'
|
extClass = '.Wcofun'
|
||||||
extVersionCode = 5
|
extVersionCode = 6
|
||||||
libVersion = '13'
|
libVersion = '13'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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<Application>()
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
@ -36,7 +36,7 @@ class Wcofun : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
|
|||||||
|
|
||||||
override val name = "Wcofun"
|
override val name = "Wcofun"
|
||||||
|
|
||||||
override val baseUrl = "https://www.wcofun.com/"
|
override val baseUrl = "https://www.wcofun.net"
|
||||||
|
|
||||||
override val lang = "en"
|
override val lang = "en"
|
||||||
|
|
||||||
@ -50,18 +50,23 @@ class Wcofun : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
|
|||||||
Injekt.get<Application>().getSharedPreferences("source_$id", 0x0000)
|
Injekt.get<Application>().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 {
|
override fun popularAnimeFromElement(element: Element): SAnime {
|
||||||
val anime = SAnime.create()
|
val anime = SAnime.create()
|
||||||
anime.setUrlWithoutDomain(element.attr("href"))
|
anime.thumbnail_url = "https:" + element.select("div.img a img").attr("src")
|
||||||
anime.title = element.text()
|
anime.setUrlWithoutDomain(element.select("div.img a").attr("href"))
|
||||||
|
anime.title = element.select("div.recent-release-episodes a").text()
|
||||||
return anime
|
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"
|
override fun episodeListSelector() = "div.cat-eps a"
|
||||||
|
|
||||||
@ -176,26 +181,22 @@ class Wcofun : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
|
|||||||
override fun searchAnimeFromElement(element: Element): SAnime {
|
override fun searchAnimeFromElement(element: Element): SAnime {
|
||||||
val anime = SAnime.create()
|
val anime = SAnime.create()
|
||||||
anime.setUrlWithoutDomain(element.attr("href"))
|
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
|
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 {
|
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()
|
val formBody = FormBody.Builder()
|
||||||
.add("catara", query)
|
.add("catara", query)
|
||||||
.add("konuara", "series")
|
.add("konuara", "series")
|
||||||
.build()
|
.build()
|
||||||
return when {
|
return POST("$baseUrl/search", headers, body = formBody)
|
||||||
query.isNotBlank() -> POST("$baseUrl/search", headers, body = formBody)
|
|
||||||
genreFilter.state != 0 -> GET("$baseUrl/search-by-genre/page/${genreFilter.toUriPart()}")
|
|
||||||
else -> GET("$baseUrl/")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun animeDetailsParse(document: Document): SAnime {
|
override fun animeDetailsParse(document: Document): SAnime {
|
||||||
@ -233,166 +234,4 @@ class Wcofun : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
|
|||||||
}
|
}
|
||||||
screen.addPreference(videoQualityPref)
|
screen.addPreference(videoQualityPref)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Filters
|
|
||||||
override fun getFilterList(): AnimeFilterList = AnimeFilterList(
|
|
||||||
AnimeFilter.Header("Text search ignores filters"),
|
|
||||||
GenreFilter()
|
|
||||||
)
|
|
||||||
|
|
||||||
private class GenreFilter : UriPartFilter(
|
|
||||||
"Genres",
|
|
||||||
arrayOf(
|
|
||||||
Pair("<select>", ""),
|
|
||||||
Pair("Adventure", "4"),
|
|
||||||
Pair("Comedy", "6"),
|
|
||||||
Pair("Fantasy", "8"),
|
|
||||||
Pair("Science Fiction", "10"),
|
|
||||||
Pair("Mystery", "12"),
|
|
||||||
Pair("Action", "14"),
|
|
||||||
Pair("Drama", "16"),
|
|
||||||
Pair("Surrealist Comedy", "18"),
|
|
||||||
Pair("Horror", "20"),
|
|
||||||
Pair("Romance", "22"),
|
|
||||||
Pair("Animated", "23"),
|
|
||||||
Pair("Thriller", "24"),
|
|
||||||
Pair("Slice Of Life", "25"),
|
|
||||||
Pair("Supernatural", "26"),
|
|
||||||
Pair("Romantic Comedy", "27"),
|
|
||||||
Pair("Harem", "28"),
|
|
||||||
Pair("Sports", "29"),
|
|
||||||
Pair("Psychological", "30"),
|
|
||||||
Pair("Magical Girl", "31"),
|
|
||||||
Pair("Mecha", "35"),
|
|
||||||
Pair("Role-playing", "33"),
|
|
||||||
Pair("Military", "34"),
|
|
||||||
Pair("Science Fantasy", "36"),
|
|
||||||
Pair("Martial Arts", "37"),
|
|
||||||
Pair("High School", "38"),
|
|
||||||
Pair("Parody", "39"),
|
|
||||||
Pair("Virtual Reality", "40"),
|
|
||||||
Pair("Space Opera", "41"),
|
|
||||||
Pair("Post-Apocalyptic", "42"),
|
|
||||||
Pair("Music", "43"),
|
|
||||||
Pair("Dark Fantasy", "44"),
|
|
||||||
Pair("Historical Fantasy", "45"),
|
|
||||||
Pair("Yuri", "46"),
|
|
||||||
Pair("Superpower", "47"),
|
|
||||||
Pair("Magic", "48"),
|
|
||||||
Pair("Alternate History", "49"),
|
|
||||||
Pair("Sci-fi", "50"),
|
|
||||||
Pair("Girls With Guns", "51"),
|
|
||||||
Pair("Tournament", "52"),
|
|
||||||
Pair("School", "53"),
|
|
||||||
Pair("Romantic Drama", "54"),
|
|
||||||
Pair("Crime", "55"),
|
|
||||||
Pair("Historical", "56"),
|
|
||||||
Pair("Dystopian", "57"),
|
|
||||||
Pair("Steampunk", "58"),
|
|
||||||
Pair("Metafiction", "59"),
|
|
||||||
Pair("Police", "60"),
|
|
||||||
Pair("Detective Fiction", "61"),
|
|
||||||
Pair("Occult", "62"),
|
|
||||||
Pair("Tragedy", "63"),
|
|
||||||
Pair("Fighter", "65"),
|
|
||||||
Pair("Animated television series", "66"),
|
|
||||||
Pair("Children's television series", "67"),
|
|
||||||
Pair("Family television series", "68"),
|
|
||||||
Pair("Absurdist humor", "69"),
|
|
||||||
Pair("Dark humor", "70"),
|
|
||||||
Pair("Surreal humor", "71"),
|
|
||||||
Pair("Adult animation", "72"),
|
|
||||||
Pair("Teen Drama", "73"),
|
|
||||||
Pair("Superhero", "74"),
|
|
||||||
Pair("Cyberpunk", "75"),
|
|
||||||
Pair("Suspense", "76"),
|
|
||||||
Pair("Sitcom", "77"),
|
|
||||||
Pair("Satire", "78"),
|
|
||||||
Pair("Anthology series", "79"),
|
|
||||||
Pair("Edutainment", "80"),
|
|
||||||
Pair("Espionage", "81"),
|
|
||||||
Pair("Surrealism", "82"),
|
|
||||||
Pair("Teen Animation", "83"),
|
|
||||||
Pair("Toilet humour", "84"),
|
|
||||||
Pair("Cutaway gag humor", "85"),
|
|
||||||
Pair("Splatter", "86"),
|
|
||||||
Pair("Deadpan", "87"),
|
|
||||||
Pair("Car racing", "88"),
|
|
||||||
Pair("Chanbara", "89"),
|
|
||||||
Pair("Goth", "90"),
|
|
||||||
Pair("Game", "93"),
|
|
||||||
Pair("Magical boy", "94"),
|
|
||||||
Pair("Shounen", "95"),
|
|
||||||
Pair("Kids", "96"),
|
|
||||||
Pair("shoujo", "97"),
|
|
||||||
Pair("Baseball", "98"),
|
|
||||||
Pair("Manga", "99"),
|
|
||||||
Pair("Friendship", "100"),
|
|
||||||
Pair("School Dormitory", "101"),
|
|
||||||
Pair("Ecchi", "102"),
|
|
||||||
Pair("Seinen", "103"),
|
|
||||||
Pair("Coming-of-age story", "104"),
|
|
||||||
Pair("Idol anime", "105"),
|
|
||||||
Pair("Samurai", "106"),
|
|
||||||
Pair("Reverse Harem", "107"),
|
|
||||||
Pair("Urban", "108"),
|
|
||||||
Pair("War", "109"),
|
|
||||||
Pair("Vampire", "110"),
|
|
||||||
Pair("Demons", "111"),
|
|
||||||
Pair("Urban fantasy", "112"),
|
|
||||||
Pair("Mythic fiction", "113"),
|
|
||||||
Pair("Space", "114"),
|
|
||||||
Pair("Shounen Ai", "115"),
|
|
||||||
Pair("Shoujo Ai", "116"),
|
|
||||||
Pair("Slapstick", "117"),
|
|
||||||
Pair("Surreal", "118"),
|
|
||||||
Pair("Chinese Cartoon", "119"),
|
|
||||||
Pair("Short", "120"),
|
|
||||||
Pair("Movie", "121"),
|
|
||||||
Pair("Family", "123"),
|
|
||||||
Pair("Animation", "125"),
|
|
||||||
Pair("Educational", "128"),
|
|
||||||
Pair("Musical", "129"),
|
|
||||||
Pair("Yaoi", "132"),
|
|
||||||
Pair("Documentary", "133"),
|
|
||||||
Pair("Football", "134"),
|
|
||||||
Pair("Learning", "135"),
|
|
||||||
Pair("Pre-School", "136"),
|
|
||||||
Pair("Graphic novel", "137"),
|
|
||||||
Pair("Contemporary fantasy", "140"),
|
|
||||||
Pair("Adult Cartoons", "141"),
|
|
||||||
Pair("Cartoon series", "142"),
|
|
||||||
Pair("Off-color humor", "143"),
|
|
||||||
Pair("History", "147"),
|
|
||||||
Pair("Superhero fiction", "149"),
|
|
||||||
Pair("Sword and sorcery", "155"),
|
|
||||||
Pair("Stop Motion", "157"),
|
|
||||||
Pair("Horror comedy", "159"),
|
|
||||||
Pair("Social satire", "163"),
|
|
||||||
Pair("Black comedy", "166"),
|
|
||||||
Pair("Animated sitcom", "168"),
|
|
||||||
Pair("Game-Show", "170"),
|
|
||||||
Pair("Western", "172"),
|
|
||||||
Pair("Comic science fiction", "176"),
|
|
||||||
Pair("Situation comedy", "178"),
|
|
||||||
Pair("Adapted Literature", "181"),
|
|
||||||
Pair("Harold and the Purple Crayon", "202"),
|
|
||||||
Pair("Spy fiction", "203"),
|
|
||||||
Pair("Children's fiction", "204"),
|
|
||||||
Pair("Sketch comedy", "205"),
|
|
||||||
Pair("Josei", "206"),
|
|
||||||
Pair("Dementia", "208"),
|
|
||||||
Pair("Cars", "209"),
|
|
||||||
Pair("Isekai", "210"),
|
|
||||||
Pair("Comedy-drama", "211"),
|
|
||||||
Pair("Senryuu Girl", "214"),
|
|
||||||
Pair("Musical comedy", "215"),
|
|
||||||
Pair("Comedy horror", "216"),
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
private open class UriPartFilter(displayName: String, val vals: Array<Pair<String, String>>) :
|
|
||||||
AnimeFilter.Select<String>(displayName, vals.map { it.first }.toTypedArray()) {
|
|
||||||
fun toUriPart() = vals[state].second
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user