From a7b6ad88c67dab14aef964108cd4301b6e595be3 Mon Sep 17 00:00:00 2001 From: Selman Trabzon Date: Wed, 9 Sep 2026 19:41:51 +0300 Subject: [PATCH 1/2] feat: round-trip Seerr discover sliders in homeSections without a type table Persist sliderId and sliderType, keep disabled stubs, and only emit builtins in homeRowOrder so Moonbase does not strip slider identity. Co-authored-by: Cursor --- .../Models/MoonfinSettingsProfile.cs | 2 + Emby/Emby.Plugins.Moonfin/Pages/moonfin.js | 174 +++++++++++++++--- .../Services/MoonfinSettingsService.cs | 7 +- .../backend/Models/MoonfinSettingsProfile.cs | 6 + Jellyfin/backend/Pages/configPage.html | 172 ++++++++++++++--- .../Services/MoonfinSettingsService.cs | 7 +- 6 files changed, 310 insertions(+), 58 deletions(-) diff --git a/Emby/Emby.Plugins.Moonfin/Models/MoonfinSettingsProfile.cs b/Emby/Emby.Plugins.Moonfin/Models/MoonfinSettingsProfile.cs index 39ca0e9..d553e75 100644 --- a/Emby/Emby.Plugins.Moonfin/Models/MoonfinSettingsProfile.cs +++ b/Emby/Emby.Plugins.Moonfin/Models/MoonfinSettingsProfile.cs @@ -349,5 +349,7 @@ public class MoonfinHomeSectionConfig [JsonPropertyName("pluginSection")] public string? PluginSection { get; set; } [JsonPropertyName("pluginAdditionalData")] public string? PluginAdditionalData { get; set; } [JsonPropertyName("pluginDisplayText")] public string? PluginDisplayText { get; set; } + [JsonPropertyName("sliderId")] public string? SliderId { get; set; } + [JsonPropertyName("sliderType")] public int? SliderType { get; set; } } } diff --git a/Emby/Emby.Plugins.Moonfin/Pages/moonfin.js b/Emby/Emby.Plugins.Moonfin/Pages/moonfin.js index 56fa7c3..c283e30 100644 --- a/Emby/Emby.Plugins.Moonfin/Pages/moonfin.js +++ b/Emby/Emby.Plugins.Moonfin/Pages/moonfin.js @@ -1102,24 +1102,51 @@ define(['baseView', 'loading', 'emby-input', 'emby-button', 'emby-checkbox', 'em { id: 'seerr', label: 'Seerr' } ]; - var HOME_LAYOUT_SEERR_TYPES = { - seerr_shortcuts: true, - seerr_recent_requests: true, - seerr_recently_added: true, - seerr_popular_movies: true, - seerr_upcoming_movies: true, - seerr_popular_series: true, - seerr_upcoming_series: true, - seerr_trending: true, - seerr_movie_genres: true, - seerr_studios: true, - seerr_series_genres: true, - seerr_networks: true, - seerr_watchlist: true - }; - function isSeerrHomeSectionType(type) { - return !!HOME_LAYOUT_SEERR_TYPES[type]; + return !!(type && type.indexOf('seerr_') === 0); + } + + function isSeerrSliderSection(section) { + return !!section && (section.kind === 'seerrSlider' || + (section.kind === 'pluginDynamic' && section.pluginSource === 'seerr') || + section.type === 'seerr_slider'); + } + + function seerrSliderIdOf(section) { + if (!section) return ''; + if (section.sliderId) return String(section.sliderId); + if (section.pluginAdditionalData) return String(section.pluginAdditionalData); + return ''; + } + + function seerrSliderTypeOf(section) { + if (!section || section.sliderType == null || section.sliderType === '') return null; + var n = Number(section.sliderType); + return Number.isFinite(n) ? n : null; + } + + function seerrSliderLiveTitle(view, section) { + var state = getHomeLayoutState(view); + var sliders = state.seerrDiscoverSliders || []; + var id = seerrSliderIdOf(section); + for (var i = 0; i < sliders.length; i++) { + if (String(sliders[i].id) !== id) continue; + return (sliders[i].title || '').trim(); + } + return (section.pluginDisplayText || '').trim(); + } + + function seerrSliderLabel(slider) { + var title = (slider && slider.title ? String(slider.title) : '').trim(); + return title || 'Seerr slider'; + } + + function isSeerrCustomSliderType(type) { + var n = Number(type); + if (!Number.isFinite(n)) return false; + if (n >= 1 && n <= 12) return false; + if (n >= 38 && n <= 43) return false; + return n > 12; } function getHomeLayoutState(view) { @@ -1139,8 +1166,13 @@ define(['baseView', 'loading', 'emby-input', 'emby-button', 'emby-checkbox', 'em return HOME_SECTION_DEFINITIONS.find(function (row) { return row.type === type; }) || null; } - function homeSectionLabel(section) { + function homeSectionLabel(view, section) { if (!section) return 'Unknown row'; + if (isSeerrSliderSection(section)) { + return seerrSliderLiveTitle(view, section) || + section.pluginDisplayText || + 'Seerr slider'; + } if (section.kind === 'pluginDynamic') { return section.pluginDisplayText || section.pluginSection || 'Dynamic row'; } @@ -1150,8 +1182,9 @@ define(['baseView', 'loading', 'emby-input', 'emby-button', 'emby-checkbox', 'em function homeSectionMeta(section) { if (!section) return 'Basics'; + if (isSeerrSliderSection(section) || isSeerrHomeSectionType(section.type)) return 'Seerr'; if (section.kind !== 'pluginDynamic') { - return isSeerrHomeSectionType(section.type) ? 'Seerr' : 'Basics'; + return 'Basics'; } if (section.pluginSource === 'collections') return 'Collection'; if (section.pluginSource === 'playlists') return 'Playlist'; @@ -1161,8 +1194,10 @@ define(['baseView', 'loading', 'emby-input', 'emby-button', 'emby-checkbox', 'em } function homeSectionBadgeClass(section) { + if (isSeerrSliderSection(section) || (section && isSeerrHomeSectionType(section.type))) { + return 'homeLayoutBadge-seerr'; + } if (!section || section.kind !== 'pluginDynamic') { - if (section && isSeerrHomeSectionType(section.type)) return 'homeLayoutBadge-seerr'; return 'homeLayoutBadge-builtin'; } if (section.pluginSource === 'collections') return 'homeLayoutBadge-collections'; @@ -1173,6 +1208,9 @@ define(['baseView', 'loading', 'emby-input', 'emby-button', 'emby-checkbox', 'em function homeSectionKey(section) { if (!section) return ''; + if (isSeerrSliderSection(section)) { + return 'seerrSlider:' + seerrSliderIdOf(section); + } if (section.kind === 'pluginDynamic') { return [ 'pluginDynamic', @@ -1202,7 +1240,9 @@ define(['baseView', 'loading', 'emby-input', 'emby-button', 'emby-checkbox', 'em function renumberHomeLayoutSections(state) { state.sections.forEach(function (section, index) { - section.enabled = true; + if (section.kind !== 'pluginDynamic' && section.kind !== 'seerrSlider') { + section.enabled = true; + } section.order = index; }); } @@ -1234,7 +1274,22 @@ define(['baseView', 'loading', 'emby-input', 'emby-button', 'emby-checkbox', 'em }).forEach(function (section) { if (!section) return; var normalized; - if (section.kind === 'pluginDynamic') { + if (isSeerrSliderSection(section)) { + var sliderId = seerrSliderIdOf(section); + if (!sliderId) return; + var sliderType = seerrSliderTypeOf(section); + normalized = { + kind: 'seerrSlider', + type: 'seerr_slider', + enabled: section.enabled !== false, + order: ordered.length, + sliderId: sliderId + }; + if (sliderType != null) normalized.sliderType = sliderType; + if (section.pluginDisplayText) { + normalized.pluginDisplayText = section.pluginDisplayText; + } + } else if (section.kind === 'pluginDynamic') { normalized = { kind: 'pluginDynamic', type: 'none', @@ -1283,6 +1338,29 @@ define(['baseView', 'loading', 'emby-input', 'emby-button', 'emby-checkbox', 'em }; } + function createSeerrSliderCandidate(slider) { + var sliderId = String(slider.id); + var label = seerrSliderLabel(slider); + var candidate = { + key: 'seerrSlider:' + sliderId, + tab: 'seerr', + label: label, + meta: 'Seerr', + badgeClass: 'homeLayoutBadge-seerr', + section: { + kind: 'seerrSlider', + type: 'seerr_slider', + enabled: true, + order: 0, + sliderId: sliderId, + pluginDisplayText: label + } + }; + var sliderType = Number(slider.type); + if (Number.isFinite(sliderType)) candidate.section.sliderType = sliderType; + return candidate; + } + function createDynamicCandidate(tab, source, sectionName, id, label, meta, serverId) { return { key: ['pluginDynamic', source, sectionName, id || ''].join(':'), @@ -1309,7 +1387,9 @@ define(['baseView', 'loading', 'emby-input', 'emby-button', 'emby-checkbox', 'em .filter(function (definition) { return !isSeerrHomeSectionType(definition.type); }) .map(createBuiltinCandidate); state.available.seerr = HOME_SECTION_DEFINITIONS - .filter(function (definition) { return isSeerrHomeSectionType(definition.type); }) + .filter(function (definition) { + return isSeerrHomeSectionType(definition.type); + }) .map(createBuiltinCandidate); } @@ -1393,7 +1473,7 @@ define(['baseView', 'loading', 'emby-input', 'emby-button', 'emby-checkbox', 'em row.dataset.index = String(index); row.innerHTML = '
' + - '
' + esc(homeSectionLabel(section)) + '
' + + '
' + esc(homeSectionLabel(view, section)) + '
' + '' + esc(homeSectionMeta(section)) + '' + '
' + '' + @@ -1534,6 +1614,39 @@ define(['baseView', 'loading', 'emby-input', 'emby-button', 'emby-checkbox', 'em loadHomeLayoutCollections(view); loadHomeLayoutPlaylists(view); loadHomeLayoutGenres(view); + loadHomeLayoutSeerrSliders(view); + } + + function seerrBuiltinCandidates() { + return HOME_SECTION_DEFINITIONS + .filter(function (definition) { + return isSeerrHomeSectionType(definition.type); + }) + .map(createBuiltinCandidate); + } + + function loadHomeLayoutSeerrSliders(view) { + var serverId = ApiClient.serverAddress ? ApiClient.serverAddress() : ''; + fetch(serverId + '/Moonfin/Seerr/Api/settings/discover', { + method: 'GET', + headers: moonfinAuthHeaders() + }).then(function (response) { + if (!response.ok) return []; + return response.json(); + }).then(function (data) { + var sliders = Array.isArray(data) ? data : []; + var state = getHomeLayoutState(view); + state.seerrDiscoverSliders = sliders; + var candidates = sliders.filter(function (slider) { + if (!slider || slider.enabled === false || !slider.id) return false; + return isSeerrCustomSliderType(slider.type); + }).map(createSeerrSliderCandidate); + setHomeLayoutAvailable(view, 'seerr', seerrBuiltinCandidates().concat(candidates)); + renderHomeSectionsEditor(view); + }).catch(function () { + getHomeLayoutState(view).seerrDiscoverSliders = []; + setHomeLayoutAvailable(view, 'seerr', seerrBuiltinCandidates()); + }); } function setHomeLayoutAvailable(view, tab, candidates) { @@ -1635,15 +1748,22 @@ define(['baseView', 'loading', 'emby-input', 'emby-button', 'emby-checkbox', 'em if (state.sections.length === 0) return null; renumberHomeLayoutSections(state); return state.sections.map(function (section) { + var isSlider = section.kind === 'seerrSlider'; var isDynamic = section.kind === 'pluginDynamic'; var result = { - type: isDynamic ? 'none' : section.type, + type: isSlider ? (section.type || 'seerr_slider') : (isDynamic ? 'none' : section.type), // Dynamic rows stay in the editor when disabled, unlike builtins, so writing // them all back as enabled would switch every custom row on. - enabled: isDynamic ? section.enabled !== false : true, + enabled: (isDynamic || isSlider) ? section.enabled !== false : true, order: section.order }; - if (isDynamic) { + if (isSlider) { + result.kind = 'seerrSlider'; + result.type = 'seerr_slider'; + result.sliderId = section.sliderId; + if (section.sliderType != null) result.sliderType = section.sliderType; + if (section.pluginDisplayText) result.pluginDisplayText = section.pluginDisplayText; + } else if (isDynamic) { result.kind = 'pluginDynamic'; result.pluginSource = section.pluginSource || 'hss'; // Custom rows have no server of their own and the client keys them on this, diff --git a/Emby/Emby.Plugins.Moonfin/Services/MoonfinSettingsService.cs b/Emby/Emby.Plugins.Moonfin/Services/MoonfinSettingsService.cs index fff9a34..065df58 100644 --- a/Emby/Emby.Plugins.Moonfin/Services/MoonfinSettingsService.cs +++ b/Emby/Emby.Plugins.Moonfin/Services/MoonfinSettingsService.cs @@ -212,10 +212,13 @@ private static (List? HomeSections, List? Home if (profile.HomeSections == null) return null; var homeRowOrder = profile.HomeSections - .Where(section => !string.Equals(section.Kind, "pluginDynamic", StringComparison.OrdinalIgnoreCase)) + .Where(section => + string.IsNullOrEmpty(section.Kind) || + string.Equals(section.Kind, "builtin", StringComparison.OrdinalIgnoreCase)) .Where(section => section.Enabled != false) .Where(section => !string.IsNullOrWhiteSpace(section.Type) && - !string.Equals(section.Type, "none", StringComparison.OrdinalIgnoreCase)) + !string.Equals(section.Type, "none", StringComparison.OrdinalIgnoreCase) && + !string.Equals(section.Type, "seerr_slider", StringComparison.OrdinalIgnoreCase)) .OrderBy(section => section.Order ?? int.MaxValue) .Select(section => section.Type!) .ToList(); diff --git a/Jellyfin/backend/Models/MoonfinSettingsProfile.cs b/Jellyfin/backend/Models/MoonfinSettingsProfile.cs index a9f1d84..d73a382 100644 --- a/Jellyfin/backend/Models/MoonfinSettingsProfile.cs +++ b/Jellyfin/backend/Models/MoonfinSettingsProfile.cs @@ -973,4 +973,10 @@ public class MoonfinHomeSectionConfig [JsonPropertyName("pluginDisplayText")] public string? PluginDisplayText { get; set; } + + [JsonPropertyName("sliderId")] + public string? SliderId { get; set; } + + [JsonPropertyName("sliderType")] + public int? SliderType { get; set; } } diff --git a/Jellyfin/backend/Pages/configPage.html b/Jellyfin/backend/Pages/configPage.html index a7f7641..8594561 100644 --- a/Jellyfin/backend/Pages/configPage.html +++ b/Jellyfin/backend/Pages/configPage.html @@ -4009,24 +4009,6 @@

Active Downloads

{ id: 'external', label: 'External Lists' } ]; - var HOME_LAYOUT_SEERR_TYPES = { - seerr_shortcuts: true, - seerr_recent_requests: true, - seerr_recently_added: true, - seerr_popular_movies: true, - seerr_upcoming_movies: true, - seerr_popular_series: true, - seerr_upcoming_series: true, - seerr_trending: true, - seerr_movie_genres: true, - seerr_studios: true, - seerr_series_genres: true, - seerr_networks: true, - seerr_watchlist: true, - radarr_calendar: true, - sonarr_calendar: true - }; - var HOME_LAYOUT_EXTERNAL_LISTS = { imdb_top_250_movies: true, imdb_top_250_tv_shows: true, @@ -4050,7 +4032,51 @@

Active Downloads

}; function isSeerrHomeSectionType(type) { - return !!HOME_LAYOUT_SEERR_TYPES[type]; + return (type && type.indexOf('seerr_') === 0) || + type === 'radarr_calendar' || + type === 'sonarr_calendar'; + } + + function isSeerrSliderSection(section) { + return !!section && (section.kind === 'seerrSlider' || + (section.kind === 'pluginDynamic' && section.pluginSource === 'seerr') || + section.type === 'seerr_slider'); + } + + function seerrSliderIdOf(section) { + if (!section) return ''; + if (section.sliderId) return String(section.sliderId); + if (section.pluginAdditionalData) return String(section.pluginAdditionalData); + return ''; + } + + function seerrSliderTypeOf(section) { + if (!section || section.sliderType == null || section.sliderType === '') return null; + var n = Number(section.sliderType); + return Number.isFinite(n) ? n : null; + } + + function seerrSliderLiveTitle(section) { + var sliders = HOME_LAYOUT_STATE.seerrDiscoverSliders || []; + var id = seerrSliderIdOf(section); + for (var i = 0; i < sliders.length; i++) { + if (String(sliders[i].id) !== id) continue; + return (sliders[i].title || '').trim(); + } + return (section.pluginDisplayText || '').trim(); + } + + function seerrSliderLabel(slider) { + var title = (slider && slider.title ? String(slider.title) : '').trim(); + return title || 'Seerr slider'; + } + + function isSeerrCustomSliderType(type) { + var n = Number(type); + if (!Number.isFinite(n)) return false; + if (n >= 1 && n <= 12) return false; + if (n >= 38 && n <= 43) return false; + return n > 12; } function isExternalHomeSectionType(type) { @@ -4086,6 +4112,11 @@

Active Downloads

function homeSectionLabel(section) { if (!section) return 'Unknown row'; + if (isSeerrSliderSection(section)) { + return seerrSliderLiveTitle(section) || + section.pluginDisplayText || + 'Seerr slider'; + } if (section.kind === 'pluginDynamic') { return section.pluginDisplayText || section.pluginSection || 'Dynamic row'; } @@ -4095,8 +4126,9 @@

Active Downloads

function homeSectionMeta(section) { if (!section) return 'Basics'; + if (isSeerrSliderSection(section) || isSeerrHomeSectionType(section.type)) return 'Seerr'; if (section.kind !== 'pluginDynamic') { - return isSeerrHomeSectionType(section.type) ? 'Seerr' : (isExternalHomeSectionType(section.type) ? 'External Lists' : 'Basics'); + return isExternalHomeSectionType(section.type) ? 'External Lists' : 'Basics'; } if (section.pluginSource === 'collections') return 'Collection'; if (section.pluginSource === 'playlists') return 'Playlist'; @@ -4105,8 +4137,10 @@

Active Downloads

} function homeSectionBadgeClass(section) { + if (isSeerrSliderSection(section) || (section && isSeerrHomeSectionType(section.type))) { + return 'homeLayoutBadge-seerr'; + } if (!section || section.kind !== 'pluginDynamic') { - if (section && isSeerrHomeSectionType(section.type)) return 'homeLayoutBadge-seerr'; if (section && isExternalHomeSectionType(section.type)) return 'homeLayoutBadge-external'; return 'homeLayoutBadge-builtin'; } @@ -4118,6 +4152,9 @@

Active Downloads

function homeSectionKey(section) { if (!section) return ''; + if (isSeerrSliderSection(section)) { + return 'seerrSlider:' + seerrSliderIdOf(section); + } if (section.kind === 'pluginDynamic') { return [ 'pluginDynamic', @@ -4147,7 +4184,9 @@

Active Downloads

function renumberHomeLayoutSections() { HOME_LAYOUT_STATE.sections.forEach(function(section, index) { - section.enabled = true; + if (section.kind !== 'pluginDynamic' && section.kind !== 'seerrSlider') { + section.enabled = true; + } section.order = index; }); } @@ -4179,7 +4218,22 @@

Active Downloads

}).forEach(function(section) { if (!section) return; var normalized; - if (section.kind === 'pluginDynamic') { + if (isSeerrSliderSection(section)) { + var sliderId = seerrSliderIdOf(section); + if (!sliderId) return; + var sliderType = seerrSliderTypeOf(section); + normalized = { + kind: 'seerrSlider', + type: 'seerr_slider', + enabled: section.enabled !== false, + order: ordered.length, + sliderId: sliderId + }; + if (sliderType != null) normalized.sliderType = sliderType; + if (section.pluginDisplayText) { + normalized.pluginDisplayText = section.pluginDisplayText; + } + } else if (section.kind === 'pluginDynamic') { normalized = { kind: 'pluginDynamic', type: 'none', @@ -4228,6 +4282,29 @@

Active Downloads

}; } + function createSeerrSliderCandidate(slider) { + var sliderId = String(slider.id); + var label = seerrSliderLabel(slider); + var candidate = { + key: 'seerrSlider:' + sliderId, + tab: 'seerr', + label: label, + meta: 'Seerr', + badgeClass: 'homeLayoutBadge-seerr', + section: { + kind: 'seerrSlider', + type: 'seerr_slider', + enabled: true, + order: 0, + sliderId: sliderId, + pluginDisplayText: label + } + }; + var sliderType = Number(slider.type); + if (Number.isFinite(sliderType)) candidate.section.sliderType = sliderType; + return candidate; + } + function createDynamicCandidate(tab, source, sectionName, id, label, meta, serverId) { return { key: ['pluginDynamic', source, sectionName, id || ''].join(':'), @@ -4254,7 +4331,9 @@

Active Downloads

.filter(function(definition) { return (!isSeerrHomeSectionType(definition.type) && !isExternalHomeSectionType(definition.type)); }) .map(createBuiltinCandidate); HOME_LAYOUT_STATE.available.seerr = HOME_SECTION_DEFINITIONS - .filter(function(definition) { return isSeerrHomeSectionType(definition.type); }) + .filter(function(definition) { + return isSeerrHomeSectionType(definition.type); + }) .map(createBuiltinCandidate); HOME_LAYOUT_STATE.available.external = HOME_SECTION_DEFINITIONS .filter(function(definition) { return isExternalHomeSectionType(definition.type); }) @@ -4482,6 +4561,38 @@

Active Downloads

loadHomeLayoutCollections(); loadHomeLayoutPlaylists(); loadHomeLayoutGenres(); + loadHomeLayoutSeerrSliders(); + } + + function seerrBuiltinCandidates() { + return HOME_SECTION_DEFINITIONS + .filter(function(definition) { + return isSeerrHomeSectionType(definition.type); + }) + .map(createBuiltinCandidate); + } + + function loadHomeLayoutSeerrSliders() { + var serverId = ApiClient.serverAddress ? ApiClient.serverAddress() : ''; + fetch(serverId + '/Moonfin/Seerr/Api/settings/discover', { + method: 'GET', + headers: getMoonfinAuthHeaders() + }).then(function(response) { + if (!response.ok) return []; + return response.json(); + }).then(function(data) { + var sliders = Array.isArray(data) ? data : []; + HOME_LAYOUT_STATE.seerrDiscoverSliders = sliders; + var candidates = sliders.filter(function(slider) { + if (!slider || slider.enabled === false || !slider.id) return false; + return isSeerrCustomSliderType(slider.type); + }).map(createSeerrSliderCandidate); + setHomeLayoutAvailable('seerr', seerrBuiltinCandidates().concat(candidates)); + renderHomeSectionsEditor(); + }).catch(function() { + HOME_LAYOUT_STATE.seerrDiscoverSliders = []; + setHomeLayoutAvailable('seerr', seerrBuiltinCandidates()); + }); } function setHomeLayoutAvailable(tab, candidates) { @@ -4581,15 +4692,22 @@

Active Downloads

if (HOME_LAYOUT_STATE.sections.length === 0) return null; renumberHomeLayoutSections(); return HOME_LAYOUT_STATE.sections.map(function(section) { + var isSlider = section.kind === 'seerrSlider'; var isDynamic = section.kind === 'pluginDynamic'; var result = { - type: isDynamic ? 'none' : section.type, + type: isSlider ? (section.type || 'seerr_slider') : (isDynamic ? 'none' : section.type), // Dynamic rows stay in the editor when disabled, unlike builtins, so // writing them all back as enabled would switch every custom row on. - enabled: isDynamic ? section.enabled !== false : true, + enabled: (isDynamic || isSlider) ? section.enabled !== false : true, order: section.order }; - if (isDynamic) { + if (isSlider) { + result.kind = 'seerrSlider'; + result.type = 'seerr_slider'; + result.sliderId = section.sliderId; + if (section.sliderType != null) result.sliderType = section.sliderType; + if (section.pluginDisplayText) result.pluginDisplayText = section.pluginDisplayText; + } else if (isDynamic) { result.kind = 'pluginDynamic'; result.pluginSource = section.pluginSource; // Custom rows have no server of their own and the client keys them on diff --git a/Jellyfin/backend/Services/MoonfinSettingsService.cs b/Jellyfin/backend/Services/MoonfinSettingsService.cs index eb48e1b..46c5941 100644 --- a/Jellyfin/backend/Services/MoonfinSettingsService.cs +++ b/Jellyfin/backend/Services/MoonfinSettingsService.cs @@ -251,10 +251,13 @@ private static (List? HomeSections, List? Home } var homeRowOrder = profile.HomeSections - .Where(section => !string.Equals(section.Kind, "pluginDynamic", StringComparison.OrdinalIgnoreCase)) + .Where(section => + string.IsNullOrEmpty(section.Kind) || + string.Equals(section.Kind, "builtin", StringComparison.OrdinalIgnoreCase)) .Where(section => section.Enabled != false) .Where(section => !string.IsNullOrWhiteSpace(section.Type) && - !string.Equals(section.Type, "none", StringComparison.OrdinalIgnoreCase)) + !string.Equals(section.Type, "none", StringComparison.OrdinalIgnoreCase) && + !string.Equals(section.Type, "seerr_slider", StringComparison.OrdinalIgnoreCase)) .OrderBy(section => section.Order ?? int.MaxValue) .Select(section => section.Type!) .ToList(); From d31e6af59ef8aa8bfdecb89fb30ae4ab3150d4b3 Mon Sep 17 00:00:00 2001 From: Selman Trabzon Date: Wed, 9 Sep 2026 20:03:36 +0300 Subject: [PATCH 2/2] fix: keep Radarr/Sonarr off the Jellyfin Seerr home-layout tab Match Emby: only types that start with seerr_ belong there. Rename isSeerrCustomSliderType to isSeerrSliderType. Co-authored-by: Cursor --- Emby/Emby.Plugins.Moonfin/Pages/moonfin.js | 4 ++-- Jellyfin/backend/Pages/configPage.html | 8 +++----- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/Emby/Emby.Plugins.Moonfin/Pages/moonfin.js b/Emby/Emby.Plugins.Moonfin/Pages/moonfin.js index c283e30..ef5a1a4 100644 --- a/Emby/Emby.Plugins.Moonfin/Pages/moonfin.js +++ b/Emby/Emby.Plugins.Moonfin/Pages/moonfin.js @@ -1141,7 +1141,7 @@ define(['baseView', 'loading', 'emby-input', 'emby-button', 'emby-checkbox', 'em return title || 'Seerr slider'; } - function isSeerrCustomSliderType(type) { + function isSeerrSliderType(type) { var n = Number(type); if (!Number.isFinite(n)) return false; if (n >= 1 && n <= 12) return false; @@ -1639,7 +1639,7 @@ define(['baseView', 'loading', 'emby-input', 'emby-button', 'emby-checkbox', 'em state.seerrDiscoverSliders = sliders; var candidates = sliders.filter(function (slider) { if (!slider || slider.enabled === false || !slider.id) return false; - return isSeerrCustomSliderType(slider.type); + return isSeerrSliderType(slider.type); }).map(createSeerrSliderCandidate); setHomeLayoutAvailable(view, 'seerr', seerrBuiltinCandidates().concat(candidates)); renderHomeSectionsEditor(view); diff --git a/Jellyfin/backend/Pages/configPage.html b/Jellyfin/backend/Pages/configPage.html index 8594561..caf55ea 100644 --- a/Jellyfin/backend/Pages/configPage.html +++ b/Jellyfin/backend/Pages/configPage.html @@ -4032,9 +4032,7 @@

Active Downloads

}; function isSeerrHomeSectionType(type) { - return (type && type.indexOf('seerr_') === 0) || - type === 'radarr_calendar' || - type === 'sonarr_calendar'; + return !!(type && type.indexOf('seerr_') === 0); } function isSeerrSliderSection(section) { @@ -4071,7 +4069,7 @@

Active Downloads

return title || 'Seerr slider'; } - function isSeerrCustomSliderType(type) { + function isSeerrSliderType(type) { var n = Number(type); if (!Number.isFinite(n)) return false; if (n >= 1 && n <= 12) return false; @@ -4585,7 +4583,7 @@

Active Downloads

HOME_LAYOUT_STATE.seerrDiscoverSliders = sliders; var candidates = sliders.filter(function(slider) { if (!slider || slider.enabled === false || !slider.id) return false; - return isSeerrCustomSliderType(slider.type); + return isSeerrSliderType(slider.type); }).map(createSeerrSliderCandidate); setHomeLayoutAvailable('seerr', seerrBuiltinCandidates().concat(candidates)); renderHomeSectionsEditor();