diff --git a/app/src/main/java/eu/kanade/tachiyomi/AppInfo.kt b/app/src/main/java/eu/kanade/tachiyomi/AppInfo.kt index 5cb55af20..7ec80cdb6 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/AppInfo.kt +++ b/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 -} \ No newline at end of file +} diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/download/DownloadCache.kt b/app/src/main/java/eu/kanade/tachiyomi/data/download/DownloadCache.kt index 8d35f4474..579ff20cb 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/download/DownloadCache.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/download/DownloadCache.kt @@ -81,7 +81,7 @@ class DownloadCache( if (sourceDir != null) { val mangaDir = sourceDir.files[provider.getMangaDirName(manga)] if (mangaDir != null) { - return provider.getValidChapterDirNames(chapter).any { it in mangaDir.files || "$it.cbz" in mangaDir.files } + return provider.getValidChapterDirNames(chapter).any { it in mangaDir.files } } } return false @@ -196,8 +196,6 @@ class DownloadCache( provider.getValidChapterDirNames(chapter).forEach { if (it in mangaDir.files) { mangaDir.files -= it - } else if ("$it.cbz" in mangaDir.files) { - mangaDir.files -= "$it.cbz" } } } @@ -229,8 +227,6 @@ class DownloadCache( provider.getValidChapterDirNames(chapter).forEach { if (it in mangaDir.files) { mangaDir.files -= it - } else if ("$it.cbz" in mangaDir.files) { - mangaDir.files -= "$it.cbz" } } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/download/DownloadManager.kt b/app/src/main/java/eu/kanade/tachiyomi/data/download/DownloadManager.kt index 32a23381f..5c623e289 100755 --- a/app/src/main/java/eu/kanade/tachiyomi/data/download/DownloadManager.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/download/DownloadManager.kt @@ -41,7 +41,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. @@ -386,12 +386,12 @@ class DownloadManager( // Assume there's only 1 version of the chapter name formats present val oldFolder = oldNames.asSequence() - .mapNotNull { mangaDir.findFile(it) ?: mangaDir.findFile("$it.cbz") } + .mapNotNull { mangaDir.findFile(it) } .firstOrNull() - if (oldFolder?.renameTo(newName + if (oldFolder.name?.endsWith(".cbz") == true) ".cbz" else "") == true) { + if (oldFolder?.renameTo(newName) == true) { cache.removeChapter(oldChapter, manga) - cache.addChapter(newName + if (oldFolder.name?.endsWith(".cbz") == true) ".cbz" else "", mangaDir, manga) + cache.addChapter(newName, mangaDir, manga) } else { logcat(LogPriority.ERROR) { "Could not rename downloaded chapter: ${oldNames.joinToString()}." } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/download/DownloadProvider.kt b/app/src/main/java/eu/kanade/tachiyomi/data/download/DownloadProvider.kt index 2b4eada28..a794f36e4 100755 --- a/app/src/main/java/eu/kanade/tachiyomi/data/download/DownloadProvider.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/download/DownloadProvider.kt @@ -90,7 +90,7 @@ class DownloadProvider(private val context: Context) { fun findChapterDir(chapter: Chapter, manga: Manga, source: Source): UniFile? { val mangaDir = findMangaDir(manga, source) return getValidChapterDirNames(chapter).asSequence() - .mapNotNull { mangaDir?.findFile(it, true) ?: mangaDir?.findFile("$it.cbz", true) } + .mapNotNull { mangaDir?.findFile(it, true) } .firstOrNull() } @@ -105,7 +105,7 @@ class DownloadProvider(private val context: Context) { val mangaDir = findMangaDir(manga, source) ?: return emptyList() return chapters.mapNotNull { chapter -> getValidChapterDirNames(chapter).asSequence() - .mapNotNull { mangaDir.findFile(it) ?: mangaDir.findFile("$it.cbz") } + .mapNotNull { mangaDir.findFile(it) } .firstOrNull() } } @@ -127,7 +127,7 @@ class DownloadProvider(private val context: Context) { return mangaDir.listFiles().orEmpty().asList().filter { chapters.find { chp -> getValidChapterDirNames(chp).any { dir -> - mangaDir.findFile(dir) ?: mangaDir.findFile("$dir.cbz") != null + mangaDir.findFile(dir) != null } } == null || it.name?.endsWith(Downloader.TMP_DIR_SUFFIX) == true } @@ -174,10 +174,14 @@ class DownloadProvider(private val context: Context) { * @param chapter the chapter to query. */ fun getValidChapterDirNames(chapter: Chapter): List { + 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) ) diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/download/Downloader.kt b/app/src/main/java/eu/kanade/tachiyomi/data/download/Downloader.kt index ce5a533f5..98efefd00 100755 --- a/app/src/main/java/eu/kanade/tachiyomi/data/download/Downloader.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/download/Downloader.kt @@ -38,6 +38,7 @@ 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 @@ -64,10 +65,10 @@ class Downloader( private val sourceManager: SourceManager ) { - private val preferences: PreferencesHelper by injectLazy() - private val chapterCache: ChapterCache by injectLazy() + private val preferences: PreferencesHelper by injectLazy() + /** * Store for persisting downloads across restarts. */ @@ -496,41 +497,8 @@ class Downloader( // Only rename the directory if it's downloaded. if (download.status == Download.State.DOWNLOADED) { - var zip: UniFile? = null - if ( - preferences.saveChaptersAsCBZ().get() && - mangaDir.createFile("$dirname.cbz.tmp").also { zip = it } != null - ) { - ZipOutputStream(zip!!.openOutputStream().buffered()).use { zipOut -> - val compressionLevel = preferences.saveChaptersAsCBZLevel().get() - - zipOut.setLevel(compressionLevel) - - if (compressionLevel == 0) { - zipOut.setMethod(ZipEntry.STORED) - } - - tmpDir.listFiles()?.forEach { img -> - img.openInputStream().use { input -> - val data = input.readBytes() - val entry = ZipEntry(img.name) - if (compressionLevel == 0) { - val crc = CRC32() - val size = img.length() - crc.update(data) - entry.crc = crc.value - entry.compressedSize = size - entry.size = size - } - zipOut.putNextEntry(entry) - zipOut.write(data) - zipOut.closeEntry() - } - } - } - - zip!!.renameTo("$dirname.cbz") - tmpDir.delete() + if (preferences.saveChaptersAsCBZ().get()) { + archiveChapter(mangaDir, dirname, tmpDir) } else { tmpDir.renameTo(dirname) } @@ -540,6 +508,40 @@ class Downloader( } } + /** + * 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. */ diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/preference/PreferencesHelper.kt b/app/src/main/java/eu/kanade/tachiyomi/data/preference/PreferencesHelper.kt index e9673935b..35a8f0775 100755 --- a/app/src/main/java/eu/kanade/tachiyomi/data/preference/PreferencesHelper.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/preference/PreferencesHelper.kt @@ -209,6 +209,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) @@ -486,10 +488,6 @@ class PreferencesHelper(val context: Context) { fun dataSaverDownloader() = flowPrefs.getBoolean("data_saver_downloader", true) - fun saveChaptersAsCBZ() = flowPrefs.getBoolean("save_chapter_as_cbz", false) - - fun saveChaptersAsCBZLevel() = flowPrefs.getInt("save_chapter_as_cbz_level", 0) - fun allowLocalSourceHiddenFolders() = flowPrefs.getBoolean("allow_local_source_hidden_folders", false) fun authenticatorTimeRanges() = flowPrefs.getStringSet("biometric_time_ranges", mutableSetOf()) diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/loader/DownloadPageLoader.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/loader/DownloadPageLoader.kt index 6b8680197..faad167d5 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/loader/DownloadPageLoader.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/loader/DownloadPageLoader.kt @@ -2,19 +2,16 @@ 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.data.download.DownloadProvider import eu.kanade.tachiyomi.source.Source import eu.kanade.tachiyomi.source.model.Page import eu.kanade.tachiyomi.ui.reader.model.ReaderChapter import eu.kanade.tachiyomi.ui.reader.model.ReaderPage -import eu.kanade.tachiyomi.util.lang.compareToCaseInsensitiveNaturalOrder -import eu.kanade.tachiyomi.util.system.ImageUtil import rx.Observable import uy.kohesive.injekt.injectLazy import java.io.File -import java.util.zip.ZipFile /** * Loader used to load a chapter from the downloaded chapters. @@ -29,54 +26,34 @@ class DownloadPageLoader( // Needed to open input streams private val context: Application by injectLazy() - private val downloadProvider by lazy { DownloadProvider(context) } - /** * Returns an observable containing the pages found on this downloaded chapter. */ override fun getPages(): Observable> { - val chapterPath = downloadProvider.findChapterDir(chapter.chapter, manga, source) + val chapterPath = downloadManager.provider.findChapterDir(chapter.chapter, manga, source) + return if (chapterPath?.isFile == true) { + getPagesFromArchive(chapterPath) + } else { + getPagesFromDirectory() + } + } - if (chapterPath?.isFile == true) { - val zip = if (!File(chapterPath.filePath!!).canRead()) { - val tmpFile = File.createTempFile(chapterPath.name!!.replace(".cbz", ""), ".cbz") - val buffer = ByteArray(1024) - chapterPath.openInputStream().use { input -> - tmpFile.outputStream().use { fileOut -> - while (true) { - val length = input.read(buffer) - if (length <= 0) break - fileOut.write(buffer, 0, length) - } - fileOut.flush() - } - } - ZipFile(tmpFile.absolutePath) - } else ZipFile(chapterPath.filePath) + private fun getPagesFromArchive(chapterPath: UniFile): Observable> { + val loader = ZipPageLoader(File(chapterPath.filePath!!)) + return loader.getPages() + } - return zip.entries().toList() - .filter { !it.isDirectory && ImageUtil.isImage(it.name) { zip.getInputStream(it) } } - .sortedWith { f1, f2 -> f1.name.compareToCaseInsensitiveNaturalOrder(f2.name) } - .mapIndexed { i, entry -> - val streamFn = { zip.getInputStream(entry) } - ReaderPage(i).apply { - stream = streamFn + private fun getPagesFromDirectory(): Observable> { + return downloadManager.buildPageList(source, manga, chapter.chapter) + .map { pages -> + pages.map { page -> + ReaderPage(page.index, page.url, page.imageUrl) { + context.contentResolver.openInputStream(page.uri ?: Uri.EMPTY)!! + }.apply { status = Page.READY } } - .let { Observable.just(it) } - } else { - return downloadManager.buildPageList(source, manga, chapter.chapter) - .map { pages -> - pages.map { page -> - ReaderPage(page.index, page.url, page.imageUrl) { - context.contentResolver.openInputStream(page.uri ?: Uri.EMPTY)!! - }.apply { - status = Page.READY - } - } - } - } + } } override fun getPage(page: ReaderPage): Observable { diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsDownloadController.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsDownloadController.kt index 17a2adf1a..ea12889e1 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsDownloadController.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsDownloadController.kt @@ -68,23 +68,10 @@ class SettingsDownloadController : SettingsController() { titleRes = R.string.connected_to_wifi defaultValue = true } - - // SY --> switchPreference { bindTo(preferences.saveChaptersAsCBZ()) titleRes = R.string.save_chapter_as_cbz } - - intListPreference { - bindTo(preferences.saveChaptersAsCBZLevel()) - titleRes = R.string.save_chapter_as_cbz_level - entries = arrayOf("0", "1", "2", "3", "4", "5", "6", "7", "8", "9") - entryValues = entries - - visibleIf(preferences.saveChaptersAsCBZ()) { it } - } - // SY <-- - preferenceCategory { titleRes = R.string.pref_category_delete_chapters diff --git a/app/src/main/res/values-fr/strings_sy.xml b/app/src/main/res/values-fr/strings_sy.xml index 297b0b7a7..552fcfdff 100644 --- a/app/src/main/res/values-fr/strings_sy.xml +++ b/app/src/main/res/values-fr/strings_sy.xml @@ -186,10 +186,6 @@ Dossiers cachés de la source locale Autoriser la source locale à lire les dossiers cachés - - Enregistrer les chapitres sous CBZ - Niveau de compression CBZ - Temps de verrouillage biométrique Modifier les heures de verrouillage diff --git a/app/src/main/res/values-in/strings_sy.xml b/app/src/main/res/values-in/strings_sy.xml index ce704ace4..381f8d795 100644 --- a/app/src/main/res/values-in/strings_sy.xml +++ b/app/src/main/res/values-in/strings_sy.xml @@ -205,10 +205,6 @@ Info manga yang diedit Semua manga yang dibaca - - Simpan bab sebagai CBZ - Level kompresi CBZ - Waktu kunci biometrik Edit waktu kunci diff --git a/app/src/main/res/values-pt-rBR/strings_sy.xml b/app/src/main/res/values-pt-rBR/strings_sy.xml index 9076426a8..a426a2a26 100644 --- a/app/src/main/res/values-pt-rBR/strings_sy.xml +++ b/app/src/main/res/values-pt-rBR/strings_sy.xml @@ -201,10 +201,6 @@ Info. de mangá personalizada Todos os mangás lidos - - Salvar Capítulos como CBZ - Nível de Compressão de CBZ - Tempos de bloqueio biométrico Editar tempos de bloqueio diff --git a/app/src/main/res/values-ru/strings_sy.xml b/app/src/main/res/values-ru/strings_sy.xml index 43b8fbeb0..f4be6c56d 100644 --- a/app/src/main/res/values-ru/strings_sy.xml +++ b/app/src/main/res/values-ru/strings_sy.xml @@ -204,10 +204,6 @@ Сведенья пользователя о серии Все прочитанные серии - - Сохранить главы как «CBZ» - Степень сжатия «CBZ» - Биометрическое время блокировки Изменить биометрическое время блокировки diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 6895e06c7..f4b2e27b7 100755 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -398,6 +398,7 @@ Auto-download Download new chapters Manga in excluded categories will not be downloaded even if they are also in included categories. + Save as CBZ archive Tracking guide diff --git a/app/src/main/res/values/strings_sy.xml b/app/src/main/res/values/strings_sy.xml index e42284405..6552eb73e 100644 --- a/app/src/main/res/values/strings_sy.xml +++ b/app/src/main/res/values/strings_sy.xml @@ -205,10 +205,6 @@ Custom manga info All read manga - - Save Chapters as CBZ - CBZ Compression level - Biometric lock times Edit lock times