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 package org.koitharu.kotatsu.parsers.site.en
import okhttp3.Headers import okhttp3.Headers
import okhttp3.HttpUrl.Companion.toHttpUrl
import org.json.JSONObject import org.json.JSONObject
import org.koitharu.kotatsu.parsers.MangaLoaderContext import org.koitharu.kotatsu.parsers.MangaLoaderContext
import org.koitharu.kotatsu.parsers.MangaParser 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.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.*
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.mapJSONIndexed import org.koitharu.kotatsu.parsers.util.json.mapJSONIndexed
import org.koitharu.kotatsu.parsers.util.json.mapJSONToSet import org.koitharu.kotatsu.parsers.util.json.mapJSONToSet
import org.koitharu.kotatsu.parsers.util.json.toJSONList import org.koitharu.kotatsu.parsers.util.json.toJSONList
import java.net.URI
import java.net.URLEncoder import java.net.URLEncoder
import java.util.* import java.util.*
import javax.crypto.Mac import javax.crypto.Mac
@ -22,17 +23,17 @@ import javax.crypto.spec.SecretKeySpec
internal abstract class LineWebtoonsParser(context: MangaLoaderContext, source: MangaSource) : MangaParser(context, source) { internal abstract class LineWebtoonsParser(context: MangaLoaderContext, source: MangaSource) : MangaParser(context, source) {
private val signer = WebtoonsUrlSigner("gUtPzJFZch4ZyAGviiyH94P99lQ3pFdRTwpJWDlSGFfwgpr6ses5ALOxWHOIT7R1") 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 override val configKeyDomain
get() = ConfigKey.Domain("webtoons.com") 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 private val apiDomain = "global.apis.naver.com"
get() = config[configKeyApiDomain] private val staticDomain = "webtoon-phinf.pstatic.net"
private val staticDomain
get() = config[configKeyStaticDomain]
override val sortOrders: Set<SortOrder> = EnumSet.of( override val sortOrders: Set<SortOrder> = EnumSet.of(
SortOrder.POPULARITY, SortOrder.POPULARITY,
@ -50,6 +51,14 @@ internal abstract class LineWebtoonsParser(context: MangaLoaderContext, source:
return page.url 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> { private suspend fun getChapters(titleNo: Long): List<MangaChapter> {
val firstResult = makeRequest("/lineWebtoon/webtoon/challengeEpisodeList.json?v=2&titleNo=$titleNo&startIndex=0&pageSize=30") 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"), number = jo.getInt("episodeSeq"),
url = "$titleNo-${jo.getString("episodeNo")}", url = "$titleNo-${jo.getString("episodeNo")}",
uploadDate = jo.getLong("modifyYmdt"), uploadDate = jo.getLong("modifyYmdt"),
// do we want to use it for anything?
branch = null, branch = null,
scanlator = null, scanlator = null,
source = source, source = source,
@ -98,7 +106,7 @@ internal abstract class LineWebtoonsParser(context: MangaLoaderContext, source:
title = jo.getString("title"), title = jo.getString("title"),
altTitle = null, altTitle = null,
url = "$titleNo", 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, rating = jo.getDouble("starScoreAverage").toFloat() / 10f,
isNsfw = jo.getBoolean("ageGradeNotice"), isNsfw = jo.getBoolean("ageGradeNotice"),
coverUrl = "https://$staticDomain${jo.getString("thumbnail")}", coverUrl = "https://$staticDomain${jo.getString("thumbnail")}",
@ -129,7 +137,7 @@ internal abstract class LineWebtoonsParser(context: MangaLoaderContext, source:
SortOrder.POPULARITY -> "READ_COUNT" SortOrder.POPULARITY -> "READ_COUNT"
SortOrder.RATING -> "LIKEIT" SortOrder.RATING -> "LIKEIT"
else -> { 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"), title = jo.getString("title"),
altTitle = null, altTitle = null,
url = "$titleNo", 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, rating = RATING_UNKNOWN,
isNsfw = false, isNsfw = false,
coverUrl = "https://$staticDomain${jo.getString("thumbnail")}", coverUrl = "https://$staticDomain${jo.getString("thumbnail")}",
@ -162,7 +170,7 @@ internal abstract class LineWebtoonsParser(context: MangaLoaderContext, source:
) )
} }
} else { } 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") val genres = result.getJSONObject("genreList")
.getJSONArray("challengeGenres") .getJSONArray("challengeGenres")
@ -180,13 +188,11 @@ internal abstract class LineWebtoonsParser(context: MangaLoaderContext, source:
title = jo.getString("title"), title = jo.getString("title"),
altTitle = null, altTitle = null,
url = "$titleNo", 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, rating = jo.getDouble("starScoreAverage").toFloat() / 10f,
isNsfw = jo.getBoolean("ageGradeNotice"), isNsfw = jo.getBoolean("ageGradeNotice"),
coverUrl = "https://$staticDomain${jo.getString("thumbnail")}", coverUrl = "https://$staticDomain${jo.getString("thumbnail")}",
largeCoverUrl = if (jo.has("thumbnailVertical")) { largeCoverUrl = jo.getStringOrNull("thumbnailVertical")?.toAbsoluteUrl(staticDomain),
"https://$staticDomain${jo.getString("thumbnailVertical")}"
} else { null },
tags = setOf(genres[jo.getString("representGenre")]!!), tags = setOf(genres[jo.getString("representGenre")]!!),
author = jo.getString("writingAuthorName"), author = jo.getString("writingAuthorName"),
description = jo.getString("synopsis"), description = jo.getString("synopsis"),
@ -201,9 +207,9 @@ internal abstract class LineWebtoonsParser(context: MangaLoaderContext, source:
} }
override suspend fun getPages(chapter: MangaChapter): List<MangaPage> { 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") .getJSONObject("episodeInfo")
.getJSONArray("imageInfo") .getJSONArray("imageInfo")
.mapJSONIndexed() { i, jo -> .mapJSONIndexed() { i, jo ->
@ -245,24 +251,18 @@ internal abstract class LineWebtoonsParser(context: MangaLoaderContext, source:
} }
private fun finalizeUrl(url: String): String { private fun finalizeUrl(url: String): String {
val urlWithHost = "https://${apiDomain}$url" val absoluteUrl = url.toAbsoluteUrl(apiDomain)
val uri = URI(urlWithHost) val parsedUrl = absoluteUrl.toHttpUrl()
val hasVersion = (uri.rawQuery ?: "").split("&").any { it.startsWith("v=") } val hasVersion = parsedUrl.queryParameter("v") != null
val hasQuery = uri.rawQuery != null val hasQuery = parsedUrl.query != 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 urlWithParams = urlWithHost + if (hasQuery) { val urlWithParams = absoluteUrl + if (hasQuery) {
"&" "&"
} else { } else {
"?" "?"
} + "serviceZone=GLOBAL&" + if (!hasVersion) { } + "serviceZone=GLOBAL&" + if (!hasVersion) {
"v=1" "v=1"
} else { "" } + "&language=${language}&locale=${language}&platform=APP_ANDROID" } else { "" } + "&language=$languageCode&locale=$languageCode&platform=APP_ANDROID"
return signer.makeEncryptUrl(urlWithParams) return signer.makeEncryptUrl(urlWithParams)
} }
@ -317,7 +317,7 @@ private class WebtoonsUrlSigner(val secret: String) {
"&" "&"
} else { } else {
"?" "?"
} + "msgpad=${msgpad}&md=${digest}" } + "msgpad=$msgpad&md=$digest"
} }
} }

Loading…
Cancel
Save