|
|
|
@ -12,10 +12,34 @@ import org.koitharu.kotatsu.parsers.MangaSourceParser
|
|
|
|
import org.koitharu.kotatsu.parsers.config.ConfigKey
|
|
|
|
import org.koitharu.kotatsu.parsers.config.ConfigKey
|
|
|
|
import org.koitharu.kotatsu.parsers.exception.NotFoundException
|
|
|
|
import org.koitharu.kotatsu.parsers.exception.NotFoundException
|
|
|
|
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.ContentType
|
|
|
|
import org.koitharu.kotatsu.parsers.util.*
|
|
|
|
import org.koitharu.kotatsu.parsers.model.Manga
|
|
|
|
import org.koitharu.kotatsu.parsers.util.json.*
|
|
|
|
import org.koitharu.kotatsu.parsers.model.MangaChapter
|
|
|
|
import java.util.*
|
|
|
|
import org.koitharu.kotatsu.parsers.model.MangaListFilter
|
|
|
|
|
|
|
|
import org.koitharu.kotatsu.parsers.model.MangaPage
|
|
|
|
|
|
|
|
import org.koitharu.kotatsu.parsers.model.MangaSource
|
|
|
|
|
|
|
|
import org.koitharu.kotatsu.parsers.model.MangaTag
|
|
|
|
|
|
|
|
import org.koitharu.kotatsu.parsers.model.RATING_UNKNOWN
|
|
|
|
|
|
|
|
import org.koitharu.kotatsu.parsers.model.SortOrder
|
|
|
|
|
|
|
|
import org.koitharu.kotatsu.parsers.util.SuspendLazy
|
|
|
|
|
|
|
|
import org.koitharu.kotatsu.parsers.util.domain
|
|
|
|
|
|
|
|
import org.koitharu.kotatsu.parsers.util.generateUid
|
|
|
|
|
|
|
|
import org.koitharu.kotatsu.parsers.util.json.getBooleanOrDefault
|
|
|
|
|
|
|
|
import org.koitharu.kotatsu.parsers.util.json.getFloatOrDefault
|
|
|
|
|
|
|
|
import org.koitharu.kotatsu.parsers.util.json.getIntOrDefault
|
|
|
|
|
|
|
|
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 org.koitharu.kotatsu.parsers.util.mapChapters
|
|
|
|
|
|
|
|
import org.koitharu.kotatsu.parsers.util.oneOrThrowIfMany
|
|
|
|
|
|
|
|
import org.koitharu.kotatsu.parsers.util.parseJson
|
|
|
|
|
|
|
|
import org.koitharu.kotatsu.parsers.util.splitTwoParts
|
|
|
|
|
|
|
|
import org.koitharu.kotatsu.parsers.util.toAbsoluteUrl
|
|
|
|
|
|
|
|
import org.koitharu.kotatsu.parsers.util.urlEncoded
|
|
|
|
|
|
|
|
import java.util.Calendar
|
|
|
|
|
|
|
|
import java.util.EnumSet
|
|
|
|
import javax.crypto.Mac
|
|
|
|
import javax.crypto.Mac
|
|
|
|
import javax.crypto.spec.SecretKeySpec
|
|
|
|
import javax.crypto.spec.SecretKeySpec
|
|
|
|
|
|
|
|
|
|
|
|
@ -45,12 +69,10 @@ internal abstract class WebtoonsParser(
|
|
|
|
SortOrder.POPULARITY, // views
|
|
|
|
SortOrder.POPULARITY, // views
|
|
|
|
SortOrder.RATING, // star rating
|
|
|
|
SortOrder.RATING, // star rating
|
|
|
|
//SortOrder.LIKE, // likes
|
|
|
|
//SortOrder.LIKE, // likes
|
|
|
|
SortOrder.UPDATED
|
|
|
|
SortOrder.UPDATED,
|
|
|
|
)
|
|
|
|
)
|
|
|
|
override val headers: Headers
|
|
|
|
override val headers: Headers
|
|
|
|
get() = Headers.Builder()
|
|
|
|
get() = Headers.Builder().add("User-Agent", "nApps (Android 12;; linewebtoon; 3.1.0)").build()
|
|
|
|
.add("User-Agent", "nApps (Android 12;; linewebtoon; 3.1.0)")
|
|
|
|
|
|
|
|
.build()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
override suspend fun getPageUrl(page: MangaPage): String {
|
|
|
|
override suspend fun getPageUrl(page: MangaPage): String {
|
|
|
|
return page.url.toAbsoluteUrl(staticDomain)
|
|
|
|
return page.url.toAbsoluteUrl(staticDomain)
|
|
|
|
@ -69,22 +91,14 @@ internal abstract class WebtoonsParser(
|
|
|
|
url = "/lineWebtoon/webtoon/episodeList.json?v=5&titleNo=$titleNo&startIndex=0&pageSize=30",
|
|
|
|
url = "/lineWebtoon/webtoon/episodeList.json?v=5&titleNo=$titleNo&startIndex=0&pageSize=30",
|
|
|
|
)
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
val totalEpisodeCount = firstResult
|
|
|
|
val totalEpisodeCount = firstResult.getJSONObject("episodeList").getInt("totalServiceEpisodeCount")
|
|
|
|
.getJSONObject("episodeList")
|
|
|
|
|
|
|
|
.getInt("totalServiceEpisodeCount")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
val episodes = firstResult
|
|
|
|
val episodes = firstResult.getJSONObject("episodeList").getJSONArray("episode").toJSONList().toMutableList()
|
|
|
|
.getJSONObject("episodeList")
|
|
|
|
|
|
|
|
.getJSONArray("episode")
|
|
|
|
|
|
|
|
.toJSONList()
|
|
|
|
|
|
|
|
.toMutableList()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
while (episodes.count() < totalEpisodeCount) {
|
|
|
|
while (episodes.count() < totalEpisodeCount) {
|
|
|
|
val page = makeRequest(
|
|
|
|
val page = makeRequest(
|
|
|
|
url = "/lineWebtoon/webtoon/episodeList.json?v=5&titleNo=$titleNo&startIndex=${episodes.count()}&pageSize=30",
|
|
|
|
url = "/lineWebtoon/webtoon/episodeList.json?v=5&titleNo=$titleNo&startIndex=${episodes.count()}&pageSize=30",
|
|
|
|
).getJSONObject("episodeList")
|
|
|
|
).getJSONObject("episodeList").getJSONArray("episode").toJSONList()
|
|
|
|
.getJSONArray("episode")
|
|
|
|
|
|
|
|
.toJSONList()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
episodes.addAll(page)
|
|
|
|
episodes.addAll(page)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
@ -107,8 +121,7 @@ internal abstract class WebtoonsParser(
|
|
|
|
val titleNo = manga.url.toLong()
|
|
|
|
val titleNo = manga.url.toLong()
|
|
|
|
val chaptersDeferred = async { getChapters(titleNo) }
|
|
|
|
val chaptersDeferred = async { getChapters(titleNo) }
|
|
|
|
|
|
|
|
|
|
|
|
makeRequest("/lineWebtoon/webtoon/titleInfo.json?titleNo=${titleNo}&anyServiceStatus=false")
|
|
|
|
makeRequest("/lineWebtoon/webtoon/titleInfo.json?titleNo=${titleNo}&anyServiceStatus=false").getJSONObject("titleInfo")
|
|
|
|
.getJSONObject("titleInfo")
|
|
|
|
|
|
|
|
.let { jo ->
|
|
|
|
.let { jo ->
|
|
|
|
Manga(
|
|
|
|
Manga(
|
|
|
|
id = generateUid(titleNo),
|
|
|
|
id = generateUid(titleNo),
|
|
|
|
@ -132,43 +145,36 @@ internal abstract class WebtoonsParser(
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private val allGenreCache = SuspendLazy {
|
|
|
|
private val allGenreCache = SuspendLazy {
|
|
|
|
makeRequest("/lineWebtoon/webtoon/genreList.json")
|
|
|
|
makeRequest("/lineWebtoon/webtoon/genreList.json").getJSONObject("genreList").getJSONArray("genres")
|
|
|
|
.getJSONObject("genreList")
|
|
|
|
.mapJSON { jo -> parseTag(jo) }.associateBy { tag -> tag.key }
|
|
|
|
.getJSONArray("genres")
|
|
|
|
|
|
|
|
.mapJSON { jo -> parseTag(jo) }
|
|
|
|
|
|
|
|
.associateBy { tag -> tag.key }
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private val allTitleCache = SuspendLazy {
|
|
|
|
private val allTitleCache = SuspendLazy {
|
|
|
|
makeRequest("/lineWebtoon/webtoon/titleList.json?")
|
|
|
|
makeRequest("/lineWebtoon/webtoon/titleList.json?").getJSONObject("titleList").getJSONArray("titles")
|
|
|
|
.getJSONObject("titleList")
|
|
|
|
|
|
|
|
.getJSONArray("titles")
|
|
|
|
|
|
|
|
.mapJSON { jo ->
|
|
|
|
.mapJSON { jo ->
|
|
|
|
val titleNo = jo.getLong("titleNo")
|
|
|
|
val titleNo = jo.getLong("titleNo")
|
|
|
|
Manga(
|
|
|
|
Manga(
|
|
|
|
id = generateUid(titleNo),
|
|
|
|
id = generateUid(titleNo),
|
|
|
|
url = titleNo.toString(),
|
|
|
|
url = titleNo.toString(),
|
|
|
|
publicUrl = "https://$domain/$languageCode/originals/a/list?title_no=$titleNo",
|
|
|
|
publicUrl = "https://$domain/$languageCode/originals/a/list?title_no=$titleNo",
|
|
|
|
title = jo.getString("title"),
|
|
|
|
title = jo.getString("title"),
|
|
|
|
coverUrl = jo.getString("thumbnail").toAbsoluteUrl(staticDomain),
|
|
|
|
coverUrl = jo.getString("thumbnail").toAbsoluteUrl(staticDomain),
|
|
|
|
altTitle = null,
|
|
|
|
altTitle = null,
|
|
|
|
author = jo.getStringOrNull("writingAuthorName"),
|
|
|
|
author = jo.getStringOrNull("writingAuthorName"),
|
|
|
|
isNsfw = jo.getBooleanOrDefault("ageGradeNotice", isNsfwSource),
|
|
|
|
isNsfw = jo.getBooleanOrDefault("ageGradeNotice", isNsfwSource),
|
|
|
|
rating = jo.getFloatOrDefault("starScoreAverage", -10f) / 10f,
|
|
|
|
rating = jo.getFloatOrDefault("starScoreAverage", -10f) / 10f,
|
|
|
|
tags = setOfNotNull(allGenreCache.get()[jo.getString("representGenre")]),
|
|
|
|
tags = setOfNotNull(allGenreCache.get()[jo.getString("representGenre")]),
|
|
|
|
description = jo.getString("synopsis"),
|
|
|
|
description = jo.getString("synopsis"),
|
|
|
|
state = null,
|
|
|
|
state = null,
|
|
|
|
source = source,
|
|
|
|
source = source,
|
|
|
|
date = jo.getLong("lastEpisodeRegisterYmdt"),
|
|
|
|
date = jo.getLong("lastEpisodeRegisterYmdt"),
|
|
|
|
readCount = jo.getLong("readCount"),
|
|
|
|
readCount = jo.getLong("readCount"),
|
|
|
|
likeCount = jo.getLong("likeitCount")
|
|
|
|
likeCount = jo.getLong("likeitCount"),
|
|
|
|
)
|
|
|
|
)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private suspend fun getAllGenreList(): Map<String, MangaTag> {
|
|
|
|
|
|
|
|
|
|
|
|
private suspend fun getAllGenreList(): Map<String, MangaTag>{
|
|
|
|
|
|
|
|
return allGenreCache.get()
|
|
|
|
return allGenreCache.get()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@ -179,68 +185,63 @@ internal abstract class WebtoonsParser(
|
|
|
|
|
|
|
|
|
|
|
|
override suspend fun getList(offset: Int, filter: MangaListFilter?): List<Manga> {
|
|
|
|
override suspend fun getList(offset: Int, filter: MangaListFilter?): List<Manga> {
|
|
|
|
|
|
|
|
|
|
|
|
val manga =
|
|
|
|
val manga = when (filter) {
|
|
|
|
when (filter) {
|
|
|
|
is MangaListFilter.Search -> {
|
|
|
|
is MangaListFilter.Search -> {
|
|
|
|
makeRequest("/lineWebtoon/webtoon/searchWebtoon?query=${filter.query.urlEncoded()}").getJSONObject("webtoonSearch")
|
|
|
|
makeRequest("/lineWebtoon/webtoon/searchWebtoon?query=${filter.query.urlEncoded()}")
|
|
|
|
.getJSONArray("titleList").mapJSON { jo ->
|
|
|
|
.getJSONObject("webtoonSearch")
|
|
|
|
val titleNo = jo.getLong("titleNo")
|
|
|
|
.getJSONArray("titleList")
|
|
|
|
Manga(
|
|
|
|
.mapJSON { jo ->
|
|
|
|
id = generateUid(titleNo),
|
|
|
|
val titleNo = jo.getLong("titleNo")
|
|
|
|
title = jo.getString("title"),
|
|
|
|
Manga(
|
|
|
|
altTitle = null,
|
|
|
|
id = generateUid(titleNo),
|
|
|
|
url = titleNo.toString(),
|
|
|
|
title = jo.getString("title"),
|
|
|
|
publicUrl = "https://$domain/$languageCode/originals/a/list?title_no=$titleNo",
|
|
|
|
altTitle = null,
|
|
|
|
rating = RATING_UNKNOWN,
|
|
|
|
url = titleNo.toString(),
|
|
|
|
isNsfw = isNsfwSource,
|
|
|
|
publicUrl = "https://$domain/$languageCode/originals/a/list?title_no=$titleNo",
|
|
|
|
coverUrl = jo.getString("thumbnail").toAbsoluteUrl(staticDomain),
|
|
|
|
rating = RATING_UNKNOWN,
|
|
|
|
largeCoverUrl = null,
|
|
|
|
isNsfw = isNsfwSource,
|
|
|
|
tags = emptySet(),
|
|
|
|
coverUrl = jo.getString("thumbnail").toAbsoluteUrl(staticDomain),
|
|
|
|
author = jo.getStringOrNull("writingAuthorName"),
|
|
|
|
largeCoverUrl = null,
|
|
|
|
description = null,
|
|
|
|
tags = emptySet(),
|
|
|
|
state = null,
|
|
|
|
author = jo.getStringOrNull("writingAuthorName"),
|
|
|
|
source = source,
|
|
|
|
description = null,
|
|
|
|
)
|
|
|
|
state = null,
|
|
|
|
}
|
|
|
|
source = source,
|
|
|
|
}
|
|
|
|
)
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
is MangaListFilter.Advanced -> {
|
|
|
|
|
|
|
|
val genre = filter.tags.oneOrThrowIfMany()?.key ?: "ALL"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
val genres = getAllGenreList()
|
|
|
|
is MangaListFilter.Advanced -> {
|
|
|
|
val result = getAllTitleList()
|
|
|
|
val genre = filter.tags.oneOrThrowIfMany()?.key ?: "ALL"
|
|
|
|
|
|
|
|
|
|
|
|
val sortedResult = when (filter.sortOrder) {
|
|
|
|
val genres = getAllGenreList()
|
|
|
|
SortOrder.UPDATED -> result.sortedBy { it.date }
|
|
|
|
val result = getAllTitleList()
|
|
|
|
SortOrder.POPULARITY -> result.sortedByDescending { it.readCount }
|
|
|
|
|
|
|
|
SortOrder.RATING -> result.sortedByDescending { it.rating }
|
|
|
|
|
|
|
|
//SortOrder.LIKE -> result.sortedBy { it.likeCount }
|
|
|
|
|
|
|
|
else -> throw IllegalArgumentException("Unsupported sort order: ${filter.sortOrder}")
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (genre != "ALL") {
|
|
|
|
val sortedResult = when (filter.sortOrder) {
|
|
|
|
sortedResult.filter { it.tags.contains(genres[genre]) }.subList(offset, offset + 20)
|
|
|
|
SortOrder.UPDATED -> result.sortedBy { it.date }
|
|
|
|
} else {
|
|
|
|
SortOrder.POPULARITY -> result.sortedByDescending { it.readCount }
|
|
|
|
sortedResult.subList(offset, offset + 20)
|
|
|
|
SortOrder.RATING -> result.sortedByDescending { it.rating }
|
|
|
|
}
|
|
|
|
//SortOrder.LIKE -> result.sortedBy { it.likeCount }
|
|
|
|
|
|
|
|
else -> throw IllegalArgumentException("Unsupported sort order: ${filter.sortOrder}")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
null -> {
|
|
|
|
if (genre != "ALL") {
|
|
|
|
getAllTitleList().subList(offset, offset + 20)
|
|
|
|
sortedResult.filter { it.tags.contains(genres[genre]) }
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
sortedResult
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return manga
|
|
|
|
null -> {
|
|
|
|
|
|
|
|
getAllTitleList()
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
return manga.subList(offset, (offset + 20).coerceAtMost(manga.size))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
override suspend fun getPages(chapter: MangaChapter): List<MangaPage> {
|
|
|
|
override suspend fun getPages(chapter: MangaChapter): List<MangaPage> {
|
|
|
|
val (titleNo, episodeNo) = requireNotNull(chapter.url.splitTwoParts('-'))
|
|
|
|
val (titleNo, episodeNo) = requireNotNull(chapter.url.splitTwoParts('-'))
|
|
|
|
return makeRequest("/lineWebtoon/webtoon/episodeInfo.json?v=4&titleNo=$titleNo&episodeNo=$episodeNo")
|
|
|
|
return makeRequest("/lineWebtoon/webtoon/episodeInfo.json?v=4&titleNo=$titleNo&episodeNo=$episodeNo").getJSONObject(
|
|
|
|
.getJSONObject("episodeInfo")
|
|
|
|
"episodeInfo",
|
|
|
|
.getJSONArray("imageInfo")
|
|
|
|
).getJSONArray("imageInfo").mapJSONIndexed { i, jo ->
|
|
|
|
.mapJSONIndexed { i, jo ->
|
|
|
|
|
|
|
|
MangaPage(
|
|
|
|
MangaPage(
|
|
|
|
id = generateUid("$titleNo-$episodeNo-$i"),
|
|
|
|
id = generateUid("$titleNo-$episodeNo-$i"),
|
|
|
|
url = jo.getString("url"),
|
|
|
|
url = jo.getString("url"),
|
|
|
|
@ -259,9 +260,7 @@ internal abstract class WebtoonsParser(
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
override suspend fun getAvailableTags(): Set<MangaTag> {
|
|
|
|
override suspend fun getAvailableTags(): Set<MangaTag> {
|
|
|
|
return makeRequest("/lineWebtoon/webtoon/genreList.json")
|
|
|
|
return makeRequest("/lineWebtoon/webtoon/genreList.json").getJSONObject("genreList").getJSONArray("genres")
|
|
|
|
.getJSONObject("genreList")
|
|
|
|
|
|
|
|
.getJSONArray("genres")
|
|
|
|
|
|
|
|
.mapJSONToSet { jo -> parseTag(jo) }
|
|
|
|
.mapJSONToSet { jo -> parseTag(jo) }
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@ -281,13 +280,11 @@ internal abstract class WebtoonsParser(
|
|
|
|
|
|
|
|
|
|
|
|
private fun finalizeUrl(url: String): HttpUrl {
|
|
|
|
private fun finalizeUrl(url: String): HttpUrl {
|
|
|
|
val httpUrl = url.toAbsoluteUrl(apiDomain).toHttpUrl()
|
|
|
|
val httpUrl = url.toAbsoluteUrl(apiDomain).toHttpUrl()
|
|
|
|
val builder = httpUrl.newBuilder()
|
|
|
|
val builder = httpUrl.newBuilder().addQueryParameter("serviceZone", "GLOBAL")
|
|
|
|
.addQueryParameter("serviceZone", "GLOBAL")
|
|
|
|
|
|
|
|
if (httpUrl.queryParameter("v") == null) {
|
|
|
|
if (httpUrl.queryParameter("v") == null) {
|
|
|
|
builder.addQueryParameter("v", "1")
|
|
|
|
builder.addQueryParameter("v", "1")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
builder.addQueryParameter("language", languageCode)
|
|
|
|
builder.addQueryParameter("language", languageCode).addQueryParameter("locale", "languageCode")
|
|
|
|
.addQueryParameter("locale", "languageCode")
|
|
|
|
|
|
|
|
.addQueryParameter("platform", "APP_ANDROID")
|
|
|
|
.addQueryParameter("platform", "APP_ANDROID")
|
|
|
|
signer.makeEncryptUrl(builder)
|
|
|
|
signer.makeEncryptUrl(builder)
|
|
|
|
return builder.build()
|
|
|
|
return builder.build()
|
|
|
|
@ -296,7 +293,6 @@ internal abstract class WebtoonsParser(
|
|
|
|
@MangaSourceParser("WEBTOONS_EN", "Webtoons English", "en", type = ContentType.MANGA)
|
|
|
|
@MangaSourceParser("WEBTOONS_EN", "Webtoons English", "en", type = ContentType.MANGA)
|
|
|
|
class English(context: MangaLoaderContext) : WebtoonsParser(context, MangaSource.WEBTOONS_EN)
|
|
|
|
class English(context: MangaLoaderContext) : WebtoonsParser(context, MangaSource.WEBTOONS_EN)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private inner class WebtoonsUrlSigner(private val secret: String) {
|
|
|
|
private inner class WebtoonsUrlSigner(private val secret: String) {
|
|
|
|
|
|
|
|
|
|
|
|
private val mac = Mac.getInstance("HmacSHA1").apply {
|
|
|
|
private val mac = Mac.getInstance("HmacSHA1").apply {
|
|
|
|
@ -315,9 +311,7 @@ internal abstract class WebtoonsParser(
|
|
|
|
fun makeEncryptUrl(urlBuilder: HttpUrl.Builder) {
|
|
|
|
fun makeEncryptUrl(urlBuilder: HttpUrl.Builder) {
|
|
|
|
val msgPad = Calendar.getInstance().timeInMillis.toString()
|
|
|
|
val msgPad = Calendar.getInstance().timeInMillis.toString()
|
|
|
|
val digest = getMessageDigest(getMessage(urlBuilder.build().toString(), msgPad))
|
|
|
|
val digest = getMessageDigest(getMessage(urlBuilder.build().toString(), msgPad))
|
|
|
|
urlBuilder
|
|
|
|
urlBuilder.addQueryParameter("msgpad", msgPad).addQueryParameter("md", digest)
|
|
|
|
.addQueryParameter("msgpad", msgPad)
|
|
|
|
|
|
|
|
.addQueryParameter("md", digest)
|
|
|
|
|
|
|
|
// .addEncodedQueryParameter("md", digest.urlEncoded())
|
|
|
|
// .addEncodedQueryParameter("md", digest.urlEncoded())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|