Prevent duplicated chapter pages (#1353)

In case "ChapterForDownload#asDownloadReady" was called in quick succession, the page list got inserted twice.

This caused problems with getting the images from the rest endpoint, because they are selected by sorting them by asc index and selecting the page by using the provided index as an offset.

This, however, only works as long as there are no duplicates, otherwise, page indexes 1, 2; 3, 4; 5, 6; ... will just return the same page.
This commit is contained in:
schroda
2025-04-28 00:54:54 +02:00
committed by GitHub
parent 1cc2a05f90
commit 59d2151c92
2 changed files with 45 additions and 3 deletions

View File

@@ -11,6 +11,9 @@ import eu.kanade.tachiyomi.source.model.Page
import eu.kanade.tachiyomi.source.model.SChapter
import io.github.oshai.kotlinlogging.KLogger
import io.github.oshai.kotlinlogging.KotlinLogging
import io.github.reactivecircus.cache4k.Cache
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
import org.jetbrains.exposed.sql.ResultRow
import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq
import org.jetbrains.exposed.sql.and
@@ -26,6 +29,7 @@ import suwayomi.tachidesk.manga.model.table.ChapterTable
import suwayomi.tachidesk.manga.model.table.MangaTable
import suwayomi.tachidesk.manga.model.table.PageTable
import suwayomi.tachidesk.manga.model.table.toDataClass
import kotlin.time.Duration.Companion.minutes
suspend fun getChapterDownloadReady(
chapterId: Int? = null,
@@ -43,6 +47,12 @@ suspend fun getChapterDownloadReadyByIndex(
mangaId: Int,
): ChapterDataClass = getChapterDownloadReady(chapterIndex = chapterIndex, mangaId = mangaId)
private val mutexByChapterId: Cache<Int, Mutex> =
Cache
.Builder<Int, Mutex>()
.expireAfterAccess(10.minutes)
.build()
private class ChapterForDownload(
optChapterId: Int? = null,
optChapterIndex: Int? = null,
@@ -69,9 +79,7 @@ private class ChapterForDownload(
markAsNotDownloaded()
val pageList = fetchPageList()
updateDatabasePages(pageList)
updatePageList()
}
return asDataClass()
@@ -109,6 +117,14 @@ private class ChapterForDownload(
}.first()
}
private suspend fun updatePageList() {
val mutex = mutexByChapterId.get(chapterId) { Mutex() }
mutex.withLock {
val pageList = fetchPageList()
updateDatabasePages(pageList)
}
}
private suspend fun fetchPageList(): List<Page> {
val mangaEntry = transaction { MangaTable.selectAll().where { MangaTable.id eq mangaId }.first() }
val source = getCatalogueSourceOrStub(mangaEntry[MangaTable.sourceReference])

View File

@@ -0,0 +1,26 @@
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.SQLMigration
@Suppress("ClassName", "unused")
class M0045_PreventDuplicatedChapterPages : SQLMigration() {
override val sql: String =
"""
DELETE FROM PAGE
WHERE ID NOT IN (
SELECT MIN(ID)
FROM PAGE
GROUP BY INDEX, "imageUrl", CHAPTER
);
ALTER TABLE PAGE
ADD CONSTRAINT UC_PAGE UNIQUE (INDEX, imageUrl, CHAPTER)
""".trimIndent()
}