fixed suggestions
This commit is contained in:
@@ -67,14 +67,7 @@ class Manga(
|
|||||||
* Manga source
|
* Manga source
|
||||||
*/
|
*/
|
||||||
@JvmField val source: MangaSource,
|
@JvmField val source: MangaSource,
|
||||||
|
) {
|
||||||
@JvmField val date: Long? = null,
|
|
||||||
|
|
||||||
@JvmField val readCount: Long? = null,
|
|
||||||
|
|
||||||
@JvmField val likeCount: Long? = null,
|
|
||||||
|
|
||||||
) {
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return if manga has a specified rating
|
* Return if manga has a specified rating
|
||||||
@@ -116,10 +109,7 @@ class Manga(
|
|||||||
largeCoverUrl = largeCoverUrl,
|
largeCoverUrl = largeCoverUrl,
|
||||||
description = description,
|
description = description,
|
||||||
chapters = chapters,
|
chapters = chapters,
|
||||||
source = source,
|
source = source
|
||||||
date = date,
|
|
||||||
readCount = readCount,
|
|
||||||
likeCount = likeCount,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
override fun equals(other: Any?): Boolean {
|
override fun equals(other: Any?): Boolean {
|
||||||
@@ -143,9 +133,7 @@ class Manga(
|
|||||||
if (description != other.description) return false
|
if (description != other.description) return false
|
||||||
if (chapters != other.chapters) return false
|
if (chapters != other.chapters) return false
|
||||||
if (source != other.source) return false
|
if (source != other.source) return false
|
||||||
if (date != other.date) return false
|
|
||||||
if (readCount != other.readCount) return false
|
|
||||||
if (likeCount != other.likeCount) return false
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -165,9 +153,6 @@ class Manga(
|
|||||||
result = 31 * result + (description?.hashCode() ?: 0)
|
result = 31 * result + (description?.hashCode() ?: 0)
|
||||||
result = 31 * result + (chapters?.hashCode() ?: 0)
|
result = 31 * result + (chapters?.hashCode() ?: 0)
|
||||||
result = 31 * result + source.hashCode()
|
result = 31 * result + source.hashCode()
|
||||||
result = 31 * result + (date?.hashCode() ?: 0)
|
|
||||||
result = 31 * result + (readCount?.hashCode() ?: 0)
|
|
||||||
result = 31 * result + (likeCount?.hashCode() ?: 0)
|
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
package org.koitharu.kotatsu.parsers.site.all
|
package org.koitharu.kotatsu.parsers.site.all
|
||||||
|
|
||||||
import kotlinx.coroutines.Dispatchers
|
|
||||||
import kotlinx.coroutines.async
|
import kotlinx.coroutines.async
|
||||||
import kotlinx.coroutines.awaitAll
|
import kotlinx.coroutines.awaitAll
|
||||||
import kotlinx.coroutines.coroutineScope
|
import kotlinx.coroutines.coroutineScope
|
||||||
@@ -24,6 +23,7 @@ import org.koitharu.kotatsu.parsers.model.MangaSource
|
|||||||
import org.koitharu.kotatsu.parsers.model.MangaTag
|
import org.koitharu.kotatsu.parsers.model.MangaTag
|
||||||
import org.koitharu.kotatsu.parsers.model.RATING_UNKNOWN
|
import org.koitharu.kotatsu.parsers.model.RATING_UNKNOWN
|
||||||
import org.koitharu.kotatsu.parsers.model.SortOrder
|
import org.koitharu.kotatsu.parsers.model.SortOrder
|
||||||
|
import org.koitharu.kotatsu.parsers.util.SoftSuspendLazy
|
||||||
import org.koitharu.kotatsu.parsers.util.SuspendLazy
|
import org.koitharu.kotatsu.parsers.util.SuspendLazy
|
||||||
import org.koitharu.kotatsu.parsers.util.domain
|
import org.koitharu.kotatsu.parsers.util.domain
|
||||||
import org.koitharu.kotatsu.parsers.util.generateUid
|
import org.koitharu.kotatsu.parsers.util.generateUid
|
||||||
@@ -86,50 +86,19 @@ internal abstract class WebtoonsParser(
|
|||||||
"zh" -> "zh-hant"
|
"zh" -> "zh-hant"
|
||||||
else -> tag
|
else -> tag
|
||||||
}
|
}
|
||||||
/*
|
|
||||||
private suspend fun getChapters(titleNo: Long): List<MangaChapter> {
|
|
||||||
val firstResult = makeRequest(
|
|
||||||
url = "/lineWebtoon/webtoon/episodeList.json?v=5&titleNo=$titleNo&startIndex=0&pageSize=30",
|
|
||||||
)
|
|
||||||
|
|
||||||
val totalEpisodeCount = firstResult.getJSONObject("episodeList").getInt("totalServiceEpisodeCount")
|
|
||||||
|
|
||||||
val episodes = firstResult.getJSONObject("episodeList").getJSONArray("episode").toJSONList().toMutableList()
|
|
||||||
|
|
||||||
while (episodes.count() < totalEpisodeCount) {
|
|
||||||
val page = makeRequest(
|
|
||||||
url = "/lineWebtoon/webtoon/episodeList.json?v=5&titleNo=$titleNo&startIndex=${episodes.count()}&pageSize=30",
|
|
||||||
).getJSONObject("episodeList").getJSONArray("episode").toJSONList()
|
|
||||||
|
|
||||||
episodes.addAll(page)
|
|
||||||
}
|
|
||||||
return episodes.mapChapters { i, jo ->
|
|
||||||
MangaChapter(
|
|
||||||
id = generateUid("$titleNo-$i"),
|
|
||||||
name = jo.getString("episodeTitle"),
|
|
||||||
number = jo.getInt("episodeSeq"),
|
|
||||||
url = "$titleNo-${jo.get("episodeNo")}",
|
|
||||||
uploadDate = jo.getLong("registerYmdt"),
|
|
||||||
branch = null,
|
|
||||||
scanlator = null,
|
|
||||||
source = source,
|
|
||||||
)
|
|
||||||
|
|
||||||
}.sortedBy { it.number }
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
private suspend fun fetchEpisodes(titleNo: Long): List<MangaChapter> = coroutineScope {
|
private suspend fun fetchEpisodes(titleNo: Long): List<MangaChapter> = coroutineScope {
|
||||||
val firstResult = makeRequest("/lineWebtoon/webtoon/episodeList.json?v=5&titleNo=$titleNo&startIndex=0&pageSize=30")
|
val firstResult =
|
||||||
|
makeRequest("/lineWebtoon/webtoon/episodeList.json?v=5&titleNo=$titleNo&startIndex=0&pageSize=30")
|
||||||
|
|
||||||
val totalEpisodeCount = firstResult.getJSONObject("episodeList").getInt("totalServiceEpisodeCount")
|
val totalEpisodeCount = firstResult.getJSONObject("episodeList").getInt("totalServiceEpisodeCount")
|
||||||
val episodes = firstResult.getJSONObject("episodeList").getJSONArray("episode").toJSONList().toMutableList()
|
val episodes = firstResult.getJSONObject("episodeList").getJSONArray("episode").toJSONList().toMutableList()
|
||||||
|
|
||||||
val additionalEpisodes = (episodes.size until totalEpisodeCount step 30).map { startIndex ->
|
val additionalEpisodes = (episodes.size until totalEpisodeCount step 30).map { startIndex ->
|
||||||
async(Dispatchers.IO) {
|
async {
|
||||||
makeRequest("/lineWebtoon/webtoon/episodeList.json?v=5&titleNo=$titleNo&startIndex=$startIndex&pageSize=30")
|
makeRequest("/lineWebtoon/webtoon/episodeList.json?v=5&titleNo=$titleNo&startIndex=$startIndex&pageSize=30").getJSONObject(
|
||||||
.getJSONObject("episodeList")
|
"episodeList",
|
||||||
.getJSONArray("episode")
|
).getJSONArray("episode").toJSONList()
|
||||||
.toJSONList()
|
|
||||||
}
|
}
|
||||||
}.awaitAll().flatten()
|
}.awaitAll().flatten()
|
||||||
|
|
||||||
@@ -145,10 +114,11 @@ internal abstract class WebtoonsParser(
|
|||||||
uploadDate = jo.getLong("registerYmdt"),
|
uploadDate = jo.getLong("registerYmdt"),
|
||||||
branch = null,
|
branch = null,
|
||||||
scanlator = null,
|
scanlator = null,
|
||||||
source = source
|
source = source,
|
||||||
)
|
)
|
||||||
}.sortedBy(MangaChapter::number)
|
}.sortedBy(MangaChapter::number)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun JSONArray.toJSONList(): List<JSONObject> {
|
private fun JSONArray.toJSONList(): List<JSONObject> {
|
||||||
val list = mutableListOf<JSONObject>()
|
val list = mutableListOf<JSONObject>()
|
||||||
for (i in 0 until length()) {
|
for (i in 0 until length()) {
|
||||||
@@ -156,31 +126,36 @@ internal abstract class WebtoonsParser(
|
|||||||
}
|
}
|
||||||
return list
|
return list
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun getDetails(manga: Manga): Manga = coroutineScope {
|
override suspend fun getDetails(manga: Manga): Manga = coroutineScope {
|
||||||
val titleNo = manga.url.toLong()
|
val titleNo = manga.url.toLong()
|
||||||
val chaptersDeferred = async { fetchEpisodes(titleNo) }
|
val chaptersDeferred = async { fetchEpisodes(titleNo) }
|
||||||
val chapters = chaptersDeferred.await()
|
val chapters = chaptersDeferred.await()
|
||||||
makeRequest("/lineWebtoon/webtoon/titleInfo.json?titleNo=${titleNo}&anyServiceStatus=false").getJSONObject("titleInfo")
|
makeRequest("/lineWebtoon/webtoon/titleInfo.json?titleNo=${titleNo}&anyServiceStatus=false").getJSONObject("titleInfo")
|
||||||
.let { jo ->
|
.let { jo ->
|
||||||
Manga(
|
MangaWebtoon(
|
||||||
id = generateUid(titleNo),
|
Manga(
|
||||||
title = jo.getString("title"),
|
id = generateUid(titleNo),
|
||||||
altTitle = null,
|
title = jo.getString("title"),
|
||||||
url = "$titleNo",
|
altTitle = null,
|
||||||
publicUrl = "https://$domain/$languageCode/originals/a/list?title_no=${titleNo}",
|
url = "$titleNo",
|
||||||
rating = jo.getFloatOrDefault("starScoreAverage", -10f) / 10f,
|
publicUrl = "https://$domain/$languageCode/originals/a/list?title_no=${titleNo}",
|
||||||
isNsfw = jo.getBooleanOrDefault("ageGradeNotice", isNsfwSource),
|
rating = jo.getFloatOrDefault("starScoreAverage", -10f) / 10f,
|
||||||
coverUrl = jo.getString("thumbnail").toAbsoluteUrl(staticDomain),
|
isNsfw = jo.getBooleanOrDefault("ageGradeNotice", isNsfwSource),
|
||||||
largeCoverUrl = jo.getStringOrNull("thumbnailVertical")?.toAbsoluteUrl(staticDomain),
|
coverUrl = jo.getString("thumbnail").toAbsoluteUrl(staticDomain),
|
||||||
tags = setOf(parseTag(jo.getJSONObject("genreInfo"))),
|
largeCoverUrl = jo.getStringOrNull("thumbnailVertical")?.toAbsoluteUrl(staticDomain),
|
||||||
author = jo.getStringOrNull("writingAuthorName"),
|
tags = setOf(parseTag(jo.getJSONObject("genreInfo"))),
|
||||||
description = jo.getString("synopsis"),
|
author = jo.getStringOrNull("writingAuthorName"),
|
||||||
// I don't think the API provides this info,
|
description = jo.getString("synopsis"),
|
||||||
state = null,
|
// I don't think the API provides this info,
|
||||||
|
state = null,
|
||||||
|
chapters = chapters,
|
||||||
|
source = source,
|
||||||
|
),
|
||||||
date = jo.getLong("lastEpisodeRegisterYmdt"),
|
date = jo.getLong("lastEpisodeRegisterYmdt"),
|
||||||
chapters = chapters,
|
readCount = jo.getLong("readCount"),
|
||||||
source = source,
|
//likeCount = jo.getLong("likeitCount")
|
||||||
)
|
).manga
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -193,23 +168,25 @@ internal abstract class WebtoonsParser(
|
|||||||
makeRequest("/lineWebtoon/webtoon/titleList.json?").getJSONObject("titleList").getJSONArray("titles")
|
makeRequest("/lineWebtoon/webtoon/titleList.json?").getJSONObject("titleList").getJSONArray("titles")
|
||||||
.mapJSON { jo ->
|
.mapJSON { jo ->
|
||||||
val titleNo = jo.getLong("titleNo")
|
val titleNo = jo.getLong("titleNo")
|
||||||
Manga(
|
MangaWebtoon(
|
||||||
id = generateUid(titleNo),
|
Manga(
|
||||||
url = titleNo.toString(),
|
id = generateUid(titleNo),
|
||||||
publicUrl = "https://$domain/$languageCode/originals/a/list?title_no=$titleNo",
|
url = titleNo.toString(),
|
||||||
title = jo.getString("title"),
|
publicUrl = "https://$domain/$languageCode/originals/a/list?title_no=$titleNo",
|
||||||
coverUrl = jo.getString("thumbnail").toAbsoluteUrl(staticDomain),
|
title = jo.getString("title"),
|
||||||
altTitle = null,
|
coverUrl = jo.getString("thumbnail").toAbsoluteUrl(staticDomain),
|
||||||
author = jo.getStringOrNull("writingAuthorName"),
|
altTitle = null,
|
||||||
isNsfw = jo.getBooleanOrDefault("ageGradeNotice", isNsfwSource),
|
author = jo.getStringOrNull("writingAuthorName"),
|
||||||
rating = jo.getFloatOrDefault("starScoreAverage", -10f) / 10f,
|
isNsfw = jo.getBooleanOrDefault("ageGradeNotice", isNsfwSource),
|
||||||
tags = setOfNotNull(allGenreCache.get()[jo.getString("representGenre")]),
|
rating = jo.getFloatOrDefault("starScoreAverage", -10f) / 10f,
|
||||||
description = jo.getString("synopsis"),
|
tags = setOfNotNull(allGenreCache.get()[jo.getString("representGenre")]),
|
||||||
state = null,
|
description = jo.getString("synopsis"),
|
||||||
source = source,
|
state = null,
|
||||||
|
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"),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -218,7 +195,7 @@ internal abstract class WebtoonsParser(
|
|||||||
return allGenreCache.get()
|
return allGenreCache.get()
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun getAllTitleList(): List<Manga> {
|
private suspend fun getAllTitleList(): List<MangaWebtoon> {
|
||||||
return allTitleCache.get()
|
return allTitleCache.get()
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -230,21 +207,26 @@ internal abstract class WebtoonsParser(
|
|||||||
makeRequest("/lineWebtoon/webtoon/searchWebtoon?query=${filter.query.urlEncoded()}").getJSONObject("webtoonSearch")
|
makeRequest("/lineWebtoon/webtoon/searchWebtoon?query=${filter.query.urlEncoded()}").getJSONObject("webtoonSearch")
|
||||||
.getJSONArray("titleList").mapJSON { jo ->
|
.getJSONArray("titleList").mapJSON { jo ->
|
||||||
val titleNo = jo.getLong("titleNo")
|
val titleNo = jo.getLong("titleNo")
|
||||||
Manga(
|
MangaWebtoon(
|
||||||
id = generateUid(titleNo),
|
Manga(
|
||||||
title = jo.getString("title"),
|
id = generateUid(titleNo),
|
||||||
altTitle = null,
|
title = jo.getString("title"),
|
||||||
url = titleNo.toString(),
|
altTitle = null,
|
||||||
publicUrl = "https://$domain/$languageCode/originals/a/list?title_no=$titleNo",
|
url = titleNo.toString(),
|
||||||
rating = RATING_UNKNOWN,
|
publicUrl = "https://$domain/$languageCode/originals/a/list?title_no=$titleNo",
|
||||||
isNsfw = isNsfwSource,
|
rating = RATING_UNKNOWN,
|
||||||
coverUrl = jo.getString("thumbnail").toAbsoluteUrl(staticDomain),
|
isNsfw = isNsfwSource,
|
||||||
largeCoverUrl = null,
|
coverUrl = jo.getString("thumbnail").toAbsoluteUrl(staticDomain),
|
||||||
tags = emptySet(),
|
largeCoverUrl = null,
|
||||||
author = jo.getStringOrNull("writingAuthorName"),
|
tags = emptySet(),
|
||||||
description = null,
|
author = jo.getStringOrNull("writingAuthorName"),
|
||||||
state = null,
|
description = null,
|
||||||
source = source,
|
state = null,
|
||||||
|
source = source,
|
||||||
|
),
|
||||||
|
date = jo.getLong("lastEpisodeRegisterYmdt"),
|
||||||
|
readCount = jo.getLong("readCount"),
|
||||||
|
//likeCount = jo.getLong("likeitCount"),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -258,13 +240,13 @@ internal abstract class WebtoonsParser(
|
|||||||
val sortedResult = when (filter.sortOrder) {
|
val sortedResult = when (filter.sortOrder) {
|
||||||
SortOrder.UPDATED -> result.sortedBy { it.date }
|
SortOrder.UPDATED -> result.sortedBy { it.date }
|
||||||
SortOrder.POPULARITY -> result.sortedByDescending { it.readCount }
|
SortOrder.POPULARITY -> result.sortedByDescending { it.readCount }
|
||||||
SortOrder.RATING -> result.sortedByDescending { it.rating }
|
SortOrder.RATING -> result.sortedByDescending { it.manga.rating }
|
||||||
//SortOrder.LIKE -> result.sortedBy { it.likeCount }
|
//SortOrder.LIKE -> result.sortedBy { it.likeitCount }
|
||||||
else -> throw IllegalArgumentException("Unsupported sort order: ${filter.sortOrder}")
|
else -> throw IllegalArgumentException("Unsupported sort order: ${filter.sortOrder}")
|
||||||
}
|
}
|
||||||
|
|
||||||
if (genre != "ALL") {
|
if (genre != "ALL") {
|
||||||
sortedResult.filter { it.tags.contains(genres[genre]) }
|
sortedResult.filter { it.manga.tags.contains(genres[genre]) }
|
||||||
} else {
|
} else {
|
||||||
sortedResult
|
sortedResult
|
||||||
}
|
}
|
||||||
@@ -273,7 +255,7 @@ internal abstract class WebtoonsParser(
|
|||||||
else -> getAllTitleList()
|
else -> getAllTitleList()
|
||||||
|
|
||||||
}
|
}
|
||||||
return webtoons.subList(offset, (offset + 20).coerceAtMost(webtoons.size))
|
return webtoons.map { it.manga }.subList(offset, (offset + 20).coerceAtMost(webtoons.size))
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun getPages(chapter: MangaChapter): List<MangaPage> {
|
override suspend fun getPages(chapter: MangaChapter): List<MangaPage> {
|
||||||
@@ -281,13 +263,13 @@ internal abstract class WebtoonsParser(
|
|||||||
return makeRequest("/lineWebtoon/webtoon/episodeInfo.json?v=4&titleNo=$titleNo&episodeNo=$episodeNo").getJSONObject(
|
return makeRequest("/lineWebtoon/webtoon/episodeInfo.json?v=4&titleNo=$titleNo&episodeNo=$episodeNo").getJSONObject(
|
||||||
"episodeInfo",
|
"episodeInfo",
|
||||||
).getJSONArray("imageInfo").mapJSONIndexed { i, jo ->
|
).getJSONArray("imageInfo").mapJSONIndexed { i, jo ->
|
||||||
MangaPage(
|
MangaPage(
|
||||||
id = generateUid("$titleNo-$episodeNo-$i"),
|
id = generateUid("$titleNo-$episodeNo-$i"),
|
||||||
url = jo.getString("url"),
|
url = jo.getString("url"),
|
||||||
preview = null,
|
preview = null,
|
||||||
source = source,
|
source = source,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun parseTag(jo: JSONObject): MangaTag {
|
private fun parseTag(jo: JSONObject): MangaTag {
|
||||||
@@ -349,8 +331,6 @@ internal abstract class WebtoonsParser(
|
|||||||
@MangaSourceParser("WEBTOONS_DE", "Webtoons German", "de", type = ContentType.MANGA)
|
@MangaSourceParser("WEBTOONS_DE", "Webtoons German", "de", type = ContentType.MANGA)
|
||||||
class German(context: MangaLoaderContext) : LineWebtoonsParser(context, MangaSource.WEBTOONS_DE)
|
class German(context: MangaLoaderContext) : LineWebtoonsParser(context, MangaSource.WEBTOONS_DE)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
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 {
|
||||||
@@ -373,4 +353,10 @@ internal abstract class WebtoonsParser(
|
|||||||
// .addEncodedQueryParameter("md", digest.urlEncoded())
|
// .addEncodedQueryParameter("md", digest.urlEncoded())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private inner class MangaWebtoon(
|
||||||
|
val manga: Manga,
|
||||||
|
@JvmField val date: Long? = null,
|
||||||
|
@JvmField val readCount: Long? = null,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ internal class TeamXNovel(context: MangaLoaderContext) : PagedMangaParser(contex
|
|||||||
override val availableStates: Set<MangaState> =
|
override val availableStates: Set<MangaState> =
|
||||||
EnumSet.of(MangaState.ONGOING, MangaState.FINISHED, MangaState.ABANDONED)
|
EnumSet.of(MangaState.ONGOING, MangaState.FINISHED, MangaState.ABANDONED)
|
||||||
|
|
||||||
override val configKeyDomain = ConfigKey.Domain("teamxnovel.com")
|
override val configKeyDomain = ConfigKey.Domain("team11x11.com")
|
||||||
override val isMultipleTagsSupported = false
|
override val isMultipleTagsSupported = false
|
||||||
|
|
||||||
override suspend fun getListPage(page: Int, filter: MangaListFilter?): List<Manga> {
|
override suspend fun getListPage(page: Int, filter: MangaListFilter?): List<Manga> {
|
||||||
|
|||||||
Reference in New Issue
Block a user