Add ShowPaginationDots configuration option and update related UI elements

This commit is contained in:
CodeDevMLH
2026-03-08 16:57:16 +01:00
parent 2993bfe3f2
commit 3a367cb2be
3 changed files with 116 additions and 66 deletions

View File

@@ -18,6 +18,7 @@ namespace Jellyfin.Plugin.MediaBarEnhanced.Configuration
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;

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,21 +58,25 @@
<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">
<label> <label>
@@ -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,18 @@
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 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 +211,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 +222,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 +231,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 +240,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 +270,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>
@@ -328,7 +359,9 @@
<h2 class="sectionTitle">Content Sorting</h2> <h2 class="sectionTitle">Content Sorting</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,14 +375,17 @@
</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>
<h2 class="sectionTitle">Content Limits</h2> <h2 class="sectionTitle">Content Limits</h2>
@@ -377,6 +413,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</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>
@@ -397,7 +442,8 @@
name="IncludeWatchedContent" /> name="IncludeWatchedContent" />
<span>Include Watched Content</span> <span>Include Watched Content</span>
</label> </label>
<div class="fieldDescription">If enabled, watched content will be included in the random selection results.</div> <div class="fieldDescription">If enabled, watched content will be included in the random
selection results.</div>
</div> </div>
</div> </div>
@@ -459,7 +505,7 @@
'EnableSeasonalContent', 'EnableClientSideSettings', 'SortBy', 'SortOrder', 'EnableSeasonalContent', 'EnableClientSideSettings', 'SortBy', 'SortOrder',
'PreferLocalTrailers', 'ApplyLimitsToCustomIds', 'SeasonalSections', 'PreferLocalTrailers', 'ApplyLimitsToCustomIds', 'SeasonalSections',
'PreferLocalBackdrops', 'RandomizeThemeVideos', 'RandomizeLocalTrailers', 'PreferLocalBackdrops', 'RandomizeThemeVideos', 'RandomizeLocalTrailers',
'IncludeWatchedContent' 'IncludeWatchedContent', 'ShowPaginationDots'
]; ];
// Manual mapping for MediaBarIsEnabled -> IsEnabled, to avoid conflicts with other plugins // Manual mapping for MediaBarIsEnabled -> IsEnabled, to avoid conflicts with other plugins
@@ -506,11 +552,11 @@
var addSeasonBtn = page.querySelector('#addSeasonBtn'); var addSeasonBtn = page.querySelector('#addSeasonBtn');
if (addSeasonBtn) { if (addSeasonBtn) {
// 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);
}); });
} }
// Handle Prefer Local Trailers visibility // Handle Prefer Local Trailers visibility
@@ -563,7 +609,7 @@
'EnableSeasonalContent', 'EnableClientSideSettings', 'SortBy', 'SortOrder', 'EnableSeasonalContent', 'EnableClientSideSettings', 'SortBy', 'SortOrder',
'PreferLocalTrailers', 'ApplyLimitsToCustomIds', 'SeasonalSections', 'PreferLocalTrailers', 'ApplyLimitsToCustomIds', 'SeasonalSections',
'PreferLocalBackdrops', 'RandomizeThemeVideos', 'RandomizeLocalTrailers', 'PreferLocalBackdrops', 'RandomizeThemeVideos', 'RandomizeLocalTrailers',
'IncludeWatchedContent' 'IncludeWatchedContent', 'ShowPaginationDots'
]; ];
keys.forEach(function (key) { keys.forEach(function (key) {
@@ -582,16 +628,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 +649,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;
@@ -648,11 +694,11 @@
' <label class="inputLabel" style="margin-bottom:0.5em; display:block;">Active Period</label>' + ' <label class="inputLabel" style="margin-bottom:0.5em; display:block;">Active Period</label>' +
' <div style="display: flex; align-items: center; flex-wrap: wrap; gap: 0.5em;">' + ' <div style="display: flex; align-items: center; flex-wrap: wrap; gap: 0.5em;">' +
' <span>From:</span>' + ' <span>From:</span>' +
mkSelect(data.StartDay, days, 'start-day') + mkSelect(data.StartDay, days, 'start-day') +
mkSelect(data.StartMonth, months, 'start-month') + mkSelect(data.StartMonth, months, 'start-month') +
' <span style="margin-left: 1em;">To:</span>' + ' <span style="margin-left: 1em;">To:</span>' +
mkSelect(data.EndDay, days, 'end-day') + mkSelect(data.EndDay, days, 'end-day') +
mkSelect(data.EndMonth, months, 'end-month') + mkSelect(data.EndMonth, months, 'end-month') +
' </div>' + ' </div>' +
' <div class="fieldDescription">Date range (inclusive) when this content is active.</div>' + ' <div class="fieldDescription">Date range (inclusive) when this content is active.</div>' +
'</div>' + '</div>' +
@@ -662,19 +708,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 +730,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 +740,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

@@ -38,6 +38,7 @@ const CONFIG = {
preloadCount: 3, preloadCount: 3,
fadeTransitionDuration: 500, fadeTransitionDuration: 500,
maxPaginationDots: 15, maxPaginationDots: 15,
showPaginationDots: true,
slideAnimationEnabled: true, slideAnimationEnabled: true,
enableVideoBackdrop: true, enableVideoBackdrop: true,
useSponsorBlock: true, useSponsorBlock: true,
@@ -2280,6 +2281,8 @@ const SlideCreator = {
const SlideshowManager = { const SlideshowManager = {
createPaginationDots() { createPaginationDots() {
if (!CONFIG.showPaginationDots) return;
let dotsContainer = document.querySelector(".dots-container"); let dotsContainer = document.querySelector(".dots-container");
if (!dotsContainer) { if (!dotsContainer) {
dotsContainer = document.createElement("div"); dotsContainer = document.createElement("div");