Add compress to CBZ on download (#6360)

(cherry picked from commit 5336c5b46e6985b6524e31ad837d7cf8907169fe)

# Conflicts:
#	app/src/main/java/eu/kanade/tachiyomi/data/download/Downloader.kt
#	app/src/main/java/eu/kanade/tachiyomi/ui/reader/loader/DownloadPageLoader.kt
#	app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsDownloadController.kt
This commit is contained in:
Seishirou101 2022-01-01 19:22:35 +00:00 committed by Jobobby04
parent dc992ee932
commit fe77aa9ab1
14 changed files with 77 additions and 132 deletions

View File

@ -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"
}
}
}

View File

@ -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()}." }
}

View File

@ -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<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)
)

View File

@ -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.
*/

View File

@ -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())

View File

@ -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<List<ReaderPage>> {
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<List<ReaderPage>> {
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<List<ReaderPage>> {
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<Int> {

View File

@ -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

View File

@ -186,10 +186,6 @@
<string name="pref_local_source_hidden_folders">Dossiers cachés de la source locale</string>
<string name="pref_local_source_hidden_folders_summery">Autoriser la source locale à lire les dossiers cachés</string>
<!-- Download settings -->
<string name="save_chapter_as_cbz">Enregistrer les chapitres sous CBZ</string>
<string name="save_chapter_as_cbz_level">Niveau de compression CBZ</string>
<!-- Security settings -->
<string name="biometric_lock_times">Temps de verrouillage biométrique</string>
<string name="action_edit_biometric_lock_times">Modifier les heures de verrouillage</string>

View File

@ -205,10 +205,6 @@
<string name="custom_manga_info">Info manga yang diedit</string>
<string name="all_read_manga">Semua manga yang dibaca</string>
<!-- Download settings -->
<string name="save_chapter_as_cbz">Simpan bab sebagai CBZ</string>
<string name="save_chapter_as_cbz_level">Level kompresi CBZ</string>
<!-- Security settings -->
<string name="biometric_lock_times">Waktu kunci biometrik</string>
<string name="action_edit_biometric_lock_times">Edit waktu kunci</string>

View File

@ -201,10 +201,6 @@
<string name="custom_manga_info">Info. de mangá personalizada</string>
<string name="all_read_manga">Todos os mangás lidos</string>
<!-- Download settings -->
<string name="save_chapter_as_cbz">Salvar Capítulos como CBZ</string>
<string name="save_chapter_as_cbz_level">Nível de Compressão de CBZ</string>
<!-- Security settings -->
<string name="biometric_lock_times">Tempos de bloqueio biométrico</string>
<string name="action_edit_biometric_lock_times">Editar tempos de bloqueio</string>

View File

@ -204,10 +204,6 @@
<string name="custom_manga_info">Сведенья пользователя о серии</string>
<string name="all_read_manga">Все прочитанные серии</string>
<!-- Download settings -->
<string name="save_chapter_as_cbz">Сохранить главы как «CBZ»</string>
<string name="save_chapter_as_cbz_level">Степень сжатия «CBZ»</string>
<!-- Security settings -->
<string name="biometric_lock_times">Биометрическое время блокировки</string>
<string name="action_edit_biometric_lock_times">Изменить биометрическое время блокировки</string>

View File

@ -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>

View File

@ -205,10 +205,6 @@
<string name="custom_manga_info">Custom manga info</string>
<string name="all_read_manga">All read manga</string>
<!-- Download settings -->
<string name="save_chapter_as_cbz">Save Chapters as CBZ</string>
<string name="save_chapter_as_cbz_level">CBZ Compression level</string>
<!-- Security settings -->
<string name="biometric_lock_times">Biometric lock times</string>
<string name="action_edit_biometric_lock_times">Edit lock times</string>