Add Save As CBZ (#84)

Co-authored-by: Fahad1998 <f1998>
This commit is contained in:
Fahad1998 2020-08-23 03:37:03 +06:00 committed by GitHub
parent fae290cf22
commit 4333999b85
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 122 additions and 12 deletions

View File

@ -145,7 +145,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

View File

@ -89,7 +89,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) }
.mapNotNull { mangaDir?.findFile(it) ?: mangaDir?.findFile("$it.cbz") }
.firstOrNull()
}
@ -104,7 +104,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) }
.mapNotNull { mangaDir.findFile(it) ?: mangaDir.findFile("$it.cbz") }
.firstOrNull()
}
}
@ -127,7 +127,7 @@ class DownloadProvider(private val context: Context) {
(
chapters.find { chp ->
getValidChapterDirNames(chp).any { dir ->
mangaDir.findFile(dir) != null
mangaDir.findFile(dir) ?: mangaDir.findFile("$dir.cbz") != null
}
} == null
) || it.name?.endsWith("_tmp") == true

View File

@ -11,6 +11,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.model.Page
import eu.kanade.tachiyomi.source.online.HttpSource
@ -22,7 +23,11 @@ import eu.kanade.tachiyomi.util.lang.plusAssign
import eu.kanade.tachiyomi.util.storage.DiskUtil
import eu.kanade.tachiyomi.util.storage.saveTo
import eu.kanade.tachiyomi.util.system.ImageUtil
import java.io.BufferedOutputStream
import java.io.File
import java.util.zip.CRC32
import java.util.zip.ZipEntry
import java.util.zip.ZipOutputStream
import kotlinx.coroutines.async
import okhttp3.Response
import rx.Observable
@ -30,6 +35,8 @@ import rx.android.schedulers.AndroidSchedulers
import rx.schedulers.Schedulers
import rx.subscriptions.CompositeSubscription
import timber.log.Timber
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
import uy.kohesive.injekt.injectLazy
/**
@ -53,6 +60,8 @@ class Downloader(
private val sourceManager: SourceManager
) {
private val preferences: PreferencesHelper = Injekt.get()
private val chapterCache: ChapterCache by injectLazy()
/**
@ -464,7 +473,39 @@ class Downloader(
// Only rename the directory if it's downloaded.
if (download.status == Download.DOWNLOADED) {
tmpDir.renameTo(dirname)
mangaDir.findFile(dirname + ".tmp")?.delete()
if (preferences.saveChaptersAsCBZ().get()) {
val zip = mangaDir.createFile(dirname + ".tmp")
val zipOut = ZipOutputStream(BufferedOutputStream(zip.openOutputStream()))
zipOut.setLevel(preferences.saveChaptersAsCBZLevel().get())
if (preferences.saveChaptersAsCBZLevel().get() == 0) {
zipOut.setMethod(ZipEntry.STORED)
}
tmpDir.listFiles()?.forEach { img ->
val input = img.openInputStream()
val data = input.readBytes()
val entry = ZipEntry(img.name)
if (preferences.saveChaptersAsCBZLevel().get() == 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)
input.close()
}
zipOut.close()
zip.renameTo(dirname + ".cbz")
tmpDir.delete()
} else {
tmpDir.renameTo(dirname)
}
cache.addChapter(dirname, mangaDir, download.manga)
DiskUtil.createNoMediaFile(tmpDir, context)

View File

@ -302,4 +302,8 @@ object PreferenceKeys {
const val dataSaverServer = "data_saver_server"
const val dataSaverColorBW = "data_saver_color_bw"
const val saveChaptersAsCBZ = "save_chapter_as_cbz"
const val saveChaptersAsCBZLevel = "save_chapter_as_cbz_level"
}

View File

@ -407,4 +407,8 @@ class PreferencesHelper(val context: Context) {
fun dataSaverServer() = flowPrefs.getString(Keys.dataSaverServer, "")
fun dataSaverColorBW() = flowPrefs.getBoolean(Keys.dataSaverColorBW, false)
fun saveChaptersAsCBZ() = flowPrefs.getBoolean(Keys.saveChaptersAsCBZ, false)
fun saveChaptersAsCBZLevel() = flowPrefs.getInt(Keys.saveChaptersAsCBZLevel, 0)
}

View File

@ -4,10 +4,16 @@ import android.app.Application
import android.net.Uri
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 java.io.File
import java.util.zip.ZipEntry
import java.util.zip.ZipFile
import rx.Observable
import uy.kohesive.injekt.injectLazy
@ -26,20 +32,54 @@ class DownloadPageLoader(
*/
private val context by injectLazy<Application>()
private val downloadProvider by lazy { DownloadProvider(context) }
/**
* Returns an observable containing the pages found on this downloaded chapter.
*/
override fun getPages(): 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 {
val chapterPath = downloadProvider.findChapterDir(chapter.chapter, manga, source)
if (chapterPath?.isFile!!) {
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)
return zip.entries().toList()
.filter { !it.isDirectory && ImageUtil.isImage(it.name) { zip.getInputStream(it) } }
.sortedWith(Comparator<ZipEntry> { f1, f2 -> f1.name.compareToCaseInsensitiveNaturalOrder(f2.name) })
.mapIndexed { i, entry ->
val streamFn = { zip.getInputStream(entry) }
ReaderPage(i).apply {
stream = streamFn
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

@ -64,6 +64,24 @@ class SettingsDownloadController : SettingsController() {
titleRes = R.string.pref_download_only_over_wifi
defaultValue = true
}
switchPreference {
key = Keys.saveChaptersAsCBZ
titleRes = R.string.save_chapter_as_cbz
defaultValue = false
}
intListPreference {
titleRes = R.string.save_chapter_as_cbz_level
key = Keys.saveChaptersAsCBZLevel
entries = arrayOf("0", "1", "2", "3", "4", "5", "6", "7", "8", "9")
entryValues = entries
defaultValue = "0"
preferences.saveChaptersAsCBZ().asImmediateFlow { isVisible = it }
.launchIn(scope)
}
preferenceCategory {
titleRes = R.string.pref_category_delete_chapters

View File

@ -478,5 +478,8 @@
<item quantity="other">%2$s, %1$d pages</item>
</plurals>
<string name="save_chapter_as_cbz">Save Chapters as CBZ</string>
<string name="save_chapter_as_cbz_level">CBZ Compression level</string>
</resources>