consistency for volumes
This commit is contained in:
@@ -145,7 +145,6 @@ class Kavita(private val suffix: String = "") : ConfigurableSource, UnmeteredSou
|
||||
when {
|
||||
// Handle both API-style and web-style URLs
|
||||
manga.url.contains("/series/") || manga.url.contains("/Series/") -> {
|
||||
// Extract series ID from URL (works for both formats)
|
||||
val seriesId = manga.url
|
||||
.substringAfterLast("/series/")
|
||||
.substringAfterLast("/Series/")
|
||||
@@ -166,7 +165,6 @@ class Kavita(private val suffix: String = "") : ConfigurableSource, UnmeteredSou
|
||||
// Use web-friendly URL format
|
||||
"$baseUrl/library/$libraryId/series/$seriesId"
|
||||
} else {
|
||||
// Fallback to original URL format if we don't have libraryId
|
||||
"$baseUrl/library/1/series/$seriesId"
|
||||
}
|
||||
}
|
||||
@@ -1439,7 +1437,7 @@ class Kavita(private val suffix: String = "") : ConfigurableSource, UnmeteredSou
|
||||
} ?: false
|
||||
|
||||
volumes.forEach { volume ->
|
||||
// This fixes specials being parsed as volumes
|
||||
// This fixes specials being parsed as volumes sometimes
|
||||
if (volume.chapters.size == 1 && volume.minNumber.toInt() != KavitaConstants.SPECIAL_NUMBER) {
|
||||
val chapter = volume.chapters.first()
|
||||
val sChapter = helper.chapterFromVolume(
|
||||
@@ -1450,11 +1448,7 @@ class Kavita(private val suffix: String = "") : ConfigurableSource, UnmeteredSou
|
||||
isWebtoon = isWebtoon,
|
||||
mangaTitle = seriesName,
|
||||
)
|
||||
sChapter.url = "/Chapter/${chapter.id}"
|
||||
sChapter.name = when {
|
||||
volume.name.any { it.isLetter() } -> "v${helper.formatVolumeNumber(volume)} - ${volume.name}"
|
||||
else -> "Volume ${helper.formatVolumeNumber(volume)}"
|
||||
}
|
||||
sChapter.url = "/Chapter/${chapter.id}" // Needed to read volumes as chapters
|
||||
sChapter.chapter_number = volume.minNumber.toFloat()
|
||||
sChapter.scanlator = if (isWebtoon) "Season" else "Volume"
|
||||
volumeItems.add(sChapter)
|
||||
@@ -1469,6 +1463,8 @@ class Kavita(private val suffix: String = "") : ConfigurableSource, UnmeteredSou
|
||||
mangaTitle = seriesName,
|
||||
)
|
||||
if (type == ChapterType.SingleFileVolume) {
|
||||
sChapter.url = "/Chapter/${chapter.id}" // Needed to read volumes as chapters
|
||||
sChapter.chapter_number = volume.minNumber.toFloat()
|
||||
volumeItems.add(sChapter)
|
||||
} else {
|
||||
chapters.add(sChapter)
|
||||
@@ -1488,11 +1484,14 @@ class Kavita(private val suffix: String = "") : ConfigurableSource, UnmeteredSou
|
||||
|
||||
// Case 3: Mixed content - chapters first, then volumes
|
||||
else -> {
|
||||
// Convert volume numbers to negative for proper sorting
|
||||
volumeItems.forEach { it.chapter_number = -it.chapter_number }
|
||||
volumeItems.forEachIndexed { index, it ->
|
||||
if (it.chapter_number <= 0f) {
|
||||
it.chapter_number = chapters.size + index + 1f
|
||||
}
|
||||
}
|
||||
(
|
||||
chapters.sortedByDescending { it.chapter_number } +
|
||||
volumeItems.sortedBy { it.chapter_number }
|
||||
volumeItems.sortedByDescending { it.chapter_number }
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -1521,6 +1520,12 @@ class Kavita(private val suffix: String = "") : ConfigurableSource, UnmeteredSou
|
||||
return GET("$apiUrl/${chapterItem.chapterId}", headersBuilder().build())
|
||||
}
|
||||
|
||||
// Handle volume URLs
|
||||
if (chapter.url.startsWith("Volume/")) {
|
||||
val volumeId = chapter.url.substringAfter("Volume/").substringBefore("_")
|
||||
return GET("$apiUrl/Volume/$volumeId", headersBuilder().build())
|
||||
}
|
||||
|
||||
// Original handling for regular chapters
|
||||
val chapterId = chapter.url.substringBefore("_")
|
||||
return GET("$apiUrl/$chapterId", headersBuilder().build())
|
||||
@@ -1581,10 +1586,13 @@ class Kavita(private val suffix: String = "") : ConfigurableSource, UnmeteredSou
|
||||
|
||||
// For normal titles
|
||||
return Observable.fromCallable {
|
||||
// Check if this is a volume URL (contains "Volume/")
|
||||
if (chapter.url.contains("/Volume/")) {
|
||||
val volumeId = chapter.url.substringAfter("/Volume/").substringBefore("?").toIntOrNull()
|
||||
?: throw IOException("Invalid volume ID")
|
||||
// Check if this is a volume URL
|
||||
if (chapter.url.contains("/Volume/") || chapter.url.startsWith("volume_")) {
|
||||
val volumeId = when {
|
||||
chapter.url.startsWith("volume_") -> chapter.url.substringAfter("volume_").substringBefore("_")
|
||||
chapter.url.contains("/Volume/") -> chapter.url.substringAfter("/Volume/").substringBefore("_")
|
||||
else -> throw IOException("Invalid volume URL format")
|
||||
}
|
||||
|
||||
// Get all pages in this volume
|
||||
val volumeRequest = GET("$apiUrl/Volume/$volumeId", headersBuilder().build())
|
||||
|
||||
@@ -5,7 +5,7 @@ object KavitaConstants {
|
||||
const val UNNUMBERED_VOLUME = -100000
|
||||
const val UNNUMBERED_VOLUME_STR = "-100000"
|
||||
const val SPECIAL_NUMBER = 100_000
|
||||
const val VOLUME_NUMBER_OFFSET = 100000f
|
||||
const val VOLUME_NUMBER_OFFSET = 10000f
|
||||
|
||||
val PERSON_ROLES = listOf(
|
||||
"Writer", "Penciller", "Inker", "Colorist",
|
||||
|
||||
@@ -104,8 +104,8 @@ class KavitaHelper {
|
||||
ChapterType.SingleFileVolume -> {
|
||||
cleanChapterTitle(
|
||||
when {
|
||||
volume.name.any { it.isLetter() } -> "v${volume.minNumber} - ${volume.name}"
|
||||
else -> "Volume ${volume.minNumber}"
|
||||
volume.name.any { it.isLetter() } -> "v${formatVolumeNumber(volume)} - ${volume.name}"
|
||||
else -> "Volume ${formatVolumeNumber(volume)}"
|
||||
},
|
||||
ChapterTitleContext(
|
||||
mangaTitle = mangaTitle,
|
||||
@@ -164,7 +164,6 @@ class KavitaHelper {
|
||||
}
|
||||
|
||||
// @todo Keeping this in case we're switching the logic here this after the Mihon PR
|
||||
// Handle decimal chapter numbers properly
|
||||
// chapter_number = when {
|
||||
// type == ChapterType.SingleFileVolume && singleFileVolumeNumber != null ->
|
||||
// singleFileVolumeNumber.toFloat()
|
||||
@@ -206,16 +205,38 @@ class KavitaHelper {
|
||||
// }
|
||||
|
||||
// Handle decimal chapter numbers properly
|
||||
chapter_number = if (type == ChapterType.SingleFileVolume) {
|
||||
if (volume.minNumber % 1 != 0.0) {
|
||||
chapter_number = when {
|
||||
type == ChapterType.SingleFileVolume && singleFileVolumeNumber != null ->
|
||||
volume.minNumber.toFloat()
|
||||
|
||||
type == ChapterType.SingleFileVolume -> {
|
||||
// For standalone volumes, use positive numbers
|
||||
volume.minNumber.toFloat()
|
||||
} else {
|
||||
volume.minNumber.toInt().toFloat()
|
||||
}
|
||||
} else if (chapter.minNumber % 1 != 0.0) {
|
||||
chapter.minNumber.toFloat()
|
||||
} else {
|
||||
chapter.minNumber.toInt().toFloat()
|
||||
|
||||
// For regular chapters
|
||||
type != ChapterType.SingleFileVolume && type != ChapterType.Special -> {
|
||||
// Handle decimal chapter numbers
|
||||
if (chapter.minNumber % 1 != 0.0) {
|
||||
chapter.minNumber.toFloat()
|
||||
} else {
|
||||
chapter.minNumber.toInt().toFloat()
|
||||
}
|
||||
}
|
||||
|
||||
// For volumes/specials in mixed content, place them below chapter 0
|
||||
else -> {
|
||||
val volumeNum = try {
|
||||
if (volume.minNumber % 1 != 0.0) {
|
||||
volume.minNumber.toFloat()
|
||||
} else {
|
||||
volume.minNumber.toInt().toFloat()
|
||||
}
|
||||
} catch (e: NumberFormatException) {
|
||||
0f
|
||||
}
|
||||
volumeNum / KavitaConstants.VOLUME_NUMBER_OFFSET
|
||||
}
|
||||
}
|
||||
|
||||
url = when {
|
||||
@@ -224,11 +245,9 @@ class KavitaHelper {
|
||||
else -> "chapter_${chapter.id}"
|
||||
}
|
||||
|
||||
// Handle multi-file chapters
|
||||
if (chapter.fileCount > 1) {
|
||||
// Add small offset to maintain order while keeping the main chapter number
|
||||
// salt/offset to recognize chapters with new merged part-chapters as new and hence unread
|
||||
chapter_number += 0.001f * chapter.fileCount
|
||||
// Append file count to URL for reference
|
||||
url = "${url}_${chapter.fileCount}"
|
||||
}
|
||||
|
||||
@@ -242,7 +261,7 @@ class KavitaHelper {
|
||||
}
|
||||
}
|
||||
|
||||
fun formatVolumeNumber(volume: VolumeDto): String {
|
||||
private fun formatVolumeNumber(volume: VolumeDto): String {
|
||||
return when {
|
||||
volume.maxNumber > volume.minNumber ->
|
||||
"${removeTrailingZero(volume.minNumber)}-${removeTrailingZero(volume.maxNumber)}"
|
||||
@@ -268,14 +287,11 @@ class KavitaHelper {
|
||||
// Helper function to remove .0 from whole numbers while preserving actual decimals
|
||||
@SuppressLint("DefaultLocale")
|
||||
private fun removeTrailingZero(number: Double): String {
|
||||
return if (number.isInfinite() || number.isNaN()) {
|
||||
number.toString()
|
||||
return if (number % 1 == 0.0) {
|
||||
number.toInt().toString()
|
||||
} else {
|
||||
val formatted = String.format("%.10f", number)
|
||||
.replace(",", ".")
|
||||
.trimEnd('0')
|
||||
.trimEnd('.')
|
||||
formatted.ifEmpty { "0" }
|
||||
// Use String.format to avoid scientific notation for very small decimals
|
||||
String.format("%.10f", number).replace(",", ".").trimEnd('0').trimEnd('.')
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user