11package eu.kanade.tachiyomi.multisrc.hiper
22
3+ import android.os.Handler
4+ import android.os.Looper
5+ import android.webkit.WebResourceRequest
6+ import android.webkit.WebResourceResponse
7+ import android.webkit.WebView
8+ import android.webkit.WebViewClient
39import eu.kanade.tachiyomi.network.GET
410import eu.kanade.tachiyomi.source.model.Filter
511import eu.kanade.tachiyomi.source.model.FilterList
@@ -8,20 +14,26 @@ import eu.kanade.tachiyomi.source.model.Page
814import eu.kanade.tachiyomi.source.model.SChapter
915import eu.kanade.tachiyomi.source.model.SManga
1016import eu.kanade.tachiyomi.source.online.HttpSource
17+ import eu.kanade.tachiyomi.util.asJsoup
18+ import keiyoushi.utils.applicationContext
1119import keiyoushi.utils.get
1220import keiyoushi.utils.parseAs
1321import kotlinx.serialization.json.JsonElement
1422import kotlinx.serialization.json.add
1523import kotlinx.serialization.json.buildJsonArray
1624import kotlinx.serialization.json.buildJsonObject
25+ import kotlinx.serialization.json.jsonObject
1726import kotlinx.serialization.json.put
1827import kotlinx.serialization.json.putJsonArray
1928import kotlinx.serialization.json.putJsonObject
2029import okhttp3.Headers
2130import okhttp3.HttpUrl.Companion.toHttpUrl
2231import okhttp3.Request
2332import okhttp3.Response
33+ import rx.Observable
2434import java.io.IOException
35+ import java.util.concurrent.CountDownLatch
36+ import java.util.concurrent.TimeUnit
2537
2638abstract class Hiper : HttpSource () {
2739
@@ -36,9 +48,22 @@ abstract class Hiper : HttpSource() {
3648 .set(" Accept" , " text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8" )
3749 .build()
3850
51+ private val handler by lazy { Handler (Looper .getMainLooper()) }
52+
53+ private var wvHeaders: Headers ? = null
54+
3955 override val client = network.client.newBuilder()
4056 .addInterceptor { chain ->
41- val response = chain.proceed(chain.request())
57+ var request = chain.request()
58+
59+ wvHeaders?.let {
60+ request = request.newBuilder()
61+ .headers(it)
62+ .build()
63+ }
64+
65+ val response = chain.proceed(request)
66+
4267 // Fetch baseUrl with accept headers which then populates a cookie
4368 if (response.code == 401 ) {
4469 response.close()
@@ -158,6 +183,8 @@ abstract class Hiper : HttpSource() {
158183
159184 // ============================ Chapters ==================================
160185
186+ override fun getChapterUrl (chapter : SChapter ) = baseUrl + chapter.url.substringBefore(' #' ) + " /" + chapter.url.substringAfterLast(' /' )
187+
161188 override fun chapterListRequest (manga : SManga ): Request {
162189 val mangaId = manga.url.substringAfterLast(" #" ).toLongOrNull()
163190 ? : throw IOException (" Migrate from $name to $name " )
@@ -251,12 +278,84 @@ abstract class Hiper : HttpSource() {
251278 return GET (url, headers)
252279 }
253280
254- override fun pageListParse (response : Response ): List <Page > {
255- val element = response.parseAs<List <JsonElement >>().last()
256- val pages = element[" result" ][" data" ][" json" ]?.parseAs<List <PageDto >>() ? : return emptyList()
281+ override fun fetchPageList (chapter : SChapter ): Observable <List <Page >> = Observable .fromCallable {
282+ val chapterUrl = getChapterUrl(chapter)
283+
284+ val request = pageListRequest(chapter)
285+ val response = client.newCall(request).execute()
286+ val resJson = parsePages(response)
287+
288+ resJson?.takeIf { it.isNotEmpty() } ? : run {
289+ fetchHeadersWv(chapterUrl)
290+
291+ wvHeaders?.let {
292+ val response = client.newCall(request).execute()
293+ parsePages(response) ? : emptyList()
294+ } ? : error(" Failed to get headers" )
295+ }
296+ }
297+
298+ private fun parsePages (response : Response ): List <Page >? {
299+ val elements = response.parseAs<List <JsonElement >>()
300+
301+ for (element in elements) {
302+ if (" error" in element.jsonObject) {
303+ return null
304+ }
305+ }
306+
307+ val lastElement = elements.last()
308+ val pages = lastElement[" result" ][" data" ][" json" ]?.parseAs<List <PageDto >>() ? : return emptyList()
257309 return pages.map(PageDto ::toPage)
258310 }
259311
312+ @Synchronized
313+ fun fetchHeadersWv (url : String ) {
314+ val latch = CountDownLatch (1 )
315+ var webView: WebView ? = null
316+ val doc = client.newCall(GET (url, headers)).execute().asJsoup()
317+
318+ handler.post {
319+ val wv = WebView (applicationContext).also { webView = it }
320+
321+ wv.settings.javaScriptEnabled = true
322+ wv.settings.domStorageEnabled = true
323+ wv.settings.loadsImagesAutomatically = false
324+ wv.settings.blockNetworkImage = true
325+ wv.settings.userAgentString = headers[" user-agent" ]
326+
327+ wv.webViewClient = object : WebViewClient () {
328+ override fun shouldInterceptRequest (
329+ view : WebView ,
330+ request : WebResourceRequest ,
331+ ): WebResourceResponse ? {
332+ val url = request.url.toString()
333+ if (url.startsWith(" $baseUrl /api" )) {
334+ val allHeaders = request.requestHeaders
335+ if (allHeaders.isNotEmpty()) {
336+ wvHeaders = Headers .Builder ().apply {
337+ allHeaders.forEach { (key, value) ->
338+ if (headers.get(key) != null ) {
339+ add(key, value)
340+ }
341+ }
342+ }.build()
343+ }
344+ latch.countDown()
345+ return null
346+ }
347+ return super .shouldInterceptRequest(view, request)
348+ }
349+ }
350+ wv.loadDataWithBaseURL(doc.location(), doc.outerHtml(), " text/html" , " utf-8" , null )
351+ }
352+
353+ latch.await(15 , TimeUnit .SECONDS )
354+ handler.post { webView?.destroy() }
355+ }
356+
357+ override fun pageListParse (response : Response ): List <Page > = throw UnsupportedOperationException ()
358+
260359 override fun imageUrlParse (response : Response ): String = throw UnsupportedOperationException ()
261360
262361 // ============================ Filters ====================================
0 commit comments