fix photo cache and setting config

This commit is contained in:
2026-09-26 16:42:24 +08:00
parent 284b9cc028
commit a046790ab0
20 changed files with 243 additions and 145 deletions
@@ -23,6 +23,7 @@ import com.zhushenwudi.lovelivemusicplayer.platform.AppLog
import com.zhushenwudi.lovelivemusicplayer.platform.PlatformServices
import java.io.File
import kotlin.random.Random
import kotlin.time.Duration.Companion.milliseconds
actual class PlayerController(context: Context) {
private val appContext = context.applicationContext
@@ -114,7 +115,7 @@ actual class PlayerController(context: Context) {
durationMs = player.duration.coerceAtLeast(0),
)
}
delay(200)
delay(200.milliseconds)
}
}
}
@@ -284,8 +284,10 @@ internal val DeStrings: Map<String, String> = mapOf(
"now_playing_song" to "Läuft gerade: %s",
"fetch_songs_fail_or_net" to "Datenfehler oder Netzwerkfehler",
"explain_songs_data_kmp" to "Songdaten: Bibliotheksmetadaten, Version",
"explain_user_data_kmp" to "Benutzerdaten: Likes/Verlauf/Playlists/Cache, dann beenden",
"explain_image_cache" to "Bildcache: heruntergeladene Cover, danach neu laden",
"explain_user_data_kmp" to "Benutzerdaten: Likes/Verlauf/Playlists/Cache",
"remove_songs_data_kmp" to "Songdaten löschen",
"clear_image_cache" to "Bildcache löschen",
"remove_user_data_kmp" to "Benutzerdaten löschen",
"confirm_delete_from_menu_short" to "Aus Playlist löschen?",
"play_text" to "Spielen,Pausierte",
@@ -284,8 +284,10 @@ internal val EnStrings: Map<String, String> = mapOf(
"now_playing_song" to "Now Playing: %s",
"fetch_songs_fail_or_net" to "Data error or network failure",
"explain_songs_data_kmp" to "Song data: library metadata, version",
"explain_user_data_kmp" to "User data: likes/history/playlists/cache, then quit",
"explain_image_cache" to "Image cache: downloaded covers, reloaded after clearing",
"explain_user_data_kmp" to "User data: likes/history/playlists/cache",
"remove_songs_data_kmp" to "Clear song data",
"clear_image_cache" to "Clear image cache",
"remove_user_data_kmp" to "Clear user data",
"confirm_delete_from_menu_short" to "Delete from playlist?",
"play_text" to "Playing,Paused",
@@ -284,8 +284,10 @@ internal val JaStrings: Map<String, String> = mapOf(
"now_playing_song" to "再生中:%s",
"fetch_songs_fail_or_net" to "データ異常またはネットワーク失敗",
"explain_songs_data_kmp" to "曲データ:ライブラリメタデータ、バージョン",
"explain_user_data_kmp" to "ユーザーデータ:好き/履歴/プレイリスト/キャッシュを削除して終了",
"explain_image_cache" to "画像キャッシュ:ダウンロード済みカバー。削除後に再読み込み",
"explain_user_data_kmp" to "ユーザーデータ:好き/履歴/プレイリスト/キャッシュ",
"remove_songs_data_kmp" to "曲データを削除",
"clear_image_cache" to "画像キャッシュを削除",
"remove_user_data_kmp" to "ユーザーデータを削除",
"confirm_delete_from_menu_short" to "プレイリストから削除しますか?",
"play_text" to "放送中,停止しました",
@@ -284,8 +284,10 @@ internal val ZhStrings: Map<String, String> = mapOf(
"now_playing_song" to "正在播放:%s",
"fetch_songs_fail_or_net" to "数据异常或网络失败",
"explain_songs_data_kmp" to "歌曲数据:曲库元数据、版本号",
"explain_user_data_kmp" to "用户数据:喜欢/历史/歌单/缓存,并退出应用",
"explain_image_cache" to "图片缓存:已下载的封面,清除后重新加载",
"explain_user_data_kmp" to "用户数据:喜欢/历史/歌单/缓存",
"remove_songs_data_kmp" to "清除歌曲数据",
"clear_image_cache" to "清除图片缓存",
"remove_user_data_kmp" to "清除用户数据",
"confirm_delete_from_menu_short" to "确认从歌单删除?",
"play_text" to "播放中,已暂停",
@@ -7,6 +7,7 @@ import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.launch
import kotlin.time.Duration.Companion.seconds
class SleepTimer(
private val scope: CoroutineScope,
@@ -23,7 +24,7 @@ class SleepTimer(
_remainingMs.value = left
job = scope.launch {
while (left > 0) {
delay(1_000)
delay(1.seconds)
left -= 1_000
_remainingMs.value = left.coerceAtLeast(0)
}
@@ -1,6 +1,8 @@
package com.zhushenwudi.lovelivemusicplayer.player
import kotlinx.cinterop.CValue
import kotlinx.cinterop.ExperimentalForeignApi
import kotlinx.cinterop.useContents
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
@@ -40,9 +42,13 @@ import platform.AVFoundation.rate
import platform.AVFoundation.replaceCurrentItemWithPlayerItem
import platform.AVFoundation.resourceLoader
import platform.AVFoundation.seekToTime
import platform.CoreGraphics.CGRectMake
import platform.CoreGraphics.CGSize
import platform.CoreGraphics.CGSizeMake
import platform.CoreMedia.CMTimeGetSeconds
import platform.CoreMedia.CMTimeMake
import platform.Foundation.NSBundle
import platform.Foundation.NSThread
import platform.Foundation.NSError
import platform.Foundation.NSMutableURLRequest
import platform.Foundation.NSNotification
@@ -69,6 +75,9 @@ import platform.MediaPlayer.MPRemoteCommandHandlerStatus
import platform.MediaPlayer.MPRemoteCommandHandlerStatusCommandFailed
import platform.MediaPlayer.MPRemoteCommandHandlerStatusSuccess
import platform.MediaPlayer.MPSeekCommandEvent
import platform.UIKit.UIGraphicsBeginImageContextWithOptions
import platform.UIKit.UIGraphicsEndImageContext
import platform.UIKit.UIGraphicsGetImageFromCurrentImageContext
import platform.UIKit.UIImage
import platform.darwin.dispatch_async
import platform.darwin.dispatch_get_main_queue
@@ -455,6 +464,10 @@ actual class PlayerController {
* 封面 + 专辑信息启用灵动岛媒体胶囊。
*/
private fun updateNowPlayingInfo() {
if (!NSThread.isMainThread) {
dispatch_async(dispatch_get_main_queue()) { updateNowPlayingInfo() }
return
}
val s = _state.value
val current = s.current ?: return
val songName = current.musicName ?: return
@@ -502,12 +515,32 @@ actual class PlayerController {
else -> raw
}
val image = UIImage.imageWithContentsOfFile(path) ?: return null
val artwork = MPMediaItemArtwork(boundsSize = image.size) { _ -> image }
val artwork = coverArtwork(image)
cachedArtworkMusicId = id
cachedArtwork = artwork
return artwork
}
/**
* 系统会按锁屏 / 控制中心要的尺寸回调。直接交原图时,负一屏封面会空白。
*/
private fun coverArtwork(image: UIImage): MPMediaItemArtwork {
return MPMediaItemArtwork(boundsSize = image.size) { requested ->
scaledCover(image, requested)
}
}
private fun scaledCover(image: UIImage, requested: CValue<CGSize>): UIImage {
val width = requested.useContents { this.width }
val height = requested.useContents { this.height }
if (width < 1.0 || height < 1.0) return image
UIGraphicsBeginImageContextWithOptions(CGSizeMake(width, height), false, 0.0)
image.drawInRect(CGRectMake(0.0, 0.0, width, height))
val scaled = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return scaled ?: image
}
private fun prefetchHttpArtwork(musicId: String, url: String) {
if (musicId == coverFetchingMusicId) return
if (musicId == cachedArtworkMusicId && cachedArtwork != null) return
@@ -537,7 +570,7 @@ actual class PlayerController {
return@dispatch_async
}
if (_state.value.current?.musicId != musicId) return@dispatch_async
val artwork = MPMediaItemArtwork(boundsSize = image.size) { _ -> image }
val artwork = coverArtwork(image)
cachedArtworkMusicId = musicId
cachedArtwork = artwork
updateNowPlayingInfo()