Skip to content

Commit

Permalink
Move downloader logic outside of caching/image download logic
Browse files Browse the repository at this point in the history
  • Loading branch information
akabhirav committed Feb 9, 2023
1 parent 4490bfb commit df9c8c1
Show file tree
Hide file tree
Showing 7 changed files with 106 additions and 23 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ import org.jetbrains.exposed.dao.id.EntityID
import org.jetbrains.exposed.sql.*
import org.jetbrains.exposed.sql.SortOrder.ASC
import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq
import org.jetbrains.exposed.sql.SqlExpressionBuilder.inList
import org.jetbrains.exposed.sql.transactions.transaction
import suwayomi.tachidesk.manga.impl.Manga.getManga
import suwayomi.tachidesk.manga.impl.util.getChapterDir
Expand Down Expand Up @@ -312,9 +311,7 @@ object Chapter {
ChapterTable.select { (ChapterTable.manga eq mangaId) and (ChapterTable.sourceOrder eq chapterIndex) }
.first()[ChapterTable.id].value

val chapterDir = getChapterDir(mangaId, chapterId)

File(chapterDir).deleteRecursively()
ChapterDownloadHelper.delete(mangaId, chapterId)

ChapterTable.update({ (ChapterTable.manga eq mangaId) and (ChapterTable.sourceOrder eq chapterIndex) }) {
it[isDownloaded] = false
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package suwayomi.tachidesk.manga.impl

import suwayomi.tachidesk.manga.impl.download.DownloadedFilesProvider
import suwayomi.tachidesk.manga.impl.download.FolderProvider
import java.io.InputStream

object ChapterDownloadHelper {
fun getImage(mangaId: Int, chapterId: Int, index: Int): Pair<InputStream, String> {
return provider(mangaId, chapterId).getImage(index)
}

fun delete(mangaId: Int, chapterId: Int): Boolean {
return provider(mangaId, chapterId).delete()
}

fun putImage(mangaId: Int, chapterId: Int, index: Int, image: InputStream): Boolean {
return provider(mangaId, chapterId).putImage(index, image)
}

// 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): DownloadedFilesProvider {
return FolderProvider(mangaId, chapterId)
}
}
7 changes: 4 additions & 3 deletions server/src/main/kotlin/suwayomi/tachidesk/manga/impl/Page.kt
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ import org.jetbrains.exposed.sql.and
import org.jetbrains.exposed.sql.select
import org.jetbrains.exposed.sql.transactions.transaction
import org.jetbrains.exposed.sql.update
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.getImageResponse
Expand Down Expand Up @@ -82,10 +81,12 @@ object Page {
}
}

val chapterDir = getChapterDir(mangaId, chapterId)
File(chapterDir).mkdirs()
val fileName = getPageName(index)

if (chapterEntry[ChapterTable.isDownloaded]) {
return ChapterDownloadHelper.getImage(mangaId, chapterId, index)
}

return getImageResponse(mangaId, chapterId, fileName, useCache) {
source.fetchImage(tachiyomiPage).awaitSingle()
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package suwayomi.tachidesk.manga.impl.download

import java.io.InputStream

/*
* Base class for downloaded chapter files provider, example: Folder, Archive
* */
abstract class DownloadedFilesProvider(val mangaId: Int, val chapterId: Int) {
abstract fun getImage(index: Int): Pair<InputStream, String>

abstract fun putImage(index: Int, image: InputStream): Boolean

abstract fun delete(): Boolean

private fun getPageName(index: Int): String {
return String.format("%03d", index + 1)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ 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.ChapterDownloadHelper
import suwayomi.tachidesk.manga.impl.Page.getPageImage
import suwayomi.tachidesk.manga.impl.chapter.getChapterDownloadReady
import suwayomi.tachidesk.manga.impl.download.model.DownloadChapter
Expand Down Expand Up @@ -99,7 +100,7 @@ class Downloader(
for (pageNum in 0 until pageCount) {
var pageProgressJob: Job? = null
try {
getPageImage(
val image = getPageImage(
mangaId = download.mangaId,
chapterIndex = download.chapterIndex,
index = pageNum,
Expand All @@ -113,7 +114,9 @@ class Downloader(
}
.launchIn(scope)
}
).first.close()
).first
ChapterDownloadHelper.putImage(download.mangaId, download.chapter.id, pageNum, image)
image.close()
} finally {
// always cancel the page progress job even if it throws an exception to avoid memory leaks
pageProgressJob?.cancel()
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package suwayomi.tachidesk.manga.impl.download

import suwayomi.tachidesk.manga.impl.Page.getPageName
import suwayomi.tachidesk.manga.impl.util.getChapterDir
import suwayomi.tachidesk.manga.impl.util.storage.ImageResponse
import java.io.File
import java.io.FileInputStream
import java.io.InputStream

/*
* Provides downloaded files when pages were downloaded into folders
* */
class FolderProvider(mangaId: Int, chapterId: Int) : DownloadedFilesProvider(mangaId, chapterId) {
override fun getImage(index: Int): Pair<InputStream, String> {
val chapterDir = getChapterDir(mangaId, chapterId)
val folder = File(chapterDir)
folder.mkdirs()
val file = folder.listFiles()?.get(index)
val fileType = file!!.name.substringAfterLast(".")
return Pair(FileInputStream(file).buffered(), "image/$fileType")
}

override fun putImage(index: Int, image: InputStream): Boolean {
val chapterDir = getChapterDir(mangaId, chapterId)
val folder = File(chapterDir)
folder.mkdirs()
val fileName = getPageName(index)
val filePath = "$chapterDir/$fileName"
ImageResponse.saveImage(filePath, image)
return true
}

override fun delete(): Boolean {
val chapterDir = getChapterDir(mangaId, chapterId)
return File(chapterDir).deleteRecursively()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -44,26 +44,29 @@ object ImageResponse {
val response = fetcher()

if (response.code == 200) {
val tmpSavePath = "$filePath.tmp"
val tmpSaveFile = File(tmpSavePath)
response.body!!.source().saveTo(tmpSaveFile)

// find image type
val imageType = response.headers["content-type"]
?: ImageUtil.findImageType { tmpSaveFile.inputStream() }?.mime
?: "image/jpeg"

val actualSavePath = "$filePath.${imageType.substringAfter("/")}"

tmpSaveFile.renameTo(File(actualSavePath))

val (actualSavePath, imageType) = saveImage(filePath, response.body!!.byteStream())
return pathToInputStream(actualSavePath) to imageType
} else {
response.closeQuietly()
throw Exception("request error! ${response.code}")
}
}

fun saveImage(filePath: String, image: InputStream): Pair<String, String> {
val tmpSavePath = "$filePath.tmp"
val tmpSaveFile = File(tmpSavePath)
image.use { input -> tmpSaveFile.outputStream().use { output -> input.copyTo(output) } }

// find image type
val imageType = ImageUtil.findImageType { tmpSaveFile.inputStream() }?.mime
?: "image/jpeg"

val actualSavePath = "$filePath.${imageType.substringAfter("/")}"

tmpSaveFile.renameTo(File(actualSavePath))
return Pair(actualSavePath, imageType)
}

fun clearCachedImage(saveDir: String, fileName: String) {
val cachedFile = findFileNameStartingWith(saveDir, fileName)
cachedFile?.also {
Expand Down Expand Up @@ -101,7 +104,7 @@ object ImageResponse {
var saveDir = ""
if (useCache) {
saveDir = getChapterDir(mangaId, chapterId, true)
File(saveDir).mkdir()
File(saveDir).mkdirs()
}
return getImageResponse(saveDir, fileName, useCache, fetcher)
}
Expand Down

0 comments on commit df9c8c1

Please sign in to comment.