aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Emby.Dlna/ContentDirectory/ContentDirectory.cs13
-rw-r--r--Emby.Dlna/ContentDirectory/ControlHandler.cs89
-rw-r--r--Emby.Dlna/Didl/DidlBuilder.cs11
-rw-r--r--Emby.Dlna/PlayTo/PlayToController.cs8
-rw-r--r--Emby.Drawing/ImageProcessor.cs9
-rw-r--r--Emby.Notifications/Api/NotificationsService.cs6
-rw-r--r--Emby.Notifications/NotificationManager.cs13
-rw-r--r--Emby.Server.Implementations/Activity/ActivityLogEntryPoint.cs42
-rw-r--r--Emby.Server.Implementations/ApplicationHost.cs9
-rw-r--r--Emby.Server.Implementations/Channels/ChannelManager.cs7
-rw-r--r--Emby.Server.Implementations/Collections/CollectionManager.cs4
-rw-r--r--Emby.Server.Implementations/Data/SqliteDisplayPreferencesRepository.cs225
-rw-r--r--Emby.Server.Implementations/Data/SqliteUserDataRepository.cs1
-rw-r--r--Emby.Server.Implementations/Data/SqliteUserRepository.cs240
-rw-r--r--Emby.Server.Implementations/Devices/DeviceManager.cs26
-rw-r--r--Emby.Server.Implementations/Dto/DtoService.cs23
-rw-r--r--Emby.Server.Implementations/EntryPoints/LibraryChangedNotifier.cs2
-rw-r--r--Emby.Server.Implementations/EntryPoints/RecordingNotifier.cs3
-rw-r--r--Emby.Server.Implementations/EntryPoints/RefreshUsersMetadata.cs77
-rw-r--r--Emby.Server.Implementations/EntryPoints/ServerEventNotifier.cs29
-rw-r--r--Emby.Server.Implementations/HttpServer/Security/AuthService.cs24
-rw-r--r--Emby.Server.Implementations/HttpServer/Security/AuthorizationContext.cs4
-rw-r--r--Emby.Server.Implementations/HttpServer/Security/SessionContext.cs4
-rw-r--r--Emby.Server.Implementations/Library/LibraryManager.cs22
-rw-r--r--Emby.Server.Implementations/Library/MediaSourceManager.cs52
-rw-r--r--Emby.Server.Implementations/Library/MediaStreamSelector.cs1
-rw-r--r--Emby.Server.Implementations/Library/MusicManager.cs32
-rw-r--r--Emby.Server.Implementations/Library/SearchEngine.cs4
-rw-r--r--Emby.Server.Implementations/Library/UserDataManager.cs10
-rw-r--r--Emby.Server.Implementations/Library/UserManager.cs1132
-rw-r--r--Emby.Server.Implementations/Library/UserViewManager.cs29
-rw-r--r--Emby.Server.Implementations/LiveTv/LiveTvManager.cs40
-rw-r--r--Emby.Server.Implementations/Playlists/ManualPlaylistsFolder.cs6
-rw-r--r--Emby.Server.Implementations/Playlists/PlaylistManager.cs4
-rw-r--r--Emby.Server.Implementations/Session/SessionManager.cs91
-rw-r--r--Emby.Server.Implementations/Sorting/DateLastMediaAddedComparer.cs2
-rw-r--r--Emby.Server.Implementations/Sorting/DatePlayedComparer.cs2
-rw-r--r--Emby.Server.Implementations/Sorting/IsFavoriteOrLikeComparer.cs2
-rw-r--r--Emby.Server.Implementations/Sorting/IsPlayedComparer.cs2
-rw-r--r--Emby.Server.Implementations/Sorting/IsUnplayedComparer.cs2
-rw-r--r--Emby.Server.Implementations/Sorting/PlayCountComparer.cs2
-rw-r--r--Emby.Server.Implementations/TV/TVSeriesManager.cs13
-rw-r--r--Jellyfin.Api/Auth/CustomAuthenticationHandler.cs5
-rw-r--r--Jellyfin.Api/Controllers/StartupController.cs4
-rw-r--r--Jellyfin.Data/DayOfWeekHelper.cs (renamed from MediaBrowser.Controller/Entities/DayOfWeekHelper.cs)7
-rw-r--r--Jellyfin.Data/Entities/AccessSchedule.cs68
-rw-r--r--Jellyfin.Data/Entities/ImageInfo.cs25
-rw-r--r--Jellyfin.Data/Entities/User.cs283
-rw-r--r--Jellyfin.Data/Enums/DynamicDayOfWeek.cs (renamed from MediaBrowser.Model/Configuration/DynamicDayOfWeek.cs)2
-rw-r--r--Jellyfin.Data/Enums/PermissionKind.cs7
-rw-r--r--Jellyfin.Data/Enums/PreferenceKind.cs11
-rw-r--r--Jellyfin.Data/Enums/SubtitlePlaybackMode.cs (renamed from MediaBrowser.Model/Configuration/SubtitlePlaybackMode.cs)4
-rw-r--r--Jellyfin.Data/Enums/UnratedItem.cs (renamed from MediaBrowser.Model/Configuration/UnratedItem.cs)2
-rw-r--r--Jellyfin.Server.Implementations/Jellyfin.Server.Implementations.csproj2
-rw-r--r--Jellyfin.Server.Implementations/JellyfinDb.cs4
-rw-r--r--Jellyfin.Server.Implementations/Migrations/20200504195702_UserSchema.Designer.cs324
-rw-r--r--Jellyfin.Server.Implementations/Migrations/20200504195702_UserSchema.cs219
-rw-r--r--Jellyfin.Server.Implementations/Migrations/JellyfinDbModelSnapshot.cs251
-rw-r--r--Jellyfin.Server.Implementations/User/DefaultAuthenticationProvider.cs (renamed from Emby.Server.Implementations/Library/DefaultAuthenticationProvider.cs)36
-rw-r--r--Jellyfin.Server.Implementations/User/DefaultPasswordResetProvider.cs (renamed from Emby.Server.Implementations/Library/DefaultPasswordResetProvider.cs)48
-rw-r--r--Jellyfin.Server.Implementations/User/DeviceAccessEntryPoint.cs65
-rw-r--r--Jellyfin.Server.Implementations/User/InvalidAuthProvider.cs (renamed from Emby.Server.Implementations/Library/InvalidAuthProvider.cs)17
-rw-r--r--Jellyfin.Server.Implementations/User/UserManager.cs771
-rw-r--r--Jellyfin.Server/Jellyfin.Server.csproj1
-rw-r--r--Jellyfin.Server/Migrations/MigrationRunner.cs3
-rw-r--r--Jellyfin.Server/Migrations/Routines/MigrateUserDb.cs144
-rw-r--r--MediaBrowser.Api/BaseApiService.cs5
-rw-r--r--MediaBrowser.Api/FilterService.cs2
-rw-r--r--MediaBrowser.Api/Images/ImageService.cs148
-rw-r--r--MediaBrowser.Api/Library/LibraryService.cs8
-rw-r--r--MediaBrowser.Api/LiveTv/LiveTvService.cs3
-rw-r--r--MediaBrowser.Api/Movies/MoviesService.cs28
-rw-r--r--MediaBrowser.Api/Music/InstantMixService.cs2
-rw-r--r--MediaBrowser.Api/Playback/BaseStreamingService.cs3
-rw-r--r--MediaBrowser.Api/Playback/MediaInfoService.cs36
-rw-r--r--MediaBrowser.Api/Sessions/SessionService.cs5
-rw-r--r--MediaBrowser.Api/SuggestionsService.cs2
-rw-r--r--MediaBrowser.Api/TvShowsService.cs8
-rw-r--r--MediaBrowser.Api/UserLibrary/BaseItemsByNameService.cs4
-rw-r--r--MediaBrowser.Api/UserLibrary/ItemsService.cs20
-rw-r--r--MediaBrowser.Api/UserLibrary/PlaystateService.cs2
-rw-r--r--MediaBrowser.Api/UserLibrary/UserLibraryService.cs2
-rw-r--r--MediaBrowser.Api/UserService.cs44
-rw-r--r--MediaBrowser.Controller/Authentication/IAuthenticationProvider.cs2
-rw-r--r--MediaBrowser.Controller/Authentication/IPasswordResetProvider.cs2
-rw-r--r--MediaBrowser.Controller/Channels/Channel.cs13
-rw-r--r--MediaBrowser.Controller/Collections/ICollectionManager.cs2
-rw-r--r--MediaBrowser.Controller/Devices/IDeviceManager.cs2
-rw-r--r--MediaBrowser.Controller/Drawing/IImageProcessor.cs3
-rw-r--r--MediaBrowser.Controller/Drawing/ImageProcessorExtensions.cs1
-rw-r--r--MediaBrowser.Controller/Dto/IDtoService.cs8
-rw-r--r--MediaBrowser.Controller/Entities/Audio/Audio.cs1
-rw-r--r--MediaBrowser.Controller/Entities/Audio/MusicAlbum.cs8
-rw-r--r--MediaBrowser.Controller/Entities/Audio/MusicArtist.cs8
-rw-r--r--MediaBrowser.Controller/Entities/AudioBook.cs1
-rw-r--r--MediaBrowser.Controller/Entities/BaseItem.cs69
-rw-r--r--MediaBrowser.Controller/Entities/Book.cs1
-rw-r--r--MediaBrowser.Controller/Entities/Folder.cs55
-rw-r--r--MediaBrowser.Controller/Entities/InternalItemsQuery.cs18
-rw-r--r--MediaBrowser.Controller/Entities/Movies/BoxSet.cs19
-rw-r--r--MediaBrowser.Controller/Entities/Movies/Movie.cs1
-rw-r--r--MediaBrowser.Controller/Entities/MusicVideo.cs1
-rw-r--r--MediaBrowser.Controller/Entities/TV/Episode.cs1
-rw-r--r--MediaBrowser.Controller/Entities/TV/Season.cs17
-rw-r--r--MediaBrowser.Controller/Entities/TV/Series.cs43
-rw-r--r--MediaBrowser.Controller/Entities/Trailer.cs1
-rw-r--r--MediaBrowser.Controller/Entities/User.cs262
-rw-r--r--MediaBrowser.Controller/Entities/UserRootFolder.cs4
-rw-r--r--MediaBrowser.Controller/Entities/UserView.cs8
-rw-r--r--MediaBrowser.Controller/Entities/UserViewBuilder.cs86
-rw-r--r--MediaBrowser.Controller/Library/IIntroProvider.cs2
-rw-r--r--MediaBrowser.Controller/Library/ILibraryManager.cs12
-rw-r--r--MediaBrowser.Controller/Library/IMediaSourceManager.cs6
-rw-r--r--MediaBrowser.Controller/Library/IMusicManager.cs6
-rw-r--r--MediaBrowser.Controller/Library/IUserDataManager.cs8
-rw-r--r--MediaBrowser.Controller/Library/IUserManager.cs127
-rw-r--r--MediaBrowser.Controller/Library/PlaybackProgressEventArgs.cs4
-rw-r--r--MediaBrowser.Controller/LiveTv/ILiveTvManager.cs12
-rw-r--r--MediaBrowser.Controller/LiveTv/LiveTvChannel.cs1
-rw-r--r--MediaBrowser.Controller/LiveTv/LiveTvProgram.cs1
-rw-r--r--MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs7
-rw-r--r--MediaBrowser.Controller/MediaEncoding/EncodingJobInfo.cs17
-rw-r--r--MediaBrowser.Controller/Net/AuthorizationInfo.cs10
-rw-r--r--MediaBrowser.Controller/Net/IAuthService.cs2
-rw-r--r--MediaBrowser.Controller/Net/ISessionContext.cs2
-rw-r--r--MediaBrowser.Controller/Notifications/INotificationService.cs2
-rw-r--r--MediaBrowser.Controller/Notifications/UserNotification.cs2
-rw-r--r--MediaBrowser.Controller/Persistence/IUserRepository.cs27
-rw-r--r--MediaBrowser.Controller/Playlists/Playlist.cs26
-rw-r--r--MediaBrowser.Controller/Providers/IProviderManager.cs3
-rw-r--r--MediaBrowser.Controller/Session/ISessionManager.cs2
-rw-r--r--MediaBrowser.Controller/Sorting/IUserBaseItemComparer.cs2
-rw-r--r--MediaBrowser.Model/Configuration/AccessSchedule.cs2
-rw-r--r--MediaBrowser.Model/Configuration/UserConfiguration.cs1
-rw-r--r--MediaBrowser.Model/Notifications/NotificationOptions.cs6
-rw-r--r--MediaBrowser.Model/Users/UserPolicy.cs7
-rw-r--r--MediaBrowser.Providers/Manager/ImageSaver.cs27
-rw-r--r--MediaBrowser.Providers/Manager/ProviderManager.cs14
-rw-r--r--MediaBrowser.Providers/Users/UserMetadataService.cs30
-rw-r--r--tests/Jellyfin.Api.Tests/Auth/CustomAuthenticationHandlerTests.cs13
140 files changed, 3075 insertions, 2885 deletions
diff --git a/Emby.Dlna/ContentDirectory/ContentDirectory.cs b/Emby.Dlna/ContentDirectory/ContentDirectory.cs
index 64cd308a2..ea577a905 100644
--- a/Emby.Dlna/ContentDirectory/ContentDirectory.cs
+++ b/Emby.Dlna/ContentDirectory/ContentDirectory.cs
@@ -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();
}
}
}
diff --git a/Emby.Dlna/ContentDirectory/ControlHandler.cs b/Emby.Dlna/ContentDirectory/ControlHandler.cs
index 28888f031..32bda2fe1 100644
--- a/Emby.Dlna/ContentDirectory/ControlHandler.cs
+++ b/Emby.Dlna/ContentDirectory/ControlHandler.cs
@@ -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)
{
diff --git a/Emby.Dlna/Didl/DidlBuilder.cs b/Emby.Dlna/Didl/DidlBuilder.cs
index f7d840c62..24932ced9 100644
--- a/Emby.Dlna/Didl/DidlBuilder.cs
+++ b/Emby.Dlna/Didl/DidlBuilder.cs
@@ -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)
{
diff --git a/Emby.Dlna/PlayTo/PlayToController.cs b/Emby.Dlna/PlayTo/PlayToController.cs
index 9d7c0d365..db786833f 100644
--- a/Emby.Dlna/PlayTo/PlayToController.cs
+++ b/Emby.Dlna/PlayTo/PlayToController.cs
@@ -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;
diff --git a/Emby.Drawing/ImageProcessor.cs b/Emby.Drawing/ImageProcessor.cs
index 0b3bbe29e..b9172d2a8 100644
--- a/Emby.Drawing/ImageProcessor.cs
+++ b/Emby.Drawing/ImageProcessor.cs
@@ -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)
diff --git a/Emby.Notifications/Api/NotificationsService.cs b/Emby.Notifications/Api/NotificationsService.cs
index 788750796..221db5423 100644
--- a/Emby.Notifications/Api/NotificationsService.cs
+++ b/Emby.Notifications/Api/NotificationsService.cs
@@ -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);
diff --git a/Emby.Notifications/NotificationManager.cs b/Emby.Notifications/NotificationManager.cs
index 639a5e1aa..9a9bc4415 100644
--- a/Emby.Notifications/NotificationManager.cs
+++ b/Emby.Notifications/NotificationManager.cs
@@ -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
{
diff --git a/Emby.Server.Implementations/Activity/ActivityLogEntryPoint.cs b/Emby.Server.Implementations/Activity/ActivityLogEntryPoint.cs
index 3983824a3..31d9609a6 100644
--- a/Emby.Server.Implementations/Activity/ActivityLogEntryPoint.cs
+++ b/Emby.Server.Implementations/Activity/ActivityLogEntryPoint.cs
@@ -88,22 +88,21 @@ namespace Emby.Server.Implementations.Activity
_subManager.SubtitleDownloadFailure += OnSubtitleDownloadFailure;
- _userManager.UserCreated += OnUserCreated;
- _userManager.UserPasswordChanged += OnUserPasswordChanged;
- _userManager.UserDeleted += OnUserDeleted;
- _userManager.UserPolicyUpdated += OnUserPolicyUpdated;
- _userManager.UserLockedOut += OnUserLockedOut;
+ _userManager.OnUserCreated += OnUserCreated;
+ _userManager.OnUserPasswordChanged += OnUserPasswordChanged;
+ _userManager.OnUserDeleted += OnUserDeleted;
+ _userManager.OnUserLockedOut += OnUserLockedOut;
return Task.CompletedTask;
}
- private async void OnUserLockedOut(object sender, GenericEventArgs<MediaBrowser.Controller.Entities.User> e)
+ private async void OnUserLockedOut(object sender, GenericEventArgs<User> e)
{
await CreateLogEntry(new ActivityLog(
string.Format(
CultureInfo.InvariantCulture,
_localization.GetLocalizedString("UserLockedOutWithName"),
- e.Argument.Name),
+ e.Argument.Username),
NotificationType.UserLockedOut.ToString(),
e.Argument.Id))
.ConfigureAwait(false);
@@ -152,7 +151,7 @@ namespace Emby.Server.Implementations.Activity
string.Format(
CultureInfo.InvariantCulture,
_localization.GetLocalizedString("UserStoppedPlayingItemWithValues"),
- user.Name,
+ user.Username,
GetItemName(item),
e.DeviceName),
GetPlaybackStoppedNotificationType(item.MediaType),
@@ -187,7 +186,7 @@ namespace Emby.Server.Implementations.Activity
string.Format(
CultureInfo.InvariantCulture,
_localization.GetLocalizedString("UserStartedPlayingItemWithValues"),
- user.Name,
+ user.Username,
GetItemName(item),
e.DeviceName),
GetPlaybackNotificationType(item.MediaType),
@@ -304,49 +303,49 @@ namespace Emby.Server.Implementations.Activity
}).ConfigureAwait(false);
}
- private async void OnUserPolicyUpdated(object sender, GenericEventArgs<MediaBrowser.Controller.Entities.User> e)
+ private async void OnUserPolicyUpdated(object sender, GenericEventArgs<User> e)
{
await CreateLogEntry(new ActivityLog(
string.Format(
CultureInfo.InvariantCulture,
_localization.GetLocalizedString("UserPolicyUpdatedWithName"),
- e.Argument.Name),
+ e.Argument.Username),
"UserPolicyUpdated",
e.Argument.Id))
.ConfigureAwait(false);
}
- private async void OnUserDeleted(object sender, GenericEventArgs<MediaBrowser.Controller.Entities.User> e)
+ private async void OnUserDeleted(object sender, GenericEventArgs<User> e)
{
await CreateLogEntry(new ActivityLog(
string.Format(
CultureInfo.InvariantCulture,
_localization.GetLocalizedString("UserDeletedWithName"),
- e.Argument.Name),
+ e.Argument.Username),
"UserDeleted",
Guid.Empty))
.ConfigureAwait(false);
}
- private async void OnUserPasswordChanged(object sender, GenericEventArgs<MediaBrowser.Controller.Entities.User> e)
+ private async void OnUserPasswordChanged(object sender, GenericEventArgs<User> e)
{
await CreateLogEntry(new ActivityLog(
string.Format(
CultureInfo.InvariantCulture,
_localization.GetLocalizedString("UserPasswordChangedWithName"),
- e.Argument.Name),
+ e.Argument.Username),
"UserPasswordChanged",
e.Argument.Id))
.ConfigureAwait(false);
}
- private async void OnUserCreated(object sender, GenericEventArgs<MediaBrowser.Controller.Entities.User> e)
+ private async void OnUserCreated(object sender, GenericEventArgs<User> e)
{
await CreateLogEntry(new ActivityLog(
string.Format(
CultureInfo.InvariantCulture,
_localization.GetLocalizedString("UserCreatedWithName"),
- e.Argument.Name),
+ e.Argument.Username),
"UserCreated",
e.Argument.Id))
.ConfigureAwait(false);
@@ -510,11 +509,10 @@ namespace Emby.Server.Implementations.Activity
_subManager.SubtitleDownloadFailure -= OnSubtitleDownloadFailure;
- _userManager.UserCreated -= OnUserCreated;
- _userManager.UserPasswordChanged -= OnUserPasswordChanged;
- _userManager.UserDeleted -= OnUserDeleted;
- _userManager.UserPolicyUpdated -= OnUserPolicyUpdated;
- _userManager.UserLockedOut -= OnUserLockedOut;
+ _userManager.OnUserCreated -= OnUserCreated;
+ _userManager.OnUserPasswordChanged -= OnUserPasswordChanged;
+ _userManager.OnUserDeleted -= OnUserDeleted;
+ _userManager.OnUserLockedOut -= OnUserLockedOut;
}
/// <summary>
diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs
index e6410f857..b2fe6a3ab 100644
--- a/Emby.Server.Implementations/ApplicationHost.cs
+++ b/Emby.Server.Implementations/ApplicationHost.cs
@@ -560,8 +560,6 @@ namespace Emby.Server.Implementations
serviceCollection.AddSingleton<IAuthenticationRepository, AuthenticationRepository>();
- serviceCollection.AddSingleton<IUserRepository, SqliteUserRepository>();
-
// TODO: Refactor to eliminate the circular dependency here so that Lazy<T> isn't required
serviceCollection.AddTransient(provider => new Lazy<IDtoService>(provider.GetRequiredService<IDtoService>));
serviceCollection.AddSingleton<IUserManager, UserManager>();
@@ -655,15 +653,11 @@ namespace Emby.Server.Implementations
((SqliteDisplayPreferencesRepository)Resolve<IDisplayPreferencesRepository>()).Initialize();
((AuthenticationRepository)Resolve<IAuthenticationRepository>()).Initialize();
- ((SqliteUserRepository)Resolve<IUserRepository>()).Initialize();
SetStaticProperties();
- var userManager = (UserManager)Resolve<IUserManager>();
- userManager.Initialize();
-
var userDataRepo = (SqliteUserDataRepository)Resolve<IUserDataRepository>();
- ((SqliteItemRepository)Resolve<IItemRepository>()).Initialize(userDataRepo, userManager);
+ ((SqliteItemRepository)Resolve<IItemRepository>()).Initialize(userDataRepo, Resolve<IUserManager>());
FindParts();
}
@@ -746,7 +740,6 @@ namespace Emby.Server.Implementations
BaseItem.ProviderManager = Resolve<IProviderManager>();
BaseItem.LocalizationManager = Resolve<ILocalizationManager>();
BaseItem.ItemRepository = Resolve<IItemRepository>();
- User.UserManager = Resolve<IUserManager>();
BaseItem.FileSystem = _fileSystemManager;
BaseItem.UserDataManager = Resolve<IUserDataManager>();
BaseItem.ChannelManager = Resolve<IChannelManager>();
diff --git a/Emby.Server.Implementations/Channels/ChannelManager.cs b/Emby.Server.Implementations/Channels/ChannelManager.cs
index 138832fb8..cb320dcb1 100644
--- a/Emby.Server.Implementations/Channels/ChannelManager.cs
+++ b/Emby.Server.Implementations/Channels/ChannelManager.cs
@@ -46,7 +46,7 @@ namespace Emby.Server.Implementations.Channels
new ConcurrentDictionary<string, Tuple<DateTime, List<MediaSourceInfo>>>();
private readonly SemaphoreSlim _resourcePool = new SemaphoreSlim(1, 1);
-
+
/// <summary>
/// Initializes a new instance of the <see cref="ChannelManager"/> class.
/// </summary>
@@ -791,8 +791,9 @@ namespace Emby.Server.Implementations.Channels
return result;
}
- private async Task<ChannelItemResult> GetChannelItems(IChannel channel,
- User user,
+ private async Task<ChannelItemResult> GetChannelItems(
+ IChannel channel,
+ Jellyfin.Data.Entities.User user,
string externalFolderId,
ChannelItemSortField? sortField,
bool sortDescending,
diff --git a/Emby.Server.Implementations/Collections/CollectionManager.cs b/Emby.Server.Implementations/Collections/CollectionManager.cs
index 7c518d483..61963b633 100644
--- a/Emby.Server.Implementations/Collections/CollectionManager.cs
+++ b/Emby.Server.Implementations/Collections/CollectionManager.cs
@@ -121,7 +121,7 @@ namespace Emby.Server.Implementations.Collections
return EnsureLibraryFolder(GetCollectionsFolderPath(), createIfNeeded);
}
- private IEnumerable<BoxSet> GetCollections(User user)
+ private IEnumerable<BoxSet> GetCollections(Jellyfin.Data.Entities.User user)
{
var folder = GetCollectionsFolder(false).Result;
@@ -325,7 +325,7 @@ namespace Emby.Server.Implementations.Collections
}
/// <inheritdoc />
- public IEnumerable<BaseItem> CollapseItemsWithinBoxSets(IEnumerable<BaseItem> items, User user)
+ public IEnumerable<BaseItem> CollapseItemsWithinBoxSets(IEnumerable<BaseItem> items, Jellyfin.Data.Entities.User user)
{
var results = new Dictionary<Guid, BaseItem>();
diff --git a/Emby.Server.Implementations/Data/SqliteDisplayPreferencesRepository.cs b/Emby.Server.Implementations/Data/SqliteDisplayPreferencesRepository.cs
deleted file mode 100644
index d474f1c6b..000000000
--- a/Emby.Server.Implementations/Data/SqliteDisplayPreferencesRepository.cs
+++ /dev/null
@@ -1,225 +0,0 @@
-#pragma warning disable CS1591
-
-using System;
-using System.Collections.Generic;
-using System.Globalization;
-using System.IO;
-using System.Text.Json;
-using System.Threading;
-using MediaBrowser.Common.Configuration;
-using MediaBrowser.Common.Extensions;
-using MediaBrowser.Common.Json;
-using MediaBrowser.Controller.Persistence;
-using MediaBrowser.Model.Entities;
-using MediaBrowser.Model.IO;
-using Microsoft.Extensions.Logging;
-using SQLitePCL.pretty;
-
-namespace Emby.Server.Implementations.Data
-{
- /// <summary>
- /// Class SQLiteDisplayPreferencesRepository.
- /// </summary>
- public class SqliteDisplayPreferencesRepository : BaseSqliteRepository, IDisplayPreferencesRepository
- {
- private readonly IFileSystem _fileSystem;
-
- private readonly JsonSerializerOptions _jsonOptions;
-
- public SqliteDisplayPreferencesRepository(ILogger<SqliteDisplayPreferencesRepository> logger, IApplicationPaths appPaths, IFileSystem fileSystem)
- : base(logger)
- {
- _fileSystem = fileSystem;
-
- _jsonOptions = JsonDefaults.GetOptions();
-
- DbFilePath = Path.Combine(appPaths.DataPath, "displaypreferences.db");
- }
-
- /// <summary>
- /// Gets the name of the repository.
- /// </summary>
- /// <value>The name.</value>
- public string Name => "SQLite";
-
- public void Initialize()
- {
- try
- {
- InitializeInternal();
- }
- catch (Exception ex)
- {
- Logger.LogError(ex, "Error loading database file. Will reset and retry.");
-
- _fileSystem.DeleteFile(DbFilePath);
-
- InitializeInternal();
- }
- }
-
- /// <summary>
- /// Opens the connection to the database
- /// </summary>
- /// <returns>Task.</returns>
- private void InitializeInternal()
- {
- string[] queries =
- {
- "create table if not exists userdisplaypreferences (id GUID NOT NULL, userId GUID NOT NULL, client text NOT NULL, data BLOB NOT NULL)",
- "create unique index if not exists userdisplaypreferencesindex on userdisplaypreferences (id, userId, client)"
- };
-
- using (var connection = GetConnection())
- {
- connection.RunQueries(queries);
- }
- }
-
- /// <summary>
- /// Save the display preferences associated with an item in the repo
- /// </summary>
- /// <param name="displayPreferences">The display preferences.</param>
- /// <param name="userId">The user id.</param>
- /// <param name="client">The client.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <exception cref="ArgumentNullException">item</exception>
- public void SaveDisplayPreferences(DisplayPreferences displayPreferences, Guid userId, string client, CancellationToken cancellationToken)
- {
- if (displayPreferences == null)
- {
- throw new ArgumentNullException(nameof(displayPreferences));
- }
-
- if (string.IsNullOrEmpty(displayPreferences.Id))
- {
- throw new ArgumentException("Display preferences has an invalid Id", nameof(displayPreferences));
- }
-
- cancellationToken.ThrowIfCancellationRequested();
-
- using (var connection = GetConnection())
- {
- connection.RunInTransaction(
- db => SaveDisplayPreferences(displayPreferences, userId, client, db),
- TransactionMode);
- }
- }
-
- private void SaveDisplayPreferences(DisplayPreferences displayPreferences, Guid userId, string client, IDatabaseConnection connection)
- {
- var serialized = JsonSerializer.SerializeToUtf8Bytes(displayPreferences, _jsonOptions);
-
- using (var statement = connection.PrepareStatement("replace into userdisplaypreferences (id, userid, client, data) values (@id, @userId, @client, @data)"))
- {
- statement.TryBind("@id", new Guid(displayPreferences.Id).ToByteArray());
- statement.TryBind("@userId", userId.ToByteArray());
- statement.TryBind("@client", client);
- statement.TryBind("@data", serialized);
-
- statement.MoveNext();
- }
- }
-
- /// <summary>
- /// Save all display preferences associated with a user in the repo
- /// </summary>
- /// <param name="displayPreferences">The display preferences.</param>
- /// <param name="userId">The user id.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <exception cref="ArgumentNullException">item</exception>
- public void SaveAllDisplayPreferences(IEnumerable<DisplayPreferences> displayPreferences, Guid userId, CancellationToken cancellationToken)
- {
- if (displayPreferences == null)
- {
- throw new ArgumentNullException(nameof(displayPreferences));
- }
-
- cancellationToken.ThrowIfCancellationRequested();
-
- using (var connection = GetConnection())
- {
- connection.RunInTransaction(
- db =>
- {
- foreach (var displayPreference in displayPreferences)
- {
- SaveDisplayPreferences(displayPreference, userId, displayPreference.Client, db);
- }
- },
- TransactionMode);
- }
- }
-
- /// <summary>
- /// Gets the display preferences.
- /// </summary>
- /// <param name="displayPreferencesId">The display preferences id.</param>
- /// <param name="userId">The user id.</param>
- /// <param name="client">The client.</param>
- /// <returns>Task{DisplayPreferences}.</returns>
- /// <exception cref="ArgumentNullException">item</exception>
- public DisplayPreferences GetDisplayPreferences(string displayPreferencesId, Guid userId, string client)
- {
- if (string.IsNullOrEmpty(displayPreferencesId))
- {
- throw new ArgumentNullException(nameof(displayPreferencesId));
- }
-
- var guidId = displayPreferencesId.GetMD5();
-
- using (var connection = GetConnection(true))
- {
- using (var statement = connection.PrepareStatement("select data from userdisplaypreferences where id = @id and userId=@userId and client=@client"))
- {
- statement.TryBind("@id", guidId.ToByteArray());
- statement.TryBind("@userId", userId.ToByteArray());
- statement.TryBind("@client", client);
-
- foreach (var row in statement.ExecuteQuery())
- {
- return Get(row);
- }
- }
- }
-
- return new DisplayPreferences
- {
- Id = guidId.ToString("N", CultureInfo.InvariantCulture)
- };
- }
-
- /// <summary>
- /// Gets all display preferences for the given user.
- /// </summary>
- /// <param name="userId">The user id.</param>
- /// <returns>Task{DisplayPreferences}.</returns>
- /// <exception cref="ArgumentNullException">item</exception>
- public IEnumerable<DisplayPreferences> GetAllDisplayPreferences(Guid userId)
- {
- var list = new List<DisplayPreferences>();
-
- using (var connection = GetConnection(true))
- using (var statement = connection.PrepareStatement("select data from userdisplaypreferences where userId=@userId"))
- {
- statement.TryBind("@userId", userId.ToByteArray());
-
- foreach (var row in statement.ExecuteQuery())
- {
- list.Add(Get(row));
- }
- }
-
- return list;
- }
-
- private DisplayPreferences Get(IReadOnlyList<IResultSetValue> row)
- => JsonSerializer.Deserialize<DisplayPreferences>(row[0].ToBlob(), _jsonOptions);
-
- public void SaveDisplayPreferences(DisplayPreferences displayPreferences, string userId, string client, CancellationToken cancellationToken)
- => SaveDisplayPreferences(displayPreferences, new Guid(userId), client, cancellationToken);
-
- public DisplayPreferences GetDisplayPreferences(string displayPreferencesId, string userId, string client)
- => GetDisplayPreferences(displayPreferencesId, new Guid(userId), client);
- }
-}
diff --git a/Emby.Server.Implementations/Data/SqliteUserDataRepository.cs b/Emby.Server.Implementations/Data/SqliteUserDataRepository.cs
index 6ee6230fc..b99b74ef8 100644
--- a/Emby.Server.Implementations/Data/SqliteUserDataRepository.cs
+++ b/Emby.Server.Implementations/Data/SqliteUserDataRepository.cs
@@ -4,6 +4,7 @@ using System;
using System.Collections.Generic;
using System.IO;
using System.Threading;
+using Jellyfin.Data.Entities;
using MediaBrowser.Common.Configuration;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Library;
diff --git a/Emby.Server.Implementations/Data/SqliteUserRepository.cs b/Emby.Server.Implementations/Data/SqliteUserRepository.cs
deleted file mode 100644
index 0c3f26974..000000000
--- a/Emby.Server.Implementations/Data/SqliteUserRepository.cs
+++ /dev/null
@@ -1,240 +0,0 @@
-#pragma warning disable CS1591
-
-using System;
-using System.Collections.Generic;
-using System.IO;
-using System.Text.Json;
-using MediaBrowser.Common.Json;
-using MediaBrowser.Controller;
-using MediaBrowser.Controller.Entities;
-using MediaBrowser.Controller.Persistence;
-using Microsoft.Extensions.Logging;
-using SQLitePCL.pretty;
-
-namespace Emby.Server.Implementations.Data
-{
- /// <summary>
- /// Class SQLiteUserRepository
- /// </summary>
- public class SqliteUserRepository : BaseSqliteRepository, IUserRepository
- {
- private readonly JsonSerializerOptions _jsonOptions;
-
- public SqliteUserRepository(
- ILogger<SqliteUserRepository> logger,
- IServerApplicationPaths appPaths)
- : base(logger)
- {
- _jsonOptions = JsonDefaults.GetOptions();
-
- DbFilePath = Path.Combine(appPaths.DataPath, "users.db");
- }
-
- /// <summary>
- /// Gets the name of the repository
- /// </summary>
- /// <value>The name.</value>
- public string Name => "SQLite";
-
- /// <summary>
- /// Opens the connection to the database.
- /// </summary>
- public void Initialize()
- {
- using (var connection = GetConnection())
- {
- var localUsersTableExists = TableExists(connection, "LocalUsersv2");
-
- connection.RunQueries(new[] {
- "create table if not exists LocalUsersv2 (Id INTEGER PRIMARY KEY, guid GUID NOT NULL, data BLOB NOT NULL)",
- "drop index if exists idx_users"
- });
-
- if (!localUsersTableExists && TableExists(connection, "Users"))
- {
- TryMigrateToLocalUsersTable(connection);
- }
-
- RemoveEmptyPasswordHashes(connection);
- }
- }
-
- private void TryMigrateToLocalUsersTable(ManagedConnection connection)
- {
- try
- {
- connection.RunQueries(new[]
- {
- "INSERT INTO LocalUsersv2 (guid, data) SELECT guid,data from users"
- });
- }
- catch (Exception ex)
- {
- Logger.LogError(ex, "Error migrating users database");
- }
- }
-
- private void RemoveEmptyPasswordHashes(ManagedConnection connection)
- {
- foreach (var user in RetrieveAllUsers(connection))
- {
- // If the user password is the sha1 hash of the empty string, remove it
- if (!string.Equals(user.Password, "DA39A3EE5E6B4B0D3255BFEF95601890AFD80709", StringComparison.Ordinal)
- && !string.Equals(user.Password, "$SHA1$DA39A3EE5E6B4B0D3255BFEF95601890AFD80709", StringComparison.Ordinal))
- {
- continue;
- }
-
- user.Password = null;
- var serialized = JsonSerializer.SerializeToUtf8Bytes(user, _jsonOptions);
-
- connection.RunInTransaction(db =>
- {
- using (var statement = db.PrepareStatement("update LocalUsersv2 set data=@data where Id=@InternalId"))
- {
- statement.TryBind("@InternalId", user.InternalId);
- statement.TryBind("@data", serialized);
- statement.MoveNext();
- }
- }, TransactionMode);
- }
- }
-
- /// <summary>
- /// Save a user in the repo
- /// </summary>
- public void CreateUser(User user)
- {
- if (user == null)
- {
- throw new ArgumentNullException(nameof(user));
- }
-
- var serialized = JsonSerializer.SerializeToUtf8Bytes(user, _jsonOptions);
-
- using (var connection = GetConnection())
- {
- connection.RunInTransaction(db =>
- {
- using (var statement = db.PrepareStatement("insert into LocalUsersv2 (guid, data) values (@guid, @data)"))
- {
- statement.TryBind("@guid", user.Id.ToByteArray());
- statement.TryBind("@data", serialized);
-
- statement.MoveNext();
- }
-
- var createdUser = GetUser(user.Id, connection);
-
- if (createdUser == null)
- {
- throw new ApplicationException("created user should never be null");
- }
-
- user.InternalId = createdUser.InternalId;
-
- }, TransactionMode);
- }
- }
-
- public void UpdateUser(User user)
- {
- if (user == null)
- {
- throw new ArgumentNullException(nameof(user));
- }
-
- var serialized = JsonSerializer.SerializeToUtf8Bytes(user, _jsonOptions);
-
- using (var connection = GetConnection())
- {
- connection.RunInTransaction(db =>
- {
- using (var statement = db.PrepareStatement("update LocalUsersv2 set data=@data where Id=@InternalId"))
- {
- statement.TryBind("@InternalId", user.InternalId);
- statement.TryBind("@data", serialized);
- statement.MoveNext();
- }
-
- }, TransactionMode);
- }
- }
-
- private User GetUser(Guid guid, ManagedConnection connection)
- {
- using (var statement = connection.PrepareStatement("select id,guid,data from LocalUsersv2 where guid=@guid"))
- {
- statement.TryBind("@guid", guid);
-
- foreach (var row in statement.ExecuteQuery())
- {
- return GetUser(row);
- }
- }
-
- return null;
- }
-
- private User GetUser(IReadOnlyList<IResultSetValue> row)
- {
- var id = row[0].ToInt64();
- var guid = row[1].ReadGuidFromBlob();
-
- var user = JsonSerializer.Deserialize<User>(row[2].ToBlob(), _jsonOptions);
- user.InternalId = id;
- user.Id = guid;
- return user;
- }
-
- /// <summary>
- /// Retrieve all users from the database
- /// </summary>
- /// <returns>IEnumerable{User}.</returns>
- public List<User> RetrieveAllUsers()
- {
- using (var connection = GetConnection(true))
- {
- return new List<User>(RetrieveAllUsers(connection));
- }
- }
-
- /// <summary>
- /// Retrieve all users from the database
- /// </summary>
- /// <returns>IEnumerable{User}.</returns>
- private IEnumerable<User> RetrieveAllUsers(ManagedConnection connection)
- {
- foreach (var row in connection.Query("select id,guid,data from LocalUsersv2"))
- {
- yield return GetUser(row);
- }
- }
-
- /// <summary>
- /// Deletes the user.
- /// </summary>
- /// <param name="user">The user.</param>
- /// <returns>Task.</returns>
- /// <exception cref="ArgumentNullException">user</exception>
- public void DeleteUser(User user)
- {
- if (user == null)
- {
- throw new ArgumentNullException(nameof(user));
- }
-
- using (var connection = GetConnection())
- {
- connection.RunInTransaction(db =>
- {
- using (var statement = db.PrepareStatement("delete from LocalUsersv2 where Id=@id"))
- {
- statement.TryBind("@id", user.InternalId);
- statement.MoveNext();
- }
- }, TransactionMode);
- }
- }
- }
-}
diff --git a/Emby.Server.Implementations/Devices/DeviceManager.cs b/Emby.Server.Implementations/Devices/DeviceManager.cs
index 2283f2433..2d44a7c28 100644
--- a/Emby.Server.Implementations/Devices/DeviceManager.cs
+++ b/Emby.Server.Implementations/Devices/DeviceManager.cs
@@ -5,6 +5,9 @@ using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
+using System.Threading.Tasks;
+using Jellyfin.Data.Enums;
+using Jellyfin.Data.Entities;
using MediaBrowser.Common.Extensions;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Devices;
@@ -181,7 +184,13 @@ namespace Emby.Server.Implementations.Devices
throw new ArgumentNullException(nameof(deviceId));
}
- if (!CanAccessDevice(user.Policy, deviceId))
+ if (user.HasPermission(PermissionKind.EnableAllDevices)
+ || user.HasPermission(PermissionKind.IsAdministrator))
+ {
+ return true;
+ }
+
+ if (!user.GetPreference(PreferenceKind.EnabledDevices).Contains(deviceId, StringComparer.OrdinalIgnoreCase))
{
var capabilities = GetCapabilities(deviceId);
@@ -193,20 +202,5 @@ namespace Emby.Server.Implementations.Devices
return true;
}
-
- private static bool CanAccessDevice(UserPolicy policy, string id)
- {
- if (policy.EnableAllDevices)
- {
- return true;
- }
-
- if (policy.IsAdministrator)
- {
- return true;
- }
-
- return policy.EnabledDevices.Contains(id, StringComparer.OrdinalIgnoreCase);
- }
}
}
diff --git a/Emby.Server.Implementations/Dto/DtoService.cs b/Emby.Server.Implementations/Dto/DtoService.cs
index c4b65d265..da40bca4c 100644
--- a/Emby.Server.Implementations/Dto/DtoService.cs
+++ b/Emby.Server.Implementations/Dto/DtoService.cs
@@ -6,6 +6,7 @@ using System.Globalization;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
+using Jellyfin.Data.Enums;
using MediaBrowser.Common;
using MediaBrowser.Controller.Channels;
using MediaBrowser.Controller.Drawing;
@@ -74,7 +75,7 @@ namespace Emby.Server.Implementations.Dto
/// <param name="owner">The owner.</param>
/// <returns>Task{DtoBaseItem}.</returns>
/// <exception cref="ArgumentNullException">item</exception>
- public BaseItemDto GetBaseItemDto(BaseItem item, ItemFields[] fields, User user = null, BaseItem owner = null)
+ public BaseItemDto GetBaseItemDto(BaseItem item, ItemFields[] fields, Jellyfin.Data.Entities.User user = null, BaseItem owner = null)
{
var options = new DtoOptions
{
@@ -85,7 +86,7 @@ namespace Emby.Server.Implementations.Dto
}
/// <inheritdoc />
- public IReadOnlyList<BaseItemDto> GetBaseItemDtos(IReadOnlyList<BaseItem> items, DtoOptions options, User user = null, BaseItem owner = null)
+ public IReadOnlyList<BaseItemDto> GetBaseItemDtos(IReadOnlyList<BaseItem> items, DtoOptions options, Jellyfin.Data.Entities.User user = null, BaseItem owner = null)
{
var returnItems = new BaseItemDto[items.Count];
var programTuples = new List<(BaseItem, BaseItemDto)>();
@@ -138,7 +139,7 @@ namespace Emby.Server.Implementations.Dto
return returnItems;
}
- public BaseItemDto GetBaseItemDto(BaseItem item, DtoOptions options, User user = null, BaseItem owner = null)
+ public BaseItemDto GetBaseItemDto(BaseItem item, DtoOptions options, Jellyfin.Data.Entities.User user = null, BaseItem owner = null)
{
var dto = GetBaseItemDtoInternal(item, options, user, owner);
if (item is LiveTvChannel tvChannel)
@@ -172,7 +173,7 @@ namespace Emby.Server.Implementations.Dto
return dto;
}
- private static IList<BaseItem> GetTaggedItems(IItemByName byName, User user, DtoOptions options)
+ private static IList<BaseItem> GetTaggedItems(IItemByName byName, Jellyfin.Data.Entities.User user, DtoOptions options)
{
return byName.GetTaggedItems(
new InternalItemsQuery(user)
@@ -182,7 +183,7 @@ namespace Emby.Server.Implementations.Dto
});
}
- private BaseItemDto GetBaseItemDtoInternal(BaseItem item, DtoOptions options, User user = null, BaseItem owner = null)
+ private BaseItemDto GetBaseItemDtoInternal(BaseItem item, DtoOptions options, Jellyfin.Data.Entities.User user = null, BaseItem owner = null)
{
var dto = new BaseItemDto
{
@@ -315,7 +316,7 @@ namespace Emby.Server.Implementations.Dto
}
}
- public BaseItemDto GetItemByNameDto(BaseItem item, DtoOptions options, List<BaseItem> taggedItems, User user = null)
+ public BaseItemDto GetItemByNameDto(BaseItem item, DtoOptions options, List<BaseItem> taggedItems, Jellyfin.Data.Entities.User user = null)
{
var dto = GetBaseItemDtoInternal(item, options, user);
@@ -327,7 +328,7 @@ namespace Emby.Server.Implementations.Dto
return dto;
}
- private static void SetItemByNameInfo(BaseItem item, BaseItemDto dto, IList<BaseItem> taggedItems, User user = null)
+ private static void SetItemByNameInfo(BaseItem item, BaseItemDto dto, IList<BaseItem> taggedItems, Jellyfin.Data.Entities.User user = null)
{
if (item is MusicArtist)
{
@@ -363,7 +364,7 @@ namespace Emby.Server.Implementations.Dto
/// <summary>
/// Attaches the user specific info.
/// </summary>
- private void AttachUserSpecificInfo(BaseItemDto dto, BaseItem item, User user, DtoOptions options)
+ private void AttachUserSpecificInfo(BaseItemDto dto, BaseItem item, Jellyfin.Data.Entities.User user, DtoOptions options)
{
if (item.IsFolder)
{
@@ -384,7 +385,7 @@ namespace Emby.Server.Implementations.Dto
if (options.ContainsField(ItemFields.ChildCount))
{
- dto.ChildCount = dto.ChildCount ?? GetChildCount(folder, user);
+ dto.ChildCount ??= GetChildCount(folder, user);
}
}
@@ -414,7 +415,7 @@ namespace Emby.Server.Implementations.Dto
if (options.ContainsField(ItemFields.BasicSyncInfo))
{
- var userCanSync = user != null && user.Policy.EnableContentDownloading;
+ var userCanSync = user != null && user.HasPermission(PermissionKind.EnableContentDownloading);
if (userCanSync && item.SupportsExternalTransfer)
{
dto.SupportsSync = true;
@@ -422,7 +423,7 @@ namespace Emby.Server.Implementations.Dto
}
}
- private static int GetChildCount(Folder folder, User user)
+ private static int GetChildCount(Folder folder, Jellyfin.Data.Entities.User user)
{
// Right now this is too slow to calculate for top level folders on a per-user basis
// Just return something so that apps that are expecting a value won't think the folders are empty
diff --git a/Emby.Server.Implementations/EntryPoints/LibraryChangedNotifier.cs b/Emby.Server.Implementations/EntryPoints/LibraryChangedNotifier.cs
index 8e3236407..7ece52cad 100644
--- a/Emby.Server.Implementations/EntryPoints/LibraryChangedNotifier.cs
+++ b/Emby.Server.Implementations/EntryPoints/LibraryChangedNotifier.cs
@@ -446,7 +446,7 @@ namespace Emby.Server.Implementations.EntryPoints
/// <param name="user">The user.</param>
/// <param name="includeIfNotFound">if set to <c>true</c> [include if not found].</param>
/// <returns>IEnumerable{``0}.</returns>
- private IEnumerable<T> TranslatePhysicalItemToUserLibrary<T>(T item, User user, bool includeIfNotFound = false)
+ private IEnumerable<T> TranslatePhysicalItemToUserLibrary<T>(T item, Jellyfin.Data.Entities.User user, bool includeIfNotFound = false)
where T : BaseItem
{
// If the physical root changed, return the user root
diff --git a/Emby.Server.Implementations/EntryPoints/RecordingNotifier.cs b/Emby.Server.Implementations/EntryPoints/RecordingNotifier.cs
index 41c0c5115..75dde5598 100644
--- a/Emby.Server.Implementations/EntryPoints/RecordingNotifier.cs
+++ b/Emby.Server.Implementations/EntryPoints/RecordingNotifier.cs
@@ -4,6 +4,7 @@ using System;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
+using Jellyfin.Data.Enums;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.LiveTv;
using MediaBrowser.Controller.Plugins;
@@ -64,7 +65,7 @@ namespace Emby.Server.Implementations.EntryPoints
private async void SendMessage(string name, TimerEventInfo info)
{
- var users = _userManager.Users.Where(i => i.Policy.EnableLiveTvAccess).Select(i => i.Id).ToList();
+ var users = _userManager.Users.Where(i => i.HasPermission(PermissionKind.EnableLiveTvAccess)).Select(i => i.Id).ToList();
try
{
diff --git a/Emby.Server.Implementations/EntryPoints/RefreshUsersMetadata.cs b/Emby.Server.Implementations/EntryPoints/RefreshUsersMetadata.cs
deleted file mode 100644
index 54f4b67e6..000000000
--- a/Emby.Server.Implementations/EntryPoints/RefreshUsersMetadata.cs
+++ /dev/null
@@ -1,77 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Threading;
-using System.Threading.Tasks;
-using MediaBrowser.Controller.Library;
-using MediaBrowser.Controller.Providers;
-using MediaBrowser.Model.IO;
-using MediaBrowser.Model.Tasks;
-
-namespace Emby.Server.Implementations.EntryPoints
-{
- /// <summary>
- /// Class RefreshUsersMetadata.
- /// </summary>
- public class RefreshUsersMetadata : IScheduledTask, IConfigurableScheduledTask
- {
- /// <summary>
- /// The user manager.
- /// </summary>
- private readonly IUserManager _userManager;
- private readonly IFileSystem _fileSystem;
-
- /// <summary>
- /// Initializes a new instance of the <see cref="RefreshUsersMetadata" /> class.
- /// </summary>
- public RefreshUsersMetadata(IUserManager userManager, IFileSystem fileSystem)
- {
- _userManager = userManager;
- _fileSystem = fileSystem;
- }
-
- /// <inheritdoc />
- public string Name => "Refresh Users";
-
- /// <inheritdoc />
- public string Key => "RefreshUsers";
-
- /// <inheritdoc />
- public string Description => "Refresh user infos";
-
- /// <inheritdoc />
- public string Category => "Library";
-
- /// <inheritdoc />
- public bool IsHidden => true;
-
- /// <inheritdoc />
- public bool IsEnabled => true;
-
- /// <inheritdoc />
- public bool IsLogged => true;
-
- /// <inheritdoc />
- public async Task Execute(CancellationToken cancellationToken, IProgress<double> progress)
- {
- foreach (var user in _userManager.Users)
- {
- cancellationToken.ThrowIfCancellationRequested();
-
- await user.RefreshMetadata(new MetadataRefreshOptions(new DirectoryService(_fileSystem)), cancellationToken).ConfigureAwait(false);
- }
- }
-
- /// <inheritdoc />
- public IEnumerable<TaskTriggerInfo> GetDefaultTriggers()
- {
- return new[]
- {
- new TaskTriggerInfo
- {
- IntervalTicks = TimeSpan.FromDays(1).Ticks,
- Type = TaskTriggerInfo.TriggerInterval
- }
- };
- }
- }
-}
diff --git a/Emby.Server.Implementations/EntryPoints/ServerEventNotifier.cs b/Emby.Server.Implementations/EntryPoints/ServerEventNotifier.cs
index e1dbb663b..436d723f0 100644
--- a/Emby.Server.Implementations/EntryPoints/ServerEventNotifier.cs
+++ b/Emby.Server.Implementations/EntryPoints/ServerEventNotifier.cs
@@ -3,10 +3,10 @@ using System.Collections.Generic;
using System.Globalization;
using System.Threading;
using System.Threading.Tasks;
+using Jellyfin.Data.Entities;
using MediaBrowser.Common.Plugins;
using MediaBrowser.Common.Updates;
using MediaBrowser.Controller;
-using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Plugins;
using MediaBrowser.Controller.Session;
@@ -67,10 +67,8 @@ namespace Emby.Server.Implementations.EntryPoints
/// <inheritdoc />
public Task RunAsync()
{
- _userManager.UserDeleted += OnUserDeleted;
- _userManager.UserUpdated += OnUserUpdated;
- _userManager.UserPolicyUpdated += OnUserPolicyUpdated;
- _userManager.UserConfigurationUpdated += OnUserConfigurationUpdated;
+ _userManager.OnUserDeleted += OnUserDeleted;
+ _userManager.OnUserUpdated += OnUserUpdated;
_appHost.HasPendingRestartChanged += OnHasPendingRestartChanged;
@@ -152,20 +150,6 @@ namespace Emby.Server.Implementations.EntryPoints
SendMessageToUserSession(e.Argument, "UserDeleted", e.Argument.Id.ToString("N", CultureInfo.InvariantCulture));
}
- private void OnUserPolicyUpdated(object sender, GenericEventArgs<User> e)
- {
- var dto = _userManager.GetUserDto(e.Argument);
-
- SendMessageToUserSession(e.Argument, "UserPolicyUpdated", dto);
- }
-
- private void OnUserConfigurationUpdated(object sender, GenericEventArgs<User> e)
- {
- var dto = _userManager.GetUserDto(e.Argument);
-
- SendMessageToUserSession(e.Argument, "UserConfigurationUpdated", dto);
- }
-
private async void SendMessageToAdminSessions<T>(string name, T data)
{
try
@@ -209,10 +193,9 @@ namespace Emby.Server.Implementations.EntryPoints
{
if (dispose)
{
- _userManager.UserDeleted -= OnUserDeleted;
- _userManager.UserUpdated -= OnUserUpdated;
- _userManager.UserPolicyUpdated -= OnUserPolicyUpdated;
- _userManager.UserConfigurationUpdated -= OnUserConfigurationUpdated;
+ _userManager.OnUserDeleted -= OnUserDeleted;
+ _userManager.OnUserUpdated -= OnUserUpdated;
+
_installationManager.PluginUninstalled -= OnPluginUninstalled;
_installationManager.PackageInstalling -= OnPackageInstalling;
diff --git a/Emby.Server.Implementations/HttpServer/Security/AuthService.cs b/Emby.Server.Implementations/HttpServer/Security/AuthService.cs
index 256b24924..ad7b76d4f 100644
--- a/Emby.Server.Implementations/HttpServer/Security/AuthService.cs
+++ b/Emby.Server.Implementations/HttpServer/Security/AuthService.cs
@@ -4,6 +4,7 @@ using System;
using System.Linq;
using System.Security.Authentication;
using Emby.Server.Implementations.SocketSharp;
+using Jellyfin.Data.Enums;
using MediaBrowser.Common.Net;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Entities;
@@ -43,14 +44,14 @@ namespace Emby.Server.Implementations.HttpServer.Security
ValidateUser(request, authAttribtues);
}
- public User Authenticate(HttpRequest request, IAuthenticationAttributes authAttributes)
+ public Jellyfin.Data.Entities.User Authenticate(HttpRequest request, IAuthenticationAttributes authAttributes)
{
var req = new WebSocketSharpRequest(request, null, request.Path, _logger);
var user = ValidateUser(req, authAttributes);
return user;
}
- private User ValidateUser(IRequest request, IAuthenticationAttributes authAttribtues)
+ private Jellyfin.Data.Entities.User ValidateUser(IRequest request, IAuthenticationAttributes authAttribtues)
{
// This code is executed before the service
var auth = _authorizationContext.GetAuthorizationInfo(request);
@@ -90,7 +91,8 @@ namespace Emby.Server.Implementations.HttpServer.Security
!string.IsNullOrEmpty(auth.Client) &&
!string.IsNullOrEmpty(auth.Device))
{
- _sessionManager.LogSessionActivity(auth.Client,
+ _sessionManager.LogSessionActivity(
+ auth.Client,
auth.Version,
auth.DeviceId,
auth.Device,
@@ -102,22 +104,22 @@ namespace Emby.Server.Implementations.HttpServer.Security
}
private void ValidateUserAccess(
- User user,
+ Jellyfin.Data.Entities.User user,
IRequest request,
IAuthenticationAttributes authAttribtues,
AuthorizationInfo auth)
{
- if (user.Policy.IsDisabled)
+ if (user.HasPermission(PermissionKind.IsDisabled))
{
throw new SecurityException("User account has been disabled.");
}
- if (!user.Policy.EnableRemoteAccess && !_networkManager.IsInLocalNetwork(request.RemoteIp))
+ if (!user.HasPermission(PermissionKind.EnableRemoteAccess) && !_networkManager.IsInLocalNetwork(request.RemoteIp))
{
throw new SecurityException("User account has been disabled.");
}
- if (!user.Policy.IsAdministrator
+ if (!user.HasPermission(PermissionKind.IsAdministrator)
&& !authAttribtues.EscapeParentalControl
&& !user.IsParentalScheduleAllowed())
{
@@ -176,11 +178,11 @@ namespace Emby.Server.Implementations.HttpServer.Security
return false;
}
- private static void ValidateRoles(string[] roles, User user)
+ private static void ValidateRoles(string[] roles, Jellyfin.Data.Entities.User user)
{
if (roles.Contains("admin", StringComparer.OrdinalIgnoreCase))
{
- if (user == null || !user.Policy.IsAdministrator)
+ if (user == null || !user.HasPermission(PermissionKind.IsAdministrator))
{
throw new SecurityException("User does not have admin access.");
}
@@ -188,7 +190,7 @@ namespace Emby.Server.Implementations.HttpServer.Security
if (roles.Contains("delete", StringComparer.OrdinalIgnoreCase))
{
- if (user == null || !user.Policy.EnableContentDeletion)
+ if (user == null || !user.HasPermission(PermissionKind.EnableContentDeletion))
{
throw new SecurityException("User does not have delete access.");
}
@@ -196,7 +198,7 @@ namespace Emby.Server.Implementations.HttpServer.Security
if (roles.Contains("download", StringComparer.OrdinalIgnoreCase))
{
- if (user == null || !user.Policy.EnableContentDownloading)
+ if (user == null || !user.HasPermission(PermissionKind.EnableContentDownloading))
{
throw new SecurityException("User does not have download access.");
}
diff --git a/Emby.Server.Implementations/HttpServer/Security/AuthorizationContext.cs b/Emby.Server.Implementations/HttpServer/Security/AuthorizationContext.cs
index 129faeaab..9558cb4c6 100644
--- a/Emby.Server.Implementations/HttpServer/Security/AuthorizationContext.cs
+++ b/Emby.Server.Implementations/HttpServer/Security/AuthorizationContext.cs
@@ -149,9 +149,9 @@ namespace Emby.Server.Implementations.HttpServer.Security
{
info.User = _userManager.GetUserById(tokenInfo.UserId);
- if (info.User != null && !string.Equals(info.User.Name, tokenInfo.UserName, StringComparison.OrdinalIgnoreCase))
+ if (info.User != null && !string.Equals(info.User.Username, tokenInfo.UserName, StringComparison.OrdinalIgnoreCase))
{
- tokenInfo.UserName = info.User.Name;
+ tokenInfo.UserName = info.User.Username;
updateToken = true;
}
}
diff --git a/Emby.Server.Implementations/HttpServer/Security/SessionContext.cs b/Emby.Server.Implementations/HttpServer/Security/SessionContext.cs
index 166952c64..3f8a64f99 100644
--- a/Emby.Server.Implementations/HttpServer/Security/SessionContext.cs
+++ b/Emby.Server.Implementations/HttpServer/Security/SessionContext.cs
@@ -42,14 +42,14 @@ namespace Emby.Server.Implementations.HttpServer.Security
return GetSession((IRequest)requestContext);
}
- public User GetUser(IRequest requestContext)
+ public Jellyfin.Data.Entities.User GetUser(IRequest requestContext)
{
var session = GetSession(requestContext);
return session == null || session.UserId.Equals(Guid.Empty) ? null : _userManager.GetUserById(session.UserId);
}
- public User GetUser(object requestContext)
+ public Jellyfin.Data.Entities.User GetUser(object requestContext)
{
return GetUser((IRequest)requestContext);
}
diff --git a/Emby.Server.Implementations/Library/LibraryManager.cs b/Emby.Server.Implementations/Library/LibraryManager.cs
index 0b86b2db7..e7cd7512c 100644
--- a/Emby.Server.Implementations/Library/LibraryManager.cs
+++ b/Emby.Server.Implementations/Library/LibraryManager.cs
@@ -17,6 +17,7 @@ using Emby.Server.Implementations.Library.Resolvers;
using Emby.Server.Implementations.Library.Validators;
using Emby.Server.Implementations.Playlists;
using Emby.Server.Implementations.ScheduledTasks;
+using Jellyfin.Data.Enums;
using MediaBrowser.Common.Extensions;
using MediaBrowser.Common.Progress;
using MediaBrowser.Controller;
@@ -1470,7 +1471,7 @@ namespace Emby.Server.Implementations.Library
query.Parent = null;
}
- private void AddUserToQuery(InternalItemsQuery query, User user, bool allowExternalContent = true)
+ private void AddUserToQuery(InternalItemsQuery query, Jellyfin.Data.Entities.User user, bool allowExternalContent = true)
{
if (query.AncestorIds.Length == 0 &&
query.ParentId.Equals(Guid.Empty) &&
@@ -1491,7 +1492,7 @@ namespace Emby.Server.Implementations.Library
}
}
- private IEnumerable<Guid> GetTopParentIdsForQuery(BaseItem item, User user)
+ private IEnumerable<Guid> GetTopParentIdsForQuery(BaseItem item, Jellyfin.Data.Entities.User user)
{
if (item is UserView view)
{
@@ -1524,7 +1525,8 @@ namespace Emby.Server.Implementations.Library
}
// Handle grouping
- if (user != null && !string.IsNullOrEmpty(view.ViewType) && UserView.IsEligibleForGrouping(view.ViewType) && user.Configuration.GroupedFolders.Length > 0)
+ if (user != null && !string.IsNullOrEmpty(view.ViewType) && UserView.IsEligibleForGrouping(view.ViewType)
+ && user.GetPreference(PreferenceKind.GroupedFolders).Length > 0)
{
return GetUserRootFolder()
.GetChildren(user, true)
@@ -1557,7 +1559,7 @@ namespace Emby.Server.Implementations.Library
/// <param name="item">The item.</param>
/// <param name="user">The user.</param>
/// <returns>IEnumerable{System.String}.</returns>
- public async Task<IEnumerable<Video>> GetIntros(BaseItem item, User user)
+ public async Task<IEnumerable<Video>> GetIntros(BaseItem item, Jellyfin.Data.Entities.User user)
{
var tasks = IntroProviders
.OrderBy(i => i.GetType().Name.Contains("Default", StringComparison.OrdinalIgnoreCase) ? 1 : 0)
@@ -1579,7 +1581,7 @@ namespace Emby.Server.Implementations.Library
/// <param name="item">The item.</param>
/// <param name="user">The user.</param>
/// <returns>Task&lt;IEnumerable&lt;IntroInfo&gt;&gt;.</returns>
- private async Task<IEnumerable<IntroInfo>> GetIntros(IIntroProvider provider, BaseItem item, User user)
+ private async Task<IEnumerable<IntroInfo>> GetIntros(IIntroProvider provider, BaseItem item, Jellyfin.Data.Entities.User user)
{
try
{
@@ -1680,7 +1682,7 @@ namespace Emby.Server.Implementations.Library
/// <param name="sortBy">The sort by.</param>
/// <param name="sortOrder">The sort order.</param>
/// <returns>IEnumerable{BaseItem}.</returns>
- public IEnumerable<BaseItem> Sort(IEnumerable<BaseItem> items, User user, IEnumerable<string> sortBy, SortOrder sortOrder)
+ public IEnumerable<BaseItem> Sort(IEnumerable<BaseItem> items, Jellyfin.Data.Entities.User user, IEnumerable<string> sortBy, SortOrder sortOrder)
{
var isFirst = true;
@@ -1703,7 +1705,7 @@ namespace Emby.Server.Implementations.Library
return orderedItems ?? items;
}
- public IEnumerable<BaseItem> Sort(IEnumerable<BaseItem> items, User user, IEnumerable<ValueTuple<string, SortOrder>> orderByList)
+ public IEnumerable<BaseItem> Sort(IEnumerable<BaseItem> items, Jellyfin.Data.Entities.User user, IEnumerable<ValueTuple<string, SortOrder>> orderByList)
{
var isFirst = true;
@@ -1740,7 +1742,7 @@ namespace Emby.Server.Implementations.Library
/// <param name="name">The name.</param>
/// <param name="user">The user.</param>
/// <returns>IBaseItemComparer.</returns>
- private IBaseItemComparer GetComparer(string name, User user)
+ private IBaseItemComparer GetComparer(string name, Jellyfin.Data.Entities.User user)
{
var comparer = Comparers.FirstOrDefault(c => string.Equals(name, c.Name, StringComparison.OrdinalIgnoreCase));
@@ -2072,7 +2074,7 @@ namespace Emby.Server.Implementations.Library
private readonly TimeSpan _viewRefreshInterval = TimeSpan.FromHours(24);
public UserView GetNamedView(
- User user,
+ Jellyfin.Data.Entities.User user,
string name,
string viewType,
string sortName)
@@ -2125,7 +2127,7 @@ namespace Emby.Server.Implementations.Library
}
public UserView GetNamedView(
- User user,
+ Jellyfin.Data.Entities.User user,
string name,
Guid parentId,
string viewType,
diff --git a/Emby.Server.Implementations/Library/MediaSourceManager.cs b/Emby.Server.Implementations/Library/MediaSourceManager.cs
index 01fe98f3a..25af69058 100644
--- a/Emby.Server.Implementations/Library/MediaSourceManager.cs
+++ b/Emby.Server.Implementations/Library/MediaSourceManager.cs
@@ -7,6 +7,7 @@ using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
+using Jellyfin.Data.Enums;
using MediaBrowser.Common.Configuration;
using MediaBrowser.Common.Extensions;
using MediaBrowser.Controller.Entities;
@@ -14,7 +15,6 @@ using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.MediaEncoding;
using MediaBrowser.Controller.Persistence;
using MediaBrowser.Controller.Providers;
-using MediaBrowser.Model.Configuration;
using MediaBrowser.Model.Dlna;
using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Entities;
@@ -145,7 +145,7 @@ namespace Emby.Server.Implementations.Library
});
}
- public async Task<List<MediaSourceInfo>> GetPlaybackMediaSources(BaseItem item, User user, bool allowMediaProbe, bool enablePathSubstitution, CancellationToken cancellationToken)
+ public async Task<List<MediaSourceInfo>> GetPlaybackMediaSources(BaseItem item, Jellyfin.Data.Entities.User user, bool allowMediaProbe, bool enablePathSubstitution, CancellationToken cancellationToken)
{
var mediaSources = GetStaticMediaSources(item, enablePathSubstitution, user);
@@ -190,10 +190,7 @@ namespace Emby.Server.Implementations.Library
{
if (string.Equals(item.MediaType, MediaType.Audio, StringComparison.OrdinalIgnoreCase))
{
- if (!user.Policy.EnableAudioPlaybackTranscoding)
- {
- source.SupportsTranscoding = false;
- }
+ source.SupportsTranscoding = user.HasPermission(PermissionKind.EnableAudioPlaybackTranscoding);
}
}
}
@@ -312,7 +309,7 @@ namespace Emby.Server.Implementations.Library
return sources.FirstOrDefault(i => string.Equals(i.Id, mediaSourceId, StringComparison.OrdinalIgnoreCase));
}
- public List<MediaSourceInfo> GetStaticMediaSources(BaseItem item, bool enablePathSubstitution, User user = null)
+ public List<MediaSourceInfo> GetStaticMediaSources(BaseItem item, bool enablePathSubstitution, Jellyfin.Data.Entities.User user = null)
{
if (item == null)
{
@@ -350,9 +347,11 @@ namespace Emby.Server.Implementations.Library
return new string[] { language };
}
- private void SetDefaultSubtitleStreamIndex(MediaSourceInfo source, UserItemData userData, User user, bool allowRememberingSelection)
+ private void SetDefaultSubtitleStreamIndex(MediaSourceInfo source, UserItemData userData, Jellyfin.Data.Entities.User user, bool allowRememberingSelection)
{
- if (userData.SubtitleStreamIndex.HasValue && user.Configuration.RememberSubtitleSelections && user.Configuration.SubtitleMode != SubtitlePlaybackMode.None && allowRememberingSelection)
+ if (userData.SubtitleStreamIndex.HasValue
+ && user.RememberSubtitleSelections
+ && user.SubtitleMode != SubtitlePlaybackMode.None && allowRememberingSelection)
{
var index = userData.SubtitleStreamIndex.Value;
// Make sure the saved index is still valid
@@ -363,26 +362,27 @@ namespace Emby.Server.Implementations.Library
}
}
- var preferredSubs = string.IsNullOrEmpty(user.Configuration.SubtitleLanguagePreference)
- ? Array.Empty<string>() : NormalizeLanguage(user.Configuration.SubtitleLanguagePreference);
+
+ var preferredSubs = string.IsNullOrEmpty(user.SubtitleLanguagePreference)
+ ? Array.Empty<string>() : NormalizeLanguage(user.SubtitleLanguagePreference);
var defaultAudioIndex = source.DefaultAudioStreamIndex;
var audioLangage = defaultAudioIndex == null
? null
: source.MediaStreams.Where(i => i.Type == MediaStreamType.Audio && i.Index == defaultAudioIndex).Select(i => i.Language).FirstOrDefault();
- source.DefaultSubtitleStreamIndex = MediaStreamSelector.GetDefaultSubtitleStreamIndex(source.MediaStreams,
+ source.DefaultSubtitleStreamIndex = MediaStreamSelector.GetDefaultSubtitleStreamIndex(
+ source.MediaStreams,
preferredSubs,
- user.Configuration.SubtitleMode,
+ user.SubtitleMode,
audioLangage);
- MediaStreamSelector.SetSubtitleStreamScores(source.MediaStreams, preferredSubs,
- user.Configuration.SubtitleMode, audioLangage);
+ MediaStreamSelector.SetSubtitleStreamScores(source.MediaStreams, preferredSubs, user.SubtitleMode, audioLangage);
}
- private void SetDefaultAudioStreamIndex(MediaSourceInfo source, UserItemData userData, User user, bool allowRememberingSelection)
+ private void SetDefaultAudioStreamIndex(MediaSourceInfo source, UserItemData userData, Jellyfin.Data.Entities.User user, bool allowRememberingSelection)
{
- if (userData.AudioStreamIndex.HasValue && user.Configuration.RememberAudioSelections && allowRememberingSelection)
+ if (userData.AudioStreamIndex.HasValue && user.RememberAudioSelections && allowRememberingSelection)
{
var index = userData.AudioStreamIndex.Value;
// Make sure the saved index is still valid
@@ -393,14 +393,14 @@ namespace Emby.Server.Implementations.Library
}
}
- var preferredAudio = string.IsNullOrEmpty(user.Configuration.AudioLanguagePreference)
+ var preferredAudio = string.IsNullOrEmpty(user.AudioLanguagePreference)
? Array.Empty<string>()
- : NormalizeLanguage(user.Configuration.AudioLanguagePreference);
+ : NormalizeLanguage(user.AudioLanguagePreference);
- source.DefaultAudioStreamIndex = MediaStreamSelector.GetDefaultAudioStreamIndex(source.MediaStreams, preferredAudio, user.Configuration.PlayDefaultAudioTrack);
+ source.DefaultAudioStreamIndex = MediaStreamSelector.GetDefaultAudioStreamIndex(source.MediaStreams, preferredAudio, user.PlayDefaultAudioTrack);
}
- public void SetDefaultAudioAndSubtitleStreamIndexes(BaseItem item, MediaSourceInfo source, User user)
+ public void SetDefaultAudioAndSubtitleStreamIndexes(BaseItem item, MediaSourceInfo source, Jellyfin.Data.Entities.User user)
{
// Item would only be null if the app didn't supply ItemId as part of the live stream open request
var mediaType = item == null ? MediaType.Video : item.MediaType;
@@ -560,17 +560,14 @@ namespace Emby.Server.Implementations.Library
{
videoStream.BitRate = 30000000;
}
-
else if (width >= 1900)
{
videoStream.BitRate = 20000000;
}
-
else if (width >= 1200)
{
videoStream.BitRate = 8000000;
}
-
else if (width >= 700)
{
videoStream.BitRate = 2000000;
@@ -674,13 +671,14 @@ namespace Emby.Server.Implementations.Library
mediaSource.AnalyzeDurationMs = 3000;
}
- mediaInfo = await _mediaEncoder.GetMediaInfo(new MediaInfoRequest
+ mediaInfo = await _mediaEncoder.GetMediaInfo(
+ new MediaInfoRequest
{
MediaSource = mediaSource,
MediaType = isAudio ? DlnaProfileType.Audio : DlnaProfileType.Video,
ExtractChapters = false
-
- }, cancellationToken).ConfigureAwait(false);
+ },
+ cancellationToken).ConfigureAwait(false);
if (cacheFilePath != null)
{
diff --git a/Emby.Server.Implementations/Library/MediaStreamSelector.cs b/Emby.Server.Implementations/Library/MediaStreamSelector.cs
index e27145a1d..a177138b7 100644
--- a/Emby.Server.Implementations/Library/MediaStreamSelector.cs
+++ b/Emby.Server.Implementations/Library/MediaStreamSelector.cs
@@ -3,6 +3,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
+using Jellyfin.Data.Enums;
using MediaBrowser.Model.Configuration;
using MediaBrowser.Model.Entities;
diff --git a/Emby.Server.Implementations/Library/MusicManager.cs b/Emby.Server.Implementations/Library/MusicManager.cs
index 1ec578371..ad8c70f5e 100644
--- a/Emby.Server.Implementations/Library/MusicManager.cs
+++ b/Emby.Server.Implementations/Library/MusicManager.cs
@@ -22,7 +22,7 @@ namespace Emby.Server.Implementations.Library
_libraryManager = libraryManager;
}
- public List<BaseItem> GetInstantMixFromSong(Audio item, User user, DtoOptions dtoOptions)
+ public List<BaseItem> GetInstantMixFromSong(Audio item, Jellyfin.Data.Entities.User user, DtoOptions dtoOptions)
{
var list = new List<Audio>
{
@@ -32,17 +32,17 @@ namespace Emby.Server.Implementations.Library
return list.Concat(GetInstantMixFromGenres(item.Genres, user, dtoOptions)).ToList();
}
- public List<BaseItem> GetInstantMixFromArtist(MusicArtist item, User user, DtoOptions dtoOptions)
+ public List<BaseItem> GetInstantMixFromArtist(MusicArtist item, Jellyfin.Data.Entities.User user, DtoOptions dtoOptions)
{
return GetInstantMixFromGenres(item.Genres, user, dtoOptions);
}
- public List<BaseItem> GetInstantMixFromAlbum(MusicAlbum item, User user, DtoOptions dtoOptions)
+ public List<BaseItem> GetInstantMixFromAlbum(MusicAlbum item, Jellyfin.Data.Entities.User user, DtoOptions dtoOptions)
{
return GetInstantMixFromGenres(item.Genres, user, dtoOptions);
}
- public List<BaseItem> GetInstantMixFromFolder(Folder item, User user, DtoOptions dtoOptions)
+ public List<BaseItem> GetInstantMixFromFolder(Folder item, Jellyfin.Data.Entities.User user, DtoOptions dtoOptions)
{
var genres = item
.GetRecursiveChildren(user, new InternalItemsQuery(user)
@@ -58,12 +58,12 @@ namespace Emby.Server.Implementations.Library
return GetInstantMixFromGenres(genres, user, dtoOptions);
}
- public List<BaseItem> GetInstantMixFromPlaylist(Playlist item, User user, DtoOptions dtoOptions)
+ public List<BaseItem> GetInstantMixFromPlaylist(Playlist item, Jellyfin.Data.Entities.User user, DtoOptions dtoOptions)
{
return GetInstantMixFromGenres(item.Genres, user, dtoOptions);
}
- public List<BaseItem> GetInstantMixFromGenres(IEnumerable<string> genres, User user, DtoOptions dtoOptions)
+ public List<BaseItem> GetInstantMixFromGenres(IEnumerable<string> genres, Jellyfin.Data.Entities.User user, DtoOptions dtoOptions)
{
var genreIds = genres.DistinctNames().Select(i =>
{
@@ -75,13 +75,12 @@ namespace Emby.Server.Implementations.Library
{
return Guid.Empty;
}
-
}).Where(i => !i.Equals(Guid.Empty)).ToArray();
return GetInstantMixFromGenreIds(genreIds, user, dtoOptions);
}
- public List<BaseItem> GetInstantMixFromGenreIds(Guid[] genreIds, User user, DtoOptions dtoOptions)
+ public List<BaseItem> GetInstantMixFromGenreIds(Guid[] genreIds, Jellyfin.Data.Entities.User user, DtoOptions dtoOptions)
{
return _libraryManager.GetItemList(new InternalItemsQuery(user)
{
@@ -97,7 +96,7 @@ namespace Emby.Server.Implementations.Library
});
}
- public List<BaseItem> GetInstantMixFromItem(BaseItem item, User user, DtoOptions dtoOptions)
+ public List<BaseItem> GetInstantMixFromItem(BaseItem item, Jellyfin.Data.Entities.User user, DtoOptions dtoOptions)
{
var genre = item as MusicGenre;
if (genre != null)
@@ -105,32 +104,27 @@ namespace Emby.Server.Implementations.Library
return GetInstantMixFromGenreIds(new[] { item.Id }, user, dtoOptions);
}
- var playlist = item as Playlist;
- if (playlist != null)
+ if (item is Playlist playlist)
{
return GetInstantMixFromPlaylist(playlist, user, dtoOptions);
}
- var album = item as MusicAlbum;
- if (album != null)
+ if (item is MusicAlbum album)
{
return GetInstantMixFromAlbum(album, user, dtoOptions);
}
- var artist = item as MusicArtist;
- if (artist != null)
+ if (item is MusicArtist artist)
{
return GetInstantMixFromArtist(artist, user, dtoOptions);
}
- var song = item as Audio;
- if (song != null)
+ if (item is Audio song)
{
return GetInstantMixFromSong(song, user, dtoOptions);
}
- var folder = item as Folder;
- if (folder != null)
+ if (item is Folder folder)
{
return GetInstantMixFromFolder(folder, user, dtoOptions);
}
diff --git a/Emby.Server.Implementations/Library/SearchEngine.cs b/Emby.Server.Implementations/Library/SearchEngine.cs
index 59a77607d..026377ada 100644
--- a/Emby.Server.Implementations/Library/SearchEngine.cs
+++ b/Emby.Server.Implementations/Library/SearchEngine.cs
@@ -30,7 +30,7 @@ namespace Emby.Server.Implementations.Library
public QueryResult<SearchHintInfo> GetSearchHints(SearchQuery query)
{
- User user = null;
+ Jellyfin.Data.Entities.User user = null;
if (query.UserId.Equals(Guid.Empty))
{
@@ -76,7 +76,7 @@ namespace Emby.Server.Implementations.Library
/// <param name="user">The user.</param>
/// <returns>IEnumerable{SearchHintResult}.</returns>
/// <exception cref="ArgumentNullException">searchTerm</exception>
- private List<SearchHintInfo> GetSearchHints(SearchQuery query, User user)
+ private List<SearchHintInfo> GetSearchHints(SearchQuery query, Jellyfin.Data.Entities.User user)
{
var searchTerm = query.SearchTerm;
diff --git a/Emby.Server.Implementations/Library/UserDataManager.cs b/Emby.Server.Implementations/Library/UserDataManager.cs
index a9772a078..19c0d7eb7 100644
--- a/Emby.Server.Implementations/Library/UserDataManager.cs
+++ b/Emby.Server.Implementations/Library/UserDataManager.cs
@@ -50,7 +50,7 @@ namespace Emby.Server.Implementations.Library
SaveUserData(user, item, userData, reason, cancellationToken);
}
- public void SaveUserData(User user, BaseItem item, UserItemData userData, UserDataSaveReason reason, CancellationToken cancellationToken)
+ public void SaveUserData(Jellyfin.Data.Entities.User user, BaseItem item, UserItemData userData, UserDataSaveReason reason, CancellationToken cancellationToken)
{
if (userData == null)
{
@@ -119,7 +119,7 @@ namespace Emby.Server.Implementations.Library
return GetUserData(user, itemId, keys);
}
- public UserItemData GetUserData(User user, Guid itemId, List<string> keys)
+ public UserItemData GetUserData(Jellyfin.Data.Entities.User user, Guid itemId, List<string> keys)
{
var userId = user.InternalId;
@@ -157,7 +157,7 @@ namespace Emby.Server.Implementations.Library
return internalUserId.ToString(CultureInfo.InvariantCulture) + "-" + itemId.ToString("N", CultureInfo.InvariantCulture);
}
- public UserItemData GetUserData(User user, BaseItem item)
+ public UserItemData GetUserData(Jellyfin.Data.Entities.User user, BaseItem item)
{
return GetUserData(user, item.Id, item.GetUserDataKeys());
}
@@ -167,7 +167,7 @@ namespace Emby.Server.Implementations.Library
return GetUserData(userId, item.Id, item.GetUserDataKeys());
}
- public UserItemDataDto GetUserDataDto(BaseItem item, User user)
+ public UserItemDataDto GetUserDataDto(BaseItem item, Jellyfin.Data.Entities.User user)
{
var userData = GetUserData(user, item);
var dto = GetUserItemDataDto(userData);
@@ -176,7 +176,7 @@ namespace Emby.Server.Implementations.Library
return dto;
}
- public UserItemDataDto GetUserDataDto(BaseItem item, BaseItemDto itemDto, User user, DtoOptions options)
+ public UserItemDataDto GetUserDataDto(BaseItem item, BaseItemDto itemDto, Jellyfin.Data.Entities.User user, DtoOptions options)
{
var userData = GetUserData(user, item);
var dto = GetUserItemDataDto(userData);
diff --git a/Emby.Server.Implementations/Library/UserManager.cs b/Emby.Server.Implementations/Library/UserManager.cs
deleted file mode 100644
index b8feb5535..000000000
--- a/Emby.Server.Implementations/Library/UserManager.cs
+++ /dev/null
@@ -1,1132 +0,0 @@
-#pragma warning disable CS1591
-
-using System;
-using System.Collections.Concurrent;
-using System.Collections.Generic;
-using System.Globalization;
-using System.IO;
-using System.Linq;
-using System.Text;
-using System.Text.RegularExpressions;
-using System.Threading;
-using System.Threading.Tasks;
-using MediaBrowser.Common.Cryptography;
-using MediaBrowser.Common.Events;
-using MediaBrowser.Common.Net;
-using MediaBrowser.Controller;
-using MediaBrowser.Controller.Authentication;
-using MediaBrowser.Controller.Devices;
-using MediaBrowser.Controller.Drawing;
-using MediaBrowser.Controller.Dto;
-using MediaBrowser.Controller.Entities;
-using MediaBrowser.Controller.Library;
-using MediaBrowser.Controller.Net;
-using MediaBrowser.Controller.Persistence;
-using MediaBrowser.Controller.Plugins;
-using MediaBrowser.Controller.Providers;
-using MediaBrowser.Controller.Security;
-using MediaBrowser.Controller.Session;
-using MediaBrowser.Model.Configuration;
-using MediaBrowser.Model.Cryptography;
-using MediaBrowser.Model.Dto;
-using MediaBrowser.Model.Entities;
-using MediaBrowser.Model.Events;
-using MediaBrowser.Model.IO;
-using MediaBrowser.Model.Serialization;
-using MediaBrowser.Model.Users;
-using Microsoft.Extensions.Logging;
-
-namespace Emby.Server.Implementations.Library
-{
- /// <summary>
- /// Class UserManager.
- /// </summary>
- public class UserManager : IUserManager
- {
- private readonly object _policySyncLock = new object();
- private readonly object _configSyncLock = new object();
-
- private readonly ILogger _logger;
- private readonly IUserRepository _userRepository;
- private readonly IXmlSerializer _xmlSerializer;
- private readonly IJsonSerializer _jsonSerializer;
- private readonly INetworkManager _networkManager;
- private readonly IImageProcessor _imageProcessor;
- private readonly Lazy<IDtoService> _dtoServiceFactory;
- private readonly IServerApplicationHost _appHost;
- private readonly IFileSystem _fileSystem;
- private readonly ICryptoProvider _cryptoProvider;
-
- private ConcurrentDictionary<Guid, User> _users;
-
- private IAuthenticationProvider[] _authenticationProviders;
- private DefaultAuthenticationProvider _defaultAuthenticationProvider;
-
- private InvalidAuthProvider _invalidAuthProvider;
-
- private IPasswordResetProvider[] _passwordResetProviders;
- private DefaultPasswordResetProvider _defaultPasswordResetProvider;
-
- private IDtoService DtoService => _dtoServiceFactory.Value;
-
- public UserManager(
- ILogger<UserManager> logger,
- IUserRepository userRepository,
- IXmlSerializer xmlSerializer,
- INetworkManager networkManager,
- IImageProcessor imageProcessor,
- Lazy<IDtoService> dtoServiceFactory,
- IServerApplicationHost appHost,
- IJsonSerializer jsonSerializer,
- IFileSystem fileSystem,
- ICryptoProvider cryptoProvider)
- {
- _logger = logger;
- _userRepository = userRepository;
- _xmlSerializer = xmlSerializer;
- _networkManager = networkManager;
- _imageProcessor = imageProcessor;
- _dtoServiceFactory = dtoServiceFactory;
- _appHost = appHost;
- _jsonSerializer = jsonSerializer;
- _fileSystem = fileSystem;
- _cryptoProvider = cryptoProvider;
- _users = null;
- }
-
- public event EventHandler<GenericEventArgs<User>> UserPasswordChanged;
-
- /// <summary>
- /// Occurs when [user updated].
- /// </summary>
- public event EventHandler<GenericEventArgs<User>> UserUpdated;
-
- public event EventHandler<GenericEventArgs<User>> UserPolicyUpdated;
-
- public event EventHandler<GenericEventArgs<User>> UserConfigurationUpdated;
-
- public event EventHandler<GenericEventArgs<User>> UserLockedOut;
-
- public event EventHandler<GenericEventArgs<User>> UserCreated;
-
- /// <summary>
- /// Occurs when [user deleted].
- /// </summary>
- public event EventHandler<GenericEventArgs<User>> UserDeleted;
-
- /// <inheritdoc />
- public IEnumerable<User> Users => _users.Values;
-
- /// <inheritdoc />
- public IEnumerable<Guid> UsersIds => _users.Keys;
-
- /// <summary>
- /// Called when [user updated].
- /// </summary>
- /// <param name="user">The user.</param>
- private void OnUserUpdated(User user)
- {
- UserUpdated?.Invoke(this, new GenericEventArgs<User> { Argument = user });
- }
-
- /// <summary>
- /// Called when [user deleted].
- /// </summary>
- /// <param name="user">The user.</param>
- private void OnUserDeleted(User user)
- {
- UserDeleted?.Invoke(this, new GenericEventArgs<User> { Argument = user });
- }
-
- public NameIdPair[] GetAuthenticationProviders()
- {
- return _authenticationProviders
- .Where(i => i.IsEnabled)
- .OrderBy(i => i is DefaultAuthenticationProvider ? 0 : 1)
- .ThenBy(i => i.Name)
- .Select(i => new NameIdPair
- {
- Name = i.Name,
- Id = GetAuthenticationProviderId(i)
- })
- .ToArray();
- }
-
- public NameIdPair[] GetPasswordResetProviders()
- {
- return _passwordResetProviders
- .Where(i => i.IsEnabled)
- .OrderBy(i => i is DefaultPasswordResetProvider ? 0 : 1)
- .ThenBy(i => i.Name)
- .Select(i => new NameIdPair
- {
- Name = i.Name,
- Id = GetPasswordResetProviderId(i)
- })
- .ToArray();
- }
-
- public void AddParts(IEnumerable<IAuthenticationProvider> authenticationProviders, IEnumerable<IPasswordResetProvider> passwordResetProviders)
- {
- _authenticationProviders = authenticationProviders.ToArray();
-
- _defaultAuthenticationProvider = _authenticationProviders.OfType<DefaultAuthenticationProvider>().First();
-
- _invalidAuthProvider = _authenticationProviders.OfType<InvalidAuthProvider>().First();
-
- _passwordResetProviders = passwordResetProviders.ToArray();
-
- _defaultPasswordResetProvider = passwordResetProviders.OfType<DefaultPasswordResetProvider>().First();
- }
-
- /// <inheritdoc />
- public User GetUserById(Guid id)
- {
- if (id == Guid.Empty)
- {
- throw new ArgumentException("Guid can't be empty", nameof(id));
- }
-
- _users.TryGetValue(id, out User user);
- return user;
- }
-
- public User GetUserByName(string name)
- {
- if (string.IsNullOrWhiteSpace(name))
- {
- throw new ArgumentException("Invalid username", nameof(name));
- }
-
- return Users.FirstOrDefault(u => string.Equals(u.Name, name, StringComparison.OrdinalIgnoreCase));
- }
-
- public void Initialize()
- {
- LoadUsers();
-
- var users = Users;
-
- // If there are no local users with admin rights, make them all admins
- if (!users.Any(i => i.Policy.IsAdministrator))
- {
- foreach (var user in users)
- {
- user.Policy.IsAdministrator = true;
- UpdateUserPolicy(user, user.Policy, false);
- }
- }
- }
-
- public static bool IsValidUsername(string username)
- {
- // This is some regex that matches only on unicode "word" characters, as well as -, _ and @
- // In theory this will cut out most if not all 'control' characters which should help minimize any weirdness
- // Usernames can contain letters (a-z + whatever else unicode is cool with), numbers (0-9), at-signs (@), dashes (-), underscores (_), apostrophes ('), and periods (.)
- return Regex.IsMatch(username, @"^[\w\-'._@]*$");
- }
-
- private static bool IsValidUsernameCharacter(char i)
- => IsValidUsername(i.ToString(CultureInfo.InvariantCulture));
-
- public string MakeValidUsername(string username)
- {
- if (IsValidUsername(username))
- {
- return username;
- }
-
- // Usernames can contain letters (a-z), numbers (0-9), dashes (-), underscores (_), apostrophes ('), and periods (.)
- var builder = new StringBuilder();
-
- foreach (var c in username)
- {
- if (IsValidUsernameCharacter(c))
- {
- builder.Append(c);
- }
- }
-
- return builder.ToString();
- }
-
- public async Task<User> AuthenticateUser(
- string username,
- string password,
- string hashedPassword,
- string remoteEndPoint,
- bool isUserSession)
- {
- if (string.IsNullOrWhiteSpace(username))
- {
- _logger.LogInformation("Authentication request without username has been denied (IP: {IP}).", remoteEndPoint);
- throw new ArgumentNullException(nameof(username));
- }
-
- var user = Users.FirstOrDefault(i => string.Equals(username, i.Name, StringComparison.OrdinalIgnoreCase));
-
- var success = false;
- IAuthenticationProvider authenticationProvider = null;
-
- if (user != null)
- {
- var authResult = await AuthenticateLocalUser(username, password, hashedPassword, user, remoteEndPoint).ConfigureAwait(false);
- authenticationProvider = authResult.authenticationProvider;
- success = authResult.success;
- }
- else
- {
- // user is null
- var authResult = await AuthenticateLocalUser(username, password, hashedPassword, null, remoteEndPoint).ConfigureAwait(false);
- authenticationProvider = authResult.authenticationProvider;
- string updatedUsername = authResult.username;
- success = authResult.success;
-
- if (success
- && authenticationProvider != null
- && !(authenticationProvider is DefaultAuthenticationProvider))
- {
- // Trust the username returned by the authentication provider
- username = updatedUsername;
-
- // Search the database for the user again
- // the authentication provider might have created it
- user = Users
- .FirstOrDefault(i => string.Equals(username, i.Name, StringComparison.OrdinalIgnoreCase));
-
- if (authenticationProvider is IHasNewUserPolicy hasNewUserPolicy)
- {
- var policy = hasNewUserPolicy.GetNewUserPolicy();
- UpdateUserPolicy(user, policy, true);
- }
- }
- }
-
- if (success && user != null && authenticationProvider != null)
- {
- var providerId = GetAuthenticationProviderId(authenticationProvider);
-
- if (!string.Equals(providerId, user.Policy.AuthenticationProviderId, StringComparison.OrdinalIgnoreCase))
- {
- user.Policy.AuthenticationProviderId = providerId;
- UpdateUserPolicy(user, user.Policy, true);
- }
- }
-
- if (user == null)
- {
- _logger.LogInformation("Authentication request for {UserName} has been denied (IP: {IP}).", username, remoteEndPoint);
- throw new AuthenticationException("Invalid username or password entered.");
- }
-
- if (user.Policy.IsDisabled)
- {
- _logger.LogInformation("Authentication request for {UserName} has been denied because this account is currently disabled (IP: {IP}).", username, remoteEndPoint);
- throw new SecurityException($"The {user.Name} account is currently disabled. Please consult with your administrator.");
- }
-
- if (!user.Policy.EnableRemoteAccess && !_networkManager.IsInLocalNetwork(remoteEndPoint))
- {
- _logger.LogInformation("Authentication request for {UserName} forbidden: remote access disabled and user not in local network (IP: {IP}).", username, remoteEndPoint);
- throw new SecurityException("Forbidden.");
- }
-
- if (!user.IsParentalScheduleAllowed())
- {
- _logger.LogInformation("Authentication request for {UserName} is not allowed at this time due parental restrictions (IP: {IP}).", username, remoteEndPoint);
- throw new SecurityException("User is not allowed access at this time.");
- }
-
- // Update LastActivityDate and LastLoginDate, then save
- if (success)
- {
- if (isUserSession)
- {
- user.LastActivityDate = user.LastLoginDate = DateTime.UtcNow;
- UpdateUser(user);
- }
-
- ResetInvalidLoginAttemptCount(user);
- _logger.LogInformation("Authentication request for {UserName} has succeeded.", user.Name);
- }
- else
- {
- IncrementInvalidLoginAttemptCount(user);
- _logger.LogInformation("Authentication request for {UserName} has been denied (IP: {IP}).", user.Name, remoteEndPoint);
- }
-
- return success ? user : null;
- }
-
-#nullable enable
-
- private static string GetAuthenticationProviderId(IAuthenticationProvider provider)
- {
- return provider.GetType().FullName;
- }
-
- private static string GetPasswordResetProviderId(IPasswordResetProvider provider)
- {
- return provider.GetType().FullName;
- }
-
- private IAuthenticationProvider GetAuthenticationProvider(User user)
- {
- return GetAuthenticationProviders(user)[0];
- }
-
- private IPasswordResetProvider GetPasswordResetProvider(User user)
- {
- return GetPasswordResetProviders(user)[0];
- }
-
- private IAuthenticationProvider[] GetAuthenticationProviders(User? user)
- {
- var authenticationProviderId = user?.Policy.AuthenticationProviderId;
-
- var providers = _authenticationProviders.Where(i => i.IsEnabled).ToArray();
-
- if (!string.IsNullOrEmpty(authenticationProviderId))
- {
- providers = providers.Where(i => string.Equals(authenticationProviderId, GetAuthenticationProviderId(i), StringComparison.OrdinalIgnoreCase)).ToArray();
- }
-
- if (providers.Length == 0)
- {
- // Assign the user to the InvalidAuthProvider since no configured auth provider was valid/found
- _logger.LogWarning("User {UserName} was found with invalid/missing Authentication Provider {AuthenticationProviderId}. Assigning user to InvalidAuthProvider until this is corrected", user?.Name, user?.Policy.AuthenticationProviderId);
- providers = new IAuthenticationProvider[] { _invalidAuthProvider };
- }
-
- return providers;
- }
-
- private IPasswordResetProvider[] GetPasswordResetProviders(User? user)
- {
- var passwordResetProviderId = user?.Policy.PasswordResetProviderId;
-
- var providers = _passwordResetProviders.Where(i => i.IsEnabled).ToArray();
-
- if (!string.IsNullOrEmpty(passwordResetProviderId))
- {
- providers = providers.Where(i => string.Equals(passwordResetProviderId, GetPasswordResetProviderId(i), StringComparison.OrdinalIgnoreCase)).ToArray();
- }
-
- if (providers.Length == 0)
- {
- providers = new IPasswordResetProvider[] { _defaultPasswordResetProvider };
- }
-
- return providers;
- }
-
- private async Task<(string username, bool success)> AuthenticateWithProvider(
- IAuthenticationProvider provider,
- string username,
- string password,
- User? resolvedUser)
- {
- try
- {
- var authenticationResult = provider is IRequiresResolvedUser requiresResolvedUser
- ? await requiresResolvedUser.Authenticate(username, password, resolvedUser).ConfigureAwait(false)
- : await provider.Authenticate(username, password).ConfigureAwait(false);
-
- if (authenticationResult.Username != username)
- {
- _logger.LogDebug("Authentication provider provided updated username {1}", authenticationResult.Username);
- username = authenticationResult.Username;
- }
-
- return (username, true);
- }
- catch (AuthenticationException ex)
- {
- _logger.LogError(ex, "Error authenticating with provider {Provider}", provider.Name);
-
- return (username, false);
- }
- }
-
- private async Task<(IAuthenticationProvider? authenticationProvider, string username, bool success)> AuthenticateLocalUser(
- string username,
- string password,
- string hashedPassword,
- User? user,
- string remoteEndPoint)
- {
- bool success = false;
- IAuthenticationProvider? authenticationProvider = null;
-
- foreach (var provider in GetAuthenticationProviders(user))
- {
- var providerAuthResult = await AuthenticateWithProvider(provider, username, password, user).ConfigureAwait(false);
- var updatedUsername = providerAuthResult.username;
- success = providerAuthResult.success;
-
- if (success)
- {
- authenticationProvider = provider;
- username = updatedUsername;
- break;
- }
- }
-
- if (!success
- && _networkManager.IsInLocalNetwork(remoteEndPoint)
- && user?.Configuration.EnableLocalPassword == true
- && !string.IsNullOrEmpty(user.EasyPassword))
- {
- // Check easy password
- var passwordHash = PasswordHash.Parse(user.EasyPassword);
- var hash = _cryptoProvider.ComputeHash(
- passwordHash.Id,
- Encoding.UTF8.GetBytes(password),
- passwordHash.Salt.ToArray());
- success = passwordHash.Hash.SequenceEqual(hash);
- }
-
- return (authenticationProvider, username, success);
- }
-
- private void ResetInvalidLoginAttemptCount(User user)
- {
- user.Policy.InvalidLoginAttemptCount = 0;
- UpdateUserPolicy(user, user.Policy, false);
- }
-
- private void IncrementInvalidLoginAttemptCount(User user)
- {
- int invalidLogins = ++user.Policy.InvalidLoginAttemptCount;
- int maxInvalidLogins = user.Policy.LoginAttemptsBeforeLockout;
- if (maxInvalidLogins > 0
- && invalidLogins >= maxInvalidLogins)
- {
- user.Policy.IsDisabled = true;
- UserLockedOut?.Invoke(this, new GenericEventArgs<User>(user));
- _logger.LogWarning(
- "Disabling user {UserName} due to {Attempts} unsuccessful login attempts.",
- user.Name,
- invalidLogins);
- }
-
- UpdateUserPolicy(user, user.Policy, false);
- }
-
- /// <summary>
- /// Loads the users from the repository.
- /// </summary>
- private void LoadUsers()
- {
- var users = _userRepository.RetrieveAllUsers();
-
- // There always has to be at least one user.
- if (users.Count != 0)
- {
- _users = new ConcurrentDictionary<Guid, User>(
- users.Select(x => new KeyValuePair<Guid, User>(x.Id, x)));
- return;
- }
-
- var defaultName = Environment.UserName;
- if (string.IsNullOrWhiteSpace(defaultName))
- {
- defaultName = "MyJellyfinUser";
- }
-
- _logger.LogWarning("No users, creating one with username {UserName}", defaultName);
-
- var name = MakeValidUsername(defaultName);
-
- var user = InstantiateNewUser(name);
-
- user.DateLastSaved = DateTime.UtcNow;
-
- _userRepository.CreateUser(user);
-
- user.Policy.IsAdministrator = true;
- user.Policy.EnableContentDeletion = true;
- user.Policy.EnableRemoteControlOfOtherUsers = true;
- UpdateUserPolicy(user, user.Policy, false);
-
- _users = new ConcurrentDictionary<Guid, User>();
- _users[user.Id] = user;
- }
-
-#nullable restore
-
- public UserDto GetUserDto(User user, string remoteEndPoint = null)
- {
- if (user == null)
- {
- throw new ArgumentNullException(nameof(user));
- }
-
- bool hasConfiguredPassword = GetAuthenticationProvider(user).HasPassword(user);
- bool hasConfiguredEasyPassword = !string.IsNullOrEmpty(GetAuthenticationProvider(user).GetEasyPasswordHash(user));
-
- bool hasPassword = user.Configuration.EnableLocalPassword && !string.IsNullOrEmpty(remoteEndPoint) && _networkManager.IsInLocalNetwork(remoteEndPoint) ?
- hasConfiguredEasyPassword :
- hasConfiguredPassword;
-
- UserDto dto = new UserDto
- {
- Id = user.Id,
- Name = user.Name,
- HasPassword = hasPassword,
- HasConfiguredPassword = hasConfiguredPassword,
- HasConfiguredEasyPassword = hasConfiguredEasyPassword,
- LastActivityDate = user.LastActivityDate,
- LastLoginDate = user.LastLoginDate,
- Configuration = user.Configuration,
- ServerId = _appHost.SystemId,
- Policy = user.Policy
- };
-
- if (!hasPassword && _users.Count == 1)
- {
- dto.EnableAutoLogin = true;
- }
-
- ItemImageInfo image = user.GetImageInfo(ImageType.Primary, 0);
-
- if (image != null)
- {
- dto.PrimaryImageTag = GetImageCacheTag(user, image);
-
- try
- {
- DtoService.AttachPrimaryImageAspectRatio(dto, user);
- }
- catch (Exception ex)
- {
- // Have to use a catch-all unfortunately because some .net image methods throw plain Exceptions
- _logger.LogError(ex, "Error generating PrimaryImageAspectRatio for {User}", user.Name);
- }
- }
-
- return dto;
- }
-
- public PublicUserDto GetPublicUserDto(User user, string remoteEndPoint = null)
- {
- if (user == null)
- {
- throw new ArgumentNullException(nameof(user));
- }
-
- IAuthenticationProvider authenticationProvider = GetAuthenticationProvider(user);
- bool hasConfiguredPassword = authenticationProvider.HasPassword(user);
- bool hasConfiguredEasyPassword = !string.IsNullOrEmpty(authenticationProvider.GetEasyPasswordHash(user));
-
- bool hasPassword = user.Configuration.EnableLocalPassword &&
- !string.IsNullOrEmpty(remoteEndPoint) &&
- _networkManager.IsInLocalNetwork(remoteEndPoint) ? hasConfiguredEasyPassword : hasConfiguredPassword;
-
- PublicUserDto dto = new PublicUserDto
- {
- Name = user.Name,
- HasPassword = hasPassword,
- HasConfiguredPassword = hasConfiguredPassword,
- };
-
- return dto;
- }
-
- public UserDto GetOfflineUserDto(User user)
- {
- var dto = GetUserDto(user);
-
- dto.ServerName = _appHost.FriendlyName;
-
- return dto;
- }
-
- private string GetImageCacheTag(BaseItem item, ItemImageInfo image)
- {
- try
- {
- return _imageProcessor.GetImageCacheTag(item, image);
- }
- catch (Exception ex)
- {
- _logger.LogError(ex, "Error getting {ImageType} image info for {ImagePath}", image.Type, image.Path);
- return null;
- }
- }
-
- /// <summary>
- /// Refreshes metadata for each user
- /// </summary>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task.</returns>
- public async Task RefreshUsersMetadata(CancellationToken cancellationToken)
- {
- foreach (var user in Users)
- {
- await user.RefreshMetadata(new MetadataRefreshOptions(new DirectoryService(_fileSystem)), cancellationToken).ConfigureAwait(false);
- }
- }
-
- /// <summary>
- /// Renames the user.
- /// </summary>
- /// <param name="user">The user.</param>
- /// <param name="newName">The new name.</param>
- /// <returns>Task.</returns>
- /// <exception cref="ArgumentNullException">user</exception>
- /// <exception cref="ArgumentException"></exception>
- public async Task RenameUser(User user, string newName)
- {
- if (user == null)
- {
- throw new ArgumentNullException(nameof(user));
- }
-
- if (string.IsNullOrWhiteSpace(newName))
- {
- throw new ArgumentException("Invalid username", nameof(newName));
- }
-
- if (user.Name.Equals(newName, StringComparison.Ordinal))
- {
- throw new ArgumentException("The new and old names must be different.");
- }
-
- if (Users.Any(
- u => u.Id != user.Id && u.Name.Equals(newName, StringComparison.OrdinalIgnoreCase)))
- {
- throw new ArgumentException(string.Format(
- CultureInfo.InvariantCulture,
- "A user with the name '{0}' already exists.",
- newName));
- }
-
- await user.Rename(newName).ConfigureAwait(false);
-
- OnUserUpdated(user);
- }
-
- /// <summary>
- /// Updates the user.
- /// </summary>
- /// <param name="user">The user.</param>
- /// <exception cref="ArgumentNullException">user</exception>
- /// <exception cref="ArgumentException"></exception>
- public void UpdateUser(User user)
- {
- if (user == null)
- {
- throw new ArgumentNullException(nameof(user));
- }
-
- if (user.Id == Guid.Empty)
- {
- throw new ArgumentException("Id can't be empty.", nameof(user));
- }
-
- if (!_users.ContainsKey(user.Id))
- {
- throw new ArgumentException(
- string.Format(
- CultureInfo.InvariantCulture,
- "A user '{0}' with Id {1} does not exist.",
- user.Name,
- user.Id),
- nameof(user));
- }
-
- user.DateModified = DateTime.UtcNow;
- user.DateLastSaved = DateTime.UtcNow;
-
- _userRepository.UpdateUser(user);
-
- OnUserUpdated(user);
- }
-
- /// <summary>
- /// Creates the user.
- /// </summary>
- /// <param name="name">The name.</param>
- /// <returns>User.</returns>
- /// <exception cref="ArgumentNullException">name</exception>
- /// <exception cref="ArgumentException"></exception>
- public User CreateUser(string name)
- {
- if (string.IsNullOrWhiteSpace(name))
- {
- throw new ArgumentNullException(nameof(name));
- }
-
- if (!IsValidUsername(name))
- {
- throw new ArgumentException("Usernames can contain unicode symbols, numbers (0-9), dashes (-), underscores (_), apostrophes ('), and periods (.)");
- }
-
- if (Users.Any(u => u.Name.Equals(name, StringComparison.OrdinalIgnoreCase)))
- {
- throw new ArgumentException(string.Format("A user with the name '{0}' already exists.", name));
- }
-
- var user = InstantiateNewUser(name);
-
- _users[user.Id] = user;
-
- user.DateLastSaved = DateTime.UtcNow;
-
- _userRepository.CreateUser(user);
-
- EventHelper.QueueEventIfNotNull(UserCreated, this, new GenericEventArgs<User> { Argument = user }, _logger);
-
- return user;
- }
-
- /// <inheritdoc />
- /// <exception cref="ArgumentNullException">The <c>user</c> is <c>null</c>.</exception>
- /// <exception cref="ArgumentException">The <c>user</c> doesn't exist, or is the last administrator.</exception>
- /// <exception cref="InvalidOperationException">The <c>user</c> can't be deleted; there are no other users.</exception>
- public void DeleteUser(User user)
- {
- if (user == null)
- {
- throw new ArgumentNullException(nameof(user));
- }
-
- if (!_users.ContainsKey(user.Id))
- {
- throw new ArgumentException(string.Format(
- CultureInfo.InvariantCulture,
- "The user cannot be deleted because there is no user with the Name {0} and Id {1}.",
- user.Name,
- user.Id));
- }
-
- if (_users.Count == 1)
- {
- throw new InvalidOperationException(string.Format(
- CultureInfo.InvariantCulture,
- "The user '{0}' cannot be deleted because there must be at least one user in the system.",
- user.Name));
- }
-
- if (user.Policy.IsAdministrator
- && Users.Count(i => i.Policy.IsAdministrator) == 1)
- {
- throw new ArgumentException(
- string.Format(
- CultureInfo.InvariantCulture,
- "The user '{0}' cannot be deleted because there must be at least one admin user in the system.",
- user.Name),
- nameof(user));
- }
-
- var configPath = GetConfigurationFilePath(user);
-
- _userRepository.DeleteUser(user);
-
- // Delete user config dir
- lock (_configSyncLock)
- lock (_policySyncLock)
- {
- try
- {
- Directory.Delete(user.ConfigurationDirectoryPath, true);
- }
- catch (IOException ex)
- {
- _logger.LogError(ex, "Error deleting user config dir: {Path}", user.ConfigurationDirectoryPath);
- }
- }
-
- _users.TryRemove(user.Id, out _);
-
- OnUserDeleted(user);
- }
-
- /// <summary>
- /// Resets the password by clearing it.
- /// </summary>
- /// <returns>Task.</returns>
- public Task ResetPassword(User user)
- {
- return ChangePassword(user, string.Empty);
- }
-
- public void ResetEasyPassword(User user)
- {
- ChangeEasyPassword(user, string.Empty, null);
- }
-
- public async Task ChangePassword(User user, string newPassword)
- {
- if (user == null)
- {
- throw new ArgumentNullException(nameof(user));
- }
-
- await GetAuthenticationProvider(user).ChangePassword(user, newPassword).ConfigureAwait(false);
-
- UpdateUser(user);
-
- UserPasswordChanged?.Invoke(this, new GenericEventArgs<User>(user));
- }
-
- public void ChangeEasyPassword(User user, string newPassword, string newPasswordHash)
- {
- if (user == null)
- {
- throw new ArgumentNullException(nameof(user));
- }
-
- GetAuthenticationProvider(user).ChangeEasyPassword(user, newPassword, newPasswordHash);
-
- UpdateUser(user);
-
- UserPasswordChanged?.Invoke(this, new GenericEventArgs<User>(user));
- }
-
- /// <summary>
- /// Instantiates the new user.
- /// </summary>
- /// <param name="name">The name.</param>
- /// <returns>User.</returns>
- private static User InstantiateNewUser(string name)
- {
- return new User
- {
- Name = name,
- Id = Guid.NewGuid(),
- DateCreated = DateTime.UtcNow,
- DateModified = DateTime.UtcNow
- };
- }
-
- public async Task<ForgotPasswordResult> StartForgotPasswordProcess(string enteredUsername, bool isInNetwork)
- {
- var user = string.IsNullOrWhiteSpace(enteredUsername) ?
- null :
- GetUserByName(enteredUsername);
-
- var action = ForgotPasswordAction.InNetworkRequired;
-
- if (user != null && isInNetwork)
- {
- var passwordResetProvider = GetPasswordResetProvider(user);
- return await passwordResetProvider.StartForgotPasswordProcess(user, isInNetwork).ConfigureAwait(false);
- }
- else
- {
- return new ForgotPasswordResult
- {
- Action = action,
- PinFile = string.Empty
- };
- }
- }
-
- public async Task<PinRedeemResult> RedeemPasswordResetPin(string pin)
- {
- foreach (var provider in _passwordResetProviders)
- {
- var result = await provider.RedeemPasswordResetPin(pin).ConfigureAwait(false);
- if (result.Success)
- {
- return result;
- }
- }
-
- return new PinRedeemResult
- {
- Success = false,
- UsersReset = Array.Empty<string>()
- };
- }
-
- public UserPolicy GetUserPolicy(User user)
- {
- var path = GetPolicyFilePath(user);
- if (!File.Exists(path))
- {
- return GetDefaultPolicy();
- }
-
- try
- {
- lock (_policySyncLock)
- {
- return (UserPolicy)_xmlSerializer.DeserializeFromFile(typeof(UserPolicy), path);
- }
- }
- catch (Exception ex)
- {
- _logger.LogError(ex, "Error reading policy file: {Path}", path);
-
- return GetDefaultPolicy();
- }
- }
-
- private static UserPolicy GetDefaultPolicy()
- {
- return new UserPolicy
- {
- EnableContentDownloading = true,
- EnableSyncTranscoding = true
- };
- }
-
- public void UpdateUserPolicy(Guid userId, UserPolicy userPolicy)
- {
- var user = GetUserById(userId);
- UpdateUserPolicy(user, userPolicy, true);
- }
-
- private void UpdateUserPolicy(User user, UserPolicy userPolicy, bool fireEvent)
- {
- // The xml serializer will output differently if the type is not exact
- if (userPolicy.GetType() != typeof(UserPolicy))
- {
- var json = _jsonSerializer.SerializeToString(userPolicy);
- userPolicy = _jsonSerializer.DeserializeFromString<UserPolicy>(json);
- }
-
- var path = GetPolicyFilePath(user);
-
- Directory.CreateDirectory(Path.GetDirectoryName(path));
-
- lock (_policySyncLock)
- {
- _xmlSerializer.SerializeToFile(userPolicy, path);
- user.Policy = userPolicy;
- }
-
- if (fireEvent)
- {
- UserPolicyUpdated?.Invoke(this, new GenericEventArgs<User> { Argument = user });
- }
- }
-
- private static string GetPolicyFilePath(User user)
- {
- return Path.Combine(user.ConfigurationDirectoryPath, "policy.xml");
- }
-
- private static string GetConfigurationFilePath(User user)
- {
- return Path.Combine(user.ConfigurationDirectoryPath, "config.xml");
- }
-
- public UserConfiguration GetUserConfiguration(User user)
- {
- var path = GetConfigurationFilePath(user);
-
- if (!File.Exists(path))
- {
- return new UserConfiguration();
- }
-
- try
- {
- lock (_configSyncLock)
- {
- return (UserConfiguration)_xmlSerializer.DeserializeFromFile(typeof(UserConfiguration), path);
- }
- }
- catch (Exception ex)
- {
- _logger.LogError(ex, "Error reading policy file: {Path}", path);
-
- return new UserConfiguration();
- }
- }
-
- public void UpdateConfiguration(Guid userId, UserConfiguration config)
- {
- var user = GetUserById(userId);
- UpdateConfiguration(user, config);
- }
-
- public void UpdateConfiguration(User user, UserConfiguration config)
- {
- UpdateConfiguration(user, config, true);
- }
-
- private void UpdateConfiguration(User user, UserConfiguration config, bool fireEvent)
- {
- var path = GetConfigurationFilePath(user);
-
- // The xml serializer will output differently if the type is not exact
- if (config.GetType() != typeof(UserConfiguration))
- {
- var json = _jsonSerializer.SerializeToString(config);
- config = _jsonSerializer.DeserializeFromString<UserConfiguration>(json);
- }
-
- Directory.CreateDirectory(Path.GetDirectoryName(path));
-
- lock (_configSyncLock)
- {
- _xmlSerializer.SerializeToFile(config, path);
- user.Configuration = config;
- }
-
- if (fireEvent)
- {
- UserConfigurationUpdated?.Invoke(this, new GenericEventArgs<User> { Argument = user });
- }
- }
- }
-
- public class DeviceAccessEntryPoint : IServerEntryPoint
- {
- private IUserManager _userManager;
- private IAuthenticationRepository _authRepo;
- private IDeviceManager _deviceManager;
- private ISessionManager _sessionManager;
-
- public DeviceAccessEntryPoint(IUserManager userManager, IAuthenticationRepository authRepo, IDeviceManager deviceManager, ISessionManager sessionManager)
- {
- _userManager = userManager;
- _authRepo = authRepo;
- _deviceManager = deviceManager;
- _sessionManager = sessionManager;
- }
-
- public Task RunAsync()
- {
- _userManager.UserPolicyUpdated += _userManager_UserPolicyUpdated;
-
- return Task.CompletedTask;
- }
-
- private void _userManager_UserPolicyUpdated(object sender, GenericEventArgs<User> e)
- {
- var user = e.Argument;
- if (!user.Policy.EnableAllDevices)
- {
- UpdateDeviceAccess(user);
- }
- }
-
- private void UpdateDeviceAccess(User user)
- {
- var existing = _authRepo.Get(new AuthenticationInfoQuery
- {
- UserId = user.Id
-
- }).Items;
-
- foreach (var authInfo in existing)
- {
- if (!string.IsNullOrEmpty(authInfo.DeviceId) && !_deviceManager.CanAccessDevice(user, authInfo.DeviceId))
- {
- _sessionManager.Logout(authInfo);
- }
- }
- }
-
- public void Dispose()
- {
-
- }
- }
-}
diff --git a/Emby.Server.Implementations/Library/UserViewManager.cs b/Emby.Server.Implementations/Library/UserViewManager.cs
index 322819b05..8263efdc4 100644
--- a/Emby.Server.Implementations/Library/UserViewManager.cs
+++ b/Emby.Server.Implementations/Library/UserViewManager.cs
@@ -5,6 +5,7 @@ using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Threading;
+using Jellyfin.Data.Enums;
using MediaBrowser.Controller.Channels;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Dto;
@@ -125,12 +126,12 @@ namespace Emby.Server.Implementations.Library
if (!query.IncludeHidden)
{
- list = list.Where(i => !user.Configuration.MyMediaExcludes.Contains(i.Id.ToString("N", CultureInfo.InvariantCulture))).ToList();
+ list = list.Where(i => !user.GetPreference(PreferenceKind.MyMediaExcludes).Contains(i.Id.ToString("N", CultureInfo.InvariantCulture))).ToList();
}
var sorted = _libraryManager.Sort(list, user, new[] { ItemSortBy.SortName }, SortOrder.Ascending).ToList();
- var orders = user.Configuration.OrderedViews.ToList();
+ var orders = user.GetPreference(PreferenceKind.OrderedViews).ToList();
return list
.OrderBy(i =>
@@ -165,7 +166,13 @@ namespace Emby.Server.Implementations.Library
return GetUserSubViewWithName(name, parentId, type, sortName);
}
- private Folder GetUserView(List<ICollectionFolder> parents, string viewType, string localizationKey, string sortName, User user, string[] presetViews)
+ private Folder GetUserView(
+ List<ICollectionFolder> parents,
+ string viewType,
+ string localizationKey,
+ string sortName,
+ Jellyfin.Data.Entities.User user,
+ string[] presetViews)
{
if (parents.Count == 1 && parents.All(i => string.Equals(i.CollectionType, viewType, StringComparison.OrdinalIgnoreCase)))
{
@@ -226,7 +233,7 @@ namespace Emby.Server.Implementations.Library
return list;
}
- private IReadOnlyList<BaseItem> GetItemsForLatestItems(User user, LatestItemsQuery request, DtoOptions options)
+ private IReadOnlyList<BaseItem> GetItemsForLatestItems(Jellyfin.Data.Entities.User user, LatestItemsQuery request, DtoOptions options)
{
var parentId = request.ParentId;
@@ -270,7 +277,8 @@ namespace Emby.Server.Implementations.Library
{
parents = _libraryManager.GetUserRootFolder().GetChildren(user, true)
.Where(i => i is Folder)
- .Where(i => !user.Configuration.LatestItemsExcludes.Contains(i.Id.ToString("N", CultureInfo.InvariantCulture)))
+ .Where(i => !user.GetPreference(PreferenceKind.LatestItemExcludes)
+ .Contains(i.Id.ToString("N", CultureInfo.InvariantCulture)))
.ToList();
}
@@ -331,12 +339,11 @@ namespace Emby.Server.Implementations.Library
var excludeItemTypes = includeItemTypes.Length == 0 && mediaTypes.Count == 0 ? new[]
{
- typeof(Person).Name,
- typeof(Studio).Name,
- typeof(Year).Name,
- typeof(MusicGenre).Name,
- typeof(Genre).Name
-
+ nameof(Person),
+ nameof(Studio),
+ nameof(Year),
+ nameof(MusicGenre),
+ nameof(Genre)
} : Array.Empty<string>();
var query = new InternalItemsQuery(user)
diff --git a/Emby.Server.Implementations/LiveTv/LiveTvManager.cs b/Emby.Server.Implementations/LiveTv/LiveTvManager.cs
index 1b10f2d27..c58860a99 100644
--- a/Emby.Server.Implementations/LiveTv/LiveTvManager.cs
+++ b/Emby.Server.Implementations/LiveTv/LiveTvManager.cs
@@ -7,6 +7,7 @@ using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Emby.Server.Implementations.Library;
+using Jellyfin.Data.Enums;
using MediaBrowser.Common.Configuration;
using MediaBrowser.Common.Extensions;
using MediaBrowser.Common.Progress;
@@ -707,7 +708,6 @@ namespace Emby.Server.Implementations.LiveTv
{
Path = info.ThumbImageUrl,
Type = ImageType.Thumb
-
}, 0);
}
}
@@ -720,7 +720,6 @@ namespace Emby.Server.Implementations.LiveTv
{
Path = info.LogoImageUrl,
Type = ImageType.Logo
-
}, 0);
}
}
@@ -733,7 +732,6 @@ namespace Emby.Server.Implementations.LiveTv
{
Path = info.BackdropImageUrl,
Type = ImageType.Backdrop
-
}, 0);
}
}
@@ -765,13 +763,14 @@ namespace Emby.Server.Implementations.LiveTv
return new Tuple<LiveTvProgram, bool, bool>(item, isNew, isUpdated);
}
- public async Task<BaseItemDto> GetProgram(string id, CancellationToken cancellationToken, User user = null)
+ public async Task<BaseItemDto> GetProgram(string id, CancellationToken cancellationToken, Jellyfin.Data.Entities.User user = null)
{
var program = _libraryManager.GetItemById(id);
var dto = _dtoService.GetBaseItemDto(program, new DtoOptions(), user);
- var list = new List<Tuple<BaseItemDto, string, string>>() {
+ var list = new List<Tuple<BaseItemDto, string, string>>
+ {
new Tuple<BaseItemDto, string, string>(dto, program.ExternalId, program.ExternalSeriesId)
};
@@ -939,7 +938,7 @@ namespace Emby.Server.Implementations.LiveTv
};
}
- private int GetRecommendationScore(LiveTvProgram program, User user, bool factorChannelWatchCount)
+ private int GetRecommendationScore(LiveTvProgram program, Jellyfin.Data.Entities.User user, bool factorChannelWatchCount)
{
var score = 0;
@@ -1325,7 +1324,7 @@ namespace Emby.Server.Implementations.LiveTv
return 7;
}
- private QueryResult<BaseItem> GetEmbyRecordings(RecordingQuery query, DtoOptions dtoOptions, User user)
+ private QueryResult<BaseItem> GetEmbyRecordings(RecordingQuery query, DtoOptions dtoOptions, Jellyfin.Data.Entities.User user)
{
if (user == null)
{
@@ -1433,7 +1432,7 @@ namespace Emby.Server.Implementations.LiveTv
return result;
}
- public Task AddInfoToProgramDto(IReadOnlyCollection<(BaseItem, BaseItemDto)> tuples, ItemFields[] fields, User user = null)
+ public Task AddInfoToProgramDto(IReadOnlyCollection<(BaseItem, BaseItemDto)> tuples, ItemFields[] fields, Jellyfin.Data.Entities.User user = null)
{
var programTuples = new List<Tuple<BaseItemDto, string, string>>();
var hasChannelImage = fields.Contains(ItemFields.ChannelImage);
@@ -1483,7 +1482,7 @@ namespace Emby.Server.Implementations.LiveTv
return EmbyTV.EmbyTV.Current.GetActiveRecordingInfo(path);
}
- public void AddInfoToRecordingDto(BaseItem item, BaseItemDto dto, ActiveRecordingInfo activeRecordingInfo, User user = null)
+ public void AddInfoToRecordingDto(BaseItem item, BaseItemDto dto, ActiveRecordingInfo activeRecordingInfo, Jellyfin.Data.Entities.User user = null)
{
var service = EmbyTV.EmbyTV.Current;
@@ -1895,7 +1894,7 @@ namespace Emby.Server.Implementations.LiveTv
return _libraryManager.GetItemById(internalChannelId);
}
- public void AddChannelInfo(IReadOnlyCollection<(BaseItemDto, LiveTvChannel)> tuples, DtoOptions options, User user)
+ public void AddChannelInfo(IReadOnlyCollection<(BaseItemDto, LiveTvChannel)> tuples, DtoOptions options, Jellyfin.Data.Entities.User user)
{
var now = DateTime.UtcNow;
@@ -2206,23 +2205,22 @@ namespace Emby.Server.Implementations.LiveTv
var info = new LiveTvInfo
{
Services = services,
- IsEnabled = services.Length > 0
+ IsEnabled = services.Length > 0,
+ EnabledUsers = _userManager.Users
+ .Where(IsLiveTvEnabled)
+ .Select(i => i.Id.ToString("N", CultureInfo.InvariantCulture))
+ .ToArray()
};
- info.EnabledUsers = _userManager.Users
- .Where(IsLiveTvEnabled)
- .Select(i => i.Id.ToString("N", CultureInfo.InvariantCulture))
- .ToArray();
-
return info;
}
- private bool IsLiveTvEnabled(User user)
+ private bool IsLiveTvEnabled(Jellyfin.Data.Entities.User user)
{
- return user.Policy.EnableLiveTvAccess && (Services.Count > 1 || GetConfiguration().TunerHosts.Length > 0);
+ return user.HasPermission(PermissionKind.EnableLiveTvAccess) && (Services.Count > 1 || GetConfiguration().TunerHosts.Length > 0);
}
- public IEnumerable<User> GetEnabledUsers()
+ public IEnumerable<Jellyfin.Data.Entities.User> GetEnabledUsers()
{
return _userManager.Users
.Where(IsLiveTvEnabled);
@@ -2472,12 +2470,12 @@ namespace Emby.Server.Implementations.LiveTv
return _tvDtoService.GetInternalProgramId(externalId);
}
- public List<BaseItem> GetRecordingFolders(User user)
+ public List<BaseItem> GetRecordingFolders(Jellyfin.Data.Entities.User user)
{
return GetRecordingFolders(user, false);
}
- private List<BaseItem> GetRecordingFolders(User user, bool refreshChannels)
+ private List<BaseItem> GetRecordingFolders(Jellyfin.Data.Entities.User user, bool refreshChannels)
{
var folders = EmbyTV.EmbyTV.Current.GetRecordingFolders()
.SelectMany(i => i.Locations)
diff --git a/Emby.Server.Implementations/Playlists/ManualPlaylistsFolder.cs b/Emby.Server.Implementations/Playlists/ManualPlaylistsFolder.cs
index cd9f7946e..52809c9c1 100644
--- a/Emby.Server.Implementations/Playlists/ManualPlaylistsFolder.cs
+++ b/Emby.Server.Implementations/Playlists/ManualPlaylistsFolder.cs
@@ -14,12 +14,12 @@ namespace Emby.Server.Implementations.Playlists
Name = "Playlists";
}
- public override bool IsVisible(User user)
+ public override bool IsVisible(Jellyfin.Data.Entities.User user)
{
return base.IsVisible(user) && GetChildren(user, true).Any();
}
- protected override IEnumerable<BaseItem> GetEligibleChildrenForRecursiveChildren(User user)
+ protected override IEnumerable<BaseItem> GetEligibleChildrenForRecursiveChildren(Jellyfin.Data.Entities.User user)
{
return base.GetEligibleChildrenForRecursiveChildren(user).OfType<Playlist>();
}
@@ -42,7 +42,7 @@ namespace Emby.Server.Implementations.Playlists
}
query.Recursive = true;
- query.IncludeItemTypes = new string[] { "Playlist" };
+ query.IncludeItemTypes = new[] { "Playlist" };
query.Parent = null;
return LibraryManager.GetItemsResult(query);
}
diff --git a/Emby.Server.Implementations/Playlists/PlaylistManager.cs b/Emby.Server.Implementations/Playlists/PlaylistManager.cs
index 9b1510ac9..9a062b196 100644
--- a/Emby.Server.Implementations/Playlists/PlaylistManager.cs
+++ b/Emby.Server.Implementations/Playlists/PlaylistManager.cs
@@ -175,7 +175,7 @@ namespace Emby.Server.Implementations.Playlists
return path;
}
- private List<BaseItem> GetPlaylistItems(IEnumerable<Guid> itemIds, string playlistMediaType, User user, DtoOptions options)
+ private List<BaseItem> GetPlaylistItems(IEnumerable<Guid> itemIds, string playlistMediaType, Jellyfin.Data.Entities.User user, DtoOptions options)
{
var items = itemIds.Select(i => _libraryManager.GetItemById(i)).Where(i => i != null);
@@ -192,7 +192,7 @@ namespace Emby.Server.Implementations.Playlists
});
}
- private void AddToPlaylistInternal(string playlistId, ICollection<Guid> newItemIds, User user, DtoOptions options)
+ private void AddToPlaylistInternal(string playlistId, ICollection<Guid> newItemIds, Jellyfin.Data.Entities.User user, DtoOptions options)
{
// Retrieve the existing playlist
var playlist = _libraryManager.GetItemById(playlistId) as Playlist
diff --git a/Emby.Server.Implementations/Session/SessionManager.cs b/Emby.Server.Implementations/Session/SessionManager.cs
index df98a35bc..84b16f1c7 100644
--- a/Emby.Server.Implementations/Session/SessionManager.cs
+++ b/Emby.Server.Implementations/Session/SessionManager.cs
@@ -5,6 +5,7 @@ using System.Globalization;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
+using Jellyfin.Data.Enums;
using MediaBrowser.Common.Events;
using MediaBrowser.Common.Extensions;
using MediaBrowser.Controller;
@@ -253,7 +254,7 @@ namespace Emby.Server.Implementations.Session
string deviceId,
string deviceName,
string remoteEndPoint,
- User user)
+ Jellyfin.Data.Entities.User user)
{
CheckDisposed();
@@ -279,7 +280,7 @@ namespace Emby.Server.Implementations.Session
if (user != null)
{
- var userLastActivityDate = user.LastActivityDate ?? DateTime.MinValue;
+ var userLastActivityDate = user.LastActivityDate;
user.LastActivityDate = activityDate;
if ((activityDate - userLastActivityDate).TotalSeconds > 60)
@@ -431,7 +432,13 @@ namespace Emby.Server.Implementations.Session
/// <param name="remoteEndPoint">The remote end point.</param>
/// <param name="user">The user.</param>
/// <returns>SessionInfo.</returns>
- private SessionInfo GetSessionInfo(string appName, string appVersion, string deviceId, string deviceName, string remoteEndPoint, User user)
+ private SessionInfo GetSessionInfo(
+ string appName,
+ string appVersion,
+ string deviceId,
+ string deviceName,
+ string remoteEndPoint,
+ Jellyfin.Data.Entities.User user)
{
CheckDisposed();
@@ -444,14 +451,13 @@ namespace Emby.Server.Implementations.Session
CheckDisposed();
- var sessionInfo = _activeConnections.GetOrAdd(key, k =>
- {
- return CreateSession(k, appName, appVersion, deviceId, deviceName, remoteEndPoint, user);
- });
+ var sessionInfo = _activeConnections.GetOrAdd(
+ key,
+ k => CreateSession(k, appName, appVersion, deviceId, deviceName, remoteEndPoint, user));
- sessionInfo.UserId = user == null ? Guid.Empty : user.Id;
- sessionInfo.UserName = user?.Name;
- sessionInfo.UserPrimaryImageTag = user == null ? null : GetImageCacheTag(user, ImageType.Primary);
+ sessionInfo.UserId = user?.Id ?? Guid.Empty;
+ sessionInfo.UserName = user?.Username;
+ sessionInfo.UserPrimaryImageTag = user == null ? null : GetImageCacheTag(user);
sessionInfo.RemoteEndPoint = remoteEndPoint;
sessionInfo.Client = appName;
@@ -470,7 +476,14 @@ namespace Emby.Server.Implementations.Session
return sessionInfo;
}
- private SessionInfo CreateSession(string key, string appName, string appVersion, string deviceId, string deviceName, string remoteEndPoint, User user)
+ private SessionInfo CreateSession(
+ string key,
+ string appName,
+ string appVersion,
+ string deviceId,
+ string deviceName,
+ string remoteEndPoint,
+ Jellyfin.Data.Entities.User user)
{
var sessionInfo = new SessionInfo(this, _logger)
{
@@ -480,11 +493,11 @@ namespace Emby.Server.Implementations.Session
Id = key.GetMD5().ToString("N", CultureInfo.InvariantCulture)
};
- var username = user?.Name;
+ var username = user?.Username;
sessionInfo.UserId = user?.Id ?? Guid.Empty;
sessionInfo.UserName = username;
- sessionInfo.UserPrimaryImageTag = user == null ? null : GetImageCacheTag(user, ImageType.Primary);
+ sessionInfo.UserPrimaryImageTag = user == null ? null : GetImageCacheTag(user);
sessionInfo.RemoteEndPoint = remoteEndPoint;
if (string.IsNullOrEmpty(deviceName))
@@ -507,9 +520,9 @@ namespace Emby.Server.Implementations.Session
return sessionInfo;
}
- private List<User> GetUsers(SessionInfo session)
+ private List<Jellyfin.Data.Entities.User> GetUsers(SessionInfo session)
{
- var users = new List<User>();
+ var users = new List<Jellyfin.Data.Entities.User>();
if (session.UserId != Guid.Empty)
{
@@ -532,10 +545,7 @@ namespace Emby.Server.Implementations.Session
private void StartIdleCheckTimer()
{
- if (_idleTimer == null)
- {
- _idleTimer = new Timer(CheckForIdlePlayback, null, TimeSpan.FromMinutes(5), TimeSpan.FromMinutes(5));
- }
+ _idleTimer ??= new Timer(CheckForIdlePlayback, null, TimeSpan.FromMinutes(5), TimeSpan.FromMinutes(5));
}
private void StopIdleCheckTimer()
@@ -670,7 +680,7 @@ namespace Emby.Server.Implementations.Session
/// </summary>
/// <param name="user">The user object.</param>
/// <param name="item">The item.</param>
- private void OnPlaybackStart(User user, BaseItem item)
+ private void OnPlaybackStart(Jellyfin.Data.Entities.User user, BaseItem item)
{
var data = _userDataManager.GetUserData(user, item);
@@ -753,7 +763,7 @@ namespace Emby.Server.Implementations.Session
StartIdleCheckTimer();
}
- private void OnPlaybackProgress(User user, BaseItem item, PlaybackProgressInfo info)
+ private void OnPlaybackProgress(Jellyfin.Data.Entities.User user, BaseItem item, PlaybackProgressInfo info)
{
var data = _userDataManager.GetUserData(user, item);
@@ -779,11 +789,11 @@ namespace Emby.Server.Implementations.Session
}
}
- private static bool UpdatePlaybackSettings(User user, PlaybackProgressInfo info, UserItemData data)
+ private static bool UpdatePlaybackSettings(Jellyfin.Data.Entities.User user, PlaybackProgressInfo info, UserItemData data)
{
var changed = false;
- if (user.Configuration.RememberAudioSelections)
+ if (user.RememberAudioSelections)
{
if (data.AudioStreamIndex != info.AudioStreamIndex)
{
@@ -800,7 +810,7 @@ namespace Emby.Server.Implementations.Session
}
}
- if (user.Configuration.RememberSubtitleSelections)
+ if (user.RememberSubtitleSelections)
{
if (data.SubtitleStreamIndex != info.SubtitleStreamIndex)
{
@@ -939,7 +949,7 @@ namespace Emby.Server.Implementations.Session
_logger);
}
- private bool OnPlaybackStopped(User user, BaseItem item, long? positionTicks, bool playbackFailed)
+ private bool OnPlaybackStopped(Jellyfin.Data.Entities.User user, BaseItem item, long? positionTicks, bool playbackFailed)
{
bool playedToCompletion = false;
@@ -1111,13 +1121,13 @@ namespace Emby.Server.Implementations.Session
if (items.Any(i => i.GetPlayAccess(user) != PlayAccess.Full))
{
throw new ArgumentException(
- string.Format(CultureInfo.InvariantCulture, "{0} is not allowed to play media.", user.Name));
+ string.Format(CultureInfo.InvariantCulture, "{0} is not allowed to play media.", user.Username));
}
}
if (user != null
&& command.ItemIds.Length == 1
- && user.Configuration.EnableNextEpisodeAutoPlay
+ && user.EnableNextEpisodeAutoPlay
&& _libraryManager.GetItemById(command.ItemIds[0]) is Episode episode)
{
var series = episode.Series;
@@ -1153,7 +1163,7 @@ namespace Emby.Server.Implementations.Session
await SendMessageToSession(session, "Play", command, cancellationToken).ConfigureAwait(false);
}
- private IEnumerable<BaseItem> TranslateItemForPlayback(Guid id, User user)
+ private IEnumerable<BaseItem> TranslateItemForPlayback(Guid id, Jellyfin.Data.Entities.User user)
{
var item = _libraryManager.GetItemById(id);
@@ -1172,7 +1182,7 @@ namespace Emby.Server.Implementations.Session
DtoOptions = new DtoOptions(false)
{
EnableImages = false,
- Fields = new ItemFields[]
+ Fields = new[]
{
ItemFields.SortName
}
@@ -1206,7 +1216,7 @@ namespace Emby.Server.Implementations.Session
return new[] { item };
}
- private IEnumerable<BaseItem> TranslateItemForInstantMix(Guid id, User user)
+ private IEnumerable<BaseItem> TranslateItemForInstantMix(Guid id, Jellyfin.Data.Entities.User user)
{
var item = _libraryManager.GetItemById(id);
@@ -1334,7 +1344,7 @@ namespace Emby.Server.Implementations.Session
list.Add(new SessionUserInfo
{
UserId = userId,
- UserName = user.Name
+ UserName = user.Username
});
session.AdditionalUsers = list.ToArray();
@@ -1389,7 +1399,7 @@ namespace Emby.Server.Implementations.Session
{
CheckDisposed();
- User user = null;
+ Jellyfin.Data.Entities.User user = null;
if (request.UserId != Guid.Empty)
{
user = _userManager.GetUserById(request.UserId);
@@ -1445,7 +1455,7 @@ namespace Emby.Server.Implementations.Session
return returnResult;
}
- private string GetAuthorizationToken(User user, string deviceId, string app, string appVersion, string deviceName)
+ private string GetAuthorizationToken(Jellyfin.Data.Entities.User user, string deviceId, string app, string appVersion, string deviceName)
{
var existing = _authRepo.Get(
new AuthenticationInfoQuery
@@ -1494,7 +1504,7 @@ namespace Emby.Server.Implementations.Session
DeviceName = deviceName,
UserId = user.Id,
AccessToken = Guid.NewGuid().ToString("N", CultureInfo.InvariantCulture),
- UserName = user.Name
+ UserName = user.Username
};
_logger.LogInformation("Creating new access token for user {0}", user.Id);
@@ -1691,15 +1701,15 @@ namespace Emby.Server.Implementations.Session
return info;
}
- private string GetImageCacheTag(BaseItem item, ImageType type)
+ private string GetImageCacheTag(Jellyfin.Data.Entities.User user)
{
try
{
- return _imageProcessor.GetImageCacheTag(item, type);
+ return _imageProcessor.GetImageCacheTag(user);
}
- catch (Exception ex)
+ catch (Exception e)
{
- _logger.LogError(ex, "Error getting image information for {Type}", type);
+ _logger.LogError(e, "Error getting image information for profile image");
return null;
}
}
@@ -1808,7 +1818,10 @@ namespace Emby.Server.Implementations.Session
{
CheckDisposed();
- var adminUserIds = _userManager.Users.Where(i => i.Policy.IsAdministrator).Select(i => i.Id).ToList();
+ var adminUserIds = _userManager.Users
+ .Where(i => i.HasPermission(PermissionKind.IsAdministrator))
+ .Select(i => i.Id)
+ .ToList();
return SendMessageToUserSessions(adminUserIds, name, data, cancellationToken);
}
diff --git a/Emby.Server.Implementations/Sorting/DateLastMediaAddedComparer.cs b/Emby.Server.Implementations/Sorting/DateLastMediaAddedComparer.cs
index 623675157..3cc294371 100644
--- a/Emby.Server.Implementations/Sorting/DateLastMediaAddedComparer.cs
+++ b/Emby.Server.Implementations/Sorting/DateLastMediaAddedComparer.cs
@@ -12,7 +12,7 @@ namespace Emby.Server.Implementations.Sorting
/// Gets or sets the user.
/// </summary>
/// <value>The user.</value>
- public User User { get; set; }
+ public Jellyfin.Data.Entities.User User { get; set; }
/// <summary>
/// Gets or sets the user manager.
diff --git a/Emby.Server.Implementations/Sorting/DatePlayedComparer.cs b/Emby.Server.Implementations/Sorting/DatePlayedComparer.cs
index 73f59f8cd..57a1a00d9 100644
--- a/Emby.Server.Implementations/Sorting/DatePlayedComparer.cs
+++ b/Emby.Server.Implementations/Sorting/DatePlayedComparer.cs
@@ -15,7 +15,7 @@ namespace Emby.Server.Implementations.Sorting
/// Gets or sets the user.
/// </summary>
/// <value>The user.</value>
- public User User { get; set; }
+ public Jellyfin.Data.Entities.User User { get; set; }
/// <summary>
/// Gets or sets the user manager.
diff --git a/Emby.Server.Implementations/Sorting/IsFavoriteOrLikeComparer.cs b/Emby.Server.Implementations/Sorting/IsFavoriteOrLikeComparer.cs
index 66de05a6a..c9feca7e3 100644
--- a/Emby.Server.Implementations/Sorting/IsFavoriteOrLikeComparer.cs
+++ b/Emby.Server.Implementations/Sorting/IsFavoriteOrLikeComparer.cs
@@ -11,7 +11,7 @@ namespace Emby.Server.Implementations.Sorting
/// Gets or sets the user.
/// </summary>
/// <value>The user.</value>
- public User User { get; set; }
+ public Jellyfin.Data.Entities.User User { get; set; }
/// <summary>
/// Compares the specified x.
diff --git a/Emby.Server.Implementations/Sorting/IsPlayedComparer.cs b/Emby.Server.Implementations/Sorting/IsPlayedComparer.cs
index da3f3dd25..6f383e65f 100644
--- a/Emby.Server.Implementations/Sorting/IsPlayedComparer.cs
+++ b/Emby.Server.Implementations/Sorting/IsPlayedComparer.cs
@@ -11,7 +11,7 @@ namespace Emby.Server.Implementations.Sorting
/// Gets or sets the user.
/// </summary>
/// <value>The user.</value>
- public User User { get; set; }
+ public Jellyfin.Data.Entities.User User { get; set; }
/// <summary>
/// Compares the specified x.
diff --git a/Emby.Server.Implementations/Sorting/IsUnplayedComparer.cs b/Emby.Server.Implementations/Sorting/IsUnplayedComparer.cs
index d99d0eff2..4845fdc0d 100644
--- a/Emby.Server.Implementations/Sorting/IsUnplayedComparer.cs
+++ b/Emby.Server.Implementations/Sorting/IsUnplayedComparer.cs
@@ -11,7 +11,7 @@ namespace Emby.Server.Implementations.Sorting
/// Gets or sets the user.
/// </summary>
/// <value>The user.</value>
- public User User { get; set; }
+ public Jellyfin.Data.Entities.User User { get; set; }
/// <summary>
/// Compares the specified x.
diff --git a/Emby.Server.Implementations/Sorting/PlayCountComparer.cs b/Emby.Server.Implementations/Sorting/PlayCountComparer.cs
index eb74ce1bd..99846db61 100644
--- a/Emby.Server.Implementations/Sorting/PlayCountComparer.cs
+++ b/Emby.Server.Implementations/Sorting/PlayCountComparer.cs
@@ -14,7 +14,7 @@ namespace Emby.Server.Implementations.Sorting
/// Gets or sets the user.
/// </summary>
/// <value>The user.</value>
- public User User { get; set; }
+ public Jellyfin.Data.Entities.User User { get; set; }
/// <summary>
/// Compares the specified x.
diff --git a/Emby.Server.Implementations/TV/TVSeriesManager.cs b/Emby.Server.Implementations/TV/TVSeriesManager.cs
index 4c2f24e6f..905a1ea99 100644
--- a/Emby.Server.Implementations/TV/TVSeriesManager.cs
+++ b/Emby.Server.Implementations/TV/TVSeriesManager.cs
@@ -2,6 +2,7 @@ using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
+using Jellyfin.Data.Enums;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Dto;
using MediaBrowser.Controller.Entities;
@@ -74,7 +75,8 @@ namespace Emby.Server.Implementations.TV
{
parents = _libraryManager.GetUserRootFolder().GetChildren(user, true)
.Where(i => i is Folder)
- .Where(i => !user.Configuration.LatestItemsExcludes.Contains(i.Id.ToString("N", CultureInfo.InvariantCulture)))
+ .Where(i => !user.GetPreference(PreferenceKind.LatestItemExcludes)
+ .Contains(i.Id.ToString("N", CultureInfo.InvariantCulture)))
.ToArray();
}
@@ -137,7 +139,7 @@ namespace Emby.Server.Implementations.TV
return GetResult(episodes, request);
}
- public IEnumerable<Episode> GetNextUpEpisodes(NextUpQuery request, User user, IEnumerable<string> seriesKeys, DtoOptions dtoOptions)
+ public IEnumerable<Episode> GetNextUpEpisodes(NextUpQuery request, Jellyfin.Data.Entities.User user, IEnumerable<string> seriesKeys, DtoOptions dtoOptions)
{
// Avoid implicitly captured closure
var currentUser = user;
@@ -186,13 +188,13 @@ namespace Emby.Server.Implementations.TV
/// Gets the next up.
/// </summary>
/// <returns>Task{Episode}.</returns>
- private Tuple<DateTime, Func<Episode>> GetNextUp(string seriesKey, User user, DtoOptions dtoOptions)
+ private Tuple<DateTime, Func<Episode>> GetNextUp(string seriesKey, Jellyfin.Data.Entities.User user, DtoOptions dtoOptions)
{
var lastWatchedEpisode = _libraryManager.GetItemList(new InternalItemsQuery(user)
{
AncestorWithPresentationUniqueKey = null,
SeriesPresentationUniqueKey = seriesKey,
- IncludeItemTypes = new[] { typeof(Episode).Name },
+ IncludeItemTypes = new[] { nameof(Episode) },
OrderBy = new[] { new ValueTuple<string, SortOrder>(ItemSortBy.SortName, SortOrder.Descending) },
IsPlayed = true,
Limit = 1,
@@ -205,7 +207,6 @@ namespace Emby.Server.Implementations.TV
},
EnableImages = false
}
-
}).FirstOrDefault();
Func<Episode> getEpisode = () =>
@@ -220,7 +221,7 @@ namespace Emby.Server.Implementations.TV
IsPlayed = false,
IsVirtualItem = false,
ParentIndexNumberNotEquals = 0,
- MinSortName = lastWatchedEpisode == null ? null : lastWatchedEpisode.SortName,
+ MinSortName = lastWatchedEpisode?.SortName,
DtoOptions = dtoOptions
}).Cast<Episode>().FirstOrDefault();
diff --git a/Jellyfin.Api/Auth/CustomAuthenticationHandler.cs b/Jellyfin.Api/Auth/CustomAuthenticationHandler.cs
index 26f7d9d2d..4929e8897 100644
--- a/Jellyfin.Api/Auth/CustomAuthenticationHandler.cs
+++ b/Jellyfin.Api/Auth/CustomAuthenticationHandler.cs
@@ -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);
diff --git a/Jellyfin.Api/Controllers/StartupController.cs b/Jellyfin.Api/Controllers/StartupController.cs
index afc9b8f3d..f965d83f3 100644
--- a/Jellyfin.Api/Controllers/StartupController.cs
+++ b/Jellyfin.Api/Controllers/StartupController.cs
@@ -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);
diff --git a/MediaBrowser.Controller/Entities/DayOfWeekHelper.cs b/Jellyfin.Data/DayOfWeekHelper.cs
index 8a79e0783..33410b732 100644
--- a/MediaBrowser.Controller/Entities/DayOfWeekHelper.cs
+++ b/Jellyfin.Data/DayOfWeekHelper.cs
@@ -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) ||
diff --git a/Jellyfin.Data/Entities/AccessSchedule.cs b/Jellyfin.Data/Entities/AccessSchedule.cs
new file mode 100644
index 000000000..7966cdb50
--- /dev/null
+++ b/Jellyfin.Data/Entities/AccessSchedule.cs
@@ -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; }
+ }
+}
diff --git a/Jellyfin.Data/Entities/ImageInfo.cs b/Jellyfin.Data/Entities/ImageInfo.cs
new file mode 100644
index 000000000..336c13b36
--- /dev/null
+++ b/Jellyfin.Data/Entities/ImageInfo.cs
@@ -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; }
+ }
+}
diff --git a/Jellyfin.Data/Entities/User.cs b/Jellyfin.Data/Entities/User.cs
index a81d5215b..17913959e 100644
--- a/Jellyfin.Data/Entities/User.cs
+++ b/Jellyfin.Data/Entities/User.cs
@@ -2,11 +2,20 @@ using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
+using System.Globalization;
+using System.Linq;
+using System.Text.Json.Serialization;
+using Jellyfin.Data.Enums;
namespace Jellyfin.Data.Entities
{
public partial class User
{
+ /// <summary>
+ /// The values being delimited here are Guids, so commas work as they do not appear in Guids.
+ /// </summary>
+ private const char Delimiter = ',';
+
partial void Init();
/// <summary>
@@ -18,69 +27,90 @@ namespace Jellyfin.Data.Entities
Permissions = new HashSet<Permission>();
ProviderMappings = new HashSet<ProviderMapping>();
Preferences = new HashSet<Preference>();
+ AccessSchedules = new HashSet<AccessSchedule>();
Init();
}
/// <summary>
- /// Replaces default constructor, since it's protected. Caller assumes responsibility for setting all required values before saving.
- /// </summary>
- public static User CreateUserUnsafe()
- {
- return new User();
- }
-
- /// <summary>
/// Public constructor with required data
/// </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)
+ /// <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)
{
- 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(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;
- 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>();
+ 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;
Init();
}
/// <summary>
+ /// Replaces default constructor, since it's protected. Caller assumes responsibility for setting all required values before saving.
+ /// </summary>
+ public static User CreateUserUnsafe()
+ {
+ 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);
}
/*************************************************************************
@@ -92,8 +122,8 @@ namespace Jellyfin.Data.Entities
/// </summary>
[Key]
[Required]
- [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
- public int Id { get; protected set; }
+ [JsonIgnore]
+ public Guid Id { get; set; }
/// <summary>
/// Required, Max length = 255
@@ -101,6 +131,7 @@ namespace Jellyfin.Data.Entities
[Required]
[MaxLength(255)]
[StringLength(255)]
+ [JsonPropertyName("Name")]
public string Username { get; set; }
/// <summary>
@@ -111,15 +142,21 @@ namespace Jellyfin.Data.Entities
public string Password { get; set; }
/// <summary>
+ /// Max length = 65535.
+ /// </summary>
+ [MaxLength(65535)]
+ [StringLength(65535)]
+ public string EasyPassword { get; set; }
+
+ /// <summary>
/// Required
/// </summary>
[Required]
public bool MustUpdatePassword { get; set; }
/// <summary>
- /// Required, Max length = 255
+ /// Max length = 255.
/// </summary>
- [Required]
[MaxLength(255)]
[StringLength(255)]
public string AudioLanguagePreference { get; set; }
@@ -132,12 +169,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
@@ -145,36 +180,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 int? LoginAttemptsBeforeLockout { get; set; }
+ public DateTime LastActivityDate { get; set; }
- /// <summary>
- /// Max length = 65535
- /// </summary>
- [MaxLength(65535)]
- [StringLength(65535)]
- public string MyMediaExcludes { get; set; }
+ public DateTime LastLoginDate { 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
@@ -187,21 +203,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; }
- public bool? DisplayMissingEpisodes { get; set; }
+ [Required]
+ public bool HidePlayedInLatest { get; set; }
+
+ [Required]
+ public bool RememberAudioSelections { get; set; }
+
+ [Required]
+ public bool RememberSubtitleSelections { get; set; }
+
+ [Required]
+ public bool EnableNextEpisodeAutoPlay { get; set; }
- public bool? DisplayCollectionsView { 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
@@ -219,17 +261,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;
+ }
}
}
-
diff --git a/MediaBrowser.Model/Configuration/DynamicDayOfWeek.cs b/Jellyfin.Data/Enums/DynamicDayOfWeek.cs
index 71b16cfba..a33cd9d1c 100644
--- a/MediaBrowser.Model/Configuration/DynamicDayOfWeek.cs
+++ b/Jellyfin.Data/Enums/DynamicDayOfWeek.cs
@@ -1,6 +1,6 @@
#pragma warning disable CS1591
-namespace MediaBrowser.Model.Configuration
+namespace Jellyfin.Data.Enums
{
public enum DynamicDayOfWeek
{
diff --git a/Jellyfin.Data/Enums/PermissionKind.cs b/Jellyfin.Data/Enums/PermissionKind.cs
index 1506471e8..df18261e6 100644
--- a/Jellyfin.Data/Enums/PermissionKind.cs
+++ b/Jellyfin.Data/Enums/PermissionKind.cs
@@ -5,8 +5,7 @@ namespace Jellyfin.Data.Enums
IsAdministrator,
IsHidden,
IsDisabled,
- BlockUnrateditems,
- EnbleSharedDeviceControl,
+ EnableSharedDeviceControl,
EnableRemoteAccess,
EnableLiveTvManagement,
EnableLiveTvAccess,
@@ -21,6 +20,8 @@ namespace Jellyfin.Data.Enums
EnableAllChannels,
EnableAllFolders,
EnablePublicSharing,
- AccessSchedules
+ EnableRemoteControlOfOtherUsers,
+ EnablePlaybackRemuxing,
+ ForceRemoteSourceTranscoding
}
}
diff --git a/Jellyfin.Data/Enums/PreferenceKind.cs b/Jellyfin.Data/Enums/PreferenceKind.cs
index cd2cb791a..ea1221e1a 100644
--- a/Jellyfin.Data/Enums/PreferenceKind.cs
+++ b/Jellyfin.Data/Enums/PreferenceKind.cs
@@ -2,12 +2,17 @@ namespace Jellyfin.Data.Enums
{
public enum PreferenceKind
{
- MaxParentalRating,
BlockedTags,
- RemoteClientBitrateLimit,
+ BlockedChannels,
+ BlockedMediaFolders,
EnabledDevices,
EnabledChannels,
EnabledFolders,
- EnableContentDeletionFromFolders
+ EnableContentDeletionFromFolders,
+ LatestItemExcludes,
+ MyMediaExcludes,
+ GroupedFolders,
+ BlockUnratedItems,
+ OrderedViews
}
}
diff --git a/MediaBrowser.Model/Configuration/SubtitlePlaybackMode.cs b/Jellyfin.Data/Enums/SubtitlePlaybackMode.cs
index f0aa2b98c..c8fc21159 100644
--- a/MediaBrowser.Model/Configuration/SubtitlePlaybackMode.cs
+++ b/Jellyfin.Data/Enums/SubtitlePlaybackMode.cs
@@ -1,6 +1,6 @@
-#pragma warning disable CS1591
+#pragma warning disable CS1591
-namespace MediaBrowser.Model.Configuration
+namespace Jellyfin.Data.Enums
{
public enum SubtitlePlaybackMode
{
diff --git a/MediaBrowser.Model/Configuration/UnratedItem.cs b/Jellyfin.Data/Enums/UnratedItem.cs
index e1d1a363d..5259e7739 100644
--- a/MediaBrowser.Model/Configuration/UnratedItem.cs
+++ b/Jellyfin.Data/Enums/UnratedItem.cs
@@ -1,6 +1,6 @@
#pragma warning disable CS1591
-namespace MediaBrowser.Model.Configuration
+namespace Jellyfin.Data.Enums
{
public enum UnratedItem
{
diff --git a/Jellyfin.Server.Implementations/Jellyfin.Server.Implementations.csproj b/Jellyfin.Server.Implementations/Jellyfin.Server.Implementations.csproj
index 149ca5020..09310f98c 100644
--- a/Jellyfin.Server.Implementations/Jellyfin.Server.Implementations.csproj
+++ b/Jellyfin.Server.Implementations/Jellyfin.Server.Implementations.csproj
@@ -23,6 +23,8 @@
<Compile Include="..\SharedVersion.cs" />
<Compile Remove="Migrations\20200430214405_InitialSchema.cs" />
<Compile Remove="Migrations\20200430214405_InitialSchema.Designer.cs" />
+ <Compile Remove="Migrations\20200504195424_UserSchema.cs" />
+ <Compile Remove="Migrations\20200504195424_UserSchema.Designer.cs" />
</ItemGroup>
<ItemGroup>
diff --git a/Jellyfin.Server.Implementations/JellyfinDb.cs b/Jellyfin.Server.Implementations/JellyfinDb.cs
index ec09a619f..9ac97a131 100644
--- a/Jellyfin.Server.Implementations/JellyfinDb.cs
+++ b/Jellyfin.Server.Implementations/JellyfinDb.cs
@@ -16,6 +16,7 @@ namespace Jellyfin.Server.Implementations
public partial class JellyfinDb : DbContext
{
public virtual DbSet<ActivityLog> ActivityLogs { 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; }
@@ -62,8 +63,7 @@ namespace Jellyfin.Server.Implementations
public virtual DbSet<Series> Series { get; set; }
public virtual DbSet<SeriesMetadata> SeriesMetadata { get; set; }
public virtual DbSet<Track> Tracks { get; set; }
- public virtual DbSet<TrackMetadata> TrackMetadata { get; set; }
- public virtual DbSet<User> Users { get; set; } */
+ public virtual DbSet<TrackMetadata> TrackMetadata { get; set; }*/
/// <summary>
/// Gets or sets the default connection string.
diff --git a/Jellyfin.Server.Implementations/Migrations/20200504195702_UserSchema.Designer.cs b/Jellyfin.Server.Implementations/Migrations/20200504195702_UserSchema.Designer.cs
new file mode 100644
index 000000000..8313c6a3b
--- /dev/null
+++ b/Jellyfin.Server.Implementations/Migrations/20200504195702_UserSchema.Designer.cs
@@ -0,0 +1,324 @@
+#pragma warning disable CS1591
+#pragma warning disable SA1601
+
+// <auto-generated />
+using System;
+using Jellyfin.Server.Implementations;
+using Microsoft.EntityFrameworkCore;
+using Microsoft.EntityFrameworkCore.Infrastructure;
+using Microsoft.EntityFrameworkCore.Migrations;
+using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
+
+namespace Jellyfin.Server.Implementations.Migrations
+{
+ [DbContext(typeof(JellyfinDb))]
+ [Migration("20200504195702_UserSchema")]
+ partial class UserSchema
+ {
+ protected override void BuildTargetModel(ModelBuilder modelBuilder)
+ {
+#pragma warning disable 612, 618
+ modelBuilder
+ .HasDefaultSchema("jellyfin")
+ .HasAnnotation("ProductVersion", "3.1.3");
+
+ modelBuilder.Entity("Jellyfin.Data.Entities.ActivityLog", b =>
+ {
+ b.Property<int>("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("INTEGER");
+
+ b.Property<DateTime>("DateCreated")
+ .HasColumnType("TEXT");
+
+ b.Property<string>("ItemId")
+ .HasColumnType("TEXT")
+ .HasMaxLength(256);
+
+ b.Property<int>("LogSeverity")
+ .HasColumnType("INTEGER");
+
+ b.Property<string>("Name")
+ .IsRequired()
+ .HasColumnType("TEXT")
+ .HasMaxLength(512);
+
+ b.Property<string>("Overview")
+ .HasColumnType("TEXT")
+ .HasMaxLength(512);
+
+ b.Property<uint>("RowVersion")
+ .IsConcurrencyToken()
+ .HasColumnType("INTEGER");
+
+ b.Property<string>("ShortOverview")
+ .HasColumnType("TEXT")
+ .HasMaxLength(512);
+
+ b.Property<string>("Type")
+ .IsRequired()
+ .HasColumnType("TEXT")
+ .HasMaxLength(256);
+
+ b.Property<Guid>("UserId")
+ .HasColumnType("TEXT");
+
+ b.HasKey("Id");
+
+ b.ToTable("ActivityLog");
+ });
+
+ modelBuilder.Entity("Jellyfin.Data.Entities.Group", b =>
+ {
+ b.Property<int>("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("INTEGER");
+
+ b.Property<int?>("Group_Groups_Id")
+ .HasColumnType("INTEGER");
+
+ b.Property<string>("Name")
+ .IsRequired()
+ .HasColumnType("TEXT")
+ .HasMaxLength(255);
+
+ b.Property<uint>("RowVersion")
+ .IsConcurrencyToken()
+ .HasColumnType("INTEGER");
+
+ b.HasKey("Id");
+
+ b.HasIndex("Group_Groups_Id");
+
+ b.ToTable("Group");
+ });
+
+ modelBuilder.Entity("Jellyfin.Data.Entities.Permission", b =>
+ {
+ b.Property<int>("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("INTEGER");
+
+ b.Property<int>("Kind")
+ .HasColumnType("INTEGER");
+
+ b.Property<int?>("Permission_GroupPermissions_Id")
+ .HasColumnType("INTEGER");
+
+ b.Property<int?>("Permission_Permissions_Id")
+ .HasColumnType("INTEGER");
+
+ b.Property<uint>("RowVersion")
+ .IsConcurrencyToken()
+ .HasColumnType("INTEGER");
+
+ b.Property<bool>("Value")
+ .HasColumnType("INTEGER");
+
+ b.HasKey("Id");
+
+ b.HasIndex("Permission_GroupPermissions_Id");
+
+ b.HasIndex("Permission_Permissions_Id");
+
+ b.ToTable("Permission");
+ });
+
+ modelBuilder.Entity("Jellyfin.Data.Entities.Preference", b =>
+ {
+ b.Property<int>("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("INTEGER");
+
+ b.Property<int>("Kind")
+ .HasColumnType("INTEGER");
+
+ b.Property<int?>("Preference_Preferences_Id")
+ .HasColumnType("INTEGER");
+
+ b.Property<uint>("RowVersion")
+ .IsConcurrencyToken()
+ .HasColumnType("INTEGER");
+
+ b.Property<string>("Value")
+ .IsRequired()
+ .HasColumnType("TEXT")
+ .HasMaxLength(65535);
+
+ b.HasKey("Id");
+
+ b.HasIndex("Preference_Preferences_Id");
+
+ b.ToTable("Preference");
+ });
+
+ modelBuilder.Entity("Jellyfin.Data.Entities.ProviderMapping", b =>
+ {
+ b.Property<int>("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("INTEGER");
+
+ b.Property<string>("ProviderData")
+ .IsRequired()
+ .HasColumnType("TEXT")
+ .HasMaxLength(65535);
+
+ b.Property<int?>("ProviderMapping_ProviderMappings_Id")
+ .HasColumnType("INTEGER");
+
+ b.Property<string>("ProviderName")
+ .IsRequired()
+ .HasColumnType("TEXT")
+ .HasMaxLength(255);
+
+ b.Property<string>("ProviderSecrets")
+ .IsRequired()
+ .HasColumnType("TEXT")
+ .HasMaxLength(65535);
+
+ b.Property<uint>("RowVersion")
+ .IsConcurrencyToken()
+ .HasColumnType("INTEGER");
+
+ b.HasKey("Id");
+
+ b.HasIndex("ProviderMapping_ProviderMappings_Id");
+
+ b.ToTable("ProviderMapping");
+ });
+
+ modelBuilder.Entity("Jellyfin.Data.Entities.User", b =>
+ {
+ b.Property<int>("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("INTEGER");
+
+ b.Property<string>("AudioLanguagePreference")
+ .IsRequired()
+ .HasColumnType("TEXT")
+ .HasMaxLength(255);
+
+ b.Property<string>("AuthenticationProviderId")
+ .IsRequired()
+ .HasColumnType("TEXT")
+ .HasMaxLength(255);
+
+ b.Property<bool?>("DisplayCollectionsView")
+ .HasColumnType("INTEGER");
+
+ b.Property<bool?>("DisplayMissingEpisodes")
+ .HasColumnType("INTEGER");
+
+ b.Property<bool?>("EnableNextEpisodeAutoPlay")
+ .HasColumnType("INTEGER");
+
+ b.Property<bool?>("EnableUserPreferenceAccess")
+ .HasColumnType("INTEGER");
+
+ b.Property<string>("GroupedFolders")
+ .HasColumnType("TEXT")
+ .HasMaxLength(65535);
+
+ b.Property<bool?>("HidePlayedInLatest")
+ .HasColumnType("INTEGER");
+
+ b.Property<int>("InvalidLoginAttemptCount")
+ .HasColumnType("INTEGER");
+
+ b.Property<string>("LatestItemExcludes")
+ .HasColumnType("TEXT")
+ .HasMaxLength(65535);
+
+ b.Property<int?>("LoginAttemptsBeforeLockout")
+ .HasColumnType("INTEGER");
+
+ b.Property<bool>("MustUpdatePassword")
+ .HasColumnType("INTEGER");
+
+ b.Property<string>("MyMediaExcludes")
+ .HasColumnType("TEXT")
+ .HasMaxLength(65535);
+
+ b.Property<string>("OrderedViews")
+ .HasColumnType("TEXT")
+ .HasMaxLength(65535);
+
+ b.Property<string>("Password")
+ .HasColumnType("TEXT")
+ .HasMaxLength(65535);
+
+ b.Property<bool>("PlayDefaultAudioTrack")
+ .HasColumnType("INTEGER");
+
+ b.Property<bool?>("RememberAudioSelections")
+ .HasColumnType("INTEGER");
+
+ b.Property<bool?>("RememberSubtitleSelections")
+ .HasColumnType("INTEGER");
+
+ b.Property<uint>("RowVersion")
+ .IsConcurrencyToken()
+ .HasColumnType("INTEGER");
+
+ b.Property<string>("SubtitleLanguagePrefernce")
+ .HasColumnType("TEXT")
+ .HasMaxLength(255);
+
+ b.Property<string>("SubtitleMode")
+ .IsRequired()
+ .HasColumnType("TEXT")
+ .HasMaxLength(255);
+
+ b.Property<string>("Username")
+ .IsRequired()
+ .HasColumnType("TEXT")
+ .HasMaxLength(255);
+
+ b.HasKey("Id");
+
+ b.ToTable("User");
+ });
+
+ modelBuilder.Entity("Jellyfin.Data.Entities.Group", b =>
+ {
+ b.HasOne("Jellyfin.Data.Entities.User", null)
+ .WithMany("Groups")
+ .HasForeignKey("Group_Groups_Id");
+ });
+
+ modelBuilder.Entity("Jellyfin.Data.Entities.Permission", b =>
+ {
+ b.HasOne("Jellyfin.Data.Entities.Group", null)
+ .WithMany("GroupPermissions")
+ .HasForeignKey("Permission_GroupPermissions_Id");
+
+ b.HasOne("Jellyfin.Data.Entities.User", null)
+ .WithMany("Permissions")
+ .HasForeignKey("Permission_Permissions_Id");
+ });
+
+ modelBuilder.Entity("Jellyfin.Data.Entities.Preference", b =>
+ {
+ b.HasOne("Jellyfin.Data.Entities.Group", null)
+ .WithMany("Preferences")
+ .HasForeignKey("Preference_Preferences_Id");
+
+ b.HasOne("Jellyfin.Data.Entities.User", null)
+ .WithMany("Preferences")
+ .HasForeignKey("Preference_Preferences_Id");
+ });
+
+ modelBuilder.Entity("Jellyfin.Data.Entities.ProviderMapping", b =>
+ {
+ b.HasOne("Jellyfin.Data.Entities.Group", null)
+ .WithMany("ProviderMappings")
+ .HasForeignKey("ProviderMapping_ProviderMappings_Id");
+
+ b.HasOne("Jellyfin.Data.Entities.User", null)
+ .WithMany("ProviderMappings")
+ .HasForeignKey("ProviderMapping_ProviderMappings_Id");
+ });
+#pragma warning restore 612, 618
+ }
+ }
+}
diff --git a/Jellyfin.Server.Implementations/Migrations/20200504195702_UserSchema.cs b/Jellyfin.Server.Implementations/Migrations/20200504195702_UserSchema.cs
new file mode 100644
index 000000000..f24ccccbf
--- /dev/null
+++ b/Jellyfin.Server.Implementations/Migrations/20200504195702_UserSchema.cs
@@ -0,0 +1,219 @@
+#pragma warning disable CS1591
+#pragma warning disable SA1601
+
+using Microsoft.EntityFrameworkCore.Migrations;
+
+namespace Jellyfin.Server.Implementations.Migrations
+{
+ public partial class UserSchema : Migration
+ {
+ protected override void Up(MigrationBuilder migrationBuilder)
+ {
+ migrationBuilder.CreateTable(
+ name: "User",
+ schema: "jellyfin",
+ columns: table => new
+ {
+ Id = table.Column<int>(nullable: false)
+ .Annotation("Sqlite:Autoincrement", true),
+ Username = table.Column<string>(maxLength: 255, nullable: false),
+ Password = table.Column<string>(maxLength: 65535, nullable: true),
+ MustUpdatePassword = table.Column<bool>(nullable: false),
+ AudioLanguagePreference = table.Column<string>(maxLength: 255, nullable: false),
+ AuthenticationProviderId = table.Column<string>(maxLength: 255, nullable: false),
+ GroupedFolders = table.Column<string>(maxLength: 65535, nullable: true),
+ InvalidLoginAttemptCount = table.Column<int>(nullable: false),
+ LatestItemExcludes = table.Column<string>(maxLength: 65535, nullable: true),
+ LoginAttemptsBeforeLockout = table.Column<int>(nullable: true),
+ MyMediaExcludes = table.Column<string>(maxLength: 65535, nullable: true),
+ OrderedViews = table.Column<string>(maxLength: 65535, nullable: true),
+ SubtitleMode = table.Column<string>(maxLength: 255, nullable: false),
+ PlayDefaultAudioTrack = table.Column<bool>(nullable: false),
+ SubtitleLanguagePrefernce = table.Column<string>(maxLength: 255, nullable: true),
+ DisplayMissingEpisodes = table.Column<bool>(nullable: true),
+ DisplayCollectionsView = table.Column<bool>(nullable: true),
+ HidePlayedInLatest = table.Column<bool>(nullable: true),
+ RememberAudioSelections = table.Column<bool>(nullable: true),
+ RememberSubtitleSelections = table.Column<bool>(nullable: true),
+ EnableNextEpisodeAutoPlay = table.Column<bool>(nullable: true),
+ EnableUserPreferenceAccess = table.Column<bool>(nullable: true),
+ RowVersion = table.Column<uint>(nullable: false)
+ },
+ constraints: table =>
+ {
+ table.PrimaryKey("PK_User", x => x.Id);
+ });
+
+ migrationBuilder.CreateTable(
+ name: "Group",
+ schema: "jellyfin",
+ columns: table => new
+ {
+ Id = table.Column<int>(nullable: false)
+ .Annotation("Sqlite:Autoincrement", true),
+ Name = table.Column<string>(maxLength: 255, nullable: false),
+ RowVersion = table.Column<uint>(nullable: false),
+ Group_Groups_Id = table.Column<int>(nullable: true)
+ },
+ constraints: table =>
+ {
+ table.PrimaryKey("PK_Group", x => x.Id);
+ table.ForeignKey(
+ name: "FK_Group_User_Group_Groups_Id",
+ column: x => x.Group_Groups_Id,
+ principalSchema: "jellyfin",
+ principalTable: "User",
+ principalColumn: "Id",
+ onDelete: ReferentialAction.Restrict);
+ });
+
+ migrationBuilder.CreateTable(
+ name: "Permission",
+ schema: "jellyfin",
+ columns: table => new
+ {
+ Id = table.Column<int>(nullable: false)
+ .Annotation("Sqlite:Autoincrement", true),
+ Kind = table.Column<int>(nullable: false),
+ Value = table.Column<bool>(nullable: false),
+ RowVersion = table.Column<uint>(nullable: false),
+ Permission_GroupPermissions_Id = table.Column<int>(nullable: true),
+ Permission_Permissions_Id = table.Column<int>(nullable: true)
+ },
+ constraints: table =>
+ {
+ table.PrimaryKey("PK_Permission", x => x.Id);
+ table.ForeignKey(
+ name: "FK_Permission_Group_Permission_GroupPermissions_Id",
+ column: x => x.Permission_GroupPermissions_Id,
+ principalSchema: "jellyfin",
+ principalTable: "Group",
+ principalColumn: "Id",
+ onDelete: ReferentialAction.Restrict);
+ table.ForeignKey(
+ name: "FK_Permission_User_Permission_Permissions_Id",
+ column: x => x.Permission_Permissions_Id,
+ principalSchema: "jellyfin",
+ principalTable: "User",
+ principalColumn: "Id",
+ onDelete: ReferentialAction.Restrict);
+ });
+
+ migrationBuilder.CreateTable(
+ name: "Preference",
+ schema: "jellyfin",
+ columns: table => new
+ {
+ Id = table.Column<int>(nullable: false)
+ .Annotation("Sqlite:Autoincrement", true),
+ Kind = table.Column<int>(nullable: false),
+ Value = table.Column<string>(maxLength: 65535, nullable: false),
+ RowVersion = table.Column<uint>(nullable: false),
+ Preference_Preferences_Id = table.Column<int>(nullable: true)
+ },
+ constraints: table =>
+ {
+ table.PrimaryKey("PK_Preference", x => x.Id);
+ table.ForeignKey(
+ name: "FK_Preference_Group_Preference_Preferences_Id",
+ column: x => x.Preference_Preferences_Id,
+ principalSchema: "jellyfin",
+ principalTable: "Group",
+ principalColumn: "Id",
+ onDelete: ReferentialAction.Restrict);
+ table.ForeignKey(
+ name: "FK_Preference_User_Preference_Preferences_Id",
+ column: x => x.Preference_Preferences_Id,
+ principalSchema: "jellyfin",
+ principalTable: "User",
+ principalColumn: "Id",
+ onDelete: ReferentialAction.Restrict);
+ });
+
+ migrationBuilder.CreateTable(
+ name: "ProviderMapping",
+ schema: "jellyfin",
+ columns: table => new
+ {
+ Id = table.Column<int>(nullable: false)
+ .Annotation("Sqlite:Autoincrement", true),
+ ProviderName = table.Column<string>(maxLength: 255, nullable: false),
+ ProviderSecrets = table.Column<string>(maxLength: 65535, nullable: false),
+ ProviderData = table.Column<string>(maxLength: 65535, nullable: false),
+ RowVersion = table.Column<uint>(nullable: false),
+ ProviderMapping_ProviderMappings_Id = table.Column<int>(nullable: true)
+ },
+ constraints: table =>
+ {
+ table.PrimaryKey("PK_ProviderMapping", x => x.Id);
+ table.ForeignKey(
+ name: "FK_ProviderMapping_Group_ProviderMapping_ProviderMappings_Id",
+ column: x => x.ProviderMapping_ProviderMappings_Id,
+ principalSchema: "jellyfin",
+ principalTable: "Group",
+ principalColumn: "Id",
+ onDelete: ReferentialAction.Restrict);
+ table.ForeignKey(
+ name: "FK_ProviderMapping_User_ProviderMapping_ProviderMappings_Id",
+ column: x => x.ProviderMapping_ProviderMappings_Id,
+ principalSchema: "jellyfin",
+ principalTable: "User",
+ principalColumn: "Id",
+ onDelete: ReferentialAction.Restrict);
+ });
+
+ migrationBuilder.CreateIndex(
+ name: "IX_Group_Group_Groups_Id",
+ schema: "jellyfin",
+ table: "Group",
+ column: "Group_Groups_Id");
+
+ migrationBuilder.CreateIndex(
+ name: "IX_Permission_Permission_GroupPermissions_Id",
+ schema: "jellyfin",
+ table: "Permission",
+ column: "Permission_GroupPermissions_Id");
+
+ migrationBuilder.CreateIndex(
+ name: "IX_Permission_Permission_Permissions_Id",
+ schema: "jellyfin",
+ table: "Permission",
+ column: "Permission_Permissions_Id");
+
+ migrationBuilder.CreateIndex(
+ name: "IX_Preference_Preference_Preferences_Id",
+ schema: "jellyfin",
+ table: "Preference",
+ column: "Preference_Preferences_Id");
+
+ migrationBuilder.CreateIndex(
+ name: "IX_ProviderMapping_ProviderMapping_ProviderMappings_Id",
+ schema: "jellyfin",
+ table: "ProviderMapping",
+ column: "ProviderMapping_ProviderMappings_Id");
+ }
+
+ protected override void Down(MigrationBuilder migrationBuilder)
+ {
+ migrationBuilder.DropTable(
+ name: "Permission",
+ schema: "jellyfin");
+
+ migrationBuilder.DropTable(
+ name: "Preference",
+ schema: "jellyfin");
+
+ migrationBuilder.DropTable(
+ name: "ProviderMapping",
+ schema: "jellyfin");
+
+ migrationBuilder.DropTable(
+ name: "Group",
+ schema: "jellyfin");
+
+ migrationBuilder.DropTable(
+ name: "User",
+ schema: "jellyfin");
+ }
+ }
+}
diff --git a/Jellyfin.Server.Implementations/Migrations/JellyfinDbModelSnapshot.cs b/Jellyfin.Server.Implementations/Migrations/JellyfinDbModelSnapshot.cs
index 1e7ffd235..0fb0ba803 100644
--- a/Jellyfin.Server.Implementations/Migrations/JellyfinDbModelSnapshot.cs
+++ b/Jellyfin.Server.Implementations/Migrations/JellyfinDbModelSnapshot.cs
@@ -60,6 +60,257 @@ namespace Jellyfin.Server.Implementations.Migrations
b.ToTable("ActivityLogs");
});
+
+ modelBuilder.Entity("Jellyfin.Data.Entities.Group", b =>
+ {
+ b.Property<int>("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("INTEGER");
+
+ b.Property<int?>("Group_Groups_Id")
+ .HasColumnType("INTEGER");
+
+ b.Property<string>("Name")
+ .IsRequired()
+ .HasColumnType("TEXT")
+ .HasMaxLength(255);
+
+ b.Property<uint>("RowVersion")
+ .IsConcurrencyToken()
+ .HasColumnType("INTEGER");
+
+ b.HasKey("Id");
+
+ b.HasIndex("Group_Groups_Id");
+
+ b.ToTable("Group");
+ });
+
+ modelBuilder.Entity("Jellyfin.Data.Entities.Permission", b =>
+ {
+ b.Property<int>("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("INTEGER");
+
+ b.Property<int>("Kind")
+ .HasColumnType("INTEGER");
+
+ b.Property<int?>("Permission_GroupPermissions_Id")
+ .HasColumnType("INTEGER");
+
+ b.Property<int?>("Permission_Permissions_Id")
+ .HasColumnType("INTEGER");
+
+ b.Property<uint>("RowVersion")
+ .IsConcurrencyToken()
+ .HasColumnType("INTEGER");
+
+ b.Property<bool>("Value")
+ .HasColumnType("INTEGER");
+
+ b.HasKey("Id");
+
+ b.HasIndex("Permission_GroupPermissions_Id");
+
+ b.HasIndex("Permission_Permissions_Id");
+
+ b.ToTable("Permission");
+ });
+
+ modelBuilder.Entity("Jellyfin.Data.Entities.Preference", b =>
+ {
+ b.Property<int>("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("INTEGER");
+
+ b.Property<int>("Kind")
+ .HasColumnType("INTEGER");
+
+ b.Property<int?>("Preference_Preferences_Id")
+ .HasColumnType("INTEGER");
+
+ b.Property<uint>("RowVersion")
+ .IsConcurrencyToken()
+ .HasColumnType("INTEGER");
+
+ b.Property<string>("Value")
+ .IsRequired()
+ .HasColumnType("TEXT")
+ .HasMaxLength(65535);
+
+ b.HasKey("Id");
+
+ b.HasIndex("Preference_Preferences_Id");
+
+ b.ToTable("Preference");
+ });
+
+ modelBuilder.Entity("Jellyfin.Data.Entities.ProviderMapping", b =>
+ {
+ b.Property<int>("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("INTEGER");
+
+ b.Property<string>("ProviderData")
+ .IsRequired()
+ .HasColumnType("TEXT")
+ .HasMaxLength(65535);
+
+ b.Property<int?>("ProviderMapping_ProviderMappings_Id")
+ .HasColumnType("INTEGER");
+
+ b.Property<string>("ProviderName")
+ .IsRequired()
+ .HasColumnType("TEXT")
+ .HasMaxLength(255);
+
+ b.Property<string>("ProviderSecrets")
+ .IsRequired()
+ .HasColumnType("TEXT")
+ .HasMaxLength(65535);
+
+ b.Property<uint>("RowVersion")
+ .IsConcurrencyToken()
+ .HasColumnType("INTEGER");
+
+ b.HasKey("Id");
+
+ b.HasIndex("ProviderMapping_ProviderMappings_Id");
+
+ b.ToTable("ProviderMapping");
+ });
+
+ modelBuilder.Entity("Jellyfin.Data.Entities.User", b =>
+ {
+ b.Property<int>("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("INTEGER");
+
+ b.Property<string>("AudioLanguagePreference")
+ .IsRequired()
+ .HasColumnType("TEXT")
+ .HasMaxLength(255);
+
+ b.Property<string>("AuthenticationProviderId")
+ .IsRequired()
+ .HasColumnType("TEXT")
+ .HasMaxLength(255);
+
+ b.Property<bool?>("DisplayCollectionsView")
+ .HasColumnType("INTEGER");
+
+ b.Property<bool?>("DisplayMissingEpisodes")
+ .HasColumnType("INTEGER");
+
+ b.Property<bool?>("EnableNextEpisodeAutoPlay")
+ .HasColumnType("INTEGER");
+
+ b.Property<bool?>("EnableUserPreferenceAccess")
+ .HasColumnType("INTEGER");
+
+ b.Property<string>("GroupedFolders")
+ .HasColumnType("TEXT")
+ .HasMaxLength(65535);
+
+ b.Property<bool?>("HidePlayedInLatest")
+ .HasColumnType("INTEGER");
+
+ b.Property<int>("InvalidLoginAttemptCount")
+ .HasColumnType("INTEGER");
+
+ b.Property<string>("LatestItemExcludes")
+ .HasColumnType("TEXT")
+ .HasMaxLength(65535);
+
+ b.Property<int?>("LoginAttemptsBeforeLockout")
+ .HasColumnType("INTEGER");
+
+ b.Property<bool>("MustUpdatePassword")
+ .HasColumnType("INTEGER");
+
+ b.Property<string>("MyMediaExcludes")
+ .HasColumnType("TEXT")
+ .HasMaxLength(65535);
+
+ b.Property<string>("OrderedViews")
+ .HasColumnType("TEXT")
+ .HasMaxLength(65535);
+
+ b.Property<string>("Password")
+ .HasColumnType("TEXT")
+ .HasMaxLength(65535);
+
+ b.Property<bool>("PlayDefaultAudioTrack")
+ .HasColumnType("INTEGER");
+
+ b.Property<bool?>("RememberAudioSelections")
+ .HasColumnType("INTEGER");
+
+ b.Property<bool?>("RememberSubtitleSelections")
+ .HasColumnType("INTEGER");
+
+ b.Property<uint>("RowVersion")
+ .IsConcurrencyToken()
+ .HasColumnType("INTEGER");
+
+ b.Property<string>("SubtitleLanguagePrefernce")
+ .HasColumnType("TEXT")
+ .HasMaxLength(255);
+
+ b.Property<string>("SubtitleMode")
+ .IsRequired()
+ .HasColumnType("TEXT")
+ .HasMaxLength(255);
+
+ b.Property<string>("Username")
+ .IsRequired()
+ .HasColumnType("TEXT")
+ .HasMaxLength(255);
+
+ b.HasKey("Id");
+
+ b.ToTable("User");
+ });
+
+ modelBuilder.Entity("Jellyfin.Data.Entities.Group", b =>
+ {
+ b.HasOne("Jellyfin.Data.Entities.User", null)
+ .WithMany("Groups")
+ .HasForeignKey("Group_Groups_Id");
+ });
+
+ modelBuilder.Entity("Jellyfin.Data.Entities.Permission", b =>
+ {
+ b.HasOne("Jellyfin.Data.Entities.Group", null)
+ .WithMany("GroupPermissions")
+ .HasForeignKey("Permission_GroupPermissions_Id");
+
+ b.HasOne("Jellyfin.Data.Entities.User", null)
+ .WithMany("Permissions")
+ .HasForeignKey("Permission_Permissions_Id");
+ });
+
+ modelBuilder.Entity("Jellyfin.Data.Entities.Preference", b =>
+ {
+ b.HasOne("Jellyfin.Data.Entities.Group", null)
+ .WithMany("Preferences")
+ .HasForeignKey("Preference_Preferences_Id");
+
+ b.HasOne("Jellyfin.Data.Entities.User", null)
+ .WithMany("Preferences")
+ .HasForeignKey("Preference_Preferences_Id");
+ });
+
+ modelBuilder.Entity("Jellyfin.Data.Entities.ProviderMapping", b =>
+ {
+ b.HasOne("Jellyfin.Data.Entities.Group", null)
+ .WithMany("ProviderMappings")
+ .HasForeignKey("ProviderMapping_ProviderMappings_Id");
+
+ b.HasOne("Jellyfin.Data.Entities.User", null)
+ .WithMany("ProviderMappings")
+ .HasForeignKey("ProviderMapping_ProviderMappings_Id");
+ });
#pragma warning restore 612, 618
}
}
diff --git a/Emby.Server.Implementations/Library/DefaultAuthenticationProvider.cs b/Jellyfin.Server.Implementations/User/DefaultAuthenticationProvider.cs
index 52c8facc3..024500bf8 100644
--- a/Emby.Server.Implementations/Library/DefaultAuthenticationProvider.cs
+++ b/Jellyfin.Server.Implementations/User/DefaultAuthenticationProvider.cs
@@ -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))
{
diff --git a/Emby.Server.Implementations/Library/DefaultPasswordResetProvider.cs b/Jellyfin.Server.Implementations/User/DefaultPasswordResetProvider.cs
index 6c6fbd86f..80ab3ce00 100644
--- a/Emby.Server.Implementations/Library/DefaultPasswordResetProvider.cs
+++ b/Jellyfin.Server.Implementations/User/DefaultPasswordResetProvider.cs
@@ -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
};
}
diff --git a/Jellyfin.Server.Implementations/User/DeviceAccessEntryPoint.cs b/Jellyfin.Server.Implementations/User/DeviceAccessEntryPoint.cs
new file mode 100644
index 000000000..d33034ab2
--- /dev/null
+++ b/Jellyfin.Server.Implementations/User/DeviceAccessEntryPoint.cs
@@ -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);
+ }
+ }
+ }
+ }
+}
diff --git a/Emby.Server.Implementations/Library/InvalidAuthProvider.cs b/Jellyfin.Server.Implementations/User/InvalidAuthProvider.cs
index dc61aacd7..a11ca128a 100644
--- a/Emby.Server.Implementations/Library/InvalidAuthProvider.cs
+++ b/Jellyfin.Server.Implementations/User/InvalidAuthProvider.cs
@@ -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;
}
diff --git a/Jellyfin.Server.Implementations/User/UserManager.cs b/Jellyfin.Server.Implementations/User/UserManager.cs
new file mode 100644
index 000000000..73905ff70
--- /dev/null
+++ b/Jellyfin.Server.Implementations/User/UserManager.cs
@@ -0,0 +1,771 @@
+#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 PublicUserDto GetPublicUserDto(Data.Entities.User user, string remoteEndPoint = null)
+ {
+ if (user == null)
+ {
+ throw new ArgumentNullException(nameof(user));
+ }
+
+ bool hasConfiguredPassword = GetAuthenticationProvider(user).HasPassword(user);
+ bool hasConfiguredEasyPassword = !string.IsNullOrEmpty(GetAuthenticationProvider(user).GetEasyPasswordHash(user));
+
+ bool hasPassword = user.EnableLocalPassword &&
+ !string.IsNullOrEmpty(remoteEndPoint) &&
+ _networkManager.IsInLocalNetwork(remoteEndPoint) ? hasConfiguredEasyPassword : hasConfiguredPassword;
+
+ return new PublicUserDto
+ {
+ Name = user.Username,
+ HasPassword = hasPassword,
+ HasConfiguredPassword = hasConfiguredPassword
+ };
+ }
+
+ 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;
+ }
+ }
+}
diff --git a/Jellyfin.Server/Jellyfin.Server.csproj b/Jellyfin.Server/Jellyfin.Server.csproj
index 9eec6ed4e..ea16c5573 100644
--- a/Jellyfin.Server/Jellyfin.Server.csproj
+++ b/Jellyfin.Server/Jellyfin.Server.csproj
@@ -41,6 +41,7 @@
<ItemGroup>
<PackageReference Include="CommandLineParser" Version="2.7.82" />
+ <PackageReference Include="Json.Net" Version="1.0.22" />
<PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="3.1.3" />
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="3.1.3" />
<PackageReference Include="prometheus-net" Version="3.5.0" />
diff --git a/Jellyfin.Server/Migrations/MigrationRunner.cs b/Jellyfin.Server/Migrations/MigrationRunner.cs
index 473f62737..c98263223 100644
--- a/Jellyfin.Server/Migrations/MigrationRunner.cs
+++ b/Jellyfin.Server/Migrations/MigrationRunner.cs
@@ -19,7 +19,8 @@ namespace Jellyfin.Server.Migrations
typeof(Routines.DisableTranscodingThrottling),
typeof(Routines.CreateUserLoggingConfigFile),
typeof(Routines.MigrateActivityLogDb),
- typeof(Routines.RemoveDuplicateExtras)
+ typeof(Routines.RemoveDuplicateExtras),
+ typeof(Routines.MigrateUserDb)
};
/// <summary>
diff --git a/Jellyfin.Server/Migrations/Routines/MigrateUserDb.cs b/Jellyfin.Server/Migrations/Routines/MigrateUserDb.cs
new file mode 100644
index 000000000..b19d7f7fc
--- /dev/null
+++ b/Jellyfin.Server/Migrations/Routines/MigrateUserDb.cs
@@ -0,0 +1,144 @@
+#pragma warning disable CS1591
+
+using System;
+using System.Collections.Generic;
+using System.IO;
+using Emby.Server.Implementations.Data;
+using Emby.Server.Implementations.Serialization;
+using Jellyfin.Data.Entities;
+using Jellyfin.Data.Enums;
+using Jellyfin.Server.Implementations;
+using MediaBrowser.Controller;
+using MediaBrowser.Model.Configuration;
+using MediaBrowser.Model.Users;
+using Microsoft.Extensions.Logging;
+using Newtonsoft.Json;
+using SQLitePCL.pretty;
+
+namespace Jellyfin.Server.Migrations.Routines
+{
+ public class MigrateUserDb : IMigrationRoutine
+ {
+ private readonly ILogger<MigrateUserDb> _logger;
+
+ private readonly IServerApplicationPaths _paths;
+
+ private readonly JellyfinDbProvider _provider;
+
+ private readonly MyXmlSerializer _xmlSerializer;
+
+ public MigrateUserDb(ILogger<MigrateUserDb> logger, IServerApplicationPaths paths, JellyfinDbProvider provider, MyXmlSerializer xmlSerializer)
+ {
+ _logger = logger;
+ _paths = paths;
+ _provider = provider;
+ _xmlSerializer = xmlSerializer;
+ }
+
+ public Guid Id => Guid.Parse("5C4B82A2-F053-4009-BD05-B6FCAD82F14C");
+
+ public string Name => "MigrateUserDb";
+
+ public void Perform()
+ {
+ var dataPath = _paths.DataPath;
+ _logger.LogInformation("Migrating the user database may take a while, do not stop Jellyfin.");
+
+ using (var connection = SQLite3.Open(Path.Combine(dataPath, "users.db"), ConnectionFlags.ReadOnly, null))
+ {
+ using var dbContext = _provider.CreateContext();
+
+ var queryResult = connection.Query("SELECT * FROM LocalUsersv2");
+
+ dbContext.RemoveRange(dbContext.Users);
+ dbContext.SaveChanges();
+
+ foreach (var entry in queryResult)
+ {
+ var json = JsonConvert.DeserializeObject<Dictionary<string, string>>(entry[2].ToString());
+ var userDataDir = Path.Combine(_paths.UserConfigurationDirectoryPath, json["Name"]);
+
+ var config = (UserConfiguration)_xmlSerializer.DeserializeFromFile(typeof(UserConfiguration), Path.Combine(userDataDir, "config.xml"));
+ var policy = (UserPolicy)_xmlSerializer.DeserializeFromFile(typeof(UserPolicy), Path.Combine(userDataDir, "policy.xml"));
+
+ var user = new User(
+ json["Name"],
+ false,
+ policy.AuthenticatioIsnProviderId,
+ policy.InvalidLoginAttemptCount,
+ config.SubtitleMode,
+ config.PlayDefaultAudioTrack)
+ {
+ Id = entry[1].ReadGuidFromBlob(),
+ InternalId = entry[0].ToInt64(),
+ MaxParentalAgeRating = policy.MaxParentalRating,
+ EnableUserPreferenceAccess = policy.EnableUserPreferenceAccess,
+ RemoteClientBitrateLimit = policy.RemoteClientBitrateLimit,
+ AuthenticationProviderId = policy.AuthenticatioIsnProviderId,
+ PasswordResetProviderId = policy.PasswordResetProviderId,
+ InvalidLoginAttemptCount = policy.InvalidLoginAttemptCount,
+ LoginAttemptsBeforeLockout = policy.LoginAttemptsBeforeLockout == -1 ? null : new int?(policy.LoginAttemptsBeforeLockout),
+ SubtitleMode = config.SubtitleMode,
+ HidePlayedInLatest = config.HidePlayedInLatest,
+ EnableLocalPassword = config.EnableLocalPassword,
+ PlayDefaultAudioTrack = config.PlayDefaultAudioTrack,
+ DisplayCollectionsView = config.DisplayCollectionsView,
+ DisplayMissingEpisodes = config.DisplayMissingEpisodes,
+ AudioLanguagePreference = config.AudioLanguagePreference,
+ RememberAudioSelections = config.RememberAudioSelections,
+ EnableNextEpisodeAutoPlay = config.EnableNextEpisodeAutoPlay,
+ RememberSubtitleSelections = config.RememberSubtitleSelections,
+ SubtitleLanguagePreference = config.SubtitleLanguagePreference,
+ };
+
+ 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);
+ 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);
+ user.SetPreference(PreferenceKind.OrderedViews, config.OrderedViews);
+ user.SetPreference(PreferenceKind.GroupedFolders, config.GroupedFolders);
+ user.SetPreference(PreferenceKind.MyMediaExcludes, config.MyMediaExcludes);
+ user.SetPreference(PreferenceKind.LatestItemExcludes, config.LatestItemsExcludes);
+ }
+
+ dbContext.SaveChanges();
+ }
+
+ try
+ {
+ File.Move(Path.Combine(dataPath, "users.db"), Path.Combine(dataPath, "users.db" + ".old"));
+ }
+ catch (IOException e)
+ {
+ _logger.LogError(e, "Error renaming legacy user database to 'users.db.old'");
+ }
+ }
+ }
+}
diff --git a/MediaBrowser.Api/BaseApiService.cs b/MediaBrowser.Api/BaseApiService.cs
index 2cd68ac1b..2ece16ee1 100644
--- a/MediaBrowser.Api/BaseApiService.cs
+++ b/MediaBrowser.Api/BaseApiService.cs
@@ -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.");
}
diff --git a/MediaBrowser.Api/FilterService.cs b/MediaBrowser.Api/FilterService.cs
index 5eb72cdb1..ac61cd491 100644
--- a/MediaBrowser.Api/FilterService.cs
+++ b/MediaBrowser.Api/FilterService.cs
@@ -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
{
diff --git a/MediaBrowser.Api/Images/ImageService.cs b/MediaBrowser.Api/Images/ImageService.cs
index 2e9b3e6cb..5bdf1618b 100644
--- a/MediaBrowser.Api/Images/ImageService.cs
+++ b/MediaBrowser.Api/Images/ImageService.cs
@@ -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,26 @@ 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();
+ var userDataPath = Path.Combine(ServerConfigurationManager.ApplicationPaths.UserConfigurationDirectoryPath, user.Username);
+
+ await _providerManager
+ .SaveImage(user, memoryStream, mimeType, Path.Combine(userDataPath, _imageProcessor.GetImageCacheTag(user)))
+ .ConfigureAwait(false);
+ await _userManager.UpdateUserAsync(user);
+ }
}
}
diff --git a/MediaBrowser.Api/Library/LibraryService.cs b/MediaBrowser.Api/Library/LibraryService.cs
index c0146dfee..50977c48f 100644
--- a/MediaBrowser.Api/Library/LibraryService.cs
+++ b/MediaBrowser.Api/Library/LibraryService.cs
@@ -759,12 +759,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)
{
@@ -842,7 +842,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)
@@ -884,7 +884,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)
{
diff --git a/MediaBrowser.Api/LiveTv/LiveTvService.cs b/MediaBrowser.Api/LiveTv/LiveTvService.cs
index 5fe4c0cca..279fd6ee9 100644
--- a/MediaBrowser.Api/LiveTv/LiveTvService.cs
+++ b/MediaBrowser.Api/LiveTv/LiveTvService.cs
@@ -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.");
}
diff --git a/MediaBrowser.Api/Movies/MoviesService.cs b/MediaBrowser.Api/Movies/MoviesService.cs
index cdd027634..aaabe07f3 100644
--- a/MediaBrowser.Api/Movies/MoviesService.cs
+++ b/MediaBrowser.Api/Movies/MoviesService.cs
@@ -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)
diff --git a/MediaBrowser.Api/Music/InstantMixService.cs b/MediaBrowser.Api/Music/InstantMixService.cs
index cacec8d64..e00b06e38 100644
--- a/MediaBrowser.Api/Music/InstantMixService.cs
+++ b/MediaBrowser.Api/Music/InstantMixService.cs
@@ -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;
diff --git a/MediaBrowser.Api/Playback/BaseStreamingService.cs b/MediaBrowser.Api/Playback/BaseStreamingService.cs
index f796aa486..a9824a5e7 100644
--- a/MediaBrowser.Api/Playback/BaseStreamingService.cs
+++ b/MediaBrowser.Api/Playback/BaseStreamingService.cs
@@ -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);
diff --git a/MediaBrowser.Api/Playback/MediaInfoService.cs b/MediaBrowser.Api/Playback/MediaInfoService.cs
index e2d771ec6..2c6534cc0 100644
--- a/MediaBrowser.Api/Playback/MediaInfoService.cs
+++ b/MediaBrowser.Api/Playback/MediaInfoService.cs
@@ -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)
{
diff --git a/MediaBrowser.Api/Sessions/SessionService.cs b/MediaBrowser.Api/Sessions/SessionService.cs
index 020bb5042..d986eea65 100644
--- a/MediaBrowser.Api/Sessions/SessionService.cs
+++ b/MediaBrowser.Api/Sessions/SessionService.cs
@@ -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));
}
diff --git a/MediaBrowser.Api/SuggestionsService.cs b/MediaBrowser.Api/SuggestionsService.cs
index 91f85db6f..51fa1eb71 100644
--- a/MediaBrowser.Api/SuggestionsService.cs
+++ b/MediaBrowser.Api/SuggestionsService.cs
@@ -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)
{
diff --git a/MediaBrowser.Api/TvShowsService.cs b/MediaBrowser.Api/TvShowsService.cs
index cd8e8dfbe..0c23d8b29 100644
--- a/MediaBrowser.Api/TvShowsService.cs
+++ b/MediaBrowser.Api/TvShowsService.cs
@@ -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)
{
diff --git a/MediaBrowser.Api/UserLibrary/BaseItemsByNameService.cs b/MediaBrowser.Api/UserLibrary/BaseItemsByNameService.cs
index 559082ff4..11d984a9a 100644
--- a/MediaBrowser.Api/UserLibrary/BaseItemsByNameService.cs
+++ b/MediaBrowser.Api/UserLibrary/BaseItemsByNameService.cs
@@ -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))
diff --git a/MediaBrowser.Api/UserLibrary/ItemsService.cs b/MediaBrowser.Api/UserLibrary/ItemsService.cs
index f3c0441e1..ca9f7aa82 100644
--- a/MediaBrowser.Api/UserLibrary/ItemsService.cs
+++ b/MediaBrowser.Api/UserLibrary/ItemsService.cs
@@ -2,6 +2,7 @@ using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
+using Jellyfin.Data.Enums;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Dto;
using MediaBrowser.Controller.Entities;
@@ -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)
{
diff --git a/MediaBrowser.Api/UserLibrary/PlaystateService.cs b/MediaBrowser.Api/UserLibrary/PlaystateService.cs
index d0faca163..fb8bda190 100644
--- a/MediaBrowser.Api/UserLibrary/PlaystateService.cs
+++ b/MediaBrowser.Api/UserLibrary/PlaystateService.cs
@@ -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);
diff --git a/MediaBrowser.Api/UserLibrary/UserLibraryService.cs b/MediaBrowser.Api/UserLibrary/UserLibraryService.cs
index 7fa750adb..f75852885 100644
--- a/MediaBrowser.Api/UserLibrary/UserLibraryService.cs
+++ b/MediaBrowser.Api/UserLibrary/UserLibraryService.cs
@@ -312,7 +312,7 @@ namespace MediaBrowser.Api.UserLibrary
if (!request.IsPlayed.HasValue)
{
- if (user.Configuration.HidePlayedInLatest)
+ if (user.HidePlayedInLatest)
{
request.IsPlayed = false;
}
diff --git a/MediaBrowser.Api/UserService.cs b/MediaBrowser.Api/UserService.cs
index 7d4d5fcf9..8d4450c1a 100644
--- a/MediaBrowser.Api/UserService.cs
+++ b/MediaBrowser.Api/UserService.cs
@@ -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;
@@ -275,12 +276,12 @@ namespace MediaBrowser.Api
{
var result = _userManager
.Users
- .Where(item => !item.Policy.IsDisabled);
+ .Where(item => !item.HasPermission(PermissionKind.IsDisabled));
if (ServerConfigurationManager.Configuration.IsStartupWizardCompleted)
{
var deviceId = _authContext.GetAuthorizationInfo(Request).DeviceId;
- result = result.Where(item => !item.Policy.IsHidden);
+ result = result.Where(item => !item.HasPermission(PermissionKind.IsHidden));
if (!string.IsNullOrWhiteSpace(deviceId))
{
@@ -289,12 +290,12 @@ namespace MediaBrowser.Api
if (!_networkManager.IsInLocalNetwork(Request.RemoteIp))
{
- result = result.Where(i => i.Policy.EnableRemoteAccess);
+ result = result.Where(i => i.HasPermission(PermissionKind.EnableRemoteAccess));
}
}
return ToOptimizedResult(result
- .OrderBy(u => u.Name)
+ .OrderBy(u => u.Username)
.Select(i => _userManager.GetPublicUserDto(i, Request.RemoteIp))
.ToArray()
);
@@ -316,12 +317,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)
@@ -338,12 +339,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();
@@ -413,7 +414,7 @@ namespace MediaBrowser.Api
// Password should always be null
return Post(new AuthenticateUserByName
{
- Username = user.Name,
+ Username = user.Username,
Password = null,
Pw = request.Pw
});
@@ -472,7 +473,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)
{
@@ -522,10 +528,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
{
@@ -584,24 +590,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.");
}
@@ -610,7 +616,7 @@ namespace MediaBrowser.Api
_sessionMananger.RevokeUserTokens(user.Id, currentToken);
}
- _userManager.UpdateUserPolicy(request.Id, request);
+ _userManager.UpdatePolicy(request.Id, request);
}
}
}
diff --git a/MediaBrowser.Controller/Authentication/IAuthenticationProvider.cs b/MediaBrowser.Controller/Authentication/IAuthenticationProvider.cs
index f5571065f..c0324a384 100644
--- a/MediaBrowser.Controller/Authentication/IAuthenticationProvider.cs
+++ b/MediaBrowser.Controller/Authentication/IAuthenticationProvider.cs
@@ -1,5 +1,5 @@
using System.Threading.Tasks;
-using MediaBrowser.Controller.Entities;
+using Jellyfin.Data.Entities;
using MediaBrowser.Model.Users;
namespace MediaBrowser.Controller.Authentication
diff --git a/MediaBrowser.Controller/Authentication/IPasswordResetProvider.cs b/MediaBrowser.Controller/Authentication/IPasswordResetProvider.cs
index 2639960e7..d9b814f69 100644
--- a/MediaBrowser.Controller/Authentication/IPasswordResetProvider.cs
+++ b/MediaBrowser.Controller/Authentication/IPasswordResetProvider.cs
@@ -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
diff --git a/MediaBrowser.Controller/Channels/Channel.cs b/MediaBrowser.Controller/Channels/Channel.cs
index cdf2ca69e..d6a1fc84e 100644
--- a/MediaBrowser.Controller/Channels/Channel.cs
+++ b/MediaBrowser.Controller/Channels/Channel.cs
@@ -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(""));
diff --git a/MediaBrowser.Controller/Collections/ICollectionManager.cs b/MediaBrowser.Controller/Collections/ICollectionManager.cs
index cfe8493d3..f51c73bd7 100644
--- a/MediaBrowser.Controller/Collections/ICollectionManager.cs
+++ b/MediaBrowser.Controller/Collections/ICollectionManager.cs
@@ -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);
}
}
diff --git a/MediaBrowser.Controller/Devices/IDeviceManager.cs b/MediaBrowser.Controller/Devices/IDeviceManager.cs
index ef3f43c75..7d279230b 100644
--- a/MediaBrowser.Controller/Devices/IDeviceManager.cs
+++ b/MediaBrowser.Controller/Devices/IDeviceManager.cs
@@ -1,5 +1,5 @@
using System;
-using MediaBrowser.Controller.Entities;
+using Jellyfin.Data.Entities;
using MediaBrowser.Model.Devices;
using MediaBrowser.Model.Events;
using MediaBrowser.Model.Querying;
diff --git a/MediaBrowser.Controller/Drawing/IImageProcessor.cs b/MediaBrowser.Controller/Drawing/IImageProcessor.cs
index 36c746624..bdb12402a 100644
--- a/MediaBrowser.Controller/Drawing/IImageProcessor.cs
+++ b/MediaBrowser.Controller/Drawing/IImageProcessor.cs
@@ -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>
diff --git a/MediaBrowser.Controller/Drawing/ImageProcessorExtensions.cs b/MediaBrowser.Controller/Drawing/ImageProcessorExtensions.cs
index df9050de5..9f505be93 100644
--- a/MediaBrowser.Controller/Drawing/ImageProcessorExtensions.cs
+++ b/MediaBrowser.Controller/Drawing/ImageProcessorExtensions.cs
@@ -1,5 +1,6 @@
using MediaBrowser.Controller.Entities;
using MediaBrowser.Model.Entities;
+using User = Jellyfin.Data.Entities.User;
namespace MediaBrowser.Controller.Drawing
{
diff --git a/MediaBrowser.Controller/Dto/IDtoService.cs b/MediaBrowser.Controller/Dto/IDtoService.cs
index ba693a065..5ac4f05c0 100644
--- a/MediaBrowser.Controller/Dto/IDtoService.cs
+++ b/MediaBrowser.Controller/Dto/IDtoService.cs
@@ -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);
}
}
diff --git a/MediaBrowser.Controller/Entities/Audio/Audio.cs b/MediaBrowser.Controller/Entities/Audio/Audio.cs
index a700d0be4..9065cb27f 100644
--- a/MediaBrowser.Controller/Entities/Audio/Audio.cs
+++ b/MediaBrowser.Controller/Entities/Audio/Audio.cs
@@ -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;
diff --git a/MediaBrowser.Controller/Entities/Audio/MusicAlbum.cs b/MediaBrowser.Controller/Entities/Audio/MusicAlbum.cs
index c216176e7..fbadeafad 100644
--- a/MediaBrowser.Controller/Entities/Audio/MusicAlbum.cs
+++ b/MediaBrowser.Controller/Entities/Audio/MusicAlbum.cs
@@ -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()
diff --git a/MediaBrowser.Controller/Entities/Audio/MusicArtist.cs b/MediaBrowser.Controller/Entities/Audio/MusicArtist.cs
index 5e3056ccb..eb5edaf34 100644
--- a/MediaBrowser.Controller/Entities/Audio/MusicArtist.cs
+++ b/MediaBrowser.Controller/Entities/Audio/MusicArtist.cs
@@ -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()
diff --git a/MediaBrowser.Controller/Entities/AudioBook.cs b/MediaBrowser.Controller/Entities/AudioBook.cs
index a13873bf9..ffe7aa8b3 100644
--- a/MediaBrowser.Controller/Entities/AudioBook.cs
+++ b/MediaBrowser.Controller/Entities/AudioBook.cs
@@ -1,5 +1,6 @@
using System;
using System.Text.Json.Serialization;
+using Jellyfin.Data.Enums;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Configuration;
diff --git a/MediaBrowser.Controller/Entities/BaseItem.cs b/MediaBrowser.Controller/Entities/BaseItem.cs
index 7ed8fa767..17d06da16 100644
--- a/MediaBrowser.Controller/Entities/BaseItem.cs
+++ b/MediaBrowser.Controller/Entities/BaseItem.cs
@@ -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>
{
diff --git a/MediaBrowser.Controller/Entities/Book.cs b/MediaBrowser.Controller/Entities/Book.cs
index dcad2554b..c4a2929dc 100644
--- a/MediaBrowser.Controller/Entities/Book.cs
+++ b/MediaBrowser.Controller/Entities/Book.cs
@@ -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;
diff --git a/MediaBrowser.Controller/Entities/Folder.cs b/MediaBrowser.Controller/Entities/Folder.cs
index a468e0c35..03644b0c6 100644
--- a/MediaBrowser.Controller/Entities/Folder.cs
+++ b/MediaBrowser.Controller/Entities/Folder.cs
@@ -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)
{
diff --git a/MediaBrowser.Controller/Entities/InternalItemsQuery.cs b/MediaBrowser.Controller/Entities/InternalItemsQuery.cs
index bd96059e3..6a2cafcba 100644
--- a/MediaBrowser.Controller/Entities/InternalItemsQuery.cs
+++ b/MediaBrowser.Controller/Entities/InternalItemsQuery.cs
@@ -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;
}
diff --git a/MediaBrowser.Controller/Entities/Movies/BoxSet.cs b/MediaBrowser.Controller/Entities/Movies/BoxSet.cs
index feaf8c45a..1c1bde3e4 100644
--- a/MediaBrowser.Controller/Entities/Movies/BoxSet.cs
+++ b/MediaBrowser.Controller/Entities/Movies/BoxSet.cs
@@ -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)
diff --git a/MediaBrowser.Controller/Entities/Movies/Movie.cs b/MediaBrowser.Controller/Entities/Movies/Movie.cs
index 11dc472b6..38359afcc 100644
--- a/MediaBrowser.Controller/Entities/Movies/Movie.cs
+++ b/MediaBrowser.Controller/Entities/Movies/Movie.cs
@@ -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;
diff --git a/MediaBrowser.Controller/Entities/MusicVideo.cs b/MediaBrowser.Controller/Entities/MusicVideo.cs
index 603242063..1b9d4614e 100644
--- a/MediaBrowser.Controller/Entities/MusicVideo.cs
+++ b/MediaBrowser.Controller/Entities/MusicVideo.cs
@@ -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;
diff --git a/MediaBrowser.Controller/Entities/TV/Episode.cs b/MediaBrowser.Controller/Entities/TV/Episode.cs
index 49229fa4b..0a89da46d 100644
--- a/MediaBrowser.Controller/Entities/TV/Episode.cs
+++ b/MediaBrowser.Controller/Entities/TV/Episode.cs
@@ -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;
diff --git a/MediaBrowser.Controller/Entities/TV/Season.cs b/MediaBrowser.Controller/Entities/TV/Season.cs
index 9c8a469e2..0d1fec62f 100644
--- a/MediaBrowser.Controller/Entities/TV/Season.cs
+++ b/MediaBrowser.Controller/Entities/TV/Season.cs
@@ -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)
diff --git a/MediaBrowser.Controller/Entities/TV/Series.cs b/MediaBrowser.Controller/Entities/TV/Series.cs
index 2475b2b7e..4aed5fbdc 100644
--- a/MediaBrowser.Controller/Entities/TV/Series.cs
+++ b/MediaBrowser.Controller/Entities/TV/Series.cs
@@ -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()
diff --git a/MediaBrowser.Controller/Entities/Trailer.cs b/MediaBrowser.Controller/Entities/Trailer.cs
index 0b8be90cd..c646e8ae6 100644
--- a/MediaBrowser.Controller/Entities/Trailer.cs
+++ b/MediaBrowser.Controller/Entities/Trailer.cs
@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Text.Json.Serialization;
+using Jellyfin.Data.Enums;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Configuration;
using MediaBrowser.Model.Entities;
diff --git a/MediaBrowser.Controller/Entities/User.cs b/MediaBrowser.Controller/Entities/User.cs
deleted file mode 100644
index 53601a610..000000000
--- a/MediaBrowser.Controller/Entities/User.cs
+++ /dev/null
@@ -1,262 +0,0 @@
-using System;
-using System.Globalization;
-using System.IO;
-using System.Text.Json.Serialization;
-using System.Threading;
-using System.Threading.Tasks;
-using MediaBrowser.Controller.Library;
-using MediaBrowser.Controller.Providers;
-using MediaBrowser.Model.Configuration;
-using MediaBrowser.Model.Users;
-
-namespace MediaBrowser.Controller.Entities
-{
- /// <summary>
- /// Class User
- /// </summary>
- public class User : BaseItem
- {
- public static IUserManager UserManager { get; set; }
-
- /// <summary>
- /// Gets or sets the password.
- /// </summary>
- /// <value>The password.</value>
- public string Password { get; set; }
- public string EasyPassword { get; set; }
-
- // Strictly to remove JsonIgnore
- public override ItemImageInfo[] ImageInfos
- {
- get => base.ImageInfos;
- set => base.ImageInfos = value;
- }
-
- /// <summary>
- /// Gets or sets the path.
- /// </summary>
- /// <value>The path.</value>
- [JsonIgnore]
- public override string Path
- {
- get => ConfigurationDirectoryPath;
- set => base.Path = value;
- }
-
- private string _name;
- /// <summary>
- /// Gets or sets the name.
- /// </summary>
- /// <value>The name.</value>
- public override string Name
- {
- get => _name;
- set
- {
- _name = value;
-
- // lazy load this again
- SortName = null;
- }
- }
-
- /// <summary>
- /// Returns the folder containing the item.
- /// If the item is a folder, it returns the folder itself
- /// </summary>
- /// <value>The containing folder path.</value>
- [JsonIgnore]
- public override string ContainingFolderPath => Path;
-
- /// <summary>
- /// Gets the root folder.
- /// </summary>
- /// <value>The root folder.</value>
- [JsonIgnore]
- public Folder RootFolder => LibraryManager.GetUserRootFolder();
-
- /// <summary>
- /// Gets or sets the last login date.
- /// </summary>
- /// <value>The last login date.</value>
- public DateTime? LastLoginDate { get; set; }
- /// <summary>
- /// Gets or sets the last activity date.
- /// </summary>
- /// <value>The last activity date.</value>
- public DateTime? LastActivityDate { get; set; }
-
- private volatile UserConfiguration _config;
- private readonly object _configSyncLock = new object();
- [JsonIgnore]
- public UserConfiguration Configuration
- {
- get
- {
- if (_config == null)
- {
- lock (_configSyncLock)
- {
- if (_config == null)
- {
- _config = UserManager.GetUserConfiguration(this);
- }
- }
- }
-
- return _config;
- }
- set => _config = value;
- }
-
- private volatile UserPolicy _policy;
- private readonly object _policySyncLock = new object();
- [JsonIgnore]
- public UserPolicy Policy
- {
- get
- {
- if (_policy == null)
- {
- lock (_policySyncLock)
- {
- if (_policy == null)
- {
- _policy = UserManager.GetUserPolicy(this);
- }
- }
- }
-
- return _policy;
- }
- set => _policy = value;
- }
-
- /// <summary>
- /// Renames the user.
- /// </summary>
- /// <param name="newName">The new name.</param>
- /// <returns>Task.</returns>
- /// <exception cref="ArgumentNullException"></exception>
- public Task Rename(string newName)
- {
- if (string.IsNullOrWhiteSpace(newName))
- {
- throw new ArgumentException("Username can't be empty", nameof(newName));
- }
-
- Name = newName;
-
- return RefreshMetadata(
- new MetadataRefreshOptions(new DirectoryService(FileSystem))
- {
- ReplaceAllMetadata = true,
- ImageRefreshMode = MetadataRefreshMode.FullRefresh,
- MetadataRefreshMode = MetadataRefreshMode.FullRefresh,
- ForceSave = true
-
- },
- CancellationToken.None);
- }
-
- public override void UpdateToRepository(ItemUpdateType updateReason, CancellationToken cancellationToken)
- {
- UserManager.UpdateUser(this);
- }
-
- /// <summary>
- /// Gets the path to the user's configuration directory
- /// </summary>
- /// <value>The configuration directory path.</value>
- [JsonIgnore]
- public string ConfigurationDirectoryPath => GetConfigurationDirectoryPath(Name);
-
- public override double GetDefaultPrimaryImageAspectRatio()
- {
- return 1;
- }
-
- /// <summary>
- /// Gets the configuration directory path.
- /// </summary>
- /// <param name="username">The username.</param>
- /// <returns>System.String.</returns>
- private string GetConfigurationDirectoryPath(string username)
- {
- var parentPath = ConfigurationManager.ApplicationPaths.UserConfigurationDirectoryPath;
-
- // TODO: Remove idPath and just use usernamePath for future releases
- var usernamePath = System.IO.Path.Combine(parentPath, username);
- var idPath = System.IO.Path.Combine(parentPath, Id.ToString("N", CultureInfo.InvariantCulture));
- if (!Directory.Exists(usernamePath) && Directory.Exists(idPath))
- {
- Directory.Move(idPath, usernamePath);
- }
-
- return usernamePath;
- }
-
- public bool IsParentalScheduleAllowed()
- {
- return IsParentalScheduleAllowed(DateTime.UtcNow);
- }
-
- public bool IsParentalScheduleAllowed(DateTime date)
- {
- var schedules = Policy.AccessSchedules;
-
- if (schedules.Length == 0)
- {
- return true;
- }
-
- foreach (var i in schedules)
- {
- if (IsParentalScheduleAllowed(i, date))
- {
- return true;
- }
- }
- return false;
- }
-
- 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;
- }
-
- public bool IsFolderGrouped(Guid id)
- {
- foreach (var i in Configuration.GroupedFolders)
- {
- if (new Guid(i) == id)
- {
- return true;
- }
- }
- return false;
- }
-
- [JsonIgnore]
- public override bool SupportsPeople => false;
-
- public long InternalId { get; set; }
-
-
- }
-}
diff --git a/MediaBrowser.Controller/Entities/UserRootFolder.cs b/MediaBrowser.Controller/Entities/UserRootFolder.cs
index 8a68f830c..9d211540d 100644
--- a/MediaBrowser.Controller/Entities/UserRootFolder.cs
+++ b/MediaBrowser.Controller/Entities/UserRootFolder.cs
@@ -63,7 +63,7 @@ namespace MediaBrowser.Controller.Entities
return UserViewBuilder.SortAndPage(result, null, query, LibraryManager, true);
}
- public override int GetChildCount(User user)
+ public override int GetChildCount(Jellyfin.Data.Entities.User user)
{
return GetChildren(user, true).Count;
}
@@ -74,7 +74,7 @@ namespace MediaBrowser.Controller.Entities
[JsonIgnore]
public override bool IsPreSorted => true;
- protected override IEnumerable<BaseItem> GetEligibleChildrenForRecursiveChildren(User user)
+ protected override IEnumerable<BaseItem> GetEligibleChildrenForRecursiveChildren(Jellyfin.Data.Entities.User user)
{
var list = base.GetEligibleChildrenForRecursiveChildren(user).ToList();
list.AddRange(LibraryManager.RootFolder.VirtualChildren);
diff --git a/MediaBrowser.Controller/Entities/UserView.cs b/MediaBrowser.Controller/Entities/UserView.cs
index 4ce9ec6f8..b44e7c191 100644
--- a/MediaBrowser.Controller/Entities/UserView.cs
+++ b/MediaBrowser.Controller/Entities/UserView.cs
@@ -48,7 +48,7 @@ namespace MediaBrowser.Controller.Entities
[JsonIgnore]
public override bool SupportsPlayedStatus => false;
- public override int GetChildCount(User user)
+ public override int GetChildCount(Jellyfin.Data.Entities.User user)
{
return GetChildren(user, true).Count;
}
@@ -70,7 +70,7 @@ namespace MediaBrowser.Controller.Entities
.GetUserItems(parent, this, CollectionType, query);
}
- 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)
{
if (query == null)
{
@@ -93,7 +93,7 @@ namespace MediaBrowser.Controller.Entities
return true;
}
- public override IEnumerable<BaseItem> GetRecursiveChildren(User user, InternalItemsQuery query)
+ public override IEnumerable<BaseItem> GetRecursiveChildren(Jellyfin.Data.Entities.User user, InternalItemsQuery query)
{
query.SetUser(user);
query.Recursive = true;
@@ -103,7 +103,7 @@ namespace MediaBrowser.Controller.Entities
return GetItemList(query);
}
- protected override IEnumerable<BaseItem> GetEligibleChildrenForRecursiveChildren(User user)
+ protected override IEnumerable<BaseItem> GetEligibleChildrenForRecursiveChildren(Jellyfin.Data.Entities.User user)
{
return GetChildren(user, false);
}
diff --git a/MediaBrowser.Controller/Entities/UserViewBuilder.cs b/MediaBrowser.Controller/Entities/UserViewBuilder.cs
index 435a1e8da..0ad8e6b71 100644
--- a/MediaBrowser.Controller/Entities/UserViewBuilder.cs
+++ b/MediaBrowser.Controller/Entities/UserViewBuilder.cs
@@ -125,7 +125,7 @@ namespace MediaBrowser.Controller.Entities
return 50;
}
- private QueryResult<BaseItem> GetMovieFolders(Folder parent, User user, InternalItemsQuery query)
+ private QueryResult<BaseItem> GetMovieFolders(Folder parent, Jellyfin.Data.Entities.User user, InternalItemsQuery query)
{
if (query.Recursive)
{
@@ -140,19 +140,20 @@ namespace MediaBrowser.Controller.Entities
return parent.QueryRecursive(query);
}
- var list = new List<BaseItem>();
-
- list.Add(GetUserView(SpecialFolder.MovieResume, "HeaderContinueWatching", "0", parent));
- list.Add(GetUserView(SpecialFolder.MovieLatest, "Latest", "1", parent));
- list.Add(GetUserView(SpecialFolder.MovieMovies, "Movies", "2", parent));
- list.Add(GetUserView(SpecialFolder.MovieCollections, "Collections", "3", parent));
- list.Add(GetUserView(SpecialFolder.MovieFavorites, "Favorites", "4", parent));
- list.Add(GetUserView(SpecialFolder.MovieGenres, "Genres", "5", parent));
+ var list = new List<BaseItem>
+ {
+ GetUserView(SpecialFolder.MovieResume, "HeaderContinueWatching", "0", parent),
+ GetUserView(SpecialFolder.MovieLatest, "Latest", "1", parent),
+ GetUserView(SpecialFolder.MovieMovies, "Movies", "2", parent),
+ GetUserView(SpecialFolder.MovieCollections, "Collections", "3", parent),
+ GetUserView(SpecialFolder.MovieFavorites, "Favorites", "4", parent),
+ GetUserView(SpecialFolder.MovieGenres, "Genres", "5", parent)
+ };
return GetResult(list, parent, query);
}
- private QueryResult<BaseItem> GetFavoriteMovies(Folder parent, User user, InternalItemsQuery query)
+ private QueryResult<BaseItem> GetFavoriteMovies(Folder parent, Jellyfin.Data.Entities.User user, InternalItemsQuery query)
{
query.Recursive = true;
query.Parent = parent;
@@ -163,7 +164,7 @@ namespace MediaBrowser.Controller.Entities
return _libraryManager.GetItemsResult(query);
}
- private QueryResult<BaseItem> GetFavoriteSeries(Folder parent, User user, InternalItemsQuery query)
+ private QueryResult<BaseItem> GetFavoriteSeries(Folder parent, Jellyfin.Data.Entities.User user, InternalItemsQuery query)
{
query.Recursive = true;
query.Parent = parent;
@@ -174,7 +175,7 @@ namespace MediaBrowser.Controller.Entities
return _libraryManager.GetItemsResult(query);
}
- private QueryResult<BaseItem> GetFavoriteEpisodes(Folder parent, User user, InternalItemsQuery query)
+ private QueryResult<BaseItem> GetFavoriteEpisodes(Folder parent, Jellyfin.Data.Entities.User user, InternalItemsQuery query)
{
query.Recursive = true;
query.Parent = parent;
@@ -185,7 +186,7 @@ namespace MediaBrowser.Controller.Entities
return _libraryManager.GetItemsResult(query);
}
- private QueryResult<BaseItem> GetMovieMovies(Folder parent, User user, InternalItemsQuery query)
+ private QueryResult<BaseItem> GetMovieMovies(Folder parent, Jellyfin.Data.Entities.User user, InternalItemsQuery query)
{
query.Recursive = true;
query.Parent = parent;
@@ -196,7 +197,7 @@ namespace MediaBrowser.Controller.Entities
return _libraryManager.GetItemsResult(query);
}
- private QueryResult<BaseItem> GetMovieCollections(Folder parent, User user, InternalItemsQuery query)
+ private QueryResult<BaseItem> GetMovieCollections(Folder parent, Jellyfin.Data.Entities.User user, InternalItemsQuery query)
{
query.Parent = null;
query.IncludeItemTypes = new[] { typeof(BoxSet).Name };
@@ -206,7 +207,7 @@ namespace MediaBrowser.Controller.Entities
return _libraryManager.GetItemsResult(query);
}
- private QueryResult<BaseItem> GetMovieLatest(Folder parent, User user, InternalItemsQuery query)
+ private QueryResult<BaseItem> GetMovieLatest(Folder parent, Jellyfin.Data.Entities.User user, InternalItemsQuery query)
{
query.OrderBy = new[] { ItemSortBy.DateCreated, ItemSortBy.SortName }.Select(i => new ValueTuple<string, SortOrder>(i, SortOrder.Descending)).ToArray();
@@ -219,7 +220,7 @@ namespace MediaBrowser.Controller.Entities
return ConvertToResult(_libraryManager.GetItemList(query));
}
- private QueryResult<BaseItem> GetMovieResume(Folder parent, User user, InternalItemsQuery query)
+ private QueryResult<BaseItem> GetMovieResume(Folder parent, Jellyfin.Data.Entities.User user, InternalItemsQuery query)
{
query.OrderBy = new[] { ItemSortBy.DatePlayed, ItemSortBy.SortName }.Select(i => new ValueTuple<string, SortOrder>(i, SortOrder.Descending)).ToArray();
query.IsResumable = true;
@@ -242,7 +243,7 @@ namespace MediaBrowser.Controller.Entities
};
}
- private QueryResult<BaseItem> GetMovieGenres(Folder parent, User user, InternalItemsQuery query)
+ private QueryResult<BaseItem> GetMovieGenres(Folder parent, Jellyfin.Data.Entities.User user, InternalItemsQuery query)
{
var genres = parent.QueryRecursive(new InternalItemsQuery(user)
{
@@ -272,7 +273,7 @@ namespace MediaBrowser.Controller.Entities
return GetResult(genres, parent, query);
}
- private QueryResult<BaseItem> GetMovieGenreItems(Folder queryParent, Folder displayParent, User user, InternalItemsQuery query)
+ private QueryResult<BaseItem> GetMovieGenreItems(Folder queryParent, Folder displayParent, Jellyfin.Data.Entities.User user, InternalItemsQuery query)
{
query.Recursive = true;
query.Parent = queryParent;
@@ -284,7 +285,7 @@ namespace MediaBrowser.Controller.Entities
return _libraryManager.GetItemsResult(query);
}
- private QueryResult<BaseItem> GetTvView(Folder parent, User user, InternalItemsQuery query)
+ private QueryResult<BaseItem> GetTvView(Folder parent, Jellyfin.Data.Entities.User user, InternalItemsQuery query)
{
if (query.Recursive)
{
@@ -293,26 +294,32 @@ namespace MediaBrowser.Controller.Entities
if (query.IncludeItemTypes.Length == 0)
{
- query.IncludeItemTypes = new[] { typeof(Series).Name, typeof(Season).Name, typeof(Episode).Name };
+ query.IncludeItemTypes = new[]
+ {
+ nameof(Series),
+ nameof(Season),
+ nameof(Episode)
+ };
}
return parent.QueryRecursive(query);
}
- var list = new List<BaseItem>();
-
- list.Add(GetUserView(SpecialFolder.TvResume, "HeaderContinueWatching", "0", parent));
- list.Add(GetUserView(SpecialFolder.TvNextUp, "HeaderNextUp", "1", parent));
- list.Add(GetUserView(SpecialFolder.TvLatest, "Latest", "2", parent));
- list.Add(GetUserView(SpecialFolder.TvShowSeries, "Shows", "3", parent));
- list.Add(GetUserView(SpecialFolder.TvFavoriteSeries, "HeaderFavoriteShows", "4", parent));
- list.Add(GetUserView(SpecialFolder.TvFavoriteEpisodes, "HeaderFavoriteEpisodes", "5", parent));
- list.Add(GetUserView(SpecialFolder.TvGenres, "Genres", "6", parent));
+ var list = new List<BaseItem>
+ {
+ GetUserView(SpecialFolder.TvResume, "HeaderContinueWatching", "0", parent),
+ GetUserView(SpecialFolder.TvNextUp, "HeaderNextUp", "1", parent),
+ GetUserView(SpecialFolder.TvLatest, "Latest", "2", parent),
+ GetUserView(SpecialFolder.TvShowSeries, "Shows", "3", parent),
+ GetUserView(SpecialFolder.TvFavoriteSeries, "HeaderFavoriteShows", "4", parent),
+ GetUserView(SpecialFolder.TvFavoriteEpisodes, "HeaderFavoriteEpisodes", "5", parent),
+ GetUserView(SpecialFolder.TvGenres, "Genres", "6", parent)
+ };
return GetResult(list, parent, query);
}
- private QueryResult<BaseItem> GetTvLatest(Folder parent, User user, InternalItemsQuery query)
+ private QueryResult<BaseItem> GetTvLatest(Folder parent, Jellyfin.Data.Entities.User user, InternalItemsQuery query)
{
query.OrderBy = new[] { ItemSortBy.DateCreated, ItemSortBy.SortName }.Select(i => new ValueTuple<string, SortOrder>(i, SortOrder.Descending)).ToArray();
@@ -341,7 +348,7 @@ namespace MediaBrowser.Controller.Entities
return result;
}
- private QueryResult<BaseItem> GetTvResume(Folder parent, User user, InternalItemsQuery query)
+ private QueryResult<BaseItem> GetTvResume(Folder parent, Jellyfin.Data.Entities.User user, InternalItemsQuery query)
{
query.OrderBy = new[] { ItemSortBy.DatePlayed, ItemSortBy.SortName }.Select(i => new ValueTuple<string, SortOrder>(i, SortOrder.Descending)).ToArray();
query.IsResumable = true;
@@ -354,7 +361,7 @@ namespace MediaBrowser.Controller.Entities
return ConvertToResult(_libraryManager.GetItemList(query));
}
- private QueryResult<BaseItem> GetTvSeries(Folder parent, User user, InternalItemsQuery query)
+ private QueryResult<BaseItem> GetTvSeries(Folder parent, Jellyfin.Data.Entities.User user, InternalItemsQuery query)
{
query.Recursive = true;
query.Parent = parent;
@@ -365,7 +372,7 @@ namespace MediaBrowser.Controller.Entities
return _libraryManager.GetItemsResult(query);
}
- private QueryResult<BaseItem> GetTvGenres(Folder parent, User user, InternalItemsQuery query)
+ private QueryResult<BaseItem> GetTvGenres(Folder parent, Jellyfin.Data.Entities.User user, InternalItemsQuery query)
{
var genres = parent.QueryRecursive(new InternalItemsQuery(user)
{
@@ -395,7 +402,7 @@ namespace MediaBrowser.Controller.Entities
return GetResult(genres, parent, query);
}
- private QueryResult<BaseItem> GetTvGenreItems(Folder queryParent, Folder displayParent, User user, InternalItemsQuery query)
+ private QueryResult<BaseItem> GetTvGenreItems(Folder queryParent, Folder displayParent, Jellyfin.Data.Entities.User user, InternalItemsQuery query)
{
query.Recursive = true;
query.Parent = queryParent;
@@ -417,7 +424,8 @@ namespace MediaBrowser.Controller.Entities
};
}
- private QueryResult<BaseItem> GetResult<T>(IEnumerable<T> items,
+ private QueryResult<BaseItem> GetResult<T>(
+ IEnumerable<T> items,
BaseItem queryParent,
InternalItemsQuery query)
where T : BaseItem
@@ -484,7 +492,7 @@ namespace MediaBrowser.Controller.Entities
};
}
- public static bool Filter(BaseItem item, User user, InternalItemsQuery query, IUserDataManager userDataManager, ILibraryManager libraryManager)
+ public static bool Filter(BaseItem item, Jellyfin.Data.Entities.User user, InternalItemsQuery query, IUserDataManager userDataManager, ILibraryManager libraryManager)
{
if (query.MediaTypes.Length > 0 && !query.MediaTypes.Contains(item.MediaType ?? string.Empty, StringComparer.OrdinalIgnoreCase))
{
@@ -942,7 +950,7 @@ namespace MediaBrowser.Controller.Entities
return true;
}
- private IEnumerable<BaseItem> GetMediaFolders(User user)
+ private IEnumerable<BaseItem> GetMediaFolders(Jellyfin.Data.Entities.User user)
{
if (user == null)
{
@@ -957,7 +965,7 @@ namespace MediaBrowser.Controller.Entities
.Where(i => user.IsFolderGrouped(i.Id) && UserView.IsEligibleForGrouping(i));
}
- private BaseItem[] GetMediaFolders(User user, IEnumerable<string> viewTypes)
+ private BaseItem[] GetMediaFolders(Jellyfin.Data.Entities.User user, IEnumerable<string> viewTypes)
{
if (user == null)
{
@@ -978,7 +986,7 @@ namespace MediaBrowser.Controller.Entities
}).ToArray();
}
- private BaseItem[] GetMediaFolders(Folder parent, User user, IEnumerable<string> viewTypes)
+ private BaseItem[] GetMediaFolders(Folder parent, Jellyfin.Data.Entities.User user, IEnumerable<string> viewTypes)
{
if (parent == null || parent is UserView)
{
diff --git a/MediaBrowser.Controller/Library/IIntroProvider.cs b/MediaBrowser.Controller/Library/IIntroProvider.cs
index d9d1ca8c7..aa7001611 100644
--- a/MediaBrowser.Controller/Library/IIntroProvider.cs
+++ b/MediaBrowser.Controller/Library/IIntroProvider.cs
@@ -15,7 +15,7 @@ namespace MediaBrowser.Controller.Library
/// <param name="item">The item.</param>
/// <param name="user">The user.</param>
/// <returns>IEnumerable{System.String}.</returns>
- Task<IEnumerable<IntroInfo>> GetIntros(BaseItem item, User user);
+ Task<IEnumerable<IntroInfo>> GetIntros(BaseItem item, Jellyfin.Data.Entities.User user);
/// <summary>
/// Gets all intro files.
diff --git a/MediaBrowser.Controller/Library/ILibraryManager.cs b/MediaBrowser.Controller/Library/ILibraryManager.cs
index 2e1c97f67..ada570bfd 100644
--- a/MediaBrowser.Controller/Library/ILibraryManager.cs
+++ b/MediaBrowser.Controller/Library/ILibraryManager.cs
@@ -141,7 +141,7 @@ namespace MediaBrowser.Controller.Library
/// <param name="item">The item.</param>
/// <param name="user">The user.</param>
/// <returns>IEnumerable{System.String}.</returns>
- Task<IEnumerable<Video>> GetIntros(BaseItem item, User user);
+ Task<IEnumerable<Video>> GetIntros(BaseItem item, Jellyfin.Data.Entities.User user);
/// <summary>
/// Gets all intro files.
@@ -172,8 +172,8 @@ namespace MediaBrowser.Controller.Library
/// <param name="sortBy">The sort by.</param>
/// <param name="sortOrder">The sort order.</param>
/// <returns>IEnumerable{BaseItem}.</returns>
- IEnumerable<BaseItem> Sort(IEnumerable<BaseItem> items, User user, IEnumerable<string> sortBy, SortOrder sortOrder);
- IEnumerable<BaseItem> Sort(IEnumerable<BaseItem> items, User user, IEnumerable<ValueTuple<string, SortOrder>> orderBy);
+ IEnumerable<BaseItem> Sort(IEnumerable<BaseItem> items, Jellyfin.Data.Entities.User user, IEnumerable<string> sortBy, SortOrder sortOrder);
+ IEnumerable<BaseItem> Sort(IEnumerable<BaseItem> items, Jellyfin.Data.Entities.User user, IEnumerable<ValueTuple<string, SortOrder>> orderBy);
/// <summary>
/// Gets the user root folder.
@@ -284,7 +284,8 @@ namespace MediaBrowser.Controller.Library
/// <param name="parentId">The parent identifier.</param>
/// <param name="viewType">Type of the view.</param>
/// <param name="sortName">Name of the sort.</param>
- UserView GetNamedView(User user,
+ UserView GetNamedView(
+ Jellyfin.Data.Entities.User user,
string name,
Guid parentId,
string viewType,
@@ -297,7 +298,8 @@ namespace MediaBrowser.Controller.Library
/// <param name="name">The name.</param>
/// <param name="viewType">Type of the view.</param>
/// <param name="sortName">Name of the sort.</param>
- UserView GetNamedView(User user,
+ UserView GetNamedView(
+ Jellyfin.Data.Entities.User user,
string name,
string viewType,
string sortName);
diff --git a/MediaBrowser.Controller/Library/IMediaSourceManager.cs b/MediaBrowser.Controller/Library/IMediaSourceManager.cs
index 0ceabd0e6..57368778a 100644
--- a/MediaBrowser.Controller/Library/IMediaSourceManager.cs
+++ b/MediaBrowser.Controller/Library/IMediaSourceManager.cs
@@ -55,12 +55,12 @@ namespace MediaBrowser.Controller.Library
/// <summary>
/// Gets the playack media sources.
/// </summary>
- Task<List<MediaSourceInfo>> GetPlaybackMediaSources(BaseItem item, User user, bool allowMediaProbe, bool enablePathSubstitution, CancellationToken cancellationToken);
+ Task<List<MediaSourceInfo>> GetPlaybackMediaSources(BaseItem item, Jellyfin.Data.Entities.User user, bool allowMediaProbe, bool enablePathSubstitution, CancellationToken cancellationToken);
/// <summary>
/// Gets the static media sources.
/// </summary>
- List<MediaSourceInfo> GetStaticMediaSources(BaseItem item, bool enablePathSubstitution, User user = null);
+ List<MediaSourceInfo> GetStaticMediaSources(BaseItem item, bool enablePathSubstitution, Jellyfin.Data.Entities.User user = null);
/// <summary>
/// Gets the static media source.
@@ -100,7 +100,7 @@ namespace MediaBrowser.Controller.Library
MediaProtocol GetPathProtocol(string path);
- void SetDefaultAudioAndSubtitleStreamIndexes(BaseItem item, MediaSourceInfo source, User user);
+ void SetDefaultAudioAndSubtitleStreamIndexes(BaseItem item, MediaSourceInfo source, Jellyfin.Data.Entities.User user);
Task AddMediaInfoWithProbe(MediaSourceInfo mediaSource, bool isAudio, string cacheKey, bool addProbeDelay, bool isLiveStream, CancellationToken cancellationToken);
diff --git a/MediaBrowser.Controller/Library/IMusicManager.cs b/MediaBrowser.Controller/Library/IMusicManager.cs
index 554dd0895..0618837bc 100644
--- a/MediaBrowser.Controller/Library/IMusicManager.cs
+++ b/MediaBrowser.Controller/Library/IMusicManager.cs
@@ -10,16 +10,16 @@ namespace MediaBrowser.Controller.Library
/// <summary>
/// Gets the instant mix from song.
/// </summary>
- List<BaseItem> GetInstantMixFromItem(BaseItem item, User user, DtoOptions dtoOptions);
+ List<BaseItem> GetInstantMixFromItem(BaseItem item, Jellyfin.Data.Entities.User user, DtoOptions dtoOptions);
/// <summary>
/// Gets the instant mix from artist.
/// </summary>
- List<BaseItem> GetInstantMixFromArtist(MusicArtist artist, User user, DtoOptions dtoOptions);
+ List<BaseItem> GetInstantMixFromArtist(MusicArtist artist, Jellyfin.Data.Entities.User user, DtoOptions dtoOptions);
/// <summary>
/// Gets the instant mix from genre.
/// </summary>
- List<BaseItem> GetInstantMixFromGenres(IEnumerable<string> genres, User user, DtoOptions dtoOptions);
+ List<BaseItem> GetInstantMixFromGenres(IEnumerable<string> genres, Jellyfin.Data.Entities.User user, DtoOptions dtoOptions);
}
}
diff --git a/MediaBrowser.Controller/Library/IUserDataManager.cs b/MediaBrowser.Controller/Library/IUserDataManager.cs
index eb735d31a..15da560ef 100644
--- a/MediaBrowser.Controller/Library/IUserDataManager.cs
+++ b/MediaBrowser.Controller/Library/IUserDataManager.cs
@@ -27,18 +27,18 @@ namespace MediaBrowser.Controller.Library
/// <param name="reason">The reason.</param>
/// <param name="cancellationToken">The cancellation token.</param>
void SaveUserData(Guid userId, BaseItem item, UserItemData userData, UserDataSaveReason reason, CancellationToken cancellationToken);
- void SaveUserData(User userId, BaseItem item, UserItemData userData, UserDataSaveReason reason, CancellationToken cancellationToken);
+ void SaveUserData(Jellyfin.Data.Entities.User user, BaseItem item, UserItemData userData, UserDataSaveReason reason, CancellationToken cancellationToken);
- UserItemData GetUserData(User user, BaseItem item);
+ UserItemData GetUserData(Jellyfin.Data.Entities.User user, BaseItem item);
UserItemData GetUserData(Guid userId, BaseItem item);
/// <summary>
/// Gets the user data dto.
/// </summary>
- UserItemDataDto GetUserDataDto(BaseItem item, User user);
+ UserItemDataDto GetUserDataDto(BaseItem item, Jellyfin.Data.Entities.User user);
- UserItemDataDto GetUserDataDto(BaseItem item, BaseItemDto itemDto, User user, DtoOptions dto_options);
+ UserItemDataDto GetUserDataDto(BaseItem item, BaseItemDto itemDto, Jellyfin.Data.Entities.User user, DtoOptions dto_options);
/// <summary>
/// Get all user data for the given user
diff --git a/MediaBrowser.Controller/Library/IUserManager.cs b/MediaBrowser.Controller/Library/IUserManager.cs
index ec6cb35eb..6685f5306 100644
--- a/MediaBrowser.Controller/Library/IUserManager.cs
+++ b/MediaBrowser.Controller/Library/IUserManager.cs
@@ -1,9 +1,8 @@
using System;
using System.Collections.Generic;
-using System.Threading;
using System.Threading.Tasks;
+using Jellyfin.Data.Entities;
using MediaBrowser.Controller.Authentication;
-using MediaBrowser.Controller.Entities;
using MediaBrowser.Model.Configuration;
using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Events;
@@ -17,36 +16,41 @@ namespace MediaBrowser.Controller.Library
public interface IUserManager
{
/// <summary>
- /// Gets the users.
+ /// Occurs when a user is updated.
/// </summary>
- /// <value>The users.</value>
- IEnumerable<User> Users { get; }
+ event EventHandler<GenericEventArgs<User>> OnUserUpdated;
/// <summary>
- /// Gets the user ids.
+ /// Occurs when a user is created.
/// </summary>
- /// <value>The users ids.</value>
- IEnumerable<Guid> UsersIds { get; }
+ event EventHandler<GenericEventArgs<User>> OnUserCreated;
/// <summary>
- /// Occurs when [user updated].
+ /// Occurs when a user is deleted.
/// </summary>
- event EventHandler<GenericEventArgs<User>> UserUpdated;
+ event EventHandler<GenericEventArgs<User>> OnUserDeleted;
/// <summary>
- /// Occurs when [user deleted].
+ /// Occurs when a user's password is changed.
/// </summary>
- event EventHandler<GenericEventArgs<User>> UserDeleted;
-
- event EventHandler<GenericEventArgs<User>> UserCreated;
+ event EventHandler<GenericEventArgs<User>> OnUserPasswordChanged;
- event EventHandler<GenericEventArgs<User>> UserPolicyUpdated;
-
- event EventHandler<GenericEventArgs<User>> UserConfigurationUpdated;
+ /// <summary>
+ /// Occurs when a user is locked out.
+ /// </summary>
+ event EventHandler<GenericEventArgs<User>> OnUserLockedOut;
- event EventHandler<GenericEventArgs<User>> UserPasswordChanged;
+ /// <summary>
+ /// Gets the users.
+ /// </summary>
+ /// <value>The users.</value>
+ IEnumerable<User> Users { get; }
- event EventHandler<GenericEventArgs<User>> UserLockedOut;
+ /// <summary>
+ /// Gets the user ids.
+ /// </summary>
+ /// <value>The users ids.</value>
+ IEnumerable<Guid> UsersIds { get; }
/// <summary>
/// Gets a user by Id.
@@ -64,13 +68,6 @@ namespace MediaBrowser.Controller.Library
User GetUserByName(string name);
/// <summary>
- /// Refreshes metadata for each user
- /// </summary>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task.</returns>
- Task RefreshUsersMetadata(CancellationToken cancellationToken);
-
- /// <summary>
/// Renames the user.
/// </summary>
/// <param name="user">The user.</param>
@@ -89,19 +86,27 @@ namespace MediaBrowser.Controller.Library
void UpdateUser(User user);
/// <summary>
- /// Creates the user.
+ /// Updates the user.
/// </summary>
- /// <param name="name">The name.</param>
- /// <returns>User.</returns>
+ /// <param name="user">The user.</param>
+ /// <exception cref="ArgumentNullException">If user is <c>null</c>.</exception>
+ /// <exception cref="ArgumentException">If the provided user doesn't exist.</exception>
+ /// <returns>A task representing the update of the user.</returns>
+ Task UpdateUserAsync(User user);
+
+ /// <summary>
+ /// Creates a user with the specified name.
+ /// </summary>
+ /// <param name="name">The name of the new user.</param>
+ /// <returns>The created user.</returns>
/// <exception cref="ArgumentNullException">name</exception>
/// <exception cref="ArgumentException"></exception>
User CreateUser(string name);
/// <summary>
- /// Deletes the user.
+ /// Deletes the specified user.
/// </summary>
- /// <param name="user">The user.</param>
- /// <returns>Task.</returns>
+ /// <param name="user">The user to be deleted.</param>
void DeleteUser(User user);
/// <summary>
@@ -112,13 +117,6 @@ namespace MediaBrowser.Controller.Library
Task ResetPassword(User user);
/// <summary>
- /// Gets the offline user dto.
- /// </summary>
- /// <param name="user">The user.</param>
- /// <returns>UserDto.</returns>
- UserDto GetOfflineUserDto(User user);
-
- /// <summary>
/// Resets the easy password.
/// </summary>
/// <param name="user">The user.</param>
@@ -146,7 +144,7 @@ namespace MediaBrowser.Controller.Library
/// <summary>
/// Gets the user public dto.
/// </summary>
- /// <param name="user">Ther user.</param>\
+ /// <param name="user">The user.</param>\
/// <param name="remoteEndPoint">The remote end point.</param>
/// <returns>A public UserDto, aka a UserDto stripped of personal data.</returns>
PublicUserDto GetPublicUserDto(User user, string remoteEndPoint = null);
@@ -171,47 +169,28 @@ namespace MediaBrowser.Controller.Library
/// <returns><c>true</c> if XXXX, <c>false</c> otherwise.</returns>
Task<PinRedeemResult> RedeemPasswordResetPin(string pin);
- /// <summary>
- /// Gets the user policy.
- /// </summary>
- /// <param name="user">The user.</param>
- /// <returns>UserPolicy.</returns>
- UserPolicy GetUserPolicy(User user);
-
- /// <summary>
- /// Gets the user configuration.
- /// </summary>
- /// <param name="user">The user.</param>
- /// <returns>UserConfiguration.</returns>
- UserConfiguration GetUserConfiguration(User user);
+ void AddParts(IEnumerable<IAuthenticationProvider> authenticationProviders, IEnumerable<IPasswordResetProvider> passwordResetProviders);
- /// <summary>
- /// Updates the configuration.
- /// </summary>
- /// <param name="userId">The user identifier.</param>
- /// <param name="newConfiguration">The new configuration.</param>
- /// <returns>Task.</returns>
- void UpdateConfiguration(Guid userId, UserConfiguration newConfiguration);
+ NameIdPair[] GetAuthenticationProviders();
- void UpdateConfiguration(User user, UserConfiguration newConfiguration);
+ NameIdPair[] GetPasswordResetProviders();
/// <summary>
- /// Updates the user policy.
+ /// This method updates the user's configuration.
+ /// This is only included as a stopgap until the new API, using this internally is not recommended.
+ /// Instead, modify the user object directlu, then call <see cref="UpdateUser"/>.
/// </summary>
- /// <param name="userId">The user identifier.</param>
- /// <param name="userPolicy">The user policy.</param>
- void UpdateUserPolicy(Guid userId, UserPolicy userPolicy);
+ /// <param name="userId">The user's Id.</param>
+ /// <param name="config">The request containing the new user configuration.</param>
+ void UpdateConfiguration(Guid userId, UserConfiguration config);
/// <summary>
- /// Makes the valid username.
+ /// This method updates the user's policy.
+ /// This is only included as a stopgap until the new API, using this internally is not recommended.
+ /// Instead, modify the user object directlu, then call <see cref="UpdateUser"/>.
/// </summary>
- /// <param name="username">The username.</param>
- /// <returns>System.String.</returns>
- string MakeValidUsername(string username);
-
- void AddParts(IEnumerable<IAuthenticationProvider> authenticationProviders, IEnumerable<IPasswordResetProvider> passwordResetProviders);
-
- NameIdPair[] GetAuthenticationProviders();
- NameIdPair[] GetPasswordResetProviders();
+ /// <param name="userId">The user's Id.</param>
+ /// <param name="policy">The request containing the new user policy.</param>
+ void UpdatePolicy(Guid userId, UserPolicy policy);
}
}
diff --git a/MediaBrowser.Controller/Library/PlaybackProgressEventArgs.cs b/MediaBrowser.Controller/Library/PlaybackProgressEventArgs.cs
index b0302d04c..83c0e3297 100644
--- a/MediaBrowser.Controller/Library/PlaybackProgressEventArgs.cs
+++ b/MediaBrowser.Controller/Library/PlaybackProgressEventArgs.cs
@@ -11,7 +11,7 @@ namespace MediaBrowser.Controller.Library
/// </summary>
public class PlaybackProgressEventArgs : EventArgs
{
- public List<User> Users { get; set; }
+ public List<Jellyfin.Data.Entities.User> Users { get; set; }
public long? PlaybackPositionTicks { get; set; }
public BaseItem Item { get; set; }
public BaseItemDto MediaInfo { get; set; }
@@ -28,7 +28,7 @@ namespace MediaBrowser.Controller.Library
public PlaybackProgressEventArgs()
{
- Users = new List<User>();
+ Users = new List<Jellyfin.Data.Entities.User>();
}
}
}
diff --git a/MediaBrowser.Controller/LiveTv/ILiveTvManager.cs b/MediaBrowser.Controller/LiveTv/ILiveTvManager.cs
index e02c387e4..99fd18bf9 100644
--- a/MediaBrowser.Controller/LiveTv/ILiveTvManager.cs
+++ b/MediaBrowser.Controller/LiveTv/ILiveTvManager.cs
@@ -115,7 +115,7 @@ namespace MediaBrowser.Controller.LiveTv
/// <param name="cancellationToken">The cancellation token.</param>
/// <param name="user">The user.</param>
/// <returns>Task{ProgramInfoDto}.</returns>
- Task<BaseItemDto> GetProgram(string id, CancellationToken cancellationToken, User user = null);
+ Task<BaseItemDto> GetProgram(string id, CancellationToken cancellationToken, Jellyfin.Data.Entities.User user = null);
/// <summary>
/// Gets the programs.
@@ -202,7 +202,7 @@ namespace MediaBrowser.Controller.LiveTv
/// Gets the enabled users.
/// </summary>
/// <returns>IEnumerable{User}.</returns>
- IEnumerable<User> GetEnabledUsers();
+ IEnumerable<Jellyfin.Data.Entities.User> GetEnabledUsers();
/// <summary>
/// Gets the internal channels.
@@ -221,7 +221,7 @@ namespace MediaBrowser.Controller.LiveTv
/// <param name="fields">The fields.</param>
/// <param name="user">The user.</param>
/// <returns>Task.</returns>
- Task AddInfoToProgramDto(IReadOnlyCollection<(BaseItem, BaseItemDto)> programs, ItemFields[] fields, User user = null);
+ Task AddInfoToProgramDto(IReadOnlyCollection<(BaseItem, BaseItemDto)> programs, ItemFields[] fields, Jellyfin.Data.Entities.User user = null);
/// <summary>
/// Saves the tuner host.
@@ -258,7 +258,7 @@ namespace MediaBrowser.Controller.LiveTv
/// <param name="items">The items.</param>
/// <param name="options">The options.</param>
/// <param name="user">The user.</param>
- void AddChannelInfo(IReadOnlyCollection<(BaseItemDto, LiveTvChannel)> items, DtoOptions options, User user);
+ void AddChannelInfo(IReadOnlyCollection<(BaseItemDto, LiveTvChannel)> items, DtoOptions options, Jellyfin.Data.Entities.User user);
Task<List<ChannelInfo>> GetChannelsForListingsProvider(string id, CancellationToken cancellationToken);
Task<List<ChannelInfo>> GetChannelsFromListingsProviderData(string id, CancellationToken cancellationToken);
@@ -277,9 +277,9 @@ namespace MediaBrowser.Controller.LiveTv
ActiveRecordingInfo GetActiveRecordingInfo(string path);
- void AddInfoToRecordingDto(BaseItem item, BaseItemDto dto, ActiveRecordingInfo activeRecordingInfo, User user = null);
+ void AddInfoToRecordingDto(BaseItem item, BaseItemDto dto, ActiveRecordingInfo activeRecordingInfo, Jellyfin.Data.Entities.User user = null);
- List<BaseItem> GetRecordingFolders(User user);
+ List<BaseItem> GetRecordingFolders(Jellyfin.Data.Entities.User user);
}
public class ActiveRecordingInfo
diff --git a/MediaBrowser.Controller/LiveTv/LiveTvChannel.cs b/MediaBrowser.Controller/LiveTv/LiveTvChannel.cs
index 60391bb83..f3ff8bd04 100644
--- a/MediaBrowser.Controller/LiveTv/LiveTvChannel.cs
+++ b/MediaBrowser.Controller/LiveTv/LiveTvChannel.cs
@@ -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.Entities;
using MediaBrowser.Model.Configuration;
using MediaBrowser.Model.Dto;
diff --git a/MediaBrowser.Controller/LiveTv/LiveTvProgram.cs b/MediaBrowser.Controller/LiveTv/LiveTvProgram.cs
index 13df85aed..e6dc4bdda 100644
--- a/MediaBrowser.Controller/LiveTv/LiveTvProgram.cs
+++ b/MediaBrowser.Controller/LiveTv/LiveTvProgram.cs
@@ -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.Common.Configuration;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Providers;
diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs
index 61a330675..2559bc248 100644
--- a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs
+++ b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs
@@ -5,6 +5,7 @@ using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
+using Jellyfin.Data.Enums;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Extensions;
using MediaBrowser.Model.Configuration;
@@ -1991,7 +1992,7 @@ namespace MediaBrowser.Controller.MediaEncoding
filters.Add("hwupload");
}
- // When the input may or may not be hardware QSV decodable
+ // When the input may or may not be hardware QSV decodable
else if (string.Equals(outputVideoCodec, "h264_qsv", StringComparison.OrdinalIgnoreCase))
{
if (!hasTextSubs)
@@ -2147,7 +2148,7 @@ namespace MediaBrowser.Controller.MediaEncoding
var user = state.User;
// If the user doesn't have access to transcoding, then force stream copy, regardless of whether it will be compatible or not
- if (user != null && !user.Policy.EnableVideoPlaybackTranscoding)
+ if (user != null && !user.HasPermission(PermissionKind.EnableVideoPlaybackTranscoding))
{
state.OutputVideoCodec = "copy";
}
@@ -2163,7 +2164,7 @@ namespace MediaBrowser.Controller.MediaEncoding
var user = state.User;
// If the user doesn't have access to transcoding, then force stream copy, regardless of whether it will be compatible or not
- if (user != null && !user.Policy.EnableAudioPlaybackTranscoding)
+ if (user != null && !user.HasPermission(PermissionKind.EnableAudioPlaybackTranscoding))
{
state.OutputAudioCodec = "copy";
}
diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingJobInfo.cs b/MediaBrowser.Controller/MediaEncoding/EncodingJobInfo.cs
index 1127a08de..dc4361fc3 100644
--- a/MediaBrowser.Controller/MediaEncoding/EncodingJobInfo.cs
+++ b/MediaBrowser.Controller/MediaEncoding/EncodingJobInfo.cs
@@ -18,23 +18,38 @@ namespace MediaBrowser.Controller.MediaEncoding
public class EncodingJobInfo
{
public MediaStream VideoStream { get; set; }
+
public VideoType VideoType { get; set; }
+
public Dictionary<string, string> RemoteHttpHeaders { get; set; }
+
public string OutputVideoCodec { get; set; }
+
public MediaProtocol InputProtocol { get; set; }
+
public string MediaPath { get; set; }
+
public bool IsInputVideo { get; set; }
+
public IIsoMount IsoMount { get; set; }
+
public string[] PlayableStreamFileNames { get; set; }
+
public string OutputAudioCodec { get; set; }
+
public int? OutputVideoBitrate { get; set; }
+
public MediaStream SubtitleStream { get; set; }
+
public SubtitleDeliveryMethod SubtitleDeliveryMethod { get; set; }
+
public string[] SupportedSubtitleCodecs { get; set; }
public int InternalSubtitleStreamOffset { get; set; }
+
public MediaSourceInfo MediaSource { get; set; }
- public User User { get; set; }
+
+ public Jellyfin.Data.Entities.User User { get; set; }
public long? RunTimeTicks { get; set; }
diff --git a/MediaBrowser.Controller/Net/AuthorizationInfo.cs b/MediaBrowser.Controller/Net/AuthorizationInfo.cs
index 3e004763d..4361e253b 100644
--- a/MediaBrowser.Controller/Net/AuthorizationInfo.cs
+++ b/MediaBrowser.Controller/Net/AuthorizationInfo.cs
@@ -1,36 +1,40 @@
using System;
-using MediaBrowser.Controller.Entities;
+using Jellyfin.Data.Entities;
namespace MediaBrowser.Controller.Net
{
public class AuthorizationInfo
{
/// <summary>
- /// Gets or sets the user identifier.
+ /// Gets the user identifier.
/// </summary>
/// <value>The user identifier.</value>
- public Guid UserId => User == null ? Guid.Empty : User.Id;
+ public Guid UserId => User?.Id ?? Guid.Empty;
/// <summary>
/// Gets or sets the device identifier.
/// </summary>
/// <value>The device identifier.</value>
public string DeviceId { get; set; }
+
/// <summary>
/// Gets or sets the device.
/// </summary>
/// <value>The device.</value>
public string Device { get; set; }
+
/// <summary>
/// Gets or sets the client.
/// </summary>
/// <value>The client.</value>
public string Client { get; set; }
+
/// <summary>
/// Gets or sets the version.
/// </summary>
/// <value>The version.</value>
public string Version { get; set; }
+
/// <summary>
/// Gets or sets the token.
/// </summary>
diff --git a/MediaBrowser.Controller/Net/IAuthService.cs b/MediaBrowser.Controller/Net/IAuthService.cs
index 9132404a0..61fc7e6e6 100644
--- a/MediaBrowser.Controller/Net/IAuthService.cs
+++ b/MediaBrowser.Controller/Net/IAuthService.cs
@@ -9,6 +9,6 @@ namespace MediaBrowser.Controller.Net
public interface IAuthService
{
void Authenticate(IRequest request, IAuthenticationAttributes authAttribtues);
- User? Authenticate(HttpRequest request, IAuthenticationAttributes authAttribtues);
+ Jellyfin.Data.Entities.User? Authenticate(HttpRequest request, IAuthenticationAttributes authAttribtues);
}
}
diff --git a/MediaBrowser.Controller/Net/ISessionContext.cs b/MediaBrowser.Controller/Net/ISessionContext.cs
index 5c3c19f6b..421ac3fe2 100644
--- a/MediaBrowser.Controller/Net/ISessionContext.cs
+++ b/MediaBrowser.Controller/Net/ISessionContext.cs
@@ -1,4 +1,4 @@
-using MediaBrowser.Controller.Entities;
+using Jellyfin.Data.Entities;
using MediaBrowser.Controller.Session;
using MediaBrowser.Model.Services;
diff --git a/MediaBrowser.Controller/Notifications/INotificationService.cs b/MediaBrowser.Controller/Notifications/INotificationService.cs
index 8c6019923..2bc751758 100644
--- a/MediaBrowser.Controller/Notifications/INotificationService.cs
+++ b/MediaBrowser.Controller/Notifications/INotificationService.cs
@@ -25,6 +25,6 @@ namespace MediaBrowser.Controller.Notifications
/// </summary>
/// <param name="user">The user.</param>
/// <returns><c>true</c> if [is enabled for user] [the specified user identifier]; otherwise, <c>false</c>.</returns>
- bool IsEnabledForUser(User user);
+ bool IsEnabledForUser(Jellyfin.Data.Entities.User user);
}
}
diff --git a/MediaBrowser.Controller/Notifications/UserNotification.cs b/MediaBrowser.Controller/Notifications/UserNotification.cs
index 3f46468b3..a1029589b 100644
--- a/MediaBrowser.Controller/Notifications/UserNotification.cs
+++ b/MediaBrowser.Controller/Notifications/UserNotification.cs
@@ -1,5 +1,5 @@
using System;
-using MediaBrowser.Controller.Entities;
+using Jellyfin.Data.Entities;
using MediaBrowser.Model.Notifications;
namespace MediaBrowser.Controller.Notifications
diff --git a/MediaBrowser.Controller/Persistence/IUserRepository.cs b/MediaBrowser.Controller/Persistence/IUserRepository.cs
deleted file mode 100644
index cd23e5223..000000000
--- a/MediaBrowser.Controller/Persistence/IUserRepository.cs
+++ /dev/null
@@ -1,27 +0,0 @@
-using System.Collections.Generic;
-using MediaBrowser.Controller.Entities;
-
-namespace MediaBrowser.Controller.Persistence
-{
- /// <summary>
- /// Provides an interface to implement a User repository
- /// </summary>
- public interface IUserRepository : IRepository
- {
- /// <summary>
- /// Deletes the user.
- /// </summary>
- /// <param name="user">The user.</param>
- /// <returns>Task.</returns>
- void DeleteUser(User user);
-
- /// <summary>
- /// Retrieves all users.
- /// </summary>
- /// <returns>IEnumerable{User}.</returns>
- List<User> RetrieveAllUsers();
-
- void CreateUser(User user);
- void UpdateUser(User user);
- }
-}
diff --git a/MediaBrowser.Controller/Playlists/Playlist.cs b/MediaBrowser.Controller/Playlists/Playlist.cs
index 3b08e72b9..03bdf1eaf 100644
--- a/MediaBrowser.Controller/Playlists/Playlist.cs
+++ b/MediaBrowser.Controller/Playlists/Playlist.cs
@@ -78,7 +78,7 @@ namespace MediaBrowser.Controller.Playlists
return 1;
}
- public override bool IsAuthorizedToDelete(User user, List<Folder> allCollectionFolders)
+ public override bool IsAuthorizedToDelete(Jellyfin.Data.Entities.User user, List<Folder> allCollectionFolders)
{
return true;
}
@@ -99,7 +99,7 @@ namespace MediaBrowser.Controller.Playlists
return Task.CompletedTask;
}
- 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 GetPlayableItems(user, query);
}
@@ -109,7 +109,7 @@ namespace MediaBrowser.Controller.Playlists
return new List<BaseItem>();
}
- public override IEnumerable<BaseItem> GetRecursiveChildren(User user, InternalItemsQuery query)
+ public override IEnumerable<BaseItem> GetRecursiveChildren(Jellyfin.Data.Entities.User user, InternalItemsQuery query)
{
return GetPlayableItems(user, query);
}
@@ -119,7 +119,7 @@ namespace MediaBrowser.Controller.Playlists
return GetLinkedChildrenInfos();
}
- private List<BaseItem> GetPlayableItems(User user, InternalItemsQuery query)
+ private List<BaseItem> GetPlayableItems(Jellyfin.Data.Entities.User user, InternalItemsQuery query)
{
if (query == null)
{
@@ -131,7 +131,7 @@ namespace MediaBrowser.Controller.Playlists
return base.GetChildren(user, true, query);
}
- public static List<BaseItem> GetPlaylistItems(string playlistMediaType, IEnumerable<BaseItem> inputItems, User user, DtoOptions options)
+ public static List<BaseItem> GetPlaylistItems(string playlistMediaType, IEnumerable<BaseItem> inputItems, Jellyfin.Data.Entities.User user, DtoOptions options)
{
if (user != null)
{
@@ -149,7 +149,7 @@ namespace MediaBrowser.Controller.Playlists
return list;
}
- private static IEnumerable<BaseItem> GetPlaylistItems(BaseItem item, User user, string mediaType, DtoOptions options)
+ private static IEnumerable<BaseItem> GetPlaylistItems(BaseItem item, Jellyfin.Data.Entities.User user, string mediaType, DtoOptions options)
{
if (item is MusicGenre musicGenre)
{
@@ -222,7 +222,7 @@ namespace MediaBrowser.Controller.Playlists
}
}
- public override bool IsVisible(User user)
+ public override bool IsVisible(Jellyfin.Data.Entities.User user)
{
if (!IsSharedItem)
{
@@ -241,18 +241,10 @@ namespace MediaBrowser.Controller.Playlists
}
var userId = user.Id.ToString("N", CultureInfo.InvariantCulture);
- foreach (var share in shares)
- {
- if (string.Equals(share.UserId, userId, StringComparison.OrdinalIgnoreCase))
- {
- return true;
- }
- }
-
- return false;
+ return shares.Any(share => string.Equals(share.UserId, userId, StringComparison.OrdinalIgnoreCase));
}
- public override bool IsVisibleStandalone(User user)
+ public override bool IsVisibleStandalone(Jellyfin.Data.Entities.User user)
{
if (!IsSharedItem)
{
diff --git a/MediaBrowser.Controller/Providers/IProviderManager.cs b/MediaBrowser.Controller/Providers/IProviderManager.cs
index 254b27460..955db0278 100644
--- a/MediaBrowser.Controller/Providers/IProviderManager.cs
+++ b/MediaBrowser.Controller/Providers/IProviderManager.cs
@@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
+using Jellyfin.Data.Entities;
using MediaBrowser.Common.Net;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Library;
@@ -70,6 +71,8 @@ namespace MediaBrowser.Controller.Providers
/// <returns>Task.</returns>
Task SaveImage(BaseItem item, string source, string mimeType, ImageType type, int? imageIndex, bool? saveLocallyWithMedia, CancellationToken cancellationToken);
+ Task SaveImage(User user, Stream source, string mimeType, string path);
+
/// <summary>
/// Adds the metadata providers.
/// </summary>
diff --git a/MediaBrowser.Controller/Session/ISessionManager.cs b/MediaBrowser.Controller/Session/ISessionManager.cs
index 771027103..32e62db14 100644
--- a/MediaBrowser.Controller/Session/ISessionManager.cs
+++ b/MediaBrowser.Controller/Session/ISessionManager.cs
@@ -74,7 +74,7 @@ namespace MediaBrowser.Controller.Session
/// <param name="deviceName">Name of the device.</param>
/// <param name="remoteEndPoint">The remote end point.</param>
/// <param name="user">The user.</param>
- SessionInfo LogSessionActivity(string appName, string appVersion, string deviceId, string deviceName, string remoteEndPoint, User user);
+ SessionInfo LogSessionActivity(string appName, string appVersion, string deviceId, string deviceName, string remoteEndPoint, Jellyfin.Data.Entities.User user);
void UpdateDeviceName(string sessionId, string reportedDeviceName);
diff --git a/MediaBrowser.Controller/Sorting/IUserBaseItemComparer.cs b/MediaBrowser.Controller/Sorting/IUserBaseItemComparer.cs
index 1e2df37bf..f079bc7ea 100644
--- a/MediaBrowser.Controller/Sorting/IUserBaseItemComparer.cs
+++ b/MediaBrowser.Controller/Sorting/IUserBaseItemComparer.cs
@@ -12,7 +12,7 @@ namespace MediaBrowser.Controller.Sorting
/// Gets or sets the user.
/// </summary>
/// <value>The user.</value>
- User User { get; set; }
+ Jellyfin.Data.Entities.User User { get; set; }
/// <summary>
/// Gets or sets the user manager.
diff --git a/MediaBrowser.Model/Configuration/AccessSchedule.cs b/MediaBrowser.Model/Configuration/AccessSchedule.cs
index 120c47dbc..7bd355449 100644
--- a/MediaBrowser.Model/Configuration/AccessSchedule.cs
+++ b/MediaBrowser.Model/Configuration/AccessSchedule.cs
@@ -1,3 +1,5 @@
+using Jellyfin.Data.Enums;
+
#pragma warning disable CS1591
namespace MediaBrowser.Model.Configuration
diff --git a/MediaBrowser.Model/Configuration/UserConfiguration.cs b/MediaBrowser.Model/Configuration/UserConfiguration.cs
index a475c9910..79cf0a065 100644
--- a/MediaBrowser.Model/Configuration/UserConfiguration.cs
+++ b/MediaBrowser.Model/Configuration/UserConfiguration.cs
@@ -1,6 +1,7 @@
#pragma warning disable CS1591
using System;
+using Jellyfin.Data.Enums;
namespace MediaBrowser.Model.Configuration
{
diff --git a/MediaBrowser.Model/Notifications/NotificationOptions.cs b/MediaBrowser.Model/Notifications/NotificationOptions.cs
index 9c54bd70e..f4132002b 100644
--- a/MediaBrowser.Model/Notifications/NotificationOptions.cs
+++ b/MediaBrowser.Model/Notifications/NotificationOptions.cs
@@ -1,6 +1,8 @@
#pragma warning disable CS1591
using System;
+using Jellyfin.Data.Enums;
+using MediaBrowser.Model.Extensions;
using System.Linq;
using MediaBrowser.Model.Users;
@@ -113,7 +115,7 @@ namespace MediaBrowser.Model.Notifications
!opt.DisabledMonitorUsers.Contains(userId.ToString(""), StringComparer.OrdinalIgnoreCase);
}
- public bool IsEnabledToSendToUser(string type, string userId, UserPolicy userPolicy)
+ public bool IsEnabledToSendToUser(string type, string userId, Jellyfin.Data.Entities.User user)
{
NotificationOption opt = GetOptions(type);
@@ -124,7 +126,7 @@ namespace MediaBrowser.Model.Notifications
return true;
}
- if (opt.SendToUserMode == SendToUserType.Admins && userPolicy.IsAdministrator)
+ if (opt.SendToUserMode == SendToUserType.Admins && user.HasPermission(PermissionKind.IsAdministrator))
{
return true;
}
diff --git a/MediaBrowser.Model/Users/UserPolicy.cs b/MediaBrowser.Model/Users/UserPolicy.cs
index ae2b3fd4e..8d94d3971 100644
--- a/MediaBrowser.Model/Users/UserPolicy.cs
+++ b/MediaBrowser.Model/Users/UserPolicy.cs
@@ -1,7 +1,8 @@
#pragma warning disable CS1591
using System;
-using MediaBrowser.Model.Configuration;
+using Jellyfin.Data.Entities;
+using Jellyfin.Data.Enums;
namespace MediaBrowser.Model.Users
{
@@ -33,7 +34,7 @@ namespace MediaBrowser.Model.Users
public string[] BlockedTags { get; set; }
public bool EnableUserPreferenceAccess { get; set; }
- public AccessSchedule[] AccessSchedules { get; set; }
+ public Jellyfin.Data.Entities.AccessSchedule[] AccessSchedules { get; set; }
public UnratedItem[] BlockUnratedItems { get; set; }
public bool EnableRemoteControlOfOtherUsers { get; set; }
public bool EnableSharedDeviceControl { get; set; }
@@ -77,7 +78,7 @@ namespace MediaBrowser.Model.Users
public string[] BlockedChannels { get; set; }
public int RemoteClientBitrateLimit { get; set; }
- public string AuthenticationProviderId { get; set; }
+ public string AuthenticatioIsnProviderId { get; set; }
public string PasswordResetProviderId { get; set; }
public UserPolicy()
diff --git a/MediaBrowser.Providers/Manager/ImageSaver.cs b/MediaBrowser.Providers/Manager/ImageSaver.cs
index 3ab621ba4..6bed38780 100644
--- a/MediaBrowser.Providers/Manager/ImageSaver.cs
+++ b/MediaBrowser.Providers/Manager/ImageSaver.cs
@@ -5,17 +5,21 @@ using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
+using Jellyfin.Data.Entities;
using MediaBrowser.Common.Configuration;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.Audio;
-using MediaBrowser.Controller.Entities.TV;
using MediaBrowser.Controller.Library;
using MediaBrowser.Model.Configuration;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.IO;
using MediaBrowser.Model.Net;
using Microsoft.Extensions.Logging;
+using Episode = MediaBrowser.Controller.Entities.TV.Episode;
+using MusicAlbum = MediaBrowser.Controller.Entities.Audio.MusicAlbum;
+using Person = MediaBrowser.Controller.Entities.Person;
+using Season = MediaBrowser.Controller.Entities.TV.Season;
namespace MediaBrowser.Providers.Manager
{
@@ -78,11 +82,6 @@ namespace MediaBrowser.Providers.Manager
var saveLocally = item.SupportsLocalMetadata && item.IsSaveLocalMetadataEnabled() && !item.ExtraType.HasValue && !(item is Audio);
- if (item is User)
- {
- saveLocally = true;
- }
-
if (type != ImageType.Primary && item is Episode)
{
saveLocally = false;
@@ -136,7 +135,7 @@ namespace MediaBrowser.Providers.Manager
var savedPaths = new List<string>();
- using (source)
+ await using (source)
{
var currentPathIndex = 0;
@@ -172,7 +171,6 @@ namespace MediaBrowser.Providers.Manager
}
catch (FileNotFoundException)
{
-
}
finally
{
@@ -181,6 +179,16 @@ namespace MediaBrowser.Providers.Manager
}
}
+ public async Task SaveImage(User user, Stream source, string mimeType, string path)
+ {
+ if (string.IsNullOrEmpty(mimeType))
+ {
+ throw new ArgumentNullException(nameof(mimeType));
+ }
+
+ await SaveImageToLocation(source, path, path, CancellationToken.None).ConfigureAwait(false);
+ }
+
private async Task<string> SaveImageToLocation(Stream source, string path, string retryPath, CancellationToken cancellationToken)
{
try
@@ -244,7 +252,7 @@ namespace MediaBrowser.Providers.Manager
_fileSystem.SetAttributes(path, false, false);
- using (var fs = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.Read, IODefaults.FileStreamBufferSize, FileOptions.Asynchronous))
+ await using (var fs = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.Read, IODefaults.FileStreamBufferSize, FileOptions.Asynchronous))
{
await source.CopyToAsync(fs, IODefaults.CopyToBufferSize, cancellationToken).ConfigureAwait(false);
}
@@ -439,7 +447,6 @@ namespace MediaBrowser.Providers.Manager
{
path = Path.Combine(Path.GetDirectoryName(item.Path), "metadata", filename + extension);
}
-
else if (item.IsInMixedFolder)
{
path = GetSavePathForItemInMixedFolder(item, type, filename, extension);
diff --git a/MediaBrowser.Providers/Manager/ProviderManager.cs b/MediaBrowser.Providers/Manager/ProviderManager.cs
index cfff89767..f4e875a24 100644
--- a/MediaBrowser.Providers/Manager/ProviderManager.cs
+++ b/MediaBrowser.Providers/Manager/ProviderManager.cs
@@ -8,6 +8,7 @@ using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
+using Jellyfin.Data.Entities;
using MediaBrowser.Common.Net;
using MediaBrowser.Common.Progress;
using MediaBrowser.Controller;
@@ -16,7 +17,6 @@ using MediaBrowser.Controller.Dto;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.Audio;
using MediaBrowser.Controller.Entities.Movies;
-using MediaBrowser.Controller.Entities.TV;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Controller.Subtitles;
@@ -28,6 +28,12 @@ using MediaBrowser.Model.Providers;
using MediaBrowser.Model.Serialization;
using Microsoft.Extensions.Logging;
using Priority_Queue;
+using Book = MediaBrowser.Controller.Entities.Book;
+using Episode = MediaBrowser.Controller.Entities.TV.Episode;
+using Movie = MediaBrowser.Controller.Entities.Movies.Movie;
+using MusicAlbum = MediaBrowser.Controller.Entities.Audio.MusicAlbum;
+using Season = MediaBrowser.Controller.Entities.TV.Season;
+using Series = MediaBrowser.Controller.Entities.TV.Series;
namespace MediaBrowser.Providers.Manager
{
@@ -182,6 +188,12 @@ namespace MediaBrowser.Providers.Manager
return new ImageSaver(_configurationManager, _libraryMonitor, _fileSystem, _logger).SaveImage(item, fileStream, mimeType, type, imageIndex, saveLocallyWithMedia, cancellationToken);
}
+ public Task SaveImage(User user, Stream source, string mimeType, string path)
+ {
+ return new ImageSaver(_configurationManager, _libraryMonitor, _fileSystem, _logger)
+ .SaveImage(user, source, mimeType, path);
+ }
+
public async Task<IEnumerable<RemoteImageInfo>> GetAvailableRemoteImages(BaseItem item, RemoteImageQuery query, CancellationToken cancellationToken)
{
var providers = GetRemoteImageProviders(item, query.IncludeDisabledProviders);
diff --git a/MediaBrowser.Providers/Users/UserMetadataService.cs b/MediaBrowser.Providers/Users/UserMetadataService.cs
deleted file mode 100644
index fb6c91b6c..000000000
--- a/MediaBrowser.Providers/Users/UserMetadataService.cs
+++ /dev/null
@@ -1,30 +0,0 @@
-using MediaBrowser.Controller.Configuration;
-using MediaBrowser.Controller.Entities;
-using MediaBrowser.Controller.Library;
-using MediaBrowser.Controller.Providers;
-using MediaBrowser.Model.Entities;
-using MediaBrowser.Model.IO;
-using MediaBrowser.Providers.Manager;
-using Microsoft.Extensions.Logging;
-
-namespace MediaBrowser.Providers.Users
-{
- public class UserMetadataService : MetadataService<User, ItemLookupInfo>
- {
- public UserMetadataService(
- IServerConfigurationManager serverConfigurationManager,
- ILogger<UserMetadataService> logger,
- IProviderManager providerManager,
- IFileSystem fileSystem,
- ILibraryManager libraryManager)
- : base(serverConfigurationManager, logger, providerManager, fileSystem, libraryManager)
- {
- }
-
- /// <inheritdoc />
- protected override void MergeData(MetadataResult<User> source, MetadataResult<User> target, MetadataFields[] lockedFields, bool replaceData, bool mergeMetadataSettings)
- {
- ProviderUtils.MergeBaseItemData(source, target, lockedFields, replaceData, mergeMetadataSettings);
- }
- }
-}
diff --git a/tests/Jellyfin.Api.Tests/Auth/CustomAuthenticationHandlerTests.cs b/tests/Jellyfin.Api.Tests/Auth/CustomAuthenticationHandlerTests.cs
index 3b3d03c8b..0cb8d8be6 100644
--- a/tests/Jellyfin.Api.Tests/Auth/CustomAuthenticationHandlerTests.cs
+++ b/tests/Jellyfin.Api.Tests/Auth/CustomAuthenticationHandlerTests.cs
@@ -7,6 +7,7 @@ using AutoFixture;
using AutoFixture.AutoMoq;
using Jellyfin.Api.Auth;
using Jellyfin.Api.Constants;
+using Jellyfin.Data.Enums;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Net;
using Microsoft.AspNetCore.Authentication;
@@ -83,7 +84,7 @@ namespace Jellyfin.Api.Tests.Auth
a => a.Authenticate(
It.IsAny<HttpRequest>(),
It.IsAny<AuthenticatedAttribute>()))
- .Returns((User?)null);
+ .Returns((Jellyfin.Data.Entities.User?)null);
var authenticateResult = await _sut.AuthenticateAsync();
@@ -124,7 +125,7 @@ namespace Jellyfin.Api.Tests.Auth
var user = SetupUser();
var authenticateResult = await _sut.AuthenticateAsync();
- Assert.True(authenticateResult.Principal.HasClaim(ClaimTypes.Name, user.Name));
+ Assert.True(authenticateResult.Principal.HasClaim(ClaimTypes.Name, user.Username));
}
[Theory]
@@ -135,7 +136,7 @@ namespace Jellyfin.Api.Tests.Auth
var user = SetupUser(isAdmin);
var authenticateResult = await _sut.AuthenticateAsync();
- var expectedRole = user.Policy.IsAdministrator ? UserRoles.Administrator : UserRoles.User;
+ var expectedRole = user.HasPermission(PermissionKind.IsAdministrator) ? UserRoles.Administrator : UserRoles.User;
Assert.True(authenticateResult.Principal.HasClaim(ClaimTypes.Role, expectedRole));
}
@@ -148,10 +149,10 @@ namespace Jellyfin.Api.Tests.Auth
Assert.Equal(_scheme.Name, authenticatedResult.Ticket.AuthenticationScheme);
}
- private User SetupUser(bool isAdmin = false)
+ private Jellyfin.Data.Entities.User SetupUser(bool isAdmin = false)
{
- var user = _fixture.Create<User>();
- user.Policy.IsAdministrator = isAdmin;
+ var user = _fixture.Create<Jellyfin.Data.Entities.User>();
+ user.SetPermission(PermissionKind.IsAdministrator, isAdmin);
_jellyfinAuthServiceMock.Setup(
a => a.Authenticate(