diff --git a/Jellyfin.Plugin.MediaBarEnhanced/Configuration/PluginConfiguration.cs b/Jellyfin.Plugin.MediaBarEnhanced/Configuration/PluginConfiguration.cs index e015864..ac445a3 100644 --- a/Jellyfin.Plugin.MediaBarEnhanced/Configuration/PluginConfiguration.cs +++ b/Jellyfin.Plugin.MediaBarEnhanced/Configuration/PluginConfiguration.cs @@ -15,6 +15,8 @@ namespace Jellyfin.Plugin.MediaBarEnhanced.Configuration public int MaxMovies { get; set; } = 15; public int MaxTvShows { get; set; } = 15; 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 FadeTransitionDuration { get; set; } = 500; public int MaxPaginationDots { get; set; } = 15; diff --git a/Jellyfin.Plugin.MediaBarEnhanced/Configuration/configPage.html b/Jellyfin.Plugin.MediaBarEnhanced/Configuration/configPage.html index ba64a44..aac6fed 100644 --- a/Jellyfin.Plugin.MediaBarEnhanced/Configuration/configPage.html +++ b/Jellyfin.Plugin.MediaBarEnhanced/Configuration/configPage.html @@ -356,7 +356,7 @@ mobile). -
Leave a setting blank to use the default value.
@@ -505,7 +515,8 @@ 'EnableSeasonalContent', 'EnableClientSideSettings', 'SortBy', 'SortOrder', 'PreferLocalTrailers', 'ApplyLimitsToCustomIds', 'SeasonalSections', 'PreferLocalBackdrops', 'RandomizeThemeVideos', 'RandomizeLocalTrailers', - 'IncludeWatchedContent', 'ShowPaginationDots' + 'IncludeWatchedContent', 'ShowPaginationDots', 'MaxParentalRating', + 'MaxDaysRecent' ]; // Manual mapping for MediaBarIsEnabled -> IsEnabled, to avoid conflicts with other plugins @@ -609,7 +620,8 @@ 'EnableSeasonalContent', 'EnableClientSideSettings', 'SortBy', 'SortOrder', 'PreferLocalTrailers', 'ApplyLimitsToCustomIds', 'SeasonalSections', 'PreferLocalBackdrops', 'RandomizeThemeVideos', 'RandomizeLocalTrailers', - 'IncludeWatchedContent', 'ShowPaginationDots' + 'IncludeWatchedContent', 'ShowPaginationDots', 'MaxParentalRating', + 'MaxDaysRecent' ]; keys.forEach(function (key) { diff --git a/Jellyfin.Plugin.MediaBarEnhanced/Web/mediaBarEnhanced.css b/Jellyfin.Plugin.MediaBarEnhanced/Web/mediaBarEnhanced.css index 14b7e2d..d6603f7 100644 --- a/Jellyfin.Plugin.MediaBarEnhanced/Web/mediaBarEnhanced.css +++ b/Jellyfin.Plugin.MediaBarEnhanced/Web/mediaBarEnhanced.css @@ -814,7 +814,6 @@ @media (max-width: 400px) { .button-container { gap: 10px; - /* top: calc(50% + 27vh); */ } .play-button, diff --git a/Jellyfin.Plugin.MediaBarEnhanced/Web/mediaBarEnhanced.js b/Jellyfin.Plugin.MediaBarEnhanced/Web/mediaBarEnhanced.js index 363a4f1..689b016 100644 --- a/Jellyfin.Plugin.MediaBarEnhanced/Web/mediaBarEnhanced.js +++ b/Jellyfin.Plugin.MediaBarEnhanced/Web/mediaBarEnhanced.js @@ -39,6 +39,8 @@ const CONFIG = { fadeTransitionDuration: 500, maxPaginationDots: 15, showPaginationDots: true, + maxParentalRating: null, + maxDaysRecent: null, slideAnimationEnabled: true, enableVideoBackdrop: true, useSponsorBlock: true, @@ -1111,13 +1113,26 @@ const ApiUtils = { // Filter by isPlayed=False unless IncludeWatchedContent is enabled const playedFilter = CONFIG.includeWatchedContent ? '' : '&isPlayed=False'; + + let parentalFilter = ''; + if (CONFIG.maxParentalRating) { + parentalFilter = `&MaxOfficialRating=${CONFIG.maxParentalRating}`; + } - const response = await fetch( - `${STATE.jellyfinData.serverAddress}/Items?IncludeItemTypes=Movie,Series&Recursive=true&hasOverview=true&imageTypes=Logo,Backdrop&${sortParams}${playedFilter}&enableUserData=true&Limit=${CONFIG.maxItems}&fields=Id`, - { - headers: this.getAuthHeaders(), - } - ); + let dateFilter = ''; + if (CONFIG.maxDaysRecent) { + const pastDate = new Date(); + pastDate.setDate(pastDate.getDate() - CONFIG.maxDaysRecent); + dateFilter = `&minDateLastSaved=${pastDate.toISOString()}`; + } + + const fetchItems = async (currentDateFilter) => { + const url = `${STATE.jellyfinData.serverAddress}/Items?IncludeItemTypes=Movie,Series&Recursive=true&hasOverview=true&imageTypes=Logo,Backdrop&${sortParams}${playedFilter}${parentalFilter}${currentDateFilter}&enableUserData=true&Limit=${CONFIG.maxItems}&fields=Id,DateCreated`; + const resp = await fetch(url, { headers: this.getAuthHeaders() }); + return resp; + }; + + let response = await fetchItems(dateFilter); if (!response.ok) { console.error("🎬 Media Bar:", @@ -1126,12 +1141,32 @@ const ApiUtils = { return []; } - const data = await response.json(); - const items = data.Items || []; + let data = await response.json(); + let items = data.Items || []; - console.log("🎬 Media Bar:", - `Successfully fetched ${items.length} random items from server` - ); + // Local exact DateCreated filter: minDateLastSaved pulls items that were merely modified recently (e.g. metadata updates) + // explicitly discard them if their actual DateCreated is older than X days + if (CONFIG.maxDaysRecent && dateFilter !== '') { + const pastDate = new Date(); + pastDate.setDate(pastDate.getDate() - CONFIG.maxDaysRecent); + items = items.filter(item => { + if (!item.DateCreated) return true; + return new Date(item.DateCreated) >= pastDate; + }); + } + + // Fallback: If we have a date filter but no items are returned, try again without it + if (items.length === 0 && dateFilter !== '') { + console.warn("🎬 Media Bar:", `No items found within the last ${CONFIG.maxDaysRecent} days. Falling back to random fetching.`); + response = await fetchItems(''); + + if (response.ok) { + data = await response.json(); + items = data.Items || []; + } + } + + console.log("🎬 Media Bar:", `Successfully fetched ${items.length} random items from server`); return items.map((item) => item.Id); } catch (error) {