diff --git a/Jellyfin.Plugin.MediaBarEnhanced/Configuration/PluginConfiguration.cs b/Jellyfin.Plugin.MediaBarEnhanced/Configuration/PluginConfiguration.cs index 162a05a..a4a631c 100644 --- a/Jellyfin.Plugin.MediaBarEnhanced/Configuration/PluginConfiguration.cs +++ b/Jellyfin.Plugin.MediaBarEnhanced/Configuration/PluginConfiguration.cs @@ -35,5 +35,7 @@ namespace Jellyfin.Plugin.MediaBarEnhanced.Configuration public bool EnableSeasonalContent { get; set; } = false; public bool IsEnabled { get; set; } = true; public bool EnableClientSideSettings { get; set; } = false; + public string SortBy { get; set; } = "Random"; + public string SortOrder { get; set; } = "Ascending"; } } diff --git a/Jellyfin.Plugin.MediaBarEnhanced/Configuration/configPage.html b/Jellyfin.Plugin.MediaBarEnhanced/Configuration/configPage.html index 76c8f94..d4e298d 100644 --- a/Jellyfin.Plugin.MediaBarEnhanced/Configuration/configPage.html +++ b/Jellyfin.Plugin.MediaBarEnhanced/Configuration/configPage.html @@ -274,6 +274,32 @@ mobile). +

Content Sorting

+
+ + +
Sort items by the selected criteria.
+
+
+ + +
Sort items in Ascending or Descending order.
+
+
+ Note: Sorting settings apply to both Server content and Custom IDs. 'Original' preserves Custom List order. +
+

Content Limits

Leave a setting blank to use the default value.

@@ -370,7 +396,7 @@ 'WaitForTrailerToEnd', 'StartMuted', 'FullWidthVideo', 'EnableMobileVideo', 'ShowTrailerButton', 'AlwaysShowArrows', 'EnableKeyboardControls', 'EnableCustomMediaIds', 'CustomMediaIds', 'EnableLoadingScreen', - 'EnableSeasonalContent', 'EnableClientSideSettings' + 'EnableSeasonalContent', 'EnableClientSideSettings', 'SortBy', 'SortOrder' ]; keys.forEach(function (key) { @@ -419,7 +445,7 @@ 'WaitForTrailerToEnd', 'StartMuted', 'FullWidthVideo', 'EnableMobileVideo', 'ShowTrailerButton', 'AlwaysShowArrows', 'EnableKeyboardControls', 'EnableCustomMediaIds', 'CustomMediaIds', 'EnableLoadingScreen', - 'EnableSeasonalContent', 'EnableClientSideSettings' + 'EnableSeasonalContent', 'EnableClientSideSettings', 'SortBy', 'SortOrder' ]; keys.forEach(function (key) { diff --git a/Jellyfin.Plugin.MediaBarEnhanced/Web/mediaBarEnhanced.js b/Jellyfin.Plugin.MediaBarEnhanced/Web/mediaBarEnhanced.js index 423d1c0..68adc20 100644 --- a/Jellyfin.Plugin.MediaBarEnhanced/Web/mediaBarEnhanced.js +++ b/Jellyfin.Plugin.MediaBarEnhanced/Web/mediaBarEnhanced.js @@ -51,6 +51,8 @@ const CONFIG = { customMediaIds: "", enableLoadingScreen: true, enableClientSideSettings: false, + sortBy: "Random", + sortOrder: "Ascending", }; // State management @@ -460,6 +462,62 @@ waitForApiClientAndInitialize(); * Utility functions for slide creation and management */ const SlideUtils = { + /** + * Sorts items based on configuration + * @param {Array} items - Array of item objects + * @param {string} sortBy - Sort criteria + * @param {string} sortOrder - Sort order 'Ascending' or 'Descending' + * @returns {Array} Sorted array of items + */ + sortItems(items, sortBy, sortOrder) { + if (sortBy === 'Random' || sortBy === 'Original') { + return items; + } + + const simpleCompare = (a, b) => { + if (a < b) return -1; + if (a > b) return 1; + return 0; + }; + + const sorted = [...items].sort((a, b) => { + let valA, valB; + + switch (sortBy) { + case 'DateCreated': + valA = new Date(a.DateCreated).getTime(); + valB = new Date(b.DateCreated).getTime(); + break; + case 'PremiereDate': + valA = new Date(a.PremiereDate).getTime(); + valB = new Date(b.PremiereDate).getTime(); + break; + case 'CommunityRating': + valA = a.CommunityRating || 0; + valB = b.CommunityRating || 0; + break; + case 'Runtime': + valA = a.RunTimeTicks || 0; + valB = b.RunTimeTicks || 0; + break; + case 'Name': + valA = (a.Name || '').toLowerCase(); + valB = (b.Name || '').toLowerCase(); + break; + default: + return 0; + } + + return simpleCompare(valA, valB); + }); + + if (sortOrder === 'Descending') { + sorted.reverse(); + } + + return sorted; + }, + /** * Shuffles array elements randomly * @param {Array} array - Array to shuffle @@ -960,8 +1018,8 @@ const ApiUtils = { } const response = await fetch( - `${STATE.jellyfinData.serverAddress}/Items/${itemId}`, - // `${STATE.jellyfinData.serverAddress}/Users/${STATE.jellyfinData.userId}/Items/${itemId}?Fields=Overview,RemoteTrailers,Genres,CommunityRating,CriticRating,OfficialRating,PremiereDate,RunTimeTicks,ProductionYear,MediaSources`, + // `${STATE.jellyfinData.serverAddress}/Items/${itemId}`, + `${STATE.jellyfinData.serverAddress}/Items/${itemId}?Fields=Overview,RemoteTrailers,Genres,CommunityRating,CriticRating,OfficialRating,PremiereDate,ProductionYear,MediaSources,RunTimeTicks`, { headers: this.getAuthHeaders(), } @@ -1033,8 +1091,16 @@ const ApiUtils = { console.log("Fetching random items from server..."); + let sortParams = `sortBy=${CONFIG.sortBy}`; + + if (CONFIG.sortBy === 'Random' || CONFIG.sortBy === 'Original') { + sortParams = 'sortBy=Random'; + } else { + sortParams += `&sortOrder=${CONFIG.sortOrder}`; + } + const response = await fetch( - `${STATE.jellyfinData.serverAddress}/Items?IncludeItemTypes=Movie,Series&Recursive=true&hasOverview=true&imageTypes=Logo,Backdrop&sortBy=Random&isPlayed=False&enableUserData=true&Limit=${CONFIG.maxItems}&fields=Id`, + `${STATE.jellyfinData.serverAddress}/Items?IncludeItemTypes=Movie,Series&Recursive=true&hasOverview=true&imageTypes=Logo,Backdrop&${sortParams}&isPlayed=False&enableUserData=true&Limit=${CONFIG.maxItems}&fields=Id`, { headers: this.getAuthHeaders(), } @@ -2550,14 +2616,12 @@ const SlideshowManager = { const focusElement = document.activeElement; switch (e.key) { - case "d": - case "D": + case "ArrowRight": SlideshowManager.nextSlide(); e.preventDefault(); break; - case "a": - case "A": + case "ArrowLeft": SlideshowManager.prevSlide(); e.preventDefault(); break; @@ -2776,10 +2840,28 @@ const SlideshowManager = { if (itemIds.length === 0) { console.log("No custom list found, fetching random items from server..."); itemIds = await ApiUtils.fetchItemIdsFromServer(); + + if (CONFIG.sortBy === 'Random') { + itemIds = SlideUtils.shuffleArray(itemIds); + } + } else { + // Custom IDs + if (CONFIG.sortBy === 'Random') { + itemIds = SlideUtils.shuffleArray(itemIds); + } else if (CONFIG.sortBy !== 'Original') { + // Client-side sort required... + console.log(`Sorting ${itemIds.length} custom items by ${CONFIG.sortBy} ${CONFIG.sortOrder}`); + const itemsWithDetails = []; + for (const id of itemIds) { + const item = await ApiUtils.fetchItemDetails(id); + if (item) itemsWithDetails.push(item); + } + + const sortedItems = SlideUtils.sortItems(itemsWithDetails, CONFIG.sortBy, CONFIG.sortOrder); + itemIds = sortedItems.map(i => i.Id); + } } - itemIds = SlideUtils.shuffleArray(itemIds); - STATE.slideshow.itemIds = itemIds; STATE.slideshow.totalItems = itemIds.length;