瀏覽代碼

Add compress to CBZ on download (#6360)

Seishirou101 3 年之前
父節點
當前提交
5336c5b46e

+ 1 - 1
app/src/main/java/eu/kanade/tachiyomi/AppInfo.kt

@@ -8,4 +8,4 @@ package eu.kanade.tachiyomi
 object AppInfo {
     fun getVersionCode() = BuildConfig.VERSION_CODE
     fun getVersionName() = BuildConfig.VERSION_NAME
-}
+}

+ 1 - 1
app/src/main/java/eu/kanade/tachiyomi/data/download/DownloadCache.kt

@@ -143,7 +143,7 @@ class DownloadCache(
             mangaDirs.values.forEach { mangaDir ->
                 val chapterDirs = mangaDir.dir.listFiles()
                     .orEmpty()
-                    .mapNotNull { it.name }
+                    .mapNotNull { it.name?.replace(".cbz", "") }
                     .toHashSet()
 
                 mangaDir.files = chapterDirs

+ 1 - 1
app/src/main/java/eu/kanade/tachiyomi/data/download/DownloadManager.kt

@@ -39,7 +39,7 @@ class DownloadManager(
     /**
      * Downloads provider, used to retrieve the folders where the chapters are or should be stored.
      */
-    private val provider = DownloadProvider(context)
+    val provider = DownloadProvider(context)
 
     /**
      * Cache of downloaded chapters.

+ 6 - 2
app/src/main/java/eu/kanade/tachiyomi/data/download/DownloadProvider.kt

@@ -148,10 +148,14 @@ class DownloadProvider(private val context: Context) {
      * @param chapter the chapter to query.
      */
     fun getValidChapterDirNames(chapter: Chapter): List<String> {
+        val chapterName = getChapterDirName(chapter)
         return listOf(
-            getChapterDirName(chapter),
+            // Folder of images
+            chapterName,
+
+            // Archived chapters
+            "$chapterName.cbz",
 
-            // TODO: remove this
             // Legacy chapter directory name used in v0.9.2 and before
             DiskUtil.buildValidFilename(chapter.name)
         )

+ 46 - 1
app/src/main/java/eu/kanade/tachiyomi/data/download/Downloader.kt

@@ -12,6 +12,7 @@ import eu.kanade.tachiyomi.data.database.models.Chapter
 import eu.kanade.tachiyomi.data.database.models.Manga
 import eu.kanade.tachiyomi.data.download.model.Download
 import eu.kanade.tachiyomi.data.download.model.DownloadQueue
+import eu.kanade.tachiyomi.data.preference.PreferencesHelper
 import eu.kanade.tachiyomi.source.SourceManager
 import eu.kanade.tachiyomi.source.UnmeteredSource
 import eu.kanade.tachiyomi.source.model.Page
@@ -35,7 +36,11 @@ import rx.android.schedulers.AndroidSchedulers
 import rx.schedulers.Schedulers
 import rx.subscriptions.CompositeSubscription
 import uy.kohesive.injekt.injectLazy
+import java.io.BufferedOutputStream
 import java.io.File
+import java.util.zip.CRC32
+import java.util.zip.ZipEntry
+import java.util.zip.ZipOutputStream
 
 /**
  * This class is the one in charge of downloading chapters.
@@ -60,6 +65,8 @@ class Downloader(
 
     private val chapterCache: ChapterCache by injectLazy()
 
+    private val preferences: PreferencesHelper by injectLazy()
+
     /**
      * Store for persisting downloads across restarts.
      */
@@ -484,13 +491,51 @@ class Downloader(
 
         // Only rename the directory if it's downloaded.
         if (download.status == Download.State.DOWNLOADED) {
-            tmpDir.renameTo(dirname)
+            if (preferences.saveChaptersAsCBZ().get()) {
+                archiveChapter(mangaDir, dirname, tmpDir)
+            } else {
+                tmpDir.renameTo(dirname)
+            }
             cache.addChapter(dirname, mangaDir, download.manga)
 
             DiskUtil.createNoMediaFile(tmpDir, context)
         }
     }
 
+    /**
+     * Archive the chapter pages as a CBZ.
+     */
+    private fun archiveChapter(
+        mangaDir: UniFile,
+        dirname: String,
+        tmpDir: UniFile,
+    ) {
+        val zip = mangaDir.createFile("$dirname.cbz.tmp")
+        ZipOutputStream(BufferedOutputStream(zip.openOutputStream())).use { zipOut ->
+            zipOut.setMethod(ZipEntry.STORED)
+
+            tmpDir.listFiles()?.forEach { img ->
+                img.openInputStream().use { input ->
+                    val data = input.readBytes()
+                    val size = img.length()
+                    val entry = ZipEntry(img.name).apply {
+                        val crc = CRC32().apply {
+                            update(data)
+                        }
+                        setCrc(crc.value)
+
+                        compressedSize = size
+                        setSize(size)
+                    }
+                    zipOut.putNextEntry(entry)
+                    zipOut.write(data)
+                }
+            }
+        }
+        zip.renameTo("$dirname.cbz")
+        tmpDir.delete()
+    }
+
     /**
      * Completes a download. This method is called in the main thread.
      */

+ 2 - 0
app/src/main/java/eu/kanade/tachiyomi/data/preference/PreferencesHelper.kt

@@ -202,6 +202,8 @@ class PreferencesHelper(val context: Context) {
 
     fun downloadOnlyOverWifi() = prefs.getBoolean(Keys.downloadOnlyOverWifi, true)
 
+    fun saveChaptersAsCBZ() = flowPrefs.getBoolean("save_chapter_as_cbz", false)
+
     fun folderPerManga() = prefs.getBoolean(Keys.folderPerManga, false)
 
     fun numberOfBackups() = flowPrefs.getInt("backup_slots", 1)

+ 16 - 0
app/src/main/java/eu/kanade/tachiyomi/ui/reader/loader/DownloadPageLoader.kt

@@ -2,6 +2,7 @@ package eu.kanade.tachiyomi.ui.reader.loader
 
 import android.app.Application
 import android.net.Uri
+import com.hippo.unifile.UniFile
 import eu.kanade.tachiyomi.data.database.models.Manga
 import eu.kanade.tachiyomi.data.download.DownloadManager
 import eu.kanade.tachiyomi.source.Source
@@ -10,6 +11,7 @@ import eu.kanade.tachiyomi.ui.reader.model.ReaderChapter
 import eu.kanade.tachiyomi.ui.reader.model.ReaderPage
 import rx.Observable
 import uy.kohesive.injekt.injectLazy
+import java.io.File
 
 /**
  * Loader used to load a chapter from the downloaded chapters.
@@ -28,6 +30,20 @@ class DownloadPageLoader(
      * Returns an observable containing the pages found on this downloaded chapter.
      */
     override fun getPages(): Observable<List<ReaderPage>> {
+        val chapterPath = downloadManager.provider.findChapterDir(chapter.chapter, manga, source)
+        return if (chapterPath?.isFile == true) {
+            getPagesFromArchive(chapterPath)
+        } else {
+            getPagesFromDirectory()
+        }
+    }
+
+    private fun getPagesFromArchive(chapterPath: UniFile): Observable<List<ReaderPage>> {
+        val loader = ZipPageLoader(File(chapterPath.filePath!!))
+        return loader.getPages()
+    }
+
+    private fun getPagesFromDirectory(): Observable<List<ReaderPage>> {
         return downloadManager.buildPageList(source, manga, chapter.chapter)
             .map { pages ->
                 pages.map { page ->

+ 4 - 0
app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsDownloadController.kt

@@ -68,6 +68,10 @@ class SettingsDownloadController : SettingsController() {
             titleRes = R.string.connected_to_wifi
             defaultValue = true
         }
+        switchPreference {
+            bindTo(preferences.saveChaptersAsCBZ())
+            titleRes = R.string.save_chapter_as_cbz
+        }
         preferenceCategory {
             titleRes = R.string.pref_category_delete_chapters
 

+ 1 - 0
app/src/main/res/values/strings.xml

@@ -398,6 +398,7 @@
     <string name="pref_category_auto_download">Auto-download</string>
     <string name="pref_download_new">Download new chapters</string>
     <string name="pref_download_new_categories_details">Manga in excluded categories will not be downloaded even if they are also in included categories.</string>
+    <string name="save_chapter_as_cbz">Save as CBZ archive</string>
 
       <!-- Tracking section -->
     <string name="tracking_guide">Tracking guide</string>