merge branch master into media-attachments

This commit is contained in:
dkanada
2020-01-08 01:21:09 +09:00
473 changed files with 5682 additions and 15096 deletions

View File

@@ -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(

View File

@@ -1,3 +1,5 @@
#pragma warning disable CS1591
using System;
using System.Linq;
using MediaBrowser.Controller.Library;

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,3 +1,5 @@
#pragma warning disable CS1591
using System.Collections.Generic;
using MediaBrowser.Common.Configuration;
using MediaBrowser.Model.Branding;

View File

@@ -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)
{

View File

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

View File

@@ -1,3 +1,5 @@
#pragma warning disable CS1591
using System.Collections.Generic;
using System.Linq;
using System.Threading;

View File

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

View File

@@ -1,3 +1,5 @@
#pragma warning disable CS1591
using System;
using System.Linq;
using System.Threading;

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,3 +1,5 @@
#pragma warning disable CS1591
using System;
using System.Threading;
using System.Threading.Tasks;

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,3 +1,5 @@
#pragma warning disable CS1591
using System;
using System.Globalization;
using System.IO;

View File

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

View File

@@ -1,3 +1,5 @@
#pragma warning disable CS1591
using System;
using System.Diagnostics;
using System.IO;

View File

@@ -1,3 +1,5 @@
#pragma warning disable CS1591
using MediaBrowser.Model.Diagnostics;
namespace Emby.Server.Implementations.Diagnostics

View File

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

View File

@@ -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" />

View File

@@ -1,3 +1,5 @@
#pragma warning disable CS1591
using System;
using System.Linq;
using System.Threading;

View File

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

View File

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

View File

@@ -1,3 +1,5 @@
#pragma warning disable CS1591
using System;
using System.Linq;
using System.Threading;

View File

@@ -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
{

View File

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

View File

@@ -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()
{
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,3 +1,5 @@
#pragma warning disable CS1591
using System;
using System.Threading;
using System.Threading.Tasks;

View File

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

View File

@@ -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)
{

View File

@@ -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(

View File

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

View File

@@ -1,3 +1,5 @@
#pragma warning disable CS1591
using System;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Library;

View File

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

View File

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

View File

@@ -1,3 +1,5 @@
#pragma warning disable CS1591
namespace Emby.Server.Implementations.IO
{
public class ExtendedFileSystemInfo

View File

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

View File

@@ -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)
{

View File

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

View File

@@ -1,3 +1,5 @@
#pragma warning disable CS1591
using System;
using System.IO;
using MediaBrowser.Model.IO;

View File

@@ -1,3 +1,5 @@
#pragma warning disable CS1591
using System;
using System.Buffers;
using System.IO;

View File

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

View File

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

View File

@@ -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))
{

View File

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

View File

@@ -1,3 +1,5 @@
#pragma warning disable CS1591
using System;
using System.Globalization;
using System.Threading;

View File

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

View File

@@ -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)
{

View File

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

View File

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

View File

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

View File

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

View File

@@ -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))
{

View File

@@ -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
{

View File

@@ -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,

View File

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

View File

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

View File

@@ -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 =>

View File

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

View File

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

View File

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

View File

@@ -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)
{

View File

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

View File

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

View File

@@ -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
{

View File

@@ -1,3 +1,5 @@
#pragma warning disable CS1591
using System;
using System.IO;
using System.Linq;

View File

@@ -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)
{

View File

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

View File

@@ -1,3 +1,5 @@
#pragma warning disable CS1591
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Library;

View File

@@ -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,

View File

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

View File

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

View File

@@ -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,

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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