diff --git a/Jellyfin.Plugin.Seasonals/Configuration/configPage.html b/Jellyfin.Plugin.Seasonals/Configuration/configPage.html index 3641951..98e3dc0 100644 --- a/Jellyfin.Plugin.Seasonals/Configuration/configPage.html +++ b/Jellyfin.Plugin.Seasonals/Configuration/configPage.html @@ -582,9 +582,9 @@
-
Enable the Spring theme in general (e.g. for automation).
+
Enables the Spring theme (grass, pollen).
-
- - -
Number of ladybugs.
-
@@ -889,6 +884,7 @@ ' ' + ' ' + ' ' + + ' ' + ' ' + ' ' + ' ' + @@ -1065,9 +1061,7 @@ // Spring document.querySelector('#EnableSpring').checked = config.Spring.EnableSpring; - document.querySelector('#SpringPetalCount').value = config.Spring.PetalCount; document.querySelector('#SpringPollenCount').value = config.Spring.PollenCount; - document.querySelector('#SpringLadybugCount').value = config.Spring.LadybugCount; document.querySelector('#SpringSunbeamCount').value = config.Spring.SunbeamCount; document.querySelector('#EnableRandomSpring').checked = config.Spring.EnableRandomSpring; document.querySelector('#EnableRandomSpringMobile').checked = config.Spring.EnableRandomSpringMobile; @@ -1088,6 +1082,13 @@ document.querySelector('#EnableRandomCarnivalMobile').checked = config.Carnival.EnableRandomCarnivalMobile; document.querySelector('#EnableDifferentDurationCarnival').checked = config.Carnival.EnableDifferentDuration; + // Cherry Blossom + document.querySelector('#EnableCherryBlossom').checked = config.CherryBlossom.EnableCherryBlossom; + document.querySelector('#CherryBlossomPetalCount').value = config.CherryBlossom.PetalCount; + document.querySelector('#EnableRandomCherryBlossom').checked = config.CherryBlossom.EnableRandomCherryBlossom; + document.querySelector('#EnableRandomCherryBlossomMobile').checked = config.CherryBlossom.EnableRandomCherryBlossomMobile; + document.querySelector('#EnableDifferentDurationCherryBlossom').checked = config.CherryBlossom.EnableDifferentDuration; + Dashboard.hideLoadingMsg(); }); }); @@ -1197,9 +1198,7 @@ // Spring config.Spring.EnableSpring = document.querySelector('#EnableSpring').checked; - config.Spring.PetalCount = parseInt(document.querySelector('#SpringPetalCount').value); config.Spring.PollenCount = parseInt(document.querySelector('#SpringPollenCount').value); - config.Spring.LadybugCount = parseInt(document.querySelector('#SpringLadybugCount').value); config.Spring.SunbeamCount = parseInt(document.querySelector('#SpringSunbeamCount').value); config.Spring.EnableRandomSpring = document.querySelector('#EnableRandomSpring').checked; config.Spring.EnableRandomSpringMobile = document.querySelector('#EnableRandomSpringMobile').checked; @@ -1220,6 +1219,13 @@ config.Carnival.EnableRandomCarnivalMobile = document.querySelector('#EnableRandomCarnivalMobile').checked; config.Carnival.EnableDifferentDuration = document.querySelector('#EnableDifferentDurationCarnival').checked; + // Cherry Blossom + config.CherryBlossom.EnableCherryBlossom = document.querySelector('#EnableCherryBlossom').checked; + config.CherryBlossom.PetalCount = parseInt(document.querySelector('#CherryBlossomPetalCount').value); + config.CherryBlossom.EnableRandomCherryBlossom = document.querySelector('#EnableRandomCherryBlossom').checked; + config.CherryBlossom.EnableRandomCherryBlossomMobile = document.querySelector('#EnableRandomCherryBlossomMobile').checked; + config.CherryBlossom.EnableDifferentDuration = document.querySelector('#EnableDifferentDurationCherryBlossom').checked; + ApiClient.updatePluginConfiguration(SeasonalsConfigPage.pluginUniqueId, config).then(function (result) { Dashboard.processPluginConfigurationUpdateResult(result); }); diff --git a/Jellyfin.Plugin.Seasonals/Web/cherryblossom.css b/Jellyfin.Plugin.Seasonals/Web/cherryblossom.css new file mode 100644 index 0000000..1b2fe2f --- /dev/null +++ b/Jellyfin.Plugin.Seasonals/Web/cherryblossom.css @@ -0,0 +1,58 @@ +.cherryblossom-container { + display: block; + position: fixed; + overflow: hidden; + top: 0; + left: 0; + width: 100%; + height: 100%; + pointer-events: none; + z-index: 1000; +} + +/* Petals */ +.cherryblossom-petal { + position: fixed; + top: -20px; + z-index: 1005; + width: 15px; + height: 10px; + background-color: #ffc0cb; + border-radius: 15px 0px 15px 0px; + + will-change: transform, top; + animation-name: cherryblossom-fall, cherryblossom-sway; + animation-timing-function: linear, ease-in-out; + animation-iteration-count: infinite, infinite; + animation-duration: 10s, 3s; +} + +.cherryblossom-petal.lighter { + background-color: #ffd1dc; + opacity: 0.8; +} + +.cherryblossom-petal.darker { + background-color: #ffb7c5; + opacity: 0.9; +} + +.cherryblossom-petal.type2 { + width: 12px; + height: 12px; + border-radius: 10px 0px 10px 5px; +} + +@keyframes cherryblossom-fall { + 0% { top: -10%; } + 100% { top: 100%; } +} + +@keyframes cherryblossom-sway { + 0%, 100% { + transform: translateX(0) rotate(0deg); + } + 50% { + transform: translateX(30px) rotate(45deg); + } +} diff --git a/Jellyfin.Plugin.Seasonals/Web/cherryblossom.js b/Jellyfin.Plugin.Seasonals/Web/cherryblossom.js new file mode 100644 index 0000000..9159589 --- /dev/null +++ b/Jellyfin.Plugin.Seasonals/Web/cherryblossom.js @@ -0,0 +1,101 @@ +const config = window.SeasonalsPluginConfig?.CherryBlossom || {}; + +const cherryBlossom = config.EnableCherryBlossom !== undefined ? config.EnableCherryBlossom : true; +const petalCount = config.PetalCount || 25; +const randomCherryBlossom = config.EnableRandomCherryBlossom !== undefined ? config.EnableRandomCherryBlossom : true; +const randomCherryBlossomMobile = config.EnableRandomCherryBlossomMobile !== undefined ? config.EnableRandomCherryBlossomMobile : false; +const enableDifferentDuration = config.EnableDifferentDuration !== undefined ? config.EnableDifferentDuration : true; + +let msgPrinted = false; + +function toggleCherryBlossom() { + const container = document.querySelector('.cherryblossom-container'); + if (!container) 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'); + + if (videoPlayer || trailerPlayer || isDashboard || hasUserMenu) { + container.style.display = 'none'; + if (!msgPrinted) { + console.log('CherryBlossom hidden'); + msgPrinted = true; + } + } else { + container.style.display = 'block'; + if (msgPrinted) { + console.log('CherryBlossom visible'); + msgPrinted = false; + } + } +} + +const observer = new MutationObserver(toggleCherryBlossom); +observer.observe(document.body, { childList: true, subtree: true, attributes: true }); + +function createPetal(container) { + const petal = document.createElement('div'); + petal.classList.add('cherryblossom-petal'); + + const type = Math.random() > 0.5 ? 'type1' : 'type2'; + petal.classList.add(type); + + const color = Math.random() > 0.7 ? 'darker' : 'lighter'; + petal.classList.add(color); + + const randomLeft = Math.random() * 100; + petal.style.left = `${randomLeft}%`; + + const size = Math.random() * 0.5 + 0.5; + petal.style.transform = `scale(${size})`; + + const duration = Math.random() * 5 + 8; + const delay = Math.random() * 10; + const swayDuration = Math.random() * 2 + 2; + + if (enableDifferentDuration) { + petal.style.animationDuration = `${duration}s, ${swayDuration}s`; + } + petal.style.animationDelay = `${delay}s, ${Math.random() * 3}s`; + + container.appendChild(petal); +} + +function addRandomObjects() { + const container = document.querySelector('.cherryblossom-container'); + if (!container) return; + + for (let i = 0; i < petalCount; i++) { + createPetal(container); + } +} + +function initObjects() { + let container = document.querySelector('.cherryblossom-container'); + if (!container) { + container = document.createElement("div"); + container.className = "cherryblossom-container"; + container.setAttribute("aria-hidden", "true"); + document.body.appendChild(container); + } + + // Initial batch + for (let i = 0; i < 15; i++) { + createPetal(container); + } +} + +function initializeCherryBlossom() { + if (!cherryBlossom) return; + initObjects(); + toggleCherryBlossom(); + + const screenWidth = window.innerWidth; + if (randomCherryBlossom && (screenWidth > 768 || randomCherryBlossomMobile)) { + addRandomObjects(); + } +} + +initializeCherryBlossom();