Refactor SpiderDen

This commit is contained in:
tarsin committed 2023-01-30 13:08:10 +08:00
1 parent e10e1c665f
commit cf31a31430
2 files changed
+70 -127

No files matched your search

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