From 5835aedff63931eb906739d8195a622944688f6b Mon Sep 17 00:00:00 2001 From: sevenrats Date: Thu, 30 Jul 2026 12:50:36 -0400 Subject: [PATCH 1/2] Fix playbackstop listener leak on the movies page moviesrecommended subscribed to playbackstop in its viewshow handler but never unsubscribed, so one listener was leaked per visit to the page and accumulated without bound for the life of the session. Each stale handler ran on every playback stop and called triggerTabChange against a view that may no longer be current. Release the subscription in viewbeforehide, mirroring the symmetric pair already present in the equivalent Shows controller (tvrecommended.js). Verified in a browser (Jellyfin Server 10.12.0, Firefox 140, legacy app) by counting subscribers on the playbackManager singleton: previously the count grew 1/2/3/4 over four visits to the page; it now stays at 0 after leaving and 1 while the page is shown. --- src/apps/legacy/controllers/movies/moviesrecommended.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/apps/legacy/controllers/movies/moviesrecommended.js b/src/apps/legacy/controllers/movies/moviesrecommended.js index 4570879d6f..ff0b706c49 100644 --- a/src/apps/legacy/controllers/movies/moviesrecommended.js +++ b/src/apps/legacy/controllers/movies/moviesrecommended.js @@ -397,6 +397,7 @@ export default function (view, params) { }); view.addEventListener('viewbeforehide', function () { inputManager.off(window, onInputCommand); + Events.off(playbackManager, 'playbackstop', onPlaybackStop); }); for (const tabController of tabControllers) { if (tabController.destroy) { From 4b971a92dc126ace2bf52f172c04c64e2b7e3a20 Mon Sep 17 00:00:00 2001 From: sevenrats Date: Mon, 3 Aug 2026 14:45:53 -0400 Subject: [PATCH 2/2] Fix playbackstop event handling onPlaybackStop read NowPlayingItem directly off the playbackstop payload, but playbackManager emits { player, state, nextItem, nextMediaType }, so the field was always undefined, the media type guard never passed, and the suggestions tab never refreshed after a video stopped. Co-authored-by: Dmitry Lyzo <56478732+dmitrylyzo@users.noreply.github.com> --- src/apps/legacy/controllers/movies/moviesrecommended.js | 4 +++- src/apps/legacy/controllers/shows/tvrecommended.js | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/apps/legacy/controllers/movies/moviesrecommended.js b/src/apps/legacy/controllers/movies/moviesrecommended.js index ff0b706c49..0d107e5ba9 100644 --- a/src/apps/legacy/controllers/movies/moviesrecommended.js +++ b/src/apps/legacy/controllers/movies/moviesrecommended.js @@ -347,7 +347,9 @@ export default function (view, params) { })); } - function onPlaybackStop(e, state) { + function onPlaybackStop(e, stopInfo) { + const state = stopInfo.state; + if (state.NowPlayingItem && state.NowPlayingItem.MediaType == 'Video') { renderedTabs = []; mainTabsManager.getTabsElement().triggerTabChange(); diff --git a/src/apps/legacy/controllers/shows/tvrecommended.js b/src/apps/legacy/controllers/shows/tvrecommended.js index 27cf8789cb..53f272dd7e 100644 --- a/src/apps/legacy/controllers/shows/tvrecommended.js +++ b/src/apps/legacy/controllers/shows/tvrecommended.js @@ -316,7 +316,9 @@ export default function (view, params) { }); } - function onPlaybackStop(e, state) { + function onPlaybackStop(e, stopInfo) { + const state = stopInfo.state; + if (state.NowPlayingItem && state.NowPlayingItem.MediaType == 'Video') { renderedTabs = []; mainTabsManager.getTabsElement().triggerTabChange();