fixup! fixup! fixed offset

master
Naga 2 years ago
parent 24a25e3dbf
commit de58333dc4

@ -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,17 +145,12 @@ 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(
@ -161,14 +169,12 @@ internal abstract class WebtoonsParser(
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,13 +185,10 @@ 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()}") makeRequest("/lineWebtoon/webtoon/searchWebtoon?query=${filter.query.urlEncoded()}").getJSONObject("webtoonSearch")
.getJSONObject("webtoonSearch") .getJSONArray("titleList").mapJSON { jo ->
.getJSONArray("titleList")
.mapJSON { jo ->
val titleNo = jo.getLong("titleNo") val titleNo = jo.getLong("titleNo")
Manga( Manga(
id = generateUid(titleNo), id = generateUid(titleNo),
@ -221,26 +224,24 @@ internal abstract class WebtoonsParser(
} }
if (genre != "ALL") { if (genre != "ALL") {
sortedResult.filter { it.tags.contains(genres[genre]) }.subList(offset, offset + 20) sortedResult.filter { it.tags.contains(genres[genre]) }
} else { } else {
sortedResult.subList(offset, offset + 20) sortedResult
} }
} }
null -> { null -> {
getAllTitleList().subList(offset, offset + 20) getAllTitleList()
} }
} }
return manga.subList(offset, (offset + 20).coerceAtMost(manga.size))
return manga
} }
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())
} }
} }

Loading…
Cancel
Save