diff --git a/app/src/main/java/net/jeena/jtubetv/ui/screens/PlayerScreen.kt b/app/src/main/java/net/jeena/jtubetv/ui/screens/PlayerScreen.kt index 95bcf0a..d11778a 100644 --- a/app/src/main/java/net/jeena/jtubetv/ui/screens/PlayerScreen.kt +++ b/app/src/main/java/net/jeena/jtubetv/ui/screens/PlayerScreen.kt @@ -323,6 +323,10 @@ private fun PlayerSurface( // 200 ms: quick flaps cancel the pending change and the overlay stays // composed, so the animation never restarts mid-playback. var bufferingPending by remember { mutableStateOf(true) } + // STATE_READY can fire before the first frame is actually drawn, leaving a black + // gap between the thumbnail and the picture. Keep the thumbnail until the first + // frame really renders (user report: "thumbnail, then black, then playing"). + var firstFrame by remember { mutableStateOf(false) } LaunchedEffect(bufferingPending) { kotlinx.coroutines.delay(200) buffering = bufferingPending @@ -436,6 +440,10 @@ private fun PlayerSurface( override fun onPlaybackStateChanged(playbackState: Int) { bufferingPending = playbackState != Player.STATE_READY } + + override fun onRenderedFirstFrame() { + firstFrame = true + } }) onDispose { viewModel.persistExit(shortUuid, (player.currentPosition / 1000).toInt()) @@ -588,7 +596,7 @@ private fun PlayerSurface( // ready to render — metadata resolution alone (the Loading state) is not // enough (issue #8). Fullscreen loading shows the video's own thumbnail // dimmed under the circle loader (issue #19) instead of plain black. - if (buffering) { + if (buffering || !firstFrame) { Box( Modifier .fillMaxSize()