Refine exporting downloaded gallery as cbz archive (#698)

This commit is contained in:
FooIbar authored and GitHub committed 2024-02-07 07:17:55 +00:00
1 parent 37e895cdca
commit 8c27feec7a
7 files changed
+61 -34

No files matched your search

@@ -30,6 +30,7 @@ import com.hippo.ehviewer.jni.providePassword
import com.hippo.ehviewer.jni.releaseByteBuffer
import com.hippo.ehviewer.util.FileUtils
import com.hippo.unifile.UniFile
import com.hippo.unifile.displayPath
import java.nio.ByteBuffer
import kotlinx.coroutines.CoroutineStart
import kotlinx.coroutines.Job
@@ -52,7 +53,7 @@ class ArchivePageLoader(
) : PageLoader2(gid, startPage) {
private lateinit var pfd: ParcelFileDescriptor
private val hostJob = launch(start = CoroutineStart.LAZY) {
Log.d(DEBUG_TAG, "Open archive ${file.uri}")
Log.d(DEBUG_TAG, "Open archive ${file.uri.displayPath}")
pfd = file.openFileDescriptor("r")
size = openArchive(pfd.fd, pfd.statSize)
if (size == 0) {
@@ -80,7 +81,7 @@ class ArchivePageLoader(
super.stop()
closeArchive()
pfd.close()
Log.d(DEBUG_TAG, "Close archive ${file.uri} successfully!")
Log.d(DEBUG_TAG, "Close archive ${file.uri.displayPath} successfully!")
}
private val mJobMap = hashMapOf<Int, Job>()
@@ -187,6 +187,7 @@ sealed interface ImageSource : AutoCloseable
interface UniFileSource : ImageSource {
val source: UniFile
val type: String
}
interface ByteBufferSource : ImageSource {
@@ -25,6 +25,7 @@ private val xml = XML {
}
xmlDeclMode = XmlDeclMode.Charset
xmlVersion = XmlVersion.XML10
indent = 2
}
fun GalleryInfo.getComicInfo(): ComicInfo {
@@ -45,10 +45,16 @@ import io.ktor.client.statement.request
import io.ktor.http.isSuccess
import io.ktor.utils.io.jvm.nio.copyTo
import kotlin.io.path.readText
import kotlinx.coroutines.CancellationException
class SpiderDen(val info: GalleryInfo) {
private val gid = info.gid
var downloadDir: UniFile? = null
private set
constructor(info: GalleryInfo, dirname: String) : this(info) {
downloadDir = downloadLocation / dirname
}
@Volatile
@SpiderQueen.Mode
@@ -213,6 +219,9 @@ class SpiderDen(val info: GalleryInfo) {
if (snapshot != null) {
return object : UniFileSource, AutoCloseable by snapshot {
override val source = snapshot.data.asUniFile()
override val type by lazy {
snapshot.metadata.toNioPath().readText()
}
}
}
}
@@ -220,22 +229,28 @@ class SpiderDen(val info: GalleryInfo) {
val source = findImageFile(dir, index) ?: return null
return object : UniFileSource {
override val source = source
override val type by lazy {
FileUtils.getExtensionFromFilename(source.name)!!
}
override fun close() {}
}
}
suspend fun exportAsCbz(file: UniFile) = resourceScope {
val pages = info.pages
val comicInfo = closeable {
val f = downloadDir!! / COMIC_INFO_FILE
if (!f.exists()) writeComicInfo()
if (!f.exists()) {
writeComicInfo()
} else if (info.pages == 0) {
info.pages = readComicInfo(f)!!.pageCount
}
f.openFileDescriptor("r")
}
val pages = info.pages
val (fdBatch, names) = (0 until pages).parMap { idx ->
val ext = requireNotNull(getExtension(idx))
val f = autoCloseable { requireNotNull(getImageSource(idx)) }
closeable { f.source.openFileDescriptor("r") }.fd to perFilename(idx, ext)
val f = autoCloseable { getImageSource(idx) ?: throw CancellationException("Image #$idx not found") }
closeable { f.source.openFileDescriptor("r") }.fd to perFilename(idx, f.type)
}.run { plus(comicInfo.fd to COMIC_INFO_FILE) }.unzip()
val arcFd = closeable { file.openFileDescriptor("rw") }
archiveFdBatch(fdBatch.toIntArray(), names.toTypedArray(), arcFd.fd, pages + 1)
@@ -245,12 +260,14 @@ class SpiderDen(val info: GalleryInfo) {
downloadDir = getGalleryDownloadDir(gid)?.takeIf { it.isDirectory }
}
suspend fun writeComicInfo() {
suspend fun writeComicInfo(fetchMetadata: Boolean = true) {
downloadDir?.run {
createFile(COMIC_INFO_FILE)?.also {
runCatching {
withNonCancellableContext {
EhEngine.fillGalleryListByApi(listOf(info))
if (fetchMetadata) {
withNonCancellableContext {
EhEngine.fillGalleryListByApi(listOf(info))
}
}
info.getComicInfo().write(it)
}.onFailure {
@@ -82,7 +82,6 @@ import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
import androidx.core.text.parseAsHtml
import androidx.lifecycle.compose.LifecycleResumeEffect
import arrow.core.partially1
import coil3.imageLoader
import com.hippo.ehviewer.EhApplication.Companion.galleryDetailCache
@@ -908,18 +907,10 @@ fun GalleryDetailScreen(args: GalleryDetailScreenArgs, navigator: DestinationsNa
val startPage by rememberInVM {
EhDB.getReadProgressFlow(galleryInfo.gid)
}.collectAsState(0)
var readButtonText by rememberSaveable { mutableStateOf(readText) }
LifecycleResumeEffect(Unit) {
coroutineScope.launchIO {
runSuspendCatching {
readButtonText = if (startPage == 0) {
readText
} else {
context.getString(R.string.read_from, startPage + 1)
}
}
}
onPauseOrDispose { }
val readButtonText = if (startPage == 0) {
readText
} else {
stringResource(R.string.read_from, startPage + 1)
}
val downloadState by EhDownloadManager.collectDownloadState(gid)
val downloadButtonText = when (downloadState) {
@@ -1241,15 +1232,26 @@ fun GalleryDetailScreen(args: GalleryDetailScreenArgs, navigator: DestinationsNa
)
}
if (uri != null) {
val success = dialogState.bgWork {
withIOContext {
SpiderDen(info).run {
initDownloadDirIfExist()
exportAsCbz(uri.asUniFile())
val file = uri.asUniFile()
val success = runCatching {
dialogState.bgWork {
withIOContext {
SpiderDen(info).run {
initDownloadDirIfExist()
exportAsCbz(file)
}
}
}
}.getOrElse {
it.printStackTrace()
false
}
val msg = if (success) {
exportSuccess
} else {
file.delete()
exportFailed
}
val msg = if (success) exportSuccess else exportFailed
snackbarState.showSnackbar(message = msg)
}
}
@@ -27,6 +27,7 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.input.nestedscroll.nestedScroll
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import arrow.fx.coroutines.parMap
import com.hippo.ehviewer.EhDB
import com.hippo.ehviewer.R
import com.hippo.ehviewer.Settings
@@ -38,6 +39,7 @@ import com.hippo.ehviewer.client.parser.GalleryDetailUrlParser
import com.hippo.ehviewer.download.DownloadManager
import com.hippo.ehviewer.download.downloadLocation
import com.hippo.ehviewer.spider.COMIC_INFO_FILE
import com.hippo.ehviewer.spider.SpiderDen
import com.hippo.ehviewer.spider.SpiderQueen.Companion.SPIDER_INFO_FILENAME
import com.hippo.ehviewer.spider.readComicInfo
import com.hippo.ehviewer.spider.readCompatFromUniFile
@@ -153,7 +155,7 @@ fun DownloadScreen(navigator: DestinationsNavigator) {
return runCatching {
val (gid, token) = file.findFile(SPIDER_INFO_FILENAME)?.let {
readCompatFromUniFile(it)?.run {
GalleryDetailUrlParser.Result(gid, token!!)
GalleryDetailUrlParser.Result(gid, token)
}
} ?: file.findFile(COMIC_INFO_FILE)?.let {
readComicInfo(it)?.run {
@@ -186,9 +188,12 @@ fun DownloadScreen(navigator: DestinationsNavigator) {
if (result.isEmpty()) {
launchSnackBar(RESTORE_COUNT_MSG(restoreDirCount))
} else {
val count = result.filterNot { it.title.isNullOrBlank() }.map {
EhDB.putDownloadDirname(it.gid, it.dirname)
DownloadManager.restoreDownload(it.galleryInfo, it.dirname)
val count = result.parMap {
if (it.pages != 0) {
EhDB.putDownloadDirname(it.gid, it.dirname)
DownloadManager.restoreDownload(it.galleryInfo, it.dirname)
SpiderDen(it.galleryInfo, it.dirname).writeComicInfo(false)
}
}.size
launchSnackBar(RESTORE_COUNT_MSG(count + restoreDirCount))
}
@@ -47,7 +47,7 @@ fun ProgressDialog() {
suspend fun <R> DialogState.bgWork(work: suspend () -> R) = dialog { cont ->
ProgressDialog()
LaunchedEffect(work) {
val result = runCatching { work() }.onFailure { it.printStackTrace() }
val result = runCatching { work() }
cont.resumeWith(result)
}
}