From 7890d9fe5f0673b94e3f0f57559dbb3cf4097306 Mon Sep 17 00:00:00 2001 From: mattsigal Date: Sun, 19 Jul 2026 13:12:38 -0700 Subject: [PATCH 1/2] Redirect content hiding preferences to global profile on server sync --- .../Services/MoonfinSettingsService.cs | 57 +++++++++++++++++++ .../Services/MoonfinSettingsService.cs | 57 +++++++++++++++++++ 2 files changed, 114 insertions(+) diff --git a/Emby/Emby.Plugins.Moonfin/Services/MoonfinSettingsService.cs b/Emby/Emby.Plugins.Moonfin/Services/MoonfinSettingsService.cs index c9f8e7b..1fefae7 100644 --- a/Emby/Emby.Plugins.Moonfin/Services/MoonfinSettingsService.cs +++ b/Emby/Emby.Plugins.Moonfin/Services/MoonfinSettingsService.cs @@ -192,6 +192,8 @@ public async Task SaveUserSettingsAsync(Guid userId, MoonfinUserSettings setting finalSettings.LastUpdatedBy = clientId ?? "unknown"; finalSettings.SchemaVersion = 2; + MoveContentHidingToGlobal(finalSettings); + var json = JsonSerializer.Serialize(finalSettings, _jsonOptions); await Task.Run(() => AtomicFile.WriteAllText(filePath, json)).ConfigureAwait(false); } @@ -226,6 +228,8 @@ public async Task SaveProfileAsync(Guid userId, string profileName, MoonfinSetti settings.LastUpdatedBy = clientId ?? "unknown"; settings.SchemaVersion = 2; + MoveContentHidingToGlobal(settings); + var serialized = JsonSerializer.Serialize(settings, _jsonOptions); await Task.Run(() => AtomicFile.WriteAllText(filePath, serialized)).ConfigureAwait(false); } @@ -557,6 +561,59 @@ private MoonfinUserSettings MigrateV1ToV2(MoonfinUserSettings v1) }; } + private void MoveContentHidingToGlobal(MoonfinUserSettings settings) + { + if (settings.Global == null) + { + settings.Global = new MoonfinSettingsProfile(); + } + + // Check Desktop + if (settings.Desktop != null) + { + if (settings.Desktop.HiddenContinueWatchingItems != null) + { + settings.Global.HiddenContinueWatchingItems = settings.Desktop.HiddenContinueWatchingItems; + settings.Desktop.HiddenContinueWatchingItems = null; + } + if (settings.Desktop.HiddenNextUpSeries != null) + { + settings.Global.HiddenNextUpSeries = settings.Desktop.HiddenNextUpSeries; + settings.Desktop.HiddenNextUpSeries = null; + } + } + + // Check Mobile + if (settings.Mobile != null) + { + if (settings.Mobile.HiddenContinueWatchingItems != null) + { + settings.Global.HiddenContinueWatchingItems = settings.Mobile.HiddenContinueWatchingItems; + settings.Mobile.HiddenContinueWatchingItems = null; + } + if (settings.Mobile.HiddenNextUpSeries != null) + { + settings.Global.HiddenNextUpSeries = settings.Mobile.HiddenNextUpSeries; + settings.Mobile.HiddenNextUpSeries = null; + } + } + + // Check Tv + if (settings.Tv != null) + { + if (settings.Tv.HiddenContinueWatchingItems != null) + { + settings.Global.HiddenContinueWatchingItems = settings.Tv.HiddenContinueWatchingItems; + settings.Tv.HiddenContinueWatchingItems = null; + } + if (settings.Tv.HiddenNextUpSeries != null) + { + settings.Global.HiddenNextUpSeries = settings.Tv.HiddenNextUpSeries; + settings.Tv.HiddenNextUpSeries = null; + } + } + } + private static void MergeProfile(MoonfinSettingsProfile existing, MoonfinSettingsProfile incoming) { // An older client that syncs homeRowOrder but not homeSections should clear the stale diff --git a/Jellyfin/backend/Services/MoonfinSettingsService.cs b/Jellyfin/backend/Services/MoonfinSettingsService.cs index 7228cbf..9156b9b 100644 --- a/Jellyfin/backend/Services/MoonfinSettingsService.cs +++ b/Jellyfin/backend/Services/MoonfinSettingsService.cs @@ -229,6 +229,8 @@ public async Task SaveUserSettingsAsync(Guid userId, MoonfinUserSettings setting finalSettings.LastUpdatedBy = clientId ?? "unknown"; finalSettings.SchemaVersion = 2; + MoveContentHidingToGlobal(finalSettings); + var json = JsonSerializer.Serialize(finalSettings, _jsonOptions); AtomicFile.WriteAllText(filePath, json); } @@ -278,6 +280,8 @@ public async Task SaveProfileAsync( settings.LastUpdatedBy = clientId ?? "unknown"; settings.SchemaVersion = 2; + MoveContentHidingToGlobal(settings); + var serialized = JsonSerializer.Serialize(settings, _jsonOptions); AtomicFile.WriteAllText(filePath, serialized); } @@ -488,6 +492,59 @@ private void ClearLegacyFields(MoonfinUserSettings settings) settings.ClientSpecific = null; } + private void MoveContentHidingToGlobal(MoonfinUserSettings settings) + { + if (settings.Global == null) + { + settings.Global = new MoonfinSettingsProfile(); + } + + // Check Desktop + if (settings.Desktop != null) + { + if (settings.Desktop.HiddenContinueWatchingItems != null) + { + settings.Global.HiddenContinueWatchingItems = settings.Desktop.HiddenContinueWatchingItems; + settings.Desktop.HiddenContinueWatchingItems = null; + } + if (settings.Desktop.HiddenNextUpSeries != null) + { + settings.Global.HiddenNextUpSeries = settings.Desktop.HiddenNextUpSeries; + settings.Desktop.HiddenNextUpSeries = null; + } + } + + // Check Mobile + if (settings.Mobile != null) + { + if (settings.Mobile.HiddenContinueWatchingItems != null) + { + settings.Global.HiddenContinueWatchingItems = settings.Mobile.HiddenContinueWatchingItems; + settings.Mobile.HiddenContinueWatchingItems = null; + } + if (settings.Mobile.HiddenNextUpSeries != null) + { + settings.Global.HiddenNextUpSeries = settings.Mobile.HiddenNextUpSeries; + settings.Mobile.HiddenNextUpSeries = null; + } + } + + // Check Tv + if (settings.Tv != null) + { + if (settings.Tv.HiddenContinueWatchingItems != null) + { + settings.Global.HiddenContinueWatchingItems = settings.Tv.HiddenContinueWatchingItems; + settings.Tv.HiddenContinueWatchingItems = null; + } + if (settings.Tv.HiddenNextUpSeries != null) + { + settings.Global.HiddenNextUpSeries = settings.Tv.HiddenNextUpSeries; + settings.Tv.HiddenNextUpSeries = null; + } + } + } + private void MergeProfile(MoonfinSettingsProfile existing, MoonfinSettingsProfile incoming) { if (incoming.HomeSections == null && incoming.HomeRowOrder != null) From e9e6aaf72d7688106668af24c35c8523ef1b5a16 Mon Sep 17 00:00:00 2001 From: RadicalMuffinMan <103554043+RadicalMuffinMan@users.noreply.github.com> Date: Mon, 20 Jul 2026 10:45:14 -0400 Subject: [PATCH 2/2] Merged per-device content hiding into the global profile instead of overwriting --- .../Services/MoonfinSettingsService.cs | 85 ++++++++++++------- .../Services/MoonfinSettingsService.cs | 85 ++++++++++++------- 2 files changed, 106 insertions(+), 64 deletions(-) diff --git a/Emby/Emby.Plugins.Moonfin/Services/MoonfinSettingsService.cs b/Emby/Emby.Plugins.Moonfin/Services/MoonfinSettingsService.cs index 1fefae7..dcb1eb1 100644 --- a/Emby/Emby.Plugins.Moonfin/Services/MoonfinSettingsService.cs +++ b/Emby/Emby.Plugins.Moonfin/Services/MoonfinSettingsService.cs @@ -568,50 +568,71 @@ private void MoveContentHidingToGlobal(MoonfinUserSettings settings) settings.Global = new MoonfinSettingsProfile(); } - // Check Desktop - if (settings.Desktop != null) + // Content hiding is a global preference, so lift each device's hidden lists into the + // global profile. Union across devices so a file that hid items on more than one device + // keeps them all instead of the last device winning. + var devices = new[] { settings.Desktop, settings.Mobile, settings.Tv }; + + var hiddenContinueWatching = UnionHiddenEntries(devices, p => p.HiddenContinueWatchingItems); + if (hiddenContinueWatching != null) { - if (settings.Desktop.HiddenContinueWatchingItems != null) - { - settings.Global.HiddenContinueWatchingItems = settings.Desktop.HiddenContinueWatchingItems; - settings.Desktop.HiddenContinueWatchingItems = null; - } - if (settings.Desktop.HiddenNextUpSeries != null) - { - settings.Global.HiddenNextUpSeries = settings.Desktop.HiddenNextUpSeries; - settings.Desktop.HiddenNextUpSeries = null; - } + settings.Global.HiddenContinueWatchingItems = hiddenContinueWatching; } - // Check Mobile - if (settings.Mobile != null) + var hiddenNextUp = UnionHiddenEntries(devices, p => p.HiddenNextUpSeries); + if (hiddenNextUp != null) { - if (settings.Mobile.HiddenContinueWatchingItems != null) - { - settings.Global.HiddenContinueWatchingItems = settings.Mobile.HiddenContinueWatchingItems; - settings.Mobile.HiddenContinueWatchingItems = null; - } - if (settings.Mobile.HiddenNextUpSeries != null) - { - settings.Global.HiddenNextUpSeries = settings.Mobile.HiddenNextUpSeries; - settings.Mobile.HiddenNextUpSeries = null; - } + settings.Global.HiddenNextUpSeries = hiddenNextUp; } - // Check Tv - if (settings.Tv != null) + foreach (var device in devices) + { + if (device == null) continue; + device.HiddenContinueWatchingItems = null; + device.HiddenNextUpSeries = null; + } + } + + private static string? UnionHiddenEntries( + MoonfinSettingsProfile?[] devices, + Func selector) + { + Dictionary? merged = null; + foreach (var device in devices) { - if (settings.Tv.HiddenContinueWatchingItems != null) + if (device == null) continue; + var value = selector(device); + if (value == null) continue; + + if (merged == null) { - settings.Global.HiddenContinueWatchingItems = settings.Tv.HiddenContinueWatchingItems; - settings.Tv.HiddenContinueWatchingItems = null; + merged = new Dictionary(); } - if (settings.Tv.HiddenNextUpSeries != null) + foreach (var pair in ParseHiddenEntries(value)) { - settings.Global.HiddenNextUpSeries = settings.Tv.HiddenNextUpSeries; - settings.Tv.HiddenNextUpSeries = null; + merged[pair.Key] = pair.Value; } } + + return merged == null ? null : JsonSerializer.Serialize(merged); + } + + private static Dictionary ParseHiddenEntries(string value) + { + if (string.IsNullOrWhiteSpace(value)) + { + return new Dictionary(); + } + + try + { + return JsonSerializer.Deserialize>(value) + ?? new Dictionary(); + } + catch (JsonException) + { + return new Dictionary(); + } } private static void MergeProfile(MoonfinSettingsProfile existing, MoonfinSettingsProfile incoming) diff --git a/Jellyfin/backend/Services/MoonfinSettingsService.cs b/Jellyfin/backend/Services/MoonfinSettingsService.cs index 9156b9b..01eec2b 100644 --- a/Jellyfin/backend/Services/MoonfinSettingsService.cs +++ b/Jellyfin/backend/Services/MoonfinSettingsService.cs @@ -499,50 +499,71 @@ private void MoveContentHidingToGlobal(MoonfinUserSettings settings) settings.Global = new MoonfinSettingsProfile(); } - // Check Desktop - if (settings.Desktop != null) + // Content hiding is a global preference, so lift each device's hidden lists into the + // global profile. Union across devices so a file that hid items on more than one device + // keeps them all instead of the last device winning. + var devices = new[] { settings.Desktop, settings.Mobile, settings.Tv }; + + var hiddenContinueWatching = UnionHiddenEntries(devices, p => p.HiddenContinueWatchingItems); + if (hiddenContinueWatching != null) { - if (settings.Desktop.HiddenContinueWatchingItems != null) - { - settings.Global.HiddenContinueWatchingItems = settings.Desktop.HiddenContinueWatchingItems; - settings.Desktop.HiddenContinueWatchingItems = null; - } - if (settings.Desktop.HiddenNextUpSeries != null) - { - settings.Global.HiddenNextUpSeries = settings.Desktop.HiddenNextUpSeries; - settings.Desktop.HiddenNextUpSeries = null; - } + settings.Global.HiddenContinueWatchingItems = hiddenContinueWatching; } - // Check Mobile - if (settings.Mobile != null) + var hiddenNextUp = UnionHiddenEntries(devices, p => p.HiddenNextUpSeries); + if (hiddenNextUp != null) { - if (settings.Mobile.HiddenContinueWatchingItems != null) - { - settings.Global.HiddenContinueWatchingItems = settings.Mobile.HiddenContinueWatchingItems; - settings.Mobile.HiddenContinueWatchingItems = null; - } - if (settings.Mobile.HiddenNextUpSeries != null) - { - settings.Global.HiddenNextUpSeries = settings.Mobile.HiddenNextUpSeries; - settings.Mobile.HiddenNextUpSeries = null; - } + settings.Global.HiddenNextUpSeries = hiddenNextUp; } - // Check Tv - if (settings.Tv != null) + foreach (var device in devices) + { + if (device == null) continue; + device.HiddenContinueWatchingItems = null; + device.HiddenNextUpSeries = null; + } + } + + private static string? UnionHiddenEntries( + MoonfinSettingsProfile?[] devices, + Func selector) + { + Dictionary? merged = null; + foreach (var device in devices) { - if (settings.Tv.HiddenContinueWatchingItems != null) + if (device == null) continue; + var value = selector(device); + if (value == null) continue; + + if (merged == null) { - settings.Global.HiddenContinueWatchingItems = settings.Tv.HiddenContinueWatchingItems; - settings.Tv.HiddenContinueWatchingItems = null; + merged = new Dictionary(); } - if (settings.Tv.HiddenNextUpSeries != null) + foreach (var pair in ParseHiddenEntries(value)) { - settings.Global.HiddenNextUpSeries = settings.Tv.HiddenNextUpSeries; - settings.Tv.HiddenNextUpSeries = null; + merged[pair.Key] = pair.Value; } } + + return merged == null ? null : JsonSerializer.Serialize(merged); + } + + private static Dictionary ParseHiddenEntries(string value) + { + if (string.IsNullOrWhiteSpace(value)) + { + return new Dictionary(); + } + + try + { + return JsonSerializer.Deserialize>(value) + ?? new Dictionary(); + } + catch (JsonException) + { + return new Dictionary(); + } } private void MergeProfile(MoonfinSettingsProfile existing, MoonfinSettingsProfile incoming)