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 {
extName = 'Kagane'
extClass = '.Kagane'
extVersionCode = 3
extVersionCode = 4
isNsfw = true
}

View File

@ -31,84 +31,68 @@ class SearchDto(
@Serializable
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
class Data(
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,
)
class AlternateTitles(
val title: String,
)
fun toSManga(): SManga = SManga.create().apply {
val desc = StringBuilder()
if (!summary.isNullOrBlank()) desc.append(summary + "\n\n")
desc.append("Source: ").append(source + "\n\n")
if (alternateTitles.isNotEmpty()) {
desc.append("Associated Name(s):\n\n")
alternateTitles.forEach { desc.append("${it.title}\n") }
}
fun toSManga(): SManga = SManga.create().apply {
val summary = StringBuilder()
summary.append(metadata.summary)
.append("\n\n")
.append("Source: ")
.append(source)
author = authors.joinToString()
description = desc.toString()
genre = genres.joinToString()
status = this@DetailsDto.status.toStatus()
}
if (metadata.alternateTitles.isNotEmpty()) {
summary.append("\n\nAssociated Name(s):")
metadata.alternateTitles.forEach { summary.append("\n").append("${it.title}") }
}
description = summary.toString()
genre = metadata.genres.joinToString()
status = metadata.status.toStatus()
}
private fun String.toStatus(): Int {
return when (this) {
"ONGOING" -> SManga.ONGOING
else -> SManga.COMPLETED
}
private fun String.toStatus(): Int {
return when (this) {
"ONGOING" -> SManga.ONGOING
"ENDED" -> SManga.COMPLETED
else -> SManga.UNKNOWN
}
}
}
@Serializable
class ChapterDto(
val data: Data,
val content: List<Book>,
) {
@Serializable
class Data(
val content: List<Book>,
class Book(
val id: String,
@SerialName("series_id")
val seriesId: String,
val title: String,
@SerialName("release_date")
val releaseDate: String?,
@SerialName("pages_count")
val pagesCount: Int,
) {
@Serializable
class Book(
val metadata: Metadata,
val id: String,
val seriesId: String,
val created: String,
) {
@Serializable
class Metadata(
val title: String,
)
fun toSChapter(): SChapter = SChapter.create().apply {
url = "$seriesId;$id"
name = metadata.title
date_upload = dateFormat.tryParse(created)
}
fun toSChapter(): SChapter = SChapter.create().apply {
url = "$seriesId;$id;$pagesCount"
name = title
date_upload = dateFormat.tryParse(releaseDate)
}
}
companion object {
private val dateFormat by lazy {
SimpleDateFormat("yyyy-MM-dd", Locale.ENGLISH)
}
private val dateFormat = SimpleDateFormat("yyyy-MM-dd", Locale.ENGLISH)
}
}
@ -117,19 +101,3 @@ class ChallengeDto(
@SerialName("access_token")
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 ===============================
override fun latestUpdatesRequest(page: Int) =
searchMangaRequest(page, "", FilterList(SortFilter(1)))
searchMangaRequest(page, "", FilterList(SortFilter(2)))
override fun latestUpdatesParse(response: Response) = searchMangaParse(response)
@ -188,7 +188,7 @@ class Kagane : HttpSource(), ConfigurableSource {
override fun mangaDetailsParse(response: Response): SManga {
val dto = response.parseAs<DetailsDto>()
return dto.data.toSManga()
return dto.toSManga()
}
override fun mangaDetailsRequest(manga: SManga): Request {
@ -203,7 +203,7 @@ class Kagane : HttpSource(), ConfigurableSource {
override fun chapterListParse(response: Response): List<SChapter> {
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 {
@ -224,15 +224,16 @@ class Kagane : HttpSource(), ConfigurableSource {
}
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)
accessToken = challengeResp.accessToken
val pageCount = getPageCountResponse(seriesId, chapterId)
if (preferences.dataSaver) {
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 {
addPathSegment(seriesId)
addPathSegment("file")
@ -363,15 +364,6 @@ class Kagane : HttpSource(), ConfigurableSource {
.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 =
arrays.reduce { acc, bytes -> acc + bytes }
@ -418,7 +410,7 @@ class Kagane : HttpSource(), ConfigurableSource {
private val SharedPreferences.showNsfw
get() = this.getBoolean(SHOW_NSFW_KEY, true)
private val SharedPreferences.dataSaver
get() = this.getBoolean(DATA_SAVER, true)
get() = this.getBoolean(DATA_SAVER, false)
override fun setupPreferenceScreen(screen: PreferenceScreen) {
SwitchPreferenceCompat(screen.context).apply {
@ -429,7 +421,7 @@ class Kagane : HttpSource(), ConfigurableSource {
SwitchPreferenceCompat(screen.context).apply {
key = DATA_SAVER
title = "Data saver"
setDefaultValue(true)
setDefaultValue(false)
}.let(screen::addPreference)
}
@ -437,7 +429,7 @@ class Kagane : HttpSource(), ConfigurableSource {
companion object {
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 ==============================
@ -449,7 +441,7 @@ class Kagane : HttpSource(), ConfigurableSource {
class SortFilter(state: Int = 0) : UriPartFilter(
"Sort By",
arrayOf(
Pair("Relevance", ""),
Pair("Relevance", "avg_views,desc"),
Pair("Latest", "updated_at"),
Pair("Latest Descending", "updated_at,desc"),
Pair("By Name", "series_name"),