This commit is contained in:
tom5079
2020-09-02 00:13:25 +09:00
parent 7704c96955
commit 9583897ada
16 changed files with 237 additions and 140 deletions

View File

@@ -25,15 +25,6 @@ lateinit var preferences: SharedPreferences
object Preferences: SharedPreferences by preferences {
@Suppress("UNCHECKED_CAST")
val putMap = mapOf<KClass<out Any>, (String, Any) -> Unit>(
String::class to { k, v -> edit().putString(k, v as String).apply() },
Int::class to { k, v -> edit().putBoolean(k, v as Boolean).apply() },
Long::class to { k, v -> edit().putLong(k, v as Long).apply() },
Boolean::class to { k, v -> edit().putBoolean(k, v as Boolean).apply() },
Set::class to { k, v -> edit().putStringSet(k, v as Set<String>).apply() }
)
val defMap = mapOf(
String::class to "",
Int::class to -1,
@@ -42,12 +33,14 @@ object Preferences: SharedPreferences by preferences {
Set::class to emptySet<Any>()
)
inline operator fun <reified T: Any> set(key: String, value: T) {
putMap[T::class]?.invoke(key, value)
}
operator fun set(key: String, value: String) = edit().putString(key, value).apply()
operator fun set(key: String, value: Int) = edit().putInt(key, value).apply()
operator fun set(key: String, value: Long) = edit().putLong(key, value).apply()
operator fun set(key: String, value: Boolean) = edit().putBoolean(key, value).apply()
operator fun set(key: String, value: Set<String>) = edit().putStringSet(key, value).apply()
inline operator fun <reified T: Any> get(key: String, defaultVal: T = defMap[T::class] as T, setIfNull: Boolean = false): T =
(all[key] as? T) ?: defaultVal.also { if (setIfNull) set(key, defaultVal) }
@Suppress("UNCHECKED_CAST")
inline operator fun <reified T: Any> get(key: String, defaultVal: T = defMap[T::class] as T): T = (all[key] as? T) ?: defaultVal
fun remove(key: String) {
edit().remove(key).apply()

View File

@@ -22,9 +22,10 @@ import android.content.Context
import android.content.ContextWrapper
import android.util.Log
import android.util.SparseArray
import kotlinx.coroutines.*
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import kotlinx.serialization.Serializable
import kotlinx.serialization.decodeFromString
import kotlinx.serialization.encodeToString
@@ -34,12 +35,12 @@ import xyz.quaver.Code
import xyz.quaver.hitomi.GalleryBlock
import xyz.quaver.hitomi.Reader
import xyz.quaver.io.FileX
import xyz.quaver.io.util.*
import xyz.quaver.io.util.getChild
import xyz.quaver.io.util.readBytes
import xyz.quaver.io.util.readText
import xyz.quaver.io.util.writeBytes
import xyz.quaver.pupil.client
import xyz.quaver.pupil.util.Preferences
import xyz.quaver.pupil.util.formatDownloadFolder
import kotlin.io.deleteRecursively
import kotlin.io.writeText
@Serializable
data class Metadata(
@@ -53,25 +54,23 @@ data class Metadata(
class Cache private constructor(context: Context, val galleryID: Int) : ContextWrapper(context) {
companion object {
private val mutex = Mutex()
private val instances = SparseArray<Cache>()
fun getInstance(context: Context, galleryID: Int) =
instances[galleryID] ?: runBlocking { mutex.withLock {
instances[galleryID] ?: synchronized(this) {
instances[galleryID] ?: Cache(context, galleryID).also { instances.put(galleryID, it) }
} }
}
fun delete(galleryID: Int) { runBlocking { mutex.withLock {
instances[galleryID]?.galleryFolder?.deleteRecursively()
fun delete(galleryID: Int) {
instances[galleryID]?.cacheFolder?.deleteRecursively()
instances.delete(galleryID)
} } }
}
}
init {
galleryFolder.mkdirs()
cacheFolder.mkdirs()
}
private val mutex = Mutex()
var metadata = kotlin.runCatching {
findFile(".metadata")?.readText()?.let {
Json.decodeFromString<Metadata>(it)
@@ -82,11 +81,10 @@ class Cache private constructor(context: Context, val galleryID: Int) : ContextW
get() = DownloadFolderManager.getInstance(this).getDownloadFolder(galleryID)
val cacheFolder: FileX
get() = FileX(this, cacheDir, "imageCache/$galleryID")
val galleryFolder: FileX
get() = DownloadFolderManager.getInstance(this).getDownloadFolder(galleryID)
?: FileX(this, cacheDir, "imageCache/$galleryID")
get() = FileX(this, cacheDir, "imageCache/$galleryID").also {
if (!it.exists())
it.mkdirs()
}
fun findFile(fileName: String): FileX? =
cacheFolder.getChild(fileName).let {
@@ -96,18 +94,19 @@ class Cache private constructor(context: Context, val galleryID: Int) : ContextW
} }
@Suppress("BlockingMethodInNonBlockingContext")
suspend fun setMetadata(change: (Metadata) -> Unit) { mutex.withLock {
fun setMetadata(change: (Metadata) -> Unit) {
change.invoke(metadata)
val file = galleryFolder.getChild(".metadata")
val file = cacheFolder.getChild(".metadata")
CoroutineScope(Dispatchers.IO).launch {
kotlin.runCatching {
kotlin.runCatching {
if (!file.exists()) {
Log.i("PUPILD", "$file")
file.createNewFile()
file.writeText(Json.encodeToString(metadata))
}
file.writeText(Json.encodeToString(metadata))
}
} }
}
suspend fun getGalleryBlock(): GalleryBlock? {
val sources = listOf(
@@ -129,7 +128,7 @@ class Cache private constructor(context: Context, val galleryID: Int) : ContextW
}
galleryBlock?.also {
launch { setMetadata { metadata -> metadata.galleryBlock = it } }
setMetadata { metadata -> metadata.galleryBlock = it }
}
}
}
@@ -145,7 +144,7 @@ class Cache private constructor(context: Context, val galleryID: Int) : ContextW
kotlin.runCatching {
client.newCall(request).execute().body()?.use { it.bytes() }
}.getOrNull()?.also { kotlin.run {
galleryFolder.getChild(".thumbnail").writeBytes(it)
cacheFolder.getChild(".thumbnail").writeBytes(it)
} }
} }
@@ -178,12 +177,12 @@ class Cache private constructor(context: Context, val galleryID: Int) : ContextW
}
reader?.also {
launch { setMetadata { metadata ->
setMetadata { metadata ->
metadata.reader = it
if (metadata.imageList == null)
metadata.imageList = MutableList(reader.galleryInfo.files.size) { null }
} }
}
}
}
}
@@ -192,8 +191,8 @@ class Cache private constructor(context: Context, val galleryID: Int) : ContextW
metadata.imageList?.get(index)?.let { findFile(it) }
@Suppress("BlockingMethodInNonBlockingContext")
suspend fun putImage(index: Int, fileName: String, data: ByteArray) {
val file = galleryFolder.getChild(fileName)
fun putImage(index: Int, fileName: String, data: ByteArray) {
val file = cacheFolder.getChild(fileName)
file.createNewFile()
file.writeBytes(data)
@@ -202,14 +201,12 @@ class Cache private constructor(context: Context, val galleryID: Int) : ContextW
@Suppress("BlockingMethodInNonBlockingContext")
fun moveToDownload() = CoroutineScope(Dispatchers.IO).launch {
if (downloadFolder == null)
DownloadFolderManager.getInstance(this@Cache).addDownloadFolder(galleryID, this@Cache.formatDownloadFolder())
val downloadFolder = downloadFolder ?: return@launch
Log.i("PUPILD", "MOVING $galleryID")
metadata.imageList?.forEach { imageName ->
imageName ?: return@forEach
Log.i("PUPIL", downloadFolder?.uri.toString())
val target = downloadFolder!!.getChild(imageName)
val target = downloadFolder.getChild(imageName)
val source = cacheFolder.getChild(imageName)
if (!source.exists())
@@ -221,14 +218,13 @@ class Cache private constructor(context: Context, val galleryID: Int) : ContextW
}
}
Log.i("PUPIL", downloadFolder?.uri.toString())
val cacheMetadata = cacheFolder.getChild(".metadata")
val downloadMetadata = downloadFolder!!.getChild(".metadata")
val downloadMetadata = downloadFolder.getChild(".metadata")
if (cacheMetadata.exists()) {
kotlin.runCatching {
downloadMetadata.createNewFile()
cacheMetadata.readBytes()?.let { downloadMetadata.writeBytes(it) }
downloadMetadata.writeText(Json.encodeToString(metadata))
cacheMetadata.delete()
}
}

View File

@@ -20,23 +20,17 @@ package xyz.quaver.pupil.util.downloader
import android.content.Context
import android.content.ContextWrapper
import android.webkit.URLUtil
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
import kotlinx.serialization.decodeFromString
import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json
import okhttp3.Call
import xyz.quaver.io.FileX
import xyz.quaver.io.util.getChild
import xyz.quaver.io.util.readText
import xyz.quaver.io.util.*
import xyz.quaver.pupil.client
import xyz.quaver.pupil.services.DownloadService
import xyz.quaver.pupil.util.Preferences
import xyz.quaver.pupil.util.formatDownloadFolder
class DownloadFolderManager private constructor(context: Context) : ContextWrapper(context) {
@@ -61,15 +55,24 @@ class DownloadFolderManager private constructor(context: Context) : ContextWrapp
}
}.invoke()
private val downloadFolderMapMutex = Mutex()
private val downloadFolderMap: MutableMap<Int, String> = runBlocking { downloadFolderMapMutex.withLock {
kotlin.runCatching {
downloadFolder.getChild(".download").readText()?.let {
Json.decodeFromString<MutableMap<Int, String>>(it)
}
}.getOrNull() ?: mutableMapOf()
} }
val downloadFolderMap: MutableMap<Int, String> = {
val file = downloadFolder.getChild(".download")
val data = if (file.exists())
kotlin.runCatching {
file.readText()?.let { Json.decodeFromString<MutableMap<Int, String>>(it) }
}.onFailure { file.delete() }.getOrNull()
else
null
data ?: {
file.createNewFile()
file.writeText("{}")
mutableMapOf<Int, String>()
}.invoke()
}.invoke()
@Synchronized
fun isDownloading(galleryID: Int): Boolean {
val isThisGallery: (Call) -> Boolean = { (it.request().tag() as? DownloadService.Tag)?.galleryID == galleryID }
@@ -77,13 +80,18 @@ class DownloadFolderManager private constructor(context: Context) : ContextWrapp
&& client.dispatcher().let { it.queuedCalls().any(isThisGallery) || it.runningCalls().any(isThisGallery) }
}
fun getDownloadFolder(galleryID: Int): FileX? = runBlocking { downloadFolderMapMutex.withLock {
@Synchronized
fun getDownloadFolder(galleryID: Int): FileX? =
downloadFolderMap[galleryID]?.let { downloadFolder.getChild(it) }
} }
fun addDownloadFolder(galleryID: Int, name: String) { runBlocking { downloadFolderMapMutex.withLock {
@Synchronized
fun addDownloadFolder(galleryID: Int) {
if (downloadFolderMap.containsKey(galleryID))
return@withLock
return
val name = runBlocking {
Cache.getInstance(this@DownloadFolderManager, galleryID).getGalleryBlock()
}?.formatDownloadFolder() ?: return
val folder = downloadFolder.getChild(name)
@@ -92,29 +100,21 @@ class DownloadFolderManager private constructor(context: Context) : ContextWrapp
downloadFolderMap[galleryID] = name
CoroutineScope(Dispatchers.IO).launch { downloadFolderMapMutex.withLock {
downloadFolder.getChild(".download").let {
it.createNewFile()
it.writeText(Json.encodeToString(downloadFolderMap))
}
} }
} } }
downloadFolder.getChild(".download").writeText(Json.encodeToString(downloadFolderMap))
}
fun deleteDownloadFolder(galleryID: Int) { runBlocking { downloadFolderMapMutex.withLock {
@Synchronized
fun deleteDownloadFolder(galleryID: Int) {
if (!downloadFolderMap.containsKey(galleryID))
return@withLock
return
downloadFolderMap[galleryID]?.let {
if (downloadFolder.getChild(it).delete()) {
kotlin.runCatching {
downloadFolder.getChild(it).delete()
downloadFolderMap.remove(galleryID)
CoroutineScope(Dispatchers.IO).launch { downloadFolderMapMutex.withLock {
downloadFolder.getChild(".download").let {
it.createNewFile()
it.writeText(Json.encodeToString(downloadFolderMap))
}
} }
downloadFolder.getChild(".download").writeText(Json.encodeToString(downloadFolderMap))
}
}
} } }
}
}

View File

@@ -30,6 +30,7 @@ import kotlinx.coroutines.sync.withLock
import okhttp3.OkHttpClient
import okhttp3.Request
import xyz.quaver.Code
import xyz.quaver.hitomi.GalleryBlock
import xyz.quaver.hitomi.Reader
import xyz.quaver.hitomi.getReferer
import xyz.quaver.hitomi.imageUrlFromImage
@@ -87,15 +88,15 @@ fun OkHttpClient.Builder.proxyInfo(proxyInfo: ProxyInfo) = this.apply {
}
}
val formatMap = mapOf<String, (Cache) -> (String)>(
"-id-" to { runBlocking { it.getGalleryBlock()?.id.toString() } },
"-title-" to { runBlocking { it.getGalleryBlock()?.title.toString() } },
val formatMap = mapOf<String, GalleryBlock.() -> (String)>(
"-id-" to { id.toString() },
"-title-" to { title },
// TODO
)
/**
* Formats download folder name with given Metadata
*/
fun Cache.formatDownloadFolder(): String =
fun GalleryBlock.formatDownloadFolder(): String =
Preferences["download_folder_format", "-id-"].let {
formatMap.entries.fold(it) { str, (k, v) ->
str.replace(k, v.invoke(this), true)
@@ -131,4 +132,10 @@ val Reader.requestBuilders: List<Request.Builder>
}
}
}
}
}
fun String.ellipsize(n: Int): String =
if (this.length > n)
this.slice(0 until n) + ""
else
this