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 46b34c42..162333df 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/manga/controller/MangaController.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/manga/controller/MangaController.kt @@ -13,6 +13,7 @@ import suwayomi.tachidesk.manga.impl.Chapter import suwayomi.tachidesk.manga.impl.Library import suwayomi.tachidesk.manga.impl.Manga import suwayomi.tachidesk.manga.impl.Page +import suwayomi.tachidesk.manga.impl.chapter.getChapterDownloadReady import suwayomi.tachidesk.manga.model.dataclass.MangaDataClass import suwayomi.tachidesk.server.JavalinSetup.future import suwayomi.tachidesk.server.util.handler @@ -124,7 +125,7 @@ object MangaController { fun chapterRetrieve(ctx: Context) { val chapterIndex = ctx.pathParam("chapterIndex").toInt() val mangaId = ctx.pathParam("mangaId").toInt() - ctx.future(future { Chapter.getChapter(chapterIndex, mangaId) }) + ctx.future(future { getChapterDownloadReady(chapterIndex, mangaId) }) } /** used to modify a chapter's parameters */ 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 91a8f758..f48dedae 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/Chapter.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/Chapter.kt @@ -7,7 +7,6 @@ package suwayomi.tachidesk.manga.impl * 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 eu.kanade.tachiyomi.source.model.SChapter import eu.kanade.tachiyomi.source.model.SManga import eu.kanade.tachiyomi.source.online.HttpSource import eu.kanade.tachiyomi.util.chapter.ChapterRecognition @@ -20,11 +19,9 @@ 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.Page.getPageName import suwayomi.tachidesk.manga.impl.util.getChapterDir import suwayomi.tachidesk.manga.impl.util.lang.awaitSingle import suwayomi.tachidesk.manga.impl.util.source.GetCatalogueSource.getCatalogueSourceOrStub -import suwayomi.tachidesk.manga.impl.util.storage.ImageResponse import suwayomi.tachidesk.manga.model.dataclass.ChapterDataClass import suwayomi.tachidesk.manga.model.dataclass.MangaChapterDataClass import suwayomi.tachidesk.manga.model.dataclass.PaginatedList @@ -154,102 +151,6 @@ object Chapter { } } - /** used to display a chapter, get a chapter in order to show it's pages */ - suspend fun getChapter(chapterIndex: Int, mangaId: Int): ChapterDataClass { - val chapterEntry = transaction { - ChapterTable.select { - (ChapterTable.sourceOrder eq chapterIndex) and (ChapterTable.manga eq mangaId) - }.first() - } - - val isPartiallyDownloaded = - !(chapterEntry[ChapterTable.isDownloaded] && firstPageExists(mangaId, chapterEntry[ChapterTable.id].value)) - - return if (isPartiallyDownloaded) { - - // chapter files may have been deleted - transaction { - ChapterTable.update({ (ChapterTable.sourceOrder eq chapterIndex) and (ChapterTable.manga eq mangaId) }) { - it[isDownloaded] = false - } - } - - val mangaEntry = transaction { MangaTable.select { MangaTable.id eq mangaId }.first() } - val source = getCatalogueSourceOrStub(mangaEntry[MangaTable.sourceReference]) - - val pageList = source.fetchPageList( - SChapter.create().apply { - url = chapterEntry[ChapterTable.url] - name = chapterEntry[ChapterTable.name] - } - ).awaitSingle() - - val chapterId = chapterEntry[ChapterTable.id].value - val chapterCount = transaction { ChapterTable.select { ChapterTable.manga eq mangaId }.count() } - - // update page list for this chapter - transaction { - pageList.forEach { page -> - val pageEntry = transaction { - PageTable.select { (PageTable.chapter eq chapterId) and (PageTable.index eq page.index) } - .firstOrNull() - } - if (pageEntry == null) { - PageTable.insert { - it[index] = page.index - it[url] = page.url - it[imageUrl] = page.imageUrl - it[chapter] = chapterId - } - } else { - PageTable.update({ (PageTable.chapter eq chapterId) and (PageTable.index eq page.index) }) { - it[url] = page.url - it[imageUrl] = page.imageUrl - } - } - } - } - - val pageCount = pageList.count() - - transaction { - ChapterTable.update({ (ChapterTable.manga eq mangaId) and (ChapterTable.sourceOrder eq chapterIndex) }) { - it[ChapterTable.pageCount] = pageCount - } - } - return ChapterDataClass( - chapterEntry[ChapterTable.url], - chapterEntry[ChapterTable.name], - chapterEntry[ChapterTable.date_upload], - chapterEntry[ChapterTable.chapter_number], - chapterEntry[ChapterTable.scanlator], - mangaId, - chapterEntry[ChapterTable.isRead], - chapterEntry[ChapterTable.isBookmarked], - chapterEntry[ChapterTable.lastPageRead], - chapterEntry[ChapterTable.lastReadAt], - - chapterEntry[ChapterTable.sourceOrder], - chapterEntry[ChapterTable.fetchedAt], - chapterEntry[ChapterTable.isDownloaded], - pageCount, - chapterCount.toInt(), - getChapterMetaMap(chapterEntry[ChapterTable.id]) - ) - } else { - ChapterTable.toDataClass(chapterEntry) - } - } - - private fun firstPageExists(mangaId: Int, chapterId: Int): Boolean { - val chapterDir = getChapterDir(mangaId, chapterId) - - return ImageResponse.findFileNameStartingWith( - chapterDir, - getPageName(1) - ) != null - } - fun modifyChapter( mangaId: Int, chapterIndex: Int, diff --git a/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/chapter/ChapterForDownload.kt b/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/chapter/ChapterForDownload.kt new file mode 100644 index 00000000..151f30d4 --- /dev/null +++ b/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/chapter/ChapterForDownload.kt @@ -0,0 +1,148 @@ +package suwayomi.tachidesk.manga.impl.chapter + +/* + * 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 eu.kanade.tachiyomi.source.model.Page +import eu.kanade.tachiyomi.source.model.SChapter +import org.jetbrains.exposed.sql.ResultRow +import org.jetbrains.exposed.sql.and +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.Page.getPageName +import suwayomi.tachidesk.manga.impl.util.getChapterDir +import suwayomi.tachidesk.manga.impl.util.lang.awaitSingle +import suwayomi.tachidesk.manga.impl.util.source.GetCatalogueSource.getCatalogueSourceOrStub +import suwayomi.tachidesk.manga.impl.util.storage.ImageResponse +import suwayomi.tachidesk.manga.model.dataclass.ChapterDataClass +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 + + +suspend fun getChapterDownloadReady(chapterIndex: Int, mangaId: Int): ChapterDataClass { + val chapter = ChapterForDownload(chapterIndex, mangaId) + + return chapter.asDownloadReady() +} + +private class ChapterForDownload( + private val chapterIndex: Int, + private val mangaId: Int +) { + suspend fun asDownloadReady(): ChapterDataClass { + + if (isNotCompletelyDownloaded()) { + markAsNotDownloaded() + + val pageList = fetchPageList() + + updateDatabasePages(pageList) + } + + return asDataClass() + } + + private fun asDataClass() = ChapterTable.toDataClass(chapterEntry) + + var chapterEntry: ResultRow = freshChapterEntry() + + private fun freshChapterEntry() = transaction { + ChapterTable.select { + (ChapterTable.sourceOrder eq chapterIndex) and (ChapterTable.manga eq mangaId) + }.first() + } + + private suspend fun fetchPageList(): List { + val mangaEntry = transaction { MangaTable.select { MangaTable.id eq mangaId }.first() } + val source = getCatalogueSourceOrStub(mangaEntry[MangaTable.sourceReference]) + + return source.fetchPageList( + SChapter.create().apply { + url = chapterEntry[ChapterTable.url] + name = chapterEntry[ChapterTable.name] + } + ).awaitSingle() + } + + private fun markAsNotDownloaded() { + // chapter may be downloaded but if we are here, then images might be deleted and database data be false + transaction { + ChapterTable.update({ (ChapterTable.sourceOrder eq chapterIndex) and (ChapterTable.manga eq mangaId) }) { + it[isDownloaded] = false + } + } + } + + private fun updateDatabasePages(pageList: List) { + val chapterId = chapterEntry[ChapterTable.id].value + val chapterIndex = chapterEntry[ChapterTable.sourceOrder] + val mangaId = chapterEntry[ChapterTable.manga].value + + transaction { + pageList.forEach { page -> + val pageEntry = transaction { + PageTable.select { (PageTable.chapter eq chapterId) and (PageTable.index eq page.index) } + .firstOrNull() + } + if (pageEntry == null) { + PageTable.insert { + it[index] = page.index + it[url] = page.url + it[imageUrl] = page.imageUrl + it[chapter] = chapterId + } + } else { + PageTable.update({ (PageTable.chapter eq chapterId) and (PageTable.index eq page.index) }) { + it[url] = page.url + it[imageUrl] = page.imageUrl + } + } + } + } + + updatePageCount(pageList, mangaId, chapterIndex) + + // chapter was updated + chapterEntry = freshChapterEntry() + } + + private fun updatePageCount( + pageList: List, + mangaId: Int, + chapterIndex: Int + ) { + val pageCount = pageList.count() + + transaction { + ChapterTable.update({ (ChapterTable.manga eq mangaId) and (ChapterTable.sourceOrder eq chapterIndex) }) { + it[ChapterTable.pageCount] = pageCount + } + } + } + + private fun isNotCompletelyDownloaded(): Boolean { + return !(chapterEntry[ChapterTable.isDownloaded] && firstPageExists()) + } + + private fun firstPageExists(): Boolean { + val chapterId = chapterEntry[ChapterTable.id].value + + val chapterDir = getChapterDir(mangaId, chapterId) + + println(chapterDir) + println(getPageName(0)) + + return ImageResponse.findFileNameStartingWith( + chapterDir, + getPageName(0) + ) != null + } +} diff --git a/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/download/Downloader.kt b/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/download/Downloader.kt index 35b1325e..9c23b0a2 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/download/Downloader.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/download/Downloader.kt @@ -12,8 +12,8 @@ import mu.KotlinLogging import org.jetbrains.exposed.sql.and import org.jetbrains.exposed.sql.transactions.transaction import org.jetbrains.exposed.sql.update -import suwayomi.tachidesk.manga.impl.Chapter.getChapter import suwayomi.tachidesk.manga.impl.Page.getPageImage +import suwayomi.tachidesk.manga.impl.chapter.getChapterDownloadReady import suwayomi.tachidesk.manga.impl.download.model.DownloadChapter import suwayomi.tachidesk.manga.impl.download.model.DownloadState.Downloading import suwayomi.tachidesk.manga.impl.download.model.DownloadState.Error @@ -47,7 +47,7 @@ class Downloader(private val downloadQueue: CopyOnWriteArrayList