Migrate Display Preferences to EF Core

pull/3578/head
Patrick Barron 4 years ago
parent 175e7b45e5
commit ab396225ea

@ -11,6 +11,7 @@ using System.Xml;
using Emby.Dlna.Didl;
using Emby.Dlna.Service;
using Jellyfin.Data.Entities;
using Jellyfin.Data.Enums;
using MediaBrowser.Common.Extensions;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Drawing;

@ -554,8 +554,6 @@ namespace Emby.Server.Implementations
serviceCollection.AddSingleton<IUserDataRepository, SqliteUserDataRepository>();
serviceCollection.AddSingleton<IUserDataManager, UserDataManager>();
serviceCollection.AddSingleton<IDisplayPreferencesRepository, SqliteDisplayPreferencesRepository>();
serviceCollection.AddSingleton<IItemRepository, SqliteItemRepository>();
serviceCollection.AddSingleton<IAuthenticationRepository, AuthenticationRepository>();
@ -650,7 +648,6 @@ namespace Emby.Server.Implementations
_httpServer = Resolve<IHttpServer>();
_httpClient = Resolve<IHttpClient>();
((SqliteDisplayPreferencesRepository)Resolve<IDisplayPreferencesRepository>()).Initialize();
((AuthenticationRepository)Resolve<IAuthenticationRepository>()).Initialize();
SetStaticProperties();

@ -7,6 +7,7 @@ using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Jellyfin.Data.Entities;
using Jellyfin.Data.Enums;
using MediaBrowser.Common.Extensions;
using MediaBrowser.Common.Progress;
using MediaBrowser.Controller.Channels;

@ -1,225 +0,0 @@
#pragma warning disable CS1591
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Text.Json;
using System.Threading;
using MediaBrowser.Common.Configuration;
using MediaBrowser.Common.Extensions;
using MediaBrowser.Common.Json;
using MediaBrowser.Controller.Persistence;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.IO;
using Microsoft.Extensions.Logging;
using SQLitePCL.pretty;
namespace Emby.Server.Implementations.Data
{
/// <summary>
/// Class SQLiteDisplayPreferencesRepository.
/// </summary>
public class SqliteDisplayPreferencesRepository : BaseSqliteRepository, IDisplayPreferencesRepository
{
private readonly IFileSystem _fileSystem;
private readonly JsonSerializerOptions _jsonOptions;
public SqliteDisplayPreferencesRepository(ILogger<SqliteDisplayPreferencesRepository> logger, IApplicationPaths appPaths, IFileSystem fileSystem)
: base(logger)
{
_fileSystem = fileSystem;
_jsonOptions = JsonDefaults.GetOptions();
DbFilePath = Path.Combine(appPaths.DataPath, "displaypreferences.db");
}
/// <summary>
/// Gets the name of the repository.
/// </summary>
/// <value>The name.</value>
public string Name => "SQLite";
public void Initialize()
{
try
{
InitializeInternal();
}
catch (Exception ex)
{
Logger.LogError(ex, "Error loading database file. Will reset and retry.");
_fileSystem.DeleteFile(DbFilePath);
InitializeInternal();
}
}
/// <summary>
/// Opens the connection to the database.
/// </summary>
/// <returns>Task.</returns>
private void InitializeInternal()
{
string[] queries =
{
"create table if not exists userdisplaypreferences (id GUID NOT NULL, userId GUID NOT NULL, client text NOT NULL, data BLOB NOT NULL)",
"create unique index if not exists userdisplaypreferencesindex on userdisplaypreferences (id, userId, client)"
};
using (var connection = GetConnection())
{
connection.RunQueries(queries);
}
}
/// <summary>
/// Save the display preferences associated with an item in the repo.
/// </summary>
/// <param name="displayPreferences">The display preferences.</param>
/// <param name="userId">The user id.</param>
/// <param name="client">The client.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <exception cref="ArgumentNullException">item</exception>
public void SaveDisplayPreferences(DisplayPreferences displayPreferences, Guid userId, string client, CancellationToken cancellationToken)
{
if (displayPreferences == null)
{
throw new ArgumentNullException(nameof(displayPreferences));
}
if (string.IsNullOrEmpty(displayPreferences.Id))
{
throw new ArgumentException("Display preferences has an invalid Id", nameof(displayPreferences));
}
cancellationToken.ThrowIfCancellationRequested();
using (var connection = GetConnection())
{
connection.RunInTransaction(
db => SaveDisplayPreferences(displayPreferences, userId, client, db),
TransactionMode);
}
}
private void SaveDisplayPreferences(DisplayPreferences displayPreferences, Guid userId, string client, IDatabaseConnection connection)
{
var serialized = JsonSerializer.SerializeToUtf8Bytes(displayPreferences, _jsonOptions);
using (var statement = connection.PrepareStatement("replace into userdisplaypreferences (id, userid, client, data) values (@id, @userId, @client, @data)"))
{
statement.TryBind("@id", new Guid(displayPreferences.Id).ToByteArray());
statement.TryBind("@userId", userId.ToByteArray());
statement.TryBind("@client", client);
statement.TryBind("@data", serialized);
statement.MoveNext();
}
}
/// <summary>
/// Save all display preferences associated with a user in the repo.
/// </summary>
/// <param name="displayPreferences">The display preferences.</param>
/// <param name="userId">The user id.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <exception cref="ArgumentNullException">item</exception>
public void SaveAllDisplayPreferences(IEnumerable<DisplayPreferences> displayPreferences, Guid userId, CancellationToken cancellationToken)
{
if (displayPreferences == null)
{
throw new ArgumentNullException(nameof(displayPreferences));
}
cancellationToken.ThrowIfCancellationRequested();
using (var connection = GetConnection())
{
connection.RunInTransaction(
db =>
{
foreach (var displayPreference in displayPreferences)
{
SaveDisplayPreferences(displayPreference, userId, displayPreference.Client, db);
}
},
TransactionMode);
}
}
/// <summary>
/// Gets the display preferences.
/// </summary>
/// <param name="displayPreferencesId">The display preferences id.</param>
/// <param name="userId">The user id.</param>
/// <param name="client">The client.</param>
/// <returns>Task{DisplayPreferences}.</returns>
/// <exception cref="ArgumentNullException">item</exception>
public DisplayPreferences GetDisplayPreferences(string displayPreferencesId, Guid userId, string client)
{
if (string.IsNullOrEmpty(displayPreferencesId))
{
throw new ArgumentNullException(nameof(displayPreferencesId));
}
var guidId = displayPreferencesId.GetMD5();
using (var connection = GetConnection(true))
{
using (var statement = connection.PrepareStatement("select data from userdisplaypreferences where id = @id and userId=@userId and client=@client"))
{
statement.TryBind("@id", guidId.ToByteArray());
statement.TryBind("@userId", userId.ToByteArray());
statement.TryBind("@client", client);
foreach (var row in statement.ExecuteQuery())
{
return Get(row);
}
}
}
return new DisplayPreferences
{
Id = guidId.ToString("N", CultureInfo.InvariantCulture)
};
}
/// <summary>
/// Gets all display preferences for the given user.
/// </summary>
/// <param name="userId">The user id.</param>
/// <returns>Task{DisplayPreferences}.</returns>
/// <exception cref="ArgumentNullException">item</exception>
public IEnumerable<DisplayPreferences> GetAllDisplayPreferences(Guid userId)
{
var list = new List<DisplayPreferences>();
using (var connection = GetConnection(true))
using (var statement = connection.PrepareStatement("select data from userdisplaypreferences where userId=@userId"))
{
statement.TryBind("@userId", userId.ToByteArray());
foreach (var row in statement.ExecuteQuery())
{
list.Add(Get(row));
}
}
return list;
}
private DisplayPreferences Get(IReadOnlyList<IResultSetValue> row)
=> JsonSerializer.Deserialize<DisplayPreferences>(row[0].ToBlob(), _jsonOptions);
public void SaveDisplayPreferences(DisplayPreferences displayPreferences, string userId, string client, CancellationToken cancellationToken)
=> SaveDisplayPreferences(displayPreferences, new Guid(userId), client, cancellationToken);
public DisplayPreferences GetDisplayPreferences(string displayPreferencesId, string userId, string client)
=> GetDisplayPreferences(displayPreferencesId, new Guid(userId), client);
}
}

@ -9,6 +9,7 @@ using System.Text;
using System.Text.Json;
using System.Threading;
using Emby.Server.Implementations.Playlists;
using Jellyfin.Data.Enums;
using MediaBrowser.Common.Extensions;
using MediaBrowser.Common.Json;
using MediaBrowser.Controller;

@ -54,7 +54,7 @@
<TargetFramework>netstandard2.1</TargetFramework>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<TreatWarningsAsErrors Condition=" '$(Configuration)' == 'Release'" >true</TreatWarningsAsErrors>
<TreatWarningsAsErrors Condition=" '$(Configuration)' == 'Release'">true</TreatWarningsAsErrors>
</PropertyGroup>
<!-- Code Analyzers-->

@ -3,7 +3,7 @@
using System;
using System.Collections.Generic;
using System.IO;
using Emby.Server.Implementations.Images;
using Jellyfin.Data.Enums;
using MediaBrowser.Common.Configuration;
using MediaBrowser.Controller.Drawing;
using MediaBrowser.Controller.Dto;

@ -1,7 +1,7 @@
#pragma warning disable CS1591
using System.Collections.Generic;
using Emby.Server.Implementations.Images;
using Jellyfin.Data.Enums;
using MediaBrowser.Common.Configuration;
using MediaBrowser.Controller.Drawing;
using MediaBrowser.Controller.Dto;

@ -1,6 +1,7 @@
#pragma warning disable CS1591
using System.Collections.Generic;
using Jellyfin.Data.Enums;
using MediaBrowser.Common.Configuration;
using MediaBrowser.Controller.Drawing;
using MediaBrowser.Controller.Dto;

@ -50,7 +50,6 @@ using Microsoft.Extensions.Logging;
using Episode = MediaBrowser.Controller.Entities.TV.Episode;
using Genre = MediaBrowser.Controller.Entities.Genre;
using Person = MediaBrowser.Controller.Entities.Person;
using SortOrder = MediaBrowser.Model.Entities.SortOrder;
using VideoResolver = Emby.Naming.Video.VideoResolver;
namespace Emby.Server.Implementations.Library

@ -4,12 +4,12 @@ using System;
using System.Collections.Generic;
using System.Linq;
using Jellyfin.Data.Entities;
using Jellyfin.Data.Enums;
using MediaBrowser.Controller.Dto;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.Audio;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Playlists;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Querying;
using MusicAlbum = MediaBrowser.Controller.Entities.Audio.MusicAlbum;

@ -4,12 +4,12 @@ using System;
using System.Collections.Generic;
using System.Linq;
using Jellyfin.Data.Entities;
using Jellyfin.Data.Enums;
using MediaBrowser.Controller.Dto;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.Audio;
using MediaBrowser.Controller.Extensions;
using MediaBrowser.Controller.Library;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Querying;
using MediaBrowser.Model.Search;
using Microsoft.Extensions.Logging;

@ -12,6 +12,7 @@ using System.Threading;
using System.Threading.Tasks;
using System.Xml;
using Emby.Server.Implementations.Library;
using Jellyfin.Data.Enums;
using MediaBrowser.Common.Configuration;
using MediaBrowser.Common.Extensions;
using MediaBrowser.Common.Net;

@ -0,0 +1,72 @@
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using Jellyfin.Data.Enums;
namespace Jellyfin.Data.Entities
{
public class DisplayPreferences
{
public DisplayPreferences(string client, Guid userId)
{
RememberIndexing = false;
ShowBackdrop = true;
Client = client;
UserId = userId;
HomeSections = new HashSet<HomeSection>();
}
protected DisplayPreferences()
{
}
[Required]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; protected set; }
[Required]
public Guid UserId { get; set; }
/// <summary>
/// Gets or sets the id of the associated item.
/// </summary>
/// <remarks>
/// This is currently unused. In the future, this will allow us to have users set
/// display preferences per item.
/// </remarks>
public Guid? ItemId { get; set; }
[Required]
[MaxLength(64)]
[StringLength(64)]
public string Client { get; set; }
[Required]
public bool RememberIndexing { get; set; }
[Required]
public bool RememberSorting { get; set; }
[Required]
public SortOrder SortOrder { get; set; }
[Required]
public bool ShowSidebar { get; set; }
[Required]
public bool ShowBackdrop { get; set; }
public string SortBy { get; set; }
public ViewType? ViewType { get; set; }
[Required]
public ScrollDirection ScrollDirection { get; set; }
public IndexingKind? IndexBy { get; set; }
public virtual ICollection<HomeSection> HomeSections { get; protected set; }
}
}

@ -0,0 +1,21 @@
using System;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using Jellyfin.Data.Enums;
namespace Jellyfin.Data.Entities
{
public class HomeSection
{
[Key]
[Required]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; protected set; }
public int DisplayPreferencesId { get; set; }
public int Order { get; set; }
public HomeSectionType Type { get; set; }
}
}

@ -349,6 +349,11 @@ namespace Jellyfin.Data.Entities
/// </summary>
public virtual ICollection<AccessSchedule> AccessSchedules { get; protected set; }
/// <summary>
/// Gets or sets the list of item display preferences.
/// </summary>
public virtual ICollection<DisplayPreferences> DisplayPreferences { get; protected set; }
/*
/// <summary>
/// Gets or sets the list of groups this user is a member of.

@ -0,0 +1,53 @@
namespace Jellyfin.Data.Enums
{
/// <summary>
/// An enum representing the different options for the home screen sections.
/// </summary>
public enum HomeSectionType
{
/// <summary>
/// My Media.
/// </summary>
SmallLibraryTiles = 0,
/// <summary>
/// My Media Small.
/// </summary>
LibraryButtons = 1,
/// <summary>
/// Active Recordings.
/// </summary>
ActiveRecordings = 2,
/// <summary>
/// Continue Watching.
/// </summary>
Resume = 3,
/// <summary>
/// Continue Listening.
/// </summary>
ResumeAudio = 4,
/// <summary>
/// Latest Media.
/// </summary>
LatestMedia = 5,
/// <summary>
/// Next Up.
/// </summary>
NextUp = 6,
/// <summary>
/// Live TV.
/// </summary>
LiveTv = 7,
/// <summary>
/// None.
/// </summary>
None = 8
}
}

@ -0,0 +1,20 @@
namespace Jellyfin.Data.Enums
{
public enum IndexingKind
{
/// <summary>
/// Index by the premiere date.
/// </summary>
PremiereDate,
/// <summary>
/// Index by the production year.
/// </summary>
ProductionYear,
/// <summary>
/// Index by the community rating.
/// </summary>
CommunityRating
}
}

@ -1,17 +1,17 @@
namespace MediaBrowser.Model.Entities
namespace Jellyfin.Data.Enums
{
/// <summary>
/// Enum ScrollDirection.
/// An enum representing the axis that should be scrolled.
/// </summary>
public enum ScrollDirection
{
/// <summary>
/// The horizontal.
/// Horizontal scrolling direction.
/// </summary>
Horizontal,
/// <summary>
/// The vertical.
/// Vertical scrolling direction.
/// </summary>
Vertical
}

@ -1,17 +1,17 @@
namespace MediaBrowser.Model.Entities
namespace Jellyfin.Data.Enums
{
/// <summary>
/// Enum SortOrder.
/// An enum representing the sorting order.
/// </summary>
public enum SortOrder
{
/// <summary>
/// The ascending.
/// Sort in increasing order.
/// </summary>
Ascending,
/// <summary>
/// The descending.
/// Sort in decreasing order.
/// </summary>
Descending
}

@ -0,0 +1,38 @@
namespace Jellyfin.Data.Enums
{
/// <summary>
/// An enum representing the type of view for a library or collection.
/// </summary>
public enum ViewType
{
/// <summary>
/// Shows banners.
/// </summary>
Banner = 0,
/// <summary>
/// Shows a list of content.
/// </summary>
List = 1,
/// <summary>
/// Shows poster artwork.
/// </summary>
Poster = 2,
/// <summary>
/// Shows poster artwork with a card containing the name and year.
/// </summary>
PosterCard = 3,
/// <summary>
/// Shows a thumbnail.
/// </summary>
Thumb = 4,
/// <summary>
/// Shows a thumbnail with a card containing the name and year.
/// </summary>
ThumbCard = 5
}
}

@ -0,0 +1,49 @@
using System;
using System.Linq;
using Jellyfin.Data.Entities;
using MediaBrowser.Controller;
namespace Jellyfin.Server.Implementations
{
/// <summary>
/// Manages the storage and retrieval of display preferences through Entity Framework.
/// </summary>
public class DisplayPreferencesManager : IDisplayPreferencesManager
{
private readonly JellyfinDbProvider _dbProvider;
/// <summary>
/// Initializes a new instance of the <see cref="DisplayPreferencesManager"/> class.
/// </summary>
/// <param name="dbProvider">The Jellyfin db provider.</param>
public DisplayPreferencesManager(JellyfinDbProvider dbProvider)
{
_dbProvider = dbProvider;
}
/// <inheritdoc />
public DisplayPreferences GetDisplayPreferences(Guid userId, string client)
{
var dbContext = _dbProvider.CreateContext();
var user = dbContext.Users.Find(userId);
#pragma warning disable CA1307
var prefs = user.DisplayPreferences.FirstOrDefault(pref => string.Equals(pref.Client, client));
if (prefs == null)
{
prefs = new DisplayPreferences(client, userId);
user.DisplayPreferences.Add(prefs);
}
return prefs;
}
/// <inheritdoc />
public void SaveChanges(DisplayPreferences preferences)
{
var dbContext = _dbProvider.CreateContext();
dbContext.Update(preferences);
dbContext.SaveChanges();
}
}
}

@ -27,6 +27,8 @@ namespace Jellyfin.Server.Implementations
public virtual DbSet<ActivityLog> ActivityLogs { get; set; }
public virtual DbSet<DisplayPreferences> DisplayPreferences { get; set; }
public virtual DbSet<ImageInfo> ImageInfos { get; set; }
public virtual DbSet<Permission> Permissions { get; set; }

@ -0,0 +1,403 @@
#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("20200630170339_AddDisplayPreferences")]
partial class AddDisplayPreferences
{
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasDefaultSchema("jellyfin")
.HasAnnotation("ProductVersion", "3.1.5");
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")
.HasColumnType("TEXT")
.HasMaxLength(256);
b.Property<int>("LogSeverity")
.HasColumnType("INTEGER");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("TEXT")
.HasMaxLength(512);
b.Property<string>("Overview")
.HasColumnType("TEXT")
.HasMaxLength(512);
b.Property<uint>("RowVersion")
.IsConcurrencyToken()
.HasColumnType("INTEGER");
b.Property<string>("ShortOverview")
.HasColumnType("TEXT")
.HasMaxLength(512);
b.Property<string>("Type")
.IsRequired()
.HasColumnType("TEXT")
.HasMaxLength(256);
b.Property<Guid>("UserId")
.HasColumnType("TEXT");
b.HasKey("Id");
b.ToTable("ActivityLogs");
});
modelBuilder.Entity("Jellyfin.Data.Entities.DisplayPreferences", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<string>("Client")
.IsRequired()
.HasColumnType("TEXT")
.HasMaxLength(64);
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<int>("ScrollDirection")
.HasColumnType("INTEGER");
b.Property<bool>("ShowBackdrop")
.HasColumnType("INTEGER");
b.Property<bool>("ShowSidebar")
.HasColumnType("INTEGER");
b.Property<string>("SortBy")
.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("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()
.HasColumnType("TEXT")
.HasMaxLength(512);
b.Property<Guid?>("UserId")
.HasColumnType("TEXT");
b.HasKey("Id");
b.HasIndex("UserId")
.IsUnique();
b.ToTable("ImageInfos");
});
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()
.HasColumnType("TEXT")
.HasMaxLength(65535);
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")
.HasColumnType("TEXT")
.HasMaxLength(255);
b.Property<string>("AuthenticationProviderId")
.IsRequired()
.HasColumnType("TEXT")
.HasMaxLength(255);
b.Property<bool>("DisplayCollectionsView")
.HasColumnType("INTEGER");
b.Property<bool>("DisplayMissingEpisodes")
.HasColumnType("INTEGER");
b.Property<string>("EasyPassword")
.HasColumnType("TEXT")
.HasMaxLength(65535);
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?>("MaxParentalAgeRating")
.HasColumnType("INTEGER");
b.Property<bool>("MustUpdatePassword")
.HasColumnType("INTEGER");
b.Property<string>("Password")
.HasColumnType("TEXT")
.HasMaxLength(65535);
b.Property<string>("PasswordResetProviderId")
.IsRequired()
.HasColumnType("TEXT")
.HasMaxLength(255);
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")
.HasColumnType("TEXT")
.HasMaxLength(255);
b.Property<int>("SubtitleMode")
.HasColumnType("INTEGER");
b.Property<int>("SyncPlayAccess")
.HasColumnType("INTEGER");
b.Property<string>("Username")
.IsRequired()
.HasColumnType("TEXT")
.HasMaxLength(255);
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)
.WithMany("DisplayPreferences")
.HasForeignKey("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.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");
});
#pragma warning restore 612, 618
}
}
}

@ -0,0 +1,92 @@
#pragma warning disable CS1591
#pragma warning disable SA1601
using System;
using Microsoft.EntityFrameworkCore.Migrations;
namespace Jellyfin.Server.Implementations.Migrations
{
public partial class AddDisplayPreferences : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "DisplayPreferences",
schema: "jellyfin",
columns: table => new
{
Id = table.Column<int>(nullable: false)
.Annotation("Sqlite:Autoincrement", true),
UserId = table.Column<Guid>(nullable: false),
ItemId = table.Column<Guid>(nullable: true),
Client = table.Column<string>(maxLength: 64, nullable: false),
RememberIndexing = table.Column<bool>(nullable: false),
RememberSorting = table.Column<bool>(nullable: false),
SortOrder = table.Column<int>(nullable: false),
ShowSidebar = table.Column<bool>(nullable: false),
ShowBackdrop = table.Column<bool>(nullable: false),
SortBy = table.Column<string>(nullable: true),
ViewType = table.Column<int>(nullable: true),
ScrollDirection = table.Column<int>(nullable: false),
IndexBy = table.Column<int>(nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_DisplayPreferences", x => x.Id);
table.ForeignKey(
name: "FK_DisplayPreferences_Users_UserId",
column: x => x.UserId,
principalSchema: "jellyfin",
principalTable: "Users",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateTable(
name: "HomeSection",
schema: "jellyfin",
columns: table => new
{
Id = table.Column<int>(nullable: false)
.Annotation("Sqlite:Autoincrement", true),
DisplayPreferencesId = table.Column<int>(nullable: false),
Order = table.Column<int>(nullable: false),
Type = table.Column<int>(nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_HomeSection", x => x.Id);
table.ForeignKey(
name: "FK_HomeSection_DisplayPreferences_DisplayPreferencesId",
column: x => x.DisplayPreferencesId,
principalSchema: "jellyfin",
principalTable: "DisplayPreferences",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateIndex(
name: "IX_DisplayPreferences_UserId",
schema: "jellyfin",
table: "DisplayPreferences",
column: "UserId");
migrationBuilder.CreateIndex(
name: "IX_HomeSection_DisplayPreferencesId",
schema: "jellyfin",
table: "HomeSection",
column: "DisplayPreferencesId");
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "HomeSection",
schema: "jellyfin");
migrationBuilder.DropTable(
name: "DisplayPreferences",
schema: "jellyfin");
}
}
}

@ -15,7 +15,7 @@ namespace Jellyfin.Server.Implementations.Migrations
#pragma warning disable 612, 618
modelBuilder
.HasDefaultSchema("jellyfin")
.HasAnnotation("ProductVersion", "3.1.4");
.HasAnnotation("ProductVersion", "3.1.5");
modelBuilder.Entity("Jellyfin.Data.Entities.AccessSchedule", b =>
{
@ -88,6 +88,79 @@ namespace Jellyfin.Server.Implementations.Migrations
b.ToTable("ActivityLogs");
});
modelBuilder.Entity("Jellyfin.Data.Entities.DisplayPreferences", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<string>("Client")
.IsRequired()
.HasColumnType("TEXT")
.HasMaxLength(64);
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<int>("ScrollDirection")
.HasColumnType("INTEGER");
b.Property<bool>("ShowBackdrop")
.HasColumnType("INTEGER");
b.Property<bool>("ShowSidebar")
.HasColumnType("INTEGER");
b.Property<string>("SortBy")
.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("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")
@ -282,6 +355,24 @@ namespace Jellyfin.Server.Implementations.Migrations
.IsRequired();
});
modelBuilder.Entity("Jellyfin.Data.Entities.DisplayPreferences", b =>
{
b.HasOne("Jellyfin.Data.Entities.User", null)
.WithMany("DisplayPreferences")
.HasForeignKey("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)

@ -9,6 +9,7 @@ using Jellyfin.Server.Implementations;
using Jellyfin.Server.Implementations.Activity;
using Jellyfin.Server.Implementations.Users;
using MediaBrowser.Common.Net;
using MediaBrowser.Controller;
using MediaBrowser.Controller.Drawing;
using MediaBrowser.Controller.Library;
using MediaBrowser.Model.Activity;
@ -73,6 +74,7 @@ namespace Jellyfin.Server
serviceCollection.AddSingleton<IActivityManager, ActivityManager>();
serviceCollection.AddSingleton<IUserManager, UserManager>();
serviceCollection.AddSingleton<IDisplayPreferencesManager, DisplayPreferencesManager>();
base.RegisterServices(serviceCollection);
}

@ -21,7 +21,8 @@ namespace Jellyfin.Server.Migrations
typeof(Routines.MigrateActivityLogDb),
typeof(Routines.RemoveDuplicateExtras),
typeof(Routines.AddDefaultPluginRepository),
typeof(Routines.MigrateUserDb)
typeof(Routines.MigrateUserDb),
typeof(Routines.MigrateDisplayPreferencesDb)
};
/// <summary>

@ -0,0 +1,118 @@
using System;
using System.IO;
using System.Text.Json;
using System.Text.Json.Serialization;
using Jellyfin.Data.Entities;
using Jellyfin.Data.Enums;
using Jellyfin.Server.Implementations;
using MediaBrowser.Controller;
using MediaBrowser.Model.Entities;
using Microsoft.Extensions.Logging;
using SQLitePCL.pretty;
namespace Jellyfin.Server.Migrations.Routines
{
/// <summary>
/// The migration routine for migrating the display preferences database to EF Core.
/// </summary>
public class MigrateDisplayPreferencesDb : IMigrationRoutine
{
private const string DbFilename = "displaypreferences.db";
private readonly ILogger<MigrateDisplayPreferencesDb> _logger;
private readonly IServerApplicationPaths _paths;
private readonly JellyfinDbProvider _provider;
private readonly JsonSerializerOptions _jsonOptions;
/// <summary>
/// Initializes a new instance of the <see cref="MigrateDisplayPreferencesDb"/> class.
/// </summary>
/// <param name="logger">The logger.</param>
/// <param name="paths">The server application paths.</param>
/// <param name="provider">The database provider.</param>
public MigrateDisplayPreferencesDb(ILogger<MigrateDisplayPreferencesDb> logger, IServerApplicationPaths paths, JellyfinDbProvider provider)
{
_logger = logger;
_paths = paths;
_provider = provider;
_jsonOptions = new JsonSerializerOptions();
_jsonOptions.Converters.Add(new JsonStringEnumConverter());
}
/// <inheritdoc />
public Guid Id => Guid.Parse("06387815-C3CC-421F-A888-FB5F9992BEA8");
/// <inheritdoc />
public string Name => "MigrateDisplayPreferencesDatabase";
/// <inheritdoc />
public void Perform()
{
HomeSectionType[] defaults =
{
HomeSectionType.SmallLibraryTiles,
HomeSectionType.Resume,
HomeSectionType.ResumeAudio,
HomeSectionType.LiveTv,
HomeSectionType.NextUp,
HomeSectionType.LatestMedia,
HomeSectionType.None,
};
var dbFilePath = Path.Combine(_paths.DataPath, DbFilename);
using (var connection = SQLite3.Open(dbFilePath, ConnectionFlags.ReadOnly, null))
{
var dbContext = _provider.CreateContext();
var results = connection.Query("SELECT * FROM userdisplaypreferences");
foreach (var result in results)
{
var dto = JsonSerializer.Deserialize<DisplayPreferencesDto>(result[3].ToString(), _jsonOptions);
var displayPreferences = new DisplayPreferences(result[2].ToString(), new Guid(result[1].ToBlob()))
{
ViewType = Enum.TryParse<ViewType>(dto.ViewType, true, out var viewType) ? viewType : (ViewType?)null,
IndexBy = Enum.TryParse<IndexingKind>(dto.IndexBy, true, out var indexBy) ? indexBy : (IndexingKind?)null,
ShowBackdrop = dto.ShowBackdrop,
ShowSidebar = dto.ShowSidebar,
SortBy = dto.SortBy,
SortOrder = dto.SortOrder,
RememberIndexing = dto.RememberIndexing,
RememberSorting = dto.RememberSorting,
ScrollDirection = dto.ScrollDirection
};
for (int i = 0; i < 7; i++)
{
dto.CustomPrefs.TryGetValue("homesection" + i, out var homeSection);
displayPreferences.HomeSections.Add(new HomeSection
{
Order = i,
Type = Enum.TryParse<HomeSectionType>(homeSection, true, out var type) ? type : defaults[i]
});
}
dbContext.Add(displayPreferences);
}
dbContext.SaveChanges();
}
try
{
File.Move(dbFilePath, dbFilePath + ".old");
var journalPath = dbFilePath + "-journal";
if (File.Exists(journalPath))
{
File.Move(journalPath, dbFilePath + ".old-journal");
}
}
catch (IOException e)
{
_logger.LogError(e, "Error renaming legacy display preferences database to 'displaypreferences.db.old'");
}
}
}
}

@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Jellyfin.Data.Enums;
using MediaBrowser.Api.UserLibrary;
using MediaBrowser.Controller.Channels;
using MediaBrowser.Controller.Configuration;
@ -11,7 +12,6 @@ using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Net;
using MediaBrowser.Model.Channels;
using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Querying;
using MediaBrowser.Model.Services;
using Microsoft.Extensions.Logging;

@ -1,9 +1,10 @@
using System.Threading;
using System;
using Jellyfin.Data.Entities;
using Jellyfin.Data.Enums;
using MediaBrowser.Controller;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Net;
using MediaBrowser.Controller.Persistence;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Serialization;
using MediaBrowser.Model.Services;
using Microsoft.Extensions.Logging;
@ -13,7 +14,7 @@ namespace MediaBrowser.Api
/// Class UpdateDisplayPreferences.
/// </summary>
[Route("/DisplayPreferences/{DisplayPreferencesId}", "POST", Summary = "Updates a user's display preferences for an item")]
public class UpdateDisplayPreferences : DisplayPreferences, IReturnVoid
public class UpdateDisplayPreferences : DisplayPreferencesDto, IReturnVoid
{
/// <summary>
/// Gets or sets the id.
@ -27,7 +28,7 @@ namespace MediaBrowser.Api
}
[Route("/DisplayPreferences/{Id}", "GET", Summary = "Gets a user's display preferences for an item")]
public class GetDisplayPreferences : IReturn<DisplayPreferences>
public class GetDisplayPreferences : IReturn<DisplayPreferencesDto>
{
/// <summary>
/// Gets or sets the id.
@ -50,28 +51,21 @@ namespace MediaBrowser.Api
public class DisplayPreferencesService : BaseApiService
{
/// <summary>
/// The _display preferences manager.
/// The user manager.
/// </summary>
private readonly IDisplayPreferencesRepository _displayPreferencesManager;
/// <summary>
/// The _json serializer.
/// </summary>
private readonly IJsonSerializer _jsonSerializer;
private readonly IDisplayPreferencesManager _displayPreferencesManager;
/// <summary>
/// Initializes a new instance of the <see cref="DisplayPreferencesService" /> class.
/// </summary>
/// <param name="jsonSerializer">The json serializer.</param>
/// <param name="displayPreferencesManager">The display preferences manager.</param>
public DisplayPreferencesService(
ILogger<DisplayPreferencesService> logger,
IServerConfigurationManager serverConfigurationManager,
IHttpResultFactory httpResultFactory,
IJsonSerializer jsonSerializer,
IDisplayPreferencesRepository displayPreferencesManager)
IDisplayPreferencesManager displayPreferencesManager)
: base(logger, serverConfigurationManager, httpResultFactory)
{
_jsonSerializer = jsonSerializer;
_displayPreferencesManager = displayPreferencesManager;
}
@ -81,9 +75,34 @@ namespace MediaBrowser.Api
/// <param name="request">The request.</param>
public object Get(GetDisplayPreferences request)
{
var result = _displayPreferencesManager.GetDisplayPreferences(request.Id, request.UserId, request.Client);
var result = _displayPreferencesManager.GetDisplayPreferences(Guid.Parse(request.UserId), request.Client);
if (result == null)
{
return null;
}
var dto = new DisplayPreferencesDto
{
Client = result.Client,
Id = result.UserId.ToString(),
ViewType = result.ViewType?.ToString(),
SortBy = result.SortBy,
SortOrder = result.SortOrder,
IndexBy = result.IndexBy?.ToString(),
RememberIndexing = result.RememberIndexing,
RememberSorting = result.RememberSorting,
ScrollDirection = result.ScrollDirection,
ShowBackdrop = result.ShowBackdrop,
ShowSidebar = result.ShowSidebar
};
return ToOptimizedResult(result);
foreach (var homeSection in result.HomeSections)
{
dto.CustomPrefs["homesection" + homeSection.Order] = homeSection.Type.ToString().ToLowerInvariant();
}
return ToOptimizedResult(dto);
}
/// <summary>
@ -92,10 +111,43 @@ namespace MediaBrowser.Api
/// <param name="request">The request.</param>
public void Post(UpdateDisplayPreferences request)
{
// Serialize to json and then back so that the core doesn't see the request dto type
var displayPreferences = _jsonSerializer.DeserializeFromString<DisplayPreferences>(_jsonSerializer.SerializeToString(request));
HomeSectionType[] defaults =
{
HomeSectionType.SmallLibraryTiles,
HomeSectionType.Resume,
HomeSectionType.ResumeAudio,
HomeSectionType.LiveTv,
HomeSectionType.NextUp,
HomeSectionType.LatestMedia,
HomeSectionType.None,
};
var prefs = _displayPreferencesManager.GetDisplayPreferences(Guid.Parse(request.UserId), request.Client);
prefs.ViewType = Enum.TryParse<ViewType>(request.ViewType, true, out var viewType) ? viewType : (ViewType?)null;
prefs.IndexBy = Enum.TryParse<IndexingKind>(request.IndexBy, true, out var indexBy) ? indexBy : (IndexingKind?)null;
prefs.ShowBackdrop = request.ShowBackdrop;
prefs.ShowSidebar = request.ShowSidebar;
prefs.SortBy = request.SortBy;
prefs.SortOrder = request.SortOrder;
prefs.RememberIndexing = request.RememberIndexing;
prefs.RememberSorting = request.RememberSorting;
prefs.ScrollDirection = request.ScrollDirection;
prefs.HomeSections.Clear();
for (int i = 0; i < 7; i++)
{
if (request.CustomPrefs.TryGetValue("homesection" + i, out var homeSection))
{
prefs.HomeSections.Add(new HomeSection
{
Order = i,
Type = Enum.TryParse<HomeSectionType>(homeSection, true, out var type) ? type : defaults[i]
});
}
}
_displayPreferencesManager.SaveDisplayPreferences(displayPreferences, request.UserId, request.Client, CancellationToken.None);
_displayPreferencesManager.SaveChanges(prefs);
}
}
}

@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using Jellyfin.Data.Entities;
using Jellyfin.Data.Enums;
using MediaBrowser.Common.Extensions;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Dto;

@ -1,6 +1,7 @@
using System;
using System.Linq;
using Jellyfin.Data.Entities;
using Jellyfin.Data.Enums;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Dto;
using MediaBrowser.Controller.Entities;

@ -2,6 +2,7 @@ using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using Jellyfin.Data.Enums;
using MediaBrowser.Common.Extensions;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Dto;

@ -1,5 +1,6 @@
using System;
using System.Linq;
using Jellyfin.Data.Enums;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Querying;
using MediaBrowser.Model.Services;
@ -466,8 +467,8 @@ namespace MediaBrowser.Api.UserLibrary
var sortOrderValue = sortOrders.Length > sortOrderIndex ? sortOrders[sortOrderIndex] : null;
var sortOrder = string.Equals(sortOrderValue, "Descending", StringComparison.OrdinalIgnoreCase)
? MediaBrowser.Model.Entities.SortOrder.Descending
: MediaBrowser.Model.Entities.SortOrder.Ascending;
? Jellyfin.Data.Enums.SortOrder.Descending
: Jellyfin.Data.Enums.SortOrder.Ascending;
result[i] = new ValueTuple<string, SortOrder>(vals[i], sortOrder);
}

@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using Jellyfin.Data.Entities;
using Jellyfin.Data.Enums;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Entities.Movies;
using MediaBrowser.Controller.Library;

@ -0,0 +1,25 @@
using System;
using Jellyfin.Data.Entities;
namespace MediaBrowser.Controller
{
/// <summary>
/// Manages the storage and retrieval of display preferences.
/// </summary>
public interface IDisplayPreferencesManager
{
/// <summary>
/// Gets the display preferences for the user and client.
/// </summary>
/// <param name="userId">The user's id.</param>
/// <param name="client">The client string.</param>
/// <returns>The associated display preferences.</returns>
DisplayPreferences GetDisplayPreferences(Guid userId, string client);
/// <summary>
/// Saves changes to the provided display preferences.
/// </summary>
/// <param name="preferences">The display preferences to save.</param>
void SaveChanges(DisplayPreferences preferences);
}
}

@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using Jellyfin.Data.Entities;
using Jellyfin.Data.Enums;
using MediaBrowser.Controller.Dto;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.Audio;

@ -1,53 +0,0 @@
using System;
using System.Collections.Generic;
using System.Threading;
using MediaBrowser.Model.Entities;
namespace MediaBrowser.Controller.Persistence
{
/// <summary>
/// Interface IDisplayPreferencesRepository.
/// </summary>
public interface IDisplayPreferencesRepository : IRepository
{
/// <summary>
/// Saves display preferences for an item.
/// </summary>
/// <param name="displayPreferences">The display preferences.</param>
/// <param name="userId">The user id.</param>
/// <param name="client">The client.</param>
/// <param name="cancellationToken">The cancellation token.</param>
void SaveDisplayPreferences(
DisplayPreferences displayPreferences,
string userId,
string client,
CancellationToken cancellationToken);
/// <summary>
/// Saves all display preferences for a user.
/// </summary>
/// <param name="displayPreferences">The display preferences.</param>
/// <param name="userId">The user id.</param>
/// <param name="cancellationToken">The cancellation token.</param>
void SaveAllDisplayPreferences(
IEnumerable<DisplayPreferences> displayPreferences,
Guid userId,
CancellationToken cancellationToken);
/// <summary>
/// Gets the display preferences.
/// </summary>
/// <param name="displayPreferencesId">The display preferences id.</param>
/// <param name="userId">The user id.</param>
/// <param name="client">The client.</param>
/// <returns>Task{DisplayPreferences}.</returns>
DisplayPreferences GetDisplayPreferences(string displayPreferencesId, string userId, string client);
/// <summary>
/// Gets all display preferences for the given user.
/// </summary>
/// <param name="userId">The user id.</param>
/// <returns>Task{DisplayPreferences}.</returns>
IEnumerable<DisplayPreferences> GetAllDisplayPreferences(Guid userId);
}
}

@ -6,6 +6,7 @@ using System.Text.Json.Serialization;
using System.Threading;
using System.Threading.Tasks;
using Jellyfin.Data.Entities;
using Jellyfin.Data.Enums;
using MediaBrowser.Controller.Dto;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.Audio;

@ -1,6 +1,6 @@
#pragma warning disable CS1591
using MediaBrowser.Model.Entities;
using Jellyfin.Data.Enums;
namespace MediaBrowser.Model.Dlna
{

@ -1,22 +1,18 @@
#nullable disable
using System.Collections.Generic;
using Jellyfin.Data.Enums;
namespace MediaBrowser.Model.Entities
{
/// <summary>
/// Defines the display preferences for any item that supports them (usually Folders).
/// </summary>
public class DisplayPreferences
public class DisplayPreferencesDto
{
/// <summary>
/// The image scale.
/// Initializes a new instance of the <see cref="DisplayPreferencesDto" /> class.
/// </summary>
private const double ImageScale = .9;
/// <summary>
/// Initializes a new instance of the <see cref="DisplayPreferences" /> class.
/// </summary>
public DisplayPreferences()
public DisplayPreferencesDto()
{
RememberIndexing = false;
PrimaryImageHeight = 250;

@ -2,7 +2,7 @@
#pragma warning disable CS1591
using System;
using MediaBrowser.Model.Entities;
using Jellyfin.Data.Enums;
namespace MediaBrowser.Model.LiveTv
{

@ -1,6 +1,6 @@
#pragma warning disable CS1591
using MediaBrowser.Model.Entities;
using Jellyfin.Data.Enums;
namespace MediaBrowser.Model.LiveTv
{

Loading…
Cancel
Save