From 452c2863578d281d74b97f87f75290dd13286d91 Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 30 Jul 2026 19:33:15 -0500 Subject: [PATCH] Stop a YouTube embed when the playlist moves off it MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The video kept playing behind the next item and its audio carried on over the top: "even when the picture is there the sound from the video continues playing." Switching away only set the WebView's visibility to GONE, and visibility is not playback state — a hidden WebView keeps running. The three paths that leave a YouTube item (image mount, local video, streamed video) all hid it and none stopped it. stop() has always blanked the WebView with about:blank; the item-switch paths simply never did. This could not surface before 1.9.26, because a YouTube item never advanced at all, so nothing ever switched away from one. Fixing the advance is what exposed it. The reporter narrowed it further without being asked, and their finding names the mechanism exactly: "picture, video -> the sound continues when the picture comes after the video. picture, video, html/text -> the sound do not play after the video." A widget loads a new URL into the SAME WebView, which replaces the YouTube page and stops it; an image only hides it. One case was silent and the other was not for precisely that reason. stopYoutubeIfPlaying() is guarded on the OUTGOING type, so it must be called before currentType is reassigned, and it cannot blank a widget that is being reused. Blanking is safe because playYoutube reloads the embed from scratch on every play. Verified on an Android 12 emulator, counting the app's own started audio players against the item on screen, before and after: 1.9.27 as released — image on screen, 1 player still started (the reported fault) with this fix — image on screen, 0 players started; 1 only while the video is up Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_01Uaeo9MvzKoyXuN6ZsbhtkL --- .../player/player/MediaPlayerManager.kt | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/android/app/src/main/java/com/remotedisplay/player/player/MediaPlayerManager.kt b/android/app/src/main/java/com/remotedisplay/player/player/MediaPlayerManager.kt index 8716357..5858c72 100644 --- a/android/app/src/main/java/com/remotedisplay/player/player/MediaPlayerManager.kt +++ b/android/app/src/main/java/com/remotedisplay/player/player/MediaPlayerManager.kt @@ -152,6 +152,7 @@ class MediaPlayerManager( // Plain image mount (visibility flip + set bitmap). Shared by the transition-done swap and the // no-transition hard cut. private fun mountImageBitmap(bitmap: Bitmap) { + stopYoutubeIfPlaying() currentType = MediaType.IMAGE currentWidgetUrl = null // surface reused - a later widget show must reload playerView.visibility = android.view.View.GONE @@ -162,6 +163,24 @@ class MediaPlayerManager( catch (e: Throwable) { Log.e("MediaPlayerManager", "setImageBitmap failed: ${e.message}"); onImageError?.invoke() } } + /** + * Stop a YouTube embed that is being switched away from. + * + * Hiding the WebView does NOT stop it — visibility is not playback state, so the video kept + * running behind the next item and its audio carried on over the top. Reported after YouTube + * items started advancing at all (before that they never ended, so nothing ever switched away + * from one and this could not surface): "even when the picture is there the sound from the + * video continues playing". + * + * Blanking is what stop() already does, and it is safe here because playYoutube always reloads + * the embed from scratch anyway. Guarded on the OUTGOING type so it must be called before + * currentType is reassigned, and so it never blanks a widget that is being reused. + */ + private fun stopYoutubeIfPlaying() { + if (currentType != MediaType.YOUTUBE) return + youtubeWebView?.loadUrl("about:blank") + } + fun playYoutube(embedUrl: String, durationSec: Int = 0, muted: Boolean = false) { Log.i("MediaPlayerManager", "Playing YouTube: $embedUrl (muted=$muted)") currentType = MediaType.YOUTUBE @@ -229,6 +248,7 @@ class MediaPlayerManager( fun playVideoFromUrl(url: String, muted: Boolean = false) { Log.i("MediaPlayerManager", "Streaming video from URL: $url (muted=$muted)") + stopYoutubeIfPlaying() currentType = MediaType.VIDEO currentWidgetUrl = null // surface reused - a later widget show must reload @@ -303,6 +323,7 @@ class MediaPlayerManager( } private fun mountVideo(file: File, muted: Boolean = false) { + stopYoutubeIfPlaying() currentType = MediaType.VIDEO currentWidgetUrl = null // surface reused - a later widget show must reload