From b0c79edd2c25f560208a40aedea05498d48ca80e Mon Sep 17 00:00:00 2001 From: crobibero Date: Thu, 3 Dec 2020 13:51:12 -0700 Subject: Add support for custom item display preferences --- Jellyfin.Server.Implementations/JellyfinDb.cs | 10 + ...3203707_AddCustomDisplayPreferences.Designer.cs | 516 +++++++++++++++++++++ .../20201203203707_AddCustomDisplayPreferences.cs | 70 +++ .../Migrations/JellyfinDbModelSnapshot.cs | 131 ++++-- .../Users/DisplayPreferencesManager.cs | 24 + 5 files changed, 712 insertions(+), 39 deletions(-) create mode 100644 Jellyfin.Server.Implementations/Migrations/20201203203707_AddCustomDisplayPreferences.Designer.cs create mode 100644 Jellyfin.Server.Implementations/Migrations/20201203203707_AddCustomDisplayPreferences.cs (limited to 'Jellyfin.Server.Implementations') diff --git a/Jellyfin.Server.Implementations/JellyfinDb.cs b/Jellyfin.Server.Implementations/JellyfinDb.cs index bf8818f8d..a8aa9b319 100644 --- a/Jellyfin.Server.Implementations/JellyfinDb.cs +++ b/Jellyfin.Server.Implementations/JellyfinDb.cs @@ -34,6 +34,8 @@ namespace Jellyfin.Server.Implementations public virtual DbSet ItemDisplayPreferences { get; set; } + public virtual DbSet CustomItemDisplayPreferences { get; set; } + public virtual DbSet Permissions { get; set; } public virtual DbSet Preferences { get; set; } @@ -153,6 +155,14 @@ namespace Jellyfin.Server.Implementations modelBuilder.Entity() .HasIndex(entity => new { entity.UserId, entity.Client }) .IsUnique(); + + modelBuilder.Entity() + .HasIndex(entity => entity.UserId) + .IsUnique(false); + + modelBuilder.Entity() + .HasIndex(entity => new { entity.UserId, entity.Client, entity.Key }) + .IsUnique(); } } } diff --git a/Jellyfin.Server.Implementations/Migrations/20201203203707_AddCustomDisplayPreferences.Designer.cs b/Jellyfin.Server.Implementations/Migrations/20201203203707_AddCustomDisplayPreferences.Designer.cs new file mode 100644 index 000000000..ce3e30b59 --- /dev/null +++ b/Jellyfin.Server.Implementations/Migrations/20201203203707_AddCustomDisplayPreferences.Designer.cs @@ -0,0 +1,516 @@ +#pragma warning disable CS1591 +// +using System; +using Jellyfin.Server.Implementations; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +namespace Jellyfin.Server.Implementations.Migrations +{ + [DbContext(typeof(JellyfinDb))] + [Migration("20201203203707_AddCustomDisplayPreferences")] + partial class AddCustomDisplayPreferences + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasDefaultSchema("jellyfin") + .HasAnnotation("ProductVersion", "5.0.0"); + + modelBuilder.Entity("Jellyfin.Data.Entities.AccessSchedule", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("DayOfWeek") + .HasColumnType("INTEGER"); + + b.Property("EndHour") + .HasColumnType("REAL"); + + b.Property("StartHour") + .HasColumnType("REAL"); + + b.Property("UserId") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("AccessSchedules"); + }); + + modelBuilder.Entity("Jellyfin.Data.Entities.ActivityLog", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("DateCreated") + .HasColumnType("TEXT"); + + b.Property("ItemId") + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.Property("LogSeverity") + .HasColumnType("INTEGER"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(512) + .HasColumnType("TEXT"); + + b.Property("Overview") + .HasMaxLength(512) + .HasColumnType("TEXT"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("INTEGER"); + + b.Property("ShortOverview") + .HasMaxLength(512) + .HasColumnType("TEXT"); + + b.Property("Type") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.Property("UserId") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("ActivityLogs"); + }); + + modelBuilder.Entity("Jellyfin.Data.Entities.CustomItemDisplayPreferences", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Client") + .IsRequired() + .HasMaxLength(32) + .HasColumnType("TEXT"); + + b.Property("Key") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("UserId") + .HasColumnType("TEXT"); + + b.Property("Value") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.HasIndex("UserId", "Client", "Key") + .IsUnique(); + + b.ToTable("CustomItemDisplayPreferences"); + }); + + modelBuilder.Entity("Jellyfin.Data.Entities.DisplayPreferences", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("ChromecastVersion") + .HasColumnType("INTEGER"); + + b.Property("Client") + .IsRequired() + .HasMaxLength(32) + .HasColumnType("TEXT"); + + b.Property("DashboardTheme") + .HasMaxLength(32) + .HasColumnType("TEXT"); + + b.Property("EnableNextVideoInfoOverlay") + .HasColumnType("INTEGER"); + + b.Property("IndexBy") + .HasColumnType("INTEGER"); + + b.Property("ScrollDirection") + .HasColumnType("INTEGER"); + + b.Property("ShowBackdrop") + .HasColumnType("INTEGER"); + + b.Property("ShowSidebar") + .HasColumnType("INTEGER"); + + b.Property("SkipBackwardLength") + .HasColumnType("INTEGER"); + + b.Property("SkipForwardLength") + .HasColumnType("INTEGER"); + + b.Property("TvHome") + .HasMaxLength(32) + .HasColumnType("TEXT"); + + b.Property("UserId") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.HasIndex("UserId", "Client") + .IsUnique(); + + b.ToTable("DisplayPreferences"); + }); + + modelBuilder.Entity("Jellyfin.Data.Entities.HomeSection", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("DisplayPreferencesId") + .HasColumnType("INTEGER"); + + b.Property("Order") + .HasColumnType("INTEGER"); + + b.Property("Type") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("DisplayPreferencesId"); + + b.ToTable("HomeSection"); + }); + + modelBuilder.Entity("Jellyfin.Data.Entities.ImageInfo", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("LastModified") + .HasColumnType("TEXT"); + + b.Property("Path") + .IsRequired() + .HasMaxLength(512) + .HasColumnType("TEXT"); + + b.Property("UserId") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("UserId") + .IsUnique(); + + b.ToTable("ImageInfos"); + }); + + modelBuilder.Entity("Jellyfin.Data.Entities.ItemDisplayPreferences", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Client") + .IsRequired() + .HasMaxLength(32) + .HasColumnType("TEXT"); + + b.Property("IndexBy") + .HasColumnType("INTEGER"); + + b.Property("ItemId") + .HasColumnType("TEXT"); + + b.Property("RememberIndexing") + .HasColumnType("INTEGER"); + + b.Property("RememberSorting") + .HasColumnType("INTEGER"); + + b.Property("SortBy") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("TEXT"); + + b.Property("SortOrder") + .HasColumnType("INTEGER"); + + b.Property("UserId") + .HasColumnType("TEXT"); + + b.Property("ViewType") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("ItemDisplayPreferences"); + }); + + modelBuilder.Entity("Jellyfin.Data.Entities.Permission", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Kind") + .HasColumnType("INTEGER"); + + b.Property("Permission_Permissions_Guid") + .HasColumnType("TEXT"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("INTEGER"); + + b.Property("Value") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("Permission_Permissions_Guid"); + + b.ToTable("Permissions"); + }); + + modelBuilder.Entity("Jellyfin.Data.Entities.Preference", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Kind") + .HasColumnType("INTEGER"); + + b.Property("Preference_Preferences_Guid") + .HasColumnType("TEXT"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("INTEGER"); + + b.Property("Value") + .IsRequired() + .HasMaxLength(65535) + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("Preference_Preferences_Guid"); + + b.ToTable("Preferences"); + }); + + modelBuilder.Entity("Jellyfin.Data.Entities.User", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("AudioLanguagePreference") + .HasMaxLength(255) + .HasColumnType("TEXT"); + + b.Property("AuthenticationProviderId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("TEXT"); + + b.Property("DisplayCollectionsView") + .HasColumnType("INTEGER"); + + b.Property("DisplayMissingEpisodes") + .HasColumnType("INTEGER"); + + b.Property("EasyPassword") + .HasMaxLength(65535) + .HasColumnType("TEXT"); + + b.Property("EnableAutoLogin") + .HasColumnType("INTEGER"); + + b.Property("EnableLocalPassword") + .HasColumnType("INTEGER"); + + b.Property("EnableNextEpisodeAutoPlay") + .HasColumnType("INTEGER"); + + b.Property("EnableUserPreferenceAccess") + .HasColumnType("INTEGER"); + + b.Property("HidePlayedInLatest") + .HasColumnType("INTEGER"); + + b.Property("InternalId") + .HasColumnType("INTEGER"); + + b.Property("InvalidLoginAttemptCount") + .HasColumnType("INTEGER"); + + b.Property("LastActivityDate") + .HasColumnType("TEXT"); + + b.Property("LastLoginDate") + .HasColumnType("TEXT"); + + b.Property("LoginAttemptsBeforeLockout") + .HasColumnType("INTEGER"); + + b.Property("MaxActiveSessions") + .HasColumnType("INTEGER"); + + b.Property("MaxParentalAgeRating") + .HasColumnType("INTEGER"); + + b.Property("MustUpdatePassword") + .HasColumnType("INTEGER"); + + b.Property("Password") + .HasMaxLength(65535) + .HasColumnType("TEXT"); + + b.Property("PasswordResetProviderId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("TEXT"); + + b.Property("PlayDefaultAudioTrack") + .HasColumnType("INTEGER"); + + b.Property("RememberAudioSelections") + .HasColumnType("INTEGER"); + + b.Property("RememberSubtitleSelections") + .HasColumnType("INTEGER"); + + b.Property("RemoteClientBitrateLimit") + .HasColumnType("INTEGER"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("INTEGER"); + + b.Property("SubtitleLanguagePreference") + .HasMaxLength(255) + .HasColumnType("TEXT"); + + b.Property("SubtitleMode") + .HasColumnType("INTEGER"); + + b.Property("SyncPlayAccess") + .HasColumnType("INTEGER"); + + b.Property("Username") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("Users"); + }); + + modelBuilder.Entity("Jellyfin.Data.Entities.AccessSchedule", b => + { + b.HasOne("Jellyfin.Data.Entities.User", null) + .WithMany("AccessSchedules") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Jellyfin.Data.Entities.DisplayPreferences", b => + { + b.HasOne("Jellyfin.Data.Entities.User", null) + .WithOne("DisplayPreferences") + .HasForeignKey("Jellyfin.Data.Entities.DisplayPreferences", "UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Jellyfin.Data.Entities.HomeSection", b => + { + b.HasOne("Jellyfin.Data.Entities.DisplayPreferences", null) + .WithMany("HomeSections") + .HasForeignKey("DisplayPreferencesId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Jellyfin.Data.Entities.ImageInfo", b => + { + b.HasOne("Jellyfin.Data.Entities.User", null) + .WithOne("ProfileImage") + .HasForeignKey("Jellyfin.Data.Entities.ImageInfo", "UserId"); + }); + + modelBuilder.Entity("Jellyfin.Data.Entities.ItemDisplayPreferences", b => + { + b.HasOne("Jellyfin.Data.Entities.User", null) + .WithMany("ItemDisplayPreferences") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Jellyfin.Data.Entities.Permission", b => + { + b.HasOne("Jellyfin.Data.Entities.User", null) + .WithMany("Permissions") + .HasForeignKey("Permission_Permissions_Guid"); + }); + + modelBuilder.Entity("Jellyfin.Data.Entities.Preference", b => + { + b.HasOne("Jellyfin.Data.Entities.User", null) + .WithMany("Preferences") + .HasForeignKey("Preference_Preferences_Guid"); + }); + + modelBuilder.Entity("Jellyfin.Data.Entities.DisplayPreferences", b => + { + b.Navigation("HomeSections"); + }); + + modelBuilder.Entity("Jellyfin.Data.Entities.User", b => + { + b.Navigation("AccessSchedules"); + + b.Navigation("DisplayPreferences") + .IsRequired(); + + b.Navigation("ItemDisplayPreferences"); + + b.Navigation("Permissions"); + + b.Navigation("Preferences"); + + b.Navigation("ProfileImage"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Jellyfin.Server.Implementations/Migrations/20201203203707_AddCustomDisplayPreferences.cs b/Jellyfin.Server.Implementations/Migrations/20201203203707_AddCustomDisplayPreferences.cs new file mode 100644 index 000000000..a07b26592 --- /dev/null +++ b/Jellyfin.Server.Implementations/Migrations/20201203203707_AddCustomDisplayPreferences.cs @@ -0,0 +1,70 @@ +#pragma warning disable CS1591 +// +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +namespace Jellyfin.Server.Implementations.Migrations +{ + public partial class AddCustomDisplayPreferences : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AlterColumn( + name: "MaxActiveSessions", + schema: "jellyfin", + table: "Users", + type: "INTEGER", + nullable: false, + defaultValue: 0, + oldClrType: typeof(int), + oldType: "INTEGER", + oldNullable: true); + + migrationBuilder.CreateTable( + name: "CustomItemDisplayPreferences", + schema: "jellyfin", + columns: table => new + { + Id = table.Column(type: "INTEGER", nullable: false) + .Annotation("Sqlite:Autoincrement", true), + UserId = table.Column(type: "TEXT", nullable: false), + Client = table.Column(type: "TEXT", maxLength: 32, nullable: false), + Key = table.Column(type: "TEXT", nullable: false), + Value = table.Column(type: "TEXT", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_CustomItemDisplayPreferences", x => x.Id); + }); + + migrationBuilder.CreateIndex( + name: "IX_CustomItemDisplayPreferences_UserId", + schema: "jellyfin", + table: "CustomItemDisplayPreferences", + column: "UserId"); + + migrationBuilder.CreateIndex( + name: "IX_CustomItemDisplayPreferences_UserId_Client_Key", + schema: "jellyfin", + table: "CustomItemDisplayPreferences", + columns: new[] { "UserId", "Client", "Key" }, + unique: true); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "CustomItemDisplayPreferences", + schema: "jellyfin"); + + migrationBuilder.AlterColumn( + name: "MaxActiveSessions", + schema: "jellyfin", + table: "Users", + type: "INTEGER", + nullable: true, + oldClrType: typeof(int), + oldType: "INTEGER"); + } + } +} diff --git a/Jellyfin.Server.Implementations/Migrations/JellyfinDbModelSnapshot.cs b/Jellyfin.Server.Implementations/Migrations/JellyfinDbModelSnapshot.cs index 16d62f482..3b85c911c 100644 --- a/Jellyfin.Server.Implementations/Migrations/JellyfinDbModelSnapshot.cs +++ b/Jellyfin.Server.Implementations/Migrations/JellyfinDbModelSnapshot.cs @@ -15,7 +15,7 @@ namespace Jellyfin.Server.Implementations.Migrations #pragma warning disable 612, 618 modelBuilder .HasDefaultSchema("jellyfin") - .HasAnnotation("ProductVersion", "3.1.8"); + .HasAnnotation("ProductVersion", "5.0.0"); modelBuilder.Entity("Jellyfin.Data.Entities.AccessSchedule", b => { @@ -52,33 +52,33 @@ namespace Jellyfin.Server.Implementations.Migrations .HasColumnType("TEXT"); b.Property("ItemId") - .HasColumnType("TEXT") - .HasMaxLength(256); + .HasMaxLength(256) + .HasColumnType("TEXT"); b.Property("LogSeverity") .HasColumnType("INTEGER"); b.Property("Name") .IsRequired() - .HasColumnType("TEXT") - .HasMaxLength(512); + .HasMaxLength(512) + .HasColumnType("TEXT"); b.Property("Overview") - .HasColumnType("TEXT") - .HasMaxLength(512); + .HasMaxLength(512) + .HasColumnType("TEXT"); b.Property("RowVersion") .IsConcurrencyToken() .HasColumnType("INTEGER"); b.Property("ShortOverview") - .HasColumnType("TEXT") - .HasMaxLength(512); + .HasMaxLength(512) + .HasColumnType("TEXT"); b.Property("Type") .IsRequired() - .HasColumnType("TEXT") - .HasMaxLength(256); + .HasMaxLength(256) + .HasColumnType("TEXT"); b.Property("UserId") .HasColumnType("TEXT"); @@ -88,6 +88,38 @@ namespace Jellyfin.Server.Implementations.Migrations b.ToTable("ActivityLogs"); }); + modelBuilder.Entity("Jellyfin.Data.Entities.CustomItemDisplayPreferences", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Client") + .IsRequired() + .HasMaxLength(32) + .HasColumnType("TEXT"); + + b.Property("Key") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("UserId") + .HasColumnType("TEXT"); + + b.Property("Value") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.HasIndex("UserId", "Client", "Key") + .IsUnique(); + + b.ToTable("CustomItemDisplayPreferences"); + }); + modelBuilder.Entity("Jellyfin.Data.Entities.DisplayPreferences", b => { b.Property("Id") @@ -99,12 +131,12 @@ namespace Jellyfin.Server.Implementations.Migrations b.Property("Client") .IsRequired() - .HasColumnType("TEXT") - .HasMaxLength(32); + .HasMaxLength(32) + .HasColumnType("TEXT"); b.Property("DashboardTheme") - .HasColumnType("TEXT") - .HasMaxLength(32); + .HasMaxLength(32) + .HasColumnType("TEXT"); b.Property("EnableNextVideoInfoOverlay") .HasColumnType("INTEGER"); @@ -128,8 +160,8 @@ namespace Jellyfin.Server.Implementations.Migrations .HasColumnType("INTEGER"); b.Property("TvHome") - .HasColumnType("TEXT") - .HasMaxLength(32); + .HasMaxLength(32) + .HasColumnType("TEXT"); b.Property("UserId") .HasColumnType("TEXT"); @@ -177,8 +209,8 @@ namespace Jellyfin.Server.Implementations.Migrations b.Property("Path") .IsRequired() - .HasColumnType("TEXT") - .HasMaxLength(512); + .HasMaxLength(512) + .HasColumnType("TEXT"); b.Property("UserId") .HasColumnType("TEXT"); @@ -199,8 +231,8 @@ namespace Jellyfin.Server.Implementations.Migrations b.Property("Client") .IsRequired() - .HasColumnType("TEXT") - .HasMaxLength(32); + .HasMaxLength(32) + .HasColumnType("TEXT"); b.Property("IndexBy") .HasColumnType("INTEGER"); @@ -216,8 +248,8 @@ namespace Jellyfin.Server.Implementations.Migrations b.Property("SortBy") .IsRequired() - .HasColumnType("TEXT") - .HasMaxLength(64); + .HasMaxLength(64) + .HasColumnType("TEXT"); b.Property("SortOrder") .HasColumnType("INTEGER"); @@ -279,8 +311,8 @@ namespace Jellyfin.Server.Implementations.Migrations b.Property("Value") .IsRequired() - .HasColumnType("TEXT") - .HasMaxLength(65535); + .HasMaxLength(65535) + .HasColumnType("TEXT"); b.HasKey("Id"); @@ -296,13 +328,13 @@ namespace Jellyfin.Server.Implementations.Migrations .HasColumnType("TEXT"); b.Property("AudioLanguagePreference") - .HasColumnType("TEXT") - .HasMaxLength(255); + .HasMaxLength(255) + .HasColumnType("TEXT"); b.Property("AuthenticationProviderId") .IsRequired() - .HasColumnType("TEXT") - .HasMaxLength(255); + .HasMaxLength(255) + .HasColumnType("TEXT"); b.Property("DisplayCollectionsView") .HasColumnType("INTEGER"); @@ -311,8 +343,8 @@ namespace Jellyfin.Server.Implementations.Migrations .HasColumnType("INTEGER"); b.Property("EasyPassword") - .HasColumnType("TEXT") - .HasMaxLength(65535); + .HasMaxLength(65535) + .HasColumnType("TEXT"); b.Property("EnableAutoLogin") .HasColumnType("INTEGER"); @@ -354,13 +386,13 @@ namespace Jellyfin.Server.Implementations.Migrations .HasColumnType("INTEGER"); b.Property("Password") - .HasColumnType("TEXT") - .HasMaxLength(65535); + .HasMaxLength(65535) + .HasColumnType("TEXT"); b.Property("PasswordResetProviderId") .IsRequired() - .HasColumnType("TEXT") - .HasMaxLength(255); + .HasMaxLength(255) + .HasColumnType("TEXT"); b.Property("PlayDefaultAudioTrack") .HasColumnType("INTEGER"); @@ -379,8 +411,8 @@ namespace Jellyfin.Server.Implementations.Migrations .HasColumnType("INTEGER"); b.Property("SubtitleLanguagePreference") - .HasColumnType("TEXT") - .HasMaxLength(255); + .HasMaxLength(255) + .HasColumnType("TEXT"); b.Property("SubtitleMode") .HasColumnType("INTEGER"); @@ -390,8 +422,8 @@ namespace Jellyfin.Server.Implementations.Migrations b.Property("Username") .IsRequired() - .HasColumnType("TEXT") - .HasMaxLength(255); + .HasMaxLength(255) + .HasColumnType("TEXT"); b.HasKey("Id"); @@ -454,6 +486,27 @@ namespace Jellyfin.Server.Implementations.Migrations .WithMany("Preferences") .HasForeignKey("Preference_Preferences_Guid"); }); + + modelBuilder.Entity("Jellyfin.Data.Entities.DisplayPreferences", b => + { + b.Navigation("HomeSections"); + }); + + modelBuilder.Entity("Jellyfin.Data.Entities.User", b => + { + b.Navigation("AccessSchedules"); + + b.Navigation("DisplayPreferences") + .IsRequired(); + + b.Navigation("ItemDisplayPreferences"); + + b.Navigation("Permissions"); + + b.Navigation("Preferences"); + + b.Navigation("ProfileImage"); + }); #pragma warning restore 612, 618 } } diff --git a/Jellyfin.Server.Implementations/Users/DisplayPreferencesManager.cs b/Jellyfin.Server.Implementations/Users/DisplayPreferencesManager.cs index 76f943385..1f3b4ff17 100644 --- a/Jellyfin.Server.Implementations/Users/DisplayPreferencesManager.cs +++ b/Jellyfin.Server.Implementations/Users/DisplayPreferencesManager.cs @@ -66,6 +66,30 @@ namespace Jellyfin.Server.Implementations.Users .ToList(); } + /// + public IDictionary ListCustomItemDisplayPreferences(Guid userId, string client) + { + return _dbContext.CustomItemDisplayPreferences + .AsQueryable() + .Where(prefs => prefs.UserId == userId && string.Equals(prefs.Client, client)) + .ToDictionary(prefs => prefs.Key, prefs => prefs.Value); + } + + /// + public void SetCustomItemDisplayPreferences(Guid userId, string client, Dictionary customPreferences) + { + var existingPrefs = _dbContext.CustomItemDisplayPreferences + .AsQueryable() + .Where(prefs => prefs.UserId == userId && string.Equals(prefs.Client, client)); + _dbContext.CustomItemDisplayPreferences.RemoveRange(existingPrefs); + + foreach (var (key, value) in customPreferences) + { + _dbContext.CustomItemDisplayPreferences + .Add(new CustomItemDisplayPreferences(userId, client, key, value)); + } + } + /// public void SaveChanges() { -- cgit v1.2.3