mirror of
https://github.com/Suwayomi/Suwayomi-Server.git
synced 2026-07-09 13:54:36 -05:00
Decouple Cache and Download behaviour (#493)
* Separate cache dir from download dir * Move downloader logic outside of caching/image download logic * remove unnecessary method duplication * moved download logic inside download provider * optimize and handle partial downloads * made code review changes
This commit is contained in:
@@ -7,6 +7,7 @@ package suwayomi.tachidesk.manga.impl.util
|
||||
* 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.ResultRow
|
||||
import org.jetbrains.exposed.sql.select
|
||||
import org.jetbrains.exposed.sql.transactions.transaction
|
||||
import org.kodein.di.DI
|
||||
@@ -21,17 +22,16 @@ import java.io.File
|
||||
|
||||
private val applicationDirs by DI.global.instance<ApplicationDirs>()
|
||||
|
||||
fun getMangaDir(mangaId: Int): String {
|
||||
val mangaEntry = transaction { MangaTable.select { MangaTable.id eq mangaId }.first() }
|
||||
fun getMangaDir(mangaId: Int, cache: Boolean = false): String {
|
||||
val mangaEntry = getMangaEntry(mangaId)
|
||||
val source = GetCatalogueSource.getCatalogueSourceOrStub(mangaEntry[MangaTable.sourceReference])
|
||||
|
||||
val sourceDir = source.toString()
|
||||
val mangaDir = SafePath.buildValidFilename(mangaEntry[MangaTable.title])
|
||||
|
||||
return "${applicationDirs.mangaDownloadsRoot}/$sourceDir/$mangaDir"
|
||||
return (if (cache) applicationDirs.cacheRoot else applicationDirs.mangaDownloadsRoot) + "/$sourceDir/$mangaDir"
|
||||
}
|
||||
|
||||
fun getChapterDir(mangaId: Int, chapterId: Int): String {
|
||||
fun getChapterDir(mangaId: Int, chapterId: Int, cache: Boolean = false): String {
|
||||
val chapterEntry = transaction { ChapterTable.select { ChapterTable.id eq chapterId }.first() }
|
||||
|
||||
val chapterDir = SafePath.buildValidFilename(
|
||||
@@ -41,12 +41,12 @@ fun getChapterDir(mangaId: Int, chapterId: Int): String {
|
||||
}
|
||||
)
|
||||
|
||||
return getMangaDir(mangaId) + "/$chapterDir"
|
||||
return getMangaDir(mangaId, cache) + "/$chapterDir"
|
||||
}
|
||||
|
||||
/** return value says if rename/move was successful */
|
||||
fun updateMangaDownloadDir(mangaId: Int, newTitle: String): Boolean {
|
||||
val mangaEntry = transaction { MangaTable.select { MangaTable.id eq mangaId }.first() }
|
||||
val mangaEntry = getMangaEntry(mangaId)
|
||||
val source = GetCatalogueSource.getCatalogueSourceOrStub(mangaEntry[MangaTable.sourceReference])
|
||||
|
||||
val sourceDir = source.toString()
|
||||
@@ -66,3 +66,7 @@ fun updateMangaDownloadDir(mangaId: Int, newTitle: String): Boolean {
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
private fun getMangaEntry(mangaId: Int): ResultRow {
|
||||
return transaction { MangaTable.select { MangaTable.id eq mangaId }.first() }
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ package suwayomi.tachidesk.manga.impl.util.storage
|
||||
|
||||
import okhttp3.Response
|
||||
import okhttp3.internal.closeQuietly
|
||||
import suwayomi.tachidesk.manga.impl.util.getChapterDir
|
||||
import java.io.File
|
||||
import java.io.FileInputStream
|
||||
import java.io.InputStream
|
||||
@@ -29,7 +30,7 @@ object ImageResponse {
|
||||
}
|
||||
|
||||
/** fetch a cached image response, calls `fetcher` if cache fails */
|
||||
suspend fun getCachedImageResponse(saveDir: String, fileName: String, fetcher: suspend () -> Response): Pair<InputStream, String> {
|
||||
private suspend fun getCachedImageResponse(saveDir: String, fileName: String, fetcher: suspend () -> Response): Pair<InputStream, String> {
|
||||
val cachedFile = findFileNameStartingWith(saveDir, fileName)
|
||||
val filePath = "$saveDir/$fileName"
|
||||
if (cachedFile != null) {
|
||||
@@ -43,19 +44,7 @@ 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()
|
||||
@@ -63,6 +52,21 @@ object ImageResponse {
|
||||
}
|
||||
}
|
||||
|
||||
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 {
|
||||
@@ -70,7 +74,7 @@ object ImageResponse {
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun getNoCacheImageResponse(fetcher: suspend () -> Response): Pair<InputStream, String> {
|
||||
private suspend fun getNoCacheImageResponse(fetcher: suspend () -> Response): Pair<InputStream, String> {
|
||||
val response = fetcher()
|
||||
|
||||
if (response.code == 200) {
|
||||
@@ -88,11 +92,20 @@ object ImageResponse {
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun getImageResponse(saveDir: String, fileName: String, useCache: Boolean = false, fetcher: suspend () -> Response): Pair<InputStream, String> {
|
||||
suspend fun getImageResponse(saveDir: String, fileName: String, useCache: Boolean, fetcher: suspend () -> Response): Pair<InputStream, String> {
|
||||
return if (useCache) {
|
||||
getCachedImageResponse(saveDir, fileName, fetcher)
|
||||
} else {
|
||||
getNoCacheImageResponse(fetcher)
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun getImageResponse(mangaId: Int, chapterId: Int, fileName: String, useCache: Boolean, fetcher: suspend () -> Response): Pair<InputStream, String> {
|
||||
var saveDir = ""
|
||||
if (useCache) {
|
||||
saveDir = getChapterDir(mangaId, chapterId, true)
|
||||
File(saveDir).mkdirs()
|
||||
}
|
||||
return getImageResponse(saveDir, fileName, useCache, fetcher)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user