From 8dec2f262172fbe51b34c311e1f47703f1d08095 Mon Sep 17 00:00:00 2001 From: Kayos Date: Tue, 26 May 2026 11:16:00 -0700 Subject: [PATCH] vc=57: hide stale inline player frame during video switch MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Cobb-reported 2026-05-26: tapping a related/search/subs video while another is playing rendered the NEW detail page (title, description) with the OLD video's last frame visible in the inline player slot. Root cause — there's a window between streamInfo resolving for the new URL and setPlayingFrom landing on the controller. PlayerView bound to the controller renders the previous video's surface during that window because the controller's MediaItem hasn't swapped yet. Fix — observe NowPlaying.current and add a branch in InlinePlayer's state-when that renders thumbnail + spinner when the controller is still on a different streamUrl. Branch sits above the PlayerView else-arm so the stale surface never gets attached. Flips to PlayerView the moment NowPlaying.claim() lands the new URL. --- buildSrc/src/main/kotlin/ProjectConfig.kt | 4 ++-- .../straw/feature/detail/VideoDetailScreen.kt | 24 +++++++++++++++++++ 2 files changed, 26 insertions(+), 2 deletions(-) diff --git a/buildSrc/src/main/kotlin/ProjectConfig.kt b/buildSrc/src/main/kotlin/ProjectConfig.kt index 225a8c548..e4f73c6c8 100644 --- a/buildSrc/src/main/kotlin/ProjectConfig.kt +++ b/buildSrc/src/main/kotlin/ProjectConfig.kt @@ -55,6 +55,6 @@ const val NEWPIPE_APPLICATION_ID_NEW = "net.newpipe.app" // vc=19 / 0.1.0-AE — rust pipeline cutover. Extraction via // strawcore-core (Sulkta-Coop/strawcore) via the UniFFI wrapper; no // NewPipeExtractor in the runtime path. -const val STRAW_VERSION_CODE = 56 -const val STRAW_VERSION_NAME = "0.1.0-BP" +const val STRAW_VERSION_CODE = 57 +const val STRAW_VERSION_NAME = "0.1.0-BQ" const val STRAW_APPLICATION_ID = "com.sulkta.straw" diff --git a/strawApp/src/main/kotlin/com/sulkta/straw/feature/detail/VideoDetailScreen.kt b/strawApp/src/main/kotlin/com/sulkta/straw/feature/detail/VideoDetailScreen.kt index e10c841f9..447c7f79e 100644 --- a/strawApp/src/main/kotlin/com/sulkta/straw/feature/detail/VideoDetailScreen.kt +++ b/strawApp/src/main/kotlin/com/sulkta/straw/feature/detail/VideoDetailScreen.kt @@ -776,6 +776,14 @@ private fun InlinePlayer( onDispose { c?.removeListener(listener) } } + // Track whether the shared controller has actually swapped over to + // THIS video's stream. Until it does (the brief window between + // streamInfo resolving and setPlayingFrom + setMediaItem landing), + // binding PlayerView to the controller would render the PREVIOUS + // video's frame under the new detail page — exactly the "new page, + // old video" bug. + val nowPlaying by NowPlaying.current.collectAsStateWithLifecycle() + val controllerOnThisVideo = nowPlaying?.streamUrl == streamUrl Box(modifier = modifier, contentAlignment = Alignment.Center) { when { controller == null || state.loading -> CircularProgressIndicator(color = Color.White) @@ -794,6 +802,22 @@ private fun InlinePlayer( color = Color.White, modifier = Modifier.padding(16.dp), ) + // Stream resolved for THIS URL but the controller hasn't + // actually swapped media items yet — show the thumbnail + // with a spinner. Without this, the PlayerView below would + // bind to the controller and render the OUTGOING video's + // last frame while the new detail page chrome shows the + // new title/description. Bug reported 2026-05-26. + !controllerOnThisVideo -> { + if (!thumbnail.isNullOrBlank()) { + AsyncImage( + model = thumbnail, + contentDescription = null, + modifier = Modifier.fillMaxSize(), + ) + } + CircularProgressIndicator(color = Color.White) + } else -> { AndroidView( factory = { ctx ->