From cdc21b067c1a341d68ea7a9c1ee565dc3959f552 Mon Sep 17 00:00:00 2001 From: schroda <50052685+schroda@users.noreply.github.com> Date: Sun, 7 Apr 2024 04:53:49 +0200 Subject: [PATCH] Fix/recognition of already downloaded chapters (#922) * Remove overrides of "ChapterFilesProvider::downloadImpl" * Check final download folder for existing page on download Downloads were changed to get downloaded to the system temp folder instead to directly into the final download folder. This broke the check for existing pages, because now only the temp folder was checked instead of both the temp and the final download folder. Regression introduced with 1c9a139006f7a9e399c964b2a88650fb757d8369 * Properly check for already existing downloaded pages The previous check was always false because the file ending of the page file is unknown and thus, missing from the created file path * Cleanup cache download folder --- .../fileProvider/ChaptersFilesProvider.kt | 39 +++++++++++++++---- .../fileProvider/impl/ArchiveProvider.kt | 26 ++++++------- .../fileProvider/impl/FolderProvider.kt | 19 +++------ 3 files changed, 50 insertions(+), 34 deletions(-) diff --git a/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/download/fileProvider/ChaptersFilesProvider.kt b/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/download/fileProvider/ChaptersFilesProvider.kt index 4b971240..20d48d35 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/download/fileProvider/ChaptersFilesProvider.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/download/fileProvider/ChaptersFilesProvider.kt @@ -13,6 +13,8 @@ import suwayomi.tachidesk.manga.impl.Page import suwayomi.tachidesk.manga.impl.download.model.DownloadChapter import suwayomi.tachidesk.manga.impl.util.createComicInfoFile import suwayomi.tachidesk.manga.impl.util.getChapterCachePath +import suwayomi.tachidesk.manga.impl.util.getChapterDownloadPath +import suwayomi.tachidesk.manga.impl.util.storage.ImageResponse import suwayomi.tachidesk.manga.model.table.ChapterTable import suwayomi.tachidesk.manga.model.table.MangaTable import java.io.File @@ -28,21 +30,40 @@ abstract class ChaptersFilesProvider(val mangaId: Int, val chapterId: Int) : Dow return RetrieveFile1Args(::getImageImpl) } + /** + * Extract the existing download to the base download folder (see [getChapterDownloadPath]) + */ + protected abstract fun extractExistingDownload() + + protected abstract suspend fun handleSuccessfulDownload() + @OptIn(FlowPreview::class) - open suspend fun downloadImpl( + private suspend fun downloadImpl( download: DownloadChapter, scope: CoroutineScope, step: suspend (DownloadChapter?, Boolean) -> Unit, ): Boolean { - val pageCount = download.chapter.pageCount - val chapterDir = getChapterCachePath(mangaId, chapterId) - val folder = File(chapterDir) - folder.mkdirs() + extractExistingDownload() + val finalDownloadFolder = getChapterDownloadPath(mangaId, chapterId) + + val cacheChapterDir = getChapterCachePath(mangaId, chapterId) + val downloadCacheFolder = File(cacheChapterDir) + downloadCacheFolder.mkdirs() + + val pageCount = download.chapter.pageCount for (pageNum in 0 until pageCount) { var pageProgressJob: Job? = null val fileName = Page.getPageName(pageNum) // might have to change this to index stored in database - if (File(folder, fileName).exists()) continue + + val pageExistsInFinalDownloadFolder = ImageResponse.findFileNameStartingWith(finalDownloadFolder, fileName) != null + val pageExistsInCacheDownloadFolder = ImageResponse.findFileNameStartingWith(cacheChapterDir, fileName) != null + + val doesPageAlreadyExist = pageExistsInFinalDownloadFolder || pageExistsInCacheDownloadFolder + if (doesPageAlreadyExist) { + continue + } + try { Page.getPageImage( mangaId = download.mangaId, @@ -69,7 +90,7 @@ abstract class ChaptersFilesProvider(val mangaId: Int, val chapterId: Int) : Dow } createComicInfoFile( - folder.toPath(), + downloadCacheFolder.toPath(), transaction { MangaTable.select { MangaTable.id eq mangaId }.first() }, @@ -78,6 +99,10 @@ abstract class ChaptersFilesProvider(val mangaId: Int, val chapterId: Int) : Dow }, ) + handleSuccessfulDownload() + + File(cacheChapterDir).deleteRecursively() + return true } diff --git a/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/download/fileProvider/impl/ArchiveProvider.kt b/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/download/fileProvider/impl/ArchiveProvider.kt index 80f0bc1d..4d7886ff 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/download/fileProvider/impl/ArchiveProvider.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/download/fileProvider/impl/ArchiveProvider.kt @@ -1,6 +1,5 @@ package suwayomi.tachidesk.manga.impl.download.fileProvider.impl -import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext import org.apache.commons.compress.archivers.zip.ZipArchiveEntry @@ -11,7 +10,6 @@ import org.kodein.di.DI import org.kodein.di.conf.global import org.kodein.di.instance import suwayomi.tachidesk.manga.impl.download.fileProvider.ChaptersFilesProvider -import suwayomi.tachidesk.manga.impl.download.model.DownloadChapter import suwayomi.tachidesk.manga.impl.util.getChapterCachePath import suwayomi.tachidesk.manga.impl.util.getChapterCbzPath import suwayomi.tachidesk.manga.impl.util.getMangaDownloadDir @@ -32,17 +30,21 @@ class ArchiveProvider(mangaId: Int, chapterId: Int) : ChaptersFilesProvider(mang return Pair(inputStream.buffered(), "image/$fileType") } - override suspend fun downloadImpl( - download: DownloadChapter, - scope: CoroutineScope, - step: suspend (DownloadChapter?, Boolean) -> Unit, - ): Boolean { + override fun extractExistingDownload() { + val outputFile = File(getChapterCbzPath(mangaId, chapterId)) + val chapterCacheFolder = File(getChapterCachePath(mangaId, chapterId)) + + if (!outputFile.exists()) { + return + } + + extractCbzFile(outputFile, chapterCacheFolder) + } + + override suspend fun handleSuccessfulDownload() { val mangaDownloadFolder = File(getMangaDownloadDir(mangaId)) val outputFile = File(getChapterCbzPath(mangaId, chapterId)) val chapterCacheFolder = File(getChapterCachePath(mangaId, chapterId)) - if (outputFile.exists()) handleExistingCbzFile(outputFile, chapterCacheFolder) - - super.downloadImpl(download, scope, step) withContext(Dispatchers.IO) { mangaDownloadFolder.mkdirs() @@ -68,8 +70,6 @@ class ArchiveProvider(mangaId: Int, chapterId: Int) : ChaptersFilesProvider(mang if (chapterCacheFolder.exists() && chapterCacheFolder.isDirectory) { chapterCacheFolder.deleteRecursively() } - - return true } override fun delete(): Boolean { @@ -83,7 +83,7 @@ class ArchiveProvider(mangaId: Int, chapterId: Int) : ChaptersFilesProvider(mang return cbzDeleted } - private fun handleExistingCbzFile( + private fun extractCbzFile( cbzFile: File, chapterFolder: File, ) { diff --git a/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/download/fileProvider/impl/FolderProvider.kt b/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/download/fileProvider/impl/FolderProvider.kt index 9c3b0b26..3b5766dc 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/download/fileProvider/impl/FolderProvider.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/download/fileProvider/impl/FolderProvider.kt @@ -1,11 +1,9 @@ package suwayomi.tachidesk.manga.impl.download.fileProvider.impl -import kotlinx.coroutines.CoroutineScope import org.kodein.di.DI import org.kodein.di.conf.global import org.kodein.di.instance import suwayomi.tachidesk.manga.impl.download.fileProvider.ChaptersFilesProvider -import suwayomi.tachidesk.manga.impl.download.model.DownloadChapter import suwayomi.tachidesk.manga.impl.util.getChapterCachePath import suwayomi.tachidesk.manga.impl.util.getChapterDownloadPath import suwayomi.tachidesk.manga.impl.util.storage.FileDeletionHelper @@ -29,23 +27,16 @@ class FolderProvider(mangaId: Int, chapterId: Int) : ChaptersFilesProvider(manga return Pair(FileInputStream(file).buffered(), "image/$fileType") } - override suspend fun downloadImpl( - download: DownloadChapter, - scope: CoroutineScope, - step: suspend (DownloadChapter?, Boolean) -> Unit, - ): Boolean { + override fun extractExistingDownload() { + // nothing to do + } + + override suspend fun handleSuccessfulDownload() { val chapterDir = getChapterDownloadPath(mangaId, chapterId) val folder = File(chapterDir) - val downloadSucceeded = super.downloadImpl(download, scope, step) - if (!downloadSucceeded) { - return false - } - val cacheChapterDir = getChapterCachePath(mangaId, chapterId) File(cacheChapterDir).copyRecursively(folder, true) - - return true } override fun delete(): Boolean {