Compare commits

..

79 Commits

Author SHA1 Message Date
CodeDevMLH
59fe6f7083 Update manifest.json for release v1.7.1.9 [skip ci] 2026-03-08 20:58:25 +00:00
CodeDevMLH
dcb2164ea1 Bump version to 1.7.1.9
All checks were successful
Auto Release Plugin / build-and-release (push) Successful in 44s
2026-03-08 21:57:41 +01:00
CodeDevMLH
2f71f7b46b Improve null checks and conditionals for better stability in localization and slideshow management
Some checks failed
Auto Release Plugin / build-and-release (push) Has been cancelled
2026-03-08 21:57:22 +01:00
CodeDevMLH
70b0a2a192 Update manifest.json for release v1.7.1.8 [skip ci] 2026-03-08 19:29:25 +00:00
CodeDevMLH
300c76890b Bump version to 1.7.1.8 in project file and manifest
All checks were successful
Auto Release Plugin / build-and-release (push) Successful in 46s
2026-03-08 20:28:38 +01:00
CodeDevMLH
64e5441aff Optimize slideshow distance calculation for circular navigation 2026-03-08 20:28:20 +01:00
CodeDevMLH
f47c9dde88 Update manifest.json for release v1.7.1.7 [skip ci] 2026-03-08 19:15:10 +00:00
CodeDevMLH
9d42b5af8d Merge branch 'main' of ssh://git.mahom03-spacecloud.de:44322/CodeDevMLH/jellyfin-plugin-media-bar-enhanced
All checks were successful
Auto Release Plugin / build-and-release (push) Successful in 46s
2026-03-08 20:14:23 +01:00
CodeDevMLH
8c5f66716f Update version to 1.7.1.7 and enhance changelog with new features and fixes 2026-03-08 20:09:58 +01:00
CodeDevMLH
41e6c1032d Add Hide Arrows on Mobile option to configuration and update related logic 2026-03-08 20:09:47 +01:00
CodeDevMLH
fe07fe9f5e Update manifest.json for release v1.7.1.6 [skip ci] 2026-03-08 18:33:15 +00:00
CodeDevMLH
22a7eb8dcb Update version to 1.7.1.6 and enhance changelog with new features and fixes
All checks were successful
Auto Release Plugin / build-and-release (push) Successful in 46s
2026-03-08 19:32:27 +01:00
CodeDevMLH
07658f4fbc Add Exclude Seasonal Content option to configuration and update related logic 2026-03-08 19:32:15 +01:00
CodeDevMLH
25ee5b73b4 Update field descriptions for Max Parental Rating and Max Days Recent inputs to clarify no limit options 2026-03-08 19:21:04 +01:00
CodeDevMLH
8f8e251054 Add max parental rating and max days recent filters to configuration 2026-03-08 19:19:20 +01:00
CodeDevMLH
05529e5627 add new tests 2026-03-08 19:18:30 +01:00
CodeDevMLH
b3e00e8ad6 Update manifest.json for release v1.7.1.5 [skip ci] 2026-03-08 16:17:52 +00:00
CodeDevMLH
39649a6dd3 Bump version to 1.7.1.5 in project file and manifest
All checks were successful
Auto Release Plugin / build-and-release (push) Successful in 49s
2026-03-08 17:17:04 +01:00
CodeDevMLH
11aca32158 Enhance mobile layout by adjusting button container and adding styles for misc-info and genre 2026-03-08 17:17:00 +01:00
CodeDevMLH
9bcb2f984a Update manifest.json for release v1.7.1.4 [skip ci] 2026-03-08 15:58:12 +00:00
CodeDevMLH
c23a614f9f Bump version to 1.7.1.4 and update changelog for new features and fixes
All checks were successful
Auto Release Plugin / build-and-release (push) Successful in 50s
2026-03-08 16:57:21 +01:00
CodeDevMLH
3a367cb2be Add ShowPaginationDots configuration option and update related UI elements 2026-03-08 16:57:16 +01:00
CodeDevMLH
2993bfe3f2 del old tmp 2026-03-08 16:56:18 +01:00
CodeDevMLH
3ffa2c262a Update manifest.json for release v1.7.1.3 [skip ci] 2026-03-08 15:20:14 +00:00
CodeDevMLH
dc88110e9c Bump version to 1.7.1.3 in project file and manifest for release
All checks were successful
Auto Release Plugin / build-and-release (push) Successful in 56s
2026-03-08 16:19:17 +01:00
CodeDevMLH
f9ae62a459 Refactor button-container styles for improved layout and responsiveness 2026-03-08 16:19:07 +01:00
CodeDevMLH
9e2f861213 Update manifest.json for release v1.7.1.2 [skip ci] 2026-03-08 15:08:30 +00:00
CodeDevMLH
4781618a52 Bump version to 1.7.1.2 in project file and manifest for release
All checks were successful
Auto Release Plugin / build-and-release (push) Successful in 50s
2026-03-08 16:07:39 +01:00
CodeDevMLH
2bed81c1f8 Refactor button-container styles for improved layout and responsiveness 2026-03-08 16:07:32 +01:00
CodeDevMLH
292fcfc389 Update manifest.json for release v1.7.1.1 [skip ci] 2026-03-08 14:48:17 +00:00
CodeDevMLH
da718a0e57 Bump version to 1.7.1.1 in project file and manifest for release
All checks were successful
Auto Release Plugin / build-and-release (push) Successful in 49s
2026-03-08 15:47:26 +01:00
CodeDevMLH
95a8907496 test2 2026-03-08 15:47:09 +01:00
CodeDevMLH
0498756529 Update manifest.json for release v1.7.1.0 [skip ci] 2026-03-08 14:32:40 +00:00
CodeDevMLH
f1d92080b2 Bump version to 1.7.1.0 and update changelog for mobile button fix
All checks were successful
Auto Release Plugin / build-and-release (push) Successful in 48s
2026-03-08 15:31:52 +01:00
CodeDevMLH
586b57d23e Enhance button layout in button-container for better responsiveness 2026-03-08 15:30:21 +01:00
CodeDevMLH
47b05a82ba Update changelog for version 1.7.0.14: enhance iframe security, fix playback issues on iOS/MacOS, disable animations for TV layout, remove list.txt functionality, and improve logging. [skip ci] 2026-03-06 04:30:04 +01:00
CodeDevMLH
cb45e0cb43 Update manifest.json for release v1.7.0.14 [skip ci] 2026-03-06 03:13:48 +00:00
CodeDevMLH
19246c3a19 Bump version to 1.7.0.14
All checks were successful
Auto Release Plugin / build-and-release (push) Successful in 52s
2026-03-06 04:12:58 +01:00
CodeDevMLH
99b4b09306 Disable animations and backdrop filters for TV layout on high pixel density screens 2026-03-06 04:12:52 +01:00
CodeDevMLH
563a1e5484 Update manifest.json for release v1.7.0.13 [skip ci] 2026-03-06 03:10:54 +00:00
CodeDevMLH
788708370d Bump version to 1.7.0.13
All checks were successful
Auto Release Plugin / build-and-release (push) Successful in 52s
2026-03-06 04:10:04 +01:00
CodeDevMLH
4bb0de11da Remove fetchItemIdsFromList function and related logic for item ID retrieval 2026-03-06 04:09:36 +01:00
CodeDevMLH
d2abfc6b46 Enhance logging in media bar with contextual messages 2026-03-06 04:06:18 +01:00
CodeDevMLH
8ba14d4df0 Update manifest.json for release v1.7.0.12 [skip ci] 2026-03-06 02:26:17 +00:00
CodeDevMLH
538138fcf3 Bump version to 1.7.0.12
All checks were successful
Auto Release Plugin / build-and-release (push) Successful in 52s
2026-03-06 03:25:23 +01:00
CodeDevMLH
08efb11d95 Remove picture-in-picture support from YouTube player integration 2026-03-06 03:25:08 +01:00
CodeDevMLH
de62c794f9 Update manifest.json for release v1.7.0.11 [skip ci] 2026-03-06 02:19:18 +00:00
CodeDevMLH
fc2491a4ef Bump version to 1.7.0.11
All checks were successful
Auto Release Plugin / build-and-release (push) Successful in 52s
2026-03-06 03:18:26 +01:00
CodeDevMLH
03276d7722 Enhance YouTube player integration with fullscreen and picture-in-picture support 2026-03-06 03:18:13 +01:00
CodeDevMLH
8676160e7b Update manifest.json for release v1.7.0.10 [skip ci] 2026-03-06 02:02:52 +00:00
CodeDevMLH
c562560bc0 Bump version to 1.7.0.10
All checks were successful
Auto Release Plugin / build-and-release (push) Successful in 51s
2026-03-06 03:02:01 +01:00
CodeDevMLH
98474d4ff6 Refactor YouTube player integration to use iframe for improved performance and security 2026-03-06 03:01:34 +01:00
CodeDevMLH
14c0eb43ed Update manifest.json for release v1.7.0.9 [skip ci] 2026-03-06 01:24:40 +00:00
CodeDevMLH
c4cbeda2b8 Bump version to 1.7.0.9
All checks were successful
Auto Release Plugin / build-and-release (push) Successful in 51s
2026-03-06 02:23:50 +01:00
CodeDevMLH
53ad568be4 Fix active slide detection logic in SlideCreator for improved video playback handling 2026-03-06 02:23:13 +01:00
CodeDevMLH
fba64bd0f6 Update manifest.json for release v1.7.0.8 [skip ci] 2026-03-06 01:17:19 +00:00
CodeDevMLH
3da16c4c5c Bump version to 1.7.0.8
All checks were successful
Auto Release Plugin / build-and-release (push) Successful in 52s
2026-03-06 02:16:28 +01:00
CodeDevMLH
c7cd7be3ee Add low-power device detection and adjust video playback settings 2026-03-06 02:16:09 +01:00
CodeDevMLH
6d90523eef Update manifest.json for release v1.7.0.7 [skip ci] 2026-03-06 00:45:38 +00:00
CodeDevMLH
2a3e8057a1 Bump version to 1.7.0.7
All checks were successful
Auto Release Plugin / build-and-release (push) Successful in 51s
2026-03-06 01:44:24 +01:00
CodeDevMLH
42026b0ee8 test revert 2026-03-06 01:44:04 +01:00
CodeDevMLH
64dbc3cfd3 Update manifest.json for release v1.7.0.6 [skip ci] 2026-03-06 00:25:04 +00:00
CodeDevMLH
c998266dd7 Bump version to 1.7.0.6
All checks were successful
Auto Release Plugin / build-and-release (push) Successful in 53s
2026-03-06 01:24:11 +01:00
CodeDevMLH
9b941e5a77 test again 2026-03-06 01:23:49 +01:00
CodeDevMLH
1d70d7166d Update manifest.json for release v1.7.0.5 [skip ci] 2026-03-05 23:59:06 +00:00
CodeDevMLH
5331f0faf1 Bump version to 1.7.0.5
All checks were successful
Auto Release Plugin / build-and-release (push) Successful in 50s
2026-03-06 00:58:15 +01:00
CodeDevMLH
0508188705 test nochmal
Some checks failed
Auto Release Plugin / build-and-release (push) Has been cancelled
2026-03-06 00:57:58 +01:00
CodeDevMLH
cc861f4263 Update manifest.json for release v1.7.0.4 [skip ci] 2026-03-05 23:35:23 +00:00
CodeDevMLH
10e6cdc4a2 Bump version to 1.7.0.4
All checks were successful
Auto Release Plugin / build-and-release (push) Successful in 51s
2026-03-06 00:34:32 +01:00
CodeDevMLH
a8c7faab6b Add Safari support for YouTube video playback using plain iframe embed 2026-03-06 00:34:19 +01:00
CodeDevMLH
6df390fa18 Update manifest.json for release v1.7.0.3 [skip ci] 2026-03-05 23:23:07 +00:00
CodeDevMLH
d0c3d7ee4d Bump version to 1.7.0.3 (test 3)
All checks were successful
Auto Release Plugin / build-and-release (push) Successful in 52s
2026-03-06 00:22:14 +01:00
CodeDevMLH
bc621aacdf test 2026-03-06 00:21:54 +01:00
CodeDevMLH
73eb30d671 Update manifest.json for release v1.7.0.2 [skip ci] 2026-03-05 22:44:56 +00:00
CodeDevMLH
2cfbec95c9 Bump version to 1.7.0.2
All checks were successful
Auto Release Plugin / build-and-release (push) Successful in 50s
2026-03-05 23:44:05 +01:00
CodeDevMLH
08fc29cba3 Improve video backdrop handling: optimize autoplay logic for fresh loads and enhance Safari compatibility with loadedmetadata event 2026-03-05 23:43:52 +01:00
CodeDevMLH
0d6b835486 Update manifest.json for release v1.7.0.1 [skip ci] 2026-03-05 21:43:51 +00:00
CodeDevMLH
bf620e447f Bump version to 1.7.0.1
All checks were successful
Auto Release Plugin / build-and-release (push) Successful in 53s
2026-03-05 22:42:58 +01:00
CodeDevMLH
3117d627dd Enhance video playback handling: enable autoplay, adjust mute settings for Safari compatibility, and improve navigation checks during autoplay. 2026-03-05 22:42:40 +01:00
12 changed files with 724 additions and 3969 deletions

View File

@@ -15,9 +15,12 @@ namespace Jellyfin.Plugin.MediaBarEnhanced.Configuration
public int MaxMovies { get; set; } = 15; public int MaxMovies { get; set; } = 15;
public int MaxTvShows { get; set; } = 15; public int MaxTvShows { get; set; } = 15;
public int MaxItems { get; set; } = 500; public int MaxItems { get; set; } = 500;
public int MaxParentalRating { get; set; } = 0;
public int MaxDaysRecent { get; set; } = 0;
public int PreloadCount { get; set; } = 3; public int PreloadCount { get; set; } = 3;
public int FadeTransitionDuration { get; set; } = 500; public int FadeTransitionDuration { get; set; } = 500;
public int MaxPaginationDots { get; set; } = 15; public int MaxPaginationDots { get; set; } = 15;
public bool ShowPaginationDots { get; set; } = true;
public bool SlideAnimationEnabled { get; set; } = true; public bool SlideAnimationEnabled { get; set; } = true;
public bool EnableVideoBackdrop { get; set; } = true; public bool EnableVideoBackdrop { get; set; } = true;
public bool UseSponsorBlock { get; set; } = true; public bool UseSponsorBlock { get; set; } = true;
@@ -33,10 +36,12 @@ namespace Jellyfin.Plugin.MediaBarEnhanced.Configuration
public bool EnableLoadingScreen { get; set; } = true; public bool EnableLoadingScreen { get; set; } = true;
public bool EnableKeyboardControls { get; set; } = true; public bool EnableKeyboardControls { get; set; } = true;
public bool AlwaysShowArrows { get; set; } = false; public bool AlwaysShowArrows { get; set; } = false;
public bool HideArrowsOnMobile { get; set; } = true;
public string CustomMediaIds { get; set; } = ""; public string CustomMediaIds { get; set; } = "";
public bool EnableCustomMediaIds { get; set; } = true; public bool EnableCustomMediaIds { get; set; } = true;
public string PreferredVideoQuality { get; set; } = "Auto"; public string PreferredVideoQuality { get; set; } = "Auto";
public bool EnableSeasonalContent { get; set; } = false; public bool EnableSeasonalContent { get; set; } = false;
public bool ExcludeSeasonalContent { get; set; } = true;
public string SeasonalSections { get; set; } = "[]"; public string SeasonalSections { get; set; } = "[]";
public bool IsEnabled { get; set; } = true; public bool IsEnabled { get; set; } = true;
public bool EnableClientSideSettings { get; set; } = false; public bool EnableClientSideSettings { get; set; } = false;

View File

@@ -43,7 +43,8 @@
<h2 class="sectionTitle">Main Plugin Settings</h2> <h2 class="sectionTitle">Main Plugin Settings</h2>
<div class="checkboxContainer checkboxContainer-withDescription"> <div class="checkboxContainer checkboxContainer-withDescription">
<label> <label>
<input is="emby-checkbox" type="checkbox" id="MediaBarIsEnabled" name="MediaBarIsEnabled" /> <input is="emby-checkbox" type="checkbox" id="MediaBarIsEnabled"
name="MediaBarIsEnabled" />
<span>Enable Media Bar Enhanced Plugin</span> <span>Enable Media Bar Enhanced Plugin</span>
</label> </label>
<div class="fieldDescription">Enable or disable the entire plugin functionality.</div> <div class="fieldDescription">Enable or disable the entire plugin functionality.</div>
@@ -57,23 +58,27 @@
<div class="fieldDescription">Show trailers as background if available.<br>Adds a <div class="fieldDescription">Show trailers as background if available.<br>Adds a
mute/unmute and pause/play button to control the video in the right top corner.</div> mute/unmute and pause/play button to control the video in the right top corner.</div>
</div> </div>
<div class="checkboxContainer checkboxContainer-withDescription" id="PreferLocalTrailersContainer"> <div class="checkboxContainer checkboxContainer-withDescription"
id="PreferLocalTrailersContainer">
<label> <label>
<input is="emby-checkbox" type="checkbox" id="PreferLocalTrailers" <input is="emby-checkbox" type="checkbox" id="PreferLocalTrailers"
name="PreferLocalTrailers" /> name="PreferLocalTrailers" />
<span>Prefer Local Trailers</span> <span>Prefer Local Trailers</span>
</label> </label>
<div class="fieldDescription">If enabled, local trailers will be preferred over remote (YouTube) trailers.</div> <div class="fieldDescription">If enabled, local trailers will be preferred over remote
(YouTube) trailers.</div>
</div> </div>
<div class="checkboxContainer checkboxContainer-withDescription" id="PreferLocalBackdropsContainer"> <div class="checkboxContainer checkboxContainer-withDescription"
id="PreferLocalBackdropsContainer">
<label> <label>
<input is="emby-checkbox" type="checkbox" id="PreferLocalBackdrops" <input is="emby-checkbox" type="checkbox" id="PreferLocalBackdrops"
name="PreferLocalBackdrops" /> name="PreferLocalBackdrops" />
<span>Prefer Local Backdrops / Theme Videos</span> <span>Prefer Local Backdrops / Theme Videos</span>
</label> </label>
<div class="fieldDescription">If enabled, local backdrop videos (Theme Videos) will be preferred over remote and local trailers.</div> <div class="fieldDescription">If enabled, local backdrop videos (Theme Videos) will be
preferred over remote and local trailers.</div>
</div> </div>
<div class="checkboxContainer checkboxContainer-withDescription"> <div class="checkboxContainer checkboxContainer-withDescription" id="WaitForTrailerToEndContainer">
<label> <label>
<input is="emby-checkbox" type="checkbox" id="WaitForTrailerToEnd" <input is="emby-checkbox" type="checkbox" id="WaitForTrailerToEnd"
name="WaitForTrailerToEnd" /> name="WaitForTrailerToEnd" />
@@ -81,7 +86,7 @@
</label> </label>
<div class="fieldDescription">Delay slide transition until trailer finishes.</div> <div class="fieldDescription">Delay slide transition until trailer finishes.</div>
</div> </div>
<div class="checkboxContainer checkboxContainer-withDescription"> <div class="checkboxContainer checkboxContainer-withDescription" id="EnableMobileVideoContainer">
<label> <label>
<input is="emby-checkbox" type="checkbox" id="EnableMobileVideo" <input is="emby-checkbox" type="checkbox" id="EnableMobileVideo"
name="EnableMobileVideo" /> name="EnableMobileVideo" />
@@ -95,8 +100,8 @@
name="ShowTrailerButton" /> name="ShowTrailerButton" />
<span>Show Trailer Button</span> <span>Show Trailer Button</span>
</label> </label>
<div class="fieldDescription">Display a button to open trailer in modal. Only visible if <div class="fieldDescription">Display a button to open trailer in modal. Button only
trailer is not set as backdrop or if no trailer is available.</div> visible if trailer is not set as backdrop.</div>
</div> </div>
</div> </div>
@@ -111,7 +116,8 @@
<span>Enable Custom Media IDs</span> <span>Enable Custom Media IDs</span>
</label> </label>
<div class="fieldDescription">If enabled, the slideshow will show the items listed <div class="fieldDescription">If enabled, the slideshow will show the items listed
below as the default content. If the list is empty, random items from your library are used.</div> below as the default content. If the list is empty, random items from your library are
used.</div>
</div> </div>
<div class="checkboxContainer checkboxContainer-withDescription"> <div class="checkboxContainer checkboxContainer-withDescription">
<label> <label>
@@ -119,35 +125,47 @@
name="ApplyLimitsToCustomIds" /> name="ApplyLimitsToCustomIds" />
<span>Apply Limits to Custom IDs</span> <span>Apply Limits to Custom IDs</span>
</label> </label>
<div class="fieldDescription">If enabled, the Max Items limit (Advanced &rarr; Content Limits) will also apply to Custom Media IDs and Collections. By default, custom lists are not limited.</div> <div class="fieldDescription">If enabled, the Max Items limit (Advanced &rarr; Content
Limits) will also apply to Custom Media IDs and Collections. By default, custom lists
are not limited.</div>
</div> </div>
<div id="customMediaIdsContainer"> <div id="customMediaIdsContainer">
<div class="inputContainer"> <div class="inputContainer">
<label class="inputLabel inputLabelUnfocused" for="CustomMediaIds">Default Media/Collection/Playlist IDs (Newline or Comma-separated)</label> <label class="inputLabel inputLabelUnfocused" for="CustomMediaIds">Default
<textarea class="emby-textarea" is="emby-textarea" id="CustomMediaIds" name="CustomMediaIds" Media/Collection/Playlist IDs (Newline or Comma-separated)</label>
<textarea class="emby-textarea" is="emby-textarea" id="CustomMediaIds"
name="CustomMediaIds"
style="width: 100%; height: 150px; font-family: monospace;"></textarea> style="width: 100%; height: 150px; font-family: monospace;"></textarea>
<div class="fieldDescription">Enter the IDs of the items you want to show in the slideshow as your default content. You can separate them by new line or comma. <div class="fieldDescription">Enter the IDs of the items you want to show in the
slideshow as your default content. You can separate them by new line or comma.
<br><br> <br><br>
<b>Manual Trailer/Video Override:</b> You can specify a YouTube URL <b>OR</b> a Jellyfin Item ID (e.g. for a Theme Video) for an item by adding it in <b>Manual Trailer/Video Override:</b> You can specify a YouTube URL <b>OR</b> a
brackets: <br> <code>e.g. ID DESCRIPTION [https://youtu.be/...]</code> or <code>ID [JellyfinItemId] DESCRIPTION</code>. Jellyfin Item ID (e.g. for a Theme Video) for an item by adding it in
brackets: <br> <code>e.g. ID DESCRIPTION [https://youtu.be/...]</code> or
<code>ID [JellyfinItemId] DESCRIPTION</code>.
<br> <br>
Methods: Methods:
<ul> <ul>
<li><b>YouTube URL:</b> Play a remote trailer from YouTube.</li> <li><b>YouTube URL:</b> Play a remote trailer from YouTube.</li>
<li><b>Jellyfin Item ID (GUID):</b> Play the video of another library item (e.g. a Theme Video or Backdrop Video) using the native player.</li> <li><b>Jellyfin Item ID (GUID):</b> Play the video of another library item (e.g.
a Theme Video or Backdrop Video) using the native player.</li>
</ul> </ul>
You can also add a description after the ID using any separator like space, pipe (|) or dash (-): <br>e.g. <code>ID DESCRIPTION</code> or <code>ID | DESCRIPTION</code> You can also add a description after the ID using any separator like space, pipe (|)
or dash (-): <br>e.g. <code>ID DESCRIPTION</code> or <code>ID | DESCRIPTION</code>
<br><br> <br><br>
<b>Note:</b> If using a <b>Collection Name</b> (instead of an ID) combined with a description, you <b>MUST</b> use the pipe (|) separator. <b>Note:</b> If using a <b>Collection Name</b> (instead of an ID) combined with a
description, you <b>MUST</b> use the pipe (|) separator.
<br> <br>
<b>Note:</b> The separator <b>MUST NOT</b> be a hex character (0-9, a-f). <b>Note:</b> The separator <b>MUST NOT</b> be a hex character (0-9, a-f).
</div> </div>
<p>You can find the IDs of your items in the URL of the item page in the web interface.<br> <p>You can find the IDs of your items in the URL of the item page in the web
interface.<br>
Example: Example:
<code>https://your-jellyfin-url/web/#/details?id=<b style="color:red;">your-item-id</b>&serverId=your-server-id</code><br><br> <code>https://your-jellyfin-url/web/#/details?id=<b style="color:red;">your-item-id</b>&serverId=your-server-id</code><br><br>
You can also insert a name of a collection or playlist to fetch the IDs of all items in You can also insert a name of a collection or playlist to fetch the IDs of all items
it (will take the first hit.<br><b>Note:</b> There is currently no feedback if the name in it (will take the first hit.<br><b>Note:</b> There is currently no feedback if
resolution succeeded, you will have to look if the bar displays the correct items). the name resolution succeeded, you will have to look if the bar displays the correct
items).
</p> </p>
</div> </div>
</div> </div>
@@ -160,14 +178,26 @@
name="EnableSeasonalContent" /> name="EnableSeasonalContent" />
<span>Enable Seasonal Content</span> <span>Enable Seasonal Content</span>
</label> </label>
<div class="fieldDescription">When enabled, seasonal sections below will override the default list or random selection <div class="fieldDescription">When enabled, seasonal sections below will override the
during their active date ranges. If no season matches the current date, the default Custom Media IDs above or random selection are used as fallback.</div> default list or random selection
during their active date ranges. If no season matches the current date, the default
Custom Media IDs above or random selection are used as fallback.</div>
</div> </div>
<div id="seasonalContentContainer" style="display: none;"> <div id="seasonalContentContainer" style="display: none;">
<div class="checkboxContainer checkboxContainer-withDescription">
<label>
<input is="emby-checkbox" type="checkbox" id="ExcludeSeasonalContent"
name="ExcludeSeasonalContent" />
<span>Exclude Seasonal Content from Random Lists</span>
</label>
<div class="fieldDescription">When enabled, any items defined in your Seasonal Sections below will be explicitly excluded from being shown when the plugin pulls random items from your library.</div>
</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>Define seasonal rules to automatically select a selection of items based on the date. Rules are evaluated from top to bottom. The first matching rule wins.</div> <div>Define seasonal rules to automatically select a selection of items based on the
date. Rules are evaluated from top to bottom. The first matching rule wins.</div>
</div> </div>
<div id="seasonalSectionsList"></div> <div id="seasonalSectionsList"></div>
@@ -189,8 +219,9 @@
name="SlideAnimationEnabled" /> name="SlideAnimationEnabled" />
<span>Enable Slide Animations</span> <span>Enable Slide Animations</span>
</label> </label>
<div class="fieldDescription">Enable the zooming-in effect on background images when a new slide is <div class="fieldDescription">Enable the zooming-in effect on background images when a new
shown (does not affect trailer backdrops). Attention: This may cause performance issues on weaker client hardware.</div> slide is shown (does not affect trailer backdrops). Attention: This may cause
performance issues on weaker client hardware.</div>
</div> </div>
<div class="checkboxContainer checkboxContainer-withDescription"> <div class="checkboxContainer checkboxContainer-withDescription">
<label> <label>
@@ -199,7 +230,8 @@
<span>Enable Client-Side Settings</span> <span>Enable Client-Side Settings</span>
</label> </label>
<div class="fieldDescription">If enabled, users will see a media bar icon in the header to <div class="fieldDescription">If enabled, users will see a media bar icon in the header to
override settings (like disabling the bar or trailer backdrops) locally on their device.</div> override settings (like disabling the bar or trailer backdrops) locally on their device.
</div>
</div> </div>
<div class="checkboxContainer checkboxContainer-withDescription"> <div class="checkboxContainer checkboxContainer-withDescription">
<label> <label>
@@ -207,7 +239,8 @@
name="RandomizeThemeVideos" /> name="RandomizeThemeVideos" />
<span>Randomize Backdrop Video</span> <span>Randomize Backdrop Video</span>
</label> </label>
<div class="fieldDescription">If enabled, a random video from the backdrops/theme videos will be selected instead of the first one (if multiple exist).</div> <div class="fieldDescription">If enabled, a random video from the backdrops/theme videos
will be selected instead of the first one (if multiple exist).</div>
</div> </div>
<div class="checkboxContainer checkboxContainer-withDescription"> <div class="checkboxContainer checkboxContainer-withDescription">
<label> <label>
@@ -215,18 +248,22 @@
name="RandomizeLocalTrailers" /> name="RandomizeLocalTrailers" />
<span>Randomize Local Trailer</span> <span>Randomize Local Trailer</span>
</label> </label>
<div class="fieldDescription">If enabled, a random local trailer will be selected instead of the first one (if multiple exist).</div> <div class="fieldDescription">If enabled, a random local trailer will be selected instead of
the first one (if multiple exist).</div>
</div> </div>
<div class="checkboxContainer checkboxContainer-withDescription"> <div class="checkboxContainer checkboxContainer-withDescription">
<label> <label>
<input is="emby-checkbox" type="checkbox" id="UseSponsorBlock" name="UseSponsorBlock" /> <input is="emby-checkbox" type="checkbox" id="UseSponsorBlock" name="UseSponsorBlock" />
<span>Use SponsorBlock</span> <span>Use SponsorBlock</span>
</label> </label>
<div class="fieldDescription">Skip intro/outro segments in YouTube trailers (if data is available).</div> <div class="fieldDescription">Skip intro/outro segments in YouTube trailers (if data is
available).</div>
</div> </div>
<div class="selectContainer"> <div class="selectContainer">
<label class="selectLabel" for="PreferredVideoQuality">Preferred YouTube Quality</label> <label class="selectLabel" for="PreferredVideoQuality">Preferred YouTube Quality</label>
<select is="emby-select" id="PreferredVideoQuality" name="PreferredVideoQuality" class="selectLayout emby-select-withcolor emby-select" style="width: 100%; -webkit-appearance: menulist; appearance: menulist;"> <select is="emby-select" id="PreferredVideoQuality" name="PreferredVideoQuality"
class="selectLayout emby-select-withcolor emby-select"
style="width: 100%; -webkit-appearance: menulist; appearance: menulist;">
<option value="Auto">Auto (Smart)</option> <option value="Auto">Auto (Smart)</option>
<option value="Maximum">Maximum (4K+)</option> <option value="Maximum">Maximum (4K+)</option>
<option value="1080p">1080p</option> <option value="1080p">1080p</option>
@@ -241,7 +278,9 @@
<span>Start Muted</span> <span>Start Muted</span>
</label> </label>
<div class="fieldDescription">Start trailer video playback muted. (Known issue: In the <div class="fieldDescription">Start trailer video playback muted. (Known issue: In the
Android/IOS app, backdrop trailers are always muted.)<br><b style="color:#ffcc00">Warning:</b> Disabling this may cause autoplay to fail on certain browsers due to strict autoplay policies.</div> Android/IOS app, backdrop trailers are always muted.)<br>
<b style="color:#ffcc00">Warning:</b> Disabling this may cause autoplay to fail on
certain browsers due to strict autoplay policies.</div>
</div> </div>
<div class="checkboxContainer checkboxContainer-withDescription"> <div class="checkboxContainer checkboxContainer-withDescription">
<label> <label>
@@ -260,16 +299,23 @@
<span>Enable Loading Screen</span> <span>Enable Loading Screen</span>
</label> </label>
<div class="fieldDescription">Show a loading screen while the slideshow initializes. (You <div class="fieldDescription">Show a loading screen while the slideshow initializes. (You
may have to reload the page twice)</div> may have to reload the page twice (after changing this setting) to take effect)</div>
</div> </div>
<div class="checkboxContainer checkboxContainer-withDescription"> <div class="checkboxContainer checkboxContainer-withDescription">
<label> <label>
<input is="emby-checkbox" type="checkbox" id="AlwaysShowArrows" <input is="emby-checkbox" type="checkbox" id="AlwaysShowArrows"
name="AlwaysShowArrows" /> name="AlwaysShowArrows" />
<span>Always Show Arrows</span> <span>Always Show Arrow Navigation Buttons</span>
</label> </label>
<div class="fieldDescription">If enabled, navigation arrows will always be visible instead <div class="fieldDescription">Force the UI arrow navigation buttons to always be visible instead of only when hovered.</div>
of only on hover.</div> </div>
<div class="checkboxContainer checkboxContainer-withDescription">
<label>
<input is="emby-checkbox" type="checkbox" id="HideArrowsOnMobile"
name="HideArrowsOnMobile" />
<span>Hide Arrows on Mobile</span>
</label>
<div class="fieldDescription">Completely disable the navigation arrows on mobile devices (since swiping is available).</div>
</div> </div>
<div class="checkboxContainer checkboxContainer-withDescription"> <div class="checkboxContainer checkboxContainer-withDescription">
<label> <label>
@@ -281,6 +327,7 @@
Space (pause), M (mute/unmute)) for Space (pause), M (mute/unmute)) for
the slideshow.</div> the slideshow.</div>
</div> </div>
<hr style="max-width: 800px; margin: 1em 0;">
<h2 class="sectionTitle">Time Settings</h2> <h2 class="sectionTitle">Time Settings</h2>
<p>Leave a setting blank to use the default value.</p> <p>Leave a setting blank to use the default value.</p>
@@ -324,11 +371,14 @@
<div class="fieldDescription">Minimum distance in pixels for a swipe to be registered (for <div class="fieldDescription">Minimum distance in pixels for a swipe to be registered (for
mobile).</div> mobile).</div>
</div> </div>
<hr style="max-width: 800px; margin: 1em 0;">
<h2 class="sectionTitle">Content Sorting</h2> <h2 class="sectionTitle">Content Sorting and Filtering</h2>
<div class="selectContainer"> <div class="selectContainer">
<label class="selectLabel" for="SortBy">Sort By</label> <label class="selectLabel" for="SortBy">Sort By</label>
<select is="emby-select" id="SortBy" name="SortBy" class="selectLayout emby-select-withcolor emby-select" style="width: 100%; -webkit-appearance: menulist; appearance: menulist;"> <select is="emby-select" id="SortBy" name="SortBy"
class="selectLayout emby-select-withcolor emby-select"
style="width: 100%; -webkit-appearance: menulist; appearance: menulist;">
<option value="Random">Random</option> <option value="Random">Random</option>
<option value="Original">Original (Custom List Order)</option> <option value="Original">Original (Custom List Order)</option>
<option value="PremiereDate">Premiere Date</option> <option value="PremiereDate">Premiere Date</option>
@@ -342,15 +392,38 @@
</div> </div>
<div class="selectContainer"> <div class="selectContainer">
<label class="selectLabel" for="SortOrder">Sort Order</label> <label class="selectLabel" for="SortOrder">Sort Order</label>
<select is="emby-select" id="SortOrder" name="SortOrder" class="selectLayout emby-select-withcolor emby-select" style="width: 100%; -webkit-appearance: menulist; appearance: menulist;"> <select is="emby-select" id="SortOrder" name="SortOrder"
class="selectLayout emby-select-withcolor emby-select"
style="width: 100%; -webkit-appearance: menulist; appearance: menulist;">
<option value="Ascending">Ascending</option> <option value="Ascending">Ascending</option>
<option value="Descending">Descending</option> <option value="Descending">Descending</option>
</select> </select>
<div class="fieldDescription">Sort items in Ascending or Descending order.</div> <div class="fieldDescription">Sort items in Ascending or Descending order.</div>
</div> </div>
<div class="fieldDescription" style="margin-bottom: 2em; color: #ffcc00;"> <div class="fieldDescription" style="margin-bottom: 2em; color: #ffcc00;">
<b>Note:</b> Sorting settings apply to both Server content and Custom IDs. 'Original' preserves Custom List order. <b>Note:</b> Sorting settings apply to both Server content and Custom IDs. 'Original'
preserves Custom List order.
</div> </div>
<div class="inputContainer">
<label class="inputLabel inputLabelUnfocused" for="MaxParentalRating">Max Parental Rating (Age Limit)</label>
<input is="emby-input" type="number" id="MaxParentalRating" name="MaxParentalRating" />
<div class="fieldDescription">Items exceeding this age rating will not be shown. Leave blank or set to 0 for no limit. Examples: 12, 16, 18.</div>
</div>
<div class="inputContainer">
<label class="inputLabel inputLabelUnfocused" for="MaxDaysRecent">Max Days Recent</label>
<input is="emby-input" type="number" id="MaxDaysRecent" name="MaxDaysRecent" />
<div class="fieldDescription">Only show items added in the last X days. Leave blank or set to 0 for no limit. Example: 30.</div>
</div>
<div class="checkboxContainer checkboxContainer-withDescription">
<label>
<input is="emby-checkbox" type="checkbox" id="IncludeWatchedContent"
name="IncludeWatchedContent" />
<span>Include Watched Content</span>
</label>
<div class="fieldDescription">If enabled, watched content will be included in the random
selection results.</div>
</div>
<hr style="max-width: 800px; margin: 1em 0;">
<h2 class="sectionTitle">Content Limits</h2> <h2 class="sectionTitle">Content Limits</h2>
<p>Leave a setting blank to use the default value.</p> <p>Leave a setting blank to use the default value.</p>
@@ -377,6 +450,15 @@
<input is="emby-input" type="number" id="PreloadCount" name="PreloadCount" /> <input is="emby-input" type="number" id="PreloadCount" name="PreloadCount" />
<div class="fieldDescription">Number of slides to preload.</div> <div class="fieldDescription">Number of slides to preload.</div>
</div> </div>
<div class="checkboxContainer checkboxContainer-withDescription">
<label>
<input is="emby-checkbox" type="checkbox" id="ShowPaginationDots"
name="ShowPaginationDots" />
<span>Show Pagination Dots/Counter</span>
</label>
<div class="fieldDescription">Show or hide the pagination dots/counter navigation at the
bottom of the slideshow.</div>
</div>
<div class="inputContainer"> <div class="inputContainer">
<label class="inputLabel inputLabelUnfocused" for="MaxPaginationDots">Max Pagination <label class="inputLabel inputLabelUnfocused" for="MaxPaginationDots">Max Pagination
Dots</label> Dots</label>
@@ -391,14 +473,6 @@
<input is="emby-input" type="number" id="MaxPlotLength" name="MaxPlotLength" /> <input is="emby-input" type="number" id="MaxPlotLength" name="MaxPlotLength" />
<div class="fieldDescription">Maximum characters for the plot summary.</div> <div class="fieldDescription">Maximum characters for the plot summary.</div>
</div> </div>
<div class="checkboxContainer checkboxContainer-withDescription">
<label>
<input is="emby-checkbox" type="checkbox" id="IncludeWatchedContent"
name="IncludeWatchedContent" />
<span>Include Watched Content</span>
</label>
<div class="fieldDescription">If enabled, watched content will be included in the random selection results.</div>
</div>
</div> </div>
<div <div
@@ -459,7 +533,8 @@
'EnableSeasonalContent', 'EnableClientSideSettings', 'SortBy', 'SortOrder', 'EnableSeasonalContent', 'EnableClientSideSettings', 'SortBy', 'SortOrder',
'PreferLocalTrailers', 'ApplyLimitsToCustomIds', 'SeasonalSections', 'PreferLocalTrailers', 'ApplyLimitsToCustomIds', 'SeasonalSections',
'PreferLocalBackdrops', 'RandomizeThemeVideos', 'RandomizeLocalTrailers', 'PreferLocalBackdrops', 'RandomizeThemeVideos', 'RandomizeLocalTrailers',
'IncludeWatchedContent' 'IncludeWatchedContent', 'ShowPaginationDots', 'MaxParentalRating',
'MaxDaysRecent', 'ExcludeSeasonalContent', 'HideArrowsOnMobile'
]; ];
// Manual mapping for MediaBarIsEnabled -> IsEnabled, to avoid conflicts with other plugins // Manual mapping for MediaBarIsEnabled -> IsEnabled, to avoid conflicts with other plugins
@@ -508,7 +583,7 @@
// Remove existing listeners to avoid duplicates if re-attached // Remove existing listeners to avoid duplicates if re-attached
var newBtn = addSeasonBtn.cloneNode(true); var newBtn = addSeasonBtn.cloneNode(true);
addSeasonBtn.parentNode.replaceChild(newBtn, addSeasonBtn); addSeasonBtn.parentNode.replaceChild(newBtn, addSeasonBtn);
newBtn.addEventListener('click', function() { newBtn.addEventListener('click', function () {
MediaBarEnhancedConfigurationPage.addSeasonalSection(page); MediaBarEnhancedConfigurationPage.addSeasonalSection(page);
}); });
} }
@@ -517,14 +592,20 @@
var enableVideoBackdropCheckbox = page.querySelector('#EnableVideoBackdrop'); var enableVideoBackdropCheckbox = page.querySelector('#EnableVideoBackdrop');
var preferLocalContainer = page.querySelector('#PreferLocalTrailersContainer'); var preferLocalContainer = page.querySelector('#PreferLocalTrailersContainer');
var preferLocalBackdropsContainer = page.querySelector('#PreferLocalBackdropsContainer'); var preferLocalBackdropsContainer = page.querySelector('#PreferLocalBackdropsContainer');
var waitForTrailerContainer = page.querySelector('#WaitForTrailerToEndContainer');
var enableMobileVideoContainer = page.querySelector('#EnableMobileVideoContainer');
function updatePreferLocalVisibility() { function updatePreferLocalVisibility() {
if (enableVideoBackdropCheckbox && enableVideoBackdropCheckbox.checked) { if (enableVideoBackdropCheckbox && enableVideoBackdropCheckbox.checked) {
if (preferLocalContainer) preferLocalContainer.style.display = 'block'; if (preferLocalContainer) preferLocalContainer.style.display = 'block';
if (preferLocalBackdropsContainer) preferLocalBackdropsContainer.style.display = 'block'; if (preferLocalBackdropsContainer) preferLocalBackdropsContainer.style.display = 'block';
if (waitForTrailerContainer) waitForTrailerContainer.style.display = 'block';
if (enableMobileVideoContainer) enableMobileVideoContainer.style.display = 'block';
} else { } else {
if (preferLocalContainer) preferLocalContainer.style.display = 'none'; if (preferLocalContainer) preferLocalContainer.style.display = 'none';
if (preferLocalBackdropsContainer) preferLocalBackdropsContainer.style.display = 'none'; if (preferLocalBackdropsContainer) preferLocalBackdropsContainer.style.display = 'none';
if (waitForTrailerContainer) waitForTrailerContainer.style.display = 'none';
if (enableMobileVideoContainer) enableMobileVideoContainer.style.display = 'none';
} }
} }
@@ -563,7 +644,8 @@
'EnableSeasonalContent', 'EnableClientSideSettings', 'SortBy', 'SortOrder', 'EnableSeasonalContent', 'EnableClientSideSettings', 'SortBy', 'SortOrder',
'PreferLocalTrailers', 'ApplyLimitsToCustomIds', 'SeasonalSections', 'PreferLocalTrailers', 'ApplyLimitsToCustomIds', 'SeasonalSections',
'PreferLocalBackdrops', 'RandomizeThemeVideos', 'RandomizeLocalTrailers', 'PreferLocalBackdrops', 'RandomizeThemeVideos', 'RandomizeLocalTrailers',
'IncludeWatchedContent' 'IncludeWatchedContent', 'ShowPaginationDots', 'MaxParentalRating',
'MaxDaysRecent', 'ExcludeSeasonalContent', 'HideArrowsOnMobile'
]; ];
keys.forEach(function (key) { keys.forEach(function (key) {
@@ -582,16 +664,16 @@
}); });
}, },
renderSeasonalSections: function(page, sections) { renderSeasonalSections: function (page, sections) {
var container = page.querySelector('#seasonalSectionsList'); var container = page.querySelector('#seasonalSectionsList');
if (!container) return; if (!container) return;
container.innerHTML = ''; container.innerHTML = '';
sections.forEach(function(section, index) { sections.forEach(function (section, index) {
MediaBarEnhancedConfigurationPage.createSectionElement(container, section, index + 1); MediaBarEnhancedConfigurationPage.createSectionElement(container, section, index + 1);
}); });
}, },
addSeasonalSection: function(page) { addSeasonalSection: function (page) {
var container = page.querySelector('#seasonalSectionsList'); var container = page.querySelector('#seasonalSectionsList');
if (!container) return; if (!container) return;
var index = container.children.length + 1; var index = container.children.length + 1;
@@ -603,25 +685,25 @@
}, index); }, index);
}, },
createSectionElement: function(container, data, index) { createSectionElement: function (container, data, index) {
var div = document.createElement('div'); var div = document.createElement('div');
div.className = 'seasonal-section'; div.className = 'seasonal-section';
div.style.cssText = 'background: rgba(0,0,0,0.2); padding: 1em; margin-bottom: 1em; border-radius: 4px; border: 1px solid rgba(255,255,255,0.1);'; div.style.cssText = 'background: rgba(0,0,0,0.2); padding: 1em; margin-bottom: 1em; border-radius: 4px; border: 1px solid rgba(255,255,255,0.1);';
var days = []; var days = [];
for(var i=1; i<=31; i++) days.push(i); for (var i = 1; i <= 31; i++) days.push(i);
var months = [ var months = [
{v:1, n:'Jan'}, {v:2, n:'Feb'}, {v:3, n:'Mar'}, {v:4, n:'Apr'}, { v: 1, n: 'Jan' }, { v: 2, n: 'Feb' }, { v: 3, n: 'Mar' }, { v: 4, n: 'Apr' },
{v:5, n:'May'}, {v:6, n:'Jun'}, {v:7, n:'Jul'}, {v:8, n:'Aug'}, { v: 5, n: 'May' }, { v: 6, n: 'Jun' }, { v: 7, n: 'Jul' }, { v: 8, n: 'Aug' },
{v:9, n:'Sep'}, {v:10, n:'Oct'}, {v:11, n:'Nov'}, {v:12, n:'Dec'} { v: 9, n: 'Sep' }, { v: 10, n: 'Oct' }, { v: 11, n: 'Nov' }, { v: 12, n: 'Dec' }
]; ];
function mkSelect(val, opts, cls) { function mkSelect(val, opts, cls) {
var h = '<select class="emby-select emby-select-withcolor ' + cls + '" style="width: auto; display: inline-block; margin-right: 5px; -webkit-appearance: menulist; appearance: menulist;">'; var h = '<select class="emby-select emby-select-withcolor ' + cls + '" style="width: auto; display: inline-block; margin-right: 5px; -webkit-appearance: menulist; appearance: menulist;">';
opts.forEach(function(o) { opts.forEach(function (o) {
var v = o.v || o; var v = o.v || o;
var n = o.n || o; var n = o.n || o;
h += '<option value="'+v+'" ' + (v == val ? 'selected' : '') + '>' + n + '</option>'; h += '<option value="' + v + '" ' + (v == val ? 'selected' : '') + '>' + n + '</option>';
}); });
h += '</select>'; h += '</select>';
return h; return h;
@@ -662,19 +744,19 @@
' <div class="fieldDescription">Comma-separated or Newline separated list of Movie/Series/Collection IDs to show during this season.<br>Same options available as for the default media IDs.</div>' + ' <div class="fieldDescription">Comma-separated or Newline separated list of Movie/Series/Collection IDs to show during this season.<br>Same options available as for the default media IDs.</div>' +
'</div>'; '</div>';
div.querySelector('.btn-remove').addEventListener('click', function() { div.querySelector('.btn-remove').addEventListener('click', function () {
div.remove(); div.remove();
MediaBarEnhancedConfigurationPage.updateSectionTitles(container); MediaBarEnhancedConfigurationPage.updateSectionTitles(container);
}); });
div.querySelector('.btn-move-up').addEventListener('click', function() { div.querySelector('.btn-move-up').addEventListener('click', function () {
if (div.previousElementSibling) { if (div.previousElementSibling) {
container.insertBefore(div, div.previousElementSibling); container.insertBefore(div, div.previousElementSibling);
MediaBarEnhancedConfigurationPage.updateSectionTitles(container); MediaBarEnhancedConfigurationPage.updateSectionTitles(container);
} }
}); });
div.querySelector('.btn-move-down').addEventListener('click', function() { div.querySelector('.btn-move-down').addEventListener('click', function () {
if (div.nextElementSibling) { if (div.nextElementSibling) {
container.insertBefore(div.nextElementSibling, div); container.insertBefore(div.nextElementSibling, div);
MediaBarEnhancedConfigurationPage.updateSectionTitles(container); MediaBarEnhancedConfigurationPage.updateSectionTitles(container);
@@ -684,9 +766,9 @@
container.appendChild(div); container.appendChild(div);
}, },
updateSectionTitles: function(container) { updateSectionTitles: function (container) {
var sections = container.querySelectorAll('.seasonal-section'); var sections = container.querySelectorAll('.seasonal-section');
sections.forEach(function(section, index) { sections.forEach(function (section, index) {
var title = section.querySelector('.section-title'); var title = section.querySelector('.section-title');
if (title) { if (title) {
title.innerText = 'Season list #' + (index + 1); title.innerText = 'Season list #' + (index + 1);
@@ -694,10 +776,10 @@
}); });
}, },
getSeasonalSectionsFromUI: function(page) { getSeasonalSectionsFromUI: function (page) {
var sections = []; var sections = [];
var els = page.querySelectorAll('.seasonal-section'); var els = page.querySelectorAll('.seasonal-section');
els.forEach(function(el) { els.forEach(function (el) {
sections.push({ sections.push({
Name: el.querySelector('.section-name').value, Name: el.querySelector('.section-name').value,
StartDay: parseInt(el.querySelector('.start-day').value), StartDay: parseInt(el.querySelector('.start-day').value),

View File

@@ -12,7 +12,7 @@
<!-- <TreatWarningsAsErrors>false</TreatWarningsAsErrors> --> <!-- <TreatWarningsAsErrors>false</TreatWarningsAsErrors> -->
<Title>Jellyfin Media Bar Enhanced Plugin</Title> <Title>Jellyfin Media Bar Enhanced Plugin</Title>
<Authors>CodeDevMLH</Authors> <Authors>CodeDevMLH</Authors>
<Version>1.7.0.0</Version> <Version>1.7.1.9</Version>
<RepositoryUrl>https://github.com/CodeDevMLH/jellyfin-plugin-media-bar-enhanced</RepositoryUrl> <RepositoryUrl>https://github.com/CodeDevMLH/jellyfin-plugin-media-bar-enhanced</RepositoryUrl>
</PropertyGroup> </PropertyGroup>

View File

@@ -768,7 +768,22 @@
.button-container { .button-container {
top: calc(50% + 25vh); top: calc(50% + 25vh);
left: 50%; left: 50%;
transform: translateX(-50%) scale(0.95); transform: translateX(-50%);
width: max-content;
max-width: 98vw;
flex-wrap: nowrap;
justify-content: center;
gap: 15px;
}
.button-container button {
margin: 0 !important;
min-width: 0 !important;
}
.button-container .detail-button,
.button-container .favorite-button {
flex-shrink: 0;
} }
.logo { .logo {
@@ -825,10 +840,6 @@
.genre { .genre {
top: calc(50% + 16vh); top: calc(50% + 16vh);
} }
.button-container {
top: calc(50% + 27vh);
}
} }
} }
@@ -1000,3 +1011,19 @@
.layout-tv .backdrop-container{ .layout-tv .backdrop-container{
top: -5%; top: -5%;
} }
@media screen and (min-width: 960px) and (-webkit-device-pixel-ratio: 1) {
.layout-tv .backdrop.animate {
animation: none !important;
}
.layout-tv .logo.animate {
animation: none !important;
}
.layout-tv .slide-counter,
.layout-tv .dots-container {
backdrop-filter: none;
-webkit-backdrop-filter: none;
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -9,12 +9,20 @@
"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.7.0.0", "version": "1.7.1.9",
"changelog": "- Add YouTube no-cookie host and referrer policy for iframe security to fix playback issues on iOS/MacOS", "changelog": "- feat: add option to disable pagination dots/counter\n- feat: add exclude seasonal content from random fetching option\n- Add hide arrows on mobile option \n- fix button issue on mobile when using ElegantFin Theme",
"targetAbi": "10.11.0.0", "targetAbi": "10.11.0.0",
"sourceUrl": "https://git.mahom03-spacecloud.de/CodeDevMLH/jellyfin-plugin-media-bar-enhanced/releases/download/v1.7.0.0/Jellyfin.Plugin.MediaBarEnhanced.zip", "sourceUrl": "https://git.mahom03-spacecloud.de/CodeDevMLH/jellyfin-plugin-media-bar-enhanced/releases/download/v1.7.1.9/Jellyfin.Plugin.MediaBarEnhanced.zip",
"checksum": "d126fd4b76c49bdcca257919fd3db8a5", "checksum": "af20c62dae53ee05dec1ac7ae6bb1149",
"timestamp": "2026-03-05T01:05:37Z" "timestamp": "2026-03-08T20:58:25Z"
},
{
"version": "1.7.0.14",
"changelog": "- Switched to YouTube no-cookie host and referrer policy for iframe security\n- fix playback issues on iOS/MacOS \n- Disable animations and backdrop filters for TV layout\n- removed list.txt functionality to clean up, use the web ui instead\n- Enhance logging with contextual messages, in order to be able to clearly assign logs to this plugin",
"targetAbi": "10.11.0.0",
"sourceUrl": "https://git.mahom03-spacecloud.de/CodeDevMLH/jellyfin-plugin-media-bar-enhanced/releases/download/v1.7.0.14/Jellyfin.Plugin.MediaBarEnhanced.zip",
"checksum": "07875c74aab766657be3b8033be6d53f",
"timestamp": "2026-03-06T03:13:48Z"
}, },
{ {
"version": "1.6.6.4", "version": "1.6.6.4",

View File

@@ -0,0 +1,73 @@
(async () => {
const apiClient = window.ApiClient;
if (!apiClient) {
console.error("ApiClient not found. Are you logged in?");
return;
}
const userId = apiClient.getCurrentUserId();
const serverAddress = apiClient.serverAddress();
const maxDaysRecent = 30; // 30 Tage Limit
const pastDate = new Date();
pastDate.setDate(pastDate.getDate() - maxDaysRecent);
const dateStr = pastDate.toISOString();
console.log(`\n%c=== TEST: DateCreated Direkt-Abfrage ===`, "color: #00a4dc; font-weight: bold; font-size: 14px;");
console.log(`Wir suchen Filme, die nach dem ${dateStr} hinzugefügt wurden.\n`);
// Wir probieren alle denkbaren Parameter-Schreibweisen aus,
// die Jellyfin historisch oder in Forks für "DateCreated" akzeptieren könnte.
const testCases = [
{ name: "MinDateCreated (PascalCase)", param: `MinDateCreated=${dateStr}` },
{ name: "minDateCreated (camelCase)", param: `minDateCreated=${dateStr}` },
{ name: "DateCreatedMin", param: `DateCreatedMin=${dateStr}` },
{ name: "dateCreatedMin", param: `dateCreatedMin=${dateStr}` },
{ name: "StartDate", param: `StartDate=${dateStr}` },
{ name: "startDate", param: `startDate=${dateStr}` }
];
try {
for (let i = 0; i < testCases.length; i++) {
const test = testCases[i];
const apiUrl = `${serverAddress}/Items?IncludeItemTypes=Movie,Series&Recursive=true&enableUserData=true&Limit=5&fields=Id,DateCreated&userId=${userId}&${test.param}`;
console.log(`%cTest ${i+1}: ${test.name}`, "color: yellow;");
console.log(`URL: ${apiUrl}`);
const response = await fetch(apiUrl, {
headers: {
Authorization: `MediaBrowser Client="${apiClient.appName()}", Device="${apiClient.deviceName()}", DeviceId="${apiClient.deviceId()}", Version="${apiClient.appVersion()}", Token="${apiClient.accessToken()}"`
}
});
if (!response.ok) {
console.error(`-> ❌ HTTP Fehler: ${response.status}`);
continue;
}
const data = await response.json();
const items = data.Items || [];
// Zur Auswertung: Wenn die Abfrage ignoriert wird, liefert er oft ALLE (hier max 5) zurück.
// Filtert er wirklich, sollten es WENIGER als 5, und zwar im besten Fall genau die RICHTIGEN sein.
console.log(`-> Ergebnis: ${items.length} Items zurückgeliefert.`);
if (items.length > 0) {
// Wir checken, ob die zurückgelieferten Items WIRKLICH neu sind
const oldItems = items.filter(item => new Date(item.DateCreated) < pastDate);
if (oldItems.length > 0) {
console.log(` ❌ Filter FEHLGESCHLAGEN: Es wurden ${oldItems.length} "alte" Filme zurückgegeben (z.B. ${oldItems[0].Name}). Er ignoriert also den Parameter.`);
} else {
console.log(` ✅ Filter KÖNNTE funktionieren: Alle zurückgegebenen Filme sind neuer als unser Datum! (Erster: ${items[0].Name})`);
}
} else {
console.log(` ❓ Keine Items gefunden (entweder strenger Filter oder gar keine neuen Filme vorhanden).`);
}
console.log("\n");
}
} catch (error) {
console.error("Fehler beim Abruf:", error);
}
})();

View File

@@ -0,0 +1,56 @@
(async () => {
const apiClient = window.ApiClient;
if (!apiClient) {
console.error("ApiClient not found. Are you logged in?");
return;
}
const userId = apiClient.getCurrentUserId();
const serverAddress = apiClient.serverAddress();
const maxDaysRecent = 30; // Test: Added in last 30 days
const pastDate = new Date();
pastDate.setDate(pastDate.getDate() - maxDaysRecent);
const dateStr = pastDate.toISOString();
console.log(`Searching for items added after: ${dateStr}`);
const testUrls = [
// Test 1: minDateCreated (CamelCase)
`${serverAddress}/Items?IncludeItemTypes=Movie,Series&Recursive=true&enableUserData=true&Limit=5&fields=Id,DateCreated&userId=${userId}&minDateCreated=${dateStr}`,
// Test 2: minDateLastSaved
`${serverAddress}/Items?IncludeItemTypes=Movie,Series&Recursive=true&enableUserData=true&Limit=5&fields=Id,DateCreated&userId=${userId}&minDateLastSaved=${dateStr}`,
];
try {
for (let i = 0; i < testUrls.length; i++) {
const apiUrl = testUrls[i];
console.log(`\n%cTest ${i+1}: Testing URL:\n${apiUrl}`, "color: yellow;");
const response = await fetch(apiUrl, {
headers: {
Authorization: `MediaBrowser Client="${apiClient.appName()}", Device="${apiClient.deviceName()}", DeviceId="${apiClient.deviceId()}", Version="${apiClient.appVersion()}", Token="${apiClient.accessToken()}"`
}
});
if (!response.ok) {
console.error(`Failed to fetch items: ${response.status} ${response.statusText}`);
continue;
}
const data = await response.json();
const items = data.Items || [];
console.log(`%cErgebnis: ${items.length} Items gefunden!`, "color: #00a4dc; font-weight: bold;");
if(items.length > 0) {
console.log("Gefundene Items:");
items.forEach(item => {
console.log(`- Name: ${item.Name}, DateCreated: ${item.DateCreated}, Art: ${item.Type}`);
});
}
}
} catch (error) {
console.error("Fehler beim Abrufen der URL:", error);
}
})();

View File

@@ -0,0 +1,72 @@
(async () => {
const apiClient = window.ApiClient;
if (!apiClient) {
console.error("ApiClient not found. Are you logged in?");
return;
}
const userId = apiClient.getCurrentUserId();
const serverAddress = apiClient.serverAddress();
const maxDaysRecent = 30; // Test: Added in last 30 days
// 1. Calculate the cutoff date
const pastDate = new Date();
pastDate.setDate(pastDate.getDate() - maxDaysRecent);
const dateStr = pastDate.toISOString();
console.log(`\n%c=== TEST: 2-Stufen "Zuletzt Hinzugefügt" Filter ===`, "color: #00a4dc; font-weight: bold; font-size: 14px;");
console.log(`Suche Items neuer als: ${dateStr} (${maxDaysRecent} Tage alt)\n`);
const apiUrl = `${serverAddress}/Items?IncludeItemTypes=Movie,Series&Recursive=true&enableUserData=true&Limit=50&fields=Id,DateCreated&userId=${userId}&minDateLastSaved=${dateStr}`;
try {
console.log(`%cSchritt 1: API Call mit minDateLastSaved...`, "color: yellow;");
console.log(`URL: ${apiUrl}`);
const response = await fetch(apiUrl, {
headers: {
Authorization: `MediaBrowser Client="${apiClient.appName()}", Device="${apiClient.deviceName()}", DeviceId="${apiClient.deviceId()}", Version="${apiClient.appVersion()}", Token="${apiClient.accessToken()}"`
}
});
if (!response.ok) {
throw new Error(`Failed to fetch items: ${response.status} ${response.statusText}`);
}
const data = await response.json();
let items = data.Items || [];
console.log(`-> API lieferte ${items.length} potenziell neue/geänderte Items zurück.\n`);
if (items.length > 0) {
console.log("Die API hielt diese Items für neu/aktuell:");
items.forEach(item => console.log(` - ${item.Name} (DateCreated: ${item.DateCreated})`));
}
console.log(`\n%cSchritt 2: Lokaler DateCreated Filter (Wie das Plugin ihn jetzt nutzt)...`, "color: yellow;");
// Exakt dieser Filter-Block ist jetzt auch so in mediaBarEnhanced.js
const finalItems = items.filter(item => {
if (!item.DateCreated) return true; // Fallback falls Jellyfin kein Datum schickt
const dCreated = new Date(item.DateCreated);
return dCreated >= pastDate;
});
console.log(`%c-> FINALES ERGEBNIS: ${finalItems.length} echte Neuzugänge bleiben übrig!`, "color: #00ff00; font-weight: bold; font-size: 14px;");
if(finalItems.length > 0) {
console.log("Diese Items schaffen es in die Slideshow:");
finalItems.forEach(item => {
console.log(` 🎬 Name: ${item.Name}, DateCreated: ${item.DateCreated}`);
});
}
// Teste den Fallback
if(finalItems.length === 0 && items.length > 0) {
console.log("\n%c💡 HINWEIS: Da nach Filterung 0 Items übrig bleiben, greift in der Slideshow jetzt automatisch unser Fallback und zeigt zufällige Filme aller Jahre!", "color: orange;");
}
} catch (error) {
console.error("Fehler beim Abruf:", error);
}
})();

View File

@@ -0,0 +1,62 @@
(async () => {
const apiClient = window.ApiClient;
if (!apiClient) {
console.error("ApiClient not found. Are you logged in?");
return;
}
const userId = apiClient.getCurrentUserId();
const serverAddress = apiClient.serverAddress();
// Example test configuration flags
const maxItems = 50;
const includeWatchedContent = false; // set to false to ONLY show unplayed (newly watched)
const maxParentalRating = 12; // Test age limit
const maxDaysRecent = 30; // Test recency limit
// Build the query parameters just like in mediaBarEnhanced.js
const sortParams = "sortBy=Random";
const playedFilter = includeWatchedContent ? '' : '&isPlayed=False';
let parentalFilter = '';
if (maxParentalRating) {
parentalFilter = `&MaxOfficialRating=${maxParentalRating}`;
}
let dateFilter = '';
if (maxDaysRecent) {
const pastDate = new Date();
pastDate.setDate(pastDate.getDate() - maxDaysRecent);
dateFilter = `&MinDateCreated=${pastDate.toISOString()}`;
}
const apiUrl = `${serverAddress}/Items?IncludeItemTypes=Movie,Series&Recursive=true&hasOverview=true&imageTypes=Logo,Backdrop&${sortParams}${playedFilter}${parentalFilter}${dateFilter}&enableUserData=true&Limit=${maxItems}&fields=Id&userId=${userId}`;
try {
console.log(`Testing generated URL with filters:\n%c${apiUrl}`, "color: yellow;");
// Execute the fetch
const response = await fetch(apiUrl, {
headers: {
Authorization: `MediaBrowser Client="${apiClient.appName()}", Device="${apiClient.deviceName()}", DeviceId="${apiClient.deviceId()}", Version="${apiClient.appVersion()}", Token="${apiClient.accessToken()}"`
}
});
if (!response.ok) {
throw new Error(`Failed to fetch items: ${response.status} ${response.statusText}`);
}
const data = await response.json();
const items = data.Items || [];
console.log(`%cErgebnis: ${items.length} Items gefunden!`, "color: #00a4dc; font-weight: bold;");
if(items.length > 0) {
console.log("Erstes Item als Beispiel:");
console.dir(items[0]);
}
} catch (error) {
console.error("Fehler beim Abrufen der URL:", error);
}
})();

View File

@@ -1,462 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Media Bar Enhanced Configuration</title>
</head>
<body>
<div id="MediaBarEnhancedConfigurationPage" data-role="page" class="page type-interior pluginConfigurationPage"
data-require="emby-input,emby-button,emby-select,emby-checkbox,emby-textarea">
<div data-role="content">
<div class="content-primary">
<div class="sectionTitleContainer">
<h2 class="sectionTitle">Media Bar Enhanced</h2>
<a is="emby-linkbutton" class="raised raised-mini emby-button" style="margin-left: 2em;"
target="_blank" href="https://github.com/CodeDevMLH/jellyfin-plugin-media-bar-enhanced">
<i class="md-icon button-icon button-icon-left secondaryText"></i>
<span>${Help}</span>
</a>
</div>
<hr style="max-width: 800px; margin: 1em 0;">
<div style="margin-bottom: 1.5em;">
<button class="jellyfin-tab-button active" onclick="showTab('basic', this)"
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;">
<h3>General Settings</h3>
</button>
<button class="jellyfin-tab-button" onclick="showTab('custom', 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>Custom Content</h3>
</button>
<button class="jellyfin-tab-button" onclick="showTab('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="mediaBarEnhancedConfigForm">
<!-- BASIC TAB -->
<div id="basic" class="tab-content">
<h2 class="sectionTitle">Main Plugin Settings</h2>
<div class="checkboxContainer checkboxContainer-withDescription">
<label>
<input is="emby-checkbox" type="checkbox" id="IsEnabled" name="IsEnabled" />
<span>Enable Media Bar Enhanced Plugin</span>
</label>
<div class="fieldDescription">Enable or disable the entire plugin functionality.</div>
</div>
<div class="checkboxContainer checkboxContainer-withDescription">
<label>
<input is="emby-checkbox" type="checkbox" id="EnableVideoBackdrop"
name="EnableVideoBackdrop" />
<span>Enable Trailer Backdrops</span>
</label>
<div class="fieldDescription">Show trailers as background if available.<br>Adds a
mute/unmute and pause/play button to control the video in the right top corner.</div>
</div>
<div class="checkboxContainer checkboxContainer-withDescription">
<label>
<input is="emby-checkbox" type="checkbox" id="WaitForTrailerToEnd"
name="WaitForTrailerToEnd" />
<span>Wait For Trailer To End</span>
</label>
<div class="fieldDescription">Delay slide transition until trailer finishes.</div>
</div>
<div class="checkboxContainer checkboxContainer-withDescription">
<label>
<input is="emby-checkbox" type="checkbox" id="EnableMobileVideo"
name="EnableMobileVideo" />
<span>Enable Trailer On Mobile</span>
</label>
<div class="fieldDescription">Allow video playback on mobile devices.</div>
</div>
<div class="checkboxContainer checkboxContainer-withDescription">
<label>
<input is="emby-checkbox" type="checkbox" id="ShowTrailerButton"
name="ShowTrailerButton" />
<span>Show Trailer Button</span>
</label>
<div class="fieldDescription">Display a button to open trailer in modal. Only visible if
trailer is not set as backdrop or if no trailer is available.</div>
</div>
</div>
<!-- CUSTOM CONTENT TAB -->
<div id="custom" class="tab-content" style="display:none;">
<h2 class="sectionTitle">Custom Media IDs</h2>
<div class="checkboxContainer checkboxContainer-withDescription">
<label>
<input is="emby-checkbox" type="checkbox" id="EnableCustomMediaIds"
name="EnableCustomMediaIds" />
<span>Enable Custom Media IDs</span>
</label>
<div class="fieldDescription">If enabled, the slideshow will try to show the items listed
below. If the list is empty, default behavior (random items) is used. Disable this
to temporarily ignore your custom list without deleting it.</div>
</div>
<div class="checkboxContainer checkboxContainer-withDescription">
<label>
<input is="emby-checkbox" type="checkbox" id="EnableSeasonalContent"
name="EnableSeasonalContent" />
<span>Enable Seasonal Content Mode</span>
</label>
<div class="fieldDescription">Enable this to define time-based lists in the field below.
</div>
</div>
<div class="inputContainer">
<label class="inputLabel inputLabelUnfocused" for="CustomMediaIds">Media/Collection/Playlist
IDs
(Newline or Comma separated)</label>
<textarea is="emby-textarea" id="CustomMediaIds" name="CustomMediaIds"
style="width: 100%; height: 150px; font-family: monospace;"></textarea>
<div class="fieldDescription" id="customMediaIdsDesc">Enter the IDs of the items you want to show in the slideshow.
You can separate them by new line or comma.
<br><br>
<b>Manual Trailer Override:</b> You can specify a YouTube URL for an item by adding it in
brackets: <br> <code>e.g. ID DESCRIPTION [https://youtu.be/...]</code> or <code>ID [https://youtu.be/...] DESCRIPTION</code>
<br><br>
You can also add a description after the ID using any separator like space, pipe
(|) or dash (-): <br>e.g. <code>ID DESCRIPTION</code> or <code>ID | DESCRIPTION</code>
<br><br>
<b>Note:</b> If using a <b>Collection Name</b> (instead of an ID) combined with a description, you <b>MUST</b> use
the pipe (|) separator.
<br>
<b>Note:</b> The separator <b>MUST NOT</b> be a hex character (0-9, a-f).</div>
<div class="fieldDescription" id="seasonalMediaIdsDesc" style="display: none;">
<b>Seasonal Mode Enabled:</b> Define lines with date ranges (Format: DD.MM-DD.MM |
<i>name</i> | <i>IDs</i>).<br>
Example:<br>
<code>20.10-31.10 | Halloween | ID1, ID2 [https://youtu.be/...]</code><br>
<code>01.12-26.12 | Christmas | ID3, ID4</code><br>
<i>Only lines matching the current date will be used. If no line matches, it will try to
use random items.</i>
</div>
<p>You can find the IDs of your items in the URL of the item page in the web interface.<br>
Example:
<code>https://your-jellyfin-url/web/#/details?id=<b style="color:red;">your-item-id</b>&serverId=your-server-id</code><br><br>
You can also insert a name of a collection or playlist to fetch the IDs of all items in
it (will take the first hit.<br><b>Note:</b> there is currently no feedback if the name
resolution succeeded, you will have to look if the bar displays the correct items.).
</p>
</div>
</div>
<!-- ADVANCED TAB -->
<div id="advanced" class="tab-content" style="display:none;">
<h2 class="sectionTitle">Features</h2>
<div class="checkboxContainer checkboxContainer-withDescription">
<label>
<input is="emby-checkbox" type="checkbox" id="SlideAnimationEnabled"
name="SlideAnimationEnabled" />
<span>Enable Slide Animations</span>
</label>
<div class="fieldDescription">Enable the zooming-in effect on background images when a new slide is
shown (does not affect trailer backdrops). Attention: This may cause performance issues on weaker client hardware.</div>
</div>
<div class="checkboxContainer checkboxContainer-withDescription">
<label>
<input is="emby-checkbox" type="checkbox" id="EnableClientSideSettings"
name="EnableClientSideSettings" />
<span>Enable Client-Side Settings</span>
</label>
<div class="fieldDescription">If enabled, users will see a media bar icon in the header to
override settings (like disabling the bar or trailer backdrops) locally on their device.</div>
</div>
<div class="checkboxContainer checkboxContainer-withDescription">
<label>
<input is="emby-checkbox" type="checkbox" id="UseSponsorBlock" name="UseSponsorBlock" />
<span>Use SponsorBlock</span>
</label>
<div class="fieldDescription">Skip intro/outro segments in YouTube trailers.</div>
</div>
<div class="selectContainer">
<label class="selectLabel" for="PreferredVideoQuality">Preferred YouTube Quality</label>
<select is="emby-select" id="PreferredVideoQuality" name="PreferredVideoQuality"
class="emby-select-withcolor emby-select">
<option value="Auto">Auto (Smart)</option>
<option value="Maximum">Maximum (4K+)</option>
<option value="1080p">1080p</option>
<option value="720p">720p</option>
</select>
<div class="fieldDescription">"Auto" selects Maximum if screen width > 1920px, otherwise
1080p.</div>
</div>
<div class="checkboxContainer checkboxContainer-withDescription">
<label>
<input is="emby-checkbox" type="checkbox" id="StartMuted" name="StartMuted" />
<span>Start Muted</span>
</label>
<div class="fieldDescription">Start trailer video playback muted. (Known issue: In the
Android/IOS app, backdrop trailers are always muted.)</div>
</div>
<div class="checkboxContainer checkboxContainer-withDescription">
<label>
<input is="emby-checkbox" type="checkbox" id="FullWidthVideo" name="FullWidthVideo" />
<span>Full Width Video</span>
</label>
<div class="fieldDescription">Stretch video to full width. Very nice on desktops, on mobile
devices only the middle of the video is visible.<br>Disable to get the full aspect ratio
on
mobile devices. (looks bad on desktops)</div>
</div>
<div class="checkboxContainer checkboxContainer-withDescription">
<label>
<input is="emby-checkbox" type="checkbox" id="EnableLoadingScreen"
name="EnableLoadingScreen" />
<span>Enable Loading Screen</span>
</label>
<div class="fieldDescription">Show a loading screen while the slideshow initializes. (You
may have to reload the page twice)</div>
</div>
<div class="checkboxContainer checkboxContainer-withDescription">
<label>
<input is="emby-checkbox" type="checkbox" id="AlwaysShowArrows"
name="AlwaysShowArrows" />
<span>Always Show Arrows</span>
</label>
<div class="fieldDescription">If enabled, navigation arrows will always be visible instead
of only on hover.</div>
</div>
<div class="checkboxContainer checkboxContainer-withDescription">
<label>
<input is="emby-checkbox" type="checkbox" id="EnableKeyboardControls"
name="EnableKeyboardControls" />
<span>Enable Keyboard Controls</span>
</label>
<div class="fieldDescription">Enable keyboard shortcuts (Arrows left/right (change slide),
Space (pause), M (mute/unmute)) for
the slideshow.</div>
</div>
<h2 class="sectionTitle">Time Settings</h2>
<p>Leave a setting blank to use the default value.</p>
<div class="inputContainer">
<label class="inputLabel inputLabelUnfocused" for="ShuffleInterval">Shuffle Interval
(ms)</label>
<input is="emby-input" type="number" id="ShuffleInterval" name="ShuffleInterval" />
<div class="fieldDescription">Time in milliseconds between changing slides.</div>
</div>
<div class="inputContainer">
<label class="inputLabel inputLabelUnfocused" for="RetryInterval">Retry Interval
(ms)</label>
<input is="emby-input" type="number" id="RetryInterval" name="RetryInterval" />
<div class="fieldDescription">Time in milliseconds to wait before retrying failed
operations.</div>
</div>
<div class="inputContainer">
<label class="inputLabel inputLabelUnfocused" for="LoadingCheckInterval">Loading Check
Interval (ms)</label>
<input is="emby-input" type="number" id="LoadingCheckInterval"
name="LoadingCheckInterval" />
<div class="fieldDescription">Frequency of checking wether the login screen or home screen
has loaded (in milliseconds).</div>
</div>
<div class="inputContainer">
<label class="inputLabel inputLabelUnfocused" for="FadeTransitionDuration">Fade
Transition Duration (ms)</label>
<input is="emby-input" type="number" id="FadeTransitionDuration"
name="FadeTransitionDuration" />
<div class="fieldDescription">Duration in milliseconds of the transition between slides.
</div>
</div>
<div class="inputContainer">
<label class="inputLabel inputLabelUnfocused" for="MinSwipeDistance">Min Swipe Distance
(px)</label>
<input is="emby-input" type="number" id="MinSwipeDistance" name="MinSwipeDistance" />
<div class="fieldDescription">Minimum distance in pixels for a swipe to be registered (for
mobile).</div>
</div>
<h2 class="sectionTitle">Content Limits</h2>
<p>Leave a setting blank to use the default value.</p>
<div class="inputContainer">
<label class="inputLabel inputLabelUnfocused" for="MaxItems">Total Max Items</label>
<input is="emby-input" type="number" id="MaxItems" name="MaxItems" />
<div class="fieldDescription">Maximum total items to fetch (for all content types combined).
</div>
</div>
<div class="inputContainer">
<label class="inputLabel inputLabelUnfocused" for="MaxMovies">Max Movies</label>
<input is="emby-input" type="number" id="MaxMovies" name="MaxMovies" />
<div class="fieldDescription">Maximum movies to include in slideshow (for random selection).
</div>
</div>
<div class="inputContainer">
<label class="inputLabel inputLabelUnfocused" for="MaxTvShows">Max TV Shows</label>
<input is="emby-input" type="number" id="MaxTvShows" name="MaxTvShows" />
<div class="fieldDescription">Maximum TV shows to include in slideshow (for random
selection).</div>
</div>
<div class="inputContainer">
<label class="inputLabel inputLabelUnfocused" for="PreloadCount">Preload Count</label>
<input is="emby-input" type="number" id="PreloadCount" name="PreloadCount" />
<div class="fieldDescription">Number of images to preload.</div>
</div>
<div class="inputContainer">
<label class="inputLabel inputLabelUnfocused" for="MaxPaginationDots">Max Pagination
Dots</label>
<input is="emby-input" type="number" id="MaxPaginationDots" name="MaxPaginationDots" />
<div class="fieldDescription">Maximum number of dots to show in navigation. If the number
will be exceeded, the dots will "turn" into a counter style (e.g. 1/100). Set to 0 to
enable counter style directly.</div>
</div>
<div class="inputContainer">
<label class="inputLabel inputLabelUnfocused" for="MaxPlotLength">Max Plot
Length</label>
<input is="emby-input" type="number" id="MaxPlotLength" name="MaxPlotLength" />
<div class="fieldDescription">Maximum characters for the plot summary.</div>
</div>
</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;">
<i class="material-icons" style="color: #00a4dc; font-size: 24px;">info</i>
<div>
All changes require a page refresh (ctrl + r or F5) after saving for changes to take effect.
<br />
If old settings persist, please force clear browser cache.
</div>
</div>
<div>
<button is="emby-button" type="submit" class="raised button-submit block emby-button">
<span>${Save}</span>
</button>
<button is="emby-button" type="button" class="raised button-cancel block btnCancel"
onclick="history.back();">
<span>${ButtonCancel}</span>
</button>
</div>
</form>
</div>
</div>
<script>
function showTab(tabId, btn) {
document.querySelectorAll('.tab-content').forEach(el => el.style.display = 'none');
document.getElementById(tabId).style.display = 'block';
document.querySelectorAll('.jellyfin-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 MediaBarEnhancedConfigurationPage = {
pluginId: 'd7e11d57-819b-4bdd-a88d-53c5f5560225',
loadConfiguration: function (page) {
Dashboard.showLoadingMsg();
ApiClient.getPluginConfiguration(MediaBarEnhancedConfigurationPage.pluginId).then(function (config) {
var keys = [
'IsEnabled', 'ShuffleInterval', 'RetryInterval', 'MinSwipeDistance',
'LoadingCheckInterval', 'MaxPlotLength', 'MaxMovies', 'MaxTvShows',
'MaxItems', 'PreloadCount', 'FadeTransitionDuration', 'MaxPaginationDots',
'SlideAnimationEnabled', 'EnableVideoBackdrop', 'UseSponsorBlock',
'WaitForTrailerToEnd', 'StartMuted', 'FullWidthVideo', 'EnableMobileVideo',
'ShowTrailerButton', 'AlwaysShowArrows', 'EnableKeyboardControls',
'EnableCustomMediaIds', 'CustomMediaIds', 'EnableLoadingScreen',
'EnableSeasonalContent', 'EnableClientSideSettings'
];
keys.forEach(function (key) {
var el = page.querySelector('#' + key);
if (el) {
if (el.type === 'checkbox') {
el.checked = config[key];
} else {
el.value = config[key];
}
}
});
// Handle Seasonal UI logic
var seasonalCheckbox = page.querySelector('#EnableSeasonalContent');
var normalDesc = page.querySelector('#customMediaIdsDesc');
var seasonalDesc = page.querySelector('#seasonalMediaIdsDesc');
function updateDesc() {
if (seasonalCheckbox && seasonalCheckbox.checked) {
if (normalDesc) normalDesc.style.display = 'none';
if (seasonalDesc) seasonalDesc.style.display = 'block';
} else {
if (normalDesc) normalDesc.style.display = 'block';
if (seasonalDesc) seasonalDesc.style.display = 'none';
}
}
if (seasonalCheckbox) {
seasonalCheckbox.addEventListener('change', updateDesc);
updateDesc();
}
Dashboard.hideLoadingMsg();
});
},
saveConfiguration: function (page) {
Dashboard.showLoadingMsg();
var config = {};
var keys = [
'IsEnabled', 'ShuffleInterval', 'RetryInterval', 'MinSwipeDistance',
'LoadingCheckInterval', 'MaxPlotLength', 'MaxMovies', 'MaxTvShows',
'MaxItems', 'PreloadCount', 'FadeTransitionDuration', 'MaxPaginationDots',
'SlideAnimationEnabled', 'EnableVideoBackdrop', 'UseSponsorBlock',
'WaitForTrailerToEnd', 'StartMuted', 'FullWidthVideo', 'EnableMobileVideo',
'ShowTrailerButton', 'AlwaysShowArrows', 'EnableKeyboardControls',
'EnableCustomMediaIds', 'CustomMediaIds', 'EnableLoadingScreen',
'EnableSeasonalContent', 'EnableClientSideSettings'
];
keys.forEach(function (key) {
var el = page.querySelector('#' + key);
if (el) {
if (el.type === 'checkbox') {
config[key] = el.checked;
} else {
config[key] = (el.type === 'number') ? parseInt(el.value, 10) : el.value;
}
}
});
ApiClient.updatePluginConfiguration(MediaBarEnhancedConfigurationPage.pluginId, config).then(function (result) {
Dashboard.processPluginConfigurationUpdateResult(result);
});
}
};
document.querySelector('#MediaBarEnhancedConfigurationPage').addEventListener('pageshow', function () {
MediaBarEnhancedConfigurationPage.loadConfiguration(this);
});
document.querySelector('#mediaBarEnhancedConfigForm').addEventListener('submit', function (e) {
e.preventDefault();
MediaBarEnhancedConfigurationPage.saveConfiguration(document.querySelector('#MediaBarEnhancedConfigurationPage'));
return false;
});
</script>
<style>
.jellyfin-tab-button.active {
color: #fff !important;
border-bottom: 2px solid #00a4dc !important;
}
</style>
</div>
</body>
</html>

File diff suppressed because it is too large Load Diff