@@ -87,6 +87,8 @@ import com.jankinwu.fntv.client.ui.component.player.VideoPlayerProgressBar
8787import com.jankinwu.fntv.client.ui.component.player.VolumeControl
8888import com.jankinwu.fntv.client.ui.component.player.formatDuration
8989import com.jankinwu.fntv.client.ui.providable.IsoTagData
90+ import com.jankinwu.fntv.client.ui.providable.LocalFileInfo
91+ import com.jankinwu.fntv.client.ui.providable.LocalFrameWindowScope
9092import com.jankinwu.fntv.client.ui.providable.LocalIsoTagData
9193import com.jankinwu.fntv.client.ui.providable.LocalPlayerManager
9294import com.jankinwu.fntv.client.ui.providable.LocalStore
@@ -95,6 +97,7 @@ import com.jankinwu.fntv.client.ui.providable.LocalTypography
9597import com.jankinwu.fntv.client.ui.providable.LocalWindowState
9698import com.jankinwu.fntv.client.ui.providable.defaultVariableFamily
9799import com.jankinwu.fntv.client.utils.HiddenPointerIcon
100+ import com.jankinwu.fntv.client.utils.chooseFile
98101import com.jankinwu.fntv.client.viewmodel.MediaPViewModel
99102import com.jankinwu.fntv.client.viewmodel.PlayInfoViewModel
100103import com.jankinwu.fntv.client.viewmodel.PlayPlayViewModel
@@ -103,6 +106,7 @@ import com.jankinwu.fntv.client.viewmodel.PlayerViewModel
103106import com.jankinwu.fntv.client.viewmodel.StreamViewModel
104107import com.jankinwu.fntv.client.viewmodel.SubtitleDeleteViewModel
105108import com.jankinwu.fntv.client.viewmodel.SubtitleMarkViewModel
109+ import com.jankinwu.fntv.client.viewmodel.SubtitleUploadViewModel
106110import com.jankinwu.fntv.client.viewmodel.TagViewModel
107111import com.jankinwu.fntv.client.viewmodel.UiState
108112import com.jankinwu.fntv.client.viewmodel.UserInfoViewModel
@@ -241,12 +245,12 @@ fun PlayerOverlay(
241245 isSpeedControlHovered || isVolumeControlHovered || isQualityControlHovered || isSettingsMenuHovered || isSubtitleControlHovered
242246 val currentPosition by mediaPlayer.currentPositionMillis.collectAsState()
243247 val playerManager = LocalPlayerManager .current
248+ val frameWindowScope = LocalFrameWindowScope .current
244249 val mediaPViewModel: MediaPViewModel = koinViewModel()
245250 val tagViewModel: TagViewModel = koinViewModel()
246251 val playerViewModel: PlayerViewModel = koinViewModel()
247252 val playingInfoCache by playerViewModel.playingInfoCache.collectAsState()
248253 val resetQualityState by mediaPViewModel.resetQualityState.collectAsState()
249-
250254 val iso6391State by tagViewModel.iso6391State.collectAsState()
251255 val iso6392State by tagViewModel.iso6392State.collectAsState()
252256 val iso3166State by tagViewModel.iso3166State.collectAsState()
@@ -312,6 +316,9 @@ fun PlayerOverlay(
312316 val subtitleDeleteViewModel: SubtitleDeleteViewModel = koinViewModel()
313317 val subtitleDeleteState by subtitleDeleteViewModel.uiState.collectAsState()
314318
319+ val subtitleUploadViewModel: SubtitleUploadViewModel = koinViewModel()
320+ val subtitleUploadState by subtitleUploadViewModel.uiState.collectAsState()
321+
315322 val streamViewModel: StreamViewModel = koinViewModel()
316323 val userInfoViewModel: UserInfoViewModel = koinViewModel()
317324
@@ -357,6 +364,13 @@ fun PlayerOverlay(
357364 }
358365 }
359366
367+ LaunchedEffect (subtitleUploadState) {
368+ if (subtitleUploadState is UiState .Success ) {
369+ refreshSubtitleList()
370+ subtitleUploadViewModel.clearError()
371+ }
372+ }
373+
360374 var currentResolution by remember { mutableStateOf(" " ) }
361375 var currentBitrate by remember { mutableStateOf<Int ?>(null ) }
362376
@@ -500,7 +514,8 @@ fun PlayerOverlay(
500514 }
501515 CompositionLocalProvider (
502516 LocalIsoTagData provides isoTagData,
503- LocalToastManager provides toastManager
517+ LocalToastManager provides toastManager,
518+ LocalFileInfo provides playingInfoCache?.currentFileStream
504519 ) {
505520 Box (
506521 modifier = Modifier
@@ -750,7 +765,22 @@ fun PlayerOverlay(
750765 showAddNasSubtitleDialog = true
751766 },
752767 onOpenAddLocalSubtitle = {
753- // Placeholder for local subtitle
768+ val mediaGuid = playingInfoCache?.currentFileStream?.guid
769+ if (mediaGuid != null ) {
770+ val file = chooseFile(
771+ frameWindowScope,
772+ arrayOf(" ass" , " srt" , " vtt" , " sub" , " ssa" ),
773+ " 选择字幕文件"
774+ )
775+ file?.let { selectedFile ->
776+ val byteArray = selectedFile.readBytes()
777+ subtitleUploadViewModel.uploadSubtitle(
778+ mediaGuid,
779+ byteArray,
780+ selectedFile.name
781+ )
782+ }
783+ }
754784 },
755785 onSubtitleControlHoverChanged = { isHovered ->
756786 isSubtitleControlHovered = isHovered
@@ -1091,7 +1121,7 @@ fun rememberPlayMediaFunction(
10911121 playerManager,
10921122 mediaGuid,
10931123 currentAudioGuid,
1094- currentSubtitleGuid
1124+ currentSubtitleGuid,
10951125 ) {
10961126 {
10971127 scope.launch {
@@ -1157,6 +1187,22 @@ private suspend fun playMedia(
11571187 }
11581188 val subtitleGuid = currentSubtitleGuid ? : subtitleStream?.guid
11591189 val fileStream = streamInfo.fileStream
1190+
1191+ // 缓存播放信息 (提前初始化,确保LocalFileInfo可用)
1192+ val cache = PlayingInfoCache (
1193+ streamInfo,
1194+ " " ,
1195+ fileStream,
1196+ videoStream,
1197+ audioStream,
1198+ subtitleStream,
1199+ playInfoResponse.item.guid,
1200+ streamInfo.qualities,
1201+ currentAudioStreamList = streamInfo.audioStreams,
1202+ currentSubtitleStreamList = streamInfo.subtitleStreams
1203+ )
1204+ playerViewModel.updatePlayingInfo(cache)
1205+
11601206 // 显示播放器
11611207 val videoDuration = videoStream.duration * 1000L
11621208 if (playInfoResponse.type == FnTvMediaType .EPISODE .value) {
@@ -1200,19 +1246,8 @@ private suspend fun playMedia(
12001246 }
12011247
12021248 // 缓存播放信息
1203- val cache = PlayingInfoCache (
1204- streamInfo,
1205- playLink,
1206- fileStream,
1207- videoStream,
1208- audioStream,
1209- subtitleStream,
1210- playInfoResponse.item.guid,
1211- streamInfo.qualities,
1212- currentAudioStreamList = streamInfo.audioStreams,
1213- currentSubtitleStreamList = streamInfo.subtitleStreams
1214- )
1215- playerViewModel.updatePlayingInfo(cache)
1249+ val finalCache = cache.copy(playLink = playLink)
1250+ playerViewModel.updatePlayingInfo(finalCache)
12161251
12171252 logger.i(" startPosition: $startPosition " )
12181253 // 设置字幕
@@ -1222,12 +1257,12 @@ private suspend fun playMedia(
12221257 } ? : MediaExtraFiles ()
12231258 // 启动播放器
12241259 startPlayback(player, playLink, startPosition, extraFiles)
1225- // 记录播放数据
1226- callPlayRecord(
1260+ // 调用playRecord接口
1261+ callPlayRecord(
12271262// itemGuid = guid,
1228- ts = if ((startPosition / 1000 ).toInt() == 0 ) 1 else (startPosition / 1000 ).toInt(),
1229- playingInfoCache = cache ,
1230- playRecordViewModel = playRecordViewModel,
1263+ ts = if ((startPosition / 1000 ).toInt() == 0 ) 1 else (startPosition / 1000 ).toInt(),
1264+ playingInfoCache = finalCache ,
1265+ playRecordViewModel = playRecordViewModel,
12311266 onSuccess = {
12321267 logger.i(" 起播时调用playRecord成功" )
12331268 },
0 commit comments