diff --git a/app/src/main/java/com/futo/platformplayer/fragment/mainactivity/main/VideoDetailView.kt b/app/src/main/java/com/futo/platformplayer/fragment/mainactivity/main/VideoDetailView.kt index d529062d..6a84494a 100644 --- a/app/src/main/java/com/futo/platformplayer/fragment/mainactivity/main/VideoDetailView.kt +++ b/app/src/main/java/com/futo/platformplayer/fragment/mainactivity/main/VideoDetailView.kt @@ -124,6 +124,7 @@ class VideoDetailView : ConstraintLayout { private var _searchVideo: IPlatformVideo? = null; var video: IPlatformVideoDetails? = null private set; + var videoLocal: VideoLocal? = null; private var _playbackTracker: IPlaybackTracker? = null; val currentUrl get() = video?.url ?: _searchVideo?.url ?: _url; @@ -1044,10 +1045,32 @@ class VideoDetailView : ConstraintLayout { _player.setPlaybackRate(Settings.instance.playback.getDefaultPlaybackSpeed()); } - val video = if(videoDetail is VideoLocal) - videoDetail; - else //TODO: Update cached video if it exists with video - StateDownloads.instance.getCachedVideo(videoDetail.id) ?: videoDetail; + var videoLocal: VideoLocal? = null; + var video: IPlatformVideoDetails? = null; + + if(videoDetail is VideoLocal) { + videoLocal = videoDetail; + video = videoDetail; + val videoTask = StatePlatform.instance.getContentDetails(videoDetail.url); + videoTask.invokeOnCompletion { ex -> + if(ex != null) { + Logger.e(TAG, "Failed to fetch live video for offline video", ex); + return@invokeOnCompletion; + } + val result = videoTask.getCompleted(); + if(this.video == videoDetail && result != null && result is IPlatformVideoDetails) { + this.video = result; + fragment.lifecycleScope.launch(Dispatchers.Main) { + updateQualitySourcesOverlay(result, videoLocal); + } + } + }; + } + else { //TODO: Update cached video if it exists with video + videoLocal = StateDownloads.instance.getCachedVideo(videoDetail.id); + video = videoDetail; + } + this.videoLocal = videoLocal; this.video = video; this._playbackTracker = null; @@ -1082,9 +1105,13 @@ class VideoDetailView : ConstraintLayout { me._playbackTracker = tracker; } catch(ex: Throwable) { - withContext(Dispatchers.Main) { - UIDialogs.showGeneralErrorDialog(context, context.getString(R.string.failed_to_get_playback_tracker), ex); + Logger.e(TAG, "Playback tracker failed", ex); + if(me.video?.isLive == true) withContext(Dispatchers.Main) { + UIDialogs.toast(context, context.getString(R.string.failed_to_get_playback_tracker)); }; + else withContext(Dispatchers.Main) { + UIDialogs.showGeneralErrorDialog(context, context.getString(R.string.failed_to_get_playback_tracker), ex); + } } }; } @@ -1235,7 +1262,7 @@ class VideoDetailView : ConstraintLayout { //Overlay - updateQualitySourcesOverlay(video); + updateQualitySourcesOverlay(video, videoLocal); setLoading(false); @@ -1514,9 +1541,9 @@ class VideoDetailView : ConstraintLayout { //Quality Selector data private fun updateQualityFormatsOverlay(liveStreamVideoFormats : List?, liveStreamAudioFormats : List?) { val v = video ?: return; - updateQualitySourcesOverlay(v, liveStreamVideoFormats, liveStreamAudioFormats); + updateQualitySourcesOverlay(v, videoLocal, liveStreamVideoFormats, liveStreamAudioFormats); } - private fun updateQualitySourcesOverlay(videoDetails: IPlatformVideoDetails?, liveStreamVideoFormats: List? = null, liveStreamAudioFormats: List? = null) { + private fun updateQualitySourcesOverlay(videoDetails: IPlatformVideoDetails?, videoLocal: VideoLocal? = null, liveStreamVideoFormats: List? = null, liveStreamAudioFormats: List? = null) { Logger.i(TAG, "updateQualitySourcesOverlay"); val video: IPlatformVideoDetails?; @@ -1524,24 +1551,35 @@ class VideoDetailView : ConstraintLayout { val localAudioSource: List?; val localSubtitleSources: List?; + val videoSources: List?; + val audioSources: List?; + if(videoDetails is VideoLocal) { - video = videoDetails.videoSerialized; + video = videoLocal?.videoSerialized; localVideoSources = videoDetails.videoSource.toList(); localAudioSource = videoDetails.audioSource.toList(); localSubtitleSources = videoDetails.subtitlesSources.toList(); + videoSources = null + audioSources = null; } else { video = videoDetails; - localVideoSources = null; - localAudioSource = null; - localSubtitleSources = null; + videoSources = video?.video?.videoSources?.toList(); + audioSources = if(video?.video?.isUnMuxed == true) + (video.video as VideoUnMuxedSourceDescriptor).audioSources.toList() + else null + if(videoLocal != null) { + localVideoSources = videoLocal.videoSource.toList(); + localAudioSource = videoLocal.audioSource.toList(); + localSubtitleSources = videoLocal.subtitlesSources.toList(); + } + else { + localVideoSources = null; + localAudioSource = null; + localSubtitleSources = null; + } } - val videoSources = video?.video?.videoSources?.toList(); - val audioSources = if(video?.video?.isUnMuxed == true) - (video.video as VideoUnMuxedSourceDescriptor).audioSources.toList() - else null - val bestVideoSources = videoSources?.map { it.height * it.width } ?.distinct() ?.map { x -> VideoHelper.selectBestVideoSource(videoSources.filter { x == it.height * it.width }, -1, FutoVideoPlayerBase.PREFERED_VIDEO_CONTAINERS) } @@ -1569,7 +1607,7 @@ class VideoDetailView : ConstraintLayout { if(localVideoSources?.isNotEmpty() == true) SlideUpMenuGroup(this.context, context.getString(R.string.offline_video), "video", - *localVideoSources.stream() + *localVideoSources .map { SlideUpMenuItem(this.context, R.drawable.ic_movie, it!!.name, "${it.width}x${it.height}", it, { handleSelectVideoTrack(it) }); @@ -1577,7 +1615,7 @@ class VideoDetailView : ConstraintLayout { else null, if(localAudioSource?.isNotEmpty() == true) SlideUpMenuGroup(this.context, context.getString(R.string.offline_audio), "audio", - *localAudioSource.stream() + *localAudioSource .map { SlideUpMenuItem(this.context, R.drawable.ic_music, it.name, it.bitrate.toHumanBitrate(), it, { handleSelectAudioTrack(it) }); @@ -1593,7 +1631,7 @@ class VideoDetailView : ConstraintLayout { else null, if(liveStreamVideoFormats?.isEmpty() == false) SlideUpMenuGroup(this.context, context.getString(R.string.stream_video), "video", - *liveStreamVideoFormats.stream() + *liveStreamVideoFormats .map { SlideUpMenuItem(this.context, R.drawable.ic_movie, it?.label ?: it.containerMimeType ?: it.bitrate.toString(), "${it.width}x${it.height}", it, { _player.selectVideoTrack(it.height) }); @@ -1601,7 +1639,7 @@ class VideoDetailView : ConstraintLayout { else null, if(liveStreamAudioFormats?.isEmpty() == false) SlideUpMenuGroup(this.context, context.getString(R.string.stream_audio), "audio", - *liveStreamAudioFormats.stream() + *liveStreamAudioFormats .map { SlideUpMenuItem(this.context, R.drawable.ic_music, "${it?.label ?: it.containerMimeType} ${it.bitrate}", "", it, { _player.selectAudioTrack(it.bitrate) }); @@ -1610,7 +1648,7 @@ class VideoDetailView : ConstraintLayout { if(bestVideoSources.isNotEmpty()) SlideUpMenuGroup(this.context, context.getString(R.string.video), "video", - *bestVideoSources.stream() + *bestVideoSources .map { SlideUpMenuItem(this.context, R.drawable.ic_movie, it!!.name, if (it.width > 0 && it.height > 0) "${it.width}x${it.height}" else "", it, { handleSelectVideoTrack(it) }); @@ -1618,7 +1656,7 @@ class VideoDetailView : ConstraintLayout { else null, if(bestAudioSources.isNotEmpty()) SlideUpMenuGroup(this.context, context.getString(R.string.audio), "audio", - *bestAudioSources.stream() + *bestAudioSources .map { SlideUpMenuItem(this.context, R.drawable.ic_music, it.name, it.bitrate.toHumanBitrate(), it, { handleSelectAudioTrack(it) }); @@ -1841,7 +1879,7 @@ class VideoDetailView : ConstraintLayout { private fun setCastEnabled(isCasting: Boolean) { Logger.i(TAG, "setCastEnabled(isCasting=$isCasting)") - video?.let { updateQualitySourcesOverlay(it); }; + video?.let { updateQualitySourcesOverlay(it, videoLocal); }; _isCasting = isCasting;