Compare commits
5 Commits
c66ccf970e
...
c5093073d0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c5093073d0 | ||
|
|
85cabf29bb | ||
|
|
b008221cf4 | ||
|
|
2bbf13c044 | ||
|
|
082120b70b |
@@ -46,7 +46,7 @@ public class PluginConfiguration : BasePluginConfiguration
|
||||
Oktoberfest = new OktoberfestOptions();
|
||||
Friday13 = new Friday13Options();
|
||||
Eid = new EidOptions();
|
||||
LegacyHalloween = new LegacyHalloweenOptions();
|
||||
Spooky = new SpookyOptions();
|
||||
Sports = new SportsOptions();
|
||||
Olympia = new OlympiaOptions();
|
||||
Space = new SpaceOptions();
|
||||
@@ -111,7 +111,7 @@ public class PluginConfiguration : BasePluginConfiguration
|
||||
public OktoberfestOptions Oktoberfest { get; set; }
|
||||
public Friday13Options Friday13 { get; set; }
|
||||
public EidOptions Eid { get; set; }
|
||||
public LegacyHalloweenOptions LegacyHalloween { get; set; }
|
||||
public SpookyOptions Spooky { get; set; }
|
||||
public SportsOptions Sports { get; set; }
|
||||
public OlympiaOptions Olympia { get; set; }
|
||||
public SpaceOptions Space { get; set; }
|
||||
@@ -370,10 +370,13 @@ public class EidOptions
|
||||
public bool EnableEid { get; set; } = true;
|
||||
}
|
||||
|
||||
public class LegacyHalloweenOptions
|
||||
public class SpookyOptions
|
||||
{
|
||||
public bool EnableLegacyHalloween { get; set; } = true;
|
||||
public bool EnableSpooky { get; set; } = true;
|
||||
public int SymbolCount { get; set; } = 25;
|
||||
public bool EnableSpookySway { get; set; } = true;
|
||||
public int SpookySize { get; set; } = 20;
|
||||
public int SpookyGlowSize { get; set; } = 2;
|
||||
}
|
||||
|
||||
public class SportsOptions
|
||||
|
||||
@@ -87,7 +87,7 @@
|
||||
<option value="oktoberfest">Oktoberfest</option>
|
||||
<option value="friday13">Friday the 13th</option>
|
||||
<option value="eid">Eid al-Fitr</option>
|
||||
<option value="legacyhalloween">Legacy Halloween</option>
|
||||
<option value="spooky">Spooky</option>
|
||||
</select>
|
||||
<div class="fieldDescription">The season to display if automation is disabled or no "Auto Selection" rule matches the current date.</div>
|
||||
</div>
|
||||
@@ -371,19 +371,35 @@
|
||||
<hr style="max-width: 800px; margin: 1em 0;">
|
||||
|
||||
<details>
|
||||
<summary>Legacy Halloween</summary>
|
||||
<summary>Spooky Theme</summary>
|
||||
<div class="checkboxContainer checkboxContainer-withDescription">
|
||||
<label class="emby-checkbox-label">
|
||||
<input id="EnableLegacyHalloween" name="EnableLegacyHalloween" type="checkbox" is="emby-checkbox" />
|
||||
<span>Enable Legacy Halloween Seasonal</span>
|
||||
<input id="EnableSpooky" name="EnableSpooky" type="checkbox" is="emby-checkbox" />
|
||||
<span>Enable Spooky Seasonal</span>
|
||||
</label>
|
||||
<div class="fieldDescription">Enable the classic Halloween theme (only floating symbols, no fog/spiders).</div>
|
||||
<div class="fieldDescription">Enable the Spooky Halloween theme (floating, swaying symbols).</div>
|
||||
</div>
|
||||
<div class="inputContainer">
|
||||
<label class="inputLabel" for="LegacyHalloweenCount">Symbol Count</label>
|
||||
<input is="emby-input" type="number" id="LegacyHalloweenCount" name="LegacyHalloweenCount" />
|
||||
<label class="inputLabel" for="SpookyCount">Symbol Count</label>
|
||||
<input is="emby-input" type="number" id="SpookyCount" name="SpookyCount" />
|
||||
<div class="fieldDescription">Number of floating symbols.</div>
|
||||
</div>
|
||||
<div class="inputContainer">
|
||||
<label class="inputLabel" for="SpookySize">Symbol Size</label>
|
||||
<input is="emby-input" type="number" id="SpookySize" name="SpookySize" />
|
||||
<div class="fieldDescription">Size of the floating symbols in pixels (default 30).</div>
|
||||
</div>
|
||||
<div class="checkboxContainer">
|
||||
<label class="emby-checkbox-label">
|
||||
<input id="EnableSpookySway" name="EnableSpookySway" type="checkbox" is="emby-checkbox" />
|
||||
<span>Enable Swaying Motion</span>
|
||||
</label>
|
||||
</div>
|
||||
<div class="inputContainer">
|
||||
<label class="inputLabel" for="SpookyGlowSize">Glow Size</label>
|
||||
<input is="emby-input" type="number" id="SpookyGlowSize" name="SpookyGlowSize" />
|
||||
<div class="fieldDescription">Size of the glow effect in pixels (0 to disable, default 5).</div>
|
||||
</div>
|
||||
</details>
|
||||
<hr style="max-width: 800px; margin: 1em 0;">
|
||||
|
||||
@@ -1623,8 +1639,8 @@
|
||||
// Eid
|
||||
document.querySelector('#EidSymbolCount').value = config.Eid.SymbolCount || 25;
|
||||
|
||||
// Legacy Halloween
|
||||
document.querySelector('#LegacyHalloweenSymbolCount').value = config.LegacyHalloween.SymbolCount || 25;
|
||||
// Spooky Theme
|
||||
document.querySelector('#SpookyCount').value = config.Spooky.SymbolCount || 25;
|
||||
|
||||
// Sports
|
||||
document.querySelector('#EnableSports').checked = config.Sports.EnableSports || false;
|
||||
@@ -1779,9 +1795,12 @@
|
||||
document.querySelector('#PrideHeartSize').value = config.Pride.HeartSize;
|
||||
document.querySelector('#PrideColorHeader').checked = config.Pride.ColorHeader;
|
||||
|
||||
// Legacy Halloween
|
||||
document.querySelector('#EnableLegacyHalloween').checked = config.LegacyHalloween.EnableLegacyHalloween !== undefined ? config.LegacyHalloween.EnableLegacyHalloween : true;
|
||||
document.querySelector('#LegacyHalloweenCount').value = config.LegacyHalloween.SymbolCount !== undefined ? config.LegacyHalloween.SymbolCount : 25;
|
||||
// Spooky Theme
|
||||
document.querySelector('#EnableSpooky').checked = config.Spooky.EnableSpooky !== undefined ? config.Spooky.EnableSpooky : true;
|
||||
document.querySelector('#SpookyCount').value = config.Spooky.SymbolCount !== undefined ? config.Spooky.SymbolCount : 25;
|
||||
document.querySelector('#SpookySize').value = config.Spooky.SpookySize !== undefined ? config.Spooky.SpookySize : 30;
|
||||
document.querySelector('#EnableSpookySway').checked = config.Spooky.EnableSpookySway !== undefined ? config.Spooky.EnableSpookySway : true;
|
||||
document.querySelector('#SpookyGlowSize').value = config.Spooky.SpookyGlowSize !== undefined ? config.Spooky.SpookyGlowSize : 5;
|
||||
|
||||
// Rain
|
||||
document.querySelector('#EnableRain').checked = config.Rain.EnableRain;
|
||||
@@ -1965,9 +1984,12 @@
|
||||
config.Resurrection.EnableRandomSymbolsMobile = document.querySelector('#EnableRandomResurrectionMobile').checked;
|
||||
config.Resurrection.EnableDifferentDuration = document.querySelector('#EnableDifferentDurationResurrection').checked;
|
||||
|
||||
// Legacy Halloween
|
||||
config.LegacyHalloween.EnableLegacyHalloween = document.querySelector('#EnableLegacyHalloween').checked;
|
||||
config.LegacyHalloween.SymbolCount = parseInt(document.querySelector('#LegacyHalloweenCount').value);
|
||||
// Spooky Theme
|
||||
config.Spooky.EnableSpooky = document.querySelector('#EnableSpooky').checked;
|
||||
config.Spooky.SymbolCount = parseInt(document.querySelector('#SpookyCount').value);
|
||||
config.Spooky.SpookySize = parseInt(document.querySelector('#SpookySize').value);
|
||||
config.Spooky.EnableSpookySway = document.querySelector('#EnableSpookySway').checked;
|
||||
config.Spooky.SpookyGlowSize = parseInt(document.querySelector('#SpookyGlowSize').value);
|
||||
|
||||
// Spring
|
||||
config.Spring.EnableSpring = document.querySelector('#EnableSpring').checked;
|
||||
|
||||
@@ -14,7 +14,9 @@
|
||||
.leaf {
|
||||
position: fixed;
|
||||
z-index: 15;
|
||||
top: -10%;
|
||||
top: 0;
|
||||
will-change: transform;
|
||||
translate: 0 -10vh;
|
||||
font-size: 1em;
|
||||
color: #fff;
|
||||
font-family: Arial, sans-serif;
|
||||
@@ -41,30 +43,30 @@
|
||||
|
||||
@-webkit-keyframes leaf-fall {
|
||||
0% {
|
||||
top: -10%;
|
||||
translate: 0 -10vh;
|
||||
}
|
||||
|
||||
100% {
|
||||
top: 110%;
|
||||
translate: 0 100vh;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes leaf-fall {
|
||||
0% {
|
||||
top: -10%;
|
||||
translate: 0 -10vh;
|
||||
}
|
||||
|
||||
100% {
|
||||
top: 110%;
|
||||
translate: 0 100vh;
|
||||
}
|
||||
}
|
||||
|
||||
@-webkit-keyframes leaf-shake {
|
||||
0%, 100% {
|
||||
-webkit-transform: translateX(0) rotate(var(--rotate-start, -20deg));
|
||||
transform: translateX(0) rotate(var(--rotate-start, -20deg));
|
||||
}
|
||||
50% {
|
||||
-webkit-transform: translateX(80px) rotate(var(--rotate-end, 20deg));
|
||||
transform: translateX(80px) rotate(var(--rotate-end, 20deg));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
.carnival-wrapper {
|
||||
position: fixed;
|
||||
z-index: 15;
|
||||
top: -20px;
|
||||
top: 0;
|
||||
will-change: transform;
|
||||
animation-name: carnival-fall;
|
||||
animation-timing-function: linear;
|
||||
@@ -34,7 +34,7 @@
|
||||
.carnival-confetti {
|
||||
width: 8px;
|
||||
height: 16px;
|
||||
background-color: #f0f;
|
||||
background-color: rgb(0, 0, 0);
|
||||
will-change: transform;
|
||||
animation-name: carnival-flutter;
|
||||
animation-timing-function: linear;
|
||||
@@ -60,10 +60,10 @@
|
||||
|
||||
@keyframes carnival-fall {
|
||||
0% {
|
||||
transform: translateY(0);
|
||||
transform: translate3d(0, -10vh, 0);
|
||||
}
|
||||
100% {
|
||||
transform: translateY(120vh);
|
||||
transform: translate3d(0, 110vh, 0);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -14,14 +14,15 @@
|
||||
/* Petals */
|
||||
.cherryblossom-petal {
|
||||
position: fixed;
|
||||
top: -20px;
|
||||
top: 0;
|
||||
z-index: 1005;
|
||||
width: 15px;
|
||||
height: 10px;
|
||||
background-color: #ffc0cb;
|
||||
border-radius: 15px 0px 15px 0px;
|
||||
|
||||
will-change: transform, top;
|
||||
will-change: transform;
|
||||
translate: 0 -10vh;
|
||||
animation-name: cherryblossom-fall, cherryblossom-sway;
|
||||
animation-timing-function: linear, ease-in-out;
|
||||
animation-iteration-count: infinite, infinite;
|
||||
@@ -45,8 +46,8 @@
|
||||
}
|
||||
|
||||
@keyframes cherryblossom-fall {
|
||||
0% { top: -10%; }
|
||||
100% { top: 110%; }
|
||||
0% { translate: 0 -10vh; }
|
||||
100% { translate: 0 110vh; }
|
||||
}
|
||||
|
||||
@keyframes cherryblossom-sway {
|
||||
|
||||
@@ -14,7 +14,9 @@
|
||||
.christmas {
|
||||
position: fixed;
|
||||
z-index: 15;
|
||||
top: -10%;
|
||||
top: 0;
|
||||
will-change: transform;
|
||||
translate: 0 -10vh;
|
||||
font-size: 1em;
|
||||
color: #fff;
|
||||
font-family: Arial, sans-serif;
|
||||
@@ -34,11 +36,11 @@
|
||||
|
||||
@-webkit-keyframes christmas-fall {
|
||||
0% {
|
||||
top: -10%;
|
||||
translate: 0 -10vh;
|
||||
}
|
||||
|
||||
100% {
|
||||
top: 110%;
|
||||
translate: 0 110vh;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -46,28 +48,25 @@
|
||||
|
||||
0%,
|
||||
100% {
|
||||
-webkit-transform: translateX(0);
|
||||
transform: translateX(0);
|
||||
}
|
||||
|
||||
50% {
|
||||
-webkit-transform: translateX(80px);
|
||||
transform: translateX(80px);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes christmas-fall {
|
||||
0% {
|
||||
top: -10%;
|
||||
translate: 0 -10vh;
|
||||
}
|
||||
|
||||
100% {
|
||||
top: 110%;
|
||||
translate: 0 110vh;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes christmas-shake {
|
||||
|
||||
0%,
|
||||
100% {
|
||||
transform: translateX(0);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
.piday-container {
|
||||
.matrix-container {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
@@ -1,15 +1,16 @@
|
||||
const config = window.SeasonalsPluginConfig?.PiDay || {};
|
||||
const config = window.SeasonalsPluginConfig?.Matrix || {};
|
||||
|
||||
const enabled = config.EnablePiDay !== undefined ? config.EnablePiDay : true;
|
||||
const enabled = config.EnableMatrix !== undefined ? config.EnableMatrix : true;
|
||||
const maxTrails = config.SymbolCount || 25;
|
||||
const backgroundMode = config.EnablePiDayBackground !== undefined ? config.EnablePiDayBackground : false;
|
||||
const backgroundMode = config.EnableMatrixBackground !== undefined ? config.EnableMatrixBackground : false;
|
||||
const matrixChars = config.MatrixChars || '0123456789';
|
||||
|
||||
let msgPrinted = false;
|
||||
let isHidden = false;
|
||||
|
||||
// Toggle Function
|
||||
function togglePiDay() {
|
||||
const container = document.querySelector('.piday-container');
|
||||
function toggleMatrix() {
|
||||
const container = document.querySelector('.matrix-container');
|
||||
if (!container) return;
|
||||
|
||||
const videoPlayer = document.querySelector('.videoPlayerContainer');
|
||||
@@ -22,7 +23,7 @@ function togglePiDay() {
|
||||
container.style.display = 'none';
|
||||
isHidden = true;
|
||||
if (!msgPrinted) {
|
||||
console.log('PiDay hidden');
|
||||
console.log('Matrix hidden');
|
||||
msgPrinted = true;
|
||||
}
|
||||
}
|
||||
@@ -31,14 +32,14 @@ function togglePiDay() {
|
||||
container.style.display = 'block';
|
||||
isHidden = false;
|
||||
if (msgPrinted) {
|
||||
console.log('PiDay visible');
|
||||
console.log('Matrix visible');
|
||||
msgPrinted = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const observer = new MutationObserver(togglePiDay);
|
||||
const observer = new MutationObserver(toggleMatrix);
|
||||
observer.observe(document.body, {
|
||||
childList: true,
|
||||
subtree: true,
|
||||
@@ -46,10 +47,10 @@ observer.observe(document.body, {
|
||||
});
|
||||
|
||||
function createElements() {
|
||||
const container = document.querySelector('.piday-container') || document.createElement('div');
|
||||
const container = document.querySelector('.matrix-container') || document.createElement('div');
|
||||
|
||||
if (!document.querySelector('.piday-container')) {
|
||||
container.className = 'piday-container';
|
||||
if (!document.querySelector('.matrix-container')) {
|
||||
container.className = 'matrix-container';
|
||||
container.setAttribute('aria-hidden', 'true');
|
||||
if (backgroundMode) container.style.zIndex = '5';
|
||||
document.body.appendChild(container);
|
||||
@@ -63,7 +64,7 @@ function createElements() {
|
||||
|
||||
const ctx = canvas.getContext('2d');
|
||||
// const chars = '0123456789πABCDEFGHIJKLMNOPQRSTUVWXYZ'.split('');
|
||||
const chars = '0123456789'.split('');
|
||||
const chars = matrixChars.split('');
|
||||
const fontSize = 18;
|
||||
|
||||
class Trail {
|
||||
@@ -145,8 +146,8 @@ function createElements() {
|
||||
}
|
||||
}
|
||||
|
||||
if (window.pidayInterval) clearInterval(window.pidayInterval);
|
||||
window.pidayInterval = setInterval(loop, 50);
|
||||
if (window.matrixInterval) clearInterval(window.matrixInterval);
|
||||
window.matrixInterval = setInterval(loop, 50);
|
||||
|
||||
window.addEventListener('resize', () => {
|
||||
canvas.width = window.innerWidth;
|
||||
@@ -154,10 +155,10 @@ function createElements() {
|
||||
});
|
||||
}
|
||||
|
||||
function initializePiDay() {
|
||||
function initializeMatrix() {
|
||||
if (!enabled) return;
|
||||
createElements();
|
||||
togglePiDay();
|
||||
toggleMatrix();
|
||||
}
|
||||
|
||||
initializePiDay();
|
||||
initializeMatrix();
|
||||
@@ -14,14 +14,22 @@
|
||||
.resurrection-symbol {
|
||||
position: fixed;
|
||||
z-index: 15;
|
||||
top: -15%;
|
||||
top: 0;
|
||||
translate: 0 -15vh;
|
||||
user-select: none;
|
||||
-webkit-user-select: none;
|
||||
cursor: default;
|
||||
animation-name: resurrection-fall, resurrection-sway;
|
||||
animation-timing-function: linear, ease-in-out;
|
||||
animation-iteration-count: infinite, infinite;
|
||||
will-change: transform, top;
|
||||
animation-name: resurrection-fall;
|
||||
animation-timing-function: linear;
|
||||
animation-iteration-count: infinite;
|
||||
will-change: transform;
|
||||
}
|
||||
|
||||
.resurrection-sway-wrapper {
|
||||
will-change: transform;
|
||||
animation-name: resurrection-sway;
|
||||
animation-timing-function: ease-in-out;
|
||||
animation-iteration-count: infinite;
|
||||
}
|
||||
|
||||
.resurrection-symbol img {
|
||||
@@ -40,11 +48,11 @@
|
||||
|
||||
@keyframes resurrection-fall {
|
||||
0% {
|
||||
top: -15%;
|
||||
transform: translate3d(0, -15vh, 0);
|
||||
}
|
||||
|
||||
100% {
|
||||
top: 110%;
|
||||
transform: translate3d(0, 105vh, 0);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -52,20 +52,27 @@ function createSymbol(imageSrc, leftPercent, delaySeconds) {
|
||||
const symbol = document.createElement('div');
|
||||
symbol.className = 'resurrection-symbol';
|
||||
|
||||
const swayWrapper = document.createElement('div');
|
||||
swayWrapper.className = 'resurrection-sway-wrapper';
|
||||
|
||||
const img = document.createElement('img');
|
||||
img.src = imageSrc;
|
||||
img.alt = '';
|
||||
|
||||
symbol.style.left = `${leftPercent}%`;
|
||||
symbol.style.animationDelay = `${delaySeconds}s, -${Math.random() * 3}s`;
|
||||
symbol.style.animationDelay = `${delaySeconds}s`;
|
||||
|
||||
if (enableDifferentDuration) {
|
||||
const fallDuration = Math.random() * 7 + 7;
|
||||
const swayDuration = Math.random() * 4 + 2;
|
||||
symbol.style.animationDuration = `${fallDuration}s, ${swayDuration}s`;
|
||||
symbol.style.animationDuration = `${fallDuration}s`;
|
||||
swayWrapper.style.animationDuration = `${swayDuration}s`;
|
||||
}
|
||||
|
||||
symbol.appendChild(img);
|
||||
swayWrapper.style.animationDelay = `${Math.random() * 3}s`;
|
||||
|
||||
swayWrapper.appendChild(img);
|
||||
symbol.appendChild(swayWrapper);
|
||||
return symbol;
|
||||
}
|
||||
|
||||
|
||||
@@ -78,10 +78,10 @@ const ThemeConfigs = {
|
||||
js: '../Seasonals/Resources/cherryblossom.js',
|
||||
containerClass: 'cherryblossom-container'
|
||||
},
|
||||
piday: {
|
||||
css: '../Seasonals/Resources/piday.css',
|
||||
js: '../Seasonals/Resources/piday.js',
|
||||
containerClass: 'piday-container'
|
||||
matrix: {
|
||||
css: '../Seasonals/Resources/matrix.css',
|
||||
js: '../Seasonals/Resources/matrix.js',
|
||||
containerClass: 'matrix-container'
|
||||
},
|
||||
eurovision: {
|
||||
css: '../Seasonals/Resources/eurovision.css',
|
||||
@@ -148,10 +148,10 @@ const ThemeConfigs = {
|
||||
js: '../Seasonals/Resources/eid.js',
|
||||
containerClass: 'eid-container'
|
||||
},
|
||||
legacyhalloween: {
|
||||
css: '../Seasonals/Resources/legacyhalloween.css',
|
||||
js: '../Seasonals/Resources/legacyhalloween.js',
|
||||
containerClass: 'legacyhalloween-container'
|
||||
spooky: {
|
||||
css: '../Seasonals/Resources/spooky.css',
|
||||
js: '../Seasonals/Resources/spooky.js',
|
||||
containerClass: 'spooky-container'
|
||||
},
|
||||
sports: {
|
||||
css: '../Seasonals/Resources/sports.css',
|
||||
|
||||
@@ -14,7 +14,9 @@
|
||||
.snowflake {
|
||||
position: fixed;
|
||||
z-index: 15;
|
||||
top: -10%;
|
||||
top: 0;
|
||||
will-change: transform;
|
||||
translate: 0 -10vh;
|
||||
font-size: 1em;
|
||||
color: #fff;
|
||||
font-family: Arial, sans-serif;
|
||||
@@ -34,11 +36,11 @@
|
||||
|
||||
@-webkit-keyframes snowflakes-fall {
|
||||
0% {
|
||||
top: -10%;
|
||||
translate: 0 -10vh;
|
||||
}
|
||||
|
||||
100% {
|
||||
top: 110%;
|
||||
translate: 0 110vh;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -46,23 +48,21 @@
|
||||
|
||||
0%,
|
||||
100% {
|
||||
-webkit-transform: translateX(0);
|
||||
transform: translateX(0);
|
||||
}
|
||||
|
||||
50% {
|
||||
-webkit-transform: translateX(80px);
|
||||
transform: translateX(80px);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes snowflakes-fall {
|
||||
0% {
|
||||
top: -10%;
|
||||
translate: 0 -10vh;
|
||||
}
|
||||
|
||||
100% {
|
||||
top: 110%;
|
||||
translate: 0 110vh;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
126
Jellyfin.Plugin.Seasonals/Web/spooky.css
Normal file
126
Jellyfin.Plugin.Seasonals/Web/spooky.css
Normal file
@@ -0,0 +1,126 @@
|
||||
.spooky-container {
|
||||
display: block;
|
||||
position: fixed;
|
||||
overflow: hidden;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
pointer-events: none;
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
.spooky {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
will-change: transform;
|
||||
translate: 0 120vh;
|
||||
z-index: 15;
|
||||
-webkit-user-select: none;
|
||||
-moz-user-select: none;
|
||||
-ms-user-select: none;
|
||||
user-select: none;
|
||||
-webkit-user-select: none;
|
||||
cursor: default;
|
||||
|
||||
-webkit-animation-name: spooky-float;
|
||||
-webkit-animation-duration: 10s;
|
||||
-webkit-animation-timing-function: linear;
|
||||
-webkit-animation-iteration-count: infinite;
|
||||
-webkit-animation-play-state: running;
|
||||
|
||||
animation-name: spooky-float;
|
||||
animation-duration: 10s;
|
||||
animation-timing-function: linear;
|
||||
animation-iteration-count: infinite;
|
||||
animation-play-state: running;
|
||||
}
|
||||
|
||||
.spooky-inner {
|
||||
width: 30px;
|
||||
height: auto;
|
||||
will-change: transform;
|
||||
|
||||
-webkit-animation-name: spooky-shake;
|
||||
-webkit-animation-duration: 3s;
|
||||
-webkit-animation-timing-function: ease-in-out;
|
||||
-webkit-animation-iteration-count: infinite;
|
||||
-webkit-animation-play-state: running;
|
||||
|
||||
animation-name: spooky-shake;
|
||||
animation-duration: 3s;
|
||||
animation-timing-function: ease-in-out;
|
||||
animation-iteration-count: infinite;
|
||||
animation-play-state: running;
|
||||
}
|
||||
|
||||
.spooky-inner img {
|
||||
height: auto;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
@-webkit-keyframes spooky-float {
|
||||
0% {
|
||||
translate: 0 120vh;
|
||||
opacity: 0;
|
||||
}
|
||||
10% {
|
||||
opacity: 0.8;
|
||||
}
|
||||
90% {
|
||||
opacity: 0.8;
|
||||
}
|
||||
100% {
|
||||
translate: 0 -150px;
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes spooky-float {
|
||||
0% {
|
||||
translate: 0 120vh;
|
||||
opacity: 0;
|
||||
}
|
||||
10% {
|
||||
opacity: 0.8;
|
||||
}
|
||||
90% {
|
||||
opacity: 0.8;
|
||||
}
|
||||
100% {
|
||||
translate: 0 -150px;
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
|
||||
@-webkit-keyframes spooky-shake {
|
||||
0%, 100% {
|
||||
transform: translateX(0) scale(1) rotate(15deg);
|
||||
}
|
||||
50% {
|
||||
transform: translateX(80px) scale(1.2) rotate(-15deg);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes spooky-shake {
|
||||
0%, 100% {
|
||||
transform: translateX(0) scale(1) rotate(15deg);
|
||||
}
|
||||
50% {
|
||||
transform: translateX(80px) scale(1.2) rotate(-15deg);
|
||||
}
|
||||
}
|
||||
|
||||
/* Base predefined starting offsets (if not overridden by js) */
|
||||
.spooky:nth-of-type(0) { left: 1%; }
|
||||
.spooky:nth-of-type(1) { left: 10%; }
|
||||
.spooky:nth-of-type(2) { left: 20%; }
|
||||
.spooky:nth-of-type(3) { left: 30%; }
|
||||
.spooky:nth-of-type(4) { left: 40%; }
|
||||
.spooky:nth-of-type(5) { left: 50%; }
|
||||
.spooky:nth-of-type(6) { left: 60%; }
|
||||
.spooky:nth-of-type(7) { left: 70%; }
|
||||
.spooky:nth-of-type(8) { left: 80%; }
|
||||
.spooky:nth-of-type(9) { left: 90%; }
|
||||
.spooky:nth-of-type(10) { left: 25%; }
|
||||
.spooky:nth-of-type(11) { left: 65%; }
|
||||
144
Jellyfin.Plugin.Seasonals/Web/spooky.js
Normal file
144
Jellyfin.Plugin.Seasonals/Web/spooky.js
Normal file
@@ -0,0 +1,144 @@
|
||||
const config = window.SeasonalsPluginConfig?.Spooky || {};
|
||||
|
||||
const spooky = config.EnableSpooky !== undefined ? config.EnableSpooky : true; // enable/disable
|
||||
const spookyCount = config.SymbolCount || 25; // count of random extra symbols
|
||||
const enableDifferentDuration = config.EnableDifferentDuration !== undefined ? config.EnableDifferentDuration : true;
|
||||
const enableSpookySway = config.EnableSpookySway !== undefined ? config.EnableSpookySway : true;
|
||||
const spookySize = config.SpookySize || 20;
|
||||
const spookyGlowSize = config.SpookyGlowSize !== undefined ? config.SpookyGlowSize : 2;
|
||||
|
||||
let msgPrinted = false;
|
||||
|
||||
// function to check and control the spooky theme
|
||||
function toggleSpooky() {
|
||||
const spookyContainer = document.querySelector('.spooky-container');
|
||||
if (!spookyContainer) 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) {
|
||||
spookyContainer.style.display = 'none'; // hide spooky
|
||||
if (!msgPrinted) {
|
||||
console.log('Spooky Theme hidden');
|
||||
msgPrinted = true;
|
||||
}
|
||||
} else {
|
||||
spookyContainer.style.display = 'block'; // show spooky
|
||||
if (msgPrinted) {
|
||||
console.log('Spooky Theme visible');
|
||||
msgPrinted = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// observe changes in the DOM
|
||||
const observer = new MutationObserver(toggleSpooky);
|
||||
observer.observe(document.body, {
|
||||
childList: true,
|
||||
subtree: true,
|
||||
attributes: true
|
||||
});
|
||||
|
||||
const spookyImages = [
|
||||
"../Seasonals/Resources/halloween_images/ghost_20x20.png",
|
||||
"../Seasonals/Resources/halloween_images/bat_20x20.png",
|
||||
"../Seasonals/Resources/halloween_images/pumpkin_20x20.png",
|
||||
];
|
||||
|
||||
// create spooky objects
|
||||
function createSpooky() {
|
||||
const container = document.querySelector('.spooky-container') || document.createElement("div");
|
||||
|
||||
if (!document.querySelector('.spooky-container')) {
|
||||
container.className = "spooky-container";
|
||||
container.setAttribute("aria-hidden", "true");
|
||||
document.body.appendChild(container);
|
||||
}
|
||||
|
||||
// Base items per image
|
||||
for (let i = 0; i < 4; i++) {
|
||||
spookyImages.forEach(imageSrc => {
|
||||
const spookyOuter = document.createElement("div");
|
||||
spookyOuter.className = "spooky";
|
||||
|
||||
const spookyInner = document.createElement("div");
|
||||
spookyInner.className = "spooky-inner";
|
||||
spookyInner.style.width = `${spookySize}px`;
|
||||
if (!enableSpookySway) spookyInner.style.animationName = 'none';
|
||||
|
||||
const img = document.createElement("img");
|
||||
img.src = imageSrc;
|
||||
img.style.filter = spookyGlowSize > 0 ? `drop-shadow(0 0 ${spookyGlowSize}px rgba(255, 120, 0, 0.4))` : 'none';
|
||||
|
||||
// randomize fall and sway (shake) speeds like halloween.js
|
||||
if (enableDifferentDuration) {
|
||||
const randomAnimationDuration = Math.random() * 10 + 6; // fall duration (6s to 10s)
|
||||
const randomAnimationDuration2 = Math.random() * 5 + 2; // shake duration (2s to 5s)
|
||||
spookyOuter.style.animationDuration = `${randomAnimationDuration}s`;
|
||||
spookyInner.style.animationDuration = `${randomAnimationDuration2}s`;
|
||||
}
|
||||
|
||||
const randomLeft = Math.random() * 100;
|
||||
const randomAnimationDelay = Math.random() * 10;
|
||||
const randomAnimationDelay2 = Math.random() * 3;
|
||||
|
||||
spookyOuter.style.left = `${randomLeft}%`;
|
||||
spookyOuter.style.animationDelay = `${randomAnimationDelay}s`;
|
||||
spookyInner.style.animationDelay = `${randomAnimationDelay2}s`;
|
||||
|
||||
spookyInner.appendChild(img);
|
||||
spookyOuter.appendChild(spookyInner);
|
||||
container.appendChild(spookyOuter);
|
||||
});
|
||||
}
|
||||
|
||||
// Add configured extra symbols
|
||||
for (let i = 0; i < spookyCount; i++) {
|
||||
const spookyOuter = document.createElement("div");
|
||||
spookyOuter.className = "spooky";
|
||||
|
||||
const spookyInner = document.createElement("div");
|
||||
spookyInner.className = "spooky-inner";
|
||||
spookyInner.style.width = `${spookySize}px`;
|
||||
if (!enableSpookySway) spookyInner.style.animationName = 'none';
|
||||
|
||||
const imageSrc = spookyImages[Math.floor(Math.random() * spookyImages.length)];
|
||||
const img = document.createElement("img");
|
||||
img.src = imageSrc;
|
||||
img.style.filter = spookyGlowSize > 0 ? `drop-shadow(0 0 ${spookyGlowSize}px rgba(255, 120, 0, 0.4))` : 'none';
|
||||
|
||||
const randomLeft = Math.random() * 100;
|
||||
const randomAnimationDelay = Math.random() * 10;
|
||||
const randomAnimationDelay2 = Math.random() * 3;
|
||||
|
||||
spookyOuter.style.left = `${randomLeft}%`;
|
||||
spookyOuter.style.animationDelay = `${randomAnimationDelay}s`;
|
||||
spookyInner.style.animationDelay = `${randomAnimationDelay2}s`;
|
||||
|
||||
if (enableDifferentDuration) {
|
||||
const randomAnimationDuration = Math.random() * 10 + 6; // delay (6s to 10s)
|
||||
const randomAnimationDuration2 = Math.random() * 5 + 2; // delay (2s to 5s)
|
||||
spookyOuter.style.animationDuration = `${randomAnimationDuration}s`;
|
||||
spookyInner.style.animationDuration = `${randomAnimationDuration2}s`;
|
||||
}
|
||||
|
||||
spookyInner.appendChild(img);
|
||||
spookyOuter.appendChild(spookyInner);
|
||||
container.appendChild(spookyOuter);
|
||||
}
|
||||
|
||||
console.log('Spooky symbols added');
|
||||
}
|
||||
|
||||
// initialize spooky
|
||||
function initializeSpooky() {
|
||||
if (!spooky) return;
|
||||
createSpooky();
|
||||
toggleSpooky();
|
||||
}
|
||||
|
||||
// initialize script
|
||||
initializeSpooky();
|
||||
@@ -240,7 +240,7 @@
|
||||
<option value="snowstorm">Snowstorm</option>
|
||||
<option value="fireworks">Fireworks</option>
|
||||
<option value="halloween">Halloween</option>
|
||||
<option value="legacyhalloween">Legacy Halloween</option>
|
||||
<option value="spooky">Spooky</option>
|
||||
<option value="hearts">Hearts</option>
|
||||
<option value="christmas">Christmas</option>
|
||||
<option value="santa">Santa</option>
|
||||
@@ -253,10 +253,10 @@
|
||||
<option value="cherryblossom">Cherryblossom</option>
|
||||
<option value="earthday">Earth Day</option>
|
||||
<option value="eurovision">Eurovision</option>
|
||||
<option value="piday">Pi-Day</option>
|
||||
<option value="matrix">Matrix</option>
|
||||
<option value="pride">Pride</option>
|
||||
<option value="rain">Rain</option>
|
||||
<option value="storm">Storm (Epilepsy Warning!)</option>
|
||||
<option value="storm">Storm (⚠️Epilepsy Warning)</option>
|
||||
<option value="frost">Frost / Ice</option>
|
||||
<option value="filmnoir">Film-Noir</option>
|
||||
<option value="oscar">Oscar Awards</option>
|
||||
@@ -346,7 +346,7 @@
|
||||
snowstorm: { css: 'snowstorm.css', js: 'snowstorm.js', container: 'snowstorm-container' },
|
||||
fireworks: { css: 'fireworks.css', js: 'fireworks.js', container: 'fireworks' },
|
||||
halloween: { css: 'halloween.css', js: 'halloween.js', container: 'halloween-container' },
|
||||
legacyhalloween: { css: 'legacyhalloween.css', js: 'legacyhalloween.js', container: 'legacyhalloween-container' },
|
||||
spooky: { css: 'spooky.css', js: 'spooky.js', container: 'spooky-container' },
|
||||
hearts: { css: 'hearts.css', js: 'hearts.js', container: 'hearts-container' },
|
||||
christmas: { css: 'christmas.css', js: 'christmas.js', container: 'christmas-container' },
|
||||
santa: { css: 'santa.css', js: 'santa.js', container: 'santa-container' },
|
||||
@@ -359,7 +359,7 @@
|
||||
cherryblossom: { css: 'cherryblossom.css', js: 'cherryblossom.js', container: 'cherryblossom-container' },
|
||||
earthday: { css: 'earthday.css', js: 'earthday.js', container: 'earthday-container' },
|
||||
eurovision: { css: 'eurovision.css', js: 'eurovision.js', container: 'eurovision-container' },
|
||||
piday: { css: 'piday.css', js: 'piday.js', container: 'piday-container' },
|
||||
matrix: { css: 'matrix.css', js: 'matrix.js', container: 'matrix-container' },
|
||||
pride: { css: 'pride.css', js: 'pride.js', container: 'pride-container' },
|
||||
rain: { css: 'rain.css', js: 'rain.js', container: 'rain-container' },
|
||||
storm: { css: 'storm.css', js: 'storm.js', container: 'storm-container' },
|
||||
@@ -399,11 +399,11 @@
|
||||
// Remove any theme-created containers on body
|
||||
const knownContainers = [
|
||||
'.snowfall-container', '.snowflakes', '.snowstorm-container',
|
||||
'.fireworks', '.halloween-container', '.legacyhalloween-container', '.hearts-container',
|
||||
'.fireworks', '.halloween-container', '.spooky-container', '.hearts-container',
|
||||
'.christmas-container', '.santa-container', '.autumn-container',
|
||||
'.easter-container', '.resurrection-container', '.spring-container',
|
||||
'.summer-container', '.carnival-container', '.cherryblossom-container',
|
||||
'.earthday-container', '.eurovision-container', '.piday-container',
|
||||
'.earthday-container', '.eurovision-container', '.matrix-container',
|
||||
'.pride-container', '.rain-container', '.storm-container',
|
||||
'.frost-container', '.filmnoir-container', '.oscar-container',
|
||||
'.marioday-container', '.starwars-container', '.oktoberfest-container',
|
||||
|
||||
Reference in New Issue
Block a user