Rebuilding Downloader
This commit is contained in:
@@ -20,6 +20,7 @@ package xyz.quaver.pupil.util.download
|
|||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.ContextWrapper
|
import android.content.ContextWrapper
|
||||||
|
import android.util.SparseArray
|
||||||
import androidx.core.content.ContextCompat
|
import androidx.core.content.ContextCompat
|
||||||
import androidx.preference.PreferenceManager
|
import androidx.preference.PreferenceManager
|
||||||
import kotlinx.coroutines.*
|
import kotlinx.coroutines.*
|
||||||
@@ -116,11 +117,9 @@ class Cache(context: Context) : ContextWrapper(context) {
|
|||||||
{ xyz.quaver.hiyobi.getReader(galleryID) }
|
{ xyz.quaver.hiyobi.getReader(galleryID) }
|
||||||
).map {
|
).map {
|
||||||
CoroutineScope(Dispatchers.IO).async {
|
CoroutineScope(Dispatchers.IO).async {
|
||||||
try {
|
kotlin.runCatching {
|
||||||
it.invoke()
|
it.invoke()
|
||||||
} catch (e: Exception) {
|
}.getOrNull()
|
||||||
null
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}.awaitAll().filterNotNull()
|
}.awaitAll().filterNotNull()
|
||||||
} else {
|
} else {
|
||||||
@@ -140,8 +139,23 @@ class Cache(context: Context) : ContextWrapper(context) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun getImage(galleryID: Int): File? {
|
fun getImages(galleryID: Int): SparseArray<File>? {
|
||||||
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
|
val regex = Regex("[0-9]+")
|
||||||
|
val gallery = getCachedGallery(galleryID) ?: return null
|
||||||
|
|
||||||
|
return SparseArray<File>().apply {
|
||||||
|
gallery.listFiles { file ->
|
||||||
|
file.nameWithoutExtension.matches(regex)
|
||||||
|
}?.forEach {
|
||||||
|
append(it.nameWithoutExtension.toInt(), it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun putImage(galleryID: Int, index: Int, data: ByteArray) {
|
||||||
|
val cache = getCachedGallery(galleryID) ?: File(cacheDir, "imageCache/$galleryID")
|
||||||
|
|
||||||
|
File(cache, index.toString()).writeBytes(data)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -20,18 +20,27 @@ package xyz.quaver.pupil.util.download
|
|||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.ContextWrapper
|
import android.content.ContextWrapper
|
||||||
|
import android.content.SharedPreferences
|
||||||
import androidx.preference.PreferenceManager
|
import androidx.preference.PreferenceManager
|
||||||
import kotlinx.coroutines.*
|
import com.crashlytics.android.Crashlytics
|
||||||
|
import io.fabric.sdk.android.Fabric
|
||||||
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||||
import kotlinx.coroutines.channels.Channel
|
import kotlinx.coroutines.channels.Channel
|
||||||
import okhttp3.OkHttpClient
|
import kotlinx.coroutines.launch
|
||||||
import okhttp3.ResponseBody
|
import okhttp3.*
|
||||||
import okio.*
|
import okio.*
|
||||||
import java.util.concurrent.Executors
|
import xyz.quaver.hitomi.Reader
|
||||||
|
import xyz.quaver.hitomi.urlFromUrlFromHash
|
||||||
|
import xyz.quaver.hiyobi.createImgList
|
||||||
|
import java.io.FileInputStream
|
||||||
|
import java.io.IOException
|
||||||
|
|
||||||
@UseExperimental(ExperimentalCoroutinesApi::class)
|
@UseExperimental(ExperimentalCoroutinesApi::class)
|
||||||
class DownloadWorker(context: Context) : ContextWrapper(context) {
|
class DownloadWorker private constructor(context: Context) : ContextWrapper(context) {
|
||||||
|
|
||||||
val preferences = PreferenceManager.getDefaultSharedPreferences(context)
|
val preferences : SharedPreferences = PreferenceManager.getDefaultSharedPreferences(this)
|
||||||
|
|
||||||
//region ProgressListener
|
//region ProgressListener
|
||||||
interface ProgressListener {
|
interface ProgressListener {
|
||||||
@@ -72,15 +81,28 @@ class DownloadWorker(context: Context) : ContextWrapper(context) {
|
|||||||
}
|
}
|
||||||
//endregion
|
//endregion
|
||||||
|
|
||||||
val queue = Channel<Int>()
|
//region Singleton
|
||||||
val progress = mutableMapOf<String, Double>()
|
companion object {
|
||||||
val worker = Executors.newCachedThreadPool().asCoroutineDispatcher()
|
|
||||||
|
|
||||||
val progressListener = object: ProgressListener {
|
@Volatile private var instance: DownloadWorker? = null
|
||||||
override fun update(tag: Any?, bytesRead: Long, contentLength: Long, done: Boolean) {
|
|
||||||
|
|
||||||
}
|
fun getInstance(context: Context) =
|
||||||
|
instance ?: synchronized(this) {
|
||||||
|
instance ?: DownloadWorker(context).also { instance = it }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
//endregion
|
||||||
|
|
||||||
|
val queue = Channel<Int>()
|
||||||
|
/* VALUE
|
||||||
|
* 0 <= value < 100 -> Download in progress
|
||||||
|
* Float.POSITIVE_INFINITY -> Download completed
|
||||||
|
* Float.NaN -> Exception
|
||||||
|
*/
|
||||||
|
val progress = mutableMapOf<String, Float>()
|
||||||
|
val result = mutableMapOf<String, ByteArray>()
|
||||||
|
val exception = mutableMapOf<String, Throwable>()
|
||||||
|
|
||||||
val client = OkHttpClient.Builder()
|
val client = OkHttpClient.Builder()
|
||||||
.addNetworkInterceptor { chain ->
|
.addNetworkInterceptor { chain ->
|
||||||
val request = chain.request()
|
val request = chain.request()
|
||||||
@@ -97,21 +119,65 @@ class DownloadWorker(context: Context) : ContextWrapper(context) {
|
|||||||
.build()
|
.build()
|
||||||
}.build()
|
}.build()
|
||||||
|
|
||||||
init {
|
|
||||||
val maxThread = preferences.getInt("max_thread", 4)
|
|
||||||
|
|
||||||
|
val progressListener = object: ProgressListener {
|
||||||
|
override fun update(tag: Any?, bytesRead: Long, contentLength: Long, done: Boolean) {
|
||||||
|
if (tag !is String)
|
||||||
|
return
|
||||||
|
|
||||||
|
if (progress[tag] != Float.POSITIVE_INFINITY)
|
||||||
|
progress[tag] = bytesRead / contentLength.toFloat()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
init {
|
||||||
CoroutineScope(Dispatchers.Unconfined).launch {
|
CoroutineScope(Dispatchers.Unconfined).launch {
|
||||||
while (!(queue.isEmpty && queue.isClosedForReceive)) {
|
while (!(queue.isEmpty && queue.isClosedForReceive)) {
|
||||||
val lowQuality = preferences.getBoolean("low_quality", false)
|
val lowQuality = preferences.getBoolean("low_quality", false)
|
||||||
val galleryID = queue.receive()
|
val galleryID = queue.receive()
|
||||||
|
|
||||||
launch(Dispatchers.IO) {
|
launch(Dispatchers.IO) io@{
|
||||||
val reader = Cache(context).getReader(galleryID) ?: return@launch
|
val reader = Cache(context).getReader(galleryID) ?: return@io
|
||||||
|
val cache = Cache(context).getImages(galleryID)
|
||||||
|
|
||||||
reader.galleryInfo.forEachIndexed { index, galleryInfo ->
|
reader.galleryInfo.forEachIndexed { index, galleryInfo ->
|
||||||
when(reader.code) {
|
val tag = "$galleryID-$index"
|
||||||
|
val url = when(reader.code) {
|
||||||
|
Reader.Code.HITOMI ->
|
||||||
|
urlFromUrlFromHash(galleryID, galleryInfo, if (lowQuality) "webp" else null)
|
||||||
|
Reader.Code.HIYOBI ->
|
||||||
|
createImgList(galleryID, reader, lowQuality)[index].path
|
||||||
|
else -> "" //Shouldn't be called anyways
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//Cache exists :P
|
||||||
|
cache?.get(index)?.let {
|
||||||
|
result[tag] = FileInputStream(it).readBytes()
|
||||||
|
progress[tag] = Float.POSITIVE_INFINITY
|
||||||
|
|
||||||
|
return@io
|
||||||
|
}
|
||||||
|
|
||||||
|
val request = Request.Builder()
|
||||||
|
.url(url)
|
||||||
|
.tag(tag)
|
||||||
|
.build()
|
||||||
|
|
||||||
|
client.newCall(request).enqueue(object: Callback {
|
||||||
|
override fun onFailure(call: Call, e: IOException) {
|
||||||
|
if (Fabric.isInitialized())
|
||||||
|
Crashlytics.logException(e)
|
||||||
|
|
||||||
|
progress[tag] = Float.NaN
|
||||||
|
exception[tag] = e
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onResponse(call: Call, response: Response) {
|
||||||
|
response.use {
|
||||||
|
result[tag] = (it.body?: return).bytes()
|
||||||
|
progress[tag] = Float.POSITIVE_INFINITY
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user