This commit is contained in:
Evan You
2025-04-11 14:38:38 +08:00
parent 932941b6b3
commit bc48c9246d
11 changed files with 137 additions and 104 deletions

View File

@@ -7,7 +7,7 @@
<meta content="width=device-width,user-scalable=no,initial-scale=1,maximum-scale=1,minimum-scale=1"
name="viewport"/>
<title>MixFile</title>
<script type="module" crossorigin src="/assets/index-DrT-F2ns.js"></script>
<script type="module" crossorigin src="/assets/index-DY6oNwSC.js"></script>
<link rel="stylesheet" crossorigin href="/assets/index-Ub7aM_kp.css">
</head>
<body>

View File

@@ -21,11 +21,13 @@ import com.donut.mixfile.util.objects.UpdateChecker
import com.donut.mixfile.util.showError
import com.donut.mixfile.util.showErrorDialog
import com.tencent.mmkv.MMKV
import kotlinx.coroutines.MainScope
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.SupervisorJob
import okhttp3.OkHttpClient
val appScope by lazy { MainScope() }
val appScope = CoroutineScope(Dispatchers.Main + SupervisorJob())
lateinit var kv: MMKV

View File

@@ -104,10 +104,12 @@ abstract class MixFileServer(
}
install(StatusPages) {
exception<Throwable> { call, cause ->
call.respondText(
"发生错误: ${cause.message} ${cause.stackTraceToString()}",
status = HttpStatusCode.InternalServerError
)
if (!call.response.isCommitted){
call.respondText(
"发生错误: ${cause.message} ${cause.stackTraceToString()}",
status = HttpStatusCode.InternalServerError
)
}
onError(cause)
}
}

View File

@@ -41,10 +41,12 @@ fun MixFileServer.getDownloadRoute(): RoutingHandler {
call.respondText("解析文件失败", status = HttpStatusCode.InternalServerError)
return@route
}
val mixFile = shareInfo.fetchMixFile(httpClient)
if (mixFile == null) {
val mixFile = try {
shareInfo.fetchMixFile(httpClient)
} catch (e: Exception) {
call.respondText(
"解析文件索引失败",
"解析文件索引失败: ${e.stackTraceToString()}",
status = HttpStatusCode.InternalServerError
)
return@route

View File

@@ -59,9 +59,7 @@ private val encodeMap = run {
fun MixShareInfo.shareCode(useShortCode: Boolean): String {
if (useShortCode) {
return "mf://${encodeHex(this.toString())}${
MixShareInfo.ENCODER.encode(
this.url.hashMD5().copyOf(6)
)
this.toString().substring(0, 8)
}"
}
return "mf://$this"

View File

@@ -125,7 +125,7 @@ data class MixShareInfo(
fun contentType(): String = fileName.parseFileMimeType()
suspend fun fetchMixFile(client: HttpClient): MixFile? {
suspend fun fetchMixFile(client: HttpClient): MixFile {
val decryptedBytes = fetchFile(url, client = client)
return MixFile.fromBytes(decryptedBytes)
}

View File

@@ -1,24 +1,27 @@
package com.donut.mixfile.util
import android.os.Handler
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import com.alibaba.fastjson2.into
import com.alibaba.fastjson2.toJSONString
import com.donut.mixfile.app
import com.donut.mixfile.appScope
import com.donut.mixfile.kv
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
import kotlinx.coroutines.withContext
fun <T> constructCachedMutableValue(
value: T,
key: String,
setVal: (value: T) -> Unit,
getVal: () -> T,
) =
object : CachedMutableValue<T>(value, key) {
object : CachedMutableValue<T>(value) {
override fun readCachedValue(): T {
return getVal()
}
@@ -32,20 +35,18 @@ fun <T> constructCachedMutableValue(
fun cachedMutableOf(value: String, key: String) =
constructCachedMutableValue(
value,
key,
{ kv.encode(key, it) },
{ kv.decodeString(key, value)!! })
fun cachedMutableOf(value: Boolean, key: String) =
constructCachedMutableValue(value, key, { kv.encode(key, it) }, { kv.decodeBool(key, value) })
constructCachedMutableValue(value, { kv.encode(key, it) }, { kv.decodeBool(key, value) })
fun cachedMutableOf(value: Long, key: String) =
constructCachedMutableValue(value, key, { kv.encode(key, it) }, { kv.decodeLong(key, value) })
constructCachedMutableValue(value, { kv.encode(key, it) }, { kv.decodeLong(key, value) })
fun cachedMutableOf(value: Set<String>, key: String) =
constructCachedMutableValue(
value,
key,
{ kv.encode(key, it) },
{ kv.decodeStringSet(key, value)!! },
)
@@ -54,7 +55,6 @@ fun cachedMutableOf(value: Set<String>, key: String) =
inline fun <reified T, reified C : Iterable<T>> cachedMutableOf(value: C, key: String) =
constructCachedMutableValue(
value,
key,
{
kv.encode(key, it.toJSONString())
},
@@ -71,37 +71,40 @@ inline fun <reified T, reified C : Iterable<T>> cachedMutableOf(value: C, key: S
abstract class CachedMutableValue<T>(
value: T,
private val key: String,
) {
var value by mutableStateOf(value)
private var saveTask: Runnable? = null
private var value by mutableStateOf(value)
private var loaded = false
private val mutex = Mutex()
private var saveTask: Job? = null
abstract fun readCachedValue(): T
abstract fun writeCachedValue(value: T)
operator fun getValue(thisRef: Any?, property: Any?): T {
if (!loaded) {
value = readCachedValue()
synchronized(this) {
if (!loaded) {
value = readCachedValue()
loaded = true
}
return value
}
loaded = true
return value
}
operator fun setValue(thisRef: Any?, property: Any?, value: T) {
if (this.value == value) {
return
}
this.value = value
synchronized(key) {
val handler = Handler(app.mainLooper)
saveTask?.let { handler.removeCallbacks(it) }
val task = Runnable {
appScope.launch(Dispatchers.IO) {
catchError {
writeCachedValue(value)
}
saveTask?.cancel()
saveTask = appScope.launch(Dispatchers.Main) {
mutex.withLock {
delay(100)
withContext(Dispatchers.IO) {
writeCachedValue(this@CachedMutableValue.value)
}
}
saveTask = task
handler.postDelayed(task, 100)
}
}
}

View File

@@ -226,7 +226,7 @@ fun debug(text: String?, tag: String = "test") {
Log.d(tag, text ?: "null")
}
inline fun catchError(tag: String = "", onError: () -> Unit = {}, block: () -> Unit) {
inline fun catchError(tag: String = "", block: () -> Unit) {
try {
block()
} catch (e: Exception) {

View File

@@ -35,6 +35,7 @@ import androidx.compose.ui.unit.sp
import androidx.compose.ui.viewinterop.AndroidView
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleEventObserver
import androidx.lifecycle.compose.LocalLifecycleOwner
import com.donut.mixfile.appScope
import com.donut.mixfile.currentActivity
import com.donut.mixfile.server.core.utils.isNotNull
@@ -125,7 +126,7 @@ fun TipText(content: String, onClick: () -> Unit = {}) {
@Composable
fun OnResume(block: () -> Unit) {
val lifecycleOwner = androidx.lifecycle.compose.LocalLifecycleOwner.current
val lifecycleOwner = LocalLifecycleOwner.current
val lifecycleObserver = remember {
LifecycleEventObserver { _, event ->
if (event == Lifecycle.Event.ON_RESUME) {

View File

@@ -1,8 +1,12 @@
package com.donut.mixfile
import io.ktor.http.URLBuilder
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.asCoroutineDispatcher
import kotlinx.coroutines.launch
import org.junit.Test
import java.util.Date
import java.util.concurrent.Executors
//appScope.launch(Dispatchers.IO) {
// repeat(100) {
@@ -37,6 +41,8 @@ class ExampleUnitTest {
val map = mapOf(1 to "aa", 2 to "bb")
@Test
fun main() {