diff --git a/server/src/main/kotlin/suwayomi/server/database/migration/M0007_ChapterIsDownloaded.kt b/server/src/main/kotlin/suwayomi/server/database/migration/M0007_ChapterIsDownloaded.kt new file mode 100644 index 0000000..f20335b --- /dev/null +++ b/server/src/main/kotlin/suwayomi/server/database/migration/M0007_ChapterIsDownloaded.kt @@ -0,0 +1,24 @@ +package suwayomi.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 org.jetbrains.exposed.sql.transactions.TransactionManager +import org.jetbrains.exposed.sql.vendors.currentDialect +import suwayomi.server.database.migration.lib.Migration + +@Suppress("ClassName", "unused") +class M0007_ChapterIsDownloaded : Migration() { + /** this migration added IS_DOWNLOADED to CHAPTER */ + override fun run() { + with(TransactionManager.current()) { + exec("ALTER TABLE CHAPTER ADD COLUMN IS_DOWNLOADED BOOLEAN DEFAULT FALSE") + commit() + currentDialect.resetCaches() + } + } +} diff --git a/server/src/main/kotlin/suwayomi/server/database/migration/M0008_ChapterPageCount.kt b/server/src/main/kotlin/suwayomi/server/database/migration/M0008_ChapterPageCount.kt new file mode 100644 index 0000000..b51e98f --- /dev/null +++ b/server/src/main/kotlin/suwayomi/server/database/migration/M0008_ChapterPageCount.kt @@ -0,0 +1,24 @@ +package suwayomi.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 org.jetbrains.exposed.sql.transactions.TransactionManager +import org.jetbrains.exposed.sql.vendors.currentDialect +import suwayomi.server.database.migration.lib.Migration + +@Suppress("ClassName", "unused") +class M0008_ChapterPageCount : Migration() { + /** this migration added PAGE_COUNT to CHAPTER */ + override fun run() { + with(TransactionManager.current()) { + exec("ALTER TABLE CHAPTER ADD COLUMN PAGE_COUNT INT DEFAULT -1") + commit() + currentDialect.resetCaches() + } + } +} diff --git a/server/src/main/kotlin/suwayomi/server/database/migration/M0009_ChapterLastReadAt.kt b/server/src/main/kotlin/suwayomi/server/database/migration/M0009_ChapterLastReadAt.kt new file mode 100644 index 0000000..e84ee73 --- /dev/null +++ b/server/src/main/kotlin/suwayomi/server/database/migration/M0009_ChapterLastReadAt.kt @@ -0,0 +1,25 @@ +package suwayomi.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 org.jetbrains.exposed.sql.transactions.TransactionManager +import org.jetbrains.exposed.sql.vendors.currentDialect +import suwayomi.server.database.migration.lib.Migration + +@Suppress("ClassName", "unused") +class M0009_ChapterLastReadAt : Migration() { + /** this migration added PAGE_COUNT to CHAPTER */ + override fun run() { + with(TransactionManager.current()) { + // BIGINT == Long + exec("ALTER TABLE CHAPTER ADD COLUMN LAST_READ_AT BIGINT DEFAULT 0") + commit() + currentDialect.resetCaches() + } + } +} diff --git a/server/src/main/kotlin/suwayomi/tachidesk/impl/Chapter.kt b/server/src/main/kotlin/suwayomi/tachidesk/impl/Chapter.kt index 4682adc..d062377 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/impl/Chapter.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/impl/Chapter.kt @@ -24,6 +24,7 @@ import suwayomi.tachidesk.model.table.ChapterTable import suwayomi.tachidesk.model.table.MangaTable import suwayomi.tachidesk.model.table.PageTable import suwayomi.tachidesk.model.table.toDataClass +import java.time.Instant object Chapter { /** get chapter list when showing a manga */ @@ -122,9 +123,14 @@ object Chapter { dbChapter[ChapterTable.isRead], dbChapter[ChapterTable.isBookmarked], dbChapter[ChapterTable.lastPageRead], + dbChapter[ChapterTable.lastReadAt], chapterCount - index, - chapterList.size + dbChapter[ChapterTable.isDownloaded], + + dbChapter[ChapterTable.pageCount], + + chapterList.size, ) } } @@ -136,54 +142,68 @@ object Chapter { (ChapterTable.chapterIndex eq chapterIndex) and (ChapterTable.manga eq mangaId) }.first() } - val mangaEntry = transaction { MangaTable.select { MangaTable.id eq mangaId }.first() } - val source = getHttpSource(mangaEntry[MangaTable.sourceReference]) - val pageList = source.fetchPageList( - SChapter.create().apply { - url = chapterEntry[ChapterTable.url] - name = chapterEntry[ChapterTable.name] - } - ).awaitSingle() + return if (!chapterEntry[ChapterTable.isDownloaded]) { + val mangaEntry = transaction { MangaTable.select { MangaTable.id eq mangaId }.first() } + val source = getHttpSource(mangaEntry[MangaTable.sourceReference]) - val chapterId = chapterEntry[ChapterTable.id].value - val chapterCount = transaction { ChapterTable.select { ChapterTable.manga eq mangaId }.count() } + val pageList = source.fetchPageList( + SChapter.create().apply { + url = chapterEntry[ChapterTable.url] + name = chapterEntry[ChapterTable.name] + } + ).awaitSingle() - // 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 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.chapterIndex 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.chapterIndex], + chapterEntry[ChapterTable.isDownloaded], + pageCount, + chapterCount.toInt() + ) + } else { + ChapterTable.toDataClass(chapterEntry) } - - 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.chapterIndex], - chapterCount.toInt(), - pageList.count() - ) } fun modifyChapter(mangaId: Int, chapterIndex: Int, isRead: Boolean?, isBookmarked: Boolean?, markPrevRead: Boolean?, lastPageRead: Int?) { @@ -198,6 +218,7 @@ object Chapter { } lastPageRead?.also { update[ChapterTable.lastPageRead] = it + update[ChapterTable.lastReadAt] = Instant.now().epochSecond } } } diff --git a/server/src/main/kotlin/suwayomi/tachidesk/impl/download/Downloader.kt b/server/src/main/kotlin/suwayomi/tachidesk/impl/download/Downloader.kt index aed8cc7..89b0e6f 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/impl/download/Downloader.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/impl/download/Downloader.kt @@ -8,6 +8,9 @@ package suwayomi.tachidesk.impl.download * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ import kotlinx.coroutines.runBlocking +import org.jetbrains.exposed.sql.and +import org.jetbrains.exposed.sql.transactions.transaction +import org.jetbrains.exposed.sql.update import suwayomi.tachidesk.impl.Chapter.getChapter import suwayomi.tachidesk.impl.Page.getPageImage import suwayomi.tachidesk.impl.download.model.DownloadChapter @@ -15,6 +18,7 @@ import suwayomi.tachidesk.impl.download.model.DownloadState.Downloading import suwayomi.tachidesk.impl.download.model.DownloadState.Error import suwayomi.tachidesk.impl.download.model.DownloadState.Finished import suwayomi.tachidesk.impl.download.model.DownloadState.Queued +import suwayomi.tachidesk.model.table.ChapterTable import java.util.concurrent.CopyOnWriteArrayList class Downloader(private val downloadQueue: CopyOnWriteArrayList, val notifier: () -> Unit) : Thread() { @@ -49,6 +53,11 @@ class Downloader(private val downloadQueue: CopyOnWriteArrayList