finishing touches of download backend, done @jipfr's requests

This commit is contained in:
Aria Moradi 2021-05-29 23:57:22 +04:30
parent b56045e984
commit d249867c4c
7 changed files with 165 additions and 44 deletions

View File

@ -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()
}
}
}

View File

@ -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()
}
}
}

View File

@ -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()
}
}
}

View File

@ -24,6 +24,7 @@ import suwayomi.tachidesk.model.table.ChapterTable
import suwayomi.tachidesk.model.table.MangaTable import suwayomi.tachidesk.model.table.MangaTable
import suwayomi.tachidesk.model.table.PageTable import suwayomi.tachidesk.model.table.PageTable
import suwayomi.tachidesk.model.table.toDataClass import suwayomi.tachidesk.model.table.toDataClass
import java.time.Instant
object Chapter { object Chapter {
/** get chapter list when showing a manga */ /** get chapter list when showing a manga */
@ -122,9 +123,14 @@ object Chapter {
dbChapter[ChapterTable.isRead], dbChapter[ChapterTable.isRead],
dbChapter[ChapterTable.isBookmarked], dbChapter[ChapterTable.isBookmarked],
dbChapter[ChapterTable.lastPageRead], dbChapter[ChapterTable.lastPageRead],
dbChapter[ChapterTable.lastReadAt],
chapterCount - index, 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) (ChapterTable.chapterIndex eq chapterIndex) and (ChapterTable.manga eq mangaId)
}.first() }.first()
} }
val mangaEntry = transaction { MangaTable.select { MangaTable.id eq mangaId }.first() }
val source = getHttpSource(mangaEntry[MangaTable.sourceReference])
val pageList = source.fetchPageList( return if (!chapterEntry[ChapterTable.isDownloaded]) {
SChapter.create().apply { val mangaEntry = transaction { MangaTable.select { MangaTable.id eq mangaId }.first() }
url = chapterEntry[ChapterTable.url] val source = getHttpSource(mangaEntry[MangaTable.sourceReference])
name = chapterEntry[ChapterTable.name]
}
).awaitSingle()
val chapterId = chapterEntry[ChapterTable.id].value val pageList = source.fetchPageList(
val chapterCount = transaction { ChapterTable.select { ChapterTable.manga eq mangaId }.count() } SChapter.create().apply {
url = chapterEntry[ChapterTable.url]
name = chapterEntry[ChapterTable.name]
}
).awaitSingle()
// update page list for this chapter val chapterId = chapterEntry[ChapterTable.id].value
transaction { val chapterCount = transaction { ChapterTable.select { ChapterTable.manga eq mangaId }.count() }
pageList.forEach { page ->
val pageEntry = transaction { PageTable.select { (PageTable.chapter eq chapterId) and (PageTable.index eq page.index) }.firstOrNull() } // update page list for this chapter
if (pageEntry == null) { transaction {
PageTable.insert { pageList.forEach { page ->
it[index] = page.index val pageEntry = transaction { PageTable.select { (PageTable.chapter eq chapterId) and (PageTable.index eq page.index) }.firstOrNull() }
it[url] = page.url if (pageEntry == null) {
it[imageUrl] = page.imageUrl PageTable.insert {
it[chapter] = chapterId it[index] = page.index
} it[url] = page.url
} else { it[imageUrl] = page.imageUrl
PageTable.update({ (PageTable.chapter eq chapterId) and (PageTable.index eq page.index) }) { it[chapter] = chapterId
it[url] = page.url }
it[imageUrl] = page.imageUrl } 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?) { fun modifyChapter(mangaId: Int, chapterIndex: Int, isRead: Boolean?, isBookmarked: Boolean?, markPrevRead: Boolean?, lastPageRead: Int?) {
@ -198,6 +218,7 @@ object Chapter {
} }
lastPageRead?.also { lastPageRead?.also {
update[ChapterTable.lastPageRead] = it update[ChapterTable.lastPageRead] = it
update[ChapterTable.lastReadAt] = Instant.now().epochSecond
} }
} }
} }

View File

@ -8,6 +8,9 @@ package suwayomi.tachidesk.impl.download
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
import kotlinx.coroutines.runBlocking 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.Chapter.getChapter
import suwayomi.tachidesk.impl.Page.getPageImage import suwayomi.tachidesk.impl.Page.getPageImage
import suwayomi.tachidesk.impl.download.model.DownloadChapter 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.Error
import suwayomi.tachidesk.impl.download.model.DownloadState.Finished import suwayomi.tachidesk.impl.download.model.DownloadState.Finished
import suwayomi.tachidesk.impl.download.model.DownloadState.Queued import suwayomi.tachidesk.impl.download.model.DownloadState.Queued
import suwayomi.tachidesk.model.table.ChapterTable
import java.util.concurrent.CopyOnWriteArrayList import java.util.concurrent.CopyOnWriteArrayList
class Downloader(private val downloadQueue: CopyOnWriteArrayList<DownloadChapter>, val notifier: () -> Unit) : Thread() { class Downloader(private val downloadQueue: CopyOnWriteArrayList<DownloadChapter>, val notifier: () -> Unit) : Thread() {
@ -49,6 +53,11 @@ class Downloader(private val downloadQueue: CopyOnWriteArrayList<DownloadChapter
step() step()
} }
download.state = Finished download.state = Finished
transaction {
ChapterTable.update({ (ChapterTable.manga eq download.mangaId) and (ChapterTable.chapterIndex eq download.chapterIndex) }) {
it[isDownloaded] = true
}
}
step() step()
} catch (e: DownloadShouldStopException) { } catch (e: DownloadShouldStopException) {
println("Downloader was stopped") println("Downloader was stopped")

View File

@ -24,12 +24,19 @@ data class ChapterDataClass(
/** last read page, zero means not read/no data */ /** last read page, zero means not read/no data */
val lastPageRead: Int, val lastPageRead: Int,
/** last read page, zero means not read/no data */
val lastReadAt: Long,
/** this chapter's index, starts with 1 */ /** this chapter's index, starts with 1 */
val index: Int, val index: Int,
/** is chapter downloaded */
val downloaded: Boolean,
/** used to construct pages in the front-end */
val pageCount: Int = -1,
/** total chapter count, used to calculate if there's a next and prev chapter */ /** total chapter count, used to calculate if there's a next and prev chapter */
val chapterCount: Int? = null, val chapterCount: Int? = null,
/** used to construct pages in the front-end */
val pageCount: Int? = null,
) )

View File

@ -9,6 +9,8 @@ package suwayomi.tachidesk.model.table
import org.jetbrains.exposed.dao.id.IntIdTable import org.jetbrains.exposed.dao.id.IntIdTable
import org.jetbrains.exposed.sql.ResultRow import org.jetbrains.exposed.sql.ResultRow
import org.jetbrains.exposed.sql.select
import org.jetbrains.exposed.sql.transactions.transaction
import suwayomi.tachidesk.model.dataclass.ChapterDataClass import suwayomi.tachidesk.model.dataclass.ChapterDataClass
object ChapterTable : IntIdTable() { object ChapterTable : IntIdTable() {
@ -21,10 +23,15 @@ object ChapterTable : IntIdTable() {
val isRead = bool("read").default(false) val isRead = bool("read").default(false)
val isBookmarked = bool("bookmark").default(false) val isBookmarked = bool("bookmark").default(false)
val lastPageRead = integer("last_page_read").default(0) val lastPageRead = integer("last_page_read").default(0)
val lastReadAt = long("last_read_at").default(0)
// index is reserved by a function // index is reserved by a function
val chapterIndex = integer("index") val chapterIndex = integer("index")
val isDownloaded = bool("is_downloaded").default(false)
val pageCount = integer("page_count").default(-1)
val manga = reference("manga", MangaTable) val manga = reference("manga", MangaTable)
} }
@ -39,5 +46,9 @@ fun ChapterTable.toDataClass(chapterEntry: ResultRow) =
chapterEntry[isRead], chapterEntry[isRead],
chapterEntry[isBookmarked], chapterEntry[isBookmarked],
chapterEntry[lastPageRead], chapterEntry[lastPageRead],
chapterEntry[lastReadAt],
chapterEntry[chapterIndex], chapterEntry[chapterIndex],
chapterEntry[isDownloaded],
chapterEntry[pageCount],
transaction { ChapterTable.select { ChapterTable.manga eq chapterEntry[manga].value }.count().toInt() },
) )