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).
-
@@ -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();