From 2648815fc53a95a99ba60fa2729bfdb24cbe7b90 Mon Sep 17 00:00:00 2001 From: "Mio." Date: Fri, 11 Jul 2025 20:03:21 +0200 Subject: [PATCH] Enhance logic for covers + add Comic Support --- .../kavita/assets/i18n/messages_en.properties | 2 +- .../tachiyomi/extension/all/kavita/Kavita.kt | 82 ++++++++++++------- .../extension/all/kavita/KavitaHelper.kt | 55 ++----------- .../extension/all/kavita/KavitaUtils.kt | 2 +- .../extension/all/kavita/dto/MangaDto.kt | 1 + 5 files changed, 64 insertions(+), 78 deletions(-) diff --git a/src/all/kavita/assets/i18n/messages_en.properties b/src/all/kavita/assets/i18n/messages_en.properties index 70a3f41b..b6072d80 100644 --- a/src/all/kavita/assets/i18n/messages_en.properties +++ b/src/all/kavita/assets/i18n/messages_en.properties @@ -8,7 +8,7 @@ pref_group_tags_summary_on=Will prefix tags with their type pref_group_tags_summary_off=List all tags together pref_last_volume_cover_title=Keep cover updated pref_last_volume_cover_summary_off=Keep current cover -pref_last_volume_cover_summary_on=Use the last volume cover when updating +pref_last_volume_cover_summary_on=Update cover to current Volume/Issue pref_show_epub_title=Show EPUB (EPUB can't be read in Mihon) pref_show_epub_summary_off=Hide EPUB titles in browsing pref_show_epub_summary_on=Show EPUB titles in browsing diff --git a/src/all/kavita/src/eu/kanade/tachiyomi/extension/all/kavita/Kavita.kt b/src/all/kavita/src/eu/kanade/tachiyomi/extension/all/kavita/Kavita.kt index 1abb5489..27ae0404 100644 --- a/src/all/kavita/src/eu/kanade/tachiyomi/extension/all/kavita/Kavita.kt +++ b/src/all/kavita/src/eu/kanade/tachiyomi/extension/all/kavita/Kavita.kt @@ -163,7 +163,7 @@ class Kavita(private val suffix: String = "") : ConfigurableSource, UnmeteredSou // Fallback - use original URL if we don't recognize the pattern else -> manga.url }.also { url -> - Log.d(LOG_TAG, "Generated webview URL: $url (tracking URL: ${manga.url})") +// Log.d(LOG_TAG, "Generated webview URL: $url (tracking URL: ${manga.url})") } } @@ -1012,45 +1012,69 @@ class Kavita(private val suffix: String = "") : ConfigurableSource, UnmeteredSou manga.thumbnail_url = if (preferences.LastVolumeCover) { try { - try { - Log.d(LOG_TAG, "Fetching volumes for series ${result.seriesId}") - val volumesRequest = GET("$apiUrl/Series/volumes?seriesId=${result.seriesId}", headersBuilder().build()) - val volumes = client.newCall(volumesRequest).execute().parseAs>() + Log.d(LOG_TAG, "Fetching volumes for series ${result.seriesId}") + val seriesId = result.seriesId ?: 0 + val volumes = client.newCall( + GET("$apiUrl/Series/volumes?seriesId=$seriesId", headersBuilder().build()), + ).execute().parseAs>() - Log.d(LOG_TAG, "Found ${volumes.size} volumes for series ${result.seriesId}") + val libraryType = getLibraryType(seriesId) + val isComicLibrary = libraryType == LibraryTypeEnum.Comic || libraryType == LibraryTypeEnum.ComicVine - // Find all volumes that have SingleFileVolume chapters and valid covers - val validVolumes = volumes.filter { volume -> + val coverCandidates = mutableListOf>() + + for (volume in volumes) { + if (isComicLibrary && volume.number == -100000) { + // Issue: use chapter covers + for (chapter in volume.chapters) { + if (!chapter.coverImage.isNullOrBlank()) { + val isUnread = chapter.pagesRead < chapter.pages + val sortKey = chapter.number.toFloatOrNull() ?: 0f + coverCandidates.add( + Triple( + "$apiUrl/Image/chapter-cover?chapterId=${chapter.id}&apiKey=$apiKey", + isUnread, + sortKey, + ), + ) + } + } + } else if (!isComicLibrary) { + // Manga: use volume cover if it has a SingleFileVolume chapter val hasSingleFile = volume.chapters.any { chapter -> ChapterType.of(chapter, volume) == ChapterType.SingleFileVolume } - val hasCover = volume.coverImage.isNotBlank() - - hasSingleFile && hasCover + if (hasSingleFile && !volume.coverImage.isNullOrBlank()) { + val isUnread = volume.pagesRead < volume.pages + val volumeNumber = volume.number.toFloat() + coverCandidates.add( + Triple( + "$apiUrl/Image/volume-cover?volumeId=${volume.id}&apiKey=$apiKey", + isUnread, + volumeNumber, + ), + ) + } } - - Log.d(LOG_TAG, "Found ${validVolumes.size} valid volumes with covers") - - // Get the last volume with a valid cover - val lastValidVolume = validVolumes.maxByOrNull { it.number } - Log.d(LOG_TAG, "Selected last volume: ${lastValidVolume?.number ?: "NONE"} (id=${lastValidVolume?.id ?: "NONE"})") - - if (lastValidVolume != null && lastValidVolume.id > 0) { - "$apiUrl/Image/volume-cover?volumeId=${lastValidVolume.id}&apiKey=$apiKey" - } else { - "$apiUrl/image/series-cover?seriesId=${result.seriesId}&apiKey=$apiKey" - } - } catch (e: Exception) { - Log.e(LOG_TAG, "Error fetching volumes for last cover", e) - "$apiUrl/image/series-cover?seriesId=${result.seriesId}&apiKey=$apiKey" } + + Log.d(LOG_TAG, "Found ${coverCandidates.size} cover candidates") + + // Prefer first unread (lowest number), else most recent (highest number) + val targetCover = coverCandidates + .filter { it.second } + .minByOrNull { it.third } + ?: coverCandidates.maxByOrNull { it.third } + + targetCover?.first ?: "$apiUrl/image/series-cover?seriesId=$seriesId&apiKey=$apiKey" } catch (e: Exception) { - Log.e(LOG_TAG, "Error fetching volumes for last cover", e) - "$apiUrl/image/series-cover?seriesId=${result.seriesId}&apiKey=$apiKey" + Log.e(LOG_TAG, "Error fetching volumes for cover selection", e) + "$apiUrl/image/series-cover?seriesId=${result.seriesId ?: 0}&apiKey=$apiKey" } } else { - "$apiUrl/image/series-cover?seriesId=${result.seriesId}&apiKey=$apiKey" + "$apiUrl/image/series-cover?seriesId=${result.seriesId ?: 0}&apiKey=$apiKey" } + manga.status = when (result.publicationStatus) { 4 -> SManga.PUBLISHING_FINISHED 2 -> SManga.COMPLETED diff --git a/src/all/kavita/src/eu/kanade/tachiyomi/extension/all/kavita/KavitaHelper.kt b/src/all/kavita/src/eu/kanade/tachiyomi/extension/all/kavita/KavitaHelper.kt index 96afe81d..e8b83ae0 100644 --- a/src/all/kavita/src/eu/kanade/tachiyomi/extension/all/kavita/KavitaHelper.kt +++ b/src/all/kavita/src/eu/kanade/tachiyomi/extension/all/kavita/KavitaHelper.kt @@ -1,6 +1,5 @@ package eu.kanade.tachiyomi.extension.all.kavita -import android.util.Log import eu.kanade.tachiyomi.extension.all.kavita.dto.ChapterDto import eu.kanade.tachiyomi.extension.all.kavita.dto.ChapterType import eu.kanade.tachiyomi.extension.all.kavita.dto.LibraryTypeEnum @@ -46,10 +45,10 @@ class KavitaHelper { fun getIdFromUrl(url: String): Int { return try { val id = url.substringAfterLast("/").toInt() - Log.d("KavitaHelper", "Extracted ID $id from URL: $url") +// Log.d("KavitaHelper", "Extracted ID $id from URL: $url") id } catch (e: Exception) { - Log.e("KavitaHelper", "Failed to extract ID from URL: $url", e) +// Log.e("KavitaHelper", "Failed to extract ID from URL: $url", e) -1 } } @@ -65,44 +64,12 @@ class KavitaHelper { fun createSeriesDto(obj: SeriesDto, baseUrl: String, apiUrl: String, apiKey: String): SManga = SManga.create().apply { - // url = "$baseUrl/library/${obj.libraryId}/series/${obj.id}" +// url = "$baseUrl/library/${obj.libraryId}/series/${obj.id}" url = "$baseUrl/Series/${obj.id}" title = obj.name thumbnail_url = "$baseUrl/image/series-cover?seriesId=${obj.id}&apiKey=$apiKey" - - // Set status based on read progress - status = when { - obj.pagesRead >= obj.pages -> SManga.COMPLETED - obj.pagesRead > 0 -> SManga.ONGOING - else -> SManga.UNKNOWN - } } -// class CompareChapters { -// companion object : Comparator { -// override fun compare(a: SChapter, b: SChapter): Int { -// if (a.chapter_number < 1.0 && b.chapter_number < 1.0) { -// // Both are volumes, multiply by 100 and do normal sort -// return if ((a.chapter_number * 100) < (b.chapter_number * 100)) { -// 1 -// } else { -// -1 -// } -// } else { -// if (a.chapter_number < 1.0 && b.chapter_number >= 1.0) { -// // A is volume, b is not. A should sort first -// return 1 -// } else if (a.chapter_number >= 1.0 && b.chapter_number < 1.0) { -// return -1 -// } -// } -// if (a.chapter_number < b.chapter_number) return 1 -// if (a.chapter_number > b.chapter_number) return -1 -// return 0 -// } -// } -// } - fun chapterFromVolume(chapter: ChapterDto, volume: VolumeDto, singleFileVolumeNumber: Int? = null, libraryType: LibraryTypeEnum? = null): SChapter = SChapter.create().apply { val type = ChapterType.of(chapter, volume, libraryType) @@ -153,16 +120,6 @@ class KavitaHelper { } } -// chapter_number = try { -// // if (type == ChapterType.SingleFileVolume) { -// // volume.number.toFloat() / 10000 -// // } else { -// // chapter.number.toFloatOrNull() ?: 0f -// // } -// // } catch (e: NumberFormatException) { -// // 0f -// // } - chapter_number = when { // If this is a SingleFileVolume and we have an explicit number (from single-file processing) type == ChapterType.SingleFileVolume && singleFileVolumeNumber != null -> @@ -183,7 +140,11 @@ class KavitaHelper { } } - // url = chapter.id.toString() + url = when { + type == ChapterType.SingleFileVolume && singleFileVolumeNumber != null -> + "volume_${volume.id}" + else -> chapter.id.toString() + } if (chapter.fileCount > 1) { // salt/offset to recognize chapters with new merged part-chapters as new and hence unread diff --git a/src/all/kavita/src/eu/kanade/tachiyomi/extension/all/kavita/KavitaUtils.kt b/src/all/kavita/src/eu/kanade/tachiyomi/extension/all/kavita/KavitaUtils.kt index 68e8363d..76f28b77 100644 --- a/src/all/kavita/src/eu/kanade/tachiyomi/extension/all/kavita/KavitaUtils.kt +++ b/src/all/kavita/src/eu/kanade/tachiyomi/extension/all/kavita/KavitaUtils.kt @@ -80,7 +80,7 @@ fun PreferenceScreen.addEditTextPreference( val result = text.isBlank() || validate?.invoke(text) ?: true if (restartRequired && result) { - Toast.makeText(context, "Restart Tachiyomi to apply new setting.", Toast.LENGTH_LONG).show() + Toast.makeText(context, "Restart Mihon to apply new setting.", Toast.LENGTH_LONG).show() } result diff --git a/src/all/kavita/src/eu/kanade/tachiyomi/extension/all/kavita/dto/MangaDto.kt b/src/all/kavita/src/eu/kanade/tachiyomi/extension/all/kavita/dto/MangaDto.kt index 7a282308..29b1e1e9 100644 --- a/src/all/kavita/src/eu/kanade/tachiyomi/extension/all/kavita/dto/MangaDto.kt +++ b/src/all/kavita/src/eu/kanade/tachiyomi/extension/all/kavita/dto/MangaDto.kt @@ -239,6 +239,7 @@ data class ChapterDto( val titleName: String, val pagesRead: Int, val coverImageLocked: Boolean, + val coverImage: String, val volumeId: Int, val created: String, val lastModifiedUtc: String,