Dataclass cleanup and minor fixes (#2115)

This commit is contained in:
Mitchell Syer
2026-06-15 14:32:09 -04:00
committed by GitHub
parent bab58daecc
commit 934459f15f
18 changed files with 120 additions and 145 deletions

View File

@@ -377,7 +377,7 @@ object SyncYomiSyncService {
// Use version number to decide which chapter to keep
val chosenChapter =
if (localChapter.version >= remoteChapter.version) {
// If there mare more chapter on remote, local sourceOrder will need to be updated to maintain correct source order.
// If there are more chapter on remote, local sourceOrder will need to be updated to maintain correct source order.
if (localChapters.size < remoteChapters.size) {
localChapter.copy(sourceOrder = remoteChapter.sourceOrder)
} else {

View File

@@ -117,12 +117,14 @@ object CategoryManga {
val transform: (ResultRow) -> MangaDataClass = {
// Map the data from the result row to the MangaDataClass
val dataClass = MangaTable.toDataClass(it)
dataClass.lastReadAt = it[lastReadAt]
dataClass.unreadCount = it[unreadCount]
dataClass.downloadCount = it[downloadedCount]
dataClass.chapterCount = it[chapterCount]
dataClass
MangaTable
.toDataClass(it)
.copy(
lastReadAt = it[lastReadAt],
unreadCount = it[unreadCount],
downloadCount = it[downloadedCount],
chapterCount = it[chapterCount],
)
}
return transaction {

View File

@@ -104,9 +104,6 @@ object Chapter {
.associateBy({ it[ChapterTable.url] }, { it })
}
val chapterIds = chapterList.map { dbChapterMap.getValue(it.url)[ChapterTable.id] }
val chapterMetas = getChaptersMetaMaps(chapterIds.map { it.value })
return chapterList.mapIndexed { index, it ->
val dbChapter = dbChapterMap.getValue(it.url)
@@ -128,10 +125,8 @@ object Chapter {
realUrl = dbChapter[ChapterTable.realUrl],
downloaded = dbChapter[ChapterTable.isDownloaded],
pageCount = dbChapter[ChapterTable.pageCount],
chapterCount = chapterList.size,
lastModifiedAt = dbChapter[ChapterTable.lastModifiedAt],
version = dbChapter[ChapterTable.version],
meta = chapterMetas.getValue(dbChapter[ChapterTable.id].value),
)
}
}
@@ -618,7 +613,7 @@ object Chapter {
.withDefault { emptyMap() }
}
fun getChapterMetaMap(chapter: EntityID<Int>): Map<String, String> =
fun getChapterMetaMap(chapter: Int): Map<String, String> =
transaction {
ChapterMetaTable
.selectAll()

View File

@@ -92,7 +92,6 @@ object Manga {
inLibrary = mangaEntry[MangaTable.inLibrary],
inLibraryAt = mangaEntry[MangaTable.inLibraryAt],
source = getSource(mangaEntry[MangaTable.sourceReference]),
meta = getMangaMetaMap(mangaId),
realUrl = mangaEntry[MangaTable.realUrl],
lastFetchedAt = mangaEntry[MangaTable.lastFetchedAt],
chaptersLastFetchedAt = mangaEntry[MangaTable.chaptersLastFetchedAt],
@@ -213,12 +212,12 @@ object Manga {
.orderBy(ChapterTable.sourceOrder to SortOrder.DESC)
.firstOrNull { it[ChapterTable.isRead] }
mangaDaaClass.unreadCount = unreadCount
mangaDaaClass.downloadCount = downloadCount
mangaDaaClass.chapterCount = chapterCount
mangaDaaClass.lastChapterRead = lastChapterRead?.let { ChapterTable.toDataClass(it) }
mangaDaaClass
mangaDaaClass.copy(
unreadCount = unreadCount,
downloadCount = downloadCount,
chapterCount = chapterCount,
lastChapterRead = lastChapterRead?.let { ChapterTable.toDataClass(it) },
)
}
}
@@ -241,7 +240,6 @@ object Manga {
inLibrary = mangaEntry[MangaTable.inLibrary],
inLibraryAt = mangaEntry[MangaTable.inLibraryAt],
source = getSource(mangaEntry[MangaTable.sourceReference]),
meta = getMangaMetaMap(mangaId),
realUrl = mangaEntry[MangaTable.realUrl],
lastFetchedAt = mangaEntry[MangaTable.lastFetchedAt],
chaptersLastFetchedAt = mangaEntry[MangaTable.chaptersLastFetchedAt],

View File

@@ -1,6 +1,7 @@
package suwayomi.tachidesk.manga.model.dataclass
import com.fasterxml.jackson.annotation.JsonValue
import suwayomi.tachidesk.manga.impl.Category
/*
* Copyright (C) Contributors to the Suwayomi project
@@ -18,7 +19,7 @@ enum class IncludeOrExclude(
;
companion object {
fun fromValue(value: Int) = IncludeOrExclude.values().find { it.value == value } ?: UNSET
fun fromValue(value: Int) = entries.find { it.value == value } ?: UNSET
}
}
@@ -27,11 +28,19 @@ data class CategoryDataClass(
val order: Int,
val name: String,
val default: Boolean,
val size: Int,
val includeInUpdate: IncludeOrExclude,
val includeInDownload: IncludeOrExclude,
val version: Long,
val uid: Long,
val lastModifiedAt: Long,
val meta: Map<String, String> = emptyMap(),
)
) {
@Deprecated("Remove with V1 Api")
val size: Int by lazy {
Category.getCategorySize(id)
}
@Deprecated("Remove with V1 Api")
val meta: Map<String, String> by lazy {
Category.getCategoryMetaMap(id)
}
}

View File

@@ -1,6 +1,11 @@
package suwayomi.tachidesk.manga.model.dataclass
import eu.kanade.tachiyomi.source.model.SChapter
import org.jetbrains.exposed.v1.core.eq
import org.jetbrains.exposed.v1.jdbc.selectAll
import org.jetbrains.exposed.v1.jdbc.transactions.transaction
import suwayomi.tachidesk.manga.impl.Chapter.getChapterMetaMap
import suwayomi.tachidesk.manga.model.table.ChapterTable
/*
* Copyright (C) Contributors to the Suwayomi project
@@ -36,12 +41,8 @@ data class ChapterDataClass(
val downloaded: Boolean,
/** used to construct pages in the front-end */
val pageCount: Int = -1,
/** total chapter count, used to calculate if there's a next and prev chapter */
val chapterCount: Int? = null,
val lastModifiedAt: Long = 0,
val version: Long = 0,
/** used to store client specific values */
val meta: Map<String, String> = emptyMap(),
) {
companion object {
fun fromSChapter(
@@ -70,4 +71,20 @@ data class ChapterDataClass(
downloaded = false,
)
}
@Deprecated("Remove with V1 Api")
val chapterCount: Int by lazy {
transaction {
ChapterTable
.selectAll()
.where { ChapterTable.manga eq mangaId }
.count()
.toInt()
}
}
@Deprecated("Remove with V1 Api")
val meta: Map<String, String> by lazy {
getChapterMetaMap(id)
}
}

View File

@@ -8,6 +8,7 @@ package suwayomi.tachidesk.manga.model.dataclass
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
import eu.kanade.tachiyomi.source.model.UpdateStrategy
import suwayomi.tachidesk.manga.impl.Manga.getMangaMetaMap
import suwayomi.tachidesk.manga.impl.util.lang.trimAll
import suwayomi.tachidesk.manga.model.table.MangaStatus
import java.time.Instant
@@ -28,18 +29,16 @@ data class MangaDataClass(
val inLibrary: Boolean = false,
val inLibraryAt: Long = 0,
val source: SourceDataClass? = null,
/** meta data for clients */
val meta: Map<String, String> = emptyMap(),
val realUrl: String? = null,
var lastFetchedAt: Long? = 0,
var chaptersLastFetchedAt: Long? = 0,
var updateStrategy: UpdateStrategy = UpdateStrategy.ALWAYS_UPDATE,
val lastFetchedAt: Long? = 0,
val chaptersLastFetchedAt: Long? = 0,
val updateStrategy: UpdateStrategy = UpdateStrategy.ALWAYS_UPDATE,
val freshData: Boolean = false,
var unreadCount: Long? = null,
var downloadCount: Long? = null,
var chapterCount: Long? = null,
var lastReadAt: Long? = null,
var lastChapterRead: ChapterDataClass? = null,
val unreadCount: Long? = null,
val downloadCount: Long? = null,
val chapterCount: Long? = null,
val lastReadAt: Long? = null,
val lastChapterRead: ChapterDataClass? = null,
val age: Long? = if (lastFetchedAt == null) 0 else Instant.now().epochSecond.minus(lastFetchedAt),
val chaptersAge: Long? = if (chaptersLastFetchedAt == null) null else Instant.now().epochSecond.minus(chaptersLastFetchedAt),
val trackers: List<MangaTrackerDataClass>? = null,
@@ -47,6 +46,11 @@ data class MangaDataClass(
val version: Long = 0,
) {
override fun toString(): String = "\"$title\" (id= $id) (sourceId= $sourceId)"
@Deprecated("Remove with V1 Api")
val meta: Map<String, String> by lazy {
getMangaMetaMap(id)
}
}
data class PagedMangaListDataClass(

View File

@@ -9,7 +9,6 @@ package suwayomi.tachidesk.manga.model.table
import org.jetbrains.exposed.v1.core.ResultRow
import org.jetbrains.exposed.v1.core.dao.id.IntIdTable
import suwayomi.tachidesk.manga.impl.Category
import suwayomi.tachidesk.manga.model.dataclass.CategoryDataClass
import suwayomi.tachidesk.manga.model.dataclass.IncludeOrExclude
@@ -28,15 +27,13 @@ object CategoryTable : IntIdTable() {
fun CategoryTable.toDataClass(categoryEntry: ResultRow) =
CategoryDataClass(
categoryEntry[id].value,
categoryEntry[order],
categoryEntry[name],
categoryEntry[isDefault],
Category.getCategorySize(categoryEntry[id].value),
IncludeOrExclude.fromValue(categoryEntry[includeInUpdate]),
IncludeOrExclude.fromValue(categoryEntry[includeInDownload]),
categoryEntry[version],
categoryEntry[uid],
categoryEntry[lastModifiedAt],
Category.getCategoryMetaMap(categoryEntry[id].value),
id = categoryEntry[id].value,
order = categoryEntry[order],
name = categoryEntry[name],
default = categoryEntry[isDefault],
includeInUpdate = IncludeOrExclude.fromValue(categoryEntry[includeInUpdate]),
includeInDownload = IncludeOrExclude.fromValue(categoryEntry[includeInDownload]),
version = categoryEntry[version],
uid = categoryEntry[uid],
lastModifiedAt = categoryEntry[lastModifiedAt],
)

View File

@@ -10,10 +10,6 @@ package suwayomi.tachidesk.manga.model.table
import org.jetbrains.exposed.v1.core.ReferenceOption
import org.jetbrains.exposed.v1.core.ResultRow
import org.jetbrains.exposed.v1.core.dao.id.IntIdTable
import org.jetbrains.exposed.v1.core.eq
import org.jetbrains.exposed.v1.jdbc.selectAll
import org.jetbrains.exposed.v1.jdbc.transactions.transaction
import suwayomi.tachidesk.manga.impl.Chapter.getChapterMetaMap
import suwayomi.tachidesk.manga.model.dataclass.ChapterDataClass
import suwayomi.tachidesk.manga.model.table.columns.truncatingVarchar
@@ -48,45 +44,24 @@ object ChapterTable : IntIdTable() {
val isSyncing = bool("is_syncing").default(false)
}
fun ChapterTable.toDataClass(
chapterEntry: ResultRow,
includeChapterCount: Boolean = true,
includeChapterMeta: Boolean = true,
) = ChapterDataClass(
id = chapterEntry[id].value,
url = chapterEntry[url],
name = chapterEntry[name],
uploadDate = chapterEntry[date_upload],
chapterNumber = chapterEntry[chapter_number],
scanlator = chapterEntry[scanlator],
mangaId = chapterEntry[manga].value,
read = chapterEntry[isRead],
bookmarked = chapterEntry[isBookmarked],
lastPageRead = chapterEntry[lastPageRead],
lastReadAt = chapterEntry[lastReadAt],
index = chapterEntry[sourceOrder],
fetchedAt = chapterEntry[fetchedAt],
realUrl = chapterEntry[realUrl],
downloaded = chapterEntry[isDownloaded],
pageCount = chapterEntry[pageCount],
chapterCount =
if (includeChapterCount) {
transaction {
ChapterTable
.selectAll()
.where { manga eq chapterEntry[manga].value }
.count()
.toInt()
}
} else {
null
},
meta =
if (includeChapterMeta) {
getChapterMetaMap(chapterEntry[id])
} else {
emptyMap()
},
lastModifiedAt = chapterEntry[lastModifiedAt],
version = chapterEntry[version],
)
fun ChapterTable.toDataClass(chapterEntry: ResultRow) =
ChapterDataClass(
id = chapterEntry[id].value,
url = chapterEntry[url],
name = chapterEntry[name],
uploadDate = chapterEntry[date_upload],
chapterNumber = chapterEntry[chapter_number],
scanlator = chapterEntry[scanlator],
mangaId = chapterEntry[manga].value,
read = chapterEntry[isRead],
bookmarked = chapterEntry[isBookmarked],
lastPageRead = chapterEntry[lastPageRead],
lastReadAt = chapterEntry[lastReadAt],
index = chapterEntry[sourceOrder],
fetchedAt = chapterEntry[fetchedAt],
realUrl = chapterEntry[realUrl],
downloaded = chapterEntry[isDownloaded],
pageCount = chapterEntry[pageCount],
lastModifiedAt = chapterEntry[lastModifiedAt],
version = chapterEntry[version],
)

View File

@@ -11,11 +11,9 @@ import eu.kanade.tachiyomi.source.model.SManga
import eu.kanade.tachiyomi.source.model.UpdateStrategy
import org.jetbrains.exposed.v1.core.ResultRow
import org.jetbrains.exposed.v1.core.dao.id.IntIdTable
import suwayomi.tachidesk.manga.impl.Manga.getMangaMetaMap
import suwayomi.tachidesk.manga.impl.MangaList.proxyThumbnailUrl
import suwayomi.tachidesk.manga.model.dataclass.MangaDataClass
import suwayomi.tachidesk.manga.model.dataclass.toGenreList
import suwayomi.tachidesk.manga.model.table.MangaStatus.Companion
import suwayomi.tachidesk.manga.model.table.columns.truncatingVarchar
import suwayomi.tachidesk.manga.model.table.columns.unlimitedVarchar
@@ -52,37 +50,29 @@ object MangaTable : IntIdTable() {
val isSyncing = bool("is_syncing").default(false)
}
fun MangaTable.toDataClass(
mangaEntry: ResultRow,
includeMangaMeta: Boolean = true,
) = MangaDataClass(
id = mangaEntry[this.id].value,
sourceId = mangaEntry[sourceReference].toString(),
url = mangaEntry[url],
title = mangaEntry[title],
thumbnailUrl = proxyThumbnailUrl(mangaEntry[this.id].value),
thumbnailUrlLastFetched = mangaEntry[thumbnailUrlLastFetched],
initialized = mangaEntry[initialized],
artist = mangaEntry[artist],
author = mangaEntry[author],
description = mangaEntry[description],
genre = mangaEntry[genre].toGenreList(),
status = Companion.valueOf(mangaEntry[status]).name,
inLibrary = mangaEntry[inLibrary],
inLibraryAt = mangaEntry[inLibraryAt],
meta =
if (includeMangaMeta) {
getMangaMetaMap(mangaEntry[id].value)
} else {
emptyMap()
},
realUrl = mangaEntry[realUrl],
lastFetchedAt = mangaEntry[lastFetchedAt],
chaptersLastFetchedAt = mangaEntry[chaptersLastFetchedAt],
updateStrategy = UpdateStrategy.valueOf(mangaEntry[updateStrategy]),
lastModifiedAt = mangaEntry[lastModifiedAt],
version = mangaEntry[version],
)
fun MangaTable.toDataClass(mangaEntry: ResultRow) =
MangaDataClass(
id = mangaEntry[this.id].value,
sourceId = mangaEntry[sourceReference].toString(),
url = mangaEntry[url],
title = mangaEntry[title],
thumbnailUrl = proxyThumbnailUrl(mangaEntry[this.id].value),
thumbnailUrlLastFetched = mangaEntry[thumbnailUrlLastFetched],
initialized = mangaEntry[initialized],
artist = mangaEntry[artist],
author = mangaEntry[author],
description = mangaEntry[description],
genre = mangaEntry[genre].toGenreList(),
status = MangaStatus.valueOf(mangaEntry[status]).name,
inLibrary = mangaEntry[inLibrary],
inLibraryAt = mangaEntry[inLibraryAt],
realUrl = mangaEntry[realUrl],
lastFetchedAt = mangaEntry[lastFetchedAt],
chaptersLastFetchedAt = mangaEntry[chaptersLastFetchedAt],
updateStrategy = UpdateStrategy.valueOf(mangaEntry[updateStrategy]),
lastModifiedAt = mangaEntry[lastModifiedAt],
version = mangaEntry[version],
)
enum class MangaStatus(
val value: Int,

View File

@@ -71,11 +71,6 @@ import java.net.Authenticator
import java.net.PasswordAuthentication
import java.security.Security
import java.util.Locale
import kotlin.concurrent.thread
import kotlin.io.path.Path
import kotlin.io.path.createDirectories
import kotlin.io.path.div
import kotlin.math.roundToInt
private val logger = KotlinLogging.logger {}

View File

@@ -11,9 +11,7 @@ import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.subscribe
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
import kotlinx.serialization.json.Json

View File

@@ -136,7 +136,7 @@ object WebInterfaceManager {
fun getAboutInfo(): AboutWebUI {
val currentVersion = getLocalVersion()
val failedToGetVersion = currentVersion === "r-1"
val failedToGetVersion = currentVersion == "r-1"
if (failedToGetVersion) {
throw Exception("Failed to get current version")
}

View File

@@ -2,14 +2,12 @@ package suwayomi.tachidesk
import android.os.Handler
import android.os.Looper
import android.os.Message
import java.util.concurrent.CountDownLatch
import java.util.concurrent.TimeUnit
import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.test.assertNotEquals
import kotlin.test.assertTrue
import kotlin.text.StringBuilder
class LooperThread : Thread() {
var mHandler: Handler? = null

View File

@@ -46,6 +46,7 @@ class TestUpdater : IUpdater {
TODO("Not yet implemented")
}
@Deprecated("Replaced with updates", replaceWith = ReplaceWith("updates"))
override val status: Flow<UpdateStatus>
get() = TODO("Not yet implemented")
override val updates: Flow<UpdateUpdates>

View File

@@ -28,7 +28,6 @@ import suwayomi.tachidesk.server.serverModule
import suwayomi.tachidesk.server.settings.SettingsRegistry
import suwayomi.tachidesk.server.util.AppMutex.handleAppMutex
import suwayomi.tachidesk.server.util.ConfigTypeRegistration
import suwayomi.tachidesk.server.util.SystemTray
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
import xyz.nulldev.androidcompat.AndroidCompatInitializer