Compare commits

..

83 Commits

Author SHA1 Message Date
CodeDevMLH
7f968ee050 Update manifest.json for release v1.7.0.11 [skip ci] 2026-02-16 22:06:11 +00:00
CodeDevMLH
dec5bbe39e Bump version to 1.7.0.11
All checks were successful
Auto Release Plugin / build-and-release (push) Successful in 52s
2026-02-16 23:05:18 +01:00
CodeDevMLH
63f3211cc4 Refactor date selection options to use SeasonalsConfigPage methods for consistency 2026-02-16 23:05:06 +01:00
CodeDevMLH
4270235c78 Update manifest.json for release v1.7.0.10 [skip ci] 2026-02-16 22:02:53 +00:00
CodeDevMLH
76d8a67914 Bump version to 1.7.0.10
All checks were successful
Auto Release Plugin / build-and-release (push) Successful in 52s
2026-02-16 23:02:00 +01:00
CodeDevMLH
1a3caf5da6 Enhance configuration page: replace input fields with dropdowns for date selection and add utility functions for generating options 2026-02-16 23:01:46 +01:00
CodeDevMLH
3b3ef77e61 fix ui [skip ci] 2026-02-16 19:31:42 +01:00
CodeDevMLH
ba580b1b52 Update manifest.json for release v1.7.0.9 [skip ci] 2026-02-16 18:27:34 +00:00
CodeDevMLH
0a6284c716 Bump version to 1.7.0.9
All checks were successful
Auto Release Plugin / build-and-release (push) Successful in 51s
2026-02-16 19:26:43 +01:00
CodeDevMLH
f83e863664 .. 2026-02-16 19:26:26 +01:00
CodeDevMLH
747e8ed6bc Update manifest.json for release v1.7.0.8 [skip ci] 2026-02-16 18:01:43 +00:00
CodeDevMLH
30845442b2 Bump version to 1.7.0.8
All checks were successful
Auto Release Plugin / build-and-release (push) Successful in 53s
2026-02-16 19:00:51 +01:00
CodeDevMLH
bb83201736 .. 2026-02-16 18:59:28 +01:00
CodeDevMLH
457ae404ba Update manifest.json for release v1.7.0.7 [skip ci] 2026-02-16 17:49:49 +00:00
CodeDevMLH
b6d679f6ef Update version to 1.7.0.7
All checks were successful
Auto Release Plugin / build-and-release (push) Successful in 58s
2026-02-16 18:48:51 +01:00
CodeDevMLH
3b88a1809d small UI changes 2026-02-16 18:48:07 +01:00
CodeDevMLH
4614ce4a7a Update manifest.json for release v1.7.0.5 [skip ci] 2026-02-16 17:16:03 +00:00
CodeDevMLH
57840bb149 Bump version to 1.7.0.6
All checks were successful
Auto Release Plugin / build-and-release (push) Successful in 1m5s
2026-02-16 18:14:59 +01:00
CodeDevMLH
dd90a4630a Update layout of seasonal rules in configuration page for improved responsiveness 2026-02-16 18:14:45 +01:00
CodeDevMLH
b5d5e5706e Update manifest.json for release v1.7.0.4 [skip ci] 2026-02-16 16:31:00 +00:00
CodeDevMLH
a4b5cf5b6b Bump version to 1.7.0.4
All checks were successful
Auto Release Plugin / build-and-release (push) Successful in 1m3s
2026-02-16 17:29:57 +01:00
CodeDevMLH
353bda10df Enhance configuration page: add section titles and improve input handling for seasonal themes 2026-02-16 17:29:41 +01:00
CodeDevMLH
0e1b91d93c Update manifest.json for release v1.7.0.3 [skip ci] 2026-02-16 15:55:27 +00:00
CodeDevMLH
9363008d07 Bump version to 1.7.0.3
All checks were successful
Auto Release Plugin / build-and-release (push) Successful in 52s
2026-02-16 16:54:37 +01:00
CodeDevMLH
faec7d8941 Refactor configuration page: update button classes, form input names, and tab handling for consistency 2026-02-16 16:54:22 +01:00
CodeDevMLH
7cc70854c4 Update manifest.json for release v1.7.0.2 [skip ci] 2026-02-16 15:14:02 +00:00
CodeDevMLH
9432f7aa86 Bump version to 1.7.0.2
All checks were successful
Auto Release Plugin / build-and-release (push) Successful in 55s
2026-02-16 16:13:07 +01:00
CodeDevMLH
4f7243bc74 test 2026-02-16 16:12:52 +01:00
CodeDevMLH
ee724fedc8 Update manifest.json for release v1.7.0.1 [skip ci] 2026-02-16 14:41:02 +00:00
CodeDevMLH
a1dbd4eb12 Bump version to 1.7.0.1
All checks were successful
Auto Release Plugin / build-and-release (push) Successful in 1m1s
2026-02-16 15:40:03 +01:00
CodeDevMLH
236d8d9e70 fix tabs 2026-02-16 15:39:42 +01:00
CodeDevMLH
6d55ae7524 Update manifest.json for release v1.7.0.0 [skip ci] 2026-02-16 01:31:56 +00:00
CodeDevMLH
99a0613893 Update version to 1.7.0.0
All checks were successful
Auto Release Plugin / build-and-release (push) Successful in 52s
2026-02-16 02:31:06 +01:00
CodeDevMLH
61952a0af7 Add seasonal rules configuration and enhance settings UI 2026-02-16 02:28:47 +01:00
CodeDevMLH
eca6ba96fb Add resurrection theme and enhance seasonal rules parsing logic [skip ci] 2026-02-16 02:28:36 +01:00
CodeDevMLH
c2f0f01689 Add release existence check to automation workflow 2026-02-16 02:28:16 +01:00
CodeDevMLH
30d17baff4 add (komisches) neues PR theme 2026-02-16 02:28:03 +01:00
CodeDevMLH
96bb1a3744 Update manifest.json for release v1.6.3.0 [skip ci] 2026-02-15 01:12:58 +00:00
CodeDevMLH
772a0dae40 rebuild
All checks were successful
Auto Release Plugin / build-and-release (push) Successful in 51s
2026-02-15 02:12:09 +01:00
CodeDevMLH
40c4454397 Update manifest.json for release v1.6.3.0 [skip ci] 2026-02-15 01:08:22 +00:00
CodeDevMLH
e5915e715a fix path issue on subpath installations
All checks were successful
Auto Release Plugin / build-and-release (push) Successful in 53s
2026-02-15 02:07:30 +01:00
CodeDevMLH
c171fc15f5 Update changelog for version 1.6.13.5 to include additional improvements [skip ci] 2026-02-04 19:37:34 +01:00
CodeDevMLH
a749b1f98e Update manifest.json for release v1.6.13.5 [skip ci] 2026-02-04 18:08:31 +00:00
CodeDevMLH
6ccf6201b4 Auto-Update MediaBar Enhanced to v1.4.0.12
All checks were successful
Auto Release Plugin / build-and-release (push) Successful in 49s
2026-02-04 18:07:46 +00:00
CodeDevMLH
a69c741a39 Update manifest.json for release v1.6.13.5 [skip ci] 2026-02-04 17:59:08 +00:00
CodeDevMLH
d54b4f9b07 Auto-Update MediaBar Enhanced to v1.4.0.11
All checks were successful
Auto Release Plugin / build-and-release (push) Successful in 50s
2026-02-04 17:58:20 +00:00
CodeDevMLH
2cd427b6e9 Update manifest.json for release v1.6.13.5 [skip ci] 2026-02-04 17:52:16 +00:00
CodeDevMLH
55c1f8b191 Auto-Update MediaBar Enhanced to v1.4.0.10
All checks were successful
Auto Release Plugin / build-and-release (push) Successful in 48s
2026-02-04 17:51:32 +00:00
CodeDevMLH
fc3d6efd1c Update manifest.json for release v1.6.13.5 [skip ci] 2026-02-04 17:40:04 +00:00
CodeDevMLH
5ba5940e5f Auto-Update MediaBar Enhanced to v1.4.0.9
All checks were successful
Auto Release Plugin / build-and-release (push) Successful in 50s
2026-02-04 17:39:18 +00:00
CodeDevMLH
621b7da344 Update manifest.json for release v1.6.13.5 [skip ci] 2026-02-04 17:28:13 +00:00
CodeDevMLH
268ce5e307 Auto-Update MediaBar Enhanced to v1.4.0.8
All checks were successful
Auto Release Plugin / build-and-release (push) Successful in 49s
2026-02-04 17:27:28 +00:00
CodeDevMLH
412cc2d981 Update manifest.json for release v1.6.13.5 [skip ci] 2026-02-04 17:10:23 +00:00
CodeDevMLH
949df24bdb Auto-Update MediaBar Enhanced to v1.4.0.7
All checks were successful
Auto Release Plugin / build-and-release (push) Successful in 49s
2026-02-04 17:09:35 +00:00
CodeDevMLH
b987969200 Update manifest.json for release v1.6.13.5 [skip ci] 2026-02-04 16:41:16 +00:00
CodeDevMLH
3306bb703d Auto-Update MediaBar Enhanced to v1.4.0.6
All checks were successful
Auto Release Plugin / build-and-release (push) Successful in 50s
2026-02-04 16:40:28 +00:00
CodeDevMLH
6587a4e3d0 Update manifest.json for release v1.6.13.5 [skip ci] 2026-02-04 16:24:07 +00:00
CodeDevMLH
f794b71f44 Auto-Update MediaBar Enhanced to v1.4.0.5
All checks were successful
Auto Release Plugin / build-and-release (push) Successful in 49s
2026-02-04 16:23:20 +00:00
CodeDevMLH
34363c502a Update manifest.json for release v1.6.13.5 [skip ci] 2026-02-04 16:18:09 +00:00
CodeDevMLH
add2f7a551 Auto-Update MediaBar Enhanced to v1.4.0.4
All checks were successful
Auto Release Plugin / build-and-release (push) Successful in 49s
2026-02-04 16:17:22 +00:00
CodeDevMLH
1d7e9e27ec Update manifest.json for release v1.6.13.5 [skip ci] 2026-02-04 16:16:56 +00:00
CodeDevMLH
6459653328 Bump version to 1.6.13.5
All checks were successful
Auto Release Plugin / build-and-release (push) Successful in 1m0s
2026-02-04 17:15:56 +01:00
CodeDevMLH
9d738e6061 Disable dragging and pointer events for seasonal settings button image 2026-02-04 17:15:39 +01:00
CodeDevMLH
8f5a3650e6 Update button image size in SeasonalSettingsManager 2026-02-04 17:13:27 +01:00
CodeDevMLH
229f9fe5ab Update manifest.json for release v1.6.13.4 [skip ci] 2026-02-04 15:53:04 +00:00
CodeDevMLH
0686129590 Auto-Update MediaBar Enhanced to v1.4.0.3
All checks were successful
Auto Release Plugin / build-and-release (push) Successful in 47s
2026-02-04 15:52:17 +00:00
CodeDevMLH
cb0392eb0d Update manifest.json for release v1.6.13.4 [skip ci] 2026-02-04 15:51:34 +00:00
CodeDevMLH
ed13e05b82 Bump version to 1.6.13.4
All checks were successful
Auto Release Plugin / build-and-release (push) Successful in 51s
2026-02-04 16:50:44 +01:00
CodeDevMLH
310fb4d496 Rename SettingsManager to SeasonalSettingsManager and update related log messages 2026-02-04 16:50:26 +01:00
CodeDevMLH
78d25106db Update manifest.json for release v1.6.13.3 [skip ci] 2026-02-04 15:46:34 +00:00
CodeDevMLH
a328171a8a Auto-Update MediaBar Enhanced to v1.4.0.2
All checks were successful
Auto Release Plugin / build-and-release (push) Successful in 50s
2026-02-04 15:45:45 +00:00
CodeDevMLH
361559cbec Update manifest.json for release v1.6.13.3 [skip ci] 2026-02-04 15:22:39 +00:00
CodeDevMLH
e08bf66a53 Bump version to 1.6.13.3
All checks were successful
Auto Release Plugin / build-and-release (push) Successful in 52s
2026-02-04 16:21:48 +01:00
CodeDevMLH
d6ef81138d typo 2026-02-04 16:21:02 +01:00
CodeDevMLH
35f21e680a Update manifest.json for release v1.6.13.2 [skip ci] 2026-02-04 15:02:42 +00:00
CodeDevMLH
705fbaed9d Auto-Update MediaBar Enhanced to v1.4.0.1
All checks were successful
Auto Release Plugin / build-and-release (push) Successful in 49s
2026-02-04 15:01:55 +00:00
CodeDevMLH
9e52198ef7 Update manifest.json for release v1.6.13.2 [skip ci] 2026-02-04 15:01:13 +00:00
CodeDevMLH
b1943dfe17 Bump version to 1.6.13.2
All checks were successful
Auto Release Plugin / build-and-release (push) Successful in 53s
2026-02-04 16:00:20 +01:00
CodeDevMLH
c55e900c0f Enhance logging messages in SeasonalsManager for better clarity 2026-02-04 14:05:41 +01:00
CodeDevMLH
503e9addee Update manifest.json for release v1.6.13.1 [skip ci] 2026-02-04 12:50:35 +00:00
CodeDevMLH
d630fdd217 Auto-Update MediaBar Enhanced to v1.4.0.0
All checks were successful
Auto Release Plugin / build-and-release (push) Successful in 47s
2026-02-04 12:49:50 +00:00
CodeDevMLH
7e4a7c2a6e Update manifest.json for release v1.6.13.1 [skip ci] 2026-02-04 12:47:05 +00:00
CodeDevMLH
1716a771f3 Auto-Update MediaBar Enhanced to v1.4.0.0
All checks were successful
Auto Release Plugin / build-and-release (push) Successful in 47s
2026-02-04 12:46:21 +00:00
21 changed files with 886 additions and 191 deletions

View File

@@ -51,7 +51,31 @@ jobs:
echo "$CHANGELOG" >> $GITHUB_ENV echo "$CHANGELOG" >> $GITHUB_ENV
echo "EOF" >> $GITHUB_ENV echo "EOF" >> $GITHUB_ENV
- name: Check if Release Already Exists
id: check_release
shell: bash
run: |
REPO_OWNER="${{ github.repository_owner }}"
REPO_NAME="${{ github.event.repository.name }}"
VERSION="${{ env.VERSION }}"
TAG="v$VERSION"
SERVER_URL="https://git.mahom03-spacecloud.de"
HTTP_STATUS=$(curl -s -o /dev/null -w "%{http_code}" -H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" "$SERVER_URL/api/v1/repos/$REPO_OWNER/$REPO_NAME/releases/tags/$TAG")
if [ "$HTTP_STATUS" -eq 200 ]; then
echo "Release $TAG already exists. Skipping release-related steps."
echo "release_exists=true" >> $GITHUB_OUTPUT
elif [ "$HTTP_STATUS" -eq 404 ]; then
echo "No existing release for $TAG. Continuing."
echo "release_exists=false" >> $GITHUB_OUTPUT
else
echo "Unexpected response when checking release: $HTTP_STATUS"
exit 1
fi
- name: Build and Zip - name: Build and Zip
if: steps.check_release.outputs.release_exists == 'false'
shell: bash shell: bash
run: | run: |
# Inject version from manifest into the build # Inject version from manifest into the build
@@ -71,6 +95,7 @@ jobs:
echo "ZIP_PATH=bin/Publish/Jellyfin.Plugin.Seasonals.zip" >> $GITHUB_ENV echo "ZIP_PATH=bin/Publish/Jellyfin.Plugin.Seasonals.zip" >> $GITHUB_ENV
- name: Update manifest.json - name: Update manifest.json
if: steps.check_release.outputs.release_exists == 'false'
shell: bash shell: bash
run: | run: |
REPO_OWNER="${{ github.repository_owner }}" REPO_OWNER="${{ github.repository_owner }}"
@@ -90,12 +115,14 @@ jobs:
manifest.json > manifest.json.tmp && mv manifest.json.tmp manifest.json manifest.json > manifest.json.tmp && mv manifest.json.tmp manifest.json
- name: Commit manifest.json - name: Commit manifest.json
if: steps.check_release.outputs.release_exists == 'false'
uses: stefanzweifel/git-auto-commit-action@v7 uses: stefanzweifel/git-auto-commit-action@v7
with: with:
commit_message: "Update manifest.json for release v${{ env.VERSION }} [skip ci]" commit_message: "Update manifest.json for release v${{ env.VERSION }} [skip ci]"
file_pattern: manifest.json file_pattern: manifest.json
- name: Create Release - name: Create Release
if: steps.check_release.outputs.release_exists == 'false'
uses: akkuman/gitea-release-action@v1 uses: akkuman/gitea-release-action@v1
with: with:
server_url: "https://git.mahom03-spacecloud.de" server_url: "https://git.mahom03-spacecloud.de"
@@ -109,6 +136,7 @@ jobs:
# Update Message in Remote Repository # Update Message in Remote Repository
- name: Checkout Central Manifest Repo - name: Checkout Central Manifest Repo
if: steps.check_release.outputs.release_exists == 'false'
uses: actions/checkout@v6 uses: actions/checkout@v6
with: with:
repository: ${{ github.repository_owner }}/jellyfin-plugin-manifest repository: ${{ github.repository_owner }}/jellyfin-plugin-manifest
@@ -116,6 +144,7 @@ jobs:
token: ${{ secrets.JELLYFIN_PLUGIN_MANIFEST_UPDATER_PAT }} token: ${{ secrets.JELLYFIN_PLUGIN_MANIFEST_UPDATER_PAT }}
- name: Update Central Manifest - name: Update Central Manifest
if: steps.check_release.outputs.release_exists == 'false'
shell: bash shell: bash
run: | run: |
cd central-manifest cd central-manifest
@@ -171,6 +200,7 @@ jobs:
manifest.json > manifest.json.tmp && mv manifest.json.tmp manifest.json manifest.json > manifest.json.tmp && mv manifest.json.tmp manifest.json
- name: Commit and Push Central Manifest - name: Commit and Push Central Manifest
if: steps.check_release.outputs.release_exists == 'false'
run: | run: |
cd central-manifest cd central-manifest
git config user.name "CodeDevMLH" git config user.name "CodeDevMLH"

View File

@@ -27,6 +27,7 @@ public class PluginConfiguration : BasePluginConfiguration
Christmas = new ChristmasOptions(); Christmas = new ChristmasOptions();
Santa = new SantaOptions(); Santa = new SantaOptions();
Easter = new EasterOptions(); Easter = new EasterOptions();
Resurrection = new ResurrectionOptions();
} }
/// <summary> /// <summary>
@@ -49,6 +50,11 @@ public class PluginConfiguration : BasePluginConfiguration
/// </summary> /// </summary>
public bool EnableClientSideToggle { get; set; } public bool EnableClientSideToggle { get; set; }
/// <summary>
/// Gets or sets the seasonal rules configuration as JSON.
/// </summary>
public string SeasonalRules { get; set; } = "[{\"Name\":\"New Year Fireworks\",\"StartDay\":28,\"StartMonth\":12,\"EndDay\":5,\"EndMonth\":1,\"Theme\":\"fireworks\"},{\"Name\":\"Valentine's Day\",\"StartDay\":10,\"StartMonth\":2,\"EndDay\":18,\"EndMonth\":2,\"Theme\":\"hearts\"},{\"Name\":\"Santa\",\"StartDay\":22,\"StartMonth\":12,\"EndDay\":27,\"EndMonth\":12,\"Theme\":\"santa\"},{\"Name\":\"Snowflakes (December)\",\"StartDay\":1,\"StartMonth\":12,\"EndDay\":31,\"EndMonth\":12,\"Theme\":\"snowflakes\"},{\"Name\":\"Snowfall (January)\",\"StartDay\":1,\"StartMonth\":1,\"EndDay\":31,\"EndMonth\":1,\"Theme\":\"snowfall\"},{\"Name\":\"Snowfall (February)\",\"StartDay\":1,\"StartMonth\":2,\"EndDay\":29,\"EndMonth\":2,\"Theme\":\"snowfall\"},{\"Name\":\"Easter\",\"StartDay\":25,\"StartMonth\":3,\"EndDay\":25,\"EndMonth\":4,\"Theme\":\"easter\"},{\"Name\":\"Halloween\",\"StartDay\":24,\"StartMonth\":10,\"EndDay\":5,\"EndMonth\":11,\"Theme\":\"halloween\"},{\"Name\":\"Autumn\",\"StartDay\":1,\"StartMonth\":9,\"EndDay\":30,\"EndMonth\":11,\"Theme\":\"autumn\"}]";
/// <summary> /// <summary>
/// Gets or sets the Seasonals options. /// Gets or sets the Seasonals options.
/// </summary> /// </summary>
@@ -62,6 +68,7 @@ public class PluginConfiguration : BasePluginConfiguration
public ChristmasOptions Christmas { get; set; } public ChristmasOptions Christmas { get; set; }
public SantaOptions Santa { get; set; } public SantaOptions Santa { get; set; }
public EasterOptions Easter { get; set; } public EasterOptions Easter { get; set; }
public ResurrectionOptions Resurrection { get; set; }
} }
public class AutumnOptions public class AutumnOptions
@@ -166,3 +173,12 @@ public class EasterOptions
public int MinBunnyRestTime { get; set; } = 2000; public int MinBunnyRestTime { get; set; } = 2000;
public int MaxBunnyRestTime { get; set; } = 5000; public int MaxBunnyRestTime { get; set; } = 5000;
} }
public class ResurrectionOptions
{
public int SymbolCount { get; set; } = 12;
public bool EnableResurrection { get; set; } = true;
public bool EnableRandomSymbols { get; set; } = true;
public bool EnableRandomSymbolsMobile { get; set; } = false;
public bool EnableDifferentDuration { get; set; } = true;
}

View File

@@ -1,4 +1,4 @@
<!DOCTYPE html> <!DOCTYPE html>
<html lang="en"> <html lang="en">
<head> <head>
<meta charset="utf-8"> <meta charset="utf-8">
@@ -6,15 +6,10 @@
</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>
select option:disabled {
color: #a3a3a3 !important;
}
</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 Configuration</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>
@@ -22,62 +17,100 @@
</a> </a>
</div> </div>
<hr style="max-width: 800px; margin: 1em 0;"> <hr style="max-width: 800px; margin: 1em 0;">
<br>
<form id="SeasonalsConfigForm">
<div class="checkboxContainer checkboxContainer-withDescription">
<label class="emby-checkbox-label">
<input id="IsEnabled" name="IsEnabled" type="checkbox" is="emby-checkbox" />
<span>Enable Seasonals</span>
</label>
<div class="fieldDescription">Enable or disable the entire plugin functionality.</div>
</div>
<div class="checkboxContainer checkboxContainer-withDescription">
<label class="emby-checkbox-label">
<input id="AutomateSeasonSelection" name="AutomateSeasonSelection" type="checkbox" is="emby-checkbox" />
<span>Automate Season Selection</span>
</label>
<div class="fieldDescription">Automatically select the season based on the date.</div>
</div>
<div class="selectContainer">
<label class="selectLabel" for="SelectedSeason">Selected Season</label>
<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="snowflakes">Snowflakes</option>
<option value="snowfall">Snowfall</option>
<option value="snowstorm">Snowstorm</option>
<option value="fireworks">Fireworks</option>
<option value="halloween">Halloween</option>
<option value="hearts">Hearts</option>
<option value="christmas">Christmas</option>
<option value="santa">Santa</option>
<option value="autumn">Autumn</option>
<option value="easter">Easter</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. Please commit ideas in a issue or PR)</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. Please commit ideas in a issue or PR)</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. Please commit ideas in a issue or PR)</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. Please commit ideas in a issue or PR)</option>
</select>
<div class="fieldDescription">The season to display if automation is disabled.</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>
<details> <div style="margin-bottom: 1.5em;">
<summary>Advanced Configuration</summary> <button class="seasonals-tab-button active" onclick="showSeasonalsTab('seasonals-basic', this)"
<p>Configure specific settings for each seasonal theme below.</p> style="background: none; border: none; color: #fff; cursor: pointer; transition: color 0.3s, border-bottom 0.3s; padding: 0.5em 1em; border-bottom: 2px solid #00a4dc;">
<p>All symbol count settings add this number in addition to the standard 12 symbols (if additional symbols is enabled).</p> <h3>General Settings</h3>
</button>
<button class="seasonals-tab-button" onclick="showSeasonalsTab('seasonals-auto-selection', this)"
style="background: none; border: none; color: #ccc; cursor: pointer; transition: color 0.3s, border-bottom 0.3s; padding: 0.5em 1em; border-bottom: 2px solid transparent;">
<h3>Auto Selection</h3>
</button>
<button class="seasonals-tab-button" onclick="showSeasonalsTab('seasonals-advanced', this)"
style="background: none; border: none; color: #ccc; cursor: pointer; transition: color 0.3s, border-bottom 0.3s; padding: 0.5em 1em; border-bottom: 2px solid transparent;">
<h3>Advanced Settings</h3>
</button>
</div>
<form id="SeasonalsConfigForm">
<!-- BASIC Tab -->
<div id="seasonals-basic" class="seasonals-tab-content">
<h2 class="sectionTitle">Main Plugin Settings</h2>
<div class="checkboxContainer checkboxContainer-withDescription">
<label class="emby-checkbox-label">
<input id="SeasonalsIsEnabled" name="SeasonalsIsEnabled" type="checkbox" is="emby-checkbox" />
<span>Enable Seasonals</span>
</label>
<div class="fieldDescription">Enable or disable the entire plugin functionality.</div>
</div>
<div class="checkboxContainer checkboxContainer-withDescription">
<label class="emby-checkbox-label">
<input id="SeasonalsAutomateSeasonSelection" name="SeasonalsAutomateSeasonSelection" type="checkbox" is="emby-checkbox" />
<span>Automate Season Selection</span>
</label>
<div class="fieldDescription">If enabled, the plugin will use the rules defined in the "Auto Selection" tab to assume the season. If no rule matches, it falls back to the "Standard Season" below.</div>
</div>
<div class="selectContainer">
<label class="selectLabel" for="SeasonalsSelectedSeason">Standard Season</label>
<select is="emby-select" id="SeasonalsSelectedSeason" name="SeasonalsSelectedSeason" class="selectLayout emby-select-withcolor emby-select" style="width: 100%; -webkit-appearance: menulist; appearance: menulist;">
<option value="none">None</option>
<option value="snowflakes">Snowflakes</option>
<option value="snowfall">Snowfall</option>
<option value="snowstorm">Snowstorm</option>
<option value="fireworks">Fireworks</option>
<option value="halloween">Halloween</option>
<option value="hearts">Hearts</option>
<option value="christmas">Christmas</option>
<option value="santa">Santa</option>
<option value="autumn">Autumn</option>
<option value="easter">Easter</option>
<option value="resurrection">Resurrection</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. Please commit ideas in a issue or PR)</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. Please commit ideas in a issue or PR)</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. Please commit ideas in a issue or PR)</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. Please commit ideas in a issue or PR)</option>
</select>
<div class="fieldDescription">The season to display if automation is disabled or no "Auto Selection" rule matches the current date.</div>
</div>
<div class="checkboxContainer checkboxContainer-withDescription">
<label class="emby-checkbox-label">
<input id="SeasonalsEnableClientSideToggle" name="SeasonalsEnableClientSideToggle" 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>
</div>
<!-- Auto Selection Tab -->
<div id="seasonals-auto-selection" class="seasonals-tab-content" style="display: none;">
<h2>Auto Selection Rules</h2>
<div style="background-color: rgba(255, 255, 255, 0.05); border-left: 4px solid #00a4dc; border-radius: 4px; padding: 1em 1.5em; margin: 1.5em 0; display: flex; align-items: center; gap: 1em;">
<i class="material-icons" style="color: #00a4dc; font-size: 24px;">info</i>
<div>Define rules to automatically select a season based on the date. Rules are evaluated from top to bottom. The first matching rule wins.</div>
</div>
<div id="seasonalRulesList">
<!-- Rules will be injected here via JS -->
</div>
<button is="emby-button" type="button" class="raised emby-button" onclick="SeasonalsConfigPage.addRule();"
style="margin-top: 1em; display: inline-flex; align-items: center; gap: 0.4em;">
<i class="material-icons" style="font-size: 24px;">add</i>
<span>Add Rule</span>
</button>
</div>
<!-- Advanced Tab -->
<div id="seasonals-advanced" class="seasonals-tab-content" style="display: none;">
<h2 class="sectionTitle">Configure specific settings for each seasonal theme</h2>
<!-- <p>Configure specific settings for each seasonal theme below.</p> -->
<p>All symbol count settings add this number in addition to the standard 12 symbols (if additional symbols is enabled).</p>
<details> <details>
<summary>Autumn</summary> <summary>Autumn</summary>
<div class="checkboxContainer checkboxContainer-withDescription"> <div class="checkboxContainer checkboxContainer-withDescription">
@@ -505,8 +538,46 @@
<div class="fieldDescription">Maximum time the bunny waits before appearing again.</div> <div class="fieldDescription">Maximum time the bunny waits before appearing again.</div>
</div> </div>
</details> </details>
</details> <hr style="max-width: 800px; margin: 1em 0;">
<details>
<summary>Resurrection</summary>
<div class="checkboxContainer checkboxContainer-withDescription">
<label class="emby-checkbox-label">
<input id="EnableResurrection" name="EnableResurrection" type="checkbox" is="emby-checkbox" />
<span>Enable Resurrection Seasonal</span>
</label>
<div class="fieldDescription">Enable the Resurrection theme in general (e.g. for automation).</div>
</div>
<div class="checkboxContainer checkboxContainer-withDescription">
<label class="emby-checkbox-label">
<input id="EnableRandomResurrection" name="EnableRandomResurrection" type="checkbox" is="emby-checkbox" />
<span>Enable Additional Random Symbols</span>
</label>
<div class="fieldDescription">Displays additional symbols randomly distributed across the screen.</div>
</div>
<div class="checkboxContainer checkboxContainer-withDescription">
<label class="emby-checkbox-label">
<input id="EnableRandomResurrectionMobile" name="EnableRandomResurrectionMobile" type="checkbox" is="emby-checkbox" />
<span>Enable Additional Random Symbols on Mobile</span>
</label>
<div class="fieldDescription">Displays additional symbols randomly distributed across the screen on mobile devices. Warning: High values may affect performance.</div>
</div>
<div class="inputContainer">
<label class="inputLabel" for="ResurrectionSymbolCount">Symbol Count</label>
<input is="emby-input" type="number" id="ResurrectionSymbolCount" name="ResurrectionSymbolCount" />
<div class="fieldDescription">Number of additional symbols (if enabled).</div>
</div>
<div class="checkboxContainer checkboxContainer-withDescription">
<label class="emby-checkbox-label">
<input id="EnableDifferentDurationResurrection" name="EnableDifferentDurationResurrection" type="checkbox" is="emby-checkbox" />
<span>Enable Different Duration</span>
</label>
<div class="fieldDescription">Randomize the movement speed.</div>
</div>
</details>
</div>
<div style="background-color: rgba(255, 255, 255, 0.05); border-left: 4px solid #00a4dc; border-radius: 4px; padding: 1em 1.5em; margin: 1.5em 0; display: flex; align-items: center; gap: 1em;"> <div style="background-color: rgba(255, 255, 255, 0.05); border-left: 4px solid #00a4dc; border-radius: 4px; padding: 1em 1.5em; margin: 1.5em 0; display: flex; align-items: center; gap: 1em;">
<i class="material-icons" style="color: #00a4dc; font-size: 24px;">info</i> <i class="material-icons" style="color: #00a4dc; font-size: 24px;">info</i>
<div> <div>
@@ -526,19 +597,271 @@
</form> </form>
</div> </div>
</div> </div>
<script type="text/javascript"> <style>
var SeasonalsConfig = { /* Styles for the Seasonal Rules List (Auto Selection Tab) */
pluginUniqueId: 'ef1e863f-cbb0-4e47-9f23-f0cbb1826ad4' .seasonal-rule {
background: rgba(0, 0, 0, 0.2);
border-radius: 8px;
padding: 15px;
margin-bottom: 15px;
display: flex;
flex-direction: column;
gap: 10px;
border: 1px solid rgba(255,255,255,0.05);
}
.seasonal-rule-header {
display: flex;
justify-content: space-between;
align-items: center;
}
.seasonal-rule-content {
display: grid;
grid-template-columns: 2fr 1.3fr 1.3fr 2fr;
gap: 15px;
align-items: end;
}
.rule-actions {
display: flex;
gap: 5px;
}
.date-range-group {
display: flex;
gap: 10px;
align-items: flex-end;
justify-content: space-between;
}
.date-range-group > .inputContainer,
.date-range-group > .selectContainer {
flex: 1;
}
@media (max-width: 800px) {
.seasonal-rule-content {
grid-template-columns: 1fr;
}
}
/* Styles for the Tabs */
.seasonals-tab-button.active {
color: #fff !important;
border-bottom: 2px solid #00a4dc !important;
}
/* Disabled options in selects */
select option:disabled {
color: #a3a3a3 !important;
}
</style>
<script>
function showSeasonalsTab(tabId, btn) {
document.querySelectorAll('.seasonals-tab-content').forEach(el => el.style.display = 'none');
document.getElementById(tabId).style.display = 'block';
document.querySelectorAll('.seasonals-tab-button').forEach(b => {
b.classList.remove('active');
b.style.color = '#ccc';
b.style.borderBottom = '2px solid transparent';
});
if (btn) {
btn.classList.add('active');
btn.style.color = '#fff';
btn.style.borderBottom = '2px solid #00a4dc';
}
}
var SeasonalsConfigPage = {
pluginUniqueId: 'ef1e863f-cbb0-4e47-9f23-f0cbb1826ad4',
getDaysOptions: function(selectedDay) {
var options = '';
for (var i = 1; i <= 31; i++) {
var isSelected = i === selectedDay ? 'selected' : '';
options += '<option value="' + i + '" ' + isSelected + '>' + i + '</option>';
}
return options;
},
getMonthsOptions: function(selectedMonth) {
var months = [
{ val: 1, name: 'Jan' },
{ val: 2, name: 'Feb' },
{ val: 3, name: 'Mar' },
{ val: 4, name: 'Apr' },
{ val: 5, name: 'May' },
{ val: 6, name: 'Jun' },
{ val: 7, name: 'Jul' },
{ val: 8, name: 'Aug' },
{ val: 9, name: 'Sep' },
{ val: 10, name: 'Oct' },
{ val: 11, name: 'Nov' },
{ val: 12, name: 'Dec' }
];
var options = '';
for (var i = 0; i < months.length; i++) {
var m = months[i];
var isSelected = m.val === selectedMonth ? 'selected' : '';
options += '<option value="' + m.val + '" ' + isSelected + '>' + m.name + '</option>';
}
return options;
},
addRule: function(data = null) {
var container = document.querySelector('#seasonalRulesList');
var div = document.createElement('div');
div.className = 'seasonal-rule';
var name = data ? (data.Name || data.name || 'New Rule') : 'New Rule';
var startDay = data ? (data.StartDay !== undefined ? data.StartDay : (data.startDay !== undefined ? data.startDay : 1)) : 1;
var startMonth = data ? (data.StartMonth !== undefined ? data.StartMonth : (data.startMonth !== undefined ? data.startMonth : 1)) : 1;
var endDay = data ? (data.EndDay !== undefined ? data.EndDay : (data.endDay !== undefined ? data.endDay : 1)) : 1;
var endMonth = data ? (data.EndMonth !== undefined ? data.EndMonth : (data.endMonth !== undefined ? data.endMonth : 1)) : 1;
var theme = data ? (data.Theme || data.theme || 'none') : 'none';
div.innerHTML = `
<div class="seasonal-rule-header">
<div style="font-weight: bold; font-size: 1.1em;" class="rule-title"></div>
<div class="rule-actions">
<button type="button" is="paper-icon-button-light" class="btn-move-up" title="Move Up"><i class="material-icons">arrow_upward</i></button>
<button type="button" is="paper-icon-button-light" class="btn-move-down" title="Move Down"><i class="material-icons">arrow_downward</i></button>
<button type="button" is="paper-icon-button-light" class="btn-remove" title="Remove"><i class="material-icons">delete</i></button>
</div>
</div>
<div class="seasonal-rule-content">
<div class="inputContainer" style="margin:0;">
<label class="inputLabel">Name</label>
<input is="emby-input" class="rule-name" onchange="SeasonalsConfigPage.updateRuleTitles();" />
</div>
<div class="date-range-group">
<div class="selectContainer" style="margin:0; flex: 1;">
<label class="selectLabel">Start Day</label>
<select is="emby-select" class="rule-start-day" style="width: 100%;">
${SeasonalsConfigPage.getDaysOptions(startDay)}
</select>
</div>
<div class="selectContainer" style="margin:0; flex: 1;">
<label class="selectLabel">Month</label>
<select is="emby-select" class="rule-start-month" style="width: 100%;">
${SeasonalsConfigPage.getMonthsOptions(startMonth)}
</select>
</div>
</div>
<div class="date-range-group">
<div class="selectContainer" style="margin:0; flex: 1;">
<label class="selectLabel">End Day</label>
<select is="emby-select" class="rule-end-day" style="width: 100%;">
${SeasonalsConfigPage.getDaysOptions(endDay)}
</select>
</div>
<div class="selectContainer" style="margin:0; flex: 1;">
<label class="selectLabel">Month</label>
<select is="emby-select" class="rule-end-month" style="width: 100%;">
${SeasonalsConfigPage.getMonthsOptions(endMonth)}
</select>
</div>
</div>
<div class="selectContainer" style="margin:0;">
<label class="selectLabel">Theme</label>
<select is="emby-select" class="rule-theme" style="width: 100%;">
<option value="none">None</option>
<option value="snowflakes">Snowflakes</option>
<option value="snowfall">Snowfall</option>
<option value="snowstorm">Snowstorm</option>
<option value="fireworks">Fireworks</option>
<option value="halloween">Halloween</option>
<option value="hearts">Hearts</option>
<option value="christmas">Christmas</option>
<option value="santa">Santa</option>
<option value="autumn">Autumn</option>
<option value="easter">Easter</option>
<option value="resurrection">Resurrection</option>
</select>
</div>
</div>
`;
container.appendChild(div);
// Set values programmatically
div.querySelector('.rule-name').value = name;
div.querySelector('.rule-start-day').value = startDay;
div.querySelector('.rule-start-month').value = startMonth;
div.querySelector('.rule-end-day').value = endDay;
div.querySelector('.rule-end-month').value = endMonth;
div.querySelector('.rule-theme').value = theme;
// Bind events
div.querySelector('.btn-remove').addEventListener('click', function() {
div.remove();
SeasonalsConfigPage.updateRuleTitles();
});
div.querySelector('.btn-move-up').addEventListener('click', function() {
if (div.previousElementSibling) {
container.insertBefore(div, div.previousElementSibling);
SeasonalsConfigPage.updateRuleTitles();
}
});
div.querySelector('.btn-move-down').addEventListener('click', function() {
if (div.nextElementSibling) {
container.insertBefore(div.nextElementSibling, div);
SeasonalsConfigPage.updateRuleTitles();
}
});
this.updateRuleTitles();
},
updateRuleTitles: function() {
var rules = document.querySelectorAll('.seasonal-rule');
rules.forEach((rule, index) => {
var name = rule.querySelector('.rule-name').value;
rule.querySelector('.rule-title').innerText = '#' + (index + 1) + ' ' + name;
});
},
renderRules: function(rules) {
var container = document.querySelector('#seasonalRulesList');
container.innerHTML = '';
if (Array.isArray(rules)) {
rules.forEach(rule => this.addRule(rule));
}
},
getRulesFromUI: function() {
var rules = [];
document.querySelectorAll('.seasonal-rule').forEach(div => {
rules.push({
Name: div.querySelector('.rule-name').value,
StartDay: parseInt(div.querySelector('.rule-start-day').value),
StartMonth: parseInt(div.querySelector('.rule-start-month').value),
EndDay: parseInt(div.querySelector('.rule-end-day').value),
EndMonth: parseInt(div.querySelector('.rule-end-month').value),
Theme: div.querySelector('.rule-theme').value
});
});
return rules;
}
}; };
document.querySelector('#SeasonalsConfigPage') document.querySelector('#SeasonalsConfigPage')
.addEventListener('pageshow', function() { .addEventListener('pageshow', function() {
Dashboard.showLoadingMsg(); Dashboard.showLoadingMsg();
ApiClient.getPluginConfiguration(SeasonalsConfig.pluginUniqueId).then(function (config) { ApiClient.getPluginConfiguration(SeasonalsConfigPage.pluginUniqueId).then(function (config) {
document.querySelector('#IsEnabled').checked = config.IsEnabled; document.querySelector('#SeasonalsIsEnabled').checked = config.IsEnabled;
document.querySelector('#SelectedSeason').value = config.SelectedSeason; document.querySelector('#SeasonalsSelectedSeason').value = config.SelectedSeason;
document.querySelector('#AutomateSeasonSelection').checked = config.AutomateSeasonSelection; document.querySelector('#SeasonalsAutomateSeasonSelection').checked = config.AutomateSeasonSelection;
document.querySelector('#EnableClientSideToggle').checked = config.EnableClientSideToggle !== undefined ? config.EnableClientSideToggle : true; document.querySelector('#SeasonalsEnableClientSideToggle').checked = config.EnableClientSideToggle !== undefined ? config.EnableClientSideToggle : true;
// Load Rules
try {
var rules = JSON.parse(config.SeasonalRules || "[]");
SeasonalsConfigPage.renderRules(rules);
} catch (e) {
console.error("Failed to parse SeasonalRules", e);
}
// Advanced Config // Advanced Config
// Autumn // Autumn
@@ -624,6 +947,13 @@
document.querySelector('#MinBunnyRestTime').value = config.Easter.MinBunnyRestTime; document.querySelector('#MinBunnyRestTime').value = config.Easter.MinBunnyRestTime;
document.querySelector('#MaxBunnyRestTime').value = config.Easter.MaxBunnyRestTime; document.querySelector('#MaxBunnyRestTime').value = config.Easter.MaxBunnyRestTime;
// Resurrection
document.querySelector('#EnableResurrection').checked = config.Resurrection.EnableResurrection;
document.querySelector('#ResurrectionSymbolCount').value = config.Resurrection.SymbolCount;
document.querySelector('#EnableRandomResurrection').checked = config.Resurrection.EnableRandomSymbols;
document.querySelector('#EnableRandomResurrectionMobile').checked = config.Resurrection.EnableRandomSymbolsMobile;
document.querySelector('#EnableDifferentDurationResurrection').checked = config.Resurrection.EnableDifferentDuration;
Dashboard.hideLoadingMsg(); Dashboard.hideLoadingMsg();
}); });
}); });
@@ -631,12 +961,14 @@
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(SeasonalsConfigPage.pluginUniqueId).then(function (config) {
config.IsEnabled = document.querySelector('#IsEnabled').checked; config.IsEnabled = document.querySelector('#SeasonalsIsEnabled').checked;
config.SelectedSeason = document.querySelector('#SelectedSeason').value; config.SelectedSeason = document.querySelector('#SeasonalsSelectedSeason').value;
config.AutomateSeasonSelection = document.querySelector('#AutomateSeasonSelection').checked; config.AutomateSeasonSelection = document.querySelector('#SeasonalsAutomateSeasonSelection').checked;
config.EnableClientSideToggle = document.querySelector('#EnableClientSideToggle').checked; config.EnableClientSideToggle = document.querySelector('#SeasonalsEnableClientSideToggle').checked;
// Save Rules
config.SeasonalRules = JSON.stringify(SeasonalsConfigPage.getRulesFromUI());
// Advanced Config // Advanced Config
// Autumn // Autumn
@@ -722,10 +1054,17 @@
config.Easter.MinBunnyRestTime = parseInt(document.querySelector('#MinBunnyRestTime').value); config.Easter.MinBunnyRestTime = parseInt(document.querySelector('#MinBunnyRestTime').value);
config.Easter.MaxBunnyRestTime = parseInt(document.querySelector('#MaxBunnyRestTime').value); config.Easter.MaxBunnyRestTime = parseInt(document.querySelector('#MaxBunnyRestTime').value);
ApiClient.updatePluginConfiguration(SeasonalsConfig.pluginUniqueId, config).then(function (result) { // Resurrection
config.Resurrection.EnableResurrection = document.querySelector('#EnableResurrection').checked;
config.Resurrection.SymbolCount = parseInt(document.querySelector('#ResurrectionSymbolCount').value);
config.Resurrection.EnableRandomSymbols = document.querySelector('#EnableRandomResurrection').checked;
config.Resurrection.EnableRandomSymbolsMobile = document.querySelector('#EnableRandomResurrectionMobile').checked;
config.Resurrection.EnableDifferentDuration = document.querySelector('#EnableDifferentDurationResurrection').checked;
ApiClient.updatePluginConfiguration(SeasonalsConfigPage.pluginUniqueId, config).then(function (result) {
Dashboard.processPluginConfigurationUpdateResult(result); Dashboard.processPluginConfigurationUpdateResult(result);
});
}); });
});
e.preventDefault(); e.preventDefault();
return false; return false;

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.6.13.1</Version> <Version>1.7.0.11</Version>
<RepositoryUrl>https://github.com/CodeDevMLH/Jellyfin-Seasonals</RepositoryUrl> <RepositoryUrl>https://github.com/CodeDevMLH/Jellyfin-Seasonals</RepositoryUrl>
</PropertyGroup> </PropertyGroup>

View File

@@ -18,7 +18,7 @@ public class ScriptInjector
{ {
private readonly IApplicationPaths _appPaths; private readonly IApplicationPaths _appPaths;
private readonly ILogger<ScriptInjector> _logger; private readonly ILogger<ScriptInjector> _logger;
public const string ScriptTag = "<script src=\"/Seasonals/Resources/seasonals.js\" defer></script>"; public const string ScriptTag = "<script src=\"../Seasonals/Resources/seasonals.js\" defer></script>";
public const string Marker = "</body>"; public const string Marker = "</body>";
/// <summary> /// <summary>

View File

@@ -48,23 +48,23 @@ observer.observe(document.body, {
const images = [ const images = [
"/Seasonals/Resources/autumn_images/acorn1.png", "../Seasonals/Resources/autumn_images/acorn1.png",
"/Seasonals/Resources/autumn_images/acorn2.png", "../Seasonals/Resources/autumn_images/acorn2.png",
"/Seasonals/Resources/autumn_images/leaf1.png", "../Seasonals/Resources/autumn_images/leaf1.png",
"/Seasonals/Resources/autumn_images/leaf2.png", "../Seasonals/Resources/autumn_images/leaf2.png",
"/Seasonals/Resources/autumn_images/leaf3.png", "../Seasonals/Resources/autumn_images/leaf3.png",
"/Seasonals/Resources/autumn_images/leaf4.png", "../Seasonals/Resources/autumn_images/leaf4.png",
"/Seasonals/Resources/autumn_images/leaf5.png", "../Seasonals/Resources/autumn_images/leaf5.png",
"/Seasonals/Resources/autumn_images/leaf6.png", "../Seasonals/Resources/autumn_images/leaf6.png",
"/Seasonals/Resources/autumn_images/leaf7.png", "../Seasonals/Resources/autumn_images/leaf7.png",
"/Seasonals/Resources/autumn_images/leaf8.png", "../Seasonals/Resources/autumn_images/leaf8.png",
"/Seasonals/Resources/autumn_images/leaf9.png", "../Seasonals/Resources/autumn_images/leaf9.png",
"/Seasonals/Resources/autumn_images/leaf10.png", "../Seasonals/Resources/autumn_images/leaf10.png",
"/Seasonals/Resources/autumn_images/leaf11.png", "../Seasonals/Resources/autumn_images/leaf11.png",
"/Seasonals/Resources/autumn_images/leaf12.png", "../Seasonals/Resources/autumn_images/leaf12.png",
"/Seasonals/Resources/autumn_images/leaf13.png", "../Seasonals/Resources/autumn_images/leaf13.png",
"/Seasonals/Resources/autumn_images/leaf14.png", "../Seasonals/Resources/autumn_images/leaf14.png",
"/Seasonals/Resources/autumn_images/leaf15.png", "../Seasonals/Resources/autumn_images/leaf15.png",
]; ];
function addRandomLeaves(count) { function addRandomLeaves(count) {

View File

@@ -61,20 +61,20 @@ observer.observe(document.body, {
const images = [ const images = [
"/Seasonals/Resources/easter_images/egg_1.png", "../Seasonals/Resources/easter_images/egg_1.png",
"/Seasonals/Resources/easter_images/egg_2.png", "../Seasonals/Resources/easter_images/egg_2.png",
"/Seasonals/Resources/easter_images/egg_3.png", "../Seasonals/Resources/easter_images/egg_3.png",
"/Seasonals/Resources/easter_images/egg_4.png", "../Seasonals/Resources/easter_images/egg_4.png",
"/Seasonals/Resources/easter_images/egg_5.png", "../Seasonals/Resources/easter_images/egg_5.png",
"/Seasonals/Resources/easter_images/egg_6.png", "../Seasonals/Resources/easter_images/egg_6.png",
"/Seasonals/Resources/easter_images/egg_7.png", "../Seasonals/Resources/easter_images/egg_7.png",
"/Seasonals/Resources/easter_images/egg_8.png", "../Seasonals/Resources/easter_images/egg_8.png",
"/Seasonals/Resources/easter_images/egg_9.png", "../Seasonals/Resources/easter_images/egg_9.png",
"/Seasonals/Resources/easter_images/egg_10.png", "../Seasonals/Resources/easter_images/egg_10.png",
"/Seasonals/Resources/easter_images/egg_11.png", "../Seasonals/Resources/easter_images/egg_11.png",
"/Seasonals/Resources/easter_images/egg_12.png", "../Seasonals/Resources/easter_images/egg_12.png",
]; ];
const rabbit = "/Seasonals/Resources/easter_images/easter-bunny.png"; const rabbit = "../Seasonals/Resources/easter_images/easter-bunny.png";
function addRandomEaster(count) { function addRandomEaster(count) {
const easterContainer = document.querySelector('.easter-container'); // get the leave container const easterContainer = document.querySelector('.easter-container'); // get the leave container

View File

@@ -46,9 +46,9 @@ observer.observe(document.body, {
const images = [ const images = [
"/Seasonals/Resources/halloween_images/ghost_20x20.png", "../Seasonals/Resources/halloween_images/ghost_20x20.png",
"/Seasonals/Resources/halloween_images/bat_20x20.png", "../Seasonals/Resources/halloween_images/bat_20x20.png",
"/Seasonals/Resources/halloween_images/pumpkin_20x20.png", "../Seasonals/Resources/halloween_images/pumpkin_20x20.png",
]; ];
function addRandomSymbols(count) { function addRandomSymbols(count) {

View File

@@ -0,0 +1,59 @@
.ressurection-container {
display: block;
position: fixed;
overflow: hidden;
top: 0;
left: 0;
width: 100%;
height: 100%;
pointer-events: none;
z-index: 10;
}
.ressurection-symbol {
position: fixed;
z-index: 15;
top: -15%;
user-select: none;
-webkit-user-select: none;
cursor: default;
animation-name: ressurection-fall, ressurection-sway;
animation-timing-function: linear, ease-in-out;
animation-iteration-count: infinite, infinite;
will-change: transform, top;
}
.ressurection-symbol img {
z-index: 15;
height: auto;
width: 56px;
opacity: 0.95;
filter: drop-shadow(0 0 8px rgba(255, 215, 130, 0.5));
}
@media (max-width: 768px) {
.ressurection-symbol img {
width: 42px;
}
}
@keyframes ressurection-fall {
0% {
top: -15%;
}
100% {
top: 105%;
}
}
@keyframes ressurection-sway {
0%,
100% {
transform: translateX(0);
}
50% {
transform: translateX(65px);
}
}

View File

@@ -0,0 +1,113 @@
const config = window.SeasonalsPluginConfig?.Resurrection || {};
const enableResurrection = config.EnableResurrection !== undefined ? config.EnableResurrection : true;
const enableRandomSymbols = config.EnableRandomSymbols !== undefined ? config.EnableRandomSymbols : true;
const enableRandomSymbolsMobile = config.EnableRandomSymbolsMobile !== undefined ? config.EnableRandomSymbolsMobile : false;
const enableDifferentDuration = config.EnableDifferentDuration !== undefined ? config.EnableDifferentDuration : true;
const symbolCount = config.SymbolCount || 12;
let animationEnabled = true;
let statusLogged = false;
const images = [
'../Seasonals/Resources/resurrection_images/crosses.png',
'../Seasonals/Resources/resurrection_images/palm-branch.png',
'../Seasonals/Resources/resurrection_images/draped-cross.png',
'../Seasonals/Resources/resurrection_images/empty-tomb.png',
'../Seasonals/Resources/resurrection_images/he-is-risen.png',
'../Seasonals/Resources/resurrection_images/crown-of-thorns.png',
'../Seasonals/Resources/resurrection_images/risen-lord.png',
'../Seasonals/Resources/resurrection_images/dove.png'
];
function toggleResurrection() {
const container = document.querySelector('.resurrection-container');
if (!container) return;
const videoPlayer = document.querySelector('.videoPlayerContainer');
const trailerPlayer = document.querySelector('.youtubePlayerContainer');
const isDashboard = document.body.classList.contains('dashboardDocument');
const hasUserMenu = document.querySelector('#app-user-menu');
animationEnabled = !(videoPlayer || trailerPlayer || isDashboard || hasUserMenu);
container.style.display = animationEnabled ? 'block' : 'none';
if (!animationEnabled && !statusLogged) {
console.log('Resurrection hidden');
statusLogged = true;
} else if (animationEnabled && statusLogged) {
console.log('Resurrection visible');
statusLogged = false;
}
}
const observer = new MutationObserver(toggleResurrection);
observer.observe(document.body, {
childList: true,
subtree: true,
attributes: true
});
function createSymbol(imageSrc, leftPercent, delaySeconds) {
const symbol = document.createElement('div');
symbol.className = 'resurrection-symbol';
const img = document.createElement('img');
img.src = imageSrc;
img.alt = '';
symbol.style.left = `${leftPercent}%`;
symbol.style.animationDelay = `${delaySeconds}s, ${Math.random() * 3}s`;
if (enableDifferentDuration) {
const fallDuration = Math.random() * 7 + 7;
const swayDuration = Math.random() * 4 + 2;
symbol.style.animationDuration = `${fallDuration}s, ${swayDuration}s`;
}
symbol.appendChild(img);
return symbol;
}
function addSymbols(count) {
const container = document.querySelector('.resurrection-container');
if (!container || !enableRandomSymbols) return;
const isDesktop = window.innerWidth > 768;
if (!isDesktop && !enableRandomSymbolsMobile) return;
for (let i = 0; i < count; i++) {
const imageSrc = images[Math.floor(Math.random() * images.length)];
const left = Math.random() * 100;
const delay = Math.random() * 12;
container.appendChild(createSymbol(imageSrc, left, delay));
}
}
function initResurrection() {
let container = document.querySelector('.resurrection-container');
if (!container) {
container = document.createElement('div');
container.className = 'resurrection-container';
container.setAttribute('aria-hidden', 'true');
document.body.appendChild(container);
}
// Place one of each of the 8 provided resurrection images first.
images.forEach((imageSrc, index) => {
const left = (index + 1) * (100 / (images.length + 1));
const delay = Math.random() * 8;
container.appendChild(createSymbol(imageSrc, left, delay));
});
const extraCount = Math.max(symbolCount - images.length, 0);
addSymbols(extraCount);
}
function initializeResurrection() {
if (!enableResurrection) return;
initResurrection();
toggleResurrection();
}
initializeResurrection();

Binary file not shown.

After

Width:  |  Height:  |  Size: 412 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 317 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 354 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 382 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 472 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 372 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 384 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 385 KiB

View File

@@ -181,18 +181,18 @@ function updateSnowflakes() {
// credits: flaticon.com // credits: flaticon.com
const presentImages = [ const presentImages = [
'/Seasonals/Resources/santa_images/gift1.png', '../Seasonals/Resources/santa_images/gift1.png',
'/Seasonals/Resources/santa_images/gift2.png', '../Seasonals/Resources/santa_images/gift2.png',
'/Seasonals/Resources/santa_images/gift3.png', '../Seasonals/Resources/santa_images/gift3.png',
'/Seasonals/Resources/santa_images/gift4.png', '../Seasonals/Resources/santa_images/gift4.png',
'/Seasonals/Resources/santa_images/gift5.png', '../Seasonals/Resources/santa_images/gift5.png',
'/Seasonals/Resources/santa_images/gift6.png', '../Seasonals/Resources/santa_images/gift6.png',
'/Seasonals/Resources/santa_images/gift7.png', '../Seasonals/Resources/santa_images/gift7.png',
'/Seasonals/Resources/santa_images/gift8.png', '../Seasonals/Resources/santa_images/gift8.png',
]; ];
// credits: https://www.animatedimages.org/img-animated-santa-claus-image-0420-85884.htm // credits: https://www.animatedimages.org/img-animated-santa-claus-image-0420-85884.htm
const santaImage = '/Seasonals/Resources/santa_images/santa.gif'; const santaImage = '../Seasonals/Resources/santa_images/santa.gif';
function createSantaElement() { function createSantaElement() {

View File

@@ -1,66 +1,71 @@
/** /*
* Seasonals Plugin (Client Side Manager Logic) * Seasonals Plugin (Client Side Manager Logic)
*/ */
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',
containerClass: 'snowflakes' containerClass: 'snowflakes'
}, },
snowfall: { snowfall: {
css: '/Seasonals/Resources/snowfall.css', css: '../Seasonals/Resources/snowfall.css',
js: '/Seasonals/Resources/snowfall.js', js: '../Seasonals/Resources/snowfall.js',
containerClass: 'snowfall-container' containerClass: 'snowfall-container'
}, },
snowstorm: { snowstorm: {
css: '/Seasonals/Resources/snowstorm.css', css: '../Seasonals/Resources/snowstorm.css',
js: '/Seasonals/Resources/snowstorm.js', js: '../Seasonals/Resources/snowstorm.js',
containerClass: 'snowstorm-container' containerClass: 'snowstorm-container'
}, },
fireworks: { fireworks: {
css: '/Seasonals/Resources/fireworks.css', css: '../Seasonals/Resources/fireworks.css',
js: '/Seasonals/Resources/fireworks.js', js: '../Seasonals/Resources/fireworks.js',
containerClass: 'fireworks' containerClass: 'fireworks'
}, },
halloween: { halloween: {
css: '/Seasonals/Resources/halloween.css', css: '../Seasonals/Resources/halloween.css',
js: '/Seasonals/Resources/halloween.js', js: '../Seasonals/Resources/halloween.js',
containerClass: 'halloween-container' containerClass: 'halloween-container'
}, },
hearts: { hearts: {
css: '/Seasonals/Resources/hearts.css', css: '../Seasonals/Resources/hearts.css',
js: '/Seasonals/Resources/hearts.js', js: '../Seasonals/Resources/hearts.js',
containerClass: 'hearts-container' containerClass: 'hearts-container'
}, },
christmas: { christmas: {
css: '/Seasonals/Resources/christmas.css', css: '../Seasonals/Resources/christmas.css',
js: '/Seasonals/Resources/christmas.js', js: '../Seasonals/Resources/christmas.js',
containerClass: 'christmas-container' containerClass: 'christmas-container'
}, },
santa: { santa: {
css: '/Seasonals/Resources/santa.css', css: '../Seasonals/Resources/santa.css',
js: '/Seasonals/Resources/santa.js', js: '../Seasonals/Resources/santa.js',
containerClass: 'santa-container' containerClass: 'santa-container'
}, },
autumn: { autumn: {
css: '/Seasonals/Resources/autumn.css', css: '../Seasonals/Resources/autumn.css',
js: '/Seasonals/Resources/autumn.js', js: '../Seasonals/Resources/autumn.js',
containerClass: 'autumn-container' containerClass: 'autumn-container'
}, },
easter: { easter: {
css: '/Seasonals/Resources/easter.css', css: '../Seasonals/Resources/easter.css',
js: '/Seasonals/Resources/easter.js', js: '../Seasonals/Resources/easter.js',
containerClass: 'easter-container' containerClass: 'easter-container'
}, },
resurrection: {
css: '../Seasonals/Resources/resurrection.css',
js: '../Seasonals/Resources/resurrection.js',
containerClass: 'resurrection-container'
},
summer: { summer: {
css: '/Seasonals/Resources/summer.css', css: '../Seasonals/Resources/summer.css',
js: '/Seasonals/Resources/summer.js', js: '../Seasonals/Resources/summer.js',
containerClass: 'summer-container' containerClass: 'summer-container'
}, },
spring: { spring: {
css: '/Seasonals/Resources/spring.css', css: '../Seasonals/Resources/spring.css',
js: '/Seasonals/Resources/spring.js', js: '../Seasonals/Resources/spring.js',
containerClass: 'spring-container' containerClass: 'spring-container'
}, },
none: { none: {
@@ -68,7 +73,7 @@ const ThemeConfigs = {
}, },
}; };
const SettingsManager = { const SeasonalSettingsManager = {
initialized: false, initialized: false,
config: null, config: null,
@@ -99,8 +104,7 @@ const SettingsManager = {
button.className = 'paper-icon-button-light headerButton seasonal-settings-button'; button.className = 'paper-icon-button-light headerButton seasonal-settings-button';
button.title = 'Seasonal Settings'; button.title = 'Seasonal Settings';
// button.innerHTML = '<span class="material-icons">ac_unit</span>'; // button.innerHTML = '<span class="material-icons">ac_unit</span>';
// button.innerHTML = '<img src="/Seasonals/Resources/assets/logo_SW.svg" style="width: 52px; height: 24px; vertical-align: middle;">'; button.innerHTML = '<img src="../Seasonals/Resources/assets/logo_SW.svg" draggable="false" style="width: 24px; height: 24px; vertical-align: middle; pointer-events: none;">';
button.innerHTML = '<img src="/Seasonals/Resources/assets/logo_SW.svg" style="width: 24px; height: 24px; vertical-align: middle;">';
button.style.verticalAlign = 'middle'; button.style.verticalAlign = 'middle';
button.addEventListener('click', (e) => { button.addEventListener('click', (e) => {
@@ -233,29 +237,29 @@ const SeasonalsManager = {
async init() { async init() {
// Fetch Config // Fetch Config
try { try {
const response = await fetch('/Seasonals/Config'); const response = await fetch('../Seasonals/Config');
if (response.ok) { if (response.ok) {
this.config = await response.json(); this.config = await response.json();
window.SeasonalsPluginConfig = this.config; window.SeasonalsPluginConfig = this.config;
console.log('Seasonals Config loaded:', this.config); console.log('Seasonals: Seasonals Config loaded:', this.config);
} }
} catch (error) { } catch (error) {
console.error('Error fetching Seasonals config:', error); console.error('Seasonals: Error fetching Seasonals config:', error);
} }
// Initialize Settings UI // Initialize Settings UI
SettingsManager.init(this.config); SeasonalSettingsManager.init(this.config);
// User Preference Check // User Preference Check
const isEnabled = SettingsManager.getSetting('enabled', 'true') === 'true'; const isEnabled = SeasonalSettingsManager.getSetting('enabled', 'true') === 'true';
if (!isEnabled) { if (!isEnabled) {
console.log('Seasonals disabled by user preference.'); console.log('Seasonals: Disabled by user preference.');
return; return;
} }
// Determine Theme // Determine Theme
const themeName = this.selectTheme(); const themeName = this.selectTheme();
console.log(`Selected theme: ${themeName}`); console.log(`Seasonals: Selected theme: ${themeName}`);
if (!themeName || themeName === 'none') { if (!themeName || themeName === 'none') {
return; return;
@@ -267,9 +271,9 @@ const SeasonalsManager = {
selectTheme() { selectTheme() {
// Check local override // Check local override
const forcedTheme = SettingsManager.getSetting('theme', 'auto'); const forcedTheme = SeasonalSettingsManager.getSetting('theme', 'auto');
if (forcedTheme !== 'auto') { if (forcedTheme !== 'auto') {
console.log(`User forced theme: ${forcedTheme}`); console.log(`Seasonals: User forced theme: ${forcedTheme}`);
return forcedTheme; return forcedTheme;
} }
@@ -284,40 +288,62 @@ const SeasonalsManager = {
}, },
determineCurrentThemeDate() { determineCurrentThemeDate() {
var rules = [];
try {
rules = JSON.parse(this.config.SeasonalRules || "[]");
} catch (e) {
console.error("Seasonals: Error parsing SeasonalRules", e);
}
if (rules.length === 0) {
// Fallback to empty/none if no rules are defined (though default should exist)
console.log("Seasonals: No auto-selection rules found.");
return 'none';
}
const date = new Date(); const date = new Date();
const month = date.getMonth(); // 0-11 const month = date.getMonth() + 1; // 1-12
const day = date.getDate(); // 1-31 const day = date.getDate(); // 1-31
if ((month === 11 && day >= 28) || (month === 0 && day <= 5)) return 'fireworks'; //new year fireworks december 28 - january 5
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 for (var i = 0; i < rules.length; i++) {
//if (month >= 5 && month <= 7) return 'summer'; // summer june, july, august var rule = rules[i];
if (this.isDateInRange(day, month, rule.StartDay, rule.StartMonth, rule.EndDay, rule.EndMonth)) {
console.log(`Seasonals: Match found for rule "${rule.Name}" (${rule.Theme})`);
return rule.Theme;
}
}
return 'none'; // No rule matched
},
if ((month === 9 && day >= 24) || (month === 10 && day <= 5)) return 'halloween'; // halloween october 24 - november 5 isDateInRange: function(day, month, startDay, startMonth, endDay, endMonth) {
if (startMonth > endMonth) {
if (month >= 8 && month <= 10) return 'autumn'; // autumn september, october, november // Wrapping year (e.g. Dec to Jan)
return this.isDateAfterOrEqual(day, month, startDay, startMonth) ||
return 'none'; // Fallback (no theme) this.isDateBeforeOrEqual(day, month, endDay, endMonth);
} else {
// Normal range
return this.isDateAfterOrEqual(day, month, startDay, startMonth) &&
this.isDateBeforeOrEqual(day, month, endDay, endMonth);
}
},
isDateAfterOrEqual: function(day, month, targetDay, targetMonth) {
if (month > targetMonth) return true;
if (month === targetMonth && day >= targetDay) return true;
return false;
},
isDateBeforeOrEqual: function(day, month, targetDay, targetMonth) {
if (month < targetMonth) return true;
if (month === targetMonth && day <= targetDay) return true;
return false;
}, },
applyTheme(themeName) { applyTheme(themeName) {
const theme = ThemeConfigs[themeName]; const theme = ThemeConfigs[themeName];
if (!theme) { if (!theme) {
console.error(`Theme "${themeName}" not found.`); console.error(`Seasonals: Theme "${themeName}" not found.`);
return; return;
} }
@@ -326,7 +352,7 @@ const SeasonalsManager = {
if (theme.css) this.loadResource('css', theme.css); if (theme.css) this.loadResource('css', theme.css);
if (theme.js) this.loadResource('js', theme.js); if (theme.js) this.loadResource('js', theme.js);
console.log(`Theme "${themeName}" applied.`); console.log(`Seasonals: Theme "${themeName}" applied.`);
}, },
updateThemeContainer(containerClass) { updateThemeContainer(containerClass) {
@@ -355,14 +381,14 @@ const SeasonalsManager = {
link.rel = 'stylesheet'; link.rel = 'stylesheet';
link.href = path; link.href = path;
// link.href = resolvePath(cssPath); // link.href = resolvePath(cssPath);
link.onerror = () => console.error(`Failed to load CSS: ${path}`); link.onerror = () => console.error(`Seasonals: Failed to load CSS: ${path}`);
document.body.appendChild(link); document.body.appendChild(link);
} else if (type === 'js') { } else if (type === 'js') {
const script = document.createElement('script'); const script = document.createElement('script');
script.src = path; script.src = path;
// script.src = resolvePath(jsPath); // script.src = resolvePath(jsPath);
script.defer = true; script.defer = true;
script.onerror = () => console.error(`Failed to load JS: ${path}`); script.onerror = () => console.error(`Seasonals: Failed to load JS: ${path}`);
document.body.appendChild(script); document.body.appendChild(script);
} }
} }

View File

@@ -9,12 +9,20 @@
"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", "version": "1.7.0.11",
"changelog": "- feat: Add client-side toggle option for seasonal settings", "changelog": "- feat: add customizable auto seasonal list via config page\n- feat: add new season theme 'resurrection' by Bioflash257",
"targetAbi": "10.11.0.0", "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", "sourceUrl": "https://git.mahom03-spacecloud.de/CodeDevMLH/Jellyfin-Seasonals-Plugin/releases/download/v1.7.0.11/Jellyfin.Plugin.Seasonals.zip",
"checksum": "73177840d232ea06a0b621b901770d4c", "checksum": "bf7a1660c57152fee961b91c5cf20e7c",
"timestamp": "2026-02-04T12:39:13Z" "timestamp": "2026-02-16T22:06:10Z"
},
{
"version": "1.6.3.0",
"changelog": "- fix path issue on subpath installations",
"targetAbi": "10.11.0.0",
"sourceUrl": "https://git.mahom03-spacecloud.de/CodeDevMLH/Jellyfin-Seasonals-Plugin/releases/download/v1.6.3.0/Jellyfin.Plugin.Seasonals.zip",
"checksum": "e9a1fb6c91b8b48978efb43c72e462a0",
"timestamp": "2026-02-15T01:12:57Z"
}, },
{ {
"version": "1.5.1.0", "version": "1.5.1.0",
@@ -107,6 +115,110 @@
"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.4.0.12",
"changelog": "- feat: Add client-side settings feature for selected media bar settings",
"targetAbi": "10.11.0.0",
"sourceUrl": "https://git.mahom03-spacecloud.de/CodeDevMLH/jellyfin-plugin-media-bar-enhanced/releases/download/v1.4.0.12/Jellyfin.Plugin.MediaBarEnhanced.zip",
"checksum": "26edee51b52dcee4ecf388aa376f3869",
"timestamp": "2026-02-04T18:07:40Z"
},
{
"version": "1.4.0.11",
"changelog": "- feat: Add client-side settings feature for selected media bar settings",
"targetAbi": "10.11.0.0",
"sourceUrl": "https://git.mahom03-spacecloud.de/CodeDevMLH/jellyfin-plugin-media-bar-enhanced/releases/download/v1.4.0.11/Jellyfin.Plugin.MediaBarEnhanced.zip",
"checksum": "ca0b3270eba5871e7a23db6b45bc5048",
"timestamp": "2026-02-04T17:58:14Z"
},
{
"version": "1.4.0.10",
"changelog": "- feat: Add client-side settings feature for selected media bar settings",
"targetAbi": "10.11.0.0",
"sourceUrl": "https://git.mahom03-spacecloud.de/CodeDevMLH/jellyfin-plugin-media-bar-enhanced/releases/download/v1.4.0.10/Jellyfin.Plugin.MediaBarEnhanced.zip",
"checksum": "9618570053f7acef445a034c1e2e044b",
"timestamp": "2026-02-04T17:51:25Z"
},
{
"version": "1.4.0.9",
"changelog": "- feat: Add client-side settings feature for selected media bar settings",
"targetAbi": "10.11.0.0",
"sourceUrl": "https://git.mahom03-spacecloud.de/CodeDevMLH/jellyfin-plugin-media-bar-enhanced/releases/download/v1.4.0.9/Jellyfin.Plugin.MediaBarEnhanced.zip",
"checksum": "29b3ffb9caeab135df88b6313032fc50",
"timestamp": "2026-02-04T17:39:11Z"
},
{
"version": "1.4.0.8",
"changelog": "- feat: Add client-side settings feature for selected media bar settings",
"targetAbi": "10.11.0.0",
"sourceUrl": "https://git.mahom03-spacecloud.de/CodeDevMLH/jellyfin-plugin-media-bar-enhanced/releases/download/v1.4.0.8/Jellyfin.Plugin.MediaBarEnhanced.zip",
"checksum": "ec343204a7cd2c1af4013e645bdddcd3",
"timestamp": "2026-02-04T17:27:22Z"
},
{
"version": "1.4.0.7",
"changelog": "- feat: Add client-side settings feature for selected media bar settings",
"targetAbi": "10.11.0.0",
"sourceUrl": "https://git.mahom03-spacecloud.de/CodeDevMLH/jellyfin-plugin-media-bar-enhanced/releases/download/v1.4.0.7/Jellyfin.Plugin.MediaBarEnhanced.zip",
"checksum": "450e5977228d08b8451b6047e4a6be94",
"timestamp": "2026-02-04T17:09:28Z"
},
{
"version": "1.4.0.6",
"changelog": "- feat: Add client-side settings feature for selected media bar settings",
"targetAbi": "10.11.0.0",
"sourceUrl": "https://git.mahom03-spacecloud.de/CodeDevMLH/jellyfin-plugin-media-bar-enhanced/releases/download/v1.4.0.6/Jellyfin.Plugin.MediaBarEnhanced.zip",
"checksum": "348ebf449ac77fd156e2afbd03e80fce",
"timestamp": "2026-02-04T16:40:21Z"
},
{
"version": "1.4.0.5",
"changelog": "- feat: Add client-side settings feature for selected media bar settings",
"targetAbi": "10.11.0.0",
"sourceUrl": "https://git.mahom03-spacecloud.de/CodeDevMLH/jellyfin-plugin-media-bar-enhanced/releases/download/v1.4.0.5/Jellyfin.Plugin.MediaBarEnhanced.zip",
"checksum": "3ba68bae1c492767bddab2dee2540226",
"timestamp": "2026-02-04T16:23:14Z"
},
{
"version": "1.4.0.4",
"changelog": "- feat: Add client-side settings feature for selected media bar settings",
"targetAbi": "10.11.0.0",
"sourceUrl": "https://git.mahom03-spacecloud.de/CodeDevMLH/jellyfin-plugin-media-bar-enhanced/releases/download/v1.4.0.4/Jellyfin.Plugin.MediaBarEnhanced.zip",
"checksum": "30a15dd883de7656c4480cfa932e9858",
"timestamp": "2026-02-04T16:17:15Z"
},
{
"version": "1.4.0.3",
"changelog": "- feat: Add client-side settings feature for selected media bar settings",
"targetAbi": "10.11.0.0",
"sourceUrl": "https://git.mahom03-spacecloud.de/CodeDevMLH/jellyfin-plugin-media-bar-enhanced/releases/download/v1.4.0.3/Jellyfin.Plugin.MediaBarEnhanced.zip",
"checksum": "9abf21c095e1ae99cdbeb51edb08f370",
"timestamp": "2026-02-04T15:52:11Z"
},
{
"version": "1.4.0.2",
"changelog": "- feat: Add client-side settings feature for selected media bar settings",
"targetAbi": "10.11.0.0",
"sourceUrl": "https://git.mahom03-spacecloud.de/CodeDevMLH/jellyfin-plugin-media-bar-enhanced/releases/download/v1.4.0.2/Jellyfin.Plugin.MediaBarEnhanced.zip",
"checksum": "6026fb8878a51f6dbe18aab1ac006df8",
"timestamp": "2026-02-04T15:45:39Z"
},
{
"version": "1.4.0.1",
"changelog": "- feat: Add client-side settings feature for selected media bar settings",
"targetAbi": "10.11.0.0",
"sourceUrl": "https://git.mahom03-spacecloud.de/CodeDevMLH/jellyfin-plugin-media-bar-enhanced/releases/download/v1.4.0.1/Jellyfin.Plugin.MediaBarEnhanced.zip",
"checksum": "4068c03b1ab809906d64d4faed1c1b0e",
"timestamp": "2026-02-04T15:01:50Z"
},
{
"version": "1.4.0.0",
"changelog": "- feat: Add client-side settings feature for selected media bar settings",
"targetAbi": "10.11.0.0",
"sourceUrl": "https://git.mahom03-spacecloud.de/CodeDevMLH/jellyfin-plugin-media-bar-enhanced/releases/download/v1.4.0.0/Jellyfin.Plugin.MediaBarEnhanced.zip",
"checksum": "20faa2a703dbb46591f4bd09e6ab7ec3",
"timestamp": "2026-02-04T12:49:45Z"
},
{ {
"version": "1.3.0.3", "version": "1.3.0.3",
"changelog": "- feat: Enhance custom media ID functionality with manual trailer override support", "changelog": "- feat: Enhance custom media ID functionality with manual trailer override support",