mirror of
https://github.com/jellyfin/jellyfin.git
synced 2026-04-20 17:14:42 +01:00
merge branch master into media-attachments
This commit is contained in:
@@ -1,3 +1,5 @@
|
||||
#pragma warning disable CS1591
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
@@ -39,6 +41,19 @@ namespace Emby.Server.Implementations.Activity
|
||||
private readonly IServerApplicationHost _appHost;
|
||||
private readonly IDeviceManager _deviceManager;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ActivityLogEntryPoint"/> class.
|
||||
/// </summary>
|
||||
/// <param name="logger"></param>
|
||||
/// <param name="sessionManager"></param>
|
||||
/// <param name="deviceManager"></param>
|
||||
/// <param name="taskManager"></param>
|
||||
/// <param name="activityManager"></param>
|
||||
/// <param name="localization"></param>
|
||||
/// <param name="installationManager"></param>
|
||||
/// <param name="subManager"></param>
|
||||
/// <param name="userManager"></param>
|
||||
/// <param name="appHost"></param>
|
||||
public ActivityLogEntryPoint(
|
||||
ILogger<ActivityLogEntryPoint> logger,
|
||||
ISessionManager sessionManager,
|
||||
@@ -616,8 +631,8 @@ namespace Emby.Server.Implementations.Activity
|
||||
/// <summary>
|
||||
/// Constructs a string description of a time-span value.
|
||||
/// </summary>
|
||||
/// <param name="value">The value of this item</param>
|
||||
/// <param name="description">The name of this item (singular form)</param>
|
||||
/// <param name="value">The value of this item.</param>
|
||||
/// <param name="description">The name of this item (singular form).</param>
|
||||
private static string CreateValueString(int value, string description)
|
||||
{
|
||||
return string.Format(
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
#pragma warning disable CS1591
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using MediaBrowser.Controller.Library;
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
#pragma warning disable CS1591
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
|
||||
@@ -4,7 +4,6 @@ using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using MediaBrowser.Common.Configuration;
|
||||
using MediaBrowser.Common.Events;
|
||||
using MediaBrowser.Common.Extensions;
|
||||
@@ -16,7 +15,7 @@ using Microsoft.Extensions.Logging;
|
||||
namespace Emby.Server.Implementations.AppBase
|
||||
{
|
||||
/// <summary>
|
||||
/// Class BaseConfigurationManager
|
||||
/// Class BaseConfigurationManager.
|
||||
/// </summary>
|
||||
public abstract class BaseConfigurationManager : IConfigurationManager
|
||||
{
|
||||
@@ -48,7 +47,7 @@ namespace Emby.Server.Implementations.AppBase
|
||||
/// <param name="applicationPaths">The application paths.</param>
|
||||
/// <param name="loggerFactory">The logger factory.</param>
|
||||
/// <param name="xmlSerializer">The XML serializer.</param>
|
||||
/// <param name="fileSystem">The file system</param>
|
||||
/// <param name="fileSystem">The file system.</param>
|
||||
protected BaseConfigurationManager(IApplicationPaths applicationPaths, ILoggerFactory loggerFactory, IXmlSerializer xmlSerializer, IFileSystem fileSystem)
|
||||
{
|
||||
CommonApplicationPaths = applicationPaths;
|
||||
@@ -85,6 +84,7 @@ namespace Emby.Server.Implementations.AppBase
|
||||
/// </summary>
|
||||
/// <value>The logger.</value>
|
||||
protected ILogger Logger { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the XML serializer.
|
||||
/// </summary>
|
||||
@@ -92,13 +92,13 @@ namespace Emby.Server.Implementations.AppBase
|
||||
protected IXmlSerializer XmlSerializer { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the application paths.
|
||||
/// Gets the application paths.
|
||||
/// </summary>
|
||||
/// <value>The application paths.</value>
|
||||
public IApplicationPaths CommonApplicationPaths { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the system configuration.
|
||||
/// Gets or sets the system configuration.
|
||||
/// </summary>
|
||||
/// <value>The configuration.</value>
|
||||
public BaseApplicationConfiguration CommonConfiguration
|
||||
@@ -124,6 +124,7 @@ namespace Emby.Server.Implementations.AppBase
|
||||
return _configuration;
|
||||
}
|
||||
}
|
||||
|
||||
protected set
|
||||
{
|
||||
_configuration = value;
|
||||
@@ -132,6 +133,10 @@ namespace Emby.Server.Implementations.AppBase
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds parts.
|
||||
/// </summary>
|
||||
/// <param name="factories">The configuration factories.</param>
|
||||
public virtual void AddParts(IEnumerable<IConfigurationFactory> factories)
|
||||
{
|
||||
_configurationFactories = factories.ToArray();
|
||||
@@ -173,7 +178,7 @@ namespace Emby.Server.Implementations.AppBase
|
||||
/// Replaces the configuration.
|
||||
/// </summary>
|
||||
/// <param name="newConfiguration">The new configuration.</param>
|
||||
/// <exception cref="ArgumentNullException">newConfiguration</exception>
|
||||
/// <exception cref="ArgumentNullException"><c>newConfiguration</c> is <c>null</c>.</exception>
|
||||
public virtual void ReplaceConfiguration(BaseApplicationConfiguration newConfiguration)
|
||||
{
|
||||
if (newConfiguration == null)
|
||||
@@ -216,7 +221,7 @@ namespace Emby.Server.Implementations.AppBase
|
||||
cachePath = CommonConfiguration.CachePath;
|
||||
}
|
||||
|
||||
Logger.LogInformation("Setting cache path to " + cachePath);
|
||||
Logger.LogInformation("Setting cache path: {Path}", cachePath);
|
||||
((BaseApplicationPaths)CommonApplicationPaths).CachePath = cachePath;
|
||||
}
|
||||
|
||||
@@ -224,7 +229,7 @@ namespace Emby.Server.Implementations.AppBase
|
||||
/// Replaces the cache path.
|
||||
/// </summary>
|
||||
/// <param name="newConfig">The new configuration.</param>
|
||||
/// <exception cref="DirectoryNotFoundException"></exception>
|
||||
/// <exception cref="DirectoryNotFoundException">The new cache path doesn't exist.</exception>
|
||||
private void ValidateCachePath(BaseApplicationConfiguration newConfig)
|
||||
{
|
||||
var newPath = newConfig.CachePath;
|
||||
@@ -235,7 +240,7 @@ namespace Emby.Server.Implementations.AppBase
|
||||
// Validate
|
||||
if (!Directory.Exists(newPath))
|
||||
{
|
||||
throw new FileNotFoundException(
|
||||
throw new DirectoryNotFoundException(
|
||||
string.Format(
|
||||
CultureInfo.InvariantCulture,
|
||||
"{0} does not exist.",
|
||||
@@ -246,6 +251,10 @@ namespace Emby.Server.Implementations.AppBase
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ensures that we have write access to the path.
|
||||
/// </summary>
|
||||
/// <param name="path">The path.</param>
|
||||
protected void EnsureWriteAccess(string path)
|
||||
{
|
||||
var file = Path.Combine(path, Guid.NewGuid().ToString());
|
||||
@@ -258,6 +267,7 @@ namespace Emby.Server.Implementations.AppBase
|
||||
return Path.Combine(CommonApplicationPaths.ConfigurationDirectoryPath, key.ToLowerInvariant() + ".xml");
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public object GetConfiguration(string key)
|
||||
{
|
||||
return _configurations.GetOrAdd(key, k =>
|
||||
@@ -304,6 +314,7 @@ namespace Emby.Server.Implementations.AppBase
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void SaveConfiguration(string key, object configuration)
|
||||
{
|
||||
var configurationStore = GetConfigurationStore(key);
|
||||
@@ -314,8 +325,7 @@ namespace Emby.Server.Implementations.AppBase
|
||||
throw new ArgumentException("Expected configuration type is " + configurationType.Name);
|
||||
}
|
||||
|
||||
var validatingStore = configurationStore as IValidatingConfiguration;
|
||||
if (validatingStore != null)
|
||||
if (configurationStore is IValidatingConfiguration validatingStore)
|
||||
{
|
||||
var currentConfiguration = GetConfiguration(key);
|
||||
|
||||
@@ -341,6 +351,11 @@ namespace Emby.Server.Implementations.AppBase
|
||||
OnNamedConfigurationUpdated(key, configuration);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Event handler for when a named configuration has been updated.
|
||||
/// </summary>
|
||||
/// <param name="key">The key of the configuration.</param>
|
||||
/// <param name="configuration">The old configuration.</param>
|
||||
protected virtual void OnNamedConfigurationUpdated(string key, object configuration)
|
||||
{
|
||||
NamedConfigurationUpdated?.Invoke(this, new ConfigurationUpdateEventArgs
|
||||
@@ -350,6 +365,7 @@ namespace Emby.Server.Implementations.AppBase
|
||||
});
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public Type GetConfigurationType(string key)
|
||||
{
|
||||
return GetConfigurationStore(key)
|
||||
|
||||
@@ -6,13 +6,13 @@ using MediaBrowser.Model.Serialization;
|
||||
namespace Emby.Server.Implementations.AppBase
|
||||
{
|
||||
/// <summary>
|
||||
/// Class ConfigurationHelper
|
||||
/// Class ConfigurationHelper.
|
||||
/// </summary>
|
||||
public static class ConfigurationHelper
|
||||
{
|
||||
/// <summary>
|
||||
/// Reads an xml configuration file from the file system
|
||||
/// It will immediately re-serialize and save if new serialization data is available due to property changes
|
||||
/// It will immediately re-serialize and save if new serialization data is available due to property changes.
|
||||
/// </summary>
|
||||
/// <param name="type">The type.</param>
|
||||
/// <param name="path">The path.</param>
|
||||
|
||||
@@ -88,7 +88,6 @@ using MediaBrowser.Model.Cryptography;
|
||||
using MediaBrowser.Model.Diagnostics;
|
||||
using MediaBrowser.Model.Dlna;
|
||||
using MediaBrowser.Model.Events;
|
||||
using MediaBrowser.Model.Extensions;
|
||||
using MediaBrowser.Model.Globalization;
|
||||
using MediaBrowser.Model.IO;
|
||||
using MediaBrowser.Model.MediaInfo;
|
||||
@@ -110,9 +109,8 @@ using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Http.Extensions;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.DependencyInjection.Extensions;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using ServiceStack;
|
||||
using Microsoft.OpenApi.Models;
|
||||
using OperatingSystem = MediaBrowser.Common.System.OperatingSystem;
|
||||
|
||||
namespace Emby.Server.Implementations
|
||||
@@ -232,7 +230,25 @@ namespace Emby.Server.Implementations
|
||||
}
|
||||
}
|
||||
|
||||
protected IServiceProvider _serviceProvider;
|
||||
/// <summary>
|
||||
/// Gets or sets the service provider.
|
||||
/// </summary>
|
||||
public IServiceProvider ServiceProvider { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the http port for the webhost.
|
||||
/// </summary>
|
||||
public int HttpPort { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the https port for the webhost.
|
||||
/// </summary>
|
||||
public int HttpsPort { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the content root for the webhost.
|
||||
/// </summary>
|
||||
public string ContentRoot { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the server configuration manager.
|
||||
@@ -321,7 +337,7 @@ namespace Emby.Server.Implementations
|
||||
private readonly IConfiguration _configuration;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the installation manager.
|
||||
/// Gets the installation manager.
|
||||
/// </summary>
|
||||
/// <value>The installation manager.</value>
|
||||
protected IInstallationManager InstallationManager { get; private set; }
|
||||
@@ -410,13 +426,17 @@ namespace Emby.Server.Implementations
|
||||
_validAddressResults.Clear();
|
||||
}
|
||||
|
||||
public string ApplicationVersion { get; } = typeof(ApplicationHost).Assembly.GetName().Version.ToString(3);
|
||||
/// <inheritdoc />
|
||||
public Version ApplicationVersion { get; } = typeof(ApplicationHost).Assembly.GetName().Version;
|
||||
|
||||
/// <inheritdoc />
|
||||
public string ApplicationVersionString { get; } = typeof(ApplicationHost).Assembly.GetName().Version.ToString(3);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the current application user agent.
|
||||
/// </summary>
|
||||
/// <value>The application user agent.</value>
|
||||
public string ApplicationUserAgent => Name.Replace(' ', '-') + "/" + ApplicationVersion;
|
||||
public string ApplicationUserAgent => Name.Replace(' ', '-') + "/" + ApplicationVersionString;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the email address for use within a comment section of a user agent field.
|
||||
@@ -452,20 +472,20 @@ namespace Emby.Server.Implementations
|
||||
public string Name => ApplicationProductName;
|
||||
|
||||
/// <summary>
|
||||
/// Creates an instance of type and resolves all constructor dependencies
|
||||
/// Creates an instance of type and resolves all constructor dependencies.
|
||||
/// </summary>
|
||||
/// <param name="type">The type.</param>
|
||||
/// <returns>System.Object.</returns>
|
||||
public object CreateInstance(Type type)
|
||||
=> ActivatorUtilities.CreateInstance(_serviceProvider, type);
|
||||
=> ActivatorUtilities.CreateInstance(ServiceProvider, type);
|
||||
|
||||
/// <summary>
|
||||
/// Creates an instance of type and resolves all constructor dependencies
|
||||
/// Creates an instance of type and resolves all constructor dependencies.
|
||||
/// </summary>
|
||||
/// /// <typeparam name="T">The type.</typeparam>
|
||||
/// <returns>T.</returns>
|
||||
public T CreateInstance<T>()
|
||||
=> ActivatorUtilities.CreateInstance<T>(_serviceProvider);
|
||||
=> ActivatorUtilities.CreateInstance<T>(ServiceProvider);
|
||||
|
||||
/// <summary>
|
||||
/// Creates the instance safe.
|
||||
@@ -477,7 +497,7 @@ namespace Emby.Server.Implementations
|
||||
try
|
||||
{
|
||||
Logger.LogDebug("Creating instance of {Type}", type);
|
||||
return ActivatorUtilities.CreateInstance(_serviceProvider, type);
|
||||
return ActivatorUtilities.CreateInstance(ServiceProvider, type);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@@ -491,12 +511,12 @@ namespace Emby.Server.Implementations
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type</typeparam>
|
||||
/// <returns>``0.</returns>
|
||||
public T Resolve<T>() => _serviceProvider.GetService<T>();
|
||||
public T Resolve<T>() => ServiceProvider.GetService<T>();
|
||||
|
||||
/// <summary>
|
||||
/// Gets the export types.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type</typeparam>
|
||||
/// <typeparam name="T">The type.</typeparam>
|
||||
/// <returns>IEnumerable{Type}.</returns>
|
||||
public IEnumerable<Type> GetExportTypes<T>()
|
||||
{
|
||||
@@ -508,11 +528,12 @@ namespace Emby.Server.Implementations
|
||||
/// <inheritdoc />
|
||||
public IReadOnlyCollection<T> GetExports<T>(bool manageLifetime = true)
|
||||
{
|
||||
// Convert to list so this isn't executed for each iteration
|
||||
var parts = GetExportTypes<T>()
|
||||
.Select(CreateInstanceSafe)
|
||||
.Where(i => i != null)
|
||||
.Cast<T>()
|
||||
.ToList(); // Convert to list so this isn't executed for each iteration
|
||||
.ToList();
|
||||
|
||||
if (manageLifetime)
|
||||
{
|
||||
@@ -607,77 +628,14 @@ namespace Emby.Server.Implementations
|
||||
|
||||
await RegisterResources(serviceCollection).ConfigureAwait(false);
|
||||
|
||||
FindParts();
|
||||
|
||||
string contentRoot = ServerConfigurationManager.Configuration.DashboardSourcePath;
|
||||
if (string.IsNullOrEmpty(contentRoot))
|
||||
ContentRoot = ServerConfigurationManager.Configuration.DashboardSourcePath;
|
||||
if (string.IsNullOrEmpty(ContentRoot))
|
||||
{
|
||||
contentRoot = ServerConfigurationManager.ApplicationPaths.WebPath;
|
||||
}
|
||||
|
||||
var host = new WebHostBuilder()
|
||||
.UseKestrel(options =>
|
||||
{
|
||||
var addresses = ServerConfigurationManager
|
||||
.Configuration
|
||||
.LocalNetworkAddresses
|
||||
.Select(NormalizeConfiguredLocalAddress)
|
||||
.Where(i => i != null)
|
||||
.ToList();
|
||||
if (addresses.Any())
|
||||
{
|
||||
foreach (var address in addresses)
|
||||
{
|
||||
Logger.LogInformation("Kestrel listening on {ipaddr}", address);
|
||||
options.Listen(address, HttpPort);
|
||||
|
||||
if (EnableHttps && Certificate != null)
|
||||
{
|
||||
options.Listen(address, HttpsPort, listenOptions => listenOptions.UseHttps(Certificate));
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger.LogInformation("Kestrel listening on all interfaces");
|
||||
options.ListenAnyIP(HttpPort);
|
||||
|
||||
if (EnableHttps && Certificate != null)
|
||||
{
|
||||
options.ListenAnyIP(HttpsPort, listenOptions => listenOptions.UseHttps(Certificate));
|
||||
}
|
||||
}
|
||||
})
|
||||
.UseContentRoot(contentRoot)
|
||||
.ConfigureServices(services =>
|
||||
{
|
||||
services.AddResponseCompression();
|
||||
services.AddHttpContextAccessor();
|
||||
})
|
||||
.Configure(app =>
|
||||
{
|
||||
app.UseWebSockets();
|
||||
|
||||
app.UseResponseCompression();
|
||||
|
||||
// TODO app.UseMiddleware<WebSocketMiddleware>();
|
||||
app.Use(ExecuteWebsocketHandlerAsync);
|
||||
app.Use(ExecuteHttpHandlerAsync);
|
||||
})
|
||||
.Build();
|
||||
|
||||
try
|
||||
{
|
||||
await host.StartAsync().ConfigureAwait(false);
|
||||
}
|
||||
catch
|
||||
{
|
||||
Logger.LogError("Kestrel failed to start! This is most likely due to an invalid address or port bind - correct your bind configuration in system.xml and try again.");
|
||||
throw;
|
||||
ContentRoot = ServerConfigurationManager.ApplicationPaths.WebPath;
|
||||
}
|
||||
}
|
||||
|
||||
private async Task ExecuteWebsocketHandlerAsync(HttpContext context, Func<Task> next)
|
||||
public async Task ExecuteWebsocketHandlerAsync(HttpContext context, Func<Task> next)
|
||||
{
|
||||
if (!context.WebSockets.IsWebSocketRequest)
|
||||
{
|
||||
@@ -688,7 +646,7 @@ namespace Emby.Server.Implementations
|
||||
await HttpServer.ProcessWebSocketRequest(context).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
private async Task ExecuteHttpHandlerAsync(HttpContext context, Func<Task> next)
|
||||
public async Task ExecuteHttpHandlerAsync(HttpContext context, Func<Task> next)
|
||||
{
|
||||
if (context.WebSockets.IsWebSocketRequest)
|
||||
{
|
||||
@@ -749,7 +707,8 @@ namespace Emby.Server.Implementations
|
||||
|
||||
serviceCollection.AddSingleton(typeof(IStreamHelper), typeof(StreamHelper));
|
||||
|
||||
serviceCollection.AddSingleton(typeof(ICryptoProvider), typeof(CryptographyProvider));
|
||||
var cryptoProvider = new CryptographyProvider();
|
||||
serviceCollection.AddSingleton<ICryptoProvider>(cryptoProvider);
|
||||
|
||||
SocketFactory = new SocketFactory();
|
||||
serviceCollection.AddSingleton(SocketFactory);
|
||||
@@ -788,16 +747,25 @@ namespace Emby.Server.Implementations
|
||||
|
||||
_userRepository = GetUserRepository();
|
||||
|
||||
UserManager = new UserManager(LoggerFactory.CreateLogger<UserManager>(), _userRepository, XmlSerializer, NetworkManager, () => ImageProcessor, () => DtoService, this, JsonSerializer, FileSystemManager);
|
||||
UserManager = new UserManager(
|
||||
LoggerFactory.CreateLogger<UserManager>(),
|
||||
_userRepository,
|
||||
XmlSerializer,
|
||||
NetworkManager,
|
||||
() => ImageProcessor,
|
||||
() => DtoService,
|
||||
this,
|
||||
JsonSerializer,
|
||||
FileSystemManager,
|
||||
cryptoProvider);
|
||||
|
||||
serviceCollection.AddSingleton(UserManager);
|
||||
|
||||
LibraryManager = new LibraryManager(this, LoggerFactory, TaskManager, UserManager, ServerConfigurationManager, UserDataManager, () => LibraryMonitor, FileSystemManager, () => ProviderManager, () => UserViewManager);
|
||||
serviceCollection.AddSingleton(LibraryManager);
|
||||
|
||||
// TODO wtaylor: investigate use of second music manager
|
||||
var musicManager = new MusicManager(LibraryManager);
|
||||
serviceCollection.AddSingleton<IMusicManager>(new MusicManager(LibraryManager));
|
||||
serviceCollection.AddSingleton<IMusicManager>(musicManager);
|
||||
|
||||
LibraryMonitor = new LibraryMonitor(LoggerFactory, LibraryManager, ServerConfigurationManager, FileSystemManager);
|
||||
serviceCollection.AddSingleton(LibraryMonitor);
|
||||
@@ -866,23 +834,20 @@ namespace Emby.Server.Implementations
|
||||
NotificationManager = new NotificationManager(LoggerFactory, UserManager, ServerConfigurationManager);
|
||||
serviceCollection.AddSingleton(NotificationManager);
|
||||
|
||||
serviceCollection.AddSingleton<IDeviceDiscovery>(
|
||||
new DeviceDiscovery(LoggerFactory, ServerConfigurationManager, SocketFactory));
|
||||
serviceCollection.AddSingleton<IDeviceDiscovery>(new DeviceDiscovery(ServerConfigurationManager));
|
||||
|
||||
ChapterManager = new ChapterManager(LibraryManager, LoggerFactory, ServerConfigurationManager, ItemRepository);
|
||||
serviceCollection.AddSingleton(ChapterManager);
|
||||
|
||||
MediaEncoder = new MediaBrowser.MediaEncoding.Encoder.MediaEncoder(
|
||||
LoggerFactory,
|
||||
JsonSerializer,
|
||||
StartupOptions.FFmpegPath,
|
||||
LoggerFactory.CreateLogger<MediaBrowser.MediaEncoding.Encoder.MediaEncoder>(),
|
||||
ServerConfigurationManager,
|
||||
FileSystemManager,
|
||||
() => SubtitleEncoder,
|
||||
() => MediaSourceManager,
|
||||
ProcessFactory,
|
||||
5000,
|
||||
LocalizationManager);
|
||||
LocalizationManager,
|
||||
() => SubtitleEncoder,
|
||||
_configuration,
|
||||
StartupOptions.FFmpegPath);
|
||||
serviceCollection.AddSingleton(MediaEncoder);
|
||||
|
||||
EncodingManager = new MediaEncoder.EncodingManager(FileSystemManager, LoggerFactory, MediaEncoder, ChapterManager, LibraryManager);
|
||||
@@ -896,13 +861,22 @@ namespace Emby.Server.Implementations
|
||||
serviceCollection.AddSingleton<IAuthorizationContext>(authContext);
|
||||
serviceCollection.AddSingleton<ISessionContext>(new SessionContext(UserManager, authContext, SessionManager));
|
||||
|
||||
AuthService = new AuthService(authContext, ServerConfigurationManager, SessionManager, NetworkManager);
|
||||
AuthService = new AuthService(LoggerFactory.CreateLogger<AuthService>(), authContext, ServerConfigurationManager, SessionManager, NetworkManager);
|
||||
serviceCollection.AddSingleton(AuthService);
|
||||
|
||||
SubtitleEncoder = new MediaBrowser.MediaEncoding.Subtitles.SubtitleEncoder(LibraryManager, LoggerFactory, ApplicationPaths, FileSystemManager, MediaEncoder, JsonSerializer, HttpClient, MediaSourceManager, ProcessFactory);
|
||||
SubtitleEncoder = new MediaBrowser.MediaEncoding.Subtitles.SubtitleEncoder(
|
||||
LibraryManager,
|
||||
LoggerFactory.CreateLogger<MediaBrowser.MediaEncoding.Subtitles.SubtitleEncoder>(),
|
||||
ApplicationPaths,
|
||||
FileSystemManager,
|
||||
MediaEncoder,
|
||||
HttpClient,
|
||||
MediaSourceManager,
|
||||
ProcessFactory);
|
||||
serviceCollection.AddSingleton(SubtitleEncoder);
|
||||
|
||||
serviceCollection.AddSingleton(typeof(IResourceFileManager), typeof(ResourceFileManager));
|
||||
serviceCollection.AddSingleton<EncodingHelper>();
|
||||
|
||||
serviceCollection.AddSingleton(typeof(MediaBrowser.Controller.MediaEncoding.IAttachmentExtractor),typeof(MediaBrowser.MediaEncoding.Attachments.AttachmentExtractor));
|
||||
|
||||
@@ -917,8 +891,6 @@ namespace Emby.Server.Implementations
|
||||
((UserDataManager)UserDataManager).Repository = userDataRepo;
|
||||
ItemRepository.Initialize(userDataRepo, UserManager);
|
||||
((LibraryManager)LibraryManager).ItemRepository = ItemRepository;
|
||||
|
||||
_serviceProvider = serviceCollection.BuildServiceProvider();
|
||||
}
|
||||
|
||||
public static void LogEnvironmentInfo(ILogger logger, IApplicationPaths appPaths)
|
||||
@@ -1009,7 +981,7 @@ namespace Emby.Server.Implementations
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Dirty hacks
|
||||
/// Dirty hacks.
|
||||
/// </summary>
|
||||
private void SetStaticProperties()
|
||||
{
|
||||
@@ -1075,9 +1047,9 @@ namespace Emby.Server.Implementations
|
||||
/// <summary>
|
||||
/// Finds the parts.
|
||||
/// </summary>
|
||||
protected void FindParts()
|
||||
public void FindParts()
|
||||
{
|
||||
InstallationManager = _serviceProvider.GetService<IInstallationManager>();
|
||||
InstallationManager = ServiceProvider.GetService<IInstallationManager>();
|
||||
InstallationManager.PluginInstalled += PluginInstalled;
|
||||
|
||||
if (!ServerConfigurationManager.Configuration.IsPortAuthorized)
|
||||
@@ -1206,7 +1178,7 @@ namespace Emby.Server.Implementations
|
||||
|
||||
private CertificateInfo CertificateInfo { get; set; }
|
||||
|
||||
protected X509Certificate2 Certificate { get; private set; }
|
||||
public X509Certificate2 Certificate { get; private set; }
|
||||
|
||||
private IEnumerable<string> GetUrlPrefixes()
|
||||
{
|
||||
@@ -1417,17 +1389,18 @@ namespace Emby.Server.Implementations
|
||||
/// <summary>
|
||||
/// Gets the system status.
|
||||
/// </summary>
|
||||
/// <param name="cancellationToken">The cancellation token</param>
|
||||
/// <param name="cancellationToken">The cancellation token.</param>
|
||||
/// <returns>SystemInfo.</returns>
|
||||
public async Task<SystemInfo> GetSystemInfo(CancellationToken cancellationToken)
|
||||
{
|
||||
var localAddress = await GetLocalApiUrl(cancellationToken).ConfigureAwait(false);
|
||||
var transcodingTempPath = ConfigurationManager.GetTranscodePath();
|
||||
|
||||
return new SystemInfo
|
||||
{
|
||||
HasPendingRestart = HasPendingRestart,
|
||||
IsShuttingDown = IsShuttingDown,
|
||||
Version = ApplicationVersion,
|
||||
Version = ApplicationVersionString,
|
||||
WebSocketPortNumber = HttpPort,
|
||||
CompletedInstallations = InstallationManager.CompletedInstallations.ToArray(),
|
||||
Id = SystemId,
|
||||
@@ -1445,7 +1418,7 @@ namespace Emby.Server.Implementations
|
||||
CanSelfRestart = CanSelfRestart,
|
||||
CanLaunchWebBrowser = CanLaunchWebBrowser,
|
||||
HasUpdateAvailable = HasUpdateAvailable,
|
||||
TranscodingTempPath = ApplicationPaths.TranscodingTempPath,
|
||||
TranscodingTempPath = transcodingTempPath,
|
||||
ServerName = FriendlyName,
|
||||
LocalAddress = localAddress,
|
||||
SupportsLibraryMonitor = true,
|
||||
@@ -1467,7 +1440,7 @@ namespace Emby.Server.Implementations
|
||||
|
||||
return new PublicSystemInfo
|
||||
{
|
||||
Version = ApplicationVersion,
|
||||
Version = ApplicationVersionString,
|
||||
ProductName = ApplicationProductName,
|
||||
Id = SystemId,
|
||||
OperatingSystem = OperatingSystem.Id.ToString(),
|
||||
@@ -1590,7 +1563,7 @@ namespace Emby.Server.Implementations
|
||||
return resultList;
|
||||
}
|
||||
|
||||
private IPAddress NormalizeConfiguredLocalAddress(string address)
|
||||
public IPAddress NormalizeConfiguredLocalAddress(string address)
|
||||
{
|
||||
var index = address.Trim('/').IndexOf('/');
|
||||
|
||||
@@ -1666,10 +1639,6 @@ namespace Emby.Server.Implementations
|
||||
? Environment.MachineName
|
||||
: ServerConfigurationManager.Configuration.ServerName;
|
||||
|
||||
public int HttpPort { get; private set; }
|
||||
|
||||
public int HttpsPort { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Shuts down.
|
||||
/// </summary>
|
||||
@@ -1732,7 +1701,7 @@ namespace Emby.Server.Implementations
|
||||
/// dns is prefixed with a valid Uri prefix.
|
||||
/// </summary>
|
||||
/// <param name="externalDns">The external dns prefix to get the hostname of.</param>
|
||||
/// <returns>The hostname in <paramref name="externalDns"/></returns>
|
||||
/// <returns>The hostname in <paramref name="externalDns"/>.</returns>
|
||||
private static string GetHostnameFromExternalDns(string externalDns)
|
||||
{
|
||||
if (string.IsNullOrEmpty(externalDns))
|
||||
@@ -1846,6 +1815,7 @@ namespace Emby.Server.Implementations
|
||||
internal class CertificateInfo
|
||||
{
|
||||
public string Path { get; set; }
|
||||
|
||||
public string Password { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,15 +10,10 @@ using SharpCompress.Readers.Zip;
|
||||
namespace Emby.Server.Implementations.Archiving
|
||||
{
|
||||
/// <summary>
|
||||
/// Class DotNetZipClient
|
||||
/// Class DotNetZipClient.
|
||||
/// </summary>
|
||||
public class ZipClient : IZipClient
|
||||
{
|
||||
public ZipClient()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Extracts all.
|
||||
/// </summary>
|
||||
@@ -144,7 +139,6 @@ namespace Emby.Server.Implementations.Archiving
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Extracts all from tar.
|
||||
/// </summary>
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
#pragma warning disable CS1591
|
||||
|
||||
using System.Collections.Generic;
|
||||
using MediaBrowser.Common.Configuration;
|
||||
using MediaBrowser.Model.Branding;
|
||||
|
||||
@@ -4,7 +4,7 @@ using MediaBrowser.Controller;
|
||||
namespace Emby.Server.Implementations.Browser
|
||||
{
|
||||
/// <summary>
|
||||
/// Class BrowserLauncher
|
||||
/// Class BrowserLauncher.
|
||||
/// </summary>
|
||||
public static class BrowserLauncher
|
||||
{
|
||||
@@ -32,6 +32,7 @@ namespace Emby.Server.Implementations.Browser
|
||||
/// <summary>
|
||||
/// Opens the URL.
|
||||
/// </summary>
|
||||
/// <param name="appHost">The application host instance.</param>
|
||||
/// <param name="url">The URL.</param>
|
||||
private static void OpenUrl(IServerApplicationHost appHost, string url)
|
||||
{
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
#pragma warning disable CS1591
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
@@ -13,11 +15,16 @@ namespace Emby.Server.Implementations.Channels
|
||||
{
|
||||
private readonly ChannelManager _channelManager;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ChannelDynamicMediaSourceProvider"/> class.
|
||||
/// </summary>
|
||||
/// <param name="channelManager">The channel manager.</param>
|
||||
public ChannelDynamicMediaSourceProvider(IChannelManager channelManager)
|
||||
{
|
||||
_channelManager = (ChannelManager)channelManager;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task<IEnumerable<MediaSourceInfo>> GetMediaSources(BaseItem item, CancellationToken cancellationToken)
|
||||
{
|
||||
if (item.SourceType == SourceType.Channel)
|
||||
@@ -28,6 +35,7 @@ namespace Emby.Server.Implementations.Channels
|
||||
return Task.FromResult<IEnumerable<MediaSourceInfo>>(new List<MediaSourceInfo>());
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task<ILiveStream> OpenMediaSource(string openToken, List<ILiveStream> currentLiveStreams, CancellationToken cancellationToken)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
#pragma warning disable CS1591
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
#pragma warning disable CS1591
|
||||
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
@@ -473,7 +475,7 @@ namespace Emby.Server.Implementations.Channels
|
||||
await item.RefreshMetadata(new MetadataRefreshOptions(new DirectoryService(_fileSystem))
|
||||
{
|
||||
ForceSave = !isNew && forceUpdate
|
||||
}, cancellationToken);
|
||||
}, cancellationToken).ConfigureAwait(false);
|
||||
|
||||
return item;
|
||||
}
|
||||
@@ -510,7 +512,7 @@ namespace Emby.Server.Implementations.Channels
|
||||
return _libraryManager.GetItemIds(new InternalItemsQuery
|
||||
{
|
||||
IncludeItemTypes = new[] { typeof(Channel).Name },
|
||||
OrderBy = new ValueTuple<string, SortOrder>[] { new ValueTuple<string, SortOrder>(ItemSortBy.SortName, SortOrder.Ascending) }
|
||||
OrderBy = new[] { (ItemSortBy.SortName, SortOrder.Ascending) }
|
||||
|
||||
}).Select(i => GetChannelFeatures(i.ToString("N", CultureInfo.InvariantCulture))).ToArray();
|
||||
}
|
||||
@@ -618,16 +620,16 @@ namespace Emby.Server.Implementations.Channels
|
||||
{
|
||||
query.OrderBy = new[]
|
||||
{
|
||||
new ValueTuple<string, SortOrder>(ItemSortBy.PremiereDate, SortOrder.Descending),
|
||||
new ValueTuple<string, SortOrder>(ItemSortBy.ProductionYear, SortOrder.Descending),
|
||||
new ValueTuple<string, SortOrder>(ItemSortBy.DateCreated, SortOrder.Descending)
|
||||
(ItemSortBy.PremiereDate, SortOrder.Descending),
|
||||
(ItemSortBy.ProductionYear, SortOrder.Descending),
|
||||
(ItemSortBy.DateCreated, SortOrder.Descending)
|
||||
};
|
||||
}
|
||||
else
|
||||
{
|
||||
query.OrderBy = new[]
|
||||
{
|
||||
new ValueTuple<string, SortOrder>(ItemSortBy.DateCreated, SortOrder.Descending)
|
||||
(ItemSortBy.DateCreated, SortOrder.Descending)
|
||||
};
|
||||
}
|
||||
|
||||
@@ -636,7 +638,7 @@ namespace Emby.Server.Implementations.Channels
|
||||
|
||||
private async Task RefreshLatestChannelItems(IChannel channel, CancellationToken cancellationToken)
|
||||
{
|
||||
var internalChannel = await GetChannel(channel, cancellationToken);
|
||||
var internalChannel = await GetChannel(channel, cancellationToken).ConfigureAwait(false);
|
||||
|
||||
var query = new InternalItemsQuery();
|
||||
query.Parent = internalChannel;
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
#pragma warning disable CS1591
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
#pragma warning disable CS1591
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
#pragma warning disable CS1591
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
@@ -76,7 +78,6 @@ namespace Emby.Server.Implementations.Collections
|
||||
.Where(i => i != null)
|
||||
.GroupBy(x => x.Id)
|
||||
.Select(x => x.First())
|
||||
.OrderBy(i => Guid.NewGuid())
|
||||
.ToList();
|
||||
}
|
||||
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
#pragma warning disable CS1591
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
@@ -29,11 +31,7 @@ namespace Emby.Server.Implementations.Collections
|
||||
private readonly ILogger _logger;
|
||||
private readonly IProviderManager _providerManager;
|
||||
private readonly ILocalizationManager _localizationManager;
|
||||
private IApplicationPaths _appPaths;
|
||||
|
||||
public event EventHandler<CollectionCreatedEventArgs> CollectionCreated;
|
||||
public event EventHandler<CollectionModifiedEventArgs> ItemsAddedToCollection;
|
||||
public event EventHandler<CollectionModifiedEventArgs> ItemsRemovedFromCollection;
|
||||
private readonly IApplicationPaths _appPaths;
|
||||
|
||||
public CollectionManager(
|
||||
ILibraryManager libraryManager,
|
||||
@@ -53,6 +51,10 @@ namespace Emby.Server.Implementations.Collections
|
||||
_appPaths = appPaths;
|
||||
}
|
||||
|
||||
public event EventHandler<CollectionCreatedEventArgs> CollectionCreated;
|
||||
public event EventHandler<CollectionModifiedEventArgs> ItemsAddedToCollection;
|
||||
public event EventHandler<CollectionModifiedEventArgs> ItemsRemovedFromCollection;
|
||||
|
||||
private IEnumerable<Folder> FindFolders(string path)
|
||||
{
|
||||
return _libraryManager
|
||||
@@ -121,7 +123,7 @@ namespace Emby.Server.Implementations.Collections
|
||||
// This could cause it to get re-resolved as a plain folder
|
||||
var folderName = _fileSystem.GetValidFilename(name) + " [boxset]";
|
||||
|
||||
var parentFolder = GetCollectionsFolder(true).Result;
|
||||
var parentFolder = GetCollectionsFolder(true).GetAwaiter().GetResult();
|
||||
|
||||
if (parentFolder == null)
|
||||
{
|
||||
@@ -339,11 +341,11 @@ namespace Emby.Server.Implementations.Collections
|
||||
}
|
||||
}
|
||||
|
||||
public class CollectionManagerEntryPoint : IServerEntryPoint
|
||||
public sealed class CollectionManagerEntryPoint : IServerEntryPoint
|
||||
{
|
||||
private readonly CollectionManager _collectionManager;
|
||||
private readonly IServerConfigurationManager _config;
|
||||
private ILogger _logger;
|
||||
private readonly ILogger _logger;
|
||||
|
||||
public CollectionManagerEntryPoint(ICollectionManager collectionManager, IServerConfigurationManager config, ILogger logger)
|
||||
{
|
||||
@@ -352,6 +354,7 @@ namespace Emby.Server.Implementations.Collections
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task RunAsync()
|
||||
{
|
||||
if (!_config.Configuration.CollectionsUpgraded && _config.Configuration.IsStartupWizardCompleted)
|
||||
@@ -375,39 +378,10 @@ namespace Emby.Server.Implementations.Collections
|
||||
}
|
||||
}
|
||||
|
||||
#region IDisposable Support
|
||||
private bool disposedValue = false; // To detect redundant calls
|
||||
|
||||
protected virtual void Dispose(bool disposing)
|
||||
{
|
||||
if (!disposedValue)
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
// TODO: dispose managed state (managed objects).
|
||||
}
|
||||
|
||||
// TODO: free unmanaged resources (unmanaged objects) and override a finalizer below.
|
||||
// TODO: set large fields to null.
|
||||
|
||||
disposedValue = true;
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: override a finalizer only if Dispose(bool disposing) above has code to free unmanaged resources.
|
||||
// ~CollectionManagerEntryPoint() {
|
||||
// // Do not change this code. Put cleanup code in Dispose(bool disposing) above.
|
||||
// Dispose(false);
|
||||
// }
|
||||
|
||||
// This code added to correctly implement the disposable pattern.
|
||||
/// <inheritdoc />
|
||||
public void Dispose()
|
||||
{
|
||||
// Do not change this code. Put cleanup code in Dispose(bool disposing) above.
|
||||
Dispose(true);
|
||||
// TODO: uncomment the following line if the finalizer is overridden above.
|
||||
// GC.SuppressFinalize(this);
|
||||
// Nothing to dispose
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using Emby.Server.Implementations.AppBase;
|
||||
using MediaBrowser.Common.Configuration;
|
||||
@@ -14,11 +15,10 @@ using Microsoft.Extensions.Logging;
|
||||
namespace Emby.Server.Implementations.Configuration
|
||||
{
|
||||
/// <summary>
|
||||
/// Class ServerConfigurationManager
|
||||
/// Class ServerConfigurationManager.
|
||||
/// </summary>
|
||||
public class ServerConfigurationManager : BaseConfigurationManager, IServerConfigurationManager
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ServerConfigurationManager" /> class.
|
||||
/// </summary>
|
||||
@@ -32,6 +32,9 @@ namespace Emby.Server.Implementations.Configuration
|
||||
UpdateMetadataPath();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Configuration updating event.
|
||||
/// </summary>
|
||||
public event EventHandler<GenericEventArgs<ServerConfiguration>> ConfigurationUpdating;
|
||||
|
||||
/// <summary>
|
||||
@@ -62,13 +65,6 @@ namespace Emby.Server.Implementations.Configuration
|
||||
base.OnConfigurationUpdated();
|
||||
}
|
||||
|
||||
public override void AddParts(IEnumerable<IConfigurationFactory> factories)
|
||||
{
|
||||
base.AddParts(factories);
|
||||
|
||||
UpdateTranscodePath();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the metadata path.
|
||||
/// </summary>
|
||||
@@ -84,28 +80,6 @@ namespace Emby.Server.Implementations.Configuration
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the transcoding temporary path.
|
||||
/// </summary>
|
||||
private void UpdateTranscodePath()
|
||||
{
|
||||
var encodingConfig = this.GetConfiguration<EncodingOptions>("encoding");
|
||||
|
||||
((ServerApplicationPaths)ApplicationPaths).TranscodingTempPath = string.IsNullOrEmpty(encodingConfig.TranscodingTempPath) ?
|
||||
null :
|
||||
Path.Combine(encodingConfig.TranscodingTempPath, "transcodes");
|
||||
}
|
||||
|
||||
protected override void OnNamedConfigurationUpdated(string key, object configuration)
|
||||
{
|
||||
base.OnNamedConfigurationUpdated(key, configuration);
|
||||
|
||||
if (string.Equals(key, "encoding", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
UpdateTranscodePath();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Replaces the configuration.
|
||||
/// </summary>
|
||||
@@ -123,12 +97,11 @@ namespace Emby.Server.Implementations.Configuration
|
||||
base.ReplaceConfiguration(newConfiguration);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Validates the SSL certificate.
|
||||
/// </summary>
|
||||
/// <param name="newConfig">The new configuration.</param>
|
||||
/// <exception cref="DirectoryNotFoundException"></exception>
|
||||
/// <exception cref="FileNotFoundException">The certificate path doesn't exist.</exception>
|
||||
private void ValidateSslCertificate(BaseApplicationConfiguration newConfig)
|
||||
{
|
||||
var serverConfig = (ServerConfiguration)newConfig;
|
||||
@@ -136,12 +109,16 @@ namespace Emby.Server.Implementations.Configuration
|
||||
var newPath = serverConfig.CertificatePath;
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(newPath)
|
||||
&& !string.Equals(Configuration.CertificatePath ?? string.Empty, newPath))
|
||||
&& !string.Equals(Configuration.CertificatePath, newPath, StringComparison.Ordinal))
|
||||
{
|
||||
// Validate
|
||||
if (!File.Exists(newPath))
|
||||
{
|
||||
throw new FileNotFoundException(string.Format("Certificate file '{0}' does not exist.", newPath));
|
||||
throw new FileNotFoundException(
|
||||
string.Format(
|
||||
CultureInfo.InvariantCulture,
|
||||
"Certificate file '{0}' does not exist.",
|
||||
newPath));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -150,24 +127,32 @@ namespace Emby.Server.Implementations.Configuration
|
||||
/// Validates the metadata path.
|
||||
/// </summary>
|
||||
/// <param name="newConfig">The new configuration.</param>
|
||||
/// <exception cref="DirectoryNotFoundException"></exception>
|
||||
/// <exception cref="DirectoryNotFoundException">The new config path doesn't exist.</exception>
|
||||
private void ValidateMetadataPath(ServerConfiguration newConfig)
|
||||
{
|
||||
var newPath = newConfig.MetadataPath;
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(newPath)
|
||||
&& !string.Equals(Configuration.MetadataPath ?? string.Empty, newPath))
|
||||
&& !string.Equals(Configuration.MetadataPath, newPath, StringComparison.Ordinal))
|
||||
{
|
||||
// Validate
|
||||
if (!Directory.Exists(newPath))
|
||||
{
|
||||
throw new FileNotFoundException(string.Format("{0} does not exist.", newPath));
|
||||
throw new DirectoryNotFoundException(
|
||||
string.Format(
|
||||
CultureInfo.InvariantCulture,
|
||||
"{0} does not exist.",
|
||||
newPath));
|
||||
}
|
||||
|
||||
EnsureWriteAccess(newPath);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets all configuration values to their optimal values.
|
||||
/// </summary>
|
||||
/// <returns>If the configuration changed.</returns>
|
||||
public bool SetOptimalValues()
|
||||
{
|
||||
var config = Configuration;
|
||||
|
||||
@@ -1,13 +1,16 @@
|
||||
using System.Collections.Generic;
|
||||
using static MediaBrowser.Controller.Extensions.ConfigurationExtensions;
|
||||
|
||||
namespace Emby.Server.Implementations
|
||||
{
|
||||
public static class ConfigurationOptions
|
||||
{
|
||||
public static readonly Dictionary<string, string> Configuration = new Dictionary<string, string>
|
||||
public static Dictionary<string, string> Configuration => new Dictionary<string, string>
|
||||
{
|
||||
{ "HttpListenerHost:DefaultRedirectPath", "web/index.html" },
|
||||
{ "MusicBrainz:BaseUrl", "https://www.musicbrainz.org" }
|
||||
{ "MusicBrainz:BaseUrl", "https://www.musicbrainz.org" },
|
||||
{ FfmpegProbeSizeKey, "1G" },
|
||||
{ FfmpegAnalyzeDurationKey, "200M" }
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,9 @@ using static MediaBrowser.Common.Cryptography.Constants;
|
||||
|
||||
namespace Emby.Server.Implementations.Cryptography
|
||||
{
|
||||
/// <summary>
|
||||
/// Class providing abstractions over cryptographic functions.
|
||||
/// </summary>
|
||||
public class CryptographyProvider : ICryptoProvider, IDisposable
|
||||
{
|
||||
private static readonly HashSet<string> _supportedHashMethods = new HashSet<string>()
|
||||
@@ -30,6 +33,9 @@ namespace Emby.Server.Implementations.Cryptography
|
||||
|
||||
private bool _disposed = false;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="CryptographyProvider"/> class.
|
||||
/// </summary>
|
||||
public CryptographyProvider()
|
||||
{
|
||||
// FIXME: When we get DotNet Standard 2.1 we need to revisit how we do the crypto
|
||||
@@ -39,8 +45,10 @@ namespace Emby.Server.Implementations.Cryptography
|
||||
_randomNumberGenerator = RandomNumberGenerator.Create();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public string DefaultHashMethod => "PBKDF2";
|
||||
|
||||
/// <inheritdoc />
|
||||
public IEnumerable<string> GetSupportedHashMethods()
|
||||
=> _supportedHashMethods;
|
||||
|
||||
@@ -59,12 +67,7 @@ namespace Emby.Server.Implementations.Cryptography
|
||||
throw new CryptographicException($"Cannot currently use PBKDF2 with requested hash method: {method}");
|
||||
}
|
||||
|
||||
public byte[] ComputeHash(string hashMethod, byte[] bytes)
|
||||
=> ComputeHash(hashMethod, bytes, Array.Empty<byte>());
|
||||
|
||||
public byte[] ComputeHashWithDefaultMethod(byte[] bytes)
|
||||
=> ComputeHash(DefaultHashMethod, bytes);
|
||||
|
||||
/// <inheritdoc />
|
||||
public byte[] ComputeHash(string hashMethod, byte[] bytes, byte[] salt)
|
||||
{
|
||||
if (hashMethod == DefaultHashMethod)
|
||||
@@ -90,15 +93,17 @@ namespace Emby.Server.Implementations.Cryptography
|
||||
}
|
||||
|
||||
throw new CryptographicException($"Requested hash method is not supported: {hashMethod}");
|
||||
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public byte[] ComputeHashWithDefaultMethod(byte[] bytes, byte[] salt)
|
||||
=> PBKDF2(DefaultHashMethod, bytes, salt, DefaultIterations);
|
||||
|
||||
/// <inheritdoc />
|
||||
public byte[] GenerateSalt()
|
||||
=> GenerateSalt(DefaultSaltLength);
|
||||
|
||||
/// <inheritdoc />
|
||||
public byte[] GenerateSalt(int length)
|
||||
{
|
||||
byte[] salt = new byte[length];
|
||||
@@ -113,6 +118,10 @@ namespace Emby.Server.Implementations.Cryptography
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Releases unmanaged and - optionally - managed resources.
|
||||
/// </summary>
|
||||
/// <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 disposing)
|
||||
{
|
||||
if (_disposed)
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
#pragma warning disable CS1591
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
@@ -11,6 +13,10 @@ namespace Emby.Server.Implementations.Data
|
||||
{
|
||||
private bool _disposed = false;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="BaseSqliteRepository"/> class.
|
||||
/// </summary>
|
||||
/// <param name="logger">The logger.</param>
|
||||
protected BaseSqliteRepository(ILogger logger)
|
||||
{
|
||||
Logger = logger;
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
#pragma warning disable CS1591
|
||||
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
#pragma warning disable CS1591
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
#pragma warning disable CS1591
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
#pragma warning disable CS1591
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
|
||||
@@ -3,12 +3,11 @@ using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Runtime.Serialization;
|
||||
using System.Text;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
using System.Threading;
|
||||
using Emby.Server.Implementations.Playlists;
|
||||
using MediaBrowser.Common.Extensions;
|
||||
using MediaBrowser.Common.Json;
|
||||
using MediaBrowser.Controller;
|
||||
using MediaBrowser.Controller.Channels;
|
||||
@@ -28,7 +27,6 @@ using MediaBrowser.Model.Entities;
|
||||
using MediaBrowser.Model.Globalization;
|
||||
using MediaBrowser.Model.LiveTv;
|
||||
using MediaBrowser.Model.Querying;
|
||||
using MediaBrowser.Model.Serialization;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using SQLitePCL.pretty;
|
||||
|
||||
@@ -689,12 +687,14 @@ namespace Emby.Server.Implementations.Data
|
||||
|
||||
private void SaveItem(BaseItem item, BaseItem topParent, string userDataKey, IStatement saveItemStatement)
|
||||
{
|
||||
saveItemStatement.TryBind("@guid", item.Id);
|
||||
saveItemStatement.TryBind("@type", item.GetType().FullName);
|
||||
Type type = item.GetType();
|
||||
|
||||
if (TypeRequiresDeserialization(item.GetType()))
|
||||
saveItemStatement.TryBind("@guid", item.Id);
|
||||
saveItemStatement.TryBind("@type", type.FullName);
|
||||
|
||||
if (TypeRequiresDeserialization(type))
|
||||
{
|
||||
saveItemStatement.TryBind("@data", JsonSerializer.SerializeToUtf8Bytes(item, _jsonOptions));
|
||||
saveItemStatement.TryBind("@data", JsonSerializer.SerializeToUtf8Bytes(item, type, _jsonOptions));
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -1208,7 +1208,7 @@ namespace Emby.Server.Implementations.Data
|
||||
{
|
||||
if (id == Guid.Empty)
|
||||
{
|
||||
throw new ArgumentException(nameof(id), "Guid can't be empty");
|
||||
throw new ArgumentException("Guid can't be empty", nameof(id));
|
||||
}
|
||||
|
||||
CheckDisposed();
|
||||
@@ -2862,8 +2862,8 @@ namespace Emby.Server.Implementations.Data
|
||||
BindSimilarParams(query, statement);
|
||||
BindSearchParams(query, statement);
|
||||
|
||||
// Running this again will bind the params
|
||||
GetWhereClauses(query, statement);
|
||||
// Running this again will bind the params
|
||||
GetWhereClauses(query, statement);
|
||||
|
||||
var hasEpisodeAttributes = HasEpisodeAttributes(query);
|
||||
var hasServiceName = HasServiceName(query);
|
||||
@@ -2912,14 +2912,14 @@ namespace Emby.Server.Implementations.Data
|
||||
|
||||
private string GetOrderByText(InternalItemsQuery query)
|
||||
{
|
||||
var orderBy = query.OrderBy;
|
||||
if (string.IsNullOrEmpty(query.SearchTerm))
|
||||
{
|
||||
int oldLen = query.OrderBy.Length;
|
||||
|
||||
if (query.SimilarTo != null && oldLen == 0)
|
||||
int oldLen = orderBy.Count;
|
||||
if (oldLen == 0 && query.SimilarTo != null)
|
||||
{
|
||||
var arr = new (string, SortOrder)[oldLen + 2];
|
||||
query.OrderBy.CopyTo(arr, 0);
|
||||
orderBy.CopyTo(arr, 0);
|
||||
arr[oldLen] = ("SimilarityScore", SortOrder.Descending);
|
||||
arr[oldLen + 1] = (ItemSortBy.Random, SortOrder.Ascending);
|
||||
query.OrderBy = arr;
|
||||
@@ -2927,16 +2927,15 @@ namespace Emby.Server.Implementations.Data
|
||||
}
|
||||
else
|
||||
{
|
||||
query.OrderBy = new []
|
||||
query.OrderBy = new[]
|
||||
{
|
||||
("SearchScore", SortOrder.Descending),
|
||||
(ItemSortBy.SortName, SortOrder.Ascending)
|
||||
};
|
||||
}
|
||||
|
||||
var orderBy = query.OrderBy;
|
||||
|
||||
if (orderBy.Length == 0)
|
||||
if (orderBy.Count == 0)
|
||||
{
|
||||
return string.Empty;
|
||||
}
|
||||
@@ -2944,14 +2943,8 @@ namespace Emby.Server.Implementations.Data
|
||||
return " ORDER BY " + string.Join(",", orderBy.Select(i =>
|
||||
{
|
||||
var columnMap = MapOrderByField(i.Item1, query);
|
||||
var columnAscending = i.Item2 == SortOrder.Ascending;
|
||||
const bool enableOrderInversion = false;
|
||||
if (columnMap.Item2 && enableOrderInversion)
|
||||
{
|
||||
columnAscending = !columnAscending;
|
||||
}
|
||||
|
||||
var sortOrder = columnAscending ? "ASC" : "DESC";
|
||||
var sortOrder = i.Item2 == SortOrder.Ascending ? "ASC" : "DESC";
|
||||
|
||||
return columnMap.Item1 + " " + sortOrder;
|
||||
}));
|
||||
@@ -4630,10 +4623,20 @@ namespace Emby.Server.Implementations.Data
|
||||
|
||||
if (query.ExcludeInheritedTags.Length > 0)
|
||||
{
|
||||
var tagValues = query.ExcludeInheritedTags.Select(i => "'" + GetCleanValue(i) + "'");
|
||||
var tagValuesList = string.Join(",", tagValues);
|
||||
|
||||
whereClauses.Add("((select CleanValue from itemvalues where ItemId=Guid and Type=6 and cleanvalue in (" + tagValuesList + ")) is null)");
|
||||
var paramName = "@ExcludeInheritedTags";
|
||||
if (statement == null)
|
||||
{
|
||||
int index = 0;
|
||||
string excludedTags = string.Join(",", query.ExcludeInheritedTags.Select(t => paramName + index++));
|
||||
whereClauses.Add("((select CleanValue from itemvalues where ItemId=Guid and Type=6 and cleanvalue in (" + excludedTags + ")) is null)");
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int index = 0; index < query.ExcludeInheritedTags.Length; index++)
|
||||
{
|
||||
statement.TryBind(paramName + index, GetCleanValue(query.ExcludeInheritedTags[index]));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (query.SeriesStatuses.Length > 0)
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
#pragma warning disable CS1591
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
#pragma warning disable CS1591
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
|
||||
@@ -5,25 +5,22 @@ using System.Linq;
|
||||
namespace Emby.Server.Implementations.Data
|
||||
{
|
||||
/// <summary>
|
||||
/// Class TypeMapper
|
||||
/// Class TypeMapper.
|
||||
/// </summary>
|
||||
public class TypeMapper
|
||||
{
|
||||
/// <summary>
|
||||
/// This holds all the types in the running assemblies so that we can de-serialize properly when we don't have strong types
|
||||
/// This holds all the types in the running assemblies
|
||||
/// so that we can de-serialize properly when we don't have strong types.
|
||||
/// </summary>
|
||||
private readonly ConcurrentDictionary<string, Type> _typeMap = new ConcurrentDictionary<string, Type>();
|
||||
|
||||
public TypeMapper()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the type.
|
||||
/// </summary>
|
||||
/// <param name="typeName">Name of the type.</param>
|
||||
/// <returns>Type.</returns>
|
||||
/// <exception cref="ArgumentNullException"></exception>
|
||||
/// <exception cref="ArgumentNullException"><c>typeName</c> is null.</exception>
|
||||
public Type GetType(string typeName)
|
||||
{
|
||||
if (string.IsNullOrEmpty(typeName))
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
#pragma warning disable CS1591
|
||||
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
#pragma warning disable CS1591
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
@@ -130,7 +132,6 @@ namespace Emby.Server.Implementations.Devices
|
||||
var session = _authRepo.Get(new AuthenticationInfoQuery
|
||||
{
|
||||
DeviceId = id
|
||||
|
||||
}).Items.FirstOrDefault();
|
||||
|
||||
var device = session == null ? null : ToDeviceInfo(session);
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
#pragma warning disable CS1591
|
||||
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
#pragma warning disable CS1591
|
||||
|
||||
using MediaBrowser.Model.Diagnostics;
|
||||
|
||||
namespace Emby.Server.Implementations.Diagnostics
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
#pragma warning disable CS1591
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Emby.Naming\Emby.Naming.csproj" />
|
||||
<ProjectReference Include="..\Emby.Notifications\Emby.Notifications.csproj" />
|
||||
<ProjectReference Include="..\Jellyfin.Api\Jellyfin.Api.csproj" />
|
||||
<ProjectReference Include="..\MediaBrowser.Model\MediaBrowser.Model.csproj" />
|
||||
<ProjectReference Include="..\MediaBrowser.Common\MediaBrowser.Common.csproj" />
|
||||
<ProjectReference Include="..\MediaBrowser.Controller\MediaBrowser.Controller.csproj" />
|
||||
@@ -10,17 +11,16 @@
|
||||
<ProjectReference Include="..\MediaBrowser.WebDashboard\MediaBrowser.WebDashboard.csproj" />
|
||||
<ProjectReference Include="..\MediaBrowser.XbmcMetadata\MediaBrowser.XbmcMetadata.csproj" />
|
||||
<ProjectReference Include="..\Emby.Dlna\Emby.Dlna.csproj" />
|
||||
<ProjectReference Include="..\Mono.Nat\Mono.Nat.csproj" />
|
||||
<ProjectReference Include="..\MediaBrowser.Api\MediaBrowser.Api.csproj" />
|
||||
<ProjectReference Include="..\MediaBrowser.LocalMetadata\MediaBrowser.LocalMetadata.csproj" />
|
||||
<ProjectReference Include="..\Emby.Photos\Emby.Photos.csproj" />
|
||||
<ProjectReference Include="..\Emby.Drawing\Emby.Drawing.csproj" />
|
||||
<ProjectReference Include="..\Emby.XmlTv\Emby.XmlTv\Emby.XmlTv.csproj" />
|
||||
<ProjectReference Include="..\MediaBrowser.MediaEncoding\MediaBrowser.MediaEncoding.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="IPNetwork2" Version="2.4.0.126" />
|
||||
<PackageReference Include="Jellyfin.XmlTv" Version="10.4.3" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Hosting" Version="2.2.7" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Hosting.Abstractions" Version="2.2.0" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Hosting.Server.Abstractions" Version="2.2.0" />
|
||||
@@ -29,12 +29,14 @@
|
||||
<PackageReference Include="Microsoft.AspNetCore.ResponseCompression" Version="2.2.0" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Server.Kestrel" Version="2.2.0" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.WebSockets" Version="2.2.1" />
|
||||
<PackageReference Include="Microsoft.Extensions.Logging" Version="3.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="3.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="3.0.0" />
|
||||
<PackageReference Include="ServiceStack.Text.Core" Version="5.6.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="3.0.1" />
|
||||
<PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="3.0.1" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="3.0.1" />
|
||||
<PackageReference Include="Mono.Nat" Version="2.0.0" />
|
||||
<PackageReference Include="ServiceStack.Text.Core" Version="5.7.0" />
|
||||
<PackageReference Include="sharpcompress" Version="0.24.0" />
|
||||
<PackageReference Include="SQLitePCL.pretty.netstandard" Version="2.0.1" />
|
||||
<PackageReference Include="System.Interactive.Async" Version="4.0.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
@@ -47,9 +49,9 @@
|
||||
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
||||
</PropertyGroup>
|
||||
|
||||
<!-- Code analysers-->
|
||||
<!-- Code Analyzers-->
|
||||
<ItemGroup Condition=" '$(Configuration)' == 'Debug' ">
|
||||
<PackageReference Include="Microsoft.CodeAnalysis.FxCopAnalyzers" Version="2.9.4" />
|
||||
<PackageReference Include="Microsoft.CodeAnalysis.FxCopAnalyzers" Version="2.9.7" />
|
||||
<PackageReference Include="SerilogAnalyzer" Version="0.15.0" />
|
||||
<PackageReference Include="StyleCop.Analyzers" Version="1.1.118" />
|
||||
<PackageReference Include="SmartAnalyzers.MultithreadingAnalyzer" Version="1.1.31" />
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
#pragma warning disable CS1591
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
#pragma warning disable CS1591
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Net;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using MediaBrowser.Common.Net;
|
||||
using MediaBrowser.Controller;
|
||||
using MediaBrowser.Controller.Configuration;
|
||||
using MediaBrowser.Controller.Plugins;
|
||||
@@ -15,179 +16,105 @@ using Mono.Nat;
|
||||
|
||||
namespace Emby.Server.Implementations.EntryPoints
|
||||
{
|
||||
/// <summary>
|
||||
/// Server entrypoint handling external port forwarding.
|
||||
/// </summary>
|
||||
public class ExternalPortForwarding : IServerEntryPoint
|
||||
{
|
||||
private readonly IServerApplicationHost _appHost;
|
||||
private readonly ILogger _logger;
|
||||
private readonly IHttpClient _httpClient;
|
||||
private readonly IServerConfigurationManager _config;
|
||||
private readonly IDeviceDiscovery _deviceDiscovery;
|
||||
|
||||
private Timer _timer;
|
||||
|
||||
private NatManager _natManager;
|
||||
|
||||
private readonly object _createdRulesLock = new object();
|
||||
private List<string> _createdRules = new List<string>();
|
||||
private readonly object _usnsHandledLock = new object();
|
||||
private List<string> _usnsHandled = new List<string>();
|
||||
private List<IPEndPoint> _createdRules = new List<IPEndPoint>();
|
||||
private Timer _timer;
|
||||
private string _lastConfigIdentifier;
|
||||
|
||||
public ExternalPortForwarding(ILoggerFactory loggerFactory, IServerApplicationHost appHost, IServerConfigurationManager config, IDeviceDiscovery deviceDiscovery, IHttpClient httpClient)
|
||||
private bool _disposed = false;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ExternalPortForwarding"/> class.
|
||||
/// </summary>
|
||||
/// <param name="logger">The logger.</param>
|
||||
/// <param name="appHost">The application host.</param>
|
||||
/// <param name="config">The configuration manager.</param>
|
||||
/// <param name="deviceDiscovery">The device discovery.</param>
|
||||
public ExternalPortForwarding(
|
||||
ILogger<ExternalPortForwarding> logger,
|
||||
IServerApplicationHost appHost,
|
||||
IServerConfigurationManager config,
|
||||
IDeviceDiscovery deviceDiscovery)
|
||||
{
|
||||
_logger = loggerFactory.CreateLogger("PortMapper");
|
||||
_logger = logger;
|
||||
_appHost = appHost;
|
||||
_config = config;
|
||||
_deviceDiscovery = deviceDiscovery;
|
||||
_httpClient = httpClient;
|
||||
_config.ConfigurationUpdated += _config_ConfigurationUpdated1;
|
||||
}
|
||||
|
||||
private void _config_ConfigurationUpdated1(object sender, EventArgs e)
|
||||
{
|
||||
_config_ConfigurationUpdated(sender, e);
|
||||
}
|
||||
|
||||
private string _lastConfigIdentifier;
|
||||
private string GetConfigIdentifier()
|
||||
{
|
||||
var values = new List<string>();
|
||||
const char Separator = '|';
|
||||
var config = _config.Configuration;
|
||||
|
||||
values.Add(config.EnableUPnP.ToString());
|
||||
values.Add(config.PublicPort.ToString(CultureInfo.InvariantCulture));
|
||||
values.Add(_appHost.HttpPort.ToString(CultureInfo.InvariantCulture));
|
||||
values.Add(_appHost.HttpsPort.ToString(CultureInfo.InvariantCulture));
|
||||
values.Add(_appHost.EnableHttps.ToString());
|
||||
values.Add((config.EnableRemoteAccess).ToString());
|
||||
|
||||
return string.Join("|", values.ToArray());
|
||||
return new StringBuilder(32)
|
||||
.Append(config.EnableUPnP).Append(Separator)
|
||||
.Append(config.PublicPort).Append(Separator)
|
||||
.Append(_appHost.HttpPort).Append(Separator)
|
||||
.Append(_appHost.HttpsPort).Append(Separator)
|
||||
.Append(_appHost.EnableHttps).Append(Separator)
|
||||
.Append(config.EnableRemoteAccess).Append(Separator)
|
||||
.ToString();
|
||||
}
|
||||
|
||||
private async void _config_ConfigurationUpdated(object sender, EventArgs e)
|
||||
private void OnConfigurationUpdated(object sender, EventArgs e)
|
||||
{
|
||||
if (!string.Equals(_lastConfigIdentifier, GetConfigIdentifier(), StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
DisposeNat();
|
||||
|
||||
await RunAsync();
|
||||
Stop();
|
||||
Start();
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task RunAsync()
|
||||
{
|
||||
if (_config.Configuration.EnableUPnP && _config.Configuration.EnableRemoteAccess)
|
||||
{
|
||||
Start();
|
||||
}
|
||||
Start();
|
||||
|
||||
_config.ConfigurationUpdated -= _config_ConfigurationUpdated;
|
||||
_config.ConfigurationUpdated += _config_ConfigurationUpdated;
|
||||
_config.ConfigurationUpdated += OnConfigurationUpdated;
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
private void Start()
|
||||
{
|
||||
_logger.LogDebug("Starting NAT discovery");
|
||||
if (_natManager == null)
|
||||
if (!_config.Configuration.EnableUPnP || !_config.Configuration.EnableRemoteAccess)
|
||||
{
|
||||
_natManager = new NatManager(_logger, _httpClient);
|
||||
_natManager.DeviceFound += NatUtility_DeviceFound;
|
||||
_natManager.StartDiscovery();
|
||||
return;
|
||||
}
|
||||
|
||||
_logger.LogDebug("Starting NAT discovery");
|
||||
|
||||
NatUtility.DeviceFound += OnNatUtilityDeviceFound;
|
||||
NatUtility.StartDiscovery();
|
||||
|
||||
_timer = new Timer(ClearCreatedRules, null, TimeSpan.FromMinutes(10), TimeSpan.FromMinutes(10));
|
||||
|
||||
_deviceDiscovery.DeviceDiscovered += _deviceDiscovery_DeviceDiscovered;
|
||||
_deviceDiscovery.DeviceDiscovered += OnDeviceDiscoveryDeviceDiscovered;
|
||||
|
||||
_lastConfigIdentifier = GetConfigIdentifier();
|
||||
}
|
||||
|
||||
private async void _deviceDiscovery_DeviceDiscovered(object sender, GenericEventArgs<UpnpDeviceInfo> e)
|
||||
private void Stop()
|
||||
{
|
||||
if (_disposed)
|
||||
{
|
||||
return;
|
||||
}
|
||||
_logger.LogDebug("Stopping NAT discovery");
|
||||
|
||||
var info = e.Argument;
|
||||
NatUtility.StopDiscovery();
|
||||
NatUtility.DeviceFound -= OnNatUtilityDeviceFound;
|
||||
|
||||
if (!info.Headers.TryGetValue("USN", out string usn)) usn = string.Empty;
|
||||
_timer?.Dispose();
|
||||
|
||||
if (!info.Headers.TryGetValue("NT", out string nt)) nt = string.Empty;
|
||||
|
||||
// Filter device type
|
||||
if (usn.IndexOf("WANIPConnection:", StringComparison.OrdinalIgnoreCase) == -1 &&
|
||||
nt.IndexOf("WANIPConnection:", StringComparison.OrdinalIgnoreCase) == -1 &&
|
||||
usn.IndexOf("WANPPPConnection:", StringComparison.OrdinalIgnoreCase) == -1 &&
|
||||
nt.IndexOf("WANPPPConnection:", StringComparison.OrdinalIgnoreCase) == -1)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var identifier = string.IsNullOrWhiteSpace(usn) ? nt : usn;
|
||||
|
||||
if (info.Location == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
lock (_usnsHandledLock)
|
||||
{
|
||||
if (_usnsHandled.Contains(identifier))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_usnsHandled.Add(identifier);
|
||||
}
|
||||
|
||||
_logger.LogDebug("Found NAT device: " + identifier);
|
||||
|
||||
if (IPAddress.TryParse(info.Location.Host, out var address))
|
||||
{
|
||||
// The Handle method doesn't need the port
|
||||
var endpoint = new IPEndPoint(address, info.Location.Port);
|
||||
|
||||
IPAddress localAddress = null;
|
||||
|
||||
try
|
||||
{
|
||||
var localAddressString = await _appHost.GetLocalApiUrl(CancellationToken.None).ConfigureAwait(false);
|
||||
|
||||
if (Uri.TryCreate(localAddressString, UriKind.Absolute, out var uri))
|
||||
{
|
||||
localAddressString = uri.Host;
|
||||
|
||||
if (!IPAddress.TryParse(localAddressString, out localAddress))
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Error");
|
||||
return;
|
||||
}
|
||||
|
||||
if (_disposed)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// This should never happen, but the Handle method will throw ArgumentNullException if it does
|
||||
if (localAddress == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var natManager = _natManager;
|
||||
if (natManager != null)
|
||||
{
|
||||
await natManager.Handle(localAddress, info, endpoint, NatProtocol.Upnp).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
_deviceDiscovery.DeviceDiscovered -= OnDeviceDiscoveryDeviceDiscovered;
|
||||
}
|
||||
|
||||
private void ClearCreatedRules(object state)
|
||||
@@ -196,30 +123,24 @@ namespace Emby.Server.Implementations.EntryPoints
|
||||
{
|
||||
_createdRules.Clear();
|
||||
}
|
||||
|
||||
lock (_usnsHandledLock)
|
||||
{
|
||||
_usnsHandled.Clear();
|
||||
}
|
||||
}
|
||||
|
||||
void NatUtility_DeviceFound(object sender, DeviceEventArgs e)
|
||||
private void OnDeviceDiscoveryDeviceDiscovered(object sender, GenericEventArgs<UpnpDeviceInfo> e)
|
||||
{
|
||||
if (_disposed)
|
||||
{
|
||||
return;
|
||||
}
|
||||
NatUtility.Search(e.Argument.LocalIpAddress, NatProtocol.Upnp);
|
||||
}
|
||||
|
||||
private void OnNatUtilityDeviceFound(object sender, DeviceEventArgs e)
|
||||
{
|
||||
try
|
||||
{
|
||||
var device = e.Device;
|
||||
|
||||
CreateRules(device);
|
||||
}
|
||||
catch
|
||||
catch (Exception ex)
|
||||
{
|
||||
// Commenting out because users are reporting problems out of our control
|
||||
//_logger.LogError(ex, "Error creating port forwarding rules");
|
||||
_logger.LogError(ex, "Error creating port forwarding rules");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -232,15 +153,13 @@ namespace Emby.Server.Implementations.EntryPoints
|
||||
|
||||
// On some systems the device discovered event seems to fire repeatedly
|
||||
// This check will help ensure we're not trying to port map the same device over and over
|
||||
var address = device.LocalAddress;
|
||||
|
||||
var addressString = address.ToString();
|
||||
var address = device.DeviceEndpoint;
|
||||
|
||||
lock (_createdRulesLock)
|
||||
{
|
||||
if (!_createdRules.Contains(addressString))
|
||||
if (!_createdRules.Contains(address))
|
||||
{
|
||||
_createdRules.Add(addressString);
|
||||
_createdRules.Add(address);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -268,54 +187,43 @@ namespace Emby.Server.Implementations.EntryPoints
|
||||
}
|
||||
}
|
||||
|
||||
private Task CreatePortMap(INatDevice device, int privatePort, int publicPort)
|
||||
private Task<Mapping> CreatePortMap(INatDevice device, int privatePort, int publicPort)
|
||||
{
|
||||
_logger.LogDebug("Creating port map on local port {0} to public port {1} with device {2}", privatePort, publicPort, device.LocalAddress.ToString());
|
||||
_logger.LogDebug(
|
||||
"Creating port map on local port {0} to public port {1} with device {2}",
|
||||
privatePort,
|
||||
publicPort,
|
||||
device.DeviceEndpoint);
|
||||
|
||||
return device.CreatePortMap(new Mapping(Protocol.Tcp, privatePort, publicPort)
|
||||
{
|
||||
Description = _appHost.Name
|
||||
});
|
||||
return device.CreatePortMapAsync(
|
||||
new Mapping(Protocol.Tcp, privatePort, publicPort, 0, _appHost.Name));
|
||||
}
|
||||
|
||||
private bool _disposed = false;
|
||||
/// <inheritdoc />
|
||||
public void Dispose()
|
||||
{
|
||||
_disposed = true;
|
||||
DisposeNat();
|
||||
Dispose(true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
private void DisposeNat()
|
||||
/// <summary>
|
||||
/// Releases unmanaged and - optionally - managed resources.
|
||||
/// </summary>
|
||||
/// <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)
|
||||
{
|
||||
_logger.LogDebug("Stopping NAT discovery");
|
||||
|
||||
if (_timer != null)
|
||||
if (_disposed)
|
||||
{
|
||||
_timer.Dispose();
|
||||
_timer = null;
|
||||
return;
|
||||
}
|
||||
|
||||
_deviceDiscovery.DeviceDiscovered -= _deviceDiscovery_DeviceDiscovered;
|
||||
_config.ConfigurationUpdated -= OnConfigurationUpdated;
|
||||
|
||||
var natManager = _natManager;
|
||||
Stop();
|
||||
|
||||
if (natManager != null)
|
||||
{
|
||||
_natManager = null;
|
||||
_timer = null;
|
||||
|
||||
using (natManager)
|
||||
{
|
||||
try
|
||||
{
|
||||
natManager.StopDiscovery();
|
||||
natManager.DeviceFound -= NatUtility_DeviceFound;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Error stopping NAT Discovery");
|
||||
}
|
||||
}
|
||||
}
|
||||
_disposed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
#pragma warning disable CS1591
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
@@ -21,7 +23,7 @@ namespace Emby.Server.Implementations.EntryPoints
|
||||
public class LibraryChangedNotifier : IServerEntryPoint
|
||||
{
|
||||
/// <summary>
|
||||
/// The _library manager
|
||||
/// The library manager.
|
||||
/// </summary>
|
||||
private readonly ILibraryManager _libraryManager;
|
||||
|
||||
@@ -30,7 +32,7 @@ namespace Emby.Server.Implementations.EntryPoints
|
||||
private readonly ILogger _logger;
|
||||
|
||||
/// <summary>
|
||||
/// The _library changed sync lock
|
||||
/// The library changed sync lock.
|
||||
/// </summary>
|
||||
private readonly object _libraryChangedSyncLock = new object();
|
||||
|
||||
@@ -48,7 +50,7 @@ namespace Emby.Server.Implementations.EntryPoints
|
||||
private Timer LibraryUpdateTimer { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The library update duration
|
||||
/// The library update duration.
|
||||
/// </summary>
|
||||
private const int LibraryUpdateDuration = 30000;
|
||||
|
||||
@@ -188,8 +190,11 @@ namespace Emby.Server.Implementations.EntryPoints
|
||||
{
|
||||
if (LibraryUpdateTimer == null)
|
||||
{
|
||||
LibraryUpdateTimer = new Timer(LibraryUpdateTimerCallback, null, LibraryUpdateDuration,
|
||||
Timeout.Infinite);
|
||||
LibraryUpdateTimer = new Timer(
|
||||
LibraryUpdateTimerCallback,
|
||||
null,
|
||||
LibraryUpdateDuration,
|
||||
Timeout.Infinite);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -452,7 +457,7 @@ namespace Emby.Server.Implementations.EntryPoints
|
||||
return new[] { item };
|
||||
}
|
||||
|
||||
return new T[] { };
|
||||
return Array.Empty<T>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
#pragma warning disable CS1591
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using MediaBrowser.Controller.Library;
|
||||
@@ -12,32 +11,19 @@ using Microsoft.Extensions.Logging;
|
||||
namespace Emby.Server.Implementations.EntryPoints
|
||||
{
|
||||
/// <summary>
|
||||
/// Class RefreshUsersMetadata
|
||||
/// Class RefreshUsersMetadata.
|
||||
/// </summary>
|
||||
public class RefreshUsersMetadata : IScheduledTask, IConfigurableScheduledTask
|
||||
{
|
||||
private readonly ILogger _logger;
|
||||
|
||||
/// <summary>
|
||||
/// The _user manager
|
||||
/// The user manager.
|
||||
/// </summary>
|
||||
private readonly IUserManager _userManager;
|
||||
|
||||
private IFileSystem _fileSystem;
|
||||
|
||||
public string Name => "Refresh Users";
|
||||
|
||||
public string Key => "RefreshUsers";
|
||||
|
||||
public string Description => "Refresh user infos";
|
||||
|
||||
public string Category => "Library";
|
||||
|
||||
public bool IsHidden => true;
|
||||
|
||||
public bool IsEnabled => true;
|
||||
|
||||
public bool IsLogged => true;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="RefreshUsersMetadata" /> class.
|
||||
/// </summary>
|
||||
@@ -48,6 +34,28 @@ namespace Emby.Server.Implementations.EntryPoints
|
||||
_fileSystem = fileSystem;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public string Name => "Refresh Users";
|
||||
|
||||
/// <inheritdoc />
|
||||
public string Key => "RefreshUsers";
|
||||
|
||||
/// <inheritdoc />
|
||||
public string Description => "Refresh user infos";
|
||||
|
||||
/// <inheritdoc />
|
||||
public string Category => "Library";
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool IsHidden => true;
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool IsEnabled => true;
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool IsLogged => true;
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task Execute(CancellationToken cancellationToken, IProgress<double> progress)
|
||||
{
|
||||
foreach (var user in _userManager.Users)
|
||||
@@ -58,9 +66,10 @@ namespace Emby.Server.Implementations.EntryPoints
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public IEnumerable<TaskTriggerInfo> GetDefaultTriggers()
|
||||
{
|
||||
return new List<TaskTriggerInfo>
|
||||
return new[]
|
||||
{
|
||||
new TaskTriggerInfo
|
||||
{
|
||||
|
||||
@@ -16,33 +16,46 @@ using MediaBrowser.Model.Tasks;
|
||||
namespace Emby.Server.Implementations.EntryPoints
|
||||
{
|
||||
/// <summary>
|
||||
/// Class WebSocketEvents
|
||||
/// Class WebSocketEvents.
|
||||
/// </summary>
|
||||
public class ServerEventNotifier : IServerEntryPoint
|
||||
{
|
||||
/// <summary>
|
||||
/// The _user manager
|
||||
/// The user manager.
|
||||
/// </summary>
|
||||
private readonly IUserManager _userManager;
|
||||
|
||||
/// <summary>
|
||||
/// The _installation manager
|
||||
/// The installation manager.
|
||||
/// </summary>
|
||||
private readonly IInstallationManager _installationManager;
|
||||
|
||||
/// <summary>
|
||||
/// The _kernel
|
||||
/// The kernel.
|
||||
/// </summary>
|
||||
private readonly IServerApplicationHost _appHost;
|
||||
|
||||
/// <summary>
|
||||
/// The _task manager
|
||||
/// The task manager.
|
||||
/// </summary>
|
||||
private readonly ITaskManager _taskManager;
|
||||
|
||||
private readonly ISessionManager _sessionManager;
|
||||
|
||||
public ServerEventNotifier(IServerApplicationHost appHost, IUserManager userManager, IInstallationManager installationManager, ITaskManager taskManager, ISessionManager sessionManager)
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ServerEventNotifier"/> class.
|
||||
/// </summary>
|
||||
/// <param name="appHost">The application host.</param>
|
||||
/// <param name="userManager">The user manager.</param>
|
||||
/// <param name="installationManager">The installation manager.</param>
|
||||
/// <param name="taskManager">The task manager.</param>
|
||||
/// <param name="sessionManager">The session manager.</param>
|
||||
public ServerEventNotifier(
|
||||
IServerApplicationHost appHost,
|
||||
IUserManager userManager,
|
||||
IInstallationManager installationManager,
|
||||
ITaskManager taskManager,
|
||||
ISessionManager sessionManager)
|
||||
{
|
||||
_userManager = userManager;
|
||||
_installationManager = installationManager;
|
||||
@@ -51,47 +64,48 @@ namespace Emby.Server.Implementations.EntryPoints
|
||||
_sessionManager = sessionManager;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task RunAsync()
|
||||
{
|
||||
_userManager.UserDeleted += userManager_UserDeleted;
|
||||
_userManager.UserUpdated += userManager_UserUpdated;
|
||||
_userManager.UserPolicyUpdated += _userManager_UserPolicyUpdated;
|
||||
_userManager.UserConfigurationUpdated += _userManager_UserConfigurationUpdated;
|
||||
_userManager.UserDeleted += OnUserDeleted;
|
||||
_userManager.UserUpdated += OnUserUpdated;
|
||||
_userManager.UserPolicyUpdated += OnUserPolicyUpdated;
|
||||
_userManager.UserConfigurationUpdated += OnUserConfigurationUpdated;
|
||||
|
||||
_appHost.HasPendingRestartChanged += kernel_HasPendingRestartChanged;
|
||||
_appHost.HasPendingRestartChanged += OnHasPendingRestartChanged;
|
||||
|
||||
_installationManager.PluginUninstalled += InstallationManager_PluginUninstalled;
|
||||
_installationManager.PackageInstalling += _installationManager_PackageInstalling;
|
||||
_installationManager.PackageInstallationCancelled += _installationManager_PackageInstallationCancelled;
|
||||
_installationManager.PackageInstallationCompleted += _installationManager_PackageInstallationCompleted;
|
||||
_installationManager.PackageInstallationFailed += _installationManager_PackageInstallationFailed;
|
||||
_installationManager.PluginUninstalled += OnPluginUninstalled;
|
||||
_installationManager.PackageInstalling += OnPackageInstalling;
|
||||
_installationManager.PackageInstallationCancelled += OnPackageInstallationCancelled;
|
||||
_installationManager.PackageInstallationCompleted += OnPackageInstallationCompleted;
|
||||
_installationManager.PackageInstallationFailed += OnPackageInstallationFailed;
|
||||
|
||||
_taskManager.TaskCompleted += _taskManager_TaskCompleted;
|
||||
_taskManager.TaskCompleted += OnTaskCompleted;
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
void _installationManager_PackageInstalling(object sender, InstallationEventArgs e)
|
||||
private void OnPackageInstalling(object sender, InstallationEventArgs e)
|
||||
{
|
||||
SendMessageToAdminSessions("PackageInstalling", e.InstallationInfo);
|
||||
}
|
||||
|
||||
void _installationManager_PackageInstallationCancelled(object sender, InstallationEventArgs e)
|
||||
private void OnPackageInstallationCancelled(object sender, InstallationEventArgs e)
|
||||
{
|
||||
SendMessageToAdminSessions("PackageInstallationCancelled", e.InstallationInfo);
|
||||
}
|
||||
|
||||
void _installationManager_PackageInstallationCompleted(object sender, InstallationEventArgs e)
|
||||
private void OnPackageInstallationCompleted(object sender, InstallationEventArgs e)
|
||||
{
|
||||
SendMessageToAdminSessions("PackageInstallationCompleted", e.InstallationInfo);
|
||||
}
|
||||
|
||||
void _installationManager_PackageInstallationFailed(object sender, InstallationFailedEventArgs e)
|
||||
private void OnPackageInstallationFailed(object sender, InstallationFailedEventArgs e)
|
||||
{
|
||||
SendMessageToAdminSessions("PackageInstallationFailed", e.InstallationInfo);
|
||||
}
|
||||
|
||||
void _taskManager_TaskCompleted(object sender, TaskCompletionEventArgs e)
|
||||
private void OnTaskCompleted(object sender, TaskCompletionEventArgs e)
|
||||
{
|
||||
SendMessageToAdminSessions("ScheduledTaskEnded", e.Result);
|
||||
}
|
||||
@@ -101,7 +115,7 @@ namespace Emby.Server.Implementations.EntryPoints
|
||||
/// </summary>
|
||||
/// <param name="sender">The sender.</param>
|
||||
/// <param name="e">The e.</param>
|
||||
void InstallationManager_PluginUninstalled(object sender, GenericEventArgs<IPlugin> e)
|
||||
private void OnPluginUninstalled(object sender, GenericEventArgs<IPlugin> e)
|
||||
{
|
||||
SendMessageToAdminSessions("PluginUninstalled", e.Argument.GetPluginInfo());
|
||||
}
|
||||
@@ -111,7 +125,7 @@ namespace Emby.Server.Implementations.EntryPoints
|
||||
/// </summary>
|
||||
/// <param name="sender">The source of the event.</param>
|
||||
/// <param name="e">The <see cref="EventArgs" /> instance containing the event data.</param>
|
||||
void kernel_HasPendingRestartChanged(object sender, EventArgs e)
|
||||
private void OnHasPendingRestartChanged(object sender, EventArgs e)
|
||||
{
|
||||
_sessionManager.SendRestartRequiredNotification(CancellationToken.None);
|
||||
}
|
||||
@@ -121,7 +135,7 @@ namespace Emby.Server.Implementations.EntryPoints
|
||||
/// </summary>
|
||||
/// <param name="sender">The sender.</param>
|
||||
/// <param name="e">The e.</param>
|
||||
void userManager_UserUpdated(object sender, GenericEventArgs<User> e)
|
||||
private void OnUserUpdated(object sender, GenericEventArgs<User> e)
|
||||
{
|
||||
var dto = _userManager.GetUserDto(e.Argument);
|
||||
|
||||
@@ -133,19 +147,19 @@ namespace Emby.Server.Implementations.EntryPoints
|
||||
/// </summary>
|
||||
/// <param name="sender">The sender.</param>
|
||||
/// <param name="e">The e.</param>
|
||||
void userManager_UserDeleted(object sender, GenericEventArgs<User> e)
|
||||
private void OnUserDeleted(object sender, GenericEventArgs<User> e)
|
||||
{
|
||||
SendMessageToUserSession(e.Argument, "UserDeleted", e.Argument.Id.ToString("N", CultureInfo.InvariantCulture));
|
||||
}
|
||||
|
||||
void _userManager_UserPolicyUpdated(object sender, GenericEventArgs<User> e)
|
||||
private void OnUserPolicyUpdated(object sender, GenericEventArgs<User> e)
|
||||
{
|
||||
var dto = _userManager.GetUserDto(e.Argument);
|
||||
|
||||
SendMessageToUserSession(e.Argument, "UserPolicyUpdated", dto);
|
||||
}
|
||||
|
||||
void _userManager_UserConfigurationUpdated(object sender, GenericEventArgs<User> e)
|
||||
private void OnUserConfigurationUpdated(object sender, GenericEventArgs<User> e)
|
||||
{
|
||||
var dto = _userManager.GetUserDto(e.Argument);
|
||||
|
||||
@@ -156,7 +170,7 @@ namespace Emby.Server.Implementations.EntryPoints
|
||||
{
|
||||
try
|
||||
{
|
||||
await _sessionManager.SendMessageToAdminSessions(name, data, CancellationToken.None);
|
||||
await _sessionManager.SendMessageToAdminSessions(name, data, CancellationToken.None).ConfigureAwait(false);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
@@ -168,7 +182,11 @@ namespace Emby.Server.Implementations.EntryPoints
|
||||
{
|
||||
try
|
||||
{
|
||||
await _sessionManager.SendMessageToUserSessions(new List<Guid> { user.Id }, name, data, CancellationToken.None);
|
||||
await _sessionManager.SendMessageToUserSessions(
|
||||
new List<Guid> { user.Id },
|
||||
name,
|
||||
data,
|
||||
CancellationToken.None).ConfigureAwait(false);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
@@ -176,12 +194,11 @@ namespace Emby.Server.Implementations.EntryPoints
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
|
||||
/// </summary>
|
||||
/// <inheritdoc />
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -192,18 +209,20 @@ namespace Emby.Server.Implementations.EntryPoints
|
||||
{
|
||||
if (dispose)
|
||||
{
|
||||
_userManager.UserDeleted -= userManager_UserDeleted;
|
||||
_userManager.UserUpdated -= userManager_UserUpdated;
|
||||
_userManager.UserPolicyUpdated -= _userManager_UserPolicyUpdated;
|
||||
_userManager.UserConfigurationUpdated -= _userManager_UserConfigurationUpdated;
|
||||
_userManager.UserDeleted -= OnUserDeleted;
|
||||
_userManager.UserUpdated -= OnUserUpdated;
|
||||
_userManager.UserPolicyUpdated -= OnUserPolicyUpdated;
|
||||
_userManager.UserConfigurationUpdated -= OnUserConfigurationUpdated;
|
||||
|
||||
_installationManager.PluginUninstalled -= InstallationManager_PluginUninstalled;
|
||||
_installationManager.PackageInstalling -= _installationManager_PackageInstalling;
|
||||
_installationManager.PackageInstallationCancelled -= _installationManager_PackageInstallationCancelled;
|
||||
_installationManager.PackageInstallationCompleted -= _installationManager_PackageInstallationCompleted;
|
||||
_installationManager.PackageInstallationFailed -= _installationManager_PackageInstallationFailed;
|
||||
_installationManager.PluginUninstalled -= OnPluginUninstalled;
|
||||
_installationManager.PackageInstalling -= OnPackageInstalling;
|
||||
_installationManager.PackageInstallationCancelled -= OnPackageInstallationCancelled;
|
||||
_installationManager.PackageInstallationCompleted -= OnPackageInstallationCompleted;
|
||||
_installationManager.PackageInstallationFailed -= OnPackageInstallationFailed;
|
||||
|
||||
_appHost.HasPendingRestartChanged -= kernel_HasPendingRestartChanged;
|
||||
_appHost.HasPendingRestartChanged -= OnHasPendingRestartChanged;
|
||||
|
||||
_taskManager.TaskCompleted -= OnTaskCompleted;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,21 +8,28 @@ using Microsoft.Extensions.Logging;
|
||||
namespace Emby.Server.Implementations.EntryPoints
|
||||
{
|
||||
/// <summary>
|
||||
/// Class StartupWizard
|
||||
/// Class StartupWizard.
|
||||
/// </summary>
|
||||
public class StartupWizard : IServerEntryPoint
|
||||
{
|
||||
/// <summary>
|
||||
/// The _app host
|
||||
/// The app host.
|
||||
/// </summary>
|
||||
private readonly IServerApplicationHost _appHost;
|
||||
|
||||
/// <summary>
|
||||
/// The _user manager
|
||||
/// The user manager.
|
||||
/// </summary>
|
||||
private readonly ILogger _logger;
|
||||
|
||||
private IServerConfigurationManager _config;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="StartupWizard"/> class.
|
||||
/// </summary>
|
||||
/// <param name="appHost">The application host.</param>
|
||||
/// <param name="logger">The logger.</param>
|
||||
/// <param name="config">The configuration manager.</param>
|
||||
public StartupWizard(IServerApplicationHost appHost, ILogger logger, IServerConfigurationManager config)
|
||||
{
|
||||
_appHost = appHost;
|
||||
@@ -30,9 +37,7 @@ namespace Emby.Server.Implementations.EntryPoints
|
||||
_config = config;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Runs this instance.
|
||||
/// </summary>
|
||||
/// <inheritdoc />
|
||||
public Task RunAsync()
|
||||
{
|
||||
if (!_appHost.CanLaunchWebBrowser)
|
||||
@@ -57,9 +62,7 @@ namespace Emby.Server.Implementations.EntryPoints
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
|
||||
/// </summary>
|
||||
/// <inheritdoc />
|
||||
public void Dispose()
|
||||
{
|
||||
}
|
||||
|
||||
@@ -10,30 +10,36 @@ using Microsoft.Extensions.Logging;
|
||||
namespace Emby.Server.Implementations.EntryPoints
|
||||
{
|
||||
/// <summary>
|
||||
/// Class UdpServerEntryPoint
|
||||
/// Class UdpServerEntryPoint.
|
||||
/// </summary>
|
||||
public class UdpServerEntryPoint : IServerEntryPoint
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the UDP server.
|
||||
/// The port of the UDP server.
|
||||
/// </summary>
|
||||
/// <value>The UDP server.</value>
|
||||
private UdpServer UdpServer { get; set; }
|
||||
public const int PortNumber = 7359;
|
||||
|
||||
/// <summary>
|
||||
/// The _logger
|
||||
/// The logger.
|
||||
/// </summary>
|
||||
private readonly ILogger _logger;
|
||||
private readonly ISocketFactory _socketFactory;
|
||||
private readonly IServerApplicationHost _appHost;
|
||||
private readonly IJsonSerializer _json;
|
||||
|
||||
public const int PortNumber = 7359;
|
||||
/// <summary>
|
||||
/// The UDP server.
|
||||
/// </summary>
|
||||
private UdpServer _udpServer;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="UdpServerEntryPoint" /> class.
|
||||
/// </summary>
|
||||
public UdpServerEntryPoint(ILogger logger, IServerApplicationHost appHost, IJsonSerializer json, ISocketFactory socketFactory)
|
||||
public UdpServerEntryPoint(
|
||||
ILogger logger,
|
||||
IServerApplicationHost appHost,
|
||||
IJsonSerializer json,
|
||||
ISocketFactory socketFactory)
|
||||
{
|
||||
_logger = logger;
|
||||
_appHost = appHost;
|
||||
@@ -41,9 +47,7 @@ namespace Emby.Server.Implementations.EntryPoints
|
||||
_socketFactory = socketFactory;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Runs this instance.
|
||||
/// </summary>
|
||||
/// <inheritdoc />
|
||||
public Task RunAsync()
|
||||
{
|
||||
var udpServer = new UdpServer(_logger, _appHost, _json, _socketFactory);
|
||||
@@ -52,7 +56,7 @@ namespace Emby.Server.Implementations.EntryPoints
|
||||
{
|
||||
udpServer.Start(PortNumber);
|
||||
|
||||
UdpServer = udpServer;
|
||||
_udpServer = udpServer;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@@ -62,12 +66,11 @@ namespace Emby.Server.Implementations.EntryPoints
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
|
||||
/// </summary>
|
||||
/// <inheritdoc />
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -78,9 +81,9 @@ namespace Emby.Server.Implementations.EntryPoints
|
||||
{
|
||||
if (dispose)
|
||||
{
|
||||
if (UdpServer != null)
|
||||
if (_udpServer != null)
|
||||
{
|
||||
UdpServer.Dispose();
|
||||
_udpServer.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
#pragma warning disable CS1591
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
|
||||
@@ -282,6 +282,7 @@ namespace Emby.Server.Implementations.HttpClientManager
|
||||
};
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task<HttpResponseInfo> Post(HttpRequestOptions options)
|
||||
=> SendAsync(options, HttpMethod.Post);
|
||||
|
||||
@@ -325,7 +326,7 @@ namespace Emby.Server.Implementations.HttpClientManager
|
||||
|
||||
if (options.LogErrorResponseBody)
|
||||
{
|
||||
var msg = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
|
||||
string msg = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
|
||||
_logger.LogError("HTTP request failed with message: {Message}", msg);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
#pragma warning disable CS1591
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
#pragma warning disable CS1591
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
@@ -7,7 +9,6 @@ using System.Net.Sockets;
|
||||
using System.Reflection;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Emby.Server.Implementations.Configuration;
|
||||
using Emby.Server.Implementations.Net;
|
||||
using Emby.Server.Implementations.Services;
|
||||
using MediaBrowser.Common.Extensions;
|
||||
@@ -16,11 +17,9 @@ using MediaBrowser.Controller;
|
||||
using MediaBrowser.Controller.Configuration;
|
||||
using MediaBrowser.Controller.Net;
|
||||
using MediaBrowser.Model.Events;
|
||||
using MediaBrowser.Model.Extensions;
|
||||
using MediaBrowser.Model.Serialization;
|
||||
using MediaBrowser.Model.Services;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Http.Internal;
|
||||
using Microsoft.AspNetCore.WebUtilities;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.Logging;
|
||||
@@ -166,7 +165,7 @@ namespace Emby.Server.Implementations.HttpServer
|
||||
{
|
||||
OnReceive = ProcessWebSocketMessageReceived,
|
||||
Url = e.Url,
|
||||
QueryString = e.QueryString ?? new QueryCollection()
|
||||
QueryString = e.QueryString
|
||||
};
|
||||
|
||||
connection.Closed += OnConnectionClosed;
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
#pragma warning disable CS1591
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
@@ -5,12 +7,10 @@ using System.IO;
|
||||
using System.IO.Compression;
|
||||
using System.Net;
|
||||
using System.Runtime.Serialization;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Xml;
|
||||
using Emby.Server.Implementations.Services;
|
||||
using MediaBrowser.Common.Extensions;
|
||||
using MediaBrowser.Controller.Net;
|
||||
using MediaBrowser.Model.IO;
|
||||
using MediaBrowser.Model.Serialization;
|
||||
@@ -24,12 +24,12 @@ using MimeTypes = MediaBrowser.Model.Net.MimeTypes;
|
||||
namespace Emby.Server.Implementations.HttpServer
|
||||
{
|
||||
/// <summary>
|
||||
/// Class HttpResultFactory
|
||||
/// Class HttpResultFactory.
|
||||
/// </summary>
|
||||
public class HttpResultFactory : IHttpResultFactory
|
||||
{
|
||||
/// <summary>
|
||||
/// The _logger
|
||||
/// The logger.
|
||||
/// </summary>
|
||||
private readonly ILogger _logger;
|
||||
private readonly IFileSystem _fileSystem;
|
||||
@@ -460,7 +460,7 @@ namespace Emby.Server.Implementations.HttpServer
|
||||
|
||||
if (string.IsNullOrEmpty(path))
|
||||
{
|
||||
throw new ArgumentNullException(nameof(path));
|
||||
throw new ArgumentException("Path can't be empty.", nameof(options));
|
||||
}
|
||||
|
||||
if (fileShare != FileShareMode.Read && fileShare != FileShareMode.ReadWrite)
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
#pragma warning disable CS1591
|
||||
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
#pragma warning disable CS1591
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
@@ -48,12 +50,14 @@ namespace Emby.Server.Implementations.HttpServer
|
||||
public IDictionary<string, string> Headers => _options;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="StreamWriter" /> class.
|
||||
/// Initializes a new instance of the <see cref="RangeRequestWriter" /> class.
|
||||
/// </summary>
|
||||
/// <param name="rangeHeader">The range header.</param>
|
||||
/// <param name="contentLength">The content length.</param>
|
||||
/// <param name="source">The source.</param>
|
||||
/// <param name="contentType">Type of the content.</param>
|
||||
/// <param name="isHeadRequest">if set to <c>true</c> [is head request].</param>
|
||||
/// <param name="logger">The logger instance.</param>
|
||||
public RangeRequestWriter(string rangeHeader, long contentLength, Stream source, string contentType, bool isHeadRequest, ILogger logger)
|
||||
{
|
||||
if (string.IsNullOrEmpty(contentType))
|
||||
|
||||
@@ -8,11 +8,17 @@ using Microsoft.Net.Http.Headers;
|
||||
|
||||
namespace Emby.Server.Implementations.HttpServer
|
||||
{
|
||||
/// <summary>
|
||||
/// Class ResponseFilter.
|
||||
/// </summary>
|
||||
public class ResponseFilter
|
||||
{
|
||||
private static readonly CultureInfo _usCulture = CultureInfo.ReadOnly(new CultureInfo("en-US"));
|
||||
private readonly ILogger _logger;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ResponseFilter"/> class.
|
||||
/// </summary>
|
||||
/// <param name="logger">The logger.</param>
|
||||
public ResponseFilter(ILogger logger)
|
||||
{
|
||||
_logger = logger;
|
||||
@@ -37,7 +43,7 @@ namespace Emby.Server.Implementations.HttpServer
|
||||
|
||||
if (!string.IsNullOrEmpty(exception.Message))
|
||||
{
|
||||
var error = exception.Message.Replace(Environment.NewLine, " ");
|
||||
var error = exception.Message.Replace(Environment.NewLine, " ", StringComparison.Ordinal);
|
||||
error = RemoveControlCharacters(error);
|
||||
|
||||
res.Headers.Add("X-Application-Error-Code", error);
|
||||
@@ -55,7 +61,7 @@ namespace Emby.Server.Implementations.HttpServer
|
||||
if (hasHeaders.Headers.TryGetValue(HeaderNames.ContentLength, out string contentLength)
|
||||
&& !string.IsNullOrEmpty(contentLength))
|
||||
{
|
||||
var length = long.Parse(contentLength, _usCulture);
|
||||
var length = long.Parse(contentLength, CultureInfo.InvariantCulture);
|
||||
|
||||
if (length > 0)
|
||||
{
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
#pragma warning disable CS1591
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using Emby.Server.Implementations.SocketSharp;
|
||||
using MediaBrowser.Common.Net;
|
||||
using MediaBrowser.Controller.Configuration;
|
||||
using MediaBrowser.Controller.Entities;
|
||||
@@ -7,22 +10,27 @@ using MediaBrowser.Controller.Net;
|
||||
using MediaBrowser.Controller.Security;
|
||||
using MediaBrowser.Controller.Session;
|
||||
using MediaBrowser.Model.Services;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace Emby.Server.Implementations.HttpServer.Security
|
||||
{
|
||||
public class AuthService : IAuthService
|
||||
{
|
||||
private readonly ILogger<AuthService> _logger;
|
||||
private readonly IAuthorizationContext _authorizationContext;
|
||||
private readonly ISessionManager _sessionManager;
|
||||
private readonly IServerConfigurationManager _config;
|
||||
private readonly INetworkManager _networkManager;
|
||||
|
||||
public AuthService(
|
||||
ILogger<AuthService> logger,
|
||||
IAuthorizationContext authorizationContext,
|
||||
IServerConfigurationManager config,
|
||||
ISessionManager sessionManager,
|
||||
INetworkManager networkManager)
|
||||
{
|
||||
_logger = logger;
|
||||
_authorizationContext = authorizationContext;
|
||||
_config = config;
|
||||
_sessionManager = sessionManager;
|
||||
@@ -34,7 +42,14 @@ namespace Emby.Server.Implementations.HttpServer.Security
|
||||
ValidateUser(request, authAttribtues);
|
||||
}
|
||||
|
||||
private void ValidateUser(IRequest request, IAuthenticationAttributes authAttribtues)
|
||||
public User Authenticate(HttpRequest request, IAuthenticationAttributes authAttributes)
|
||||
{
|
||||
var req = new WebSocketSharpRequest(request, null, request.Path, _logger);
|
||||
var user = ValidateUser(req, authAttributes);
|
||||
return user;
|
||||
}
|
||||
|
||||
private User ValidateUser(IRequest request, IAuthenticationAttributes authAttribtues)
|
||||
{
|
||||
// This code is executed before the service
|
||||
var auth = _authorizationContext.GetAuthorizationInfo(request);
|
||||
@@ -81,6 +96,8 @@ namespace Emby.Server.Implementations.HttpServer.Security
|
||||
request.RemoteIp,
|
||||
user);
|
||||
}
|
||||
|
||||
return user;
|
||||
}
|
||||
|
||||
private void ValidateUserAccess(
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
#pragma warning disable CS1591
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
#pragma warning disable CS1591
|
||||
|
||||
using System;
|
||||
using MediaBrowser.Controller.Entities;
|
||||
using MediaBrowser.Controller.Library;
|
||||
|
||||
@@ -10,37 +10,20 @@ using Microsoft.Net.Http.Headers;
|
||||
namespace Emby.Server.Implementations.HttpServer
|
||||
{
|
||||
/// <summary>
|
||||
/// Class StreamWriter
|
||||
/// Class StreamWriter.
|
||||
/// </summary>
|
||||
public class StreamWriter : IAsyncStreamWriter, IHasHeaders
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the source stream.
|
||||
/// </summary>
|
||||
/// <value>The source stream.</value>
|
||||
private Stream SourceStream { get; set; }
|
||||
|
||||
private byte[] SourceBytes { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The _options
|
||||
/// The options.
|
||||
/// </summary>
|
||||
private readonly IDictionary<string, string> _options = new Dictionary<string, string>();
|
||||
/// <summary>
|
||||
/// Gets the options.
|
||||
/// </summary>
|
||||
/// <value>The options.</value>
|
||||
public IDictionary<string, string> Headers => _options;
|
||||
|
||||
public Action OnComplete { get; set; }
|
||||
public Action OnError { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="StreamWriter" /> class.
|
||||
/// </summary>
|
||||
/// <param name="source">The source.</param>
|
||||
/// <param name="contentType">Type of the content.</param>
|
||||
/// <param name="logger">The logger.</param>
|
||||
public StreamWriter(Stream source, string contentType)
|
||||
{
|
||||
if (string.IsNullOrEmpty(contentType))
|
||||
@@ -65,6 +48,7 @@ namespace Emby.Server.Implementations.HttpServer
|
||||
/// </summary>
|
||||
/// <param name="source">The source.</param>
|
||||
/// <param name="contentType">Type of the content.</param>
|
||||
/// <param name="contentLength">The content length.</param>
|
||||
public StreamWriter(byte[] source, string contentType, int contentLength)
|
||||
{
|
||||
if (string.IsNullOrEmpty(contentType))
|
||||
@@ -78,6 +62,31 @@ namespace Emby.Server.Implementations.HttpServer
|
||||
Headers[HeaderNames.ContentType] = contentType;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the source stream.
|
||||
/// </summary>
|
||||
/// <value>The source stream.</value>
|
||||
private Stream SourceStream { get; set; }
|
||||
|
||||
private byte[] SourceBytes { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the options.
|
||||
/// </summary>
|
||||
/// <value>The options.</value>
|
||||
public IDictionary<string, string> Headers => _options;
|
||||
|
||||
/// <summary>
|
||||
/// Fires when complete.
|
||||
/// </summary>
|
||||
public Action OnComplete { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Fires when an error occours.
|
||||
/// </summary>
|
||||
public Action OnError { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task WriteToAsync(Stream responseStream, CancellationToken cancellationToken)
|
||||
{
|
||||
try
|
||||
@@ -98,19 +107,13 @@ namespace Emby.Server.Implementations.HttpServer
|
||||
}
|
||||
catch
|
||||
{
|
||||
if (OnError != null)
|
||||
{
|
||||
OnError();
|
||||
}
|
||||
OnError?.Invoke();
|
||||
|
||||
throw;
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (OnComplete != null)
|
||||
{
|
||||
OnComplete();
|
||||
}
|
||||
OnComplete?.Invoke();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,7 +7,6 @@ using Emby.Server.Implementations.Net;
|
||||
using MediaBrowser.Controller.Net;
|
||||
using MediaBrowser.Model.Net;
|
||||
using MediaBrowser.Model.Serialization;
|
||||
using MediaBrowser.Model.Services;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using UtfUnknown;
|
||||
@@ -15,32 +14,74 @@ using UtfUnknown;
|
||||
namespace Emby.Server.Implementations.HttpServer
|
||||
{
|
||||
/// <summary>
|
||||
/// Class WebSocketConnection
|
||||
/// Class WebSocketConnection.
|
||||
/// </summary>
|
||||
public class WebSocketConnection : IWebSocketConnection
|
||||
{
|
||||
public event EventHandler<EventArgs> Closed;
|
||||
|
||||
/// <summary>
|
||||
/// The _socket
|
||||
/// </summary>
|
||||
private readonly IWebSocket _socket;
|
||||
|
||||
/// <summary>
|
||||
/// The _remote end point
|
||||
/// </summary>
|
||||
public string RemoteEndPoint { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// The logger
|
||||
/// The logger.
|
||||
/// </summary>
|
||||
private readonly ILogger _logger;
|
||||
|
||||
/// <summary>
|
||||
/// The _json serializer
|
||||
/// The json serializer.
|
||||
/// </summary>
|
||||
private readonly IJsonSerializer _jsonSerializer;
|
||||
|
||||
/// <summary>
|
||||
/// The socket.
|
||||
/// </summary>
|
||||
private readonly IWebSocket _socket;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="WebSocketConnection" /> class.
|
||||
/// </summary>
|
||||
/// <param name="socket">The socket.</param>
|
||||
/// <param name="remoteEndPoint">The remote end point.</param>
|
||||
/// <param name="jsonSerializer">The json serializer.</param>
|
||||
/// <param name="logger">The logger.</param>
|
||||
/// <exception cref="ArgumentNullException">socket</exception>
|
||||
public WebSocketConnection(IWebSocket socket, string remoteEndPoint, IJsonSerializer jsonSerializer, ILogger logger)
|
||||
{
|
||||
if (socket == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(socket));
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(remoteEndPoint))
|
||||
{
|
||||
throw new ArgumentNullException(nameof(remoteEndPoint));
|
||||
}
|
||||
|
||||
if (jsonSerializer == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(jsonSerializer));
|
||||
}
|
||||
|
||||
if (logger == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(logger));
|
||||
}
|
||||
|
||||
Id = Guid.NewGuid();
|
||||
_jsonSerializer = jsonSerializer;
|
||||
_socket = socket;
|
||||
_socket.OnReceiveBytes = OnReceiveInternal;
|
||||
|
||||
RemoteEndPoint = remoteEndPoint;
|
||||
_logger = logger;
|
||||
|
||||
socket.Closed += OnSocketClosed;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public event EventHandler<EventArgs> Closed;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the remote end point.
|
||||
/// </summary>
|
||||
public string RemoteEndPoint { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the receive action.
|
||||
/// </summary>
|
||||
@@ -64,6 +105,7 @@ namespace Emby.Server.Implementations.HttpServer
|
||||
/// </summary>
|
||||
/// <value>The URL.</value>
|
||||
public string Url { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the query string.
|
||||
/// </summary>
|
||||
@@ -71,44 +113,12 @@ namespace Emby.Server.Implementations.HttpServer
|
||||
public IQueryCollection QueryString { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="WebSocketConnection" /> class.
|
||||
/// Gets the state.
|
||||
/// </summary>
|
||||
/// <param name="socket">The socket.</param>
|
||||
/// <param name="remoteEndPoint">The remote end point.</param>
|
||||
/// <param name="jsonSerializer">The json serializer.</param>
|
||||
/// <param name="logger">The logger.</param>
|
||||
/// <exception cref="ArgumentNullException">socket</exception>
|
||||
public WebSocketConnection(IWebSocket socket, string remoteEndPoint, IJsonSerializer jsonSerializer, ILogger logger)
|
||||
{
|
||||
if (socket == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(socket));
|
||||
}
|
||||
if (string.IsNullOrEmpty(remoteEndPoint))
|
||||
{
|
||||
throw new ArgumentNullException(nameof(remoteEndPoint));
|
||||
}
|
||||
if (jsonSerializer == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(jsonSerializer));
|
||||
}
|
||||
if (logger == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(logger));
|
||||
}
|
||||
/// <value>The state.</value>
|
||||
public WebSocketState State => _socket.State;
|
||||
|
||||
Id = Guid.NewGuid();
|
||||
_jsonSerializer = jsonSerializer;
|
||||
_socket = socket;
|
||||
_socket.OnReceiveBytes = OnReceiveInternal;
|
||||
|
||||
RemoteEndPoint = remoteEndPoint;
|
||||
_logger = logger;
|
||||
|
||||
socket.Closed += socket_Closed;
|
||||
}
|
||||
|
||||
void socket_Closed(object sender, EventArgs e)
|
||||
void OnSocketClosed(object sender, EventArgs e)
|
||||
{
|
||||
Closed?.Invoke(this, EventArgs.Empty);
|
||||
}
|
||||
@@ -210,6 +220,7 @@ namespace Emby.Server.Implementations.HttpServer
|
||||
return _socket.SendAsync(buffer, true, cancellationToken);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task SendAsync(string text, CancellationToken cancellationToken)
|
||||
{
|
||||
if (string.IsNullOrEmpty(text))
|
||||
@@ -222,18 +233,11 @@ namespace Emby.Server.Implementations.HttpServer
|
||||
return _socket.SendAsync(text, true, cancellationToken);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the state.
|
||||
/// </summary>
|
||||
/// <value>The state.</value>
|
||||
public WebSocketState State => _socket.State;
|
||||
|
||||
/// <summary>
|
||||
/// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
|
||||
/// </summary>
|
||||
/// <inheritdoc />
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
#pragma warning disable CS1591
|
||||
|
||||
namespace Emby.Server.Implementations.IO
|
||||
{
|
||||
public class ExtendedFileSystemInfo
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
#pragma warning disable CS1591
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
#pragma warning disable CS1591
|
||||
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
@@ -16,22 +18,22 @@ namespace Emby.Server.Implementations.IO
|
||||
public class LibraryMonitor : ILibraryMonitor
|
||||
{
|
||||
/// <summary>
|
||||
/// The file system watchers
|
||||
/// The file system watchers.
|
||||
/// </summary>
|
||||
private readonly ConcurrentDictionary<string, FileSystemWatcher> _fileSystemWatchers = new ConcurrentDictionary<string, FileSystemWatcher>(StringComparer.OrdinalIgnoreCase);
|
||||
|
||||
/// <summary>
|
||||
/// The affected paths
|
||||
/// The affected paths.
|
||||
/// </summary>
|
||||
private readonly List<FileRefresher> _activeRefreshers = new List<FileRefresher>();
|
||||
|
||||
/// <summary>
|
||||
/// A dynamic list of paths that should be ignored. Added to during our own file sytem modifications.
|
||||
/// A dynamic list of paths that should be ignored. Added to during our own file system modifications.
|
||||
/// </summary>
|
||||
private readonly ConcurrentDictionary<string, string> _tempIgnoredPaths = new ConcurrentDictionary<string, string>(StringComparer.OrdinalIgnoreCase);
|
||||
|
||||
/// <summary>
|
||||
/// Any file name ending in any of these will be ignored by the watchers
|
||||
/// Any file name ending in any of these will be ignored by the watchers.
|
||||
/// </summary>
|
||||
private static readonly HashSet<string> _alwaysIgnoreFiles = new HashSet<string>(StringComparer.OrdinalIgnoreCase)
|
||||
{
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
#pragma warning disable CS1591
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
#pragma warning disable CS1591
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using MediaBrowser.Model.IO;
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
#pragma warning disable CS1591
|
||||
|
||||
using System;
|
||||
using System.Buffers;
|
||||
using System.IO;
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
#pragma warning disable CS1591
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
|
||||
@@ -42,6 +42,10 @@ namespace Emby.Server.Implementations.Library
|
||||
".grab",
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="CoreResolutionIgnoreRule"/> class.
|
||||
/// </summary>
|
||||
/// <param name="libraryManager">The library manager.</param>
|
||||
public CoreResolutionIgnoreRule(ILibraryManager libraryManager)
|
||||
{
|
||||
_libraryManager = libraryManager;
|
||||
|
||||
@@ -10,10 +10,17 @@ using MediaBrowser.Model.Cryptography;
|
||||
|
||||
namespace Emby.Server.Implementations.Library
|
||||
{
|
||||
/// <summary>
|
||||
/// The default authentication provider.
|
||||
/// </summary>
|
||||
public class DefaultAuthenticationProvider : IAuthenticationProvider, IRequiresResolvedUser
|
||||
{
|
||||
private readonly ICryptoProvider _cryptographyProvider;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="DefaultAuthenticationProvider"/> class.
|
||||
/// </summary>
|
||||
/// <param name="cryptographyProvider">The cryptography provider.</param>
|
||||
public DefaultAuthenticationProvider(ICryptoProvider cryptographyProvider)
|
||||
{
|
||||
_cryptographyProvider = cryptographyProvider;
|
||||
@@ -38,12 +45,13 @@ namespace Emby.Server.Implementations.Library
|
||||
// This is the version that we need to use for local users. Because reasons.
|
||||
public Task<ProviderAuthenticationResult> Authenticate(string username, string password, User resolvedUser)
|
||||
{
|
||||
bool success = false;
|
||||
if (resolvedUser == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(resolvedUser));
|
||||
}
|
||||
|
||||
bool success = false;
|
||||
|
||||
// As long as jellyfin supports passwordless users, we need this little block here to accommodate
|
||||
if (!HasPassword(resolvedUser) && string.IsNullOrEmpty(password))
|
||||
{
|
||||
@@ -59,7 +67,10 @@ namespace Emby.Server.Implementations.Library
|
||||
if (_cryptographyProvider.GetSupportedHashMethods().Contains(readyHash.Id)
|
||||
|| _cryptographyProvider.DefaultHashMethod == readyHash.Id)
|
||||
{
|
||||
byte[] calculatedHash = _cryptographyProvider.ComputeHash(readyHash.Id, passwordbytes, readyHash.Salt);
|
||||
byte[] calculatedHash = _cryptographyProvider.ComputeHash(
|
||||
readyHash.Id,
|
||||
passwordbytes,
|
||||
readyHash.Salt);
|
||||
|
||||
if (calculatedHash.SequenceEqual(readyHash.Hash))
|
||||
{
|
||||
|
||||
@@ -12,6 +12,9 @@ using MediaBrowser.Model.Users;
|
||||
|
||||
namespace Emby.Server.Implementations.Library
|
||||
{
|
||||
/// <summary>
|
||||
/// The default password reset provider.
|
||||
/// </summary>
|
||||
public class DefaultPasswordResetProvider : IPasswordResetProvider
|
||||
{
|
||||
private const string BaseResetFileName = "passwordreset";
|
||||
@@ -22,6 +25,12 @@ namespace Emby.Server.Implementations.Library
|
||||
private readonly string _passwordResetFileBase;
|
||||
private readonly string _passwordResetFileBaseDir;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="DefaultPasswordResetProvider"/> class.
|
||||
/// </summary>
|
||||
/// <param name="configurationManager">The configuration manager.</param>
|
||||
/// <param name="jsonSerializer">The JSON serializer.</param>
|
||||
/// <param name="userManager">The user manager.</param>
|
||||
public DefaultPasswordResetProvider(
|
||||
IServerConfigurationManager configurationManager,
|
||||
IJsonSerializer jsonSerializer,
|
||||
@@ -56,8 +65,8 @@ namespace Emby.Server.Implementations.Library
|
||||
File.Delete(resetfile);
|
||||
}
|
||||
else if (string.Equals(
|
||||
spr.Pin.Replace("-", string.Empty),
|
||||
pin.Replace("-", string.Empty),
|
||||
spr.Pin.Replace("-", string.Empty, StringComparison.Ordinal),
|
||||
pin.Replace("-", string.Empty, StringComparison.Ordinal),
|
||||
StringComparison.InvariantCultureIgnoreCase))
|
||||
{
|
||||
var resetUser = _userManager.GetUserByName(spr.UserName);
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
#pragma warning disable CS1591
|
||||
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using System.Threading;
|
||||
|
||||
@@ -4,37 +4,48 @@ using MediaBrowser.Controller.Entities;
|
||||
|
||||
namespace Emby.Server.Implementations.Library
|
||||
{
|
||||
/// <summary>
|
||||
/// An invalid authentication provider.
|
||||
/// </summary>
|
||||
public class InvalidAuthProvider : IAuthenticationProvider
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public string Name => "InvalidOrMissingAuthenticationProvider";
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool IsEnabled => true;
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task<ProviderAuthenticationResult> Authenticate(string username, string password)
|
||||
{
|
||||
throw new AuthenticationException("User Account cannot login with this provider. The Normal provider for this user cannot be found");
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool HasPassword(User user)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task ChangePassword(User user, string newPassword)
|
||||
{
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void ChangeEasyPassword(User user, string newPassword, string newPasswordHash)
|
||||
{
|
||||
// Nothing here
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public string GetPasswordHash(User user)
|
||||
{
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public string GetEasyPasswordHash(User user)
|
||||
{
|
||||
return string.Empty;
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
#pragma warning disable CS1591
|
||||
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
@@ -390,9 +392,9 @@ namespace Emby.Server.Implementations.Library
|
||||
// Add this flag to GetDeletePaths if required in the future
|
||||
var isRequiredForDelete = true;
|
||||
|
||||
foreach (var fileSystemInfo in item.GetDeletePaths().ToList())
|
||||
foreach (var fileSystemInfo in item.GetDeletePaths())
|
||||
{
|
||||
if (File.Exists(fileSystemInfo.FullName))
|
||||
if (Directory.Exists(fileSystemInfo.FullName) || File.Exists(fileSystemInfo.FullName))
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -829,7 +831,7 @@ namespace Emby.Server.Implementations.Library
|
||||
{
|
||||
Path = path,
|
||||
IsFolder = isFolder,
|
||||
OrderBy = new[] { ItemSortBy.DateCreated }.Select(i => new ValueTuple<string, SortOrder>(i, SortOrder.Descending)).ToArray(),
|
||||
OrderBy = new[] { (ItemSortBy.DateCreated, SortOrder.Descending) },
|
||||
Limit = 1,
|
||||
DtoOptions = new DtoOptions(true)
|
||||
};
|
||||
@@ -1257,7 +1259,7 @@ namespace Emby.Server.Implementations.Library
|
||||
|
||||
public List<BaseItem> GetItemList(InternalItemsQuery query, bool allowExternalContent)
|
||||
{
|
||||
if (query.Recursive && !query.ParentId.Equals(Guid.Empty))
|
||||
if (query.Recursive && query.ParentId != Guid.Empty)
|
||||
{
|
||||
var parent = GetItemById(query.ParentId);
|
||||
if (parent != null)
|
||||
@@ -1899,7 +1901,7 @@ namespace Emby.Server.Implementations.Library
|
||||
/// <param name="cancellationToken">The cancellation token.</param>
|
||||
public void UpdateItem(BaseItem item, BaseItem parent, ItemUpdateType updateReason, CancellationToken cancellationToken)
|
||||
{
|
||||
UpdateItems(new [] { item }, parent, updateReason, cancellationToken);
|
||||
UpdateItems(new[] { item }, parent, updateReason, cancellationToken);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -2175,7 +2177,6 @@ namespace Emby.Server.Implementations.Library
|
||||
DisplayParentId = parentId
|
||||
};
|
||||
|
||||
|
||||
CreateItem(item, null);
|
||||
|
||||
isNew = true;
|
||||
@@ -2197,7 +2198,6 @@ namespace Emby.Server.Implementations.Library
|
||||
{
|
||||
// Need to force save to increment DateLastSaved
|
||||
ForceSave = true
|
||||
|
||||
},
|
||||
RefreshPriority.Normal);
|
||||
}
|
||||
@@ -2487,6 +2487,15 @@ namespace Emby.Server.Implementations.Library
|
||||
{
|
||||
episode.ParentIndexNumber = season.IndexNumber;
|
||||
}
|
||||
else
|
||||
{
|
||||
/*
|
||||
Anime series don't generally have a season in their file name, however,
|
||||
tvdb needs a season to correctly get the metadata.
|
||||
Hence, a null season needs to be filled with something. */
|
||||
//FIXME perhaps this would be better for tvdb parser to ask for season 1 if no season is specified
|
||||
episode.ParentIndexNumber = 1;
|
||||
}
|
||||
|
||||
if (episode.ParentIndexNumber.HasValue)
|
||||
{
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
#pragma warning disable CS1591
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
#pragma warning disable CS1591
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
#pragma warning disable CS1591
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
#pragma warning disable CS1591
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
@@ -89,10 +91,9 @@ namespace Emby.Server.Implementations.Library
|
||||
|
||||
Limit = 200,
|
||||
|
||||
OrderBy = new[] { new ValueTuple<string, SortOrder>(ItemSortBy.Random, SortOrder.Ascending) },
|
||||
OrderBy = new[] { (ItemSortBy.Random, SortOrder.Ascending) },
|
||||
|
||||
DtoOptions = dtoOptions
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -3,6 +3,9 @@ using System.Text.RegularExpressions;
|
||||
|
||||
namespace Emby.Server.Implementations.Library
|
||||
{
|
||||
/// <summary>
|
||||
/// Class providing extension methods for working with paths.
|
||||
/// </summary>
|
||||
public static class PathExtensions
|
||||
{
|
||||
/// <summary>
|
||||
@@ -32,6 +35,7 @@ namespace Emby.Server.Implementations.Library
|
||||
int end = str.IndexOf(']', start);
|
||||
return str.Substring(start, end - start);
|
||||
}
|
||||
|
||||
// for imdbid we also accept pattern matching
|
||||
if (string.Equals(attrib, "imdbid", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
#pragma warning disable CS1591
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
@@ -13,7 +15,7 @@ using MediaBrowser.Model.IO;
|
||||
namespace Emby.Server.Implementations.Library.Resolvers.Audio
|
||||
{
|
||||
/// <summary>
|
||||
/// Class AudioResolver
|
||||
/// Class AudioResolver.
|
||||
/// </summary>
|
||||
public class AudioResolver : ItemResolver<MediaBrowser.Controller.Entities.Audio.Audio>, IMultiItemResolver
|
||||
{
|
||||
|
||||
@@ -13,7 +13,7 @@ using Microsoft.Extensions.Logging;
|
||||
namespace Emby.Server.Implementations.Library.Resolvers.Audio
|
||||
{
|
||||
/// <summary>
|
||||
/// Class MusicAlbumResolver
|
||||
/// Class MusicAlbumResolver.
|
||||
/// </summary>
|
||||
public class MusicAlbumResolver : ItemResolver<MusicAlbum>
|
||||
{
|
||||
@@ -21,6 +21,12 @@ namespace Emby.Server.Implementations.Library.Resolvers.Audio
|
||||
private readonly IFileSystem _fileSystem;
|
||||
private readonly ILibraryManager _libraryManager;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="MusicAlbumResolver"/> class.
|
||||
/// </summary>
|
||||
/// <param name="logger">The logger.</param>
|
||||
/// <param name="fileSystem">The file system.</param>
|
||||
/// <param name="libraryManager">The library manager.</param>
|
||||
public MusicAlbumResolver(ILogger logger, IFileSystem fileSystem, ILibraryManager libraryManager)
|
||||
{
|
||||
_logger = logger;
|
||||
@@ -50,16 +56,25 @@ namespace Emby.Server.Implementations.Library.Resolvers.Audio
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!args.IsDirectory) return null;
|
||||
if (!args.IsDirectory)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
// Avoid mis-identifying top folders
|
||||
if (args.HasParent<MusicAlbum>()) return null;
|
||||
if (args.Parent.IsRoot) return null;
|
||||
if (args.HasParent<MusicAlbum>())
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
if (args.Parent.IsRoot)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return IsMusicAlbum(args) ? new MusicAlbum() : null;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Determine if the supplied file data points to a music album
|
||||
/// </summary>
|
||||
@@ -78,8 +93,11 @@ namespace Emby.Server.Implementations.Library.Resolvers.Audio
|
||||
// Args points to an album if parent is an Artist folder or it directly contains music
|
||||
if (args.IsDirectory)
|
||||
{
|
||||
//if (args.Parent is MusicArtist) return true; //saves us from testing children twice
|
||||
if (ContainsMusic(args.FileSystemChildren, true, args.DirectoryService, _logger, _fileSystem, args.GetLibraryOptions(), _libraryManager)) return true;
|
||||
// if (args.Parent is MusicArtist) return true; //saves us from testing children twice
|
||||
if (ContainsMusic(args.FileSystemChildren, true, args.DirectoryService, _logger, _fileSystem, args.GetLibraryOptions(), _libraryManager))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
@@ -88,7 +106,8 @@ namespace Emby.Server.Implementations.Library.Resolvers.Audio
|
||||
/// <summary>
|
||||
/// Determine if the supplied list contains what we should consider music
|
||||
/// </summary>
|
||||
private bool ContainsMusic(IEnumerable<FileSystemMetadata> list,
|
||||
private bool ContainsMusic(
|
||||
IEnumerable<FileSystemMetadata> list,
|
||||
bool allowSubfolders,
|
||||
IDirectoryService directoryService,
|
||||
ILogger logger,
|
||||
|
||||
@@ -11,7 +11,7 @@ using Microsoft.Extensions.Logging;
|
||||
namespace Emby.Server.Implementations.Library.Resolvers.Audio
|
||||
{
|
||||
/// <summary>
|
||||
/// Class MusicArtistResolver
|
||||
/// Class MusicArtistResolver.
|
||||
/// </summary>
|
||||
public class MusicArtistResolver : ItemResolver<MusicArtist>
|
||||
{
|
||||
@@ -20,6 +20,13 @@ namespace Emby.Server.Implementations.Library.Resolvers.Audio
|
||||
private readonly ILibraryManager _libraryManager;
|
||||
private readonly IServerConfigurationManager _config;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="MusicArtistResolver"/> class.
|
||||
/// </summary>
|
||||
/// <param name="logger">The logger.</param>
|
||||
/// <param name="fileSystem">The file system.</param>
|
||||
/// <param name="libraryManager">The library manager.</param>
|
||||
/// <param name="config">The configuration manager.</param>
|
||||
public MusicArtistResolver(ILogger logger, IFileSystem fileSystem, ILibraryManager libraryManager, IServerConfigurationManager config)
|
||||
{
|
||||
_logger = logger;
|
||||
@@ -41,7 +48,10 @@ namespace Emby.Server.Implementations.Library.Resolvers.Audio
|
||||
/// <returns>MusicArtist.</returns>
|
||||
protected override MusicArtist Resolve(ItemResolveArgs args)
|
||||
{
|
||||
if (!args.IsDirectory) return null;
|
||||
if (!args.IsDirectory)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
// Don't allow nested artists
|
||||
if (args.HasParent<MusicArtist>() || args.HasParent<MusicAlbum>())
|
||||
@@ -79,6 +89,5 @@ namespace Emby.Server.Implementations.Library.Resolvers.Audio
|
||||
// If we contain an album assume we are an artist folder
|
||||
return args.FileSystemChildren.Where(i => i.IsDirectory).Any(i => albumResolver.IsMusicAlbum(i.FullName, directoryService, args.GetLibraryOptions())) ? new MusicArtist() : null;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
#pragma warning disable CS1591
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
@@ -10,7 +12,7 @@ using MediaBrowser.Model.Entities;
|
||||
namespace Emby.Server.Implementations.Library.Resolvers
|
||||
{
|
||||
/// <summary>
|
||||
/// Resolves a Path into a Video or Video subclass
|
||||
/// Resolves a Path into a Video or Video subclass.
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
public abstract class BaseVideoResolver<T> : MediaBrowser.Controller.Resolvers.ItemResolver<T>
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
#pragma warning disable CS1591
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
@@ -7,18 +9,10 @@ using MediaBrowser.Model.Entities;
|
||||
|
||||
namespace Emby.Server.Implementations.Library.Resolvers.Books
|
||||
{
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public class BookResolver : MediaBrowser.Controller.Resolvers.ItemResolver<Book>
|
||||
{
|
||||
private readonly string[] _validExtensions = { ".pdf", ".epub", ".mobi", ".cbr", ".cbz", ".azw3" };
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="args"></param>
|
||||
/// <returns></returns>
|
||||
protected override Book Resolve(ItemResolveArgs args)
|
||||
{
|
||||
var collectionType = args.GetCollectionType();
|
||||
@@ -47,11 +41,6 @@ namespace Emby.Server.Implementations.Library.Resolvers.Books
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="args"></param>
|
||||
/// <returns></returns>
|
||||
private Book GetBook(ItemResolveArgs args)
|
||||
{
|
||||
var bookFiles = args.FileSystemChildren.Where(f =>
|
||||
|
||||
@@ -5,7 +5,7 @@ using MediaBrowser.Controller.Resolvers;
|
||||
namespace Emby.Server.Implementations.Library.Resolvers
|
||||
{
|
||||
/// <summary>
|
||||
/// Class FolderResolver
|
||||
/// Class FolderResolver.
|
||||
/// </summary>
|
||||
public class FolderResolver : FolderResolver<Folder>
|
||||
{
|
||||
@@ -32,7 +32,7 @@ namespace Emby.Server.Implementations.Library.Resolvers
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Class FolderResolver
|
||||
/// Class FolderResolver.
|
||||
/// </summary>
|
||||
/// <typeparam name="TItemType">The type of the T item type.</typeparam>
|
||||
public abstract class FolderResolver<TItemType> : ItemResolver<TItemType>
|
||||
|
||||
@@ -5,7 +5,7 @@ using MediaBrowser.Controller.Resolvers;
|
||||
namespace Emby.Server.Implementations.Library.Resolvers
|
||||
{
|
||||
/// <summary>
|
||||
/// Class ItemResolver
|
||||
/// Class ItemResolver.
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
public abstract class ItemResolver<T> : IItemResolver
|
||||
|
||||
@@ -4,12 +4,11 @@ using MediaBrowser.Controller.Entities;
|
||||
using MediaBrowser.Controller.Entities.Movies;
|
||||
using MediaBrowser.Controller.Library;
|
||||
using MediaBrowser.Model.Entities;
|
||||
using MediaBrowser.Model.Extensions;
|
||||
|
||||
namespace Emby.Server.Implementations.Library.Resolvers.Movies
|
||||
{
|
||||
/// <summary>
|
||||
/// Class BoxSetResolver
|
||||
/// Class BoxSetResolver.
|
||||
/// </summary>
|
||||
public class BoxSetResolver : FolderResolver<BoxSet>
|
||||
{
|
||||
@@ -63,7 +62,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.Movies
|
||||
/// <param name="item">The item.</param>
|
||||
private static void SetProviderIdFromPath(BaseItem item)
|
||||
{
|
||||
//we need to only look at the name of this actual item (not parents)
|
||||
// we need to only look at the name of this actual item (not parents)
|
||||
var justName = Path.GetFileName(item.Path);
|
||||
|
||||
var id = justName.GetAttributeValue("tmdbid");
|
||||
|
||||
@@ -17,7 +17,7 @@ using MediaBrowser.Model.IO;
|
||||
namespace Emby.Server.Implementations.Library.Resolvers.Movies
|
||||
{
|
||||
/// <summary>
|
||||
/// Class MovieResolver
|
||||
/// Class MovieResolver.
|
||||
/// </summary>
|
||||
public class MovieResolver : BaseVideoResolver<Video>, IMultiItemResolver
|
||||
{
|
||||
@@ -27,6 +27,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.Movies
|
||||
/// <value>The priority.</value>
|
||||
public override ResolverPriority Priority => ResolverPriority.Third;
|
||||
|
||||
/// <inheritdoc />
|
||||
public MultiItemResolverResult ResolveMultiple(
|
||||
Folder parent,
|
||||
List<FileSystemMetadata> files,
|
||||
@@ -522,7 +523,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.Movies
|
||||
CollectionType.MusicVideos,
|
||||
CollectionType.Movies,
|
||||
CollectionType.Photos
|
||||
};
|
||||
};
|
||||
|
||||
private bool IsInvalid(Folder parent, string collectionType)
|
||||
{
|
||||
@@ -544,6 +545,11 @@ namespace Emby.Server.Implementations.Library.Resolvers.Movies
|
||||
|
||||
private IImageProcessor _imageProcessor;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="MovieResolver"/> class.
|
||||
/// </summary>
|
||||
/// <param name="libraryManager">The library manager.</param>
|
||||
/// <param name="imageProcessor">The image processor.</param>
|
||||
public MovieResolver(ILibraryManager libraryManager, IImageProcessor imageProcessor)
|
||||
: base(libraryManager)
|
||||
{
|
||||
|
||||
@@ -7,11 +7,19 @@ using MediaBrowser.Model.Entities;
|
||||
|
||||
namespace Emby.Server.Implementations.Library.Resolvers
|
||||
{
|
||||
/// <summary>
|
||||
/// Class PhotoAlbumResolver.
|
||||
/// </summary>
|
||||
public class PhotoAlbumResolver : FolderResolver<PhotoAlbum>
|
||||
{
|
||||
private readonly IImageProcessor _imageProcessor;
|
||||
private ILibraryManager _libraryManager;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="PhotoAlbumResolver"/> class.
|
||||
/// </summary>
|
||||
/// <param name="imageProcessor">The image processor.</param>
|
||||
/// <param name="libraryManager">The library manager.</param>
|
||||
public PhotoAlbumResolver(IImageProcessor imageProcessor, ILibraryManager libraryManager)
|
||||
{
|
||||
_imageProcessor = imageProcessor;
|
||||
@@ -74,9 +82,11 @@ namespace Emby.Server.Implementations.Library.Resolvers
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override ResolverPriority Priority => ResolverPriority.Second;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
#pragma warning disable CS1591
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
#pragma warning disable CS1591
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using MediaBrowser.Controller.Library;
|
||||
using MediaBrowser.Controller.Playlists;
|
||||
using MediaBrowser.Model.Entities;
|
||||
using MediaBrowser.Model.Extensions;
|
||||
|
||||
namespace Emby.Server.Implementations.Library.Resolvers
|
||||
{
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
#pragma warning disable CS1591
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
|
||||
@@ -7,7 +7,7 @@ using MediaBrowser.Model.Entities;
|
||||
namespace Emby.Server.Implementations.Library.Resolvers.TV
|
||||
{
|
||||
/// <summary>
|
||||
/// Class EpisodeResolver
|
||||
/// Class EpisodeResolver.
|
||||
/// </summary>
|
||||
public class EpisodeResolver : BaseVideoResolver<Episode>
|
||||
{
|
||||
@@ -26,6 +26,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.TV
|
||||
}
|
||||
|
||||
var season = parent as Season;
|
||||
|
||||
// Just in case the user decided to nest episodes.
|
||||
// Not officially supported but in some cases we can handle it.
|
||||
if (season == null)
|
||||
@@ -73,6 +74,10 @@ namespace Emby.Server.Implementations.Library.Resolvers.TV
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="EpisodeResolver"/> class.
|
||||
/// </summary>
|
||||
/// <param name="libraryManager">The library manager.</param>
|
||||
public EpisodeResolver(ILibraryManager libraryManager)
|
||||
: base(libraryManager)
|
||||
{
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
#pragma warning disable CS1591
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
@@ -14,7 +16,7 @@ using Microsoft.Extensions.Logging;
|
||||
namespace Emby.Server.Implementations.Library.Resolvers.TV
|
||||
{
|
||||
/// <summary>
|
||||
/// Class SeriesResolver
|
||||
/// Class SeriesResolver.
|
||||
/// </summary>
|
||||
public class SeriesResolver : FolderResolver<Series>
|
||||
{
|
||||
@@ -22,6 +24,12 @@ namespace Emby.Server.Implementations.Library.Resolvers.TV
|
||||
private readonly ILogger _logger;
|
||||
private readonly ILibraryManager _libraryManager;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="SeriesResolver"/> class.
|
||||
/// </summary>
|
||||
/// <param name="fileSystem">The file system.</param>
|
||||
/// <param name="logger">The logger.</param>
|
||||
/// <param name="libraryManager">The library manager.</param>
|
||||
public SeriesResolver(IFileSystem fileSystem, ILogger logger, ILibraryManager libraryManager)
|
||||
{
|
||||
_fileSystem = fileSystem;
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
#pragma warning disable CS1591
|
||||
|
||||
using MediaBrowser.Controller.Entities;
|
||||
using MediaBrowser.Controller.Library;
|
||||
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
#pragma warning disable CS1591
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
@@ -13,8 +15,6 @@ using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace Emby.Server.Implementations.Library
|
||||
{
|
||||
/// <summary>
|
||||
/// </summary>
|
||||
public class SearchEngine : ISearchEngine
|
||||
{
|
||||
private readonly ILibraryManager _libraryManager;
|
||||
@@ -83,7 +83,7 @@ namespace Emby.Server.Implementations.Library
|
||||
|
||||
if (string.IsNullOrEmpty(searchTerm))
|
||||
{
|
||||
throw new ArgumentNullException(nameof(searchTerm));
|
||||
throw new ArgumentNullException("SearchTerm can't be empty.", nameof(searchTerm));
|
||||
}
|
||||
|
||||
searchTerm = searchTerm.Trim().RemoveDiacritics();
|
||||
@@ -162,7 +162,7 @@ namespace Emby.Server.Implementations.Library
|
||||
Limit = query.Limit,
|
||||
IncludeItemsByName = string.IsNullOrEmpty(query.ParentId),
|
||||
ParentId = string.IsNullOrEmpty(query.ParentId) ? Guid.Empty : new Guid(query.ParentId),
|
||||
OrderBy = new[] { new ValueTuple<string, SortOrder>(ItemSortBy.SortName, SortOrder.Ascending) },
|
||||
OrderBy = new[] { (ItemSortBy.SortName, SortOrder.Ascending) },
|
||||
Recursive = true,
|
||||
|
||||
IsKids = query.IsKids,
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
#pragma warning disable CS1591
|
||||
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
@@ -15,7 +17,7 @@ using Microsoft.Extensions.Logging;
|
||||
namespace Emby.Server.Implementations.Library
|
||||
{
|
||||
/// <summary>
|
||||
/// Class UserDataManager
|
||||
/// Class UserDataManager.
|
||||
/// </summary>
|
||||
public class UserDataManager : IUserDataManager
|
||||
{
|
||||
@@ -55,6 +57,7 @@ namespace Emby.Server.Implementations.Library
|
||||
{
|
||||
throw new ArgumentNullException(nameof(userData));
|
||||
}
|
||||
|
||||
if (item == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(item));
|
||||
@@ -160,11 +163,6 @@ namespace Emby.Server.Implementations.Library
|
||||
return GetUserData(user, item.Id, item.GetUserDataKeys());
|
||||
}
|
||||
|
||||
public UserItemData GetUserData(string userId, BaseItem item)
|
||||
{
|
||||
return GetUserData(new Guid(userId), item);
|
||||
}
|
||||
|
||||
public UserItemData GetUserData(Guid userId, BaseItem item)
|
||||
{
|
||||
return GetUserData(userId, item.Id, item.GetUserDataKeys());
|
||||
@@ -228,24 +226,21 @@ namespace Emby.Server.Implementations.Library
|
||||
{
|
||||
var pctIn = decimal.Divide(positionTicks, runtimeTicks) * 100;
|
||||
|
||||
// Don't track in very beginning
|
||||
if (pctIn < _config.Configuration.MinResumePct)
|
||||
{
|
||||
// ignore progress during the beginning
|
||||
positionTicks = 0;
|
||||
}
|
||||
|
||||
// If we're at the end, assume completed
|
||||
else if (pctIn > _config.Configuration.MaxResumePct || positionTicks >= runtimeTicks)
|
||||
{
|
||||
// mark as completed close to the end
|
||||
positionTicks = 0;
|
||||
data.Played = playedToCompletion = true;
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
// Enforce MinResumeDuration
|
||||
var durationSeconds = TimeSpan.FromTicks(runtimeTicks).TotalSeconds;
|
||||
|
||||
if (durationSeconds < _config.Configuration.MinResumeDurationSeconds)
|
||||
{
|
||||
positionTicks = 0;
|
||||
@@ -265,6 +260,7 @@ namespace Emby.Server.Implementations.Library
|
||||
positionTicks = 0;
|
||||
data.Played = false;
|
||||
}
|
||||
|
||||
if (!item.SupportsPositionTicksResume)
|
||||
{
|
||||
positionTicks = 0;
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
#pragma warning disable CS1591
|
||||
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
@@ -8,7 +10,6 @@ using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using MediaBrowser.Common;
|
||||
using MediaBrowser.Common.Cryptography;
|
||||
using MediaBrowser.Common.Events;
|
||||
using MediaBrowser.Common.Net;
|
||||
@@ -25,6 +26,7 @@ using MediaBrowser.Controller.Providers;
|
||||
using MediaBrowser.Controller.Security;
|
||||
using MediaBrowser.Controller.Session;
|
||||
using MediaBrowser.Model.Configuration;
|
||||
using MediaBrowser.Model.Cryptography;
|
||||
using MediaBrowser.Model.Dto;
|
||||
using MediaBrowser.Model.Entities;
|
||||
using MediaBrowser.Model.Events;
|
||||
@@ -36,19 +38,19 @@ using Microsoft.Extensions.Logging;
|
||||
namespace Emby.Server.Implementations.Library
|
||||
{
|
||||
/// <summary>
|
||||
/// Class UserManager
|
||||
/// Class UserManager.
|
||||
/// </summary>
|
||||
public class UserManager : IUserManager
|
||||
{
|
||||
private readonly object _policySyncLock = new object();
|
||||
private readonly object _configSyncLock = new object();
|
||||
/// <summary>
|
||||
/// The _logger
|
||||
/// The logger.
|
||||
/// </summary>
|
||||
private readonly ILogger _logger;
|
||||
|
||||
private readonly object _policySyncLock = new object();
|
||||
|
||||
/// <summary>
|
||||
/// Gets the active user repository
|
||||
/// Gets the active user repository.
|
||||
/// </summary>
|
||||
/// <value>The user repository.</value>
|
||||
private readonly IUserRepository _userRepository;
|
||||
@@ -60,6 +62,7 @@ namespace Emby.Server.Implementations.Library
|
||||
private readonly Func<IDtoService> _dtoServiceFactory;
|
||||
private readonly IServerApplicationHost _appHost;
|
||||
private readonly IFileSystem _fileSystem;
|
||||
private readonly ICryptoProvider _cryptoProvider;
|
||||
|
||||
private ConcurrentDictionary<Guid, User> _users;
|
||||
|
||||
@@ -80,7 +83,8 @@ namespace Emby.Server.Implementations.Library
|
||||
Func<IDtoService> dtoServiceFactory,
|
||||
IServerApplicationHost appHost,
|
||||
IJsonSerializer jsonSerializer,
|
||||
IFileSystem fileSystem)
|
||||
IFileSystem fileSystem,
|
||||
ICryptoProvider cryptoProvider)
|
||||
{
|
||||
_logger = logger;
|
||||
_userRepository = userRepository;
|
||||
@@ -91,6 +95,7 @@ namespace Emby.Server.Implementations.Library
|
||||
_appHost = appHost;
|
||||
_jsonSerializer = jsonSerializer;
|
||||
_fileSystem = fileSystem;
|
||||
_cryptoProvider = cryptoProvider;
|
||||
_users = null;
|
||||
}
|
||||
|
||||
@@ -179,12 +184,7 @@ namespace Emby.Server.Implementations.Library
|
||||
_defaultPasswordResetProvider = passwordResetProviders.OfType<DefaultPasswordResetProvider>().First();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a User by Id.
|
||||
/// </summary>
|
||||
/// <param name="id">The id.</param>
|
||||
/// <returns>User.</returns>
|
||||
/// <exception cref="ArgumentException"></exception>
|
||||
/// <inheritdoc />
|
||||
public User GetUserById(Guid id)
|
||||
{
|
||||
if (id == Guid.Empty)
|
||||
@@ -196,14 +196,6 @@ namespace Emby.Server.Implementations.Library
|
||||
return user;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the user by identifier.
|
||||
/// </summary>
|
||||
/// <param name="id">The identifier.</param>
|
||||
/// <returns>User.</returns>
|
||||
public User GetUserById(string id)
|
||||
=> GetUserById(new Guid(id));
|
||||
|
||||
public User GetUserByName(string name)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(name))
|
||||
@@ -263,7 +255,12 @@ namespace Emby.Server.Implementations.Library
|
||||
return builder.ToString();
|
||||
}
|
||||
|
||||
public async Task<User> AuthenticateUser(string username, string password, string hashedPassword, string remoteEndPoint, bool isUserSession)
|
||||
public async Task<User> AuthenticateUser(
|
||||
string username,
|
||||
string password,
|
||||
string hashedPassword,
|
||||
string remoteEndPoint,
|
||||
bool isUserSession)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(username))
|
||||
{
|
||||
@@ -364,6 +361,8 @@ namespace Emby.Server.Implementations.Library
|
||||
return success ? user : null;
|
||||
}
|
||||
|
||||
#nullable enable
|
||||
|
||||
private static string GetAuthenticationProviderId(IAuthenticationProvider provider)
|
||||
{
|
||||
return provider.GetType().FullName;
|
||||
@@ -384,7 +383,7 @@ namespace Emby.Server.Implementations.Library
|
||||
return GetPasswordResetProviders(user)[0];
|
||||
}
|
||||
|
||||
private IAuthenticationProvider[] GetAuthenticationProviders(User user)
|
||||
private IAuthenticationProvider[] GetAuthenticationProviders(User? user)
|
||||
{
|
||||
var authenticationProviderId = user?.Policy.AuthenticationProviderId;
|
||||
|
||||
@@ -398,14 +397,14 @@ namespace Emby.Server.Implementations.Library
|
||||
if (providers.Length == 0)
|
||||
{
|
||||
// Assign the user to the InvalidAuthProvider since no configured auth provider was valid/found
|
||||
_logger.LogWarning("User {UserName} was found with invalid/missing Authentication Provider {AuthenticationProviderId}. Assigning user to InvalidAuthProvider until this is corrected", user.Name, user.Policy.AuthenticationProviderId);
|
||||
_logger.LogWarning("User {UserName} was found with invalid/missing Authentication Provider {AuthenticationProviderId}. Assigning user to InvalidAuthProvider until this is corrected", user?.Name, user?.Policy.AuthenticationProviderId);
|
||||
providers = new IAuthenticationProvider[] { _invalidAuthProvider };
|
||||
}
|
||||
|
||||
return providers;
|
||||
}
|
||||
|
||||
private IPasswordResetProvider[] GetPasswordResetProviders(User user)
|
||||
private IPasswordResetProvider[] GetPasswordResetProviders(User? user)
|
||||
{
|
||||
var passwordResetProviderId = user?.Policy.PasswordResetProviderId;
|
||||
|
||||
@@ -424,11 +423,14 @@ namespace Emby.Server.Implementations.Library
|
||||
return providers;
|
||||
}
|
||||
|
||||
private async Task<(string username, bool success)> AuthenticateWithProvider(IAuthenticationProvider provider, string username, string password, User resolvedUser)
|
||||
private async Task<(string username, bool success)> AuthenticateWithProvider(
|
||||
IAuthenticationProvider provider,
|
||||
string username,
|
||||
string password,
|
||||
User? resolvedUser)
|
||||
{
|
||||
try
|
||||
{
|
||||
|
||||
var authenticationResult = provider is IRequiresResolvedUser requiresResolvedUser
|
||||
? await requiresResolvedUser.Authenticate(username, password, resolvedUser).ConfigureAwait(false)
|
||||
: await provider.Authenticate(username, password).ConfigureAwait(false);
|
||||
@@ -449,15 +451,15 @@ namespace Emby.Server.Implementations.Library
|
||||
}
|
||||
}
|
||||
|
||||
private async Task<(IAuthenticationProvider authenticationProvider, string username, bool success)> AuthenticateLocalUser(
|
||||
private async Task<(IAuthenticationProvider? authenticationProvider, string username, bool success)> AuthenticateLocalUser(
|
||||
string username,
|
||||
string password,
|
||||
string hashedPassword,
|
||||
User user,
|
||||
User? user,
|
||||
string remoteEndPoint)
|
||||
{
|
||||
bool success = false;
|
||||
IAuthenticationProvider authenticationProvider = null;
|
||||
IAuthenticationProvider? authenticationProvider = null;
|
||||
|
||||
foreach (var provider in GetAuthenticationProviders(user))
|
||||
{
|
||||
@@ -475,24 +477,21 @@ namespace Emby.Server.Implementations.Library
|
||||
|
||||
if (!success
|
||||
&& _networkManager.IsInLocalNetwork(remoteEndPoint)
|
||||
&& user.Configuration.EnableLocalPassword)
|
||||
&& user?.Configuration.EnableLocalPassword == true
|
||||
&& !string.IsNullOrEmpty(user.EasyPassword))
|
||||
{
|
||||
success = string.Equals(
|
||||
GetLocalPasswordHash(user),
|
||||
_defaultAuthenticationProvider.GetHashedString(user, password),
|
||||
StringComparison.OrdinalIgnoreCase);
|
||||
// Check easy password
|
||||
var passwordHash = PasswordHash.Parse(user.EasyPassword);
|
||||
var hash = _cryptoProvider.ComputeHash(
|
||||
passwordHash.Id,
|
||||
Encoding.UTF8.GetBytes(password),
|
||||
passwordHash.Salt);
|
||||
success = passwordHash.Hash.SequenceEqual(hash);
|
||||
}
|
||||
|
||||
return (authenticationProvider, username, success);
|
||||
}
|
||||
|
||||
private string GetLocalPasswordHash(User user)
|
||||
{
|
||||
return string.IsNullOrEmpty(user.EasyPassword)
|
||||
? null
|
||||
: Hex.Encode(PasswordHash.Parse(user.EasyPassword).Hash);
|
||||
}
|
||||
|
||||
private void ResetInvalidLoginAttemptCount(User user)
|
||||
{
|
||||
user.Policy.InvalidLoginAttemptCount = 0;
|
||||
@@ -538,6 +537,8 @@ namespace Emby.Server.Implementations.Library
|
||||
defaultName = "MyJellyfinUser";
|
||||
}
|
||||
|
||||
_logger.LogWarning("No users, creating one with username {UserName}", defaultName);
|
||||
|
||||
var name = MakeValidUsername(defaultName);
|
||||
|
||||
var user = InstantiateNewUser(name);
|
||||
@@ -555,6 +556,8 @@ namespace Emby.Server.Implementations.Library
|
||||
_users[user.Id] = user;
|
||||
}
|
||||
|
||||
#nullable restore
|
||||
|
||||
public UserDto GetUserDto(User user, string remoteEndPoint = null)
|
||||
{
|
||||
if (user == null)
|
||||
@@ -601,7 +604,7 @@ namespace Emby.Server.Implementations.Library
|
||||
catch (Exception ex)
|
||||
{
|
||||
// Have to use a catch-all unfortunately because some .net image methods throw plain Exceptions
|
||||
_logger.LogError(ex, "Error generating PrimaryImageAspectRatio for {user}", user.Name);
|
||||
_logger.LogError(ex, "Error generating PrimaryImageAspectRatio for {User}", user.Name);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -625,7 +628,7 @@ namespace Emby.Server.Implementations.Library
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Error getting {imageType} image info for {imagePath}", image.Type, image.Path);
|
||||
_logger.LogError(ex, "Error getting {ImageType} image info for {ImagePath}", image.Type, image.Path);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -756,13 +759,10 @@ namespace Emby.Server.Implementations.Library
|
||||
return user;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deletes the user.
|
||||
/// </summary>
|
||||
/// <param name="user">The user.</param>
|
||||
/// <returns>Task.</returns>
|
||||
/// <exception cref="ArgumentNullException">user</exception>
|
||||
/// <exception cref="ArgumentException"></exception>
|
||||
/// <inheritdoc />
|
||||
/// <exception cref="ArgumentNullException">The <c>user</c> is <c>null</c>.</exception>
|
||||
/// <exception cref="ArgumentException">The <c>user</c> doesn't exist, or is the last administrator.</exception>
|
||||
/// <exception cref="InvalidOperationException">The <c>user</c> can't be deleted; there are no other users.</exception>
|
||||
public void DeleteUser(User user)
|
||||
{
|
||||
if (user == null)
|
||||
@@ -781,7 +781,7 @@ namespace Emby.Server.Implementations.Library
|
||||
|
||||
if (_users.Count == 1)
|
||||
{
|
||||
throw new ArgumentException(string.Format(
|
||||
throw new InvalidOperationException(string.Format(
|
||||
CultureInfo.InvariantCulture,
|
||||
"The user '{0}' cannot be deleted because there must be at least one user in the system.",
|
||||
user.Name));
|
||||
@@ -802,16 +802,19 @@ namespace Emby.Server.Implementations.Library
|
||||
|
||||
_userRepository.DeleteUser(user);
|
||||
|
||||
try
|
||||
// Delete user config dir
|
||||
lock (_configSyncLock)
|
||||
lock (_policySyncLock)
|
||||
{
|
||||
_fileSystem.DeleteFile(configPath);
|
||||
try
|
||||
{
|
||||
Directory.Delete(user.ConfigurationDirectoryPath, true);
|
||||
}
|
||||
catch (IOException ex)
|
||||
{
|
||||
_logger.LogError(ex, "Error deleting user config dir: {Path}", user.ConfigurationDirectoryPath);
|
||||
}
|
||||
}
|
||||
catch (IOException ex)
|
||||
{
|
||||
_logger.LogError(ex, "Error deleting file {path}", configPath);
|
||||
}
|
||||
|
||||
DeleteUserPolicy(user);
|
||||
|
||||
_users.TryRemove(user.Id, out _);
|
||||
|
||||
@@ -920,10 +923,9 @@ namespace Emby.Server.Implementations.Library
|
||||
public UserPolicy GetUserPolicy(User user)
|
||||
{
|
||||
var path = GetPolicyFilePath(user);
|
||||
|
||||
if (!File.Exists(path))
|
||||
{
|
||||
return GetDefaultPolicy(user);
|
||||
return GetDefaultPolicy();
|
||||
}
|
||||
|
||||
try
|
||||
@@ -933,19 +935,15 @@ namespace Emby.Server.Implementations.Library
|
||||
return (UserPolicy)_xmlSerializer.DeserializeFromFile(typeof(UserPolicy), path);
|
||||
}
|
||||
}
|
||||
catch (IOException)
|
||||
{
|
||||
return GetDefaultPolicy(user);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Error reading policy file: {path}", path);
|
||||
_logger.LogError(ex, "Error reading policy file: {Path}", path);
|
||||
|
||||
return GetDefaultPolicy(user);
|
||||
return GetDefaultPolicy();
|
||||
}
|
||||
}
|
||||
|
||||
private static UserPolicy GetDefaultPolicy(User user)
|
||||
private static UserPolicy GetDefaultPolicy()
|
||||
{
|
||||
return new UserPolicy
|
||||
{
|
||||
@@ -985,27 +983,6 @@ namespace Emby.Server.Implementations.Library
|
||||
}
|
||||
}
|
||||
|
||||
private void DeleteUserPolicy(User user)
|
||||
{
|
||||
var path = GetPolicyFilePath(user);
|
||||
|
||||
try
|
||||
{
|
||||
lock (_policySyncLock)
|
||||
{
|
||||
_fileSystem.DeleteFile(path);
|
||||
}
|
||||
}
|
||||
catch (IOException)
|
||||
{
|
||||
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Error deleting policy file");
|
||||
}
|
||||
}
|
||||
|
||||
private static string GetPolicyFilePath(User user)
|
||||
{
|
||||
return Path.Combine(user.ConfigurationDirectoryPath, "policy.xml");
|
||||
@@ -1032,19 +1009,14 @@ namespace Emby.Server.Implementations.Library
|
||||
return (UserConfiguration)_xmlSerializer.DeserializeFromFile(typeof(UserConfiguration), path);
|
||||
}
|
||||
}
|
||||
catch (IOException)
|
||||
{
|
||||
return new UserConfiguration();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Error reading policy file: {path}", path);
|
||||
_logger.LogError(ex, "Error reading policy file: {Path}", path);
|
||||
|
||||
return new UserConfiguration();
|
||||
}
|
||||
}
|
||||
|
||||
private readonly object _configSyncLock = new object();
|
||||
public void UpdateConfiguration(Guid userId, UserConfiguration config)
|
||||
{
|
||||
var user = GetUserById(userId);
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
#pragma warning disable CS1591
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
@@ -42,6 +44,11 @@ namespace Emby.Server.Implementations.Library
|
||||
{
|
||||
var user = _userManager.GetUserById(query.UserId);
|
||||
|
||||
if (user == null)
|
||||
{
|
||||
throw new ArgumentException("User Id specified in the query does not exist.", nameof(query));
|
||||
}
|
||||
|
||||
var folders = _libraryManager.GetUserRootFolder()
|
||||
.GetChildren(user, true)
|
||||
.OfType<Folder>()
|
||||
@@ -54,7 +61,7 @@ namespace Emby.Server.Implementations.Library
|
||||
foreach (var folder in folders)
|
||||
{
|
||||
var collectionFolder = folder as ICollectionFolder;
|
||||
var folderViewType = collectionFolder == null ? null : collectionFolder.CollectionType;
|
||||
var folderViewType = collectionFolder?.CollectionType;
|
||||
|
||||
if (UserView.IsUserSpecific(folder))
|
||||
{
|
||||
@@ -130,16 +137,11 @@ namespace Emby.Server.Implementations.Library
|
||||
{
|
||||
var index = orders.IndexOf(i.Id.ToString("N", CultureInfo.InvariantCulture));
|
||||
|
||||
if (index == -1)
|
||||
if (index == -1
|
||||
&& i is UserView view
|
||||
&& view.DisplayParentId != Guid.Empty)
|
||||
{
|
||||
var view = i as UserView;
|
||||
if (view != null)
|
||||
{
|
||||
if (!view.DisplayParentId.Equals(Guid.Empty))
|
||||
{
|
||||
index = orders.IndexOf(view.DisplayParentId.ToString("N", CultureInfo.InvariantCulture));
|
||||
}
|
||||
}
|
||||
index = orders.IndexOf(view.DisplayParentId.ToString("N", CultureInfo.InvariantCulture));
|
||||
}
|
||||
|
||||
return index == -1 ? int.MaxValue : index;
|
||||
@@ -340,7 +342,7 @@ namespace Emby.Server.Implementations.Library
|
||||
var query = new InternalItemsQuery(user)
|
||||
{
|
||||
IncludeItemTypes = includeItemTypes,
|
||||
OrderBy = new[] { new ValueTuple<string, SortOrder>(ItemSortBy.DateCreated, SortOrder.Descending) },
|
||||
OrderBy = new[] { (ItemSortBy.DateCreated, SortOrder.Descending) },
|
||||
IsFolder = includeItemTypes.Length == 0 ? false : (bool?)null,
|
||||
ExcludeItemTypes = excludeItemTypes,
|
||||
IsVirtualItem = false,
|
||||
|
||||
@@ -8,12 +8,12 @@ using Microsoft.Extensions.Logging;
|
||||
namespace Emby.Server.Implementations.Library.Validators
|
||||
{
|
||||
/// <summary>
|
||||
/// Class ArtistsPostScanTask
|
||||
/// Class ArtistsPostScanTask.
|
||||
/// </summary>
|
||||
public class ArtistsPostScanTask : ILibraryPostScanTask
|
||||
{
|
||||
/// <summary>
|
||||
/// The _library manager
|
||||
/// The _library manager.
|
||||
/// </summary>
|
||||
private readonly ILibraryManager _libraryManager;
|
||||
private readonly ILogger _logger;
|
||||
@@ -23,6 +23,8 @@ namespace Emby.Server.Implementations.Library.Validators
|
||||
/// Initializes a new instance of the <see cref="ArtistsPostScanTask" /> class.
|
||||
/// </summary>
|
||||
/// <param name="libraryManager">The library manager.</param>
|
||||
/// <param name="logger">The logger.</param>
|
||||
/// <param name="itemRepo">The item repository.</param>
|
||||
public ArtistsPostScanTask(ILibraryManager libraryManager, ILogger logger, IItemRepository itemRepo)
|
||||
{
|
||||
_libraryManager = libraryManager;
|
||||
|
||||
@@ -12,26 +12,27 @@ using Microsoft.Extensions.Logging;
|
||||
namespace Emby.Server.Implementations.Library.Validators
|
||||
{
|
||||
/// <summary>
|
||||
/// Class ArtistsValidator
|
||||
/// Class ArtistsValidator.
|
||||
/// </summary>
|
||||
public class ArtistsValidator
|
||||
{
|
||||
/// <summary>
|
||||
/// The _library manager
|
||||
/// The library manager.
|
||||
/// </summary>
|
||||
private readonly ILibraryManager _libraryManager;
|
||||
|
||||
/// <summary>
|
||||
/// The _logger
|
||||
/// The logger.
|
||||
/// </summary>
|
||||
private readonly ILogger _logger;
|
||||
private readonly IItemRepository _itemRepo;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ArtistsPostScanTask" /> class.
|
||||
/// Initializes a new instance of the <see cref="ArtistsValidator" /> class.
|
||||
/// </summary>
|
||||
/// <param name="libraryManager">The library manager.</param>
|
||||
/// <param name="logger">The logger.</param>
|
||||
/// <param name="itemRepo">The item repository.</param>
|
||||
public ArtistsValidator(ILibraryManager libraryManager, ILogger logger, IItemRepository itemRepo)
|
||||
{
|
||||
_libraryManager = libraryManager;
|
||||
|
||||
@@ -7,20 +7,24 @@ using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace Emby.Server.Implementations.Library.Validators
|
||||
{
|
||||
/// <summary>
|
||||
/// Class GenresPostScanTask.
|
||||
/// </summary>
|
||||
public class GenresPostScanTask : ILibraryPostScanTask
|
||||
{
|
||||
/// <summary>
|
||||
/// The _library manager
|
||||
/// The _library manager.
|
||||
/// </summary>
|
||||
private readonly ILibraryManager _libraryManager;
|
||||
private readonly ILogger _logger;
|
||||
private readonly IItemRepository _itemRepo;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ArtistsPostScanTask" /> class.
|
||||
/// Initializes a new instance of the <see cref="GenresPostScanTask" /> class.
|
||||
/// </summary>
|
||||
/// <param name="libraryManager">The library manager.</param>
|
||||
/// <param name="logger">The logger.</param>
|
||||
/// <param name="itemRepo">The item repository.</param>
|
||||
public GenresPostScanTask(ILibraryManager libraryManager, ILogger logger, IItemRepository itemRepo)
|
||||
{
|
||||
_libraryManager = libraryManager;
|
||||
|
||||
@@ -7,19 +7,28 @@ using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace Emby.Server.Implementations.Library.Validators
|
||||
{
|
||||
class GenresValidator
|
||||
/// <summary>
|
||||
/// Class GenresValidator.
|
||||
/// </summary>
|
||||
public class GenresValidator
|
||||
{
|
||||
/// <summary>
|
||||
/// The _library manager
|
||||
/// The library manager.
|
||||
/// </summary>
|
||||
private readonly ILibraryManager _libraryManager;
|
||||
private readonly IItemRepository _itemRepo;
|
||||
|
||||
/// <summary>
|
||||
/// The _logger
|
||||
/// The logger.
|
||||
/// </summary>
|
||||
private readonly ILogger _logger;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="GenresValidator"/> class.
|
||||
/// </summary>
|
||||
/// <param name="libraryManager">The library manager.</param>
|
||||
/// <param name="logger">The logger.</param>
|
||||
/// <param name="itemRepo">The item repository.</param>
|
||||
public GenresValidator(ILibraryManager libraryManager, ILogger logger, IItemRepository itemRepo)
|
||||
{
|
||||
_libraryManager = libraryManager;
|
||||
|
||||
@@ -8,22 +8,23 @@ using Microsoft.Extensions.Logging;
|
||||
namespace Emby.Server.Implementations.Library.Validators
|
||||
{
|
||||
/// <summary>
|
||||
/// Class MusicGenresPostScanTask
|
||||
/// Class MusicGenresPostScanTask.
|
||||
/// </summary>
|
||||
public class MusicGenresPostScanTask : ILibraryPostScanTask
|
||||
{
|
||||
/// <summary>
|
||||
/// The _library manager
|
||||
/// The library manager.
|
||||
/// </summary>
|
||||
private readonly ILibraryManager _libraryManager;
|
||||
private readonly ILogger _logger;
|
||||
private readonly IItemRepository _itemRepo;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ArtistsPostScanTask" /> class.
|
||||
/// Initializes a new instance of the <see cref="MusicGenresPostScanTask" /> class.
|
||||
/// </summary>
|
||||
/// <param name="libraryManager">The library manager.</param>
|
||||
/// <param name="logger">The logger.</param>
|
||||
/// <param name="itemRepo">The item repository.</param>
|
||||
public MusicGenresPostScanTask(ILibraryManager libraryManager, ILogger logger, IItemRepository itemRepo)
|
||||
{
|
||||
_libraryManager = libraryManager;
|
||||
|
||||
@@ -7,19 +7,28 @@ using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace Emby.Server.Implementations.Library.Validators
|
||||
{
|
||||
class MusicGenresValidator
|
||||
/// <summary>
|
||||
/// Class MusicGenresValidator.
|
||||
/// </summary>
|
||||
public class MusicGenresValidator
|
||||
{
|
||||
/// <summary>
|
||||
/// The _library manager
|
||||
/// The library manager.
|
||||
/// </summary>
|
||||
private readonly ILibraryManager _libraryManager;
|
||||
|
||||
/// <summary>
|
||||
/// The _logger
|
||||
/// The logger.
|
||||
/// </summary>
|
||||
private readonly ILogger _logger;
|
||||
private readonly IItemRepository _itemRepo;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="MusicGenresValidator" /> class.
|
||||
/// </summary>
|
||||
/// <param name="libraryManager">The library manager.</param>
|
||||
/// <param name="logger">The logger.</param>
|
||||
/// <param name="itemRepo">The item repository.</param>
|
||||
public MusicGenresValidator(ILibraryManager libraryManager, ILogger logger, IItemRepository itemRepo)
|
||||
{
|
||||
_libraryManager = libraryManager;
|
||||
|
||||
@@ -32,6 +32,7 @@ namespace Emby.Server.Implementations.Library.Validators
|
||||
/// </summary>
|
||||
/// <param name="libraryManager">The library manager.</param>
|
||||
/// <param name="logger">The logger.</param>
|
||||
/// <param name="fileSystem">The file system.</param>
|
||||
public PeopleValidator(ILibraryManager libraryManager, ILogger logger, IFileSystem fileSystem)
|
||||
{
|
||||
_libraryManager = libraryManager;
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user