Merge pull request #1106 from Bond-009/warn2

More warning fixes
This commit is contained in:
Vasily 2019-03-14 19:54:44 +03:00 committed by GitHub
commit 208585d3f6
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
12 changed files with 532 additions and 487 deletions

View file

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

View file

@ -9,14 +9,14 @@ namespace Emby.Server.Implementations.Diagnostics
{ {
public class CommonProcess : IProcess public class CommonProcess : IProcess
{ {
public event EventHandler Exited;
private readonly ProcessOptions _options;
private readonly Process _process; private readonly Process _process;
private bool _disposed = false;
private bool _hasExited;
public CommonProcess(ProcessOptions options) public CommonProcess(ProcessOptions options)
{ {
_options = options; StartInfo = options;
var startInfo = new ProcessStartInfo var startInfo = new ProcessStartInfo
{ {
@ -27,10 +27,10 @@ namespace Emby.Server.Implementations.Diagnostics
CreateNoWindow = options.CreateNoWindow, CreateNoWindow = options.CreateNoWindow,
RedirectStandardError = options.RedirectStandardError, RedirectStandardError = options.RedirectStandardError,
RedirectStandardInput = options.RedirectStandardInput, RedirectStandardInput = options.RedirectStandardInput,
RedirectStandardOutput = options.RedirectStandardOutput RedirectStandardOutput = options.RedirectStandardOutput,
ErrorDialog = options.ErrorDialog
}; };
startInfo.ErrorDialog = options.ErrorDialog;
if (options.IsHidden) if (options.IsHidden)
{ {
@ -45,11 +45,22 @@ namespace Emby.Server.Implementations.Diagnostics
if (options.EnableRaisingEvents) if (options.EnableRaisingEvents)
{ {
_process.EnableRaisingEvents = true; _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 private bool HasExited
{ {
get 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() public void Start()
{ {
_process.Start(); _process.Start();
@ -108,7 +100,7 @@ namespace Emby.Server.Implementations.Diagnostics
public Task<bool> WaitForExitAsync(int timeMs) 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) if (HasExited)
{ {
@ -130,7 +122,29 @@ namespace Emby.Server.Implementations.Diagnostics
public void Dispose() 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);
} }
} }
} }

View file

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

View file

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

View file

@ -116,14 +116,14 @@ namespace Emby.Server.Implementations.Session
_authRepo = authRepo; _authRepo = authRepo;
_deviceManager = deviceManager; _deviceManager = deviceManager;
_mediaSourceManager = mediaSourceManager; _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) 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)) 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() 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; _disposed = true;
_deviceManager.DeviceOptionsUpdated -= _deviceManager_DeviceOptionsUpdated;
} }
public void CheckDisposed() public void CheckDisposed()
@ -157,7 +175,7 @@ namespace Emby.Server.Implementations.Session
/// Gets all connections. /// Gets all connections.
/// </summary> /// </summary>
/// <value>All connections.</value> /// <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) private void OnSessionStarted(SessionInfo info)
{ {
@ -171,20 +189,27 @@ namespace Emby.Server.Implementations.Session
} }
} }
EventHelper.QueueEventIfNotNull(SessionStarted, this, new SessionEventArgs EventHelper.QueueEventIfNotNull(
{ SessionStarted,
SessionInfo = info this,
new SessionEventArgs
}, _logger); {
SessionInfo = info
},
_logger);
} }
private void OnSessionEnded(SessionInfo info) private void OnSessionEnded(SessionInfo info)
{ {
EventHelper.QueueEventIfNotNull(SessionEnded, this, new SessionEventArgs EventHelper.QueueEventIfNotNull(
{ SessionEnded,
SessionInfo = info this,
new SessionEventArgs
{
SessionInfo = info
}, _logger); },
_logger);
info.Dispose(); info.Dispose();
} }
@ -192,9 +217,6 @@ namespace Emby.Server.Implementations.Session
public void UpdateDeviceName(string sessionId, string deviceName) public void UpdateDeviceName(string sessionId, string deviceName)
{ {
var session = GetSession(sessionId); var session = GetSession(sessionId);
var key = GetSessionKey(session.Client, session.DeviceId);
if (session != null) if (session != null)
{ {
session.DeviceName = deviceName; session.DeviceName = deviceName;
@ -210,10 +232,10 @@ namespace Emby.Server.Implementations.Session
/// <param name="deviceName">Name of the device.</param> /// <param name="deviceName">Name of the device.</param>
/// <param name="remoteEndPoint">The remote end point.</param> /// <param name="remoteEndPoint">The remote end point.</param>
/// <param name="user">The user.</param> /// <param name="user">The user.</param>
/// <returns>Task.</returns> /// <returns>SessionInfo.</returns>
/// <exception cref="ArgumentNullException">user</exception> /// <exception cref="ArgumentNullException">user</exception>
/// <exception cref="UnauthorizedAccessException"></exception> public SessionInfo LogSessionActivity(
public SessionInfo LogSessionActivity(string appName, string appName,
string appVersion, string appVersion,
string deviceId, string deviceId,
string deviceName, string deviceName,
@ -226,10 +248,12 @@ namespace Emby.Server.Implementations.Session
{ {
throw new ArgumentNullException(nameof(appName)); throw new ArgumentNullException(nameof(appName));
} }
if (string.IsNullOrEmpty(appVersion)) if (string.IsNullOrEmpty(appVersion))
{ {
throw new ArgumentNullException(nameof(appVersion)); throw new ArgumentNullException(nameof(appVersion));
} }
if (string.IsNullOrEmpty(deviceId)) if (string.IsNullOrEmpty(deviceId))
{ {
throw new ArgumentNullException(nameof(deviceId)); throw new ArgumentNullException(nameof(deviceId));
@ -260,10 +284,12 @@ namespace Emby.Server.Implementations.Session
if ((activityDate - lastActivityDate).TotalSeconds > 10) if ((activityDate - lastActivityDate).TotalSeconds > 10)
{ {
SessionActivity?.Invoke(this, new SessionEventArgs SessionActivity?.Invoke(
{ this,
SessionInfo = session new SessionEventArgs
}); {
SessionInfo = session
});
} }
return session; return session;
@ -304,6 +330,7 @@ namespace Emby.Server.Implementations.Session
/// <summary> /// <summary>
/// Updates the now playing item id. /// Updates the now playing item id.
/// </summary> /// </summary>
/// <returns>Task.</returns>
private async Task UpdateNowPlayingItem(SessionInfo session, PlaybackProgressInfo info, BaseItem libraryItem, bool updateLastCheckInTime) private async Task UpdateNowPlayingItem(SessionInfo session, PlaybackProgressInfo info, BaseItem libraryItem, bool updateLastCheckInTime)
{ {
if (string.IsNullOrEmpty(info.MediaSourceId)) if (string.IsNullOrEmpty(info.MediaSourceId))
@ -418,7 +445,7 @@ namespace Emby.Server.Implementations.Session
}); });
sessionInfo.UserId = user == null ? Guid.Empty : user.Id; 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.UserPrimaryImageTag = user == null ? null : GetImageCacheTag(user, ImageType.Primary);
sessionInfo.RemoteEndPoint = remoteEndPoint; sessionInfo.RemoteEndPoint = remoteEndPoint;
sessionInfo.Client = appName; sessionInfo.Client = appName;
@ -432,7 +459,7 @@ namespace Emby.Server.Implementations.Session
if (user == null) if (user == null)
{ {
sessionInfo.AdditionalUsers = new SessionUserInfo[] { }; sessionInfo.AdditionalUsers = Array.Empty<SessionUserInfo>();
} }
return sessionInfo; return sessionInfo;
@ -449,9 +476,9 @@ namespace Emby.Server.Implementations.Session
ServerId = _appHost.SystemId 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.UserName = username;
sessionInfo.UserPrimaryImageTag = user == null ? null : GetImageCacheTag(user, ImageType.Primary); sessionInfo.UserPrimaryImageTag = user == null ? null : GetImageCacheTag(user, ImageType.Primary);
sessionInfo.RemoteEndPoint = remoteEndPoint; sessionInfo.RemoteEndPoint = remoteEndPoint;
@ -508,6 +535,7 @@ namespace Emby.Server.Implementations.Session
_idleTimer = new Timer(CheckForIdlePlayback, null, TimeSpan.FromMinutes(5), TimeSpan.FromMinutes(5)); _idleTimer = new Timer(CheckForIdlePlayback, null, TimeSpan.FromMinutes(5), TimeSpan.FromMinutes(5));
} }
} }
private void StopIdleCheckTimer() private void StopIdleCheckTimer()
{ {
if (_idleTimer != null) if (_idleTimer != null)
@ -539,9 +567,9 @@ namespace Emby.Server.Implementations.Session
Item = session.NowPlayingItem, Item = session.NowPlayingItem,
ItemId = session.NowPlayingItem == null ? Guid.Empty : session.NowPlayingItem.Id, ItemId = session.NowPlayingItem == null ? Guid.Empty : session.NowPlayingItem.Id,
SessionId = session.Id, SessionId = session.Id,
MediaSourceId = session.PlayState == null ? null : session.PlayState.MediaSourceId, MediaSourceId = session.PlayState?.MediaSourceId,
PositionTicks = session.PlayState == null ? null : session.PlayState.PositionTicks PositionTicks = session.PlayState?.PositionTicks
}); }).ConfigureAwait(false);
} }
catch (Exception ex) catch (Exception ex)
{ {
@ -616,18 +644,22 @@ namespace Emby.Server.Implementations.Session
// Nothing to save here // Nothing to save here
// Fire events to inform plugins // Fire events to inform plugins
EventHelper.QueueEventIfNotNull(PlaybackStart, this, new PlaybackProgressEventArgs EventHelper.QueueEventIfNotNull(
{ PlaybackStart,
Item = libraryItem, this,
Users = users, new PlaybackProgressEventArgs
MediaSourceId = info.MediaSourceId, {
MediaInfo = info.Item, Item = libraryItem,
DeviceName = session.DeviceName, Users = users,
ClientName = session.Client, MediaSourceId = info.MediaSourceId,
DeviceId = session.DeviceId, MediaInfo = info.Item,
Session = session DeviceName = session.DeviceName,
ClientName = session.Client,
DeviceId = session.DeviceId,
Session = session
}, _logger); },
_logger);
StartIdleCheckTimer(); StartIdleCheckTimer();
} }
@ -667,6 +699,7 @@ namespace Emby.Server.Implementations.Session
/// <summary> /// <summary>
/// Used to report playback progress for an item /// Used to report playback progress for an item
/// </summary> /// </summary>
/// <returns>Task.</returns>
public async Task OnPlaybackProgress(PlaybackProgressInfo info, bool isAutomated) public async Task OnPlaybackProgress(PlaybackProgressInfo info, bool isAutomated)
{ {
CheckDisposed(); CheckDisposed();
@ -695,21 +728,23 @@ namespace Emby.Server.Implementations.Session
} }
} }
PlaybackProgress?.Invoke(this, new PlaybackProgressEventArgs PlaybackProgress?.Invoke(
{ this,
Item = libraryItem, new PlaybackProgressEventArgs
Users = users, {
PlaybackPositionTicks = session.PlayState.PositionTicks, Item = libraryItem,
MediaSourceId = session.PlayState.MediaSourceId, Users = users,
MediaInfo = info.Item, PlaybackPositionTicks = session.PlayState.PositionTicks,
DeviceName = session.DeviceName, MediaSourceId = session.PlayState.MediaSourceId,
ClientName = session.Client, MediaInfo = info.Item,
DeviceId = session.DeviceId, DeviceName = session.DeviceName,
IsPaused = info.IsPaused, ClientName = session.Client,
PlaySessionId = info.PlaySessionId, DeviceId = session.DeviceId,
IsAutomated = isAutomated, IsPaused = info.IsPaused,
Session = session PlaySessionId = info.PlaySessionId,
}); IsAutomated = isAutomated,
Session = session
});
if (!isAutomated) if (!isAutomated)
{ {
@ -830,8 +865,7 @@ namespace Emby.Server.Implementations.Session
{ {
MediaSourceInfo mediaSource = null; MediaSourceInfo mediaSource = null;
var hasMediaSources = libraryItem as IHasMediaSources; if (libraryItem is IHasMediaSources hasMediaSources)
if (hasMediaSources != null)
{ {
mediaSource = await GetMediaSource(libraryItem, info.MediaSourceId, info.LiveStreamId).ConfigureAwait(false); 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"; 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.Client,
session.ApplicationVersion, session.ApplicationVersion,
info.Item.Name, info.Item.Name,
@ -887,20 +922,24 @@ namespace Emby.Server.Implementations.Session
} }
} }
EventHelper.QueueEventIfNotNull(PlaybackStopped, this, new PlaybackStopEventArgs EventHelper.QueueEventIfNotNull(
{ PlaybackStopped,
Item = libraryItem, this,
Users = users, new PlaybackStopEventArgs
PlaybackPositionTicks = info.PositionTicks, {
PlayedToCompletion = playedToCompletion, Item = libraryItem,
MediaSourceId = info.MediaSourceId, Users = users,
MediaInfo = info.Item, PlaybackPositionTicks = info.PositionTicks,
DeviceName = session.DeviceName, PlayedToCompletion = playedToCompletion,
ClientName = session.Client, MediaSourceId = info.MediaSourceId,
DeviceId = session.DeviceId, MediaInfo = info.Item,
Session = session DeviceName = session.DeviceName,
ClientName = session.Client,
DeviceId = session.DeviceId,
Session = session
}, _logger); },
_logger);
} }
private bool OnPlaybackStopped(User user, BaseItem item, long? positionTicks, bool playbackFailed) 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="sessionId">The session identifier.</param>
/// <param name="throwOnMissing">if set to <c>true</c> [throw on missing].</param> /// <param name="throwOnMissing">if set to <c>true</c> [throw on missing].</param>
/// <returns>SessionInfo.</returns> /// <returns>SessionInfo.</returns>
/// <exception cref="ResourceNotFoundException"></exception> /// <exception cref="ResourceNotFoundException">sessionId</exception>
private SessionInfo GetSession(string sessionId, bool throwOnMissing = true) 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) if (session == null && throwOnMissing)
{ {
throw new ResourceNotFoundException(string.Format("Session {0} not found.", sessionId)); throw new ResourceNotFoundException(string.Format("Session {0} not found.", sessionId));
@ -952,7 +990,7 @@ namespace Emby.Server.Implementations.Session
private SessionInfo GetSessionToRemoteControl(string sessionId) private SessionInfo GetSessionToRemoteControl(string sessionId)
{ {
// Accept either device id or session id // 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) if (session == null)
{ {
@ -1061,10 +1099,12 @@ namespace Emby.Server.Implementations.Session
var series = episode.Series; var series = episode.Series;
if (series != null) if (series != null)
{ {
var episodes = series.GetEpisodes(user, new DtoOptions(false) var episodes = series.GetEpisodes(
{ user,
EnableImages = false new DtoOptions(false)
}) {
EnableImages = false
})
.Where(i => !i.IsVirtualItem) .Where(i => !i.IsVirtualItem)
.SkipWhile(i => i.Id != episode.Id) .SkipWhile(i => i.Id != episode.Id)
.ToList(); .ToList();
@ -1100,9 +1140,7 @@ namespace Emby.Server.Implementations.Session
return new List<BaseItem>(); return new List<BaseItem>();
} }
var byName = item as IItemByName; if (item is IItemByName byName)
if (byName != null)
{ {
return byName.GetTaggedItems(new InternalItemsQuery(user) return byName.GetTaggedItems(new InternalItemsQuery(user)
{ {
@ -1152,7 +1190,7 @@ namespace Emby.Server.Implementations.Session
if (item == null) 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>(); return new List<BaseItem>();
} }
@ -1163,13 +1201,15 @@ namespace Emby.Server.Implementations.Session
{ {
var generalCommand = new GeneralCommand 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); 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 token = GetAuthorizationToken(user, request.DeviceId, request.App, request.AppVersion, request.DeviceName);
var session = LogSessionActivity(request.App, var session = LogSessionActivity(
request.App,
request.AppVersion, request.AppVersion,
request.DeviceId, request.DeviceId,
request.DeviceName, request.DeviceName,
@ -1454,9 +1495,9 @@ namespace Emby.Server.Implementations.Session
{ {
Logout(auth); 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); ReportCapabilities(session, capabilities, true);
} }
private void ReportCapabilities(SessionInfo session, private void ReportCapabilities(
SessionInfo session,
ClientCapabilities capabilities, ClientCapabilities capabilities,
bool saveCapabilities) bool saveCapabilities)
{ {
@ -1580,10 +1622,12 @@ namespace Emby.Server.Implementations.Session
if (saveCapabilities) if (saveCapabilities)
{ {
CapabilitiesChanged?.Invoke(this, new SessionEventArgs CapabilitiesChanged?.Invoke(
{ this,
SessionInfo = session new SessionEventArgs
}); {
SessionInfo = session
});
try try
{ {

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -53,7 +53,7 @@ namespace MediaBrowser.Providers.Music
var releaseId = searchInfo.GetReleaseId(); var releaseId = searchInfo.GetReleaseId();
var releaseGroupId = searchInfo.GetReleaseGroupId(); var releaseGroupId = searchInfo.GetReleaseGroupId();
string url = null; string url;
var isNameSearch = false; var isNameSearch = false;
bool forceMusicBrainzProper = 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)) using (var oReader = new StreamReader(stream, Encoding.UTF8))
{ {
@ -149,7 +149,7 @@ namespace MediaBrowser.Providers.Music
return result; return result;
}).ToList(); });
} }
} }
} }
@ -301,7 +301,7 @@ namespace MediaBrowser.Providers.Music
public List<ValueTuple<string, string>> Artists = new List<ValueTuple<string, string>>(); 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.MoveToContent();
reader.Read(); 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.MoveToContent();
reader.Read(); reader.Read();
@ -369,7 +367,7 @@ namespace MediaBrowser.Providers.Music
var release = ParseRelease(subReader, releaseId); var release = ParseRelease(subReader, releaseId);
if (release != null) if (release != null)
{ {
list.Add(release); yield return release;
} }
} }
break; break;
@ -386,8 +384,6 @@ namespace MediaBrowser.Providers.Music
reader.Read(); reader.Read();
} }
} }
return list;
} }
private static ReleaseResult ParseRelease(XmlReader reader, string releaseId) private static ReleaseResult ParseRelease(XmlReader reader, string releaseId)
@ -552,7 +548,7 @@ namespace MediaBrowser.Providers.Music
return (null, null); 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.MoveToContent();
reader.Read(); 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) private async Task<string> GetReleaseIdFromReleaseGroupId(string releaseGroupId, CancellationToken cancellationToken)

View file

@ -50,7 +50,7 @@ namespace MediaBrowser.Providers.Music
{ {
using (var stream = response.Content) using (var stream = response.Content)
{ {
var results = GetResultsFromResponse(stream); var results = GetResultsFromResponse(stream).ToList();
if (results.Count > 0) 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)) 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.MoveToContent();
reader.Read(); reader.Read();
@ -159,7 +157,7 @@ namespace MediaBrowser.Providers.Music
var artist = ParseArtist(subReader, mbzId); var artist = ParseArtist(subReader, mbzId);
if (artist != null) if (artist != null)
{ {
list.Add(artist); yield return artist;
} }
} }
break; break;
@ -176,8 +174,6 @@ namespace MediaBrowser.Providers.Music
reader.Read(); reader.Read();
} }
} }
return list;
} }
private RemoteSearchResult ParseArtist(XmlReader reader, string artistId) private RemoteSearchResult ParseArtist(XmlReader reader, string artistId)
@ -277,7 +273,7 @@ namespace MediaBrowser.Providers.Music
/// </summary> /// </summary>
/// <param name="name">The name.</param> /// <param name="name">The name.</param>
/// <returns>System.String.</returns> /// <returns>System.String.</returns>
private string UrlEncode(string name) private static string UrlEncode(string name)
{ {
return WebUtility.UrlEncode(name); return WebUtility.UrlEncode(name);
} }