Update parsers and fix compatibility

This commit is contained in:
Koitharu
2025-03-02 19:22:27 +02:00
parent d918b1e274
commit 5d91e20844
14 changed files with 67 additions and 18 deletions

View File

@@ -20,7 +20,7 @@ import org.koitharu.kotatsu.core.db.TABLE_CHAPTERS
data class ChapterEntity( data class ChapterEntity(
@ColumnInfo(name = "chapter_id") val chapterId: Long, @ColumnInfo(name = "chapter_id") val chapterId: Long,
@ColumnInfo(name = "manga_id") val mangaId: Long, @ColumnInfo(name = "manga_id") val mangaId: Long,
@ColumnInfo(name = "name") val name: String, @ColumnInfo(name = "name") val title: String,
@ColumnInfo(name = "number") val number: Float, @ColumnInfo(name = "number") val number: Float,
@ColumnInfo(name = "volume") val volume: Int, @ColumnInfo(name = "volume") val volume: Int,
@ColumnInfo(name = "url") val url: String, @ColumnInfo(name = "url") val url: String,

View File

@@ -9,6 +9,7 @@ import org.koitharu.kotatsu.parsers.model.MangaTag
import org.koitharu.kotatsu.parsers.model.SortOrder import org.koitharu.kotatsu.parsers.model.SortOrder
import org.koitharu.kotatsu.parsers.util.longHashCode import org.koitharu.kotatsu.parsers.util.longHashCode
import org.koitharu.kotatsu.parsers.util.mapToSet import org.koitharu.kotatsu.parsers.util.mapToSet
import org.koitharu.kotatsu.parsers.util.nullIfEmpty
import org.koitharu.kotatsu.parsers.util.toArraySet import org.koitharu.kotatsu.parsers.util.toArraySet
import org.koitharu.kotatsu.parsers.util.toTitleCase import org.koitharu.kotatsu.parsers.util.toTitleCase
@@ -50,7 +51,7 @@ fun Collection<MangaWithTags>.toMangaList() = map { it.toManga() }
fun ChapterEntity.toMangaChapter() = MangaChapter( fun ChapterEntity.toMangaChapter() = MangaChapter(
id = chapterId, id = chapterId,
name = name, title = title.nullIfEmpty(),
number = number, number = number,
volume = volume, volume = volume,
url = url, url = url,
@@ -93,7 +94,7 @@ fun Iterable<IndexedValue<MangaChapter>>.toEntities(mangaId: Long) = map { (inde
ChapterEntity( ChapterEntity(
chapterId = chapter.id, chapterId = chapter.id,
mangaId = mangaId, mangaId = mangaId,
name = chapter.name, title = chapter.title.orEmpty(),
number = chapter.number, number = chapter.number,
volume = chapter.volume, volume = chapter.volume,
url = chapter.url, url = chapter.url,

View File

@@ -1,5 +1,6 @@
package org.koitharu.kotatsu.core.model package org.koitharu.kotatsu.core.model
import android.content.res.Resources
import android.net.Uri import android.net.Uri
import android.text.SpannableStringBuilder import android.text.SpannableStringBuilder
import androidx.annotation.DrawableRes import androidx.annotation.DrawableRes
@@ -168,3 +169,18 @@ private fun SpannableStringBuilder.appendTagsSummary(filter: MangaListFilter) {
} }
} }
} }
fun MangaChapter.getLocalizedTitle(resources: Resources): String {
title?.let {
if (it.isNotBlank()) {
return it
}
}
val num = numberString()
val vol = volumeString()
return when {
num != null && vol != null -> resources.getString(R.string.chapter_volume_number, vol, num)
num != null -> resources.getString(R.string.chapter_number, num)
else -> resources.getString(R.string.unnamed_chapter) // TODO fallback to manga title + index
}
}

View File

@@ -17,7 +17,7 @@ data class ParcelableChapter(
override fun create(parcel: Parcel) = ParcelableChapter( override fun create(parcel: Parcel) = ParcelableChapter(
MangaChapter( MangaChapter(
id = parcel.readLong(), id = parcel.readLong(),
name = parcel.readString().orEmpty(), title = parcel.readString(),
number = parcel.readFloat(), number = parcel.readFloat(),
volume = parcel.readInt(), volume = parcel.readInt(),
url = parcel.readString().orEmpty(), url = parcel.readString().orEmpty(),
@@ -30,7 +30,7 @@ data class ParcelableChapter(
override fun ParcelableChapter.write(parcel: Parcel, flags: Int) = with(chapter) { override fun ParcelableChapter.write(parcel: Parcel, flags: Int) = with(chapter) {
parcel.writeLong(id) parcel.writeLong(id)
parcel.writeString(name) parcel.writeString(title)
parcel.writeFloat(number) parcel.writeFloat(number)
parcel.writeInt(volume) parcel.writeInt(volume)
parcel.writeString(url) parcel.writeString(url)

View File

@@ -229,7 +229,7 @@ class ExternalPluginContentSource(
do { do {
result += MangaChapter( result += MangaChapter(
id = cursor.getLong(COLUMN_ID), id = cursor.getLong(COLUMN_ID),
name = cursor.getString(COLUMN_NAME), title = cursor.getStringOrNull(COLUMN_NAME),
number = cursor.getFloatOrDefault(COLUMN_NUMBER, 0f), number = cursor.getFloatOrDefault(COLUMN_NUMBER, 0f),
volume = cursor.getIntOrDefault(COLUMN_VOLUME, 0), volume = cursor.getIntOrDefault(COLUMN_VOLUME, 0),
url = cursor.getString(COLUMN_URL), url = cursor.getString(COLUMN_URL),

View File

@@ -25,7 +25,7 @@ fun chapterListItemAD(
AdapterDelegateClickListenerAdapter(this, clickListener).attach(itemView) AdapterDelegateClickListenerAdapter(this, clickListener).attach(itemView)
bind { bind {
binding.textViewTitle.text = item.chapter.name binding.textViewTitle.text = item.getTitle(context.resources)
binding.textViewDescription.textAndVisible = item.description binding.textViewDescription.textAndVisible = item.description
when { when {
item.isCurrent -> { item.isCurrent -> {

View File

@@ -1,7 +1,9 @@
package org.koitharu.kotatsu.details.ui.model package org.koitharu.kotatsu.details.ui.model
import android.content.res.Resources
import android.text.format.DateUtils import android.text.format.DateUtils
import org.jsoup.internal.StringUtil.StringJoiner import org.jsoup.internal.StringUtil.StringJoiner
import org.koitharu.kotatsu.core.model.getLocalizedTitle
import org.koitharu.kotatsu.list.ui.model.ListModel import org.koitharu.kotatsu.list.ui.model.ListModel
import org.koitharu.kotatsu.parsers.model.MangaChapter import org.koitharu.kotatsu.parsers.model.MangaChapter
import kotlin.experimental.and import kotlin.experimental.and
@@ -11,6 +13,8 @@ data class ChapterListItem(
val flags: Byte, val flags: Byte,
) : ListModel { ) : ListModel {
private var cachedTitle: String? = null
var description: String? = null var description: String? = null
private set private set
get() { get() {
@@ -50,6 +54,22 @@ data class ChapterListItem(
val isGrid: Boolean val isGrid: Boolean
get() = hasFlag(FLAG_GRID) get() = hasFlag(FLAG_GRID)
operator fun contains(query: String): Boolean = with(chapter) {
title?.contains(query, ignoreCase = true) == true
|| numberString()?.contains(query) == true
|| volumeString()?.contains(query) == true
}
fun getTitle(resources: Resources): String {
cachedTitle?.let {
return it
}
return chapter.getLocalizedTitle(resources).also {
cachedTitle = it
}
}
private fun buildDescription(): String { private fun buildDescription(): String {
val joiner = StringJoiner("") val joiner = StringJoiner("")
chapter.numberString()?.let { chapter.numberString()?.let {

View File

@@ -216,9 +216,7 @@ abstract class ChaptersPagesViewModel(
if (query.isEmpty() || this.isEmpty()) { if (query.isEmpty() || this.isEmpty()) {
return this return this
} }
return filter { return filter { it.contains(query) }
it.chapter.name.contains(query, ignoreCase = true)
}
} }
private suspend fun onDownloadComplete(downloadedManga: LocalManga?) { private suspend fun onDownloadComplete(downloadedManga: LocalManga?) {

View File

@@ -27,6 +27,7 @@ import org.koitharu.kotatsu.parsers.util.json.getLongOrDefault
import org.koitharu.kotatsu.parsers.util.json.getStringOrNull import org.koitharu.kotatsu.parsers.util.json.getStringOrNull
import org.koitharu.kotatsu.parsers.util.json.mapJSONToSet import org.koitharu.kotatsu.parsers.util.json.mapJSONToSet
import org.koitharu.kotatsu.parsers.util.json.toStringSet import org.koitharu.kotatsu.parsers.util.json.toStringSet
import org.koitharu.kotatsu.parsers.util.nullIfEmpty
import org.koitharu.kotatsu.parsers.util.runCatchingCancellable import org.koitharu.kotatsu.parsers.util.runCatchingCancellable
import org.koitharu.kotatsu.parsers.util.toTitleCase import org.koitharu.kotatsu.parsers.util.toTitleCase
import java.io.File import java.io.File
@@ -110,7 +111,7 @@ class MangaIndex(source: String?) {
jo.put(KEY_NUMBER, chapter.value.number) jo.put(KEY_NUMBER, chapter.value.number)
jo.put(KEY_VOLUME, chapter.value.volume) jo.put(KEY_VOLUME, chapter.value.volume)
jo.put(KEY_URL, chapter.value.url) jo.put(KEY_URL, chapter.value.url)
jo.put(KEY_NAME, chapter.value.name) jo.put(KEY_NAME, chapter.value.title.orEmpty())
jo.put(KEY_UPLOAD_DATE, chapter.value.uploadDate) jo.put(KEY_UPLOAD_DATE, chapter.value.uploadDate)
jo.put(KEY_SCANLATOR, chapter.value.scanlator) jo.put(KEY_SCANLATOR, chapter.value.scanlator)
jo.put(KEY_BRANCH, chapter.value.branch) jo.put(KEY_BRANCH, chapter.value.branch)
@@ -178,7 +179,7 @@ class MangaIndex(source: String?) {
chapters.add( chapters.add(
MangaChapter( MangaChapter(
id = k.toLong(), id = k.toLong(),
name = v.getString(KEY_NAME), title = v.getStringOrNull(KEY_NAME),
url = v.getString(KEY_URL), url = v.getString(KEY_URL),
number = v.getFloatOrDefault(KEY_NUMBER, 0f), number = v.getFloatOrDefault(KEY_NUMBER, 0f),
volume = v.getIntOrDefault(KEY_VOLUME, 0), volume = v.getIntOrDefault(KEY_VOLUME, 0),

View File

@@ -113,7 +113,7 @@ class LocalMangaParser(private val uri: Uri) {
}.toString().removePrefix(Path.DIRECTORY_SEPARATOR) }.toString().removePrefix(Path.DIRECTORY_SEPARATOR)
MangaChapter( MangaChapter(
id = "$i$s".longHashCode(), id = "$i$s".longHashCode(),
name = s.fileNameToTitle().ifEmpty { title }, title = null,
number = 0f, number = 0f,
volume = 0, volume = 0,
source = LocalMangaSource, source = LocalMangaSource,

View File

@@ -18,6 +18,7 @@ import org.koitharu.kotatsu.local.data.MangaIndex
import org.koitharu.kotatsu.local.data.input.LocalMangaParser import org.koitharu.kotatsu.local.data.input.LocalMangaParser
import org.koitharu.kotatsu.parsers.model.Manga import org.koitharu.kotatsu.parsers.model.Manga
import org.koitharu.kotatsu.parsers.model.MangaChapter import org.koitharu.kotatsu.parsers.model.MangaChapter
import org.koitharu.kotatsu.parsers.util.nullIfEmpty
import java.io.File import java.io.File
class LocalMangaDirOutput( class LocalMangaDirOutput(
@@ -145,7 +146,16 @@ class LocalMangaDirOutput(
index.getChapterFileName(chapter.value.id)?.let { index.getChapterFileName(chapter.value.id)?.let {
return it return it
} }
val baseName = "${chapter.index}_${chapter.value.name.toFileNameSafe()}".take(32) val baseName = buildString {
append(chapter.index)
chapter.value.title?.nullIfEmpty()?.let {
append('_')
append(it.toFileNameSafe())
}
if (length > 32) {
deleteRange(31, lastIndex)
}
}
var i = 0 var i = 0
while (true) { while (true) {
val name = (if (i == 0) baseName else baseName + "_$i") + ".cbz" val name = (if (i == 0) baseName else baseName + "_$i") + ".cbz"

View File

@@ -16,6 +16,7 @@ import coil3.ImageLoader
import coil3.request.ImageRequest import coil3.request.ImageRequest
import org.koitharu.kotatsu.R import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.core.LocalizedAppContext import org.koitharu.kotatsu.core.LocalizedAppContext
import org.koitharu.kotatsu.core.model.getLocalizedTitle
import org.koitharu.kotatsu.core.nav.AppRouter import org.koitharu.kotatsu.core.nav.AppRouter
import org.koitharu.kotatsu.core.prefs.AppSettings import org.koitharu.kotatsu.core.prefs.AppSettings
import org.koitharu.kotatsu.core.util.ext.checkNotificationPermission import org.koitharu.kotatsu.core.util.ext.checkNotificationPermission
@@ -76,7 +77,7 @@ class TrackerNotificationHelper @Inject constructor(
setGroup(GROUP_NEW_CHAPTERS) setGroup(GROUP_NEW_CHAPTERS)
val style = NotificationCompat.InboxStyle(this) val style = NotificationCompat.InboxStyle(this)
for (chapter in newChapters) { for (chapter in newChapters) {
style.addLine(chapter.name) style.addLine(chapter.getLocalizedTitle(applicationContext.resources))
} }
style.setSummaryText(manga.title) style.setSummaryText(manga.title)
style.setBigContentTitle(summary) style.setBigContentTitle(summary)
@@ -190,7 +191,6 @@ class TrackerNotificationHelper @Inject constructor(
const val TAG = "tracker" const val TAG = "tracker"
private const val LEGACY_CHANNELS_GROUP_ID = "trackers" private const val LEGACY_CHANNELS_GROUP_ID = "trackers"
private const val LEGACY_CHANNEL_ID_PREFIX = "track_fav_"
private const val LEGACY_CHANNEL_ID_HISTORY = "track_history" private const val LEGACY_CHANNEL_ID_HISTORY = "track_history"
private const val LEGACY_CHANNEL_ID = "tracking" private const val LEGACY_CHANNEL_ID = "tracking"
} }

View File

@@ -806,4 +806,7 @@
<string name="global_search">Global search</string> <string name="global_search">Global search</string>
<string name="disable_captcha_notifications">Disable captcha notifications</string> <string name="disable_captcha_notifications">Disable captcha notifications</string>
<string name="disable_captcha_notifications_summary">You will not receive notifications about solving CAPTCHA for this source but this can lead to breaking background operations (checking for new chapters, obtaining recommendations, etc)</string> <string name="disable_captcha_notifications_summary">You will not receive notifications about solving CAPTCHA for this source but this can lead to breaking background operations (checking for new chapters, obtaining recommendations, etc)</string>
<string name="chapter_volume_number">Vol %1$s Chapter %2$s</string>
<string name="chapter_number">Chapter %s</string>
<string name="unnamed_chapter">Unnamed chapter</string>
</resources> </resources>

View File

@@ -15,7 +15,7 @@ coroutines = "1.10.1"
desugar = "2.1.5" desugar = "2.1.5"
diskLruCache = "1.4" diskLruCache = "1.4"
fragment = "1.8.6" fragment = "1.8.6"
gradle = "8.8.1" gradle = "8.8.2"
guava = "33.3.1-android" guava = "33.3.1-android"
dagger = "2.55" dagger = "2.55"
hilt = "1.2.0" hilt = "1.2.0"
@@ -31,7 +31,7 @@ material = "1.13.0-alpha11"
moshi = "1.15.2" moshi = "1.15.2"
okhttp = "4.12.0" okhttp = "4.12.0"
okio = "3.10.2" okio = "3.10.2"
parsers = "843d1f1bea" parsers = "531145c7f9"
preference = "1.2.1" preference = "1.2.1"
recyclerview = "1.4.0" recyclerview = "1.4.0"
room = "2.6.1" room = "2.6.1"