diff --git a/server/src/main/kotlin/suwayomi/tachidesk/manga/controller/CategoryController.kt b/server/src/main/kotlin/suwayomi/tachidesk/manga/controller/CategoryController.kt index 96b56b5a..c4ec3ef3 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/manga/controller/CategoryController.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/manga/controller/CategoryController.kt @@ -61,14 +61,15 @@ object CategoryController { pathParam("categoryId"), formParam("name"), formParam("default"), + formParam("includeInUpdate"), documentWith = { withOperation { summary("Category modify") description("Modify a category") } }, - behaviorOf = { ctx, categoryId, name, isDefault -> - Category.updateCategory(categoryId, name, isDefault) + behaviorOf = { ctx, categoryId, name, isDefault, includeInUpdate -> + Category.updateCategory(categoryId, name, isDefault, includeInUpdate) ctx.status(200) }, withResults = { diff --git a/server/src/main/kotlin/suwayomi/tachidesk/manga/controller/UpdateController.kt b/server/src/main/kotlin/suwayomi/tachidesk/manga/controller/UpdateController.kt index 172c7d96..06f055a6 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/manga/controller/UpdateController.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/manga/controller/UpdateController.kt @@ -13,10 +13,7 @@ import suwayomi.tachidesk.manga.impl.Chapter import suwayomi.tachidesk.manga.impl.update.IUpdater import suwayomi.tachidesk.manga.impl.update.UpdateStatus import suwayomi.tachidesk.manga.impl.update.UpdaterSocket -import suwayomi.tachidesk.manga.model.dataclass.CategoryDataClass -import suwayomi.tachidesk.manga.model.dataclass.MangaChapterDataClass -import suwayomi.tachidesk.manga.model.dataclass.MangaDataClass -import suwayomi.tachidesk.manga.model.dataclass.PaginatedList +import suwayomi.tachidesk.manga.model.dataclass.* import suwayomi.tachidesk.server.JavalinSetup.future import suwayomi.tachidesk.server.util.formParam import suwayomi.tachidesk.server.util.handler @@ -94,11 +91,21 @@ object UpdateController { updater.reset() } - val mangasToUpdate = categories - .flatMap { CategoryManga.getCategoryMangaList(it.id) } - .distinctBy { it.id } - .sortedWith(compareBy(String.CASE_INSENSITIVE_ORDER, MangaDataClass::title)) - .filter { it.updateStrategy == UpdateStrategy.ALWAYS_UPDATE } + val includeInUpdateStatusToCategoryMap = categories.groupBy { it.includeInUpdate } + val excludedCategories = includeInUpdateStatusToCategoryMap[IncludeInUpdate.EXCLUDE].orEmpty() + val includedCategories = includeInUpdateStatusToCategoryMap[IncludeInUpdate.INCLUDE].orEmpty() + val unsetCategories = includeInUpdateStatusToCategoryMap[IncludeInUpdate.UNSET].orEmpty() + val categoriesToUpdate = includedCategories.ifEmpty { unsetCategories } + + logger.debug { "Updating categories: '${categoriesToUpdate.joinToString("', '") { it.name }}'" } + + val categoriesToUpdateMangas = categoriesToUpdate + .flatMap { CategoryManga.getCategoryMangaList(it.id) } + .distinctBy { it.id } + val mangasToCategoriesMap = CategoryManga.getMangasCategories(categoriesToUpdateMangas.map { it.id }) + val mangasToUpdate = categoriesToUpdateMangas + .filter { it.updateStrategy == UpdateStrategy.ALWAYS_UPDATE } + .filter { !excludedCategories.any { category -> mangasToCategoriesMap[it.id]?.contains(category) == true } } // In case no manga gets updated and no update job was running before, the client would never receive an info about its update request if (mangasToUpdate.isEmpty()) { @@ -106,7 +113,10 @@ object UpdateController { return } - updater.addMangasToQueue(mangasToUpdate) + updater.addMangasToQueue( + mangasToUpdate + .sortedWith(compareBy(String.CASE_INSENSITIVE_ORDER, MangaDataClass::title)), + ) } fun categoryUpdateWS(ws: WsConfig) { diff --git a/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/Category.kt b/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/Category.kt index 92e7ede9..6e6ac649 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/Category.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/Category.kt @@ -19,6 +19,7 @@ import org.jetbrains.exposed.sql.transactions.transaction import org.jetbrains.exposed.sql.update import suwayomi.tachidesk.manga.impl.CategoryManga.removeMangaFromCategory import suwayomi.tachidesk.manga.model.dataclass.CategoryDataClass +import suwayomi.tachidesk.manga.model.dataclass.IncludeInUpdate import suwayomi.tachidesk.manga.model.table.CategoryMangaTable import suwayomi.tachidesk.manga.model.table.CategoryMetaTable import suwayomi.tachidesk.manga.model.table.CategoryTable @@ -49,11 +50,12 @@ object Category { } } - fun updateCategory(categoryId: Int, name: String?, isDefault: Boolean?) { + fun updateCategory(categoryId: Int, name: String?, isDefault: Boolean?, includeInUpdate: Int?) { transaction { CategoryTable.update({ CategoryTable.id eq categoryId }) { if (name != null && !name.equals(DEFAULT_CATEGORY_NAME, ignoreCase = true)) it[CategoryTable.name] = name if (isDefault != null) it[CategoryTable.isDefault] = isDefault + if (includeInUpdate != null) it[CategoryTable.includeInUpdate] = includeInUpdate } } } @@ -100,7 +102,7 @@ object Category { private fun addDefaultIfNecessary(categories: List): List { val defaultCategorySize = MangaTable.select { (MangaTable.inLibrary eq true) and (MangaTable.defaultCategory eq true) }.count().toInt() return if (defaultCategorySize > 0) { - listOf(CategoryDataClass(DEFAULT_CATEGORY_ID, 0, DEFAULT_CATEGORY_NAME, true, defaultCategorySize)) + categories + listOf(CategoryDataClass(DEFAULT_CATEGORY_ID, 0, DEFAULT_CATEGORY_NAME, true, defaultCategorySize, IncludeInUpdate.UNSET)) + categories } else { categories } diff --git a/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/CategoryManga.kt b/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/CategoryManga.kt index 8316dcc7..b571c53d 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/CategoryManga.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/CategoryManga.kt @@ -116,4 +116,20 @@ object CategoryManga { } } } + + fun getMangasCategories(mangaIDs: List): Map> { + return buildMap { + transaction { + CategoryMangaTable.innerJoin(CategoryTable) + .select { CategoryMangaTable.manga inList mangaIDs } + .groupBy { it[CategoryMangaTable.manga] } + .forEach { + val mangaId = it.key.value + val categories = it.value + + set(mangaId, categories.map { category -> CategoryTable.toDataClass(category) }) + } + } + } + } } diff --git a/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/update/Updater.kt b/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/update/Updater.kt index 5cef31ab..446011d7 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/update/Updater.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/update/Updater.kt @@ -76,7 +76,7 @@ class Updater : IUpdater { override fun addMangasToQueue(mangas: List) { mangas.forEach { tracker[it.id] = UpdateJob(it) } - _status.update { UpdateStatus(tracker.values.toList(), true) } + _status.update { UpdateStatus(tracker.values.toList(), mangas.isNotEmpty()) } mangas.forEach { addMangaToQueue(it) } } diff --git a/server/src/main/kotlin/suwayomi/tachidesk/manga/model/dataclass/CategoryDataClass.kt b/server/src/main/kotlin/suwayomi/tachidesk/manga/model/dataclass/CategoryDataClass.kt index 70389d48..641aeea1 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/manga/model/dataclass/CategoryDataClass.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/manga/model/dataclass/CategoryDataClass.kt @@ -1,5 +1,7 @@ package suwayomi.tachidesk.manga.model.dataclass +import com.fasterxml.jackson.annotation.JsonValue + /* * Copyright (C) Contributors to the Suwayomi project * @@ -7,11 +9,20 @@ package suwayomi.tachidesk.manga.model.dataclass * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ +enum class IncludeInUpdate(@JsonValue val value: Int) { + EXCLUDE(0), INCLUDE(1), UNSET(-1); + + companion object { + fun fromValue(value: Int) = IncludeInUpdate.values().find { it.value == value } ?: UNSET + } +} + data class CategoryDataClass( val id: Int, val order: Int, val name: String, val default: Boolean, val size: Int, + val includeInUpdate: IncludeInUpdate, val meta: Map = emptyMap() ) diff --git a/server/src/main/kotlin/suwayomi/tachidesk/manga/model/table/CategoryTable.kt b/server/src/main/kotlin/suwayomi/tachidesk/manga/model/table/CategoryTable.kt index 32bef6b6..47e86a39 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/manga/model/table/CategoryTable.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/manga/model/table/CategoryTable.kt @@ -11,11 +11,13 @@ import org.jetbrains.exposed.dao.id.IntIdTable import org.jetbrains.exposed.sql.ResultRow import suwayomi.tachidesk.manga.impl.Category import suwayomi.tachidesk.manga.model.dataclass.CategoryDataClass +import suwayomi.tachidesk.manga.model.dataclass.IncludeInUpdate object CategoryTable : IntIdTable() { val name = varchar("name", 64) val order = integer("order").default(0) val isDefault = bool("is_default").default(false) + val includeInUpdate = integer("include_in_update").default(IncludeInUpdate.UNSET.value) } fun CategoryTable.toDataClass(categoryEntry: ResultRow) = CategoryDataClass( @@ -24,5 +26,6 @@ fun CategoryTable.toDataClass(categoryEntry: ResultRow) = CategoryDataClass( categoryEntry[name], categoryEntry[isDefault], Category.getCategorySize(categoryEntry[id].value), + IncludeInUpdate.fromValue(categoryEntry[includeInUpdate]), Category.getCategoryMetaMap(categoryEntry[id].value) ) diff --git a/server/src/main/kotlin/suwayomi/tachidesk/server/database/migration/M0026_CategoryIncludeInUpdate.kt b/server/src/main/kotlin/suwayomi/tachidesk/server/database/migration/M0026_CategoryIncludeInUpdate.kt new file mode 100644 index 00000000..15f92ef2 --- /dev/null +++ b/server/src/main/kotlin/suwayomi/tachidesk/server/database/migration/M0026_CategoryIncludeInUpdate.kt @@ -0,0 +1,19 @@ +package suwayomi.tachidesk.server.database.migration + +/* + * Copyright (C) Contributors to the Suwayomi project + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ + +import de.neonew.exposed.migrations.helpers.AddColumnMigration +import suwayomi.tachidesk.manga.model.dataclass.IncludeInUpdate + +@Suppress("ClassName", "unused") +class M0026_CategoryIncludeInUpdate : AddColumnMigration( + "Category", + "include_in_update", + "INT", + IncludeInUpdate.UNSET.value.toString() +)