Compare commits

..

119 Commits

Author SHA1 Message Date
CodeDevMLH
36347cc4b0 Update manifest.json for release v1.6.13.1 [skip ci] 2026-02-04 12:39:14 +00:00
CodeDevMLH
7f94164e55 Update version to 1.6.13.1
All checks were successful
Auto Release Plugin / build-and-release (push) Successful in 50s
2026-02-04 13:38:23 +01:00
CodeDevMLH
cbab7de546 Refactor seasonals.js 2026-02-04 13:37:49 +01:00
CodeDevMLH
d0de5cd021 Update seasonals settings to use namespaced localStorage keys and enhance field description for client-side toggle 2026-02-04 12:40:50 +01:00
CodeDevMLH
16628e9902 Update manifest.json for release v1.6.13.0 [skip ci] 2026-02-04 01:42:27 +00:00
CodeDevMLH
72bfe0a14a Auto-Update MediaBar Enhanced to v1.3.0.3
All checks were successful
Auto Release Plugin / build-and-release (push) Successful in 50s
2026-02-04 01:41:42 +00:00
CodeDevMLH
6498ec4216 Update manifest.json for release v1.6.13.0 [skip ci] 2026-02-04 01:28:50 +00:00
CodeDevMLH
0d350fc76b Auto-Update MediaBar Enhanced to v1.3.0.2
All checks were successful
Auto Release Plugin / build-and-release (push) Successful in 51s
2026-02-04 01:28:02 +00:00
CodeDevMLH
2c6e4ce610 Update manifest.json for release v1.6.13.0 [skip ci] 2026-02-04 01:15:12 +00:00
CodeDevMLH
0c552774dc Auto-Update MediaBar Enhanced to v1.3.0.1
All checks were successful
Auto Release Plugin / build-and-release (push) Successful in 50s
2026-02-04 01:14:26 +00:00
CodeDevMLH
9ab605bb74 Update manifest.json for release v1.6.13.0 [skip ci] 2026-02-04 00:08:09 +00:00
CodeDevMLH
3d6cba0fe4 Auto-Update MediaBar Enhanced to v1.3.0.0
All checks were successful
Auto Release Plugin / build-and-release (push) Successful in 50s
2026-02-04 00:07:22 +00:00
CodeDevMLH
32e5e2b690 Update manifest.json for release v1.6.13.0 [skip ci] 2026-02-03 23:12:29 +00:00
CodeDevMLH
c967c1e308 Bump version to 1.6.13.0 and update changelog for new features and fixes
All checks were successful
Auto Release Plugin / build-and-release (push) Successful in 51s
2026-02-04 00:11:38 +01:00
CodeDevMLH
ae28d5219b Enhance README.md with additional details and images for configuration UI and user toggle features 2026-02-03 23:47:58 +01:00
CodeDevMLH
e4228f889e Add guidance for safely resetting 'dev' branch to 'main' in Git [skip ci] 2026-02-03 23:22:33 +01:00
CodeDevMLH
6d721c755e Update README.md to enhance documentation and add new seasonal themes 2026-02-03 23:06:47 +01:00
CodeDevMLH
6948953778 Add optional stash commands to Git rebase instructions 2026-02-03 23:04:58 +01:00
CodeDevMLH
8a50cef330 Update Git rebase instructions to include fetching from origin [skip ci] 2026-02-03 22:37:20 +01:00
CodeDevMLH
a0bf5370bd Update manifest.json for release v1.6.12.0 [skip ci] 2026-02-03 21:28:05 +00:00
CodeDevMLH
c5800b431d Bump version to 1.6.12.0 and update disabled options in configuration page
All checks were successful
Auto Release Plugin / build-and-release (push) Successful in 51s
2026-02-03 22:27:15 +01:00
CodeDevMLH
9a4056651d Update manifest.json for release v1.6.11.0 [skip ci] 2026-02-03 21:10:34 +00:00
CodeDevMLH
87382db78e Bump version to 1.6.11.0 and update configuration styles in the settings page
All checks were successful
Auto Release Plugin / build-and-release (push) Successful in 50s
2026-02-03 22:09:43 +01:00
CodeDevMLH
5d9afa762f Update manifest.json for release v1.6.10.0 [skip ci] 2026-02-03 21:03:41 +00:00
CodeDevMLH
2f88587dab Bump version to 1.6.10.0 and update logo source in settings
All checks were successful
Auto Release Plugin / build-and-release (push) Successful in 52s
2026-02-03 22:02:50 +01:00
CodeDevMLH
360a959b69 rename logo [skip ci] 2026-02-03 22:02:01 +01:00
CodeDevMLH
36fba545cf Update manifest.json for release v1.6.9.0 [skip ci] 2026-02-03 21:01:40 +00:00
CodeDevMLH
7faa2cc766 Merge branch 'main' of ssh://git.mahom03-spacecloud.de:44322/CodeDevMLH/Jellyfin-Seasonals-Plugin
Some checks failed
Auto Release Plugin / build-and-release (push) Has been cancelled
2026-02-03 22:00:43 +01:00
CodeDevMLH
aa832e93aa add logo [skip ci] 2026-02-03 22:00:32 +01:00
CodeDevMLH
86bbeb583d Update manifest.json for release v1.6.9.0 [skip ci] 2026-02-03 20:59:03 +00:00
CodeDevMLH
7a642b34b8 add assets [skip CI]
All checks were successful
Auto Release Plugin / build-and-release (push) Successful in 53s
2026-02-03 21:58:01 +01:00
CodeDevMLH
926b30b8ce Update manifest.json for release v1.6.9.0 [skip ci] 2026-02-03 20:30:56 +00:00
CodeDevMLH
5b672cef42 Bump version to 1.6.9.0; update logo asset path and manifest for new release
All checks were successful
Auto Release Plugin / build-and-release (push) Successful in 53s
2026-02-03 21:30:03 +01:00
CodeDevMLH
ceccbf4ded Update manifest.json for release v1.6.8.0 [skip ci] 2026-02-03 20:24:42 +00:00
CodeDevMLH
9cba2a0755 Bump version to 1.6.8.0; update manifest and add new logo assets
All checks were successful
Auto Release Plugin / build-and-release (push) Successful in 1m6s
2026-02-03 21:23:36 +01:00
CodeDevMLH
af036a9aa4 Update manifest.json for release v1.6.7.0 [skip ci] 2026-02-03 19:59:06 +00:00
CodeDevMLH
cfefd2d2d3 Bump version to 1.6.7.0; update manifest and configuration files for improved seasonal settings
All checks were successful
Auto Release Plugin / build-and-release (push) Successful in 52s
2026-02-03 20:58:14 +01:00
CodeDevMLH
8e7299508b Update manifest.json for release v1.6.6.0 [skip ci] 2026-02-03 19:51:20 +00:00
CodeDevMLH
fc7aa36f41 Bump version to 1.6.6.0; update manifest and settings popup for improved theme selection
All checks were successful
Auto Release Plugin / build-and-release (push) Successful in 53s
2026-02-03 20:50:28 +01:00
CodeDevMLH
fc9896048f Update manifest.json for release v1.6.5.0 [skip ci] 2026-02-03 19:37:37 +00:00
CodeDevMLH
572c4d9ace Bump version to 1.6.5.0 and update manifest; add new select options in settings popup
All checks were successful
Auto Release Plugin / build-and-release (push) Successful in 51s
2026-02-03 20:36:45 +01:00
CodeDevMLH
2572e085f6 Update manifest.json for release v1.6.4.0 [skip ci] 2026-02-03 18:46:36 +00:00
CodeDevMLH
8297f989fd Bump version to 1.6.4.0 and update changelog for new features; modify select classes in config page and settings popup
All checks were successful
Auto Release Plugin / build-and-release (push) Successful in 58s
2026-02-03 19:45:38 +01:00
CodeDevMLH
636aaa2a4a Update manifest.json for release v1.6.3.0 [skip ci] 2026-02-03 18:33:25 +00:00
CodeDevMLH
5e70621e93 Update version to 1.6.3.0, modify checkbox layout, and adjust popup styles
All checks were successful
Auto Release Plugin / build-and-release (push) Successful in 52s
2026-02-03 19:32:34 +01:00
CodeDevMLH
0b4434c51c Update manifest.json for release v1.6.2.0 [skip ci] 2026-02-03 18:18:35 +00:00
CodeDevMLH
dd6583c055 Bump version to 1.6.2.0 and update changelog for new features
All checks were successful
Auto Release Plugin / build-and-release (push) Successful in 52s
2026-02-03 19:17:44 +01:00
CodeDevMLH
a0b0514159 Update manifest.json for release v1.6.1.0 [skip ci] 2026-02-03 18:08:59 +00:00
CodeDevMLH
e977c83e8f Bump version to 1.6.1.0 in project file and manifest
All checks were successful
Auto Release Plugin / build-and-release (push) Successful in 53s
2026-02-03 19:08:07 +01:00
CodeDevMLH
e281f5c579 test other ui design 2026-02-03 19:08:02 +01:00
CodeDevMLH
e6b646e478 Update .gitignore to include test-site-new.html [skip CI] 2026-02-03 18:50:22 +01:00
CodeDevMLH
8d6bc12fa4 Update manifest.json for release v1.6.0.0 [skip ci] 2026-02-03 17:49:06 +00:00
CodeDevMLH
f036e748da Add client-side toggle option for seasonal settings
All checks were successful
Auto Release Plugin / build-and-release (push) Successful in 54s
2026-02-03 18:48:11 +01:00
CodeDevMLH
0177a7caea Update manifest.json for release v1.5.1.0 [skip ci] 2026-01-28 22:40:50 +00:00
CodeDevMLH
c45e9f6156 Auto-Update MediaBar Enhanced to v1.2.3.7
All checks were successful
Auto Release Plugin / build-and-release (push) Successful in 52s
2026-01-28 22:40:00 +00:00
CodeDevMLH
8dc9b9f157 Update manifest.json for release v1.5.1.0 [skip ci] 2026-01-28 21:32:51 +00:00
CodeDevMLH
e73c6c14bb Auto-Update MediaBar Enhanced to v1.2.3.6
All checks were successful
Auto Release Plugin / build-and-release (push) Successful in 52s
2026-01-28 21:32:01 +00:00
CodeDevMLH
aa1c60f9ce Update manifest.json for release v1.5.1.0 [skip ci] 2026-01-28 20:22:17 +00:00
CodeDevMLH
d1e668bcff Auto-Update MediaBar Enhanced to v1.2.3.5
All checks were successful
Auto Release Plugin / build-and-release (push) Successful in 51s
2026-01-28 20:21:27 +00:00
CodeDevMLH
0454d43f32 Update manifest.json for release v1.5.1.0 [skip ci] 2026-01-28 01:10:34 +00:00
CodeDevMLH
61b3b51139 Auto-Update MediaBar Enhanced to v1.2.3.4
All checks were successful
Auto Release Plugin / build-and-release (push) Successful in 52s
2026-01-28 01:09:45 +00:00
CodeDevMLH
60d1a546a2 Update manifest.json for release v1.5.1.0 [skip ci] 2026-01-28 01:06:40 +00:00
CodeDevMLH
669933d270 Auto-Update MediaBar Enhanced to v1.2.3.3
All checks were successful
Auto Release Plugin / build-and-release (push) Successful in 51s
2026-01-28 01:05:53 +00:00
CodeDevMLH
053f0ccfa7 Update manifest.json for release v1.5.1.0 [skip ci] 2026-01-28 00:46:16 +00:00
CodeDevMLH
60e998fc7f Auto-Update MediaBar Enhanced to v1.2.3.3
All checks were successful
Auto Release Plugin / build-and-release (push) Successful in 51s
2026-01-28 00:45:27 +00:00
CodeDevMLH
3aa631198a Update manifest.json for release v1.5.1.0 [skip ci] 2026-01-28 00:31:33 +00:00
CodeDevMLH
b1876d655e Auto-Update MediaBar Enhanced to v1.2.3.2
All checks were successful
Auto Release Plugin / build-and-release (push) Successful in 52s
2026-01-28 00:30:43 +00:00
CodeDevMLH
9a9f89c1fc Update manifest.json for release v1.5.1.0 [skip ci] 2026-01-28 00:18:24 +00:00
CodeDevMLH
2fb41f6442 Auto-Update MediaBar Enhanced to v1.2.3.1
All checks were successful
Auto Release Plugin / build-and-release (push) Successful in 52s
2026-01-28 00:17:35 +00:00
CodeDevMLH
4ab949e6d7 Update manifest.json for release v1.5.1.0 [skip ci] 2026-01-27 23:55:34 +00:00
CodeDevMLH
7d1024c917 Auto-Update MediaBar Enhanced to v1.2.3.0
All checks were successful
Auto Release Plugin / build-and-release (push) Successful in 50s
2026-01-27 23:54:49 +00:00
CodeDevMLH
c2e3d55110 Update manifest.json for release v1.5.1.0 [skip ci] 2026-01-24 22:54:52 +00:00
CodeDevMLH
c6503a90bb Auto-Update MediaBar Enhanced to v1.2.2.0
All checks were successful
Auto Release Plugin / build-and-release (push) Successful in 53s
2026-01-24 22:54:01 +00:00
CodeDevMLH
b70787d5ec Update manifest.json for release v1.5.1.0 [skip ci] 2026-01-22 23:51:52 +00:00
CodeDevMLH
a9eb8113a6 Auto-Update MediaBar Enhanced to v1.2.1.0
All checks were successful
Auto Release Plugin / build-and-release (push) Successful in 51s
2026-01-22 23:51:03 +00:00
CodeDevMLH
aaf15d8934 Update manifest.json for release v1.5.1.0 [skip ci] 2026-01-21 23:12:11 +00:00
CodeDevMLH
ec298ebde0 bump version to 1.5.1.0 in project file
All checks were successful
Auto Release Plugin / build-and-release (push) Successful in 53s
2026-01-22 00:11:19 +01:00
CodeDevMLH
bb108d0f49 Update manifest.json for release v1.5.1.0 [skip ci] 2026-01-21 22:53:15 +00:00
MLH
f271e1715d Merge pull request 'fix: snowfall effect sometimes not scaling on window resize, which leads to clustering and rain effect of snowflakes' (#1) from dev into main
All checks were successful
Auto Release Plugin / build-and-release (push) Successful in 57s
Reviewed-on: #1
2026-01-21 23:52:18 +01:00
CodeDevMLH
bd0e2779e5 fix: snowfall effect sometimes not scaling on window resize, which leads to clustering and rain effect of snowflakes
All checks were successful
🏗️ Build Plugin / build (push) Successful in 53s
🏗️ Build Plugin / build (pull_request) Successful in 58s
2026-01-21 23:51:23 +01:00
CodeDevMLH
53a1682868 Enhance descriptions and overviews in manifest.json for Seasonals and Media Bar Enhanced plugins [skip ci] 2026-01-09 00:32:40 +01:00
CodeDevMLH
a7df2fd832 Update manifest.json for release v1.5.0.0 [skip ci] 2026-01-08 23:31:54 +00:00
CodeDevMLH
c56cde860b Auto-Update MediaBar Enhanced to v1.2.0.0
All checks were successful
Auto Release Plugin / build-and-release (push) Successful in 51s
2026-01-08 23:31:04 +00:00
CodeDevMLH
59211e27c6 Update manifest.json for release v1.5.0.0 [skip ci] 2026-01-08 23:15:57 +00:00
CodeDevMLH
a2b1179353 Auto-Update MediaBar Enhanced to v1.2.0.0
All checks were successful
Auto Release Plugin / build-and-release (push) Successful in 47s
2026-01-08 23:15:12 +00:00
CodeDevMLH
c7f34ec92f Update manifest.json for release v1.5.0.0 [skip ci] 2026-01-08 22:16:58 +00:00
CodeDevMLH
4c011cf560 Auto-Update MediaBar Enhanced to v1.2.0.0
All checks were successful
Auto Release Plugin / build-and-release (push) Successful in 48s
2026-01-08 22:16:11 +00:00
CodeDevMLH
e5f78c711d Update manifest.json for release v1.5.0.0 [skip ci] 2026-01-08 15:27:46 +00:00
CodeDevMLH
98a536315b Auto-Update MediaBar Enhanced to v1.1.2.0
All checks were successful
Auto Release Plugin / build-and-release (push) Successful in 50s
2026-01-08 15:27:00 +00:00
CodeDevMLH
01343848e3 Update manifest.json for release v1.5.0.0 [skip ci] 2026-01-08 14:55:53 +00:00
CodeDevMLH
113e7dd0f7 Auto-Update MediaBar Enhanced to v1.1.1.0
All checks were successful
Auto Release Plugin / build-and-release (push) Successful in 50s
2026-01-08 14:55:04 +00:00
CodeDevMLH
1bc4176771 Update manifest.json for release v1.5.0.0 [skip ci] 2026-01-08 02:16:44 +00:00
CodeDevMLH
b091e5592d Auto-Update MediaBar Enhanced to v1.1.0.0
All checks were successful
Auto Release Plugin / build-and-release (push) Successful in 50s
2026-01-08 02:15:57 +00:00
CodeDevMLH
cb2d86340e Update manifest.json for release v1.5.0.0 [skip ci] 2026-01-06 23:34:26 +00:00
CodeDevMLH
57a92e94de Remove build.yaml configuration file for Seasonals plugin
All checks were successful
Auto Release Plugin / build-and-release (push) Successful in 57s
2026-01-07 00:33:36 +01:00
CodeDevMLH
30bc8bef39 Update manifest.json for release v1.5.0.0 [skip ci] 2026-01-06 23:27:26 +00:00
CodeDevMLH
2d237063a3 Auto-Update MediaBar Enhanced to v1.0.0.3
All checks were successful
Auto Release Plugin / build-and-release (push) Successful in 54s
2026-01-06 23:26:36 +00:00
CodeDevMLH
049a5075b5 Update manifest.json for release v1.5.0.0 [skip ci] 2026-01-06 23:18:28 +00:00
CodeDevMLH
cb2e9c4b07 Auto-Update MediaBar Enhanced to v1.0.0.3
All checks were successful
Auto Release Plugin / build-and-release (push) Successful in 51s
2026-01-06 23:17:39 +00:00
CodeDevMLH
5a39f85082 Update manifest.json for release v1.5.0.0 [skip ci] 2026-01-06 23:13:12 +00:00
CodeDevMLH
b863d201b9 Auto-Update MediaBar Enhanced to v1.0.0.3
All checks were successful
Auto Release Plugin / build-and-release (push) Successful in 51s
2026-01-06 23:12:24 +00:00
CodeDevMLH
78b50b41c2 Update manifest.json for release v1.5.0.0 [skip ci] 2026-01-06 23:11:24 +00:00
CodeDevMLH
0ba1545fd6 test
All checks were successful
Auto Release Plugin / build-and-release (push) Successful in 49s
2026-01-07 00:10:33 +01:00
CodeDevMLH
16c4e0f29b fix link 2026-01-07 00:09:41 +01:00
CodeDevMLH
3581b8cbb2 Update manifest.json for release v1.5.0.0 [skip ci] 2026-01-06 22:57:27 +00:00
CodeDevMLH
7184c93f6f Auto-Update MediaBar Enhanced to v1.0.0.3
All checks were successful
Auto Release Plugin / build-and-release (push) Successful in 50s
2026-01-06 22:56:39 +00:00
CodeDevMLH
0969f0238f Update manifest.json for release v1.5.0.0 [skip ci] 2026-01-06 22:45:32 +00:00
CodeDevMLH
12f868d3f9 test ..
All checks were successful
Auto Release Plugin / build-and-release (push) Successful in 52s
2026-01-06 23:44:42 +01:00
CodeDevMLH
dc7be56807 Merge branch 'main' of ssh://git.mahom03-spacecloud.de:44322/CodeDevMLH/Jellyfin-Seasonals-Plugin 2026-01-06 23:41:57 +01:00
CodeDevMLH
cb60690e6b fix log name 2026-01-06 23:41:55 +01:00
CodeDevMLH
e0397bb2e8 Update manifest.json for release v1.5.0.0 [skip ci] 2026-01-06 22:40:15 +00:00
CodeDevMLH
822bcafd11 Auto-Update MediaBar Enhanced to v1.0.0.3
All checks were successful
Auto Release Plugin / build-and-release (push) Successful in 50s
2026-01-06 22:39:29 +00:00
CodeDevMLH
429d96c816 Update manifest.json for release v1.5.0.0 [skip ci] 2026-01-06 22:37:52 +00:00
CodeDevMLH
e948055f0f test
All checks were successful
Auto Release Plugin / build-and-release (push) Successful in 51s
2026-01-06 23:37:01 +01:00
CodeDevMLH
5129d46163 fix remote repo 2026-01-06 23:36:39 +01:00
CodeDevMLH
660f7142ef Update manifest.json for release v1.5.0.0 [skip ci] 2026-01-06 22:33:07 +00:00
CodeDevMLH
9f657588d8 fix changelog
All checks were successful
Auto Release Plugin / build-and-release (push) Successful in 52s
2026-01-06 23:32:16 +01:00
CodeDevMLH
0457f1a764 Merge branch 'main' of ssh://git.mahom03-spacecloud.de:44322/CodeDevMLH/Jellyfin-Seasonals-Plugin 2026-01-06 23:30:37 +01:00
CodeDevMLH
f196c6c296 fix url 2026-01-06 23:30:36 +01:00
20 changed files with 2235 additions and 391 deletions

View File

@@ -40,6 +40,11 @@ jobs:
echo "Detected Version: $VERSION" echo "Detected Version: $VERSION"
echo "VERSION=$VERSION" >> $GITHUB_ENV echo "VERSION=$VERSION" >> $GITHUB_ENV
echo "TARGET_ABI=$TARGET_ABI" >> $GITHUB_ENV
# Also export GUID for later use
PLUGIN_GUID=$(jq -r '.[0].guid' manifest.json)
echo "PLUGIN_GUID=$PLUGIN_GUID" >> $GITHUB_ENV
# Escape newlines in changelog for GITHUB_ENV # Escape newlines in changelog for GITHUB_ENV
echo "CHANGELOG<<EOF" >> $GITHUB_ENV echo "CHANGELOG<<EOF" >> $GITHUB_ENV
@@ -71,7 +76,7 @@ jobs:
REPO_OWNER="${{ github.repository_owner }}" REPO_OWNER="${{ github.repository_owner }}"
REPO_NAME="${{ github.event.repository.name }}" REPO_NAME="${{ github.event.repository.name }}"
VERSION="${{ env.VERSION }}" VERSION="${{ env.VERSION }}"
DOWNLOAD_URL="https://git.mahom03-spacecloud.de/${{ github.repository }}/releases/download/v$VERSION/Jellyfin.Plugin.Seasonals.zip" DOWNLOAD_URL="https://git.mahom03-spacecloud.de/$REPO_OWNER/$REPO_NAME/releases/download/v$VERSION/Jellyfin.Plugin.Seasonals.zip"
echo "Updating manifest.json with:" echo "Updating manifest.json with:"
echo "Hash: ${{ env.ZIP_HASH }}" echo "Hash: ${{ env.ZIP_HASH }}"
@@ -115,11 +120,14 @@ jobs:
run: | run: |
cd central-manifest cd central-manifest
REPO_OWNER="${{ github.repository_owner }}"
REPO_NAME="${{ github.event.repository.name }}"
# 1. Get info from previous steps # 1. Get info from previous steps
VERSION="${{ env.VERSION }}" VERSION="${{ env.VERSION }}"
HASH="${{ env.ZIP_HASH }}" HASH="${{ env.ZIP_HASH }}"
TIME="${{ env.BUILD_TIME }}" TIME="${{ env.BUILD_TIME }}"
DOWNLOAD_URL="https://git.mahom03-spacecloud.de/${{ github.repository }}/releases/download/v$VERSION/Jellyfin.Plugin.Seasonals.zip" DOWNLOAD_URL="https://git.mahom03-spacecloud.de/$REPO_OWNER/$REPO_NAME/releases/download/v$VERSION/Jellyfin.Plugin.Seasonals.zip"
# 2. Get info from env # 2. Get info from env
PLUGIN_GUID="${{ env.PLUGIN_GUID }}" PLUGIN_GUID="${{ env.PLUGIN_GUID }}"
@@ -171,7 +179,7 @@ jobs:
# Check if there are changes # Check if there are changes
if [[ -n $(git status -s) ]]; then if [[ -n $(git status -s) ]]; then
git add manifest.json git add manifest.json
git commit -m "Auto-Update MediaBar Enhanced to v${{ env.VERSION }}" git commit -m "Auto-Update Seasonals to v${{ env.VERSION }}"
git push git push
else else
echo "No changes to central manifest." echo "No changes to central manifest."

View File

@@ -39,6 +39,11 @@ jobs:
echo "Detected Version: $VERSION" echo "Detected Version: $VERSION"
echo "VERSION=$VERSION" >> $GITHUB_ENV echo "VERSION=$VERSION" >> $GITHUB_ENV
echo "TARGET_ABI=$TARGET_ABI" >> $GITHUB_ENV
# Also export GUID for later use
PLUGIN_GUID=$(jq -r '.[0].guid' manifest.json)
echo "PLUGIN_GUID=$PLUGIN_GUID" >> $GITHUB_ENV
# Escape newlines in changelog for GITHUB_ENV # Escape newlines in changelog for GITHUB_ENV
echo "CHANGELOG<<EOF" >> $GITHUB_ENV echo "CHANGELOG<<EOF" >> $GITHUB_ENV
@@ -113,6 +118,9 @@ jobs:
run: | run: |
cd central-manifest cd central-manifest
REPO_OWNER="${{ github.repository_owner }}"
REPO_NAME="${{ github.event.repository.name }}"
# 1. Get info from previous steps # 1. Get info from previous steps
VERSION="${{ env.VERSION }}" VERSION="${{ env.VERSION }}"
HASH="${{ env.ZIP_HASH }}" HASH="${{ env.ZIP_HASH }}"
@@ -169,7 +177,7 @@ jobs:
# Check if there are changes # Check if there are changes
if [[ -n $(git status -s) ]]; then if [[ -n $(git status -s) ]]; then
git add manifest.json git add manifest.json
git commit -m "Auto-Update MediaBar Enhanced to v${{ env.VERSION }}" git commit -m "Auto-Update Seasonals to v${{ env.VERSION }}"
git push git push
else else
echo "No changes to central manifest." echo "No changes to central manifest."

1
.gitignore vendored
View File

@@ -5,4 +5,5 @@ obj/
artifacts artifacts
test-site.html test-site.html
test-site-new.html
RELEASE_GUIDE.md RELEASE_GUIDE.md

View File

@@ -62,6 +62,7 @@ public class SeasonalsController : ControllerBase
if (path.EndsWith(".png", StringComparison.OrdinalIgnoreCase)) return "image/png"; if (path.EndsWith(".png", StringComparison.OrdinalIgnoreCase)) return "image/png";
if (path.EndsWith(".jpg", StringComparison.OrdinalIgnoreCase)) return "image/jpeg"; if (path.EndsWith(".jpg", StringComparison.OrdinalIgnoreCase)) return "image/jpeg";
if (path.EndsWith(".gif", StringComparison.OrdinalIgnoreCase)) return "image/gif"; if (path.EndsWith(".gif", StringComparison.OrdinalIgnoreCase)) return "image/gif";
if (path.EndsWith(".svg", StringComparison.OrdinalIgnoreCase)) return "image/svg+xml";
return "application/octet-stream"; return "application/octet-stream";
} }
} }

View File

@@ -15,6 +15,7 @@ public class PluginConfiguration : BasePluginConfiguration
IsEnabled = true; IsEnabled = true;
SelectedSeason = "none"; SelectedSeason = "none";
AutomateSeasonSelection = true; AutomateSeasonSelection = true;
EnableClientSideToggle = true;
Autumn = new AutumnOptions(); Autumn = new AutumnOptions();
Snowflakes = new SnowflakesOptions(); Snowflakes = new SnowflakesOptions();
@@ -43,6 +44,14 @@ public class PluginConfiguration : BasePluginConfiguration
/// </summary> /// </summary>
public bool AutomateSeasonSelection { get; set; } public bool AutomateSeasonSelection { get; set; }
/// <summary>
/// Gets or sets a value indicating whether to enable client-side toggle for users.
/// </summary>
public bool EnableClientSideToggle { get; set; }
/// <summary>
/// Gets or sets the Seasonals options.
/// </summary>
public AutumnOptions Autumn { get; set; } public AutumnOptions Autumn { get; set; }
public SnowflakesOptions Snowflakes { get; set; } public SnowflakesOptions Snowflakes { get; set; }
public SnowfallOptions Snowfall { get; set; } public SnowfallOptions Snowfall { get; set; }

View File

@@ -6,19 +6,19 @@
</head> </head>
<body> <body>
<div id="SeasonalsConfigPage" data-role="page" class="page type-interior pluginConfigurationPage" data-require="emby-input,emby-button,emby-select,emby-checkbox"> <div id="SeasonalsConfigPage" data-role="page" class="page type-interior pluginConfigurationPage" data-require="emby-input,emby-button,emby-select,emby-checkbox">
<style> <style>
select option:disabled { select option:disabled {
color: #a3a3a3 !important; color: #a3a3a3 !important;
} }
</style> </style>
<div data-role="content"> <div data-role="content">
<div class="content-primary"> <div class="content-primary">
<div class="sectionTitleContainer"> <div class="sectionTitleContainer">
<h2 class="sectionTitle">Seasonals</h2> <h2 class="sectionTitle">Seasonals</h2>
<a is="emby-linkbutton" class="raised raised-mini emby-button" style="margin-left: 2em;" <a is="emby-linkbutton" class="raised raised-mini emby-button" style="margin-left: 2em;"
target="_blank" href="https://github.com/CodeDevMLH/Jellyfin-Seasonals"> target="_blank" href="https://github.com/CodeDevMLH/Jellyfin-Seasonals">
<i class="md-icon button-icon button-icon-left secondaryText"></i> <i class="md-icon button-icon button-icon-left secondaryText"></i>
<span>Help</span> <span>${Help}</span>
</a> </a>
</div> </div>
<hr style="max-width: 800px; margin: 1em 0;"> <hr style="max-width: 800px; margin: 1em 0;">
@@ -40,7 +40,7 @@
</div> </div>
<div class="selectContainer"> <div class="selectContainer">
<label class="selectLabel" for="SelectedSeason">Selected Season</label> <label class="selectLabel" for="SelectedSeason">Selected Season</label>
<select is="emby-select" id="SelectedSeason" name="SelectedSeason" class="emby-select-withcolor emby-select"> <select id="SelectedSeason" name="SelectedSeason" class="emby-select" style="width: 100%; padding: 0.5em; background-color: #333; border: 1px solid #444; color: #fff; border-radius: 4px;">
<option value="none">None</option> <option value="none">None</option>
<option value="snowflakes">Snowflakes</option> <option value="snowflakes">Snowflakes</option>
<option value="snowfall">Snowfall</option> <option value="snowfall">Snowfall</option>
@@ -52,17 +52,25 @@
<option value="santa">Santa</option> <option value="santa">Santa</option>
<option value="autumn">Autumn</option> <option value="autumn">Autumn</option>
<option value="easter">Easter</option> <option value="easter">Easter</option>
<option value="summer" disabled>Summer (not implemented yet, no idea for a theme. Please commit ideas in a issue)</option> <option value="summer" disabled>Summer (not implemented yet. Please commit ideas in a issue or PR)</option>
<option value="spring" disabled>Spring (not implemented yet, no idea for a theme. Please commit ideas in a issue)</option> <option value="spring" disabled>Spring (not implemented yet. Please commit ideas in a issue or PR)</option>
<option value="oktoberfest" disabled>Oktoberfest (not implemented yet, no idea for a theme. Please commit ideas in a issue)</option> <option value="oktoberfest" disabled>Oktoberfest (not implemented yet. Please commit ideas in a issue or PR)</option>
<option value="carnival" disabled>Carnival (not implemented yet, no idea for a theme. Please commit ideas in a issue)</option> <option value="carnival" disabled>Carnival (not implemented yet. Please commit ideas in a issue or PR)</option>
<option value="championships" disabled>European/World Championships (not implemented yet, no idea for a theme. Please commit ideas in a issue)</option> <option value="championships" disabled>European/World Championships (not implemented yet. Please commit ideas in a issue or PR)</option>
<option value="patrick" disabled>St. Patrick's Day (not implemented yet, no idea for a theme. Please commit ideas in a issue)</option> <option value="patrick" disabled>St. Patrick's Day (not implemented yet. Please commit ideas in a issue or PR)</option>
<option value="thanksgiving" disabled>Thanksgiving (not implemented yet, no idea for a theme. Please commit ideas in a issue)</option> <option value="thanksgiving" disabled>Thanksgiving (not implemented yet. Please commit ideas in a issue or PR)</option>
<option value="pride" disabled>Pride (not implemented yet, no idea for a theme. Please commit ideas in a issue)</option> <option value="pride" disabled>Pride (not implemented yet. Please commit ideas in a issue or PR)</option>
</select> </select>
<div class="fieldDescription">The season to display if automation is disabled.</div> <div class="fieldDescription">The season to display if automation is disabled.</div>
</div> </div>
<div class="checkboxContainer checkboxContainer-withDescription">
<label class="emby-checkbox-label">
<input id="EnableClientSideToggle" name="EnableClientSideToggle" type="checkbox"
is="emby-checkbox" />
<span>Allow Client-Side Toggle</span>
</label>
<div class="fieldDescription">If enabled, users will see a seasonals icon in the header to toggle seasonals for their browser (device-specific).</div>
</div>
<br> <br>
<details> <details>
@@ -530,7 +538,8 @@
document.querySelector('#IsEnabled').checked = config.IsEnabled; document.querySelector('#IsEnabled').checked = config.IsEnabled;
document.querySelector('#SelectedSeason').value = config.SelectedSeason; document.querySelector('#SelectedSeason').value = config.SelectedSeason;
document.querySelector('#AutomateSeasonSelection').checked = config.AutomateSeasonSelection; document.querySelector('#AutomateSeasonSelection').checked = config.AutomateSeasonSelection;
document.querySelector('#EnableClientSideToggle').checked = config.EnableClientSideToggle !== undefined ? config.EnableClientSideToggle : true;
// Advanced Config // Advanced Config
// Autumn // Autumn
document.querySelector('#EnableAutumn').checked = config.Autumn.EnableAutumn; document.querySelector('#EnableAutumn').checked = config.Autumn.EnableAutumn;
@@ -621,98 +630,100 @@
document.querySelector('#SeasonalsConfigForm') document.querySelector('#SeasonalsConfigForm')
.addEventListener('submit', function(e) { .addEventListener('submit', function(e) {
Dashboard.showLoadingMsg(); Dashboard.showLoadingMsg();
ApiClient.getPluginConfiguration(SeasonalsConfig.pluginUniqueId).then(function (config) { ApiClient.getPluginConfiguration(SeasonalsConfig.pluginUniqueId).then(function (config) {
config.IsEnabled = document.querySelector('#IsEnabled').checked; config.IsEnabled = document.querySelector('#IsEnabled').checked;
config.SelectedSeason = document.querySelector('#SelectedSeason').value; config.SelectedSeason = document.querySelector('#SelectedSeason').value;
config.AutomateSeasonSelection = document.querySelector('#AutomateSeasonSelection').checked; config.AutomateSeasonSelection = document.querySelector('#AutomateSeasonSelection').checked;
config.EnableClientSideToggle = document.querySelector('#EnableClientSideToggle').checked;
// Advanced Config
// Autumn
config.Autumn.EnableAutumn = document.querySelector('#EnableAutumn').checked;
config.Autumn.LeafCount = parseInt(document.querySelector('#AutumnLeafCount').value);
config.Autumn.EnableRandomLeaves = document.querySelector('#EnableRandomLeaves').checked;
config.Autumn.EnableRandomLeavesMobile = document.querySelector('#EnableRandomLeavesMobile').checked;
config.Autumn.EnableDifferentDuration = document.querySelector('#EnableDifferentDurationAutumn').checked;
config.Autumn.EnableRotation = document.querySelector('#EnableRotation').checked;
// Snowflakes
config.Snowflakes.SnowflakeCount = parseInt(document.querySelector('#SnowflakesCount').value);
config.Snowflakes.EnableSnowflakes = document.querySelector('#EnableSnowflakes').checked;
config.Snowflakes.EnableRandomSnowflakes = document.querySelector('#EnableRandomSnowflakes').checked;
config.Snowflakes.EnableRandomSnowflakesMobile = document.querySelector('#EnableRandomSnowflakesMobile').checked;
config.Snowflakes.EnableColoredSnowflakes = document.querySelector('#EnableColoredSnowflakes').checked;
config.Snowflakes.EnableDifferentDuration = document.querySelector('#EnableDifferentDurationSnowflakes').checked;
// Snowfall // Advanced Config
config.Snowfall.EnableSnowfall = document.querySelector('#EnableSnowfall').checked; // Autumn
config.Snowfall.SnowflakesCount = parseInt(document.querySelector('#SnowfallCount').value); config.Autumn.EnableAutumn = document.querySelector('#EnableAutumn').checked;
config.Snowfall.SnowflakesCountMobile = parseInt(document.querySelector('#SnowfallCountMobile').value); config.Autumn.LeafCount = parseInt(document.querySelector('#AutumnLeafCount').value);
config.Snowfall.Speed = parseFloat(document.querySelector('#SnowfallSpeed').value); config.Autumn.EnableRandomLeaves = document.querySelector('#EnableRandomLeaves').checked;
config.Autumn.EnableRandomLeavesMobile = document.querySelector('#EnableRandomLeavesMobile').checked;
config.Autumn.EnableDifferentDuration = document.querySelector('#EnableDifferentDurationAutumn').checked;
config.Autumn.EnableRotation = document.querySelector('#EnableRotation').checked;
// Snowstorm // Snowflakes
config.Snowstorm.EnableSnowstorm = document.querySelector('#EnableSnowstorm').checked; config.Snowflakes.SnowflakeCount = parseInt(document.querySelector('#SnowflakesCount').value);
config.Snowstorm.SnowflakesCount = parseInt(document.querySelector('#SnowstormCount').value); config.Snowflakes.EnableSnowflakes = document.querySelector('#EnableSnowflakes').checked;
config.Snowstorm.SnowflakesCountMobile = parseInt(document.querySelector('#SnowstormCountMobile').value); config.Snowflakes.EnableRandomSnowflakes = document.querySelector('#EnableRandomSnowflakes').checked;
config.Snowstorm.Speed = parseFloat(document.querySelector('#SnowstormSpeed').value); config.Snowflakes.EnableRandomSnowflakesMobile = document.querySelector('#EnableRandomSnowflakesMobile').checked;
config.Snowstorm.HorizontalWind = parseFloat(document.querySelector('#SnowstormHorizontalWind').value); config.Snowflakes.EnableColoredSnowflakes = document.querySelector('#EnableColoredSnowflakes').checked;
config.Snowstorm.VerticalVariation = parseFloat(document.querySelector('#SnowstormVerticalVariation').value); config.Snowflakes.EnableDifferentDuration = document.querySelector('#EnableDifferentDurationSnowflakes').checked;
// Fireworks // Snowfall
config.Fireworks.EnableFireworks = document.querySelector('#EnableFireworks').checked; config.Snowfall.EnableSnowfall = document.querySelector('#EnableSnowfall').checked;
config.Fireworks.ParticleCount = parseInt(document.querySelector('#FireworksParticles').value); config.Snowfall.SnowflakesCount = parseInt(document.querySelector('#SnowfallCount').value);
config.Fireworks.LaunchInterval = parseInt(document.querySelector('#FireworksInterval').value); config.Snowfall.SnowflakesCountMobile = parseInt(document.querySelector('#SnowfallCountMobile').value);
config.Fireworks.ScrollFireworks = document.querySelector('#ScrollFireworks').checked; config.Snowfall.Speed = parseFloat(document.querySelector('#SnowfallSpeed').value);
config.Fireworks.MinFireworks = parseInt(document.querySelector('#MinFireworks').value);
config.Fireworks.MaxFireworks = parseInt(document.querySelector('#MaxFireworks').value);
// Halloween // Snowstorm
config.Halloween.EnableHalloween = document.querySelector('#EnableHalloween').checked; config.Snowstorm.EnableSnowstorm = document.querySelector('#EnableSnowstorm').checked;
config.Halloween.SymbolCount = parseInt(document.querySelector('#HalloweenCount').value); config.Snowstorm.SnowflakesCount = parseInt(document.querySelector('#SnowstormCount').value);
config.Halloween.EnableRandomSymbols = document.querySelector('#EnableRandomHalloween').checked; config.Snowstorm.SnowflakesCountMobile = parseInt(document.querySelector('#SnowstormCountMobile').value);
config.Halloween.EnableRandomSymbolsMobile = document.querySelector('#EnableRandomHalloweenMobile').checked; config.Snowstorm.Speed = parseFloat(document.querySelector('#SnowstormSpeed').value);
config.Halloween.EnableDifferentDuration = document.querySelector('#EnableDifferentDurationHalloween').checked; config.Snowstorm.HorizontalWind = parseFloat(document.querySelector('#SnowstormHorizontalWind').value);
config.Snowstorm.VerticalVariation = parseFloat(document.querySelector('#SnowstormVerticalVariation').value);
// Hearts // Fireworks
config.Hearts.EnableHearts = document.querySelector('#EnableHearts').checked; config.Fireworks.EnableFireworks = document.querySelector('#EnableFireworks').checked;
config.Hearts.SymbolCount = parseInt(document.querySelector('#HeartsCount').value); config.Fireworks.ParticleCount = parseInt(document.querySelector('#FireworksParticles').value);
config.Hearts.EnableRandomSymbols = document.querySelector('#EnableRandomHearts').checked; config.Fireworks.LaunchInterval = parseInt(document.querySelector('#FireworksInterval').value);
config.Hearts.EnableRandomSymbolsMobile = document.querySelector('#EnableRandomHeartsMobile').checked; config.Fireworks.ScrollFireworks = document.querySelector('#ScrollFireworks').checked;
config.Hearts.EnableDifferentDuration = document.querySelector('#EnableDifferentDurationHearts').checked; config.Fireworks.MinFireworks = parseInt(document.querySelector('#MinFireworks').value);
config.Fireworks.MaxFireworks = parseInt(document.querySelector('#MaxFireworks').value);
// Christmas // Halloween
config.Christmas.EnableChristmas = document.querySelector('#EnableChristmas').checked; config.Halloween.EnableHalloween = document.querySelector('#EnableHalloween').checked;
config.Christmas.SymbolCount = parseInt(document.querySelector('#ChristmasCount').value); config.Halloween.SymbolCount = parseInt(document.querySelector('#HalloweenCount').value);
config.Christmas.EnableRandomChristmas = document.querySelector('#EnableRandomChristmas').checked; config.Halloween.EnableRandomSymbols = document.querySelector('#EnableRandomHalloween').checked;
config.Christmas.EnableRandomChristmasMobile = document.querySelector('#EnableRandomChristmasMobile').checked; config.Halloween.EnableRandomSymbolsMobile = document.querySelector('#EnableRandomHalloweenMobile').checked;
config.Christmas.EnableDifferentDuration = document.querySelector('#EnableDifferentDurationChristmas').checked; config.Halloween.EnableDifferentDuration = document.querySelector('#EnableDifferentDurationHalloween').checked;
// Santa // Hearts
config.Santa.EnableSanta = document.querySelector('#EnableSanta').checked; config.Hearts.EnableHearts = document.querySelector('#EnableHearts').checked;
config.Santa.SnowflakesCount = parseInt(document.querySelector('#SantaSnowflakes').value); config.Hearts.SymbolCount = parseInt(document.querySelector('#HeartsCount').value);
config.Santa.SnowflakesCountMobile = parseInt(document.querySelector('#SantaSnowflakesMobile').value); config.Hearts.EnableRandomSymbols = document.querySelector('#EnableRandomHearts').checked;
config.Santa.SantaSpeed = parseFloat(document.querySelector('#SantaSpeed').value); config.Hearts.EnableRandomSymbolsMobile = document.querySelector('#EnableRandomHeartsMobile').checked;
config.Santa.SantaSpeedMobile = parseFloat(document.querySelector('#SantaSpeedMobile').value); config.Hearts.EnableDifferentDuration = document.querySelector('#EnableDifferentDurationHearts').checked;
config.Santa.SnowFallSpeed = parseFloat(document.querySelector('#SantaSnowFallSpeed').value);
config.Santa.MaxSantaRestTime = parseFloat(document.querySelector('#MaxSantaRestTime').value);
config.Santa.MinSantaRestTime = parseFloat(document.querySelector('#MinSantaRestTime').value);
config.Santa.MaxPresentFallSpeed = parseFloat(document.querySelector('#MaxPresentFallSpeed').value);
config.Santa.MinPresentFallSpeed = parseFloat(document.querySelector('#MinPresentFallSpeed').value);
// Easter // Christmas
config.Easter.EnableEaster = document.querySelector('#EnableEaster').checked; config.Christmas.EnableChristmas = document.querySelector('#EnableChristmas').checked;
config.Easter.EggCount = parseInt(document.querySelector('#EasterEggCount').value); config.Christmas.SymbolCount = parseInt(document.querySelector('#ChristmasCount').value);
config.Easter.EnableRandomEaster = document.querySelector('#EnableRandomEaster').checked; config.Christmas.EnableRandomChristmas = document.querySelector('#EnableRandomChristmas').checked;
config.Easter.EnableRandomEasterMobile = document.querySelector('#EnableRandomEasterMobile').checked; config.Christmas.EnableRandomChristmasMobile = document.querySelector('#EnableRandomChristmasMobile').checked;
config.Easter.EnableDifferentDuration = document.querySelector('#EnableDifferentDurationEaster').checked; config.Christmas.EnableDifferentDuration = document.querySelector('#EnableDifferentDurationChristmas').checked;
config.Easter.EnableBunny = document.querySelector('#EasterBunny').checked;
config.Easter.BunnyDuration = parseInt(document.querySelector('#BunnyDuration').value);
config.Easter.HopHeight = parseInt(document.querySelector('#HopHeight').value);
config.Easter.MinBunnyRestTime = parseInt(document.querySelector('#MinBunnyRestTime').value);
config.Easter.MaxBunnyRestTime = parseInt(document.querySelector('#MaxBunnyRestTime').value);
ApiClient.updatePluginConfiguration(SeasonalsConfig.pluginUniqueId, config).then(function (result) { // Santa
Dashboard.processPluginConfigurationUpdateResult(result); config.Santa.EnableSanta = document.querySelector('#EnableSanta').checked;
config.Santa.SnowflakesCount = parseInt(document.querySelector('#SantaSnowflakes').value);
config.Santa.SnowflakesCountMobile = parseInt(document.querySelector('#SantaSnowflakesMobile').value);
config.Santa.SantaSpeed = parseFloat(document.querySelector('#SantaSpeed').value);
config.Santa.SantaSpeedMobile = parseFloat(document.querySelector('#SantaSpeedMobile').value);
config.Santa.SnowFallSpeed = parseFloat(document.querySelector('#SantaSnowFallSpeed').value);
config.Santa.MaxSantaRestTime = parseFloat(document.querySelector('#MaxSantaRestTime').value);
config.Santa.MinSantaRestTime = parseFloat(document.querySelector('#MinSantaRestTime').value);
config.Santa.MaxPresentFallSpeed = parseFloat(document.querySelector('#MaxPresentFallSpeed').value);
config.Santa.MinPresentFallSpeed = parseFloat(document.querySelector('#MinPresentFallSpeed').value);
// Easter
config.Easter.EnableEaster = document.querySelector('#EnableEaster').checked;
config.Easter.EggCount = parseInt(document.querySelector('#EasterEggCount').value);
config.Easter.EnableRandomEaster = document.querySelector('#EnableRandomEaster').checked;
config.Easter.EnableRandomEasterMobile = document.querySelector('#EnableRandomEasterMobile').checked;
config.Easter.EnableDifferentDuration = document.querySelector('#EnableDifferentDurationEaster').checked;
config.Easter.EnableBunny = document.querySelector('#EasterBunny').checked;
config.Easter.BunnyDuration = parseInt(document.querySelector('#BunnyDuration').value);
config.Easter.HopHeight = parseInt(document.querySelector('#HopHeight').value);
config.Easter.MinBunnyRestTime = parseInt(document.querySelector('#MinBunnyRestTime').value);
config.Easter.MaxBunnyRestTime = parseInt(document.querySelector('#MaxBunnyRestTime').value);
ApiClient.updatePluginConfiguration(SeasonalsConfig.pluginUniqueId, config).then(function (result) {
Dashboard.processPluginConfigurationUpdateResult(result);
}); });
}); });

View File

@@ -12,7 +12,7 @@
<!-- <TreatWarningsAsErrors>false</TreatWarningsAsErrors> --> <!-- <TreatWarningsAsErrors>false</TreatWarningsAsErrors> -->
<Title>Jellyfin Seasonals Plugin</Title> <Title>Jellyfin Seasonals Plugin</Title>
<Authors>CodeDevMLH</Authors> <Authors>CodeDevMLH</Authors>
<Version>1.5.0.0</Version> <Version>1.6.13.1</Version>
<RepositoryUrl>https://github.com/CodeDevMLH/Jellyfin-Seasonals</RepositoryUrl> <RepositoryUrl>https://github.com/CodeDevMLH/Jellyfin-Seasonals</RepositoryUrl>
</PropertyGroup> </PropertyGroup>

View File

@@ -11,7 +11,7 @@ using Microsoft.Extensions.Logging;
namespace Jellyfin.Plugin.Seasonals; namespace Jellyfin.Plugin.Seasonals;
/// <summary> /// <summary>
/// The main plugin. /// The main plugin.
/// </summary> /// </summary>
public class SeasonalsPlugin : BasePlugin<PluginConfiguration>, IHasWebPages public class SeasonalsPlugin : BasePlugin<PluginConfiguration>, IHasWebPages
{ {

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 250 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

View File

@@ -60,6 +60,7 @@ observer.observe(document.body, {
attributes: true // observe changes to attributes (e.g. class changes) attributes: true // observe changes to attributes (e.g. class changes)
}); });
let resizeObserver; // Observer for resize events
function initializeCanvas() { function initializeCanvas() {
if (document.getElementById('snowfallCanvas')) { if (document.getElementById('snowfallCanvas')) {
@@ -78,8 +79,12 @@ function initializeCanvas() {
container.appendChild(canvas); container.appendChild(canvas);
ctx = canvas.getContext('2d'); ctx = canvas.getContext('2d');
// Initial resize
resizeCanvas(container); resizeCanvas(container);
window.addEventListener('resize', () => resizeCanvas(container));
// Initialize ResizeObserver
resizeObserver = new ResizeObserver(() => resizeCanvas(container));
resizeObserver.observe(container);
} }
function removeCanvas() { function removeCanvas() {
@@ -96,15 +101,37 @@ function removeCanvas() {
animationFrameIdSanta = null; animationFrameIdSanta = null;
console.log('Santa animation frame canceled'); console.log('Santa animation frame canceled');
} }
// Disconnect ResizeObserver
if (resizeObserver) {
resizeObserver.disconnect();
resizeObserver = null;
}
console.log('Canvas removed'); console.log('Canvas removed');
} }
} }
function resizeCanvas(container) { function resizeCanvas(container) {
if (!canvas) return; if (!canvas) return;
const oldWidth = canvas.width;
const oldHeight = canvas.height;
const rect = container.getBoundingClientRect(); const rect = container.getBoundingClientRect();
canvas.width = rect.width; canvas.width = rect.width;
canvas.height = rect.height; canvas.height = rect.height;
// Scale snowflakes positions if dimensions changed (to avoid clustering)
if (oldWidth > 0 && oldHeight > 0 && snowflakes.length > 0) {
const scaleX = canvas.width / oldWidth;
const scaleY = canvas.height / oldHeight;
snowflakes.forEach(flake => {
flake.x *= scaleX;
flake.y *= scaleY;
});
}
} }
function createSnowflakes(container) { function createSnowflakes(container) {
@@ -210,7 +237,7 @@ function reloadSantaGif() {
function animateSanta() { function animateSanta() {
const santa = document.querySelector('.santa'); const santa = document.querySelector('.santa');
function startAnimation() { function startAnimation() {
const santaHeight = santa.offsetHeight; const santaHeight = santa.offsetHeight;
if (santaHeight === 0) { if (santaHeight === 0) {

View File

@@ -1,7 +1,8 @@
// theme-configs.js /**
* Seasonals Plugin (Client Side Manager Logic)
*/
// theme configurations const ThemeConfigs = {
const themeConfigs = {
snowflakes: { snowflakes: {
css: '/Seasonals/Resources/snowflakes.css', css: '/Seasonals/Resources/snowflakes.css',
js: '/Seasonals/Resources/snowflakes.js', js: '/Seasonals/Resources/snowflakes.js',
@@ -67,140 +68,304 @@ const themeConfigs = {
}, },
}; };
// determine current theme based on the current month const SettingsManager = {
function determineCurrentTheme() { initialized: false,
const date = new Date(); config: null,
const month = date.getMonth(); // 0-11
const day = date.getDate(); // 1-31
if ((month === 11 && day >= 28) || (month === 0 && day <= 5)) return 'fireworks'; //new year fireworks december 28 - january 5 init(config) {
if (this.initialized) return;
if (month === 1 && day >= 10 && day <= 18) return 'hearts'; // valentine's day february 10 - 18 this.config = config;
if (month === 11 && day >= 22 && day <= 27) return 'santa'; // christmas december 22 - 27 // Only inject settings if enabled on server by admin
// if (month === 11 && day >= 22 && day <= 27) return 'christmas'; // christmas december 22 - 27 if (this.config && this.config.EnableClientSideToggle !== false) {
this.injectSettingsIcon();
if (month === 11) return 'snowflakes'; // snowflakes december this.initialized = true;
if (month === 0 || month === 1) return 'snowfall'; // snow january, february console.log("Seasonals: Client-Side Settings Manager initialized.");
// if (month === 0 || month === 1) return 'snowstorm'; // snow january, february }
},
if ((month === 2 && day >= 25) || (month === 3 && day <= 25)) return 'easter'; // easter march 25 - april 25
getSetting(key, defaultValue) {
//NOT IMPLEMENTED YET const value = localStorage.getItem(`seasonals-${key}`);
//if (month >= 2 && month <= 4) return 'spring'; // spring march, april, may return value !== null ? value : defaultValue;
},
//NOT IMPLEMENTED YET
//if (month >= 5 && month <= 7) return 'summer'; // summer june, july, august setSetting(key, value) {
localStorage.setItem(`seasonals-${key}`, value);
if ((month === 9 && day >= 24) || (month === 10 && day <= 5)) return 'halloween'; // halloween october 24 - november 5 },
if (month >= 8 && month <= 10) return 'autumn'; // autumn september, october, november createIcon() {
const button = document.createElement('button');
return 'none'; // Fallback (nothing) button.type = 'button';
} button.className = 'paper-icon-button-light headerButton seasonal-settings-button';
button.title = 'Seasonal Settings';
// load theme csss // button.innerHTML = '<span class="material-icons">ac_unit</span>';
function loadThemeCSS(cssPath) { // button.innerHTML = '<img src="/Seasonals/Resources/assets/logo_SW.svg" style="width: 52px; height: 24px; vertical-align: middle;">';
if (!cssPath) return; button.innerHTML = '<img src="/Seasonals/Resources/assets/logo_SW.svg" style="width: 24px; height: 24px; vertical-align: middle;">';
button.style.verticalAlign = 'middle';
const link = document.createElement('link');
link.rel = 'stylesheet'; button.addEventListener('click', (e) => {
link.href = cssPath; e.stopPropagation();
this.toggleSettingsPopup(button);
link.onerror = () => { });
console.error(`Failed to load CSS: ${cssPath}`);
}; return button;
},
document.body.appendChild(link);
console.log(`CSS file "${cssPath}" loaded.`); injectSettingsIcon() {
} const observer = new MutationObserver((mutations, obs) => {
const headerRight = document.querySelector('.headerRight');
// load theme js if (headerRight && !document.querySelector('.seasonal-settings-button')) {
function loadThemeJS(jsPath) { const icon = this.createIcon();
if (!jsPath) return; headerRight.prepend(icon);
}
const script = document.createElement('script'); });
script.src = jsPath;
script.defer = true; observer.observe(document.body, {
childList: true,
script.onerror = () => { subtree: true
console.error(`Failed to load JS: ${jsPath}`); });
}; },
document.body.appendChild(script); createPopup(anchorElement) {
console.log(`JS file "${jsPath}" loaded.`); const existing = document.querySelector('.seasonal-settings-popup');
} if (existing) existing.remove();
// update theme container class name const popup = document.createElement('div');
function updateThemeContainer(containerClass) { popup.className = 'seasonal-settings-popup dialog';
// Create container if it doesn't exist
let container = document.querySelector('.seasonals-container'); Object.assign(popup.style, {
if (!container) { position: 'fixed',
container = document.createElement('div'); zIndex: '10000',
container.className = 'seasonals-container'; backgroundColor: '#202020',
document.body.appendChild(container); padding: '1em',
} borderRadius: '0.3em',
boxShadow: '0 0 20px rgba(0,0,0,0.5)',
container.className = `seasonals-container ${containerClass}`; minWidth: '200px',
console.log(`Seasonals-Container class updated to "${containerClass}".`); color: '#fff',
} maxWidth: '250px'
});
function removeSelf() {
const script = document.currentScript; const rect = anchorElement.getBoundingClientRect();
if (script) script.parentNode.removeChild(script);
console.log('External script removed:', script); // Positioning logic
} let rightPos = window.innerWidth - rect.right;
if (window.innerWidth < 450 || (window.innerWidth - rightPos) < 260) {
// initialize theme popup.style.right = '1rem';
async function initializeTheme() { popup.style.left = 'auto';
let automateThemeSelection = true; } else {
let defaultTheme = 'none'; popup.style.right = `${rightPos}px`;
popup.style.left = 'auto';
try { }
const response = await fetch('/Seasonals/Config'); popup.style.top = `${rect.bottom + 10}px`;
if (response.ok) {
const config = await response.json(); // Popup HTML
automateThemeSelection = config.AutomateSeasonSelection; let html = `
defaultTheme = config.SelectedSeason; <h3 style="margin-top:0; margin-bottom:1em; border-bottom:1px solid #444; padding-bottom:0.5em;">Seasonal Settings</h3>
window.SeasonalsPluginConfig = config;
console.log('Seasonals Config loaded:', config); <div class="checkboxContainer checkboxContainer-withDescription" style="margin-bottom: 0.5em;">
} else { <label class="emby-checkbox-label">
console.error('Failed to fetch Seasonals config'); <input id="seasonal-enable-toggle" type="checkbox" is="emby-checkbox" class="emby-checkbox" />
<span class="checkboxLabel">Enable Seasonals</span>
</label>
</div>
<div class="selectContainer" style="margin-bottom: 0.5em;">
<label class="selectLabel" for="seasonal-theme-select" style="margin-bottom: 0.5em; display: block; color: inherit;">Force Theme</label>
<select id="seasonal-theme-select" class="emby-select" style="width: 100%; padding: 0.5em; background-color: #333; border: 1px solid #444; color: #fff; border-radius: 4px;">
<option value="auto">Server-Side</option>
</select>
</div>
`;
popup.innerHTML = html;
// Populate Select Options
const themeSelect = popup.querySelector('#seasonal-theme-select');
Object.keys(ThemeConfigs).forEach(key => {
if (key === 'none') return;
const option = document.createElement('option');
option.value = key;
option.textContent = key.charAt(0).toUpperCase() + key.slice(1);
themeSelect.appendChild(option);
});
// Set Initial Values
const enabledCheckbox = popup.querySelector('#seasonal-enable-toggle');
enabledCheckbox.checked = this.getSetting('enabled', 'true') === 'true';
themeSelect.value = this.getSetting('theme', 'auto');
// Event Listeners
enabledCheckbox.addEventListener('change', (e) => {
this.setSetting('enabled', e.target.checked);
location.reload();
});
themeSelect.addEventListener('change', (e) => {
this.setSetting('theme', e.target.value);
location.reload();
});
// Close on outside click
const closeHandler = (e) => {
if (!popup.contains(e.target) && e.target !== anchorElement && !anchorElement.contains(e.target)) {
popup.remove();
document.removeEventListener('click', closeHandler);
}
};
setTimeout(() => document.addEventListener('click', closeHandler), 0);
document.body.appendChild(popup);
},
toggleSettingsPopup(anchorElement) {
const existing = document.querySelector('.seasonal-settings-popup');
if (existing) {
existing.remove();
} else {
this.createPopup(anchorElement);
} }
} catch (error) {
console.error('Error fetching Seasonals config:', error);
} }
};
let currentTheme; const SeasonalsManager = {
if (automateThemeSelection === false) { config: null,
currentTheme = defaultTheme;
} else {
currentTheme = determineCurrentTheme();
}
console.log(`Selected theme: ${currentTheme}`); async init() {
// Fetch Config
try {
const response = await fetch('/Seasonals/Config');
if (response.ok) {
this.config = await response.json();
window.SeasonalsPluginConfig = this.config;
console.log('Seasonals Config loaded:', this.config);
}
} catch (error) {
console.error('Error fetching Seasonals config:', error);
}
if (!currentTheme || currentTheme === 'none') { // Initialize Settings UI
console.log('No theme selected.'); SettingsManager.init(this.config);
removeSelf();
return;
}
const theme = themeConfigs[currentTheme]; // User Preference Check
const isEnabled = SettingsManager.getSetting('enabled', 'true') === 'true';
if (!isEnabled) {
console.log('Seasonals disabled by user preference.');
return;
}
// Determine Theme
const themeName = this.selectTheme();
console.log(`Selected theme: ${themeName}`);
if (!themeName || themeName === 'none') {
return;
}
// Apply Theme
this.applyTheme(themeName);
},
selectTheme() {
// Check local override
const forcedTheme = SettingsManager.getSetting('theme', 'auto');
if (forcedTheme !== 'auto') {
console.log(`User forced theme: ${forcedTheme}`);
return forcedTheme;
}
const automate = this.config ? this.config.AutomateSeasonSelection : true;
const defaultTheme = this.config ? this.config.SelectedSeason : 'none';
if (!automate) {
return defaultTheme;
}
return this.determineCurrentThemeDate();
},
determineCurrentThemeDate() {
const date = new Date();
const month = date.getMonth(); // 0-11
const day = date.getDate(); // 1-31
if (!theme) { if ((month === 11 && day >= 28) || (month === 0 && day <= 5)) return 'fireworks'; //new year fireworks december 28 - january 5
console.error(`Theme "${currentTheme}" not found.`);
return; if (month === 1 && day >= 10 && day <= 18) return 'hearts'; // valentine's day february 10 - 18
if (month === 11 && day >= 22 && day <= 27) return 'santa'; // santa december 22 - 27
// if (month === 11 && day >= 22 && day <= 27) return 'christmas'; // christmas december 22 - 27
if (month === 11) return 'snowflakes'; // snowflakes december
if (month === 0 || month === 1) return 'snowfall'; // snow january, february
// if (month === 0 || month === 1) return 'snowstorm'; // snow january, february
if ((month === 2 && day >= 25) || (month === 3 && day <= 25)) return 'easter'; // easter march 25 - april 25
//NOT IMPLEMENTED YET
//if (month >= 2 && month <= 4) return 'spring'; // spring march, april, may
//NOT IMPLEMENTED YET
//if (month >= 5 && month <= 7) return 'summer'; // summer june, july, august
if ((month === 9 && day >= 24) || (month === 10 && day <= 5)) return 'halloween'; // halloween october 24 - november 5
if (month >= 8 && month <= 10) return 'autumn'; // autumn september, october, november
return 'none'; // Fallback (no theme)
},
applyTheme(themeName) {
const theme = ThemeConfigs[themeName];
if (!theme) {
console.error(`Theme "${themeName}" not found.`);
return;
}
this.updateThemeContainer(theme.containerClass);
if (theme.css) this.loadResource('css', theme.css);
if (theme.js) this.loadResource('js', theme.js);
console.log(`Theme "${themeName}" applied.`);
},
updateThemeContainer(containerClass) {
let container = document.querySelector('.seasonals-container');
if (!container) {
container = document.createElement('div');
container.className = 'seasonals-container';
document.body.appendChild(container);
}
container.className = `seasonals-container ${containerClass}`;
},
// helper to resolve paths for local testing vs production
resolvePath(path) {
if (window.location.protocol === 'file:' || window.location.hostname === 'localhost' || window.location.hostname === '127.0.0.1') {
return path.replace('/Seasonals/Resources/', './');
}
return path;
},
loadResource(type, path) {
if (!path) return;
if (type === 'css') {
const link = document.createElement('link');
link.rel = 'stylesheet';
link.href = path;
// link.href = resolvePath(cssPath);
link.onerror = () => console.error(`Failed to load CSS: ${path}`);
document.body.appendChild(link);
} else if (type === 'js') {
const script = document.createElement('script');
script.src = path;
// script.src = resolvePath(jsPath);
script.defer = true;
script.onerror = () => console.error(`Failed to load JS: ${path}`);
document.body.appendChild(script);
}
} }
updateThemeContainer(theme.containerClass); };
if (theme.css) loadThemeCSS(theme.css); SeasonalsManager.init();
if (theme.js) loadThemeJS(theme.js);
console.log(`Theme "${currentTheme}" loaded.`);
removeSelf();
}
initializeTheme();

View File

@@ -55,6 +55,7 @@ observer.observe(document.body, {
attributes: true // observe changes to attributes (e.g. class changes) attributes: true // observe changes to attributes (e.g. class changes)
}); });
let resizeObserver; // Observer for resize events
function initializeCanvas() { function initializeCanvas() {
if (document.getElementById('snowfallCanvas')) { if (document.getElementById('snowfallCanvas')) {
@@ -73,8 +74,12 @@ function initializeCanvas() {
container.appendChild(canvas); container.appendChild(canvas);
ctx = canvas.getContext('2d'); ctx = canvas.getContext('2d');
// Initial resize
resizeCanvas(container); resizeCanvas(container);
window.addEventListener('resize', () => resizeCanvas(container));
// Initialize ResizeObserver
resizeObserver = new ResizeObserver(() => resizeCanvas(container));
resizeObserver.observe(container);
} }
function removeCanvas() { function removeCanvas() {
@@ -86,15 +91,37 @@ function removeCanvas() {
animationFrameId = null; animationFrameId = null;
console.log('Animation frame canceled'); console.log('Animation frame canceled');
} }
// Disconnect ResizeObserver
if (resizeObserver) {
resizeObserver.disconnect();
resizeObserver = null;
}
console.log('Canvas removed'); console.log('Canvas removed');
} }
} }
function resizeCanvas(container) { function resizeCanvas(container) {
if (!canvas) return; if (!canvas) return;
const oldWidth = canvas.width;
const oldHeight = canvas.height;
const rect = container.getBoundingClientRect(); const rect = container.getBoundingClientRect();
canvas.width = rect.width; canvas.width = rect.width;
canvas.height = rect.height; canvas.height = rect.height;
// Scale snowflakes positions if dimensions changed (to avoid clustering)
if (oldWidth > 0 && oldHeight > 0 && snowflakes.length > 0) {
const scaleX = canvas.width / oldWidth;
const scaleY = canvas.height / oldHeight;
snowflakes.forEach(flake => {
flake.x *= scaleX;
flake.y *= scaleY;
});
}
} }
function createSnowflakes(container) { function createSnowflakes(container) {

View File

@@ -57,6 +57,7 @@ observer.observe(document.body, {
attributes: true // observe changes to attributes (e.g. class changes) attributes: true // observe changes to attributes (e.g. class changes)
}); });
let resizeObserver; // Observer for resize events
function initializeCanvas() { function initializeCanvas() {
if (document.getElementById('snowfallCanvas')) { if (document.getElementById('snowfallCanvas')) {
@@ -75,8 +76,12 @@ function initializeCanvas() {
container.appendChild(canvas); container.appendChild(canvas);
ctx = canvas.getContext('2d'); ctx = canvas.getContext('2d');
// Initial resize
resizeCanvas(container); resizeCanvas(container);
window.addEventListener('resize', () => resizeCanvas(container));
// Initialize ResizeObserver
resizeObserver = new ResizeObserver(() => resizeCanvas(container));
resizeObserver.observe(container);
} }
function removeCanvas() { function removeCanvas() {
@@ -88,15 +93,37 @@ function removeCanvas() {
animationFrameId = null; animationFrameId = null;
console.log('Animation frame canceled'); console.log('Animation frame canceled');
} }
// Disconnect ResizeObserver
if (resizeObserver) {
resizeObserver.disconnect();
resizeObserver = null;
}
console.log('Canvas removed'); console.log('Canvas removed');
} }
} }
function resizeCanvas(container) { function resizeCanvas(container) {
if (!canvas) return; if (!canvas) return;
const oldWidth = canvas.width;
const oldHeight = canvas.height;
const rect = container.getBoundingClientRect(); const rect = container.getBoundingClientRect();
canvas.width = rect.width; canvas.width = rect.width;
canvas.height = rect.height; canvas.height = rect.height;
// Scale snowflakes positions if dimensions changed (to avoid clustering)
if (oldWidth > 0 && oldHeight > 0 && snowflakes.length > 0) {
const scaleX = canvas.width / oldWidth;
const scaleY = canvas.height / oldHeight;
snowflakes.forEach(flake => {
flake.x *= scaleX;
flake.y *= scaleY;
});
}
} }
function createSnowflakes(container) { function createSnowflakes(container) {

View File

@@ -0,0 +1,79 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Seasonals Test Display</title>
<link rel="stylesheet" href="snowfall.css">
<style>
body {
background-color: black;
color: white;
margin: 0;
font-family: sans-serif;
}
/* Mock Jellyfin Header */
.skinHeader {
background-color: #101010;
height: 60px;
display: flex;
align-items: center;
padding: 0 1em;
border-bottom: 1px solid #333;
}
.headerRight {
margin-left: auto;
display: flex;
align-items: center;
}
.paper-icon-button-light {
background: none;
border: none;
color: white;
cursor: pointer;
padding: 10px;
}
.material-icons {
font-family: 'Material Icons';
/* Placeholder if not loaded */
font-weight: normal;
font-style: normal;
font-size: 24px;
display: inline-block;
line-height: 1;
text-transform: none;
letter-spacing: normal;
word-wrap: normal;
white-space: nowrap;
direction: ltr;
}
</style>
<!-- Load Material Icons for the snowflake -->
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
</head>
<body>
<!-- Mock Header Structure -->
<div class="skinHeader">
<div class="skinHeader-content">
<span>Jellyfin Mock Header</span>
</div>
<div class="headerRight">
<button class="paper-icon-button-light">
<span class="material-icons">person</span>
</button>
</div>
</div>
<div class="seasonals-container"></div>
<!-- Load the main script -->
<script src="seasonals.js"></script>
</body>
</html>

188
README.md
View File

@@ -13,20 +13,17 @@ This plugin is based on my manual mod (see the [legacy branch](https://github.co
- [Table of Contents](#table-of-contents) - [Table of Contents](#table-of-contents)
- [Features](#features) - [Features](#features)
- [Overview](#overview) - [Overview](#overview)
- [Ideas for additional seasonals](#ideas-for-additional-seasonals)
- [Installation](#installation) - [Installation](#installation)
- [Client Compatibility](#client-compatibility) - [Client Compatibility](#client-compatibility)
- [Configuration](#configuration) - [Configuration](#configuration)
- [Automatic Theme Selection](#automatic-theme-selection) - [Automatic Theme Selection](#automatic-theme-selection)
- [Theme Settings](#theme-settings) - [Theme Settings](#theme-settings)
- [Build The Plugin By Yourself](#build-the-plugin-by-yourself) - [Build the plugin by yourself](#build-the-plugin-by-yourself)
- [Troubleshooting](#troubleshooting) - [Troubleshooting](#troubleshooting)
- [Effects Not Showing](#effects-not-showing) - [Effects Not Showing](#effects-not-showing)
- [Docker Permission Issues](#docker-permission-issues) - [Docker Permission Issues](#docker-permission-issues)
- [Contributing](#contributing) - [Contributing](#contributing)
- [Legacy Manual Installation](#legacy-manual-installation)
- [Installation](#installation-1)
- [Usage](#usage)
- [Additional Directory: Separate Single Seasonals](#additional-directory-separate-single-seasonals)
--- ---
@@ -34,39 +31,83 @@ This plugin is based on my manual mod (see the [legacy branch](https://github.co
- **Automatic Theme Selection**: Dynamically updates the theme based on the date (e.g., snowflakes in December, fireworks for new year's eve). - **Automatic Theme Selection**: Dynamically updates the theme based on the date (e.g., snowflakes in December, fireworks for new year's eve).
- **Easy Integration**: No manual file editing required. The plugin injects everything automatically. - **Easy Integration**: No manual file editing required. The plugin injects everything automatically.
- **Configuration UI**: Configure settings directly in the Jellyfin Dashboard (very basic for now, needs some work in the future). - **Configuration UI**: Configure settings directly in the Jellyfin Dashboard.
<details>
<summary>Have a look:</summary>
<img width="852" height="782" alt="Admin-Settings" src="https://github.com/user-attachments/assets/03d04ea8-7dd9-418e-88f8-9ae2937c06bb" />
</details>
- **User Toggle**: Optionally allow users to enable/disable seasonal effects from their client.
<details>
<summary>Have a look:</summary>
<img width="467" height="263" alt="Client-Settings" src="https://github.com/user-attachments/assets/a8dfc90a-16c8-409c-9133-4139f6527b0b" />
</details>
## Overview ## Overview
Click on the following themes to expand them and see the theme in action:
<details>
<summary>Easter</summary>
**Easter**
![easter](https://github.com/user-attachments/assets/63665099-5c3c-4209-be6e-dda3686b2a49) ![easter](https://github.com/user-attachments/assets/63665099-5c3c-4209-be6e-dda3686b2a49)
</details>
<details>
<summary>Autumn</summary>
**Autumn**
![autumn](https://github.com/user-attachments/assets/df27d61c-d2d6-4776-82d7-89bf789a7462) ![autumn](https://github.com/user-attachments/assets/df27d61c-d2d6-4776-82d7-89bf789a7462)
</details>
<details>
<summary>Santa</summary>
**Santa**
![Santa-10](https://github.com/user-attachments/assets/a69b0aa3-537d-4463-b6bc-166f0a316c37) ![Santa-10](https://github.com/user-attachments/assets/a69b0aa3-537d-4463-b6bc-166f0a316c37)
</details>
<details>
<summary>Christmas</summary>
**Christmas**
![christmas](https://github.com/user-attachments/assets/e70a425d-866f-4617-bbfe-3c03e3654717) ![christmas](https://github.com/user-attachments/assets/e70a425d-866f-4617-bbfe-3c03e3654717)
</details>
<details>
<summary>Fireworks</summary>
**Fireworks**
![fireworks](https://github.com/user-attachments/assets/6c8b629e-b338-4dde-910b-c832aa29d77d) ![fireworks](https://github.com/user-attachments/assets/6c8b629e-b338-4dde-910b-c832aa29d77d)
</details>
<details>
<summary>Halloween</summary>
**Halloween**
![halloween](https://github.com/user-attachments/assets/221a1390-847e-45a4-b8eb-dc5b45d5df5c) ![halloween](https://github.com/user-attachments/assets/221a1390-847e-45a4-b8eb-dc5b45d5df5c)
</details>
<details>
<summary>Hearts</summary>
**Hearts**
![hearts](https://github.com/user-attachments/assets/e084cb0c-246e-4234-b6dd-d561923c6c91) ![hearts](https://github.com/user-attachments/assets/e084cb0c-246e-4234-b6dd-d561923c6c91)
</details>
<details>
<summary>Snowfall</summary>
**Snowfall**
![snowfall](https://github.com/user-attachments/assets/24bfdd84-f354-4129-933c-bb29b4180517) ![snowfall](https://github.com/user-attachments/assets/24bfdd84-f354-4129-933c-bb29b4180517)
</details>
<details>
<summary>Snowflakes</summary>
**Snowflakes**
![snowflakes](https://github.com/user-attachments/assets/78f2e925-8cf6-4a0b-8a25-f05594de4efd) ![snowflakes](https://github.com/user-attachments/assets/78f2e925-8cf6-4a0b-8a25-f05594de4efd)
</details>
<details>
<summary>Snowstorm</summary>
**Snowstorm**
![snowstorm](https://github.com/user-attachments/assets/6fd726d2-34d1-4f80-8ed6-2f482155059f) ![snowstorm](https://github.com/user-attachments/assets/6fd726d2-34d1-4f80-8ed6-2f482155059f)
</details>
## Ideas for additional seasonals
If you have any (specific) ideas for additional seasonals, feel free to open an issue or create a pull request.
## Installation ## Installation
@@ -135,7 +176,7 @@ If automatic selection is enabled, the following themes are applied based on the
## Theme Settings ## Theme Settings
Each theme contains additional settings to customize its behavior. Expand the advanced configuration section to configure each theme, adjust parameters like particle count, animation speed etc. Each theme contains additional settings to customize its behavior. Expand the advanced configuration section to configure each theme, adjust parameters like particle count, animation speed etc.
## Build The Plugin By Yourself ## Build the plugin by yourself
If you want to build the plugin yourself: If you want to build the plugin yourself:
@@ -203,116 +244,3 @@ volumes:
Feel free to contribute to this project by creating pull requests or reporting issues. Feel free to contribute to this project by creating pull requests or reporting issues.
---
## Legacy Manual Installation
<details>
<summary>Click to expand instructions for the old manual installation method (without plugin)</summary>
### Installation
> [!TIP]
> Take a look at [CodeDevMLH/Jellyfin-Mods-Automated-Script](https://github.com/CodeDevMLH/Jellyfin-Mods-Automated-Script)
1. **Add Seasonal Container to `index.html`**
Edit the `index.html` file of your Jellyfin web instance. Add the following code inside the `<body>` tag:
```html
<div class="seasonals-container"></div>
<script src="seasonals/seasonals.js"></script>
```
2. **Deploy Files**
Place the seasonals folder (including seasonals.js, CSS, and additional JavaScript files for each theme [this one](https://github.com/CodeDevMLH/Jellyfin-Seasonals/tree/legacy/seasonals)) inside the Jellyfin web server directory (labeld with "web").
3. **Configure Themes**
Customize the theme-configs.js file to modify or add new themes. The default configuration is shown below:
```javascript
const automateThemeSelection = true; // Set to false to disable automatic theme selection based on current date
const defaultTheme = 'none'; // Default theme if automatic selection is off
const themeConfigs = {
snowflakes: {
css: 'seasonals/snowflakes.css',
js: 'seasonals/snowflakes.js',
containerClass: 'snowflakes'
},
snowfall: {
css: 'seasonals/snowfall.css',
js: 'seasonals/snowfall.js',
containerClass: 'snowfall-container'
},
// more configs...
none: {
containerClass: 'none'
},
};
```
4. **Reload the web interface**
After making these changes, restart your Jellyfin server and/or refresh the web interface (ctrl+F5, sometimes you need to clear the browsers temp files/cache (every time with firefox ;-()) to see the changes.
### Theme Settings
Each theme's JavaScript file contains additional settings to customize its behavior. Here are examples for the `autumn` and `snowflakes` themes:
**Autumn Theme Settings**
```javascript
const leaves = true; // Enable/disable leaves
const randomLeaves = true; // Enable random leaves
const randomLeavesMobile = false; // Enable random leaves on mobile devices
const enableDiffrentDuration = true; // Enable different animation duration for random leaves
const leafCount = 25; // Number of random extra leaves
```
**Snowflakes Theme Settings**
```javascript
const snowflakes = true; // Enable/disable snowflakes
const randomSnowflakes = true; // Enable random snowflakes
const randomSnowflakesMobile = false; // Enable random snowflakes on mobile devices
const enableColoredSnowflakes = true; // Enable colored snowflakes on mobile devices
const enableDiffrentDuration = true; // Enable different animation duration for random snowflakes
const snowflakeCount = 25; // Number of random extra snowflakes
```
### Usage
**Automatic Theme Selection**
By default, the theme is automatically selected based on the date. For example:
Snowfall: January
Hearts: February (Valentine's Day)
Halloween: October
Modify the determineCurrentTheme() function in seasonals.js to adjust date-based logic.
**Manual Theme Selection**
To use a fixed theme, set automateThemeSelection to false in the theme-configs.js file and specify a defaultTheme.
**Custom Themes**
1. Add your CSS and JavaScript files to the seasonals folder.
2. Extend the themeConfigs object with your theme details:
```javascript
myTheme: {
css: 'seasonals/my-theme.css',
js: 'seasonals/my-theme.js',
containerClass: 'my-theme-container',
}
```
### Additional Directory: Separate Single Seasonals
For users who prefer not to use the automatic seasonal theme selection, individual seasonals are available in the `separate single seasonals` folder. Each seasonal theme can be independently loaded and used without relying on the main automatic selection system.
but this requires to the modify of the `index.html` with adding the html in `add_to_index_html`.
To use a single seasonal theme, include its specific CSS and JS files in your `index.html` inside the `<body> </body>` tags provided by `add_to_index_html.html` in the sub-theme-folders as shown below:
```html
<div class="seasonalsname-container"></div>
<script src="separate single seasonals/snowflakes.js"></script>
<link rel="stylesheet" href="separate single seasonals/snowflakes.css">
</details>

View File

@@ -1,15 +0,0 @@
---
name: "Seasonals"
guid: "ef1e863f-cbb0-4e47-9f23-f0cbb1826ad4"
version: "1.3.4.0"
targetAbi: "10.11.0.0"
framework: "net9.0"
overview: "Seasonal effects for Jellyfin"
description: >
Adds seasonal effects like snow, leaves, etc. to the Jellyfin web interface.
category: "General"
owner: "CodeDevMLH"
artifacts:
- "Jellyfin.Plugin.Seasonals.dll"
changelog: >
Added Advanced Configuration UI for customizing individual seasonal effects.

View File

@@ -26,8 +26,11 @@ git merge main
Setzt deine Commits neu auf die Spitze eines anderen Branches. Die Commit-IDs werden dabei neu geschrieben. Setzt deine Commits neu auf die Spitze eines anderen Branches. Die Commit-IDs werden dabei neu geschrieben.
```bash ```bash
git stash # (optional) Änderungen parken.
git checkout dev git checkout dev
git rebase main git fetch origin
git rebase origin/main
git stash pop # (optional) Änderungen zurückholen.
``` ```
| Details | | | Details | |
@@ -38,6 +41,60 @@ git rebase main
--- ---
## **Git: dev zurücksetzen & „main ist Chef“**
Problem
Beim Arbeiten mit Git passiert oft Folgendes:
- `dev` hat Commits, die nicht in `main` sind
- diese Commits brauche ich nicht mehr
- beim `git rebase origin/main` will Git trotzdem mergen oder Konflikte lösen
⚠️ Wichtiges Missverständnis:
Das sind keine lokalen Änderungen, sondern Commits, die auf `dev` existieren.
`git reset --hard origin/dev` entfernt nur lokale, nicht gepushte Commits,
aber nicht Commits, die bereits auf `origin/dev` liegen.
Praktische Befehle & sichere Vorgehensweise
- Prüfen — welche Commits würden entfernt werden:
```bash
git fetch origin
git log --oneline origin/main..origin/dev
```
- Optional: Backup des aktuellen `origin/dev`, falls etwas schiefgeht:
```bash
git fetch origin
git branch backup/dev origin/dev
```
- Sicheres Zurücksetzen von `dev` auf `main` (lokal + remote):
```bash
git checkout dev
git fetch origin
git reset --hard origin/main
git push --force-with-lease origin dev
```
- Alternative (ohne lokalen Checkout): Push von `main` direkt nach `dev`:
```bash
git fetch origin
git push --force-with-lease origin origin/main:refs/heads/dev
```
- Warnung: Diese Aktionen schreiben die RemoteHistory um. Koordiniere dich mit
dem Team bevor du ein ForcePush ausführst. Verwende `--force-with-lease`
anstelle von `--force` für etwas mehr Sicherheit.
## 📦 Temporäres Speichern & Spezialbefehle ## 📦 Temporäres Speichern & Spezialbefehle
### Stash (Das "Regal") ### Stash (Das "Regal")

View File

@@ -2,19 +2,35 @@
{ {
"guid": "ef1e863f-cbb0-4e47-9f23-f0cbb1826ad4", "guid": "ef1e863f-cbb0-4e47-9f23-f0cbb1826ad4",
"name": "Seasonals", "name": "Seasonals",
"description": "Adds seasonal effects like snow, leaves, etc. to the Jellyfin web interface.", "description": "Adds atmospheric, animated seasonal effects like snow, falling leaves, hearts, fireworks and more. The plugin provides a settings page for configuration. If you do not have write permissions to the web folder, please also install the file-transformation plugin.",
"overview": "Seasonal effects for Jellyfin", "overview": "Atmospheric, configurable seasonal effects for the Jellyfin web interface",
"owner": "CodeDevMLH", "owner": "CodeDevMLH",
"category": "General", "category": "General",
"imageUrl": "https://git.mahom03-spacecloud.de/CodeDevMLH/Jellyfin-Seasonals-Plugin/raw/branch/main/logo.png", "imageUrl": "https://git.mahom03-spacecloud.de/CodeDevMLH/Jellyfin-Seasonals-Plugin/raw/branch/main/logo.png",
"versions": [ "versions": [
{
"version": "1.6.13.1",
"changelog": "- feat: Add client-side toggle option for seasonal settings",
"targetAbi": "10.11.0.0",
"sourceUrl": "https://git.mahom03-spacecloud.de/CodeDevMLH/Jellyfin-Seasonals-Plugin/releases/download/v1.6.13.1/Jellyfin.Plugin.Seasonals.zip",
"checksum": "73177840d232ea06a0b621b901770d4c",
"timestamp": "2026-02-04T12:39:13Z"
},
{
"version": "1.5.1.0",
"changelog": "- fix: snowfall effect sometimes not scaling on window resize, which leads to clustering and rain effect of snowflakes",
"targetAbi": "10.11.0.0",
"sourceUrl": "https://git.mahom03-spacecloud.de/CodeDevMLH/Jellyfin-Seasonals-Plugin/releases/download/v1.5.1.0/Jellyfin.Plugin.Seasonals.zip",
"checksum": "a66b5c24c1f4bfff14bba5e93576bb80",
"timestamp": "2026-01-28T22:40:49Z"
},
{ {
"version": "1.5.0.0", "version": "1.5.0.0",
"changelog": "- Refactor SeasonalsPlugin: Simplify script injection logic", "changelog": "- Refactor script injection logic",
"targetAbi": "10.11.0.0", "targetAbi": "10.11.0.0",
"sourceUrl": "https://git.mahom03-spacecloud.de/CodeDevMLH/Jellyfin-Seasonals-Plugin/releases/download/v1.5.0.0/Jellyfin.Plugin.Seasonals.zip", "sourceUrl": "https://git.mahom03-spacecloud.de/CodeDevMLH/Jellyfin-Seasonals-Plugin/releases/download/v1.5.0.0/Jellyfin.Plugin.Seasonals.zip",
"checksum": "fa174c764164b0524fb2e8f11ffbcace", "checksum": "ba8d3c358df3e0546b99113b43f54fea",
"timestamp": "2026-01-06T22:21:49Z" "timestamp": "2026-01-08T23:31:52Z"
}, },
{ {
"version": "1.4.1.0", "version": "1.4.1.0",
@@ -85,12 +101,164 @@
{ {
"guid": "d7e11d57-819b-4bdd-a88d-53c5f5560225", "guid": "d7e11d57-819b-4bdd-a88d-53c5f5560225",
"name": "Media Bar Enhanced", "name": "Media Bar Enhanced",
"description": "A jellyfin plugin to display a media bar (featured content) for jellyfin web.", "description": "A feature-rich fork of the original Media Bar script by MakD that brings your home screen to life.\n\n-> 100% Configurable via Web UI: Manage all features, lists, and settings effortlessly through the plugin configuration page.\n\nKey Highlights:\n- Cinematic Video Backdrops: Supports local & YouTube trailers (incl. SponsorBlock)\n- Custom Content: Curate your slideshow with specific Collections, Playlists, or seasonal events\n\nAdditional Features:\n- Full-width immersive mode\n- Smart resolution handling (up to 4K)\n- Full keyboard navigation & playback control\n- Wait-for-trailer options\n- Customizable pagination & animations\n\nIf you do not have write permissions to the web folder, please also install the file-transformation plugin.",
"overview": "Media Bar for Jellyfin", "overview": "Transforms your Jellyfin home screen with an immersive, fully configurable media slideshow featuring video backdrops.",
"owner": "CodeDevMLH", "owner": "CodeDevMLH",
"category": "General", "category": "General",
"imageUrl": "https://git.mahom03-spacecloud.de/CodeDevMLH/jellyfin-plugin-media-bar-enhanced/raw/branch/main/logo.png", "imageUrl": "https://git.mahom03-spacecloud.de/CodeDevMLH/jellyfin-plugin-media-bar-enhanced/raw/branch/main/logo.png",
"versions": [ "versions": [
{
"version": "1.3.0.3",
"changelog": "- feat: Enhance custom media ID functionality with manual trailer override support",
"targetAbi": "10.11.0.0",
"sourceUrl": "https://git.mahom03-spacecloud.de/CodeDevMLH/jellyfin-plugin-media-bar-enhanced/releases/download/v1.3.0.3/Jellyfin.Plugin.MediaBarEnhanced.zip",
"checksum": "1d9e0a8342d46f84aed3f7bd1bee32d3",
"timestamp": "2026-02-04T01:41:35Z"
},
{
"version": "1.3.0.2",
"changelog": "- feat: Enhance custom media ID functionality with manual trailer override support",
"targetAbi": "10.11.0.0",
"sourceUrl": "https://git.mahom03-spacecloud.de/CodeDevMLH/jellyfin-plugin-media-bar-enhanced/releases/download/v1.3.0.2/Jellyfin.Plugin.MediaBarEnhanced.zip",
"checksum": "22e79daa5f433ca09a3db4f8e37679b4",
"timestamp": "2026-02-04T01:27:55Z"
},
{
"version": "1.3.0.1",
"changelog": "- feat: Enhance custom media ID functionality with manual trailer override support",
"targetAbi": "10.11.0.0",
"sourceUrl": "https://git.mahom03-spacecloud.de/CodeDevMLH/jellyfin-plugin-media-bar-enhanced/releases/download/v1.3.0.1/Jellyfin.Plugin.MediaBarEnhanced.zip",
"checksum": "5a4f555e29c733dabd51169f6ace56eb",
"timestamp": "2026-02-04T01:14:19Z"
},
{
"version": "1.3.0.0",
"changelog": "- feat: Enhance custom media ID functionality with manual trailer override support",
"targetAbi": "10.11.0.0",
"sourceUrl": "https://git.mahom03-spacecloud.de/CodeDevMLH/jellyfin-plugin-media-bar-enhanced/releases/download/v1.3.0.0/Jellyfin.Plugin.MediaBarEnhanced.zip",
"checksum": "83c26ba8f7ad6e1a7fe73c7190f532f3",
"timestamp": "2026-02-04T00:07:15Z"
},
{
"version": "1.2.3.7",
"changelog": "- Fixes the issue where buttons were cut off on smaller screens such as on S24/S25.\n- 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.2.3.7/Jellyfin.Plugin.MediaBarEnhanced.zip",
"checksum": "fa1bf48cff159cc7dbf0aab48511a37c",
"timestamp": "2026-01-28T22:39:54Z"
},
{
"version": "1.2.3.6",
"changelog": "- Fixes the issue where buttons were cut off on smaller screens such as on S24/S25.",
"targetAbi": "10.11.0.0",
"sourceUrl": "https://git.mahom03-spacecloud.de/CodeDevMLH/jellyfin-plugin-media-bar-enhanced/releases/download/v1.2.3.6/Jellyfin.Plugin.MediaBarEnhanced.zip",
"checksum": "da73bb490548c122906419d2762a2d00",
"timestamp": "2026-01-28T21:31:54Z"
},
{
"version": "1.2.3.5",
"changelog": "- Fixes the issue where buttons were cut off on smaller screens such as on S24/S25.",
"targetAbi": "10.11.0.0",
"sourceUrl": "https://git.mahom03-spacecloud.de/CodeDevMLH/jellyfin-plugin-media-bar-enhanced/releases/download/v1.2.3.5/Jellyfin.Plugin.MediaBarEnhanced.zip",
"checksum": "b5efea79ec465522dad31e4ee5f1710c",
"timestamp": "2026-01-28T20:21:20Z"
},
{
"version": "1.2.3.4",
"changelog": "- Fixes the issue where buttons were cut off on smaller screens such as on S24/S25.",
"targetAbi": "10.11.0.0",
"sourceUrl": "https://git.mahom03-spacecloud.de/CodeDevMLH/jellyfin-plugin-media-bar-enhanced/releases/download/v1.2.3.4/Jellyfin.Plugin.MediaBarEnhanced.zip",
"checksum": "4683f75e2df2590db663303bdd329ccd",
"timestamp": "2026-01-28T01:09:38Z"
},
{
"version": "1.2.3.3",
"changelog": "- Fixes the issue where buttons were cut off on smaller screens such as on S24/S25.",
"targetAbi": "10.11.0.0",
"sourceUrl": "https://git.mahom03-spacecloud.de/CodeDevMLH/jellyfin-plugin-media-bar-enhanced/releases/download/v1.2.3.3/Jellyfin.Plugin.MediaBarEnhanced.zip",
"checksum": "3c18a84b2f59c86c130e91da83f980a2",
"timestamp": "2026-01-28T01:05:45Z"
},
{
"version": "1.2.3.2",
"changelog": "- Fixes the issue where buttons were cut off on smaller screens such as on S24/S25.",
"targetAbi": "10.11.0.0",
"sourceUrl": "https://git.mahom03-spacecloud.de/CodeDevMLH/jellyfin-plugin-media-bar-enhanced/releases/download/v1.2.3.2/Jellyfin.Plugin.MediaBarEnhanced.zip",
"checksum": "4109a3ea10eb3145217b24ee8f8b37b5",
"timestamp": "2026-01-28T00:30:36Z"
},
{
"version": "1.2.3.1",
"changelog": "- Fixes the issue where buttons were cut off on smaller screens such as on S24/S25.",
"targetAbi": "10.11.0.0",
"sourceUrl": "https://git.mahom03-spacecloud.de/CodeDevMLH/jellyfin-plugin-media-bar-enhanced/releases/download/v1.2.3.1/Jellyfin.Plugin.MediaBarEnhanced.zip",
"checksum": "e73029ac767e24d36742a27678758b6f",
"timestamp": "2026-01-28T00:17:28Z"
},
{
"version": "1.2.3.0",
"changelog": "- Fixes the issue where buttons were cut off on smaller screens such as on S24/S25.",
"targetAbi": "10.11.0.0",
"sourceUrl": "https://git.mahom03-spacecloud.de/CodeDevMLH/jellyfin-plugin-media-bar-enhanced/releases/download/v1.2.3.0/Jellyfin.Plugin.MediaBarEnhanced.zip",
"checksum": "ae6f34bee76f9d7873964a71ca191bf3",
"timestamp": "2026-01-27T23:54:42Z"
},
{
"version": "1.2.2.0",
"changelog": "- Fixes issues with persistent slides-container visibility",
"targetAbi": "10.11.0.0",
"sourceUrl": "https://git.mahom03-spacecloud.de/CodeDevMLH/jellyfin-plugin-media-bar-enhanced/releases/download/v1.2.2.0/Jellyfin.Plugin.MediaBarEnhanced.zip",
"checksum": "3362f93815845c4e85b66b31bcd0f52c",
"timestamp": "2026-01-24T22:53:55Z"
},
{
"version": "1.2.1.0",
"changelog": "- Update mediaBarEnhanced.js and mediaBarEnhanced.css with version 3.0.8 from original repo",
"targetAbi": "10.11.0.0",
"sourceUrl": "https://git.mahom03-spacecloud.de/CodeDevMLH/jellyfin-plugin-media-bar-enhanced/releases/download/v1.2.1.0/Jellyfin.Plugin.MediaBarEnhanced.zip",
"checksum": "70defc1fb29a17ff4c9362bf7bdc53b5",
"timestamp": "2026-01-22T23:50:56Z"
},
{
"version": "1.2.0.0",
"changelog": "- Add video quality preference setting (Auto / 1080p / Highres)\n- Set preferred video quality on YouTube player based on setting",
"targetAbi": "10.11.0.0",
"sourceUrl": "https://git.mahom03-spacecloud.de/CodeDevMLH/jellyfin-plugin-media-bar-enhanced/releases/download/v1.2.0.0/Jellyfin.Plugin.MediaBarEnhanced.zip",
"checksum": "0b6379f68990026240d97fe8f77fbef1",
"timestamp": "2026-01-08T23:30:58Z"
},
{
"version": "1.1.2.0",
"changelog": "- Add method to resume video playback when slideshow is active",
"targetAbi": "10.11.0.0",
"sourceUrl": "https://git.mahom03-spacecloud.de/CodeDevMLH/jellyfin-plugin-media-bar-enhanced/releases/download/v1.1.2.0/Jellyfin.Plugin.MediaBarEnhanced.zip",
"checksum": "a0e8ff5e59b22a1bdedc916cd5e1c16a",
"timestamp": "2026-01-08T15:26:55Z"
},
{
"version": "1.1.1.0",
"changelog": "- Add method to pause all video playback when navigating away from home screen",
"targetAbi": "10.11.0.0",
"sourceUrl": "https://git.mahom03-spacecloud.de/CodeDevMLH/jellyfin-plugin-media-bar-enhanced/releases/download/v1.1.1.0/Jellyfin.Plugin.MediaBarEnhanced.zip",
"checksum": "09da95fc561b11191d23a5cfa30ea731",
"timestamp": "2026-01-08T14:54:57Z"
},
{
"version": "1.1.0.0",
"changelog": "- 'custom media IDs' setting is now enabled by default (no input --> random selection)\n- improve GUID handling in slideshow manager to handle seperator and description",
"targetAbi": "10.11.0.0",
"sourceUrl": "https://git.mahom03-spacecloud.de/CodeDevMLH/jellyfin-plugin-media-bar-enhanced/releases/download/v1.1.0.0/Jellyfin.Plugin.MediaBarEnhanced.zip",
"checksum": "32305d72b8d704acf8eef0c22277fee9",
"timestamp": "2026-01-08T02:15:50Z"
},
{
"version": "1.0.0.3",
"changelog": "fixes",
"targetAbi": "10.11.0.0",
"sourceUrl": "https://git.mahom03-spacecloud.de/CodeDevMLH/jellyfin-plugin-media-bar-enhanced/releases/download/v1.0.0.3/Jellyfin.Plugin.MediaBarEnhanced.zip",
"checksum": "e6180c42836069029072e96ac4860c42",
"timestamp": "2026-01-06T23:26:29Z"
},
{ {
"version": "1.0.0.2", "version": "1.0.0.2",
"changelog": "fixes", "changelog": "fixes",