Files
Suwayomi-Server/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/ChapterDownloadHelper.kt
renovate[bot] c117d380a3 Update exposed to v1 (major) (#1868)
* Update exposed to v1

* Update Exposed

* Add Kotlinx.DateTime extensions

* Update H2

* Review comments

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Syer10 <syer10@users.noreply.github.com>
2026-05-12 12:53:41 -04:00

113 lines
4.3 KiB
Kotlin

package suwayomi.tachidesk.manga.impl
import kotlinx.coroutines.CoroutineScope
import org.jetbrains.exposed.v1.core.eq
import org.jetbrains.exposed.v1.jdbc.select
import org.jetbrains.exposed.v1.jdbc.transactions.transaction
import suwayomi.tachidesk.manga.impl.chapter.getChapterDownloadReady
import suwayomi.tachidesk.manga.impl.download.fileProvider.ChaptersFilesProvider
import suwayomi.tachidesk.manga.impl.download.fileProvider.impl.ArchiveProvider
import suwayomi.tachidesk.manga.impl.download.fileProvider.impl.FolderProvider
import suwayomi.tachidesk.manga.impl.download.model.DownloadQueueItem
import suwayomi.tachidesk.manga.impl.util.getChapterCbzPath
import suwayomi.tachidesk.manga.impl.util.getChapterDownloadPath
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.toDataClass
import suwayomi.tachidesk.server.serverConfig
import java.io.File
import java.io.InputStream
object ChapterDownloadHelper {
fun getImage(
mangaId: Int,
chapterId: Int,
index: Int,
): Pair<InputStream, String> = provider(mangaId, chapterId).getImage().execute(index)
fun getImageCount(
mangaId: Int,
chapterId: Int,
): Int = provider(mangaId, chapterId).getImageCount()
fun delete(
mangaId: Int,
chapterId: Int,
): Boolean = provider(mangaId, chapterId).delete()
/**
* This function should never be called without calling [getChapterDownloadReady] beforehand.
*/
suspend fun download(
mangaId: Int,
chapterId: Int,
download: DownloadQueueItem,
scope: CoroutineScope,
step: suspend (DownloadQueueItem?, Boolean) -> Unit,
): Boolean = provider(mangaId, chapterId).download().execute(download, scope, step)
// return the appropriate provider based on how the download was saved. For the logic is simple but will evolve when new types of downloads are available
private fun provider(
mangaId: Int,
chapterId: Int,
): ChaptersFilesProvider<*> {
val chapterFolder = File(getChapterDownloadPath(mangaId, chapterId))
val cbzFile = File(getChapterCbzPath(mangaId, chapterId))
if (cbzFile.exists()) return ArchiveProvider(mangaId, chapterId)
if (!chapterFolder.exists() && serverConfig.downloadAsCbz.value) return ArchiveProvider(mangaId, chapterId)
return FolderProvider(mangaId, chapterId)
}
fun getArchiveStreamWithSize(
mangaId: Int,
chapterId: Int,
): Pair<InputStream, Long> = provider(mangaId, chapterId).getAsArchiveStream()
private fun getChapterWithCbzFileName(chapterId: Int): Pair<ChapterDataClass, String> =
transaction {
val row =
(ChapterTable innerJoin MangaTable)
.select(ChapterTable.columns + MangaTable.columns)
.where { ChapterTable.id eq chapterId }
.firstOrNull() ?: throw IllegalArgumentException("ChapterId $chapterId not found")
val chapter = ChapterTable.toDataClass(row)
val mangaTitle = row[MangaTable.title]
val scanlatorPart = chapter.scanlator?.let { "[$it] " } ?: ""
val fileName = "$mangaTitle - $scanlatorPart${chapter.name}.cbz"
Pair(chapter, fileName)
}
fun getCbzForDownload(
chapterId: Int,
markAsRead: Boolean?,
): Triple<InputStream, String, Long> {
val (chapterData, fileName) = getChapterWithCbzFileName(chapterId)
val cbzFile = provider(chapterData.mangaId, chapterData.id).getAsArchiveStream()
if (markAsRead == true) {
Chapter.modifyChapter(
chapterData.mangaId,
chapterData.index,
isRead = true,
isBookmarked = null,
markPrevRead = null,
lastPageRead = null,
)
}
return Triple(cbzFile.first, fileName, cbzFile.second)
}
fun getCbzMetadataForDownload(chapterId: Int): Pair<String, Long> { // fileName, fileSize
val (chapterData, fileName) = getChapterWithCbzFileName(chapterId)
val fileSize = provider(chapterData.mangaId, chapterData.id).getArchiveSize()
return Pair(fileName, fileSize)
}
}