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:
parent
dc992ee932
commit
fe77aa9ab1
@ -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"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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()}." }
|
||||
}
|
||||
|
@ -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)
|
||||
)
|
||||
|
@ -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.
|
||||
*/
|
||||
|
@ -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())
|
||||
|
@ -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> {
|
||||
|
@ -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
|
||||
|
||||
|
@ -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>
|
||||
|
@ -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>
|
||||
|
@ -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>
|
||||
|
@ -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>
|
||||
|
@ -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>
|
||||
|
@ -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>
|
||||
|
Loading…
x
Reference in New Issue
Block a user