diff --git a/src/vi/goctruyentranhvui/build.gradle b/src/vi/goctruyentranhvui/build.gradle index 4b73943c4..a67ea57c8 100644 --- a/src/vi/goctruyentranhvui/build.gradle +++ b/src/vi/goctruyentranhvui/build.gradle @@ -1,7 +1,7 @@ ext { extName = 'Goc Truyen Tranh Vui' extClass = '.GocTruyenTranhVui' - extVersionCode = 3 + extVersionCode = 4 isNsfw = true } diff --git a/src/vi/goctruyentranhvui/src/eu/kanade/tachiyomi/extension/vi/goctruyentranhvui/GocTruyenTranhVui.kt b/src/vi/goctruyentranhvui/src/eu/kanade/tachiyomi/extension/vi/goctruyentranhvui/GocTruyenTranhVui.kt index 1a29a5ab8..ff60affc1 100644 --- a/src/vi/goctruyentranhvui/src/eu/kanade/tachiyomi/extension/vi/goctruyentranhvui/GocTruyenTranhVui.kt +++ b/src/vi/goctruyentranhvui/src/eu/kanade/tachiyomi/extension/vi/goctruyentranhvui/GocTruyenTranhVui.kt @@ -1,8 +1,18 @@ package eu.kanade.tachiyomi.extension.vi.goctruyentranhvui +import android.annotation.SuppressLint +import android.app.Application +import android.content.SharedPreferences +import android.os.Handler +import android.os.Looper +import android.webkit.WebView +import android.webkit.WebViewClient +import androidx.preference.EditTextPreference +import androidx.preference.PreferenceScreen import eu.kanade.tachiyomi.network.GET import eu.kanade.tachiyomi.network.POST import eu.kanade.tachiyomi.network.interceptor.rateLimit +import eu.kanade.tachiyomi.source.ConfigurableSource import eu.kanade.tachiyomi.source.model.FilterList import eu.kanade.tachiyomi.source.model.MangasPage import eu.kanade.tachiyomi.source.model.Page @@ -10,6 +20,7 @@ import eu.kanade.tachiyomi.source.model.SChapter import eu.kanade.tachiyomi.source.model.SManga import eu.kanade.tachiyomi.source.online.HttpSource import eu.kanade.tachiyomi.util.asJsoup +import keiyoushi.utils.getPreferences import keiyoushi.utils.parseAs import okhttp3.FormBody import okhttp3.Headers @@ -17,8 +28,12 @@ import okhttp3.HttpUrl.Companion.toHttpUrl import okhttp3.OkHttpClient 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 GocTruyenTranhVui : HttpSource() { +class GocTruyenTranhVui : HttpSource(), ConfigurableSource { override val lang = "vi" override val baseUrl = "https://goctruyentranhvui17.com" @@ -29,6 +44,8 @@ class GocTruyenTranhVui : HttpSource() { override val supportsLatest: Boolean = true + private val preferences: SharedPreferences = getPreferences() + override val client: OkHttpClient = network.cloudflareClient.newBuilder() .rateLimit(3) .build() @@ -76,12 +93,12 @@ class GocTruyenTranhVui : HttpSource() { override fun mangaDetailsParse(response: Response): SManga = SManga.create().apply { val document = response.asJsoup() - title = document.selectFirst(".v-card-title")!!.text() + title = document.select(".v-card-title").text() genre = document.select(".group-content > .v-chip-link").joinToString { it.text() } - thumbnail_url = document.selectFirst("img.image")!!.absUrl("src") - status = parseStatus(document.selectFirst(".mb-1:contains(Trạng thái:) span")!!.text()) - author = document.selectFirst(".mb-1:contains(Tác giả:) span")!!.text() - description = document.selectFirst(".v-card-text")!!.text() + thumbnail_url = document.selectFirst("img.image")?.absUrl("src") + status = parseStatus(document.selectFirst(".mb-1:contains(Trạng thái:) span")?.text()) + author = document.selectFirst(".mb-1:contains(Tác giả:) span")?.text() + description = document.select(".v-card-text").joinToString { it.wholeText() } } private fun parseStatus(status: String?) = when { @@ -103,8 +120,11 @@ class GocTruyenTranhVui : HttpSource() { val mangaId = matchId.groups[1]!!.value val nameEn = response.request.url.toString().substringAfter("/truyen/").substringBefore("/") val chapterNumber = response.request.url.toString().substringAfterLast("chuong-") - val body = FormBody.Builder().add("comicId", mangaId) - .add("chapterNumber", chapterNumber).add("nameEn", nameEn).build() + val body = FormBody.Builder() + .add("comicId", mangaId) + .add("chapterNumber", chapterNumber) + .add("nameEn", nameEn) + .build() val request = POST("$baseUrl/api/chapter/auth", pageHeaders, body) client.newCall(request).execute().parseAs>().result.data } else { @@ -116,10 +136,49 @@ class GocTruyenTranhVui : HttpSource() { } } private val pageHeaders by lazy { - headersBuilder() - .add("X-Requested-With", "XMLHttpRequest") - .add("Authorization", TOKEN_KEY) - .build() + getToken()?.let { + headersBuilder() + .add("X-Requested-With", "XMLHttpRequest") + .add("Authorization", it) + .build() + } ?: throw Exception("Vui lòng đăng nhập trong WebView") + } + private var _token: String? = null + + @SuppressLint("SetJavaScriptEnabled") + private fun getToken(): String? { + _token?.also { return it } + val handler = Handler(Looper.getMainLooper()) + val latch = CountDownLatch(1) + val customToken = preferences.getString("custom_token", null) + if (!customToken.isNullOrBlank()) { + return customToken + } + if (_token != null) return _token + + handler.post { + val webview = WebView(Injekt.get()) + with(webview.settings) { + javaScriptEnabled = true + domStorageEnabled = true + databaseEnabled = true + blockNetworkImage = true + } + webview.webViewClient = object : WebViewClient() { + override fun onPageFinished(view: WebView?, url: String?) { + // Get token + view!!.evaluateJavascript("window.localStorage.getItem('Authorization')") { token -> + _token = token.takeUnless { it == "null" }?.removeSurrounding("\"") + latch.countDown() + webview.destroy() + } + } + } + webview.loadDataWithBaseURL(baseUrl, " ", "text/html", "UTF-8", null) + } + + latch.await(10, TimeUnit.SECONDS) + return _token } override fun imageUrlParse(response: Response): String = throw UnsupportedOperationException() @@ -150,5 +209,15 @@ class GocTruyenTranhVui : HttpSource() { SortByList(getSortByList()), GenreList(getGenreList()), ) + + override fun setupPreferenceScreen(screen: PreferenceScreen) { + val tokenPref = EditTextPreference(screen.context).apply { + key = "custom_token" + title = "Authorization Token" + summary = "Enter token manually" + dialogTitle = "Authorization Token" + dialogMessage = "Token: ${getToken()}" + } + screen.addPreference(tokenPref) + } } -private const val TOKEN_KEY = "Bearer eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiJEcmFrZW4iLCJjb21pY0lkcyI6W10sInJvbGVJZCI6bnVsbCwiZ3JvdXBJZCI6bnVsbCwiYWRtaW4iOmZhbHNlLCJyYW5rIjowLCJwZXJtaXNzaW9uIjpbXSwiaWQiOiIwMDAxMTE1OTg2IiwidGVhbSI6ZmFsc2UsImlhdCI6MTc1NzU5NjQxMSwiZW1haWwiOiJudWxsIn0.VcGDaVQvyowtvja04CTUpfCP5XiC5qIdPmANZL0Gjz2kjz__PJ8LATQ9s44FpNohMpgLgPQO0TVs67D_YFlLNw"