diff --git a/app/src/main/java/com/futo/platformplayer/fragment/mainactivity/main/VideoDetailFragment.kt b/app/src/main/java/com/futo/platformplayer/fragment/mainactivity/main/VideoDetailFragment.kt index fcf15c3e..fc411de7 100644 --- a/app/src/main/java/com/futo/platformplayer/fragment/mainactivity/main/VideoDetailFragment.kt +++ b/app/src/main/java/com/futo/platformplayer/fragment/mainactivity/main/VideoDetailFragment.kt @@ -96,7 +96,7 @@ class VideoDetailFragment : MainFragment { detectWindowSize() - val isLandscapeVideo: Boolean = _viewDetail?.isLandscapeVideo() ?: true + val isLandscapeVideo: Boolean = _viewDetail?.isLandscapeVideo() ?: false if ( isSmallWindow @@ -117,13 +117,29 @@ class VideoDetailFragment : MainFragment { } private fun onStateChanged(state: State) { - if (isSmallWindow && state == State.MAXIMIZED && !isFullscreen && resources.configuration.orientation == Configuration.ORIENTATION_LANDSCAPE) { + if ( + isSmallWindow + && state == State.MAXIMIZED + && !isFullscreen + && resources.configuration.orientation == Configuration.ORIENTATION_LANDSCAPE + ) { _viewDetail?.setFullscreen(true) } updateOrientation() } + private fun onVideoChanged(videoWidth : Int, videoHeight: Int) { + if ( + isSmallWindow + && state == State.MAXIMIZED + && !isFullscreen + && videoHeight > videoWidth + ) { + _viewDetail?.setFullscreen(true) + } + } + @SuppressLint("SourceLockedOrientationActivity") fun updateOrientation() { val a = activity ?: return @@ -133,7 +149,7 @@ class VideoDetailFragment : MainFragment { val isReversePortraitAllowed = Settings.instance.playback.reversePortrait val rotationLock = StatePlayer.instance.rotationLock - val isLandscapeVideo: Boolean = _viewDetail?.isLandscapeVideo() ?: true + val isLandscapeVideo: Boolean = _viewDetail?.isLandscapeVideo() ?: false // For small windows if the device isn't landscape right now and full screen portrait isn't allowed then we should force landscape if (isSmallWindow && isFullscreen && !isFullScreenPortraitAllowed && resources.configuration.orientation == Configuration.ORIENTATION_PORTRAIT && !rotationLock && isLandscapeVideo) { @@ -247,6 +263,7 @@ class VideoDetailFragment : MainFragment { _viewDetail = _view!!.findViewById(R.id.fragview_videodetail).also { it.applyFragment(this); it.onFullscreenChanged.subscribe(::onFullscreenChanged); + it.onVideoChanged.subscribe(::onVideoChanged) it.onMinimize.subscribe { isMinimizing = true _view!!.transitionToStart(); 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 3ee7a06b..78d1d41d 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 @@ -80,6 +80,7 @@ import com.futo.platformplayer.casting.CastConnectionState import com.futo.platformplayer.casting.StateCasting import com.futo.platformplayer.constructs.Event0 import com.futo.platformplayer.constructs.Event1 +import com.futo.platformplayer.constructs.Event2 import com.futo.platformplayer.constructs.TaskHandler import com.futo.platformplayer.downloads.VideoLocal import com.futo.platformplayer.dp @@ -300,6 +301,7 @@ class VideoDetailView : ConstraintLayout { val onFullscreenChanged = Event1(); val onEnterPictureInPicture = Event0(); val onPlayChanged = Event1(); + val onVideoChanged = Event2() var allowBackground : Boolean = false private set; @@ -1205,6 +1207,7 @@ class VideoDetailView : ConstraintLayout { switchContentView(_container_content_main); } + @OptIn(ExperimentalCoroutinesApi::class) fun setVideoDetails(videoDetail: IPlatformVideoDetails, newVideo: Boolean = false) { Logger.i(TAG, "setVideoDetails (${videoDetail.name})") @@ -1213,7 +1216,7 @@ class VideoDetailView : ConstraintLayout { _autoplayVideo = null Logger.i(TAG, "Autoplay video cleared (setVideoDetails)") - if(newVideo && this.video?.url == videoDetail.url) + if (newVideo && this.video?.url == videoDetail.url) return; if (newVideo) { @@ -1222,8 +1225,13 @@ class VideoDetailView : ConstraintLayout { _lastSubtitleSource = null; } - if(videoDetail.datetime != null && videoDetail.datetime!! > OffsetDateTime.now()) - UIDialogs.toast(context, context.getString(R.string.planned_in) + " ${videoDetail.datetime?.toHumanNowDiffString(true)}") + if (videoDetail.datetime != null && videoDetail.datetime!! > OffsetDateTime.now()) + UIDialogs.toast( + context, + context.getString(R.string.planned_in) + " ${ + videoDetail.datetime?.toHumanNowDiffString(true) + }" + ) if (!videoDetail.isLive) { _player.setPlaybackRate(Settings.instance.playback.getDefaultPlaybackSpeed()); @@ -1232,26 +1240,25 @@ class VideoDetailView : ConstraintLayout { val videoLocal: VideoLocal?; val video: IPlatformVideoDetails?; - if(videoDetail is VideoLocal) { + if (videoDetail is VideoLocal) { videoLocal = videoDetail; video = videoDetail; this.video = video; val videoTask = StatePlatform.instance.getContentDetails(videoDetail.url); videoTask.invokeOnCompletion { ex -> - if(ex != null) { + 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 is IPlatformVideoDetails) { + if (this.video == videoDetail && result is IPlatformVideoDetails) { this.video = result; fragment.lifecycleScope.launch(Dispatchers.Main) { updateQualitySourcesOverlay(result, videoLocal); } } }; - } - else { //TODO: Update cached video if it exists with video + } else { //TODO: Update cached video if it exists with video videoLocal = StateDownloads.instance.getCachedVideo(videoDetail.id); video = videoDetail; } @@ -1259,7 +1266,9 @@ class VideoDetailView : ConstraintLayout { this.video = video; cleanupPlaybackTracker(); - if(video is JSVideoDetails) { + onVideoChanged.emit(video.video.videoSources[0].width, video.video.videoSources[0].height) + + if (video is JSVideoDetails) { val me = this; fragment.lifecycleScope.launch(Dispatchers.IO) { try { @@ -1267,8 +1276,7 @@ class VideoDetailView : ConstraintLayout { val chapters = null ?: StatePlatform.instance.getContentChapters(video.url); _player.setChapters(chapters); _cast.setChapters(chapters); - } - catch(ex: Throwable) { + } catch (ex: Throwable) { Logger.e(TAG, "Failed to get chapters", ex); _player.setChapters(null); _cast.setChapters(null); @@ -1278,7 +1286,7 @@ class VideoDetailView : ConstraintLayout { }*/ } try { - if(!StateApp.instance.privateMode) { + if (!StateApp.instance.privateMode) { val stopwatch = com.futo.platformplayer.debug.Stopwatch() var tracker = video.getPlaybackTracker() Logger.i(TAG, "video.getPlaybackTracker took ${stopwatch.elapsedMs}ms") @@ -1294,17 +1302,22 @@ class VideoDetailView : ConstraintLayout { if (me.video == video) me._playbackTracker = tracker; - } - else if(me.video == video) + } else if (me.video == video) me._playbackTracker = null; - } - catch(ex: Throwable) { + } catch (ex: Throwable) { 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)); + 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); + UIDialogs.showGeneralErrorDialog( + context, + context.getString(R.string.failed_to_get_playback_tracker), + ex + ); } } }; @@ -1321,8 +1334,11 @@ class VideoDetailView : ConstraintLayout { if (Settings.instance.comments.recommendationsDefault && !Settings.instance.comments.hideRecommendations) { setTabIndex(2, true) } else { - when(Settings.instance.comments.defaultCommentSection) { - 0 -> if(Settings.instance.other.polycentricEnabled) setTabIndex(0, true) else setTabIndex(1, true); + when (Settings.instance.comments.defaultCommentSection) { + 0 -> if (Settings.instance.other.polycentricEnabled) setTabIndex( + 0, + true + ) else setTabIndex(1, true); 1 -> setTabIndex(1, true); 2 -> setTabIndex(StateMeta.instance.getLastCommentSection(), true) } @@ -1332,9 +1348,16 @@ class VideoDetailView : ConstraintLayout { //UI _title.text = video.name; _channelName.text = video.author.name; - if(video.author.subscribers != null) { - _channelMeta.text = if((video.author.subscribers ?: 0) > 0) video.author.subscribers!!.toHumanNumber() + " " + context.getString(R.string.subscribers) else ""; - (_channelName.layoutParams as MarginLayoutParams).setMargins(0, (DP_5 * -1).toInt(), 0, 0); + if (video.author.subscribers != null) { + _channelMeta.text = if ((video.author.subscribers + ?: 0) > 0 + ) video.author.subscribers!!.toHumanNumber() + " " + context.getString(R.string.subscribers) else ""; + (_channelName.layoutParams as MarginLayoutParams).setMargins( + 0, + (DP_5 * -1).toInt(), + 0, + 0 + ); } else { _channelMeta.text = ""; (_channelName.layoutParams as MarginLayoutParams).setMargins(0, (DP_2).toInt(), 0, 0); @@ -1342,7 +1365,7 @@ class VideoDetailView : ConstraintLayout { video.author.let { - if(it is PlatformAuthorMembershipLink && !it.membershipUrl.isNullOrEmpty()) + if (it is PlatformAuthorMembershipLink && !it.membershipUrl.isNullOrEmpty()) _monetization.setPlatformMembership(video.id.pluginId, it.membershipUrl); else _monetization.setPlatformMembership(null, null); @@ -1356,7 +1379,8 @@ class VideoDetailView : ConstraintLayout { _creatorThumbnail.setThumbnail(video.author.thumbnail, false); - val cachedPolycentricProfile = PolycentricCache.instance.getCachedProfile(video.author.url, true); + val cachedPolycentricProfile = + PolycentricCache.instance.getCachedProfile(video.author.url, true); if (cachedPolycentricProfile != null) { setPolycentricProfile(cachedPolycentricProfile, animate = false); } else { @@ -1365,13 +1389,19 @@ class VideoDetailView : ConstraintLayout { } _platform.setPlatformFromClientID(video.id.pluginId); - val subTitleSegments : ArrayList = ArrayList(); - if(video.viewCount > 0) - subTitleSegments.add("${video.viewCount.toHumanNumber()} ${if(video.isLive) context.getString(R.string.watching_now) else context.getString(R.string.views)}"); - if(video.datetime != null) { + val subTitleSegments: ArrayList = ArrayList(); + if (video.viewCount > 0) + subTitleSegments.add( + "${video.viewCount.toHumanNumber()} ${ + if (video.isLive) context.getString( + R.string.watching_now + ) else context.getString(R.string.views) + }" + ); + if (video.datetime != null) { val diff = video.datetime?.getNowDiffSeconds() ?: 0; val ago = video.datetime?.toHumanNowDiffString(true) - if(diff >= 0) + if (diff >= 0) subTitleSegments.add("${ago} ago"); else subTitleSegments.add("available in ${ago}"); @@ -1384,20 +1414,27 @@ class VideoDetailView : ConstraintLayout { fragment.lifecycleScope.launch(Dispatchers.IO) { try { - val queryReferencesResponse = ApiMethods.getQueryReferences(PolycentricCache.SERVER, ref, null,null, + val queryReferencesResponse = ApiMethods.getQueryReferences( + PolycentricCache.SERVER, ref, null, null, arrayListOf( - Protocol.QueryReferencesRequestCountLWWElementReferences.newBuilder().setFromType(ContentType.OPINION.value).setValue( - ByteString.copyFrom(Opinion.like.data)).build(), - Protocol.QueryReferencesRequestCountLWWElementReferences.newBuilder().setFromType(ContentType.OPINION.value).setValue( - ByteString.copyFrom(Opinion.dislike.data)).build() + Protocol.QueryReferencesRequestCountLWWElementReferences.newBuilder() + .setFromType(ContentType.OPINION.value).setValue( + ByteString.copyFrom(Opinion.like.data) + ).build(), + Protocol.QueryReferencesRequestCountLWWElementReferences.newBuilder() + .setFromType(ContentType.OPINION.value).setValue( + ByteString.copyFrom(Opinion.dislike.data) + ).build() ), extraByteReferences = listOfNotNull(extraBytesRef) ); val likes = queryReferencesResponse.countsList[0]; val dislikes = queryReferencesResponse.countsList[1]; - val hasLiked = StatePolycentric.instance.hasLiked(ref.toByteArray())/* || extraBytesRef?.let { StatePolycentric.instance.hasLiked(it) } ?: false*/; - val hasDisliked = StatePolycentric.instance.hasDisliked(ref.toByteArray())/* || extraBytesRef?.let { StatePolycentric.instance.hasDisliked(it) } ?: false*/; + val hasLiked = + StatePolycentric.instance.hasLiked(ref.toByteArray())/* || extraBytesRef?.let { StatePolycentric.instance.hasLiked(it) } ?: false*/; + val hasDisliked = + StatePolycentric.instance.hasDisliked(ref.toByteArray())/* || extraBytesRef?.let { StatePolycentric.instance.hasDisliked(it) } ?: false*/; withContext(Dispatchers.Main) { _rating.visibility = View.VISIBLE; @@ -1421,7 +1458,11 @@ class VideoDetailView : ConstraintLayout { } } - StatePolycentric.instance.updateLikeMap(ref, args.hasLiked, args.hasDisliked) + StatePolycentric.instance.updateLikeMap( + ref, + args.hasLiked, + args.hasDisliked + ) }; } } catch (e: Throwable) { @@ -1443,6 +1484,7 @@ class VideoDetailView : ConstraintLayout { _textDislikes.visibility = View.VISIBLE; _textDislikes.text = r.dislikes.toHumanNumber(); } + is RatingLikes -> { val r = video.rating as RatingLikes; _layoutRating.visibility = View.VISIBLE; @@ -1454,6 +1496,7 @@ class VideoDetailView : ConstraintLayout { _imageDislikeIcon.visibility = View.GONE; _textDislikes.visibility = View.GONE; } + else -> { _layoutRating.visibility = View.GONE; } @@ -1465,6 +1508,7 @@ class VideoDetailView : ConstraintLayout { setLoading(false); + //Set Mediasource val toResume = _videoResumePositionMilliseconds; @@ -1481,9 +1525,22 @@ class VideoDetailView : ConstraintLayout { val historyItem = getHistoryIndex(videoDetail) ?: return@launch; withContext(Dispatchers.Main) { - _historicalPosition = StateHistory.instance.updateHistoryPosition(video, historyItem,false, (toResume.toFloat() / 1000.0f).toLong(), null, true); - Logger.i(TAG, "Historical position: $_historicalPosition, last position: $lastPositionMilliseconds"); - if (_historicalPosition > 60 && video.duration - _historicalPosition > 5 && Math.abs(_historicalPosition - lastPositionMilliseconds / 1000) > 5.0) { + _historicalPosition = StateHistory.instance.updateHistoryPosition( + video, + historyItem, + false, + (toResume.toFloat() / 1000.0f).toLong(), + null, + true + ); + Logger.i( + TAG, + "Historical position: $_historicalPosition, last position: $lastPositionMilliseconds" + ); + if (_historicalPosition > 60 && video.duration - _historicalPosition > 5 && Math.abs( + _historicalPosition - lastPositionMilliseconds / 1000 + ) > 5.0 + ) { _layoutResume.visibility = View.VISIBLE; _textResume.text = "Resume at ${_historicalPosition.toHumanTime(false)}"; @@ -1509,10 +1566,10 @@ class VideoDetailView : ConstraintLayout { _liveChat?.stop(); _liveChat = null; - if(video.isLive && video.live != null) { + if (video.isLive && video.live != null) { loadLiveChat(video); } - if(video.isLive && video.live == null && !video.video.videoSources.any()) + if (video.isLive && video.live == null && !video.video.videoSources.any()) startLiveTry(video); @@ -2241,14 +2298,14 @@ class VideoDetailView : ConstraintLayout { } } - fun isLandscapeVideo(): Boolean { + fun isLandscapeVideo(): Boolean? { var videoSourceWidth = _player.exoPlayer?.player?.videoSize?.width var videoSourceHeight = _player.exoPlayer?.player?.videoSize?.height - return if (videoSourceHeight != null && videoSourceWidth != null) { - videoSourceWidth > videoSourceHeight - } else { - true + return if (videoSourceWidth == null || videoSourceHeight == null || videoSourceWidth == 0 || videoSourceHeight == 0){ + null + } else{ + videoSourceWidth >= videoSourceHeight } }