瀏覽代碼

Merge pull request #1106 from Bond-009/warn2

More warning fixes
Vasily 6 年之前
父節點
當前提交
208585d3f6

+ 113 - 96
Emby.Server.Implementations/ApplicationHost.cs

@@ -34,7 +34,6 @@ using Emby.Server.Implementations.IO;
 using Emby.Server.Implementations.Library;
 using Emby.Server.Implementations.LiveTv;
 using Emby.Server.Implementations.Localization;
-using Emby.Server.Implementations.Middleware;
 using Emby.Server.Implementations.Net;
 using Emby.Server.Implementations.Playlists;
 using Emby.Server.Implementations.ScheduledTasks;
@@ -161,7 +160,7 @@ namespace Emby.Server.Implementations
         public event EventHandler<GenericEventArgs<PackageVersionInfo>> ApplicationUpdated;
 
         /// <summary>
-        /// Gets or sets a value indicating whether this instance has changes that require the entire application to restart.
+        /// Gets a value indicating whether this instance has changes that require the entire application to restart.
         /// </summary>
         /// <value><c>true</c> if this instance has pending application restart; otherwise, <c>false</c>.</value>
         public bool HasPendingRestart { get; private set; }
@@ -175,7 +174,7 @@ namespace Emby.Server.Implementations
         protected ILogger Logger { get; set; }
 
         /// <summary>
-        /// Gets or sets the plugins.
+        /// Gets the plugins.
         /// </summary>
         /// <value>The plugins.</value>
         public IPlugin[] Plugins { get; protected set; }
@@ -187,13 +186,13 @@ namespace Emby.Server.Implementations
         public ILoggerFactory LoggerFactory { get; protected set; }
 
         /// <summary>
-        /// Gets the application paths.
+        /// Gets or sets the application paths.
         /// </summary>
         /// <value>The application paths.</value>
         protected ServerApplicationPaths ApplicationPaths { get; set; }
 
         /// <summary>
-        /// Gets all concrete types.
+        /// Gets or sets all concrete types.
         /// </summary>
         /// <value>All concrete types.</value>
         public Type[] AllConcreteTypes { get; protected set; }
@@ -201,7 +200,7 @@ namespace Emby.Server.Implementations
         /// <summary>
         /// The disposable parts
         /// </summary>
-        protected readonly List<IDisposable> DisposableParts = new List<IDisposable>();
+        protected readonly List<IDisposable> _disposableParts = new List<IDisposable>();
 
         /// <summary>
         /// Gets the configuration manager.
@@ -240,27 +239,33 @@ namespace Emby.Server.Implementations
         /// </summary>
         /// <value>The user manager.</value>
         public IUserManager UserManager { get; set; }
+
         /// <summary>
         /// Gets or sets the library manager.
         /// </summary>
         /// <value>The library manager.</value>
         internal ILibraryManager LibraryManager { get; set; }
+
         /// <summary>
         /// Gets or sets the directory watchers.
         /// </summary>
         /// <value>The directory watchers.</value>
         private ILibraryMonitor LibraryMonitor { get; set; }
+
         /// <summary>
         /// Gets or sets the provider manager.
         /// </summary>
         /// <value>The provider manager.</value>
         private IProviderManager ProviderManager { get; set; }
+
         /// <summary>
         /// Gets or sets the HTTP server.
         /// </summary>
         /// <value>The HTTP server.</value>
         private IHttpServer HttpServer { get; set; }
+
         private IDtoService DtoService { get; set; }
+
         public IImageProcessor ImageProcessor { get; set; }
 
         /// <summary>
@@ -268,6 +273,7 @@ namespace Emby.Server.Implementations
         /// </summary>
         /// <value>The media encoder.</value>
         private IMediaEncoder MediaEncoder { get; set; }
+
         private ISubtitleEncoder SubtitleEncoder { get; set; }
 
         private ISessionManager SessionManager { get; set; }
@@ -277,6 +283,7 @@ namespace Emby.Server.Implementations
         public LocalizationManager LocalizationManager { get; set; }
 
         private IEncodingManager EncodingManager { get; set; }
+
         private IChannelManager ChannelManager { get; set; }
 
         /// <summary>
@@ -284,20 +291,29 @@ namespace Emby.Server.Implementations
         /// </summary>
         /// <value>The user data repository.</value>
         private IUserDataManager UserDataManager { get; set; }
+
         private IUserRepository UserRepository { get; set; }
+
         internal SqliteItemRepository ItemRepository { get; set; }
 
         private INotificationManager NotificationManager { get; set; }
+
         private ISubtitleManager SubtitleManager { get; set; }
+
         private IChapterManager ChapterManager { get; set; }
+
         private IDeviceManager DeviceManager { get; set; }
 
         internal IUserViewManager UserViewManager { get; set; }
 
         private IAuthenticationRepository AuthenticationRepository { get; set; }
+
         private ITVSeriesManager TVSeriesManager { get; set; }
+
         private ICollectionManager CollectionManager { get; set; }
+
         private IMediaSourceManager MediaSourceManager { get; set; }
+
         private IPlaylistManager PlaylistManager { get; set; }
 
         private readonly IConfiguration _configuration;
@@ -313,28 +329,37 @@ namespace Emby.Server.Implementations
         /// </summary>
         /// <value>The zip client.</value>
         protected IZipClient ZipClient { get; private set; }
+
         protected IHttpResultFactory HttpResultFactory { get; private set; }
+
         protected IAuthService AuthService { get; private set; }
 
-        public IStartupOptions StartupOptions { get; private set; }
+        public IStartupOptions StartupOptions { get; }
 
         internal IImageEncoder ImageEncoder { get; private set; }
 
         protected IProcessFactory ProcessFactory { get; private set; }
+
         protected ICryptoProvider CryptographyProvider = new CryptographyProvider();
         protected readonly IXmlSerializer XmlSerializer;
 
         protected ISocketFactory SocketFactory { get; private set; }
+
         protected ITaskManager TaskManager { get; private set; }
+
         public IHttpClient HttpClient { get; private set; }
+
         protected INetworkManager NetworkManager { get; set; }
+
         public IJsonSerializer JsonSerializer { get; private set; }
+
         protected IIsoManager IsoManager { get; private set; }
 
         /// <summary>
         /// Initializes a new instance of the <see cref="ApplicationHost" /> class.
         /// </summary>
-        public ApplicationHost(ServerApplicationPaths applicationPaths,
+        public ApplicationHost(
+            ServerApplicationPaths applicationPaths,
             ILoggerFactory loggerFactory,
             IStartupOptions options,
             IFileSystem fileSystem,
@@ -395,7 +420,7 @@ namespace Emby.Server.Implementations
             _validAddressResults.Clear();
         }
 
-        public string ApplicationVersion => typeof(ApplicationHost).Assembly.GetName().Version.ToString(3);
+        public string ApplicationVersion { get; } = typeof(ApplicationHost).Assembly.GetName().Version.ToString(3);
 
         /// <summary>
         /// Gets the current application user agent
@@ -404,13 +429,16 @@ namespace Emby.Server.Implementations
         public string ApplicationUserAgent => Name.Replace(' ','-') + "/" + ApplicationVersion;
 
         private string _productName;
+
         /// <summary>
         /// Gets the current application name
         /// </summary>
         /// <value>The application name.</value>
-        public string ApplicationProductName => _productName ?? (_productName = FileVersionInfo.GetVersionInfo(Assembly.GetEntryAssembly().Location).ProductName);
+        public string ApplicationProductName
+            => _productName ?? (_productName = FileVersionInfo.GetVersionInfo(Assembly.GetEntryAssembly().Location).ProductName);
 
         private DeviceId _deviceId;
+
         public string SystemId
         {
             get
@@ -441,15 +469,15 @@ namespace Emby.Server.Implementations
         /// <summary>
         /// Creates an instance of type and resolves all constructor dependencies
         /// </summary>
-        /// <param name="type">The type.</param>
-        /// <returns>System.Object.</returns>
+        /// /// <typeparam name="T">The type</typeparam>
+        /// <returns>T</returns>
         public T CreateInstance<T>()
             => ActivatorUtilities.CreateInstance<T>(_serviceProvider);
 
         /// <summary>
         /// Creates the instance safe.
         /// </summary>
-        /// <param name="typeInfo">The type information.</param>
+        /// <param name="type">The type.</param>
         /// <returns>System.Object.</returns>
         protected object CreateInstanceSafe(Type type)
         {
@@ -468,14 +496,14 @@ namespace Emby.Server.Implementations
         /// <summary>
         /// Resolves this instance.
         /// </summary>
-        /// <typeparam name="T"></typeparam>
+        /// <typeparam name="T">The type</typeparam>
         /// <returns>``0.</returns>
         public T Resolve<T>() => _serviceProvider.GetService<T>();
 
         /// <summary>
         /// Gets the export types.
         /// </summary>
-        /// <typeparam name="T"></typeparam>
+        /// <typeparam name="T">The type</typeparam>
         /// <returns>IEnumerable{Type}.</returns>
         public IEnumerable<Type> GetExportTypes<T>()
         {
@@ -487,22 +515,22 @@ namespace Emby.Server.Implementations
         /// <summary>
         /// Gets the exports.
         /// </summary>
-        /// <typeparam name="T"></typeparam>
+        /// <typeparam name="T">The type</typeparam>
         /// <param name="manageLifetime">if set to <c>true</c> [manage lifetime].</param>
         /// <returns>IEnumerable{``0}.</returns>
         public IEnumerable<T> GetExports<T>(bool manageLifetime = true)
         {
             var parts = GetExportTypes<T>()
-                .Select(x => CreateInstanceSafe(x))
+                .Select(CreateInstanceSafe)
                 .Where(i => i != null)
                 .Cast<T>()
                 .ToList(); // Convert to list so this isn't executed for each iteration
 
             if (manageLifetime)
             {
-                lock (DisposableParts)
+                lock (_disposableParts)
                 {
-                    DisposableParts.AddRange(parts.OfType<IDisposable>());
+                    _disposableParts.AddRange(parts.OfType<IDisposable>());
                 }
             }
 
@@ -522,29 +550,20 @@ namespace Emby.Server.Implementations
 
             MediaEncoder.SetFFmpegPath();
 
-            //if (string.IsNullOrWhiteSpace(MediaEncoder.EncoderPath))
-            //{
-            //    if (ServerConfigurationManager.Configuration.IsStartupWizardCompleted)
-            //    {
-            //        ServerConfigurationManager.Configuration.IsStartupWizardCompleted = false;
-            //        ServerConfigurationManager.SaveConfiguration();
-            //    }
-            //}
-
             Logger.LogInformation("ServerId: {0}", SystemId);
 
-            var entryPoints = GetExports<IServerEntryPoint>();
+            var entryPoints = GetExports<IServerEntryPoint>().ToList();
 
             var stopWatch = new Stopwatch();
             stopWatch.Start();
-            await Task.WhenAll(StartEntryPoints(entryPoints, true));
+            await Task.WhenAll(StartEntryPoints(entryPoints, true)).ConfigureAwait(false);
             Logger.LogInformation("Executed all pre-startup entry points in {Elapsed:g}", stopWatch.Elapsed);
 
             Logger.LogInformation("Core startup complete");
             HttpServer.GlobalResponse = null;
 
             stopWatch.Restart();
-            await Task.WhenAll(StartEntryPoints(entryPoints, false));
+            await Task.WhenAll(StartEntryPoints(entryPoints, false)).ConfigureAwait(false);
             Logger.LogInformation("Executed all post-startup entry points in {Elapsed:g}", stopWatch.Elapsed);
             stopWatch.Stop();
         }
@@ -594,7 +613,7 @@ namespace Emby.Server.Implementations
 
             SetHttpLimit();
 
-            await RegisterResources(serviceCollection);
+            await RegisterResources(serviceCollection).ConfigureAwait(false);
 
             FindParts();
 
@@ -631,7 +650,7 @@ namespace Emby.Server.Implementations
                 })
                 .Build();
 
-            await host.StartAsync();
+            await host.StartAsync().ConfigureAwait(false);
         }
 
         private async Task ExecuteWebsocketHandlerAsync(HttpContext context, Func<Task> next)
@@ -729,8 +748,8 @@ namespace Emby.Server.Implementations
 
             serviceCollection.AddSingleton(ServerConfigurationManager);
 
-            LocalizationManager = new LocalizationManager(ServerConfigurationManager, FileSystemManager, JsonSerializer, LoggerFactory);
-            await LocalizationManager.LoadAll();
+            LocalizationManager = new LocalizationManager(ServerConfigurationManager, JsonSerializer, LoggerFactory);
+            await LocalizationManager.LoadAll().ConfigureAwait(false);
             serviceCollection.AddSingleton<ILocalizationManager>(LocalizationManager);
 
             serviceCollection.AddSingleton<IBlurayExaminer>(new BdInfoExaminer(FileSystemManager));
@@ -769,16 +788,19 @@ namespace Emby.Server.Implementations
             CertificateInfo = GetCertificateInfo(true);
             Certificate = GetCertificate(CertificateInfo);
 
-            HttpServer = new HttpListenerHost(this,
+            HttpServer = new HttpListenerHost(
+                this,
                 LoggerFactory,
                 ServerConfigurationManager,
                 _configuration,
                 NetworkManager,
                 JsonSerializer,
                 XmlSerializer,
-                CreateHttpListener());
+                CreateHttpListener())
+            {
+                GlobalResponse = LocalizationManager.GetLocalizedString("StartupEmbyServerIsLoading")
+            };
 
-            HttpServer.GlobalResponse = LocalizationManager.GetLocalizedString("StartupEmbyServerIsLoading");
             serviceCollection.AddSingleton(HttpServer);
 
             ImageProcessor = GetImageProcessor();
@@ -933,7 +955,7 @@ namespace Emby.Server.Implementations
                 var password = string.IsNullOrWhiteSpace(info.Password) ? null : info.Password;
 
                 var localCert = new X509Certificate2(certificateLocation, password);
-                //localCert.PrivateKey = PrivateKey.CreateFromFile(pvk_file).RSA;
+                // localCert.PrivateKey = PrivateKey.CreateFromFile(pvk_file).RSA;
                 if (!localCert.HasPrivateKey)
                 {
                     Logger.LogError("No private key included in SSL cert {CertificateLocation}.", certificateLocation);
@@ -1034,13 +1056,15 @@ namespace Emby.Server.Implementations
 
             HttpServer.Init(GetExports<IService>(false), GetExports<IWebSocketListener>(), GetUrlPrefixes());
 
-            LibraryManager.AddParts(GetExports<IResolverIgnoreRule>(),
+            LibraryManager.AddParts(
+                GetExports<IResolverIgnoreRule>(),
                 GetExports<IItemResolver>(),
                 GetExports<IIntroProvider>(),
                 GetExports<IBaseItemComparer>(),
                 GetExports<ILibraryPostScanTask>());
 
-            ProviderManager.AddParts(GetExports<IImageProvider>(),
+            ProviderManager.AddParts(
+                GetExports<IImageProvider>(),
                 GetExports<IMetadataService>(),
                 GetExports<IMetadataProvider>(),
                 GetExports<IMetadataSaver>(),
@@ -1121,6 +1145,7 @@ namespace Emby.Server.Implementations
         }
 
         private CertificateInfo CertificateInfo { get; set; }
+
         protected X509Certificate2 Certificate { get; private set; }
 
         private IEnumerable<string> GetUrlPrefixes()
@@ -1131,7 +1156,7 @@ namespace Emby.Server.Implementations
             {
                 var prefixes = new List<string>
                 {
-                    "http://"+i+":" + HttpPort + "/"
+                    "http://" + i + ":" + HttpPort + "/"
                 };
 
                 if (CertificateInfo != null)
@@ -1160,30 +1185,12 @@ namespace Emby.Server.Implementations
             // Generate self-signed cert
             var certHost = GetHostnameFromExternalDns(ServerConfigurationManager.Configuration.WanDdns);
             var certPath = Path.Combine(ServerConfigurationManager.ApplicationPaths.ProgramDataPath, "ssl", "cert_" + (certHost + "2").GetMD5().ToString("N") + ".pfx");
-            var password = "embycert";
-
-            //if (generateCertificate)
-            //{
-            //    if (!File.Exists(certPath))
-            //    {
-            //        FileSystemManager.CreateDirectory(FileSystemManager.GetDirectoryName(certPath));
-
-            //        try
-            //        {
-            //            CertificateGenerator.CreateSelfSignCertificatePfx(certPath, certHost, password, Logger);
-            //        }
-            //        catch (Exception ex)
-            //        {
-            //            Logger.LogError(ex, "Error creating ssl cert");
-            //            return null;
-            //        }
-            //    }
-            //}
+            const string Password = "embycert";
 
             return new CertificateInfo
             {
                 Path = certPath,
-                Password = password
+                Password = Password
             };
         }
 
@@ -1218,9 +1225,9 @@ namespace Emby.Server.Implementations
                 requiresRestart = true;
             }
 
-            var currentCertPath = CertificateInfo == null ? null : CertificateInfo.Path;
+            var currentCertPath = CertificateInfo?.Path;
             var newCertInfo = GetCertificateInfo(false);
-            var newCertPath = newCertInfo == null ? null : newCertInfo.Path;
+            var newCertPath = newCertInfo?.Path;
 
             if (!string.Equals(currentCertPath, newCertPath, StringComparison.OrdinalIgnoreCase))
             {
@@ -1353,6 +1360,7 @@ namespace Emby.Server.Implementations
         /// <summary>
         /// Gets the system status.
         /// </summary>
+        /// <param name="cancellationToken">The cancellation token</param>
         /// <returns>SystemInfo.</returns>
         public async Task<SystemInfo> GetSystemInfo(CancellationToken cancellationToken)
         {
@@ -1447,19 +1455,19 @@ namespace Emby.Server.Implementations
 
         public async Task<string> GetWanApiUrl(CancellationToken cancellationToken)
         {
-            const string url = "http://ipv4.icanhazip.com";
+            const string Url = "http://ipv4.icanhazip.com";
             try
             {
                 using (var response = await HttpClient.Get(new HttpRequestOptions
                 {
-                    Url = url,
+                    Url = Url,
                     LogErrorResponseBody = false,
                     LogErrors = false,
                     LogRequest = false,
                     TimeoutMs = 10000,
                     BufferContent = false,
                     CancellationToken = cancellationToken
-                }))
+                }).ConfigureAwait(false))
                 {
                     return GetLocalApiUrl(response.ReadToEnd().Trim());
                 }
@@ -1547,10 +1555,12 @@ namespace Emby.Server.Implementations
             {
                 return result;
             }
+
             return null;
         }
 
         private readonly ConcurrentDictionary<string, bool> _validAddressResults = new ConcurrentDictionary<string, bool>(StringComparer.OrdinalIgnoreCase);
+
         private async Task<bool> IsIpAddressValidAsync(IpAddressInfo address, CancellationToken cancellationToken)
         {
             if (address.Equals(IpAddressInfo.Loopback) ||
@@ -1567,26 +1577,26 @@ namespace Emby.Server.Implementations
                 return cachedResult;
             }
 
-            var logPing = false;
-
 #if DEBUG
-            logPing = true;
+            const bool LogPing = true;
+#else
+            const bool LogPing = false;
 #endif
 
             try
             {
-                using (var response = await HttpClient.SendAsync(new HttpRequestOptions
-                {
-                    Url = apiUrl,
-                    LogErrorResponseBody = false,
-                    LogErrors = logPing,
-                    LogRequest = logPing,
-                    TimeoutMs = 5000,
-                    BufferContent = false,
-
-                    CancellationToken = cancellationToken
-
-                }, "POST").ConfigureAwait(false))
+                using (var response = await HttpClient.SendAsync(
+                    new HttpRequestOptions
+                    {
+                        Url = apiUrl,
+                        LogErrorResponseBody = false,
+                        LogErrors = LogPing,
+                        LogRequest = LogPing,
+                        TimeoutMs = 5000,
+                        BufferContent = false,
+
+                        CancellationToken = cancellationToken
+                    }, "POST").ConfigureAwait(false))
                 {
                     using (var reader = new StreamReader(response.Content))
                     {
@@ -1651,6 +1661,7 @@ namespace Emby.Server.Implementations
         public event EventHandler HasUpdateAvailableChanged;
 
         private bool _hasUpdateAvailable;
+
         public bool HasUpdateAvailable
         {
             get => _hasUpdateAvailable;
@@ -1711,7 +1722,7 @@ namespace Emby.Server.Implementations
             var process = ProcessFactory.Create(new ProcessOptions
             {
                 FileName = url,
-                //EnableRaisingEvents = true,
+                EnableRaisingEvents = true,
                 UseShellExecute = true,
                 ErrorDialog = false
             });
@@ -1746,26 +1757,25 @@ namespace Emby.Server.Implementations
         {
             Logger.LogInformation("Application has been updated to version {0}", package.versionStr);
 
-            ApplicationUpdated?.Invoke(this, new GenericEventArgs<PackageVersionInfo>
-            {
-                Argument = package
-            });
+            ApplicationUpdated?.Invoke(
+                this,
+                new GenericEventArgs<PackageVersionInfo>()
+                {
+                    Argument = package
+                });
 
             NotifyPendingRestart();
         }
 
-        private bool _disposed;
+        private bool _disposed = false;
+
         /// <summary>
         /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
         /// </summary>
         public void Dispose()
         {
-            if (!_disposed)
-            {
-                _disposed = true;
-
-                Dispose(true);
-            }
+            Dispose(true);
+            GC.SuppressFinalize(this);
         }
 
         /// <summary>
@@ -1774,14 +1784,19 @@ namespace Emby.Server.Implementations
         /// <param name="dispose"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
         protected virtual void Dispose(bool dispose)
         {
+            if (_disposed)
+            {
+                return;
+            }
+
             if (dispose)
             {
                 var type = GetType();
 
                 Logger.LogInformation("Disposing {Type}", type.Name);
 
-                var parts = DisposableParts.Distinct().Where(i => i.GetType() != type).ToList();
-                DisposableParts.Clear();
+                var parts = _disposableParts.Distinct().Where(i => i.GetType() != type).ToList();
+                _disposableParts.Clear();
 
                 foreach (var part in parts)
                 {
@@ -1797,6 +1812,8 @@ namespace Emby.Server.Implementations
                     }
                 }
             }
+
+            _disposed = true;
         }
     }
 

+ 43 - 29
Emby.Server.Implementations/Diagnostics/CommonProcess.cs

@@ -9,14 +9,14 @@ namespace Emby.Server.Implementations.Diagnostics
 {
     public class CommonProcess : IProcess
     {
-        public event EventHandler Exited;
-
-        private readonly ProcessOptions _options;
         private readonly Process _process;
 
+        private bool _disposed = false;
+        private bool _hasExited;
+
         public CommonProcess(ProcessOptions options)
         {
-            _options = options;
+            StartInfo = options;
 
             var startInfo = new ProcessStartInfo
             {
@@ -27,10 +27,10 @@ namespace Emby.Server.Implementations.Diagnostics
                 CreateNoWindow = options.CreateNoWindow,
                 RedirectStandardError = options.RedirectStandardError,
                 RedirectStandardInput = options.RedirectStandardInput,
-                RedirectStandardOutput = options.RedirectStandardOutput
+                RedirectStandardOutput = options.RedirectStandardOutput,
+                ErrorDialog = options.ErrorDialog
             };
 
-            startInfo.ErrorDialog = options.ErrorDialog;
 
             if (options.IsHidden)
             {
@@ -45,11 +45,22 @@ namespace Emby.Server.Implementations.Diagnostics
             if (options.EnableRaisingEvents)
             {
                 _process.EnableRaisingEvents = true;
-                _process.Exited += _process_Exited;
+                _process.Exited += OnProcessExited;
             }
         }
 
-        private bool _hasExited;
+        public event EventHandler Exited;
+
+        public ProcessOptions StartInfo { get; }
+
+        public StreamWriter StandardInput => _process.StandardInput;
+
+        public StreamReader StandardError => _process.StandardError;
+
+        public StreamReader StandardOutput => _process.StandardOutput;
+
+        public int ExitCode => _process.ExitCode;
+
         private bool HasExited
         {
             get
@@ -72,25 +83,6 @@ namespace Emby.Server.Implementations.Diagnostics
             }
         }
 
-        private void _process_Exited(object sender, EventArgs e)
-        {
-            _hasExited = true;
-            if (Exited != null)
-            {
-                Exited(this, e);
-            }
-        }
-
-        public ProcessOptions StartInfo => _options;
-
-        public StreamWriter StandardInput => _process.StandardInput;
-
-        public StreamReader StandardError => _process.StandardError;
-
-        public StreamReader StandardOutput => _process.StandardOutput;
-
-        public int ExitCode => _process.ExitCode;
-
         public void Start()
         {
             _process.Start();
@@ -108,7 +100,7 @@ namespace Emby.Server.Implementations.Diagnostics
 
         public Task<bool> WaitForExitAsync(int timeMs)
         {
-            //Note: For this function to work correctly, the option EnableRisingEvents needs to be set to true.
+            // Note: For this function to work correctly, the option EnableRisingEvents needs to be set to true.
 
             if (HasExited)
             {
@@ -130,7 +122,29 @@ namespace Emby.Server.Implementations.Diagnostics
 
         public void Dispose()
         {
-            _process?.Dispose();
+            Dispose(true);
+            GC.SuppressFinalize(this);
+        }
+
+        protected virtual void Dispose(bool disposing)
+        {
+            if (_disposed)
+            {
+                return;
+            }
+
+            if (disposing)
+            {
+                _process?.Dispose();
+            }
+
+            _disposed = true;
+        }
+
+        private void OnProcessExited(object sender, EventArgs e)
+        {
+            _hasExited = true;
+            Exited?.Invoke(this, e);
         }
     }
 }

+ 165 - 154
Emby.Server.Implementations/Library/LibraryManager.cs

@@ -58,22 +58,23 @@ namespace Emby.Server.Implementations.Library
         private ILibraryPostScanTask[] PostscanTasks { get; set; }
 
         /// <summary>
-        /// Gets the intro providers.
+        /// Gets or sets the intro providers.
         /// </summary>
         /// <value>The intro providers.</value>
         private IIntroProvider[] IntroProviders { get; set; }
 
         /// <summary>
-        /// Gets the list of entity resolution ignore rules
+        /// Gets or sets the list of entity resolution ignore rules
         /// </summary>
         /// <value>The entity resolution ignore rules.</value>
         private IResolverIgnoreRule[] EntityResolutionIgnoreRules { get; set; }
 
         /// <summary>
-        /// Gets the list of currently registered entity resolvers
+        /// Gets or sets the list of currently registered entity resolvers
         /// </summary>
         /// <value>The entity resolvers enumerable.</value>
         private IItemResolver[] EntityResolvers { get; set; }
+
         private IMultiItemResolver[] MultiItemResolvers { get; set; }
 
         /// <summary>
@@ -83,7 +84,7 @@ namespace Emby.Server.Implementations.Library
         private IBaseItemComparer[] Comparers { get; set; }
 
         /// <summary>
-        /// Gets the active item repository
+        /// Gets or sets the active item repository
         /// </summary>
         /// <value>The item repository.</value>
         public IItemRepository ItemRepository { get; set; }
@@ -133,12 +134,14 @@ namespace Emby.Server.Implementations.Library
         private readonly Func<IProviderManager> _providerManagerFactory;
         private readonly Func<IUserViewManager> _userviewManager;
         public bool IsScanRunning { get; private set; }
+
         private IServerApplicationHost _appHost;
 
         /// <summary>
         /// The _library items cache
         /// </summary>
         private readonly ConcurrentDictionary<Guid, BaseItem> _libraryItemsCache;
+
         /// <summary>
         /// Gets the library items cache.
         /// </summary>
@@ -150,7 +153,8 @@ namespace Emby.Server.Implementations.Library
         /// <summary>
         /// Initializes a new instance of the <see cref="LibraryManager" /> class.
         /// </summary>
-        /// <param name="logger">The logger.</param>
+        /// <param name="appHost">The application host</param>
+        /// <param name="loggerFactory">The logger factory.</param>
         /// <param name="taskManager">The task manager.</param>
         /// <param name="userManager">The user manager.</param>
         /// <param name="configurationManager">The configuration manager.</param>
@@ -167,6 +171,7 @@ namespace Emby.Server.Implementations.Library
             Func<IProviderManager> providerManagerFactory,
             Func<IUserViewManager> userviewManager)
         {
+            _appHost = appHost;
             _logger = loggerFactory.CreateLogger(nameof(LibraryManager));
             _taskManager = taskManager;
             _userManager = userManager;
@@ -176,7 +181,7 @@ namespace Emby.Server.Implementations.Library
             _fileSystem = fileSystem;
             _providerManagerFactory = providerManagerFactory;
             _userviewManager = userviewManager;
-            _appHost = appHost;
+
             _libraryItemsCache = new ConcurrentDictionary<Guid, BaseItem>();
 
             ConfigurationManager.ConfigurationUpdated += ConfigurationUpdated;
@@ -191,8 +196,9 @@ namespace Emby.Server.Implementations.Library
         /// <param name="resolvers">The resolvers.</param>
         /// <param name="introProviders">The intro providers.</param>
         /// <param name="itemComparers">The item comparers.</param>
-        /// <param name="postscanTasks">The postscan tasks.</param>
-        public void AddParts(IEnumerable<IResolverIgnoreRule> rules,
+        /// <param name="postscanTasks">The post scan tasks.</param>
+        public void AddParts(
+            IEnumerable<IResolverIgnoreRule> rules,
             IEnumerable<IItemResolver> resolvers,
             IEnumerable<IIntroProvider> introProviders,
             IEnumerable<IBaseItemComparer> itemComparers,
@@ -203,24 +209,19 @@ namespace Emby.Server.Implementations.Library
             MultiItemResolvers = EntityResolvers.OfType<IMultiItemResolver>().ToArray();
             IntroProviders = introProviders.ToArray();
             Comparers = itemComparers.ToArray();
-
-            PostscanTasks = postscanTasks.OrderBy(i =>
-            {
-                var hasOrder = i as IHasOrder;
-
-                return hasOrder == null ? 0 : hasOrder.Order;
-
-            }).ToArray();
+            PostscanTasks = postscanTasks.ToArray();
         }
 
         /// <summary>
         /// The _root folder
         /// </summary>
         private volatile AggregateFolder _rootFolder;
+
         /// <summary>
         /// The _root folder sync lock
         /// </summary>
         private readonly object _rootFolderSyncLock = new object();
+
         /// <summary>
         /// Gets the root folder.
         /// </summary>
@@ -239,11 +240,13 @@ namespace Emby.Server.Implementations.Library
                         }
                     }
                 }
+
                 return _rootFolder;
             }
         }
 
         private bool _wizardCompleted;
+
         /// <summary>
         /// Records the configuration values.
         /// </summary>
@@ -258,7 +261,7 @@ namespace Emby.Server.Implementations.Library
         /// </summary>
         /// <param name="sender">The sender.</param>
         /// <param name="e">The <see cref="EventArgs" /> instance containing the event data.</param>
-        void ConfigurationUpdated(object sender, EventArgs e)
+        private void ConfigurationUpdated(object sender, EventArgs e)
         {
             var config = ConfigurationManager.Configuration;
 
@@ -335,12 +338,14 @@ namespace Emby.Server.Implementations.Library
                         // channel no longer installed
                     }
                 }
+
                 options.DeleteFileLocation = false;
             }
 
             if (item is LiveTvProgram)
             {
-                _logger.LogDebug("Deleting item, Type: {0}, Name: {1}, Path: {2}, Id: {3}",
+                _logger.LogDebug(
+                    "Deleting item, Type: {0}, Name: {1}, Path: {2}, Id: {3}",
                     item.GetType().Name,
                     item.Name ?? "Unknown name",
                     item.Path ?? string.Empty,
@@ -348,7 +353,8 @@ namespace Emby.Server.Implementations.Library
             }
             else
             {
-                _logger.LogInformation("Deleting item, Type: {0}, Name: {1}, Path: {2}, Id: {3}",
+                _logger.LogInformation(
+                    "Deleting item, Type: {0}, Name: {1}, Path: {2}, Id: {3}",
                     item.GetType().Name,
                     item.Name ?? "Unknown name",
                     item.Path ?? string.Empty,
@@ -488,12 +494,13 @@ namespace Emby.Server.Implementations.Library
             {
                 throw new ArgumentNullException(nameof(key));
             }
+
             if (type == null)
             {
                 throw new ArgumentNullException(nameof(type));
             }
 
-            if (key.StartsWith(ConfigurationManager.ApplicationPaths.ProgramDataPath))
+            if (key.StartsWith(ConfigurationManager.ApplicationPaths.ProgramDataPath, StringComparison.Ordinal))
             {
                 // Try to normalize paths located underneath program-data in an attempt to make them more portable
                 key = key.Substring(ConfigurationManager.ApplicationPaths.ProgramDataPath.Length)
@@ -511,13 +518,11 @@ namespace Emby.Server.Implementations.Library
             return key.GetMD5();
         }
 
-        public BaseItem ResolvePath(FileSystemMetadata fileInfo,
-            Folder parent = null)
-        {
-            return ResolvePath(fileInfo, new DirectoryService(_logger, _fileSystem), null, parent);
-        }
+        public BaseItem ResolvePath(FileSystemMetadata fileInfo, Folder parent = null)
+            => ResolvePath(fileInfo, new DirectoryService(_logger, _fileSystem), null, parent);
 
-        private BaseItem ResolvePath(FileSystemMetadata fileInfo,
+        private BaseItem ResolvePath(
+            FileSystemMetadata fileInfo,
             IDirectoryService directoryService,
             IItemResolver[] resolvers,
             Folder parent = null,
@@ -572,7 +577,7 @@ namespace Emby.Server.Implementations.Library
                     {
                         _logger.LogError(ex, "Error in GetFilteredFileSystemEntries isPhysicalRoot: {0} IsVf: {1}", isPhysicalRoot, isVf);
 
-                        files = new FileSystemMetadata[] { };
+                        files = Array.Empty<FileSystemMetadata>();
                     }
                     else
                     {
@@ -600,13 +605,7 @@ namespace Emby.Server.Implementations.Library
         }
 
         public bool IgnoreFile(FileSystemMetadata file, BaseItem parent)
-        {
-            if (EntityResolutionIgnoreRules.Any(r => r.ShouldIgnore(file, parent)))
-            {
-                return true;
-            }
-            return false;
-        }
+            => EntityResolutionIgnoreRules.Any(r => r.ShouldIgnore(file, parent));
 
         public List<FileSystemMetadata> NormalizeRootPathList(IEnumerable<FileSystemMetadata> paths)
         {
@@ -646,7 +645,8 @@ namespace Emby.Server.Implementations.Library
             return ResolvePaths(files, directoryService, parent, libraryOptions, collectionType, EntityResolvers);
         }
 
-        public IEnumerable<BaseItem> ResolvePaths(IEnumerable<FileSystemMetadata> files,
+        public IEnumerable<BaseItem> ResolvePaths(
+            IEnumerable<FileSystemMetadata> files,
             IDirectoryService directoryService,
             Folder parent,
             LibraryOptions libraryOptions,
@@ -672,6 +672,7 @@ namespace Emby.Server.Implementations.Library
                         {
                             ResolverHelper.SetInitialItemValues(item, parent, _fileSystem, this, directoryService);
                         }
+
                         items.AddRange(ResolveFileList(result.ExtraFiles, directoryService, parent, collectionType, resolvers, libraryOptions));
                         return items;
                     }
@@ -681,7 +682,8 @@ namespace Emby.Server.Implementations.Library
             return ResolveFileList(fileList, directoryService, parent, collectionType, resolvers, libraryOptions);
         }
 
-        private IEnumerable<BaseItem> ResolveFileList(IEnumerable<FileSystemMetadata> fileList,
+        private IEnumerable<BaseItem> ResolveFileList(
+            IEnumerable<FileSystemMetadata> fileList,
             IDirectoryService directoryService,
             Folder parent,
             string collectionType,
@@ -766,6 +768,7 @@ namespace Emby.Server.Implementations.Library
 
         private volatile UserRootFolder _userRootFolder;
         private readonly object _syncLock = new object();
+
         public Folder GetUserRootFolder()
         {
             if (_userRootFolder == null)
@@ -810,8 +813,6 @@ namespace Emby.Server.Implementations.Library
                 throw new ArgumentNullException(nameof(path));
             }
 
-            //_logger.LogInformation("FindByPath {0}", path);
-
             var query = new InternalItemsQuery
             {
                 Path = path,
@@ -885,7 +886,6 @@ namespace Emby.Server.Implementations.Library
         /// </summary>
         /// <param name="value">The value.</param>
         /// <returns>Task{Year}.</returns>
-        /// <exception cref="ArgumentOutOfRangeException"></exception>
         public Year GetYear(int value)
         {
             if (value <= 0)
@@ -1027,20 +1027,25 @@ namespace Emby.Server.Implementations.Library
 
         private async Task ValidateTopLibraryFolders(CancellationToken cancellationToken)
         {
-            var rootChildren = RootFolder.Children.ToList();
-            rootChildren = GetUserRootFolder().Children.ToList();
-
             await RootFolder.RefreshMetadata(cancellationToken).ConfigureAwait(false);
 
             // Start by just validating the children of the root, but go no further
-            await RootFolder.ValidateChildren(new SimpleProgress<double>(), cancellationToken, new MetadataRefreshOptions(new DirectoryService(_logger, _fileSystem)), recursive: false);
+            await RootFolder.ValidateChildren(
+                new SimpleProgress<double>(),
+                cancellationToken,
+                new MetadataRefreshOptions(new DirectoryService(_logger, _fileSystem)),
+                recursive: false).ConfigureAwait(false);
 
             await GetUserRootFolder().RefreshMetadata(cancellationToken).ConfigureAwait(false);
 
-            await GetUserRootFolder().ValidateChildren(new SimpleProgress<double>(), cancellationToken, new MetadataRefreshOptions(new DirectoryService(_logger, _fileSystem)), recursive: false).ConfigureAwait(false);
+            await GetUserRootFolder().ValidateChildren(
+                new SimpleProgress<double>(),
+                cancellationToken,
+                new MetadataRefreshOptions(new DirectoryService(_logger, _fileSystem)),
+                recursive: false).ConfigureAwait(false);
 
             // Quickly scan CollectionFolders for changes
-            foreach (var folder in GetUserRootFolder().Children.OfType<Folder>().ToList())
+            foreach (var folder in GetUserRootFolder().Children.OfType<Folder>())
             {
                 await folder.RefreshMetadata(cancellationToken).ConfigureAwait(false);
             }
@@ -1204,7 +1209,7 @@ namespace Emby.Server.Implementations.Library
         private string GetCollectionType(string path)
         {
             return _fileSystem.GetFilePaths(path, new[] { ".collection" }, true, false)
-                .Select(i => Path.GetFileNameWithoutExtension(i))
+                .Select(Path.GetFileNameWithoutExtension)
                 .FirstOrDefault(i => !string.IsNullOrEmpty(i));
         }
 
@@ -1218,7 +1223,7 @@ namespace Emby.Server.Implementations.Library
         {
             if (id == Guid.Empty)
             {
-                throw new ArgumentException(nameof(id), "Guid can't be empty");
+                throw new ArgumentException("Guid can't be empty", nameof(id));
             }
 
             if (LibraryItemsCache.TryGetValue(id, out BaseItem item))
@@ -1386,17 +1391,7 @@ namespace Emby.Server.Implementations.Library
 
             var parents = query.AncestorIds.Select(i => GetItemById(i)).ToList();
 
-            if (parents.All(i =>
-            {
-                if (i is ICollectionFolder || i is UserView)
-                {
-                    return true;
-                }
-
-                //_logger.LogDebug("Query requires ancestor query due to type: " + i.GetType().Name);
-                return false;
-
-            }))
+            if (parents.All(i => i is ICollectionFolder || i is UserView))
             {
                 // Optimize by querying against top level views
                 query.TopParentIds = parents.SelectMany(i => GetTopParentIdsForQuery(i, query.User)).ToArray();
@@ -1452,17 +1447,7 @@ namespace Emby.Server.Implementations.Library
 
         private void SetTopParentIdsOrAncestors(InternalItemsQuery query, List<BaseItem> parents)
         {
-            if (parents.All(i =>
-            {
-                if (i is ICollectionFolder || i is UserView)
-                {
-                    return true;
-                }
-
-                //_logger.LogDebug("Query requires ancestor query due to type: " + i.GetType().Name);
-                return false;
-
-            }))
+            if (parents.All(i => i is ICollectionFolder || i is UserView))
             {
                 // Optimize by querying against top level views
                 query.TopParentIds = parents.SelectMany(i => GetTopParentIdsForQuery(i, query.User)).ToArray();
@@ -1511,11 +1496,9 @@ namespace Emby.Server.Implementations.Library
 
         private IEnumerable<Guid> GetTopParentIdsForQuery(BaseItem item, User user)
         {
-            var view = item as UserView;
-
-            if (view != null)
+            if (item is UserView view)
             {
-                if (string.Equals(view.ViewType, CollectionType.LiveTv))
+                if (string.Equals(view.ViewType, CollectionType.LiveTv, StringComparison.Ordinal))
                 {
                     return new[] { view.Id };
                 }
@@ -1528,8 +1511,10 @@ namespace Emby.Server.Implementations.Library
                     {
                         return GetTopParentIdsForQuery(displayParent, user);
                     }
+
                     return Array.Empty<Guid>();
                 }
+
                 if (!view.ParentId.Equals(Guid.Empty))
                 {
                     var displayParent = GetItemById(view.ParentId);
@@ -1537,6 +1522,7 @@ namespace Emby.Server.Implementations.Library
                     {
                         return GetTopParentIdsForQuery(displayParent, user);
                     }
+
                     return Array.Empty<Guid>();
                 }
 
@@ -1550,11 +1536,11 @@ namespace Emby.Server.Implementations.Library
                         .Where(i => user.IsFolderGrouped(i.Id))
                         .SelectMany(i => GetTopParentIdsForQuery(i, user));
                 }
+
                 return Array.Empty<Guid>();
             }
 
-            var collectionFolder = item as CollectionFolder;
-            if (collectionFolder != null)
+            if (item is CollectionFolder collectionFolder)
             {
                 return collectionFolder.PhysicalFolderIds;
             }
@@ -1564,6 +1550,7 @@ namespace Emby.Server.Implementations.Library
             {
                 return new[] { topParent.Id };
             }
+
             return Array.Empty<Guid>();
         }
 
@@ -1760,19 +1747,16 @@ namespace Emby.Server.Implementations.Library
         {
             var comparer = Comparers.FirstOrDefault(c => string.Equals(name, c.Name, StringComparison.OrdinalIgnoreCase));
 
-            if (comparer != null)
+            // If it requires a user, create a new one, and assign the user
+            if (comparer is IUserBaseItemComparer)
             {
-                // If it requires a user, create a new one, and assign the user
-                if (comparer is IUserBaseItemComparer)
-                {
-                    var userComparer = (IUserBaseItemComparer)Activator.CreateInstance(comparer.GetType());
+                var userComparer = (IUserBaseItemComparer)Activator.CreateInstance(comparer.GetType());
 
-                    userComparer.User = user;
-                    userComparer.UserManager = _userManager;
-                    userComparer.UserDataRepository = _userDataRepository;
+                userComparer.User = user;
+                userComparer.UserManager = _userManager;
+                userComparer.UserDataRepository = _userDataRepository;
 
-                    return userComparer;
-                }
+                return userComparer;
             }
 
             return comparer;
@@ -1783,7 +1767,6 @@ namespace Emby.Server.Implementations.Library
         /// </summary>
         /// <param name="item">The item.</param>
         /// <param name="parent">The parent item.</param>
-        /// <returns>Task.</returns>
         public void CreateItem(BaseItem item, BaseItem parent)
         {
             CreateItems(new[] { item }, parent, CancellationToken.None);
@@ -1793,20 +1776,23 @@ namespace Emby.Server.Implementations.Library
         /// Creates the items.
         /// </summary>
         /// <param name="items">The items.</param>
+        /// <param name="parent">The parent item</param>
         /// <param name="cancellationToken">The cancellation token.</param>
-        /// <returns>Task.</returns>
         public void CreateItems(IEnumerable<BaseItem> items, BaseItem parent, CancellationToken cancellationToken)
         {
-            ItemRepository.SaveItems(items, cancellationToken);
+            // Don't iterate multiple times
+            var itemsList = items.ToList();
+
+            ItemRepository.SaveItems(itemsList, cancellationToken);
 
-            foreach (var item in items)
+            foreach (var item in itemsList)
             {
                 RegisterItem(item);
             }
 
             if (ItemAdded != null)
             {
-                foreach (var item in items)
+                foreach (var item in itemsList)
                 {
                     // With the live tv guide this just creates too much noise
                     if (item.SourceType != SourceType.Library)
@@ -1816,11 +1802,13 @@ namespace Emby.Server.Implementations.Library
 
                     try
                     {
-                        ItemAdded(this, new ItemChangeEventArgs
-                        {
-                            Item = item,
-                            Parent = parent ?? item.GetParent()
-                        });
+                        ItemAdded(
+                            this,
+                            new ItemChangeEventArgs
+                            {
+                                Item = item,
+                                Parent = parent ?? item.GetParent()
+                            });
                     }
                     catch (Exception ex)
                     {
@@ -1842,7 +1830,10 @@ namespace Emby.Server.Implementations.Library
         /// </summary>
         public void UpdateItems(IEnumerable<BaseItem> items, BaseItem parent, ItemUpdateType updateReason, CancellationToken cancellationToken)
         {
-            foreach (var item in items)
+            // Don't iterate multiple times
+            var itemsList = items.ToList();
+
+            foreach (var item in itemsList)
             {
                 if (item.IsFileProtocol)
                 {
@@ -1854,14 +1845,11 @@ namespace Emby.Server.Implementations.Library
                 RegisterItem(item);
             }
 
-            //var logName = item.LocationType == LocationType.Remote ? item.Name ?? item.Path : item.Path ?? item.Name;
-            //_logger.LogDebug("Saving {0} to database.", logName);
-
-            ItemRepository.SaveItems(items, cancellationToken);
+            ItemRepository.SaveItems(itemsList, cancellationToken);
 
             if (ItemUpdated != null)
             {
-                foreach (var item in items)
+                foreach (var item in itemsList)
                 {
                     // With the live tv guide this just creates too much noise
                     if (item.SourceType != SourceType.Library)
@@ -1871,12 +1859,14 @@ namespace Emby.Server.Implementations.Library
 
                     try
                     {
-                        ItemUpdated(this, new ItemChangeEventArgs
-                        {
-                            Item = item,
-                            Parent = parent,
-                            UpdateReason = updateReason
-                        });
+                        ItemUpdated(
+                            this,
+                            new ItemChangeEventArgs
+                            {
+                                Item = item,
+                                Parent = parent,
+                                UpdateReason = updateReason
+                            });
                     }
                     catch (Exception ex)
                     {
@@ -1890,9 +1880,9 @@ namespace Emby.Server.Implementations.Library
         /// Updates the item.
         /// </summary>
         /// <param name="item">The item.</param>
+        /// <param name="parent">The parent item.</param>
         /// <param name="updateReason">The update reason.</param>
         /// <param name="cancellationToken">The cancellation token.</param>
-        /// <returns>Task.</returns>
         public void UpdateItem(BaseItem item, BaseItem parent, ItemUpdateType updateReason, CancellationToken cancellationToken)
         {
             UpdateItems(new [] { item }, parent, updateReason, cancellationToken);
@@ -1902,17 +1892,20 @@ namespace Emby.Server.Implementations.Library
         /// Reports the item removed.
         /// </summary>
         /// <param name="item">The item.</param>
+        /// <param name="parent">The parent item.</param>
         public void ReportItemRemoved(BaseItem item, BaseItem parent)
         {
             if (ItemRemoved != null)
             {
                 try
                 {
-                    ItemRemoved(this, new ItemChangeEventArgs
-                    {
-                        Item = item,
-                        Parent = parent
-                    });
+                    ItemRemoved(
+                        this,
+                        new ItemChangeEventArgs
+                        {
+                            Item = item,
+                            Parent = parent
+                        });
                 }
                 catch (Exception ex)
                 {
@@ -2038,8 +2031,7 @@ namespace Emby.Server.Implementations.Library
 
         public string GetConfiguredContentType(BaseItem item, bool inheritConfiguredPath)
         {
-            var collectionFolder = item as ICollectionFolder;
-            if (collectionFolder != null)
+            if (item is ICollectionFolder collectionFolder)
             {
                 return collectionFolder.CollectionType;
             }
@@ -2049,13 +2041,11 @@ namespace Emby.Server.Implementations.Library
 
         private string GetContentTypeOverride(string path, bool inherit)
         {
-            var nameValuePair = ConfigurationManager.Configuration.ContentTypes.FirstOrDefault(i => _fileSystem.AreEqual(i.Name, path) || (inherit && !string.IsNullOrEmpty(i.Name) && _fileSystem.ContainsSubPath(i.Name, path)));
-            if (nameValuePair != null)
-            {
-                return nameValuePair.Value;
-            }
-
-            return null;
+            var nameValuePair = ConfigurationManager.Configuration.ContentTypes
+                                    .FirstOrDefault(i => _fileSystem.AreEqual(i.Name, path)
+                                                         || (inherit && !string.IsNullOrEmpty(i.Name)
+                                                                     && _fileSystem.ContainsSubPath(i.Name, path)));
+            return nameValuePair?.Value;
         }
 
         private string GetTopFolderContentType(BaseItem item)
@@ -2072,6 +2062,7 @@ namespace Emby.Server.Implementations.Library
                 {
                     break;
                 }
+
                 item = parent;
             }
 
@@ -2083,9 +2074,9 @@ namespace Emby.Server.Implementations.Library
         }
 
         private readonly TimeSpan _viewRefreshInterval = TimeSpan.FromHours(24);
-        //private readonly TimeSpan _viewRefreshInterval = TimeSpan.FromMinutes(1);
 
-        public UserView GetNamedView(User user,
+        public UserView GetNamedView(
+            User user,
             string name,
             string viewType,
             string sortName)
@@ -2093,13 +2084,15 @@ namespace Emby.Server.Implementations.Library
             return GetNamedView(user, name, Guid.Empty, viewType, sortName);
         }
 
-        public UserView GetNamedView(string name,
+        public UserView GetNamedView(
+            string name,
             string viewType,
             string sortName)
         {
-            var path = Path.Combine(ConfigurationManager.ApplicationPaths.InternalMetadataPath,
-                                    "views",
-                                    _fileSystem.GetValidFilename(viewType));
+            var path = Path.Combine(
+                ConfigurationManager.ApplicationPaths.InternalMetadataPath,
+                "views",
+                _fileSystem.GetValidFilename(viewType));
 
             var id = GetNewItemId(path + "_namedview_" + name, typeof(UserView));
 
@@ -2135,7 +2128,8 @@ namespace Emby.Server.Implementations.Library
             return item;
         }
 
-        public UserView GetNamedView(User user,
+        public UserView GetNamedView(
+            User user,
             string name,
             Guid parentId,
             string viewType,
@@ -2164,10 +2158,10 @@ namespace Emby.Server.Implementations.Library
                     Name = name,
                     ViewType = viewType,
                     ForcedSortName = sortName,
-                    UserId = user.Id
+                    UserId = user.Id,
+                    DisplayParentId = parentId
                 };
 
-                item.DisplayParentId = parentId;
 
                 CreateItem(item, null);
 
@@ -2184,20 +2178,24 @@ namespace Emby.Server.Implementations.Library
 
             if (refresh)
             {
-                _providerManagerFactory().QueueRefresh(item.Id, new MetadataRefreshOptions(new DirectoryService(_logger, _fileSystem))
-                {
-                    // Need to force save to increment DateLastSaved
-                    ForceSave = true
+                _providerManagerFactory().QueueRefresh(
+                    item.Id,
+                    new MetadataRefreshOptions(new DirectoryService(_logger, _fileSystem))
+                    {
+                        // Need to force save to increment DateLastSaved
+                        ForceSave = true
 
-                }, RefreshPriority.Normal);
+                    },
+                    RefreshPriority.Normal);
             }
 
             return item;
         }
 
-        public UserView GetShadowView(BaseItem parent,
-        string viewType,
-        string sortName)
+        public UserView GetShadowView(
+            BaseItem parent,
+            string viewType,
+            string sortName)
         {
             if (parent == null)
             {
@@ -2248,18 +2246,21 @@ namespace Emby.Server.Implementations.Library
 
             if (refresh)
             {
-                _providerManagerFactory().QueueRefresh(item.Id, new MetadataRefreshOptions(new DirectoryService(_logger, _fileSystem))
-                {
-                    // Need to force save to increment DateLastSaved
-                    ForceSave = true
-
-                }, RefreshPriority.Normal);
+                _providerManagerFactory().QueueRefresh(
+                    item.Id,
+                    new MetadataRefreshOptions(new DirectoryService(_logger, _fileSystem))
+                    {
+                        // Need to force save to increment DateLastSaved
+                        ForceSave = true
+                    },
+                    RefreshPriority.Normal);
             }
 
             return item;
         }
 
-        public UserView GetNamedView(string name,
+        public UserView GetNamedView(
+            string name,
             Guid parentId,
             string viewType,
             string sortName,
@@ -2322,17 +2323,21 @@ namespace Emby.Server.Implementations.Library
 
             if (refresh)
             {
-                _providerManagerFactory().QueueRefresh(item.Id, new MetadataRefreshOptions(new DirectoryService(_logger, _fileSystem))
-                {
-                    // Need to force save to increment DateLastSaved
-                    ForceSave = true
-                }, RefreshPriority.Normal);
+                _providerManagerFactory().QueueRefresh(
+                    item.Id,
+                    new MetadataRefreshOptions(new DirectoryService(_logger, _fileSystem))
+                    {
+                        // Need to force save to increment DateLastSaved
+                        ForceSave = true
+                    },
+                    RefreshPriority.Normal);
             }
 
             return item;
         }
 
-        public void AddExternalSubtitleStreams(List<MediaStream> streams,
+        public void AddExternalSubtitleStreams(
+            List<MediaStream> streams,
             string videoPath,
             string[] files)
         {
@@ -2436,6 +2441,7 @@ namespace Emby.Server.Implementations.Library
                     {
                         changed = true;
                     }
+
                     episode.IndexNumber = episodeInfo.EpisodeNumber;
                 }
 
@@ -2445,6 +2451,7 @@ namespace Emby.Server.Implementations.Library
                     {
                         changed = true;
                     }
+
                     episode.IndexNumberEnd = episodeInfo.EndingEpsiodeNumber;
                 }
 
@@ -2454,6 +2461,7 @@ namespace Emby.Server.Implementations.Library
                     {
                         changed = true;
                     }
+
                     episode.ParentIndexNumber = episodeInfo.SeasonNumber;
                 }
             }
@@ -2483,6 +2491,7 @@ namespace Emby.Server.Implementations.Library
 
         private NamingOptions _namingOptions;
         private string[] _videoFileExtensions;
+
         private NamingOptions GetNamingOptionsInternal()
         {
             if (_namingOptions == null)
@@ -2679,7 +2688,7 @@ namespace Emby.Server.Implementations.Library
             var newPath = path.Replace(from, to, StringComparison.OrdinalIgnoreCase);
             var changed = false;
 
-            if (!string.Equals(newPath, path))
+            if (!string.Equals(newPath, path, StringComparison.Ordinal))
             {
                 if (to.IndexOf('/') != -1)
                 {
@@ -2803,6 +2812,7 @@ namespace Emby.Server.Implementations.Library
                     {
                         continue;
                     }
+
                     throw;
                 }
             }
@@ -2907,6 +2917,7 @@ namespace Emby.Server.Implementations.Library
         }
 
         private const string ShortcutFileExtension = ".mblink";
+
         public void AddMediaPath(string virtualFolderName, MediaPathInfo pathInfo)
         {
             AddMediaPathInternal(virtualFolderName, pathInfo, true);
@@ -2923,7 +2934,7 @@ namespace Emby.Server.Implementations.Library
 
             if (string.IsNullOrWhiteSpace(path))
             {
-                throw new ArgumentNullException(nameof(path));
+                throw new ArgumentException(nameof(path));
             }
 
             if (!Directory.Exists(path))

+ 19 - 18
Emby.Server.Implementations/Localization/LocalizationManager.cs

@@ -11,7 +11,6 @@ using MediaBrowser.Controller.Configuration;
 using MediaBrowser.Model.Entities;
 using MediaBrowser.Model.Extensions;
 using MediaBrowser.Model.Globalization;
-using MediaBrowser.Model.IO;
 using MediaBrowser.Model.Serialization;
 using Microsoft.Extensions.Logging;
 
@@ -35,7 +34,6 @@ namespace Emby.Server.Implementations.Localization
         private readonly Dictionary<string, Dictionary<string, ParentalRating>> _allParentalRatings =
             new Dictionary<string, Dictionary<string, ParentalRating>>(StringComparer.OrdinalIgnoreCase);
 
-        private readonly IFileSystem _fileSystem;
         private readonly IJsonSerializer _jsonSerializer;
         private readonly ILogger _logger;
         private static readonly Assembly _assembly = typeof(LocalizationManager).Assembly;
@@ -44,40 +42,38 @@ namespace Emby.Server.Implementations.Localization
         /// Initializes a new instance of the <see cref="LocalizationManager" /> class.
         /// </summary>
         /// <param name="configurationManager">The configuration manager.</param>
-        /// <param name="fileSystem">The file system.</param>
         /// <param name="jsonSerializer">The json serializer.</param>
+        /// <param name="loggerFactory">The logger factory</param>
         public LocalizationManager(
             IServerConfigurationManager configurationManager,
-            IFileSystem fileSystem,
             IJsonSerializer jsonSerializer,
             ILoggerFactory loggerFactory)
         {
             _configurationManager = configurationManager;
-            _fileSystem = fileSystem;
             _jsonSerializer = jsonSerializer;
             _logger = loggerFactory.CreateLogger(nameof(LocalizationManager));
         }
 
         public async Task LoadAll()
         {
-            const string ratingsResource = "Emby.Server.Implementations.Localization.Ratings.";
+            const string RatingsResource = "Emby.Server.Implementations.Localization.Ratings.";
 
             // Extract from the assembly
             foreach (var resource in _assembly.GetManifestResourceNames())
             {
-                if (!resource.StartsWith(ratingsResource))
+                if (!resource.StartsWith(RatingsResource, StringComparison.Ordinal))
                 {
                     continue;
                 }
 
-                string countryCode = resource.Substring(ratingsResource.Length, 2);
+                string countryCode = resource.Substring(RatingsResource.Length, 2);
                 var dict = new Dictionary<string, ParentalRating>(StringComparer.OrdinalIgnoreCase);
 
                 using (var str = _assembly.GetManifestResourceStream(resource))
                 using (var reader = new StreamReader(str))
                 {
                     string line;
-                    while ((line = await reader.ReadLineAsync()) != null)
+                    while ((line = await reader.ReadLineAsync().ConfigureAwait(false)) != null)
                     {
                         if (string.IsNullOrWhiteSpace(line))
                         {
@@ -102,7 +98,7 @@ namespace Emby.Server.Implementations.Localization
                 _allParentalRatings[countryCode] = dict;
             }
 
-            await LoadCultures();
+            await LoadCultures().ConfigureAwait(false);
         }
 
         public string NormalizeFormKD(string text)
@@ -121,14 +117,14 @@ namespace Emby.Server.Implementations.Localization
         {
             List<CultureDto> list = new List<CultureDto>();
 
-            const string path = "Emby.Server.Implementations.Localization.iso6392.txt";
+            const string ResourcePath = "Emby.Server.Implementations.Localization.iso6392.txt";
 
-            using (var stream = _assembly.GetManifestResourceStream(path))
+            using (var stream = _assembly.GetManifestResourceStream(ResourcePath))
             using (var reader = new StreamReader(stream))
             {
                 while (!reader.EndOfStream)
                 {
-                    var line = await reader.ReadLineAsync();
+                    var line = await reader.ReadLineAsync().ConfigureAwait(false);
 
                     if (string.IsNullOrWhiteSpace(line))
                     {
@@ -154,11 +150,11 @@ namespace Emby.Server.Implementations.Localization
                         string[] threeletterNames;
                         if (string.IsNullOrWhiteSpace(parts[1]))
                         {
-                            threeletterNames = new [] { parts[0] };
+                            threeletterNames = new[] { parts[0] };
                         }
                         else
                         {
-                            threeletterNames = new [] { parts[0], parts[1] };
+                            threeletterNames = new[] { parts[0], parts[1] };
                         }
 
                         list.Add(new CultureDto
@@ -218,6 +214,7 @@ namespace Emby.Server.Implementations.Localization
         /// Gets the ratings.
         /// </summary>
         /// <param name="countryCode">The country code.</param>
+        /// <returns>The ratings</returns>
         private Dictionary<string, ParentalRating> GetRatings(string countryCode)
         {
             _allParentalRatings.TryGetValue(countryCode, out var value);
@@ -227,9 +224,12 @@ namespace Emby.Server.Implementations.Localization
 
         private static readonly string[] _unratedValues = { "n/a", "unrated", "not rated" };
 
+        /// <inheritdoc />
         /// <summary>
         /// Gets the rating level.
         /// </summary>
+        /// <param name="rating">Rating field</param>
+        /// <returns>The rating level</returns>&gt;
         public int? GetRatingLevel(string rating)
         {
             if (string.IsNullOrEmpty(rating))
@@ -301,6 +301,7 @@ namespace Emby.Server.Implementations.Localization
             {
                 culture = _configurationManager.Configuration.UICulture;
             }
+
             if (string.IsNullOrEmpty(culture))
             {
                 culture = DefaultCulture;
@@ -346,8 +347,8 @@ namespace Emby.Server.Implementations.Localization
 
             var namespaceName = GetType().Namespace + "." + prefix;
 
-            await CopyInto(dictionary, namespaceName + "." + baseFilename);
-            await CopyInto(dictionary, namespaceName + "." + GetResourceFilename(culture));
+            await CopyInto(dictionary, namespaceName + "." + baseFilename).ConfigureAwait(false);
+            await CopyInto(dictionary, namespaceName + "." + GetResourceFilename(culture)).ConfigureAwait(false);
 
             return dictionary;
         }
@@ -359,7 +360,7 @@ namespace Emby.Server.Implementations.Localization
                 // If a Culture doesn't have a translation the stream will be null and it defaults to en-us further up the chain
                 if (stream != null)
                 {
-                    var dict = await _jsonSerializer.DeserializeFromStreamAsync<Dictionary<string, string>>(stream);
+                    var dict = await _jsonSerializer.DeserializeFromStreamAsync<Dictionary<string, string>>(stream).ConfigureAwait(false);
 
                     foreach (var key in dict.Keys)
                     {

+ 144 - 100
Emby.Server.Implementations/Session/SessionManager.cs

@@ -116,14 +116,14 @@ namespace Emby.Server.Implementations.Session
             _authRepo = authRepo;
             _deviceManager = deviceManager;
             _mediaSourceManager = mediaSourceManager;
-            _deviceManager.DeviceOptionsUpdated += _deviceManager_DeviceOptionsUpdated;
+            _deviceManager.DeviceOptionsUpdated += OnDeviceManagerDeviceOptionsUpdated;
         }
 
-        private void _deviceManager_DeviceOptionsUpdated(object sender, GenericEventArgs<Tuple<string, DeviceOptions>> e)
+        private void OnDeviceManagerDeviceOptionsUpdated(object sender, GenericEventArgs<Tuple<string, DeviceOptions>> e)
         {
             foreach (var session in Sessions)
             {
-                if (string.Equals(session.DeviceId, e.Argument.Item1))
+                if (string.Equals(session.DeviceId, e.Argument.Item1, StringComparison.Ordinal))
                 {
                     if (!string.IsNullOrWhiteSpace(e.Argument.Item2.CustomName))
                     {
@@ -138,11 +138,29 @@ namespace Emby.Server.Implementations.Session
             }
         }
 
-        private bool _disposed;
+        private bool _disposed = false;
+
         public void Dispose()
         {
+            Dispose(true);
+            GC.SuppressFinalize(this);
+        }
+
+        protected virtual void Dispose(bool disposing)
+        {
+            if (_disposed)
+            {
+                return;
+            }
+
+            if (disposing)
+            {
+                // TODO: dispose stuff
+            }
+
+            _deviceManager.DeviceOptionsUpdated -= OnDeviceManagerDeviceOptionsUpdated;
+
             _disposed = true;
-            _deviceManager.DeviceOptionsUpdated -= _deviceManager_DeviceOptionsUpdated;
         }
 
         public void CheckDisposed()
@@ -157,7 +175,7 @@ namespace Emby.Server.Implementations.Session
         /// Gets all connections.
         /// </summary>
         /// <value>All connections.</value>
-        public IEnumerable<SessionInfo> Sessions => _activeConnections.Values.OrderByDescending(c => c.LastActivityDate).ToList();
+        public IEnumerable<SessionInfo> Sessions => _activeConnections.Values.OrderByDescending(c => c.LastActivityDate);
 
         private void OnSessionStarted(SessionInfo info)
         {
@@ -171,20 +189,27 @@ namespace Emby.Server.Implementations.Session
                 }
             }
 
-            EventHelper.QueueEventIfNotNull(SessionStarted, this, new SessionEventArgs
-            {
-                SessionInfo = info
-
-            }, _logger);
+            EventHelper.QueueEventIfNotNull(
+                SessionStarted,
+                this,
+                new SessionEventArgs
+                {
+                    SessionInfo = info
+                },
+                _logger);
         }
 
         private void OnSessionEnded(SessionInfo info)
         {
-            EventHelper.QueueEventIfNotNull(SessionEnded, this, new SessionEventArgs
-            {
-                SessionInfo = info
+            EventHelper.QueueEventIfNotNull(
+                SessionEnded,
+                this,
+                new SessionEventArgs
+                {
+                    SessionInfo = info
 
-            }, _logger);
+                },
+                _logger);
 
             info.Dispose();
         }
@@ -192,9 +217,6 @@ namespace Emby.Server.Implementations.Session
         public void UpdateDeviceName(string sessionId, string deviceName)
         {
             var session = GetSession(sessionId);
-
-            var key = GetSessionKey(session.Client, session.DeviceId);
-
             if (session != null)
             {
                 session.DeviceName = deviceName;
@@ -210,10 +232,10 @@ namespace Emby.Server.Implementations.Session
         /// <param name="deviceName">Name of the device.</param>
         /// <param name="remoteEndPoint">The remote end point.</param>
         /// <param name="user">The user.</param>
-        /// <returns>Task.</returns>
+        /// <returns>SessionInfo.</returns>
         /// <exception cref="ArgumentNullException">user</exception>
-        /// <exception cref="UnauthorizedAccessException"></exception>
-        public SessionInfo LogSessionActivity(string appName,
+        public SessionInfo LogSessionActivity(
+            string appName,
             string appVersion,
             string deviceId,
             string deviceName,
@@ -226,10 +248,12 @@ namespace Emby.Server.Implementations.Session
             {
                 throw new ArgumentNullException(nameof(appName));
             }
+
             if (string.IsNullOrEmpty(appVersion))
             {
                 throw new ArgumentNullException(nameof(appVersion));
             }
+
             if (string.IsNullOrEmpty(deviceId))
             {
                 throw new ArgumentNullException(nameof(deviceId));
@@ -260,10 +284,12 @@ namespace Emby.Server.Implementations.Session
 
             if ((activityDate - lastActivityDate).TotalSeconds > 10)
             {
-                SessionActivity?.Invoke(this, new SessionEventArgs
-                {
-                    SessionInfo = session
-                });
+                SessionActivity?.Invoke(
+                    this,
+                    new SessionEventArgs
+                    {
+                        SessionInfo = session
+                    });
             }
 
             return session;
@@ -304,6 +330,7 @@ namespace Emby.Server.Implementations.Session
         /// <summary>
         /// Updates the now playing item id.
         /// </summary>
+        /// <returns>Task.</returns>
         private async Task UpdateNowPlayingItem(SessionInfo session, PlaybackProgressInfo info, BaseItem libraryItem, bool updateLastCheckInTime)
         {
             if (string.IsNullOrEmpty(info.MediaSourceId))
@@ -418,7 +445,7 @@ namespace Emby.Server.Implementations.Session
             });
 
             sessionInfo.UserId = user == null ? Guid.Empty : user.Id;
-            sessionInfo.UserName = user == null ? null : user.Name;
+            sessionInfo.UserName = user?.Name;
             sessionInfo.UserPrimaryImageTag = user == null ? null : GetImageCacheTag(user, ImageType.Primary);
             sessionInfo.RemoteEndPoint = remoteEndPoint;
             sessionInfo.Client = appName;
@@ -432,7 +459,7 @@ namespace Emby.Server.Implementations.Session
 
             if (user == null)
             {
-                sessionInfo.AdditionalUsers = new SessionUserInfo[] { };
+                sessionInfo.AdditionalUsers = Array.Empty<SessionUserInfo>();
             }
 
             return sessionInfo;
@@ -449,9 +476,9 @@ namespace Emby.Server.Implementations.Session
                 ServerId = _appHost.SystemId
             };
 
-            var username = user == null ? null : user.Name;
+            var username = user?.Name;
 
-            sessionInfo.UserId = user == null ? Guid.Empty : user.Id;
+            sessionInfo.UserId = user?.Id ?? Guid.Empty;
             sessionInfo.UserName = username;
             sessionInfo.UserPrimaryImageTag = user == null ? null : GetImageCacheTag(user, ImageType.Primary);
             sessionInfo.RemoteEndPoint = remoteEndPoint;
@@ -508,6 +535,7 @@ namespace Emby.Server.Implementations.Session
                 _idleTimer = new Timer(CheckForIdlePlayback, null, TimeSpan.FromMinutes(5), TimeSpan.FromMinutes(5));
             }
         }
+
         private void StopIdleCheckTimer()
         {
             if (_idleTimer != null)
@@ -539,9 +567,9 @@ namespace Emby.Server.Implementations.Session
                             Item = session.NowPlayingItem,
                             ItemId = session.NowPlayingItem == null ? Guid.Empty : session.NowPlayingItem.Id,
                             SessionId = session.Id,
-                            MediaSourceId = session.PlayState == null ? null : session.PlayState.MediaSourceId,
-                            PositionTicks = session.PlayState == null ? null : session.PlayState.PositionTicks
-                        });
+                            MediaSourceId = session.PlayState?.MediaSourceId,
+                            PositionTicks = session.PlayState?.PositionTicks
+                        }).ConfigureAwait(false);
                     }
                     catch (Exception ex)
                     {
@@ -616,18 +644,22 @@ namespace Emby.Server.Implementations.Session
 
             // Nothing to save here
             // Fire events to inform plugins
-            EventHelper.QueueEventIfNotNull(PlaybackStart, this, new PlaybackProgressEventArgs
-            {
-                Item = libraryItem,
-                Users = users,
-                MediaSourceId = info.MediaSourceId,
-                MediaInfo = info.Item,
-                DeviceName = session.DeviceName,
-                ClientName = session.Client,
-                DeviceId = session.DeviceId,
-                Session = session
-
-            }, _logger);
+            EventHelper.QueueEventIfNotNull(
+                PlaybackStart,
+                this,
+                new PlaybackProgressEventArgs
+                {
+                    Item = libraryItem,
+                    Users = users,
+                    MediaSourceId = info.MediaSourceId,
+                    MediaInfo = info.Item,
+                    DeviceName = session.DeviceName,
+                    ClientName = session.Client,
+                    DeviceId = session.DeviceId,
+                    Session = session
+
+                },
+                _logger);
 
             StartIdleCheckTimer();
         }
@@ -667,6 +699,7 @@ namespace Emby.Server.Implementations.Session
         /// <summary>
         /// Used to report playback progress for an item
         /// </summary>
+        /// <returns>Task.</returns>
         public async Task OnPlaybackProgress(PlaybackProgressInfo info, bool isAutomated)
         {
             CheckDisposed();
@@ -695,21 +728,23 @@ namespace Emby.Server.Implementations.Session
                 }
             }
 
-            PlaybackProgress?.Invoke(this, new PlaybackProgressEventArgs
-            {
-                Item = libraryItem,
-                Users = users,
-                PlaybackPositionTicks = session.PlayState.PositionTicks,
-                MediaSourceId = session.PlayState.MediaSourceId,
-                MediaInfo = info.Item,
-                DeviceName = session.DeviceName,
-                ClientName = session.Client,
-                DeviceId = session.DeviceId,
-                IsPaused = info.IsPaused,
-                PlaySessionId = info.PlaySessionId,
-                IsAutomated = isAutomated,
-                Session = session
-            });
+            PlaybackProgress?.Invoke(
+                this,
+                new PlaybackProgressEventArgs
+                {
+                    Item = libraryItem,
+                    Users = users,
+                    PlaybackPositionTicks = session.PlayState.PositionTicks,
+                    MediaSourceId = session.PlayState.MediaSourceId,
+                    MediaInfo = info.Item,
+                    DeviceName = session.DeviceName,
+                    ClientName = session.Client,
+                    DeviceId = session.DeviceId,
+                    IsPaused = info.IsPaused,
+                    PlaySessionId = info.PlaySessionId,
+                    IsAutomated = isAutomated,
+                    Session = session
+                });
 
             if (!isAutomated)
             {
@@ -830,8 +865,7 @@ namespace Emby.Server.Implementations.Session
                 {
                     MediaSourceInfo mediaSource = null;
 
-                    var hasMediaSources = libraryItem as IHasMediaSources;
-                    if (hasMediaSources != null)
+                    if (libraryItem is IHasMediaSources hasMediaSources)
                     {
                         mediaSource = await GetMediaSource(libraryItem, info.MediaSourceId, info.LiveStreamId).ConfigureAwait(false);
                     }
@@ -848,7 +882,8 @@ namespace Emby.Server.Implementations.Session
             {
                 var msString = info.PositionTicks.HasValue ? (info.PositionTicks.Value / 10000).ToString(CultureInfo.InvariantCulture) : "unknown";
 
-                _logger.LogInformation("Playback stopped reported by app {0} {1} playing {2}. Stopped at {3} ms",
+                _logger.LogInformation(
+                    "Playback stopped reported by app {0} {1} playing {2}. Stopped at {3} ms",
                     session.Client,
                     session.ApplicationVersion,
                     info.Item.Name,
@@ -887,20 +922,24 @@ namespace Emby.Server.Implementations.Session
                 }
             }
 
-            EventHelper.QueueEventIfNotNull(PlaybackStopped, this, new PlaybackStopEventArgs
-            {
-                Item = libraryItem,
-                Users = users,
-                PlaybackPositionTicks = info.PositionTicks,
-                PlayedToCompletion = playedToCompletion,
-                MediaSourceId = info.MediaSourceId,
-                MediaInfo = info.Item,
-                DeviceName = session.DeviceName,
-                ClientName = session.Client,
-                DeviceId = session.DeviceId,
-                Session = session
+            EventHelper.QueueEventIfNotNull(
+                PlaybackStopped,
+                this,
+                new PlaybackStopEventArgs
+                {
+                    Item = libraryItem,
+                    Users = users,
+                    PlaybackPositionTicks = info.PositionTicks,
+                    PlayedToCompletion = playedToCompletion,
+                    MediaSourceId = info.MediaSourceId,
+                    MediaInfo = info.Item,
+                    DeviceName = session.DeviceName,
+                    ClientName = session.Client,
+                    DeviceId = session.DeviceId,
+                    Session = session
 
-            }, _logger);
+                },
+                _logger);
         }
 
         private bool OnPlaybackStopped(User user, BaseItem item, long? positionTicks, bool playbackFailed)
@@ -936,11 +975,10 @@ namespace Emby.Server.Implementations.Session
         /// <param name="sessionId">The session identifier.</param>
         /// <param name="throwOnMissing">if set to <c>true</c> [throw on missing].</param>
         /// <returns>SessionInfo.</returns>
-        /// <exception cref="ResourceNotFoundException"></exception>
+        /// <exception cref="ResourceNotFoundException">sessionId</exception>
         private SessionInfo GetSession(string sessionId, bool throwOnMissing = true)
         {
-            var session = Sessions.FirstOrDefault(i => string.Equals(i.Id, sessionId));
-
+            var session = Sessions.FirstOrDefault(i => string.Equals(i.Id, sessionId, StringComparison.Ordinal));
             if (session == null && throwOnMissing)
             {
                 throw new ResourceNotFoundException(string.Format("Session {0} not found.", sessionId));
@@ -952,7 +990,7 @@ namespace Emby.Server.Implementations.Session
         private SessionInfo GetSessionToRemoteControl(string sessionId)
         {
             // Accept either device id or session id
-            var session = Sessions.FirstOrDefault(i => string.Equals(i.Id, sessionId));
+            var session = Sessions.FirstOrDefault(i => string.Equals(i.Id, sessionId, StringComparison.Ordinal));
 
             if (session == null)
             {
@@ -1061,10 +1099,12 @@ namespace Emby.Server.Implementations.Session
                     var series = episode.Series;
                     if (series != null)
                     {
-                        var episodes = series.GetEpisodes(user, new DtoOptions(false)
-                        {
-                            EnableImages = false
-                        })
+                        var episodes = series.GetEpisodes(
+                                user,
+                                new DtoOptions(false)
+                                {
+                                    EnableImages = false
+                                })
                             .Where(i => !i.IsVirtualItem)
                             .SkipWhile(i => i.Id != episode.Id)
                             .ToList();
@@ -1100,9 +1140,7 @@ namespace Emby.Server.Implementations.Session
                 return new List<BaseItem>();
             }
 
-            var byName = item as IItemByName;
-
-            if (byName != null)
+            if (item is IItemByName byName)
             {
                 return byName.GetTaggedItems(new InternalItemsQuery(user)
                 {
@@ -1152,7 +1190,7 @@ namespace Emby.Server.Implementations.Session
 
             if (item == null)
             {
-                _logger.LogError("A non-existant item Id {0} was passed into TranslateItemForInstantMix", id);
+                _logger.LogError("A non-existent item Id {0} was passed into TranslateItemForInstantMix", id);
                 return new List<BaseItem>();
             }
 
@@ -1163,13 +1201,15 @@ namespace Emby.Server.Implementations.Session
         {
             var generalCommand = new GeneralCommand
             {
-                Name = GeneralCommandType.DisplayContent.ToString()
+                Name = GeneralCommandType.DisplayContent.ToString(),
+                Arguments =
+                {
+                    ["ItemId"] = command.ItemId,
+                    ["ItemName"] = command.ItemName,
+                    ["ItemType"] = command.ItemType
+                }
             };
 
-            generalCommand.Arguments["ItemId"] = command.ItemId;
-            generalCommand.Arguments["ItemName"] = command.ItemName;
-            generalCommand.Arguments["ItemType"] = command.ItemType;
-
             return SendGeneralCommand(controllingSessionId, sessionId, generalCommand, cancellationToken);
         }
 
@@ -1410,7 +1450,8 @@ namespace Emby.Server.Implementations.Session
 
             var token = GetAuthorizationToken(user, request.DeviceId, request.App, request.AppVersion, request.DeviceName);
 
-            var session = LogSessionActivity(request.App,
+            var session = LogSessionActivity(
+                request.App,
                 request.AppVersion,
                 request.DeviceId,
                 request.DeviceName,
@@ -1454,9 +1495,9 @@ namespace Emby.Server.Implementations.Session
                     {
                         Logout(auth);
                     }
-                    catch
+                    catch (Exception ex)
                     {
-
+                        _logger.LogError(ex, "Error while logging out.");
                     }
                 }
             }
@@ -1572,7 +1613,8 @@ namespace Emby.Server.Implementations.Session
             ReportCapabilities(session, capabilities, true);
         }
 
-        private void ReportCapabilities(SessionInfo session,
+        private void ReportCapabilities(
+            SessionInfo session,
             ClientCapabilities capabilities,
             bool saveCapabilities)
         {
@@ -1580,10 +1622,12 @@ namespace Emby.Server.Implementations.Session
 
             if (saveCapabilities)
             {
-                CapabilitiesChanged?.Invoke(this, new SessionEventArgs
-                {
-                    SessionInfo = session
-                });
+                CapabilitiesChanged?.Invoke(
+                    this,
+                    new SessionEventArgs
+                    {
+                        SessionInfo = session
+                    });
 
                 try
                 {

+ 1 - 1
Emby.Server.Implementations/Sorting/AlbumArtistComparer.cs

@@ -32,7 +32,7 @@ namespace Emby.Server.Implementations.Sorting
         {
             var audio = x as IHasAlbumArtist;
 
-            return audio != null ? audio.AlbumArtists.FirstOrDefault() : null;
+            return audio?.AlbumArtists.FirstOrDefault();
         }
 
         /// <summary>

+ 7 - 7
Emby.Server.Implementations/Sorting/SeriesSortNameComparer.cs

@@ -18,17 +18,17 @@ namespace Emby.Server.Implementations.Sorting
             return string.Compare(GetValue(x), GetValue(y), StringComparison.CurrentCultureIgnoreCase);
         }
 
-        private static string GetValue(BaseItem item)
-        {
-            var hasSeries = item as IHasSeries;
-
-            return hasSeries != null ? hasSeries.FindSeriesSortName() : null;
-        }
-
         /// <summary>
         /// Gets the name.
         /// </summary>
         /// <value>The name.</value>
         public string Name => ItemSortBy.SeriesSortName;
+
+        private static string GetValue(BaseItem item)
+        {
+            var hasSeries = item as IHasSeries;
+
+            return hasSeries?.FindSeriesSortName();
+        }
     }
 }

+ 15 - 20
MediaBrowser.Api/BaseApiService.cs

@@ -165,6 +165,7 @@ namespace MediaBrowser.Api
                 {
                     options.ImageTypeLimit = hasDtoOptions.ImageTypeLimit.Value;
                 }
+
                 if (hasDtoOptions.EnableUserData.HasValue)
                 {
                     options.EnableUserData = hasDtoOptions.EnableUserData.Value;
@@ -307,7 +308,7 @@ namespace MediaBrowser.Api
             return pathInfo[index];
         }
 
-        private string[] Parse(string pathUri)
+        private static string[] Parse(string pathUri)
         {
             var actionParts = pathUri.Split(new[] { "://" }, StringSplitOptions.None);
 
@@ -329,38 +330,32 @@ namespace MediaBrowser.Api
         /// </summary>
         protected BaseItem GetItemByName(string name, string type, ILibraryManager libraryManager, DtoOptions dtoOptions)
         {
-            BaseItem item;
-
-            if (type.IndexOf("Person", StringComparison.OrdinalIgnoreCase) == 0)
-            {
-                item = GetPerson(name, libraryManager, dtoOptions);
-            }
-            else if (type.IndexOf("Artist", StringComparison.OrdinalIgnoreCase) == 0)
+            if (type.Equals("Person", StringComparison.OrdinalIgnoreCase))
             {
-                item = GetArtist(name, libraryManager, dtoOptions);
+                return GetPerson(name, libraryManager, dtoOptions);
             }
-            else if (type.IndexOf("Genre", StringComparison.OrdinalIgnoreCase) == 0)
+            else if (type.Equals("Artist", StringComparison.OrdinalIgnoreCase))
             {
-                item = GetGenre(name, libraryManager, dtoOptions);
+                return GetArtist(name, libraryManager, dtoOptions);
             }
-            else if (type.IndexOf("MusicGenre", StringComparison.OrdinalIgnoreCase) == 0)
+            else if (type.Equals("Genre", StringComparison.OrdinalIgnoreCase))
             {
-                item = GetMusicGenre(name, libraryManager, dtoOptions);
+                return GetGenre(name, libraryManager, dtoOptions);
             }
-            else if (type.IndexOf("Studio", StringComparison.OrdinalIgnoreCase) == 0)
+            else if (type.Equals("MusicGenre", StringComparison.OrdinalIgnoreCase))
             {
-                item = GetStudio(name, libraryManager, dtoOptions);
+                return GetMusicGenre(name, libraryManager, dtoOptions);
             }
-            else if (type.IndexOf("Year", StringComparison.OrdinalIgnoreCase) == 0)
+            else if (type.Equals("Studio", StringComparison.OrdinalIgnoreCase))
             {
-                item = libraryManager.GetYear(int.Parse(name));
+                return GetStudio(name, libraryManager, dtoOptions);
             }
-            else
+            else if (type.Equals("Year", StringComparison.OrdinalIgnoreCase))
             {
-                throw new ArgumentException();
+                return libraryManager.GetYear(int.Parse(name));
             }
 
-            return item;
+            throw new ArgumentException("Invalid type", nameof(type));
         }
     }
 }

+ 0 - 10
MediaBrowser.Api/UserLibrary/ArtistsService.cs

@@ -87,11 +87,6 @@ namespace MediaBrowser.Api.UserLibrary
         /// <returns>System.Object.</returns>
         public object Get(GetArtists request)
         {
-            if (string.IsNullOrWhiteSpace(request.IncludeItemTypes))
-            {
-                //request.IncludeItemTypes = "Audio,MusicVideo";
-            }
-
             return GetResultSlim(request);
         }
 
@@ -102,11 +97,6 @@ namespace MediaBrowser.Api.UserLibrary
         /// <returns>System.Object.</returns>
         public object Get(GetAlbumArtists request)
         {
-            if (string.IsNullOrWhiteSpace(request.IncludeItemTypes))
-            {
-                //request.IncludeItemTypes = "Audio,MusicVideo";
-            }
-
             var result = GetResultSlim(request);
 
             return ToOptimizedResult(result);

+ 10 - 29
MediaBrowser.Providers/Manager/ProviderManager.cs

@@ -942,10 +942,7 @@ namespace MediaBrowser.Providers.Manager
                 _activeRefreshes[id] = 0;
             }
 
-            if (RefreshStarted != null)
-            {
-                RefreshStarted(this, new GenericEventArgs<BaseItem>(item));
-            }
+            RefreshStarted?.Invoke(this, new GenericEventArgs<BaseItem>(item));
         }
 
         public void OnRefreshComplete(BaseItem item)
@@ -956,10 +953,7 @@ namespace MediaBrowser.Providers.Manager
                 _activeRefreshes.Remove(item.Id);
             }
 
-            if (RefreshCompleted != null)
-            {
-                RefreshCompleted(this, new GenericEventArgs<BaseItem>(item));
-            }
+            RefreshCompleted?.Invoke(this, new GenericEventArgs<BaseItem>(item));
         }
 
         public double? GetRefreshProgress(Guid id)
@@ -986,10 +980,7 @@ namespace MediaBrowser.Providers.Manager
                 {
                     _activeRefreshes[id] = progress;
 
-                    if (RefreshProgress != null)
-                    {
-                        RefreshProgress(this, new GenericEventArgs<Tuple<BaseItem, double>>(new Tuple<BaseItem, double>(item, progress)));
-                    }
+                    RefreshProgress?.Invoke(this, new GenericEventArgs<Tuple<BaseItem, double>>(new Tuple<BaseItem, double>(item, progress)));
                 }
                 else
                 {
@@ -1079,17 +1070,14 @@ namespace MediaBrowser.Providers.Manager
             await item.RefreshMetadata(options, cancellationToken).ConfigureAwait(false);
 
             // Collection folders don't validate their children so we'll have to simulate that here
-            var collectionFolder = item as CollectionFolder;
 
-            if (collectionFolder != null)
+            if (item is CollectionFolder collectionFolder)
             {
                 await RefreshCollectionFolderChildren(options, collectionFolder, cancellationToken).ConfigureAwait(false);
             }
             else
             {
-                var folder = item as Folder;
-
-                if (folder != null)
+                if (item is Folder folder)
                 {
                     await folder.ValidateChildren(new SimpleProgress<double>(), cancellationToken, options).ConfigureAwait(false);
                 }
@@ -1098,16 +1086,11 @@ namespace MediaBrowser.Providers.Manager
 
         private async Task RefreshCollectionFolderChildren(MetadataRefreshOptions options, CollectionFolder collectionFolder, CancellationToken cancellationToken)
         {
-            foreach (var child in collectionFolder.GetPhysicalFolders().ToList())
+            foreach (var child in collectionFolder.GetPhysicalFolders())
             {
                 await child.RefreshMetadata(options, cancellationToken).ConfigureAwait(false);
 
-                if (child.IsFolder)
-                {
-                    var folder = (Folder)child;
-
-                    await folder.ValidateChildren(new SimpleProgress<double>(), cancellationToken, options, true).ConfigureAwait(false);
-                }
+                await child.ValidateChildren(new SimpleProgress<double>(), cancellationToken, options, true).ConfigureAwait(false);
             }
         }
 
@@ -1116,20 +1099,18 @@ namespace MediaBrowser.Providers.Manager
             var albums = _libraryManagerFactory()
                 .GetItemList(new InternalItemsQuery
                 {
-                    IncludeItemTypes = new[] { typeof(MusicAlbum).Name },
+                    IncludeItemTypes = new[] { nameof(MusicAlbum) },
                     ArtistIds = new[] { item.Id },
                     DtoOptions = new DtoOptions(false)
                     {
                         EnableImages = false
                     }
                 })
-                .OfType<MusicAlbum>()
-                .ToList();
+                .OfType<MusicAlbum>();
 
             var musicArtists = albums
                 .Select(i => i.MusicArtist)
-                .Where(i => i != null)
-                .ToList();
+                .Where(i => i != null);
 
             var musicArtistRefreshTasks = musicArtists.Select(i => i.ValidateChildren(new SimpleProgress<double>(), cancellationToken, options, true));
 

+ 10 - 14
MediaBrowser.Providers/Music/MusicBrainzAlbumProvider.cs

@@ -53,7 +53,7 @@ namespace MediaBrowser.Providers.Music
             var releaseId = searchInfo.GetReleaseId();
             var releaseGroupId = searchInfo.GetReleaseGroupId();
 
-            string url = null;
+            string url;
             var isNameSearch = false;
             bool forceMusicBrainzProper = false;
 
@@ -100,10 +100,10 @@ namespace MediaBrowser.Providers.Music
                 }
             }
 
-            return new List<RemoteSearchResult>();
+            return Enumerable.Empty<RemoteSearchResult>();
         }
 
-        private List<RemoteSearchResult> GetResultsFromResponse(Stream stream)
+        private IEnumerable<RemoteSearchResult> GetResultsFromResponse(Stream stream)
         {
             using (var oReader = new StreamReader(stream, Encoding.UTF8))
             {
@@ -149,7 +149,7 @@ namespace MediaBrowser.Providers.Music
 
                         return result;
 
-                    }).ToList();
+                    });
                 }
             }
         }
@@ -301,7 +301,7 @@ namespace MediaBrowser.Providers.Music
 
             public List<ValueTuple<string, string>> Artists = new List<ValueTuple<string, string>>();
 
-            public static List<ReleaseResult> Parse(XmlReader reader)
+            public static IEnumerable<ReleaseResult> Parse(XmlReader reader)
             {
                 reader.MoveToContent();
                 reader.Read();
@@ -338,13 +338,11 @@ namespace MediaBrowser.Providers.Music
                     }
                 }
 
-                return new List<ReleaseResult>();
+                return Enumerable.Empty<ReleaseResult>();
             }
 
-            private static List<ReleaseResult> ParseReleaseList(XmlReader reader)
+            private static IEnumerable<ReleaseResult> ParseReleaseList(XmlReader reader)
             {
-                var list = new List<ReleaseResult>();
-
                 reader.MoveToContent();
                 reader.Read();
 
@@ -369,7 +367,7 @@ namespace MediaBrowser.Providers.Music
                                         var release = ParseRelease(subReader, releaseId);
                                         if (release != null)
                                         {
-                                            list.Add(release);
+                                            yield return release;
                                         }
                                     }
                                     break;
@@ -386,8 +384,6 @@ namespace MediaBrowser.Providers.Music
                         reader.Read();
                     }
                 }
-
-                return list;
             }
 
             private static ReleaseResult ParseRelease(XmlReader reader, string releaseId)
@@ -552,7 +548,7 @@ namespace MediaBrowser.Providers.Music
             return (null, null);
         }
 
-        private static ValueTuple<string, string> ParseArtistArtistCredit(XmlReader reader, string artistId)
+        private static (string name, string id) ParseArtistArtistCredit(XmlReader reader, string artistId)
         {
             reader.MoveToContent();
             reader.Read();
@@ -586,7 +582,7 @@ namespace MediaBrowser.Providers.Music
                 }
             }
 
-            return new ValueTuple<string, string>(name, artistId);
+            return (name, artistId);
         }
 
         private async Task<string> GetReleaseIdFromReleaseGroupId(string releaseGroupId, CancellationToken cancellationToken)

+ 7 - 11
MediaBrowser.Providers/Music/MusicBrainzArtistProvider.cs

@@ -50,7 +50,7 @@ namespace MediaBrowser.Providers.Music
                 {
                     using (var stream = response.Content)
                     {
-                        var results = GetResultsFromResponse(stream);
+                        var results = GetResultsFromResponse(stream).ToList();
 
                         if (results.Count > 0)
                         {
@@ -74,10 +74,10 @@ namespace MediaBrowser.Providers.Music
                 }
             }
 
-            return new List<RemoteSearchResult>();
+            return Enumerable.Empty<RemoteSearchResult>();
         }
 
-        private List<RemoteSearchResult> GetResultsFromResponse(Stream stream)
+        private IEnumerable<RemoteSearchResult> GetResultsFromResponse(Stream stream)
         {
             using (var oReader = new StreamReader(stream, Encoding.UTF8))
             {
@@ -126,15 +126,13 @@ namespace MediaBrowser.Providers.Music
                         }
                     }
 
-                    return new List<RemoteSearchResult>();
+                    return Enumerable.Empty<RemoteSearchResult>();
                 }
             }
         }
 
-        private List<RemoteSearchResult> ParseArtistList(XmlReader reader)
+        private IEnumerable<RemoteSearchResult> ParseArtistList(XmlReader reader)
         {
-            var list = new List<RemoteSearchResult>();
-
             reader.MoveToContent();
             reader.Read();
 
@@ -159,7 +157,7 @@ namespace MediaBrowser.Providers.Music
                                     var artist = ParseArtist(subReader, mbzId);
                                     if (artist != null)
                                     {
-                                        list.Add(artist);
+                                        yield return artist;
                                     }
                                 }
                                 break;
@@ -176,8 +174,6 @@ namespace MediaBrowser.Providers.Music
                     reader.Read();
                 }
             }
-
-            return list;
         }
 
         private RemoteSearchResult ParseArtist(XmlReader reader, string artistId)
@@ -277,7 +273,7 @@ namespace MediaBrowser.Providers.Music
         /// </summary>
         /// <param name="name">The name.</param>
         /// <returns>System.String.</returns>
-        private string UrlEncode(string name)
+        private static string UrlEncode(string name)
         {
             return WebUtility.UrlEncode(name);
         }