Skip to content

Commit 0942de6

Browse files
committed
Move downloader logic outside of caching/image download logic
1 parent 4490bfb commit 0942de6

File tree

6 files changed

+98
-9
lines changed

6 files changed

+98
-9
lines changed

server/src/main/kotlin/suwayomi/tachidesk/manga/impl/Chapter.kt

+1-4
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@ import org.jetbrains.exposed.dao.id.EntityID
1515
import org.jetbrains.exposed.sql.*
1616
import org.jetbrains.exposed.sql.SortOrder.ASC
1717
import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq
18-
import org.jetbrains.exposed.sql.SqlExpressionBuilder.inList
1918
import org.jetbrains.exposed.sql.transactions.transaction
2019
import suwayomi.tachidesk.manga.impl.Manga.getManga
2120
import suwayomi.tachidesk.manga.impl.util.getChapterDir
@@ -312,9 +311,7 @@ object Chapter {
312311
ChapterTable.select { (ChapterTable.manga eq mangaId) and (ChapterTable.sourceOrder eq chapterIndex) }
313312
.first()[ChapterTable.id].value
314313

315-
val chapterDir = getChapterDir(mangaId, chapterId)
316-
317-
File(chapterDir).deleteRecursively()
314+
ChapterDownloadHelper.delete(mangaId, chapterId)
318315

319316
ChapterTable.update({ (ChapterTable.manga eq mangaId) and (ChapterTable.sourceOrder eq chapterIndex) }) {
320317
it[isDownloaded] = false
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
package suwayomi.tachidesk.manga.impl
2+
3+
import suwayomi.tachidesk.manga.impl.download.DownloadedFilesProvider
4+
import suwayomi.tachidesk.manga.impl.download.FolderProvider
5+
import java.io.InputStream
6+
7+
object ChapterDownloadHelper {
8+
fun getImage(mangaId: Int, chapterId: Int, index: Int): Pair<InputStream, String> {
9+
return provider(mangaId, chapterId).getImage(index)
10+
}
11+
12+
fun delete(mangaId: Int, chapterId: Int): Boolean {
13+
return provider(mangaId, chapterId).delete()
14+
}
15+
16+
fun putImage(mangaId: Int, chapterId: Int, index: Int, image: InputStream): Boolean {
17+
return provider(mangaId, chapterId).putImage(index, image)
18+
}
19+
20+
// 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
21+
private fun provider(mangaId: Int, chapterId: Int): DownloadedFilesProvider {
22+
return FolderProvider(mangaId, chapterId)
23+
}
24+
25+
}

server/src/main/kotlin/suwayomi/tachidesk/manga/impl/Page.kt

+4-3
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@ import org.jetbrains.exposed.sql.and
1515
import org.jetbrains.exposed.sql.select
1616
import org.jetbrains.exposed.sql.transactions.transaction
1717
import org.jetbrains.exposed.sql.update
18-
import suwayomi.tachidesk.manga.impl.util.getChapterDir
1918
import suwayomi.tachidesk.manga.impl.util.lang.awaitSingle
2019
import suwayomi.tachidesk.manga.impl.util.source.GetCatalogueSource.getCatalogueSourceOrStub
2120
import suwayomi.tachidesk.manga.impl.util.storage.ImageResponse.getImageResponse
@@ -82,10 +81,12 @@ object Page {
8281
}
8382
}
8483

85-
val chapterDir = getChapterDir(mangaId, chapterId)
86-
File(chapterDir).mkdirs()
8784
val fileName = getPageName(index)
8885

86+
if (chapterEntry[ChapterTable.isDownloaded]) {
87+
return ChapterDownloadHelper.getImage(mangaId, chapterId, index)
88+
}
89+
8990
return getImageResponse(mangaId, chapterId, fileName, useCache) {
9091
source.fetchImage(tachiyomiPage).awaitSingle()
9192
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
package suwayomi.tachidesk.manga.impl.download
2+
3+
import java.io.InputStream
4+
5+
/*
6+
* Base class for downloaded chapter files provider, example: Folder, Archive
7+
* */
8+
abstract class DownloadedFilesProvider(val mangaId: Int, val chapterId: Int) {
9+
abstract fun getImage(index: Int): Pair<InputStream, String>
10+
11+
abstract fun putImage(index: Int, image: InputStream): Boolean
12+
13+
abstract fun delete(): Boolean
14+
15+
private fun getPageName(index: Int): String {
16+
return String.format("%03d", index + 1)
17+
}
18+
}

server/src/main/kotlin/suwayomi/tachidesk/manga/impl/download/Downloader.kt

+5-2
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import mu.KotlinLogging
2323
import org.jetbrains.exposed.sql.and
2424
import org.jetbrains.exposed.sql.transactions.transaction
2525
import org.jetbrains.exposed.sql.update
26+
import suwayomi.tachidesk.manga.impl.ChapterDownloadHelper
2627
import suwayomi.tachidesk.manga.impl.Page.getPageImage
2728
import suwayomi.tachidesk.manga.impl.chapter.getChapterDownloadReady
2829
import suwayomi.tachidesk.manga.impl.download.model.DownloadChapter
@@ -99,7 +100,7 @@ class Downloader(
99100
for (pageNum in 0 until pageCount) {
100101
var pageProgressJob: Job? = null
101102
try {
102-
getPageImage(
103+
val image = getPageImage(
103104
mangaId = download.mangaId,
104105
chapterIndex = download.chapterIndex,
105106
index = pageNum,
@@ -113,7 +114,9 @@ class Downloader(
113114
}
114115
.launchIn(scope)
115116
}
116-
).first.close()
117+
).first
118+
ChapterDownloadHelper.putImage(download.mangaId, download.chapter.id, pageNum, image)
119+
image.close()
117120
} finally {
118121
// always cancel the page progress job even if it throws an exception to avoid memory leaks
119122
pageProgressJob?.cancel()
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
package suwayomi.tachidesk.manga.impl.download
2+
3+
import suwayomi.tachidesk.manga.impl.Page.getPageName
4+
import suwayomi.tachidesk.manga.impl.util.getChapterDir
5+
import suwayomi.tachidesk.manga.impl.util.storage.ImageUtil
6+
import java.io.File
7+
import java.io.FileInputStream
8+
import java.io.InputStream
9+
10+
/*
11+
* Provides downloaded files when pages were downloaded into folders
12+
* */
13+
class FolderProvider(mangaId: Int, chapterId: Int) : DownloadedFilesProvider(mangaId, chapterId) {
14+
override fun getImage(index: Int): Pair<InputStream, String> {
15+
val chapterDir = getChapterDir(mangaId, chapterId)
16+
val folder = File(chapterDir)
17+
folder.mkdirs()
18+
val file = folder.listFiles()?.get(index)
19+
val fileType = file!!.name.substringAfterLast(".")
20+
return Pair(FileInputStream(file).buffered(), "image/$fileType")
21+
}
22+
23+
override fun putImage(index: Int, image: InputStream): Boolean {
24+
val chapterDir = getChapterDir(mangaId, chapterId)
25+
val fileName = getPageName(index)
26+
val filePath = "$chapterDir/$fileName"
27+
val tmpSavePath = "$filePath.tmp"
28+
val tmpSaveFile = File(tmpSavePath)
29+
image.use { input -> tmpSaveFile.outputStream().use { output -> input.copyTo(output) } }
30+
31+
// find image type
32+
val imageType = ImageUtil.findImageType { tmpSaveFile.inputStream() }?.mime
33+
?: "image/jpeg"
34+
35+
val actualSavePath = "$filePath.${imageType.substringAfter("/")}"
36+
37+
tmpSaveFile.renameTo(File(actualSavePath))
38+
return true
39+
}
40+
41+
override fun delete(): Boolean {
42+
val chapterDir = getChapterDir(mangaId, chapterId)
43+
return File(chapterDir).deleteRecursively()
44+
}
45+
}

0 commit comments

Comments
 (0)