Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/zh/zaimanhua/build.gradle
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
ext {
extName = 'Zaimanhua'
extClass = '.Zaimanhua'
extVersionCode = 12
extVersionCode = 13
isNsfw = false
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import android.text.Layout
import android.text.StaticLayout
import android.text.TextPaint
import eu.kanade.tachiyomi.extension.zh.zaimanhua.Zaimanhua.Companion.COMMENTS_FLAG
import keiyoushi.utils.parseAs
import okhttp3.Interceptor
import okhttp3.MediaType.Companion.toMediaType
import okhttp3.Response
Expand Down Expand Up @@ -60,7 +61,7 @@ object CommentsInterceptor : Interceptor {
true,
)

val comments = parseChapterComments(response).toMutableList()
val comments = parseChapterComments(response)
val paintBody = TextPaint().apply {
color = Color.BLACK
textSize = BODY_FONT_SIZE
Expand Down
Original file line number Diff line number Diff line change
@@ -1,21 +1,6 @@
package eu.kanade.tachiyomi.extension.zh.zaimanhua

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) {
"连载中" -> SManga.ONGOING
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,17 +19,6 @@ class RankingGroup : Filter.Group<Filter<*>>(
SortFilter(),
),
) {
private class TimeFilter : QueryFilter(
"榜单",
"by_time",
arrayOf(
Pair("日排行", "0"),
Pair("周排行", "1"),
Pair("月排行", "2"),
Pair("总排行", "3"),
),
)

private class SortFilter : QueryFilter(
"排序",
"rank_type",
Expand All @@ -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"),
),
)
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package eu.kanade.tachiyomi.extension.zh.zaimanhua

import android.content.SharedPreferences
import android.util.Base64
import androidx.preference.EditTextPreference
import androidx.preference.ListPreference
import androidx.preference.PreferenceScreen
Expand All @@ -9,13 +10,16 @@ import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.network.POST
import eu.kanade.tachiyomi.network.interceptor.rateLimit
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.MangasPage
import eu.kanade.tachiyomi.source.model.Page
import eu.kanade.tachiyomi.source.model.SChapter
import eu.kanade.tachiyomi.source.model.SManga
import eu.kanade.tachiyomi.source.online.HttpSource
import keiyoushi.utils.firstInstanceOrNull
import keiyoushi.utils.getPreferences
import keiyoushi.utils.parseAs
import kotlinx.serialization.decodeFromString
import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json
Expand Down Expand Up @@ -66,7 +70,7 @@ class Zaimanhua : HttpSource(), ConfigurableSource {
}

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
}
var token: String = preferences.getString(TOKEN_PREF, "")!!
Expand All @@ -75,9 +79,9 @@ class Zaimanhua : HttpSource(), ConfigurableSource {
val password = preferences.getString(PASSWORD_PREF, "")!!
token = getToken(username, password)
if (token.isBlank()) {
preferences.edit().putString(TOKEN_PREF, "").apply()
preferences.edit().putString(USERNAME_PREF, "").apply()
preferences.edit().putString(PASSWORD_PREF, "").apply()
preferences.edit().putString(TOKEN_PREF, "")
.putString(USERNAME_PREF, "")
.putString(PASSWORD_PREF, "").apply()
return response
} else {
preferences.edit().putString(TOKEN_PREF, token).apply()
Expand All @@ -101,6 +105,11 @@ class Zaimanhua : HttpSource(), ConfigurableSource {

private fun isValid(token: String): Boolean {
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(
GET(
"$accountApiUrl/userInfo/get",
Expand Down Expand Up @@ -233,37 +242,48 @@ class Zaimanhua : HttpSource(), ConfigurableSource {
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)

// Search
private fun searchApiUrl(): HttpUrl.Builder =
"$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 {
val ranking = filters.filterIsInstance<RankingGroup>().firstOrNull()
val url = if (query.isEmpty() && ranking != null) {
rankApiUrl().apply {
ranking.state.filterIsInstance<QueryFilter>().forEach {
it.addQuery(this)
}
val ranking = filters.firstInstanceOrNull<RankingGroup>()
val genres = filters.firstInstanceOrNull<GenreGroup>()
val url = when {
query.isEmpty() && ranking != null && (ranking.state[0] as TimeFilter).state != 0 -> rankApiUrl().apply {
ranking.state.filterIsInstance<QueryFilter>().forEach { it.addQuery(this) }
addQueryParameter("page", page.toString())
}.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("page", page.toString())
}.build()
}
return GET(url, apiHeaders)
}

override fun searchMangaParse(response: Response): MangasPage =
if (response.request.url.toString().startsWith("$apiUrl/comic/rank/list")) {
override fun searchMangaParse(response: Response): MangasPage {
val url = response.request.url
return if (url.toString().startsWith("$apiUrl/comic/rank/list")) {
latestUpdatesParse(response)
} 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
// "$apiUrl/comic/update/list/1/$page" is same content
Expand All @@ -280,6 +300,9 @@ class Zaimanhua : HttpSource(), ConfigurableSource {

override fun getFilterList() = FilterList(
RankingGroup(),
Filter.Separator(),
Filter.Header("分类(搜索/查看排行榜时无效)"),
GenreGroup(),
)

private fun chapterCommentsUrl(comicId: String, chapterId: String) = "$apiUrl/viewpoint/list?comicId=$comicId&chapterId=$chapterId"
Expand All @@ -292,6 +315,7 @@ class Zaimanhua : HttpSource(), ConfigurableSource {
const val COMMENTS_PREF = "COMMENTS"
const val COMMENTS_FLAG = "COMMENTS"
const val IMAGE_RETRY_FLAG = "IMAGE_RETRY"
const val DEFAULT_PAGE_SIZE = 20
}

override fun setupPreferenceScreen(screen: PreferenceScreen) {
Expand Down
Loading