Kagane: Fix MissingFieldException Error (#10981)

* fix dto and small fixes

* bump

* fix authors

* remove unused code and fix review comment

* fix lint

* fix lintx2 :(
This commit is contained in:
dngonz 2025-10-11 15:44:36 +02:00 committed by Draff
parent ccbde23c1f
commit 4ac7d3559c
Signed by: Draff
GPG Key ID: E8A89F3211677653
3 changed files with 56 additions and 96 deletions

View File

@ -1,7 +1,7 @@
ext { ext {
extName = 'Kagane' extName = 'Kagane'
extClass = '.Kagane' extClass = '.Kagane'
extVersionCode = 3 extVersionCode = 4
isNsfw = true isNsfw = true
} }

View File

@ -31,84 +31,68 @@ class SearchDto(
@Serializable @Serializable
class DetailsDto( class DetailsDto(
val data: Data, val source: String,
val authors: List<String>,
val status: String,
val summary: String?,
val genres: List<String>,
@SerialName("alternate_titles")
val alternateTitles: List<AlternateTitles>,
) { ) {
@Serializable @Serializable
class Data( class AlternateTitles(
val metadata: Metadata,
val source: String,
) {
@Serializable
class Metadata(
val genres: List<String>,
val status: String,
val summary: String,
val alternateTitles: List<Title>,
) {
@Serializable
class Title(
val title: String, val title: String,
) )
}
fun toSManga(): SManga = SManga.create().apply { fun toSManga(): SManga = SManga.create().apply {
val summary = StringBuilder() val desc = StringBuilder()
summary.append(metadata.summary) if (!summary.isNullOrBlank()) desc.append(summary + "\n\n")
.append("\n\n") desc.append("Source: ").append(source + "\n\n")
.append("Source: ")
.append(source)
if (metadata.alternateTitles.isNotEmpty()) { if (alternateTitles.isNotEmpty()) {
summary.append("\n\nAssociated Name(s):") desc.append("Associated Name(s):\n\n")
metadata.alternateTitles.forEach { summary.append("\n").append("${it.title}") } alternateTitles.forEach { desc.append("${it.title}\n") }
} }
description = summary.toString() author = authors.joinToString()
genre = metadata.genres.joinToString() description = desc.toString()
status = metadata.status.toStatus() genre = genres.joinToString()
status = this@DetailsDto.status.toStatus()
} }
private fun String.toStatus(): Int { private fun String.toStatus(): Int {
return when (this) { return when (this) {
"ONGOING" -> SManga.ONGOING "ONGOING" -> SManga.ONGOING
else -> SManga.COMPLETED "ENDED" -> SManga.COMPLETED
} else -> SManga.UNKNOWN
} }
} }
} }
@Serializable @Serializable
class ChapterDto( class ChapterDto(
val data: Data, val content: List<Book>,
) { ) {
@Serializable
class Data(
val content: List<Book>,
) {
@Serializable @Serializable
class Book( class Book(
val metadata: Metadata,
val id: String, val id: String,
@SerialName("series_id")
val seriesId: String, val seriesId: String,
val created: String,
) {
@Serializable
class Metadata(
val title: String, val title: String,
) @SerialName("release_date")
val releaseDate: String?,
@SerialName("pages_count")
val pagesCount: Int,
) {
fun toSChapter(): SChapter = SChapter.create().apply { fun toSChapter(): SChapter = SChapter.create().apply {
url = "$seriesId;$id" url = "$seriesId;$id;$pagesCount"
name = metadata.title name = title
date_upload = dateFormat.tryParse(created) date_upload = dateFormat.tryParse(releaseDate)
}
} }
} }
companion object { companion object {
private val dateFormat by lazy { private val dateFormat = SimpleDateFormat("yyyy-MM-dd", Locale.ENGLISH)
SimpleDateFormat("yyyy-MM-dd", Locale.ENGLISH)
}
} }
} }
@ -117,19 +101,3 @@ class ChallengeDto(
@SerialName("access_token") @SerialName("access_token")
val accessToken: String, val accessToken: String,
) )
@Serializable
class PagesCountDto(
val data: Data,
) {
@Serializable
class Data(
val media: PagesCount,
) {
@Serializable
class PagesCount(
@SerialName("pagesCount")
val pagesCount: Int,
)
}
}

View File

@ -144,7 +144,7 @@ class Kagane : HttpSource(), ConfigurableSource {
// =============================== Latest =============================== // =============================== Latest ===============================
override fun latestUpdatesRequest(page: Int) = override fun latestUpdatesRequest(page: Int) =
searchMangaRequest(page, "", FilterList(SortFilter(1))) searchMangaRequest(page, "", FilterList(SortFilter(2)))
override fun latestUpdatesParse(response: Response) = searchMangaParse(response) override fun latestUpdatesParse(response: Response) = searchMangaParse(response)
@ -188,7 +188,7 @@ class Kagane : HttpSource(), ConfigurableSource {
override fun mangaDetailsParse(response: Response): SManga { override fun mangaDetailsParse(response: Response): SManga {
val dto = response.parseAs<DetailsDto>() val dto = response.parseAs<DetailsDto>()
return dto.data.toSManga() return dto.toSManga()
} }
override fun mangaDetailsRequest(manga: SManga): Request { override fun mangaDetailsRequest(manga: SManga): Request {
@ -203,7 +203,7 @@ class Kagane : HttpSource(), ConfigurableSource {
override fun chapterListParse(response: Response): List<SChapter> { override fun chapterListParse(response: Response): List<SChapter> {
val dto = response.parseAs<ChapterDto>() val dto = response.parseAs<ChapterDto>()
return dto.data.content.map { it.toSChapter() }.reversed() return dto.content.map { it.toSChapter() }.reversed()
} }
override fun chapterListRequest(manga: SManga): Request { override fun chapterListRequest(manga: SManga): Request {
@ -224,15 +224,16 @@ class Kagane : HttpSource(), ConfigurableSource {
} }
override fun fetchPageList(chapter: SChapter): Observable<List<Page>> { override fun fetchPageList(chapter: SChapter): Observable<List<Page>> {
var (seriesId, chapterId) = chapter.url.split(";") if (chapter.url.count { it == ';' } == 2) throw Exception("Chapter url error, please refresh chapter list.")
var (seriesId, chapterId, pageCount) = chapter.url.split(";")
val challengeResp = getChallengeResponse(seriesId, chapterId) val challengeResp = getChallengeResponse(seriesId, chapterId)
accessToken = challengeResp.accessToken accessToken = challengeResp.accessToken
val pageCount = getPageCountResponse(seriesId, chapterId)
if (preferences.dataSaver) { if (preferences.dataSaver) {
chapterId = chapterId + "_ds" chapterId = chapterId + "_ds"
} }
val pages = (0 until pageCount).map { page ->
val pages = (0 until pageCount.toInt()).map { page ->
val pageUrl = "$apiUrl/api/v1/books".toHttpUrl().newBuilder().apply { val pageUrl = "$apiUrl/api/v1/books".toHttpUrl().newBuilder().apply {
addPathSegment(seriesId) addPathSegment(seriesId)
addPathSegment("file") addPathSegment("file")
@ -363,15 +364,6 @@ class Kagane : HttpSource(), ConfigurableSource {
.parseAs<ChallengeDto>() .parseAs<ChallengeDto>()
} }
private fun getPageCountResponse(seriesId: String, chapterId: String): Int {
val challengeUrl = "$apiUrl/api/v1/books/$seriesId/metadata/$chapterId"
val dto = client.newCall(GET(challengeUrl, apiHeaders)).execute()
.parseAs<PagesCountDto>()
return dto.data.media.pagesCount
}
private fun concat(vararg arrays: ByteArray): ByteArray = private fun concat(vararg arrays: ByteArray): ByteArray =
arrays.reduce { acc, bytes -> acc + bytes } arrays.reduce { acc, bytes -> acc + bytes }
@ -418,7 +410,7 @@ class Kagane : HttpSource(), ConfigurableSource {
private val SharedPreferences.showNsfw private val SharedPreferences.showNsfw
get() = this.getBoolean(SHOW_NSFW_KEY, true) get() = this.getBoolean(SHOW_NSFW_KEY, true)
private val SharedPreferences.dataSaver private val SharedPreferences.dataSaver
get() = this.getBoolean(DATA_SAVER, true) get() = this.getBoolean(DATA_SAVER, false)
override fun setupPreferenceScreen(screen: PreferenceScreen) { override fun setupPreferenceScreen(screen: PreferenceScreen) {
SwitchPreferenceCompat(screen.context).apply { SwitchPreferenceCompat(screen.context).apply {
@ -429,7 +421,7 @@ class Kagane : HttpSource(), ConfigurableSource {
SwitchPreferenceCompat(screen.context).apply { SwitchPreferenceCompat(screen.context).apply {
key = DATA_SAVER key = DATA_SAVER
title = "Data saver" title = "Data saver"
setDefaultValue(true) setDefaultValue(false)
}.let(screen::addPreference) }.let(screen::addPreference)
} }
@ -437,7 +429,7 @@ class Kagane : HttpSource(), ConfigurableSource {
companion object { companion object {
private const val SHOW_NSFW_KEY = "pref_show_nsfw" private const val SHOW_NSFW_KEY = "pref_show_nsfw"
private const val DATA_SAVER = "data_saver" private const val DATA_SAVER = "data_saver_default"
} }
// ============================= Filters ============================== // ============================= Filters ==============================
@ -449,7 +441,7 @@ class Kagane : HttpSource(), ConfigurableSource {
class SortFilter(state: Int = 0) : UriPartFilter( class SortFilter(state: Int = 0) : UriPartFilter(
"Sort By", "Sort By",
arrayOf( arrayOf(
Pair("Relevance", ""), Pair("Relevance", "avg_views,desc"),
Pair("Latest", "updated_at"), Pair("Latest", "updated_at"),
Pair("Latest Descending", "updated_at,desc"), Pair("Latest Descending", "updated_at,desc"),
Pair("By Name", "series_name"), Pair("By Name", "series_name"),