From cf31a31430ba7b9a2379a7ba5708f794dce5661d Mon Sep 17 00:00:00 2001 From: tarsin Date: Mon, 30 Jan 2023 13:08:10 +0800 Subject: [PATCH] Refactor SpiderDen --- .../com/hippo/ehviewer/spider/SpiderDen.kt | 99 +++++++++---------- .../hippo/ehviewer/spider/SpiderQueen.java | 98 +++++------------- 2 files changed, 70 insertions(+), 127 deletions(-) diff --git a/app/src/main/java/com/hippo/ehviewer/spider/SpiderDen.kt b/app/src/main/java/com/hippo/ehviewer/spider/SpiderDen.kt index d311abcad..e399cb6f3 100644 --- a/app/src/main/java/com/hippo/ehviewer/spider/SpiderDen.kt +++ b/app/src/main/java/com/hippo/ehviewer/spider/SpiderDen.kt @@ -17,6 +17,7 @@ package com.hippo.ehviewer.spider +import android.webkit.MimeTypeMap import coil.annotation.ExperimentalCoilApi import coil.disk.DiskCache import com.hippo.Native.mapFd @@ -30,23 +31,19 @@ import com.hippo.ehviewer.client.data.GalleryInfo import com.hippo.ehviewer.gallery.PageLoader2 import com.hippo.image.Image.ByteBufferSource import com.hippo.io.UniFileInputStreamPipe -import com.hippo.io.UniFileOutputStreamPipe import com.hippo.streampipe.InputStreamPipe -import com.hippo.streampipe.OutputStreamPipe import com.hippo.unifile.RawFile import com.hippo.unifile.UniFile import com.hippo.yorozuya.FileUtils import com.hippo.yorozuya.IOUtils import com.hippo.yorozuya.MathUtils import com.hippo.yorozuya.Utilities -import okio.buffer -import okio.sink +import okhttp3.ResponseBody import java.io.File import java.io.FileInputStream import java.io.FileOutputStream import java.io.IOException import java.io.InputStream -import java.io.OutputStream import java.io.RandomAccessFile import java.nio.ByteBuffer import java.nio.channels.FileChannel @@ -177,63 +174,59 @@ class SpiderDen(private val mGalleryInfo: GalleryInfo) { return removeFromCache(index) or removeFromDownloadDir(index) } - private fun openCacheOutputStreamPipe(index: Int, extension: String): OutputStreamPipe? { - val key = EhCacheKeyFactory.getImageKey(mGid, index) - val editor = sCache.edit(key) ?: return null - try { - editor.metadata.toFile().sink().buffer().use { sink -> sink.writeUtf8(extension) } - } catch (e: IOException) { - e.printStackTrace() - } - return object : OutputStreamPipe { - private var os: OutputStream? = null - override fun release() { - editor.commit() - } - - @Throws(IOException::class) - override fun open(): OutputStream { - if (os != null) IOUtils.closeQuietly(os) - os = FileOutputStream(editor.data.toFile()) - return os!! - } - - override fun close() { - if (os != null) IOUtils.closeQuietly(os) - } - } - } - - /** - * @param extension without dot - */ - private fun openDownloadOutputStreamPipe(index: Int, extension: String): OutputStreamPipe? { - var extension = extension + private fun findDownloadFileForIndex(index: Int, extension: String): UniFile? { val dir = downloadDir ?: return null - extension = fixExtension(".$extension") - val file = dir.createFile(generateImageFilename(index, extension)) - return file?.let { UniFileOutputStreamPipe(it) } + val ext = fixExtension(".$extension") + return dir.createFile(generateImageFilename(index, ext)) } - fun openOutputStreamPipe(index: Int, extension: String): OutputStreamPipe? { - return when (mMode) { - SpiderQueen.MODE_READ -> { - // Return the download pipe is the gallery has been downloaded - var pipe = openDownloadOutputStreamPipe(index, extension) - if (pipe == null) { - pipe = openCacheOutputStreamPipe(index, extension) + // TODO: Support cancellation after spiderqueen coroutinize + fun saveFromBufferedSource(index: Int, body: ResponseBody, notifyProgress: (Long, Long, Int) -> Unit): Long { + val extension = MimeTypeMap.getSingleton().getExtensionFromMimeType(body.contentType().toString()) ?: "jpg" + + fun doSave(outChannel: FileChannel): Long { + var receivedSize: Long = 0 + body.source().use { source -> + while (true) { + val bytesRead = outChannel.transferFrom(source, receivedSize, TRANSFER_BLOCK) + receivedSize += bytesRead + notifyProgress(body.contentLength(), receivedSize, bytesRead.toInt()) + if (bytesRead.toInt() == 0) break } - pipe } + return receivedSize + } - SpiderQueen.MODE_DOWNLOAD -> { - openDownloadOutputStreamPipe(index, extension) + findDownloadFileForIndex(index, extension)?.runCatching { + openOutputStream().use { outputStream -> + (outputStream as FileOutputStream).channel.use { return doSave(it) } } + }?.onFailure { + it.printStackTrace() + return -1 + } - else -> { - null + // Read Mode, allow save to cache + if (mMode == SpiderQueen.MODE_READ) { + val key = EhCacheKeyFactory.getImageKey(mGid, index) + val editor = sCache.edit(key) ?: return -1 + var received: Long = 0 + runCatching { + editor.metadata.toFile().writeText(extension) + FileOutputStream(editor.data.toFile()).use { outputStream -> + outputStream.channel.use { received = doSave(it) } + } + }.onFailure { + editor.abort() + it.printStackTrace() + return -1 + }.onSuccess { + editor.commit() + return received } } + + return -1 } private fun openCacheInputStreamPipe(index: Int): InputStreamPipe? { @@ -371,6 +364,8 @@ class SpiderDen(private val mGalleryInfo: GalleryInfo) { } companion object { + private const val TRANSFER_BLOCK: Long = 40960 + // We use data to store image file, and metadata for image type private val sCache by lazy { DiskCache.Builder().directory(File(application.cacheDir, "gallery_image")) diff --git a/app/src/main/java/com/hippo/ehviewer/spider/SpiderQueen.java b/app/src/main/java/com/hippo/ehviewer/spider/SpiderQueen.java index bd3362d97..d7ad325b3 100644 --- a/app/src/main/java/com/hippo/ehviewer/spider/SpiderQueen.java +++ b/app/src/main/java/com/hippo/ehviewer/spider/SpiderQueen.java @@ -46,7 +46,6 @@ import com.hippo.ehviewer.client.parser.GalleryPageParser; import com.hippo.ehviewer.client.parser.GalleryPageUrlParser; import com.hippo.image.Image; import com.hippo.streampipe.InputStreamPipe; -import com.hippo.streampipe.OutputStreamPipe; import com.hippo.unifile.UniFile; import com.hippo.util.ExceptionUtils; import com.hippo.yorozuya.IOUtils; @@ -57,7 +56,6 @@ import com.hippo.yorozuya.thread.PriorityThread; import com.hippo.yorozuya.thread.PriorityThreadFactory; import java.io.BufferedInputStream; -import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; @@ -77,11 +75,9 @@ import java.util.concurrent.atomic.AtomicReference; import eu.kanade.tachiyomi.ui.reader.loader.PageLoader; import okhttp3.Call; -import okhttp3.MediaType; import okhttp3.OkHttpClient; import okhttp3.Request; import okhttp3.Response; -import okhttp3.ResponseBody; public final class SpiderQueen implements Runnable { @@ -1213,7 +1209,6 @@ public final class SpiderQueen implements Runnable { Call call = mHttpClient.newCall(new EhRequestBuilder(targetImageUrl, referer).build()); Response response = call.execute(); - ResponseBody responseBody = response.body(); if (response.code() >= 400) { // Maybe 404 @@ -1223,78 +1218,31 @@ public final class SpiderQueen implements Runnable { continue; } - // Get extension - String extension = null; - MediaType mediaType = responseBody.contentType(); - if (mediaType != null) { - extension = MimeTypeMap.getSingleton().getExtensionFromMimeType(mediaType.toString()); + var body = response.body(); + var received = mSpiderDen.saveFromBufferedSource(index, body , (contentLength, receivedSize, bytesRead) -> { + mPagePercentMap.put(index, (float) receivedSize / contentLength); + notifyPageDownload(index, contentLength, receivedSize, bytesRead); + return null; + }); + var contentLength = body.contentLength(); + body.close(); + response.close(); + + if (received == -1) { + error = GetText.getString(R.string.error_write_failed); + response.close(); + break; } - OutputStreamPipe osPipe = null; - try { - // Get out put pipe - osPipe = mSpiderDen.openOutputStreamPipe(index, extension); - if (osPipe == null) { - // Can't get pipe - error = GetText.getString(R.string.error_write_failed); - response.close(); - break; - } - - long contentLength = responseBody.contentLength(); - long receivedSize = 0; - OutputStream os = osPipe.open(); - if (os instanceof FileOutputStream fileOutputStream) { - try (var channel = fileOutputStream.getChannel(); var source = responseBody.source(); responseBody; response) { - while (!mStoped) { - // Is 40k a good size ? - long bytesRead = channel.transferFrom(source, receivedSize, 40960); - if (bytesRead == 0) { - break; - } - receivedSize += bytesRead; - if (contentLength > 0) { - mPagePercentMap.put(index, (float) receivedSize / contentLength); - } - notifyPageDownload(index, contentLength, receivedSize, (int) bytesRead); - } - } - } else { - final byte[] data = new byte[1024 * 4]; - try (var is = responseBody.byteStream(); responseBody; response) { - while (!mStoped) { - int bytesRead = is.read(data); - if (bytesRead == -1) { - break; - } - os.write(data, 0, bytesRead); - receivedSize += bytesRead; - // Update page percent - if (contentLength > 0) { - mPagePercentMap.put(index, (float) receivedSize / contentLength); - } - // Notify listener - notifyPageDownload(index, contentLength, receivedSize, bytesRead); - } - os.flush(); - } - } - - // check download size - if (contentLength >= 0) { - if (receivedSize < contentLength) { - Log.e(TAG, "Can't download all of image data"); - error = "Incomplete"; - forceHtml = true; - continue; - } else if (receivedSize > contentLength) { - Log.w(TAG, "Received data is more than contentLength"); - } - } - } finally { - if (osPipe != null) { - osPipe.close(); - osPipe.release(); + // check download size + if (contentLength >= 0) { + if (received < contentLength) { + Log.e(TAG, "Can't download all of image data"); + error = "Incomplete"; + forceHtml = true; + continue; + } else if (received > contentLength) { + Log.w(TAG, "Received data is more than contentLength"); } }