Enhance logic for covers + add Comic Support
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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})")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1011,46 +1011,70 @@ 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<List<VolumeDto>>()
|
||||
val seriesId = result.seriesId ?: 0
|
||||
val volumes = client.newCall(
|
||||
GET("$apiUrl/Series/volumes?seriesId=$seriesId", headersBuilder().build()),
|
||||
).execute().parseAs<List<VolumeDto>>()
|
||||
|
||||
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<Triple<String, Boolean, Float>>()
|
||||
|
||||
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")
|
||||
Log.d(LOG_TAG, "Found ${coverCandidates.size} cover candidates")
|
||||
|
||||
// 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"})")
|
||||
// Prefer first unread (lowest number), else most recent (highest number)
|
||||
val targetCover = coverCandidates
|
||||
.filter { it.second }
|
||||
.minByOrNull { it.third }
|
||||
?: coverCandidates.maxByOrNull { it.third }
|
||||
|
||||
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"
|
||||
}
|
||||
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"
|
||||
}
|
||||
} 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
|
||||
|
||||
@@ -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,43 +64,11 @@ 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<SChapter> {
|
||||
// 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 {
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user