Skip to content

Commit 7a2222b

Browse files
authored
Adding Metdata on Player (Initial Draft) (TV) (#2461)
1 parent 76a2feb commit 7a2222b

11 files changed

Lines changed: 392 additions & 12 deletions

File tree

app/src/main/java/com/lagradost/cloudstream3/ui/player/FullScreenPlayer.kt

Lines changed: 64 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ import android.view.animation.AccelerateDecelerateInterpolator
3535
import android.view.animation.AlphaAnimation
3636
import android.view.animation.Animation
3737
import android.view.animation.AnimationUtils
38+
import android.view.animation.DecelerateInterpolator
3839
import android.widget.LinearLayout
3940
import androidx.annotation.OptIn
4041
import androidx.appcompat.app.AlertDialog
@@ -97,6 +98,8 @@ import kotlin.math.max
9798
import kotlin.math.min
9899
import kotlin.math.round
99100
import kotlin.math.roundToInt
101+
import com.lagradost.cloudstream3.utils.AppContextUtils.shouldShowPlayerMetadata
102+
100103

101104
// You can zoom out more than 100%, but it will zoom back into 100%
102105
const val MINIMUM_ZOOM = 0.95f
@@ -133,7 +136,7 @@ open class FullScreenPlayer : AbstractPlayerFragment() {
133136
private var uiShowingBeforeGesture = false
134137
protected var isLocked = false
135138
protected var timestampShowState = false
136-
139+
private var metadataVisibilityToken = 0
137140
protected var hasEpisodes = false
138141
private set
139142
// protected val hasEpisodes
@@ -235,10 +238,55 @@ open class FullScreenPlayer : AbstractPlayerFragment() {
235238
requestUpdateBrightnessOverlayOnNextLayout()
236239
}
237240
}
238-
239241
return root
240242
}
241243

244+
private fun scheduleMetadataVisibility() {
245+
val metadataScrim = playerBinding?.playerMetadataScrim ?: return
246+
val ctx = metadataScrim.context ?: return
247+
248+
if (!ctx.shouldShowPlayerMetadata()) {
249+
metadataScrim.isVisible = false
250+
metadataVisibilityToken++
251+
return
252+
}
253+
254+
if (isLayout(PHONE)) {
255+
metadataScrim.isVisible = false
256+
metadataVisibilityToken++
257+
return
258+
}
259+
260+
val isPaused = currentPlayerStatus == CSPlayerLoading.IsPaused
261+
val token = ++metadataVisibilityToken
262+
263+
if (isPaused) {
264+
metadataScrim.postDelayed({
265+
if (token != metadataVisibilityToken) return@postDelayed
266+
metadataScrim.alpha = 0f
267+
metadataScrim.isVisible = true
268+
metadataScrim.animate()
269+
.alpha(1f)
270+
.setDuration(500L)
271+
.setInterpolator(DecelerateInterpolator())
272+
.start()
273+
hidePlayerUI()
274+
}, 8000L)
275+
} else {
276+
if (metadataScrim.isVisible) {
277+
metadataScrim.animate()
278+
.alpha(0f)
279+
.setDuration(300L)
280+
.setInterpolator(AccelerateDecelerateInterpolator())
281+
.withEndAction {
282+
metadataScrim.alpha = 0f // force final state
283+
metadataScrim.isVisible = false
284+
}
285+
.start()
286+
}
287+
}
288+
}
289+
242290
@SuppressLint("UnsafeOptInUsageError")
243291
override fun playerUpdated(player: Any?) {
244292
super.playerUpdated(player)
@@ -456,6 +504,12 @@ open class FullScreenPlayer : AbstractPlayerFragment() {
456504
start()
457505
}
458506
}
507+
playerBinding?.playerMetadataScrim?.let {
508+
ObjectAnimator.ofFloat(it, "translationY", 1f).apply {
509+
duration = 200
510+
start()
511+
}
512+
}
459513

460514
val playerBarMove = if (isShowing) 0f else 50.toPx.toFloat()
461515
playerBinding?.bottomPlayerBar?.let {
@@ -522,7 +576,7 @@ open class FullScreenPlayer : AbstractPlayerFragment() {
522576
override fun subtitlesChanged() {
523577
val tracks = player.getVideoTracks()
524578
val isBuiltinSubtitles = tracks.currentTextTracks.all { track ->
525-
track.sampleMimeType == MimeTypes.APPLICATION_MEDIA3_CUES
579+
track.sampleMimeType == MimeTypes.APPLICATION_MEDIA3_CUES
526580
}
527581
// Subtitle offset is not possible on built-in media3 tracks
528582
playerBinding?.playerSubtitleOffsetBtt?.isGone =
@@ -1013,7 +1067,6 @@ open class FullScreenPlayer : AbstractPlayerFragment() {
10131067
// BOTTOM
10141068
playerLockHolder.startAnimation(fadeAnimation)
10151069
// player_go_back_holder?.startAnimation(fadeAnimation)
1016-
10171070
shadowOverlay.isVisible = true
10181071
shadowOverlay.startAnimation(fadeAnimation)
10191072
}
@@ -1084,6 +1137,7 @@ open class FullScreenPlayer : AbstractPlayerFragment() {
10841137

10851138
override fun playerStatusChanged() {
10861139
super.playerStatusChanged()
1140+
scheduleMetadataVisibility()
10871141
delayHide()
10881142
}
10891143

@@ -2177,6 +2231,12 @@ open class FullScreenPlayer : AbstractPlayerFragment() {
21772231
}
21782232

21792233
protected fun uiReset() {
2234+
metadataVisibilityToken++
2235+
playerBinding?.playerMetadataScrim?.let {
2236+
it.animate().cancel()
2237+
it.alpha = 0f
2238+
it.isVisible = false
2239+
}
21802240
isShowing = false
21812241
toggleEpisodesOverlay(false)
21822242
// if nothing has loaded these buttons should not be visible

app/src/main/java/com/lagradost/cloudstream3/ui/player/GeneratorPlayer.kt

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,7 @@ import com.lagradost.cloudstream3.ui.result.EpisodeAdapter
8787
import com.lagradost.cloudstream3.ui.result.FOCUS_SELF
8888
import com.lagradost.cloudstream3.ui.result.ResultEpisode
8989
import com.lagradost.cloudstream3.ui.result.ResultFragment
90+
import com.lagradost.cloudstream3.ui.result.ResultFragment.bindLogo
9091
import com.lagradost.cloudstream3.ui.result.ResultViewModel2
9192
import com.lagradost.cloudstream3.ui.result.SyncViewModel
9293
import com.lagradost.cloudstream3.ui.result.setLinearListLayout
@@ -98,6 +99,7 @@ import com.lagradost.cloudstream3.ui.settings.Globals.isLayout
9899
import com.lagradost.cloudstream3.ui.subtitles.SUBTITLE_AUTO_SELECT_KEY
99100
import com.lagradost.cloudstream3.ui.subtitles.SubtitlesFragment
100101
import com.lagradost.cloudstream3.ui.subtitles.SubtitlesFragment.Companion.getAutoSelectLanguageTagIETF
102+
import com.lagradost.cloudstream3.utils.AppContextUtils.getShortSeasonText
101103
import com.lagradost.cloudstream3.utils.AppContextUtils.html
102104
import com.lagradost.cloudstream3.utils.AppContextUtils.sortSubs
103105
import com.lagradost.cloudstream3.utils.Coroutines.ioSafe
@@ -1546,6 +1548,54 @@ class GeneratorPlayer : FullScreenPlayer() {
15461548
return
15471549
}
15481550
loadLink(links.first(), false)
1551+
showPlayerMetadata()
1552+
}
1553+
1554+
private fun showPlayerMetadata() {
1555+
val overlay = playerBinding?.playerMetadataScrim ?: return
1556+
1557+
val titleView = overlay.findViewById<TextView>(R.id.player_movie_title)
1558+
val logoView = overlay.findViewById<ImageView>(R.id.player_movie_logo)
1559+
val metaView = overlay.findViewById<TextView>(R.id.player_movie_meta)
1560+
val descView = overlay.findViewById<TextView>(R.id.player_movie_overview)
1561+
1562+
val load = viewModel.getLoadResponse() ?: return
1563+
val episode = currentMeta as? ResultEpisode
1564+
titleView.text = load.name
1565+
1566+
bindLogo(
1567+
url = load.logoUrl,
1568+
headers = load.posterHeaders,
1569+
titleView = titleView,
1570+
logoView = logoView
1571+
)
1572+
1573+
val meta = arrayOf(
1574+
load.tags?.takeIf { it.isNotEmpty() }?.joinToString(", "),
1575+
load.year?.toString(),
1576+
if (!load.type.isMovieType())
1577+
context?.getShortSeasonText(
1578+
episode = episode?.episode,
1579+
season = episode?.season
1580+
)
1581+
else null,
1582+
load.score?.let { "$it" }
1583+
).filterNotNull()
1584+
.joinToString("")
1585+
1586+
metaView.text = meta
1587+
metaView.isVisible = meta.isNotBlank()
1588+
1589+
1590+
val description = load.plot
1591+
1592+
if (!description.isNullOrBlank()) {
1593+
descView.isVisible = true
1594+
descView.text = description
1595+
} else {
1596+
descView.isVisible = false
1597+
1598+
}
15491599
}
15501600

15511601
override fun nextEpisode() {

app/src/main/java/com/lagradost/cloudstream3/utils/AppContextUtils.kt

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -449,6 +449,14 @@ object AppContextUtils {
449449
return settingsManager.getBoolean(this.getString(R.string.show_trailers_key), true)
450450
}
451451

452+
fun Context.shouldShowPlayerMetadata(): Boolean {
453+
val prefs = PreferenceManager.getDefaultSharedPreferences(this)
454+
return prefs.getBoolean(
455+
getString(R.string.show_player_metadata_key),
456+
true
457+
)
458+
}
459+
452460
fun Context.filterProviderByPreferredMedia(hasHomePageIsRequired: Boolean = true): List<MainAPI> {
453461
// We are getting the weirdest crash ever done:
454462
// java.lang.ClassCastException: com.lagradost.cloudstream3.TvType cannot be cast to com.lagradost.cloudstream3.TvType
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
<shape xmlns:android="http://schemas.android.com/apk/res/android">
2+
<gradient
3+
android:startColor="#E6000000"
4+
android:centerColor="#99000000"
5+
android:endColor="#00000000"
6+
android:angle="0" />
7+
</shape>
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
<vector xmlns:android="http://schemas.android.com/apk/res/android"
2+
android:width="24dp"
3+
android:height="24dp"
4+
android:viewportWidth="24"
5+
android:viewportHeight="24">
6+
7+
<path
8+
android:fillColor="?android:attr/colorControlNormal"
9+
android:fillAlpha="0.4"
10+
android:pathData="
11+
M4,6
12+
h12
13+
a2,2 0 0,1 2,2
14+
v8
15+
a2,2 0 0,1 -2,2
16+
h-12
17+
a2,2 0 0,1 -2,-2
18+
v-8
19+
a2,2 0 0,1 2,-2
20+
z" />
21+
22+
<path
23+
android:fillColor="?android:attr/colorControlNormal"
24+
android:pathData="
25+
M7,4
26+
h12
27+
a2,2 0 0,1 2,2
28+
v8
29+
a2,2 0 0,1 -2,2
30+
h-12
31+
a2,2 0 0,1 -2,-2
32+
v-8
33+
a2,2 0 0,1 2,-2
34+
z" />
35+
36+
</vector>

app/src/main/res/layout/player_custom_layout.xml

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,78 @@
77
android:orientation="vertical"
88
tools:orientation="vertical">
99

10+
<FrameLayout
11+
android:id="@+id/player_metadata_scrim"
12+
android:layout_width="640dp"
13+
android:layout_height="match_parent"
14+
android:background="@drawable/bg_player_metadata_scrim_netflix"
15+
app:layout_constraintStart_toStartOf="parent"
16+
app:layout_constraintTop_toTopOf="parent"
17+
app:layout_constraintBottom_toBottomOf="parent">
18+
19+
<LinearLayout
20+
android:id="@+id/player_metadata_overlay"
21+
android:layout_width="match_parent"
22+
android:layout_height="wrap_content"
23+
android:orientation="vertical"
24+
android:layout_gravity="center_vertical"
25+
android:paddingStart="64dp"
26+
android:paddingEnd="32dp"
27+
android:paddingBottom="32dp">
28+
29+
<!-- TITLE / LOGO -->
30+
<FrameLayout
31+
android:layout_width="match_parent"
32+
android:layout_height="88dp">
33+
34+
<ImageView
35+
android:id="@+id/player_movie_logo"
36+
android:layout_width="wrap_content"
37+
android:layout_height="match_parent"
38+
android:adjustViewBounds="true"
39+
android:scaleType="fitStart"
40+
android:visibility="gone"
41+
tools:visibility="visible"/>
42+
43+
<TextView
44+
android:id="@+id/player_movie_title"
45+
android:layout_width="match_parent"
46+
android:layout_height="wrap_content"
47+
android:layout_gravity="center_vertical"
48+
android:textColor="@android:color/white"
49+
android:textSize="30sp"
50+
android:textStyle="bold"
51+
android:maxLines="2"
52+
android:ellipsize="end"
53+
tools:text="Zootopia 2"/>
54+
</FrameLayout>
55+
56+
<!-- GENRES / YEAR / RATING -->
57+
<TextView
58+
android:id="@+id/player_movie_meta"
59+
android:layout_width="match_parent"
60+
android:layout_height="wrap_content"
61+
android:layout_marginTop="12dp"
62+
android:textColor="#B3FFFFFF"
63+
android:textSize="14sp"
64+
android:maxLines="2"
65+
tools:text="Animation • Action • Adventure • 2025 • ⭐ 7.6"/>
66+
67+
<!-- SYNOPSIS -->
68+
<TextView
69+
android:id="@+id/player_movie_overview"
70+
android:layout_width="match_parent"
71+
android:layout_height="wrap_content"
72+
android:layout_marginTop="20dp"
73+
android:textColor="#E6FFFFFF"
74+
android:textSize="16sp"
75+
android:lineSpacingExtra="8dp"
76+
android:maxLines="5"
77+
tools:text="Brave rabbit cop Judy Hopps and her friend, the fox Nick Wilde, team up again to crack a new case."/>
78+
79+
</LinearLayout>
80+
</FrameLayout>
81+
1082
<ImageView
1183
android:visibility="gone"
1284
android:id="@+id/video_outline"

0 commit comments

Comments
 (0)