From 39490ce7bac3a2452e302b75b21035f2ba5dbd0f Mon Sep 17 00:00:00 2001 From: Valter Martinek Date: Wed, 9 Nov 2022 18:13:29 +0100 Subject: [PATCH] Add batch chapter update endpoint (#442) --- .../suwayomi/tachidesk/manga/MangaAPI.kt | 1 + .../manga/controller/MangaController.kt | 26 +++++++++ .../suwayomi/tachidesk/manga/impl/Chapter.kt | 54 ++++++++++++++++--- 3 files changed, 75 insertions(+), 6 deletions(-) diff --git a/server/src/main/kotlin/suwayomi/tachidesk/manga/MangaAPI.kt b/server/src/main/kotlin/suwayomi/tachidesk/manga/MangaAPI.kt index 2ee57ddf..ce7db417 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/manga/MangaAPI.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/manga/MangaAPI.kt @@ -66,6 +66,7 @@ object MangaAPI { patch("{mangaId}/meta", MangaController.meta) get("{mangaId}/chapters", MangaController.chapterList) + post("{mangaId}/chapter/batch", MangaController.chapterBatch) get("{mangaId}/chapter/{chapterIndex}", MangaController.chapterRetrieve) patch("{mangaId}/chapter/{chapterIndex}", MangaController.chapterModify) delete("{mangaId}/chapter/{chapterIndex}", MangaController.chapterDelete) diff --git a/server/src/main/kotlin/suwayomi/tachidesk/manga/controller/MangaController.kt b/server/src/main/kotlin/suwayomi/tachidesk/manga/controller/MangaController.kt index 0baced05..9c02fffb 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/manga/controller/MangaController.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/manga/controller/MangaController.kt @@ -8,6 +8,11 @@ package suwayomi.tachidesk.manga.controller * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ import io.javalin.http.HttpCode +import kotlinx.serialization.decodeFromString +import kotlinx.serialization.json.Json +import org.kodein.di.DI +import org.kodein.di.conf.global +import org.kodein.di.instance import suwayomi.tachidesk.manga.impl.CategoryManga import suwayomi.tachidesk.manga.impl.Chapter import suwayomi.tachidesk.manga.impl.Library @@ -26,6 +31,8 @@ import suwayomi.tachidesk.server.util.withOperation import kotlin.time.Duration.Companion.days object MangaController { + private val json by DI.global.instance() + /** get manga info */ val retrieve = handler( pathParam("mangaId"), @@ -211,6 +218,25 @@ object MangaController { } ) + /** batch edit chapters */ + val chapterBatch = handler( + pathParam("mangaId"), + documentWith = { + withOperation { + summary("Chapters update multiple") + description("Update multiple chapters. For batch marking as read, or bookmarking") + } + body() + }, + behaviorOf = { ctx, mangaId -> + val input = json.decodeFromString(ctx.body()) + Chapter.modifyChapters(input, mangaId) + }, + withResults = { + httpCode(HttpCode.OK) + } + ) + /** used to display a chapter, get a chapter in order to show its pages */ val chapterRetrieve = handler( pathParam("mangaId"), diff --git a/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/Chapter.kt b/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/Chapter.kt index c9cb89b9..10dd1d5d 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/Chapter.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/Chapter.kt @@ -10,16 +10,12 @@ package suwayomi.tachidesk.manga.impl import eu.kanade.tachiyomi.source.model.SManga import eu.kanade.tachiyomi.source.online.HttpSource import eu.kanade.tachiyomi.util.chapter.ChapterRecognition +import kotlinx.serialization.Serializable import org.jetbrains.exposed.dao.id.EntityID -import org.jetbrains.exposed.sql.SortOrder +import org.jetbrains.exposed.sql.* import org.jetbrains.exposed.sql.SortOrder.ASC import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq -import org.jetbrains.exposed.sql.and -import org.jetbrains.exposed.sql.deleteWhere -import org.jetbrains.exposed.sql.insert -import org.jetbrains.exposed.sql.select import org.jetbrains.exposed.sql.transactions.transaction -import org.jetbrains.exposed.sql.update import suwayomi.tachidesk.manga.impl.Manga.getManga import suwayomi.tachidesk.manga.impl.util.getChapterDir import suwayomi.tachidesk.manga.impl.util.lang.awaitSingle @@ -198,6 +194,52 @@ object Chapter { } } + @Serializable + data class ChapterChange( + val isRead: Boolean? = null, + val isBookmarked: Boolean? = null, + val lastPageRead: Int? = null // this probably won't be very useful, but for completion's sake + ) + + @Serializable + data class ChapterBatchEditInput( + val chapterIds: List? = null, + val chapterIndexes: List? = null, + val change: ChapterChange? + ) + + fun modifyChapters(input: ChapterBatchEditInput, mangaId: Int) { + // Make sure change is defined + if (input.change == null) return + val (isRead, isBookmarked, lastPageRead) = input.change + if (isRead == null && isBookmarked == null) return + + // Make sure some filter is defined + val condition = when { + input.chapterIds != null -> + Op.build { (ChapterTable.manga eq mangaId) and (ChapterTable.id inList input.chapterIds) } + input.chapterIndexes != null -> + Op.build { (ChapterTable.manga eq mangaId) and (ChapterTable.sourceOrder inList input.chapterIndexes) } + else -> null + } ?: return + + transaction { + val now = Instant.now().epochSecond + ChapterTable.update({ condition }) { update -> + isRead?.also { + update[ChapterTable.isRead] = it + } + isBookmarked?.also { + update[ChapterTable.isBookmarked] = it + } + lastPageRead?.also { + update[ChapterTable.lastPageRead] = it + update[ChapterTable.lastReadAt] = now + } + } + } + } + fun getChaptersMetaMaps(chapterIds: List>): Map, Map> { return transaction { ChapterMetaTable.select { ChapterMetaTable.ref inList chapterIds }