Zaimanhua: add genre filter & check token expiration (#10357)
* Zaimanhua: make comments list immutable * Zaimanhua: add genre filter Also refactors the ranking filter to allow disabling it. * Zaimanhua: check JWT token expiration * Zaimanhua: use parseAs functions from utils * misc
This commit is contained in:
parent
0fae25ac43
commit
105e329c47
@ -1,7 +1,7 @@
|
|||||||
ext {
|
ext {
|
||||||
extName = 'Zaimanhua'
|
extName = 'Zaimanhua'
|
||||||
extClass = '.Zaimanhua'
|
extClass = '.Zaimanhua'
|
||||||
extVersionCode = 12
|
extVersionCode = 13
|
||||||
isNsfw = false
|
isNsfw = false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -8,6 +8,7 @@ import android.text.Layout
|
|||||||
import android.text.StaticLayout
|
import android.text.StaticLayout
|
||||||
import android.text.TextPaint
|
import android.text.TextPaint
|
||||||
import eu.kanade.tachiyomi.extension.zh.zaimanhua.Zaimanhua.Companion.COMMENTS_FLAG
|
import eu.kanade.tachiyomi.extension.zh.zaimanhua.Zaimanhua.Companion.COMMENTS_FLAG
|
||||||
|
import keiyoushi.utils.parseAs
|
||||||
import okhttp3.Interceptor
|
import okhttp3.Interceptor
|
||||||
import okhttp3.MediaType.Companion.toMediaType
|
import okhttp3.MediaType.Companion.toMediaType
|
||||||
import okhttp3.Response
|
import okhttp3.Response
|
||||||
@ -60,7 +61,7 @@ object CommentsInterceptor : Interceptor {
|
|||||||
true,
|
true,
|
||||||
)
|
)
|
||||||
|
|
||||||
val comments = parseChapterComments(response).toMutableList()
|
val comments = parseChapterComments(response)
|
||||||
val paintBody = TextPaint().apply {
|
val paintBody = TextPaint().apply {
|
||||||
color = Color.BLACK
|
color = Color.BLACK
|
||||||
textSize = BODY_FONT_SIZE
|
textSize = BODY_FONT_SIZE
|
||||||
|
|||||||
@ -1,21 +1,6 @@
|
|||||||
package eu.kanade.tachiyomi.extension.zh.zaimanhua
|
package eu.kanade.tachiyomi.extension.zh.zaimanhua
|
||||||
|
|
||||||
import eu.kanade.tachiyomi.source.model.SManga
|
import eu.kanade.tachiyomi.source.model.SManga
|
||||||
import kotlinx.serialization.decodeFromString
|
|
||||||
import kotlinx.serialization.json.Json
|
|
||||||
import okhttp3.Response
|
|
||||||
import okhttp3.ResponseBody
|
|
||||||
import uy.kohesive.injekt.injectLazy
|
|
||||||
|
|
||||||
val json: Json by injectLazy()
|
|
||||||
|
|
||||||
inline fun <reified T> Response.parseAs(): T {
|
|
||||||
return json.decodeFromString(body.string())
|
|
||||||
}
|
|
||||||
|
|
||||||
inline fun <reified T> ResponseBody.parseAs(): T {
|
|
||||||
return json.decodeFromString(this.string())
|
|
||||||
}
|
|
||||||
|
|
||||||
fun parseStatus(status: String): Int = when (status) {
|
fun parseStatus(status: String): Int = when (status) {
|
||||||
"连载中" -> SManga.ONGOING
|
"连载中" -> SManga.ONGOING
|
||||||
|
|||||||
@ -19,17 +19,6 @@ class RankingGroup : Filter.Group<Filter<*>>(
|
|||||||
SortFilter(),
|
SortFilter(),
|
||||||
),
|
),
|
||||||
) {
|
) {
|
||||||
private class TimeFilter : QueryFilter(
|
|
||||||
"榜单",
|
|
||||||
"by_time",
|
|
||||||
arrayOf(
|
|
||||||
Pair("日排行", "0"),
|
|
||||||
Pair("周排行", "1"),
|
|
||||||
Pair("月排行", "2"),
|
|
||||||
Pair("总排行", "3"),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
private class SortFilter : QueryFilter(
|
private class SortFilter : QueryFilter(
|
||||||
"排序",
|
"排序",
|
||||||
"rank_type",
|
"rank_type",
|
||||||
@ -40,3 +29,130 @@ class RankingGroup : Filter.Group<Filter<*>>(
|
|||||||
),
|
),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class TimeFilter : QueryFilter(
|
||||||
|
"榜单",
|
||||||
|
"by_time",
|
||||||
|
arrayOf(
|
||||||
|
Pair("不查看榜单", ""),
|
||||||
|
Pair("日排行", "0"),
|
||||||
|
Pair("周排行", "1"),
|
||||||
|
Pair("月排行", "2"),
|
||||||
|
Pair("总排行", "3"),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
class GenreGroup : Filter.Group<Filter<*>>(
|
||||||
|
"筛选出满足以下所有条件的漫画",
|
||||||
|
listOf<Filter<*>>(
|
||||||
|
SortTypeFilter(),
|
||||||
|
StatusFilter(),
|
||||||
|
CateFilter(),
|
||||||
|
ZoneFilter(),
|
||||||
|
ThemeFilter(),
|
||||||
|
),
|
||||||
|
) {
|
||||||
|
|
||||||
|
private class SortTypeFilter : QueryFilter(
|
||||||
|
"排序",
|
||||||
|
"sortType",
|
||||||
|
arrayOf(
|
||||||
|
Pair("更新排序", "1"),
|
||||||
|
Pair("人气排序", "2"),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
private class StatusFilter : QueryFilter(
|
||||||
|
"进度",
|
||||||
|
"status",
|
||||||
|
arrayOf(
|
||||||
|
Pair("全部", "0"),
|
||||||
|
Pair("连载中", "2309"),
|
||||||
|
Pair("已完结", "2310"),
|
||||||
|
Pair("短篇", "29205"),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
private class CateFilter : QueryFilter(
|
||||||
|
"读者群",
|
||||||
|
"cate",
|
||||||
|
arrayOf(
|
||||||
|
Pair("全部", "0"),
|
||||||
|
Pair("少年漫画", "3262"),
|
||||||
|
Pair("少女漫画", "3263"),
|
||||||
|
Pair("青年漫画", "3264"),
|
||||||
|
Pair("女青漫画", "13626"),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
private class ZoneFilter : QueryFilter(
|
||||||
|
"地区",
|
||||||
|
"zone",
|
||||||
|
arrayOf(
|
||||||
|
Pair("全部", "0"),
|
||||||
|
Pair("日本", "2304"),
|
||||||
|
Pair("韩国", "2305"),
|
||||||
|
Pair("欧美", "2306"),
|
||||||
|
Pair("港台", "2307"),
|
||||||
|
Pair("内地", "2308"),
|
||||||
|
Pair("其他", "8435"),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
private class ThemeFilter : QueryFilter(
|
||||||
|
"题材",
|
||||||
|
"theme",
|
||||||
|
arrayOf(
|
||||||
|
Pair("全部", "0"),
|
||||||
|
Pair("冒险", "4"),
|
||||||
|
Pair("欢乐向", "5"),
|
||||||
|
Pair("格斗", "6"),
|
||||||
|
Pair("科幻", "7"),
|
||||||
|
Pair("爱情", "8"),
|
||||||
|
Pair("侦探", "9"),
|
||||||
|
Pair("竞技", "10"),
|
||||||
|
Pair("魔法", "11"),
|
||||||
|
Pair("神鬼", "12"),
|
||||||
|
Pair("校园", "13"),
|
||||||
|
Pair("惊悚", "14"),
|
||||||
|
Pair("其他", "16"),
|
||||||
|
Pair("四格", "17"),
|
||||||
|
Pair("亲情", "3242"),
|
||||||
|
Pair("ゆり", "3243"),
|
||||||
|
Pair("秀吉", "3244"),
|
||||||
|
Pair("悬疑", "3245"),
|
||||||
|
Pair("纯爱", "3246"),
|
||||||
|
Pair("热血", "3248"),
|
||||||
|
Pair("泛爱", "3249"),
|
||||||
|
Pair("历史", "3250"),
|
||||||
|
Pair("战争", "3251"),
|
||||||
|
Pair("萌系", "3252"),
|
||||||
|
Pair("宅系", "3253"),
|
||||||
|
Pair("治愈", "3254"),
|
||||||
|
Pair("励志", "3255"),
|
||||||
|
Pair("武侠", "3324"),
|
||||||
|
Pair("机战", "3325"),
|
||||||
|
Pair("音乐舞蹈", "3326"),
|
||||||
|
Pair("美食", "3327"),
|
||||||
|
Pair("职场", "3328"),
|
||||||
|
Pair("西方魔幻", "3365"),
|
||||||
|
Pair("高清单行", "4459"),
|
||||||
|
Pair("TS", "4518"),
|
||||||
|
Pair("东方", "5077"),
|
||||||
|
Pair("魔幻", "5806"),
|
||||||
|
Pair("奇幻", "5848"),
|
||||||
|
Pair("节操", "6219"),
|
||||||
|
Pair("轻小说", "6316"),
|
||||||
|
Pair("颜艺", "6437"),
|
||||||
|
Pair("搞笑", "7568"),
|
||||||
|
Pair("仙侠", "7900"),
|
||||||
|
Pair("舰娘", "13627"),
|
||||||
|
Pair("动画", "17192"),
|
||||||
|
Pair("AA", "18522"),
|
||||||
|
Pair("福瑞", "23323"),
|
||||||
|
Pair("生存", "23388"),
|
||||||
|
Pair("日常", "30788"),
|
||||||
|
Pair("画集", "31137"),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
package eu.kanade.tachiyomi.extension.zh.zaimanhua
|
package eu.kanade.tachiyomi.extension.zh.zaimanhua
|
||||||
|
|
||||||
import android.content.SharedPreferences
|
import android.content.SharedPreferences
|
||||||
|
import android.util.Base64
|
||||||
import androidx.preference.EditTextPreference
|
import androidx.preference.EditTextPreference
|
||||||
import androidx.preference.ListPreference
|
import androidx.preference.ListPreference
|
||||||
import androidx.preference.PreferenceScreen
|
import androidx.preference.PreferenceScreen
|
||||||
@ -9,13 +10,16 @@ import eu.kanade.tachiyomi.network.GET
|
|||||||
import eu.kanade.tachiyomi.network.POST
|
import eu.kanade.tachiyomi.network.POST
|
||||||
import eu.kanade.tachiyomi.network.interceptor.rateLimit
|
import eu.kanade.tachiyomi.network.interceptor.rateLimit
|
||||||
import eu.kanade.tachiyomi.source.ConfigurableSource
|
import eu.kanade.tachiyomi.source.ConfigurableSource
|
||||||
|
import eu.kanade.tachiyomi.source.model.Filter
|
||||||
import eu.kanade.tachiyomi.source.model.FilterList
|
import eu.kanade.tachiyomi.source.model.FilterList
|
||||||
import eu.kanade.tachiyomi.source.model.MangasPage
|
import eu.kanade.tachiyomi.source.model.MangasPage
|
||||||
import eu.kanade.tachiyomi.source.model.Page
|
import eu.kanade.tachiyomi.source.model.Page
|
||||||
import eu.kanade.tachiyomi.source.model.SChapter
|
import eu.kanade.tachiyomi.source.model.SChapter
|
||||||
import eu.kanade.tachiyomi.source.model.SManga
|
import eu.kanade.tachiyomi.source.model.SManga
|
||||||
import eu.kanade.tachiyomi.source.online.HttpSource
|
import eu.kanade.tachiyomi.source.online.HttpSource
|
||||||
|
import keiyoushi.utils.firstInstanceOrNull
|
||||||
import keiyoushi.utils.getPreferences
|
import keiyoushi.utils.getPreferences
|
||||||
|
import keiyoushi.utils.parseAs
|
||||||
import kotlinx.serialization.decodeFromString
|
import kotlinx.serialization.decodeFromString
|
||||||
import kotlinx.serialization.encodeToString
|
import kotlinx.serialization.encodeToString
|
||||||
import kotlinx.serialization.json.Json
|
import kotlinx.serialization.json.Json
|
||||||
@ -66,7 +70,7 @@ class Zaimanhua : HttpSource(), ConfigurableSource {
|
|||||||
}
|
}
|
||||||
|
|
||||||
val response = chain.proceed(request)
|
val response = chain.proceed(request)
|
||||||
if (!request.headers["authorization"].isNullOrBlank() && response.peekBody(Long.MAX_VALUE).parseAs<ResponseDto<DataWrapperDto<CanReadDto>>>().data.data?.canRead != false) {
|
if (!request.headers["authorization"].isNullOrBlank() && response.peekBody(Long.MAX_VALUE).string().parseAs<ResponseDto<DataWrapperDto<CanReadDto>>>().data.data?.canRead != false) {
|
||||||
return response
|
return response
|
||||||
}
|
}
|
||||||
var token: String = preferences.getString(TOKEN_PREF, "")!!
|
var token: String = preferences.getString(TOKEN_PREF, "")!!
|
||||||
@ -75,9 +79,9 @@ class Zaimanhua : HttpSource(), ConfigurableSource {
|
|||||||
val password = preferences.getString(PASSWORD_PREF, "")!!
|
val password = preferences.getString(PASSWORD_PREF, "")!!
|
||||||
token = getToken(username, password)
|
token = getToken(username, password)
|
||||||
if (token.isBlank()) {
|
if (token.isBlank()) {
|
||||||
preferences.edit().putString(TOKEN_PREF, "").apply()
|
preferences.edit().putString(TOKEN_PREF, "")
|
||||||
preferences.edit().putString(USERNAME_PREF, "").apply()
|
.putString(USERNAME_PREF, "")
|
||||||
preferences.edit().putString(PASSWORD_PREF, "").apply()
|
.putString(PASSWORD_PREF, "").apply()
|
||||||
return response
|
return response
|
||||||
} else {
|
} else {
|
||||||
preferences.edit().putString(TOKEN_PREF, token).apply()
|
preferences.edit().putString(TOKEN_PREF, token).apply()
|
||||||
@ -101,6 +105,11 @@ class Zaimanhua : HttpSource(), ConfigurableSource {
|
|||||||
|
|
||||||
private fun isValid(token: String): Boolean {
|
private fun isValid(token: String): Boolean {
|
||||||
if (token.isBlank()) return false
|
if (token.isBlank()) return false
|
||||||
|
val parts = token.split(".")
|
||||||
|
if (parts.size != 3) throw Exception("token格式错误,不符合JWT规范")
|
||||||
|
val payload = Base64.decode(parts[1], Base64.DEFAULT).toString(Charsets.UTF_8).parseAs<JwtPayload>()
|
||||||
|
if (payload.expirationTime * 1000 < System.currentTimeMillis()) return false
|
||||||
|
|
||||||
val response = client.newCall(
|
val response = client.newCall(
|
||||||
GET(
|
GET(
|
||||||
"$accountApiUrl/userInfo/get",
|
"$accountApiUrl/userInfo/get",
|
||||||
@ -233,24 +242,32 @@ class Zaimanhua : HttpSource(), ConfigurableSource {
|
|||||||
apiHeaders,
|
apiHeaders,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
private fun genreApiUrl(): HttpUrl.Builder =
|
||||||
|
"$apiUrl/comic/filter/list".toHttpUrl().newBuilder()
|
||||||
|
.addQueryParameter("size", DEFAULT_PAGE_SIZE.toString())
|
||||||
|
|
||||||
override fun popularMangaParse(response: Response): MangasPage = latestUpdatesParse(response)
|
override fun popularMangaParse(response: Response): MangasPage = latestUpdatesParse(response)
|
||||||
|
|
||||||
// Search
|
// Search
|
||||||
private fun searchApiUrl(): HttpUrl.Builder =
|
private fun searchApiUrl(): HttpUrl.Builder =
|
||||||
"$apiUrl/search/index".toHttpUrl().newBuilder().addQueryParameter("source", "0")
|
"$apiUrl/search/index".toHttpUrl().newBuilder().addQueryParameter("source", "0")
|
||||||
.addQueryParameter("size", "20")
|
.addQueryParameter("size", DEFAULT_PAGE_SIZE.toString())
|
||||||
|
|
||||||
override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request {
|
override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request {
|
||||||
val ranking = filters.filterIsInstance<RankingGroup>().firstOrNull()
|
val ranking = filters.firstInstanceOrNull<RankingGroup>()
|
||||||
val url = if (query.isEmpty() && ranking != null) {
|
val genres = filters.firstInstanceOrNull<GenreGroup>()
|
||||||
rankApiUrl().apply {
|
val url = when {
|
||||||
ranking.state.filterIsInstance<QueryFilter>().forEach {
|
query.isEmpty() && ranking != null && (ranking.state[0] as TimeFilter).state != 0 -> rankApiUrl().apply {
|
||||||
it.addQuery(this)
|
ranking.state.filterIsInstance<QueryFilter>().forEach { it.addQuery(this) }
|
||||||
}
|
|
||||||
addQueryParameter("page", page.toString())
|
addQueryParameter("page", page.toString())
|
||||||
}.build()
|
}.build()
|
||||||
} else {
|
|
||||||
searchApiUrl().apply {
|
query.isEmpty() && genres != null -> genreApiUrl().apply {
|
||||||
|
genres.state.filterIsInstance<QueryFilter>().forEach { it.addQuery(this) }
|
||||||
|
addQueryParameter("page", page.toString())
|
||||||
|
}.build()
|
||||||
|
|
||||||
|
else -> searchApiUrl().apply {
|
||||||
addQueryParameter("keyword", query)
|
addQueryParameter("keyword", query)
|
||||||
addQueryParameter("page", page.toString())
|
addQueryParameter("page", page.toString())
|
||||||
}.build()
|
}.build()
|
||||||
@ -258,11 +275,14 @@ class Zaimanhua : HttpSource(), ConfigurableSource {
|
|||||||
return GET(url, apiHeaders)
|
return GET(url, apiHeaders)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun searchMangaParse(response: Response): MangasPage =
|
override fun searchMangaParse(response: Response): MangasPage {
|
||||||
if (response.request.url.toString().startsWith("$apiUrl/comic/rank/list")) {
|
val url = response.request.url
|
||||||
|
return if (url.toString().startsWith("$apiUrl/comic/rank/list")) {
|
||||||
latestUpdatesParse(response)
|
latestUpdatesParse(response)
|
||||||
} else {
|
} else {
|
||||||
response.parseAs<ResponseDto<PageDto>>().data.toMangasPage()
|
// "$apiUrl/comic/filter/list" or "$apiUrl/search/index"
|
||||||
|
response.parseAs<ResponseDto<PageDto>>().data.toMangasPage(url.queryParameter("page")!!.toInt())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Latest
|
// Latest
|
||||||
@ -280,6 +300,9 @@ class Zaimanhua : HttpSource(), ConfigurableSource {
|
|||||||
|
|
||||||
override fun getFilterList() = FilterList(
|
override fun getFilterList() = FilterList(
|
||||||
RankingGroup(),
|
RankingGroup(),
|
||||||
|
Filter.Separator(),
|
||||||
|
Filter.Header("分类(搜索/查看排行榜时无效)"),
|
||||||
|
GenreGroup(),
|
||||||
)
|
)
|
||||||
|
|
||||||
private fun chapterCommentsUrl(comicId: String, chapterId: String) = "$apiUrl/viewpoint/list?comicId=$comicId&chapterId=$chapterId"
|
private fun chapterCommentsUrl(comicId: String, chapterId: String) = "$apiUrl/viewpoint/list?comicId=$comicId&chapterId=$chapterId"
|
||||||
@ -292,6 +315,7 @@ class Zaimanhua : HttpSource(), ConfigurableSource {
|
|||||||
const val COMMENTS_PREF = "COMMENTS"
|
const val COMMENTS_PREF = "COMMENTS"
|
||||||
const val COMMENTS_FLAG = "COMMENTS"
|
const val COMMENTS_FLAG = "COMMENTS"
|
||||||
const val IMAGE_RETRY_FLAG = "IMAGE_RETRY"
|
const val IMAGE_RETRY_FLAG = "IMAGE_RETRY"
|
||||||
|
const val DEFAULT_PAGE_SIZE = 20
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun setupPreferenceScreen(screen: PreferenceScreen) {
|
override fun setupPreferenceScreen(screen: PreferenceScreen) {
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
package eu.kanade.tachiyomi.extension.zh.zaimanhua
|
package eu.kanade.tachiyomi.extension.zh.zaimanhua
|
||||||
|
|
||||||
|
import eu.kanade.tachiyomi.extension.zh.zaimanhua.Zaimanhua.Companion.DEFAULT_PAGE_SIZE
|
||||||
import eu.kanade.tachiyomi.source.model.MangasPage
|
import eu.kanade.tachiyomi.source.model.MangasPage
|
||||||
import eu.kanade.tachiyomi.source.model.SChapter
|
import eu.kanade.tachiyomi.source.model.SChapter
|
||||||
import eu.kanade.tachiyomi.source.model.SManga
|
import eu.kanade.tachiyomi.source.model.SManga
|
||||||
@ -100,23 +101,37 @@ class ChapterImagesDto(
|
|||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
class PageDto(
|
class PageDto(
|
||||||
|
// Only genre(/comic/filter/list) use `comicList`, others use `list`
|
||||||
|
@JsonNames("comicList")
|
||||||
private val list: List<PageItemDto>?,
|
private val list: List<PageItemDto>?,
|
||||||
private val page: Int,
|
// Genre(/comic/filter/list) doesn't have `page` and `size`
|
||||||
private val size: Int,
|
private val page: Int?,
|
||||||
|
private val size: Int?,
|
||||||
|
// Only genre(/comic/filter/list) use `totalNum`, others use `total`
|
||||||
|
@JsonNames("totalNum")
|
||||||
private val total: Int,
|
private val total: Int,
|
||||||
) {
|
) {
|
||||||
fun toMangasPage(): MangasPage {
|
fun toMangasPage(page: Int): MangasPage {
|
||||||
|
val currentPage = this.page ?: page
|
||||||
|
val pageSize = this.size ?: DEFAULT_PAGE_SIZE
|
||||||
if (list.isNullOrEmpty()) throw Exception("漫画结果为空,请检查输入")
|
if (list.isNullOrEmpty()) throw Exception("漫画结果为空,请检查输入")
|
||||||
val hasNextPage = page * size < total
|
val hasNextPage = currentPage * pageSize < total
|
||||||
return MangasPage(list.map { it.toSManga() }, hasNextPage)
|
return MangasPage(list.map { it.toSManga() }, hasNextPage)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
class PageItemDto(
|
class PageItemDto(
|
||||||
|
// must have at least one of id and comicId
|
||||||
|
// Genre(/comic/filter/list) only have `id`
|
||||||
|
// Ranking(/comic/rank/list) only have `comic_id`
|
||||||
|
// latest(/comic/update/list) have both `id` (always 0) and `comic_id`
|
||||||
|
// Search(/search/index) have both `id` and `comic_id` (always 0)
|
||||||
private val id: Int?,
|
private val id: Int?,
|
||||||
@SerialName("comic_id")
|
@SerialName("comic_id")
|
||||||
private val comicId: Int,
|
private val comicId: Int?,
|
||||||
|
// Only genre(/comic/filter/list) use `name`, others use `title`
|
||||||
|
@JsonNames("name")
|
||||||
private val title: String,
|
private val title: String,
|
||||||
private val authors: String?,
|
private val authors: String?,
|
||||||
private val status: String?,
|
private val status: String?,
|
||||||
@ -124,7 +139,7 @@ class PageItemDto(
|
|||||||
private val types: String?,
|
private val types: String?,
|
||||||
) {
|
) {
|
||||||
fun toSManga() = SManga.create().apply {
|
fun toSManga() = SManga.create().apply {
|
||||||
url = (this@PageItemDto.id?.takeIf { it != 0 } ?: this@PageItemDto.comicId).toString()
|
url = (this@PageItemDto.comicId?.takeIf { it != 0 } ?: this@PageItemDto.id)!!.toString()
|
||||||
title = this@PageItemDto.title
|
title = this@PageItemDto.title
|
||||||
author = authors?.formatList()
|
author = authors?.formatList()
|
||||||
genre = types?.formatList()
|
genre = types?.formatList()
|
||||||
@ -192,3 +207,9 @@ class CommentDataDto(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
class JwtPayload(
|
||||||
|
@SerialName("exp")
|
||||||
|
val expirationTime: Long,
|
||||||
|
)
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user