From 22d40fb248c440c4cde61bf7662f66e6ed7e2f06 Mon Sep 17 00:00:00 2001 From: CodeDevMLH <145071728+CodeDevMLH@users.noreply.github.com> Date: Tue, 24 Feb 2026 19:22:57 +0100 Subject: [PATCH] Add Oktoberfest feature: implement CSS and JS for Oktoberfest animations and visibility control --- Jellyfin.Plugin.Seasonals/Web/oktoberfest.css | 33 +++++++++ Jellyfin.Plugin.Seasonals/Web/oktoberfest.js | 67 +++++++++++++++++++ 2 files changed, 100 insertions(+) create mode 100644 Jellyfin.Plugin.Seasonals/Web/oktoberfest.css create mode 100644 Jellyfin.Plugin.Seasonals/Web/oktoberfest.js diff --git a/Jellyfin.Plugin.Seasonals/Web/oktoberfest.css b/Jellyfin.Plugin.Seasonals/Web/oktoberfest.css new file mode 100644 index 0000000..3e7da8a --- /dev/null +++ b/Jellyfin.Plugin.Seasonals/Web/oktoberfest.css @@ -0,0 +1,33 @@ +.oktoberfest-container { + display: block; + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; + pointer-events: none; + z-index: 10; + contain: strict; + overflow: hidden; +} + +.oktoberfest-symbol { + position: absolute; + top: -10%; + font-size: 2.2em; + user-select: none; + animation-name: oktoberfest-fall, oktoberfest-sway; + animation-timing-function: linear, ease-in-out; + animation-iteration-count: infinite, infinite; +} + +@keyframes oktoberfest-fall { + 0% { transform: translateY(0); opacity: 0; } + 10% { opacity: 1; } + 100% { transform: translateY(120vh); opacity: 1; } +} + +@keyframes oktoberfest-sway { + 0%, 100% { margin-left: 0; } + 50% { margin-left: 50px; } +} diff --git a/Jellyfin.Plugin.Seasonals/Web/oktoberfest.js b/Jellyfin.Plugin.Seasonals/Web/oktoberfest.js new file mode 100644 index 0000000..99ecb63 --- /dev/null +++ b/Jellyfin.Plugin.Seasonals/Web/oktoberfest.js @@ -0,0 +1,67 @@ +const config = window.SeasonalsPluginConfig?.Oktoberfest || {}; +const oktoberfest = config.EnableOktoberfest !== undefined ? config.EnableOktoberfest : true; + +const oktoberfestSymbols = ['🥨', '🍺', '🍻', '🥨', '🥨']; + +let msgPrinted = false; + +// function to check and control the oktoberfest +function toggleOktoberfest() { + const oktoberfestContainer = document.querySelector('.oktoberfest-container'); + if (!oktoberfestContainer) return; + + const videoPlayer = document.querySelector('.videoPlayerContainer'); + const trailerPlayer = document.querySelector('.youtubePlayerContainer'); + const isDashboard = document.body.classList.contains('dashboardDocument'); + const hasUserMenu = document.querySelector('#app-user-menu'); + + // hide oktoberfest if video/trailer player is active or dashboard is visible + if (videoPlayer || trailerPlayer || isDashboard || hasUserMenu) { + oktoberfestContainer.style.display = 'none'; // hide oktoberfest + if (!msgPrinted) { + console.log('Oktoberfest hidden'); + msgPrinted = true; + } + } else { + oktoberfestContainer.style.display = 'block'; // show oktoberfest + if (msgPrinted) { + console.log('Oktoberfest visible'); + msgPrinted = false; + } + } +} + +// observe changes in the DOM +const observer = new MutationObserver(toggleOktoberfest); +observer.observe(document.body, { + childList: true, + subtree: true, + attributes: true +}); + +function createOktoberfest(container) { + for (let i = 0; i < 20; i++) { + const symbol = document.createElement('div'); + symbol.className = 'oktoberfest-symbol'; + symbol.textContent = oktoberfestSymbols[Math.floor(Math.random() * oktoberfestSymbols.length)]; + symbol.style.left = `${Math.random() * 100}%`; + symbol.style.animationDelay = `${Math.random() * 10}s, ${Math.random() * 5}s`; + const duration1 = Math.random() * 5 + 8; + const duration2 = Math.random() * 3 + 3; + symbol.style.animationDuration = `${duration1}s, ${duration2}s`; + + container.appendChild(symbol); + } +} + +function initializeOktoberfest() { + if (!oktoberfest) return; + const container = document.querySelector('.oktoberfest-container') || document.createElement("div"); + if (!document.querySelector('.oktoberfest-container')) { + container.className = "oktoberfest-container"; + container.setAttribute("aria-hidden", "true"); + document.body.appendChild(container); + } + createOktoberfest(container); +} +initializeOktoberfest();