package suwayomi.tachidesk.manga.impl import kotlinx.coroutines.CoroutineScope import org.jetbrains.exposed.sql.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 = 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 = provider(mangaId, chapterId).getAsArchiveStream() private fun getChapterWithCbzFileName(chapterId: Int): Pair = 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 { 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 { // fileName, fileSize val (chapterData, fileName) = getChapterWithCbzFileName(chapterId) val fileSize = provider(chapterData.mangaId, chapterData.id).getArchiveSize() return Pair(fileName, fileSize) } }