Migrate image downloading to ktor
This commit is contained in:
1 parent
6117ea755f
commit
057ba0c6e1
3 files changed
+66
-69
No files matched your search
@@ -13,24 +13,21 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.hippo.ehviewer.client
|
||||
|
||||
package com.hippo.ehviewer.client;
|
||||
import com.hippo.okhttp.ChromeRequestBuilder
|
||||
import io.ktor.http.HttpMessageBuilder
|
||||
|
||||
import com.hippo.okhttp.ChromeRequestBuilder;
|
||||
|
||||
public class EhRequestBuilder extends ChromeRequestBuilder {
|
||||
|
||||
public EhRequestBuilder(String url, String referer) {
|
||||
this(url, referer, null);
|
||||
}
|
||||
|
||||
public EhRequestBuilder(String url, String referer, String origin) {
|
||||
super(url);
|
||||
if (referer != null) {
|
||||
addHeader("Referer", referer);
|
||||
}
|
||||
if (origin != null) {
|
||||
addHeader("Origin", origin);
|
||||
}
|
||||
class EhRequestBuilder @JvmOverloads constructor(
|
||||
url: String,
|
||||
referer: String?,
|
||||
origin: String? = null
|
||||
) : ChromeRequestBuilder(url) {
|
||||
init {
|
||||
referer?.let { addHeader("Referer", it) }
|
||||
origin?.let { addHeader("Origin", it) }
|
||||
}
|
||||
}
|
||||
|
||||
fun HttpMessageBuilder.referer(value: String?): Unit =
|
||||
value?.let { headers.append("Referer", it) } ?: Unit
|
||||
@@ -15,16 +15,17 @@
|
||||
*/
|
||||
package com.hippo.ehviewer.spider
|
||||
|
||||
import android.webkit.MimeTypeMap
|
||||
import coil.disk.DiskCache
|
||||
import com.hippo.Native.mapFd
|
||||
import com.hippo.Native.unmapDirectByteBuffer
|
||||
import com.hippo.ehviewer.EhApplication
|
||||
import com.hippo.ehviewer.EhApplication.Companion.application
|
||||
import com.hippo.ehviewer.EhDB
|
||||
import com.hippo.ehviewer.Settings
|
||||
import com.hippo.ehviewer.client.EhCacheKeyFactory
|
||||
import com.hippo.ehviewer.client.EhUtils.getSuitableTitle
|
||||
import com.hippo.ehviewer.client.data.GalleryInfo
|
||||
import com.hippo.ehviewer.client.referer
|
||||
import com.hippo.ehviewer.gallery.PageLoader2
|
||||
import com.hippo.image.Image.ByteBufferSource
|
||||
import com.hippo.unifile.RawFile
|
||||
@@ -32,16 +33,27 @@ import com.hippo.unifile.UniFile
|
||||
import com.hippo.yorozuya.FileUtils
|
||||
import com.hippo.yorozuya.MathUtils
|
||||
import com.hippo.yorozuya.Utilities
|
||||
import okhttp3.ResponseBody
|
||||
import io.ktor.client.plugins.onDownload
|
||||
import io.ktor.client.request.prepareGet
|
||||
import io.ktor.client.statement.HttpResponse
|
||||
import io.ktor.client.statement.bodyAsChannel
|
||||
import io.ktor.http.contentLength
|
||||
import io.ktor.http.contentType
|
||||
import io.ktor.utils.io.jvm.nio.copyTo
|
||||
import kotlinx.coroutines.cancel
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import java.io.File
|
||||
import java.io.FileInputStream
|
||||
import java.io.FileOutputStream
|
||||
import java.io.IOException
|
||||
import java.io.RandomAccessFile
|
||||
import java.nio.ByteBuffer
|
||||
import java.nio.channels.FileChannel
|
||||
import java.util.Locale
|
||||
import kotlin.io.path.readText
|
||||
|
||||
private val client = EhApplication.ktorClient
|
||||
|
||||
class SpiderDen(private val mGalleryInfo: GalleryInfo) {
|
||||
private val mGid: Long = mGalleryInfo.gid
|
||||
private var mDownloadDir: UniFile? = getGalleryDownloadDir(mGid)
|
||||
@@ -173,36 +185,45 @@ class SpiderDen(private val mGalleryInfo: GalleryInfo) {
|
||||
return dir.createFile(generateImageFilename(index, ext))
|
||||
}
|
||||
|
||||
// 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
|
||||
@Throws(IOException::class)
|
||||
fun makeHttpCallAndSaveImage(index: Int, url: String, referer: String?, notifyProgress: (Long, Long, Int) -> Unit): Boolean {
|
||||
return runBlocking {
|
||||
client.prepareGet(url) {
|
||||
var state: Long = 0
|
||||
referer(referer)
|
||||
onDownload { bytesSentTotal, contentLength ->
|
||||
runCatching { notifyProgress(contentLength, bytesSentTotal, (bytesSentTotal - state).toInt()) }
|
||||
.onFailure { cancel() }
|
||||
state = bytesSentTotal
|
||||
}
|
||||
}.execute {
|
||||
if (it.status.value >= 400) return@execute false
|
||||
saveFromHttpResponse(index, it)
|
||||
}
|
||||
return receivedSize
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun saveFromHttpResponse(index: Int, body: HttpResponse): Boolean {
|
||||
val extension = body.contentType()?.contentSubtype ?: "jpg"
|
||||
val length = body.contentLength() ?: return false
|
||||
|
||||
suspend fun doSave(outChannel: FileChannel): Long {
|
||||
return body.bodyAsChannel().copyTo(outChannel)
|
||||
}
|
||||
|
||||
findDownloadFileForIndex(index, extension)?.runCatching {
|
||||
openOutputStream().use { outputStream ->
|
||||
(outputStream as FileOutputStream).channel.use { return doSave(it) }
|
||||
(outputStream as FileOutputStream).channel.use { return doSave(it) == length }
|
||||
}
|
||||
}?.onFailure {
|
||||
it.printStackTrace()
|
||||
return -1
|
||||
return false
|
||||
}
|
||||
|
||||
// Read Mode, allow save to cache
|
||||
if (mMode == SpiderQueen.MODE_READ) {
|
||||
val key = EhCacheKeyFactory.getImageKey(mGid, index)
|
||||
val editor = sCache.edit(key) ?: return -1
|
||||
val editor = sCache.edit(key) ?: return false
|
||||
var received: Long = 0
|
||||
runCatching {
|
||||
editor.metadata.toFile().writeText(extension)
|
||||
@@ -212,14 +233,14 @@ class SpiderDen(private val mGalleryInfo: GalleryInfo) {
|
||||
}.onFailure {
|
||||
editor.abort()
|
||||
it.printStackTrace()
|
||||
return -1
|
||||
return false
|
||||
}.onSuccess {
|
||||
editor.commit()
|
||||
return received
|
||||
return received == length
|
||||
}
|
||||
}
|
||||
|
||||
return -1
|
||||
return false
|
||||
}
|
||||
|
||||
fun saveToUniFile(index: Int, file: UniFile): Boolean {
|
||||
|
||||
@@ -68,7 +68,6 @@ import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
import eu.kanade.tachiyomi.ui.reader.loader.PageLoader;
|
||||
import okhttp3.Call;
|
||||
import okhttp3.OkHttpClient;
|
||||
import okhttp3.Request;
|
||||
import okhttp3.Response;
|
||||
@@ -1039,7 +1038,7 @@ public final class SpiderQueen implements Runnable {
|
||||
boolean forceHtml = false;
|
||||
boolean leakSkipHathKey = false;
|
||||
|
||||
for (int i = 0; i < 5; i++) {
|
||||
for (int i = 0; i < 2; i++) {
|
||||
String imageUrl = null;
|
||||
String localShowKey;
|
||||
|
||||
@@ -1145,44 +1144,24 @@ public final class SpiderQueen implements Runnable {
|
||||
Log.d(TAG, "Start download image " + index);
|
||||
}
|
||||
|
||||
Call call = mHttpClient.newCall(new EhRequestBuilder(targetImageUrl, referer).build());
|
||||
Response response = call.execute();
|
||||
|
||||
if (response.code() >= 400) {
|
||||
// Maybe 404
|
||||
response.close();
|
||||
error = "Bad code: " + response.code();
|
||||
forceHtml = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
var body = response.body();
|
||||
var received = mSpiderDen.saveFromBufferedSource(index, body , (contentLength, receivedSize, bytesRead) -> {
|
||||
var success = mSpiderDen.makeHttpCallAndSaveImage(index, targetImageUrl, referer, (contentLength, receivedSize, bytesRead) -> {
|
||||
if (mStoped) throw new CancellationException();
|
||||
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();
|
||||
// Check Stopped
|
||||
if (mStoped) {
|
||||
error = "Interrupted";
|
||||
break;
|
||||
}
|
||||
|
||||
// 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");
|
||||
}
|
||||
if (!success) {
|
||||
Log.e(TAG, "Can't download all of image data");
|
||||
error = "Incomplete";
|
||||
forceHtml = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (mSpiderDen.checkPlainText(index)) {
|
||||
|
||||
Reference in new issue
Block a user