Clean up code after review

- Do not use ConfigKey.Domain for supplementary domains
- use IllegalArgumentException
- use "$var" instead of "${var}" where possible
- fix the language in `publicUrl`
- use `?` to handle `largeCoverUrl`
- use `requireNotNull` instead of `!!`
pull/273/head
Nikita Strygin 3 years ago
parent 887fc691d1
commit 753c27a90c

@ -1,6 +1,7 @@
package org.koitharu.kotatsu.parsers.site.en
import okhttp3.Headers
import okhttp3.HttpUrl.Companion.toHttpUrl
import org.json.JSONObject
import org.koitharu.kotatsu.parsers.MangaLoaderContext
import org.koitharu.kotatsu.parsers.MangaParser
@ -9,11 +10,11 @@ import org.koitharu.kotatsu.parsers.config.ConfigKey
import org.koitharu.kotatsu.parsers.exception.ParseException
import org.koitharu.kotatsu.parsers.model.*
import org.koitharu.kotatsu.parsers.util.*
import org.koitharu.kotatsu.parsers.util.json.getStringOrNull
import org.koitharu.kotatsu.parsers.util.json.mapJSON
import org.koitharu.kotatsu.parsers.util.json.mapJSONIndexed
import org.koitharu.kotatsu.parsers.util.json.mapJSONToSet
import org.koitharu.kotatsu.parsers.util.json.toJSONList
import java.net.URI
import java.net.URLEncoder
import java.util.*
import javax.crypto.Mac
@ -22,17 +23,17 @@ import javax.crypto.spec.SecretKeySpec
internal abstract class LineWebtoonsParser(context: MangaLoaderContext, source: MangaSource) : MangaParser(context, source) {
private val signer = WebtoonsUrlSigner("gUtPzJFZch4ZyAGviiyH94P99lQ3pFdRTwpJWDlSGFfwgpr6ses5ALOxWHOIT7R1")
// we don't __really__ support changing this domain because:
// 1. I don't think other websites have this exact API
// 2. most communication is done with other domains (hosting API and static content), which are not configurable
// 3. we rely on the HTTP client setting the referer header to webtoons.com
//
// This effectively means that changing the domain will break the source. Yikes
override val configKeyDomain
get() = ConfigKey.Domain("webtoons.com")
private val configKeyApiDomain
get() = ConfigKey.Domain("global.apis.naver.com")
private val configKeyStaticDomain
get() = ConfigKey.Domain("webtoon-phinf.pstatic.net")
private val apiDomain
get() = config[configKeyApiDomain]
private val staticDomain
get() = config[configKeyStaticDomain]
private val apiDomain = "global.apis.naver.com"
private val staticDomain = "webtoon-phinf.pstatic.net"
override val sortOrders: Set<SortOrder> = EnumSet.of(
SortOrder.POPULARITY,
@ -50,6 +51,14 @@ internal abstract class LineWebtoonsParser(context: MangaLoaderContext, source:
return page.url
}
// some language tags do not map perfectly to the ones used by the API
private val languageCode: String
get() = when (val tag = sourceLocale.toLanguageTag()) {
"in" -> "id"
"zh" -> "zh-hant"
else -> tag
}
private suspend fun getChapters(titleNo: Long): List<MangaChapter> {
val firstResult = makeRequest("/lineWebtoon/webtoon/challengeEpisodeList.json?v=2&titleNo=$titleNo&startIndex=0&pageSize=30")
@ -79,7 +88,6 @@ internal abstract class LineWebtoonsParser(context: MangaLoaderContext, source:
number = jo.getInt("episodeSeq"),
url = "$titleNo-${jo.getString("episodeNo")}",
uploadDate = jo.getLong("modifyYmdt"),
// do we want to use it for anything?
branch = null,
scanlator = null,
source = source,
@ -98,7 +106,7 @@ internal abstract class LineWebtoonsParser(context: MangaLoaderContext, source:
title = jo.getString("title"),
altTitle = null,
url = "$titleNo",
publicUrl = "https://${domain}/en/canvas/a/list?title_no=${titleNo}",
publicUrl = "https://$domain/$languageCode/canvas/a/list?title_no=${titleNo}",
rating = jo.getDouble("starScoreAverage").toFloat() / 10f,
isNsfw = jo.getBoolean("ageGradeNotice"),
coverUrl = "https://$staticDomain${jo.getString("thumbnail")}",
@ -129,7 +137,7 @@ internal abstract class LineWebtoonsParser(context: MangaLoaderContext, source:
SortOrder.POPULARITY -> "READ_COUNT"
SortOrder.RATING -> "LIKEIT"
else -> {
throw Exception("Unreachable")
throw IllegalArgumentException("Unsupported sort order: $sortOrder")
}
}
@ -149,7 +157,7 @@ internal abstract class LineWebtoonsParser(context: MangaLoaderContext, source:
title = jo.getString("title"),
altTitle = null,
url = "$titleNo",
publicUrl = "https://${domain}/en/canvas/a/list?title_no=${titleNo}",
publicUrl = "https://$domain/$languageCode/canvas/a/list?title_no=$titleNo",
rating = RATING_UNKNOWN,
isNsfw = false,
coverUrl = "https://$staticDomain${jo.getString("thumbnail")}",
@ -162,7 +170,7 @@ internal abstract class LineWebtoonsParser(context: MangaLoaderContext, source:
)
}
} else {
val result = makeRequest("/lineWebtoon/webtoon/challengeGenreTitleList.json?genre=${genre}&sortOrder=${sortOrderStr}&startIndex=${offset+1}&pageSize=20")
val result = makeRequest("/lineWebtoon/webtoon/challengeGenreTitleList.json?genre=$genre&sortOrder=$sortOrderStr&startIndex=${offset+1}&pageSize=20")
val genres = result.getJSONObject("genreList")
.getJSONArray("challengeGenres")
@ -180,13 +188,11 @@ internal abstract class LineWebtoonsParser(context: MangaLoaderContext, source:
title = jo.getString("title"),
altTitle = null,
url = "$titleNo",
publicUrl = "https://${domain}/en/canvas/a/list?title_no=${titleNo}",
publicUrl = "https://$domain/$languageCode/canvas/a/list?title_no=$titleNo",
rating = jo.getDouble("starScoreAverage").toFloat() / 10f,
isNsfw = jo.getBoolean("ageGradeNotice"),
coverUrl = "https://$staticDomain${jo.getString("thumbnail")}",
largeCoverUrl = if (jo.has("thumbnailVertical")) {
"https://$staticDomain${jo.getString("thumbnailVertical")}"
} else { null },
largeCoverUrl = jo.getStringOrNull("thumbnailVertical")?.toAbsoluteUrl(staticDomain),
tags = setOf(genres[jo.getString("representGenre")]!!),
author = jo.getString("writingAuthorName"),
description = jo.getString("synopsis"),
@ -201,9 +207,9 @@ internal abstract class LineWebtoonsParser(context: MangaLoaderContext, source:
}
override suspend fun getPages(chapter: MangaChapter): List<MangaPage> {
val (titleNo, episodeNo) = chapter.url.splitTwoParts('-')!!
val (titleNo, episodeNo) = requireNotNull(chapter.url.splitTwoParts('-'))
return makeRequest("/lineWebtoon/webtoon/challengeEpisodeInfo.json?v=2&titleNo=${titleNo}&episodeNo=${episodeNo}")
return makeRequest("/lineWebtoon/webtoon/challengeEpisodeInfo.json?v=2&titleNo=$titleNo&episodeNo=$episodeNo")
.getJSONObject("episodeInfo")
.getJSONArray("imageInfo")
.mapJSONIndexed() { i, jo ->
@ -245,24 +251,18 @@ internal abstract class LineWebtoonsParser(context: MangaLoaderContext, source:
}
private fun finalizeUrl(url: String): String {
val urlWithHost = "https://${apiDomain}$url"
val uri = URI(urlWithHost)
val hasVersion = (uri.rawQuery ?: "").split("&").any { it.startsWith("v=") }
val hasQuery = uri.rawQuery != null
// some language tags do not map perfectly to the ones used by the API
val language = when (val tag = sourceLocale.toLanguageTag()) {
"in" -> "id"
"zh" -> "zh-hant"
else -> tag
}
val absoluteUrl = url.toAbsoluteUrl(apiDomain)
val parsedUrl = absoluteUrl.toHttpUrl()
val hasVersion = parsedUrl.queryParameter("v") != null
val hasQuery = parsedUrl.query != null
val urlWithParams = urlWithHost + if (hasQuery) {
val urlWithParams = absoluteUrl + if (hasQuery) {
"&"
} else {
"?"
} + "serviceZone=GLOBAL&" + if (!hasVersion) {
"v=1"
} else { "" } + "&language=${language}&locale=${language}&platform=APP_ANDROID"
} else { "" } + "&language=$languageCode&locale=$languageCode&platform=APP_ANDROID"
return signer.makeEncryptUrl(urlWithParams)
}
@ -317,7 +317,7 @@ private class WebtoonsUrlSigner(val secret: String) {
"&"
} else {
"?"
} + "msgpad=${msgpad}&md=${digest}"
} + "msgpad=$msgpad&md=$digest"
}
}

Loading…
Cancel
Save