Initial migration code

pull/3423/head
Patrick Barron 4 years ago
parent a78184ef44
commit 9ad839c776

@ -1,8 +1,10 @@
#pragma warning disable CS1591
using System;
using System.Linq;
using System.Threading.Tasks;
using Emby.Dlna.Service;
using Jellyfin.Data.Enums;
using MediaBrowser.Common.Net;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Dlna;
@ -104,7 +106,7 @@ namespace Emby.Dlna.ContentDirectory
.ProcessControlRequestAsync(request);
}
private User GetUser(DeviceProfile profile)
private Jellyfin.Data.Entities.User GetUser(DeviceProfile profile)
{
if (!string.IsNullOrEmpty(profile.UserId))
{
@ -130,18 +132,13 @@ namespace Emby.Dlna.ContentDirectory
foreach (var user in _userManager.Users)
{
if (user.Policy.IsAdministrator)
if (user.HasPermission(PermissionKind.IsAdministrator))
{
return user;
}
}
foreach (var user in _userManager.Users)
{
return user;
}
return null;
return _userManager.Users.FirstOrDefault();
}
}
}

@ -36,7 +36,7 @@ namespace Emby.Dlna.ContentDirectory
private readonly ILibraryManager _libraryManager;
private readonly IUserDataManager _userDataManager;
private readonly IServerConfigurationManager _config;
private readonly User _user;
private readonly Jellyfin.Data.Entities.User _user;
private readonly IUserViewManager _userViewManager;
private readonly ITVSeriesManager _tvSeriesManager;
@ -59,7 +59,7 @@ namespace Emby.Dlna.ContentDirectory
string accessToken,
IImageProcessor imageProcessor,
IUserDataManager userDataManager,
User user,
Jellyfin.Data.Entities.User user,
int systemUpdateId,
IServerConfigurationManager config,
ILocalizationManager localization,
@ -432,7 +432,7 @@ namespace Emby.Dlna.ContentDirectory
xmlWriter.WriteElementString("UpdateID", _systemUpdateId.ToString(CultureInfo.InvariantCulture));
}
private QueryResult<BaseItem> GetChildrenSorted(BaseItem item, User user, SearchCriteria search, SortCriteria sort, int? startIndex, int? limit)
private QueryResult<BaseItem> GetChildrenSorted(BaseItem item, Jellyfin.Data.Entities.User user, SearchCriteria search, SortCriteria sort, int? startIndex, int? limit)
{
var folder = (Folder)item;
@ -489,7 +489,7 @@ namespace Emby.Dlna.ContentDirectory
return new DtoOptions(true);
}
private QueryResult<ServerItem> GetUserItems(BaseItem item, StubType? stubType, User user, SortCriteria sort, int? startIndex, int? limit)
private QueryResult<ServerItem> GetUserItems(BaseItem item, StubType? stubType, Jellyfin.Data.Entities.User user, SortCriteria sort, int? startIndex, int? limit)
{
if (item is MusicGenre)
{
@ -558,7 +558,7 @@ namespace Emby.Dlna.ContentDirectory
return ToResult(queryResult);
}
private QueryResult<ServerItem> GetLiveTvChannels(User user, SortCriteria sort, int? startIndex, int? limit)
private QueryResult<ServerItem> GetLiveTvChannels(Jellyfin.Data.Entities.User user, SortCriteria sort, int? startIndex, int? limit)
{
var query = new InternalItemsQuery(user)
{
@ -574,7 +574,7 @@ namespace Emby.Dlna.ContentDirectory
return ToResult(result);
}
private QueryResult<ServerItem> GetMusicFolders(BaseItem item, User user, StubType? stubType, SortCriteria sort, int? startIndex, int? limit)
private QueryResult<ServerItem> GetMusicFolders(BaseItem item, Jellyfin.Data.Entities.User user, StubType? stubType, SortCriteria sort, int? startIndex, int? limit)
{
var query = new InternalItemsQuery(user)
{
@ -692,7 +692,7 @@ namespace Emby.Dlna.ContentDirectory
};
}
private QueryResult<ServerItem> GetMovieFolders(BaseItem item, User user, StubType? stubType, SortCriteria sort, int? startIndex, int? limit)
private QueryResult<ServerItem> GetMovieFolders(BaseItem item, Jellyfin.Data.Entities.User user, StubType? stubType, SortCriteria sort, int? startIndex, int? limit)
{
var query = new InternalItemsQuery(user)
{
@ -731,7 +731,7 @@ namespace Emby.Dlna.ContentDirectory
return GetGenres(item, user, query);
}
var array = new ServerItem[]
var array = new[]
{
new ServerItem(item)
{
@ -766,7 +766,7 @@ namespace Emby.Dlna.ContentDirectory
};
}
private QueryResult<ServerItem> GetFolders(User user, int? startIndex, int? limit)
private QueryResult<ServerItem> GetFolders(Jellyfin.Data.Entities.User user, int? startIndex, int? limit)
{
var folders = _libraryManager.GetUserRootFolder().GetChildren(user, true)
.OrderBy(i => i.SortName)
@ -783,7 +783,7 @@ namespace Emby.Dlna.ContentDirectory
}, startIndex, limit);
}
private QueryResult<ServerItem> GetTvFolders(BaseItem item, User user, StubType? stubType, SortCriteria sort, int? startIndex, int? limit)
private QueryResult<ServerItem> GetTvFolders(BaseItem item, Jellyfin.Data.Entities.User user, StubType? stubType, SortCriteria sort, int? startIndex, int? limit)
{
var query = new InternalItemsQuery(user)
{
@ -871,7 +871,7 @@ namespace Emby.Dlna.ContentDirectory
};
}
private QueryResult<ServerItem> GetMovieContinueWatching(BaseItem parent, User user, InternalItemsQuery query)
private QueryResult<ServerItem> GetMovieContinueWatching(BaseItem parent, Jellyfin.Data.Entities.User user, InternalItemsQuery query)
{
query.Recursive = true;
query.Parent = parent;
@ -891,7 +891,7 @@ namespace Emby.Dlna.ContentDirectory
return ToResult(result);
}
private QueryResult<ServerItem> GetSeries(BaseItem parent, User user, InternalItemsQuery query)
private QueryResult<ServerItem> GetSeries(BaseItem parent, Jellyfin.Data.Entities.User user, InternalItemsQuery query)
{
query.Recursive = true;
query.Parent = parent;
@ -904,7 +904,7 @@ namespace Emby.Dlna.ContentDirectory
return ToResult(result);
}
private QueryResult<ServerItem> GetMovieMovies(BaseItem parent, User user, InternalItemsQuery query)
private QueryResult<ServerItem> GetMovieMovies(BaseItem parent, Jellyfin.Data.Entities.User user, InternalItemsQuery query)
{
query.Recursive = true;
query.Parent = parent;
@ -917,7 +917,7 @@ namespace Emby.Dlna.ContentDirectory
return ToResult(result);
}
private QueryResult<ServerItem> GetMovieCollections(User user, InternalItemsQuery query)
private QueryResult<ServerItem> GetMovieCollections(Jellyfin.Data.Entities.User user, InternalItemsQuery query)
{
query.Recursive = true;
//query.Parent = parent;
@ -930,7 +930,7 @@ namespace Emby.Dlna.ContentDirectory
return ToResult(result);
}
private QueryResult<ServerItem> GetMusicAlbums(BaseItem parent, User user, InternalItemsQuery query)
private QueryResult<ServerItem> GetMusicAlbums(BaseItem parent, Jellyfin.Data.Entities.User user, InternalItemsQuery query)
{
query.Recursive = true;
query.Parent = parent;
@ -943,7 +943,7 @@ namespace Emby.Dlna.ContentDirectory
return ToResult(result);
}
private QueryResult<ServerItem> GetMusicSongs(BaseItem parent, User user, InternalItemsQuery query)
private QueryResult<ServerItem> GetMusicSongs(BaseItem parent, Jellyfin.Data.Entities.User user, InternalItemsQuery query)
{
query.Recursive = true;
query.Parent = parent;
@ -956,7 +956,7 @@ namespace Emby.Dlna.ContentDirectory
return ToResult(result);
}
private QueryResult<ServerItem> GetFavoriteSongs(BaseItem parent, User user, InternalItemsQuery query)
private QueryResult<ServerItem> GetFavoriteSongs(BaseItem parent, Jellyfin.Data.Entities.User user, InternalItemsQuery query)
{
query.Recursive = true;
query.Parent = parent;
@ -969,7 +969,7 @@ namespace Emby.Dlna.ContentDirectory
return ToResult(result);
}
private QueryResult<ServerItem> GetFavoriteSeries(BaseItem parent, User user, InternalItemsQuery query)
private QueryResult<ServerItem> GetFavoriteSeries(BaseItem parent, Jellyfin.Data.Entities.User user, InternalItemsQuery query)
{
query.Recursive = true;
query.Parent = parent;
@ -982,7 +982,7 @@ namespace Emby.Dlna.ContentDirectory
return ToResult(result);
}
private QueryResult<ServerItem> GetFavoriteEpisodes(BaseItem parent, User user, InternalItemsQuery query)
private QueryResult<ServerItem> GetFavoriteEpisodes(BaseItem parent, Jellyfin.Data.Entities.User user, InternalItemsQuery query)
{
query.Recursive = true;
query.Parent = parent;
@ -995,7 +995,7 @@ namespace Emby.Dlna.ContentDirectory
return ToResult(result);
}
private QueryResult<ServerItem> GetMovieFavorites(BaseItem parent, User user, InternalItemsQuery query)
private QueryResult<ServerItem> GetMovieFavorites(BaseItem parent, Jellyfin.Data.Entities.User user, InternalItemsQuery query)
{
query.Recursive = true;
query.Parent = parent;
@ -1008,7 +1008,7 @@ namespace Emby.Dlna.ContentDirectory
return ToResult(result);
}
private QueryResult<ServerItem> GetFavoriteAlbums(BaseItem parent, User user, InternalItemsQuery query)
private QueryResult<ServerItem> GetFavoriteAlbums(BaseItem parent, Jellyfin.Data.Entities.User user, InternalItemsQuery query)
{
query.Recursive = true;
query.Parent = parent;
@ -1021,7 +1021,7 @@ namespace Emby.Dlna.ContentDirectory
return ToResult(result);
}
private QueryResult<ServerItem> GetGenres(BaseItem parent, User user, InternalItemsQuery query)
private QueryResult<ServerItem> GetGenres(BaseItem parent, Jellyfin.Data.Entities.User user, InternalItemsQuery query)
{
var genresResult = _libraryManager.GetGenres(new InternalItemsQuery(user)
{
@ -1039,7 +1039,7 @@ namespace Emby.Dlna.ContentDirectory
return ToResult(result);
}
private QueryResult<ServerItem> GetMusicGenres(BaseItem parent, User user, InternalItemsQuery query)
private QueryResult<ServerItem> GetMusicGenres(BaseItem parent, Jellyfin.Data.Entities.User user, InternalItemsQuery query)
{
var genresResult = _libraryManager.GetMusicGenres(new InternalItemsQuery(user)
{
@ -1057,7 +1057,7 @@ namespace Emby.Dlna.ContentDirectory
return ToResult(result);
}
private QueryResult<ServerItem> GetMusicAlbumArtists(BaseItem parent, User user, InternalItemsQuery query)
private QueryResult<ServerItem> GetMusicAlbumArtists(BaseItem parent, Jellyfin.Data.Entities.User user, InternalItemsQuery query)
{
var artists = _libraryManager.GetAlbumArtists(new InternalItemsQuery(user)
{
@ -1075,7 +1075,7 @@ namespace Emby.Dlna.ContentDirectory
return ToResult(result);
}
private QueryResult<ServerItem> GetMusicArtists(BaseItem parent, User user, InternalItemsQuery query)
private QueryResult<ServerItem> GetMusicArtists(BaseItem parent, Jellyfin.Data.Entities.User user, InternalItemsQuery query)
{
var artists = _libraryManager.GetArtists(new InternalItemsQuery(user)
{
@ -1093,7 +1093,7 @@ namespace Emby.Dlna.ContentDirectory
return ToResult(result);
}
private QueryResult<ServerItem> GetFavoriteArtists(BaseItem parent, User user, InternalItemsQuery query)
private QueryResult<ServerItem> GetFavoriteArtists(BaseItem parent, Jellyfin.Data.Entities.User user, InternalItemsQuery query)
{
var artists = _libraryManager.GetArtists(new InternalItemsQuery(user)
{
@ -1112,10 +1112,10 @@ namespace Emby.Dlna.ContentDirectory
return ToResult(result);
}
private QueryResult<ServerItem> GetMusicPlaylists(User user, InternalItemsQuery query)
private QueryResult<ServerItem> GetMusicPlaylists(Jellyfin.Data.Entities.User user, InternalItemsQuery query)
{
query.Parent = null;
query.IncludeItemTypes = new[] { typeof(Playlist).Name };
query.IncludeItemTypes = new[] { nameof(Playlist) };
query.SetUser(user);
query.Recursive = true;
@ -1124,7 +1124,7 @@ namespace Emby.Dlna.ContentDirectory
return ToResult(result);
}
private QueryResult<ServerItem> GetMusicLatest(BaseItem parent, User user, InternalItemsQuery query)
private QueryResult<ServerItem> GetMusicLatest(BaseItem parent, Jellyfin.Data.Entities.User user, InternalItemsQuery query)
{
query.OrderBy = Array.Empty<(string, SortOrder)>();
@ -1132,10 +1132,9 @@ namespace Emby.Dlna.ContentDirectory
{
UserId = user.Id,
Limit = 50,
IncludeItemTypes = new[] { typeof(Audio).Name },
ParentId = parent == null ? Guid.Empty : parent.Id,
IncludeItemTypes = new[] { nameof(Audio) },
ParentId = parent?.Id ?? Guid.Empty,
GroupItems = true
}, query.DtoOptions).Select(i => i.Item1 ?? i.Item2.FirstOrDefault()).Where(i => i != null).ToArray();
return ToResult(items);
@ -1150,13 +1149,12 @@ namespace Emby.Dlna.ContentDirectory
Limit = query.Limit,
StartIndex = query.StartIndex,
UserId = query.User.Id
}, new[] { parent }, query.DtoOptions);
return ToResult(result);
}
private QueryResult<ServerItem> GetTvLatest(BaseItem parent, User user, InternalItemsQuery query)
private QueryResult<ServerItem> GetTvLatest(BaseItem parent, Jellyfin.Data.Entities.User user, InternalItemsQuery query)
{
query.OrderBy = Array.Empty<(string, SortOrder)>();
@ -1167,30 +1165,29 @@ namespace Emby.Dlna.ContentDirectory
IncludeItemTypes = new[] { typeof(Episode).Name },
ParentId = parent == null ? Guid.Empty : parent.Id,
GroupItems = false
}, query.DtoOptions).Select(i => i.Item1 ?? i.Item2.FirstOrDefault()).Where(i => i != null).ToArray();
return ToResult(items);
}
private QueryResult<ServerItem> GetMovieLatest(BaseItem parent, User user, InternalItemsQuery query)
private QueryResult<ServerItem> GetMovieLatest(BaseItem parent, Jellyfin.Data.Entities.User user, InternalItemsQuery query)
{
query.OrderBy = Array.Empty<(string, SortOrder)>();
var items = _userViewManager.GetLatestItems(new LatestItemsQuery
var items = _userViewManager.GetLatestItems(
new LatestItemsQuery
{
UserId = user.Id,
Limit = 50,
IncludeItemTypes = new[] { typeof(Movie).Name },
ParentId = parent == null ? Guid.Empty : parent.Id,
IncludeItemTypes = new[] { nameof(Movie) },
ParentId = parent?.Id ?? Guid.Empty,
GroupItems = true
}, query.DtoOptions).Select(i => i.Item1 ?? i.Item2.FirstOrDefault()).Where(i => i != null).ToArray();
return ToResult(items);
}
private QueryResult<ServerItem> GetMusicArtistItems(BaseItem item, Guid parentId, User user, SortCriteria sort, int? startIndex, int? limit)
private QueryResult<ServerItem> GetMusicArtistItems(BaseItem item, Guid parentId, Jellyfin.Data.Entities.User user, SortCriteria sort, int? startIndex, int? limit)
{
var query = new InternalItemsQuery(user)
{
@ -1210,14 +1207,18 @@ namespace Emby.Dlna.ContentDirectory
return ToResult(result);
}
private QueryResult<ServerItem> GetGenreItems(BaseItem item, Guid parentId, User user, SortCriteria sort, int? startIndex, int? limit)
private QueryResult<ServerItem> GetGenreItems(BaseItem item, Guid parentId, Jellyfin.Data.Entities.User user, SortCriteria sort, int? startIndex, int? limit)
{
var query = new InternalItemsQuery(user)
{
Recursive = true,
ParentId = parentId,
GenreIds = new[] { item.Id },
IncludeItemTypes = new[] { typeof(Movie).Name, typeof(Series).Name },
IncludeItemTypes = new[]
{
nameof(Movie),
nameof(Series)
},
Limit = limit,
StartIndex = startIndex,
DtoOptions = GetDtoOptions()
@ -1230,7 +1231,7 @@ namespace Emby.Dlna.ContentDirectory
return ToResult(result);
}
private QueryResult<ServerItem> GetMusicGenreItems(BaseItem item, Guid parentId, User user, SortCriteria sort, int? startIndex, int? limit)
private QueryResult<ServerItem> GetMusicGenreItems(BaseItem item, Guid parentId, Jellyfin.Data.Entities.User user, SortCriteria sort, int? startIndex, int? limit)
{
var query = new InternalItemsQuery(user)
{

@ -6,7 +6,6 @@ using System.IO;
using System.Linq;
using System.Text;
using System.Xml;
using Emby.Dlna.Configuration;
using Emby.Dlna.ContentDirectory;
using MediaBrowser.Controller.Channels;
using MediaBrowser.Controller.Drawing;
@ -39,7 +38,7 @@ namespace Emby.Dlna.Didl
private readonly IImageProcessor _imageProcessor;
private readonly string _serverAddress;
private readonly string _accessToken;
private readonly User _user;
private readonly Jellyfin.Data.Entities.User _user;
private readonly IUserDataManager _userDataManager;
private readonly ILocalizationManager _localization;
private readonly IMediaSourceManager _mediaSourceManager;
@ -49,7 +48,7 @@ namespace Emby.Dlna.Didl
public DidlBuilder(
DeviceProfile profile,
User user,
Jellyfin.Data.Entities.User user,
IImageProcessor imageProcessor,
string serverAddress,
string accessToken,
@ -78,7 +77,7 @@ namespace Emby.Dlna.Didl
return url + "&dlnaheaders=true";
}
public string GetItemDidl(BaseItem item, User user, BaseItem context, string deviceId, Filter filter, StreamInfo streamInfo)
public string GetItemDidl(BaseItem item, Jellyfin.Data.Entities.User user, BaseItem context, string deviceId, Filter filter, StreamInfo streamInfo)
{
var settings = new XmlWriterSettings
{
@ -132,7 +131,7 @@ namespace Emby.Dlna.Didl
public void WriteItemElement(
XmlWriter writer,
BaseItem item,
User user,
Jellyfin.Data.Entities.User user,
BaseItem context,
StubType? contextStubType,
string deviceId,
@ -663,7 +662,7 @@ namespace Emby.Dlna.Didl
writer.WriteFullEndElement();
}
private void AddSamsungBookmarkInfo(BaseItem item, User user, XmlWriter writer, StreamInfo streamInfo)
private void AddSamsungBookmarkInfo(BaseItem item, Jellyfin.Data.Entities.User user, XmlWriter writer, StreamInfo streamInfo)
{
if (!item.SupportsPositionTicksResume || item is Folder)
{

@ -441,7 +441,13 @@ namespace Emby.Dlna.PlayTo
}
}
private PlaylistItem CreatePlaylistItem(BaseItem item, User user, long startPostionTicks, string mediaSourceId, int? audioStreamIndex, int? subtitleStreamIndex)
private PlaylistItem CreatePlaylistItem(
BaseItem item,
Jellyfin.Data.Entities.User user,
long startPostionTicks,
string mediaSourceId,
int? audioStreamIndex,
int? subtitleStreamIndex)
{
var deviceInfo = _device.Properties;

@ -4,6 +4,7 @@ using System.Globalization;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using Jellyfin.Data.Entities;
using MediaBrowser.Common.Extensions;
using MediaBrowser.Controller;
using MediaBrowser.Controller.Drawing;
@ -14,6 +15,7 @@ using MediaBrowser.Model.Entities;
using MediaBrowser.Model.IO;
using MediaBrowser.Model.Net;
using Microsoft.Extensions.Logging;
using Photo = MediaBrowser.Controller.Entities.Photo;
namespace Emby.Drawing
{
@ -328,6 +330,13 @@ namespace Emby.Drawing
});
}
/// <inheritdoc />
public string GetImageCacheTag(User user)
{
return (user.ProfileImage.Path + user.ProfileImage.LastModified.Ticks).GetMD5()
.ToString("N", CultureInfo.InvariantCulture);
}
private async Task<(string path, DateTime dateModified)> GetSupportedImage(string originalImagePath, DateTime dateModified)
{
var inputFormat = Path.GetExtension(originalImagePath)

@ -8,6 +8,7 @@ using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Jellyfin.Data.Enums;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Net;
using MediaBrowser.Controller.Notifications;
@ -164,7 +165,10 @@ namespace Emby.Notifications.Api
Level = request.Level,
Name = request.Name,
Url = request.Url,
UserIds = _userManager.Users.Where(i => i.Policy.IsAdministrator).Select(i => i.Id).ToArray()
UserIds = _userManager.Users
.Where(p => p.Permissions.Select(x => x.Kind).Contains(PermissionKind.IsAdministrator))
.Select(p => p.Id)
.ToArray()
};
return _notificationManager.SendNotification(notification, CancellationToken.None);

@ -4,6 +4,7 @@ using System.Globalization;
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.Configuration;
@ -81,7 +82,7 @@ namespace Emby.Notifications
private Task SendNotification(
NotificationRequest request,
INotificationService service,
IEnumerable<User> users,
IEnumerable<Jellyfin.Data.Entities.User> users,
string title,
string description,
CancellationToken cancellationToken)
@ -101,7 +102,7 @@ namespace Emby.Notifications
switch (request.SendToUserMode.Value)
{
case SendToUserType.Admins:
return _userManager.Users.Where(i => i.Policy.IsAdministrator)
return _userManager.Users.Where(i => i.HasPermission(PermissionKind.IsAdministrator))
.Select(i => i.Id);
case SendToUserType.All:
return _userManager.UsersIds;
@ -117,7 +118,7 @@ namespace Emby.Notifications
var config = GetConfiguration();
return _userManager.Users
.Where(i => config.IsEnabledToSendToUser(request.NotificationType, i.Id.ToString("N", CultureInfo.InvariantCulture), i.Policy))
.Where(i => config.IsEnabledToSendToUser(request.NotificationType, i.Id.ToString("N", CultureInfo.InvariantCulture), i))
.Select(i => i.Id);
}
@ -129,7 +130,7 @@ namespace Emby.Notifications
INotificationService service,
string title,
string description,
User user,
Jellyfin.Data.Entities.User user,
CancellationToken cancellationToken)
{
var notification = new UserNotification
@ -142,7 +143,7 @@ namespace Emby.Notifications
User = user
};
_logger.LogDebug("Sending notification via {0} to user {1}", service.Name, user.Name);
_logger.LogDebug("Sending notification via {0} to user {1}", service.Name, user.Username);
try
{
@ -154,7 +155,7 @@ namespace Emby.Notifications
}
}
private bool IsEnabledForUser(INotificationService service, User user)
private bool IsEnabledForUser(INotificationService service, Jellyfin.Data.Entities.User user)
{
try
{

@ -93,11 +93,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;
_deviceManager.CameraImageUploaded += OnCameraImageUploaded;
@ -118,13 +117,13 @@ namespace Emby.Server.Implementations.Activity
.ConfigureAwait(false);
}
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,
DateTime.UtcNow,
@ -177,7 +176,7 @@ namespace Emby.Server.Implementations.Activity
string.Format(
CultureInfo.InvariantCulture,
_localization.GetLocalizedString("UserStoppedPlayingItemWithValues"),
user.Name,
user.Username,
GetItemName(item),
e.DeviceName),
GetPlaybackStoppedNotificationType(item.MediaType),
@ -214,7 +213,7 @@ namespace Emby.Server.Implementations.Activity
string.Format(
CultureInfo.InvariantCulture,
_localization.GetLocalizedString("UserStartedPlayingItemWithValues"),
user.Name,
user.Username,
GetItemName(item),
e.DeviceName),
GetPlaybackNotificationType(item.MediaType),
@ -338,13 +337,13 @@ 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,
DateTime.UtcNow,
@ -352,13 +351,13 @@ namespace Emby.Server.Implementations.Activity
.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,
DateTime.UtcNow,
@ -366,26 +365,26 @@ namespace Emby.Server.Implementations.Activity
.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,
DateTime.UtcNow,
LogLevel.Trace)).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,
DateTime.UtcNow,
@ -562,11 +561,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;
_deviceManager.CameraImageUploaded -= OnCameraImageUploaded;
}

@ -48,6 +48,7 @@ using Emby.Server.Implementations.TV;
using Emby.Server.Implementations.Updates;
using Jellyfin.Server.Implementations;
using Jellyfin.Server.Implementations.Activity;
using Jellyfin.Server.Implementations.User;
using MediaBrowser.Api;
using MediaBrowser.Common;
using MediaBrowser.Common.Configuration;
@ -595,17 +596,12 @@ namespace Emby.Server.Implementations
serviceCollection.AddSingleton<IBlurayExaminer, BdInfoExaminer>();
serviceCollection.AddSingleton<IUserDataRepository, SqliteUserDataRepository>();
serviceCollection.AddSingleton<IUserDataManager, UserDataManager>();
serviceCollection.AddSingleton<IDisplayPreferencesRepository, SqliteDisplayPreferencesRepository>();
serviceCollection.AddSingleton<IItemRepository, SqliteItemRepository>();
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>();
@ -700,17 +696,11 @@ namespace Emby.Server.Implementations
_httpServer = Resolve<IHttpServer>();
_httpClient = Resolve<IHttpClient>();
((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();
FindParts();
}
@ -793,7 +783,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>();

@ -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,

@ -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>();

@ -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);
}
}

@ -102,7 +102,7 @@ namespace Emby.Server.Implementations.Data
/// <summary>
/// Opens the connection to the database
/// </summary>
public void Initialize(SqliteUserDataRepository userDataRepo, IUserManager userManager)
public void Initialize()
{
const string CreateMediaStreamsTableCommand
= "create table if not exists mediastreams (ItemId GUID, StreamIndex INT, StreamType TEXT, Codec TEXT, Language TEXT, ChannelLayout TEXT, Profile TEXT, AspectRatio TEXT, Path TEXT, IsInterlaced BIT, BitRate INT NULL, Channels INT NULL, SampleRate INT NULL, IsDefault BIT, IsForced BIT, IsExternal BIT, Height INT NULL, Width INT NULL, AverageFrameRate FLOAT NULL, RealFrameRate FLOAT NULL, Level FLOAT NULL, PixelFormat TEXT, BitDepth INT NULL, IsAnamorphic BIT NULL, RefFrames INT NULL, CodecTag TEXT NULL, Comment TEXT NULL, NalLengthSize TEXT NULL, IsAvc BIT NULL, Title TEXT NULL, TimeBase TEXT NULL, CodecTimeBase TEXT NULL, ColorPrimaries TEXT NULL, ColorSpace TEXT NULL, ColorTransfer TEXT NULL, PRIMARY KEY (ItemId, StreamIndex))";
@ -324,8 +324,6 @@ namespace Emby.Server.Implementations.Data
connection.RunQueries(postQueries);
}
userDataRepo.Initialize(userManager, WriteLock, WriteConnection);
}
private static readonly string[] _retriveItemColumns =

@ -1,379 +0,0 @@
#pragma warning disable CS1591
using System;
using System.Collections.Generic;
using System.IO;
using System.Threading;
using MediaBrowser.Common.Configuration;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Persistence;
using Microsoft.Extensions.Logging;
using SQLitePCL.pretty;
namespace Emby.Server.Implementations.Data
{
public class SqliteUserDataRepository : BaseSqliteRepository, IUserDataRepository
{
public SqliteUserDataRepository(
ILogger<SqliteUserDataRepository> logger,
IApplicationPaths appPaths)
: base(logger)
{
DbFilePath = Path.Combine(appPaths.DataPath, "library.db");
}
/// <inheritdoc />
public string Name => "SQLite";
/// <summary>
/// Opens the connection to the database.
/// </summary>
public void Initialize(IUserManager userManager, SemaphoreSlim dbLock, SQLiteDatabaseConnection dbConnection)
{
WriteLock.Dispose();
WriteLock = dbLock;
WriteConnection?.Dispose();
WriteConnection = dbConnection;
using (var connection = GetConnection())
{
var userDatasTableExists = TableExists(connection, "UserDatas");
var userDataTableExists = TableExists(connection, "userdata");
var users = userDatasTableExists ? null : userManager.Users;
connection.RunInTransaction(db =>
{
db.ExecuteAll(string.Join(";", new[] {
"create table if not exists UserDatas (key nvarchar not null, userId INT not null, rating float null, played bit not null, playCount int not null, isFavorite bit not null, playbackPositionTicks bigint not null, lastPlayedDate datetime null, AudioStreamIndex INT, SubtitleStreamIndex INT)",
"drop index if exists idx_userdata",
"drop index if exists idx_userdata1",
"drop index if exists idx_userdata2",
"drop index if exists userdataindex1",
"drop index if exists userdataindex",
"drop index if exists userdataindex3",
"drop index if exists userdataindex4",
"create unique index if not exists UserDatasIndex1 on UserDatas (key, userId)",
"create index if not exists UserDatasIndex2 on UserDatas (key, userId, played)",
"create index if not exists UserDatasIndex3 on UserDatas (key, userId, playbackPositionTicks)",
"create index if not exists UserDatasIndex4 on UserDatas (key, userId, isFavorite)"
}));
if (userDataTableExists)
{
var existingColumnNames = GetColumnNames(db, "userdata");
AddColumn(db, "userdata", "InternalUserId", "int", existingColumnNames);
AddColumn(db, "userdata", "AudioStreamIndex", "int", existingColumnNames);
AddColumn(db, "userdata", "SubtitleStreamIndex", "int", existingColumnNames);
if (!userDatasTableExists)
{
ImportUserIds(db, users);
db.ExecuteAll("INSERT INTO UserDatas (key, userId, rating, played, playCount, isFavorite, playbackPositionTicks, lastPlayedDate, AudioStreamIndex, SubtitleStreamIndex) SELECT key, InternalUserId, rating, played, playCount, isFavorite, playbackPositionTicks, lastPlayedDate, AudioStreamIndex, SubtitleStreamIndex from userdata where InternalUserId not null");
}
}
}, TransactionMode);
}
}
private void ImportUserIds(IDatabaseConnection db, IEnumerable<User> users)
{
var userIdsWithUserData = GetAllUserIdsWithUserData(db);
using (var statement = db.PrepareStatement("update userdata set InternalUserId=@InternalUserId where UserId=@UserId"))
{
foreach (var user in users)
{
if (!userIdsWithUserData.Contains(user.Id))
{
continue;
}
statement.TryBind("@UserId", user.Id.ToByteArray());
statement.TryBind("@InternalUserId", user.InternalId);
statement.MoveNext();
statement.Reset();
}
}
}
private List<Guid> GetAllUserIdsWithUserData(IDatabaseConnection db)
{
var list = new List<Guid>();
using (var statement = PrepareStatement(db, "select DISTINCT UserId from UserData where UserId not null"))
{
foreach (var row in statement.ExecuteQuery())
{
try
{
list.Add(row[0].ReadGuidFromBlob());
}
catch (Exception ex)
{
Logger.LogError(ex, "Error while getting user");
}
}
}
return list;
}
/// <summary>
/// Saves the user data.
/// </summary>
public void SaveUserData(long internalUserId, string key, UserItemData userData, CancellationToken cancellationToken)
{
if (userData == null)
{
throw new ArgumentNullException(nameof(userData));
}
if (internalUserId <= 0)
{
throw new ArgumentNullException(nameof(internalUserId));
}
if (string.IsNullOrEmpty(key))
{
throw new ArgumentNullException(nameof(key));
}
PersistUserData(internalUserId, key, userData, cancellationToken);
}
public void SaveAllUserData(long internalUserId, UserItemData[] userData, CancellationToken cancellationToken)
{
if (userData == null)
{
throw new ArgumentNullException(nameof(userData));
}
if (internalUserId <= 0)
{
throw new ArgumentNullException(nameof(internalUserId));
}
PersistAllUserData(internalUserId, userData, cancellationToken);
}
/// <summary>
/// Persists the user data.
/// </summary>
/// <param name="internalUserId">The user id.</param>
/// <param name="key">The key.</param>
/// <param name="userData">The user data.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Task.</returns>
public void PersistUserData(long internalUserId, string key, UserItemData userData, CancellationToken cancellationToken)
{
cancellationToken.ThrowIfCancellationRequested();
using (var connection = GetConnection())
{
connection.RunInTransaction(db =>
{
SaveUserData(db, internalUserId, key, userData);
}, TransactionMode);
}
}
private static void SaveUserData(IDatabaseConnection db, long internalUserId, string key, UserItemData userData)
{
using (var statement = db.PrepareStatement("replace into UserDatas (key, userId, rating,played,playCount,isFavorite,playbackPositionTicks,lastPlayedDate,AudioStreamIndex,SubtitleStreamIndex) values (@key, @userId, @rating,@played,@playCount,@isFavorite,@playbackPositionTicks,@lastPlayedDate,@AudioStreamIndex,@SubtitleStreamIndex)"))
{
statement.TryBind("@userId", internalUserId);
statement.TryBind("@key", key);
if (userData.Rating.HasValue)
{
statement.TryBind("@rating", userData.Rating.Value);
}
else
{
statement.TryBindNull("@rating");
}
statement.TryBind("@played", userData.Played);
statement.TryBind("@playCount", userData.PlayCount);
statement.TryBind("@isFavorite", userData.IsFavorite);
statement.TryBind("@playbackPositionTicks", userData.PlaybackPositionTicks);
if (userData.LastPlayedDate.HasValue)
{
statement.TryBind("@lastPlayedDate", userData.LastPlayedDate.Value.ToDateTimeParamValue());
}
else
{
statement.TryBindNull("@lastPlayedDate");
}
if (userData.AudioStreamIndex.HasValue)
{
statement.TryBind("@AudioStreamIndex", userData.AudioStreamIndex.Value);
}
else
{
statement.TryBindNull("@AudioStreamIndex");
}
if (userData.SubtitleStreamIndex.HasValue)
{
statement.TryBind("@SubtitleStreamIndex", userData.SubtitleStreamIndex.Value);
}
else
{
statement.TryBindNull("@SubtitleStreamIndex");
}
statement.MoveNext();
}
}
/// <summary>
/// Persist all user data for the specified user
/// </summary>
private void PersistAllUserData(long internalUserId, UserItemData[] userDataList, CancellationToken cancellationToken)
{
cancellationToken.ThrowIfCancellationRequested();
using (var connection = GetConnection())
{
connection.RunInTransaction(db =>
{
foreach (var userItemData in userDataList)
{
SaveUserData(db, internalUserId, userItemData.Key, userItemData);
}
}, TransactionMode);
}
}
/// <summary>
/// Gets the user data.
/// </summary>
/// <param name="internalUserId">The user id.</param>
/// <param name="key">The key.</param>
/// <returns>Task{UserItemData}.</returns>
/// <exception cref="ArgumentNullException">
/// userId
/// or
/// key
/// </exception>
public UserItemData GetUserData(long internalUserId, string key)
{
if (internalUserId <= 0)
{
throw new ArgumentNullException(nameof(internalUserId));
}
if (string.IsNullOrEmpty(key))
{
throw new ArgumentNullException(nameof(key));
}
using (var connection = GetConnection(true))
{
using (var statement = connection.PrepareStatement("select key,userid,rating,played,playCount,isFavorite,playbackPositionTicks,lastPlayedDate,AudioStreamIndex,SubtitleStreamIndex from UserDatas where key =@Key and userId=@UserId"))
{
statement.TryBind("@UserId", internalUserId);
statement.TryBind("@Key", key);
foreach (var row in statement.ExecuteQuery())
{
return ReadRow(row);
}
}
return null;
}
}
public UserItemData GetUserData(long internalUserId, List<string> keys)
{
if (keys == null)
{
throw new ArgumentNullException(nameof(keys));
}
if (keys.Count == 0)
{
return null;
}
return GetUserData(internalUserId, keys[0]);
}
/// <summary>
/// Return all user-data associated with the given user
/// </summary>
/// <param name="internalUserId"></param>
/// <returns></returns>
public List<UserItemData> GetAllUserData(long internalUserId)
{
if (internalUserId <= 0)
{
throw new ArgumentNullException(nameof(internalUserId));
}
var list = new List<UserItemData>();
using (var connection = GetConnection())
{
using (var statement = connection.PrepareStatement("select key,userid,rating,played,playCount,isFavorite,playbackPositionTicks,lastPlayedDate,AudioStreamIndex,SubtitleStreamIndex from UserDatas where userId=@UserId"))
{
statement.TryBind("@UserId", internalUserId);
foreach (var row in statement.ExecuteQuery())
{
list.Add(ReadRow(row));
}
}
}
return list;
}
/// <summary>
/// Read a row from the specified reader into the provided userData object
/// </summary>
/// <param name="reader"></param>
private UserItemData ReadRow(IReadOnlyList<IResultSetValue> reader)
{
var userData = new UserItemData();
userData.Key = reader[0].ToString();
//userData.UserId = reader[1].ReadGuidFromBlob();
if (reader[2].SQLiteType != SQLiteType.Null)
{
userData.Rating = reader[2].ToDouble();
}
userData.Played = reader[3].ToBool();
userData.PlayCount = reader[4].ToInt();
userData.IsFavorite = reader[5].ToBool();
userData.PlaybackPositionTicks = reader[6].ToInt64();
if (reader[7].SQLiteType != SQLiteType.Null)
{
userData.LastPlayedDate = reader[7].TryReadDateTime();
}
if (reader[8].SQLiteType != SQLiteType.Null)
{
userData.AudioStreamIndex = reader[8].ToInt();
}
if (reader[9].SQLiteType != SQLiteType.Null)
{
userData.SubtitleStreamIndex = reader[9].ToInt();
}
return userData;
}
}
}

@ -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);
}
}
}
}

@ -6,6 +6,7 @@ using System.Globalization;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using Jellyfin.Data.Enums;
using MediaBrowser.Common.Configuration;
using MediaBrowser.Common.Extensions;
using MediaBrowser.Controller.Configuration;
@ -360,7 +361,7 @@ namespace Emby.Server.Implementations.Devices
private string DefaultCameraUploadsPath => Path.Combine(_config.CommonApplicationPaths.DataPath, "camerauploads");
public bool CanAccessDevice(User user, string deviceId)
public bool CanAccessDevice(Jellyfin.Data.Entities.User user, string deviceId)
{
if (user == null)
{
@ -371,7 +372,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);
@ -383,21 +390,6 @@ 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);
}
}
public class DeviceManagerEntryPoint : IServerEntryPoint

@ -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

@ -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

@ -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
{

@ -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
}
};
}
}
}

@ -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;

@ -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.");
}

@ -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;
}
}

@ -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);
}

@ -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,

@ -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)
{

@ -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;

@ -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);
}

@ -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;

@ -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);

File diff suppressed because it is too large Load Diff

@ -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)

@ -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)

@ -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);
}

@ -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

@ -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)
{
@ -481,11 +494,11 @@ namespace Emby.Server.Implementations.Session
ServerId = _appHost.SystemId
};
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))
@ -508,9 +521,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)
{
@ -533,10 +546,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()
@ -671,7 +681,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);
@ -754,7 +764,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);
@ -780,11 +790,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)
{
@ -801,7 +811,7 @@ namespace Emby.Server.Implementations.Session
}
}
if (user.Configuration.RememberSubtitleSelections)
if (user.RememberSubtitleSelections)
{
if (data.SubtitleStreamIndex != info.SubtitleStreamIndex)
{
@ -940,7 +950,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;
@ -1112,13 +1122,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;
@ -1154,7 +1164,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);
@ -1173,7 +1183,7 @@ namespace Emby.Server.Implementations.Session
DtoOptions = new DtoOptions(false)
{
EnableImages = false,
Fields = new ItemFields[]
Fields = new[]
{
ItemFields.SortName
}
@ -1207,7 +1217,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);
@ -1335,7 +1345,7 @@ namespace Emby.Server.Implementations.Session
list.Add(new SessionUserInfo
{
UserId = userId,
UserName = user.Name
UserName = user.Username
});
session.AdditionalUsers = list.ToArray();
@ -1390,7 +1400,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);
@ -1446,7 +1456,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
@ -1495,7 +1505,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);
@ -1692,15 +1702,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;
}
}
@ -1809,7 +1819,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);
}

@ -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.

@ -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.

@ -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.

@ -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.

@ -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.

@ -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.

@ -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();

@ -2,6 +2,7 @@ using System.Security.Claims;
using System.Text.Encodings.Web;
using System.Threading.Tasks;
using Jellyfin.Api.Constants;
using Jellyfin.Data.Enums;
using MediaBrowser.Controller.Net;
using Microsoft.AspNetCore.Authentication;
using Microsoft.Extensions.Logging;
@ -48,10 +49,10 @@ namespace Jellyfin.Api.Auth
var claims = new[]
{
new Claim(ClaimTypes.Name, user.Name),
new Claim(ClaimTypes.Name, user.Username),
new Claim(
ClaimTypes.Role,
value: user.Policy.IsAdministrator ? UserRoles.Administrator : UserRoles.User)
value: user.HasPermission(PermissionKind.IsAdministrator) ? UserRoles.Administrator : UserRoles.User)
};
var identity = new ClaimsIdentity(claims, Scheme.Name);
var principal = new ClaimsPrincipal(identity);

@ -98,7 +98,7 @@ namespace Jellyfin.Api.Controllers
var user = _userManager.Users.First();
return new StartupUserDto
{
Name = user.Name,
Name = user.Username,
Password = user.Password
};
}
@ -113,7 +113,7 @@ namespace Jellyfin.Api.Controllers
{
var user = _userManager.Users.First();
user.Name = startupUserDto.Name;
user.Username = startupUserDto.Name;
_userManager.UpdateUser(user);

@ -1,8 +1,8 @@
using System;
using System.Collections.Generic;
using MediaBrowser.Model.Configuration;
using Jellyfin.Data.Enums;
namespace MediaBrowser.Controller.Entities
namespace Jellyfin.Data
{
public static class DayOfWeekHelper
{
@ -40,8 +40,7 @@ namespace MediaBrowser.Controller.Entities
days.Contains(DynamicDayOfWeek.Weekday) ||
days.Contains(DynamicDayOfWeek.Everyday))
{
list.Add(DayOfWeek.Tuesday
);
list.Add(DayOfWeek.Tuesday);
}
if (days.Contains(DynamicDayOfWeek.Wednesday) ||

@ -0,0 +1,68 @@
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using Jellyfin.Data.Enums;
namespace Jellyfin.Data.Entities
{
public class AccessSchedule
{
/// <summary>
/// Initializes a new instance of the <see cref="AccessSchedule"/> class.
/// Default constructor. Protected due to required properties, but present because EF needs it.
/// </summary>
protected AccessSchedule()
{
}
/// <summary>
/// Initializes a new instance of the <see cref="AccessSchedule"/> class.
/// </summary>
/// <param name="dayOfWeek">The day of the week.</param>
/// <param name="startHour">The start hour.</param>
/// <param name="endHour">The end hour.</param>
public AccessSchedule(DynamicDayOfWeek dayOfWeek, double startHour, double endHour)
{
DayOfWeek = dayOfWeek;
StartHour = startHour;
EndHour = endHour;
}
/// <summary>
/// Factory method
/// </summary>
/// <param name="dayOfWeek">The day of the week.</param>
/// <param name="startHour">The start hour.</param>
/// <param name="endHour">The end hour.</param>
/// <returns>The newly created instance.</returns>
public static AccessSchedule CreateInstance(DynamicDayOfWeek dayOfWeek, double startHour, double endHour)
{
return new AccessSchedule(dayOfWeek, startHour, endHour);
}
[Key]
[Required]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; protected set; }
/// <summary>
/// Gets or sets the day of week.
/// </summary>
/// <value>The day of week.</value>
[Required]
public DynamicDayOfWeek DayOfWeek { get; set; }
/// <summary>
/// Gets or sets the start hour.
/// </summary>
/// <value>The start hour.</value>
[Required]
public double StartHour { get; set; }
/// <summary>
/// Gets or sets the end hour.
/// </summary>
/// <value>The end hour.</value>
[Required]
public double EndHour { get; set; }
}
}

@ -0,0 +1,25 @@
using System;
using System.ComponentModel.DataAnnotations;
namespace Jellyfin.Data.Entities
{
public class ImageInfo
{
public ImageInfo(string path)
{
Path = path;
LastModified = DateTime.UtcNow;
}
[Key]
[Required]
public int Id { get; protected set; }
[Required]
public string Path { get; set; }
[Required]
public DateTime LastModified { get; set; }
}
}

@ -1,18 +1,20 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Globalization;
using System.Linq;
using System.Runtime.CompilerServices;
using Jellyfin.Data.Enums;
namespace Jellyfin.Data.Entities
{
[Table("User")]
public partial class User
public class User
{
partial void Init();
/// <summary>
/// The values being delimited here are Guids, so commas work as they do not appear in Guids.
/// </summary>
private const char Delimiter = ',';
/// <summary>
/// Default constructor. Protected due to required properties, but present because EF needs it.
@ -23,69 +25,86 @@ namespace Jellyfin.Data.Entities
Permissions = new HashSet<Permission>();
ProviderMappings = new HashSet<ProviderMapping>();
Preferences = new HashSet<Preference>();
Init();
AccessSchedules = new HashSet<AccessSchedule>();
}
/// <summary>
/// Replaces default constructor, since it's protected. Caller assumes responsibility for setting all required values before saving.
/// Public constructor with required data
/// </summary>
public static User CreateUserUnsafe()
/// <param name="username"></param>
/// <param name="mustUpdatePassword"></param>
/// <param name="authenticationProviderId"></param>
/// <param name="invalidLoginAttemptCount"></param>
/// <param name="subtitleMode"></param>
/// <param name="playDefaultAudioTrack"></param>
public User(
string username,
bool mustUpdatePassword,
string authenticationProviderId,
int invalidLoginAttemptCount,
SubtitlePlaybackMode subtitleMode,
bool playDefaultAudioTrack)
{
return new User();
if (string.IsNullOrEmpty(username))
{
throw new ArgumentNullException(nameof(username));
}
if (string.IsNullOrEmpty(authenticationProviderId))
{
throw new ArgumentNullException(nameof(authenticationProviderId));
}
Username = username;
MustUpdatePassword = mustUpdatePassword;
AuthenticationProviderId = authenticationProviderId;
InvalidLoginAttemptCount = invalidLoginAttemptCount;
SubtitleMode = subtitleMode;
PlayDefaultAudioTrack = playDefaultAudioTrack;
Groups = new HashSet<Group>();
Permissions = new HashSet<Permission>();
ProviderMappings = new HashSet<ProviderMapping>();
Preferences = new HashSet<Preference>();
AccessSchedules = new HashSet<AccessSchedule>();
// Set default values
Id = Guid.NewGuid();
DisplayMissingEpisodes = false;
DisplayCollectionsView = false;
HidePlayedInLatest = true;
RememberAudioSelections = true;
RememberSubtitleSelections = true;
EnableNextEpisodeAutoPlay = true;
EnableAutoLogin = false;
}
/// <summary>
/// Public constructor with required data
/// Replaces default constructor, since it's protected. Caller assumes responsibility for setting all required values before saving.
/// </summary>
/// <param name="username"></param>
/// <param name="mustupdatepassword"></param>
/// <param name="audiolanguagepreference"></param>
/// <param name="authenticationproviderid"></param>
/// <param name="invalidloginattemptcount"></param>
/// <param name="subtitlemode"></param>
/// <param name="playdefaultaudiotrack"></param>
public User(string username, bool mustupdatepassword, string audiolanguagepreference, string authenticationproviderid, int invalidloginattemptcount, string subtitlemode, bool playdefaultaudiotrack)
public static User CreateUserUnsafe()
{
if (string.IsNullOrEmpty(username)) throw new ArgumentNullException(nameof(username));
this.Username = username;
this.MustUpdatePassword = mustupdatepassword;
if (string.IsNullOrEmpty(audiolanguagepreference)) throw new ArgumentNullException(nameof(audiolanguagepreference));
this.AudioLanguagePreference = audiolanguagepreference;
if (string.IsNullOrEmpty(authenticationproviderid)) throw new ArgumentNullException(nameof(authenticationproviderid));
this.AuthenticationProviderId = authenticationproviderid;
this.InvalidLoginAttemptCount = invalidloginattemptcount;
if (string.IsNullOrEmpty(subtitlemode)) throw new ArgumentNullException(nameof(subtitlemode));
this.SubtitleMode = subtitlemode;
this.PlayDefaultAudioTrack = playdefaultaudiotrack;
this.Groups = new HashSet<Group>();
this.Permissions = new HashSet<Permission>();
this.ProviderMappings = new HashSet<ProviderMapping>();
this.Preferences = new HashSet<Preference>();
Init();
return new User();
}
/// <summary>
/// Static create function (for use in LINQ queries, etc.)
/// </summary>
/// <param name="username"></param>
/// <param name="mustupdatepassword"></param>
/// <param name="audiolanguagepreference"></param>
/// <param name="authenticationproviderid"></param>
/// <param name="invalidloginattemptcount"></param>
/// <param name="subtitlemode"></param>
/// <param name="playdefaultaudiotrack"></param>
public static User Create(string username, bool mustupdatepassword, string audiolanguagepreference, string authenticationproviderid, int invalidloginattemptcount, string subtitlemode, bool playdefaultaudiotrack)
/// <param name="mustUpdatePassword"></param>
/// <param name="authenticationProviderId"></param>
/// <param name="invalidLoginAttemptCount"></param>
/// <param name="subtitleMode"></param>
/// <param name="playDefaultAudioTrack"></param>
public static User Create(
string username,
bool mustUpdatePassword,
string authenticationProviderId,
int invalidLoginAttemptCount,
SubtitlePlaybackMode subtitleMode,
bool playDefaultAudioTrack)
{
return new User(username, mustupdatepassword, audiolanguagepreference, authenticationproviderid, invalidloginattemptcount, subtitlemode, playdefaultaudiotrack);
return new User(username, mustUpdatePassword, authenticationProviderId, invalidLoginAttemptCount, subtitleMode, playDefaultAudioTrack);
}
/*************************************************************************
@ -97,8 +116,7 @@ namespace Jellyfin.Data.Entities
/// </summary>
[Key]
[Required]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; protected set; }
public Guid Id { get; protected set; }
/// <summary>
/// Required, Max length = 255
@ -115,6 +133,13 @@ namespace Jellyfin.Data.Entities
[StringLength(65535)]
public string Password { get; set; }
/// <summary>
/// Max length = 65535.
/// </summary>
[MaxLength(65535)]
[StringLength(65535)]
public string EasyPassword { get; set; }
/// <summary>
/// Required
/// </summary>
@ -122,9 +147,8 @@ namespace Jellyfin.Data.Entities
public bool MustUpdatePassword { get; set; }
/// <summary>
/// Required, Max length = 255
/// Max length = 255.
/// </summary>
[Required]
[MaxLength(255)]
[StringLength(255)]
public string AudioLanguagePreference { get; set; }
@ -137,12 +161,10 @@ namespace Jellyfin.Data.Entities
[StringLength(255)]
public string AuthenticationProviderId { get; set; }
/// <summary>
/// Max length = 65535
/// </summary>
[MaxLength(65535)]
[StringLength(65535)]
public string GroupedFolders { get; set; }
[Required]
[MaxLength(255)]
[StringLength(255)]
public string PasswordResetProviderId { get; set; }
/// <summary>
/// Required
@ -150,36 +172,17 @@ namespace Jellyfin.Data.Entities
[Required]
public int InvalidLoginAttemptCount { get; set; }
/// <summary>
/// Max length = 65535
/// </summary>
[MaxLength(65535)]
[StringLength(65535)]
public string LatestItemExcludes { get; set; }
public DateTime LastActivityDate { get; set; }
public int? LoginAttemptsBeforeLockout { get; set; }
public DateTime LastLoginDate { get; set; }
/// <summary>
/// Max length = 65535
/// </summary>
[MaxLength(65535)]
[StringLength(65535)]
public string MyMediaExcludes { get; set; }
/// <summary>
/// Max length = 65535
/// </summary>
[MaxLength(65535)]
[StringLength(65535)]
public string OrderedViews { get; set; }
public int? LoginAttemptsBeforeLockout { get; set; }
/// <summary>
/// Required, Max length = 255
/// Required.
/// </summary>
[Required]
[MaxLength(255)]
[StringLength(255)]
public string SubtitleMode { get; set; }
public SubtitlePlaybackMode SubtitleMode { get; set; }
/// <summary>
/// Required
@ -192,21 +195,47 @@ namespace Jellyfin.Data.Entities
/// </summary>
[MaxLength(255)]
[StringLength(255)]
public string SubtitleLanguagePrefernce { get; set; }
public string SubtitleLanguagePreference { get; set; }
[Required]
public bool DisplayMissingEpisodes { get; set; }
[Required]
public bool DisplayCollectionsView { get; set; }
[Required]
public bool EnableLocalPassword { get; set; }
[Required]
public bool HidePlayedInLatest { get; set; }
[Required]
public bool RememberAudioSelections { get; set; }
public bool? DisplayMissingEpisodes { get; set; }
[Required]
public bool RememberSubtitleSelections { get; set; }
public bool? DisplayCollectionsView { get; set; }
[Required]
public bool EnableNextEpisodeAutoPlay { get; set; }
[Required]
public bool EnableAutoLogin { get; set; }
public bool? HidePlayedInLatest { get; set; }
[Required]
public bool EnableUserPreferenceAccess { get; set; }
public bool? RememberAudioSelections { get; set; }
public int? MaxParentalAgeRating { get; set; }
public bool? RememberSubtitleSelections { get; set; }
public int? RemoteClientBitrateLimit { get; set; }
public bool? EnableNextEpisodeAutoPlay { get; set; }
/// <summary>
/// This is a temporary stopgap for until the library db is migrated.
/// This corresponds to the value of the index of this user in the library db.
/// </summary>
[Required]
public long InternalId { get; set; }
public bool? EnableUserPreferenceAccess { get; set; }
public ImageInfo ProfileImage { get; set; }
/// <summary>
/// Required, ConcurrenyToken
@ -224,17 +253,76 @@ namespace Jellyfin.Data.Entities
* Navigation properties
*************************************************************************/
[ForeignKey("Group_Groups_Id")]
public virtual ICollection<Group> Groups { get; protected set; }
public ICollection<Group> Groups { get; protected set; }
[ForeignKey("Permission_Permissions_Id")]
public virtual ICollection<Permission> Permissions { get; protected set; }
public ICollection<Permission> Permissions { get; protected set; }
[ForeignKey("ProviderMapping_ProviderMappings_Id")]
public virtual ICollection<ProviderMapping> ProviderMappings { get; protected set; }
public ICollection<ProviderMapping> ProviderMappings { get; protected set; }
[ForeignKey("Preference_Preferences_Id")]
public virtual ICollection<Preference> Preferences { get; protected set; }
public ICollection<Preference> Preferences { get; protected set; }
public ICollection<AccessSchedule> AccessSchedules { get; protected set; }
public bool HasPermission(PermissionKind permission)
{
return Permissions.Select(p => p.Kind).Contains(permission);
}
public void SetPermission(PermissionKind kind, bool value)
{
var permissionObj = Permissions.First(p => p.Kind == kind);
permissionObj.Value = value;
}
public string[] GetPreference(PreferenceKind preference)
{
return Preferences
.Where(p => p.Kind == preference)
.Select(p => p.Value)
.First()
.Split(Delimiter);
}
public void SetPreference(PreferenceKind preference, string[] values)
{
var pref = Preferences.First(p => p.Kind == preference);
pref.Value = string.Join(Delimiter.ToString(CultureInfo.InvariantCulture), values);
}
public bool IsParentalScheduleAllowed()
{
var schedules = this.AccessSchedules;
return schedules.Count == 0 || schedules.Any(i => IsParentalScheduleAllowed(i, DateTime.Now));
}
public bool IsFolderGrouped(Guid id)
{
return GetPreference(PreferenceKind.GroupedFolders).Any(i => new Guid(i) == id);
}
private bool IsParentalScheduleAllowed(AccessSchedule schedule, DateTime date)
{
if (date.Kind != DateTimeKind.Utc)
{
throw new ArgumentException("Utc date expected");
}
var localTime = date.ToLocalTime();
return DayOfWeekHelper.GetDaysOfWeek(schedule.DayOfWeek).Contains(localTime.DayOfWeek) &&
IsWithinTime(schedule, localTime);
}
private bool IsWithinTime(AccessSchedule schedule, DateTime localTime)
{
var hour = localTime.TimeOfDay.TotalHours;
return hour >= schedule.StartHour && hour <= schedule.EndHour;
}
}
}

@ -1,6 +1,6 @@
#pragma warning disable CS1591
namespace MediaBrowser.Model.Configuration
namespace Jellyfin.Data.Enums
{
public enum DynamicDayOfWeek
{

@ -1,14 +1,11 @@
using System;
namespace Jellyfin.Data.Enums
{
public enum PermissionKind : Int32
public enum PermissionKind
{
IsAdministrator,
IsHidden,
IsDisabled,
BlockUnrateditems,
EnbleSharedDeviceControl,
EnableSharedDeviceControl,
EnableRemoteAccess,
EnableLiveTvManagement,
EnableLiveTvAccess,
@ -23,6 +20,8 @@ namespace Jellyfin.Data.Enums
EnableAllChannels,
EnableAllFolders,
EnablePublicSharing,
AccessSchedules
EnableRemoteControlOfOtherUsers,
EnablePlaybackRemuxing,
ForceRemoteSourceTranscoding
}
}

@ -2,14 +2,19 @@ using System;
namespace Jellyfin.Data.Enums
{
public enum PreferenceKind : Int32
public enum PreferenceKind
{
MaxParentalRating,
BlockedTags,
RemoteClientBitrateLimit,
BlockedChannels,
BlockedMediaFolders,
EnabledDevices,
EnabledChannels,
EnabledFolders,
EnableContentDeletionFromFolders
EnableContentDeletionFromFolders,
LatestItemExcludes,
MyMediaExcludes,
GroupedFolders,
BlockUnratedItems,
OrderedViews
}
}

@ -1,6 +1,6 @@
#pragma warning disable CS1591
#pragma warning disable CS1591
namespace MediaBrowser.Model.Configuration
namespace Jellyfin.Data.Enums
{
public enum SubtitlePlaybackMode
{

@ -1,6 +1,6 @@
#pragma warning disable CS1591
namespace MediaBrowser.Model.Configuration
namespace Jellyfin.Data.Enums
{
public enum UnratedItem
{

@ -20,7 +20,7 @@ namespace Jellyfin.Server.Implementations
public virtual DbSet<Permission> Permissions { get; set; }
public virtual DbSet<Preference> Preferences { get; set; }
public virtual DbSet<ProviderMapping> ProviderMappings { get; set; }
public virtual DbSet<User> Users { get; set; }
public virtual DbSet<Data.Entities.User> Users { get; set; }
/*public virtual DbSet<Artwork> Artwork { get; set; }
public virtual DbSet<Book> Books { get; set; }
public virtual DbSet<BookMetadata> BookMetadata { get; set; }

@ -5,10 +5,9 @@ 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
namespace Jellyfin.Server.Implementations.User
{
/// <summary>
/// The default authentication provider.
@ -43,17 +42,17 @@ namespace Emby.Server.Implementations.Library
/// <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)
public Task<ProviderAuthenticationResult> Authenticate(string username, string password, Data.Entities.User resolvedUser)
{
if (resolvedUser == null)
{
throw new AuthenticationException($"Specified user does not exist.");
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))
if (!HasPassword(resolvedUser))
{
return Task.FromResult(new ProviderAuthenticationResult
{
@ -61,7 +60,7 @@ namespace Emby.Server.Implementations.Library
});
}
byte[] passwordbytes = Encoding.UTF8.GetBytes(password);
byte[] passwordBytes = Encoding.UTF8.GetBytes(password);
PasswordHash readyHash = PasswordHash.Parse(resolvedUser.Password);
if (_cryptographyProvider.GetSupportedHashMethods().Contains(readyHash.Id)
@ -69,7 +68,7 @@ namespace Emby.Server.Implementations.Library
{
byte[] calculatedHash = _cryptographyProvider.ComputeHash(
readyHash.Id,
passwordbytes,
passwordBytes,
readyHash.Salt.ToArray());
if (readyHash.Hash.SequenceEqual(calculatedHash))
@ -94,11 +93,11 @@ namespace Emby.Server.Implementations.Library
}
/// <inheritdoc />
public bool HasPassword(User user)
public bool HasPassword(Data.Entities.User user)
=> !string.IsNullOrEmpty(user.Password);
/// <inheritdoc />
public Task ChangePassword(User user, string newPassword)
public Task ChangePassword(Data.Entities.User user, string newPassword)
{
if (string.IsNullOrEmpty(newPassword))
{
@ -113,7 +112,7 @@ namespace Emby.Server.Implementations.Library
}
/// <inheritdoc />
public void ChangeEasyPassword(User user, string newPassword, string newPasswordHash)
public void ChangeEasyPassword(Data.Entities.User user, string newPassword, string newPasswordHash)
{
if (newPassword != null)
{
@ -129,7 +128,7 @@ namespace Emby.Server.Implementations.Library
}
/// <inheritdoc />
public string GetEasyPasswordHash(User user)
public string GetEasyPasswordHash(Data.Entities.User user)
{
return string.IsNullOrEmpty(user.EasyPassword)
? null
@ -137,9 +136,12 @@ namespace Emby.Server.Implementations.Library
}
/// <summary>
/// Gets the hashed string.
/// Hashes the provided string.
/// </summary>
public string GetHashedString(User user, string str)
/// <param name="user">The user.</param>
/// <param name="str">The string to hash.</param>
/// <returns>The hashed string.</returns>
public string GetHashedString(Data.Entities.User user, string str)
{
if (string.IsNullOrEmpty(user.Password))
{
@ -159,7 +161,13 @@ namespace Emby.Server.Implementations.Library
passwordHash.Parameters.ToDictionary(x => x.Key, y => y.Value)).ToString();
}
public ReadOnlySpan<byte> GetHashed(User user, string str)
/// <summary>
/// Hashes the provided string.
/// </summary>
/// <param name="user">The user.</param>
/// <param name="str">The string to hash.</param>
/// <returns>The hashed string.</returns>
public ReadOnlySpan<byte> GetHashed(Data.Entities.User user, string str)
{
if (string.IsNullOrEmpty(user.Password))
{

@ -10,7 +10,7 @@ using MediaBrowser.Controller.Library;
using MediaBrowser.Model.Serialization;
using MediaBrowser.Model.Users;
namespace Emby.Server.Implementations.Library
namespace Jellyfin.Server.Implementations.User
{
/// <summary>
/// The default password reset provider.
@ -52,17 +52,17 @@ namespace Emby.Server.Implementations.Library
public async Task<PinRedeemResult> RedeemPasswordResetPin(string pin)
{
SerializablePasswordReset spr;
List<string> usersreset = new List<string>();
foreach (var resetfile in Directory.EnumerateFiles(_passwordResetFileBaseDir, $"{BaseResetFileName}*"))
List<string> usersReset = new List<string>();
foreach (var resetFile in Directory.EnumerateFiles(_passwordResetFileBaseDir, $"{BaseResetFileName}*"))
{
using (var str = File.OpenRead(resetfile))
await using (var str = File.OpenRead(resetFile))
{
spr = await _jsonSerializer.DeserializeFromStreamAsync<SerializablePasswordReset>(str).ConfigureAwait(false);
}
if (spr.ExpirationDate < DateTime.Now)
{
File.Delete(resetfile);
File.Delete(resetFile);
}
else if (string.Equals(
spr.Pin.Replace("-", string.Empty, StringComparison.Ordinal),
@ -76,29 +76,27 @@ namespace Emby.Server.Implementations.Library
}
await _userManager.ChangePassword(resetUser, pin).ConfigureAwait(false);
usersreset.Add(resetUser.Name);
File.Delete(resetfile);
usersReset.Add(resetUser.Username);
File.Delete(resetFile);
}
}
if (usersreset.Count < 1)
if (usersReset.Count < 1)
{
throw new ResourceNotFoundException($"No Users found with a password reset request matching pin {pin}");
}
else
return new PinRedeemResult
{
return new PinRedeemResult
{
Success = true,
UsersReset = usersreset.ToArray()
};
}
Success = true,
UsersReset = usersReset.ToArray()
};
}
/// <inheritdoc />
public async Task<ForgotPasswordResult> StartForgotPasswordProcess(MediaBrowser.Controller.Entities.User user, bool isInNetwork)
public async Task<ForgotPasswordResult> StartForgotPasswordProcess(Jellyfin.Data.Entities.User user, bool isInNetwork)
{
string pin = string.Empty;
string pin;
using (var cryptoRandom = RandomNumberGenerator.Create())
{
byte[] bytes = new byte[4];
@ -107,26 +105,14 @@ namespace Emby.Server.Implementations.Library
}
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);
}
user.EasyPassword = pin;
await _userManager.UpdateUserAsync(user).ConfigureAwait(false);
return new ForgotPasswordResult
{
Action = ForgotPasswordAction.PinCode,
PinExpirationDate = expireTime,
PinFile = filePath
};
}

@ -0,0 +1,65 @@
#pragma warning disable CS1591
using System.Threading.Tasks;
using Jellyfin.Data.Enums;
using MediaBrowser.Controller.Devices;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Plugins;
using MediaBrowser.Controller.Security;
using MediaBrowser.Controller.Session;
using MediaBrowser.Model.Events;
namespace Jellyfin.Server.Implementations.User
{
public sealed class DeviceAccessEntryPoint : IServerEntryPoint
{
private readonly IUserManager _userManager;
private readonly IAuthenticationRepository _authRepo;
private readonly IDeviceManager _deviceManager;
private readonly ISessionManager _sessionManager;
public DeviceAccessEntryPoint(IUserManager userManager, IAuthenticationRepository authRepo, IDeviceManager deviceManager, ISessionManager sessionManager)
{
_userManager = userManager;
_authRepo = authRepo;
_deviceManager = deviceManager;
_sessionManager = sessionManager;
}
public Task RunAsync()
{
_userManager.OnUserUpdated += OnUserUpdated;
return Task.CompletedTask;
}
private void OnUserUpdated(object sender, GenericEventArgs<Data.Entities.User> e)
{
var user = e.Argument;
if (!user.HasPermission(PermissionKind.EnableAllDevices))
{
UpdateDeviceAccess(user);
}
}
public void Dispose()
{
}
private void UpdateDeviceAccess(Data.Entities.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);
}
}
}
}
}

@ -1,8 +1,7 @@
using System.Threading.Tasks;
using MediaBrowser.Controller.Authentication;
using MediaBrowser.Controller.Entities;
namespace Emby.Server.Implementations.Library
namespace Jellyfin.Server.Implementations.User
{
/// <summary>
/// An invalid authentication provider.
@ -22,31 +21,25 @@ namespace Emby.Server.Implementations.Library
}
/// <inheritdoc />
public bool HasPassword(User user)
public bool HasPassword(Data.Entities.User user)
{
return true;
}
/// <inheritdoc />
public Task ChangePassword(User user, string newPassword)
public Task ChangePassword(Data.Entities.User user, string newPassword)
{
return Task.CompletedTask;
}
/// <inheritdoc />
public void ChangeEasyPassword(User user, string newPassword, string newPasswordHash)
public void ChangeEasyPassword(Data.Entities.User user, string newPassword, string newPasswordHash)
{
// Nothing here
}
/// <inheritdoc />
public string GetPasswordHash(User user)
{
return string.Empty;
}
/// <inheritdoc />
public string GetEasyPasswordHash(User user)
public string GetEasyPasswordHash(Data.Entities.User user)
{
return string.Empty;
}

@ -0,0 +1,749 @@
#pragma warning disable CS0067
#pragma warning disable CS1591
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Jellyfin.Data.Enums;
using MediaBrowser.Common.Cryptography;
using MediaBrowser.Common.Net;
using MediaBrowser.Controller.Authentication;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Net;
using MediaBrowser.Model.Configuration;
using MediaBrowser.Model.Cryptography;
using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Events;
using MediaBrowser.Model.Users;
using Microsoft.Extensions.Logging;
namespace Jellyfin.Server.Implementations.User
{
public class UserManager : IUserManager
{
private readonly JellyfinDbProvider _dbProvider;
private readonly ICryptoProvider _cryptoProvider;
private readonly INetworkManager _networkManager;
private readonly ILogger<IUserManager> _logger;
private IAuthenticationProvider[] _authenticationProviders;
private DefaultAuthenticationProvider _defaultAuthenticationProvider;
private InvalidAuthProvider _invalidAuthProvider;
private IPasswordResetProvider[] _passwordResetProviders;
private DefaultPasswordResetProvider _defaultPasswordResetProvider;
public UserManager(
JellyfinDbProvider dbProvider,
ICryptoProvider cryptoProvider,
INetworkManager networkManager,
ILogger<IUserManager> logger)
{
_dbProvider = dbProvider;
_cryptoProvider = cryptoProvider;
_networkManager = networkManager;
_logger = logger;
}
public event EventHandler<GenericEventArgs<Data.Entities.User>> OnUserPasswordChanged;
/// <inheritdoc/>
public event EventHandler<GenericEventArgs<Data.Entities.User>> OnUserUpdated;
/// <inheritdoc/>
public event EventHandler<GenericEventArgs<Data.Entities.User>> OnUserCreated;
/// <inheritdoc/>
public event EventHandler<GenericEventArgs<Data.Entities.User>> OnUserDeleted;
public event EventHandler<GenericEventArgs<Data.Entities.User>> OnUserLockedOut;
public IEnumerable<Data.Entities.User> Users
{
get
{
using var dbContext = _dbProvider.CreateContext();
return dbContext.Users;
}
}
public IEnumerable<Guid> UsersIds
{
get
{
using var dbContext = _dbProvider.CreateContext();
return dbContext.Users.Select(u => u.Id);
}
}
public Data.Entities.User GetUserById(Guid id)
{
if (id == Guid.Empty)
{
throw new ArgumentException("Guid can't be empty", nameof(id));
}
using var dbContext = _dbProvider.CreateContext();
return dbContext.Users.Find(id);
}
public Data.Entities.User GetUserByName(string name)
{
if (string.IsNullOrWhiteSpace(name))
{
throw new ArgumentException("Invalid username", nameof(name));
}
using var dbContext = _dbProvider.CreateContext();
return dbContext.Users.FirstOrDefault(u =>
string.Equals(u.Username, name, StringComparison.OrdinalIgnoreCase));
}
public async Task RenameUser(Data.Entities.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.Username.Equals(newName, StringComparison.Ordinal))
{
throw new ArgumentException("The new and old names must be different.");
}
if (Users.Any(
u => u.Id != user.Id && u.Username.Equals(newName, StringComparison.OrdinalIgnoreCase)))
{
throw new ArgumentException(string.Format(
CultureInfo.InvariantCulture,
"A user with the name '{0}' already exists.",
newName));
}
user.Username = newName;
await UpdateUserAsync(user).ConfigureAwait(false);
OnUserUpdated?.Invoke(this, new GenericEventArgs<Data.Entities.User>(user));
}
public void UpdateUser(Data.Entities.User user)
{
using var dbContext = _dbProvider.CreateContext();
dbContext.Users.Update(user);
dbContext.SaveChanges();
}
public async Task UpdateUserAsync(Data.Entities.User user)
{
await using var dbContext = _dbProvider.CreateContext();
dbContext.Users.Update(user);
await dbContext.SaveChangesAsync().ConfigureAwait(false);
}
public Data.Entities.User CreateUser(string name)
{
using var dbContext = _dbProvider.CreateContext();
var newUser = CreateUserObject(name);
dbContext.Users.Add(newUser);
dbContext.SaveChanges();
return newUser;
}
public void DeleteUser(Data.Entities.User user)
{
if (user == null)
{
throw new ArgumentNullException(nameof(user));
}
using var dbContext = _dbProvider.CreateContext();
if (!dbContext.Users.Contains(user))
{
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.Username,
user.Id));
}
if (dbContext.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.Username));
}
if (user.HasPermission(PermissionKind.IsAdministrator)
&& Users.Count(i => i.HasPermission(PermissionKind.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.Username),
nameof(user));
}
dbContext.Users.Remove(user);
dbContext.SaveChanges();
}
public Task ResetPassword(Data.Entities.User user)
{
return ChangePassword(user, string.Empty);
}
public void ResetEasyPassword(Data.Entities.User user)
{
ChangeEasyPassword(user, string.Empty, null);
}
public async Task ChangePassword(Data.Entities.User user, string newPassword)
{
if (user == null)
{
throw new ArgumentNullException(nameof(user));
}
await GetAuthenticationProvider(user).ChangePassword(user, newPassword).ConfigureAwait(false);
await UpdateUserAsync(user).ConfigureAwait(false);
OnUserPasswordChanged?.Invoke(this, new GenericEventArgs<Data.Entities.User>(user));
}
public void ChangeEasyPassword(Data.Entities.User user, string newPassword, string newPasswordSha1)
{
if (user == null)
{
throw new ArgumentNullException(nameof(user));
}
GetAuthenticationProvider(user).ChangeEasyPassword(user, newPassword, newPasswordSha1);
UpdateUser(user);
OnUserPasswordChanged?.Invoke(this, new GenericEventArgs<Data.Entities.User>(user));
}
public UserDto GetUserDto(Data.Entities.User user, string remoteEndPoint = null)
{
return new UserDto
{
Id = user.Id,
HasPassword = user.Password == null,
EnableAutoLogin = user.EnableAutoLogin,
LastLoginDate = user.LastLoginDate,
LastActivityDate = user.LastActivityDate,
Configuration = new UserConfiguration
{
SubtitleMode = user.SubtitleMode,
HidePlayedInLatest = user.HidePlayedInLatest,
EnableLocalPassword = user.EnableLocalPassword,
PlayDefaultAudioTrack = user.PlayDefaultAudioTrack,
DisplayCollectionsView = user.DisplayCollectionsView,
DisplayMissingEpisodes = user.DisplayMissingEpisodes,
AudioLanguagePreference = user.AudioLanguagePreference,
RememberAudioSelections = user.RememberAudioSelections,
EnableNextEpisodeAutoPlay = user.EnableNextEpisodeAutoPlay,
RememberSubtitleSelections = user.RememberSubtitleSelections,
SubtitleLanguagePreference = user.SubtitleLanguagePreference,
OrderedViews = user.GetPreference(PreferenceKind.OrderedViews),
GroupedFolders = user.GetPreference(PreferenceKind.GroupedFolders),
MyMediaExcludes = user.GetPreference(PreferenceKind.MyMediaExcludes),
LatestItemsExcludes = user.GetPreference(PreferenceKind.LatestItemExcludes)
},
Policy = new UserPolicy
{
MaxParentalRating = user.MaxParentalAgeRating,
EnableUserPreferenceAccess = user.EnableUserPreferenceAccess,
RemoteClientBitrateLimit = user.RemoteClientBitrateLimit.GetValueOrDefault(),
AuthenticatioIsnProviderId = user.AuthenticationProviderId,
PasswordResetProviderId = user.PasswordResetProviderId,
InvalidLoginAttemptCount = user.InvalidLoginAttemptCount,
LoginAttemptsBeforeLockout = user.LoginAttemptsBeforeLockout.GetValueOrDefault(),
IsAdministrator = user.HasPermission(PermissionKind.IsAdministrator),
IsHidden = user.HasPermission(PermissionKind.IsHidden),
IsDisabled = user.HasPermission(PermissionKind.IsDisabled),
EnableSharedDeviceControl = user.HasPermission(PermissionKind.EnableSharedDeviceControl),
EnableRemoteAccess = user.HasPermission(PermissionKind.EnableRemoteAccess),
EnableLiveTvManagement = user.HasPermission(PermissionKind.EnableLiveTvManagement),
EnableLiveTvAccess = user.HasPermission(PermissionKind.EnableLiveTvAccess),
EnableMediaPlayback = user.HasPermission(PermissionKind.EnableMediaPlayback),
EnableAudioPlaybackTranscoding = user.HasPermission(PermissionKind.EnableAudioPlaybackTranscoding),
EnableVideoPlaybackTranscoding = user.HasPermission(PermissionKind.EnableVideoPlaybackTranscoding),
EnableContentDeletion = user.HasPermission(PermissionKind.EnableContentDeletion),
EnableContentDownloading = user.HasPermission(PermissionKind.EnableContentDownloading),
EnableSyncTranscoding = user.HasPermission(PermissionKind.EnableSyncTranscoding),
EnableMediaConversion = user.HasPermission(PermissionKind.EnableMediaConversion),
EnableAllChannels = user.HasPermission(PermissionKind.EnableAllChannels),
EnableAllDevices = user.HasPermission(PermissionKind.EnableAllDevices),
EnableAllFolders = user.HasPermission(PermissionKind.EnableAllFolders),
EnableRemoteControlOfOtherUsers = user.HasPermission(PermissionKind.EnableRemoteControlOfOtherUsers),
EnablePlaybackRemuxing = user.HasPermission(PermissionKind.EnablePlaybackRemuxing),
ForceRemoteSourceTranscoding = user.HasPermission(PermissionKind.ForceRemoteSourceTranscoding),
EnablePublicSharing = user.HasPermission(PermissionKind.EnablePublicSharing),
AccessSchedules = user.AccessSchedules.ToArray(),
BlockedTags = user.GetPreference(PreferenceKind.BlockedTags),
EnabledChannels = user.GetPreference(PreferenceKind.EnabledChannels),
EnabledDevices = user.GetPreference(PreferenceKind.EnabledDevices),
EnabledFolders = user.GetPreference(PreferenceKind.EnabledFolders),
EnableContentDeletionFromFolders = user.GetPreference(PreferenceKind.EnableContentDeletionFromFolders)
}
};
}
public async Task<Data.Entities.User> AuthenticateUser(
string username,
string password,
string passwordSha1,
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.Username, StringComparison.OrdinalIgnoreCase));
bool success;
IAuthenticationProvider authenticationProvider;
if (user != null)
{
var authResult = await AuthenticateLocalUser(username, password, user, remoteEndPoint)
.ConfigureAwait(false);
authenticationProvider = authResult.authenticationProvider;
success = authResult.success;
}
else
{
var authResult = await AuthenticateLocalUser(username, password, 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.Username, StringComparison.OrdinalIgnoreCase));
if (authenticationProvider is IHasNewUserPolicy hasNewUserPolicy)
{
UpdatePolicy(user.Id, hasNewUserPolicy.GetNewUserPolicy());
await UpdateUserAsync(user).ConfigureAwait(false);
}
}
}
if (success && user != null && authenticationProvider != null)
{
var providerId = authenticationProvider.GetType().FullName;
if (!string.Equals(providerId, user.AuthenticationProviderId, StringComparison.OrdinalIgnoreCase))
{
user.AuthenticationProviderId = providerId;
await UpdateUserAsync(user).ConfigureAwait(false);
}
}
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.HasPermission(PermissionKind.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.Username} account is currently disabled. Please consult with your administrator.");
}
if (!user.HasPermission(PermissionKind.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.Username);
}
else
{
IncrementInvalidLoginAttemptCount(user);
_logger.LogInformation(
"Authentication request for {UserName} has been denied (IP: {IP}).",
user.Username,
remoteEndPoint);
}
return success ? user : null;
}
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);
}
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 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();
}
public NameIdPair[] GetAuthenticationProviders()
{
return _authenticationProviders
.Where(provider => provider.IsEnabled)
.OrderBy(i => i is DefaultAuthenticationProvider ? 0 : 1)
.ThenBy(i => i.Name)
.Select(i => new NameIdPair
{
Name = i.Name,
Id = i.GetType().FullName
})
.ToArray();
}
public NameIdPair[] GetPasswordResetProviders()
{
return _passwordResetProviders
.Where(provider => provider.IsEnabled)
.OrderBy(i => i is DefaultPasswordResetProvider ? 0 : 1)
.ThenBy(i => i.Name)
.Select(i => new NameIdPair
{
Name = i.Name,
Id = i.GetType().FullName
})
.ToArray();
}
public void UpdateConfiguration(Guid userId, UserConfiguration config)
{
var user = GetUserById(userId);
user.SubtitleMode = config.SubtitleMode;
user.HidePlayedInLatest = config.HidePlayedInLatest;
user.EnableLocalPassword = config.EnableLocalPassword;
user.PlayDefaultAudioTrack = config.PlayDefaultAudioTrack;
user.DisplayCollectionsView = config.DisplayCollectionsView;
user.DisplayMissingEpisodes = config.DisplayMissingEpisodes;
user.AudioLanguagePreference = config.AudioLanguagePreference;
user.RememberAudioSelections = config.RememberAudioSelections;
user.EnableNextEpisodeAutoPlay = config.EnableNextEpisodeAutoPlay;
user.RememberSubtitleSelections = config.RememberSubtitleSelections;
user.SubtitleLanguagePreference = config.SubtitleLanguagePreference;
user.SetPreference(PreferenceKind.OrderedViews, config.OrderedViews);
user.SetPreference(PreferenceKind.GroupedFolders, config.GroupedFolders);
user.SetPreference(PreferenceKind.MyMediaExcludes, config.MyMediaExcludes);
user.SetPreference(PreferenceKind.LatestItemExcludes, config.LatestItemsExcludes);
UpdateUser(user);
}
public void UpdatePolicy(Guid userId, UserPolicy policy)
{
var user = GetUserById(userId);
user.MaxParentalAgeRating = policy.MaxParentalRating;
user.EnableUserPreferenceAccess = policy.EnableUserPreferenceAccess;
user.RemoteClientBitrateLimit = policy.RemoteClientBitrateLimit;
user.AuthenticationProviderId = policy.AuthenticatioIsnProviderId;
user.PasswordResetProviderId = policy.PasswordResetProviderId;
user.InvalidLoginAttemptCount = policy.InvalidLoginAttemptCount;
user.LoginAttemptsBeforeLockout = policy.LoginAttemptsBeforeLockout == -1
? null
: new int?(policy.LoginAttemptsBeforeLockout);
user.SetPermission(PermissionKind.IsAdministrator, policy.IsAdministrator);
user.SetPermission(PermissionKind.IsHidden, policy.IsHidden);
user.SetPermission(PermissionKind.IsDisabled, policy.IsDisabled);
user.SetPermission(PermissionKind.EnableSharedDeviceControl, policy.EnableSharedDeviceControl);
user.SetPermission(PermissionKind.EnableRemoteAccess, policy.EnableRemoteAccess);
user.SetPermission(PermissionKind.EnableLiveTvManagement, policy.EnableLiveTvManagement);
user.SetPermission(PermissionKind.EnableLiveTvAccess, policy.EnableLiveTvAccess);
user.SetPermission(PermissionKind.EnableMediaPlayback, policy.EnableMediaPlayback);
user.SetPermission(PermissionKind.EnableAudioPlaybackTranscoding, policy.EnableAudioPlaybackTranscoding);
user.SetPermission(PermissionKind.EnableVideoPlaybackTranscoding, policy.EnableVideoPlaybackTranscoding);
user.SetPermission(PermissionKind.EnableContentDeletion, policy.EnableContentDeletion);
user.SetPermission(PermissionKind.EnableContentDownloading, policy.EnableContentDownloading);
user.SetPermission(PermissionKind.EnableSyncTranscoding, policy.EnableSyncTranscoding);
user.SetPermission(PermissionKind.EnableMediaConversion, policy.EnableMediaConversion);
user.SetPermission(PermissionKind.EnableAllChannels, policy.EnableAllChannels);
user.SetPermission(PermissionKind.EnableAllDevices, policy.EnableAllDevices);
user.SetPermission(PermissionKind.EnableAllFolders, policy.EnableAllFolders);
user.SetPermission(PermissionKind.EnableRemoteControlOfOtherUsers, policy.EnableRemoteControlOfOtherUsers);
user.SetPermission(PermissionKind.EnablePlaybackRemuxing, policy.EnablePlaybackRemuxing);
user.SetPermission(PermissionKind.ForceRemoteSourceTranscoding, policy.ForceRemoteSourceTranscoding);
user.SetPermission(PermissionKind.EnablePublicSharing, policy.EnablePublicSharing);
user.AccessSchedules.Clear();
foreach (var policyAccessSchedule in policy.AccessSchedules)
{
user.AccessSchedules.Add(policyAccessSchedule);
}
user.SetPreference(PreferenceKind.BlockedTags, policy.BlockedTags);
user.SetPreference(PreferenceKind.EnabledChannels, policy.EnabledChannels);
user.SetPreference(PreferenceKind.EnabledDevices, policy.EnabledDevices);
user.SetPreference(PreferenceKind.EnabledFolders, policy.EnabledFolders);
user.SetPreference(PreferenceKind.EnableContentDeletionFromFolders, policy.EnableContentDeletionFromFolders);
}
private Data.Entities.User CreateUserObject(string name)
{
return new Data.Entities.User(
username: name,
mustUpdatePassword: false,
authenticationProviderId: _defaultAuthenticationProvider.GetType().FullName,
invalidLoginAttemptCount: -1,
subtitleMode: SubtitlePlaybackMode.Default,
playDefaultAudioTrack: true);
}
private IAuthenticationProvider GetAuthenticationProvider(Data.Entities.User user)
{
return GetAuthenticationProviders(user)[0];
}
private IPasswordResetProvider GetPasswordResetProvider(Data.Entities.User user)
{
return GetPasswordResetProviders(user)[0];
}
private IList<IAuthenticationProvider> GetAuthenticationProviders(Data.Entities.User user)
{
var authenticationProviderId = user?.AuthenticationProviderId;
var providers = _authenticationProviders.Where(i => i.IsEnabled).ToList();
if (!string.IsNullOrEmpty(authenticationProviderId))
{
providers = providers.Where(i => string.Equals(authenticationProviderId, i.GetType().FullName, StringComparison.OrdinalIgnoreCase)).ToList();
}
if (providers.Count == 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?.Username,
user?.AuthenticationProviderId);
providers = new List<IAuthenticationProvider>
{
_invalidAuthProvider
};
}
return providers;
}
private IList<IPasswordResetProvider> GetPasswordResetProviders(Data.Entities.User user)
{
var passwordResetProviderId = user?.PasswordResetProviderId;
var providers = _passwordResetProviders.Where(i => i.IsEnabled).ToArray();
if (!string.IsNullOrEmpty(passwordResetProviderId))
{
providers = providers.Where(i =>
string.Equals(passwordResetProviderId, i.GetType().FullName, StringComparison.OrdinalIgnoreCase))
.ToArray();
}
if (providers.Length == 0)
{
providers = new IPasswordResetProvider[]
{
_defaultPasswordResetProvider
};
}
return providers;
}
private async Task<(IAuthenticationProvider authenticationProvider, string username, bool success)>
AuthenticateLocalUser(
string username,
string password,
Jellyfin.Data.Entities.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?.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 async Task<(string username, bool success)> AuthenticateWithProvider(
IAuthenticationProvider provider,
string username,
string password,
Data.Entities.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 void IncrementInvalidLoginAttemptCount(Data.Entities.User user)
{
int invalidLogins = user.InvalidLoginAttemptCount;
int? maxInvalidLogins = user.LoginAttemptsBeforeLockout;
if (maxInvalidLogins.HasValue
&& invalidLogins >= maxInvalidLogins)
{
user.SetPermission(PermissionKind.IsDisabled, true);
OnUserLockedOut?.Invoke(this, new GenericEventArgs<Data.Entities.User>(user));
_logger.LogWarning(
"Disabling user {UserName} due to {Attempts} unsuccessful login attempts.",
user.Username,
invalidLogins);
}
UpdateUser(user);
}
private void ResetInvalidLoginAttemptCount(Data.Entities.User user)
{
user.InvalidLoginAttemptCount = 0;
}
}
}

@ -0,0 +1,8 @@
#pragma warning disable CS1591
namespace Jellyfin.Server.Migrations.Routines
{
public class MigrateUserDb
{
}
}

@ -1,6 +1,7 @@
using System;
using System.IO;
using System.Linq;
using Jellyfin.Data.Enums;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Dto;
using MediaBrowser.Controller.Entities;
@ -94,8 +95,8 @@ namespace MediaBrowser.Api
var authenticatedUser = auth.User;
// If they're going to update the record of another user, they must be an administrator
if ((!userId.Equals(auth.UserId) && !authenticatedUser.Policy.IsAdministrator)
|| (restrictUserPreferences && !authenticatedUser.Policy.EnableUserPreferenceAccess))
if ((!userId.Equals(auth.UserId) && !authenticatedUser.HasPermission(PermissionKind.IsAdministrator))
|| (restrictUserPreferences && !authenticatedUser.EnableUserPreferenceAccess))
{
throw new SecurityException("Unauthorized access.");
}

@ -220,7 +220,7 @@ namespace MediaBrowser.Api
return result;
}
private InternalItemsQuery GetItemsQuery(GetQueryFiltersLegacy request, User user)
private InternalItemsQuery GetItemsQuery(GetQueryFiltersLegacy request, Jellyfin.Data.Entities.User user)
{
var query = new InternalItemsQuery
{

@ -20,6 +20,7 @@ using MediaBrowser.Model.IO;
using MediaBrowser.Model.Services;
using Microsoft.Extensions.Logging;
using Microsoft.Net.Http.Headers;
using User = Jellyfin.Data.Entities.User;
namespace MediaBrowser.Api.Images
{
@ -399,14 +400,14 @@ namespace MediaBrowser.Api.Images
{
var item = _userManager.GetUserById(request.Id);
return GetImage(request, Guid.Empty, item, false);
return GetImage(request, item, false);
}
public object Head(GetUserImage request)
{
var item = _userManager.GetUserById(request.Id);
return GetImage(request, Guid.Empty, item, true);
return GetImage(request, item, true);
}
public object Get(GetItemByNameImage request)
@ -439,9 +440,9 @@ namespace MediaBrowser.Api.Images
request.Type = Enum.Parse<ImageType>(GetPathValue(3).ToString(), true);
var item = _userManager.GetUserById(id);
var user = _userManager.GetUserById(id);
return PostImage(item, request.RequestStream, request.Type, Request.ContentType);
return PostImage(user, request.RequestStream, Request.ContentType);
}
/// <summary>
@ -468,9 +469,9 @@ namespace MediaBrowser.Api.Images
var userId = request.Id;
AssertCanUpdateUser(_authContext, _userManager, userId, true);
var item = _userManager.GetUserById(userId);
var user = _userManager.GetUserById(userId);
item.DeleteImage(request.Type, request.Index ?? 0);
user.ProfileImage = null;
}
/// <summary>
@ -555,18 +556,17 @@ namespace MediaBrowser.Api.Images
var imageInfo = GetImageInfo(request, item);
if (imageInfo == null)
{
var displayText = item == null ? itemId.ToString() : item.Name;
throw new ResourceNotFoundException(string.Format("{0} does not have an image of type {1}", displayText, request.Type));
throw new ResourceNotFoundException(string.Format("{0} does not have an image of type {1}", item.Name, request.Type));
}
bool cropwhitespace;
bool cropWhitespace;
if (request.CropWhitespace.HasValue)
{
cropwhitespace = request.CropWhitespace.Value;
cropWhitespace = request.CropWhitespace.Value;
}
else
{
cropwhitespace = request.Type == ImageType.Logo || request.Type == ImageType.Art;
cropWhitespace = request.Type == ImageType.Logo || request.Type == ImageType.Art;
}
var outputFormats = GetOutputFormats(request);
@ -589,13 +589,94 @@ namespace MediaBrowser.Api.Images
itemId,
request,
imageInfo,
cropwhitespace,
cropWhitespace,
outputFormats,
cacheDuration,
responseHeaders,
isHeadRequest);
}
public Task<object> GetImage(ImageRequest request, User user, bool isHeadRequest)
{
var imageInfo = GetImageInfo(request, user);
TimeSpan? cacheDuration = null;
if (!string.IsNullOrEmpty(request.Tag))
{
cacheDuration = TimeSpan.FromDays(365);
}
var responseHeaders = new Dictionary<string, string>
{
{"transferMode.dlna.org", "Interactive"},
{"realTimeInfo.dlna.org", "DLNA.ORG_TLAG=*"}
};
var outputFormats = GetOutputFormats(request);
return GetImageResult(
user,
user.Id,
request,
imageInfo,
false,
outputFormats,
cacheDuration,
responseHeaders,
isHeadRequest);
}
private async Task<object> GetImageResult(
User user,
Guid itemId,
ImageRequest request,
ItemImageInfo info,
bool cropWhitespace,
IReadOnlyCollection<ImageFormat> supportedFormats,
TimeSpan? cacheDuration,
IDictionary<string, string> headers,
bool isHeadRequest)
{
var options = new ImageProcessingOptions
{
CropWhiteSpace = true,
Height = request.Height,
ImageIndex = request.Index ?? 0,
Image = info,
Item = null, // Hack alert
ItemId = itemId,
MaxHeight = request.MaxHeight,
MaxWidth = request.MaxWidth,
Quality = request.Quality ?? 100,
Width = request.Width,
AddPlayedIndicator = request.AddPlayedIndicator,
PercentPlayed = 0,
UnplayedCount = request.UnplayedCount,
Blur = request.Blur,
BackgroundColor = request.BackgroundColor,
ForegroundLayer = request.ForegroundLayer,
SupportedOutputFormats = supportedFormats
};
var imageResult = await _imageProcessor.ProcessImage(options).ConfigureAwait(false);
headers[HeaderNames.Vary] = HeaderNames.Accept;
return await ResultFactory.GetStaticFileResult(Request, new StaticFileResultOptions
{
CacheDuration = cacheDuration,
ResponseHeaders = headers,
ContentType = imageResult.Item2,
DateLastModified = imageResult.Item3,
IsHeadRequest = isHeadRequest,
Path = imageResult.Item1,
FileShare = FileShare.Read
}).ConfigureAwait(false);
}
private async Task<object> GetImageResult(
BaseItem item,
Guid itemId,
@ -740,6 +821,28 @@ namespace MediaBrowser.Api.Images
return item.GetImageInfo(request.Type, index);
}
private ItemImageInfo GetImageInfo(ImageRequest request, User user)
{
var info = new ItemImageInfo
{
Path = user.ProfileImage.Path,
Type = ImageType.Primary,
DateModified = user.ProfileImage.LastModified,
};
if (request.Width.HasValue)
{
info.Width = request.Width.Value;
}
if (request.Height.HasValue)
{
info.Height = request.Height.Value;
}
return info;
}
/// <summary>
/// Posts the image.
/// </summary>
@ -767,5 +870,25 @@ namespace MediaBrowser.Api.Images
entity.UpdateToRepository(ItemUpdateType.ImageUpdate, CancellationToken.None);
}
public async Task PostImage(User user, Stream inputStream, string mimeType)
{
using var reader = new StreamReader(inputStream);
var text = await reader.ReadToEndAsync().ConfigureAwait(false);
var bytes = Convert.FromBase64String(text);
var memoryStream = new MemoryStream(bytes)
{
Position = 0
};
// Handle image/png; charset=utf-8
mimeType = mimeType.Split(';').FirstOrDefault();
await _providerManager
.SaveImage(user, memoryStream, mimeType, Path.Combine(ServerConfigurationManager.ApplicationPaths.UserConfigurationDirectoryPath, _imageProcessor.GetImageCacheTag(user)))
.ConfigureAwait(false);
await _userManager.UpdateUserAsync(user);
}
}
}

@ -755,12 +755,12 @@ namespace MediaBrowser.Api.Library
});
}
private void LogDownload(BaseItem item, User user, AuthorizationInfo auth)
private void LogDownload(BaseItem item, Jellyfin.Data.Entities.User user, AuthorizationInfo auth)
{
try
{
_activityManager.Create(new Jellyfin.Data.Entities.ActivityLog(
string.Format(_localization.GetLocalizedString("UserDownloadingItemWithValues"), user.Name, item.Name),
string.Format(_localization.GetLocalizedString("UserDownloadingItemWithValues"), user.Username, item.Name),
"UserDownloadingContent",
auth.UserId,
DateTime.UtcNow,
@ -840,7 +840,7 @@ namespace MediaBrowser.Api.Library
return baseItemDtos;
}
private BaseItem TranslateParentItem(BaseItem item, User user)
private BaseItem TranslateParentItem(BaseItem item, Jellyfin.Data.Entities.User user)
{
return item.GetParent() is AggregateFolder
? _libraryManager.GetUserRootFolder().GetChildren(user, true)
@ -882,7 +882,7 @@ namespace MediaBrowser.Api.Library
return ToOptimizedResult(counts);
}
private int GetCount(Type type, User user, GetItemCounts request)
private int GetCount(Type type, Jellyfin.Data.Entities.User user, GetItemCounts request)
{
var query = new InternalItemsQuery(user)
{

@ -7,6 +7,7 @@ using System.Security.Cryptography;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Jellyfin.Data.Enums;
using MediaBrowser.Api.UserLibrary;
using MediaBrowser.Common;
using MediaBrowser.Common.Configuration;
@ -859,7 +860,7 @@ namespace MediaBrowser.Api.LiveTv
throw new SecurityException("Anonymous live tv management is not allowed.");
}
if (!user.Policy.EnableLiveTvManagement)
if (!user.HasPermission(PermissionKind.EnableLiveTvManagement))
{
throw new SecurityException("The current user does not have permission to manage live tv.");
}

@ -148,7 +148,12 @@ namespace MediaBrowser.Api.Movies
return result;
}
private IEnumerable<RecommendationDto> GetRecommendationCategories(User user, string parentId, int categoryLimit, int itemLimit, DtoOptions dtoOptions)
private IEnumerable<RecommendationDto> GetRecommendationCategories(
Jellyfin.Data.Entities.User user,
string parentId,
int categoryLimit,
int itemLimit,
DtoOptions dtoOptions)
{
var categories = new List<RecommendationDto>();
@ -251,7 +256,12 @@ namespace MediaBrowser.Api.Movies
return categories.OrderBy(i => i.RecommendationType);
}
private IEnumerable<RecommendationDto> GetWithDirector(User user, IEnumerable<string> names, int itemLimit, DtoOptions dtoOptions, RecommendationType type)
private IEnumerable<RecommendationDto> GetWithDirector(
Jellyfin.Data.Entities.User user,
IEnumerable<string> names,
int itemLimit,
DtoOptions dtoOptions,
RecommendationType type)
{
var itemTypes = new List<string> { typeof(Movie).Name };
if (ServerConfigurationManager.Configuration.EnableExternalContentInSuggestions)
@ -293,7 +303,12 @@ namespace MediaBrowser.Api.Movies
}
}
private IEnumerable<RecommendationDto> GetWithActor(User user, IEnumerable<string> names, int itemLimit, DtoOptions dtoOptions, RecommendationType type)
private IEnumerable<RecommendationDto> GetWithActor(
Jellyfin.Data.Entities.User user,
IEnumerable<string> names,
int itemLimit,
DtoOptions dtoOptions,
RecommendationType type)
{
var itemTypes = new List<string> { typeof(Movie).Name };
if (ServerConfigurationManager.Configuration.EnableExternalContentInSuggestions)
@ -334,7 +349,12 @@ namespace MediaBrowser.Api.Movies
}
}
private IEnumerable<RecommendationDto> GetSimilarTo(User user, List<BaseItem> baselineItems, int itemLimit, DtoOptions dtoOptions, RecommendationType type)
private IEnumerable<RecommendationDto> GetSimilarTo(
Jellyfin.Data.Entities.User user,
List<BaseItem> baselineItems,
int itemLimit,
DtoOptions dtoOptions,
RecommendationType type)
{
var itemTypes = new List<string> { typeof(Movie).Name };
if (ServerConfigurationManager.Configuration.EnableExternalContentInSuggestions)

@ -171,7 +171,7 @@ namespace MediaBrowser.Api.Music
return GetResult(items, user, request, dtoOptions);
}
private object GetResult(List<BaseItem> items, User user, BaseGetSimilarItems request, DtoOptions dtoOptions)
private object GetResult(List<BaseItem> items, Jellyfin.Data.Entities.User user, BaseGetSimilarItems request, DtoOptions dtoOptions)
{
var list = items;

@ -7,6 +7,7 @@ using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Jellyfin.Data.Enums;
using MediaBrowser.Common.Configuration;
using MediaBrowser.Common.Extensions;
using MediaBrowser.Controller.Configuration;
@ -196,7 +197,7 @@ namespace MediaBrowser.Api.Playback
if (state.VideoRequest != null && !string.Equals(state.OutputVideoCodec, "copy", StringComparison.OrdinalIgnoreCase))
{
var auth = AuthorizationContext.GetAuthorizationInfo(Request);
if (auth.User != null && !auth.User.Policy.EnableVideoPlaybackTranscoding)
if (auth.User != null && !auth.User.HasPermission(PermissionKind.EnableVideoPlaybackTranscoding))
{
ApiEntryPoint.Instance.OnTranscodeFailedToStart(outputPath, TranscodingJobType, state);

@ -9,6 +9,7 @@ using System.Linq;
using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;
using Jellyfin.Data.Enums;
using MediaBrowser.Common.Net;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Devices;
@ -400,21 +401,24 @@ namespace MediaBrowser.Api.Playback
if (item is Audio)
{
Logger.LogInformation("User policy for {0}. EnableAudioPlaybackTranscoding: {1}", user.Name, user.Policy.EnableAudioPlaybackTranscoding);
Logger.LogInformation(
"User policy for {0}. EnableAudioPlaybackTranscoding: {1}",
user.Username,
user.HasPermission(PermissionKind.EnableAudioPlaybackTranscoding));
}
else
{
Logger.LogInformation("User policy for {0}. EnablePlaybackRemuxing: {1} EnableVideoPlaybackTranscoding: {2} EnableAudioPlaybackTranscoding: {3}",
user.Name,
user.Policy.EnablePlaybackRemuxing,
user.Policy.EnableVideoPlaybackTranscoding,
user.Policy.EnableAudioPlaybackTranscoding);
user.Username,
user.HasPermission(PermissionKind.EnablePlaybackRemuxing),
user.HasPermission(PermissionKind.EnableVideoPlaybackTranscoding),
user.HasPermission(PermissionKind.EnableAudioPlaybackTranscoding));
}
// Beginning of Playback Determination: Attempt DirectPlay first
if (mediaSource.SupportsDirectPlay)
{
if (mediaSource.IsRemote && user.Policy.ForceRemoteSourceTranscoding)
if (mediaSource.IsRemote && user.HasPermission(PermissionKind.ForceRemoteSourceTranscoding))
{
mediaSource.SupportsDirectPlay = false;
}
@ -428,14 +432,16 @@ namespace MediaBrowser.Api.Playback
if (item is Audio)
{
if (!user.Policy.EnableAudioPlaybackTranscoding)
if (!user.HasPermission(PermissionKind.EnableAudioPlaybackTranscoding))
{
options.ForceDirectPlay = true;
}
}
else if (item is Video)
{
if (!user.Policy.EnableAudioPlaybackTranscoding && !user.Policy.EnableVideoPlaybackTranscoding && !user.Policy.EnablePlaybackRemuxing)
if (!user.HasPermission(PermissionKind.EnableAudioPlaybackTranscoding)
&& !user.HasPermission(PermissionKind.EnableVideoPlaybackTranscoding)
&& !user.HasPermission(PermissionKind.EnablePlaybackRemuxing))
{
options.ForceDirectPlay = true;
}
@ -463,7 +469,7 @@ namespace MediaBrowser.Api.Playback
if (mediaSource.SupportsDirectStream)
{
if (mediaSource.IsRemote && user.Policy.ForceRemoteSourceTranscoding)
if (mediaSource.IsRemote && user.HasPermission(PermissionKind.ForceRemoteSourceTranscoding))
{
mediaSource.SupportsDirectStream = false;
}
@ -473,14 +479,16 @@ namespace MediaBrowser.Api.Playback
if (item is Audio)
{
if (!user.Policy.EnableAudioPlaybackTranscoding)
if (!user.HasPermission(PermissionKind.EnableAudioPlaybackTranscoding))
{
options.ForceDirectStream = true;
}
}
else if (item is Video)
{
if (!user.Policy.EnableAudioPlaybackTranscoding && !user.Policy.EnableVideoPlaybackTranscoding && !user.Policy.EnablePlaybackRemuxing)
if (!user.HasPermission(PermissionKind.EnableAudioPlaybackTranscoding)
&& !user.HasPermission(PermissionKind.EnableVideoPlaybackTranscoding)
&& !user.HasPermission(PermissionKind.EnablePlaybackRemuxing))
{
options.ForceDirectStream = true;
}
@ -512,7 +520,7 @@ namespace MediaBrowser.Api.Playback
? streamBuilder.BuildAudioItem(options)
: streamBuilder.BuildVideoItem(options);
if (mediaSource.IsRemote && user.Policy.ForceRemoteSourceTranscoding)
if (mediaSource.IsRemote && user.HasPermission(PermissionKind.ForceRemoteSourceTranscoding))
{
if (streamInfo != null)
{
@ -576,10 +584,10 @@ namespace MediaBrowser.Api.Playback
}
}
private long? GetMaxBitrate(long? clientMaxBitrate, User user)
private long? GetMaxBitrate(long? clientMaxBitrate, Jellyfin.Data.Entities.User user)
{
var maxBitrate = clientMaxBitrate;
var remoteClientMaxBitrate = user?.Policy.RemoteClientBitrateLimit ?? 0;
var remoteClientMaxBitrate = user?.RemoteClientBitrateLimit ?? 0;
if (remoteClientMaxBitrate <= 0)
{

@ -2,6 +2,7 @@ using System;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Jellyfin.Data.Enums;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Devices;
using MediaBrowser.Controller.Library;
@ -326,12 +327,12 @@ namespace MediaBrowser.Api.Sessions
var user = _userManager.GetUserById(request.ControllableByUserId);
if (!user.Policy.EnableRemoteControlOfOtherUsers)
if (!user.HasPermission(PermissionKind.EnableRemoteControlOfOtherUsers))
{
result = result.Where(i => i.UserId.Equals(Guid.Empty) || i.ContainsUser(request.ControllableByUserId));
}
if (!user.Policy.EnableSharedDeviceControl)
if (!user.HasPermission(PermissionKind.EnableSharedDeviceControl))
{
result = result.Where(i => !i.UserId.Equals(Guid.Empty));
}

@ -78,7 +78,7 @@ namespace MediaBrowser.Api
};
}
private QueryResult<BaseItem> GetItems(GetSuggestedItems request, User user, DtoOptions dtoOptions)
private QueryResult<BaseItem> GetItems(GetSuggestedItems request, Jellyfin.Data.Entities.User user, DtoOptions dtoOptions)
{
return _libraryManager.GetItemsResult(new InternalItemsQuery(user)
{

@ -378,7 +378,7 @@ namespace MediaBrowser.Api
{
var user = _userManager.GetUserById(request.UserId);
var series = GetSeries(request.Id, user);
var series = GetSeries(request.Id);
if (series == null)
{
@ -404,7 +404,7 @@ namespace MediaBrowser.Api
};
}
private Series GetSeries(string seriesId, User user)
private Series GetSeries(string seriesId)
{
if (!string.IsNullOrWhiteSpace(seriesId))
{
@ -433,7 +433,7 @@ namespace MediaBrowser.Api
}
else if (request.Season.HasValue)
{
var series = GetSeries(request.Id, user);
var series = GetSeries(request.Id);
if (series == null)
{
@ -446,7 +446,7 @@ namespace MediaBrowser.Api
}
else
{
var series = GetSeries(request.Id, user);
var series = GetSeries(request.Id);
if (series == null)
{

@ -94,7 +94,7 @@ namespace MediaBrowser.Api.UserLibrary
{
var dtoOptions = GetDtoOptions(AuthorizationContext, request);
User user = null;
Jellyfin.Data.Entities.User user = null;
BaseItem parentItem;
if (!request.UserId.Equals(Guid.Empty))
@ -246,7 +246,7 @@ namespace MediaBrowser.Api.UserLibrary
{
var dtoOptions = GetDtoOptions(AuthorizationContext, request);
User user = null;
Jellyfin.Data.Entities.User user = null;
BaseItem parentItem;
if (!request.UserId.Equals(Guid.Empty))

@ -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;
@ -86,7 +87,7 @@ namespace MediaBrowser.Api.UserLibrary
var ancestorIds = Array.Empty<Guid>();
var excludeFolderIds = user.Configuration.LatestItemsExcludes;
var excludeFolderIds = user.GetPreference(PreferenceKind.LatestItemExcludes);
if (parentIdGuid.Equals(Guid.Empty) && excludeFolderIds.Length > 0)
{
ancestorIds = _libraryManager.GetUserRootFolder().GetChildren(user, true)
@ -179,7 +180,7 @@ namespace MediaBrowser.Api.UserLibrary
/// <summary>
/// Gets the items to serialize.
/// </summary>
private QueryResult<BaseItem> GetQueryResult(GetItems request, DtoOptions dtoOptions, User user)
private QueryResult<BaseItem> GetQueryResult(GetItems request, DtoOptions dtoOptions, Jellyfin.Data.Entities.User user)
{
if (string.Equals(request.IncludeItemTypes, "Playlist", StringComparison.OrdinalIgnoreCase)
|| string.Equals(request.IncludeItemTypes, "BoxSet", StringComparison.OrdinalIgnoreCase))
@ -211,14 +212,14 @@ namespace MediaBrowser.Api.UserLibrary
request.IncludeItemTypes = "Playlist";
}
bool isInEnabledFolder = user.Policy.EnabledFolders.Any(i => new Guid(i) == item.Id)
bool isInEnabledFolder = user.GetPreference(PreferenceKind.EnabledFolders).Any(i => new Guid(i) == item.Id)
// Assume all folders inside an EnabledChannel are enabled
|| user.Policy.EnabledChannels.Any(i => new Guid(i) == item.Id);
|| user.GetPreference(PreferenceKind.EnabledChannels).Any(i => new Guid(i) == item.Id);
var collectionFolders = _libraryManager.GetCollectionFolders(item);
foreach (var collectionFolder in collectionFolders)
{
if (user.Policy.EnabledFolders.Contains(
if (user.GetPreference(PreferenceKind.EnabledFolders).Contains(
collectionFolder.Id.ToString("N", CultureInfo.InvariantCulture),
StringComparer.OrdinalIgnoreCase))
{
@ -226,9 +227,12 @@ namespace MediaBrowser.Api.UserLibrary
}
}
if (!(item is UserRootFolder) && !user.Policy.EnableAllFolders && !isInEnabledFolder && !user.Policy.EnableAllChannels)
if (!(item is UserRootFolder)
&& !isInEnabledFolder
&& !user.HasPermission(PermissionKind.EnableAllFolders)
&& !user.HasPermission(PermissionKind.EnableAllChannels))
{
Logger.LogWarning("{UserName} is not permitted to access Library {ItemName}.", user.Name, item.Name);
Logger.LogWarning("{UserName} is not permitted to access Library {ItemName}.", user.Username, item.Name);
return new QueryResult<BaseItem>
{
Items = Array.Empty<BaseItem>(),
@ -251,7 +255,7 @@ namespace MediaBrowser.Api.UserLibrary
};
}
private InternalItemsQuery GetItemsQuery(GetItems request, DtoOptions dtoOptions, User user)
private InternalItemsQuery GetItemsQuery(GetItems request, DtoOptions dtoOptions, Jellyfin.Data.Entities.User user)
{
var query = new InternalItemsQuery(user)
{

@ -437,7 +437,7 @@ namespace MediaBrowser.Api.UserLibrary
/// <param name="wasPlayed">if set to <c>true</c> [was played].</param>
/// <param name="datePlayed">The date played.</param>
/// <returns>Task.</returns>
private UserItemDataDto UpdatePlayedStatus(User user, string itemId, bool wasPlayed, DateTime? datePlayed)
private UserItemDataDto UpdatePlayedStatus(Jellyfin.Data.Entities.User user, string itemId, bool wasPlayed, DateTime? datePlayed)
{
var item = _libraryManager.GetItemById(itemId);

@ -312,7 +312,7 @@ namespace MediaBrowser.Api.UserLibrary
if (!request.IsPlayed.HasValue)
{
if (user.Configuration.HidePlayedInLatest)
if (user.HidePlayedInLatest)
{
request.IsPlayed = false;
}

@ -1,6 +1,7 @@
using System;
using System.Linq;
using System.Threading.Tasks;
using Jellyfin.Data.Enums;
using MediaBrowser.Common.Extensions;
using MediaBrowser.Common.Net;
using MediaBrowser.Controller.Authentication;
@ -300,12 +301,12 @@ namespace MediaBrowser.Api
if (request.IsDisabled.HasValue)
{
users = users.Where(i => i.Policy.IsDisabled == request.IsDisabled.Value);
users = users.Where(i => i.HasPermission(PermissionKind.IsDisabled) == request.IsDisabled.Value);
}
if (request.IsHidden.HasValue)
{
users = users.Where(i => i.Policy.IsHidden == request.IsHidden.Value);
users = users.Where(i => i.HasPermission(PermissionKind.IsHidden) == request.IsHidden.Value);
}
if (filterByDevice)
@ -322,12 +323,12 @@ namespace MediaBrowser.Api
{
if (!_networkManager.IsInLocalNetwork(Request.RemoteIp))
{
users = users.Where(i => i.Policy.EnableRemoteAccess);
users = users.Where(i => i.HasPermission(PermissionKind.EnableRemoteAccess));
}
}
var result = users
.OrderBy(u => u.Name)
.OrderBy(u => u.Username)
.Select(i => _userManager.GetUserDto(i, Request.RemoteIp))
.ToArray();
@ -397,7 +398,7 @@ namespace MediaBrowser.Api
// Password should always be null
return Post(new AuthenticateUserByName
{
Username = user.Name,
Username = user.Username,
Password = null,
Pw = request.Pw
});
@ -456,7 +457,12 @@ namespace MediaBrowser.Api
}
else
{
var success = await _userManager.AuthenticateUser(user.Name, request.CurrentPw, request.CurrentPassword, Request.RemoteIp, false).ConfigureAwait(false);
var success = await _userManager.AuthenticateUser(
user.Username,
request.CurrentPw,
request.CurrentPassword,
Request.RemoteIp,
false).ConfigureAwait(false);
if (success == null)
{
@ -506,10 +512,10 @@ namespace MediaBrowser.Api
var user = _userManager.GetUserById(id);
if (string.Equals(user.Name, dtoUser.Name, StringComparison.Ordinal))
if (string.Equals(user.Username, dtoUser.Name, StringComparison.Ordinal))
{
_userManager.UpdateUser(user);
_userManager.UpdateConfiguration(user, dtoUser.Configuration);
await _userManager.UpdateUserAsync(user);
_userManager.UpdateConfiguration(user.Id, dtoUser.Configuration);
}
else
{
@ -568,24 +574,24 @@ namespace MediaBrowser.Api
var user = _userManager.GetUserById(request.Id);
// If removing admin access
if (!request.IsAdministrator && user.Policy.IsAdministrator)
if (!request.IsAdministrator && user.HasPermission(PermissionKind.IsAdministrator))
{
if (_userManager.Users.Count(i => i.Policy.IsAdministrator) == 1)
if (_userManager.Users.Count(i => i.HasPermission(PermissionKind.IsAdministrator)) == 1)
{
throw new ArgumentException("There must be at least one user in the system with administrative access.");
}
}
// If disabling
if (request.IsDisabled && user.Policy.IsAdministrator)
if (request.IsDisabled && user.HasPermission(PermissionKind.IsAdministrator))
{
throw new ArgumentException("Administrators cannot be disabled.");
}
// If disabling
if (request.IsDisabled && !user.Policy.IsDisabled)
if (request.IsDisabled && !user.HasPermission(PermissionKind.IsDisabled))
{
if (_userManager.Users.Count(i => !i.Policy.IsDisabled) == 1)
if (_userManager.Users.Count(i => !i.HasPermission(PermissionKind.IsDisabled)) == 1)
{
throw new ArgumentException("There must be at least one enabled user in the system.");
}
@ -594,7 +600,7 @@ namespace MediaBrowser.Api
_sessionMananger.RevokeUserTokens(user.Id, currentToken);
}
_userManager.UpdateUserPolicy(request.Id, request);
_userManager.UpdatePolicy(request.Id, request);
}
}
}

@ -1,5 +1,5 @@
using System.Threading.Tasks;
using MediaBrowser.Controller.Entities;
using Jellyfin.Data.Entities;
using MediaBrowser.Model.Users;
namespace MediaBrowser.Controller.Authentication

@ -1,6 +1,6 @@
using System;
using System.Threading.Tasks;
using MediaBrowser.Controller.Entities;
using Jellyfin.Data.Entities;
using MediaBrowser.Model.Users;
namespace MediaBrowser.Controller.Authentication

@ -3,6 +3,7 @@ using System.Globalization;
using System.Linq;
using System.Text.Json.Serialization;
using System.Threading;
using Jellyfin.Data.Enums;
using MediaBrowser.Common.Progress;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Model.Querying;
@ -11,18 +12,20 @@ namespace MediaBrowser.Controller.Channels
{
public class Channel : Folder
{
public override bool IsVisible(User user)
public override bool IsVisible(Jellyfin.Data.Entities.User user)
{
if (user.Policy.BlockedChannels != null)
if (user.GetPreference(PreferenceKind.BlockedChannels) != null)
{
if (user.Policy.BlockedChannels.Contains(Id.ToString("N", CultureInfo.InvariantCulture), StringComparer.OrdinalIgnoreCase))
if (user.GetPreference(PreferenceKind.BlockedChannels).Contains(Id.ToString("N", CultureInfo.InvariantCulture), StringComparer.OrdinalIgnoreCase))
{
return false;
}
}
else
{
if (!user.Policy.EnableAllChannels && !user.Policy.EnabledChannels.Contains(Id.ToString("N", CultureInfo.InvariantCulture), StringComparer.OrdinalIgnoreCase))
if (!user.HasPermission(PermissionKind.EnableAllChannels)
&& !user.GetPreference(PreferenceKind.EnabledChannels)
.Contains(Id.ToString("N", CultureInfo.InvariantCulture), StringComparer.OrdinalIgnoreCase))
{
return false;
}
@ -74,7 +77,7 @@ namespace MediaBrowser.Controller.Channels
return false;
}
internal static bool IsChannelVisible(BaseItem channelItem, User user)
internal static bool IsChannelVisible(BaseItem channelItem, Jellyfin.Data.Entities.User user)
{
var channel = ChannelManager.GetChannel(channelItem.ChannelId.ToString(""));

@ -51,6 +51,6 @@ namespace MediaBrowser.Controller.Collections
/// <param name="items">The items.</param>
/// <param name="user">The user.</param>
/// <returns>IEnumerable{BaseItem}.</returns>
IEnumerable<BaseItem> CollapseItemsWithinBoxSets(IEnumerable<BaseItem> items, User user);
IEnumerable<BaseItem> CollapseItemsWithinBoxSets(IEnumerable<BaseItem> items, Jellyfin.Data.Entities.User user);
}
}

@ -1,7 +1,7 @@
using System;
using System.IO;
using System.Threading.Tasks;
using MediaBrowser.Controller.Entities;
using Jellyfin.Data.Entities;
using MediaBrowser.Model.Devices;
using MediaBrowser.Model.Events;
using MediaBrowser.Model.Querying;

@ -47,8 +47,11 @@ namespace MediaBrowser.Controller.Drawing
/// <param name="image">The image.</param>
/// <returns>Guid.</returns>
string GetImageCacheTag(BaseItem item, ItemImageInfo image);
string GetImageCacheTag(BaseItem item, ChapterInfo info);
string GetImageCacheTag(Jellyfin.Data.Entities.User user);
/// <summary>
/// Processes the image.
/// </summary>

@ -1,5 +1,6 @@
using MediaBrowser.Controller.Entities;
using MediaBrowser.Model.Entities;
using User = Jellyfin.Data.Entities.User;
namespace MediaBrowser.Controller.Drawing
{

@ -38,7 +38,7 @@ namespace MediaBrowser.Controller.Dto
/// <param name="fields">The fields.</param>
/// <param name="user">The user.</param>
/// <param name="owner">The owner.</param>
BaseItemDto GetBaseItemDto(BaseItem item, ItemFields[] fields, User user = null, BaseItem owner = null);
BaseItemDto GetBaseItemDto(BaseItem item, ItemFields[] fields, Jellyfin.Data.Entities.User user = null, BaseItem owner = null);
/// <summary>
/// Gets the base item dto.
@ -48,7 +48,7 @@ namespace MediaBrowser.Controller.Dto
/// <param name="user">The user.</param>
/// <param name="owner">The owner.</param>
/// <returns>BaseItemDto.</returns>
BaseItemDto GetBaseItemDto(BaseItem item, DtoOptions options, User user = null, BaseItem owner = null);
BaseItemDto GetBaseItemDto(BaseItem item, DtoOptions options, Jellyfin.Data.Entities.User user = null, BaseItem owner = null);
/// <summary>
/// Gets the base item dtos.
@ -57,11 +57,11 @@ namespace MediaBrowser.Controller.Dto
/// <param name="options">The options.</param>
/// <param name="user">The user.</param>
/// <param name="owner">The owner.</param>
IReadOnlyList<BaseItemDto> GetBaseItemDtos(IReadOnlyList<BaseItem> items, DtoOptions options, User user = null, BaseItem owner = null);
IReadOnlyList<BaseItemDto> GetBaseItemDtos(IReadOnlyList<BaseItem> items, DtoOptions options, Jellyfin.Data.Entities.User user = null, BaseItem owner = null);
/// <summary>
/// Gets the item by name dto.
/// </summary>
BaseItemDto GetItemByNameDto(BaseItem item, DtoOptions options, List<BaseItem> taggedItems, User user = null);
BaseItemDto GetItemByNameDto(BaseItem item, DtoOptions options, List<BaseItem> taggedItems, Jellyfin.Data.Entities.User user = null);
}
}

@ -2,6 +2,7 @@ using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.Json.Serialization;
using Jellyfin.Data.Enums;
using MediaBrowser.Controller.Persistence;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Configuration;

@ -1,9 +1,11 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text.Json.Serialization;
using System.Threading;
using System.Threading.Tasks;
using Jellyfin.Data.Enums;
using MediaBrowser.Controller.Dto;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Providers;
@ -77,7 +79,7 @@ namespace MediaBrowser.Controller.Entities.Audio
[JsonIgnore]
public IEnumerable<Audio> Tracks => GetRecursiveChildren(i => i is Audio).Cast<Audio>();
protected override IEnumerable<BaseItem> GetEligibleChildrenForRecursiveChildren(User user)
protected override IEnumerable<BaseItem> GetEligibleChildrenForRecursiveChildren(Jellyfin.Data.Entities.User user)
{
return Tracks;
}
@ -114,9 +116,9 @@ namespace MediaBrowser.Controller.Entities.Audio
return list;
}
protected override bool GetBlockUnratedValue(UserPolicy config)
protected override bool GetBlockUnratedValue(Jellyfin.Data.Entities.User user)
{
return config.BlockUnratedItems.Contains(UnratedItem.Music);
return user.GetPreference(PreferenceKind.BlockUnratedItems).Contains(UnratedItem.Music.ToString());
}
public override UnratedItem GetBlockUnratedType()

@ -4,6 +4,7 @@ using System.Linq;
using System.Text.Json.Serialization;
using System.Threading;
using System.Threading.Tasks;
using Jellyfin.Data.Enums;
using MediaBrowser.Controller.Extensions;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Configuration;
@ -74,7 +75,7 @@ namespace MediaBrowser.Controller.Entities.Audio
}
}
public override int GetChildCount(User user)
public override int GetChildCount(Jellyfin.Data.Entities.User user)
{
if (IsAccessedByName)
{
@ -142,9 +143,10 @@ namespace MediaBrowser.Controller.Entities.Audio
{
return "Artist-" + (Name ?? string.Empty).RemoveDiacritics();
}
protected override bool GetBlockUnratedValue(UserPolicy config)
protected override bool GetBlockUnratedValue(Jellyfin.Data.Entities.User user)
{
return config.BlockUnratedItems.Contains(UnratedItem.Music);
return user.GetPreference(PreferenceKind.BlockUnratedItems).Contains(UnratedItem.Music.ToString());
}
public override UnratedItem GetBlockUnratedType()

@ -1,5 +1,6 @@
using System;
using System.Text.Json.Serialization;
using Jellyfin.Data.Enums;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Configuration;

@ -7,6 +7,7 @@ using System.Text;
using System.Text.Json.Serialization;
using System.Threading;
using System.Threading.Tasks;
using Jellyfin.Data.Enums;
using MediaBrowser.Common.Extensions;
using MediaBrowser.Controller.Channels;
using MediaBrowser.Controller.Configuration;
@ -479,14 +480,14 @@ namespace MediaBrowser.Controller.Entities
return IsFileProtocol;
}
public virtual bool IsAuthorizedToDelete(User user, List<Folder> allCollectionFolders)
public virtual bool IsAuthorizedToDelete(Jellyfin.Data.Entities.User user, List<Folder> allCollectionFolders)
{
if (user.Policy.EnableContentDeletion)
if (user.HasPermission(PermissionKind.EnableContentDeletion))
{
return true;
}
var allowed = user.Policy.EnableContentDeletionFromFolders;
var allowed = user.GetPreference(PreferenceKind.EnableContentDeletionFromFolders);
if (SourceType == SourceType.Channel)
{
@ -508,12 +509,12 @@ namespace MediaBrowser.Controller.Entities
return false;
}
public bool CanDelete(User user, List<Folder> allCollectionFolders)
public bool CanDelete(Jellyfin.Data.Entities.User user, List<Folder> allCollectionFolders)
{
return CanDelete() && IsAuthorizedToDelete(user, allCollectionFolders);
}
public bool CanDelete(User user)
public bool CanDelete(Jellyfin.Data.Entities.User user)
{
var allCollectionFolders = LibraryManager.GetUserRootFolder().Children.OfType<Folder>().ToList();
@ -525,12 +526,12 @@ namespace MediaBrowser.Controller.Entities
return false;
}
public virtual bool IsAuthorizedToDownload(User user)
public virtual bool IsAuthorizedToDownload(Jellyfin.Data.Entities.User user)
{
return user.Policy.EnableContentDownloading;
return user.HasPermission(PermissionKind.EnableContentDownloading);
}
public bool CanDownload(User user)
public bool CanDownload(Jellyfin.Data.Entities.User user)
{
return CanDownload() && IsAuthorizedToDownload(user);
}
@ -1002,9 +1003,9 @@ namespace MediaBrowser.Controller.Entities
/// </summary>
/// <param name="user">The user.</param>
/// <returns>PlayAccess.</returns>
public PlayAccess GetPlayAccess(User user)
public PlayAccess GetPlayAccess(Jellyfin.Data.Entities.User user)
{
if (!user.Policy.EnableMediaPlayback)
if (!user.HasPermission(PermissionKind.EnableMediaPlayback))
{
return PlayAccess.None;
}
@ -1760,7 +1761,7 @@ namespace MediaBrowser.Controller.Entities
/// <param name="user">The user.</param>
/// <returns><c>true</c> if [is parental allowed] [the specified user]; otherwise, <c>false</c>.</returns>
/// <exception cref="ArgumentNullException">user</exception>
public bool IsParentalAllowed(User user)
public bool IsParentalAllowed(Jellyfin.Data.Entities.User user)
{
if (user == null)
{
@ -1772,7 +1773,7 @@ namespace MediaBrowser.Controller.Entities
return false;
}
var maxAllowedRating = user.Policy.MaxParentalRating;
var maxAllowedRating = user.MaxParentalAgeRating;
if (maxAllowedRating == null)
{
@ -1788,7 +1789,7 @@ namespace MediaBrowser.Controller.Entities
if (string.IsNullOrEmpty(rating))
{
return !GetBlockUnratedValue(user.Policy);
return !GetBlockUnratedValue(user);
}
var value = LocalizationManager.GetRatingLevel(rating);
@ -1796,7 +1797,7 @@ namespace MediaBrowser.Controller.Entities
// Could not determine the integer value
if (!value.HasValue)
{
var isAllowed = !GetBlockUnratedValue(user.Policy);
var isAllowed = !GetBlockUnratedValue(user);
if (!isAllowed)
{
@ -1856,10 +1857,9 @@ namespace MediaBrowser.Controller.Entities
return list.Distinct(StringComparer.OrdinalIgnoreCase).ToList();
}
private bool IsVisibleViaTags(User user)
private bool IsVisibleViaTags(Jellyfin.Data.Entities.User user)
{
var policy = user.Policy;
if (policy.BlockedTags.Any(i => Tags.Contains(i, StringComparer.OrdinalIgnoreCase)))
if (user.GetPreference(PreferenceKind.BlockedTags).Any(i => Tags.Contains(i, StringComparer.OrdinalIgnoreCase)))
{
return false;
}
@ -1885,22 +1885,18 @@ namespace MediaBrowser.Controller.Entities
/// <summary>
/// Gets the block unrated value.
/// </summary>
/// <param name="config">The configuration.</param>
/// <param name="user">The configuration.</param>
/// <returns><c>true</c> if XXXX, <c>false</c> otherwise.</returns>
protected virtual bool GetBlockUnratedValue(UserPolicy config)
protected virtual bool GetBlockUnratedValue(Jellyfin.Data.Entities.User user)
{
// Don't block plain folders that are unrated. Let the media underneath get blocked
// Special folders like series and albums will override this method.
if (IsFolder)
{
return false;
}
if (this is IItemByName)
if (IsFolder || this is IItemByName)
{
return false;
}
return config.BlockUnratedItems.Contains(GetBlockUnratedType());
return user.GetPreference(PreferenceKind.BlockUnratedItems).Contains(GetBlockUnratedType().ToString());
}
/// <summary>
@ -1910,7 +1906,7 @@ namespace MediaBrowser.Controller.Entities
/// <param name="user">The user.</param>
/// <returns><c>true</c> if the specified user is visible; otherwise, <c>false</c>.</returns>
/// <exception cref="ArgumentNullException">user</exception>
public virtual bool IsVisible(User user)
public virtual bool IsVisible(Jellyfin.Data.Entities.User user)
{
if (user == null)
{
@ -1920,7 +1916,7 @@ namespace MediaBrowser.Controller.Entities
return IsParentalAllowed(user);
}
public virtual bool IsVisibleStandalone(User user)
public virtual bool IsVisibleStandalone(Jellyfin.Data.Entities.User user)
{
if (SourceType == SourceType.Channel)
{
@ -1933,7 +1929,7 @@ namespace MediaBrowser.Controller.Entities
[JsonIgnore]
public virtual bool SupportsInheritedParentImages => false;
protected bool IsVisibleStandaloneInternal(User user, bool checkFolders)
protected bool IsVisibleStandaloneInternal(Jellyfin.Data.Entities.User user, bool checkFolders)
{
if (!IsVisible(user))
{
@ -2130,7 +2126,8 @@ namespace MediaBrowser.Controller.Entities
/// <param name="resetPosition">if set to <c>true</c> [reset position].</param>
/// <returns>Task.</returns>
/// <exception cref="ArgumentNullException"></exception>
public virtual void MarkPlayed(User user,
public virtual void MarkPlayed(
Jellyfin.Data.Entities.User user,
DateTime? datePlayed,
bool resetPosition)
{
@ -2167,7 +2164,7 @@ namespace MediaBrowser.Controller.Entities
/// <param name="user">The user.</param>
/// <returns>Task.</returns>
/// <exception cref="ArgumentNullException"></exception>
public virtual void MarkUnplayed(User user)
public virtual void MarkUnplayed(Jellyfin.Data.Entities.User user)
{
if (user == null)
{
@ -2543,21 +2540,21 @@ namespace MediaBrowser.Controller.Entities
UpdateToRepository(ItemUpdateType.ImageUpdate, CancellationToken.None);
}
public virtual bool IsPlayed(User user)
public virtual bool IsPlayed(Jellyfin.Data.Entities.User user)
{
var userdata = UserDataManager.GetUserData(user, this);
return userdata != null && userdata.Played;
}
public bool IsFavoriteOrLiked(User user)
public bool IsFavoriteOrLiked(Jellyfin.Data.Entities.User user)
{
var userdata = UserDataManager.GetUserData(user, this);
return userdata != null && (userdata.IsFavorite || (userdata.Likes ?? false));
}
public virtual bool IsUnplayed(User user)
public virtual bool IsUnplayed(Jellyfin.Data.Entities.User user)
{
if (user == null)
{
@ -2623,7 +2620,7 @@ namespace MediaBrowser.Controller.Entities
return path;
}
public virtual void FillUserDataDtoValues(UserItemDataDto dto, UserItemData userData, BaseItemDto itemDto, User user, DtoOptions fields)
public virtual void FillUserDataDtoValues(UserItemDataDto dto, UserItemData userData, BaseItemDto itemDto, Jellyfin.Data.Entities.User user, DtoOptions fields)
{
if (RunTimeTicks.HasValue)
{
@ -2736,14 +2733,14 @@ namespace MediaBrowser.Controller.Entities
return RefreshMetadataForOwnedItem(video, copyTitleMetadata, newOptions, cancellationToken);
}
public string GetEtag(User user)
public string GetEtag(Jellyfin.Data.Entities.User user)
{
var list = GetEtagValues(user);
return string.Join("|", list).GetMD5().ToString("N", CultureInfo.InvariantCulture);
}
protected virtual List<string> GetEtagValues(User user)
protected virtual List<string> GetEtagValues(Jellyfin.Data.Entities.User user)
{
return new List<string>
{

@ -1,6 +1,7 @@
using System;
using System.Linq;
using System.Text.Json.Serialization;
using Jellyfin.Data.Enums;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Configuration;

@ -8,6 +8,7 @@ using System.Linq;
using System.Text.Json.Serialization;
using System.Threading;
using System.Threading.Tasks;
using Jellyfin.Data.Enums;
using MediaBrowser.Common.Progress;
using MediaBrowser.Controller.Channels;
using MediaBrowser.Controller.Collections;
@ -173,23 +174,25 @@ namespace MediaBrowser.Controller.Entities
[JsonIgnore]
public IEnumerable<BaseItem> RecursiveChildren => GetRecursiveChildren();
public override bool IsVisible(User user)
public override bool IsVisible(Jellyfin.Data.Entities.User user)
{
if (this is ICollectionFolder && !(this is BasePluginFolder))
{
if (user.Policy.BlockedMediaFolders != null)
if (user.GetPreference(PreferenceKind.BlockedMediaFolders) != null)
{
if (user.Policy.BlockedMediaFolders.Contains(Id.ToString("N", CultureInfo.InvariantCulture), StringComparer.OrdinalIgnoreCase) ||
if (user.GetPreference(PreferenceKind.BlockedMediaFolders).Contains(Id.ToString("N", CultureInfo.InvariantCulture), StringComparer.OrdinalIgnoreCase) ||
// Backwards compatibility
user.Policy.BlockedMediaFolders.Contains(Name, StringComparer.OrdinalIgnoreCase))
user.GetPreference(PreferenceKind.BlockedMediaFolders).Contains(Name, StringComparer.OrdinalIgnoreCase))
{
return false;
}
}
else
{
if (!user.Policy.EnableAllFolders && !user.Policy.EnabledFolders.Contains(Id.ToString("N", CultureInfo.InvariantCulture), StringComparer.OrdinalIgnoreCase))
if (!user.HasPermission(PermissionKind.EnableAllFolders)
&& !user.GetPreference(PreferenceKind.EnabledFolders)
.Contains(Id.ToString("N", CultureInfo.InvariantCulture), StringComparer.OrdinalIgnoreCase))
{
return false;
}
@ -583,7 +586,7 @@ namespace MediaBrowser.Controller.Entities
});
}
public virtual int GetChildCount(User user)
public virtual int GetChildCount(Jellyfin.Data.Entities.User user)
{
if (LinkedChildren.Length > 0)
{
@ -608,7 +611,7 @@ namespace MediaBrowser.Controller.Entities
return result.TotalRecordCount;
}
public virtual int GetRecursiveChildCount(User user)
public virtual int GetRecursiveChildCount(Jellyfin.Data.Entities.User user)
{
return GetItems(new InternalItemsQuery(user)
{
@ -877,7 +880,7 @@ namespace MediaBrowser.Controller.Entities
try
{
query.Parent = this;
query.ChannelIds = new Guid[] { ChannelId };
query.ChannelIds = new[] { ChannelId };
// Don't blow up here because it could cause parent screens with other content to fail
return ChannelManager.GetChannelItemsInternal(query, new SimpleProgress<double>(), CancellationToken.None).Result;
@ -947,11 +950,13 @@ namespace MediaBrowser.Controller.Entities
return UserViewBuilder.SortAndPage(items, null, query, LibraryManager, enableSorting);
}
private static IEnumerable<BaseItem> CollapseBoxSetItemsIfNeeded(IEnumerable<BaseItem> items,
private static IEnumerable<BaseItem> CollapseBoxSetItemsIfNeeded(
IEnumerable<BaseItem> items,
InternalItemsQuery query,
BaseItem queryParent,
User user,
IServerConfigurationManager configurationManager, ICollectionManager collectionManager)
Jellyfin.Data.Entities.User user,
IServerConfigurationManager configurationManager,
ICollectionManager collectionManager)
{
if (items == null)
{
@ -968,7 +973,7 @@ namespace MediaBrowser.Controller.Entities
private static bool CollapseBoxSetItems(InternalItemsQuery query,
BaseItem queryParent,
User user,
Jellyfin.Data.Entities.User user,
IServerConfigurationManager configurationManager)
{
// Could end up stuck in a loop like this
@ -1191,7 +1196,7 @@ namespace MediaBrowser.Controller.Entities
return true;
}
public List<BaseItem> GetChildren(User user, bool includeLinkedChildren)
public List<BaseItem> GetChildren(Jellyfin.Data.Entities.User user, bool includeLinkedChildren)
{
if (user == null)
{
@ -1201,7 +1206,7 @@ namespace MediaBrowser.Controller.Entities
return GetChildren(user, includeLinkedChildren, null);
}
public virtual List<BaseItem> GetChildren(User user, bool includeLinkedChildren, InternalItemsQuery query)
public virtual List<BaseItem> GetChildren(Jellyfin.Data.Entities.User user, bool includeLinkedChildren, InternalItemsQuery query)
{
if (user == null)
{
@ -1221,7 +1226,7 @@ namespace MediaBrowser.Controller.Entities
return result.Values.ToList();
}
protected virtual IEnumerable<BaseItem> GetEligibleChildrenForRecursiveChildren(User user)
protected virtual IEnumerable<BaseItem> GetEligibleChildrenForRecursiveChildren(Jellyfin.Data.Entities.User user)
{
return Children;
}
@ -1230,7 +1235,7 @@ namespace MediaBrowser.Controller.Entities
/// Adds the children to list.
/// </summary>
/// <returns><c>true</c> if XXXX, <c>false</c> otherwise</returns>
private void AddChildren(User user, bool includeLinkedChildren, Dictionary<Guid, BaseItem> result, bool recursive, InternalItemsQuery query)
private void AddChildren(Jellyfin.Data.Entities.User user, bool includeLinkedChildren, Dictionary<Guid, BaseItem> result, bool recursive, InternalItemsQuery query)
{
foreach (var child in GetEligibleChildrenForRecursiveChildren(user))
{
@ -1279,12 +1284,12 @@ namespace MediaBrowser.Controller.Entities
/// <param name="includeLinkedChildren">if set to <c>true</c> [include linked children].</param>
/// <returns>IEnumerable{BaseItem}.</returns>
/// <exception cref="ArgumentNullException"></exception>
public IEnumerable<BaseItem> GetRecursiveChildren(User user, bool includeLinkedChildren = true)
public IEnumerable<BaseItem> GetRecursiveChildren(Jellyfin.Data.Entities.User user, bool includeLinkedChildren = true)
{
return GetRecursiveChildren(user, null);
}
public virtual IEnumerable<BaseItem> GetRecursiveChildren(User user, InternalItemsQuery query)
public virtual IEnumerable<BaseItem> GetRecursiveChildren(Jellyfin.Data.Entities.User user, InternalItemsQuery query)
{
if (user == null)
{
@ -1403,7 +1408,7 @@ namespace MediaBrowser.Controller.Entities
return false;
}
public List<BaseItem> GetLinkedChildren(User user)
public List<BaseItem> GetLinkedChildren(Jellyfin.Data.Entities.User user)
{
if (!FilterLinkedChildrenPerUser || user == null)
{
@ -1565,7 +1570,7 @@ namespace MediaBrowser.Controller.Entities
/// <param name="datePlayed">The date played.</param>
/// <param name="resetPosition">if set to <c>true</c> [reset position].</param>
/// <returns>Task.</returns>
public override void MarkPlayed(User user,
public override void MarkPlayed(Jellyfin.Data.Entities.User user,
DateTime? datePlayed,
bool resetPosition)
{
@ -1577,7 +1582,7 @@ namespace MediaBrowser.Controller.Entities
EnableTotalRecordCount = false
};
if (!user.Configuration.DisplayMissingEpisodes)
if (!user.DisplayMissingEpisodes)
{
query.IsVirtualItem = false;
}
@ -1606,7 +1611,7 @@ namespace MediaBrowser.Controller.Entities
/// </summary>
/// <param name="user">The user.</param>
/// <returns>Task.</returns>
public override void MarkUnplayed(User user)
public override void MarkUnplayed(Jellyfin.Data.Entities.User user)
{
var itemsResult = GetItemList(new InternalItemsQuery
{
@ -1624,7 +1629,7 @@ namespace MediaBrowser.Controller.Entities
}
}
public override bool IsPlayed(User user)
public override bool IsPlayed(Jellyfin.Data.Entities.User user)
{
var itemsResult = GetItemList(new InternalItemsQuery(user)
{
@ -1639,7 +1644,7 @@ namespace MediaBrowser.Controller.Entities
.All(i => i.IsPlayed(user));
}
public override bool IsUnplayed(User user)
public override bool IsUnplayed(Jellyfin.Data.Entities.User user)
{
return !IsPlayed(user);
}
@ -1684,7 +1689,7 @@ namespace MediaBrowser.Controller.Entities
}
}
public override void FillUserDataDtoValues(UserItemDataDto dto, UserItemData userData, BaseItemDto itemDto, User user, DtoOptions fields)
public override void FillUserDataDtoValues(UserItemDataDto dto, UserItemData userData, BaseItemDto itemDto, Jellyfin.Data.Entities.User user, DtoOptions fields)
{
if (!SupportsUserDataFromChildren)
{

@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Jellyfin.Data.Enums;
using MediaBrowser.Controller.Dto;
using MediaBrowser.Model.Configuration;
using MediaBrowser.Model.Entities;
@ -15,7 +16,7 @@ namespace MediaBrowser.Controller.Entities
public int? Limit { get; set; }
public User User { get; set; }
public Jellyfin.Data.Entities.User User { get; set; }
public BaseItem SimilarTo { get; set; }
@ -213,25 +214,26 @@ namespace MediaBrowser.Controller.Entities
Years = Array.Empty<int>();
}
public InternalItemsQuery(User user)
public InternalItemsQuery(Jellyfin.Data.Entities.User user)
: this()
{
SetUser(user);
}
public void SetUser(User user)
public void SetUser(Jellyfin.Data.Entities.User user)
{
if (user != null)
{
var policy = user.Policy;
MaxParentalRating = policy.MaxParentalRating;
MaxParentalRating = user.MaxParentalAgeRating;
if (policy.MaxParentalRating.HasValue)
if (MaxParentalRating.HasValue)
{
BlockUnratedItems = policy.BlockUnratedItems.Where(i => i != UnratedItem.Other).ToArray();
BlockUnratedItems = user.GetPreference(PreferenceKind.BlockUnratedItems)
.Where(i => i != UnratedItem.Other.ToString())
.Select(e => Enum.Parse<UnratedItem>(e, true)).ToArray();
}
ExcludeInheritedTags = policy.BlockedTags;
ExcludeInheritedTags = user.GetPreference(PreferenceKind.BlockedTags);
User = user;
}

@ -2,11 +2,10 @@ using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.Json.Serialization;
using Jellyfin.Data.Enums;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Configuration;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Querying;
using MediaBrowser.Model.Users;
namespace MediaBrowser.Controller.Entities.Movies
{
@ -45,9 +44,9 @@ namespace MediaBrowser.Controller.Entities.Movies
/// <value>The display order.</value>
public string DisplayOrder { get; set; }
protected override bool GetBlockUnratedValue(UserPolicy config)
protected override bool GetBlockUnratedValue(Jellyfin.Data.Entities.User user)
{
return config.BlockUnratedItems.Contains(UnratedItem.Movie);
return user.GetPreference(PreferenceKind.BlockUnratedItems).Contains(UnratedItem.Movie.ToString());
}
public override double GetDefaultPrimaryImageAspectRatio()
@ -101,7 +100,7 @@ namespace MediaBrowser.Controller.Entities.Movies
[JsonIgnore]
public override bool IsPreSorted => true;
public override bool IsAuthorizedToDelete(User user, List<Folder> allCollectionFolders)
public override bool IsAuthorizedToDelete(Jellyfin.Data.Entities.User user, List<Folder> allCollectionFolders)
{
return true;
}
@ -111,7 +110,7 @@ namespace MediaBrowser.Controller.Entities.Movies
return true;
}
public override List<BaseItem> GetChildren(User user, bool includeLinkedChildren, InternalItemsQuery query)
public override List<BaseItem> GetChildren(Jellyfin.Data.Entities.User user, bool includeLinkedChildren, InternalItemsQuery query)
{
var children = base.GetChildren(user, includeLinkedChildren, query);
@ -131,7 +130,7 @@ namespace MediaBrowser.Controller.Entities.Movies
return LibraryManager.Sort(children, user, new[] { ItemSortBy.ProductionYear, ItemSortBy.PremiereDate, ItemSortBy.SortName }, SortOrder.Ascending).ToList();
}
public override IEnumerable<BaseItem> GetRecursiveChildren(User user, InternalItemsQuery query)
public override IEnumerable<BaseItem> GetRecursiveChildren(Jellyfin.Data.Entities.User user, InternalItemsQuery query)
{
var children = base.GetRecursiveChildren(user, query);
@ -149,7 +148,7 @@ namespace MediaBrowser.Controller.Entities.Movies
return GetItemLookupInfo<BoxSetInfo>();
}
public override bool IsVisible(User user)
public override bool IsVisible(Jellyfin.Data.Entities.User user)
{
if (IsLegacyBoxSet)
{
@ -177,7 +176,7 @@ namespace MediaBrowser.Controller.Entities.Movies
return false;
}
public override bool IsVisibleStandalone(User user)
public override bool IsVisibleStandalone(Jellyfin.Data.Entities.User user)
{
if (IsLegacyBoxSet)
{
@ -189,7 +188,7 @@ namespace MediaBrowser.Controller.Entities.Movies
public Guid[] LibraryFolderIds { get; set; }
private Guid[] GetLibraryFolderIds(User user)
private Guid[] GetLibraryFolderIds(Jellyfin.Data.Entities.User user)
{
return LibraryManager.GetUserRootFolder().GetChildren(user, true)
.Select(i => i.Id)

@ -4,6 +4,7 @@ using System.Linq;
using System.Text.Json.Serialization;
using System.Threading;
using System.Threading.Tasks;
using Jellyfin.Data.Enums;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Configuration;
using MediaBrowser.Model.Entities;

@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Text.Json.Serialization;
using Jellyfin.Data.Enums;
using MediaBrowser.Controller.Entities.Audio;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Configuration;

@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text.Json.Serialization;
using Jellyfin.Data.Enums;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Configuration;
using MediaBrowser.Model.Entities;

@ -2,6 +2,7 @@ using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.Json.Serialization;
using Jellyfin.Data.Enums;
using MediaBrowser.Controller.Dto;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Configuration;
@ -60,7 +61,7 @@ namespace MediaBrowser.Controller.Entities.TV
return list;
}
public override int GetChildCount(User user)
public override int GetChildCount(Jellyfin.Data.Entities.User user)
{
var result = GetChildren(user, true).Count;
@ -143,17 +144,17 @@ namespace MediaBrowser.Controller.Entities.TV
/// <summary>
/// Gets the episodes.
/// </summary>
public List<BaseItem> GetEpisodes(User user, DtoOptions options)
public List<BaseItem> GetEpisodes(Jellyfin.Data.Entities.User user, DtoOptions options)
{
return GetEpisodes(Series, user, options);
}
public List<BaseItem> GetEpisodes(Series series, User user, DtoOptions options)
public List<BaseItem> GetEpisodes(Series series, Jellyfin.Data.Entities.User user, DtoOptions options)
{
return GetEpisodes(series, user, null, options);
}
public List<BaseItem> GetEpisodes(Series series, User user, IEnumerable<Episode> allSeriesEpisodes, DtoOptions options)
public List<BaseItem> GetEpisodes(Series series, Jellyfin.Data.Entities.User user, IEnumerable<Episode> allSeriesEpisodes, DtoOptions options)
{
return series.GetSeasonEpisodes(this, user, allSeriesEpisodes, options);
}
@ -163,12 +164,12 @@ namespace MediaBrowser.Controller.Entities.TV
return Series.GetSeasonEpisodes(this, null, null, new DtoOptions(true));
}
public override List<BaseItem> GetChildren(User user, bool includeLinkedChildren, InternalItemsQuery query)
public override List<BaseItem> GetChildren(Jellyfin.Data.Entities.User user, bool includeLinkedChildren, InternalItemsQuery query)
{
return GetEpisodes(user, new DtoOptions(true));
}
protected override bool GetBlockUnratedValue(UserPolicy config)
protected override bool GetBlockUnratedValue(Jellyfin.Data.Entities.User config)
{
// Don't block. Let either the entire series rating or episode rating determine it
return false;
@ -203,7 +204,7 @@ namespace MediaBrowser.Controller.Entities.TV
public Guid FindSeriesId()
{
var series = FindParent<Series>();
return series == null ? Guid.Empty : series.Id;
return series?.Id ?? Guid.Empty;
}
/// <summary>
@ -234,7 +235,7 @@ namespace MediaBrowser.Controller.Entities.TV
if (!IndexNumber.HasValue && !string.IsNullOrEmpty(Path))
{
IndexNumber = IndexNumber ?? LibraryManager.GetSeasonNumberFromPath(Path);
IndexNumber ??= LibraryManager.GetSeasonNumberFromPath(Path);
// If a change was made record it
if (IndexNumber.HasValue)

@ -5,13 +5,12 @@ using System.Linq;
using System.Text.Json.Serialization;
using System.Threading;
using System.Threading.Tasks;
using Jellyfin.Data.Enums;
using MediaBrowser.Controller.Dto;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Configuration;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Providers;
using MediaBrowser.Model.Querying;
using MediaBrowser.Model.Users;
namespace MediaBrowser.Controller.Entities.TV
{
@ -111,7 +110,7 @@ namespace MediaBrowser.Controller.Entities.TV
return series.GetPresentationUniqueKey();
}
public override int GetChildCount(User user)
public override int GetChildCount(Jellyfin.Data.Entities.User user)
{
var seriesKey = GetUniqueSeriesKey(this);
@ -119,7 +118,7 @@ namespace MediaBrowser.Controller.Entities.TV
{
AncestorWithPresentationUniqueKey = null,
SeriesPresentationUniqueKey = seriesKey,
IncludeItemTypes = new[] { typeof(Season).Name },
IncludeItemTypes = new[] { nameof(Season) },
IsVirtualItem = false,
Limit = 0,
DtoOptions = new DtoOptions(false)
@ -131,7 +130,7 @@ namespace MediaBrowser.Controller.Entities.TV
return result;
}
public override int GetRecursiveChildCount(User user)
public override int GetRecursiveChildCount(Jellyfin.Data.Entities.User user)
{
var seriesKey = GetUniqueSeriesKey(this);
@ -179,12 +178,12 @@ namespace MediaBrowser.Controller.Entities.TV
return list;
}
public override List<BaseItem> GetChildren(User user, bool includeLinkedChildren, InternalItemsQuery query)
public override List<BaseItem> GetChildren(Jellyfin.Data.Entities.User user, bool includeLinkedChildren, InternalItemsQuery query)
{
return GetSeasons(user, new DtoOptions(true));
}
public List<BaseItem> GetSeasons(User user, DtoOptions options)
public List<BaseItem> GetSeasons(Jellyfin.Data.Entities.User user, DtoOptions options)
{
var query = new InternalItemsQuery(user)
{
@ -196,7 +195,7 @@ namespace MediaBrowser.Controller.Entities.TV
return LibraryManager.GetItemList(query);
}
private void SetSeasonQueryOptions(InternalItemsQuery query, User user)
private void SetSeasonQueryOptions(InternalItemsQuery query, Jellyfin.Data.Entities.User user)
{
var seriesKey = GetUniqueSeriesKey(this);
@ -205,14 +204,9 @@ namespace MediaBrowser.Controller.Entities.TV
query.IncludeItemTypes = new[] { typeof(Season).Name };
query.OrderBy = new[] { ItemSortBy.SortName }.Select(i => new ValueTuple<string, SortOrder>(i, SortOrder.Ascending)).ToArray();
if (user != null)
if (user != null && !user.DisplayMissingEpisodes)
{
var config = user.Configuration;
if (!config.DisplayMissingEpisodes)
{
query.IsMissing = false;
}
query.IsMissing = false;
}
}
@ -245,7 +239,7 @@ namespace MediaBrowser.Controller.Entities.TV
return LibraryManager.GetItemsResult(query);
}
public IEnumerable<BaseItem> GetEpisodes(User user, DtoOptions options)
public IEnumerable<BaseItem> GetEpisodes(Jellyfin.Data.Entities.User user, DtoOptions options)
{
var seriesKey = GetUniqueSeriesKey(this);
@ -257,8 +251,8 @@ namespace MediaBrowser.Controller.Entities.TV
OrderBy = new[] { ItemSortBy.SortName }.Select(i => new ValueTuple<string, SortOrder>(i, SortOrder.Ascending)).ToArray(),
DtoOptions = options
};
var config = user.Configuration;
if (!config.DisplayMissingEpisodes)
if (!user.DisplayMissingEpisodes)
{
query.IsMissing = false;
}
@ -311,7 +305,7 @@ namespace MediaBrowser.Controller.Entities.TV
// Refresh episodes and other children
foreach (var item in items)
{
if ((item is Season))
if (item is Season)
{
continue;
}
@ -351,7 +345,7 @@ namespace MediaBrowser.Controller.Entities.TV
await ProviderManager.RefreshSingleItem(this, refreshOptions, cancellationToken).ConfigureAwait(false);
}
public List<BaseItem> GetSeasonEpisodes(Season parentSeason, User user, DtoOptions options)
public List<BaseItem> GetSeasonEpisodes(Season parentSeason, Jellyfin.Data.Entities.User user, DtoOptions options)
{
var queryFromSeries = ConfigurationManager.Configuration.DisplaySpecialsWithinSeasons;
@ -370,8 +364,7 @@ namespace MediaBrowser.Controller.Entities.TV
};
if (user != null)
{
var config = user.Configuration;
if (!config.DisplayMissingEpisodes)
if (!user.DisplayMissingEpisodes)
{
query.IsMissing = false;
}
@ -382,7 +375,7 @@ namespace MediaBrowser.Controller.Entities.TV
return GetSeasonEpisodes(parentSeason, user, allItems, options);
}
public List<BaseItem> GetSeasonEpisodes(Season parentSeason, User user, IEnumerable<BaseItem> allSeriesEpisodes, DtoOptions options)
public List<BaseItem> GetSeasonEpisodes(Season parentSeason, Jellyfin.Data.Entities.User user, IEnumerable<BaseItem> allSeriesEpisodes, DtoOptions options)
{
if (allSeriesEpisodes == null)
{
@ -452,9 +445,9 @@ namespace MediaBrowser.Controller.Entities.TV
}
protected override bool GetBlockUnratedValue(UserPolicy config)
protected override bool GetBlockUnratedValue(Jellyfin.Data.Entities.User user)
{
return config.BlockUnratedItems.Contains(UnratedItem.Series);
return user.GetPreference(PreferenceKind.BlockUnratedItems).Contains(UnratedItem.Series.ToString());
}
public override UnratedItem GetBlockUnratedType()

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save