Handle 401 on Remanga

pull/6/head
Koitharu 4 years ago
parent e15dbf2a4b
commit 1ba2bba12e
No known key found for this signature in database
GPG Key ID: 8E861F8CE6E7CE27

@ -1,13 +1,16 @@
package org.koitharu.kotatsu.parsers.site package org.koitharu.kotatsu.parsers.site
import okhttp3.Headers import okhttp3.Headers
import okhttp3.Response
import org.json.JSONArray import org.json.JSONArray
import org.json.JSONException import org.json.JSONException
import org.json.JSONObject import org.json.JSONObject
import org.jsoup.HttpStatusException
import org.koitharu.kotatsu.parsers.MangaLoaderContext import org.koitharu.kotatsu.parsers.MangaLoaderContext
import org.koitharu.kotatsu.parsers.MangaParser import org.koitharu.kotatsu.parsers.MangaParser
import org.koitharu.kotatsu.parsers.MangaParserAuthProvider import org.koitharu.kotatsu.parsers.MangaParserAuthProvider
import org.koitharu.kotatsu.parsers.config.ConfigKey import org.koitharu.kotatsu.parsers.config.ConfigKey
import org.koitharu.kotatsu.parsers.exception.AuthRequiredException
import org.koitharu.kotatsu.parsers.exception.ParseException import org.koitharu.kotatsu.parsers.exception.ParseException
import org.koitharu.kotatsu.parsers.model.* import org.koitharu.kotatsu.parsers.model.*
import org.koitharu.kotatsu.parsers.util.* import org.koitharu.kotatsu.parsers.util.*
@ -15,6 +18,7 @@ import org.koitharu.kotatsu.parsers.util.json.getStringOrNull
import org.koitharu.kotatsu.parsers.util.json.mapJSON import org.koitharu.kotatsu.parsers.util.json.mapJSON
import org.koitharu.kotatsu.parsers.util.json.mapJSONTo import org.koitharu.kotatsu.parsers.util.json.mapJSONTo
import org.koitharu.kotatsu.parsers.util.json.mapJSONToSet import org.koitharu.kotatsu.parsers.util.json.mapJSONToSet
import java.net.HttpURLConnection
import java.net.URLDecoder import java.net.URLDecoder
import java.text.DateFormat import java.text.DateFormat
import java.text.SimpleDateFormat import java.text.SimpleDateFormat
@ -109,10 +113,12 @@ internal class RemangaParser(
val domain = getDomain() val domain = getDomain()
val slug = manga.url.find(regexLastUrlPath) val slug = manga.url.find(regexLastUrlPath)
?: throw ParseException("Cannot obtain slug from ${manga.url}") ?: throw ParseException("Cannot obtain slug from ${manga.url}")
val data = context.httpGet( val data = catch401 {
url = "https://api.$domain/api/titles/$slug/", context.httpGet(
headers = getApiHeaders(), url = "https://api.$domain/api/titles/$slug/",
).parseJson() headers = getApiHeaders(),
)
}.parseJson()
val content = try { val content = try {
data.getJSONObject("content") data.getJSONObject("content")
} catch (e: JSONException) { } catch (e: JSONException) {
@ -166,7 +172,9 @@ internal class RemangaParser(
override suspend fun getPages(chapter: MangaChapter): List<MangaPage> { override suspend fun getPages(chapter: MangaChapter): List<MangaPage> {
val referer = "https://${getDomain()}/" val referer = "https://${getDomain()}/"
val content = context.httpGet(chapter.url.withDomain(subdomain = "api"), getApiHeaders()).parseJson() val content = catch401 {
context.httpGet(chapter.url.withDomain(subdomain = "api"), getApiHeaders())
}.parseJson()
.getJSONObject("content") .getJSONObject("content")
val pages = content.optJSONArray("pages") val pages = content.optJSONArray("pages")
if (pages == null) { if (pages == null) {
@ -205,10 +213,12 @@ internal class RemangaParser(
} }
override suspend fun getUsername(): String { override suspend fun getUsername(): String {
val jo = context.httpGet( val jo = catch401 {
url = "https://api.${getDomain()}/api/users/current/", context.httpGet(
headers = getApiHeaders(), url = "https://api.${getDomain()}/api/users/current/",
).parseJson() headers = getApiHeaders(),
)
}.parseJson()
return jo.getJSONObject("content").getString("username") return jo.getJSONObject("content").getString("username")
} }
@ -246,10 +256,12 @@ internal class RemangaParser(
val result = ArrayList<JSONObject>(100) val result = ArrayList<JSONObject>(100)
var page = 1 var page = 1
while (true) { while (true) {
val content = context.httpGet( val content = catch401 {
url = "https://api.$domain/api/titles/chapters/?branch_id=$branchId&page=$page&count=100", context.httpGet(
headers = getApiHeaders(), url = "https://api.$domain/api/titles/chapters/?branch_id=$branchId&page=$page&count=100",
).parseJson().getJSONArray("content") headers = getApiHeaders(),
)
}.parseJson().getJSONArray("content")
val len = content.length() val len = content.length()
if (len == 0) { if (len == 0) {
break break
@ -262,4 +274,14 @@ internal class RemangaParser(
} }
return result return result
} }
private inline fun catch401(block: () -> Response): Response = try {
block()
} catch (e: HttpStatusException) {
if (e.statusCode == HttpURLConnection.HTTP_UNAUTHORIZED) {
throw AuthRequiredException(source)
} else {
throw e
}
}
} }

@ -0,0 +1,36 @@
package org.koitharu.kotatsu.parsers.util
import kotlinx.coroutines.CancellableContinuation
import kotlinx.coroutines.CompletionHandler
import okhttp3.Call
import okhttp3.Callback
import okhttp3.Response
import java.io.IOException
import kotlin.coroutines.resume
import kotlin.coroutines.resumeWithException
internal class ContinuationCallCallback(
private val call: Call,
private val continuation: CancellableContinuation<Response>,
) : Callback, CompletionHandler {
override fun onResponse(call: Call, response: Response) {
if (continuation.isActive) {
continuation.resume(response)
}
}
override fun onFailure(call: Call, e: IOException) {
if (!call.isCanceled() && continuation.isActive) {
continuation.resumeWithException(e)
}
}
override fun invoke(cause: Throwable?) {
runCatching {
call.cancel()
}.onFailure { e ->
cause?.addSuppressed(e)
}
}
}

@ -2,31 +2,12 @@ package org.koitharu.kotatsu.parsers.util
import kotlinx.coroutines.suspendCancellableCoroutine import kotlinx.coroutines.suspendCancellableCoroutine
import okhttp3.Call import okhttp3.Call
import okhttp3.Callback
import okhttp3.Response import okhttp3.Response
import java.io.IOException
import kotlin.coroutines.resume
import kotlin.coroutines.resumeWithException
suspend fun Call.await() = suspendCancellableCoroutine<Response> { cont -> suspend fun Call.await(): Response = suspendCancellableCoroutine { continuation ->
this.enqueue( val callback = ContinuationCallCallback(this, continuation)
object : Callback { enqueue(callback)
override fun onFailure(call: Call, e: IOException) { continuation.invokeOnCancellation(callback)
if (cont.isActive) {
cont.resumeWithException(e)
}
}
override fun onResponse(call: Call, response: Response) {
if (cont.isActive) {
cont.resume(response)
}
}
},
)
cont.invokeOnCancellation {
this.cancel()
}
} }
val Response.mimeType: String? val Response.mimeType: String?

Loading…
Cancel
Save