mirror of
https://github.com/Aviortheking/Neo-Store.git
synced 2025-04-24 03:42:15 +00:00
Improve: Start working on new downloader
This commit is contained in:
parent
f465f5da2a
commit
3654b50d54
@ -0,0 +1,15 @@
|
||||
package com.looker.droidify.network
|
||||
|
||||
sealed class DownloadResult<T>(
|
||||
val progress: Long? = 0,
|
||||
val total: Long? = 0,
|
||||
val data: T? = null,
|
||||
val message: String? = null
|
||||
) {
|
||||
class Loading<T>(progress: Long? = null, total: Long? = null, data: T? = null) :
|
||||
DownloadResult<T>(progress, total, data)
|
||||
|
||||
class Success<T>(data: T?) : DownloadResult<T>(data = data)
|
||||
class Error<T>(message: String, data: T? = null) :
|
||||
DownloadResult<T>(data = data, message = message)
|
||||
}
|
118
src/main/kotlin/com/looker/droidify/network/DownloaderX.kt
Normal file
118
src/main/kotlin/com/looker/droidify/network/DownloaderX.kt
Normal file
@ -0,0 +1,118 @@
|
||||
package com.looker.droidify.network
|
||||
|
||||
import com.looker.droidify.utility.ProgressInputStream
|
||||
import com.looker.droidify.utility.extension.await
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.currentCoroutineContext
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.flow
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import okhttp3.Cache
|
||||
import okhttp3.Call
|
||||
import okhttp3.OkHttpClient
|
||||
import okhttp3.Request
|
||||
import java.io.File
|
||||
import java.io.FileOutputStream
|
||||
import java.net.InetSocketAddress
|
||||
import java.net.Proxy
|
||||
import java.util.concurrent.TimeUnit.SECONDS
|
||||
|
||||
object DownloaderX {
|
||||
private val client = OkHttpClient()
|
||||
|
||||
private data class ClientConfiguration(val cache: Cache?, val onion: Boolean)
|
||||
|
||||
private val clients = mutableMapOf<ClientConfiguration, OkHttpClient>()
|
||||
private val onionProxy = Proxy(Proxy.Type.SOCKS, InetSocketAddress("127.0.0.1", 9050))
|
||||
|
||||
var proxy: Proxy? = null
|
||||
set(value) {
|
||||
if (field != value) {
|
||||
synchronized(clients) {
|
||||
field = value
|
||||
clients.keys.removeAll { !it.onion }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun createCall(request: Request.Builder, authentication: String): Call {
|
||||
val oldRequest = request.build()
|
||||
val newRequest = if (authentication.isNotEmpty()) {
|
||||
request.addHeader("Authorization", authentication).build()
|
||||
} else {
|
||||
request.build()
|
||||
}
|
||||
val onion = oldRequest.url.host.endsWith(".onion")
|
||||
val client = synchronized(clients) {
|
||||
val proxy = if (onion) onionProxy else proxy
|
||||
val clientConfiguration = ClientConfiguration(null, onion)
|
||||
clients[clientConfiguration] ?: run {
|
||||
val client = this.client
|
||||
.newBuilder()
|
||||
.connectTimeout(30L, SECONDS)
|
||||
.readTimeout(15L, SECONDS)
|
||||
.writeTimeout(15L, SECONDS)
|
||||
.proxy(proxy).build()
|
||||
clients[clientConfiguration] = client
|
||||
client
|
||||
}
|
||||
}
|
||||
return client.newCall(newRequest)
|
||||
}
|
||||
|
||||
suspend fun startDownload(
|
||||
url: String,
|
||||
partialFile: File,
|
||||
authentication: String
|
||||
): Flow<DownloadResult<Unit>> = flow {
|
||||
val scope = currentCoroutineContext()
|
||||
val start = if (partialFile.exists()) partialFile.length()
|
||||
.let { if (it > 0L) it else null } else null
|
||||
val request = Request.Builder().url(url)
|
||||
.apply { if (start != null) addHeader("Range", "bytes=$start-") }
|
||||
|
||||
val response = createCall(request, authentication).await()
|
||||
|
||||
response.use { it ->
|
||||
if (it.code == 304) emit(DownloadResult.Loading())
|
||||
else {
|
||||
val body = it.body!!
|
||||
val append = start != null && it.header("Content-Range") != null
|
||||
val progressStart = if (append && start != null) start else 0L
|
||||
val progressTotal =
|
||||
body.contentLength().let { if (it >= 0L) it else null }
|
||||
?.let { progressStart + it }
|
||||
withContext(Dispatchers.IO + scope) {
|
||||
val inputStream = ProgressInputStream(body.byteStream()) {
|
||||
if (Thread.interrupted()) {
|
||||
launch { emit(DownloadResult.Error("Thread Interrupted")) }
|
||||
throw InterruptedException()
|
||||
}
|
||||
launch {
|
||||
emit(
|
||||
DownloadResult.Loading(
|
||||
progress = progressStart + it,
|
||||
total = progressTotal
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
inputStream.use { input ->
|
||||
val outputStream =
|
||||
if (append) FileOutputStream(partialFile, true)
|
||||
else FileOutputStream(partialFile)
|
||||
outputStream.use { output ->
|
||||
input.copyTo(output)
|
||||
output.fd.runCatching { sync() }
|
||||
.onSuccess { emit(DownloadResult.Success(Unit)) }
|
||||
.onFailure { emit(DownloadResult.Error(it.message.toString())) }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,42 @@
|
||||
package com.looker.droidify.utility.extension
|
||||
|
||||
import kotlinx.coroutines.suspendCancellableCoroutine
|
||||
import okhttp3.Call
|
||||
import okhttp3.Callback
|
||||
import okhttp3.Response
|
||||
import okhttp3.internal.closeQuietly
|
||||
import okio.IOException
|
||||
import kotlin.coroutines.resumeWithException
|
||||
|
||||
suspend fun Call.await(): Response {
|
||||
return suspendCancellableCoroutine { continuation ->
|
||||
enqueue(
|
||||
object : Callback {
|
||||
override fun onResponse(call: Call, response: Response) {
|
||||
if (!response.isSuccessful) {
|
||||
continuation.resumeWithException(Exception("HTTP error ${response.code}"))
|
||||
return
|
||||
}
|
||||
|
||||
continuation.resume(response) {
|
||||
response.body?.closeQuietly()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onFailure(call: Call, e: IOException) {
|
||||
// Don't bother with resuming the continuation if it is already cancelled.
|
||||
if (continuation.isCancelled) return
|
||||
continuation.resumeWithException(e)
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
continuation.invokeOnCancellation {
|
||||
try {
|
||||
cancel()
|
||||
} catch (ex: Throwable) {
|
||||
// Ignore cancel exception
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user