瀏覽代碼

Merge branch 'master' into hwaccel

dkanada 5 年之前
父節點
當前提交
dabb869a6b
共有 100 個文件被更改,包括 447 次插入1907 次删除
  1. 12 19
      .ci/azure-pipelines-abi.yml
  2. 9 0
      .github/dependabot.yml
  3. 2 0
      CONTRIBUTORS.md
  4. 5 3
      Emby.Dlna/ContentDirectory/ContentDirectory.cs
  5. 23 16
      Emby.Dlna/ContentDirectory/ControlHandler.cs
  6. 17 14
      Emby.Dlna/Didl/DidlBuilder.cs
  7. 1 2
      Emby.Dlna/Didl/Filter.cs
  8. 6 7
      Emby.Dlna/DlnaManager.cs
  9. 0 1
      Emby.Dlna/Eventing/EventManager.cs
  10. 7 5
      Emby.Dlna/Main/DlnaEntryPoint.cs
  11. 1 4
      Emby.Dlna/PlayTo/Device.cs
  12. 9 1
      Emby.Dlna/PlayTo/PlayToController.cs
  13. 3 4
      Emby.Dlna/PlayTo/PlayToManager.cs
  14. 0 1
      Emby.Dlna/PlayTo/SsdpHttpClient.cs
  15. 1 1
      Emby.Dlna/Service/BaseService.cs
  16. 1 1
      Emby.Dlna/Ssdp/DeviceDiscovery.cs
  17. 11 2
      Emby.Drawing/ImageProcessor.cs
  18. 6 4
      Emby.Notifications/Api/NotificationsService.cs
  19. 1 1
      Emby.Notifications/NotificationEntryPoint.cs
  20. 6 4
      Emby.Notifications/NotificationManager.cs
  21. 2 2
      Emby.Photos/PhotoProvider.cs
  22. 22 34
      Emby.Server.Implementations/Activity/ActivityLogEntryPoint.cs
  23. 2 2
      Emby.Server.Implementations/AppBase/BaseConfigurationManager.cs
  24. 2 10
      Emby.Server.Implementations/ApplicationHost.cs
  25. 1 1
      Emby.Server.Implementations/Browser/BrowserLauncher.cs
  26. 10 5
      Emby.Server.Implementations/Channels/ChannelManager.cs
  27. 1 1
      Emby.Server.Implementations/Channels/RefreshChannelsScheduledTask.cs
  28. 4 3
      Emby.Server.Implementations/Collections/CollectionManager.cs
  29. 2 3
      Emby.Server.Implementations/Data/BaseSqliteRepository.cs
  30. 1 2
      Emby.Server.Implementations/Data/CleanDatabaseScheduledTask.cs
  31. 1 1
      Emby.Server.Implementations/Data/SqliteDisplayPreferencesRepository.cs
  32. 6 13
      Emby.Server.Implementations/Data/SqliteItemRepository.cs
  33. 2 1
      Emby.Server.Implementations/Data/SqliteUserDataRepository.cs
  34. 0 240
      Emby.Server.Implementations/Data/SqliteUserRepository.cs
  35. 2 2
      Emby.Server.Implementations/Devices/DeviceId.cs
  36. 10 21
      Emby.Server.Implementations/Devices/DeviceManager.cs
  37. 21 17
      Emby.Server.Implementations/Dto/DtoService.cs
  38. 5 5
      Emby.Server.Implementations/Emby.Server.Implementations.csproj
  39. 1 1
      Emby.Server.Implementations/EntryPoints/ExternalPortForwarding.cs
  40. 2 2
      Emby.Server.Implementations/EntryPoints/LibraryChangedNotifier.cs
  41. 3 2
      Emby.Server.Implementations/EntryPoints/RecordingNotifier.cs
  42. 0 77
      Emby.Server.Implementations/EntryPoints/RefreshUsersMetadata.cs
  43. 5 25
      Emby.Server.Implementations/EntryPoints/ServerEventNotifier.cs
  44. 1 2
      Emby.Server.Implementations/EntryPoints/UdpServerEntryPoint.cs
  45. 1 1
      Emby.Server.Implementations/HttpClientManager/HttpClientManager.cs
  46. 5 3
      Emby.Server.Implementations/HttpServer/HttpListenerHost.cs
  47. 2 3
      Emby.Server.Implementations/HttpServer/HttpResultFactory.cs
  48. 0 1
      Emby.Server.Implementations/HttpServer/ResponseFilter.cs
  49. 15 13
      Emby.Server.Implementations/HttpServer/Security/AuthService.cs
  50. 2 3
      Emby.Server.Implementations/HttpServer/Security/AuthorizationContext.cs
  51. 1 1
      Emby.Server.Implementations/HttpServer/Security/SessionContext.cs
  52. 7 5
      Emby.Server.Implementations/HttpServer/WebSocketConnection.cs
  53. 1 3
      Emby.Server.Implementations/IO/LibraryMonitor.cs
  54. 2 2
      Emby.Server.Implementations/IO/ManagedFileSystem.cs
  55. 0 1
      Emby.Server.Implementations/Images/CollectionFolderImageProvider.cs
  56. 0 1
      Emby.Server.Implementations/Images/DynamicImageProvider.cs
  57. 19 12
      Emby.Server.Implementations/Library/LibraryManager.cs
  58. 2 5
      Emby.Server.Implementations/Library/LiveStreamHelper.cs
  59. 26 32
      Emby.Server.Implementations/Library/MediaSourceManager.cs
  60. 1 1
      Emby.Server.Implementations/Library/MediaStreamSelector.cs
  61. 7 11
      Emby.Server.Implementations/Library/MusicManager.cs
  62. 2 2
      Emby.Server.Implementations/Library/Resolvers/Audio/AudioResolver.cs
  63. 2 2
      Emby.Server.Implementations/Library/Resolvers/Audio/MusicAlbumResolver.cs
  64. 2 2
      Emby.Server.Implementations/Library/Resolvers/BaseVideoResolver.cs
  65. 1 1
      Emby.Server.Implementations/Library/Resolvers/Movies/BoxSetResolver.cs
  66. 2 2
      Emby.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs
  67. 1 2
      Emby.Server.Implementations/Library/Resolvers/SpecialFolderResolver.cs
  68. 1 2
      Emby.Server.Implementations/Library/Resolvers/TV/SeasonResolver.cs
  69. 4 4
      Emby.Server.Implementations/Library/Resolvers/TV/SeriesResolver.cs
  70. 4 2
      Emby.Server.Implementations/Library/SearchEngine.cs
  71. 3 1
      Emby.Server.Implementations/Library/UserDataManager.cs
  72. 0 1107
      Emby.Server.Implementations/Library/UserManager.cs
  73. 20 10
      Emby.Server.Implementations/Library/UserViewManager.cs
  74. 2 2
      Emby.Server.Implementations/Library/Validators/ArtistsPostScanTask.cs
  75. 2 3
      Emby.Server.Implementations/Library/Validators/ArtistsValidator.cs
  76. 2 2
      Emby.Server.Implementations/Library/Validators/GenresPostScanTask.cs
  77. 2 2
      Emby.Server.Implementations/Library/Validators/GenresValidator.cs
  78. 2 2
      Emby.Server.Implementations/Library/Validators/MusicGenresPostScanTask.cs
  79. 2 2
      Emby.Server.Implementations/Library/Validators/MusicGenresValidator.cs
  80. 2 2
      Emby.Server.Implementations/Library/Validators/StudiosPostScanTask.cs
  81. 2 3
      Emby.Server.Implementations/Library/Validators/StudiosValidator.cs
  82. 9 10
      Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs
  83. 5 5
      Emby.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs
  84. 0 1
      Emby.Server.Implementations/LiveTv/EmbyTV/RecordingHelper.cs
  85. 6 7
      Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs
  86. 1 1
      Emby.Server.Implementations/LiveTv/Listings/XmlTvListingsProvider.cs
  87. 1 1
      Emby.Server.Implementations/LiveTv/LiveTvDtoService.cs
  88. 20 27
      Emby.Server.Implementations/LiveTv/LiveTvManager.cs
  89. 1 1
      Emby.Server.Implementations/LiveTv/LiveTvMediaSourceProvider.cs
  90. 3 5
      Emby.Server.Implementations/LiveTv/TunerHosts/BaseTunerHost.cs
  91. 2 5
      Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs
  92. 7 7
      Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunUdpStream.cs
  93. 0 2
      Emby.Server.Implementations/LiveTv/TunerHosts/LiveStream.cs
  94. 0 1
      Emby.Server.Implementations/LiveTv/TunerHosts/M3UTunerHost.cs
  95. 1 2
      Emby.Server.Implementations/LiveTv/TunerHosts/M3uParser.cs
  96. 10 10
      Emby.Server.Implementations/LiveTv/TunerHosts/SharedHttpStream.cs
  97. 1 1
      Emby.Server.Implementations/Localization/LocalizationManager.cs
  98. 1 1
      Emby.Server.Implementations/MediaEncoder/EncodingManager.cs
  99. 1 3
      Emby.Server.Implementations/Net/SocketFactory.cs
  100. 2 2
      Emby.Server.Implementations/Networking/NetworkManager.cs

+ 12 - 19
.ci/azure-pipelines-abi.yml

@@ -33,6 +33,13 @@ jobs:
           packageType: sdk
           version: ${{ parameters.DotNetSdkVersion }}
 
+      - task: DotNetCoreCLI@2
+        displayName: 'Install ABI CompatibilityChecker tool'
+        inputs:
+          command: custom
+          custom: tool
+          arguments: 'update compatibilitychecker -g'
+
       - task: DownloadPipelineArtifact@2
         displayName: "Download New Assembly Build Artifact"
         inputs:
@@ -72,25 +79,11 @@ jobs:
           overWrite: true
           flattenFolders: true
 
-      - task: DownloadGitHubRelease@0
-        displayName: "Download ABI Compatibility Check Tool"
-        inputs:
-          connection: Jellyfin Release Download
-          userRepository: EraYaN/dotnet-compatibility
-          defaultVersionType: "latest"
-          itemPattern: "**-ci.zip"
-          downloadPath: "$(System.ArtifactsDirectory)"
-
-      - task: ExtractFiles@1
-        displayName: "Extract ABI Compatibility Check Tool"
-        inputs:
-          archiveFilePatterns: "$(System.ArtifactsDirectory)/*-ci.zip"
-          destinationFolder: $(System.ArtifactsDirectory)/tools
-          cleanDestinationFolder: true
-
       # The `--warnings-only` switch will swallow the return code and not emit any errors.
-      - task: CmdLine@2
-        displayName: "Execute ABI Compatibility Check Tool"
+      - task: DotNetCoreCLI@2
+        displayName: 'Execute ABI Compatibility Check Tool'
         inputs:
-          script: "dotnet tools/CompatibilityCheckerCLI.dll current-release/$(AssemblyFileName) new-release/$(AssemblyFileName) --azure-pipelines --warnings-only"
+          command: custom
+          custom: compat
+          arguments: 'current-release/$(AssemblyFileName) new-release/$(AssemblyFileName) --azure-pipelines --warnings-only'
           workingDirectory: $(System.ArtifactsDirectory)

+ 9 - 0
.github/dependabot.yml

@@ -0,0 +1,9 @@
+version: 2
+updates:
+- package-ecosystem: nuget
+  directory: "/"
+  schedule:
+    interval: weekly
+    time: '12:00'
+  open-pull-requests-limit: 10
+  

+ 2 - 0
CONTRIBUTORS.md

@@ -7,6 +7,7 @@
  - [anthonylavado](https://github.com/anthonylavado)
  - [Artiume](https://github.com/Artiume)
  - [AThomsen](https://github.com/AThomsen)
+ - [barronpm](https://github.com/barronpm)
  - [bilde2910](https://github.com/bilde2910)
  - [bfayers](https://github.com/bfayers)
  - [BnMcG](https://github.com/BnMcG)
@@ -130,6 +131,7 @@
  - [XVicarious](https://github.com/XVicarious)
  - [YouKnowBlom](https://github.com/YouKnowBlom)
  - [KristupasSavickas](https://github.com/KristupasSavickas)
+ - [Pusta](https://github.com/pusta)
 
 # Emby Contributors
 

+ 5 - 3
Emby.Dlna/ContentDirectory/ContentDirectory.cs

@@ -4,11 +4,12 @@ using System;
 using System.Linq;
 using System.Threading.Tasks;
 using Emby.Dlna.Service;
+using Jellyfin.Data.Entities;
+using Jellyfin.Data.Enums;
 using MediaBrowser.Common.Net;
 using MediaBrowser.Controller.Configuration;
 using MediaBrowser.Controller.Dlna;
 using MediaBrowser.Controller.Drawing;
-using MediaBrowser.Controller.Entities;
 using MediaBrowser.Controller.Library;
 using MediaBrowser.Controller.MediaEncoding;
 using MediaBrowser.Controller.TV;
@@ -32,7 +33,8 @@ namespace Emby.Dlna.ContentDirectory
         private readonly IMediaEncoder _mediaEncoder;
         private readonly ITVSeriesManager _tvSeriesManager;
 
-        public ContentDirectory(IDlnaManager dlna,
+        public ContentDirectory(
+            IDlnaManager dlna,
             IUserDataManager userDataManager,
             IImageProcessor imageProcessor,
             ILibraryManager libraryManager,
@@ -131,7 +133,7 @@ namespace Emby.Dlna.ContentDirectory
 
             foreach (var user in _userManager.Users)
             {
-                if (user.Policy.IsAdministrator)
+                if (user.HasPermission(PermissionKind.IsAdministrator))
                 {
                     return user;
                 }

+ 23 - 16
Emby.Dlna/ContentDirectory/ControlHandler.cs

@@ -10,6 +10,7 @@ using System.Threading;
 using System.Xml;
 using Emby.Dlna.Didl;
 using Emby.Dlna.Service;
+using Jellyfin.Data.Entities;
 using MediaBrowser.Common.Extensions;
 using MediaBrowser.Controller.Configuration;
 using MediaBrowser.Controller.Drawing;
@@ -17,7 +18,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.LiveTv;
 using MediaBrowser.Controller.MediaEncoding;
@@ -28,6 +28,12 @@ using MediaBrowser.Model.Entities;
 using MediaBrowser.Model.Globalization;
 using MediaBrowser.Model.Querying;
 using Microsoft.Extensions.Logging;
+using Book = MediaBrowser.Controller.Entities.Book;
+using Episode = MediaBrowser.Controller.Entities.TV.Episode;
+using Genre = MediaBrowser.Controller.Entities.Genre;
+using Movie = MediaBrowser.Controller.Entities.Movies.Movie;
+using MusicAlbum = MediaBrowser.Controller.Entities.Audio.MusicAlbum;
+using Series = MediaBrowser.Controller.Entities.TV.Series;
 
 namespace Emby.Dlna.ContentDirectory
 {
@@ -460,12 +466,12 @@ namespace Emby.Dlna.ContentDirectory
             }
             else if (search.SearchType == SearchType.Playlist)
             {
-                //items = items.OfType<Playlist>();
+                // items = items.OfType<Playlist>();
                 isFolder = true;
             }
             else if (search.SearchType == SearchType.MusicAlbum)
             {
-                //items = items.OfType<MusicAlbum>();
+                // items = items.OfType<MusicAlbum>();
                 isFolder = true;
             }
 
@@ -731,7 +737,7 @@ namespace Emby.Dlna.ContentDirectory
                 return GetGenres(item, user, query);
             }
 
-            var array = new ServerItem[]
+            var array = new[]
             {
                 new ServerItem(item)
                 {
@@ -920,7 +926,7 @@ namespace Emby.Dlna.ContentDirectory
         private QueryResult<ServerItem> GetMovieCollections(User user, InternalItemsQuery query)
         {
             query.Recursive = true;
-            //query.Parent = parent;
+            // query.Parent = parent;
             query.SetUser(user);
 
             query.IncludeItemTypes = new[] { typeof(BoxSet).Name };
@@ -1115,7 +1121,7 @@ namespace Emby.Dlna.ContentDirectory
         private QueryResult<ServerItem> GetMusicPlaylists(User user, InternalItemsQuery query)
         {
             query.Parent = null;
-            query.IncludeItemTypes = new[] { typeof(Playlist).Name };
+            query.IncludeItemTypes = new[] { nameof(Playlist) };
             query.SetUser(user);
             query.Recursive = true;
 
@@ -1132,10 +1138,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,7 +1155,6 @@ namespace Emby.Dlna.ContentDirectory
                 Limit = query.Limit,
                 StartIndex = query.StartIndex,
                 UserId = query.User.Id
-
             }, new[] { parent }, query.DtoOptions);
 
             return ToResult(result);
@@ -1167,7 +1171,6 @@ 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);
@@ -1177,14 +1180,14 @@ namespace Emby.Dlna.ContentDirectory
         {
             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);
@@ -1217,7 +1220,11 @@ namespace Emby.Dlna.ContentDirectory
                 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()

+ 17 - 14
Emby.Dlna/Didl/DidlBuilder.cs

@@ -6,14 +6,13 @@ using System.IO;
 using System.Linq;
 using System.Text;
 using System.Xml;
-using Emby.Dlna.Configuration;
 using Emby.Dlna.ContentDirectory;
+using Jellyfin.Data.Entities;
 using MediaBrowser.Controller.Channels;
 using MediaBrowser.Controller.Drawing;
 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.MediaEncoding;
 using MediaBrowser.Controller.Playlists;
@@ -23,6 +22,13 @@ using MediaBrowser.Model.Entities;
 using MediaBrowser.Model.Globalization;
 using MediaBrowser.Model.Net;
 using Microsoft.Extensions.Logging;
+using Episode = MediaBrowser.Controller.Entities.TV.Episode;
+using Genre = MediaBrowser.Controller.Entities.Genre;
+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;
+using XmlAttribute = MediaBrowser.Model.Dlna.XmlAttribute;
 
 namespace Emby.Dlna.Didl
 {
@@ -92,21 +98,21 @@ namespace Emby.Dlna.Didl
             {
                 using (var writer = XmlWriter.Create(builder, settings))
                 {
-                    //writer.WriteStartDocument();
+                    // writer.WriteStartDocument();
 
                     writer.WriteStartElement(string.Empty, "DIDL-Lite", NS_DIDL);
 
                     writer.WriteAttributeString("xmlns", "dc", null, NS_DC);
                     writer.WriteAttributeString("xmlns", "dlna", null, NS_DLNA);
                     writer.WriteAttributeString("xmlns", "upnp", null, NS_UPNP);
-                    //didl.SetAttribute("xmlns:sec", NS_SEC);
+                    // didl.SetAttribute("xmlns:sec", NS_SEC);
 
                     WriteXmlRootAttributes(_profile, writer);
 
                     WriteItemElement(writer, item, user, context, null, deviceId, filter, streamInfo);
 
                     writer.WriteFullEndElement();
-                    //writer.WriteEndDocument();
+                    // writer.WriteEndDocument();
                 }
 
                 return builder.ToString();
@@ -421,7 +427,6 @@ namespace Emby.Dlna.Didl
                     case StubType.FavoriteSeries: return _localization.GetLocalizedString("HeaderFavoriteShows");
                     case StubType.FavoriteEpisodes: return _localization.GetLocalizedString("HeaderFavoriteEpisodes");
                     case StubType.Series: return _localization.GetLocalizedString("Shows");
-                    default: break;
                 }
             }
 
@@ -670,7 +675,7 @@ namespace Emby.Dlna.Didl
                 return;
             }
 
-            MediaBrowser.Model.Dlna.XmlAttribute secAttribute = null;
+            XmlAttribute secAttribute = null;
             foreach (var attribute in _profile.XmlRootAttributes)
             {
                 if (string.Equals(attribute.Name, "xmlns:sec", StringComparison.OrdinalIgnoreCase))
@@ -706,7 +711,7 @@ namespace Emby.Dlna.Didl
         {
             // Don't filter on dc:title because not all devices will include it in the filter
             // MediaMonkey for example won't display content without a title
-            //if (filter.Contains("dc:title"))
+            // if (filter.Contains("dc:title"))
             {
                 AddValue(writer, "dc", "title", GetDisplayName(item, itemStubType, context), NS_DC);
             }
@@ -745,7 +750,7 @@ namespace Emby.Dlna.Didl
                         AddValue(writer, "dc", "description", desc, NS_DC);
                     }
                 }
-                //if (filter.Contains("upnp:longDescription"))
+                // if (filter.Contains("upnp:longDescription"))
                 //{
                 //    if (!string.IsNullOrWhiteSpace(item.Overview))
                 //    {
@@ -995,7 +1000,6 @@ namespace Emby.Dlna.Didl
             }
 
             AddImageResElement(item, writer, 160, 160, "jpg", "JPEG_TN");
-
         }
 
         private void AddImageResElement(
@@ -1131,25 +1135,24 @@ namespace Emby.Dlna.Didl
 
             if (width == 0 || height == 0)
             {
-                //_imageProcessor.GetImageSize(item, imageInfo);
+                // _imageProcessor.GetImageSize(item, imageInfo);
                 width = null;
                 height = null;
             }
-
             else if (width == -1 || height == -1)
             {
                 width = null;
                 height = null;
             }
 
-            //try
+            // try
             //{
             //    var size = _imageProcessor.GetImageSize(imageInfo);
 
             //    width = size.Width;
             //    height = size.Height;
             //}
-            //catch
+            // catch
             //{
 
             //}

+ 1 - 2
Emby.Dlna/Didl/Filter.cs

@@ -12,7 +12,6 @@ namespace Emby.Dlna.Didl
         public Filter()
             : this("*")
         {
-
         }
 
         public Filter(string filter)
@@ -26,7 +25,7 @@ namespace Emby.Dlna.Didl
         {
             // Don't bother with this. Some clients (media monkey) use the filter and then don't display very well when very little data comes back.
             return true;
-            //return _all || ListHelper.ContainsIgnoreCase(_fields, field);
+            // return _all || ListHelper.ContainsIgnoreCase(_fields, field);
         }
     }
 }

+ 6 - 7
Emby.Dlna/DlnaManager.cs

@@ -31,7 +31,7 @@ namespace Emby.Dlna
         private readonly IApplicationPaths _appPaths;
         private readonly IXmlSerializer _xmlSerializer;
         private readonly IFileSystem _fileSystem;
-        private readonly ILogger _logger;
+        private readonly ILogger<DlnaManager> _logger;
         private readonly IJsonSerializer _jsonSerializer;
         private readonly IServerApplicationHost _appHost;
         private static readonly Assembly _assembly = typeof(DlnaManager).Assembly;
@@ -49,7 +49,7 @@ namespace Emby.Dlna
             _xmlSerializer = xmlSerializer;
             _fileSystem = fileSystem;
             _appPaths = appPaths;
-            _logger = loggerFactory.CreateLogger("Dlna");
+            _logger = loggerFactory.CreateLogger<DlnaManager>();
             _jsonSerializer = jsonSerializer;
             _appHost = appHost;
         }
@@ -88,7 +88,6 @@ namespace Emby.Dlna
                     .Select(i => i.Item2)
                     .ToList();
             }
-
         }
 
         public DeviceProfile GetDefaultProfile()
@@ -251,7 +250,7 @@ namespace Emby.Dlna
                         return string.Equals(value, header.Value, StringComparison.OrdinalIgnoreCase);
                     case HeaderMatchType.Substring:
                         var isMatch = value.ToString().IndexOf(header.Value, StringComparison.OrdinalIgnoreCase) != -1;
-                        //_logger.LogDebug("IsMatch-Substring value: {0} testValue: {1} isMatch: {2}", value, header.Value, isMatch);
+                        // _logger.LogDebug("IsMatch-Substring value: {0} testValue: {1} isMatch: {2}", value, header.Value, isMatch);
                         return isMatch;
                     case HeaderMatchType.Regex:
                         return Regex.IsMatch(value, header.Value, RegexOptions.IgnoreCase);
@@ -566,9 +565,9 @@ namespace Emby.Dlna
                 new Foobar2000Profile(),
                 new SharpSmartTvProfile(),
                 new MediaMonkeyProfile(),
-                //new Windows81Profile(),
-                //new WindowsMediaCenterProfile(),
-                //new WindowsPhoneProfile(),
+                // new Windows81Profile(),
+                // new WindowsMediaCenterProfile(),
+                // new WindowsPhoneProfile(),
                 new DirectTvProfile(),
                 new DishHopperJoeyProfile(),
                 new DefaultProfile(),

+ 0 - 1
Emby.Dlna/Eventing/EventManager.cs

@@ -169,7 +169,6 @@ namespace Emby.Dlna.Eventing
             {
                 using (await _httpClient.SendAsync(options, new HttpMethod("NOTIFY")).ConfigureAwait(false))
                 {
-
                 }
             }
             catch (OperationCanceledException)

+ 7 - 5
Emby.Dlna/Main/DlnaEntryPoint.cs

@@ -33,7 +33,7 @@ namespace Emby.Dlna.Main
     public class DlnaEntryPoint : IServerEntryPoint, IRunBeforeStartup
     {
         private readonly IServerConfigurationManager _config;
-        private readonly ILogger _logger;
+        private readonly ILogger<DlnaEntryPoint> _logger;
         private readonly IServerApplicationHost _appHost;
 
         private PlayToManager _manager;
@@ -65,7 +65,8 @@ namespace Emby.Dlna.Main
 
         public static DlnaEntryPoint Current;
 
-        public DlnaEntryPoint(IServerConfigurationManager config,
+        public DlnaEntryPoint(
+            IServerConfigurationManager config,
             ILoggerFactory loggerFactory,
             IServerApplicationHost appHost,
             ISessionManager sessionManager,
@@ -99,7 +100,7 @@ namespace Emby.Dlna.Main
             _mediaEncoder = mediaEncoder;
             _socketFactory = socketFactory;
             _networkManager = networkManager;
-            _logger = loggerFactory.CreateLogger("Dlna");
+            _logger = loggerFactory.CreateLogger<DlnaEntryPoint>();
 
             ContentDirectory = new ContentDirectory.ContentDirectory(
                 dlnaManager,
@@ -275,7 +276,7 @@ namespace Emby.Dlna.Main
 
                 var device = new SsdpRootDevice
                 {
-                    CacheLifetime = TimeSpan.FromSeconds(1800), //How long SSDP clients can cache this info.
+                    CacheLifetime = TimeSpan.FromSeconds(1800), // How long SSDP clients can cache this info.
                     Location = uri, // Must point to the URL that serves your devices UPnP description document.
                     Address = address,
                     SubnetMask = _networkManager.GetLocalIpSubnetMask(address),
@@ -347,7 +348,8 @@ namespace Emby.Dlna.Main
 
                 try
                 {
-                    _manager = new PlayToManager(_logger,
+                    _manager = new PlayToManager(
+                        _logger,
                         _sessionManager,
                         _libraryManager,
                         _userManager,

+ 1 - 4
Emby.Dlna/PlayTo/Device.cs

@@ -750,7 +750,7 @@ namespace Emby.Dlna.PlayTo
 
             if (track == null)
             {
-                //If track is null, some vendors do this, use GetMediaInfo instead
+                // If track is null, some vendors do this, use GetMediaInfo instead
                 return (true, null);
             }
 
@@ -794,7 +794,6 @@ namespace Emby.Dlna.PlayTo
             }
             catch (XmlException)
             {
-
             }
 
             // first try to add a root node with a dlna namesapce
@@ -806,7 +805,6 @@ namespace Emby.Dlna.PlayTo
             }
             catch (XmlException)
             {
-
             }
 
             // some devices send back invalid xml
@@ -816,7 +814,6 @@ namespace Emby.Dlna.PlayTo
             }
             catch (XmlException)
             {
-
             }
 
             return null;

+ 9 - 1
Emby.Dlna/PlayTo/PlayToController.cs

@@ -7,6 +7,7 @@ using System.Linq;
 using System.Threading;
 using System.Threading.Tasks;
 using Emby.Dlna.Didl;
+using Jellyfin.Data.Entities;
 using MediaBrowser.Common.Configuration;
 using MediaBrowser.Controller.Dlna;
 using MediaBrowser.Controller.Drawing;
@@ -22,6 +23,7 @@ using MediaBrowser.Model.Globalization;
 using MediaBrowser.Model.Session;
 using Microsoft.AspNetCore.WebUtilities;
 using Microsoft.Extensions.Logging;
+using Photo = MediaBrowser.Controller.Entities.Photo;
 
 namespace Emby.Dlna.PlayTo
 {
@@ -446,7 +448,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,
+            User user,
+            long startPostionTicks,
+            string mediaSourceId,
+            int? audioStreamIndex,
+            int? subtitleStreamIndex)
         {
             var deviceInfo = _device.Properties;
 

+ 3 - 4
Emby.Dlna/PlayTo/PlayToManager.cs

@@ -88,7 +88,7 @@ namespace Emby.Dlna.PlayTo
             if (usn.IndexOf("MediaRenderer:", StringComparison.OrdinalIgnoreCase) == -1 &&
                      nt.IndexOf("MediaRenderer:", StringComparison.OrdinalIgnoreCase) == -1)
             {
-                //_logger.LogDebug("Upnp device {0} does not contain a MediaRenderer device (0).", location);
+                // _logger.LogDebug("Upnp device {0} does not contain a MediaRenderer device (0).", location);
                 return;
             }
 
@@ -112,7 +112,6 @@ namespace Emby.Dlna.PlayTo
             }
             catch (OperationCanceledException)
             {
-
             }
             catch (Exception ex)
             {
@@ -184,7 +183,8 @@ namespace Emby.Dlna.PlayTo
                     serverAddress = _appHost.GetLocalApiUrl(info.LocalIpAddress);
                 }
 
-                controller = new PlayToController(sessionInfo,
+                controller = new PlayToController(
+                    sessionInfo,
                    _sessionManager,
                    _libraryManager,
                    _logger,
@@ -242,7 +242,6 @@ namespace Emby.Dlna.PlayTo
             }
             catch
             {
-
             }
 
             _sessionLock.Dispose();

+ 0 - 1
Emby.Dlna/PlayTo/SsdpHttpClient.cs

@@ -91,7 +91,6 @@ namespace Emby.Dlna.PlayTo
 
             using (await _httpClient.SendAsync(options, new HttpMethod("SUBSCRIBE")).ConfigureAwait(false))
             {
-
             }
         }
 

+ 1 - 1
Emby.Dlna/Service/BaseService.cs

@@ -17,7 +17,7 @@ namespace Emby.Dlna.Service
             Logger = logger;
             HttpClient = httpClient;
 
-            EventManager = new EventManager(Logger, HttpClient);
+            EventManager = new EventManager(logger, HttpClient);
         }
 
         public EventSubscriptionResponse CancelEventSubscription(string subscriptionId)

+ 1 - 1
Emby.Dlna/Ssdp/DeviceDiscovery.cs

@@ -77,7 +77,7 @@ namespace Emby.Dlna.Ssdp
                     // (Optional) Set the filter so we only see notifications for devices we care about
                     // (can be any search target value i.e device type, uuid value etc - any value that appears in the
                     // DiscoverdSsdpDevice.NotificationType property or that is used with the searchTarget parameter of the Search method).
-                    //_DeviceLocator.NotificationFilter = "upnp:rootdevice";
+                    // _DeviceLocator.NotificationFilter = "upnp:rootdevice";
 
                     // Connect our event handler so we process devices as they are found
                     _deviceLocator.DeviceAvailable += OnDeviceLocatorDeviceAvailable;

+ 11 - 2
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
 {
@@ -28,7 +30,7 @@ namespace Emby.Drawing
         private static readonly HashSet<string> _transparentImageTypes
             = new HashSet<string>(StringComparer.OrdinalIgnoreCase) { ".png", ".webp", ".gif" };
 
-        private readonly ILogger _logger;
+        private readonly ILogger<ImageProcessor> _logger;
         private readonly IFileSystem _fileSystem;
         private readonly IServerApplicationPaths _appPaths;
         private readonly IImageEncoder _imageEncoder;
@@ -300,7 +302,7 @@ namespace Emby.Drawing
             }
 
             string path = info.Path;
-            _logger.LogInformation("Getting image size for item {ItemType} {Path}", item.GetType().Name, path);
+            _logger.LogDebug("Getting image size for item {ItemType} {Path}", item.GetType().Name, path);
 
             ImageDimensions size = GetImageDimensions(path);
             info.Width = size.Width;
@@ -349,6 +351,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)

+ 6 - 4
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;
@@ -149,9 +150,7 @@ namespace Emby.Notifications.Api
         [SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "request", Justification = "Required for ServiceStack")]
         public object Get(GetNotificationsSummary request)
         {
-            return new NotificationsSummary
-            {
-            };
+            return new NotificationsSummary();
         }
 
         public Task Post(AddAdminNotification request)
@@ -164,7 +163,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(user => user.HasPermission(PermissionKind.IsAdministrator))
+                    .Select(user => user.Id)
+                    .ToArray()
             };
 
             return _notificationManager.SendNotification(notification, CancellationToken.None);

+ 1 - 1
Emby.Notifications/NotificationEntryPoint.cs

@@ -25,7 +25,7 @@ namespace Emby.Notifications
     /// </summary>
     public class NotificationEntryPoint : IServerEntryPoint
     {
-        private readonly ILogger _logger;
+        private readonly ILogger<NotificationEntryPoint> _logger;
         private readonly IActivityManager _activityManager;
         private readonly ILocalizationManager _localization;
         private readonly INotificationManager _notificationManager;

+ 6 - 4
Emby.Notifications/NotificationManager.cs

@@ -4,6 +4,8 @@ using System.Globalization;
 using System.Linq;
 using System.Threading;
 using System.Threading.Tasks;
+using Jellyfin.Data.Entities;
+using Jellyfin.Data.Enums;
 using MediaBrowser.Common.Configuration;
 using MediaBrowser.Common.Extensions;
 using MediaBrowser.Controller.Configuration;
@@ -21,7 +23,7 @@ namespace Emby.Notifications
     /// </summary>
     public class NotificationManager : INotificationManager
     {
-        private readonly ILogger _logger;
+        private readonly ILogger<NotificationManager> _logger;
         private readonly IUserManager _userManager;
         private readonly IServerConfigurationManager _config;
 
@@ -101,7 +103,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 +119,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);
             }
 
@@ -142,7 +144,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
             {

+ 2 - 2
Emby.Photos/PhotoProvider.cs

@@ -22,7 +22,7 @@ namespace Emby.Photos
     /// </summary>
     public class PhotoProvider : ICustomMetadataProvider<Photo>, IForcedProvider, IHasItemChangeMonitor
     {
-        private readonly ILogger _logger;
+        private readonly ILogger<PhotoProvider> _logger;
         private readonly IImageProcessor _imageProcessor;
 
         // These are causing taglib to hang
@@ -104,7 +104,7 @@ namespace Emby.Photos
                             item.Overview = image.ImageTag.Comment;
 
                             if (!string.IsNullOrWhiteSpace(image.ImageTag.Title)
-                                && !item.LockedFields.Contains(MetadataFields.Name))
+                                && !item.LockedFields.Contains(MetadataField.Name))
                             {
                                 item.Name = image.ImageTag.Title;
                             }

+ 22 - 34
Emby.Server.Implementations/Activity/ActivityLogEntryPoint.cs

@@ -88,25 +88,26 @@ 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);
+                    e.Argument.Id)
+            {
+                LogSeverity = LogLevel.Error
+            }).ConfigureAwait(false);
         }
 
         private async void OnSubtitleDownloadFailure(object sender, SubtitleDownloadFailureEventArgs e)
@@ -152,7 +153,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 +188,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 +305,37 @@ namespace Emby.Server.Implementations.Activity
             }).ConfigureAwait(false);
         }
 
-        private async void OnUserPolicyUpdated(object sender, GenericEventArgs<MediaBrowser.Controller.Entities.User> e)
-        {
-            await CreateLogEntry(new ActivityLog(
-                string.Format(
-                    CultureInfo.InvariantCulture,
-                    _localization.GetLocalizedString("UserPolicyUpdatedWithName"),
-                    e.Argument.Name),
-                "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 +499,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>

+ 2 - 2
Emby.Server.Implementations/AppBase/BaseConfigurationManager.cs

@@ -53,7 +53,7 @@ namespace Emby.Server.Implementations.AppBase
             CommonApplicationPaths = applicationPaths;
             XmlSerializer = xmlSerializer;
             _fileSystem = fileSystem;
-            Logger = loggerFactory.CreateLogger(GetType().Name);
+            Logger = loggerFactory.CreateLogger<BaseConfigurationManager>();
 
             UpdateCachePath();
         }
@@ -83,7 +83,7 @@ namespace Emby.Server.Implementations.AppBase
         /// Gets the logger.
         /// </summary>
         /// <value>The logger.</value>
-        protected ILogger Logger { get; private set; }
+        protected ILogger<BaseConfigurationManager> Logger { get; private set; }
 
         /// <summary>
         /// Gets the XML serializer.

+ 2 - 10
Emby.Server.Implementations/ApplicationHost.cs

@@ -173,7 +173,7 @@ namespace Emby.Server.Implementations
         /// <summary>
         /// Gets the logger.
         /// </summary>
-        protected ILogger Logger { get; }
+        protected ILogger<ApplicationHost> Logger { get; }
 
         private IPlugin[] _plugins;
 
@@ -562,11 +562,8 @@ 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>();
 
             // TODO: Refactor to eliminate the circular dependency here so that Lazy<T> isn't required
             // TODO: Add StartupOptions.FFmpegPath to IConfiguration and remove this custom activation
@@ -659,15 +656,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();
         }
@@ -750,7 +743,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>();

+ 1 - 1
Emby.Server.Implementations/Browser/BrowserLauncher.cs

@@ -41,7 +41,7 @@ namespace Emby.Server.Implementations.Browser
             }
             catch (Exception ex)
             {
-                var logger = appHost.Resolve<ILogger>();
+                var logger = appHost.Resolve<ILogger<IServerApplicationHost>>();
                 logger?.LogError(ex, "Failed to open browser window with URL {URL}", relativeUrl);
             }
         }

+ 10 - 5
Emby.Server.Implementations/Channels/ChannelManager.cs

@@ -6,6 +6,7 @@ using System.IO;
 using System.Linq;
 using System.Threading;
 using System.Threading.Tasks;
+using Jellyfin.Data.Entities;
 using MediaBrowser.Common.Extensions;
 using MediaBrowser.Common.Progress;
 using MediaBrowser.Controller.Channels;
@@ -13,8 +14,6 @@ using MediaBrowser.Controller.Configuration;
 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.Model.Channels;
@@ -24,6 +23,11 @@ using MediaBrowser.Model.IO;
 using MediaBrowser.Model.Querying;
 using MediaBrowser.Model.Serialization;
 using Microsoft.Extensions.Logging;
+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 Emby.Server.Implementations.Channels
 {
@@ -36,7 +40,7 @@ namespace Emby.Server.Implementations.Channels
         private readonly IUserDataManager _userDataManager;
         private readonly IDtoService _dtoService;
         private readonly ILibraryManager _libraryManager;
-        private readonly ILogger _logger;
+        private readonly ILogger<ChannelManager> _logger;
         private readonly IServerConfigurationManager _config;
         private readonly IFileSystem _fileSystem;
         private readonly IJsonSerializer _jsonSerializer;
@@ -791,7 +795,8 @@ namespace Emby.Server.Implementations.Channels
             return result;
         }
 
-        private async Task<ChannelItemResult> GetChannelItems(IChannel channel,
+        private async Task<ChannelItemResult> GetChannelItems(
+            IChannel channel,
             User user,
             string externalFolderId,
             ChannelItemSortField? sortField,
@@ -1067,7 +1072,7 @@ namespace Emby.Server.Implementations.Channels
             }
 
             // was used for status
-            //if (!string.Equals(item.ExternalEtag ?? string.Empty, info.Etag ?? string.Empty, StringComparison.Ordinal))
+            // if (!string.Equals(item.ExternalEtag ?? string.Empty, info.Etag ?? string.Empty, StringComparison.Ordinal))
             //{
             //    item.ExternalEtag = info.Etag;
             //    forceUpdate = true;

+ 1 - 1
Emby.Server.Implementations/Channels/RefreshChannelsScheduledTask.cs

@@ -17,7 +17,7 @@ namespace Emby.Server.Implementations.Channels
     public class RefreshChannelsScheduledTask : IScheduledTask, IConfigurableScheduledTask
     {
         private readonly IChannelManager _channelManager;
-        private readonly ILogger _logger;
+        private readonly ILogger<RefreshChannelsScheduledTask> _logger;
         private readonly ILibraryManager _libraryManager;
         private readonly ILocalizationManager _localization;
 

+ 4 - 3
Emby.Server.Implementations/Collections/CollectionManager.cs

@@ -5,6 +5,7 @@ using System.IO;
 using System.Linq;
 using System.Threading;
 using System.Threading.Tasks;
+using Jellyfin.Data.Entities;
 using MediaBrowser.Common.Configuration;
 using MediaBrowser.Controller.Collections;
 using MediaBrowser.Controller.Configuration;
@@ -29,7 +30,7 @@ namespace Emby.Server.Implementations.Collections
         private readonly ILibraryManager _libraryManager;
         private readonly IFileSystem _fileSystem;
         private readonly ILibraryMonitor _iLibraryMonitor;
-        private readonly ILogger _logger;
+        private readonly ILogger<CollectionManager> _logger;
         private readonly IProviderManager _providerManager;
         private readonly ILocalizationManager _localizationManager;
         private readonly IApplicationPaths _appPaths;
@@ -56,7 +57,7 @@ namespace Emby.Server.Implementations.Collections
             _libraryManager = libraryManager;
             _fileSystem = fileSystem;
             _iLibraryMonitor = iLibraryMonitor;
-            _logger = loggerFactory.CreateLogger(nameof(CollectionManager));
+            _logger = loggerFactory.CreateLogger<CollectionManager>();
             _providerManager = providerManager;
             _localizationManager = localizationManager;
             _appPaths = appPaths;
@@ -370,7 +371,7 @@ namespace Emby.Server.Implementations.Collections
     {
         private readonly CollectionManager _collectionManager;
         private readonly IServerConfigurationManager _config;
-        private readonly ILogger _logger;
+        private readonly ILogger<CollectionManagerEntryPoint> _logger;
 
         /// <summary>
         /// Initializes a new instance of the <see cref="CollectionManagerEntryPoint"/> class.

+ 2 - 3
Emby.Server.Implementations/Data/BaseSqliteRepository.cs

@@ -17,7 +17,7 @@ namespace Emby.Server.Implementations.Data
         /// Initializes a new instance of the <see cref="BaseSqliteRepository"/> class.
         /// </summary>
         /// <param name="logger">The logger.</param>
-        protected BaseSqliteRepository(ILogger logger)
+        protected BaseSqliteRepository(ILogger<BaseSqliteRepository> logger)
         {
             Logger = logger;
         }
@@ -32,7 +32,7 @@ namespace Emby.Server.Implementations.Data
         /// Gets the logger.
         /// </summary>
         /// <value>The logger.</value>
-        protected ILogger Logger { get; }
+        protected ILogger<BaseSqliteRepository> Logger { get; }
 
         /// <summary>
         /// Gets the default connection flags.
@@ -162,7 +162,6 @@ namespace Emby.Server.Implementations.Data
                 }
 
                 return false;
-
             }, ReadTransactionMode);
         }
 

+ 1 - 2
Emby.Server.Implementations/Data/CleanDatabaseScheduledTask.cs

@@ -12,7 +12,7 @@ namespace Emby.Server.Implementations.Data
     public class CleanDatabaseScheduledTask : ILibraryPostScanTask
     {
         private readonly ILibraryManager _libraryManager;
-        private readonly ILogger _logger;
+        private readonly ILogger<CleanDatabaseScheduledTask> _logger;
 
         public CleanDatabaseScheduledTask(ILibraryManager libraryManager, ILogger<CleanDatabaseScheduledTask> logger)
         {
@@ -51,7 +51,6 @@ namespace Emby.Server.Implementations.Data
                     _libraryManager.DeleteItem(item, new DeleteOptions
                     {
                         DeleteFileLocation = false
-
                     });
                 }
 

+ 1 - 1
Emby.Server.Implementations/Data/SqliteDisplayPreferencesRepository.cs

@@ -1,4 +1,4 @@
-#pragma warning disable CS1591
+#pragma warning disable CS1591
 
 using System;
 using System.Collections.Generic;

+ 6 - 13
Emby.Server.Implementations/Data/SqliteItemRepository.cs

@@ -321,7 +321,6 @@ namespace Emby.Server.Implementations.Data
                     AddColumn(db, "MediaStreams", "ColorPrimaries", "TEXT", existingColumnNames);
                     AddColumn(db, "MediaStreams", "ColorSpace", "TEXT", existingColumnNames);
                     AddColumn(db, "MediaStreams", "ColorTransfer", "TEXT", existingColumnNames);
-
                 }, TransactionMode);
 
                 connection.RunQueries(postQueries);
@@ -1626,11 +1625,11 @@ namespace Emby.Server.Implementations.Data
             {
                 if (!reader.IsDBNull(index))
                 {
-                    IEnumerable<MetadataFields> GetLockedFields(string s)
+                    IEnumerable<MetadataField> GetLockedFields(string s)
                     {
                         foreach (var i in s.Split(new[] { '|' }, StringSplitOptions.RemoveEmptyEntries))
                         {
-                            if (Enum.TryParse(i, true, out MetadataFields parsedValue))
+                            if (Enum.TryParse(i, true, out MetadataField parsedValue))
                             {
                                 yield return parsedValue;
                             }
@@ -2044,7 +2043,6 @@ namespace Emby.Server.Implementations.Data
                     db.Execute("delete from " + ChaptersTableName + " where ItemId=@ItemId", idBlob);
 
                     InsertChapters(idBlob, chapters, db);
-
                 }, TransactionMode);
             }
         }
@@ -2734,7 +2732,7 @@ namespace Emby.Server.Implementations.Data
 
                 foreach (var providerId in newItem.ProviderIds)
                 {
-                    if (providerId.Key == MetadataProviders.TmdbCollection.ToString())
+                    if (providerId.Key == MetadataProvider.TmdbCollection.ToString())
                     {
                         continue;
                     }
@@ -4324,7 +4322,7 @@ namespace Emby.Server.Implementations.Data
                 var index = 0;
                 foreach (var pair in query.ExcludeProviderIds)
                 {
-                    if (string.Equals(pair.Key, MetadataProviders.TmdbCollection.ToString(), StringComparison.OrdinalIgnoreCase))
+                    if (string.Equals(pair.Key, MetadataProvider.TmdbCollection.ToString(), StringComparison.OrdinalIgnoreCase))
                     {
                         continue;
                     }
@@ -4353,14 +4351,14 @@ namespace Emby.Server.Implementations.Data
                 var index = 0;
                 foreach (var pair in query.HasAnyProviderId)
                 {
-                    if (string.Equals(pair.Key, MetadataProviders.TmdbCollection.ToString(), StringComparison.OrdinalIgnoreCase))
+                    if (string.Equals(pair.Key, MetadataProvider.TmdbCollection.ToString(), StringComparison.OrdinalIgnoreCase))
                     {
                         continue;
                     }
 
                     // TODO this seems to be an idea for a better schema where ProviderIds are their own table
                     //      buut this is not implemented
-                    //hasProviderIds.Add("(COALESCE((select value from ProviderIds where ItemId=Guid and Name = '" + pair.Key + "'), '') <> " + paramName + ")");
+                    // hasProviderIds.Add("(COALESCE((select value from ProviderIds where ItemId=Guid and Name = '" + pair.Key + "'), '') <> " + paramName + ")");
 
                     // TODO this is a really BAD way to do it since the pair:
                     //      Tmdb, 1234 matches Tmdb=1234 but also Tmdb=1234567
@@ -4789,7 +4787,6 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type
                 connection.RunInTransaction(db =>
                 {
                     connection.ExecuteAll(sql);
-
                 }, TransactionMode);
             }
         }
@@ -5180,7 +5177,6 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type
                         }
                     }
                 }
-
             }
 
             LogQueryTime("GetItemValueNames", commandText, now);
@@ -5631,7 +5627,6 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type
                     db.Execute("delete from People where ItemId=@ItemId", itemIdBlob);
 
                     InsertPeople(itemIdBlob, people, db);
-
                 }, TransactionMode);
             }
         }
@@ -5788,7 +5783,6 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type
                     db.Execute("delete from mediastreams where ItemId=@ItemId", itemIdBlob);
 
                     InsertMediaStreams(itemIdBlob, streams, db);
-
                 }, TransactionMode);
             }
         }
@@ -6134,7 +6128,6 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type
                     db.Execute("delete from mediaattachments where ItemId=@ItemId", itemIdBlob);
 
                     InsertMediaAttachments(itemIdBlob, attachments, db, cancellationToken);
-
                 }, TransactionMode);
             }
         }

+ 2 - 1
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;
@@ -346,7 +347,7 @@ namespace Emby.Server.Implementations.Data
             var userData = new UserItemData();
 
             userData.Key = reader[0].ToString();
-            //userData.UserId = reader[1].ReadGuidFromBlob();
+            // userData.UserId = reader[1].ReadGuidFromBlob();
 
             if (reader[2].SQLiteType != SQLiteType.Null)
             {

+ 0 - 240
Emby.Server.Implementations/Data/SqliteUserRepository.cs

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

+ 2 - 2
Emby.Server.Implementations/Devices/DeviceId.cs

@@ -12,7 +12,7 @@ namespace Emby.Server.Implementations.Devices
     public class DeviceId
     {
         private readonly IApplicationPaths _appPaths;
-        private readonly ILogger _logger;
+        private readonly ILogger<DeviceId> _logger;
 
         private readonly object _syncLock = new object();
 
@@ -90,7 +90,7 @@ namespace Emby.Server.Implementations.Devices
         public DeviceId(IApplicationPaths appPaths, ILoggerFactory loggerFactory)
         {
             _appPaths = appPaths;
-            _logger = loggerFactory.CreateLogger("SystemId");
+            _logger = loggerFactory.CreateLogger<DeviceId>();
         }
 
         public string Value => _id ?? (_id = GetDeviceId());

+ 10 - 21
Emby.Server.Implementations/Devices/DeviceManager.cs

@@ -5,10 +5,11 @@ using System.Collections.Generic;
 using System.Globalization;
 using System.IO;
 using System.Linq;
+using Jellyfin.Data.Enums;
+using Jellyfin.Data.Entities;
 using MediaBrowser.Common.Extensions;
 using MediaBrowser.Controller.Configuration;
 using MediaBrowser.Controller.Devices;
-using MediaBrowser.Controller.Entities;
 using MediaBrowser.Controller.Library;
 using MediaBrowser.Controller.Security;
 using MediaBrowser.Model.Devices;
@@ -16,7 +17,6 @@ using MediaBrowser.Model.Events;
 using MediaBrowser.Model.Querying;
 using MediaBrowser.Model.Serialization;
 using MediaBrowser.Model.Session;
-using MediaBrowser.Model.Users;
 
 namespace Emby.Server.Implementations.Devices
 {
@@ -27,11 +27,10 @@ namespace Emby.Server.Implementations.Devices
         private readonly IServerConfigurationManager _config;
         private readonly IAuthenticationRepository _authRepo;
         private readonly Dictionary<string, ClientCapabilities> _capabilitiesCache;
+        private readonly object _capabilitiesSyncLock = new object();
 
         public event EventHandler<GenericEventArgs<Tuple<string, DeviceOptions>>> DeviceOptionsUpdated;
 
-        private readonly object _capabilitiesSyncLock = new object();
-
         public DeviceManager(
             IAuthenticationRepository authRepo,
             IJsonSerializer json,
@@ -113,7 +112,7 @@ namespace Emby.Server.Implementations.Devices
         {
             IEnumerable<AuthenticationInfo> sessions = _authRepo.Get(new AuthenticationInfoQuery
             {
-                //UserId = query.UserId
+                // UserId = query.UserId
                 HasUser = true
             }).Items;
 
@@ -175,7 +174,12 @@ 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);
 
@@ -187,20 +191,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);
-        }
     }
 }

+ 21 - 17
Emby.Server.Implementations/Dto/DtoService.cs

@@ -6,14 +6,14 @@ using System.Globalization;
 using System.IO;
 using System.Linq;
 using System.Threading.Tasks;
+using Jellyfin.Data.Entities;
+using Jellyfin.Data.Enums;
 using MediaBrowser.Common;
 using MediaBrowser.Controller.Channels;
 using MediaBrowser.Controller.Drawing;
 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.LiveTv;
 using MediaBrowser.Controller.Persistence;
@@ -24,12 +24,20 @@ using MediaBrowser.Model.Dto;
 using MediaBrowser.Model.Entities;
 using MediaBrowser.Model.Querying;
 using Microsoft.Extensions.Logging;
+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 Person = MediaBrowser.Controller.Entities.Person;
+using Photo = MediaBrowser.Controller.Entities.Photo;
+using Season = MediaBrowser.Controller.Entities.TV.Season;
+using Series = MediaBrowser.Controller.Entities.TV.Series;
 
 namespace Emby.Server.Implementations.Dto
 {
     public class DtoService : IDtoService
     {
-        private readonly ILogger _logger;
+        private readonly ILogger<DtoService> _logger;
         private readonly ILibraryManager _libraryManager;
         private readonly IUserDataManager _userDataRepository;
         private readonly IItemRepository _itemRepo;
@@ -384,7 +392,7 @@ namespace Emby.Server.Implementations.Dto
 
                     if (options.ContainsField(ItemFields.ChildCount))
                     {
-                        dto.ChildCount = dto.ChildCount ?? GetChildCount(folder, user);
+                        dto.ChildCount ??= GetChildCount(folder, user);
                     }
                 }
 
@@ -398,7 +406,6 @@ namespace Emby.Server.Implementations.Dto
                     dto.DateLastMediaAdded = folder.DateLastMediaAdded;
                 }
             }
-
             else
             {
                 if (options.EnableUserData)
@@ -414,7 +421,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;
@@ -586,7 +593,6 @@ namespace Emby.Server.Implementations.Dto
                         _logger.LogError(ex, "Error getting person {Name}", c);
                         return null;
                     }
-
                 }).Where(i => i != null)
                 .GroupBy(i => i.Name, StringComparer.OrdinalIgnoreCase)
                 .Select(x => x.First())
@@ -939,7 +945,7 @@ namespace Emby.Server.Implementations.Dto
                     dto.AlbumPrimaryImageTag = GetTagAndFillBlurhash(dto, albumParent, ImageType.Primary);
                 }
 
-                //if (options.ContainsField(ItemFields.MediaSourceCount))
+                // if (options.ContainsField(ItemFields.MediaSourceCount))
                 //{
                 // Songs always have one
                 //}
@@ -949,13 +955,13 @@ namespace Emby.Server.Implementations.Dto
             {
                 dto.Artists = hasArtist.Artists;
 
-                //var artistItems = _libraryManager.GetArtists(new InternalItemsQuery
+                // var artistItems = _libraryManager.GetArtists(new InternalItemsQuery
                 //{
                 //    EnableTotalRecordCount = false,
                 //    ItemIds = new[] { item.Id.ToString("N", CultureInfo.InvariantCulture) }
                 //});
 
-                //dto.ArtistItems = artistItems.Items
+                // dto.ArtistItems = artistItems.Items
                 //    .Select(i =>
                 //    {
                 //        var artist = i.Item1;
@@ -968,7 +974,7 @@ namespace Emby.Server.Implementations.Dto
                 //    .ToList();
 
                 // Include artists that are not in the database yet, e.g., just added via metadata editor
-                //var foundArtists = artistItems.Items.Select(i => i.Item1.Name).ToList();
+                // var foundArtists = artistItems.Items.Select(i => i.Item1.Name).ToList();
                 dto.ArtistItems = hasArtist.Artists
                     //.Except(foundArtists, new DistinctNameComparer())
                     .Select(i =>
@@ -993,7 +999,6 @@ namespace Emby.Server.Implementations.Dto
                         }
 
                         return null;
-
                     }).Where(i => i != null).ToArray();
             }
 
@@ -1002,13 +1007,13 @@ namespace Emby.Server.Implementations.Dto
             {
                 dto.AlbumArtist = hasAlbumArtist.AlbumArtists.FirstOrDefault();
 
-                //var artistItems = _libraryManager.GetAlbumArtists(new InternalItemsQuery
+                // var artistItems = _libraryManager.GetAlbumArtists(new InternalItemsQuery
                 //{
                 //    EnableTotalRecordCount = false,
                 //    ItemIds = new[] { item.Id.ToString("N", CultureInfo.InvariantCulture) }
                 //});
 
-                //dto.AlbumArtists = artistItems.Items
+                // dto.AlbumArtists = artistItems.Items
                 //    .Select(i =>
                 //    {
                 //        var artist = i.Item1;
@@ -1044,7 +1049,6 @@ namespace Emby.Server.Implementations.Dto
                         }
 
                         return null;
-
                     }).Where(i => i != null).ToArray();
             }
 
@@ -1158,7 +1162,7 @@ namespace Emby.Server.Implementations.Dto
 
                 // this block will add the series poster for episodes without a poster
                 // TODO maybe remove the if statement entirely
-                //if (options.ContainsField(ItemFields.SeriesPrimaryImage))
+                // if (options.ContainsField(ItemFields.SeriesPrimaryImage))
                 {
                     episodeSeries = episodeSeries ?? episode.Series;
                     if (episodeSeries != null)
@@ -1204,7 +1208,7 @@ namespace Emby.Server.Implementations.Dto
 
                 // this block will add the series poster for seasons without a poster
                 // TODO maybe remove the if statement entirely
-                //if (options.ContainsField(ItemFields.SeriesPrimaryImage))
+                // if (options.ContainsField(ItemFields.SeriesPrimaryImage))
                 {
                     series = series ?? season.Series;
                     if (series != null)

+ 5 - 5
Emby.Server.Implementations/Emby.Server.Implementations.csproj

@@ -34,13 +34,13 @@
     <PackageReference Include="Microsoft.AspNetCore.ResponseCompression" Version="2.2.0" />
     <PackageReference Include="Microsoft.AspNetCore.Server.Kestrel" Version="2.2.0" />
     <PackageReference Include="Microsoft.AspNetCore.WebSockets" Version="2.2.1" />
-    <PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="3.1.4" />
-    <PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="3.1.4" />
-    <PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="3.1.4" />
-    <PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="3.1.4" />
+    <PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="3.1.5" />
+    <PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="3.1.5" />
+    <PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="3.1.5" />
+    <PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="3.1.5" />
     <PackageReference Include="Mono.Nat" Version="2.0.1" />
     <PackageReference Include="prometheus-net.DotNetRuntime" Version="3.3.1" />
-    <PackageReference Include="ServiceStack.Text.Core" Version="5.8.0" />
+    <PackageReference Include="ServiceStack.Text.Core" Version="5.9.0" />
     <PackageReference Include="sharpcompress" Version="0.25.0" />
     <PackageReference Include="SQLitePCL.pretty.netstandard" Version="2.1.0" />
     <PackageReference Include="DotNet.Glob" Version="3.0.9" />

+ 1 - 1
Emby.Server.Implementations/EntryPoints/ExternalPortForwarding.cs

@@ -23,7 +23,7 @@ namespace Emby.Server.Implementations.EntryPoints
     public class ExternalPortForwarding : IServerEntryPoint
     {
         private readonly IServerApplicationHost _appHost;
-        private readonly ILogger _logger;
+        private readonly ILogger<ExternalPortForwarding> _logger;
         private readonly IServerConfigurationManager _config;
         private readonly IDeviceDiscovery _deviceDiscovery;
 

+ 2 - 2
Emby.Server.Implementations/EntryPoints/LibraryChangedNotifier.cs

@@ -6,6 +6,7 @@ using System.Globalization;
 using System.Linq;
 using System.Threading;
 using System.Threading.Tasks;
+using Jellyfin.Data.Entities;
 using MediaBrowser.Controller.Channels;
 using MediaBrowser.Controller.Entities;
 using MediaBrowser.Controller.Entities.Audio;
@@ -28,7 +29,7 @@ namespace Emby.Server.Implementations.EntryPoints
 
         private readonly ISessionManager _sessionManager;
         private readonly IUserManager _userManager;
-        private readonly ILogger _logger;
+        private readonly ILogger<LibraryChangedNotifier> _logger;
 
         /// <summary>
         /// The library changed sync lock.
@@ -131,7 +132,6 @@ namespace Emby.Server.Implementations.EntryPoints
                 }
                 catch
                 {
-
                 }
             }
         }

+ 3 - 2
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;
@@ -17,7 +18,7 @@ namespace Emby.Server.Implementations.EntryPoints
         private readonly ILiveTvManager _liveTvManager;
         private readonly ISessionManager _sessionManager;
         private readonly IUserManager _userManager;
-        private readonly ILogger _logger;
+        private readonly ILogger<RecordingNotifier> _logger;
 
         public RecordingNotifier(
             ISessionManager sessionManager,
@@ -64,7 +65,7 @@ namespace Emby.Server.Implementations.EntryPoints
 
         private async Task 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
             {

+ 0 - 77
Emby.Server.Implementations/EntryPoints/RefreshUsersMetadata.cs

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

+ 5 - 25
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;
@@ -68,10 +68,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;
 
@@ -153,20 +151,6 @@ namespace Emby.Server.Implementations.EntryPoints
             await SendMessageToUserSession(e.Argument, "UserDeleted", e.Argument.Id.ToString("N", CultureInfo.InvariantCulture)).ConfigureAwait(false);
         }
 
-        private async void OnUserPolicyUpdated(object sender, GenericEventArgs<User> e)
-        {
-            var dto = _userManager.GetUserDto(e.Argument);
-
-            await SendMessageToUserSession(e.Argument, "UserPolicyUpdated", dto).ConfigureAwait(false);
-        }
-
-        private async void OnUserConfigurationUpdated(object sender, GenericEventArgs<User> e)
-        {
-            var dto = _userManager.GetUserDto(e.Argument);
-
-            await SendMessageToUserSession(e.Argument, "UserConfigurationUpdated", dto).ConfigureAwait(false);
-        }
-
         private async Task SendMessageToAdminSessions<T>(string name, T data)
         {
             try
@@ -175,7 +159,6 @@ namespace Emby.Server.Implementations.EntryPoints
             }
             catch (Exception)
             {
-
             }
         }
 
@@ -191,7 +174,6 @@ namespace Emby.Server.Implementations.EntryPoints
             }
             catch (Exception)
             {
-
             }
         }
 
@@ -210,10 +192,8 @@ 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;

+ 1 - 2
Emby.Server.Implementations/EntryPoints/UdpServerEntryPoint.cs

@@ -21,7 +21,7 @@ namespace Emby.Server.Implementations.EntryPoints
         /// <summary>
         /// The logger.
         /// </summary>
-        private readonly ILogger _logger;
+        private readonly ILogger<UdpServerEntryPoint> _logger;
         private readonly IServerApplicationHost _appHost;
         private readonly IConfiguration _config;
 
@@ -43,7 +43,6 @@ namespace Emby.Server.Implementations.EntryPoints
             _logger = logger;
             _appHost = appHost;
             _config = configuration;
-
         }
 
         /// <inheritdoc />

+ 1 - 1
Emby.Server.Implementations/HttpClientManager/HttpClientManager.cs

@@ -22,7 +22,7 @@ namespace Emby.Server.Implementations.HttpClientManager
     /// </summary>
     public class HttpClientManager : IHttpClient
     {
-        private readonly ILogger _logger;
+        private readonly ILogger<HttpClientManager> _logger;
         private readonly IApplicationPaths _appPaths;
         private readonly IFileSystem _fileSystem;
         private readonly IApplicationHost _appHost;

+ 5 - 3
Emby.Server.Implementations/HttpServer/HttpListenerHost.cs

@@ -41,7 +41,7 @@ namespace Emby.Server.Implementations.HttpServer
         /// </summary>
         public const string DefaultRedirectKey = "HttpListenerHost:DefaultRedirectPath";
 
-        private readonly ILogger _logger;
+        private readonly ILogger<HttpListenerHost> _logger;
         private readonly ILoggerFactory _loggerFactory;
         private readonly IServerConfigurationManager _config;
         private readonly INetworkManager _networkManager;
@@ -230,7 +230,9 @@ namespace Emby.Server.Implementations.HttpServer
 
             httpRes.StatusCode = statusCode;
 
-            var errContent = NormalizeExceptionMessage(ex) ?? string.Empty;
+            var errContent = _hostEnvironment.IsDevelopment()
+                    ? (NormalizeExceptionMessage(ex) ?? string.Empty)
+                    : "Error processing request.";
             httpRes.ContentType = "text/plain";
             httpRes.ContentLength = errContent.Length;
             await httpRes.WriteAsync(errContent).ConfigureAwait(false);
@@ -397,7 +399,7 @@ namespace Emby.Server.Implementations.HttpServer
             var response = context.Response;
             var localPath = context.Request.Path.ToString();
 
-            var req = new WebSocketSharpRequest(request, response, request.Path, _logger);
+            var req = new WebSocketSharpRequest(request, response, request.Path);
             return RequestHandler(req, request.GetDisplayUrl(), request.Host.ToString(), localPath, context.RequestAborted);
         }
 

+ 2 - 3
Emby.Server.Implementations/HttpServer/HttpResultFactory.cs

@@ -37,7 +37,7 @@ namespace Emby.Server.Implementations.HttpServer
         /// <summary>
         /// The logger.
         /// </summary>
-        private readonly ILogger _logger;
+        private readonly ILogger<HttpResultFactory> _logger;
         private readonly IFileSystem _fileSystem;
         private readonly IJsonSerializer _jsonSerializer;
         private readonly IStreamHelper _streamHelper;
@@ -50,7 +50,7 @@ namespace Emby.Server.Implementations.HttpServer
             _fileSystem = fileSystem;
             _jsonSerializer = jsonSerializer;
             _streamHelper = streamHelper;
-            _logger = loggerfactory.CreateLogger("HttpResultFactory");
+            _logger = loggerfactory.CreateLogger<HttpResultFactory>();
         }
 
         /// <summary>
@@ -580,7 +580,6 @@ namespace Emby.Server.Implementations.HttpServer
                 }
                 catch (NotSupportedException)
                 {
-
                 }
             }
 

+ 0 - 1
Emby.Server.Implementations/HttpServer/ResponseFilter.cs

@@ -1,5 +1,4 @@
 using System;
-using System.Collections.Generic;
 using System.Globalization;
 using System.Text;
 using MediaBrowser.Controller.Net;

+ 15 - 13
Emby.Server.Implementations/HttpServer/Security/AuthService.cs

@@ -2,11 +2,12 @@
 
 using System;
 using System.Linq;
-using System.Security.Authentication;
 using Emby.Server.Implementations.SocketSharp;
+using Jellyfin.Data.Entities;
+using Jellyfin.Data.Enums;
 using MediaBrowser.Common.Net;
+using MediaBrowser.Controller.Authentication;
 using MediaBrowser.Controller.Configuration;
-using MediaBrowser.Controller.Entities;
 using MediaBrowser.Controller.Net;
 using MediaBrowser.Controller.Security;
 using MediaBrowser.Controller.Session;
@@ -45,7 +46,7 @@ namespace Emby.Server.Implementations.HttpServer.Security
 
         public User Authenticate(HttpRequest request, IAuthenticationAttributes authAttributes)
         {
-            var req = new WebSocketSharpRequest(request, null, request.Path, _logger);
+            var req = new WebSocketSharpRequest(request, null, request.Path);
             var user = ValidateUser(req, authAttributes);
             return user;
         }
@@ -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,
@@ -104,21 +106,21 @@ namespace Emby.Server.Implementations.HttpServer.Security
         private void ValidateUserAccess(
             User user,
             IRequest request,
-            IAuthenticationAttributes authAttribtues,
+            IAuthenticationAttributes authAttributes,
             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
-                && !authAttribtues.EscapeParentalControl
+            if (!user.HasPermission(PermissionKind.IsAdministrator)
+                && !authAttributes.EscapeParentalControl
                 && !user.IsParentalScheduleAllowed())
             {
                 request.Response.Headers.Add("X-Application-Error-Code", "ParentalControl");
@@ -180,7 +182,7 @@ namespace Emby.Server.Implementations.HttpServer.Security
         {
             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.");
                 }
@@ -223,7 +225,7 @@ namespace Emby.Server.Implementations.HttpServer.Security
                 throw new AuthenticationException("Access token is invalid or expired.");
             }
 
-            //if (!string.IsNullOrEmpty(info.UserId))
+            // if (!string.IsNullOrEmpty(info.UserId))
             //{
             //    var user = _userManager.GetUserById(info.UserId);
 

+ 2 - 3
Emby.Server.Implementations/HttpServer/Security/AuthorizationContext.cs

@@ -116,7 +116,6 @@ namespace Emby.Server.Implementations.HttpServer.Security
                     {
                         info.Device = tokenInfo.DeviceName;
                     }
-
                     else if (!string.Equals(info.Device, tokenInfo.DeviceName, StringComparison.OrdinalIgnoreCase))
                     {
                         if (allowTokenInfoUpdate)
@@ -149,9 +148,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;
                         }
                     }

+ 1 - 1
Emby.Server.Implementations/HttpServer/Security/SessionContext.cs

@@ -1,7 +1,7 @@
 #pragma warning disable CS1591
 
 using System;
-using MediaBrowser.Controller.Entities;
+using Jellyfin.Data.Entities;
 using MediaBrowser.Controller.Library;
 using MediaBrowser.Controller.Net;
 using MediaBrowser.Controller.Security;

+ 7 - 5
Emby.Server.Implementations/HttpServer/WebSocketConnection.cs

@@ -24,7 +24,7 @@ namespace Emby.Server.Implementations.HttpServer
         /// <summary>
         /// The logger.
         /// </summary>
-        private readonly ILogger _logger;
+        private readonly ILogger<WebSocketConnection> _logger;
 
         /// <summary>
         /// The json serializer options.
@@ -234,10 +234,12 @@ namespace Emby.Server.Implementations.HttpServer
         private Task SendKeepAliveResponse()
         {
             LastKeepAliveDate = DateTime.UtcNow;
-            return SendAsync(new WebSocketMessage<string>
-            {
-                MessageType = "KeepAlive"
-            }, CancellationToken.None);
+            return SendAsync(
+                new WebSocketMessage<string>
+                {
+                    MessageId = Guid.NewGuid(),
+                    MessageType = "KeepAlive"
+                }, CancellationToken.None);
         }
 
         /// <inheritdoc />

+ 1 - 3
Emby.Server.Implementations/IO/LibraryMonitor.cs

@@ -18,7 +18,7 @@ namespace Emby.Server.Implementations.IO
 {
     public class LibraryMonitor : ILibraryMonitor
     {
-        private readonly ILogger _logger;
+        private readonly ILogger<LibraryMonitor> _logger;
         private readonly ILibraryManager _libraryManager;
         private readonly IServerConfigurationManager _configurationManager;
         private readonly IFileSystem _fileSystem;
@@ -266,7 +266,6 @@ namespace Emby.Server.Implementations.IO
                     {
                         DisposeWatcher(newWatcher, false);
                     }
-
                 }
                 catch (Exception ex)
                 {
@@ -393,7 +392,6 @@ namespace Emby.Server.Implementations.IO
                 }
 
                 return false;
-
             }))
             {
                 monitorPath = false;

+ 2 - 2
Emby.Server.Implementations/IO/ManagedFileSystem.cs

@@ -20,7 +20,7 @@ namespace Emby.Server.Implementations.IO
     /// </summary>
     public class ManagedFileSystem : IFileSystem
     {
-        protected ILogger Logger;
+        protected ILogger<ManagedFileSystem> Logger;
 
         private readonly List<IShortcutHandler> _shortcutHandlers = new List<IShortcutHandler>();
         private readonly string _tempPath;
@@ -237,7 +237,7 @@ namespace Emby.Server.Implementations.IO
             {
                 result.IsDirectory = info is DirectoryInfo || (info.Attributes & FileAttributes.Directory) == FileAttributes.Directory;
 
-                //if (!result.IsDirectory)
+                // if (!result.IsDirectory)
                 //{
                 //    result.IsHidden = (info.Attributes & FileAttributes.Hidden) == FileAttributes.Hidden;
                 //}

+ 0 - 1
Emby.Server.Implementations/Images/CollectionFolderImageProvider.cs

@@ -71,7 +71,6 @@ namespace Emby.Server.Implementations.Images
                     new ValueTuple<string, SortOrder>(ItemSortBy.Random, SortOrder.Ascending)
                 },
                 IncludeItemTypes = includeItemTypes
-
             });
         }
 

+ 0 - 1
Emby.Server.Implementations/Images/DynamicImageProvider.cs

@@ -78,7 +78,6 @@ namespace Emby.Server.Implementations.Images
                 }
 
                 return i;
-
             }).GroupBy(x => x.Id)
             .Select(x => x.First());
 

+ 19 - 12
Emby.Server.Implementations/Library/LibraryManager.cs

@@ -17,6 +17,8 @@ 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.Entities;
+using Jellyfin.Data.Enums;
 using MediaBrowser.Common.Extensions;
 using MediaBrowser.Common.Progress;
 using MediaBrowser.Controller;
@@ -25,7 +27,6 @@ using MediaBrowser.Controller.Drawing;
 using MediaBrowser.Controller.Dto;
 using MediaBrowser.Controller.Entities;
 using MediaBrowser.Controller.Entities.Audio;
-using MediaBrowser.Controller.Entities.TV;
 using MediaBrowser.Controller.IO;
 using MediaBrowser.Controller.Library;
 using MediaBrowser.Controller.LiveTv;
@@ -46,6 +47,9 @@ using MediaBrowser.Model.Querying;
 using MediaBrowser.Model.Tasks;
 using MediaBrowser.Providers.MediaInfo;
 using Microsoft.Extensions.Logging;
+using Episode = MediaBrowser.Controller.Entities.TV.Episode;
+using Genre = MediaBrowser.Controller.Entities.Genre;
+using Person = MediaBrowser.Controller.Entities.Person;
 using SortOrder = MediaBrowser.Model.Entities.SortOrder;
 using VideoResolver = Emby.Naming.Video.VideoResolver;
 
@@ -56,7 +60,7 @@ namespace Emby.Server.Implementations.Library
     /// </summary>
     public class LibraryManager : ILibraryManager
     {
-        private readonly ILogger _logger;
+        private readonly ILogger<LibraryManager> _logger;
         private readonly ITaskManager _taskManager;
         private readonly IUserManager _userManager;
         private readonly IUserDataManager _userDataRepository;
@@ -510,8 +514,8 @@ namespace Emby.Server.Implementations.Library
             return key.GetMD5();
         }
 
-        public BaseItem ResolvePath(FileSystemMetadata fileInfo, Folder parent = null)
-            => ResolvePath(fileInfo, new DirectoryService(_fileSystem), null, parent);
+        public BaseItem ResolvePath(FileSystemMetadata fileInfo, Folder parent = null, bool allowIgnorePath = true)
+            => ResolvePath(fileInfo, new DirectoryService(_fileSystem), null, parent, allowIgnorePath: allowIgnorePath);
 
         private BaseItem ResolvePath(
             FileSystemMetadata fileInfo,
@@ -519,7 +523,8 @@ namespace Emby.Server.Implementations.Library
             IItemResolver[] resolvers,
             Folder parent = null,
             string collectionType = null,
-            LibraryOptions libraryOptions = null)
+            LibraryOptions libraryOptions = null,
+            bool allowIgnorePath = true)
         {
             if (fileInfo == null)
             {
@@ -543,7 +548,7 @@ namespace Emby.Server.Implementations.Library
             };
 
             // Return null if ignore rules deem that we should do so
-            if (IgnoreFile(args.FileInfo, args.Parent))
+            if (allowIgnorePath && IgnoreFile(args.FileInfo, args.Parent))
             {
                 return null;
             }
@@ -707,7 +712,9 @@ namespace Emby.Server.Implementations.Library
 
             Directory.CreateDirectory(rootFolderPath);
 
-            var rootFolder = GetItemById(GetNewItemId(rootFolderPath, typeof(AggregateFolder))) as AggregateFolder ?? ((Folder)ResolvePath(_fileSystem.GetDirectoryInfo(rootFolderPath))).DeepCopy<Folder, AggregateFolder>();
+            var rootFolder = GetItemById(GetNewItemId(rootFolderPath, typeof(AggregateFolder))) as AggregateFolder ??
+                             ((Folder) ResolvePath(_fileSystem.GetDirectoryInfo(rootFolderPath), allowIgnorePath: false))
+                             .DeepCopy<Folder, AggregateFolder>();
 
             // In case program data folder was moved
             if (!string.Equals(rootFolder.Path, rootFolderPath, StringComparison.Ordinal))
@@ -788,7 +795,7 @@ namespace Emby.Server.Implementations.Library
                         if (tmpItem == null)
                         {
                             _logger.LogDebug("Creating new userRootFolder with DeepCopy");
-                            tmpItem = ((Folder)ResolvePath(_fileSystem.GetDirectoryInfo(userRootPath))).DeepCopy<Folder, UserRootFolder>();
+                            tmpItem = ((Folder)ResolvePath(_fileSystem.GetDirectoryInfo(userRootPath), allowIgnorePath: false)).DeepCopy<Folder, UserRootFolder>();
                         }
 
                         // In case program data folder was moved
@@ -1536,7 +1543,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)
@@ -2587,7 +2595,7 @@ namespace Emby.Server.Implementations.Library
                     Anime series don't generally have a season in their file name, however,
                     tvdb needs a season to correctly get the metadata.
                     Hence, a null season needs to be filled with something. */
-                    //FIXME perhaps this would be better for tvdb parser to ask for season 1 if no season is specified
+                    // FIXME perhaps this would be better for tvdb parser to ask for season 1 if no season is specified
                     episode.ParentIndexNumber = 1;
                 }
 
@@ -2853,7 +2861,6 @@ namespace Emby.Server.Implementations.Library
                     _logger.LogError(ex, "Error getting person");
                     return null;
                 }
-
             }).Where(i => i != null).ToList();
         }
 
@@ -2983,7 +2990,7 @@ namespace Emby.Server.Implementations.Library
 
         private static bool ValidateNetworkPath(string path)
         {
-            //if (Environment.OSVersion.Platform == PlatformID.Win32NT)
+            // if (Environment.OSVersion.Platform == PlatformID.Win32NT)
             //{
             //    // We can't validate protocol-based paths, so just allow them
             //    if (path.IndexOf("://", StringComparison.OrdinalIgnoreCase) == -1)

+ 2 - 5
Emby.Server.Implementations/Library/LiveStreamHelper.cs

@@ -50,7 +50,7 @@ namespace Emby.Server.Implementations.Library
                 {
                     mediaInfo = _json.DeserializeFromFile<MediaInfo>(cacheFilePath);
 
-                    //_logger.LogDebug("Found cached media info");
+                    // _logger.LogDebug("Found cached media info");
                 }
                 catch
                 {
@@ -85,7 +85,7 @@ namespace Emby.Server.Implementations.Library
                     Directory.CreateDirectory(Path.GetDirectoryName(cacheFilePath));
                     _json.SerializeToFile(mediaInfo, cacheFilePath);
 
-                    //_logger.LogDebug("Saved media info to {0}", cacheFilePath);
+                    // _logger.LogDebug("Saved media info to {0}", cacheFilePath);
                 }
             }
 
@@ -148,17 +148,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;

+ 26 - 32
Emby.Server.Implementations/Library/MediaSourceManager.cs

@@ -7,6 +7,8 @@ using System.IO;
 using System.Linq;
 using System.Threading;
 using System.Threading.Tasks;
+using Jellyfin.Data.Entities;
+using Jellyfin.Data.Enums;
 using MediaBrowser.Common.Configuration;
 using MediaBrowser.Common.Extensions;
 using MediaBrowser.Controller.Entities;
@@ -14,7 +16,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;
@@ -33,7 +34,7 @@ namespace Emby.Server.Implementations.Library
         private readonly ILibraryManager _libraryManager;
         private readonly IJsonSerializer _jsonSerializer;
         private readonly IFileSystem _fileSystem;
-        private readonly ILogger _logger;
+        private readonly ILogger<MediaSourceManager> _logger;
         private readonly IUserDataManager _userDataManager;
         private readonly IMediaEncoder _mediaEncoder;
         private readonly ILocalizationManager _localizationManager;
@@ -190,10 +191,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);
                     }
                 }
             }
@@ -352,7 +350,9 @@ namespace Emby.Server.Implementations.Library
 
         private void SetDefaultSubtitleStreamIndex(MediaSourceInfo source, UserItemData userData, 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 +363,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)
         {
-            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,11 +394,11 @@ 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)
@@ -435,7 +436,6 @@ namespace Emby.Server.Implementations.Library
                 }
 
                 return 1;
-
             }).ThenBy(i => i.Video3DFormat.HasValue ? 1 : 0)
             .ThenByDescending(i =>
             {
@@ -534,7 +534,7 @@ namespace Emby.Server.Implementations.Library
                 mediaSource.RunTimeTicks = null;
             }
 
-            var audioStream = mediaSource.MediaStreams.FirstOrDefault(i => i.Type == MediaBrowser.Model.Entities.MediaStreamType.Audio);
+            var audioStream = mediaSource.MediaStreams.FirstOrDefault(i => i.Type == MediaStreamType.Audio);
 
             if (audioStream == null || audioStream.Index == -1)
             {
@@ -545,7 +545,7 @@ namespace Emby.Server.Implementations.Library
                 mediaSource.DefaultAudioStreamIndex = audioStream.Index;
             }
 
-            var videoStream = mediaSource.MediaStreams.FirstOrDefault(i => i.Type == MediaBrowser.Model.Entities.MediaStreamType.Video);
+            var videoStream = mediaSource.MediaStreams.FirstOrDefault(i => i.Type == MediaStreamType.Video);
             if (videoStream != null)
             {
                 if (!videoStream.BitRate.HasValue)
@@ -556,17 +556,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;
@@ -622,7 +619,6 @@ namespace Emby.Server.Implementations.Library
                     MediaSource = mediaSource,
                     ExtractChapters = false,
                     MediaType = DlnaProfileType.Video
-
                 }, cancellationToken).ConfigureAwait(false);
 
                 mediaSource.MediaStreams = info.MediaStreams;
@@ -648,7 +644,7 @@ namespace Emby.Server.Implementations.Library
                 {
                     mediaInfo = _jsonSerializer.DeserializeFromFile<MediaInfo>(cacheFilePath);
 
-                    //_logger.LogDebug("Found cached media info");
+                    // _logger.LogDebug("Found cached media info");
                 }
                 catch (Exception ex)
                 {
@@ -670,20 +666,21 @@ 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)
                 {
                     Directory.CreateDirectory(Path.GetDirectoryName(cacheFilePath));
                     _jsonSerializer.SerializeToFile(mediaInfo, cacheFilePath);
 
-                    //_logger.LogDebug("Saved media info to {0}", cacheFilePath);
+                    // _logger.LogDebug("Saved media info to {0}", cacheFilePath);
                 }
             }
 
@@ -749,17 +746,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;

+ 1 - 1
Emby.Server.Implementations/Library/MediaStreamSelector.cs

@@ -3,7 +3,7 @@
 using System;
 using System.Collections.Generic;
 using System.Linq;
-using MediaBrowser.Model.Configuration;
+using Jellyfin.Data.Enums;
 using MediaBrowser.Model.Entities;
 
 namespace Emby.Server.Implementations.Library

+ 7 - 11
Emby.Server.Implementations/Library/MusicManager.cs

@@ -3,6 +3,7 @@
 using System;
 using System.Collections.Generic;
 using System.Linq;
+using Jellyfin.Data.Entities;
 using MediaBrowser.Controller.Dto;
 using MediaBrowser.Controller.Entities;
 using MediaBrowser.Controller.Entities.Audio;
@@ -10,6 +11,7 @@ using MediaBrowser.Controller.Library;
 using MediaBrowser.Controller.Playlists;
 using MediaBrowser.Model.Entities;
 using MediaBrowser.Model.Querying;
+using MusicAlbum = MediaBrowser.Controller.Entities.Audio.MusicAlbum;
 
 namespace Emby.Server.Implementations.Library
 {
@@ -75,7 +77,6 @@ namespace Emby.Server.Implementations.Library
                 {
                     return Guid.Empty;
                 }
-
             }).Where(i => !i.Equals(Guid.Empty)).ToArray();
 
             return GetInstantMixFromGenreIds(genreIds, user, dtoOptions);
@@ -105,32 +106,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);
             }

+ 2 - 2
Emby.Server.Implementations/Library/Resolvers/Audio/AudioResolver.cs

@@ -209,8 +209,8 @@ namespace Emby.Server.Implementations.Library.Resolvers.Audio
                     Name = parseName ?
                         resolvedItem.Name :
                         Path.GetFileNameWithoutExtension(firstMedia.Path),
-                    //AdditionalParts = resolvedItem.Files.Skip(1).Select(i => i.Path).ToArray(),
-                    //LocalAlternateVersions = resolvedItem.AlternateVersions.Select(i => i.Path).ToArray()
+                    // AdditionalParts = resolvedItem.Files.Skip(1).Select(i => i.Path).ToArray(),
+                    // LocalAlternateVersions = resolvedItem.AlternateVersions.Select(i => i.Path).ToArray()
                 };
 
                 result.Items.Add(libraryItem);

+ 2 - 2
Emby.Server.Implementations/Library/Resolvers/Audio/MusicAlbumResolver.cs

@@ -92,7 +92,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.Audio
             // Args points to an album if parent is an Artist folder or it directly contains music
             if (args.IsDirectory)
             {
-                // if (args.Parent is MusicArtist) return true;  //saves us from testing children twice
+                // if (args.Parent is MusicArtist) return true;  // saves us from testing children twice
                 if (ContainsMusic(args.FileSystemChildren, true, args.DirectoryService, _logger, _fileSystem, _libraryManager))
                 {
                     return true;
@@ -109,7 +109,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.Audio
             IEnumerable<FileSystemMetadata> list,
             bool allowSubfolders,
             IDirectoryService directoryService,
-            ILogger logger,
+            ILogger<MusicAlbumResolver> logger,
             IFileSystem fileSystem,
             ILibraryManager libraryManager)
         {

+ 2 - 2
Emby.Server.Implementations/Library/Resolvers/BaseVideoResolver.cs

@@ -292,7 +292,7 @@ namespace Emby.Server.Implementations.Library.Resolvers
             }
 
             return true;
-            //var blurayExtensions = new[]
+            // var blurayExtensions = new[]
             //{
             //    ".mts",
             //    ".m2ts",
@@ -300,7 +300,7 @@ namespace Emby.Server.Implementations.Library.Resolvers
             //    ".mpls"
             //};
 
-            //return directoryService.GetFiles(fullPath).Any(i => blurayExtensions.Contains(i.Extension ?? string.Empty, StringComparer.OrdinalIgnoreCase));
+            // return directoryService.GetFiles(fullPath).Any(i => blurayExtensions.Contains(i.Extension ?? string.Empty, StringComparer.OrdinalIgnoreCase));
         }
     }
 }

+ 1 - 1
Emby.Server.Implementations/Library/Resolvers/Movies/BoxSetResolver.cs

@@ -69,7 +69,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.Movies
 
             if (!string.IsNullOrEmpty(id))
             {
-                item.SetProviderId(MetadataProviders.Tmdb, id);
+                item.SetProviderId(MetadataProvider.Tmdb, id);
             }
         }
     }

+ 2 - 2
Emby.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs

@@ -350,7 +350,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.Movies
 
                     if (!string.IsNullOrWhiteSpace(tmdbid))
                     {
-                        item.SetProviderId(MetadataProviders.Tmdb, tmdbid);
+                        item.SetProviderId(MetadataProvider.Tmdb, tmdbid);
                     }
                 }
 
@@ -361,7 +361,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.Movies
 
                     if (!string.IsNullOrWhiteSpace(imdbid))
                     {
-                        item.SetProviderId(MetadataProviders.Imdb, imdbid);
+                        item.SetProviderId(MetadataProvider.Imdb, imdbid);
                     }
                 }
             }

+ 1 - 2
Emby.Server.Implementations/Library/Resolvers/SpecialFolderResolver.cs

@@ -43,7 +43,7 @@ namespace Emby.Server.Implementations.Library.Resolvers
                 }
                 if (string.Equals(args.Path, _appPaths.DefaultUserViewsPath, StringComparison.OrdinalIgnoreCase))
                 {
-                    return new UserRootFolder();  //if we got here and still a root - must be user root
+                    return new UserRootFolder();  // if we got here and still a root - must be user root
                 }
                 if (args.IsVf)
                 {
@@ -73,7 +73,6 @@ namespace Emby.Server.Implementations.Library.Resolvers
                     {
                         return false;
                     }
-
                 })
                 .Select(i => _fileSystem.GetFileNameWithoutExtension(i))
                 .FirstOrDefault();

+ 1 - 2
Emby.Server.Implementations/Library/Resolvers/TV/SeasonResolver.cs

@@ -16,7 +16,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.TV
         private readonly IServerConfigurationManager _config;
         private readonly ILibraryManager _libraryManager;
         private readonly ILocalizationManager _localization;
-        private readonly ILogger _logger;
+        private readonly ILogger<SeasonResolver> _logger;
 
         /// <summary>
         /// Initializes a new instance of the <see cref="SeasonResolver"/> class.
@@ -94,7 +94,6 @@ namespace Emby.Server.Implementations.Library.Resolvers.TV
                             _localization.GetLocalizedString("NameSeasonNumber"),
                             seasonNumber,
                             args.GetLibraryOptions().PreferredMetadataLanguage);
-
                 }
 
                 return season;

+ 4 - 4
Emby.Server.Implementations/Library/Resolvers/TV/SeriesResolver.cs

@@ -20,7 +20,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.TV
     public class SeriesResolver : FolderResolver<Series>
     {
         private readonly IFileSystem _fileSystem;
-        private readonly ILogger _logger;
+        private readonly ILogger<SeriesResolver> _logger;
         private readonly ILibraryManager _libraryManager;
 
         /// <summary>
@@ -59,7 +59,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.TV
                 var collectionType = args.GetCollectionType();
                 if (string.Equals(collectionType, CollectionType.TvShows, StringComparison.OrdinalIgnoreCase))
                 {
-                    //if (args.ContainsFileSystemEntryByName("tvshow.nfo"))
+                    // if (args.ContainsFileSystemEntryByName("tvshow.nfo"))
                     //{
                     //    return new Series
                     //    {
@@ -119,7 +119,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.TV
             IEnumerable<FileSystemMetadata> fileSystemChildren,
             IDirectoryService directoryService,
             IFileSystem fileSystem,
-            ILogger logger,
+            ILogger<SeriesResolver> logger,
             ILibraryManager libraryManager,
             bool isTvContentType)
         {
@@ -217,7 +217,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.TV
 
             if (!string.IsNullOrEmpty(id))
             {
-                item.SetProviderId(MetadataProviders.Tvdb, id);
+                item.SetProviderId(MetadataProvider.Tvdb, id);
             }
         }
     }

+ 4 - 2
Emby.Server.Implementations/Library/SearchEngine.cs

@@ -3,6 +3,7 @@
 using System;
 using System.Collections.Generic;
 using System.Linq;
+using Jellyfin.Data.Entities;
 using MediaBrowser.Controller.Dto;
 using MediaBrowser.Controller.Entities;
 using MediaBrowser.Controller.Entities.Audio;
@@ -12,12 +13,14 @@ using MediaBrowser.Model.Entities;
 using MediaBrowser.Model.Querying;
 using MediaBrowser.Model.Search;
 using Microsoft.Extensions.Logging;
+using Genre = MediaBrowser.Controller.Entities.Genre;
+using Person = MediaBrowser.Controller.Entities.Person;
 
 namespace Emby.Server.Implementations.Library
 {
     public class SearchEngine : ISearchEngine
     {
-        private readonly ILogger _logger;
+        private readonly ILogger<SearchEngine> _logger;
         private readonly ILibraryManager _libraryManager;
         private readonly IUserManager _userManager;
 
@@ -204,7 +207,6 @@ namespace Emby.Server.Implementations.Library
             return mediaItems.Select(i => new SearchHintInfo
             {
                 Item = i
-
             }).ToList();
         }
     }

+ 3 - 1
Emby.Server.Implementations/Library/UserDataManager.cs

@@ -5,6 +5,7 @@ using System.Collections.Concurrent;
 using System.Collections.Generic;
 using System.Globalization;
 using System.Threading;
+using Jellyfin.Data.Entities;
 using MediaBrowser.Controller.Configuration;
 using MediaBrowser.Controller.Dto;
 using MediaBrowser.Controller.Entities;
@@ -13,6 +14,7 @@ using MediaBrowser.Controller.Persistence;
 using MediaBrowser.Model.Dto;
 using MediaBrowser.Model.Entities;
 using Microsoft.Extensions.Logging;
+using Book = MediaBrowser.Controller.Entities.Book;
 
 namespace Emby.Server.Implementations.Library
 {
@@ -26,7 +28,7 @@ namespace Emby.Server.Implementations.Library
         private readonly ConcurrentDictionary<string, UserItemData> _userData =
             new ConcurrentDictionary<string, UserItemData>(StringComparer.OrdinalIgnoreCase);
 
-        private readonly ILogger _logger;
+        private readonly ILogger<UserDataManager> _logger;
         private readonly IServerConfigurationManager _config;
         private readonly IUserManager _userManager;
         private readonly IUserDataRepository _repository;

+ 0 - 1107
Emby.Server.Implementations/Library/UserManager.cs

@@ -1,1107 +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>(user));
-        }
-
-        /// <summary>
-        /// Called when [user deleted].
-        /// </summary>
-        /// <param name="user">The user.</param>
-        private void OnUserDeleted(User user)
-        {
-            UserDeleted?.Invoke(this, new GenericEventArgs<User>(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 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>(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>(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>(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()
-        {
-
-        }
-    }
-}

+ 20 - 10
Emby.Server.Implementations/Library/UserViewManager.cs

@@ -5,6 +5,8 @@ using System.Collections.Generic;
 using System.Globalization;
 using System.Linq;
 using System.Threading;
+using Jellyfin.Data.Entities;
+using Jellyfin.Data.Enums;
 using MediaBrowser.Controller.Channels;
 using MediaBrowser.Controller.Configuration;
 using MediaBrowser.Controller.Dto;
@@ -17,6 +19,8 @@ using MediaBrowser.Model.Entities;
 using MediaBrowser.Model.Globalization;
 using MediaBrowser.Model.Library;
 using MediaBrowser.Model.Querying;
+using Genre = MediaBrowser.Controller.Entities.Genre;
+using Person = MediaBrowser.Controller.Entities.Person;
 
 namespace Emby.Server.Implementations.Library
 {
@@ -125,12 +129,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 +169,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)))
             {
@@ -270,7 +280,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 +342,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)

+ 2 - 2
Emby.Server.Implementations/Library/Validators/ArtistsPostScanTask.cs

@@ -16,7 +16,7 @@ namespace Emby.Server.Implementations.Library.Validators
         /// The _library manager.
         /// </summary>
         private readonly ILibraryManager _libraryManager;
-        private readonly ILogger _logger;
+        private readonly ILogger<ArtistsValidator> _logger;
         private readonly IItemRepository _itemRepo;
 
         /// <summary>
@@ -27,7 +27,7 @@ namespace Emby.Server.Implementations.Library.Validators
         /// <param name="itemRepo">The item repository.</param>
         public ArtistsPostScanTask(
             ILibraryManager libraryManager,
-            ILogger<ArtistsPostScanTask> logger,
+            ILogger<ArtistsValidator> logger,
             IItemRepository itemRepo)
         {
             _libraryManager = libraryManager;

+ 2 - 3
Emby.Server.Implementations/Library/Validators/ArtistsValidator.cs

@@ -24,7 +24,7 @@ namespace Emby.Server.Implementations.Library.Validators
         /// <summary>
         /// The logger.
         /// </summary>
-        private readonly ILogger _logger;
+        private readonly ILogger<ArtistsValidator> _logger;
         private readonly IItemRepository _itemRepo;
 
         /// <summary>
@@ -33,7 +33,7 @@ namespace Emby.Server.Implementations.Library.Validators
         /// <param name="libraryManager">The library manager.</param>
         /// <param name="logger">The logger.</param>
         /// <param name="itemRepo">The item repository.</param>
-        public ArtistsValidator(ILibraryManager libraryManager, ILogger logger, IItemRepository itemRepo)
+        public ArtistsValidator(ILibraryManager libraryManager, ILogger<ArtistsValidator> logger, IItemRepository itemRepo)
         {
             _libraryManager = libraryManager;
             _logger = logger;
@@ -98,7 +98,6 @@ namespace Emby.Server.Implementations.Library.Validators
                 _libraryManager.DeleteItem(item, new DeleteOptions
                 {
                     DeleteFileLocation = false
-
                 }, false);
             }
 

+ 2 - 2
Emby.Server.Implementations/Library/Validators/GenresPostScanTask.cs

@@ -16,7 +16,7 @@ namespace Emby.Server.Implementations.Library.Validators
         /// The _library manager.
         /// </summary>
         private readonly ILibraryManager _libraryManager;
-        private readonly ILogger _logger;
+        private readonly ILogger<GenresValidator> _logger;
         private readonly IItemRepository _itemRepo;
 
         /// <summary>
@@ -27,7 +27,7 @@ namespace Emby.Server.Implementations.Library.Validators
         /// <param name="itemRepo">The item repository.</param>
         public GenresPostScanTask(
             ILibraryManager libraryManager,
-            ILogger<GenresPostScanTask> logger,
+            ILogger<GenresValidator> logger,
             IItemRepository itemRepo)
         {
             _libraryManager = libraryManager;

+ 2 - 2
Emby.Server.Implementations/Library/Validators/GenresValidator.cs

@@ -21,7 +21,7 @@ namespace Emby.Server.Implementations.Library.Validators
         /// <summary>
         /// The logger.
         /// </summary>
-        private readonly ILogger _logger;
+        private readonly ILogger<GenresValidator> _logger;
 
         /// <summary>
         /// Initializes a new instance of the <see cref="GenresValidator"/> class.
@@ -29,7 +29,7 @@ namespace Emby.Server.Implementations.Library.Validators
         /// <param name="libraryManager">The library manager.</param>
         /// <param name="logger">The logger.</param>
         /// <param name="itemRepo">The item repository.</param>
-        public GenresValidator(ILibraryManager libraryManager, ILogger logger, IItemRepository itemRepo)
+        public GenresValidator(ILibraryManager libraryManager, ILogger<GenresValidator> logger, IItemRepository itemRepo)
         {
             _libraryManager = libraryManager;
             _logger = logger;

+ 2 - 2
Emby.Server.Implementations/Library/Validators/MusicGenresPostScanTask.cs

@@ -16,7 +16,7 @@ namespace Emby.Server.Implementations.Library.Validators
         /// The library manager.
         /// </summary>
         private readonly ILibraryManager _libraryManager;
-        private readonly ILogger _logger;
+        private readonly ILogger<MusicGenresValidator> _logger;
         private readonly IItemRepository _itemRepo;
 
         /// <summary>
@@ -27,7 +27,7 @@ namespace Emby.Server.Implementations.Library.Validators
         /// <param name="itemRepo">The item repository.</param>
         public MusicGenresPostScanTask(
             ILibraryManager libraryManager,
-            ILogger<MusicGenresPostScanTask> logger,
+            ILogger<MusicGenresValidator> logger,
             IItemRepository itemRepo)
         {
             _libraryManager = libraryManager;

+ 2 - 2
Emby.Server.Implementations/Library/Validators/MusicGenresValidator.cs

@@ -20,7 +20,7 @@ namespace Emby.Server.Implementations.Library.Validators
         /// <summary>
         /// The logger.
         /// </summary>
-        private readonly ILogger _logger;
+        private readonly ILogger<MusicGenresValidator> _logger;
         private readonly IItemRepository _itemRepo;
 
         /// <summary>
@@ -29,7 +29,7 @@ namespace Emby.Server.Implementations.Library.Validators
         /// <param name="libraryManager">The library manager.</param>
         /// <param name="logger">The logger.</param>
         /// <param name="itemRepo">The item repository.</param>
-        public MusicGenresValidator(ILibraryManager libraryManager, ILogger logger, IItemRepository itemRepo)
+        public MusicGenresValidator(ILibraryManager libraryManager, ILogger<MusicGenresValidator> logger, IItemRepository itemRepo)
         {
             _libraryManager = libraryManager;
             _logger = logger;

+ 2 - 2
Emby.Server.Implementations/Library/Validators/StudiosPostScanTask.cs

@@ -17,7 +17,7 @@ namespace Emby.Server.Implementations.Library.Validators
         /// </summary>
         private readonly ILibraryManager _libraryManager;
 
-        private readonly ILogger _logger;
+        private readonly ILogger<StudiosValidator> _logger;
         private readonly IItemRepository _itemRepo;
 
         /// <summary>
@@ -28,7 +28,7 @@ namespace Emby.Server.Implementations.Library.Validators
         /// <param name="itemRepo">The item repository.</param>
         public StudiosPostScanTask(
             ILibraryManager libraryManager,
-            ILogger<StudiosPostScanTask> logger,
+            ILogger<StudiosValidator> logger,
             IItemRepository itemRepo)
         {
             _libraryManager = libraryManager;

+ 2 - 3
Emby.Server.Implementations/Library/Validators/StudiosValidator.cs

@@ -24,7 +24,7 @@ namespace Emby.Server.Implementations.Library.Validators
         /// <summary>
         /// The logger.
         /// </summary>
-        private readonly ILogger _logger;
+        private readonly ILogger<StudiosValidator> _logger;
 
         /// <summary>
         /// Initializes a new instance of the <see cref="StudiosValidator" /> class.
@@ -32,7 +32,7 @@ namespace Emby.Server.Implementations.Library.Validators
         /// <param name="libraryManager">The library manager.</param>
         /// <param name="logger">The logger.</param>
         /// <param name="itemRepo">The item repository.</param>
-        public StudiosValidator(ILibraryManager libraryManager, ILogger logger, IItemRepository itemRepo)
+        public StudiosValidator(ILibraryManager libraryManager, ILogger<StudiosValidator> logger, IItemRepository itemRepo)
         {
             _libraryManager = libraryManager;
             _logger = logger;
@@ -92,7 +92,6 @@ namespace Emby.Server.Implementations.Library.Validators
                 _libraryManager.DeleteItem(item, new DeleteOptions
                 {
                     DeleteFileLocation = false
-
                 }, false);
             }
 

+ 9 - 10
Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs

@@ -46,7 +46,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
         private const int TunerDiscoveryDurationMs = 3000;
 
         private readonly IServerApplicationHost _appHost;
-        private readonly ILogger _logger;
+        private readonly ILogger<EmbyTV> _logger;
         private readonly IHttpClient _httpClient;
         private readonly IServerConfigurationManager _config;
         private readonly IJsonSerializer _jsonSerializer;
@@ -1547,7 +1547,6 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
                         IsFolder = false,
                         Recursive = true,
                         DtoOptions = new DtoOptions(true)
-
                     })
                     .Where(i => i.IsFileProtocol && File.Exists(i.Path))
                     .Skip(seriesTimer.KeepUpTo - 1)
@@ -1893,22 +1892,22 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
                     writer.WriteStartDocument(true);
                     writer.WriteStartElement("tvshow");
                     string id;
-                    if (timer.SeriesProviderIds.TryGetValue(MetadataProviders.Tvdb.ToString(), out id))
+                    if (timer.SeriesProviderIds.TryGetValue(MetadataProvider.Tvdb.ToString(), out id))
                     {
                         writer.WriteElementString("id", id);
                     }
 
-                    if (timer.SeriesProviderIds.TryGetValue(MetadataProviders.Imdb.ToString(), out id))
+                    if (timer.SeriesProviderIds.TryGetValue(MetadataProvider.Imdb.ToString(), out id))
                     {
                         writer.WriteElementString("imdb_id", id);
                     }
 
-                    if (timer.SeriesProviderIds.TryGetValue(MetadataProviders.Tmdb.ToString(), out id))
+                    if (timer.SeriesProviderIds.TryGetValue(MetadataProvider.Tmdb.ToString(), out id))
                     {
                         writer.WriteElementString("tmdbid", id);
                     }
 
-                    if (timer.SeriesProviderIds.TryGetValue(MetadataProviders.Zap2It.ToString(), out id))
+                    if (timer.SeriesProviderIds.TryGetValue(MetadataProvider.Zap2It.ToString(), out id))
                     {
                         writer.WriteElementString("zap2itid", id);
                     }
@@ -2075,14 +2074,14 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
                         writer.WriteElementString("credits", person);
                     }
 
-                    var tmdbCollection = item.GetProviderId(MetadataProviders.TmdbCollection);
+                    var tmdbCollection = item.GetProviderId(MetadataProvider.TmdbCollection);
 
                     if (!string.IsNullOrEmpty(tmdbCollection))
                     {
                         writer.WriteElementString("collectionnumber", tmdbCollection);
                     }
 
-                    var imdb = item.GetProviderId(MetadataProviders.Imdb);
+                    var imdb = item.GetProviderId(MetadataProvider.Imdb);
                     if (!string.IsNullOrEmpty(imdb))
                     {
                         if (!isSeriesEpisode)
@@ -2096,7 +2095,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
                         lockData = false;
                     }
 
-                    var tvdb = item.GetProviderId(MetadataProviders.Tvdb);
+                    var tvdb = item.GetProviderId(MetadataProvider.Tvdb);
                     if (!string.IsNullOrEmpty(tvdb))
                     {
                         writer.WriteElementString("tvdbid", tvdb);
@@ -2105,7 +2104,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
                         lockData = false;
                     }
 
-                    var tmdb = item.GetProviderId(MetadataProviders.Tmdb);
+                    var tmdb = item.GetProviderId(MetadataProvider.Tmdb);
                     if (!string.IsNullOrEmpty(tmdb))
                     {
                         writer.WriteElementString("tmdbid", tmdb);

+ 5 - 5
Emby.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs

@@ -183,7 +183,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
 
             var subtitleArgs = CopySubtitles ? " -codec:s copy" : " -sn";
 
-            //var outputParam = string.Equals(Path.GetExtension(targetFile), ".mp4", StringComparison.OrdinalIgnoreCase) ?
+            // var outputParam = string.Equals(Path.GetExtension(targetFile), ".mp4", StringComparison.OrdinalIgnoreCase) ?
             //    " -f mp4 -movflags frag_keyframe+empty_moov" :
             //    string.Empty;
 
@@ -206,13 +206,13 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
         {
             return "-codec:a:0 copy";
 
-            //var audioChannels = 2;
-            //var audioStream = mediaStreams.FirstOrDefault(i => i.Type == MediaStreamType.Audio);
-            //if (audioStream != null)
+            // var audioChannels = 2;
+            // var audioStream = mediaStreams.FirstOrDefault(i => i.Type == MediaStreamType.Audio);
+            // if (audioStream != null)
             //{
             //    audioChannels = audioStream.Channels ?? audioChannels;
             //}
-            //return "-codec:a:0 aac -strict experimental -ab 320000";
+            // return "-codec:a:0 aac -strict experimental -ab 320000";
         }
 
         private static bool EncodeVideo(MediaSourceInfo mediaSource)

+ 0 - 1
Emby.Server.Implementations/LiveTv/EmbyTV/RecordingHelper.cs

@@ -56,7 +56,6 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
                     name += " " + info.EpisodeTitle;
                 }
             }
-
             else if (info.IsMovie && info.ProductionYear != null)
             {
                 name += " (" + info.ProductionYear + ")";

+ 6 - 7
Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs

@@ -24,7 +24,7 @@ namespace Emby.Server.Implementations.LiveTv.Listings
 {
     public class SchedulesDirect : IListingsProvider
     {
-        private readonly ILogger _logger;
+        private readonly ILogger<SchedulesDirect> _logger;
         private readonly IJsonSerializer _jsonSerializer;
         private readonly IHttpClient _httpClient;
         private readonly SemaphoreSlim _tokenSemaphore = new SemaphoreSlim(1, 1);
@@ -145,7 +145,7 @@ namespace Emby.Server.Implementations.LiveTv.Listings
                     var programsInfo = new List<ProgramInfo>();
                     foreach (ScheduleDirect.Program schedule in dailySchedules.SelectMany(d => d.programs))
                     {
-                        //_logger.LogDebug("Proccesing Schedule for statio ID " + stationID +
+                        // _logger.LogDebug("Proccesing Schedule for statio ID " + stationID +
                         //              " which corresponds to channel " + channelNumber + " and program id " +
                         //              schedule.programID + " which says it has images? " +
                         //              programDict[schedule.programID].hasImageArtwork);
@@ -178,7 +178,7 @@ namespace Emby.Server.Implementations.LiveTv.Listings
 
                                 programEntry.backdropImage = GetProgramImage(ApiUrl, imagesWithoutText, true, WideAspect);
 
-                                //programEntry.bannerImage = GetProgramImage(ApiUrl, data, "Banner", false) ??
+                                // programEntry.bannerImage = GetProgramImage(ApiUrl, data, "Banner", false) ??
                                 //    GetProgramImage(ApiUrl, data, "Banner-L1", false) ??
                                 //    GetProgramImage(ApiUrl, data, "Banner-LO", false) ??
                                 //    GetProgramImage(ApiUrl, data, "Banner-LOT", false);
@@ -276,7 +276,7 @@ namespace Emby.Server.Implementations.LiveTv.Listings
                 CommunityRating = null,
                 EpisodeTitle = episodeTitle,
                 Audio = audioType,
-                //IsNew = programInfo.@new ?? false,
+                // IsNew = programInfo.@new ?? false,
                 IsRepeat = programInfo.@new == null,
                 IsSeries = string.Equals(details.entityType, "episode", StringComparison.OrdinalIgnoreCase),
                 ImageUrl = details.primaryImage,
@@ -342,7 +342,7 @@ namespace Emby.Server.Implementations.LiveTv.Listings
             {
                 info.SeriesId = programId.Substring(0, 10);
 
-                info.SeriesProviderIds[MetadataProviders.Zap2It.ToString()] = info.SeriesId;
+                info.SeriesProviderIds[MetadataProvider.Zap2It.ToString()] = info.SeriesId;
 
                 if (details.metadata != null)
                 {
@@ -701,7 +701,7 @@ namespace Emby.Server.Implementations.LiveTv.Listings
                 CancellationToken = cancellationToken,
                 LogErrorResponseBody = true
             };
-            //_logger.LogInformation("Obtaining token from Schedules Direct from addres: " + httpOptions.Url + " with body " +
+            // _logger.LogInformation("Obtaining token from Schedules Direct from addres: " + httpOptions.Url + " with body " +
             // httpOptions.RequestContent);
 
             using (var response = await Post(httpOptions, false, null).ConfigureAwait(false))
@@ -1218,7 +1218,6 @@ namespace Emby.Server.Implementations.LiveTv.Listings
                 public string programID { get; set; }
                 public List<ImageData> data { get; set; }
             }
-
         }
     }
 }

+ 1 - 1
Emby.Server.Implementations/LiveTv/Listings/XmlTvListingsProvider.cs

@@ -26,7 +26,7 @@ namespace Emby.Server.Implementations.LiveTv.Listings
     {
         private readonly IServerConfigurationManager _config;
         private readonly IHttpClient _httpClient;
-        private readonly ILogger _logger;
+        private readonly ILogger<XmlTvListingsProvider> _logger;
         private readonly IFileSystem _fileSystem;
         private readonly IZipClient _zipClient;
 

+ 1 - 1
Emby.Server.Implementations/LiveTv/LiveTvDtoService.cs

@@ -26,7 +26,7 @@ namespace Emby.Server.Implementations.LiveTv
 
         private const string ServiceName = "Emby";
 
-        private readonly ILogger _logger;
+        private readonly ILogger<LiveTvDtoService> _logger;
         private readonly IImageProcessor _imageProcessor;
         private readonly IDtoService _dtoService;
         private readonly IApplicationHost _appHost;

+ 20 - 27
Emby.Server.Implementations/LiveTv/LiveTvManager.cs

@@ -7,6 +7,8 @@ using System.Linq;
 using System.Threading;
 using System.Threading.Tasks;
 using Emby.Server.Implementations.Library;
+using Jellyfin.Data.Entities;
+using Jellyfin.Data.Enums;
 using MediaBrowser.Common.Configuration;
 using MediaBrowser.Common.Extensions;
 using MediaBrowser.Common.Progress;
@@ -14,8 +16,6 @@ using MediaBrowser.Controller.Channels;
 using MediaBrowser.Controller.Configuration;
 using MediaBrowser.Controller.Dto;
 using MediaBrowser.Controller.Entities;
-using MediaBrowser.Controller.Entities.Movies;
-using MediaBrowser.Controller.Entities.TV;
 using MediaBrowser.Controller.Library;
 using MediaBrowser.Controller.LiveTv;
 using MediaBrowser.Controller.Persistence;
@@ -31,6 +31,8 @@ using MediaBrowser.Model.Querying;
 using MediaBrowser.Model.Serialization;
 using MediaBrowser.Model.Tasks;
 using Microsoft.Extensions.Logging;
+using Episode = MediaBrowser.Controller.Entities.TV.Episode;
+using Movie = MediaBrowser.Controller.Entities.Movies.Movie;
 
 namespace Emby.Server.Implementations.LiveTv
 {
@@ -44,7 +46,7 @@ namespace Emby.Server.Implementations.LiveTv
         private const string EtagKey = "ProgramEtag";
 
         private readonly IServerConfigurationManager _config;
-        private readonly ILogger _logger;
+        private readonly ILogger<LiveTvManager> _logger;
         private readonly IItemRepository _itemRepo;
         private readonly IUserManager _userManager;
         private readonly IDtoService _dtoService;
@@ -404,8 +406,8 @@ namespace Emby.Server.Implementations.LiveTv
             if (!(service is EmbyTV.EmbyTV))
             {
                 // We can't trust that we'll be able to direct stream it through emby server, no matter what the provider says
-                //mediaSource.SupportsDirectPlay = false;
-                //mediaSource.SupportsDirectStream = false;
+                // mediaSource.SupportsDirectPlay = false;
+                // mediaSource.SupportsDirectStream = false;
                 mediaSource.SupportsTranscoding = true;
                 foreach (var stream in mediaSource.MediaStreams)
                 {
@@ -556,7 +558,7 @@ namespace Emby.Server.Implementations.LiveTv
             }
             item.ParentId = channel.Id;
 
-            //item.ChannelType = channelType;
+            // item.ChannelType = channelType;
 
             item.Audio = info.Audio;
             item.ChannelId = channel.Id;
@@ -696,7 +698,6 @@ namespace Emby.Server.Implementations.LiveTv
                     {
                         Path = info.ThumbImageUrl,
                         Type = ImageType.Thumb
-
                     }, 0);
                 }
             }
@@ -709,7 +710,6 @@ namespace Emby.Server.Implementations.LiveTv
                     {
                         Path = info.LogoImageUrl,
                         Type = ImageType.Logo
-
                     }, 0);
                 }
             }
@@ -722,7 +722,6 @@ namespace Emby.Server.Implementations.LiveTv
                     {
                         Path = info.BackdropImageUrl,
                         Type = ImageType.Backdrop
-
                     }, 0);
                 }
             }
@@ -760,7 +759,8 @@ namespace Emby.Server.Implementations.LiveTv
 
             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)
             };
 
@@ -1168,7 +1168,6 @@ namespace Emby.Server.Implementations.LiveTv
                         IncludeItemTypes = new string[] { typeof(LiveTvProgram).Name },
                         ChannelIds = new Guid[] { currentChannel.Id },
                         DtoOptions = new DtoOptions(true)
-
                     }).Cast<LiveTvProgram>().ToDictionary(i => i.Id);
 
                     var newPrograms = new List<LiveTvProgram>();
@@ -1368,10 +1367,10 @@ namespace Emby.Server.Implementations.LiveTv
                 // limit = (query.Limit ?? 10) * 2;
                 limit = null;
 
-                //var allActivePaths = EmbyTV.EmbyTV.Current.GetAllActiveRecordings().Select(i => i.Path).ToArray();
-                //var items = allActivePaths.Select(i => _libraryManager.FindByPath(i, false)).Where(i => i != null).ToArray();
+                // var allActivePaths = EmbyTV.EmbyTV.Current.GetAllActiveRecordings().Select(i => i.Path).ToArray();
+                // var items = allActivePaths.Select(i => _libraryManager.FindByPath(i, false)).Where(i => i != null).ToArray();
 
-                //return new QueryResult<BaseItem>
+                // return new QueryResult<BaseItem>
                 //{
                 //    Items = items,
                 //    TotalRecordCount = items.Length
@@ -1738,7 +1737,6 @@ namespace Emby.Server.Implementations.LiveTv
             var results = await GetTimers(new TimerQuery
             {
                 Id = id
-
             }, cancellationToken).ConfigureAwait(false);
 
             return results.Items.FirstOrDefault(i => string.Equals(i.Id, id, StringComparison.OrdinalIgnoreCase));
@@ -1790,7 +1788,6 @@ namespace Emby.Server.Implementations.LiveTv
                 .Select(i =>
                 {
                     return i.Item1;
-
                 })
                 .ToArray();
 
@@ -1845,7 +1842,6 @@ namespace Emby.Server.Implementations.LiveTv
                     }
 
                     return _tvDtoService.GetSeriesTimerInfoDto(i.Item1, i.Item2, channelName);
-
                 })
                 .ToArray();
 
@@ -1878,7 +1874,6 @@ namespace Emby.Server.Implementations.LiveTv
                 OrderBy = new[] { (ItemSortBy.StartDate, SortOrder.Ascending) },
                 TopParentIds = new[] { GetInternalLiveTvFolder(CancellationToken.None).Id },
                 DtoOptions = options
-
             }) : new List<BaseItem>();
 
             RemoveFields(options);
@@ -1956,7 +1951,7 @@ namespace Emby.Server.Implementations.LiveTv
                     OriginalAirDate = program.PremiereDate,
                     Overview = program.Overview,
                     StartDate = program.StartDate,
-                    //ImagePath = program.ExternalImagePath,
+                    // ImagePath = program.ExternalImagePath,
                     Name = program.Name,
                     OfficialRating = program.OfficialRating
                 };
@@ -2167,20 +2162,19 @@ 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)
         {
-            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()
@@ -2457,7 +2451,6 @@ namespace Emby.Server.Implementations.LiveTv
                 UserId = user.Id,
                 IsRecordingsFolder = true,
                 RefreshLatestChannelItems = refreshChannels
-
             }).Items);
 
             return folders.Cast<BaseItem>().ToList();

+ 1 - 1
Emby.Server.Implementations/LiveTv/LiveTvMediaSourceProvider.cs

@@ -23,7 +23,7 @@ namespace Emby.Server.Implementations.LiveTv
         private const string StreamIdDelimeterString = "_";
 
         private readonly ILiveTvManager _liveTvManager;
-        private readonly ILogger _logger;
+        private readonly ILogger<LiveTvMediaSourceProvider> _logger;
         private readonly IMediaSourceManager _mediaSourceManager;
         private readonly IServerApplicationHost _appHost;
 

+ 3 - 5
Emby.Server.Implementations/LiveTv/TunerHosts/BaseTunerHost.cs

@@ -22,14 +22,14 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
     public abstract class BaseTunerHost
     {
         protected readonly IServerConfigurationManager Config;
-        protected readonly ILogger Logger;
+        protected readonly ILogger<BaseTunerHost> Logger;
         protected IJsonSerializer JsonSerializer;
         protected readonly IFileSystem FileSystem;
 
         private readonly ConcurrentDictionary<string, ChannelCache> _channelCache =
             new ConcurrentDictionary<string, ChannelCache>(StringComparer.OrdinalIgnoreCase);
 
-        protected BaseTunerHost(IServerConfigurationManager config, ILogger logger, IJsonSerializer jsonSerializer, IFileSystem fileSystem)
+        protected BaseTunerHost(IServerConfigurationManager config, ILogger<BaseTunerHost> logger, IJsonSerializer jsonSerializer, IFileSystem fileSystem)
         {
             Config = config;
             Logger = logger;
@@ -54,7 +54,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
 
             var result = await GetChannelsInternal(tuner, cancellationToken).ConfigureAwait(false);
             var list = result.ToList();
-            //logger.LogInformation("Channels from {0}: {1}", tuner.Url, JsonSerializer.SerializeToString(list));
+            // logger.LogInformation("Channels from {0}: {1}", tuner.Url, JsonSerializer.SerializeToString(list));
 
             if (!string.IsNullOrEmpty(key) && list.Count > 0)
             {
@@ -99,7 +99,6 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
                         }
                         catch (IOException)
                         {
-
                         }
                     }
                 }
@@ -116,7 +115,6 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
                         }
                         catch (IOException)
                         {
-
                         }
                     }
                 }

+ 2 - 5
Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs

@@ -111,7 +111,6 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
                 ChannelType = ChannelType.TV,
                 IsLegacyTuner = (i.URL ?? string.Empty).StartsWith("hdhomerun", StringComparison.OrdinalIgnoreCase),
                 Path = i.URL
-
             }).Cast<ChannelInfo>().ToList();
         }
 
@@ -481,7 +480,6 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
                                 Height = height,
                                 BitRate = videoBitrate,
                                 NalLengthSize = nal
-
                             },
                             new MediaStream
                             {
@@ -502,8 +500,8 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
                 SupportsTranscoding = true,
                 IsInfiniteStream = true,
                 IgnoreDts = true,
-                //IgnoreIndex = true,
-                //ReadAtNativeFramerate = true
+                // IgnoreIndex = true,
+                // ReadAtNativeFramerate = true
             };
 
             mediaSource.InferTotalBitrate();
@@ -722,7 +720,6 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
                             }
                         }
                     }
-
                 }
                 catch (OperationCanceledException)
                 {

+ 7 - 7
Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunUdpStream.cs

@@ -117,17 +117,17 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
                 taskCompletionSource,
                 LiveStreamCancellationTokenSource.Token).ConfigureAwait(false);
 
-            //OpenedMediaSource.Protocol = MediaProtocol.File;
-            //OpenedMediaSource.Path = tempFile;
-            //OpenedMediaSource.ReadAtNativeFramerate = true;
+            // OpenedMediaSource.Protocol = MediaProtocol.File;
+            // OpenedMediaSource.Path = tempFile;
+            // OpenedMediaSource.ReadAtNativeFramerate = true;
 
             MediaSource.Path = _appHost.GetLoopbackHttpApiUrl() + "/LiveTv/LiveStreamFiles/" + UniqueId + "/stream.ts";
             MediaSource.Protocol = MediaProtocol.Http;
-            //OpenedMediaSource.SupportsDirectPlay = false;
-            //OpenedMediaSource.SupportsDirectStream = true;
-            //OpenedMediaSource.SupportsTranscoding = true;
+            // OpenedMediaSource.SupportsDirectPlay = false;
+            // OpenedMediaSource.SupportsDirectStream = true;
+            // OpenedMediaSource.SupportsTranscoding = true;
 
-            //await Task.Delay(5000).ConfigureAwait(false);
+            // await Task.Delay(5000).ConfigureAwait(false);
             await taskCompletionSource.Task.ConfigureAwait(false);
         }
 

+ 0 - 2
Emby.Server.Implementations/LiveTv/TunerHosts/LiveStream.cs

@@ -220,11 +220,9 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
             }
             catch (IOException)
             {
-
             }
             catch (ArgumentException)
             {
-
             }
             catch (Exception ex)
             {

+ 0 - 1
Emby.Server.Implementations/LiveTv/TunerHosts/M3UTunerHost.cs

@@ -127,7 +127,6 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
         {
             using (var stream = await new M3uParser(Logger, _httpClient, _appHost).GetListingsStream(info.Url, CancellationToken.None).ConfigureAwait(false))
             {
-
             }
         }
 

+ 1 - 2
Emby.Server.Implementations/LiveTv/TunerHosts/M3uParser.cs

@@ -210,7 +210,6 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
                         }
                     }
                 }
-
             }
 
             if (!IsValidChannelNumber(numberString))
@@ -284,7 +283,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
 
                     if (double.TryParse(numberPart, NumberStyles.Any, CultureInfo.InvariantCulture, out var number))
                     {
-                        //channel.Number = number.ToString();
+                        // channel.Number = number.ToString();
                         nameInExtInf = nameInExtInf.Substring(numberIndex + 1).Trim(new[] { ' ', '-' });
                     }
                 }

+ 10 - 10
Emby.Server.Implementations/LiveTv/TunerHosts/SharedHttpStream.cs

@@ -103,21 +103,21 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
 
             _ = StartStreaming(response, taskCompletionSource, LiveStreamCancellationTokenSource.Token);
 
-            //OpenedMediaSource.Protocol = MediaProtocol.File;
-            //OpenedMediaSource.Path = tempFile;
-            //OpenedMediaSource.ReadAtNativeFramerate = true;
+            // OpenedMediaSource.Protocol = MediaProtocol.File;
+            // OpenedMediaSource.Path = tempFile;
+            // OpenedMediaSource.ReadAtNativeFramerate = true;
 
             MediaSource.Path = _appHost.GetLoopbackHttpApiUrl() + "/LiveTv/LiveStreamFiles/" + UniqueId + "/stream.ts";
             MediaSource.Protocol = MediaProtocol.Http;
 
-            //OpenedMediaSource.Path = TempFilePath;
-            //OpenedMediaSource.Protocol = MediaProtocol.File;
+            // OpenedMediaSource.Path = TempFilePath;
+            // OpenedMediaSource.Protocol = MediaProtocol.File;
 
-            //OpenedMediaSource.Path = _tempFilePath;
-            //OpenedMediaSource.Protocol = MediaProtocol.File;
-            //OpenedMediaSource.SupportsDirectPlay = false;
-            //OpenedMediaSource.SupportsDirectStream = true;
-            //OpenedMediaSource.SupportsTranscoding = true;
+            // OpenedMediaSource.Path = _tempFilePath;
+            // OpenedMediaSource.Protocol = MediaProtocol.File;
+            // OpenedMediaSource.SupportsDirectPlay = false;
+            // OpenedMediaSource.SupportsDirectStream = true;
+            // OpenedMediaSource.SupportsTranscoding = true;
             await taskCompletionSource.Task.ConfigureAwait(false);
             if (taskCompletionSource.Task.Exception != null)
             {

+ 1 - 1
Emby.Server.Implementations/Localization/LocalizationManager.cs

@@ -25,7 +25,7 @@ namespace Emby.Server.Implementations.Localization
 
         private readonly IServerConfigurationManager _configurationManager;
         private readonly IJsonSerializer _jsonSerializer;
-        private readonly ILogger _logger;
+        private readonly ILogger<LocalizationManager> _logger;
 
         private readonly Dictionary<string, Dictionary<string, ParentalRating>> _allParentalRatings =
             new Dictionary<string, Dictionary<string, ParentalRating>>(StringComparer.OrdinalIgnoreCase);

+ 1 - 1
Emby.Server.Implementations/MediaEncoder/EncodingManager.cs

@@ -23,7 +23,7 @@ namespace Emby.Server.Implementations.MediaEncoder
     {
         private readonly CultureInfo _usCulture = new CultureInfo("en-US");
         private readonly IFileSystem _fileSystem;
-        private readonly ILogger _logger;
+        private readonly ILogger<EncodingManager> _logger;
         private readonly IMediaEncoder _encoder;
         private readonly IChapterManager _chapterManager;
         private readonly ILibraryManager _libraryManager;

+ 1 - 3
Emby.Server.Implementations/Net/SocketFactory.cs

@@ -98,7 +98,6 @@ namespace Emby.Server.Implementations.Net
             }
             catch (SocketException)
             {
-
             }
 
             try
@@ -109,12 +108,11 @@ namespace Emby.Server.Implementations.Net
             }
             catch (SocketException)
             {
-
             }
 
             try
             {
-                //retVal.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.Broadcast, true);
+                // retVal.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.Broadcast, true);
                 retVal.SetSocketOption(SocketOptionLevel.IP, SocketOptionName.MulticastTimeToLive, multicastTimeToLive);
 
                 var localIp = IPAddress.Any;

+ 2 - 2
Emby.Server.Implementations/Networking/NetworkManager.cs

@@ -15,7 +15,7 @@ namespace Emby.Server.Implementations.Networking
 {
     public class NetworkManager : INetworkManager
     {
-        private readonly ILogger _logger;
+        private readonly ILogger<NetworkManager> _logger;
 
         private IPAddress[] _localIpAddresses;
         private readonly object _localIpAddressSyncLock = new object();
@@ -167,7 +167,7 @@ namespace Emby.Server.Implementations.Networking
 
                 foreach (var subnet_Match in subnets)
                 {
-                    //logger.LogDebug("subnet_Match:" + subnet_Match);
+                    // logger.LogDebug("subnet_Match:" + subnet_Match);
 
                     if (endpoint.StartsWith(subnet_Match + ".", StringComparison.OrdinalIgnoreCase))
                     {

部分文件因文件數量過多而無法顯示