aboutsummaryrefslogtreecommitdiff
path: root/Emby.Server.Implementations
diff options
context:
space:
mode:
Diffstat (limited to 'Emby.Server.Implementations')
-rw-r--r--Emby.Server.Implementations/Activity/ActivityLogEntryPoint.cs42
-rw-r--r--Emby.Server.Implementations/ApplicationHost.cs9
-rw-r--r--Emby.Server.Implementations/Channels/ChannelManager.cs7
-rw-r--r--Emby.Server.Implementations/Collections/CollectionManager.cs4
-rw-r--r--Emby.Server.Implementations/Data/SqliteDisplayPreferencesRepository.cs225
-rw-r--r--Emby.Server.Implementations/Data/SqliteUserDataRepository.cs1
-rw-r--r--Emby.Server.Implementations/Data/SqliteUserRepository.cs240
-rw-r--r--Emby.Server.Implementations/Devices/DeviceManager.cs26
-rw-r--r--Emby.Server.Implementations/Dto/DtoService.cs23
-rw-r--r--Emby.Server.Implementations/EntryPoints/LibraryChangedNotifier.cs2
-rw-r--r--Emby.Server.Implementations/EntryPoints/RecordingNotifier.cs3
-rw-r--r--Emby.Server.Implementations/EntryPoints/RefreshUsersMetadata.cs77
-rw-r--r--Emby.Server.Implementations/EntryPoints/ServerEventNotifier.cs29
-rw-r--r--Emby.Server.Implementations/HttpServer/Security/AuthService.cs24
-rw-r--r--Emby.Server.Implementations/HttpServer/Security/AuthorizationContext.cs4
-rw-r--r--Emby.Server.Implementations/HttpServer/Security/SessionContext.cs4
-rw-r--r--Emby.Server.Implementations/Library/DefaultAuthenticationProvider.cs177
-rw-r--r--Emby.Server.Implementations/Library/DefaultPasswordResetProvider.cs140
-rw-r--r--Emby.Server.Implementations/Library/InvalidAuthProvider.cs54
-rw-r--r--Emby.Server.Implementations/Library/LibraryManager.cs22
-rw-r--r--Emby.Server.Implementations/Library/MediaSourceManager.cs52
-rw-r--r--Emby.Server.Implementations/Library/MediaStreamSelector.cs1
-rw-r--r--Emby.Server.Implementations/Library/MusicManager.cs32
-rw-r--r--Emby.Server.Implementations/Library/SearchEngine.cs4
-rw-r--r--Emby.Server.Implementations/Library/UserDataManager.cs10
-rw-r--r--Emby.Server.Implementations/Library/UserManager.cs1132
-rw-r--r--Emby.Server.Implementations/Library/UserViewManager.cs29
-rw-r--r--Emby.Server.Implementations/LiveTv/LiveTvManager.cs40
-rw-r--r--Emby.Server.Implementations/Playlists/ManualPlaylistsFolder.cs6
-rw-r--r--Emby.Server.Implementations/Playlists/PlaylistManager.cs4
-rw-r--r--Emby.Server.Implementations/Session/SessionManager.cs91
-rw-r--r--Emby.Server.Implementations/Sorting/DateLastMediaAddedComparer.cs2
-rw-r--r--Emby.Server.Implementations/Sorting/DatePlayedComparer.cs2
-rw-r--r--Emby.Server.Implementations/Sorting/IsFavoriteOrLikeComparer.cs2
-rw-r--r--Emby.Server.Implementations/Sorting/IsPlayedComparer.cs2
-rw-r--r--Emby.Server.Implementations/Sorting/IsUnplayedComparer.cs2
-rw-r--r--Emby.Server.Implementations/Sorting/PlayCountComparer.cs2
-rw-r--r--Emby.Server.Implementations/TV/TVSeriesManager.cs13
38 files changed, 241 insertions, 2298 deletions
diff --git a/Emby.Server.Implementations/Activity/ActivityLogEntryPoint.cs b/Emby.Server.Implementations/Activity/ActivityLogEntryPoint.cs
index 3983824a3..31d9609a6 100644
--- a/Emby.Server.Implementations/Activity/ActivityLogEntryPoint.cs
+++ b/Emby.Server.Implementations/Activity/ActivityLogEntryPoint.cs
@@ -88,22 +88,21 @@ namespace Emby.Server.Implementations.Activity
_subManager.SubtitleDownloadFailure += OnSubtitleDownloadFailure;
- _userManager.UserCreated += OnUserCreated;
- _userManager.UserPasswordChanged += OnUserPasswordChanged;
- _userManager.UserDeleted += OnUserDeleted;
- _userManager.UserPolicyUpdated += OnUserPolicyUpdated;
- _userManager.UserLockedOut += OnUserLockedOut;
+ _userManager.OnUserCreated += OnUserCreated;
+ _userManager.OnUserPasswordChanged += OnUserPasswordChanged;
+ _userManager.OnUserDeleted += OnUserDeleted;
+ _userManager.OnUserLockedOut += OnUserLockedOut;
return Task.CompletedTask;
}
- private async void OnUserLockedOut(object sender, GenericEventArgs<MediaBrowser.Controller.Entities.User> e)
+ private async void OnUserLockedOut(object sender, GenericEventArgs<User> e)
{
await CreateLogEntry(new ActivityLog(
string.Format(
CultureInfo.InvariantCulture,
_localization.GetLocalizedString("UserLockedOutWithName"),
- e.Argument.Name),
+ e.Argument.Username),
NotificationType.UserLockedOut.ToString(),
e.Argument.Id))
.ConfigureAwait(false);
@@ -152,7 +151,7 @@ namespace Emby.Server.Implementations.Activity
string.Format(
CultureInfo.InvariantCulture,
_localization.GetLocalizedString("UserStoppedPlayingItemWithValues"),
- user.Name,
+ user.Username,
GetItemName(item),
e.DeviceName),
GetPlaybackStoppedNotificationType(item.MediaType),
@@ -187,7 +186,7 @@ namespace Emby.Server.Implementations.Activity
string.Format(
CultureInfo.InvariantCulture,
_localization.GetLocalizedString("UserStartedPlayingItemWithValues"),
- user.Name,
+ user.Username,
GetItemName(item),
e.DeviceName),
GetPlaybackNotificationType(item.MediaType),
@@ -304,49 +303,49 @@ namespace Emby.Server.Implementations.Activity
}).ConfigureAwait(false);
}
- private async void OnUserPolicyUpdated(object sender, GenericEventArgs<MediaBrowser.Controller.Entities.User> e)
+ private async void OnUserPolicyUpdated(object sender, GenericEventArgs<User> e)
{
await CreateLogEntry(new ActivityLog(
string.Format(
CultureInfo.InvariantCulture,
_localization.GetLocalizedString("UserPolicyUpdatedWithName"),
- e.Argument.Name),
+ e.Argument.Username),
"UserPolicyUpdated",
e.Argument.Id))
.ConfigureAwait(false);
}
- private async void OnUserDeleted(object sender, GenericEventArgs<MediaBrowser.Controller.Entities.User> e)
+ private async void OnUserDeleted(object sender, GenericEventArgs<User> e)
{
await CreateLogEntry(new ActivityLog(
string.Format(
CultureInfo.InvariantCulture,
_localization.GetLocalizedString("UserDeletedWithName"),
- e.Argument.Name),
+ e.Argument.Username),
"UserDeleted",
Guid.Empty))
.ConfigureAwait(false);
}
- private async void OnUserPasswordChanged(object sender, GenericEventArgs<MediaBrowser.Controller.Entities.User> e)
+ private async void OnUserPasswordChanged(object sender, GenericEventArgs<User> e)
{
await CreateLogEntry(new ActivityLog(
string.Format(
CultureInfo.InvariantCulture,
_localization.GetLocalizedString("UserPasswordChangedWithName"),
- e.Argument.Name),
+ e.Argument.Username),
"UserPasswordChanged",
e.Argument.Id))
.ConfigureAwait(false);
}
- private async void OnUserCreated(object sender, GenericEventArgs<MediaBrowser.Controller.Entities.User> e)
+ private async void OnUserCreated(object sender, GenericEventArgs<User> e)
{
await CreateLogEntry(new ActivityLog(
string.Format(
CultureInfo.InvariantCulture,
_localization.GetLocalizedString("UserCreatedWithName"),
- e.Argument.Name),
+ e.Argument.Username),
"UserCreated",
e.Argument.Id))
.ConfigureAwait(false);
@@ -510,11 +509,10 @@ namespace Emby.Server.Implementations.Activity
_subManager.SubtitleDownloadFailure -= OnSubtitleDownloadFailure;
- _userManager.UserCreated -= OnUserCreated;
- _userManager.UserPasswordChanged -= OnUserPasswordChanged;
- _userManager.UserDeleted -= OnUserDeleted;
- _userManager.UserPolicyUpdated -= OnUserPolicyUpdated;
- _userManager.UserLockedOut -= OnUserLockedOut;
+ _userManager.OnUserCreated -= OnUserCreated;
+ _userManager.OnUserPasswordChanged -= OnUserPasswordChanged;
+ _userManager.OnUserDeleted -= OnUserDeleted;
+ _userManager.OnUserLockedOut -= OnUserLockedOut;
}
/// <summary>
diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs
index e6410f857..b2fe6a3ab 100644
--- a/Emby.Server.Implementations/ApplicationHost.cs
+++ b/Emby.Server.Implementations/ApplicationHost.cs
@@ -560,8 +560,6 @@ namespace Emby.Server.Implementations
serviceCollection.AddSingleton<IAuthenticationRepository, AuthenticationRepository>();
- serviceCollection.AddSingleton<IUserRepository, SqliteUserRepository>();
-
// TODO: Refactor to eliminate the circular dependency here so that Lazy<T> isn't required
serviceCollection.AddTransient(provider => new Lazy<IDtoService>(provider.GetRequiredService<IDtoService>));
serviceCollection.AddSingleton<IUserManager, UserManager>();
@@ -655,15 +653,11 @@ namespace Emby.Server.Implementations
((SqliteDisplayPreferencesRepository)Resolve<IDisplayPreferencesRepository>()).Initialize();
((AuthenticationRepository)Resolve<IAuthenticationRepository>()).Initialize();
- ((SqliteUserRepository)Resolve<IUserRepository>()).Initialize();
SetStaticProperties();
- var userManager = (UserManager)Resolve<IUserManager>();
- userManager.Initialize();
-
var userDataRepo = (SqliteUserDataRepository)Resolve<IUserDataRepository>();
- ((SqliteItemRepository)Resolve<IItemRepository>()).Initialize(userDataRepo, userManager);
+ ((SqliteItemRepository)Resolve<IItemRepository>()).Initialize(userDataRepo, Resolve<IUserManager>());
FindParts();
}
@@ -746,7 +740,6 @@ namespace Emby.Server.Implementations
BaseItem.ProviderManager = Resolve<IProviderManager>();
BaseItem.LocalizationManager = Resolve<ILocalizationManager>();
BaseItem.ItemRepository = Resolve<IItemRepository>();
- User.UserManager = Resolve<IUserManager>();
BaseItem.FileSystem = _fileSystemManager;
BaseItem.UserDataManager = Resolve<IUserDataManager>();
BaseItem.ChannelManager = Resolve<IChannelManager>();
diff --git a/Emby.Server.Implementations/Channels/ChannelManager.cs b/Emby.Server.Implementations/Channels/ChannelManager.cs
index 138832fb8..cb320dcb1 100644
--- a/Emby.Server.Implementations/Channels/ChannelManager.cs
+++ b/Emby.Server.Implementations/Channels/ChannelManager.cs
@@ -46,7 +46,7 @@ namespace Emby.Server.Implementations.Channels
new ConcurrentDictionary<string, Tuple<DateTime, List<MediaSourceInfo>>>();
private readonly SemaphoreSlim _resourcePool = new SemaphoreSlim(1, 1);
-
+
/// <summary>
/// Initializes a new instance of the <see cref="ChannelManager"/> class.
/// </summary>
@@ -791,8 +791,9 @@ namespace Emby.Server.Implementations.Channels
return result;
}
- private async Task<ChannelItemResult> GetChannelItems(IChannel channel,
- User user,
+ private async Task<ChannelItemResult> GetChannelItems(
+ IChannel channel,
+ Jellyfin.Data.Entities.User user,
string externalFolderId,
ChannelItemSortField? sortField,
bool sortDescending,
diff --git a/Emby.Server.Implementations/Collections/CollectionManager.cs b/Emby.Server.Implementations/Collections/CollectionManager.cs
index 7c518d483..61963b633 100644
--- a/Emby.Server.Implementations/Collections/CollectionManager.cs
+++ b/Emby.Server.Implementations/Collections/CollectionManager.cs
@@ -121,7 +121,7 @@ namespace Emby.Server.Implementations.Collections
return EnsureLibraryFolder(GetCollectionsFolderPath(), createIfNeeded);
}
- private IEnumerable<BoxSet> GetCollections(User user)
+ private IEnumerable<BoxSet> GetCollections(Jellyfin.Data.Entities.User user)
{
var folder = GetCollectionsFolder(false).Result;
@@ -325,7 +325,7 @@ namespace Emby.Server.Implementations.Collections
}
/// <inheritdoc />
- public IEnumerable<BaseItem> CollapseItemsWithinBoxSets(IEnumerable<BaseItem> items, User user)
+ public IEnumerable<BaseItem> CollapseItemsWithinBoxSets(IEnumerable<BaseItem> items, Jellyfin.Data.Entities.User user)
{
var results = new Dictionary<Guid, BaseItem>();
diff --git a/Emby.Server.Implementations/Data/SqliteDisplayPreferencesRepository.cs b/Emby.Server.Implementations/Data/SqliteDisplayPreferencesRepository.cs
deleted file mode 100644
index d474f1c6b..000000000
--- a/Emby.Server.Implementations/Data/SqliteDisplayPreferencesRepository.cs
+++ /dev/null
@@ -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);
- }
-}
diff --git a/Emby.Server.Implementations/Data/SqliteUserDataRepository.cs b/Emby.Server.Implementations/Data/SqliteUserDataRepository.cs
index 6ee6230fc..b99b74ef8 100644
--- a/Emby.Server.Implementations/Data/SqliteUserDataRepository.cs
+++ b/Emby.Server.Implementations/Data/SqliteUserDataRepository.cs
@@ -4,6 +4,7 @@ using System;
using System.Collections.Generic;
using System.IO;
using System.Threading;
+using Jellyfin.Data.Entities;
using MediaBrowser.Common.Configuration;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Library;
diff --git a/Emby.Server.Implementations/Data/SqliteUserRepository.cs b/Emby.Server.Implementations/Data/SqliteUserRepository.cs
deleted file mode 100644
index 0c3f26974..000000000
--- a/Emby.Server.Implementations/Data/SqliteUserRepository.cs
+++ /dev/null
@@ -1,240 +0,0 @@
-#pragma warning disable CS1591
-
-using System;
-using System.Collections.Generic;
-using System.IO;
-using System.Text.Json;
-using MediaBrowser.Common.Json;
-using MediaBrowser.Controller;
-using MediaBrowser.Controller.Entities;
-using MediaBrowser.Controller.Persistence;
-using Microsoft.Extensions.Logging;
-using SQLitePCL.pretty;
-
-namespace Emby.Server.Implementations.Data
-{
- /// <summary>
- /// Class SQLiteUserRepository
- /// </summary>
- public class SqliteUserRepository : BaseSqliteRepository, IUserRepository
- {
- private readonly JsonSerializerOptions _jsonOptions;
-
- public SqliteUserRepository(
- ILogger<SqliteUserRepository> logger,
- IServerApplicationPaths appPaths)
- : base(logger)
- {
- _jsonOptions = JsonDefaults.GetOptions();
-
- DbFilePath = Path.Combine(appPaths.DataPath, "users.db");
- }
-
- /// <summary>
- /// Gets the name of the repository
- /// </summary>
- /// <value>The name.</value>
- public string Name => "SQLite";
-
- /// <summary>
- /// Opens the connection to the database.
- /// </summary>
- public void Initialize()
- {
- using (var connection = GetConnection())
- {
- var localUsersTableExists = TableExists(connection, "LocalUsersv2");
-
- connection.RunQueries(new[] {
- "create table if not exists LocalUsersv2 (Id INTEGER PRIMARY KEY, guid GUID NOT NULL, data BLOB NOT NULL)",
- "drop index if exists idx_users"
- });
-
- if (!localUsersTableExists && TableExists(connection, "Users"))
- {
- TryMigrateToLocalUsersTable(connection);
- }
-
- RemoveEmptyPasswordHashes(connection);
- }
- }
-
- private void TryMigrateToLocalUsersTable(ManagedConnection connection)
- {
- try
- {
- connection.RunQueries(new[]
- {
- "INSERT INTO LocalUsersv2 (guid, data) SELECT guid,data from users"
- });
- }
- catch (Exception ex)
- {
- Logger.LogError(ex, "Error migrating users database");
- }
- }
-
- private void RemoveEmptyPasswordHashes(ManagedConnection connection)
- {
- foreach (var user in RetrieveAllUsers(connection))
- {
- // If the user password is the sha1 hash of the empty string, remove it
- if (!string.Equals(user.Password, "DA39A3EE5E6B4B0D3255BFEF95601890AFD80709", StringComparison.Ordinal)
- && !string.Equals(user.Password, "$SHA1$DA39A3EE5E6B4B0D3255BFEF95601890AFD80709", StringComparison.Ordinal))
- {
- continue;
- }
-
- user.Password = null;
- var serialized = JsonSerializer.SerializeToUtf8Bytes(user, _jsonOptions);
-
- connection.RunInTransaction(db =>
- {
- using (var statement = db.PrepareStatement("update LocalUsersv2 set data=@data where Id=@InternalId"))
- {
- statement.TryBind("@InternalId", user.InternalId);
- statement.TryBind("@data", serialized);
- statement.MoveNext();
- }
- }, TransactionMode);
- }
- }
-
- /// <summary>
- /// Save a user in the repo
- /// </summary>
- public void CreateUser(User user)
- {
- if (user == null)
- {
- throw new ArgumentNullException(nameof(user));
- }
-
- var serialized = JsonSerializer.SerializeToUtf8Bytes(user, _jsonOptions);
-
- using (var connection = GetConnection())
- {
- connection.RunInTransaction(db =>
- {
- using (var statement = db.PrepareStatement("insert into LocalUsersv2 (guid, data) values (@guid, @data)"))
- {
- statement.TryBind("@guid", user.Id.ToByteArray());
- statement.TryBind("@data", serialized);
-
- statement.MoveNext();
- }
-
- var createdUser = GetUser(user.Id, connection);
-
- if (createdUser == null)
- {
- throw new ApplicationException("created user should never be null");
- }
-
- user.InternalId = createdUser.InternalId;
-
- }, TransactionMode);
- }
- }
-
- public void UpdateUser(User user)
- {
- if (user == null)
- {
- throw new ArgumentNullException(nameof(user));
- }
-
- var serialized = JsonSerializer.SerializeToUtf8Bytes(user, _jsonOptions);
-
- using (var connection = GetConnection())
- {
- connection.RunInTransaction(db =>
- {
- using (var statement = db.PrepareStatement("update LocalUsersv2 set data=@data where Id=@InternalId"))
- {
- statement.TryBind("@InternalId", user.InternalId);
- statement.TryBind("@data", serialized);
- statement.MoveNext();
- }
-
- }, TransactionMode);
- }
- }
-
- private User GetUser(Guid guid, ManagedConnection connection)
- {
- using (var statement = connection.PrepareStatement("select id,guid,data from LocalUsersv2 where guid=@guid"))
- {
- statement.TryBind("@guid", guid);
-
- foreach (var row in statement.ExecuteQuery())
- {
- return GetUser(row);
- }
- }
-
- return null;
- }
-
- private User GetUser(IReadOnlyList<IResultSetValue> row)
- {
- var id = row[0].ToInt64();
- var guid = row[1].ReadGuidFromBlob();
-
- var user = JsonSerializer.Deserialize<User>(row[2].ToBlob(), _jsonOptions);
- user.InternalId = id;
- user.Id = guid;
- return user;
- }
-
- /// <summary>
- /// Retrieve all users from the database
- /// </summary>
- /// <returns>IEnumerable{User}.</returns>
- public List<User> RetrieveAllUsers()
- {
- using (var connection = GetConnection(true))
- {
- return new List<User>(RetrieveAllUsers(connection));
- }
- }
-
- /// <summary>
- /// Retrieve all users from the database
- /// </summary>
- /// <returns>IEnumerable{User}.</returns>
- private IEnumerable<User> RetrieveAllUsers(ManagedConnection connection)
- {
- foreach (var row in connection.Query("select id,guid,data from LocalUsersv2"))
- {
- yield return GetUser(row);
- }
- }
-
- /// <summary>
- /// Deletes the user.
- /// </summary>
- /// <param name="user">The user.</param>
- /// <returns>Task.</returns>
- /// <exception cref="ArgumentNullException">user</exception>
- public void DeleteUser(User user)
- {
- if (user == null)
- {
- throw new ArgumentNullException(nameof(user));
- }
-
- using (var connection = GetConnection())
- {
- connection.RunInTransaction(db =>
- {
- using (var statement = db.PrepareStatement("delete from LocalUsersv2 where Id=@id"))
- {
- statement.TryBind("@id", user.InternalId);
- statement.MoveNext();
- }
- }, TransactionMode);
- }
- }
- }
-}
diff --git a/Emby.Server.Implementations/Devices/DeviceManager.cs b/Emby.Server.Implementations/Devices/DeviceManager.cs
index 2283f2433..2d44a7c28 100644
--- a/Emby.Server.Implementations/Devices/DeviceManager.cs
+++ b/Emby.Server.Implementations/Devices/DeviceManager.cs
@@ -5,6 +5,9 @@ using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
+using System.Threading.Tasks;
+using Jellyfin.Data.Enums;
+using Jellyfin.Data.Entities;
using MediaBrowser.Common.Extensions;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Devices;
@@ -181,7 +184,13 @@ namespace Emby.Server.Implementations.Devices
throw new ArgumentNullException(nameof(deviceId));
}
- if (!CanAccessDevice(user.Policy, deviceId))
+ if (user.HasPermission(PermissionKind.EnableAllDevices)
+ || user.HasPermission(PermissionKind.IsAdministrator))
+ {
+ return true;
+ }
+
+ if (!user.GetPreference(PreferenceKind.EnabledDevices).Contains(deviceId, StringComparer.OrdinalIgnoreCase))
{
var capabilities = GetCapabilities(deviceId);
@@ -193,20 +202,5 @@ namespace Emby.Server.Implementations.Devices
return true;
}
-
- private static bool CanAccessDevice(UserPolicy policy, string id)
- {
- if (policy.EnableAllDevices)
- {
- return true;
- }
-
- if (policy.IsAdministrator)
- {
- return true;
- }
-
- return policy.EnabledDevices.Contains(id, StringComparer.OrdinalIgnoreCase);
- }
}
}
diff --git a/Emby.Server.Implementations/Dto/DtoService.cs b/Emby.Server.Implementations/Dto/DtoService.cs
index c4b65d265..da40bca4c 100644
--- a/Emby.Server.Implementations/Dto/DtoService.cs
+++ b/Emby.Server.Implementations/Dto/DtoService.cs
@@ -6,6 +6,7 @@ using System.Globalization;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
+using Jellyfin.Data.Enums;
using MediaBrowser.Common;
using MediaBrowser.Controller.Channels;
using MediaBrowser.Controller.Drawing;
@@ -74,7 +75,7 @@ namespace Emby.Server.Implementations.Dto
/// <param name="owner">The owner.</param>
/// <returns>Task{DtoBaseItem}.</returns>
/// <exception cref="ArgumentNullException">item</exception>
- public BaseItemDto GetBaseItemDto(BaseItem item, ItemFields[] fields, User user = null, BaseItem owner = null)
+ public BaseItemDto GetBaseItemDto(BaseItem item, ItemFields[] fields, Jellyfin.Data.Entities.User user = null, BaseItem owner = null)
{
var options = new DtoOptions
{
@@ -85,7 +86,7 @@ namespace Emby.Server.Implementations.Dto
}
/// <inheritdoc />
- public IReadOnlyList<BaseItemDto> GetBaseItemDtos(IReadOnlyList<BaseItem> items, DtoOptions options, User user = null, BaseItem owner = null)
+ public IReadOnlyList<BaseItemDto> GetBaseItemDtos(IReadOnlyList<BaseItem> items, DtoOptions options, Jellyfin.Data.Entities.User user = null, BaseItem owner = null)
{
var returnItems = new BaseItemDto[items.Count];
var programTuples = new List<(BaseItem, BaseItemDto)>();
@@ -138,7 +139,7 @@ namespace Emby.Server.Implementations.Dto
return returnItems;
}
- public BaseItemDto GetBaseItemDto(BaseItem item, DtoOptions options, User user = null, BaseItem owner = null)
+ public BaseItemDto GetBaseItemDto(BaseItem item, DtoOptions options, Jellyfin.Data.Entities.User user = null, BaseItem owner = null)
{
var dto = GetBaseItemDtoInternal(item, options, user, owner);
if (item is LiveTvChannel tvChannel)
@@ -172,7 +173,7 @@ namespace Emby.Server.Implementations.Dto
return dto;
}
- private static IList<BaseItem> GetTaggedItems(IItemByName byName, User user, DtoOptions options)
+ private static IList<BaseItem> GetTaggedItems(IItemByName byName, Jellyfin.Data.Entities.User user, DtoOptions options)
{
return byName.GetTaggedItems(
new InternalItemsQuery(user)
@@ -182,7 +183,7 @@ namespace Emby.Server.Implementations.Dto
});
}
- private BaseItemDto GetBaseItemDtoInternal(BaseItem item, DtoOptions options, User user = null, BaseItem owner = null)
+ private BaseItemDto GetBaseItemDtoInternal(BaseItem item, DtoOptions options, Jellyfin.Data.Entities.User user = null, BaseItem owner = null)
{
var dto = new BaseItemDto
{
@@ -315,7 +316,7 @@ namespace Emby.Server.Implementations.Dto
}
}
- public BaseItemDto GetItemByNameDto(BaseItem item, DtoOptions options, List<BaseItem> taggedItems, User user = null)
+ public BaseItemDto GetItemByNameDto(BaseItem item, DtoOptions options, List<BaseItem> taggedItems, Jellyfin.Data.Entities.User user = null)
{
var dto = GetBaseItemDtoInternal(item, options, user);
@@ -327,7 +328,7 @@ namespace Emby.Server.Implementations.Dto
return dto;
}
- private static void SetItemByNameInfo(BaseItem item, BaseItemDto dto, IList<BaseItem> taggedItems, User user = null)
+ private static void SetItemByNameInfo(BaseItem item, BaseItemDto dto, IList<BaseItem> taggedItems, Jellyfin.Data.Entities.User user = null)
{
if (item is MusicArtist)
{
@@ -363,7 +364,7 @@ namespace Emby.Server.Implementations.Dto
/// <summary>
/// Attaches the user specific info.
/// </summary>
- private void AttachUserSpecificInfo(BaseItemDto dto, BaseItem item, User user, DtoOptions options)
+ private void AttachUserSpecificInfo(BaseItemDto dto, BaseItem item, Jellyfin.Data.Entities.User user, DtoOptions options)
{
if (item.IsFolder)
{
@@ -384,7 +385,7 @@ namespace Emby.Server.Implementations.Dto
if (options.ContainsField(ItemFields.ChildCount))
{
- dto.ChildCount = dto.ChildCount ?? GetChildCount(folder, user);
+ dto.ChildCount ??= GetChildCount(folder, user);
}
}
@@ -414,7 +415,7 @@ namespace Emby.Server.Implementations.Dto
if (options.ContainsField(ItemFields.BasicSyncInfo))
{
- var userCanSync = user != null && user.Policy.EnableContentDownloading;
+ var userCanSync = user != null && user.HasPermission(PermissionKind.EnableContentDownloading);
if (userCanSync && item.SupportsExternalTransfer)
{
dto.SupportsSync = true;
@@ -422,7 +423,7 @@ namespace Emby.Server.Implementations.Dto
}
}
- private static int GetChildCount(Folder folder, User user)
+ private static int GetChildCount(Folder folder, Jellyfin.Data.Entities.User user)
{
// Right now this is too slow to calculate for top level folders on a per-user basis
// Just return something so that apps that are expecting a value won't think the folders are empty
diff --git a/Emby.Server.Implementations/EntryPoints/LibraryChangedNotifier.cs b/Emby.Server.Implementations/EntryPoints/LibraryChangedNotifier.cs
index 8e3236407..7ece52cad 100644
--- a/Emby.Server.Implementations/EntryPoints/LibraryChangedNotifier.cs
+++ b/Emby.Server.Implementations/EntryPoints/LibraryChangedNotifier.cs
@@ -446,7 +446,7 @@ namespace Emby.Server.Implementations.EntryPoints
/// <param name="user">The user.</param>
/// <param name="includeIfNotFound">if set to <c>true</c> [include if not found].</param>
/// <returns>IEnumerable{``0}.</returns>
- private IEnumerable<T> TranslatePhysicalItemToUserLibrary<T>(T item, User user, bool includeIfNotFound = false)
+ private IEnumerable<T> TranslatePhysicalItemToUserLibrary<T>(T item, Jellyfin.Data.Entities.User user, bool includeIfNotFound = false)
where T : BaseItem
{
// If the physical root changed, return the user root
diff --git a/Emby.Server.Implementations/EntryPoints/RecordingNotifier.cs b/Emby.Server.Implementations/EntryPoints/RecordingNotifier.cs
index 41c0c5115..75dde5598 100644
--- a/Emby.Server.Implementations/EntryPoints/RecordingNotifier.cs
+++ b/Emby.Server.Implementations/EntryPoints/RecordingNotifier.cs
@@ -4,6 +4,7 @@ using System;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
+using Jellyfin.Data.Enums;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.LiveTv;
using MediaBrowser.Controller.Plugins;
@@ -64,7 +65,7 @@ namespace Emby.Server.Implementations.EntryPoints
private async void SendMessage(string name, TimerEventInfo info)
{
- var users = _userManager.Users.Where(i => i.Policy.EnableLiveTvAccess).Select(i => i.Id).ToList();
+ var users = _userManager.Users.Where(i => i.HasPermission(PermissionKind.EnableLiveTvAccess)).Select(i => i.Id).ToList();
try
{
diff --git a/Emby.Server.Implementations/EntryPoints/RefreshUsersMetadata.cs b/Emby.Server.Implementations/EntryPoints/RefreshUsersMetadata.cs
deleted file mode 100644
index 54f4b67e6..000000000
--- a/Emby.Server.Implementations/EntryPoints/RefreshUsersMetadata.cs
+++ /dev/null
@@ -1,77 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Threading;
-using System.Threading.Tasks;
-using MediaBrowser.Controller.Library;
-using MediaBrowser.Controller.Providers;
-using MediaBrowser.Model.IO;
-using MediaBrowser.Model.Tasks;
-
-namespace Emby.Server.Implementations.EntryPoints
-{
- /// <summary>
- /// Class RefreshUsersMetadata.
- /// </summary>
- public class RefreshUsersMetadata : IScheduledTask, IConfigurableScheduledTask
- {
- /// <summary>
- /// The user manager.
- /// </summary>
- private readonly IUserManager _userManager;
- private readonly IFileSystem _fileSystem;
-
- /// <summary>
- /// Initializes a new instance of the <see cref="RefreshUsersMetadata" /> class.
- /// </summary>
- public RefreshUsersMetadata(IUserManager userManager, IFileSystem fileSystem)
- {
- _userManager = userManager;
- _fileSystem = fileSystem;
- }
-
- /// <inheritdoc />
- public string Name => "Refresh Users";
-
- /// <inheritdoc />
- public string Key => "RefreshUsers";
-
- /// <inheritdoc />
- public string Description => "Refresh user infos";
-
- /// <inheritdoc />
- public string Category => "Library";
-
- /// <inheritdoc />
- public bool IsHidden => true;
-
- /// <inheritdoc />
- public bool IsEnabled => true;
-
- /// <inheritdoc />
- public bool IsLogged => true;
-
- /// <inheritdoc />
- public async Task Execute(CancellationToken cancellationToken, IProgress<double> progress)
- {
- foreach (var user in _userManager.Users)
- {
- cancellationToken.ThrowIfCancellationRequested();
-
- await user.RefreshMetadata(new MetadataRefreshOptions(new DirectoryService(_fileSystem)), cancellationToken).ConfigureAwait(false);
- }
- }
-
- /// <inheritdoc />
- public IEnumerable<TaskTriggerInfo> GetDefaultTriggers()
- {
- return new[]
- {
- new TaskTriggerInfo
- {
- IntervalTicks = TimeSpan.FromDays(1).Ticks,
- Type = TaskTriggerInfo.TriggerInterval
- }
- };
- }
- }
-}
diff --git a/Emby.Server.Implementations/EntryPoints/ServerEventNotifier.cs b/Emby.Server.Implementations/EntryPoints/ServerEventNotifier.cs
index e1dbb663b..436d723f0 100644
--- a/Emby.Server.Implementations/EntryPoints/ServerEventNotifier.cs
+++ b/Emby.Server.Implementations/EntryPoints/ServerEventNotifier.cs
@@ -3,10 +3,10 @@ using System.Collections.Generic;
using System.Globalization;
using System.Threading;
using System.Threading.Tasks;
+using Jellyfin.Data.Entities;
using MediaBrowser.Common.Plugins;
using MediaBrowser.Common.Updates;
using MediaBrowser.Controller;
-using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Plugins;
using MediaBrowser.Controller.Session;
@@ -67,10 +67,8 @@ namespace Emby.Server.Implementations.EntryPoints
/// <inheritdoc />
public Task RunAsync()
{
- _userManager.UserDeleted += OnUserDeleted;
- _userManager.UserUpdated += OnUserUpdated;
- _userManager.UserPolicyUpdated += OnUserPolicyUpdated;
- _userManager.UserConfigurationUpdated += OnUserConfigurationUpdated;
+ _userManager.OnUserDeleted += OnUserDeleted;
+ _userManager.OnUserUpdated += OnUserUpdated;
_appHost.HasPendingRestartChanged += OnHasPendingRestartChanged;
@@ -152,20 +150,6 @@ namespace Emby.Server.Implementations.EntryPoints
SendMessageToUserSession(e.Argument, "UserDeleted", e.Argument.Id.ToString("N", CultureInfo.InvariantCulture));
}
- private void OnUserPolicyUpdated(object sender, GenericEventArgs<User> e)
- {
- var dto = _userManager.GetUserDto(e.Argument);
-
- SendMessageToUserSession(e.Argument, "UserPolicyUpdated", dto);
- }
-
- private void OnUserConfigurationUpdated(object sender, GenericEventArgs<User> e)
- {
- var dto = _userManager.GetUserDto(e.Argument);
-
- SendMessageToUserSession(e.Argument, "UserConfigurationUpdated", dto);
- }
-
private async void SendMessageToAdminSessions<T>(string name, T data)
{
try
@@ -209,10 +193,9 @@ namespace Emby.Server.Implementations.EntryPoints
{
if (dispose)
{
- _userManager.UserDeleted -= OnUserDeleted;
- _userManager.UserUpdated -= OnUserUpdated;
- _userManager.UserPolicyUpdated -= OnUserPolicyUpdated;
- _userManager.UserConfigurationUpdated -= OnUserConfigurationUpdated;
+ _userManager.OnUserDeleted -= OnUserDeleted;
+ _userManager.OnUserUpdated -= OnUserUpdated;
+
_installationManager.PluginUninstalled -= OnPluginUninstalled;
_installationManager.PackageInstalling -= OnPackageInstalling;
diff --git a/Emby.Server.Implementations/HttpServer/Security/AuthService.cs b/Emby.Server.Implementations/HttpServer/Security/AuthService.cs
index 256b24924..ad7b76d4f 100644
--- a/Emby.Server.Implementations/HttpServer/Security/AuthService.cs
+++ b/Emby.Server.Implementations/HttpServer/Security/AuthService.cs
@@ -4,6 +4,7 @@ using System;
using System.Linq;
using System.Security.Authentication;
using Emby.Server.Implementations.SocketSharp;
+using Jellyfin.Data.Enums;
using MediaBrowser.Common.Net;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Entities;
@@ -43,14 +44,14 @@ namespace Emby.Server.Implementations.HttpServer.Security
ValidateUser(request, authAttribtues);
}
- public User Authenticate(HttpRequest request, IAuthenticationAttributes authAttributes)
+ public Jellyfin.Data.Entities.User Authenticate(HttpRequest request, IAuthenticationAttributes authAttributes)
{
var req = new WebSocketSharpRequest(request, null, request.Path, _logger);
var user = ValidateUser(req, authAttributes);
return user;
}
- private User ValidateUser(IRequest request, IAuthenticationAttributes authAttribtues)
+ private Jellyfin.Data.Entities.User ValidateUser(IRequest request, IAuthenticationAttributes authAttribtues)
{
// This code is executed before the service
var auth = _authorizationContext.GetAuthorizationInfo(request);
@@ -90,7 +91,8 @@ namespace Emby.Server.Implementations.HttpServer.Security
!string.IsNullOrEmpty(auth.Client) &&
!string.IsNullOrEmpty(auth.Device))
{
- _sessionManager.LogSessionActivity(auth.Client,
+ _sessionManager.LogSessionActivity(
+ auth.Client,
auth.Version,
auth.DeviceId,
auth.Device,
@@ -102,22 +104,22 @@ namespace Emby.Server.Implementations.HttpServer.Security
}
private void ValidateUserAccess(
- User user,
+ Jellyfin.Data.Entities.User user,
IRequest request,
IAuthenticationAttributes authAttribtues,
AuthorizationInfo auth)
{
- if (user.Policy.IsDisabled)
+ if (user.HasPermission(PermissionKind.IsDisabled))
{
throw new SecurityException("User account has been disabled.");
}
- if (!user.Policy.EnableRemoteAccess && !_networkManager.IsInLocalNetwork(request.RemoteIp))
+ if (!user.HasPermission(PermissionKind.EnableRemoteAccess) && !_networkManager.IsInLocalNetwork(request.RemoteIp))
{
throw new SecurityException("User account has been disabled.");
}
- if (!user.Policy.IsAdministrator
+ if (!user.HasPermission(PermissionKind.IsAdministrator)
&& !authAttribtues.EscapeParentalControl
&& !user.IsParentalScheduleAllowed())
{
@@ -176,11 +178,11 @@ namespace Emby.Server.Implementations.HttpServer.Security
return false;
}
- private static void ValidateRoles(string[] roles, User user)
+ private static void ValidateRoles(string[] roles, Jellyfin.Data.Entities.User user)
{
if (roles.Contains("admin", StringComparer.OrdinalIgnoreCase))
{
- if (user == null || !user.Policy.IsAdministrator)
+ if (user == null || !user.HasPermission(PermissionKind.IsAdministrator))
{
throw new SecurityException("User does not have admin access.");
}
@@ -188,7 +190,7 @@ namespace Emby.Server.Implementations.HttpServer.Security
if (roles.Contains("delete", StringComparer.OrdinalIgnoreCase))
{
- if (user == null || !user.Policy.EnableContentDeletion)
+ if (user == null || !user.HasPermission(PermissionKind.EnableContentDeletion))
{
throw new SecurityException("User does not have delete access.");
}
@@ -196,7 +198,7 @@ namespace Emby.Server.Implementations.HttpServer.Security
if (roles.Contains("download", StringComparer.OrdinalIgnoreCase))
{
- if (user == null || !user.Policy.EnableContentDownloading)
+ if (user == null || !user.HasPermission(PermissionKind.EnableContentDownloading))
{
throw new SecurityException("User does not have download access.");
}
diff --git a/Emby.Server.Implementations/HttpServer/Security/AuthorizationContext.cs b/Emby.Server.Implementations/HttpServer/Security/AuthorizationContext.cs
index 129faeaab..9558cb4c6 100644
--- a/Emby.Server.Implementations/HttpServer/Security/AuthorizationContext.cs
+++ b/Emby.Server.Implementations/HttpServer/Security/AuthorizationContext.cs
@@ -149,9 +149,9 @@ namespace Emby.Server.Implementations.HttpServer.Security
{
info.User = _userManager.GetUserById(tokenInfo.UserId);
- if (info.User != null && !string.Equals(info.User.Name, tokenInfo.UserName, StringComparison.OrdinalIgnoreCase))
+ if (info.User != null && !string.Equals(info.User.Username, tokenInfo.UserName, StringComparison.OrdinalIgnoreCase))
{
- tokenInfo.UserName = info.User.Name;
+ tokenInfo.UserName = info.User.Username;
updateToken = true;
}
}
diff --git a/Emby.Server.Implementations/HttpServer/Security/SessionContext.cs b/Emby.Server.Implementations/HttpServer/Security/SessionContext.cs
index 166952c64..3f8a64f99 100644
--- a/Emby.Server.Implementations/HttpServer/Security/SessionContext.cs
+++ b/Emby.Server.Implementations/HttpServer/Security/SessionContext.cs
@@ -42,14 +42,14 @@ namespace Emby.Server.Implementations.HttpServer.Security
return GetSession((IRequest)requestContext);
}
- public User GetUser(IRequest requestContext)
+ public Jellyfin.Data.Entities.User GetUser(IRequest requestContext)
{
var session = GetSession(requestContext);
return session == null || session.UserId.Equals(Guid.Empty) ? null : _userManager.GetUserById(session.UserId);
}
- public User GetUser(object requestContext)
+ public Jellyfin.Data.Entities.User GetUser(object requestContext)
{
return GetUser((IRequest)requestContext);
}
diff --git a/Emby.Server.Implementations/Library/DefaultAuthenticationProvider.cs b/Emby.Server.Implementations/Library/DefaultAuthenticationProvider.cs
deleted file mode 100644
index 52c8facc3..000000000
--- a/Emby.Server.Implementations/Library/DefaultAuthenticationProvider.cs
+++ /dev/null
@@ -1,177 +0,0 @@
-using System;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
-using MediaBrowser.Common;
-using MediaBrowser.Common.Cryptography;
-using MediaBrowser.Controller.Authentication;
-using MediaBrowser.Controller.Entities;
-using MediaBrowser.Model.Cryptography;
-
-namespace Emby.Server.Implementations.Library
-{
- /// <summary>
- /// The default authentication provider.
- /// </summary>
- public class DefaultAuthenticationProvider : IAuthenticationProvider, IRequiresResolvedUser
- {
- private readonly ICryptoProvider _cryptographyProvider;
-
- /// <summary>
- /// Initializes a new instance of the <see cref="DefaultAuthenticationProvider"/> class.
- /// </summary>
- /// <param name="cryptographyProvider">The cryptography provider.</param>
- public DefaultAuthenticationProvider(ICryptoProvider cryptographyProvider)
- {
- _cryptographyProvider = cryptographyProvider;
- }
-
- /// <inheritdoc />
- public string Name => "Default";
-
- /// <inheritdoc />
- public bool IsEnabled => true;
-
- /// <inheritdoc />
- // This is dumb and an artifact of the backwards way auth providers were designed.
- // This version of authenticate was never meant to be called, but needs to be here for interface compat
- // Only the providers that don't provide local user support use this
- public Task<ProviderAuthenticationResult> Authenticate(string username, string password)
- {
- throw new NotImplementedException();
- }
-
- /// <inheritdoc />
- // This is the version that we need to use for local users. Because reasons.
- public Task<ProviderAuthenticationResult> Authenticate(string username, string password, User resolvedUser)
- {
- if (resolvedUser == null)
- {
- throw new AuthenticationException($"Specified user does not exist.");
- }
-
- bool success = false;
-
- // As long as jellyfin supports passwordless users, we need this little block here to accommodate
- if (!HasPassword(resolvedUser) && string.IsNullOrEmpty(password))
- {
- return Task.FromResult(new ProviderAuthenticationResult
- {
- Username = username
- });
- }
-
- byte[] passwordbytes = Encoding.UTF8.GetBytes(password);
-
- PasswordHash readyHash = PasswordHash.Parse(resolvedUser.Password);
- if (_cryptographyProvider.GetSupportedHashMethods().Contains(readyHash.Id)
- || _cryptographyProvider.DefaultHashMethod == readyHash.Id)
- {
- byte[] calculatedHash = _cryptographyProvider.ComputeHash(
- readyHash.Id,
- passwordbytes,
- readyHash.Salt.ToArray());
-
- if (readyHash.Hash.SequenceEqual(calculatedHash))
- {
- success = true;
- }
- }
- else
- {
- throw new AuthenticationException($"Requested crypto method not available in provider: {readyHash.Id}");
- }
-
- if (!success)
- {
- throw new AuthenticationException("Invalid username or password");
- }
-
- return Task.FromResult(new ProviderAuthenticationResult
- {
- Username = username
- });
- }
-
- /// <inheritdoc />
- public bool HasPassword(User user)
- => !string.IsNullOrEmpty(user.Password);
-
- /// <inheritdoc />
- public Task ChangePassword(User user, string newPassword)
- {
- if (string.IsNullOrEmpty(newPassword))
- {
- user.Password = null;
- return Task.CompletedTask;
- }
-
- PasswordHash newPasswordHash = _cryptographyProvider.CreatePasswordHash(newPassword);
- user.Password = newPasswordHash.ToString();
-
- return Task.CompletedTask;
- }
-
- /// <inheritdoc />
- public void ChangeEasyPassword(User user, string newPassword, string newPasswordHash)
- {
- if (newPassword != null)
- {
- newPasswordHash = _cryptographyProvider.CreatePasswordHash(newPassword).ToString();
- }
-
- if (string.IsNullOrWhiteSpace(newPasswordHash))
- {
- throw new ArgumentNullException(nameof(newPasswordHash));
- }
-
- user.EasyPassword = newPasswordHash;
- }
-
- /// <inheritdoc />
- public string GetEasyPasswordHash(User user)
- {
- return string.IsNullOrEmpty(user.EasyPassword)
- ? null
- : Hex.Encode(PasswordHash.Parse(user.EasyPassword).Hash);
- }
-
- /// <summary>
- /// Gets the hashed string.
- /// </summary>
- public string GetHashedString(User user, string str)
- {
- if (string.IsNullOrEmpty(user.Password))
- {
- return _cryptographyProvider.CreatePasswordHash(str).ToString();
- }
-
- // TODO: make use of iterations parameter?
- PasswordHash passwordHash = PasswordHash.Parse(user.Password);
- var salt = passwordHash.Salt.ToArray();
- return new PasswordHash(
- passwordHash.Id,
- _cryptographyProvider.ComputeHash(
- passwordHash.Id,
- Encoding.UTF8.GetBytes(str),
- salt),
- salt,
- passwordHash.Parameters.ToDictionary(x => x.Key, y => y.Value)).ToString();
- }
-
- public ReadOnlySpan<byte> GetHashed(User user, string str)
- {
- if (string.IsNullOrEmpty(user.Password))
- {
- return _cryptographyProvider.CreatePasswordHash(str).Hash;
- }
-
- // TODO: make use of iterations parameter?
- PasswordHash passwordHash = PasswordHash.Parse(user.Password);
- return _cryptographyProvider.ComputeHash(
- passwordHash.Id,
- Encoding.UTF8.GetBytes(str),
- passwordHash.Salt.ToArray());
- }
- }
-}
diff --git a/Emby.Server.Implementations/Library/DefaultPasswordResetProvider.cs b/Emby.Server.Implementations/Library/DefaultPasswordResetProvider.cs
deleted file mode 100644
index 6c6fbd86f..000000000
--- a/Emby.Server.Implementations/Library/DefaultPasswordResetProvider.cs
+++ /dev/null
@@ -1,140 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.IO;
-using System.Security.Cryptography;
-using System.Threading.Tasks;
-using MediaBrowser.Common.Extensions;
-using MediaBrowser.Controller.Authentication;
-using MediaBrowser.Controller.Configuration;
-using MediaBrowser.Controller.Library;
-using MediaBrowser.Model.Serialization;
-using MediaBrowser.Model.Users;
-
-namespace Emby.Server.Implementations.Library
-{
- /// <summary>
- /// The default password reset provider.
- /// </summary>
- public class DefaultPasswordResetProvider : IPasswordResetProvider
- {
- private const string BaseResetFileName = "passwordreset";
-
- private readonly IJsonSerializer _jsonSerializer;
- private readonly IUserManager _userManager;
-
- private readonly string _passwordResetFileBase;
- private readonly string _passwordResetFileBaseDir;
-
- /// <summary>
- /// Initializes a new instance of the <see cref="DefaultPasswordResetProvider"/> class.
- /// </summary>
- /// <param name="configurationManager">The configuration manager.</param>
- /// <param name="jsonSerializer">The JSON serializer.</param>
- /// <param name="userManager">The user manager.</param>
- public DefaultPasswordResetProvider(
- IServerConfigurationManager configurationManager,
- IJsonSerializer jsonSerializer,
- IUserManager userManager)
- {
- _passwordResetFileBaseDir = configurationManager.ApplicationPaths.ProgramDataPath;
- _passwordResetFileBase = Path.Combine(_passwordResetFileBaseDir, BaseResetFileName);
- _jsonSerializer = jsonSerializer;
- _userManager = userManager;
- }
-
- /// <inheritdoc />
- public string Name => "Default Password Reset Provider";
-
- /// <inheritdoc />
- public bool IsEnabled => true;
-
- /// <inheritdoc />
- public async Task<PinRedeemResult> RedeemPasswordResetPin(string pin)
- {
- SerializablePasswordReset spr;
- List<string> usersreset = new List<string>();
- foreach (var resetfile in Directory.EnumerateFiles(_passwordResetFileBaseDir, $"{BaseResetFileName}*"))
- {
- using (var str = File.OpenRead(resetfile))
- {
- spr = await _jsonSerializer.DeserializeFromStreamAsync<SerializablePasswordReset>(str).ConfigureAwait(false);
- }
-
- if (spr.ExpirationDate < DateTime.Now)
- {
- File.Delete(resetfile);
- }
- else if (string.Equals(
- spr.Pin.Replace("-", string.Empty, StringComparison.Ordinal),
- pin.Replace("-", string.Empty, StringComparison.Ordinal),
- StringComparison.InvariantCultureIgnoreCase))
- {
- var resetUser = _userManager.GetUserByName(spr.UserName);
- if (resetUser == null)
- {
- throw new ResourceNotFoundException($"User with a username of {spr.UserName} not found");
- }
-
- await _userManager.ChangePassword(resetUser, pin).ConfigureAwait(false);
- usersreset.Add(resetUser.Name);
- File.Delete(resetfile);
- }
- }
-
- if (usersreset.Count < 1)
- {
- throw new ResourceNotFoundException($"No Users found with a password reset request matching pin {pin}");
- }
- else
- {
- return new PinRedeemResult
- {
- Success = true,
- UsersReset = usersreset.ToArray()
- };
- }
- }
-
- /// <inheritdoc />
- public async Task<ForgotPasswordResult> StartForgotPasswordProcess(MediaBrowser.Controller.Entities.User user, bool isInNetwork)
- {
- string pin = string.Empty;
- using (var cryptoRandom = RandomNumberGenerator.Create())
- {
- byte[] bytes = new byte[4];
- cryptoRandom.GetBytes(bytes);
- pin = BitConverter.ToString(bytes);
- }
-
- DateTime expireTime = DateTime.Now.AddMinutes(30);
- string filePath = _passwordResetFileBase + user.InternalId + ".json";
- SerializablePasswordReset spr = new SerializablePasswordReset
- {
- ExpirationDate = expireTime,
- Pin = pin,
- PinFile = filePath,
- UserName = user.Name
- };
-
- using (FileStream fileStream = File.OpenWrite(filePath))
- {
- _jsonSerializer.SerializeToStream(spr, fileStream);
- await fileStream.FlushAsync().ConfigureAwait(false);
- }
-
- return new ForgotPasswordResult
- {
- Action = ForgotPasswordAction.PinCode,
- PinExpirationDate = expireTime,
- PinFile = filePath
- };
- }
-
- private class SerializablePasswordReset : PasswordPinCreationResult
- {
- public string Pin { get; set; }
-
- public string UserName { get; set; }
- }
- }
-}
diff --git a/Emby.Server.Implementations/Library/InvalidAuthProvider.cs b/Emby.Server.Implementations/Library/InvalidAuthProvider.cs
deleted file mode 100644
index dc61aacd7..000000000
--- a/Emby.Server.Implementations/Library/InvalidAuthProvider.cs
+++ /dev/null
@@ -1,54 +0,0 @@
-using System.Threading.Tasks;
-using MediaBrowser.Controller.Authentication;
-using MediaBrowser.Controller.Entities;
-
-namespace Emby.Server.Implementations.Library
-{
- /// <summary>
- /// An invalid authentication provider.
- /// </summary>
- public class InvalidAuthProvider : IAuthenticationProvider
- {
- /// <inheritdoc />
- public string Name => "InvalidOrMissingAuthenticationProvider";
-
- /// <inheritdoc />
- public bool IsEnabled => true;
-
- /// <inheritdoc />
- public Task<ProviderAuthenticationResult> Authenticate(string username, string password)
- {
- throw new AuthenticationException("User Account cannot login with this provider. The Normal provider for this user cannot be found");
- }
-
- /// <inheritdoc />
- public bool HasPassword(User user)
- {
- return true;
- }
-
- /// <inheritdoc />
- public Task ChangePassword(User user, string newPassword)
- {
- return Task.CompletedTask;
- }
-
- /// <inheritdoc />
- public void ChangeEasyPassword(User user, string newPassword, string newPasswordHash)
- {
- // Nothing here
- }
-
- /// <inheritdoc />
- public string GetPasswordHash(User user)
- {
- return string.Empty;
- }
-
- /// <inheritdoc />
- public string GetEasyPasswordHash(User user)
- {
- return string.Empty;
- }
- }
-}
diff --git a/Emby.Server.Implementations/Library/LibraryManager.cs b/Emby.Server.Implementations/Library/LibraryManager.cs
index 0b86b2db7..e7cd7512c 100644
--- a/Emby.Server.Implementations/Library/LibraryManager.cs
+++ b/Emby.Server.Implementations/Library/LibraryManager.cs
@@ -17,6 +17,7 @@ using Emby.Server.Implementations.Library.Resolvers;
using Emby.Server.Implementations.Library.Validators;
using Emby.Server.Implementations.Playlists;
using Emby.Server.Implementations.ScheduledTasks;
+using Jellyfin.Data.Enums;
using MediaBrowser.Common.Extensions;
using MediaBrowser.Common.Progress;
using MediaBrowser.Controller;
@@ -1470,7 +1471,7 @@ namespace Emby.Server.Implementations.Library
query.Parent = null;
}
- private void AddUserToQuery(InternalItemsQuery query, User user, bool allowExternalContent = true)
+ private void AddUserToQuery(InternalItemsQuery query, Jellyfin.Data.Entities.User user, bool allowExternalContent = true)
{
if (query.AncestorIds.Length == 0 &&
query.ParentId.Equals(Guid.Empty) &&
@@ -1491,7 +1492,7 @@ namespace Emby.Server.Implementations.Library
}
}
- private IEnumerable<Guid> GetTopParentIdsForQuery(BaseItem item, User user)
+ private IEnumerable<Guid> GetTopParentIdsForQuery(BaseItem item, Jellyfin.Data.Entities.User user)
{
if (item is UserView view)
{
@@ -1524,7 +1525,8 @@ namespace Emby.Server.Implementations.Library
}
// Handle grouping
- if (user != null && !string.IsNullOrEmpty(view.ViewType) && UserView.IsEligibleForGrouping(view.ViewType) && user.Configuration.GroupedFolders.Length > 0)
+ if (user != null && !string.IsNullOrEmpty(view.ViewType) && UserView.IsEligibleForGrouping(view.ViewType)
+ && user.GetPreference(PreferenceKind.GroupedFolders).Length > 0)
{
return GetUserRootFolder()
.GetChildren(user, true)
@@ -1557,7 +1559,7 @@ namespace Emby.Server.Implementations.Library
/// <param name="item">The item.</param>
/// <param name="user">The user.</param>
/// <returns>IEnumerable{System.String}.</returns>
- public async Task<IEnumerable<Video>> GetIntros(BaseItem item, User user)
+ public async Task<IEnumerable<Video>> GetIntros(BaseItem item, Jellyfin.Data.Entities.User user)
{
var tasks = IntroProviders
.OrderBy(i => i.GetType().Name.Contains("Default", StringComparison.OrdinalIgnoreCase) ? 1 : 0)
@@ -1579,7 +1581,7 @@ namespace Emby.Server.Implementations.Library
/// <param name="item">The item.</param>
/// <param name="user">The user.</param>
/// <returns>Task&lt;IEnumerable&lt;IntroInfo&gt;&gt;.</returns>
- private async Task<IEnumerable<IntroInfo>> GetIntros(IIntroProvider provider, BaseItem item, User user)
+ private async Task<IEnumerable<IntroInfo>> GetIntros(IIntroProvider provider, BaseItem item, Jellyfin.Data.Entities.User user)
{
try
{
@@ -1680,7 +1682,7 @@ namespace Emby.Server.Implementations.Library
/// <param name="sortBy">The sort by.</param>
/// <param name="sortOrder">The sort order.</param>
/// <returns>IEnumerable{BaseItem}.</returns>
- public IEnumerable<BaseItem> Sort(IEnumerable<BaseItem> items, User user, IEnumerable<string> sortBy, SortOrder sortOrder)
+ public IEnumerable<BaseItem> Sort(IEnumerable<BaseItem> items, Jellyfin.Data.Entities.User user, IEnumerable<string> sortBy, SortOrder sortOrder)
{
var isFirst = true;
@@ -1703,7 +1705,7 @@ namespace Emby.Server.Implementations.Library
return orderedItems ?? items;
}
- public IEnumerable<BaseItem> Sort(IEnumerable<BaseItem> items, User user, IEnumerable<ValueTuple<string, SortOrder>> orderByList)
+ public IEnumerable<BaseItem> Sort(IEnumerable<BaseItem> items, Jellyfin.Data.Entities.User user, IEnumerable<ValueTuple<string, SortOrder>> orderByList)
{
var isFirst = true;
@@ -1740,7 +1742,7 @@ namespace Emby.Server.Implementations.Library
/// <param name="name">The name.</param>
/// <param name="user">The user.</param>
/// <returns>IBaseItemComparer.</returns>
- private IBaseItemComparer GetComparer(string name, User user)
+ private IBaseItemComparer GetComparer(string name, Jellyfin.Data.Entities.User user)
{
var comparer = Comparers.FirstOrDefault(c => string.Equals(name, c.Name, StringComparison.OrdinalIgnoreCase));
@@ -2072,7 +2074,7 @@ namespace Emby.Server.Implementations.Library
private readonly TimeSpan _viewRefreshInterval = TimeSpan.FromHours(24);
public UserView GetNamedView(
- User user,
+ Jellyfin.Data.Entities.User user,
string name,
string viewType,
string sortName)
@@ -2125,7 +2127,7 @@ namespace Emby.Server.Implementations.Library
}
public UserView GetNamedView(
- User user,
+ Jellyfin.Data.Entities.User user,
string name,
Guid parentId,
string viewType,
diff --git a/Emby.Server.Implementations/Library/MediaSourceManager.cs b/Emby.Server.Implementations/Library/MediaSourceManager.cs
index 01fe98f3a..25af69058 100644
--- a/Emby.Server.Implementations/Library/MediaSourceManager.cs
+++ b/Emby.Server.Implementations/Library/MediaSourceManager.cs
@@ -7,6 +7,7 @@ using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
+using Jellyfin.Data.Enums;
using MediaBrowser.Common.Configuration;
using MediaBrowser.Common.Extensions;
using MediaBrowser.Controller.Entities;
@@ -14,7 +15,6 @@ using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.MediaEncoding;
using MediaBrowser.Controller.Persistence;
using MediaBrowser.Controller.Providers;
-using MediaBrowser.Model.Configuration;
using MediaBrowser.Model.Dlna;
using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Entities;
@@ -145,7 +145,7 @@ namespace Emby.Server.Implementations.Library
});
}
- public async Task<List<MediaSourceInfo>> GetPlaybackMediaSources(BaseItem item, User user, bool allowMediaProbe, bool enablePathSubstitution, CancellationToken cancellationToken)
+ public async Task<List<MediaSourceInfo>> GetPlaybackMediaSources(BaseItem item, Jellyfin.Data.Entities.User user, bool allowMediaProbe, bool enablePathSubstitution, CancellationToken cancellationToken)
{
var mediaSources = GetStaticMediaSources(item, enablePathSubstitution, user);
@@ -190,10 +190,7 @@ namespace Emby.Server.Implementations.Library
{
if (string.Equals(item.MediaType, MediaType.Audio, StringComparison.OrdinalIgnoreCase))
{
- if (!user.Policy.EnableAudioPlaybackTranscoding)
- {
- source.SupportsTranscoding = false;
- }
+ source.SupportsTranscoding = user.HasPermission(PermissionKind.EnableAudioPlaybackTranscoding);
}
}
}
@@ -312,7 +309,7 @@ namespace Emby.Server.Implementations.Library
return sources.FirstOrDefault(i => string.Equals(i.Id, mediaSourceId, StringComparison.OrdinalIgnoreCase));
}
- public List<MediaSourceInfo> GetStaticMediaSources(BaseItem item, bool enablePathSubstitution, User user = null)
+ public List<MediaSourceInfo> GetStaticMediaSources(BaseItem item, bool enablePathSubstitution, Jellyfin.Data.Entities.User user = null)
{
if (item == null)
{
@@ -350,9 +347,11 @@ namespace Emby.Server.Implementations.Library
return new string[] { language };
}
- private void SetDefaultSubtitleStreamIndex(MediaSourceInfo source, UserItemData userData, User user, bool allowRememberingSelection)
+ private void SetDefaultSubtitleStreamIndex(MediaSourceInfo source, UserItemData userData, Jellyfin.Data.Entities.User user, bool allowRememberingSelection)
{
- if (userData.SubtitleStreamIndex.HasValue && user.Configuration.RememberSubtitleSelections && user.Configuration.SubtitleMode != SubtitlePlaybackMode.None && allowRememberingSelection)
+ if (userData.SubtitleStreamIndex.HasValue
+ && user.RememberSubtitleSelections
+ && user.SubtitleMode != SubtitlePlaybackMode.None && allowRememberingSelection)
{
var index = userData.SubtitleStreamIndex.Value;
// Make sure the saved index is still valid
@@ -363,26 +362,27 @@ namespace Emby.Server.Implementations.Library
}
}
- var preferredSubs = string.IsNullOrEmpty(user.Configuration.SubtitleLanguagePreference)
- ? Array.Empty<string>() : NormalizeLanguage(user.Configuration.SubtitleLanguagePreference);
+
+ var preferredSubs = string.IsNullOrEmpty(user.SubtitleLanguagePreference)
+ ? Array.Empty<string>() : NormalizeLanguage(user.SubtitleLanguagePreference);
var defaultAudioIndex = source.DefaultAudioStreamIndex;
var audioLangage = defaultAudioIndex == null
? null
: source.MediaStreams.Where(i => i.Type == MediaStreamType.Audio && i.Index == defaultAudioIndex).Select(i => i.Language).FirstOrDefault();
- source.DefaultSubtitleStreamIndex = MediaStreamSelector.GetDefaultSubtitleStreamIndex(source.MediaStreams,
+ source.DefaultSubtitleStreamIndex = MediaStreamSelector.GetDefaultSubtitleStreamIndex(
+ source.MediaStreams,
preferredSubs,
- user.Configuration.SubtitleMode,
+ user.SubtitleMode,
audioLangage);
- MediaStreamSelector.SetSubtitleStreamScores(source.MediaStreams, preferredSubs,
- user.Configuration.SubtitleMode, audioLangage);
+ MediaStreamSelector.SetSubtitleStreamScores(source.MediaStreams, preferredSubs, user.SubtitleMode, audioLangage);
}
- private void SetDefaultAudioStreamIndex(MediaSourceInfo source, UserItemData userData, User user, bool allowRememberingSelection)
+ private void SetDefaultAudioStreamIndex(MediaSourceInfo source, UserItemData userData, Jellyfin.Data.Entities.User user, bool allowRememberingSelection)
{
- if (userData.AudioStreamIndex.HasValue && user.Configuration.RememberAudioSelections && allowRememberingSelection)
+ if (userData.AudioStreamIndex.HasValue && user.RememberAudioSelections && allowRememberingSelection)
{
var index = userData.AudioStreamIndex.Value;
// Make sure the saved index is still valid
@@ -393,14 +393,14 @@ namespace Emby.Server.Implementations.Library
}
}
- var preferredAudio = string.IsNullOrEmpty(user.Configuration.AudioLanguagePreference)
+ var preferredAudio = string.IsNullOrEmpty(user.AudioLanguagePreference)
? Array.Empty<string>()
- : NormalizeLanguage(user.Configuration.AudioLanguagePreference);
+ : NormalizeLanguage(user.AudioLanguagePreference);
- source.DefaultAudioStreamIndex = MediaStreamSelector.GetDefaultAudioStreamIndex(source.MediaStreams, preferredAudio, user.Configuration.PlayDefaultAudioTrack);
+ source.DefaultAudioStreamIndex = MediaStreamSelector.GetDefaultAudioStreamIndex(source.MediaStreams, preferredAudio, user.PlayDefaultAudioTrack);
}
- public void SetDefaultAudioAndSubtitleStreamIndexes(BaseItem item, MediaSourceInfo source, User user)
+ public void SetDefaultAudioAndSubtitleStreamIndexes(BaseItem item, MediaSourceInfo source, Jellyfin.Data.Entities.User user)
{
// Item would only be null if the app didn't supply ItemId as part of the live stream open request
var mediaType = item == null ? MediaType.Video : item.MediaType;
@@ -560,17 +560,14 @@ namespace Emby.Server.Implementations.Library
{
videoStream.BitRate = 30000000;
}
-
else if (width >= 1900)
{
videoStream.BitRate = 20000000;
}
-
else if (width >= 1200)
{
videoStream.BitRate = 8000000;
}
-
else if (width >= 700)
{
videoStream.BitRate = 2000000;
@@ -674,13 +671,14 @@ namespace Emby.Server.Implementations.Library
mediaSource.AnalyzeDurationMs = 3000;
}
- mediaInfo = await _mediaEncoder.GetMediaInfo(new MediaInfoRequest
+ mediaInfo = await _mediaEncoder.GetMediaInfo(
+ new MediaInfoRequest
{
MediaSource = mediaSource,
MediaType = isAudio ? DlnaProfileType.Audio : DlnaProfileType.Video,
ExtractChapters = false
-
- }, cancellationToken).ConfigureAwait(false);
+ },
+ cancellationToken).ConfigureAwait(false);
if (cacheFilePath != null)
{
diff --git a/Emby.Server.Implementations/Library/MediaStreamSelector.cs b/Emby.Server.Implementations/Library/MediaStreamSelector.cs
index e27145a1d..a177138b7 100644
--- a/Emby.Server.Implementations/Library/MediaStreamSelector.cs
+++ b/Emby.Server.Implementations/Library/MediaStreamSelector.cs
@@ -3,6 +3,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
+using Jellyfin.Data.Enums;
using MediaBrowser.Model.Configuration;
using MediaBrowser.Model.Entities;
diff --git a/Emby.Server.Implementations/Library/MusicManager.cs b/Emby.Server.Implementations/Library/MusicManager.cs
index 1ec578371..ad8c70f5e 100644
--- a/Emby.Server.Implementations/Library/MusicManager.cs
+++ b/Emby.Server.Implementations/Library/MusicManager.cs
@@ -22,7 +22,7 @@ namespace Emby.Server.Implementations.Library
_libraryManager = libraryManager;
}
- public List<BaseItem> GetInstantMixFromSong(Audio item, User user, DtoOptions dtoOptions)
+ public List<BaseItem> GetInstantMixFromSong(Audio item, Jellyfin.Data.Entities.User user, DtoOptions dtoOptions)
{
var list = new List<Audio>
{
@@ -32,17 +32,17 @@ namespace Emby.Server.Implementations.Library
return list.Concat(GetInstantMixFromGenres(item.Genres, user, dtoOptions)).ToList();
}
- public List<BaseItem> GetInstantMixFromArtist(MusicArtist item, User user, DtoOptions dtoOptions)
+ public List<BaseItem> GetInstantMixFromArtist(MusicArtist item, Jellyfin.Data.Entities.User user, DtoOptions dtoOptions)
{
return GetInstantMixFromGenres(item.Genres, user, dtoOptions);
}
- public List<BaseItem> GetInstantMixFromAlbum(MusicAlbum item, User user, DtoOptions dtoOptions)
+ public List<BaseItem> GetInstantMixFromAlbum(MusicAlbum item, Jellyfin.Data.Entities.User user, DtoOptions dtoOptions)
{
return GetInstantMixFromGenres(item.Genres, user, dtoOptions);
}
- public List<BaseItem> GetInstantMixFromFolder(Folder item, User user, DtoOptions dtoOptions)
+ public List<BaseItem> GetInstantMixFromFolder(Folder item, Jellyfin.Data.Entities.User user, DtoOptions dtoOptions)
{
var genres = item
.GetRecursiveChildren(user, new InternalItemsQuery(user)
@@ -58,12 +58,12 @@ namespace Emby.Server.Implementations.Library
return GetInstantMixFromGenres(genres, user, dtoOptions);
}
- public List<BaseItem> GetInstantMixFromPlaylist(Playlist item, User user, DtoOptions dtoOptions)
+ public List<BaseItem> GetInstantMixFromPlaylist(Playlist item, Jellyfin.Data.Entities.User user, DtoOptions dtoOptions)
{
return GetInstantMixFromGenres(item.Genres, user, dtoOptions);
}
- public List<BaseItem> GetInstantMixFromGenres(IEnumerable<string> genres, User user, DtoOptions dtoOptions)
+ public List<BaseItem> GetInstantMixFromGenres(IEnumerable<string> genres, Jellyfin.Data.Entities.User user, DtoOptions dtoOptions)
{
var genreIds = genres.DistinctNames().Select(i =>
{
@@ -75,13 +75,12 @@ namespace Emby.Server.Implementations.Library
{
return Guid.Empty;
}
-
}).Where(i => !i.Equals(Guid.Empty)).ToArray();
return GetInstantMixFromGenreIds(genreIds, user, dtoOptions);
}
- public List<BaseItem> GetInstantMixFromGenreIds(Guid[] genreIds, User user, DtoOptions dtoOptions)
+ public List<BaseItem> GetInstantMixFromGenreIds(Guid[] genreIds, Jellyfin.Data.Entities.User user, DtoOptions dtoOptions)
{
return _libraryManager.GetItemList(new InternalItemsQuery(user)
{
@@ -97,7 +96,7 @@ namespace Emby.Server.Implementations.Library
});
}
- public List<BaseItem> GetInstantMixFromItem(BaseItem item, User user, DtoOptions dtoOptions)
+ public List<BaseItem> GetInstantMixFromItem(BaseItem item, Jellyfin.Data.Entities.User user, DtoOptions dtoOptions)
{
var genre = item as MusicGenre;
if (genre != null)
@@ -105,32 +104,27 @@ namespace Emby.Server.Implementations.Library
return GetInstantMixFromGenreIds(new[] { item.Id }, user, dtoOptions);
}
- var playlist = item as Playlist;
- if (playlist != null)
+ if (item is Playlist playlist)
{
return GetInstantMixFromPlaylist(playlist, user, dtoOptions);
}
- var album = item as MusicAlbum;
- if (album != null)
+ if (item is MusicAlbum album)
{
return GetInstantMixFromAlbum(album, user, dtoOptions);
}
- var artist = item as MusicArtist;
- if (artist != null)
+ if (item is MusicArtist artist)
{
return GetInstantMixFromArtist(artist, user, dtoOptions);
}
- var song = item as Audio;
- if (song != null)
+ if (item is Audio song)
{
return GetInstantMixFromSong(song, user, dtoOptions);
}
- var folder = item as Folder;
- if (folder != null)
+ if (item is Folder folder)
{
return GetInstantMixFromFolder(folder, user, dtoOptions);
}
diff --git a/Emby.Server.Implementations/Library/SearchEngine.cs b/Emby.Server.Implementations/Library/SearchEngine.cs
index 59a77607d..026377ada 100644
--- a/Emby.Server.Implementations/Library/SearchEngine.cs
+++ b/Emby.Server.Implementations/Library/SearchEngine.cs
@@ -30,7 +30,7 @@ namespace Emby.Server.Implementations.Library
public QueryResult<SearchHintInfo> GetSearchHints(SearchQuery query)
{
- User user = null;
+ Jellyfin.Data.Entities.User user = null;
if (query.UserId.Equals(Guid.Empty))
{
@@ -76,7 +76,7 @@ namespace Emby.Server.Implementations.Library
/// <param name="user">The user.</param>
/// <returns>IEnumerable{SearchHintResult}.</returns>
/// <exception cref="ArgumentNullException">searchTerm</exception>
- private List<SearchHintInfo> GetSearchHints(SearchQuery query, User user)
+ private List<SearchHintInfo> GetSearchHints(SearchQuery query, Jellyfin.Data.Entities.User user)
{
var searchTerm = query.SearchTerm;
diff --git a/Emby.Server.Implementations/Library/UserDataManager.cs b/Emby.Server.Implementations/Library/UserDataManager.cs
index a9772a078..19c0d7eb7 100644
--- a/Emby.Server.Implementations/Library/UserDataManager.cs
+++ b/Emby.Server.Implementations/Library/UserDataManager.cs
@@ -50,7 +50,7 @@ namespace Emby.Server.Implementations.Library
SaveUserData(user, item, userData, reason, cancellationToken);
}
- public void SaveUserData(User user, BaseItem item, UserItemData userData, UserDataSaveReason reason, CancellationToken cancellationToken)
+ public void SaveUserData(Jellyfin.Data.Entities.User user, BaseItem item, UserItemData userData, UserDataSaveReason reason, CancellationToken cancellationToken)
{
if (userData == null)
{
@@ -119,7 +119,7 @@ namespace Emby.Server.Implementations.Library
return GetUserData(user, itemId, keys);
}
- public UserItemData GetUserData(User user, Guid itemId, List<string> keys)
+ public UserItemData GetUserData(Jellyfin.Data.Entities.User user, Guid itemId, List<string> keys)
{
var userId = user.InternalId;
@@ -157,7 +157,7 @@ namespace Emby.Server.Implementations.Library
return internalUserId.ToString(CultureInfo.InvariantCulture) + "-" + itemId.ToString("N", CultureInfo.InvariantCulture);
}
- public UserItemData GetUserData(User user, BaseItem item)
+ public UserItemData GetUserData(Jellyfin.Data.Entities.User user, BaseItem item)
{
return GetUserData(user, item.Id, item.GetUserDataKeys());
}
@@ -167,7 +167,7 @@ namespace Emby.Server.Implementations.Library
return GetUserData(userId, item.Id, item.GetUserDataKeys());
}
- public UserItemDataDto GetUserDataDto(BaseItem item, User user)
+ public UserItemDataDto GetUserDataDto(BaseItem item, Jellyfin.Data.Entities.User user)
{
var userData = GetUserData(user, item);
var dto = GetUserItemDataDto(userData);
@@ -176,7 +176,7 @@ namespace Emby.Server.Implementations.Library
return dto;
}
- public UserItemDataDto GetUserDataDto(BaseItem item, BaseItemDto itemDto, User user, DtoOptions options)
+ public UserItemDataDto GetUserDataDto(BaseItem item, BaseItemDto itemDto, Jellyfin.Data.Entities.User user, DtoOptions options)
{
var userData = GetUserData(user, item);
var dto = GetUserItemDataDto(userData);
diff --git a/Emby.Server.Implementations/Library/UserManager.cs b/Emby.Server.Implementations/Library/UserManager.cs
deleted file mode 100644
index b8feb5535..000000000
--- a/Emby.Server.Implementations/Library/UserManager.cs
+++ /dev/null
@@ -1,1132 +0,0 @@
-#pragma warning disable CS1591
-
-using System;
-using System.Collections.Concurrent;
-using System.Collections.Generic;
-using System.Globalization;
-using System.IO;
-using System.Linq;
-using System.Text;
-using System.Text.RegularExpressions;
-using System.Threading;
-using System.Threading.Tasks;
-using MediaBrowser.Common.Cryptography;
-using MediaBrowser.Common.Events;
-using MediaBrowser.Common.Net;
-using MediaBrowser.Controller;
-using MediaBrowser.Controller.Authentication;
-using MediaBrowser.Controller.Devices;
-using MediaBrowser.Controller.Drawing;
-using MediaBrowser.Controller.Dto;
-using MediaBrowser.Controller.Entities;
-using MediaBrowser.Controller.Library;
-using MediaBrowser.Controller.Net;
-using MediaBrowser.Controller.Persistence;
-using MediaBrowser.Controller.Plugins;
-using MediaBrowser.Controller.Providers;
-using MediaBrowser.Controller.Security;
-using MediaBrowser.Controller.Session;
-using MediaBrowser.Model.Configuration;
-using MediaBrowser.Model.Cryptography;
-using MediaBrowser.Model.Dto;
-using MediaBrowser.Model.Entities;
-using MediaBrowser.Model.Events;
-using MediaBrowser.Model.IO;
-using MediaBrowser.Model.Serialization;
-using MediaBrowser.Model.Users;
-using Microsoft.Extensions.Logging;
-
-namespace Emby.Server.Implementations.Library
-{
- /// <summary>
- /// Class UserManager.
- /// </summary>
- public class UserManager : IUserManager
- {
- private readonly object _policySyncLock = new object();
- private readonly object _configSyncLock = new object();
-
- private readonly ILogger _logger;
- private readonly IUserRepository _userRepository;
- private readonly IXmlSerializer _xmlSerializer;
- private readonly IJsonSerializer _jsonSerializer;
- private readonly INetworkManager _networkManager;
- private readonly IImageProcessor _imageProcessor;
- private readonly Lazy<IDtoService> _dtoServiceFactory;
- private readonly IServerApplicationHost _appHost;
- private readonly IFileSystem _fileSystem;
- private readonly ICryptoProvider _cryptoProvider;
-
- private ConcurrentDictionary<Guid, User> _users;
-
- private IAuthenticationProvider[] _authenticationProviders;
- private DefaultAuthenticationProvider _defaultAuthenticationProvider;
-
- private InvalidAuthProvider _invalidAuthProvider;
-
- private IPasswordResetProvider[] _passwordResetProviders;
- private DefaultPasswordResetProvider _defaultPasswordResetProvider;
-
- private IDtoService DtoService => _dtoServiceFactory.Value;
-
- public UserManager(
- ILogger<UserManager> logger,
- IUserRepository userRepository,
- IXmlSerializer xmlSerializer,
- INetworkManager networkManager,
- IImageProcessor imageProcessor,
- Lazy<IDtoService> dtoServiceFactory,
- IServerApplicationHost appHost,
- IJsonSerializer jsonSerializer,
- IFileSystem fileSystem,
- ICryptoProvider cryptoProvider)
- {
- _logger = logger;
- _userRepository = userRepository;
- _xmlSerializer = xmlSerializer;
- _networkManager = networkManager;
- _imageProcessor = imageProcessor;
- _dtoServiceFactory = dtoServiceFactory;
- _appHost = appHost;
- _jsonSerializer = jsonSerializer;
- _fileSystem = fileSystem;
- _cryptoProvider = cryptoProvider;
- _users = null;
- }
-
- public event EventHandler<GenericEventArgs<User>> UserPasswordChanged;
-
- /// <summary>
- /// Occurs when [user updated].
- /// </summary>
- public event EventHandler<GenericEventArgs<User>> UserUpdated;
-
- public event EventHandler<GenericEventArgs<User>> UserPolicyUpdated;
-
- public event EventHandler<GenericEventArgs<User>> UserConfigurationUpdated;
-
- public event EventHandler<GenericEventArgs<User>> UserLockedOut;
-
- public event EventHandler<GenericEventArgs<User>> UserCreated;
-
- /// <summary>
- /// Occurs when [user deleted].
- /// </summary>
- public event EventHandler<GenericEventArgs<User>> UserDeleted;
-
- /// <inheritdoc />
- public IEnumerable<User> Users => _users.Values;
-
- /// <inheritdoc />
- public IEnumerable<Guid> UsersIds => _users.Keys;
-
- /// <summary>
- /// Called when [user updated].
- /// </summary>
- /// <param name="user">The user.</param>
- private void OnUserUpdated(User user)
- {
- UserUpdated?.Invoke(this, new GenericEventArgs<User> { Argument = user });
- }
-
- /// <summary>
- /// Called when [user deleted].
- /// </summary>
- /// <param name="user">The user.</param>
- private void OnUserDeleted(User user)
- {
- UserDeleted?.Invoke(this, new GenericEventArgs<User> { Argument = user });
- }
-
- public NameIdPair[] GetAuthenticationProviders()
- {
- return _authenticationProviders
- .Where(i => i.IsEnabled)
- .OrderBy(i => i is DefaultAuthenticationProvider ? 0 : 1)
- .ThenBy(i => i.Name)
- .Select(i => new NameIdPair
- {
- Name = i.Name,
- Id = GetAuthenticationProviderId(i)
- })
- .ToArray();
- }
-
- public NameIdPair[] GetPasswordResetProviders()
- {
- return _passwordResetProviders
- .Where(i => i.IsEnabled)
- .OrderBy(i => i is DefaultPasswordResetProvider ? 0 : 1)
- .ThenBy(i => i.Name)
- .Select(i => new NameIdPair
- {
- Name = i.Name,
- Id = GetPasswordResetProviderId(i)
- })
- .ToArray();
- }
-
- public void AddParts(IEnumerable<IAuthenticationProvider> authenticationProviders, IEnumerable<IPasswordResetProvider> passwordResetProviders)
- {
- _authenticationProviders = authenticationProviders.ToArray();
-
- _defaultAuthenticationProvider = _authenticationProviders.OfType<DefaultAuthenticationProvider>().First();
-
- _invalidAuthProvider = _authenticationProviders.OfType<InvalidAuthProvider>().First();
-
- _passwordResetProviders = passwordResetProviders.ToArray();
-
- _defaultPasswordResetProvider = passwordResetProviders.OfType<DefaultPasswordResetProvider>().First();
- }
-
- /// <inheritdoc />
- public User GetUserById(Guid id)
- {
- if (id == Guid.Empty)
- {
- throw new ArgumentException("Guid can't be empty", nameof(id));
- }
-
- _users.TryGetValue(id, out User user);
- return user;
- }
-
- public User GetUserByName(string name)
- {
- if (string.IsNullOrWhiteSpace(name))
- {
- throw new ArgumentException("Invalid username", nameof(name));
- }
-
- return Users.FirstOrDefault(u => string.Equals(u.Name, name, StringComparison.OrdinalIgnoreCase));
- }
-
- public void Initialize()
- {
- LoadUsers();
-
- var users = Users;
-
- // If there are no local users with admin rights, make them all admins
- if (!users.Any(i => i.Policy.IsAdministrator))
- {
- foreach (var user in users)
- {
- user.Policy.IsAdministrator = true;
- UpdateUserPolicy(user, user.Policy, false);
- }
- }
- }
-
- public static bool IsValidUsername(string username)
- {
- // This is some regex that matches only on unicode "word" characters, as well as -, _ and @
- // In theory this will cut out most if not all 'control' characters which should help minimize any weirdness
- // Usernames can contain letters (a-z + whatever else unicode is cool with), numbers (0-9), at-signs (@), dashes (-), underscores (_), apostrophes ('), and periods (.)
- return Regex.IsMatch(username, @"^[\w\-'._@]*$");
- }
-
- private static bool IsValidUsernameCharacter(char i)
- => IsValidUsername(i.ToString(CultureInfo.InvariantCulture));
-
- public string MakeValidUsername(string username)
- {
- if (IsValidUsername(username))
- {
- return username;
- }
-
- // Usernames can contain letters (a-z), numbers (0-9), dashes (-), underscores (_), apostrophes ('), and periods (.)
- var builder = new StringBuilder();
-
- foreach (var c in username)
- {
- if (IsValidUsernameCharacter(c))
- {
- builder.Append(c);
- }
- }
-
- return builder.ToString();
- }
-
- public async Task<User> AuthenticateUser(
- string username,
- string password,
- string hashedPassword,
- string remoteEndPoint,
- bool isUserSession)
- {
- if (string.IsNullOrWhiteSpace(username))
- {
- _logger.LogInformation("Authentication request without username has been denied (IP: {IP}).", remoteEndPoint);
- throw new ArgumentNullException(nameof(username));
- }
-
- var user = Users.FirstOrDefault(i => string.Equals(username, i.Name, StringComparison.OrdinalIgnoreCase));
-
- var success = false;
- IAuthenticationProvider authenticationProvider = null;
-
- if (user != null)
- {
- var authResult = await AuthenticateLocalUser(username, password, hashedPassword, user, remoteEndPoint).ConfigureAwait(false);
- authenticationProvider = authResult.authenticationProvider;
- success = authResult.success;
- }
- else
- {
- // user is null
- var authResult = await AuthenticateLocalUser(username, password, hashedPassword, null, remoteEndPoint).ConfigureAwait(false);
- authenticationProvider = authResult.authenticationProvider;
- string updatedUsername = authResult.username;
- success = authResult.success;
-
- if (success
- && authenticationProvider != null
- && !(authenticationProvider is DefaultAuthenticationProvider))
- {
- // Trust the username returned by the authentication provider
- username = updatedUsername;
-
- // Search the database for the user again
- // the authentication provider might have created it
- user = Users
- .FirstOrDefault(i => string.Equals(username, i.Name, StringComparison.OrdinalIgnoreCase));
-
- if (authenticationProvider is IHasNewUserPolicy hasNewUserPolicy)
- {
- var policy = hasNewUserPolicy.GetNewUserPolicy();
- UpdateUserPolicy(user, policy, true);
- }
- }
- }
-
- if (success && user != null && authenticationProvider != null)
- {
- var providerId = GetAuthenticationProviderId(authenticationProvider);
-
- if (!string.Equals(providerId, user.Policy.AuthenticationProviderId, StringComparison.OrdinalIgnoreCase))
- {
- user.Policy.AuthenticationProviderId = providerId;
- UpdateUserPolicy(user, user.Policy, true);
- }
- }
-
- if (user == null)
- {
- _logger.LogInformation("Authentication request for {UserName} has been denied (IP: {IP}).", username, remoteEndPoint);
- throw new AuthenticationException("Invalid username or password entered.");
- }
-
- if (user.Policy.IsDisabled)
- {
- _logger.LogInformation("Authentication request for {UserName} has been denied because this account is currently disabled (IP: {IP}).", username, remoteEndPoint);
- throw new SecurityException($"The {user.Name} account is currently disabled. Please consult with your administrator.");
- }
-
- if (!user.Policy.EnableRemoteAccess && !_networkManager.IsInLocalNetwork(remoteEndPoint))
- {
- _logger.LogInformation("Authentication request for {UserName} forbidden: remote access disabled and user not in local network (IP: {IP}).", username, remoteEndPoint);
- throw new SecurityException("Forbidden.");
- }
-
- if (!user.IsParentalScheduleAllowed())
- {
- _logger.LogInformation("Authentication request for {UserName} is not allowed at this time due parental restrictions (IP: {IP}).", username, remoteEndPoint);
- throw new SecurityException("User is not allowed access at this time.");
- }
-
- // Update LastActivityDate and LastLoginDate, then save
- if (success)
- {
- if (isUserSession)
- {
- user.LastActivityDate = user.LastLoginDate = DateTime.UtcNow;
- UpdateUser(user);
- }
-
- ResetInvalidLoginAttemptCount(user);
- _logger.LogInformation("Authentication request for {UserName} has succeeded.", user.Name);
- }
- else
- {
- IncrementInvalidLoginAttemptCount(user);
- _logger.LogInformation("Authentication request for {UserName} has been denied (IP: {IP}).", user.Name, remoteEndPoint);
- }
-
- return success ? user : null;
- }
-
-#nullable enable
-
- private static string GetAuthenticationProviderId(IAuthenticationProvider provider)
- {
- return provider.GetType().FullName;
- }
-
- private static string GetPasswordResetProviderId(IPasswordResetProvider provider)
- {
- return provider.GetType().FullName;
- }
-
- private IAuthenticationProvider GetAuthenticationProvider(User user)
- {
- return GetAuthenticationProviders(user)[0];
- }
-
- private IPasswordResetProvider GetPasswordResetProvider(User user)
- {
- return GetPasswordResetProviders(user)[0];
- }
-
- private IAuthenticationProvider[] GetAuthenticationProviders(User? user)
- {
- var authenticationProviderId = user?.Policy.AuthenticationProviderId;
-
- var providers = _authenticationProviders.Where(i => i.IsEnabled).ToArray();
-
- if (!string.IsNullOrEmpty(authenticationProviderId))
- {
- providers = providers.Where(i => string.Equals(authenticationProviderId, GetAuthenticationProviderId(i), StringComparison.OrdinalIgnoreCase)).ToArray();
- }
-
- if (providers.Length == 0)
- {
- // Assign the user to the InvalidAuthProvider since no configured auth provider was valid/found
- _logger.LogWarning("User {UserName} was found with invalid/missing Authentication Provider {AuthenticationProviderId}. Assigning user to InvalidAuthProvider until this is corrected", user?.Name, user?.Policy.AuthenticationProviderId);
- providers = new IAuthenticationProvider[] { _invalidAuthProvider };
- }
-
- return providers;
- }
-
- private IPasswordResetProvider[] GetPasswordResetProviders(User? user)
- {
- var passwordResetProviderId = user?.Policy.PasswordResetProviderId;
-
- var providers = _passwordResetProviders.Where(i => i.IsEnabled).ToArray();
-
- if (!string.IsNullOrEmpty(passwordResetProviderId))
- {
- providers = providers.Where(i => string.Equals(passwordResetProviderId, GetPasswordResetProviderId(i), StringComparison.OrdinalIgnoreCase)).ToArray();
- }
-
- if (providers.Length == 0)
- {
- providers = new IPasswordResetProvider[] { _defaultPasswordResetProvider };
- }
-
- return providers;
- }
-
- private async Task<(string username, bool success)> AuthenticateWithProvider(
- IAuthenticationProvider provider,
- string username,
- string password,
- User? resolvedUser)
- {
- try
- {
- var authenticationResult = provider is IRequiresResolvedUser requiresResolvedUser
- ? await requiresResolvedUser.Authenticate(username, password, resolvedUser).ConfigureAwait(false)
- : await provider.Authenticate(username, password).ConfigureAwait(false);
-
- if (authenticationResult.Username != username)
- {
- _logger.LogDebug("Authentication provider provided updated username {1}", authenticationResult.Username);
- username = authenticationResult.Username;
- }
-
- return (username, true);
- }
- catch (AuthenticationException ex)
- {
- _logger.LogError(ex, "Error authenticating with provider {Provider}", provider.Name);
-
- return (username, false);
- }
- }
-
- private async Task<(IAuthenticationProvider? authenticationProvider, string username, bool success)> AuthenticateLocalUser(
- string username,
- string password,
- string hashedPassword,
- User? user,
- string remoteEndPoint)
- {
- bool success = false;
- IAuthenticationProvider? authenticationProvider = null;
-
- foreach (var provider in GetAuthenticationProviders(user))
- {
- var providerAuthResult = await AuthenticateWithProvider(provider, username, password, user).ConfigureAwait(false);
- var updatedUsername = providerAuthResult.username;
- success = providerAuthResult.success;
-
- if (success)
- {
- authenticationProvider = provider;
- username = updatedUsername;
- break;
- }
- }
-
- if (!success
- && _networkManager.IsInLocalNetwork(remoteEndPoint)
- && user?.Configuration.EnableLocalPassword == true
- && !string.IsNullOrEmpty(user.EasyPassword))
- {
- // Check easy password
- var passwordHash = PasswordHash.Parse(user.EasyPassword);
- var hash = _cryptoProvider.ComputeHash(
- passwordHash.Id,
- Encoding.UTF8.GetBytes(password),
- passwordHash.Salt.ToArray());
- success = passwordHash.Hash.SequenceEqual(hash);
- }
-
- return (authenticationProvider, username, success);
- }
-
- private void ResetInvalidLoginAttemptCount(User user)
- {
- user.Policy.InvalidLoginAttemptCount = 0;
- UpdateUserPolicy(user, user.Policy, false);
- }
-
- private void IncrementInvalidLoginAttemptCount(User user)
- {
- int invalidLogins = ++user.Policy.InvalidLoginAttemptCount;
- int maxInvalidLogins = user.Policy.LoginAttemptsBeforeLockout;
- if (maxInvalidLogins > 0
- && invalidLogins >= maxInvalidLogins)
- {
- user.Policy.IsDisabled = true;
- UserLockedOut?.Invoke(this, new GenericEventArgs<User>(user));
- _logger.LogWarning(
- "Disabling user {UserName} due to {Attempts} unsuccessful login attempts.",
- user.Name,
- invalidLogins);
- }
-
- UpdateUserPolicy(user, user.Policy, false);
- }
-
- /// <summary>
- /// Loads the users from the repository.
- /// </summary>
- private void LoadUsers()
- {
- var users = _userRepository.RetrieveAllUsers();
-
- // There always has to be at least one user.
- if (users.Count != 0)
- {
- _users = new ConcurrentDictionary<Guid, User>(
- users.Select(x => new KeyValuePair<Guid, User>(x.Id, x)));
- return;
- }
-
- var defaultName = Environment.UserName;
- if (string.IsNullOrWhiteSpace(defaultName))
- {
- defaultName = "MyJellyfinUser";
- }
-
- _logger.LogWarning("No users, creating one with username {UserName}", defaultName);
-
- var name = MakeValidUsername(defaultName);
-
- var user = InstantiateNewUser(name);
-
- user.DateLastSaved = DateTime.UtcNow;
-
- _userRepository.CreateUser(user);
-
- user.Policy.IsAdministrator = true;
- user.Policy.EnableContentDeletion = true;
- user.Policy.EnableRemoteControlOfOtherUsers = true;
- UpdateUserPolicy(user, user.Policy, false);
-
- _users = new ConcurrentDictionary<Guid, User>();
- _users[user.Id] = user;
- }
-
-#nullable restore
-
- public UserDto GetUserDto(User user, string remoteEndPoint = null)
- {
- if (user == null)
- {
- throw new ArgumentNullException(nameof(user));
- }
-
- bool hasConfiguredPassword = GetAuthenticationProvider(user).HasPassword(user);
- bool hasConfiguredEasyPassword = !string.IsNullOrEmpty(GetAuthenticationProvider(user).GetEasyPasswordHash(user));
-
- bool hasPassword = user.Configuration.EnableLocalPassword && !string.IsNullOrEmpty(remoteEndPoint) && _networkManager.IsInLocalNetwork(remoteEndPoint) ?
- hasConfiguredEasyPassword :
- hasConfiguredPassword;
-
- UserDto dto = new UserDto
- {
- Id = user.Id,
- Name = user.Name,
- HasPassword = hasPassword,
- HasConfiguredPassword = hasConfiguredPassword,
- HasConfiguredEasyPassword = hasConfiguredEasyPassword,
- LastActivityDate = user.LastActivityDate,
- LastLoginDate = user.LastLoginDate,
- Configuration = user.Configuration,
- ServerId = _appHost.SystemId,
- Policy = user.Policy
- };
-
- if (!hasPassword && _users.Count == 1)
- {
- dto.EnableAutoLogin = true;
- }
-
- ItemImageInfo image = user.GetImageInfo(ImageType.Primary, 0);
-
- if (image != null)
- {
- dto.PrimaryImageTag = GetImageCacheTag(user, image);
-
- try
- {
- DtoService.AttachPrimaryImageAspectRatio(dto, user);
- }
- catch (Exception ex)
- {
- // Have to use a catch-all unfortunately because some .net image methods throw plain Exceptions
- _logger.LogError(ex, "Error generating PrimaryImageAspectRatio for {User}", user.Name);
- }
- }
-
- return dto;
- }
-
- public PublicUserDto GetPublicUserDto(User user, string remoteEndPoint = null)
- {
- if (user == null)
- {
- throw new ArgumentNullException(nameof(user));
- }
-
- IAuthenticationProvider authenticationProvider = GetAuthenticationProvider(user);
- bool hasConfiguredPassword = authenticationProvider.HasPassword(user);
- bool hasConfiguredEasyPassword = !string.IsNullOrEmpty(authenticationProvider.GetEasyPasswordHash(user));
-
- bool hasPassword = user.Configuration.EnableLocalPassword &&
- !string.IsNullOrEmpty(remoteEndPoint) &&
- _networkManager.IsInLocalNetwork(remoteEndPoint) ? hasConfiguredEasyPassword : hasConfiguredPassword;
-
- PublicUserDto dto = new PublicUserDto
- {
- Name = user.Name,
- HasPassword = hasPassword,
- HasConfiguredPassword = hasConfiguredPassword,
- };
-
- return dto;
- }
-
- public UserDto GetOfflineUserDto(User user)
- {
- var dto = GetUserDto(user);
-
- dto.ServerName = _appHost.FriendlyName;
-
- return dto;
- }
-
- private string GetImageCacheTag(BaseItem item, ItemImageInfo image)
- {
- try
- {
- return _imageProcessor.GetImageCacheTag(item, image);
- }
- catch (Exception ex)
- {
- _logger.LogError(ex, "Error getting {ImageType} image info for {ImagePath}", image.Type, image.Path);
- return null;
- }
- }
-
- /// <summary>
- /// Refreshes metadata for each user
- /// </summary>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task.</returns>
- public async Task RefreshUsersMetadata(CancellationToken cancellationToken)
- {
- foreach (var user in Users)
- {
- await user.RefreshMetadata(new MetadataRefreshOptions(new DirectoryService(_fileSystem)), cancellationToken).ConfigureAwait(false);
- }
- }
-
- /// <summary>
- /// Renames the user.
- /// </summary>
- /// <param name="user">The user.</param>
- /// <param name="newName">The new name.</param>
- /// <returns>Task.</returns>
- /// <exception cref="ArgumentNullException">user</exception>
- /// <exception cref="ArgumentException"></exception>
- public async Task RenameUser(User user, string newName)
- {
- if (user == null)
- {
- throw new ArgumentNullException(nameof(user));
- }
-
- if (string.IsNullOrWhiteSpace(newName))
- {
- throw new ArgumentException("Invalid username", nameof(newName));
- }
-
- if (user.Name.Equals(newName, StringComparison.Ordinal))
- {
- throw new ArgumentException("The new and old names must be different.");
- }
-
- if (Users.Any(
- u => u.Id != user.Id && u.Name.Equals(newName, StringComparison.OrdinalIgnoreCase)))
- {
- throw new ArgumentException(string.Format(
- CultureInfo.InvariantCulture,
- "A user with the name '{0}' already exists.",
- newName));
- }
-
- await user.Rename(newName).ConfigureAwait(false);
-
- OnUserUpdated(user);
- }
-
- /// <summary>
- /// Updates the user.
- /// </summary>
- /// <param name="user">The user.</param>
- /// <exception cref="ArgumentNullException">user</exception>
- /// <exception cref="ArgumentException"></exception>
- public void UpdateUser(User user)
- {
- if (user == null)
- {
- throw new ArgumentNullException(nameof(user));
- }
-
- if (user.Id == Guid.Empty)
- {
- throw new ArgumentException("Id can't be empty.", nameof(user));
- }
-
- if (!_users.ContainsKey(user.Id))
- {
- throw new ArgumentException(
- string.Format(
- CultureInfo.InvariantCulture,
- "A user '{0}' with Id {1} does not exist.",
- user.Name,
- user.Id),
- nameof(user));
- }
-
- user.DateModified = DateTime.UtcNow;
- user.DateLastSaved = DateTime.UtcNow;
-
- _userRepository.UpdateUser(user);
-
- OnUserUpdated(user);
- }
-
- /// <summary>
- /// Creates the user.
- /// </summary>
- /// <param name="name">The name.</param>
- /// <returns>User.</returns>
- /// <exception cref="ArgumentNullException">name</exception>
- /// <exception cref="ArgumentException"></exception>
- public User CreateUser(string name)
- {
- if (string.IsNullOrWhiteSpace(name))
- {
- throw new ArgumentNullException(nameof(name));
- }
-
- if (!IsValidUsername(name))
- {
- throw new ArgumentException("Usernames can contain unicode symbols, numbers (0-9), dashes (-), underscores (_), apostrophes ('), and periods (.)");
- }
-
- if (Users.Any(u => u.Name.Equals(name, StringComparison.OrdinalIgnoreCase)))
- {
- throw new ArgumentException(string.Format("A user with the name '{0}' already exists.", name));
- }
-
- var user = InstantiateNewUser(name);
-
- _users[user.Id] = user;
-
- user.DateLastSaved = DateTime.UtcNow;
-
- _userRepository.CreateUser(user);
-
- EventHelper.QueueEventIfNotNull(UserCreated, this, new GenericEventArgs<User> { Argument = user }, _logger);
-
- return user;
- }
-
- /// <inheritdoc />
- /// <exception cref="ArgumentNullException">The <c>user</c> is <c>null</c>.</exception>
- /// <exception cref="ArgumentException">The <c>user</c> doesn't exist, or is the last administrator.</exception>
- /// <exception cref="InvalidOperationException">The <c>user</c> can't be deleted; there are no other users.</exception>
- public void DeleteUser(User user)
- {
- if (user == null)
- {
- throw new ArgumentNullException(nameof(user));
- }
-
- if (!_users.ContainsKey(user.Id))
- {
- throw new ArgumentException(string.Format(
- CultureInfo.InvariantCulture,
- "The user cannot be deleted because there is no user with the Name {0} and Id {1}.",
- user.Name,
- user.Id));
- }
-
- if (_users.Count == 1)
- {
- throw new InvalidOperationException(string.Format(
- CultureInfo.InvariantCulture,
- "The user '{0}' cannot be deleted because there must be at least one user in the system.",
- user.Name));
- }
-
- if (user.Policy.IsAdministrator
- && Users.Count(i => i.Policy.IsAdministrator) == 1)
- {
- throw new ArgumentException(
- string.Format(
- CultureInfo.InvariantCulture,
- "The user '{0}' cannot be deleted because there must be at least one admin user in the system.",
- user.Name),
- nameof(user));
- }
-
- var configPath = GetConfigurationFilePath(user);
-
- _userRepository.DeleteUser(user);
-
- // Delete user config dir
- lock (_configSyncLock)
- lock (_policySyncLock)
- {
- try
- {
- Directory.Delete(user.ConfigurationDirectoryPath, true);
- }
- catch (IOException ex)
- {
- _logger.LogError(ex, "Error deleting user config dir: {Path}", user.ConfigurationDirectoryPath);
- }
- }
-
- _users.TryRemove(user.Id, out _);
-
- OnUserDeleted(user);
- }
-
- /// <summary>
- /// Resets the password by clearing it.
- /// </summary>
- /// <returns>Task.</returns>
- public Task ResetPassword(User user)
- {
- return ChangePassword(user, string.Empty);
- }
-
- public void ResetEasyPassword(User user)
- {
- ChangeEasyPassword(user, string.Empty, null);
- }
-
- public async Task ChangePassword(User user, string newPassword)
- {
- if (user == null)
- {
- throw new ArgumentNullException(nameof(user));
- }
-
- await GetAuthenticationProvider(user).ChangePassword(user, newPassword).ConfigureAwait(false);
-
- UpdateUser(user);
-
- UserPasswordChanged?.Invoke(this, new GenericEventArgs<User>(user));
- }
-
- public void ChangeEasyPassword(User user, string newPassword, string newPasswordHash)
- {
- if (user == null)
- {
- throw new ArgumentNullException(nameof(user));
- }
-
- GetAuthenticationProvider(user).ChangeEasyPassword(user, newPassword, newPasswordHash);
-
- UpdateUser(user);
-
- UserPasswordChanged?.Invoke(this, new GenericEventArgs<User>(user));
- }
-
- /// <summary>
- /// Instantiates the new user.
- /// </summary>
- /// <param name="name">The name.</param>
- /// <returns>User.</returns>
- private static User InstantiateNewUser(string name)
- {
- return new User
- {
- Name = name,
- Id = Guid.NewGuid(),
- DateCreated = DateTime.UtcNow,
- DateModified = DateTime.UtcNow
- };
- }
-
- public async Task<ForgotPasswordResult> StartForgotPasswordProcess(string enteredUsername, bool isInNetwork)
- {
- var user = string.IsNullOrWhiteSpace(enteredUsername) ?
- null :
- GetUserByName(enteredUsername);
-
- var action = ForgotPasswordAction.InNetworkRequired;
-
- if (user != null && isInNetwork)
- {
- var passwordResetProvider = GetPasswordResetProvider(user);
- return await passwordResetProvider.StartForgotPasswordProcess(user, isInNetwork).ConfigureAwait(false);
- }
- else
- {
- return new ForgotPasswordResult
- {
- Action = action,
- PinFile = string.Empty
- };
- }
- }
-
- public async Task<PinRedeemResult> RedeemPasswordResetPin(string pin)
- {
- foreach (var provider in _passwordResetProviders)
- {
- var result = await provider.RedeemPasswordResetPin(pin).ConfigureAwait(false);
- if (result.Success)
- {
- return result;
- }
- }
-
- return new PinRedeemResult
- {
- Success = false,
- UsersReset = Array.Empty<string>()
- };
- }
-
- public UserPolicy GetUserPolicy(User user)
- {
- var path = GetPolicyFilePath(user);
- if (!File.Exists(path))
- {
- return GetDefaultPolicy();
- }
-
- try
- {
- lock (_policySyncLock)
- {
- return (UserPolicy)_xmlSerializer.DeserializeFromFile(typeof(UserPolicy), path);
- }
- }
- catch (Exception ex)
- {
- _logger.LogError(ex, "Error reading policy file: {Path}", path);
-
- return GetDefaultPolicy();
- }
- }
-
- private static UserPolicy GetDefaultPolicy()
- {
- return new UserPolicy
- {
- EnableContentDownloading = true,
- EnableSyncTranscoding = true
- };
- }
-
- public void UpdateUserPolicy(Guid userId, UserPolicy userPolicy)
- {
- var user = GetUserById(userId);
- UpdateUserPolicy(user, userPolicy, true);
- }
-
- private void UpdateUserPolicy(User user, UserPolicy userPolicy, bool fireEvent)
- {
- // The xml serializer will output differently if the type is not exact
- if (userPolicy.GetType() != typeof(UserPolicy))
- {
- var json = _jsonSerializer.SerializeToString(userPolicy);
- userPolicy = _jsonSerializer.DeserializeFromString<UserPolicy>(json);
- }
-
- var path = GetPolicyFilePath(user);
-
- Directory.CreateDirectory(Path.GetDirectoryName(path));
-
- lock (_policySyncLock)
- {
- _xmlSerializer.SerializeToFile(userPolicy, path);
- user.Policy = userPolicy;
- }
-
- if (fireEvent)
- {
- UserPolicyUpdated?.Invoke(this, new GenericEventArgs<User> { Argument = user });
- }
- }
-
- private static string GetPolicyFilePath(User user)
- {
- return Path.Combine(user.ConfigurationDirectoryPath, "policy.xml");
- }
-
- private static string GetConfigurationFilePath(User user)
- {
- return Path.Combine(user.ConfigurationDirectoryPath, "config.xml");
- }
-
- public UserConfiguration GetUserConfiguration(User user)
- {
- var path = GetConfigurationFilePath(user);
-
- if (!File.Exists(path))
- {
- return new UserConfiguration();
- }
-
- try
- {
- lock (_configSyncLock)
- {
- return (UserConfiguration)_xmlSerializer.DeserializeFromFile(typeof(UserConfiguration), path);
- }
- }
- catch (Exception ex)
- {
- _logger.LogError(ex, "Error reading policy file: {Path}", path);
-
- return new UserConfiguration();
- }
- }
-
- public void UpdateConfiguration(Guid userId, UserConfiguration config)
- {
- var user = GetUserById(userId);
- UpdateConfiguration(user, config);
- }
-
- public void UpdateConfiguration(User user, UserConfiguration config)
- {
- UpdateConfiguration(user, config, true);
- }
-
- private void UpdateConfiguration(User user, UserConfiguration config, bool fireEvent)
- {
- var path = GetConfigurationFilePath(user);
-
- // The xml serializer will output differently if the type is not exact
- if (config.GetType() != typeof(UserConfiguration))
- {
- var json = _jsonSerializer.SerializeToString(config);
- config = _jsonSerializer.DeserializeFromString<UserConfiguration>(json);
- }
-
- Directory.CreateDirectory(Path.GetDirectoryName(path));
-
- lock (_configSyncLock)
- {
- _xmlSerializer.SerializeToFile(config, path);
- user.Configuration = config;
- }
-
- if (fireEvent)
- {
- UserConfigurationUpdated?.Invoke(this, new GenericEventArgs<User> { Argument = user });
- }
- }
- }
-
- public class DeviceAccessEntryPoint : IServerEntryPoint
- {
- private IUserManager _userManager;
- private IAuthenticationRepository _authRepo;
- private IDeviceManager _deviceManager;
- private ISessionManager _sessionManager;
-
- public DeviceAccessEntryPoint(IUserManager userManager, IAuthenticationRepository authRepo, IDeviceManager deviceManager, ISessionManager sessionManager)
- {
- _userManager = userManager;
- _authRepo = authRepo;
- _deviceManager = deviceManager;
- _sessionManager = sessionManager;
- }
-
- public Task RunAsync()
- {
- _userManager.UserPolicyUpdated += _userManager_UserPolicyUpdated;
-
- return Task.CompletedTask;
- }
-
- private void _userManager_UserPolicyUpdated(object sender, GenericEventArgs<User> e)
- {
- var user = e.Argument;
- if (!user.Policy.EnableAllDevices)
- {
- UpdateDeviceAccess(user);
- }
- }
-
- private void UpdateDeviceAccess(User user)
- {
- var existing = _authRepo.Get(new AuthenticationInfoQuery
- {
- UserId = user.Id
-
- }).Items;
-
- foreach (var authInfo in existing)
- {
- if (!string.IsNullOrEmpty(authInfo.DeviceId) && !_deviceManager.CanAccessDevice(user, authInfo.DeviceId))
- {
- _sessionManager.Logout(authInfo);
- }
- }
- }
-
- public void Dispose()
- {
-
- }
- }
-}
diff --git a/Emby.Server.Implementations/Library/UserViewManager.cs b/Emby.Server.Implementations/Library/UserViewManager.cs
index 322819b05..8263efdc4 100644
--- a/Emby.Server.Implementations/Library/UserViewManager.cs
+++ b/Emby.Server.Implementations/Library/UserViewManager.cs
@@ -5,6 +5,7 @@ using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Threading;
+using Jellyfin.Data.Enums;
using MediaBrowser.Controller.Channels;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Dto;
@@ -125,12 +126,12 @@ namespace Emby.Server.Implementations.Library
if (!query.IncludeHidden)
{
- list = list.Where(i => !user.Configuration.MyMediaExcludes.Contains(i.Id.ToString("N", CultureInfo.InvariantCulture))).ToList();
+ list = list.Where(i => !user.GetPreference(PreferenceKind.MyMediaExcludes).Contains(i.Id.ToString("N", CultureInfo.InvariantCulture))).ToList();
}
var sorted = _libraryManager.Sort(list, user, new[] { ItemSortBy.SortName }, SortOrder.Ascending).ToList();
- var orders = user.Configuration.OrderedViews.ToList();
+ var orders = user.GetPreference(PreferenceKind.OrderedViews).ToList();
return list
.OrderBy(i =>
@@ -165,7 +166,13 @@ namespace Emby.Server.Implementations.Library
return GetUserSubViewWithName(name, parentId, type, sortName);
}
- private Folder GetUserView(List<ICollectionFolder> parents, string viewType, string localizationKey, string sortName, User user, string[] presetViews)
+ private Folder GetUserView(
+ List<ICollectionFolder> parents,
+ string viewType,
+ string localizationKey,
+ string sortName,
+ Jellyfin.Data.Entities.User user,
+ string[] presetViews)
{
if (parents.Count == 1 && parents.All(i => string.Equals(i.CollectionType, viewType, StringComparison.OrdinalIgnoreCase)))
{
@@ -226,7 +233,7 @@ namespace Emby.Server.Implementations.Library
return list;
}
- private IReadOnlyList<BaseItem> GetItemsForLatestItems(User user, LatestItemsQuery request, DtoOptions options)
+ private IReadOnlyList<BaseItem> GetItemsForLatestItems(Jellyfin.Data.Entities.User user, LatestItemsQuery request, DtoOptions options)
{
var parentId = request.ParentId;
@@ -270,7 +277,8 @@ namespace Emby.Server.Implementations.Library
{
parents = _libraryManager.GetUserRootFolder().GetChildren(user, true)
.Where(i => i is Folder)
- .Where(i => !user.Configuration.LatestItemsExcludes.Contains(i.Id.ToString("N", CultureInfo.InvariantCulture)))
+ .Where(i => !user.GetPreference(PreferenceKind.LatestItemExcludes)
+ .Contains(i.Id.ToString("N", CultureInfo.InvariantCulture)))
.ToList();
}
@@ -331,12 +339,11 @@ namespace Emby.Server.Implementations.Library
var excludeItemTypes = includeItemTypes.Length == 0 && mediaTypes.Count == 0 ? new[]
{
- typeof(Person).Name,
- typeof(Studio).Name,
- typeof(Year).Name,
- typeof(MusicGenre).Name,
- typeof(Genre).Name
-
+ nameof(Person),
+ nameof(Studio),
+ nameof(Year),
+ nameof(MusicGenre),
+ nameof(Genre)
} : Array.Empty<string>();
var query = new InternalItemsQuery(user)
diff --git a/Emby.Server.Implementations/LiveTv/LiveTvManager.cs b/Emby.Server.Implementations/LiveTv/LiveTvManager.cs
index 1b10f2d27..c58860a99 100644
--- a/Emby.Server.Implementations/LiveTv/LiveTvManager.cs
+++ b/Emby.Server.Implementations/LiveTv/LiveTvManager.cs
@@ -7,6 +7,7 @@ using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Emby.Server.Implementations.Library;
+using Jellyfin.Data.Enums;
using MediaBrowser.Common.Configuration;
using MediaBrowser.Common.Extensions;
using MediaBrowser.Common.Progress;
@@ -707,7 +708,6 @@ namespace Emby.Server.Implementations.LiveTv
{
Path = info.ThumbImageUrl,
Type = ImageType.Thumb
-
}, 0);
}
}
@@ -720,7 +720,6 @@ namespace Emby.Server.Implementations.LiveTv
{
Path = info.LogoImageUrl,
Type = ImageType.Logo
-
}, 0);
}
}
@@ -733,7 +732,6 @@ namespace Emby.Server.Implementations.LiveTv
{
Path = info.BackdropImageUrl,
Type = ImageType.Backdrop
-
}, 0);
}
}
@@ -765,13 +763,14 @@ namespace Emby.Server.Implementations.LiveTv
return new Tuple<LiveTvProgram, bool, bool>(item, isNew, isUpdated);
}
- public async Task<BaseItemDto> GetProgram(string id, CancellationToken cancellationToken, User user = null)
+ public async Task<BaseItemDto> GetProgram(string id, CancellationToken cancellationToken, Jellyfin.Data.Entities.User user = null)
{
var program = _libraryManager.GetItemById(id);
var dto = _dtoService.GetBaseItemDto(program, new DtoOptions(), user);
- var list = new List<Tuple<BaseItemDto, string, string>>() {
+ var list = new List<Tuple<BaseItemDto, string, string>>
+ {
new Tuple<BaseItemDto, string, string>(dto, program.ExternalId, program.ExternalSeriesId)
};
@@ -939,7 +938,7 @@ namespace Emby.Server.Implementations.LiveTv
};
}
- private int GetRecommendationScore(LiveTvProgram program, User user, bool factorChannelWatchCount)
+ private int GetRecommendationScore(LiveTvProgram program, Jellyfin.Data.Entities.User user, bool factorChannelWatchCount)
{
var score = 0;
@@ -1325,7 +1324,7 @@ namespace Emby.Server.Implementations.LiveTv
return 7;
}
- private QueryResult<BaseItem> GetEmbyRecordings(RecordingQuery query, DtoOptions dtoOptions, User user)
+ private QueryResult<BaseItem> GetEmbyRecordings(RecordingQuery query, DtoOptions dtoOptions, Jellyfin.Data.Entities.User user)
{
if (user == null)
{
@@ -1433,7 +1432,7 @@ namespace Emby.Server.Implementations.LiveTv
return result;
}
- public Task AddInfoToProgramDto(IReadOnlyCollection<(BaseItem, BaseItemDto)> tuples, ItemFields[] fields, User user = null)
+ public Task AddInfoToProgramDto(IReadOnlyCollection<(BaseItem, BaseItemDto)> tuples, ItemFields[] fields, Jellyfin.Data.Entities.User user = null)
{
var programTuples = new List<Tuple<BaseItemDto, string, string>>();
var hasChannelImage = fields.Contains(ItemFields.ChannelImage);
@@ -1483,7 +1482,7 @@ namespace Emby.Server.Implementations.LiveTv
return EmbyTV.EmbyTV.Current.GetActiveRecordingInfo(path);
}
- public void AddInfoToRecordingDto(BaseItem item, BaseItemDto dto, ActiveRecordingInfo activeRecordingInfo, User user = null)
+ public void AddInfoToRecordingDto(BaseItem item, BaseItemDto dto, ActiveRecordingInfo activeRecordingInfo, Jellyfin.Data.Entities.User user = null)
{
var service = EmbyTV.EmbyTV.Current;
@@ -1895,7 +1894,7 @@ namespace Emby.Server.Implementations.LiveTv
return _libraryManager.GetItemById(internalChannelId);
}
- public void AddChannelInfo(IReadOnlyCollection<(BaseItemDto, LiveTvChannel)> tuples, DtoOptions options, User user)
+ public void AddChannelInfo(IReadOnlyCollection<(BaseItemDto, LiveTvChannel)> tuples, DtoOptions options, Jellyfin.Data.Entities.User user)
{
var now = DateTime.UtcNow;
@@ -2206,23 +2205,22 @@ namespace Emby.Server.Implementations.LiveTv
var info = new LiveTvInfo
{
Services = services,
- IsEnabled = services.Length > 0
+ IsEnabled = services.Length > 0,
+ EnabledUsers = _userManager.Users
+ .Where(IsLiveTvEnabled)
+ .Select(i => i.Id.ToString("N", CultureInfo.InvariantCulture))
+ .ToArray()
};
- info.EnabledUsers = _userManager.Users
- .Where(IsLiveTvEnabled)
- .Select(i => i.Id.ToString("N", CultureInfo.InvariantCulture))
- .ToArray();
-
return info;
}
- private bool IsLiveTvEnabled(User user)
+ private bool IsLiveTvEnabled(Jellyfin.Data.Entities.User user)
{
- return user.Policy.EnableLiveTvAccess && (Services.Count > 1 || GetConfiguration().TunerHosts.Length > 0);
+ return user.HasPermission(PermissionKind.EnableLiveTvAccess) && (Services.Count > 1 || GetConfiguration().TunerHosts.Length > 0);
}
- public IEnumerable<User> GetEnabledUsers()
+ public IEnumerable<Jellyfin.Data.Entities.User> GetEnabledUsers()
{
return _userManager.Users
.Where(IsLiveTvEnabled);
@@ -2472,12 +2470,12 @@ namespace Emby.Server.Implementations.LiveTv
return _tvDtoService.GetInternalProgramId(externalId);
}
- public List<BaseItem> GetRecordingFolders(User user)
+ public List<BaseItem> GetRecordingFolders(Jellyfin.Data.Entities.User user)
{
return GetRecordingFolders(user, false);
}
- private List<BaseItem> GetRecordingFolders(User user, bool refreshChannels)
+ private List<BaseItem> GetRecordingFolders(Jellyfin.Data.Entities.User user, bool refreshChannels)
{
var folders = EmbyTV.EmbyTV.Current.GetRecordingFolders()
.SelectMany(i => i.Locations)
diff --git a/Emby.Server.Implementations/Playlists/ManualPlaylistsFolder.cs b/Emby.Server.Implementations/Playlists/ManualPlaylistsFolder.cs
index cd9f7946e..52809c9c1 100644
--- a/Emby.Server.Implementations/Playlists/ManualPlaylistsFolder.cs
+++ b/Emby.Server.Implementations/Playlists/ManualPlaylistsFolder.cs
@@ -14,12 +14,12 @@ namespace Emby.Server.Implementations.Playlists
Name = "Playlists";
}
- public override bool IsVisible(User user)
+ public override bool IsVisible(Jellyfin.Data.Entities.User user)
{
return base.IsVisible(user) && GetChildren(user, true).Any();
}
- protected override IEnumerable<BaseItem> GetEligibleChildrenForRecursiveChildren(User user)
+ protected override IEnumerable<BaseItem> GetEligibleChildrenForRecursiveChildren(Jellyfin.Data.Entities.User user)
{
return base.GetEligibleChildrenForRecursiveChildren(user).OfType<Playlist>();
}
@@ -42,7 +42,7 @@ namespace Emby.Server.Implementations.Playlists
}
query.Recursive = true;
- query.IncludeItemTypes = new string[] { "Playlist" };
+ query.IncludeItemTypes = new[] { "Playlist" };
query.Parent = null;
return LibraryManager.GetItemsResult(query);
}
diff --git a/Emby.Server.Implementations/Playlists/PlaylistManager.cs b/Emby.Server.Implementations/Playlists/PlaylistManager.cs
index 9b1510ac9..9a062b196 100644
--- a/Emby.Server.Implementations/Playlists/PlaylistManager.cs
+++ b/Emby.Server.Implementations/Playlists/PlaylistManager.cs
@@ -175,7 +175,7 @@ namespace Emby.Server.Implementations.Playlists
return path;
}
- private List<BaseItem> GetPlaylistItems(IEnumerable<Guid> itemIds, string playlistMediaType, User user, DtoOptions options)
+ private List<BaseItem> GetPlaylistItems(IEnumerable<Guid> itemIds, string playlistMediaType, Jellyfin.Data.Entities.User user, DtoOptions options)
{
var items = itemIds.Select(i => _libraryManager.GetItemById(i)).Where(i => i != null);
@@ -192,7 +192,7 @@ namespace Emby.Server.Implementations.Playlists
});
}
- private void AddToPlaylistInternal(string playlistId, ICollection<Guid> newItemIds, User user, DtoOptions options)
+ private void AddToPlaylistInternal(string playlistId, ICollection<Guid> newItemIds, Jellyfin.Data.Entities.User user, DtoOptions options)
{
// Retrieve the existing playlist
var playlist = _libraryManager.GetItemById(playlistId) as Playlist
diff --git a/Emby.Server.Implementations/Session/SessionManager.cs b/Emby.Server.Implementations/Session/SessionManager.cs
index df98a35bc..84b16f1c7 100644
--- a/Emby.Server.Implementations/Session/SessionManager.cs
+++ b/Emby.Server.Implementations/Session/SessionManager.cs
@@ -5,6 +5,7 @@ using System.Globalization;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
+using Jellyfin.Data.Enums;
using MediaBrowser.Common.Events;
using MediaBrowser.Common.Extensions;
using MediaBrowser.Controller;
@@ -253,7 +254,7 @@ namespace Emby.Server.Implementations.Session
string deviceId,
string deviceName,
string remoteEndPoint,
- User user)
+ Jellyfin.Data.Entities.User user)
{
CheckDisposed();
@@ -279,7 +280,7 @@ namespace Emby.Server.Implementations.Session
if (user != null)
{
- var userLastActivityDate = user.LastActivityDate ?? DateTime.MinValue;
+ var userLastActivityDate = user.LastActivityDate;
user.LastActivityDate = activityDate;
if ((activityDate - userLastActivityDate).TotalSeconds > 60)
@@ -431,7 +432,13 @@ namespace Emby.Server.Implementations.Session
/// <param name="remoteEndPoint">The remote end point.</param>
/// <param name="user">The user.</param>
/// <returns>SessionInfo.</returns>
- private SessionInfo GetSessionInfo(string appName, string appVersion, string deviceId, string deviceName, string remoteEndPoint, User user)
+ private SessionInfo GetSessionInfo(
+ string appName,
+ string appVersion,
+ string deviceId,
+ string deviceName,
+ string remoteEndPoint,
+ Jellyfin.Data.Entities.User user)
{
CheckDisposed();
@@ -444,14 +451,13 @@ namespace Emby.Server.Implementations.Session
CheckDisposed();
- var sessionInfo = _activeConnections.GetOrAdd(key, k =>
- {
- return CreateSession(k, appName, appVersion, deviceId, deviceName, remoteEndPoint, user);
- });
+ var sessionInfo = _activeConnections.GetOrAdd(
+ key,
+ k => CreateSession(k, appName, appVersion, deviceId, deviceName, remoteEndPoint, user));
- sessionInfo.UserId = user == null ? Guid.Empty : user.Id;
- sessionInfo.UserName = user?.Name;
- sessionInfo.UserPrimaryImageTag = user == null ? null : GetImageCacheTag(user, ImageType.Primary);
+ sessionInfo.UserId = user?.Id ?? Guid.Empty;
+ sessionInfo.UserName = user?.Username;
+ sessionInfo.UserPrimaryImageTag = user == null ? null : GetImageCacheTag(user);
sessionInfo.RemoteEndPoint = remoteEndPoint;
sessionInfo.Client = appName;
@@ -470,7 +476,14 @@ namespace Emby.Server.Implementations.Session
return sessionInfo;
}
- private SessionInfo CreateSession(string key, string appName, string appVersion, string deviceId, string deviceName, string remoteEndPoint, User user)
+ private SessionInfo CreateSession(
+ string key,
+ string appName,
+ string appVersion,
+ string deviceId,
+ string deviceName,
+ string remoteEndPoint,
+ Jellyfin.Data.Entities.User user)
{
var sessionInfo = new SessionInfo(this, _logger)
{
@@ -480,11 +493,11 @@ namespace Emby.Server.Implementations.Session
Id = key.GetMD5().ToString("N", CultureInfo.InvariantCulture)
};
- var username = user?.Name;
+ var username = user?.Username;
sessionInfo.UserId = user?.Id ?? Guid.Empty;
sessionInfo.UserName = username;
- sessionInfo.UserPrimaryImageTag = user == null ? null : GetImageCacheTag(user, ImageType.Primary);
+ sessionInfo.UserPrimaryImageTag = user == null ? null : GetImageCacheTag(user);
sessionInfo.RemoteEndPoint = remoteEndPoint;
if (string.IsNullOrEmpty(deviceName))
@@ -507,9 +520,9 @@ namespace Emby.Server.Implementations.Session
return sessionInfo;
}
- private List<User> GetUsers(SessionInfo session)
+ private List<Jellyfin.Data.Entities.User> GetUsers(SessionInfo session)
{
- var users = new List<User>();
+ var users = new List<Jellyfin.Data.Entities.User>();
if (session.UserId != Guid.Empty)
{
@@ -532,10 +545,7 @@ namespace Emby.Server.Implementations.Session
private void StartIdleCheckTimer()
{
- if (_idleTimer == null)
- {
- _idleTimer = new Timer(CheckForIdlePlayback, null, TimeSpan.FromMinutes(5), TimeSpan.FromMinutes(5));
- }
+ _idleTimer ??= new Timer(CheckForIdlePlayback, null, TimeSpan.FromMinutes(5), TimeSpan.FromMinutes(5));
}
private void StopIdleCheckTimer()
@@ -670,7 +680,7 @@ namespace Emby.Server.Implementations.Session
/// </summary>
/// <param name="user">The user object.</param>
/// <param name="item">The item.</param>
- private void OnPlaybackStart(User user, BaseItem item)
+ private void OnPlaybackStart(Jellyfin.Data.Entities.User user, BaseItem item)
{
var data = _userDataManager.GetUserData(user, item);
@@ -753,7 +763,7 @@ namespace Emby.Server.Implementations.Session
StartIdleCheckTimer();
}
- private void OnPlaybackProgress(User user, BaseItem item, PlaybackProgressInfo info)
+ private void OnPlaybackProgress(Jellyfin.Data.Entities.User user, BaseItem item, PlaybackProgressInfo info)
{
var data = _userDataManager.GetUserData(user, item);
@@ -779,11 +789,11 @@ namespace Emby.Server.Implementations.Session
}
}
- private static bool UpdatePlaybackSettings(User user, PlaybackProgressInfo info, UserItemData data)
+ private static bool UpdatePlaybackSettings(Jellyfin.Data.Entities.User user, PlaybackProgressInfo info, UserItemData data)
{
var changed = false;
- if (user.Configuration.RememberAudioSelections)
+ if (user.RememberAudioSelections)
{
if (data.AudioStreamIndex != info.AudioStreamIndex)
{
@@ -800,7 +810,7 @@ namespace Emby.Server.Implementations.Session
}
}
- if (user.Configuration.RememberSubtitleSelections)
+ if (user.RememberSubtitleSelections)
{
if (data.SubtitleStreamIndex != info.SubtitleStreamIndex)
{
@@ -939,7 +949,7 @@ namespace Emby.Server.Implementations.Session
_logger);
}
- private bool OnPlaybackStopped(User user, BaseItem item, long? positionTicks, bool playbackFailed)
+ private bool OnPlaybackStopped(Jellyfin.Data.Entities.User user, BaseItem item, long? positionTicks, bool playbackFailed)
{
bool playedToCompletion = false;
@@ -1111,13 +1121,13 @@ namespace Emby.Server.Implementations.Session
if (items.Any(i => i.GetPlayAccess(user) != PlayAccess.Full))
{
throw new ArgumentException(
- string.Format(CultureInfo.InvariantCulture, "{0} is not allowed to play media.", user.Name));
+ string.Format(CultureInfo.InvariantCulture, "{0} is not allowed to play media.", user.Username));
}
}
if (user != null
&& command.ItemIds.Length == 1
- && user.Configuration.EnableNextEpisodeAutoPlay
+ && user.EnableNextEpisodeAutoPlay
&& _libraryManager.GetItemById(command.ItemIds[0]) is Episode episode)
{
var series = episode.Series;
@@ -1153,7 +1163,7 @@ namespace Emby.Server.Implementations.Session
await SendMessageToSession(session, "Play", command, cancellationToken).ConfigureAwait(false);
}
- private IEnumerable<BaseItem> TranslateItemForPlayback(Guid id, User user)
+ private IEnumerable<BaseItem> TranslateItemForPlayback(Guid id, Jellyfin.Data.Entities.User user)
{
var item = _libraryManager.GetItemById(id);
@@ -1172,7 +1182,7 @@ namespace Emby.Server.Implementations.Session
DtoOptions = new DtoOptions(false)
{
EnableImages = false,
- Fields = new ItemFields[]
+ Fields = new[]
{
ItemFields.SortName
}
@@ -1206,7 +1216,7 @@ namespace Emby.Server.Implementations.Session
return new[] { item };
}
- private IEnumerable<BaseItem> TranslateItemForInstantMix(Guid id, User user)
+ private IEnumerable<BaseItem> TranslateItemForInstantMix(Guid id, Jellyfin.Data.Entities.User user)
{
var item = _libraryManager.GetItemById(id);
@@ -1334,7 +1344,7 @@ namespace Emby.Server.Implementations.Session
list.Add(new SessionUserInfo
{
UserId = userId,
- UserName = user.Name
+ UserName = user.Username
});
session.AdditionalUsers = list.ToArray();
@@ -1389,7 +1399,7 @@ namespace Emby.Server.Implementations.Session
{
CheckDisposed();
- User user = null;
+ Jellyfin.Data.Entities.User user = null;
if (request.UserId != Guid.Empty)
{
user = _userManager.GetUserById(request.UserId);
@@ -1445,7 +1455,7 @@ namespace Emby.Server.Implementations.Session
return returnResult;
}
- private string GetAuthorizationToken(User user, string deviceId, string app, string appVersion, string deviceName)
+ private string GetAuthorizationToken(Jellyfin.Data.Entities.User user, string deviceId, string app, string appVersion, string deviceName)
{
var existing = _authRepo.Get(
new AuthenticationInfoQuery
@@ -1494,7 +1504,7 @@ namespace Emby.Server.Implementations.Session
DeviceName = deviceName,
UserId = user.Id,
AccessToken = Guid.NewGuid().ToString("N", CultureInfo.InvariantCulture),
- UserName = user.Name
+ UserName = user.Username
};
_logger.LogInformation("Creating new access token for user {0}", user.Id);
@@ -1691,15 +1701,15 @@ namespace Emby.Server.Implementations.Session
return info;
}
- private string GetImageCacheTag(BaseItem item, ImageType type)
+ private string GetImageCacheTag(Jellyfin.Data.Entities.User user)
{
try
{
- return _imageProcessor.GetImageCacheTag(item, type);
+ return _imageProcessor.GetImageCacheTag(user);
}
- catch (Exception ex)
+ catch (Exception e)
{
- _logger.LogError(ex, "Error getting image information for {Type}", type);
+ _logger.LogError(e, "Error getting image information for profile image");
return null;
}
}
@@ -1808,7 +1818,10 @@ namespace Emby.Server.Implementations.Session
{
CheckDisposed();
- var adminUserIds = _userManager.Users.Where(i => i.Policy.IsAdministrator).Select(i => i.Id).ToList();
+ var adminUserIds = _userManager.Users
+ .Where(i => i.HasPermission(PermissionKind.IsAdministrator))
+ .Select(i => i.Id)
+ .ToList();
return SendMessageToUserSessions(adminUserIds, name, data, cancellationToken);
}
diff --git a/Emby.Server.Implementations/Sorting/DateLastMediaAddedComparer.cs b/Emby.Server.Implementations/Sorting/DateLastMediaAddedComparer.cs
index 623675157..3cc294371 100644
--- a/Emby.Server.Implementations/Sorting/DateLastMediaAddedComparer.cs
+++ b/Emby.Server.Implementations/Sorting/DateLastMediaAddedComparer.cs
@@ -12,7 +12,7 @@ namespace Emby.Server.Implementations.Sorting
/// Gets or sets the user.
/// </summary>
/// <value>The user.</value>
- public User User { get; set; }
+ public Jellyfin.Data.Entities.User User { get; set; }
/// <summary>
/// Gets or sets the user manager.
diff --git a/Emby.Server.Implementations/Sorting/DatePlayedComparer.cs b/Emby.Server.Implementations/Sorting/DatePlayedComparer.cs
index 73f59f8cd..57a1a00d9 100644
--- a/Emby.Server.Implementations/Sorting/DatePlayedComparer.cs
+++ b/Emby.Server.Implementations/Sorting/DatePlayedComparer.cs
@@ -15,7 +15,7 @@ namespace Emby.Server.Implementations.Sorting
/// Gets or sets the user.
/// </summary>
/// <value>The user.</value>
- public User User { get; set; }
+ public Jellyfin.Data.Entities.User User { get; set; }
/// <summary>
/// Gets or sets the user manager.
diff --git a/Emby.Server.Implementations/Sorting/IsFavoriteOrLikeComparer.cs b/Emby.Server.Implementations/Sorting/IsFavoriteOrLikeComparer.cs
index 66de05a6a..c9feca7e3 100644
--- a/Emby.Server.Implementations/Sorting/IsFavoriteOrLikeComparer.cs
+++ b/Emby.Server.Implementations/Sorting/IsFavoriteOrLikeComparer.cs
@@ -11,7 +11,7 @@ namespace Emby.Server.Implementations.Sorting
/// Gets or sets the user.
/// </summary>
/// <value>The user.</value>
- public User User { get; set; }
+ public Jellyfin.Data.Entities.User User { get; set; }
/// <summary>
/// Compares the specified x.
diff --git a/Emby.Server.Implementations/Sorting/IsPlayedComparer.cs b/Emby.Server.Implementations/Sorting/IsPlayedComparer.cs
index da3f3dd25..6f383e65f 100644
--- a/Emby.Server.Implementations/Sorting/IsPlayedComparer.cs
+++ b/Emby.Server.Implementations/Sorting/IsPlayedComparer.cs
@@ -11,7 +11,7 @@ namespace Emby.Server.Implementations.Sorting
/// Gets or sets the user.
/// </summary>
/// <value>The user.</value>
- public User User { get; set; }
+ public Jellyfin.Data.Entities.User User { get; set; }
/// <summary>
/// Compares the specified x.
diff --git a/Emby.Server.Implementations/Sorting/IsUnplayedComparer.cs b/Emby.Server.Implementations/Sorting/IsUnplayedComparer.cs
index d99d0eff2..4845fdc0d 100644
--- a/Emby.Server.Implementations/Sorting/IsUnplayedComparer.cs
+++ b/Emby.Server.Implementations/Sorting/IsUnplayedComparer.cs
@@ -11,7 +11,7 @@ namespace Emby.Server.Implementations.Sorting
/// Gets or sets the user.
/// </summary>
/// <value>The user.</value>
- public User User { get; set; }
+ public Jellyfin.Data.Entities.User User { get; set; }
/// <summary>
/// Compares the specified x.
diff --git a/Emby.Server.Implementations/Sorting/PlayCountComparer.cs b/Emby.Server.Implementations/Sorting/PlayCountComparer.cs
index eb74ce1bd..99846db61 100644
--- a/Emby.Server.Implementations/Sorting/PlayCountComparer.cs
+++ b/Emby.Server.Implementations/Sorting/PlayCountComparer.cs
@@ -14,7 +14,7 @@ namespace Emby.Server.Implementations.Sorting
/// Gets or sets the user.
/// </summary>
/// <value>The user.</value>
- public User User { get; set; }
+ public Jellyfin.Data.Entities.User User { get; set; }
/// <summary>
/// Compares the specified x.
diff --git a/Emby.Server.Implementations/TV/TVSeriesManager.cs b/Emby.Server.Implementations/TV/TVSeriesManager.cs
index 4c2f24e6f..905a1ea99 100644
--- a/Emby.Server.Implementations/TV/TVSeriesManager.cs
+++ b/Emby.Server.Implementations/TV/TVSeriesManager.cs
@@ -2,6 +2,7 @@ using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
+using Jellyfin.Data.Enums;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Dto;
using MediaBrowser.Controller.Entities;
@@ -74,7 +75,8 @@ namespace Emby.Server.Implementations.TV
{
parents = _libraryManager.GetUserRootFolder().GetChildren(user, true)
.Where(i => i is Folder)
- .Where(i => !user.Configuration.LatestItemsExcludes.Contains(i.Id.ToString("N", CultureInfo.InvariantCulture)))
+ .Where(i => !user.GetPreference(PreferenceKind.LatestItemExcludes)
+ .Contains(i.Id.ToString("N", CultureInfo.InvariantCulture)))
.ToArray();
}
@@ -137,7 +139,7 @@ namespace Emby.Server.Implementations.TV
return GetResult(episodes, request);
}
- public IEnumerable<Episode> GetNextUpEpisodes(NextUpQuery request, User user, IEnumerable<string> seriesKeys, DtoOptions dtoOptions)
+ public IEnumerable<Episode> GetNextUpEpisodes(NextUpQuery request, Jellyfin.Data.Entities.User user, IEnumerable<string> seriesKeys, DtoOptions dtoOptions)
{
// Avoid implicitly captured closure
var currentUser = user;
@@ -186,13 +188,13 @@ namespace Emby.Server.Implementations.TV
/// Gets the next up.
/// </summary>
/// <returns>Task{Episode}.</returns>
- private Tuple<DateTime, Func<Episode>> GetNextUp(string seriesKey, User user, DtoOptions dtoOptions)
+ private Tuple<DateTime, Func<Episode>> GetNextUp(string seriesKey, Jellyfin.Data.Entities.User user, DtoOptions dtoOptions)
{
var lastWatchedEpisode = _libraryManager.GetItemList(new InternalItemsQuery(user)
{
AncestorWithPresentationUniqueKey = null,
SeriesPresentationUniqueKey = seriesKey,
- IncludeItemTypes = new[] { typeof(Episode).Name },
+ IncludeItemTypes = new[] { nameof(Episode) },
OrderBy = new[] { new ValueTuple<string, SortOrder>(ItemSortBy.SortName, SortOrder.Descending) },
IsPlayed = true,
Limit = 1,
@@ -205,7 +207,6 @@ namespace Emby.Server.Implementations.TV
},
EnableImages = false
}
-
}).FirstOrDefault();
Func<Episode> getEpisode = () =>
@@ -220,7 +221,7 @@ namespace Emby.Server.Implementations.TV
IsPlayed = false,
IsVirtualItem = false,
ParentIndexNumberNotEquals = 0,
- MinSortName = lastWatchedEpisode == null ? null : lastWatchedEpisode.SortName,
+ MinSortName = lastWatchedEpisode?.SortName,
DtoOptions = dtoOptions
}).Cast<Episode>().FirstOrDefault();