commit
208585d3f6
|
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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))
|
||||||
|
|
|
@ -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>>
|
||||||
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)
|
||||||
{
|
{
|
||||||
|
|
|
@ -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
|
||||||
{
|
{
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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));
|
||||||
|
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user