Ru: Remanga. Fix login from cookie and webview due to google captcha on login api request (#10690)

This commit is contained in:
Pavka 2022-02-04 13:27:02 +03:00 committed by GitHub
parent 81e98ae6ae
commit 05c2a47a17
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 20 additions and 74 deletions

View File

@ -6,7 +6,7 @@ ext {
extName = 'Remanga' extName = 'Remanga'
pkgNameSuffix = 'ru.remanga' pkgNameSuffix = 'ru.remanga'
extClass = '.Remanga' extClass = '.Remanga'
extVersionCode = 43 extVersionCode = 44
} }
dependencies { dependencies {

View File

@ -16,12 +16,10 @@ import android.annotation.TargetApi
import android.app.Application import android.app.Application
import android.content.SharedPreferences import android.content.SharedPreferences
import android.os.Build import android.os.Build
import android.text.InputType
import android.widget.Toast import android.widget.Toast
import androidx.preference.ListPreference import androidx.preference.ListPreference
import eu.kanade.tachiyomi.lib.dataimage.DataImageInterceptor import eu.kanade.tachiyomi.lib.dataimage.DataImageInterceptor
import eu.kanade.tachiyomi.network.GET import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.network.POST
import eu.kanade.tachiyomi.network.asObservable import eu.kanade.tachiyomi.network.asObservable
import eu.kanade.tachiyomi.network.asObservableSuccess import eu.kanade.tachiyomi.network.asObservableSuccess
import eu.kanade.tachiyomi.source.ConfigurableSource import eu.kanade.tachiyomi.source.ConfigurableSource
@ -36,27 +34,26 @@ import kotlinx.serialization.SerializationException
import kotlinx.serialization.decodeFromString import kotlinx.serialization.decodeFromString
import kotlinx.serialization.json.Json import kotlinx.serialization.json.Json
import kotlinx.serialization.json.JsonObject import kotlinx.serialization.json.JsonObject
import kotlinx.serialization.json.buildJsonObject
import kotlinx.serialization.json.decodeFromJsonElement import kotlinx.serialization.json.decodeFromJsonElement
import kotlinx.serialization.json.put
import okhttp3.Headers import okhttp3.Headers
import okhttp3.HttpUrl.Companion.toHttpUrlOrNull import okhttp3.HttpUrl
import okhttp3.HttpUrl.Companion.toHttpUrl
import okhttp3.Interceptor import okhttp3.Interceptor
import okhttp3.MediaType.Companion.toMediaTypeOrNull
import okhttp3.OkHttpClient import okhttp3.OkHttpClient
import okhttp3.Request import okhttp3.Request
import okhttp3.RequestBody.Companion.toRequestBody
import okhttp3.Response import okhttp3.Response
import org.jsoup.Jsoup import org.jsoup.Jsoup
import rx.Observable import rx.Observable
import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get import uy.kohesive.injekt.api.get
import uy.kohesive.injekt.injectLazy import uy.kohesive.injekt.injectLazy
import java.net.URLDecoder
import java.text.SimpleDateFormat import java.text.SimpleDateFormat
import java.util.Date import java.util.Date
import java.util.Locale import java.util.Locale
import kotlin.math.absoluteValue import kotlin.math.absoluteValue
import kotlin.random.Random import kotlin.random.Random
class Remanga : ConfigurableSource, HttpSource() { class Remanga : ConfigurableSource, HttpSource() {
override val name = "Remanga" override val name = "Remanga"
@ -69,13 +66,13 @@ class Remanga : ConfigurableSource, HttpSource() {
private val baseOrig: String = "https://api.remanga.org" private val baseOrig: String = "https://api.remanga.org"
private val baseMirr: String = "https://api.xn--80aaig9ahr.xn--c1avg" // https://реманга.орг private val baseMirr: String = "https://api.xn--80aaig9ahr.xn--c1avg" // https://реманга.орг
private val baseCookieUrl: HttpUrl = "https://remanga.org".toHttpUrl()
private val domain: String? = preferences.getString(DOMAIN_PREF, baseOrig) private val domain: String? = preferences.getString(DOMAIN_PREF, baseOrig)
override val baseUrl = domain.toString() override val baseUrl = domain.toString()
override val supportsLatest = true override val supportsLatest = true
private var token: String = ""
private val userAgentRandomizer = " ${Random.nextInt().absoluteValue}" private val userAgentRandomizer = " ${Random.nextInt().absoluteValue}"
override fun headersBuilder(): Headers.Builder = Headers.Builder() override fun headersBuilder(): Headers.Builder = Headers.Builder()
@ -85,15 +82,17 @@ class Remanga : ConfigurableSource, HttpSource() {
private fun authIntercept(chain: Interceptor.Chain): Response { private fun authIntercept(chain: Interceptor.Chain): Response {
val request = chain.request() val request = chain.request()
if (username.isEmpty() or password.isEmpty()) {
return chain.proceed(request)
}
if (token.isEmpty()) { val cookies = client.cookieJar.loadForRequest(baseCookieUrl)
token = this.login(chain, username, password) val authCookie = cookies
} .firstOrNull { cookie -> cookie.name == USER_COOKIE_NAME }
?.let { cookie -> URLDecoder.decode(cookie.value, "UTF-8") }
?.let { jsonString -> json.decodeFromString<UserDto>(jsonString) }
?: return chain.proceed(request)
USER_ID = authCookie.id.toString()
val authRequest = request.newBuilder() val authRequest = request.newBuilder()
.addHeader("Authorization", "bearer $token") .addHeader("Authorization", "bearer ${authCookie.access_token}")
.build() .build()
return chain.proceed(authRequest) return chain.proceed(authRequest)
} }
@ -108,21 +107,6 @@ class Remanga : ConfigurableSource, HttpSource() {
private var branches = mutableMapOf<String, List<BranchesDto>>() private var branches = mutableMapOf<String, List<BranchesDto>>()
private fun login(chain: Interceptor.Chain, username: String, password: String): String {
val jsonObject = buildJsonObject {
put("user", username)
put("password", password)
}
val body = jsonObject.toString().toRequestBody(MEDIA_TYPE)
val response = chain.proceed(POST("$baseUrl/api/users/login/", headers, body))
if (response.code >= 400) {
throw Exception("Не удалось войти")
}
val user = json.decodeFromString<SeriesWrapperDto<UserDto>>(response.body!!.string()).content
USER_ID = user.id.toString()
return user.access_token
}
override fun popularMangaRequest(page: Int) = GET("$baseUrl/api/search/catalog/?ordering=-rating&count=$count&page=$page", headers) override fun popularMangaRequest(page: Int) = GET("$baseUrl/api/search/catalog/?ordering=-rating&count=$count&page=$page", headers)
override fun popularMangaParse(response: Response): MangasPage = searchMangaParse(response) override fun popularMangaParse(response: Response): MangasPage = searchMangaParse(response)
@ -173,9 +157,9 @@ class Remanga : ConfigurableSource, HttpSource() {
} }
override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request { override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request {
var url = "$baseUrl/api/search/catalog/?page=$page".toHttpUrlOrNull()!!.newBuilder() var url = "$baseUrl/api/search/catalog/?page=$page".toHttpUrl().newBuilder()
if (query.isNotEmpty()) { if (query.isNotEmpty()) {
url = "$baseUrl/api/search/?page=$page".toHttpUrlOrNull()!!.newBuilder() url = "$baseUrl/api/search/?page=$page".toHttpUrl().newBuilder()
url.addQueryParameter("query", query) url.addQueryParameter("query", query)
} }
(if (filters.isEmpty()) getFilterList() else filters).forEach { filter -> (if (filters.isEmpty()) getFilterList() else filters).forEach { filter ->
@ -215,7 +199,7 @@ class Remanga : ConfigurableSource, HttpSource() {
throw Exception("Пользователь не найден") throw Exception("Пользователь не найден")
} }
val TypeQ = getMyList()[filter.state].id val TypeQ = getMyList()[filter.state].id
val UserProfileUrl = "$baseUrl/api/users/$USER_ID/bookmarks/?type=$TypeQ&page=$page".toHttpUrlOrNull()!!.newBuilder() val UserProfileUrl = "$baseUrl/api/users/$USER_ID/bookmarks/?type=$TypeQ&page=$page".toHttpUrl().newBuilder()
return GET(UserProfileUrl.toString(), headers) return GET(UserProfileUrl.toString(), headers)
} }
} }
@ -648,32 +632,6 @@ class Remanga : ConfigurableSource, HttpSource() {
MyListUnit("Не интересно ", "5") MyListUnit("Не интересно ", "5")
) )
private fun androidx.preference.PreferenceScreen.editTextPreference(title: String, default: String, value: String, isPassword: Boolean = false): androidx.preference.EditTextPreference {
return androidx.preference.EditTextPreference(context).apply {
key = title
this.title = title
summary = value
this.setDefaultValue(default)
dialogTitle = title
if (isPassword) {
if (value.isNotBlank()) { summary = "*****" }
setOnBindEditTextListener {
it.inputType = InputType.TYPE_CLASS_TEXT or InputType.TYPE_TEXT_VARIATION_PASSWORD
}
}
setOnPreferenceChangeListener { _, newValue ->
try {
val res = preferences.edit().putString(title, newValue as String).commit()
Toast.makeText(context, "Перезапустите Tachiyomi, чтобы применить новую настройку.", Toast.LENGTH_LONG).show()
res
} catch (e: Exception) {
e.printStackTrace()
false
}
}
}
}
override fun setupPreferenceScreen(screen: androidx.preference.PreferenceScreen) { override fun setupPreferenceScreen(screen: androidx.preference.PreferenceScreen) {
val domainPref = ListPreference(screen.context).apply { val domainPref = ListPreference(screen.context).apply {
key = DOMAIN_PREF key = DOMAIN_PREF
@ -719,26 +677,14 @@ class Remanga : ConfigurableSource, HttpSource() {
screen.addPreference(domainPref) screen.addPreference(domainPref)
screen.addPreference(paidChapterShow) screen.addPreference(paidChapterShow)
screen.addPreference(bookmarksHide) screen.addPreference(bookmarksHide)
screen.addPreference(screen.editTextPreference(USERNAME_TITLE, USERNAME_DEFAULT, username))
screen.addPreference(screen.editTextPreference(PASSWORD_TITLE, PASSWORD_DEFAULT, password, true))
} }
private fun getPrefUsername(): String = preferences.getString(USERNAME_TITLE, USERNAME_DEFAULT)!!
private fun getPrefPassword(): String = preferences.getString(PASSWORD_TITLE, PASSWORD_DEFAULT)!!
private val json: Json by injectLazy() private val json: Json by injectLazy()
private val username by lazy { getPrefUsername() }
private val password by lazy { getPrefPassword() }
companion object { companion object {
private var USER_ID = "" private var USER_ID = ""
private val MEDIA_TYPE = "application/json; charset=utf-8".toMediaTypeOrNull() private const val USER_COOKIE_NAME = "user"
private const val USERNAME_TITLE = "Username"
private const val USERNAME_DEFAULT = ""
private const val PASSWORD_TITLE = "Password"
private const val PASSWORD_DEFAULT = ""
const val PREFIX_SLUG_SEARCH = "slug:" const val PREFIX_SLUG_SEARCH = "slug:"