mirror of
https://github.com/Suwayomi/Suwayomi-Server.git
synced 2026-07-04 03:14:40 -05:00
Add logic to only update specific categories (#520)
Makes it possible to only update specific categories. In case a manga is in an excluded category it will be excluded even if it is also in an included category.
This commit is contained in:
@@ -61,14 +61,15 @@ object CategoryController {
|
|||||||
pathParam<Int>("categoryId"),
|
pathParam<Int>("categoryId"),
|
||||||
formParam<String?>("name"),
|
formParam<String?>("name"),
|
||||||
formParam<Boolean?>("default"),
|
formParam<Boolean?>("default"),
|
||||||
|
formParam<Int?>("includeInUpdate"),
|
||||||
documentWith = {
|
documentWith = {
|
||||||
withOperation {
|
withOperation {
|
||||||
summary("Category modify")
|
summary("Category modify")
|
||||||
description("Modify a category")
|
description("Modify a category")
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
behaviorOf = { ctx, categoryId, name, isDefault ->
|
behaviorOf = { ctx, categoryId, name, isDefault, includeInUpdate ->
|
||||||
Category.updateCategory(categoryId, name, isDefault)
|
Category.updateCategory(categoryId, name, isDefault, includeInUpdate)
|
||||||
ctx.status(200)
|
ctx.status(200)
|
||||||
},
|
},
|
||||||
withResults = {
|
withResults = {
|
||||||
|
|||||||
@@ -13,10 +13,7 @@ import suwayomi.tachidesk.manga.impl.Chapter
|
|||||||
import suwayomi.tachidesk.manga.impl.update.IUpdater
|
import suwayomi.tachidesk.manga.impl.update.IUpdater
|
||||||
import suwayomi.tachidesk.manga.impl.update.UpdateStatus
|
import suwayomi.tachidesk.manga.impl.update.UpdateStatus
|
||||||
import suwayomi.tachidesk.manga.impl.update.UpdaterSocket
|
import suwayomi.tachidesk.manga.impl.update.UpdaterSocket
|
||||||
import suwayomi.tachidesk.manga.model.dataclass.CategoryDataClass
|
import suwayomi.tachidesk.manga.model.dataclass.*
|
||||||
import suwayomi.tachidesk.manga.model.dataclass.MangaChapterDataClass
|
|
||||||
import suwayomi.tachidesk.manga.model.dataclass.MangaDataClass
|
|
||||||
import suwayomi.tachidesk.manga.model.dataclass.PaginatedList
|
|
||||||
import suwayomi.tachidesk.server.JavalinSetup.future
|
import suwayomi.tachidesk.server.JavalinSetup.future
|
||||||
import suwayomi.tachidesk.server.util.formParam
|
import suwayomi.tachidesk.server.util.formParam
|
||||||
import suwayomi.tachidesk.server.util.handler
|
import suwayomi.tachidesk.server.util.handler
|
||||||
@@ -94,11 +91,21 @@ object UpdateController {
|
|||||||
updater.reset()
|
updater.reset()
|
||||||
}
|
}
|
||||||
|
|
||||||
val mangasToUpdate = categories
|
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) }
|
.flatMap { CategoryManga.getCategoryMangaList(it.id) }
|
||||||
.distinctBy { it.id }
|
.distinctBy { it.id }
|
||||||
.sortedWith(compareBy(String.CASE_INSENSITIVE_ORDER, MangaDataClass::title))
|
val mangasToCategoriesMap = CategoryManga.getMangasCategories(categoriesToUpdateMangas.map { it.id })
|
||||||
|
val mangasToUpdate = categoriesToUpdateMangas
|
||||||
.filter { it.updateStrategy == UpdateStrategy.ALWAYS_UPDATE }
|
.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
|
// 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()) {
|
if (mangasToUpdate.isEmpty()) {
|
||||||
@@ -106,7 +113,10 @@ object UpdateController {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
updater.addMangasToQueue(mangasToUpdate)
|
updater.addMangasToQueue(
|
||||||
|
mangasToUpdate
|
||||||
|
.sortedWith(compareBy(String.CASE_INSENSITIVE_ORDER, MangaDataClass::title)),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun categoryUpdateWS(ws: WsConfig) {
|
fun categoryUpdateWS(ws: WsConfig) {
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ import org.jetbrains.exposed.sql.transactions.transaction
|
|||||||
import org.jetbrains.exposed.sql.update
|
import org.jetbrains.exposed.sql.update
|
||||||
import suwayomi.tachidesk.manga.impl.CategoryManga.removeMangaFromCategory
|
import suwayomi.tachidesk.manga.impl.CategoryManga.removeMangaFromCategory
|
||||||
import suwayomi.tachidesk.manga.model.dataclass.CategoryDataClass
|
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.CategoryMangaTable
|
||||||
import suwayomi.tachidesk.manga.model.table.CategoryMetaTable
|
import suwayomi.tachidesk.manga.model.table.CategoryMetaTable
|
||||||
import suwayomi.tachidesk.manga.model.table.CategoryTable
|
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 {
|
transaction {
|
||||||
CategoryTable.update({ CategoryTable.id eq categoryId }) {
|
CategoryTable.update({ CategoryTable.id eq categoryId }) {
|
||||||
if (name != null && !name.equals(DEFAULT_CATEGORY_NAME, ignoreCase = true)) it[CategoryTable.name] = name
|
if (name != null && !name.equals(DEFAULT_CATEGORY_NAME, ignoreCase = true)) it[CategoryTable.name] = name
|
||||||
if (isDefault != null) it[CategoryTable.isDefault] = isDefault
|
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<CategoryDataClass>): List<CategoryDataClass> {
|
private fun addDefaultIfNecessary(categories: List<CategoryDataClass>): List<CategoryDataClass> {
|
||||||
val defaultCategorySize = MangaTable.select { (MangaTable.inLibrary eq true) and (MangaTable.defaultCategory eq true) }.count().toInt()
|
val defaultCategorySize = MangaTable.select { (MangaTable.inLibrary eq true) and (MangaTable.defaultCategory eq true) }.count().toInt()
|
||||||
return if (defaultCategorySize > 0) {
|
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 {
|
} else {
|
||||||
categories
|
categories
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -116,4 +116,20 @@ object CategoryManga {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun getMangasCategories(mangaIDs: List<Int>): Map<Int, List<CategoryDataClass>> {
|
||||||
|
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) })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -76,7 +76,7 @@ class Updater : IUpdater {
|
|||||||
|
|
||||||
override fun addMangasToQueue(mangas: List<MangaDataClass>) {
|
override fun addMangasToQueue(mangas: List<MangaDataClass>) {
|
||||||
mangas.forEach { tracker[it.id] = UpdateJob(it) }
|
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) }
|
mangas.forEach { addMangaToQueue(it) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
package suwayomi.tachidesk.manga.model.dataclass
|
package suwayomi.tachidesk.manga.model.dataclass
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonValue
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright (C) Contributors to the Suwayomi project
|
* 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
|
* 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/. */
|
* 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(
|
data class CategoryDataClass(
|
||||||
val id: Int,
|
val id: Int,
|
||||||
val order: Int,
|
val order: Int,
|
||||||
val name: String,
|
val name: String,
|
||||||
val default: Boolean,
|
val default: Boolean,
|
||||||
val size: Int,
|
val size: Int,
|
||||||
|
val includeInUpdate: IncludeInUpdate,
|
||||||
val meta: Map<String, String> = emptyMap()
|
val meta: Map<String, String> = emptyMap()
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -11,11 +11,13 @@ import org.jetbrains.exposed.dao.id.IntIdTable
|
|||||||
import org.jetbrains.exposed.sql.ResultRow
|
import org.jetbrains.exposed.sql.ResultRow
|
||||||
import suwayomi.tachidesk.manga.impl.Category
|
import suwayomi.tachidesk.manga.impl.Category
|
||||||
import suwayomi.tachidesk.manga.model.dataclass.CategoryDataClass
|
import suwayomi.tachidesk.manga.model.dataclass.CategoryDataClass
|
||||||
|
import suwayomi.tachidesk.manga.model.dataclass.IncludeInUpdate
|
||||||
|
|
||||||
object CategoryTable : IntIdTable() {
|
object CategoryTable : IntIdTable() {
|
||||||
val name = varchar("name", 64)
|
val name = varchar("name", 64)
|
||||||
val order = integer("order").default(0)
|
val order = integer("order").default(0)
|
||||||
val isDefault = bool("is_default").default(false)
|
val isDefault = bool("is_default").default(false)
|
||||||
|
val includeInUpdate = integer("include_in_update").default(IncludeInUpdate.UNSET.value)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun CategoryTable.toDataClass(categoryEntry: ResultRow) = CategoryDataClass(
|
fun CategoryTable.toDataClass(categoryEntry: ResultRow) = CategoryDataClass(
|
||||||
@@ -24,5 +26,6 @@ fun CategoryTable.toDataClass(categoryEntry: ResultRow) = CategoryDataClass(
|
|||||||
categoryEntry[name],
|
categoryEntry[name],
|
||||||
categoryEntry[isDefault],
|
categoryEntry[isDefault],
|
||||||
Category.getCategorySize(categoryEntry[id].value),
|
Category.getCategorySize(categoryEntry[id].value),
|
||||||
|
IncludeInUpdate.fromValue(categoryEntry[includeInUpdate]),
|
||||||
Category.getCategoryMetaMap(categoryEntry[id].value)
|
Category.getCategoryMetaMap(categoryEntry[id].value)
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -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()
|
||||||
|
)
|
||||||
Reference in New Issue
Block a user