diff options
| author | Joshua M. Boniface <joshua@boniface.me> | 2020-12-04 20:05:04 -0500 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2020-12-04 20:05:04 -0500 |
| commit | 4e6584c345a45c3ac46ac3aa95d39472eb6fbcbf (patch) | |
| tree | b40c4f5fc23e6a818ed8c48ac4088dff77cddb35 /Jellyfin.Server.Implementations | |
| parent | 3980b5ce9f6a6cbd3c0eed5316f9f0758e0fa542 (diff) | |
| parent | 76250a88959fa54646b45bbbb412dfeda30d556d (diff) | |
Merge pull request #4652 from crobibero/display-preferences
Add support for custom item display preferences
Diffstat (limited to 'Jellyfin.Server.Implementations')
5 files changed, 771 insertions, 44 deletions
diff --git a/Jellyfin.Server.Implementations/JellyfinDb.cs b/Jellyfin.Server.Implementations/JellyfinDb.cs index bf8818f8d..7f3f83749 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> ItemDisplayPreferences { get; set; } + public virtual DbSet<CustomItemDisplayPreferences> CustomItemDisplayPreferences { get; set; } + public virtual DbSet<Permission> Permissions { get; set; } public virtual DbSet<Preference> Preferences { get; set; } @@ -151,7 +153,15 @@ namespace Jellyfin.Server.Implementations .IsUnique(false); modelBuilder.Entity<DisplayPreferences>() - .HasIndex(entity => new { entity.UserId, entity.Client }) + .HasIndex(entity => new { entity.UserId, entity.ItemId, entity.Client }) + .IsUnique(); + + modelBuilder.Entity<CustomItemDisplayPreferences>() + .HasIndex(entity => entity.UserId) + .IsUnique(false); + + modelBuilder.Entity<CustomItemDisplayPreferences>() + .HasIndex(entity => new { entity.UserId, entity.ItemId, entity.Client, entity.Key }) .IsUnique(); } } diff --git a/Jellyfin.Server.Implementations/Migrations/20201204223655_AddCustomDisplayPreferences.Designer.cs b/Jellyfin.Server.Implementations/Migrations/20201204223655_AddCustomDisplayPreferences.Designer.cs new file mode 100644 index 000000000..10663d065 --- /dev/null +++ b/Jellyfin.Server.Implementations/Migrations/20201204223655_AddCustomDisplayPreferences.Designer.cs @@ -0,0 +1,522 @@ +#pragma warning disable CS1591 +// <auto-generated /> +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("20201204223655_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<int>("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property<int>("DayOfWeek") + .HasColumnType("INTEGER"); + + b.Property<double>("EndHour") + .HasColumnType("REAL"); + + b.Property<double>("StartHour") + .HasColumnType("REAL"); + + b.Property<Guid>("UserId") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("AccessSchedules"); + }); + + modelBuilder.Entity("Jellyfin.Data.Entities.ActivityLog", b => + { + b.Property<int>("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property<DateTime>("DateCreated") + .HasColumnType("TEXT"); + + b.Property<string>("ItemId") + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.Property<int>("LogSeverity") + .HasColumnType("INTEGER"); + + b.Property<string>("Name") + .IsRequired() + .HasMaxLength(512) + .HasColumnType("TEXT"); + + b.Property<string>("Overview") + .HasMaxLength(512) + .HasColumnType("TEXT"); + + b.Property<uint>("RowVersion") + .IsConcurrencyToken() + .HasColumnType("INTEGER"); + + b.Property<string>("ShortOverview") + .HasMaxLength(512) + .HasColumnType("TEXT"); + + b.Property<string>("Type") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.Property<Guid>("UserId") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("ActivityLogs"); + }); + + modelBuilder.Entity("Jellyfin.Data.Entities.CustomItemDisplayPreferences", b => + { + b.Property<int>("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property<string>("Client") + .IsRequired() + .HasMaxLength(32) + .HasColumnType("TEXT"); + + b.Property<Guid>("ItemId") + .HasColumnType("TEXT"); + + b.Property<string>("Key") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property<Guid>("UserId") + .HasColumnType("TEXT"); + + b.Property<string>("Value") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.HasIndex("UserId", "ItemId", "Client", "Key") + .IsUnique(); + + b.ToTable("CustomItemDisplayPreferences"); + }); + + modelBuilder.Entity("Jellyfin.Data.Entities.DisplayPreferences", b => + { + b.Property<int>("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property<int>("ChromecastVersion") + .HasColumnType("INTEGER"); + + b.Property<string>("Client") + .IsRequired() + .HasMaxLength(32) + .HasColumnType("TEXT"); + + b.Property<string>("DashboardTheme") + .HasMaxLength(32) + .HasColumnType("TEXT"); + + b.Property<bool>("EnableNextVideoInfoOverlay") + .HasColumnType("INTEGER"); + + b.Property<int?>("IndexBy") + .HasColumnType("INTEGER"); + + b.Property<Guid>("ItemId") + .HasColumnType("TEXT"); + + b.Property<int>("ScrollDirection") + .HasColumnType("INTEGER"); + + b.Property<bool>("ShowBackdrop") + .HasColumnType("INTEGER"); + + b.Property<bool>("ShowSidebar") + .HasColumnType("INTEGER"); + + b.Property<int>("SkipBackwardLength") + .HasColumnType("INTEGER"); + + b.Property<int>("SkipForwardLength") + .HasColumnType("INTEGER"); + + b.Property<string>("TvHome") + .HasMaxLength(32) + .HasColumnType("TEXT"); + + b.Property<Guid>("UserId") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.HasIndex("UserId", "ItemId", "Client") + .IsUnique(); + + b.ToTable("DisplayPreferences"); + }); + + modelBuilder.Entity("Jellyfin.Data.Entities.HomeSection", b => + { + b.Property<int>("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property<int>("DisplayPreferencesId") + .HasColumnType("INTEGER"); + + b.Property<int>("Order") + .HasColumnType("INTEGER"); + + b.Property<int>("Type") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("DisplayPreferencesId"); + + b.ToTable("HomeSection"); + }); + + modelBuilder.Entity("Jellyfin.Data.Entities.ImageInfo", b => + { + b.Property<int>("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property<DateTime>("LastModified") + .HasColumnType("TEXT"); + + b.Property<string>("Path") + .IsRequired() + .HasMaxLength(512) + .HasColumnType("TEXT"); + + b.Property<Guid?>("UserId") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("UserId") + .IsUnique(); + + b.ToTable("ImageInfos"); + }); + + modelBuilder.Entity("Jellyfin.Data.Entities.ItemDisplayPreferences", b => + { + b.Property<int>("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property<string>("Client") + .IsRequired() + .HasMaxLength(32) + .HasColumnType("TEXT"); + + b.Property<int?>("IndexBy") + .HasColumnType("INTEGER"); + + b.Property<Guid>("ItemId") + .HasColumnType("TEXT"); + + b.Property<bool>("RememberIndexing") + .HasColumnType("INTEGER"); + + b.Property<bool>("RememberSorting") + .HasColumnType("INTEGER"); + + b.Property<string>("SortBy") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("TEXT"); + + b.Property<int>("SortOrder") + .HasColumnType("INTEGER"); + + b.Property<Guid>("UserId") + .HasColumnType("TEXT"); + + b.Property<int>("ViewType") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("ItemDisplayPreferences"); + }); + + modelBuilder.Entity("Jellyfin.Data.Entities.Permission", b => + { + b.Property<int>("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property<int>("Kind") + .HasColumnType("INTEGER"); + + b.Property<Guid?>("Permission_Permissions_Guid") + .HasColumnType("TEXT"); + + b.Property<uint>("RowVersion") + .IsConcurrencyToken() + .HasColumnType("INTEGER"); + + b.Property<bool>("Value") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("Permission_Permissions_Guid"); + + b.ToTable("Permissions"); + }); + + modelBuilder.Entity("Jellyfin.Data.Entities.Preference", b => + { + b.Property<int>("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property<int>("Kind") + .HasColumnType("INTEGER"); + + b.Property<Guid?>("Preference_Preferences_Guid") + .HasColumnType("TEXT"); + + b.Property<uint>("RowVersion") + .IsConcurrencyToken() + .HasColumnType("INTEGER"); + + b.Property<string>("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<Guid>("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property<string>("AudioLanguagePreference") + .HasMaxLength(255) + .HasColumnType("TEXT"); + + b.Property<string>("AuthenticationProviderId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("TEXT"); + + b.Property<bool>("DisplayCollectionsView") + .HasColumnType("INTEGER"); + + b.Property<bool>("DisplayMissingEpisodes") + .HasColumnType("INTEGER"); + + b.Property<string>("EasyPassword") + .HasMaxLength(65535) + .HasColumnType("TEXT"); + + b.Property<bool>("EnableAutoLogin") + .HasColumnType("INTEGER"); + + b.Property<bool>("EnableLocalPassword") + .HasColumnType("INTEGER"); + + b.Property<bool>("EnableNextEpisodeAutoPlay") + .HasColumnType("INTEGER"); + + b.Property<bool>("EnableUserPreferenceAccess") + .HasColumnType("INTEGER"); + + b.Property<bool>("HidePlayedInLatest") + .HasColumnType("INTEGER"); + + b.Property<long>("InternalId") + .HasColumnType("INTEGER"); + + b.Property<int>("InvalidLoginAttemptCount") + .HasColumnType("INTEGER"); + + b.Property<DateTime?>("LastActivityDate") + .HasColumnType("TEXT"); + + b.Property<DateTime?>("LastLoginDate") + .HasColumnType("TEXT"); + + b.Property<int?>("LoginAttemptsBeforeLockout") + .HasColumnType("INTEGER"); + + b.Property<int>("MaxActiveSessions") + .HasColumnType("INTEGER"); + + b.Property<int?>("MaxParentalAgeRating") + .HasColumnType("INTEGER"); + + b.Property<bool>("MustUpdatePassword") + .HasColumnType("INTEGER"); + + b.Property<string>("Password") + .HasMaxLength(65535) + .HasColumnType("TEXT"); + + b.Property<string>("PasswordResetProviderId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("TEXT"); + + b.Property<bool>("PlayDefaultAudioTrack") + .HasColumnType("INTEGER"); + + b.Property<bool>("RememberAudioSelections") + .HasColumnType("INTEGER"); + + b.Property<bool>("RememberSubtitleSelections") + .HasColumnType("INTEGER"); + + b.Property<int?>("RemoteClientBitrateLimit") + .HasColumnType("INTEGER"); + + b.Property<uint>("RowVersion") + .IsConcurrencyToken() + .HasColumnType("INTEGER"); + + b.Property<string>("SubtitleLanguagePreference") + .HasMaxLength(255) + .HasColumnType("TEXT"); + + b.Property<int>("SubtitleMode") + .HasColumnType("INTEGER"); + + b.Property<int>("SyncPlayAccess") + .HasColumnType("INTEGER"); + + b.Property<string>("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/20201204223655_AddCustomDisplayPreferences.cs b/Jellyfin.Server.Implementations/Migrations/20201204223655_AddCustomDisplayPreferences.cs new file mode 100644 index 000000000..fbc0bffa9 --- /dev/null +++ b/Jellyfin.Server.Implementations/Migrations/20201204223655_AddCustomDisplayPreferences.cs @@ -0,0 +1,108 @@ +#pragma warning disable CS1591 +// <auto-generated /> +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +namespace Jellyfin.Server.Implementations.Migrations +{ + public partial class AddCustomDisplayPreferences : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropIndex( + name: "IX_DisplayPreferences_UserId_Client", + schema: "jellyfin", + table: "DisplayPreferences"); + + migrationBuilder.AlterColumn<int>( + name: "MaxActiveSessions", + schema: "jellyfin", + table: "Users", + type: "INTEGER", + nullable: false, + defaultValue: 0, + oldClrType: typeof(int), + oldType: "INTEGER", + oldNullable: true); + + migrationBuilder.AddColumn<Guid>( + name: "ItemId", + schema: "jellyfin", + table: "DisplayPreferences", + type: "TEXT", + nullable: false, + defaultValue: new Guid("00000000-0000-0000-0000-000000000000")); + + migrationBuilder.CreateTable( + name: "CustomItemDisplayPreferences", + schema: "jellyfin", + columns: table => new + { + Id = table.Column<int>(type: "INTEGER", nullable: false) + .Annotation("Sqlite:Autoincrement", true), + UserId = table.Column<Guid>(type: "TEXT", nullable: false), + ItemId = table.Column<Guid>(type: "TEXT", nullable: false), + Client = table.Column<string>(type: "TEXT", maxLength: 32, nullable: false), + Key = table.Column<string>(type: "TEXT", nullable: false), + Value = table.Column<string>(type: "TEXT", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_CustomItemDisplayPreferences", x => x.Id); + }); + + migrationBuilder.CreateIndex( + name: "IX_DisplayPreferences_UserId_ItemId_Client", + schema: "jellyfin", + table: "DisplayPreferences", + columns: new[] { "UserId", "ItemId", "Client" }, + unique: true); + + migrationBuilder.CreateIndex( + name: "IX_CustomItemDisplayPreferences_UserId", + schema: "jellyfin", + table: "CustomItemDisplayPreferences", + column: "UserId"); + + migrationBuilder.CreateIndex( + name: "IX_CustomItemDisplayPreferences_UserId_ItemId_Client_Key", + schema: "jellyfin", + table: "CustomItemDisplayPreferences", + columns: new[] { "UserId", "ItemId", "Client", "Key" }, + unique: true); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "CustomItemDisplayPreferences", + schema: "jellyfin"); + + migrationBuilder.DropIndex( + name: "IX_DisplayPreferences_UserId_ItemId_Client", + schema: "jellyfin", + table: "DisplayPreferences"); + + migrationBuilder.DropColumn( + name: "ItemId", + schema: "jellyfin", + table: "DisplayPreferences"); + + migrationBuilder.AlterColumn<int>( + name: "MaxActiveSessions", + schema: "jellyfin", + table: "Users", + type: "INTEGER", + nullable: true, + oldClrType: typeof(int), + oldType: "INTEGER"); + + migrationBuilder.CreateIndex( + name: "IX_DisplayPreferences_UserId_Client", + schema: "jellyfin", + table: "DisplayPreferences", + columns: new[] { "UserId", "Client" }, + unique: true); + } + } +} diff --git a/Jellyfin.Server.Implementations/Migrations/JellyfinDbModelSnapshot.cs b/Jellyfin.Server.Implementations/Migrations/JellyfinDbModelSnapshot.cs index 16d62f482..1614a88ef 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<string>("ItemId") - .HasColumnType("TEXT") - .HasMaxLength(256); + .HasMaxLength(256) + .HasColumnType("TEXT"); b.Property<int>("LogSeverity") .HasColumnType("INTEGER"); b.Property<string>("Name") .IsRequired() - .HasColumnType("TEXT") - .HasMaxLength(512); + .HasMaxLength(512) + .HasColumnType("TEXT"); b.Property<string>("Overview") - .HasColumnType("TEXT") - .HasMaxLength(512); + .HasMaxLength(512) + .HasColumnType("TEXT"); b.Property<uint>("RowVersion") .IsConcurrencyToken() .HasColumnType("INTEGER"); b.Property<string>("ShortOverview") - .HasColumnType("TEXT") - .HasMaxLength(512); + .HasMaxLength(512) + .HasColumnType("TEXT"); b.Property<string>("Type") .IsRequired() - .HasColumnType("TEXT") - .HasMaxLength(256); + .HasMaxLength(256) + .HasColumnType("TEXT"); b.Property<Guid>("UserId") .HasColumnType("TEXT"); @@ -88,6 +88,41 @@ namespace Jellyfin.Server.Implementations.Migrations b.ToTable("ActivityLogs"); }); + modelBuilder.Entity("Jellyfin.Data.Entities.CustomItemDisplayPreferences", b => + { + b.Property<int>("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property<string>("Client") + .IsRequired() + .HasMaxLength(32) + .HasColumnType("TEXT"); + + b.Property<Guid>("ItemId") + .HasColumnType("TEXT"); + + b.Property<string>("Key") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property<Guid>("UserId") + .HasColumnType("TEXT"); + + b.Property<string>("Value") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.HasIndex("UserId", "ItemId", "Client", "Key") + .IsUnique(); + + b.ToTable("CustomItemDisplayPreferences"); + }); + modelBuilder.Entity("Jellyfin.Data.Entities.DisplayPreferences", b => { b.Property<int>("Id") @@ -99,12 +134,12 @@ namespace Jellyfin.Server.Implementations.Migrations b.Property<string>("Client") .IsRequired() - .HasColumnType("TEXT") - .HasMaxLength(32); + .HasMaxLength(32) + .HasColumnType("TEXT"); b.Property<string>("DashboardTheme") - .HasColumnType("TEXT") - .HasMaxLength(32); + .HasMaxLength(32) + .HasColumnType("TEXT"); b.Property<bool>("EnableNextVideoInfoOverlay") .HasColumnType("INTEGER"); @@ -112,6 +147,9 @@ namespace Jellyfin.Server.Implementations.Migrations b.Property<int?>("IndexBy") .HasColumnType("INTEGER"); + b.Property<Guid>("ItemId") + .HasColumnType("TEXT"); + b.Property<int>("ScrollDirection") .HasColumnType("INTEGER"); @@ -128,8 +166,8 @@ namespace Jellyfin.Server.Implementations.Migrations .HasColumnType("INTEGER"); b.Property<string>("TvHome") - .HasColumnType("TEXT") - .HasMaxLength(32); + .HasMaxLength(32) + .HasColumnType("TEXT"); b.Property<Guid>("UserId") .HasColumnType("TEXT"); @@ -138,7 +176,7 @@ namespace Jellyfin.Server.Implementations.Migrations b.HasIndex("UserId"); - b.HasIndex("UserId", "Client") + b.HasIndex("UserId", "ItemId", "Client") .IsUnique(); b.ToTable("DisplayPreferences"); @@ -177,8 +215,8 @@ namespace Jellyfin.Server.Implementations.Migrations b.Property<string>("Path") .IsRequired() - .HasColumnType("TEXT") - .HasMaxLength(512); + .HasMaxLength(512) + .HasColumnType("TEXT"); b.Property<Guid?>("UserId") .HasColumnType("TEXT"); @@ -199,8 +237,8 @@ namespace Jellyfin.Server.Implementations.Migrations b.Property<string>("Client") .IsRequired() - .HasColumnType("TEXT") - .HasMaxLength(32); + .HasMaxLength(32) + .HasColumnType("TEXT"); b.Property<int?>("IndexBy") .HasColumnType("INTEGER"); @@ -216,8 +254,8 @@ namespace Jellyfin.Server.Implementations.Migrations b.Property<string>("SortBy") .IsRequired() - .HasColumnType("TEXT") - .HasMaxLength(64); + .HasMaxLength(64) + .HasColumnType("TEXT"); b.Property<int>("SortOrder") .HasColumnType("INTEGER"); @@ -279,8 +317,8 @@ namespace Jellyfin.Server.Implementations.Migrations b.Property<string>("Value") .IsRequired() - .HasColumnType("TEXT") - .HasMaxLength(65535); + .HasMaxLength(65535) + .HasColumnType("TEXT"); b.HasKey("Id"); @@ -296,13 +334,13 @@ namespace Jellyfin.Server.Implementations.Migrations .HasColumnType("TEXT"); b.Property<string>("AudioLanguagePreference") - .HasColumnType("TEXT") - .HasMaxLength(255); + .HasMaxLength(255) + .HasColumnType("TEXT"); b.Property<string>("AuthenticationProviderId") .IsRequired() - .HasColumnType("TEXT") - .HasMaxLength(255); + .HasMaxLength(255) + .HasColumnType("TEXT"); b.Property<bool>("DisplayCollectionsView") .HasColumnType("INTEGER"); @@ -311,8 +349,8 @@ namespace Jellyfin.Server.Implementations.Migrations .HasColumnType("INTEGER"); b.Property<string>("EasyPassword") - .HasColumnType("TEXT") - .HasMaxLength(65535); + .HasMaxLength(65535) + .HasColumnType("TEXT"); b.Property<bool>("EnableAutoLogin") .HasColumnType("INTEGER"); @@ -354,13 +392,13 @@ namespace Jellyfin.Server.Implementations.Migrations .HasColumnType("INTEGER"); b.Property<string>("Password") - .HasColumnType("TEXT") - .HasMaxLength(65535); + .HasMaxLength(65535) + .HasColumnType("TEXT"); b.Property<string>("PasswordResetProviderId") .IsRequired() - .HasColumnType("TEXT") - .HasMaxLength(255); + .HasMaxLength(255) + .HasColumnType("TEXT"); b.Property<bool>("PlayDefaultAudioTrack") .HasColumnType("INTEGER"); @@ -379,8 +417,8 @@ namespace Jellyfin.Server.Implementations.Migrations .HasColumnType("INTEGER"); b.Property<string>("SubtitleLanguagePreference") - .HasColumnType("TEXT") - .HasMaxLength(255); + .HasMaxLength(255) + .HasColumnType("TEXT"); b.Property<int>("SubtitleMode") .HasColumnType("INTEGER"); @@ -390,8 +428,8 @@ namespace Jellyfin.Server.Implementations.Migrations b.Property<string>("Username") .IsRequired() - .HasColumnType("TEXT") - .HasMaxLength(255); + .HasMaxLength(255) + .HasColumnType("TEXT"); b.HasKey("Id"); @@ -454,6 +492,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..c8a589cab 100644 --- a/Jellyfin.Server.Implementations/Users/DisplayPreferencesManager.cs +++ b/Jellyfin.Server.Implementations/Users/DisplayPreferencesManager.cs @@ -26,16 +26,16 @@ namespace Jellyfin.Server.Implementations.Users } /// <inheritdoc /> - public DisplayPreferences GetDisplayPreferences(Guid userId, string client) + public DisplayPreferences GetDisplayPreferences(Guid userId, Guid itemId, string client) { var prefs = _dbContext.DisplayPreferences .Include(pref => pref.HomeSections) .FirstOrDefault(pref => - pref.UserId == userId && string.Equals(pref.Client, client)); + pref.UserId == userId && string.Equals(pref.Client, client) && pref.ItemId == itemId); if (prefs == null) { - prefs = new DisplayPreferences(userId, client); + prefs = new DisplayPreferences(userId, itemId, client); _dbContext.DisplayPreferences.Add(prefs); } @@ -67,6 +67,34 @@ namespace Jellyfin.Server.Implementations.Users } /// <inheritdoc /> + public IDictionary<string, string> ListCustomItemDisplayPreferences(Guid userId, Guid itemId, string client) + { + return _dbContext.CustomItemDisplayPreferences + .AsQueryable() + .Where(prefs => prefs.UserId == userId + && prefs.ItemId == itemId + && string.Equals(prefs.Client, client)) + .ToDictionary(prefs => prefs.Key, prefs => prefs.Value); + } + + /// <inheritdoc /> + public void SetCustomItemDisplayPreferences(Guid userId, Guid itemId, string client, Dictionary<string, string> customPreferences) + { + var existingPrefs = _dbContext.CustomItemDisplayPreferences + .AsQueryable() + .Where(prefs => prefs.UserId == userId + && prefs.ItemId == itemId + && string.Equals(prefs.Client, client)); + _dbContext.CustomItemDisplayPreferences.RemoveRange(existingPrefs); + + foreach (var (key, value) in customPreferences) + { + _dbContext.CustomItemDisplayPreferences + .Add(new CustomItemDisplayPreferences(userId, itemId, client, key, value)); + } + } + + /// <inheritdoc /> public void SaveChanges() { _dbContext.SaveChanges(); |
