diff --git a/Jellyfin.Plugin.MediaBarEnhanced/Jellyfin.Plugin.MediaBarEnhanced.csproj b/Jellyfin.Plugin.MediaBarEnhanced/Jellyfin.Plugin.MediaBarEnhanced.csproj index 8ff4a42..6aa07ee 100644 --- a/Jellyfin.Plugin.MediaBarEnhanced/Jellyfin.Plugin.MediaBarEnhanced.csproj +++ b/Jellyfin.Plugin.MediaBarEnhanced/Jellyfin.Plugin.MediaBarEnhanced.csproj @@ -12,7 +12,7 @@ Jellyfin Media Bar Enhanced Plugin CodeDevMLH - 1.4.0.12 + 1.5.0.0 https://github.com/CodeDevMLH/jellyfin-plugin-media-bar-enhanced diff --git a/Jellyfin.Plugin.MediaBarEnhanced/Web/mediaBarEnhanced.js b/Jellyfin.Plugin.MediaBarEnhanced/Web/mediaBarEnhanced.js index b3f8410..310aa30 100644 --- a/Jellyfin.Plugin.MediaBarEnhanced/Web/mediaBarEnhanced.js +++ b/Jellyfin.Plugin.MediaBarEnhanced/Web/mediaBarEnhanced.js @@ -1,5 +1,5 @@ /* - * Jellyfin Slideshow by M0RPH3US v3.0.9 + * Jellyfin Slideshow by M0RPH3US v4.0.1 * Modified by CodeDevMLH v1.1.0.0 * * New features: @@ -81,6 +81,7 @@ const STATE = { sponsorBlockInterval: null, isMuted: CONFIG.startMuted, customTrailerUrls: {}, + ytPromise: null, }, }; @@ -582,23 +583,25 @@ const SlideUtils = { * @returns {Promise} */ loadYouTubeIframeAPI() { - return new Promise((resolve) => { + if (STATE.slideshow.ytPromise) return STATE.slideshow.ytPromise; + + STATE.slideshow.ytPromise = new Promise((resolve) => { if (window.YT && window.YT.Player) { - resolve(); + resolve(window.YT); return; } - const tag = document.createElement('script'); - tag.src = "https://www.youtube.com/iframe_api"; - const firstScriptTag = document.getElementsByTagName('script')[0]; - firstScriptTag.parentNode.insertBefore(tag, firstScriptTag); - - const previousOnYouTubeIframeAPIReady = window.onYouTubeIframeAPIReady; - window.onYouTubeIframeAPIReady = () => { - if (previousOnYouTubeIframeAPIReady) previousOnYouTubeIframeAPIReady(); - resolve(); - }; + window.onYouTubeIframeAPIReady = () => resolve(window.YT); + + if (!document.querySelector('script[src*="youtube.com/iframe_api"]')) { + const tag = document.createElement('script'); + tag.src = "https://www.youtube.com/iframe_api"; + const firstScriptTag = document.getElementsByTagName('script')[0]; + firstScriptTag.parentNode.insertBefore(tag, firstScriptTag); + } }); + + return STATE.slideshow.ytPromise; }, /** @@ -3109,6 +3112,84 @@ const MediaBarEnhancedSettingsManager = { } }; +/** + * Initialize page visibility handling to pause when tab is inactive + */ +const initPageVisibilityHandler = () => { + let wasVideoPlayingBeforeHide = false; + + document.addEventListener("visibilitychange", () => { + if (document.hidden) { + console.log("Tab inactive - pausing slideshow and videos"); + wasVideoPlayingBeforeHide = STATE.slideshow.isVideoPlaying; + + if (STATE.slideshow.slideInterval) { + STATE.slideshow.slideInterval.stop(); + } + + // Pause active video if playing + const currentItemId = STATE.slideshow.itemIds[STATE.slideshow.currentSlideIndex]; + if (currentItemId) { + // YouTube + if (STATE.slideshow.videoPlayers && STATE.slideshow.videoPlayers[currentItemId]) { + const player = STATE.slideshow.videoPlayers[currentItemId]; + if (typeof player.pauseVideo === "function") { + try { + player.pauseVideo(); + STATE.slideshow.isVideoPlaying = false; + } catch (e) { + console.warn("Error pausing video on tab hide:", e); + } + } else if (player.tagName === 'VIDEO') { // HTML5 Video + player.pause(); + STATE.slideshow.isVideoPlaying = false; + } + } + } + } else { + console.log("Tab active - resuming slideshow"); + if (!STATE.slideshow.isPaused) { + const currentItemId = STATE.slideshow.itemIds[STATE.slideshow.currentSlideIndex]; + + if (wasVideoPlayingBeforeHide && currentItemId && STATE.slideshow.videoPlayers && STATE.slideshow.videoPlayers[currentItemId]) { + const player = STATE.slideshow.videoPlayers[currentItemId]; + + // YouTube + if (typeof player.playVideo === "function") { + try { + player.playVideo(); + STATE.slideshow.isVideoPlaying = true; + } catch (e) { + console.warn("Error resuming video on tab show:", e); + if (STATE.slideshow.slideInterval) { + STATE.slideshow.slideInterval.start(); + } + } + } else if (player.tagName === 'VIDEO') { // HTML5 Video + try { + player.play().catch(e => console.warn("Error resuming HTML5 video:", e)); + STATE.slideshow.isVideoPlaying = true; + } catch(e) { console.warn(e); } + } + } else { + // No video was playing, just restart interval + const activeSlide = document.querySelector('.slide.active'); + const hasVideo = activeSlide && activeSlide.querySelector('.video-backdrop'); + + if (CONFIG.waitForTrailerToEnd && hasVideo) { + // Don't restart interval if waiting for trailer + } else { + if (STATE.slideshow.slideInterval) { + STATE.slideshow.slideInterval.start(); + } + } + } + wasVideoPlayingBeforeHide = false; + } + } + }); +}; + /** * Initialize the slideshow */ @@ -3222,6 +3303,8 @@ const slidesInit = async () => { SlideshowManager.initKeyboardEvents(); + initPageVisibilityHandler(); + VisibilityObserver.init(); console.log("✅ Enhanced Jellyfin Slideshow initialized successfully"); diff --git a/manifest.json b/manifest.json index 44dc1aa..a288372 100644 --- a/manifest.json +++ b/manifest.json @@ -9,10 +9,10 @@ "imageUrl": "https://git.mahom03-spacecloud.de/CodeDevMLH/jellyfin-plugin-media-bar-enhanced/raw/branch/main/logo.png", "versions": [ { - "version": "1.4.0.12", - "changelog": "- feat: Add client-side settings feature for selected media bar settings", + "version": "1.5.0.0", + "changelog": "- Update mediaBarEnhanced.js and mediaBarEnhanced.css with version 3.0.9 from original repo", "targetAbi": "10.11.0.0", - "sourceUrl": "https://git.mahom03-spacecloud.de/CodeDevMLH/jellyfin-plugin-media-bar-enhanced/releases/download/v1.4.0.12/Jellyfin.Plugin.MediaBarEnhanced.zip", + "sourceUrl": "https://git.mahom03-spacecloud.de/CodeDevMLH/jellyfin-plugin-media-bar-enhanced/releases/download/v1.5.0.0/Jellyfin.Plugin.MediaBarEnhanced.zip", "checksum": "26edee51b52dcee4ecf388aa376f3869", "timestamp": "2026-02-04T18:07:40Z" },