This commit is contained in:
1
2025-07-09 13:05:19 +08:00
parent 42255f417f
commit 8483aa1f34
12 changed files with 162 additions and 29 deletions

View File

@@ -16,8 +16,8 @@ android {
applicationId = "com.donut.mixfile"
minSdk = 26
targetSdk = 35
versionCode = 140
versionName = "1.18.2"
versionCode = 141
versionName = "1.18.3"
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
vectorDrawables {

View File

@@ -50,6 +50,7 @@ class App : Application(), ImageLoaderFactory {
super.onCreate()
MMKV.initialize(this)
kv = MMKV.defaultMMKV()
kv.enableCompareBeforeSet()
Thread.setDefaultUncaughtExceptionHandler { t, e ->
showError(e)
if (Looper.myLooper() == null) {

View File

@@ -184,14 +184,15 @@ fun VideoPlayerScreen(
player = player,
videos = videoUris,
onPlayTimeChange = {
playHistory = playHistory.filter { it.hash != hash }.toMutableList().apply {
val history =
VideoHistory(player.currentPosition, hash, player.currentMediaItemIndex)
add(0, history)
if (size > 500) {
removeAt(lastIndex)
playHistory =
playHistory.filter { !it.hash.contentEquals(hash) }.toMutableList().apply {
val history =
VideoHistory(player.currentPosition, hash, player.currentMediaItemIndex)
add(0, history)
if (size > 500) {
removeAt(lastIndex)
}
}
}
},
onTrackTimeChange = {
controlsVisible.set(true)

View File

@@ -2,8 +2,8 @@ package com.donut.mixfile.server
import com.donut.mixfile.server.core.Uploader
import com.donut.mixfile.server.core.uploaders.A3Uploader
import com.donut.mixfile.server.core.uploaders.hidden.A1Uploader
import com.donut.mixfile.server.core.uploaders.hidden.A2Uploader
import com.donut.mixfile.server.core.uploaders.A1Uploader
import com.donut.mixfile.server.core.uploaders.A2Uploader
import com.donut.mixfile.util.cachedMutableOf
import io.ktor.client.HttpClient
import io.ktor.client.request.get

View File

@@ -137,7 +137,7 @@ val mixFileServer = object : MixFileServer(
}
var serverStarted by mutableStateOf(false)
val WEB_DAV_KEY = "mixfile_web_dav_data"
const val WEB_DAV_KEY = "mixfile_web_dav_data"
class FileService : Service() {

View File

@@ -40,7 +40,7 @@ fun MixFileServer.getUploadRoute(): RoutingHandler {
call.respondText(uploadFile(call.receiveChannel(), name, size, add.toBoolean()) {
call.respondText("上传已取消", status = HttpStatusCode.InternalServerError)
})
}.first)
}
}
@@ -51,7 +51,7 @@ suspend fun MixFileServer.uploadFile(
add: Boolean = true,
key: ByteArray = generateRandomByteArray(32),
onStop: suspend () -> Unit = {}
): String {
): Pair<String, Long> {
val uploadTask = getUploadTask(name, size, add)
@@ -78,7 +78,7 @@ suspend fun MixFileServer.uploadFile(
referer = uploader.referer
)
uploadTask.complete(mixShareInfo)
return mixShareInfo.toString()
return mixShareInfo.toString() to fileSize
}
private suspend fun MixFileServer.doUploadFile(

View File

@@ -168,9 +168,14 @@ fun MixFileServer.getWebDAVRoute(): Route.() -> Unit {
call.respond(HttpStatusCode.Conflict)
return@webdav
}
val shareInfo = uploadFile(call.receiveChannel(), davFileName, fileSize, add = false)
val (shareInfo, finalSize) = uploadFile(
call.receiveChannel(),
davFileName,
fileSize,
add = false
)
val fileNode =
WebDavFile(size = fileSize, shareInfoData = shareInfo, name = davFileName)
WebDavFile(size = finalSize, shareInfoData = shareInfo, name = davFileName)
webDav.addFileNode(davParentPath, fileNode)
call.respond(HttpStatusCode.Created)
webDav.saveData()
@@ -181,6 +186,10 @@ fun MixFileServer.getWebDAVRoute(): Route.() -> Unit {
webDav.saveData()
}
webdav("MKCOL") {
if (davPath.isEmpty()) {
call.respond(HttpStatusCode.Created)
return@webdav
}
val fileList = webDav.listFiles(davParentPath)
if (fileList == null) {
call.respond(HttpStatusCode.Conflict)

View File

@@ -0,0 +1,37 @@
package com.donut.mixfile.server.core.uploaders
import com.alibaba.fastjson2.JSONArray
import com.alibaba.fastjson2.to
import com.donut.mixfile.server.core.Uploader
import com.donut.mixfile.server.core.utils.add
import com.donut.mixfile.server.core.utils.fileFormHeaders
import io.ktor.client.HttpClient
import io.ktor.client.call.body
import io.ktor.client.request.forms.formData
import io.ktor.client.request.forms.submitFormWithBinaryData
object A1Uploader : Uploader("线路A1") {
override val referer: String
get() = "wfey".sCode
override suspend fun doUpload(fileData: ByteArray, client: HttpClient): String {
val result =
client.submitFormWithBinaryData(
"${referer}service/upload",
formData {
add("flag", "")
add("FileUploadForm[file]", fileData, fileFormHeaders())
}) {
}.body<String>().to<JSONArray>()
if (result.isEmpty()) {
throw Exception("上传失败")
}
val data = result.getJSONObject(0)
val url = data.getString("url") ?: throw Exception("上传失败")
return "https:${url}"
}
}

View File

@@ -0,0 +1,92 @@
package com.donut.mixfile.server.core.uploaders
import com.alibaba.fastjson2.to
import com.donut.mixfile.server.core.Uploader
import com.donut.mixfile.server.core.utils.add
import com.donut.mixfile.server.core.utils.decodeHex
import com.donut.mixfile.server.core.utils.fileFormHeaders
import com.donut.mixfile.server.core.utils.genRandomString
import io.ktor.client.HttpClient
import io.ktor.client.call.body
import io.ktor.client.request.forms.formData
import io.ktor.client.request.forms.submitFormWithBinaryData
import io.ktor.client.request.get
import io.ktor.http.isSuccess
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
val String.sCode: String
get() = decodeHex(this)
object A2Uploader : Uploader("线路A2") {
private val domain =
"dwa".sCode
init {
//旧文件兼容
registerUrlTransform("A2") {
it.replace(
"COgP".sCode,
"d5w3".sCode
)
}
}
data class Token(
val host: String,
val accessid: String,
val policy: String,
val signature: String,
val dir: String,
)
override val referer: String
get() = domain
var tokenCache: Token? = null
var tokenCacheTime: Long = 0
val tokenLock = Mutex()
private suspend fun getToken(client: HttpClient): Token {
tokenLock.withLock {
val cached = tokenCache
if (cached != null && System.currentTimeMillis() - tokenCacheTime < 1000 * 60) {
return cached
}
val response =
client.get("${domain}handler/getoss.ashx")
if (!response.status.isSuccess()) {
throw Exception("上传失败")
}
val data: Token = response.body<String>().to()
tokenCache = data
tokenCacheTime = System.currentTimeMillis()
return data
}
}
override suspend fun doUpload(fileData: ByteArray, client: HttpClient): String {
val token = getToken(client)
val key = "${token.dir}${genRandomString()}"
val response = client.submitFormWithBinaryData(token.host, formData {
add("policy", token.policy)
add("OSSAccessKeyId", token.accessid)
add("Signature", token.signature)
add("key", key)
add("content-type", "image/gif")
add("file", fileData, fileFormHeaders())
})
if (!response.status.isSuccess()) {
throw Exception("上传失败")
}
return "${"awa".sCode}/${key}"
}
}

View File

@@ -43,7 +43,7 @@ import com.donut.mixfile.server.UPLOADERS
import com.donut.mixfile.server.UPLOAD_RETRY_COUNT
import com.donut.mixfile.server.UPLOAD_TASK_COUNT
import com.donut.mixfile.server.core.Uploader
import com.donut.mixfile.server.core.uploaders.hidden.A1Uploader
import com.donut.mixfile.server.core.uploaders.A1Uploader
import com.donut.mixfile.server.currentUploader
import com.donut.mixfile.server.getCurrentUploader
import com.donut.mixfile.ui.component.common.CommonSwitch

View File

@@ -126,7 +126,7 @@ val Favorites = MixNavPage(
LaunchedEffect(searchVal, currentCategory, favorites, favoriteSort) {
result = if (searchVal.trim().isNotEmpty()) {
favorites.filter {
it.name.contains(searchVal)
it.name.contains(searchVal, true)
}.asReversed()
} else {
favorites.asReversed()

View File

@@ -8,12 +8,9 @@ import com.alibaba.fastjson2.toJSONString
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(
@@ -77,7 +74,7 @@ abstract class CachedMutableValue<T>(
) {
private var loaded = false
private val mutex = Mutex()
private var saveTask: Job? = null
private var stateValue by mutableLongStateOf(0)
abstract fun readCachedValue(): T
@@ -103,13 +100,9 @@ abstract class CachedMutableValue<T>(
}
stateValue++
this.value = value
saveTask?.cancel()
saveTask = appScope.launch(Dispatchers.Main) {
appScope.launch(Dispatchers.IO) {
mutex.withLock {
delay(100)
withContext(Dispatchers.IO) {
writeCachedValue(this@CachedMutableValue.value)
}
writeCachedValue(this@CachedMutableValue.value)
}
}
}