From d56d74fe74c1dc2427aa9559854be2b593e41b41 Mon Sep 17 00:00:00 2001 From: Phisher98 Date: Wed, 28 Jan 2026 23:00:29 +0530 Subject: [PATCH 1/7] Adding Metdata on Player (Initial Draft) --- .../ui/player/FullScreenPlayer.kt | 37 +++++++++- .../cloudstream3/ui/player/GeneratorPlayer.kt | 52 ++++++++++++++ .../bg_player_metadata_scrim_netflix.xml | 7 ++ .../res/layout/player_custom_layout_tv.xml | 69 +++++++++++++++++++ 4 files changed, 162 insertions(+), 3 deletions(-) create mode 100644 app/src/main/res/drawable/bg_player_metadata_scrim_netflix.xml diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/player/FullScreenPlayer.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/player/FullScreenPlayer.kt index 94d4a53be2..9d3326269f 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/player/FullScreenPlayer.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/player/FullScreenPlayer.kt @@ -231,6 +231,37 @@ open class FullScreenPlayer : AbstractPlayerFragment() { } + private fun scheduleMetadataVisibility() { + val metadataScrim = + playerBinding?.root?.findViewById(R.id.player_metadata_scrim) ?: return + + val isPaused = currentPlayerStatus == CSPlayerLoading.IsPaused + + metadataScrim.animate().cancel() + + if (isPaused) { + metadataScrim.postDelayed({ + metadataScrim.apply { + isVisible = true + alpha = 0f + animate() + .alpha(1f) + .setDuration(300L) + .start() + } + }, 8000L) + } else { + metadataScrim.animate() + .alpha(0f) + .setDuration(200L) + .withEndAction { + metadataScrim.isVisible = false + } + .start() + } + } + + fun setGpuExtraBrightness(extra: Float) { gpuBrightnessFilter?.setBrightness(extra) } @@ -387,7 +418,6 @@ open class FullScreenPlayer : AbstractPlayerFragment() { start() } } - val playerBarMove = if (isShowing) 0f else 50.toPx.toFloat() playerBinding?.bottomPlayerBar?.let { ObjectAnimator.ofFloat(it, "translationY", playerBarMove).apply { @@ -453,7 +483,7 @@ open class FullScreenPlayer : AbstractPlayerFragment() { override fun subtitlesChanged() { val tracks = player.getVideoTracks() val isBuiltinSubtitles = tracks.currentTextTracks.all { track -> - track.sampleMimeType == MimeTypes.APPLICATION_MEDIA3_CUES + track.sampleMimeType == MimeTypes.APPLICATION_MEDIA3_CUES } // Subtitle offset is not possible on built-in media3 tracks playerBinding?.playerSubtitleOffsetBtt?.isGone = @@ -563,6 +593,7 @@ open class FullScreenPlayer : AbstractPlayerFragment() { override fun onResume() { enterFullscreen() verifyVolume() + playerBinding?.root?.findViewById(R.id.player_metadata_scrim)?.isVisible = false activity?.attachBackPressedCallback("FullScreenPlayer") { if (isShowingEpisodeOverlay) { // isShowingEpisodeOverlay pauses, so this makes it easier to unpause @@ -938,7 +969,6 @@ open class FullScreenPlayer : AbstractPlayerFragment() { // BOTTOM playerLockHolder.startAnimation(fadeAnimation) // player_go_back_holder?.startAnimation(fadeAnimation) - shadowOverlay.isVisible = true shadowOverlay.startAnimation(fadeAnimation) } @@ -1009,6 +1039,7 @@ open class FullScreenPlayer : AbstractPlayerFragment() { override fun playerStatusChanged() { super.playerStatusChanged() + scheduleMetadataVisibility() delayHide() } diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/player/GeneratorPlayer.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/player/GeneratorPlayer.kt index 78655590b3..78d912042f 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/player/GeneratorPlayer.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/player/GeneratorPlayer.kt @@ -1541,6 +1541,58 @@ class GeneratorPlayer : FullScreenPlayer() { return } loadLink(links.first(), false) + showPlayerMetadata() + } + + private fun showPlayerMetadata() { + val root = binding?.root ?: return + val overlay = root.findViewById(R.id.player_metadata_scrim) ?: return + + val titleView = overlay.findViewById(R.id.player_movie_title) + val logoView = overlay.findViewById(R.id.player_movie_logo) + val metaView = overlay.findViewById(R.id.player_movie_meta) + val descView = overlay.findViewById(R.id.player_movie_overview) + + val load = viewModel.getLoadResponse() ?: return + val episode = currentMeta as? ResultEpisode + + val logoUrl = load.logoUrl + + if (!logoUrl.isNullOrBlank()) { + logoView.isVisible = true + titleView.isVisible = false + CloudStreamApp.context?.getImageBitmapFromUrl(logoUrl)?.let { + logoView.setImageBitmap(it) + } + } else { + logoView.isVisible = false + titleView.isVisible = true + titleView.text = load.name + } + + val meta = arrayOf( + load.tags?.takeIf { it.isNotEmpty() }?.joinToString(", "), + load.year?.toString(), + if (!load.type.isMovieType()) + episode?.let { "S${it.season} • E${it.episode}" } + else null, + load.score?.let { "⭐ $it" } + ).filterNotNull() + .joinToString(" • ") + + metaView.text = meta + metaView.isVisible = meta.isNotBlank() + + + val description = load.plot + + if (!description.isNullOrBlank()) { + descView.isVisible = true + descView.text = description + } else { + descView.isVisible = false + + } } override fun nextEpisode() { diff --git a/app/src/main/res/drawable/bg_player_metadata_scrim_netflix.xml b/app/src/main/res/drawable/bg_player_metadata_scrim_netflix.xml new file mode 100644 index 0000000000..b4701e42a7 --- /dev/null +++ b/app/src/main/res/drawable/bg_player_metadata_scrim_netflix.xml @@ -0,0 +1,7 @@ + + + \ No newline at end of file diff --git a/app/src/main/res/layout/player_custom_layout_tv.xml b/app/src/main/res/layout/player_custom_layout_tv.xml index 8030765b2f..4443c2e76d 100644 --- a/app/src/main/res/layout/player_custom_layout_tv.xml +++ b/app/src/main/res/layout/player_custom_layout_tv.xml @@ -9,6 +9,75 @@ tools:orientation="vertical"> + + + + + + + + + + + + + + + + + + Date: Thu, 29 Jan 2026 10:13:11 +0530 Subject: [PATCH 2/7] Minor Fix and Changes to Metdata on Player --- .../ui/player/FullScreenPlayer.kt | 25 +++--- .../cloudstream3/ui/player/GeneratorPlayer.kt | 30 ++++--- .../main/res/layout/player_custom_layout.xml | 81 +++++++++++++++++++ .../main/res/layout/trailer_custom_layout.xml | 70 ++++++++++++++++ 4 files changed, 175 insertions(+), 31 deletions(-) diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/player/FullScreenPlayer.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/player/FullScreenPlayer.kt index 9d3326269f..f71d6949f9 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/player/FullScreenPlayer.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/player/FullScreenPlayer.kt @@ -32,6 +32,7 @@ import android.view.animation.AccelerateDecelerateInterpolator import android.view.animation.AlphaAnimation import android.view.animation.Animation import android.view.animation.AnimationUtils +import android.view.animation.DecelerateInterpolator import android.widget.LinearLayout import androidx.annotation.OptIn import androidx.appcompat.app.AlertDialog @@ -232,8 +233,7 @@ open class FullScreenPlayer : AbstractPlayerFragment() { private fun scheduleMetadataVisibility() { - val metadataScrim = - playerBinding?.root?.findViewById(R.id.player_metadata_scrim) ?: return + val metadataScrim = playerBinding?.playerMetadataScrim ?: return val isPaused = currentPlayerStatus == CSPlayerLoading.IsPaused @@ -243,21 +243,10 @@ open class FullScreenPlayer : AbstractPlayerFragment() { metadataScrim.postDelayed({ metadataScrim.apply { isVisible = true - alpha = 0f - animate() - .alpha(1f) - .setDuration(300L) - .start() } }, 8000L) } else { - metadataScrim.animate() - .alpha(0f) - .setDuration(200L) - .withEndAction { - metadataScrim.isVisible = false - } - .start() + metadataScrim.isVisible = false } } @@ -418,6 +407,13 @@ open class FullScreenPlayer : AbstractPlayerFragment() { start() } } + playerBinding?.playerMetadataScrim?.let { + ObjectAnimator.ofFloat(it, "translationY", 1f).apply { + duration = 200 + start() + } + } + val playerBarMove = if (isShowing) 0f else 50.toPx.toFloat() playerBinding?.bottomPlayerBar?.let { ObjectAnimator.ofFloat(it, "translationY", playerBarMove).apply { @@ -593,7 +589,6 @@ open class FullScreenPlayer : AbstractPlayerFragment() { override fun onResume() { enterFullscreen() verifyVolume() - playerBinding?.root?.findViewById(R.id.player_metadata_scrim)?.isVisible = false activity?.attachBackPressedCallback("FullScreenPlayer") { if (isShowingEpisodeOverlay) { // isShowingEpisodeOverlay pauses, so this makes it easier to unpause diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/player/GeneratorPlayer.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/player/GeneratorPlayer.kt index 78d912042f..c434463451 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/player/GeneratorPlayer.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/player/GeneratorPlayer.kt @@ -85,6 +85,7 @@ import com.lagradost.cloudstream3.ui.result.EpisodeAdapter import com.lagradost.cloudstream3.ui.result.FOCUS_SELF import com.lagradost.cloudstream3.ui.result.ResultEpisode import com.lagradost.cloudstream3.ui.result.ResultFragment +import com.lagradost.cloudstream3.ui.result.ResultFragment.bindLogo import com.lagradost.cloudstream3.ui.result.ResultViewModel2 import com.lagradost.cloudstream3.ui.result.setLinearListLayout import com.lagradost.cloudstream3.ui.result.SyncViewModel @@ -96,6 +97,7 @@ import com.lagradost.cloudstream3.ui.settings.Globals.TV import com.lagradost.cloudstream3.ui.subtitles.SUBTITLE_AUTO_SELECT_KEY import com.lagradost.cloudstream3.ui.subtitles.SubtitlesFragment import com.lagradost.cloudstream3.ui.subtitles.SubtitlesFragment.Companion.getAutoSelectLanguageTagIETF +import com.lagradost.cloudstream3.utils.AppContextUtils.getShortSeasonText import com.lagradost.cloudstream3.utils.AppContextUtils.html import com.lagradost.cloudstream3.utils.AppContextUtils.sortSubs import com.lagradost.cloudstream3.utils.Coroutines.ioSafe @@ -1545,8 +1547,7 @@ class GeneratorPlayer : FullScreenPlayer() { } private fun showPlayerMetadata() { - val root = binding?.root ?: return - val overlay = root.findViewById(R.id.player_metadata_scrim) ?: return + val overlay = playerBinding?.playerMetadataScrim ?: return val titleView = overlay.findViewById(R.id.player_movie_title) val logoView = overlay.findViewById(R.id.player_movie_logo) @@ -1555,26 +1556,23 @@ class GeneratorPlayer : FullScreenPlayer() { val load = viewModel.getLoadResponse() ?: return val episode = currentMeta as? ResultEpisode + titleView.text = load.name - val logoUrl = load.logoUrl - - if (!logoUrl.isNullOrBlank()) { - logoView.isVisible = true - titleView.isVisible = false - CloudStreamApp.context?.getImageBitmapFromUrl(logoUrl)?.let { - logoView.setImageBitmap(it) - } - } else { - logoView.isVisible = false - titleView.isVisible = true - titleView.text = load.name - } + bindLogo( + url = load.logoUrl, + headers = load.posterHeaders, + titleView = titleView, + logoView = logoView + ) val meta = arrayOf( load.tags?.takeIf { it.isNotEmpty() }?.joinToString(", "), load.year?.toString(), if (!load.type.isMovieType()) - episode?.let { "S${it.season} • E${it.episode}" } + context?.getShortSeasonText( + episode = episode?.episode, + season = episode?.season + ) else null, load.score?.let { "⭐ $it" } ).filterNotNull() diff --git a/app/src/main/res/layout/player_custom_layout.xml b/app/src/main/res/layout/player_custom_layout.xml index d95c92e010..ac21b165d4 100644 --- a/app/src/main/res/layout/player_custom_layout.xml +++ b/app/src/main/res/layout/player_custom_layout.xml @@ -7,6 +7,87 @@ android:orientation="vertical" tools:orientation="vertical"> + + + + + + + + + + + + + + + + + + + + + + +