aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBond-009 <bond.009@outlook.com>2019-02-16 17:05:44 +0100
committerGitHub <noreply@github.com>2019-02-16 17:05:44 +0100
commit4811e76860c9ad71c14dc8548e70ef001458bf80 (patch)
tree997310af3cace45c32d502ebf7841c41efcef078
parent5835c4b21d5ab2666478f29c240d2e56316e1f67 (diff)
parent25c2267a89af5c2e82774c638cdad0defcc894b5 (diff)
Merge branch 'master' into perf
-rw-r--r--.editorconfig80
-rw-r--r--Emby.Dlna/DlnaManager.cs4
-rw-r--r--Emby.Dlna/MediaReceiverRegistrar/ControlHandler.cs3
-rw-r--r--Emby.Server.Implementations/AppBase/BaseApplicationPaths.cs103
-rw-r--r--Emby.Server.Implementations/ApplicationHost.cs540
-rw-r--r--Emby.Server.Implementations/Channels/RefreshChannelsScheduledTask.cs2
-rw-r--r--Emby.Server.Implementations/Emby.Server.Implementations.csproj3
-rw-r--r--Emby.Server.Implementations/EntryPoints/UserDataChangeNotifier.cs2
-rw-r--r--Emby.Server.Implementations/Library/Resolvers/SpecialFolderResolver.cs2
-rw-r--r--Emby.Server.Implementations/LiveTv/TunerHosts/M3uParser.cs74
-rw-r--r--Emby.Server.Implementations/LiveTv/TunerHosts/SharedHttpStream.cs2
-rw-r--r--Emby.Server.Implementations/ScheduledTasks/Tasks/ChapterImagesTask.cs2
-rw-r--r--Emby.Server.Implementations/ServerApplicationPaths.cs15
-rw-r--r--Emby.Server.Implementations/Sorting/AiredEpisodeOrderComparer.cs2
-rw-r--r--Emby.Server.Implementations/Sorting/SeriesSortNameComparer.cs2
-rw-r--r--Jellyfin.Drawing.Skia/SkiaEncoder.cs2
-rw-r--r--Jellyfin.Server/CoreAppHost.cs11
-rw-r--r--Jellyfin.Server/Jellyfin.Server.csproj7
-rw-r--r--Jellyfin.Server/Program.cs215
-rw-r--r--Jellyfin.Server/SocketSharp/RequestMono.cs152
-rw-r--r--Jellyfin.Server/SocketSharp/SharpWebSocket.cs44
-rw-r--r--Jellyfin.Server/SocketSharp/WebSocketSharpListener.cs61
-rw-r--r--Jellyfin.Server/SocketSharp/WebSocketSharpRequest.cs25
-rw-r--r--Jellyfin.Server/SocketSharp/WebSocketSharpResponse.cs20
-rw-r--r--MediaBrowser.Api/Session/SessionInfoWebSocketListener.cs2
-rw-r--r--MediaBrowser.Api/System/ActivityLogWebSocketListener.cs2
-rw-r--r--MediaBrowser.Common/IApplicationHost.cs16
-rw-r--r--MediaBrowser.Common/MediaBrowser.Common.csproj4
-rw-r--r--MediaBrowser.LocalMetadata/Providers/PlaylistXmlProvider.cs2
-rw-r--r--MediaBrowser.Model/System/PublicSystemInfo.cs6
-rw-r--r--MediaBrowser.Model/System/SystemInfo.cs3
-rw-r--r--MediaBrowser.Providers/BoxSets/MovieDbBoxSetImageProvider.cs2
-rw-r--r--MediaBrowser.Providers/Movies/MovieDbImageProvider.cs2
-rw-r--r--MediaBrowser.Providers/Music/MusicVideoMetadataService.cs2
-rw-r--r--MediaBrowser.Providers/Photos/PhotoAlbumMetadataService.cs2
-rw-r--r--MediaBrowser.Providers/Photos/PhotoMetadataService.cs2
-rw-r--r--MediaBrowser.Providers/Playlists/PlaylistMetadataService.cs2
-rw-r--r--MediaBrowser.Providers/TV/Omdb/OmdbEpisodeProvider.cs2
-rw-r--r--MediaBrowser.Providers/TV/TheMovieDb/MovieDbEpisodeProvider.cs2
m---------MediaBrowser.WebDashboard/jellyfin-web0
-rw-r--r--MediaBrowser.XbmcMetadata/Parsers/MovieNfoParser.cs2
-rw-r--r--jellyfin.ruleset8
42 files changed, 635 insertions, 799 deletions
diff --git a/.editorconfig b/.editorconfig
index b2891188d..21f27df13 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -55,15 +55,77 @@ dotnet_style_prefer_conditional_expression_over_return = true:silent
###############################
# Naming Conventions #
###############################
-# Style Definitions
-dotnet_naming_style.pascal_case_style.capitalization = pascal_case
-# Use PascalCase for constant fields
-dotnet_naming_rule.constant_fields_should_be_pascal_case.severity = suggestion
-dotnet_naming_rule.constant_fields_should_be_pascal_case.symbols = constant_fields
-dotnet_naming_rule.constant_fields_should_be_pascal_case.style = pascal_case_style
-dotnet_naming_symbols.constant_fields.applicable_kinds = field
-dotnet_naming_symbols.constant_fields.applicable_accessibilities = *
-dotnet_naming_symbols.constant_fields.required_modifiers = const
+# Style Definitions (From Roslyn)
+
+# Non-private static fields are PascalCase
+dotnet_naming_rule.non_private_static_fields_should_be_pascal_case.severity = suggestion
+dotnet_naming_rule.non_private_static_fields_should_be_pascal_case.symbols = non_private_static_fields
+dotnet_naming_rule.non_private_static_fields_should_be_pascal_case.style = non_private_static_field_style
+
+dotnet_naming_symbols.non_private_static_fields.applicable_kinds = field
+dotnet_naming_symbols.non_private_static_fields.applicable_accessibilities = public, protected, internal, protected internal, private protected
+dotnet_naming_symbols.non_private_static_fields.required_modifiers = static
+
+dotnet_naming_style.non_private_static_field_style.capitalization = pascal_case
+
+# Constants are PascalCase
+dotnet_naming_rule.constants_should_be_pascal_case.severity = suggestion
+dotnet_naming_rule.constants_should_be_pascal_case.symbols = constants
+dotnet_naming_rule.constants_should_be_pascal_case.style = constant_style
+
+dotnet_naming_symbols.constants.applicable_kinds = field, local
+dotnet_naming_symbols.constants.required_modifiers = const
+
+dotnet_naming_style.constant_style.capitalization = pascal_case
+
+# Static fields are camelCase and start with s_
+dotnet_naming_rule.static_fields_should_be_camel_case.severity = suggestion
+dotnet_naming_rule.static_fields_should_be_camel_case.symbols = static_fields
+dotnet_naming_rule.static_fields_should_be_camel_case.style = static_field_style
+
+dotnet_naming_symbols.static_fields.applicable_kinds = field
+dotnet_naming_symbols.static_fields.required_modifiers = static
+
+dotnet_naming_style.static_field_style.capitalization = camel_case
+dotnet_naming_style.static_field_style.required_prefix = _
+
+# Instance fields are camelCase and start with _
+dotnet_naming_rule.instance_fields_should_be_camel_case.severity = suggestion
+dotnet_naming_rule.instance_fields_should_be_camel_case.symbols = instance_fields
+dotnet_naming_rule.instance_fields_should_be_camel_case.style = instance_field_style
+
+dotnet_naming_symbols.instance_fields.applicable_kinds = field
+
+dotnet_naming_style.instance_field_style.capitalization = camel_case
+dotnet_naming_style.instance_field_style.required_prefix = _
+
+# Locals and parameters are camelCase
+dotnet_naming_rule.locals_should_be_camel_case.severity = suggestion
+dotnet_naming_rule.locals_should_be_camel_case.symbols = locals_and_parameters
+dotnet_naming_rule.locals_should_be_camel_case.style = camel_case_style
+
+dotnet_naming_symbols.locals_and_parameters.applicable_kinds = parameter, local
+
+dotnet_naming_style.camel_case_style.capitalization = camel_case
+
+# Local functions are PascalCase
+dotnet_naming_rule.local_functions_should_be_pascal_case.severity = suggestion
+dotnet_naming_rule.local_functions_should_be_pascal_case.symbols = local_functions
+dotnet_naming_rule.local_functions_should_be_pascal_case.style = local_function_style
+
+dotnet_naming_symbols.local_functions.applicable_kinds = local_function
+
+dotnet_naming_style.local_function_style.capitalization = pascal_case
+
+# By default, name items with PascalCase
+dotnet_naming_rule.members_should_be_pascal_case.severity = suggestion
+dotnet_naming_rule.members_should_be_pascal_case.symbols = all_members
+dotnet_naming_rule.members_should_be_pascal_case.style = pascal_case_style
+
+dotnet_naming_symbols.all_members.applicable_kinds = *
+
+dotnet_naming_style.pascal_case_style.capitalization = pascal_case
+
###############################
# C# Coding Conventions #
###############################
diff --git a/Emby.Dlna/DlnaManager.cs b/Emby.Dlna/DlnaManager.cs
index c507b14e9..f53d27451 100644
--- a/Emby.Dlna/DlnaManager.cs
+++ b/Emby.Dlna/DlnaManager.cs
@@ -38,7 +38,9 @@ namespace Emby.Dlna
IFileSystem fileSystem,
IApplicationPaths appPaths,
ILoggerFactory loggerFactory,
- IJsonSerializer jsonSerializer, IServerApplicationHost appHost, IAssemblyInfo assemblyInfo)
+ IJsonSerializer jsonSerializer,
+ IServerApplicationHost appHost,
+ IAssemblyInfo assemblyInfo)
{
_xmlSerializer = xmlSerializer;
_fileSystem = fileSystem;
diff --git a/Emby.Dlna/MediaReceiverRegistrar/ControlHandler.cs b/Emby.Dlna/MediaReceiverRegistrar/ControlHandler.cs
index 6257892b1..ae8175f4a 100644
--- a/Emby.Dlna/MediaReceiverRegistrar/ControlHandler.cs
+++ b/Emby.Dlna/MediaReceiverRegistrar/ControlHandler.cs
@@ -36,7 +36,8 @@ namespace Emby.Dlna.MediaReceiverRegistrar
};
}
- public ControlHandler(IServerConfigurationManager config, ILogger logger, IXmlReaderSettingsFactory xmlReaderSettingsFactory) : base(config, logger, xmlReaderSettingsFactory)
+ public ControlHandler(IServerConfigurationManager config, ILogger logger, IXmlReaderSettingsFactory xmlReaderSettingsFactory)
+ : base(config, logger, xmlReaderSettingsFactory)
{
}
}
diff --git a/Emby.Server.Implementations/AppBase/BaseApplicationPaths.cs b/Emby.Server.Implementations/AppBase/BaseApplicationPaths.cs
index 701c04f9e..f26cc4f62 100644
--- a/Emby.Server.Implementations/AppBase/BaseApplicationPaths.cs
+++ b/Emby.Server.Implementations/AppBase/BaseApplicationPaths.cs
@@ -1,3 +1,4 @@
+using System;
using System.IO;
using MediaBrowser.Common.Configuration;
@@ -14,50 +15,44 @@ namespace Emby.Server.Implementations.AppBase
/// </summary>
protected BaseApplicationPaths(
string programDataPath,
- string appFolderPath,
- string logDirectoryPath = null,
- string configurationDirectoryPath = null,
- string cacheDirectoryPath = null)
+ string logDirectoryPath,
+ string configurationDirectoryPath,
+ string cacheDirectoryPath)
{
ProgramDataPath = programDataPath;
- ProgramSystemPath = appFolderPath;
LogDirectoryPath = logDirectoryPath;
ConfigurationDirectoryPath = configurationDirectoryPath;
CachePath = cacheDirectoryPath;
+
+ DataPath = Path.Combine(ProgramDataPath, "data");
}
+ /// <summary>
+ /// Gets the path to the program data folder
+ /// </summary>
+ /// <value>The program data path.</value>
public string ProgramDataPath { get; private set; }
/// <summary>
/// Gets the path to the system folder
/// </summary>
- public string ProgramSystemPath { get; private set; }
+ public string ProgramSystemPath { get; } = AppContext.BaseDirectory;
/// <summary>
- /// The _data directory
- /// </summary>
- private string _dataDirectory;
- /// <summary>
/// Gets the folder path to the data directory
/// </summary>
/// <value>The data directory.</value>
+ private string _dataPath;
public string DataPath
{
- get
- {
- if (_dataDirectory == null)
- {
- _dataDirectory = Path.Combine(ProgramDataPath, "data");
-
- Directory.CreateDirectory(_dataDirectory);
- }
-
- return _dataDirectory;
- }
+ get => _dataPath;
+ private set => _dataPath = Directory.CreateDirectory(value).FullName;
}
- private const string _virtualDataPath = "%AppDataPath%";
- public string VirtualDataPath => _virtualDataPath;
+ /// <summary>
+ /// Gets the magic strings used for virtual path manipulation.
+ /// </summary>
+ public string VirtualDataPath { get; } = "%AppDataPath%";
/// <summary>
/// Gets the image cache path.
@@ -84,54 +79,16 @@ namespace Emby.Server.Implementations.AppBase
public string TempUpdatePath => Path.Combine(ProgramDataPath, "updates");
/// <summary>
- /// The _log directory
- /// </summary>
- private string _logDirectoryPath;
-
- /// <summary>
/// Gets the path to the log directory
/// </summary>
/// <value>The log directory path.</value>
- public string LogDirectoryPath
- {
- get
- {
- if (string.IsNullOrEmpty(_logDirectoryPath))
- {
- _logDirectoryPath = Path.Combine(ProgramDataPath, "logs");
-
- Directory.CreateDirectory(_logDirectoryPath);
- }
-
- return _logDirectoryPath;
- }
- set => _logDirectoryPath = value;
- }
-
- /// <summary>
- /// The _config directory
- /// </summary>
- private string _configurationDirectoryPath;
+ public string LogDirectoryPath { get; private set; }
/// <summary>
/// Gets the path to the application configuration root directory
/// </summary>
/// <value>The configuration directory path.</value>
- public string ConfigurationDirectoryPath
- {
- get
- {
- if (string.IsNullOrEmpty(_configurationDirectoryPath))
- {
- _configurationDirectoryPath = Path.Combine(ProgramDataPath, "config");
-
- Directory.CreateDirectory(_configurationDirectoryPath);
- }
-
- return _configurationDirectoryPath;
- }
- set => _configurationDirectoryPath = value;
- }
+ public string ConfigurationDirectoryPath { get; private set; }
/// <summary>
/// Gets the path to the system configuration file
@@ -140,28 +97,10 @@ namespace Emby.Server.Implementations.AppBase
public string SystemConfigurationFilePath => Path.Combine(ConfigurationDirectoryPath, "system.xml");
/// <summary>
- /// The _cache directory
- /// </summary>
- private string _cachePath;
- /// <summary>
/// Gets the folder path to the cache directory
/// </summary>
/// <value>The cache directory.</value>
- public string CachePath
- {
- get
- {
- if (string.IsNullOrEmpty(_cachePath))
- {
- _cachePath = Path.Combine(ProgramDataPath, "cache");
-
- Directory.CreateDirectory(_cachePath);
- }
-
- return _cachePath;
- }
- set => _cachePath = value;
- }
+ public string CachePath { get; set; }
/// <summary>
/// Gets the folder path to the temp directory within the cache folder
diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs
index bb475eb2c..784486d52 100644
--- a/Emby.Server.Implementations/ApplicationHost.cs
+++ b/Emby.Server.Implementations/ApplicationHost.cs
@@ -105,6 +105,7 @@ using MediaBrowser.Providers.Subtitles;
using MediaBrowser.WebDashboard.Api;
using MediaBrowser.XbmcMetadata.Providers;
using Microsoft.Extensions.Logging;
+using Microsoft.Extensions.DependencyInjection;
using ServiceStack;
using ServiceStack.Text.Jsv;
using X509Certificate = System.Security.Cryptography.X509Certificates.X509Certificate;
@@ -202,7 +203,7 @@ namespace Emby.Server.Implementations
/// Gets all concrete types.
/// </summary>
/// <value>All concrete types.</value>
- public Tuple<Type, string>[] AllConcreteTypes { get; protected set; }
+ public Type[] AllConcreteTypes { get; protected set; }
/// <summary>
/// The disposable parts
@@ -219,8 +220,6 @@ namespace Emby.Server.Implementations
protected IEnvironmentInfo EnvironmentInfo { get; set; }
- private IBlurayExaminer BlurayExaminer { get; set; }
-
public PackageVersionClass SystemUpdateLevel
{
get
@@ -232,12 +231,7 @@ namespace Emby.Server.Implementations
}
}
- public virtual string OperatingSystemDisplayName => EnvironmentInfo.OperatingSystemName;
-
- /// <summary>
- /// The container
- /// </summary>
- protected readonly SimpleInjector.Container Container = new SimpleInjector.Container();
+ protected IServiceProvider _serviceProvider;
/// <summary>
/// Gets the server configuration manager.
@@ -309,7 +303,6 @@ namespace Emby.Server.Implementations
/// <value>The user data repository.</value>
private IUserDataManager UserDataManager { get; set; }
private IUserRepository UserRepository { get; set; }
- internal IDisplayPreferencesRepository DisplayPreferencesRepository { get; set; }
internal SqliteItemRepository ItemRepository { get; set; }
private INotificationManager NotificationManager { get; set; }
@@ -453,138 +446,58 @@ namespace Emby.Server.Implementations
/// <value>The name.</value>
public string Name => ApplicationProductName;
- private static Tuple<Assembly, string> GetAssembly(Type type)
- {
- var assembly = type.GetTypeInfo().Assembly;
-
- return new Tuple<Assembly, string>(assembly, null);
- }
-
- public virtual IStreamHelper CreateStreamHelper()
- {
- return new StreamHelper();
- }
-
/// <summary>
- /// Creates an instance of type and resolves all constructor dependancies
+ /// 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)
- {
- return Container.GetInstance(type);
- }
+ => ActivatorUtilities.CreateInstance(_serviceProvider, type);
+
+ /// <summary>
+ /// Creates an instance of type and resolves all constructor dependencies
+ /// </summary>
+ /// <param name="type">The type.</param>
+ /// <returns>System.Object.</returns>
+ public T CreateInstance<T>()
+ => ActivatorUtilities.CreateInstance<T>(_serviceProvider);
/// <summary>
/// Creates the instance safe.
/// </summary>
/// <param name="typeInfo">The type information.</param>
/// <returns>System.Object.</returns>
- protected object CreateInstanceSafe(Tuple<Type, string> typeInfo)
+ protected object CreateInstanceSafe(Type type)
{
- var type = typeInfo.Item1;
-
try
{
- return Container.GetInstance(type);
+ Logger.LogWarning("Creating instance of {Type}", type);
+ return ActivatorUtilities.CreateInstance(_serviceProvider, type);
}
catch (Exception ex)
{
- Logger.LogError(ex, "Error creating {type}", type.FullName);
- // Don't blow up in release mode
+ Logger.LogError(ex, "Error creating {Type}", type);
return null;
}
}
/// <summary>
- /// Registers the specified obj.
- /// </summary>
- /// <typeparam name="T"></typeparam>
- /// <param name="obj">The obj.</param>
- /// <param name="manageLifetime">if set to <c>true</c> [manage lifetime].</param>
- protected void RegisterSingleInstance<T>(T obj, bool manageLifetime = true)
- where T : class
- {
- Container.RegisterInstance<T>(obj);
-
- if (manageLifetime)
- {
- var disposable = obj as IDisposable;
-
- if (disposable != null)
- {
- DisposableParts.Add(disposable);
- }
- }
- }
-
- /// <summary>
- /// Registers the single instance.
- /// </summary>
- /// <typeparam name="T"></typeparam>
- /// <param name="func">The func.</param>
- protected void RegisterSingleInstance<T>(Func<T> func)
- where T : class
- {
- Container.RegisterSingleton(func);
- }
-
- /// <summary>
/// Resolves this instance.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <returns>``0.</returns>
- public T Resolve<T>()
- {
- return (T)Container.GetRegistration(typeof(T), true).GetInstance();
- }
-
- /// <summary>
- /// Resolves this instance.
- /// </summary>
- /// <typeparam name="T"></typeparam>
- /// <returns>``0.</returns>
- public T TryResolve<T>()
- {
- var result = Container.GetRegistration(typeof(T), false);
-
- if (result == null)
- {
- return default(T);
- }
- return (T)result.GetInstance();
- }
-
- /// <summary>
- /// Loads the assembly.
- /// </summary>
- /// <param name="file">The file.</param>
- /// <returns>Assembly.</returns>
- protected Tuple<Assembly, string> LoadAssembly(string file)
- {
- try
- {
- var assembly = Assembly.Load(File.ReadAllBytes(file));
-
- return new Tuple<Assembly, string>(assembly, file);
- }
- catch (Exception ex)
- {
- Logger.LogError(ex, "Error loading assembly {File}", file);
- return null;
- }
- }
+ public T Resolve<T>() => _serviceProvider.GetService<T>();
/// <summary>
/// Gets the export types.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <returns>IEnumerable{Type}.</returns>
- public IEnumerable<Tuple<Type, string>> GetExportTypes<T>()
+ public IEnumerable<Type> GetExportTypes<T>()
{
var currentType = typeof(T);
- return AllConcreteTypes.Where(i => currentType.IsAssignableFrom(i.Item1));
+ return AllConcreteTypes.Where(i => currentType.IsAssignableFrom(i));
}
/// <summary>
@@ -596,9 +509,10 @@ namespace Emby.Server.Implementations
public IEnumerable<T> GetExports<T>(bool manageLifetime = true)
{
var parts = GetExportTypes<T>()
- .Select(CreateInstanceSafe)
+ .Select(x => CreateInstanceSafe(x))
.Where(i => i != null)
- .Cast<T>();
+ .Cast<T>()
+ .ToList(); // Convert to list so this isn't executed for each iteration
if (manageLifetime)
{
@@ -611,33 +525,6 @@ namespace Emby.Server.Implementations
return parts;
}
- public List<Tuple<T, string>> GetExportsWithInfo<T>(bool manageLifetime = true)
- {
- var parts = GetExportTypes<T>()
- .Select(i =>
- {
- var obj = CreateInstanceSafe(i);
-
- if (obj == null)
- {
- return null;
- }
- return new Tuple<T, string>((T)obj, i.Item2);
- })
- .Where(i => i != null)
- .ToList();
-
- if (manageLifetime)
- {
- lock (DisposableParts)
- {
- DisposableParts.AddRange(parts.Select(i => i.Item1).OfType<IDisposable>());
- }
- }
-
- return parts;
- }
-
/// <summary>
/// Runs the startup tasks.
/// </summary>
@@ -691,7 +578,7 @@ namespace Emby.Server.Implementations
}
}
- public async Task Init()
+ public async Task Init(IServiceCollection serviceCollection)
{
HttpPort = ServerConfigurationManager.Configuration.HttpServerPortNumber;
HttpsPort = ServerConfigurationManager.Configuration.HttpsPortNumber;
@@ -721,7 +608,7 @@ namespace Emby.Server.Implementations
SetHttpLimit();
- await RegisterResources();
+ await RegisterResources(serviceCollection);
FindParts();
}
@@ -736,104 +623,103 @@ namespace Emby.Server.Implementations
/// <summary>
/// Registers resources that classes will depend on
/// </summary>
- protected async Task RegisterResources()
+ protected async Task RegisterResources(IServiceCollection serviceCollection)
{
- RegisterSingleInstance(ConfigurationManager);
- RegisterSingleInstance<IApplicationHost>(this);
+ serviceCollection.AddSingleton(ConfigurationManager);
+ serviceCollection.AddSingleton<IApplicationHost>(this);
+
+ serviceCollection.AddSingleton<IApplicationPaths>(ApplicationPaths);
- RegisterSingleInstance<IApplicationPaths>(ApplicationPaths);
- RegisterSingleInstance(JsonSerializer);
+ serviceCollection.AddSingleton(JsonSerializer);
- RegisterSingleInstance(LoggerFactory, false);
- RegisterSingleInstance(Logger);
+ serviceCollection.AddSingleton(LoggerFactory);
+ serviceCollection.AddLogging();
+ serviceCollection.AddSingleton(Logger);
- RegisterSingleInstance(EnvironmentInfo);
+ serviceCollection.AddSingleton(EnvironmentInfo);
- RegisterSingleInstance(FileSystemManager);
+ serviceCollection.AddSingleton(FileSystemManager);
HttpClient = CreateHttpClient();
- RegisterSingleInstance(HttpClient);
+ serviceCollection.AddSingleton(HttpClient);
- RegisterSingleInstance(NetworkManager);
+ serviceCollection.AddSingleton(NetworkManager);
IsoManager = new IsoManager();
- RegisterSingleInstance(IsoManager);
+ serviceCollection.AddSingleton(IsoManager);
TaskManager = new TaskManager(ApplicationPaths, JsonSerializer, LoggerFactory, FileSystemManager);
- RegisterSingleInstance(TaskManager);
+ serviceCollection.AddSingleton(TaskManager);
- RegisterSingleInstance(XmlSerializer);
+ serviceCollection.AddSingleton(XmlSerializer);
ProcessFactory = new ProcessFactory();
- RegisterSingleInstance(ProcessFactory);
+ serviceCollection.AddSingleton(ProcessFactory);
- var streamHelper = CreateStreamHelper();
- ApplicationHost.StreamHelper = streamHelper;
- RegisterSingleInstance(streamHelper);
+ ApplicationHost.StreamHelper = new StreamHelper();
+ serviceCollection.AddSingleton(StreamHelper);
- RegisterSingleInstance(CryptographyProvider);
+ serviceCollection.AddSingleton(CryptographyProvider);
SocketFactory = new SocketFactory();
- RegisterSingleInstance(SocketFactory);
+ serviceCollection.AddSingleton(SocketFactory);
InstallationManager = new InstallationManager(LoggerFactory, this, ApplicationPaths, HttpClient, JsonSerializer, ServerConfigurationManager, FileSystemManager, CryptographyProvider, PackageRuntime);
- RegisterSingleInstance(InstallationManager);
+ serviceCollection.AddSingleton(InstallationManager);
ZipClient = new ZipClient(FileSystemManager);
- RegisterSingleInstance(ZipClient);
+ serviceCollection.AddSingleton(ZipClient);
HttpResultFactory = new HttpResultFactory(LoggerFactory, FileSystemManager, JsonSerializer, CreateBrotliCompressor());
- RegisterSingleInstance(HttpResultFactory);
+ serviceCollection.AddSingleton(HttpResultFactory);
- RegisterSingleInstance<IServerApplicationHost>(this);
- RegisterSingleInstance<IServerApplicationPaths>(ApplicationPaths);
+ serviceCollection.AddSingleton<IServerApplicationHost>(this);
+ serviceCollection.AddSingleton<IServerApplicationPaths>(ApplicationPaths);
- RegisterSingleInstance(ServerConfigurationManager);
+ serviceCollection.AddSingleton(ServerConfigurationManager);
- IAssemblyInfo assemblyInfo = new AssemblyInfo();
- RegisterSingleInstance(assemblyInfo);
+ var assemblyInfo = new AssemblyInfo();
+ serviceCollection.AddSingleton<IAssemblyInfo>(assemblyInfo);
LocalizationManager = new LocalizationManager(ServerConfigurationManager, FileSystemManager, JsonSerializer, LoggerFactory);
await LocalizationManager.LoadAll();
- RegisterSingleInstance<ILocalizationManager>(LocalizationManager);
+ serviceCollection.AddSingleton<ILocalizationManager>(LocalizationManager);
- BlurayExaminer = new BdInfoExaminer(FileSystemManager);
- RegisterSingleInstance(BlurayExaminer);
+ serviceCollection.AddSingleton<IBlurayExaminer>(new BdInfoExaminer(FileSystemManager));
- RegisterSingleInstance<IXmlReaderSettingsFactory>(new XmlReaderSettingsFactory());
+ serviceCollection.AddSingleton<IXmlReaderSettingsFactory>(new XmlReaderSettingsFactory());
UserDataManager = new UserDataManager(LoggerFactory, ServerConfigurationManager, () => UserManager);
- RegisterSingleInstance(UserDataManager);
+ serviceCollection.AddSingleton(UserDataManager);
UserRepository = GetUserRepository();
// This is only needed for disposal purposes. If removing this, make sure to have the manager handle disposing it
- RegisterSingleInstance(UserRepository);
+ serviceCollection.AddSingleton(UserRepository);
var displayPreferencesRepo = new SqliteDisplayPreferencesRepository(LoggerFactory, JsonSerializer, ApplicationPaths, FileSystemManager);
- DisplayPreferencesRepository = displayPreferencesRepo;
- RegisterSingleInstance(DisplayPreferencesRepository);
+ serviceCollection.AddSingleton<IDisplayPreferencesRepository>(displayPreferencesRepo);
ItemRepository = new SqliteItemRepository(ServerConfigurationManager, this, JsonSerializer, LoggerFactory, assemblyInfo);
- RegisterSingleInstance<IItemRepository>(ItemRepository);
+ serviceCollection.AddSingleton<IItemRepository>(ItemRepository);
AuthenticationRepository = GetAuthenticationRepository();
- RegisterSingleInstance(AuthenticationRepository);
+ serviceCollection.AddSingleton(AuthenticationRepository);
UserManager = new UserManager(LoggerFactory, ServerConfigurationManager, UserRepository, XmlSerializer, NetworkManager, () => ImageProcessor, () => DtoService, this, JsonSerializer, FileSystemManager, CryptographyProvider);
- RegisterSingleInstance(UserManager);
+ serviceCollection.AddSingleton(UserManager);
LibraryManager = new LibraryManager(this, LoggerFactory, TaskManager, UserManager, ServerConfigurationManager, UserDataManager, () => LibraryMonitor, FileSystemManager, () => ProviderManager, () => UserViewManager);
- RegisterSingleInstance(LibraryManager);
+ serviceCollection.AddSingleton(LibraryManager);
// TODO wtaylor: investigate use of second music manager
var musicManager = new MusicManager(LibraryManager);
- RegisterSingleInstance<IMusicManager>(new MusicManager(LibraryManager));
+ serviceCollection.AddSingleton<IMusicManager>(new MusicManager(LibraryManager));
LibraryMonitor = new LibraryMonitor(LoggerFactory, TaskManager, LibraryManager, ServerConfigurationManager, FileSystemManager, EnvironmentInfo);
- RegisterSingleInstance(LibraryMonitor);
+ serviceCollection.AddSingleton(LibraryMonitor);
- RegisterSingleInstance<ISearchEngine>(() => new SearchEngine(LoggerFactory, LibraryManager, UserManager));
+ serviceCollection.AddSingleton<ISearchEngine>(new SearchEngine(LoggerFactory, LibraryManager, UserManager));
CertificateInfo = GetCertificateInfo(true);
Certificate = GetCertificate(CertificateInfo);
@@ -848,81 +734,82 @@ namespace Emby.Server.Implementations
GetParseFn);
HttpServer.GlobalResponse = LocalizationManager.GetLocalizedString("StartupEmbyServerIsLoading");
- RegisterSingleInstance(HttpServer);
+ serviceCollection.AddSingleton(HttpServer);
ImageProcessor = GetImageProcessor();
- RegisterSingleInstance(ImageProcessor);
+ serviceCollection.AddSingleton(ImageProcessor);
TVSeriesManager = new TVSeriesManager(UserManager, UserDataManager, LibraryManager, ServerConfigurationManager);
- RegisterSingleInstance(TVSeriesManager);
+ serviceCollection.AddSingleton(TVSeriesManager);
var encryptionManager = new EncryptionManager();
- RegisterSingleInstance<IEncryptionManager>(encryptionManager);
+ serviceCollection.AddSingleton<IEncryptionManager>(encryptionManager);
DeviceManager = new DeviceManager(AuthenticationRepository, JsonSerializer, LibraryManager, LocalizationManager, UserManager, FileSystemManager, LibraryMonitor, ServerConfigurationManager, LoggerFactory, NetworkManager);
- RegisterSingleInstance(DeviceManager);
+ serviceCollection.AddSingleton(DeviceManager);
MediaSourceManager = new MediaSourceManager(ItemRepository, ApplicationPaths, LocalizationManager, UserManager, LibraryManager, LoggerFactory, JsonSerializer, FileSystemManager, UserDataManager, () => MediaEncoder);
- RegisterSingleInstance(MediaSourceManager);
+ serviceCollection.AddSingleton(MediaSourceManager);
SubtitleManager = new SubtitleManager(LoggerFactory, FileSystemManager, LibraryMonitor, MediaSourceManager, LocalizationManager);
- RegisterSingleInstance(SubtitleManager);
+ serviceCollection.AddSingleton(SubtitleManager);
ProviderManager = new ProviderManager(HttpClient, SubtitleManager, ServerConfigurationManager, LibraryMonitor, LoggerFactory, FileSystemManager, ApplicationPaths, () => LibraryManager, JsonSerializer);
- RegisterSingleInstance(ProviderManager);
+ serviceCollection.AddSingleton(ProviderManager);
DtoService = new DtoService(LoggerFactory, LibraryManager, UserDataManager, ItemRepository, ImageProcessor, ServerConfigurationManager, FileSystemManager, ProviderManager, () => ChannelManager, this, () => DeviceManager, () => MediaSourceManager, () => LiveTvManager);
- RegisterSingleInstance(DtoService);
+ serviceCollection.AddSingleton(DtoService);
ChannelManager = new ChannelManager(UserManager, DtoService, LibraryManager, LoggerFactory, ServerConfigurationManager, FileSystemManager, UserDataManager, JsonSerializer, LocalizationManager, HttpClient, ProviderManager);
- RegisterSingleInstance(ChannelManager);
+ serviceCollection.AddSingleton(ChannelManager);
SessionManager = new SessionManager(UserDataManager, LoggerFactory, LibraryManager, UserManager, musicManager, DtoService, ImageProcessor, JsonSerializer, this, HttpClient, AuthenticationRepository, DeviceManager, MediaSourceManager);
- RegisterSingleInstance(SessionManager);
+ serviceCollection.AddSingleton(SessionManager);
- var dlnaManager = new DlnaManager(XmlSerializer, FileSystemManager, ApplicationPaths, LoggerFactory, JsonSerializer, this, assemblyInfo);
- RegisterSingleInstance<IDlnaManager>(dlnaManager);
+ serviceCollection.AddSingleton<IDlnaManager>(
+ new DlnaManager(XmlSerializer, FileSystemManager, ApplicationPaths, LoggerFactory, JsonSerializer, this, assemblyInfo));
CollectionManager = new CollectionManager(LibraryManager, ApplicationPaths, LocalizationManager, FileSystemManager, LibraryMonitor, LoggerFactory, ProviderManager);
- RegisterSingleInstance(CollectionManager);
+ serviceCollection.AddSingleton(CollectionManager);
PlaylistManager = new PlaylistManager(LibraryManager, FileSystemManager, LibraryMonitor, LoggerFactory, UserManager, ProviderManager);
- RegisterSingleInstance(PlaylistManager);
+ serviceCollection.AddSingleton(PlaylistManager);
LiveTvManager = new LiveTvManager(this, ServerConfigurationManager, LoggerFactory, ItemRepository, ImageProcessor, UserDataManager, DtoService, UserManager, LibraryManager, TaskManager, LocalizationManager, JsonSerializer, FileSystemManager, () => ChannelManager);
- RegisterSingleInstance(LiveTvManager);
+ serviceCollection.AddSingleton(LiveTvManager);
UserViewManager = new UserViewManager(LibraryManager, LocalizationManager, UserManager, ChannelManager, LiveTvManager, ServerConfigurationManager);
- RegisterSingleInstance(UserViewManager);
+ serviceCollection.AddSingleton(UserViewManager);
NotificationManager = new NotificationManager(LoggerFactory, UserManager, ServerConfigurationManager);
- RegisterSingleInstance(NotificationManager);
+ serviceCollection.AddSingleton(NotificationManager);
- RegisterSingleInstance<IDeviceDiscovery>(new DeviceDiscovery(LoggerFactory, ServerConfigurationManager, SocketFactory));
+ serviceCollection.AddSingleton<IDeviceDiscovery>(
+ new DeviceDiscovery(LoggerFactory, ServerConfigurationManager, SocketFactory));
ChapterManager = new ChapterManager(LibraryManager, LoggerFactory, ServerConfigurationManager, ItemRepository);
- RegisterSingleInstance(ChapterManager);
+ serviceCollection.AddSingleton(ChapterManager);
- RegisterMediaEncoder(assemblyInfo);
+ RegisterMediaEncoder(serviceCollection);
EncodingManager = new MediaEncoder.EncodingManager(FileSystemManager, LoggerFactory, MediaEncoder, ChapterManager, LibraryManager);
- RegisterSingleInstance(EncodingManager);
+ serviceCollection.AddSingleton(EncodingManager);
var activityLogRepo = GetActivityLogRepository();
- RegisterSingleInstance(activityLogRepo);
- RegisterSingleInstance<IActivityManager>(new ActivityManager(LoggerFactory, activityLogRepo, UserManager));
+ serviceCollection.AddSingleton(activityLogRepo);
+ serviceCollection.AddSingleton<IActivityManager>(new ActivityManager(LoggerFactory, activityLogRepo, UserManager));
var authContext = new AuthorizationContext(AuthenticationRepository, UserManager);
- RegisterSingleInstance<IAuthorizationContext>(authContext);
- RegisterSingleInstance<ISessionContext>(new SessionContext(UserManager, authContext, SessionManager));
+ serviceCollection.AddSingleton<IAuthorizationContext>(authContext);
+ serviceCollection.AddSingleton<ISessionContext>(new SessionContext(UserManager, authContext, SessionManager));
AuthService = new AuthService(UserManager, authContext, ServerConfigurationManager, SessionManager, NetworkManager);
- RegisterSingleInstance(AuthService);
+ serviceCollection.AddSingleton(AuthService);
SubtitleEncoder = new MediaBrowser.MediaEncoding.Subtitles.SubtitleEncoder(LibraryManager, LoggerFactory, ApplicationPaths, FileSystemManager, MediaEncoder, JsonSerializer, HttpClient, MediaSourceManager, ProcessFactory);
- RegisterSingleInstance(SubtitleEncoder);
+ serviceCollection.AddSingleton(SubtitleEncoder);
- RegisterSingleInstance(CreateResourceFileManager());
+ serviceCollection.AddSingleton(CreateResourceFileManager());
displayPreferencesRepo.Initialize();
@@ -935,6 +822,8 @@ namespace Emby.Server.Implementations
((UserDataManager)UserDataManager).Repository = userDataRepo;
ItemRepository.Initialize(userDataRepo, UserManager);
((LibraryManager)LibraryManager).ItemRepository = ItemRepository;
+
+ _serviceProvider = serviceCollection.BuildServiceProvider();
}
protected virtual IBrotliCompressor CreateBrotliCompressor()
@@ -1066,7 +955,7 @@ namespace Emby.Server.Implementations
/// Registers the media encoder.
/// </summary>
/// <returns>Task.</returns>
- private void RegisterMediaEncoder(IAssemblyInfo assemblyInfo)
+ private void RegisterMediaEncoder(IServiceCollection serviceCollection)
{
string encoderPath = null;
string probePath = null;
@@ -1098,7 +987,7 @@ namespace Emby.Server.Implementations
5000);
MediaEncoder = mediaEncoder;
- RegisterSingleInstance(MediaEncoder);
+ serviceCollection.AddSingleton(MediaEncoder);
}
/// <summary>
@@ -1174,7 +1063,10 @@ namespace Emby.Server.Implementations
}
ConfigurationManager.AddParts(GetExports<IConfigurationFactory>());
- Plugins = GetExportsWithInfo<IPlugin>().Select(LoadPlugin).Where(i => i != null).ToArray();
+ Plugins = GetExports<IPlugin>()
+ .Select(LoadPlugin)
+ .Where(i => i != null)
+ .ToArray();
HttpServer.Init(GetExports<IService>(false), GetExports<IWebSocketListener>());
@@ -1208,19 +1100,15 @@ namespace Emby.Server.Implementations
IsoManager.AddParts(GetExports<IIsoMounter>());
}
- private IPlugin LoadPlugin(Tuple<IPlugin, string> info)
+ private IPlugin LoadPlugin(IPlugin plugin)
{
- var plugin = info.Item1;
- var assemblyFilePath = info.Item2;
-
try
{
- var assemblyPlugin = plugin as IPluginAssembly;
-
- if (assemblyPlugin != null)
+ if (plugin is IPluginAssembly assemblyPlugin)
{
var assembly = plugin.GetType().Assembly;
var assemblyName = assembly.GetName();
+ var assemblyFilePath = assembly.Location;
var dataFolderPath = Path.Combine(ApplicationPaths.PluginsPath, Path.GetFileNameWithoutExtension(assemblyFilePath));
@@ -1264,78 +1152,15 @@ namespace Emby.Server.Implementations
{
Logger.LogInformation("Loading assemblies");
- var assemblyInfos = GetComposablePartAssemblies();
-
- foreach (var assemblyInfo in assemblyInfos)
- {
- var assembly = assemblyInfo.Item1;
- var path = assemblyInfo.Item2;
-
- if (path == null)
- {
- Logger.LogInformation("Loading {assemblyName}", assembly.FullName);
- }
- else
+ AllConcreteTypes = GetComposablePartAssemblies()
+ .SelectMany(x => x.ExportedTypes)
+ .Where(type =>
{
- Logger.LogInformation("Loading {assemblyName} from {path}", assembly.FullName, path);
- }
- }
-
- AllConcreteTypes = assemblyInfos
- .SelectMany(GetTypes)
- .Where(info =>
- {
- var t = info.Item1;
- return t.IsClass && !t.IsAbstract && !t.IsInterface && !t.IsGenericType;
+ return type.IsClass && !type.IsAbstract && !type.IsInterface && !type.IsGenericType;
})
.ToArray();
}
- /// <summary>
- /// Gets a list of types within an assembly
- /// This will handle situations that would normally throw an exception - such as a type within the assembly that depends on some other non-existant reference
- /// </summary>
- protected List<Tuple<Type, string>> GetTypes(Tuple<Assembly, string> assemblyInfo)
- {
- if (assemblyInfo == null)
- {
- return new List<Tuple<Type, string>>();
- }
-
- var assembly = assemblyInfo.Item1;
-
- try
- {
- // This null checking really shouldn't be needed but adding it due to some
- // unhandled exceptions in mono 5.0 that are a little hard to hunt down
- var types = assembly.GetTypes() ?? new Type[] { };
- return types.Where(t => t != null).Select(i => new Tuple<Type, string>(i, assemblyInfo.Item2)).ToList();
- }
- catch (ReflectionTypeLoadException ex)
- {
- if (ex.LoaderExceptions != null)
- {
- foreach (var loaderException in ex.LoaderExceptions)
- {
- if (loaderException != null)
- {
- Logger.LogError("LoaderException: " + loaderException.Message);
- }
- }
- }
-
- // If it fails we can still get a list of the Types it was able to resolve
- var types = ex.Types ?? new Type[] { };
- return types.Where(t => t != null).Select(i => new Tuple<Type, string>(i, assemblyInfo.Item2)).ToList();
- }
- catch (Exception ex)
- {
- Logger.LogError(ex, "Error loading types from assembly");
-
- return new List<Tuple<Type, string>>();
- }
- }
-
private CertificateInfo CertificateInfo { get; set; }
protected X509Certificate Certificate { get; private set; }
@@ -1546,150 +1371,63 @@ namespace Emby.Server.Implementations
/// Gets the composable part assemblies.
/// </summary>
/// <returns>IEnumerable{Assembly}.</returns>
- protected List<Tuple<Assembly, string>> GetComposablePartAssemblies()
+ protected IEnumerable<Assembly> GetComposablePartAssemblies()
{
- var list = GetPluginAssemblies(ApplicationPaths.PluginsPath);
-
- // Gets all plugin assemblies by first reading all bytes of the .dll and calling Assembly.Load against that
- // This will prevent the .dll file from getting locked, and allow us to replace it when needed
+ if (Directory.Exists(ApplicationPaths.PluginsPath))
+ {
+ foreach (var file in Directory.EnumerateFiles(ApplicationPaths.PluginsPath, "*.dll", SearchOption.TopDirectoryOnly))
+ {
+ Logger.LogInformation("Loading assembly {Path}", file);
+ yield return Assembly.LoadFrom(file);
+ }
+ }
// Include composable parts in the Api assembly
- list.Add(GetAssembly(typeof(ApiEntryPoint)));
+ yield return typeof(ApiEntryPoint).Assembly;
// Include composable parts in the Dashboard assembly
- list.Add(GetAssembly(typeof(DashboardService)));
+ yield return typeof(DashboardService).Assembly;
// Include composable parts in the Model assembly
- list.Add(GetAssembly(typeof(SystemInfo)));
+ yield return typeof(SystemInfo).Assembly;
// Include composable parts in the Common assembly
- list.Add(GetAssembly(typeof(IApplicationHost)));
+ yield return typeof(IApplicationHost).Assembly;
// Include composable parts in the Controller assembly
- list.Add(GetAssembly(typeof(IServerApplicationHost)));
+ yield return typeof(IServerApplicationHost).Assembly;
// Include composable parts in the Providers assembly
- list.Add(GetAssembly(typeof(ProviderUtils)));
+ yield return typeof(ProviderUtils).Assembly;
// Include composable parts in the Photos assembly
- list.Add(GetAssembly(typeof(PhotoProvider)));
+ yield return typeof(PhotoProvider).Assembly;
// Emby.Server implementations
- list.Add(GetAssembly(typeof(InstallationManager)));
+ yield return typeof(InstallationManager).Assembly;
// MediaEncoding
- list.Add(GetAssembly(typeof(MediaBrowser.MediaEncoding.Encoder.MediaEncoder)));
+ yield return typeof(MediaBrowser.MediaEncoding.Encoder.MediaEncoder).Assembly;
// Dlna
- list.Add(GetAssembly(typeof(DlnaEntryPoint)));
+ yield return typeof(DlnaEntryPoint).Assembly;
// Local metadata
- list.Add(GetAssembly(typeof(BoxSetXmlSaver)));
+ yield return typeof(BoxSetXmlSaver).Assembly;
// Notifications
- list.Add(GetAssembly(typeof(NotificationManager)));
+ yield return typeof(NotificationManager).Assembly;
// Xbmc
- list.Add(GetAssembly(typeof(ArtistNfoProvider)));
-
- list.AddRange(GetAssembliesWithPartsInternal().Select(i => new Tuple<Assembly, string>(i, null)));
+ yield return typeof(ArtistNfoProvider).Assembly;
- return list.ToList();
- }
-
- protected abstract IEnumerable<Assembly> GetAssembliesWithPartsInternal();
-
- private List<Tuple<Assembly, string>> GetPluginAssemblies(string path)
- {
- try
+ foreach (var i in GetAssembliesWithPartsInternal())
{
- return FilterAssembliesToLoad(Directory.EnumerateFiles(path, "*.dll", SearchOption.TopDirectoryOnly))
- .Select(LoadAssembly)
- .Where(a => a != null)
- .ToList();
- }
- catch (DirectoryNotFoundException)
- {
- return new List<Tuple<Assembly, string>>();
+ yield return i;
}
}
- private IEnumerable<string> FilterAssembliesToLoad(IEnumerable<string> paths)
- {
-
- var exclude = new[]
- {
- "mbplus.dll",
- "mbintros.dll",
- "embytv.dll",
- "Messenger.dll",
- "Messages.dll",
- "MediaBrowser.Plugins.TvMazeProvider.dll",
- "MBBookshelf.dll",
- "MediaBrowser.Channels.Adult.YouJizz.dll",
- "MediaBrowser.Channels.Vine-co.dll",
- "MediaBrowser.Plugins.Vimeo.dll",
- "MediaBrowser.Channels.Vevo.dll",
- "MediaBrowser.Plugins.Twitch.dll",
- "MediaBrowser.Channels.SvtPlay.dll",
- "MediaBrowser.Plugins.SoundCloud.dll",
- "MediaBrowser.Plugins.SnesBox.dll",
- "MediaBrowser.Plugins.RottenTomatoes.dll",
- "MediaBrowser.Plugins.Revision3.dll",
- "MediaBrowser.Plugins.NesBox.dll",
- "MBChapters.dll",
- "MediaBrowser.Channels.LeagueOfLegends.dll",
- "MediaBrowser.Plugins.ADEProvider.dll",
- "MediaBrowser.Channels.BallStreams.dll",
- "MediaBrowser.Channels.Adult.Beeg.dll",
- "ChannelDownloader.dll",
- "Hamstercat.Emby.EmbyBands.dll",
- "EmbyTV.dll",
- "MediaBrowser.Channels.HitboxTV.dll",
- "MediaBrowser.Channels.HockeyStreams.dll",
- "MediaBrowser.Plugins.ITV.dll",
- "MediaBrowser.Plugins.Lastfm.dll",
- "ServerRestart.dll",
- "MediaBrowser.Plugins.NotifyMyAndroidNotifications.dll",
- "MetadataViewer.dll"
- };
-
- var minRequiredVersions = new Dictionary<string, Version>(StringComparer.OrdinalIgnoreCase)
- {
- { "moviethemesongs.dll", new Version(1, 6) },
- { "themesongs.dll", new Version(1, 2) }
- };
-
- return paths.Where(path =>
- {
- var filename = Path.GetFileName(path);
- if (exclude.Contains(filename ?? string.Empty, StringComparer.OrdinalIgnoreCase))
- {
- return false;
- }
-
- if (minRequiredVersions.TryGetValue(filename, out Version minRequiredVersion))
- {
- try
- {
- var version = Version.Parse(FileVersionInfo.GetVersionInfo(path).FileVersion);
-
- if (version < minRequiredVersion)
- {
- Logger.LogInformation("Not loading {filename} {version} because the minimum supported version is {minRequiredVersion}. Please update to the newer version", filename, version, minRequiredVersion);
- return false;
- }
- }
- catch (Exception ex)
- {
- Logger.LogError(ex, "Error getting version number from {path}", path);
-
- return false;
- }
- }
- return true;
- });
- }
+ protected abstract IEnumerable<Assembly> GetAssembliesWithPartsInternal();
/// <summary>
/// Gets the system status.
@@ -1718,7 +1456,7 @@ namespace Emby.Server.Implementations
SupportsHttps = SupportsHttps,
HttpsPortNumber = HttpsPort,
OperatingSystem = EnvironmentInfo.OperatingSystem.ToString(),
- OperatingSystemDisplayName = OperatingSystemDisplayName,
+ OperatingSystemDisplayName = EnvironmentInfo.OperatingSystemName,
CanSelfRestart = CanSelfRestart,
CanSelfUpdate = CanSelfUpdate,
CanLaunchWebBrowser = CanLaunchWebBrowser,
@@ -1788,7 +1526,7 @@ namespace Emby.Server.Implementations
public async Task<string> GetWanApiUrl(CancellationToken cancellationToken)
{
- var url = "http://ipv4.icanhazip.com";
+ const string url = "http://ipv4.icanhazip.com";
try
{
using (var response = await HttpClient.Get(new HttpRequestOptions
diff --git a/Emby.Server.Implementations/Channels/RefreshChannelsScheduledTask.cs b/Emby.Server.Implementations/Channels/RefreshChannelsScheduledTask.cs
index 844f77a1a..303a8ac7b 100644
--- a/Emby.Server.Implementations/Channels/RefreshChannelsScheduledTask.cs
+++ b/Emby.Server.Implementations/Channels/RefreshChannelsScheduledTask.cs
@@ -10,7 +10,7 @@ using Microsoft.Extensions.Logging;
namespace Emby.Server.Implementations.Channels
{
- class RefreshChannelsScheduledTask : IScheduledTask, IConfigurableScheduledTask
+ public class RefreshChannelsScheduledTask : IScheduledTask, IConfigurableScheduledTask
{
private readonly IChannelManager _channelManager;
private readonly IUserManager _userManager;
diff --git a/Emby.Server.Implementations/Emby.Server.Implementations.csproj b/Emby.Server.Implementations/Emby.Server.Implementations.csproj
index 8356a9501..6bf776f53 100644
--- a/Emby.Server.Implementations/Emby.Server.Implementations.csproj
+++ b/Emby.Server.Implementations/Emby.Server.Implementations.csproj
@@ -22,9 +22,10 @@
</ItemGroup>
<ItemGroup>
+ <PackageReference Include="Microsoft.Extensions.Logging" Version="2.2.0" />
+ <PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="2.2.0" />
<PackageReference Include="ServiceStack.Text.Core" Version="5.4.0" />
<PackageReference Include="sharpcompress" Version="0.22.0" />
- <PackageReference Include="SimpleInjector" Version="4.4.2" />
<PackageReference Include="SQLitePCL.pretty.netstandard" Version="1.0.0" />
<PackageReference Include="UTF.Unknown" Version="1.0.0-beta1" />
</ItemGroup>
diff --git a/Emby.Server.Implementations/EntryPoints/UserDataChangeNotifier.cs b/Emby.Server.Implementations/EntryPoints/UserDataChangeNotifier.cs
index 774ed09da..a5badacee 100644
--- a/Emby.Server.Implementations/EntryPoints/UserDataChangeNotifier.cs
+++ b/Emby.Server.Implementations/EntryPoints/UserDataChangeNotifier.cs
@@ -14,7 +14,7 @@ using Microsoft.Extensions.Logging;
namespace Emby.Server.Implementations.EntryPoints
{
- class UserDataChangeNotifier : IServerEntryPoint
+ public class UserDataChangeNotifier : IServerEntryPoint
{
private readonly ISessionManager _sessionManager;
private readonly ILogger _logger;
diff --git a/Emby.Server.Implementations/Library/Resolvers/SpecialFolderResolver.cs b/Emby.Server.Implementations/Library/Resolvers/SpecialFolderResolver.cs
index fa8c89e88..7e4b38b4c 100644
--- a/Emby.Server.Implementations/Library/Resolvers/SpecialFolderResolver.cs
+++ b/Emby.Server.Implementations/Library/Resolvers/SpecialFolderResolver.cs
@@ -9,7 +9,7 @@ using MediaBrowser.Model.IO;
namespace Emby.Server.Implementations.Library.Resolvers
{
- class SpecialFolderResolver : FolderResolver<Folder>
+ public class SpecialFolderResolver : FolderResolver<Folder>
{
private readonly IFileSystem _fileSystem;
private readonly IServerApplicationPaths _appPaths;
diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/M3uParser.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/M3uParser.cs
index 9a01c42d3..d2a835b85 100644
--- a/Emby.Server.Implementations/LiveTv/TunerHosts/M3uParser.cs
+++ b/Emby.Server.Implementations/LiveTv/TunerHosts/M3uParser.cs
@@ -157,56 +157,56 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
var nameInExtInf = nameParts.Length > 1 ? nameParts.Last().Trim() : null;
string numberString = null;
+ string attributeValue;
+ double doubleValue;
- // Check for channel number with the format from SatIp
- // #EXTINF:0,84. VOX Schweiz
- // #EXTINF:0,84.0 - VOX Schweiz
- if (!string.IsNullOrWhiteSpace(nameInExtInf))
+ if (attributes.TryGetValue("tvg-chno", out attributeValue))
{
- var numberIndex = nameInExtInf.IndexOf(' ');
- if (numberIndex > 0)
+ if (double.TryParse(attributeValue, NumberStyles.Any, CultureInfo.InvariantCulture, out doubleValue))
{
- var numberPart = nameInExtInf.Substring(0, numberIndex).Trim(new[] { ' ', '.' });
-
- if (double.TryParse(numberPart, NumberStyles.Any, CultureInfo.InvariantCulture, out var number))
- {
- numberString = numberPart;
- }
+ numberString = attributeValue;
}
}
- if (!string.IsNullOrWhiteSpace(numberString))
- {
- numberString = numberString.Trim();
- }
-
if (!IsValidChannelNumber(numberString))
{
- if (attributes.TryGetValue("tvg-id", out string value))
+ if (attributes.TryGetValue("tvg-id", out attributeValue))
{
- if (double.TryParse(value, NumberStyles.Any, CultureInfo.InvariantCulture, out var doubleValue))
+ if (double.TryParse(attributeValue, NumberStyles.Any, CultureInfo.InvariantCulture, out doubleValue))
{
- numberString = value;
+ numberString = attributeValue;
+ }
+ else if (attributes.TryGetValue("channel-id", out attributeValue))
+ {
+ if (double.TryParse(attributeValue, NumberStyles.Any, CultureInfo.InvariantCulture, out doubleValue))
+ {
+ numberString = attributeValue;
+ }
}
}
- }
- if (!string.IsNullOrWhiteSpace(numberString))
- {
- numberString = numberString.Trim();
- }
-
- if (!IsValidChannelNumber(numberString))
- {
- if (attributes.TryGetValue("channel-id", out string value))
+ if (String.IsNullOrWhiteSpace(numberString))
{
- numberString = value;
+ // Using this as a fallback now as this leads to Problems with channels like "5 USA"
+ // where 5 isnt ment to be the channel number
+ // Check for channel number with the format from SatIp
+ // #EXTINF:0,84. VOX Schweiz
+ // #EXTINF:0,84.0 - VOX Schweiz
+ if (!string.IsNullOrWhiteSpace(nameInExtInf))
+ {
+ var numberIndex = nameInExtInf.IndexOf(' ');
+ if (numberIndex > 0)
+ {
+ var numberPart = nameInExtInf.Substring(0, numberIndex).Trim(new[] { ' ', '.' });
+
+ if (double.TryParse(numberPart, NumberStyles.Any, CultureInfo.InvariantCulture, out var number))
+ {
+ numberString = numberPart;
+ }
+ }
+ }
}
- }
- if (!string.IsNullOrWhiteSpace(numberString))
- {
- numberString = numberString.Trim();
}
if (!IsValidChannelNumber(numberString))
@@ -214,7 +214,11 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
numberString = null;
}
- if (string.IsNullOrWhiteSpace(numberString))
+ if (!string.IsNullOrWhiteSpace(numberString))
+ {
+ numberString = numberString.Trim();
+ }
+ else
{
if (string.IsNullOrWhiteSpace(mediaUrl))
{
diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/SharedHttpStream.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/SharedHttpStream.cs
index 4eff9252e..d74cf3be2 100644
--- a/Emby.Server.Implementations/LiveTv/TunerHosts/SharedHttpStream.cs
+++ b/Emby.Server.Implementations/LiveTv/TunerHosts/SharedHttpStream.cs
@@ -94,7 +94,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
var now = DateTime.UtcNow;
- var _ = StartStreaming(response, taskCompletionSource, LiveStreamCancellationTokenSource.Token);
+ _ = StartStreaming(response, taskCompletionSource, LiveStreamCancellationTokenSource.Token);
//OpenedMediaSource.Protocol = MediaProtocol.File;
//OpenedMediaSource.Path = tempFile;
diff --git a/Emby.Server.Implementations/ScheduledTasks/Tasks/ChapterImagesTask.cs b/Emby.Server.Implementations/ScheduledTasks/Tasks/ChapterImagesTask.cs
index 81fdb96d2..2f07ff15a 100644
--- a/Emby.Server.Implementations/ScheduledTasks/Tasks/ChapterImagesTask.cs
+++ b/Emby.Server.Implementations/ScheduledTasks/Tasks/ChapterImagesTask.cs
@@ -21,7 +21,7 @@ namespace Emby.Server.Implementations.ScheduledTasks
/// <summary>
/// Class ChapterImagesTask
/// </summary>
- class ChapterImagesTask : IScheduledTask
+ public class ChapterImagesTask : IScheduledTask
{
/// <summary>
/// The _logger
diff --git a/Emby.Server.Implementations/ServerApplicationPaths.cs b/Emby.Server.Implementations/ServerApplicationPaths.cs
index 36975df50..05f6469ec 100644
--- a/Emby.Server.Implementations/ServerApplicationPaths.cs
+++ b/Emby.Server.Implementations/ServerApplicationPaths.cs
@@ -15,21 +15,17 @@ namespace Emby.Server.Implementations
/// </summary>
public ServerApplicationPaths(
string programDataPath,
- string appFolderPath,
- string applicationResourcesPath,
- string logDirectoryPath = null,
- string configurationDirectoryPath = null,
- string cacheDirectoryPath = null)
+ string logDirectoryPath,
+ string configurationDirectoryPath,
+ string cacheDirectoryPath)
: base(programDataPath,
- appFolderPath,
logDirectoryPath,
configurationDirectoryPath,
cacheDirectoryPath)
{
- ApplicationResourcesPath = applicationResourcesPath;
}
- public string ApplicationResourcesPath { get; private set; }
+ public string ApplicationResourcesPath { get; } = AppContext.BaseDirectory;
/// <summary>
/// Gets the path to the base root media directory
@@ -148,7 +144,6 @@ namespace Emby.Server.Implementations
set => _internalMetadataPath = value;
}
- private const string _virtualInternalMetadataPath = "%MetadataPath%";
- public string VirtualInternalMetadataPath => _virtualInternalMetadataPath;
+ public string VirtualInternalMetadataPath { get; } = "%MetadataPath%";
}
}
diff --git a/Emby.Server.Implementations/Sorting/AiredEpisodeOrderComparer.cs b/Emby.Server.Implementations/Sorting/AiredEpisodeOrderComparer.cs
index 271188314..16507466f 100644
--- a/Emby.Server.Implementations/Sorting/AiredEpisodeOrderComparer.cs
+++ b/Emby.Server.Implementations/Sorting/AiredEpisodeOrderComparer.cs
@@ -6,7 +6,7 @@ using MediaBrowser.Model.Querying;
namespace Emby.Server.Implementations.Sorting
{
- class AiredEpisodeOrderComparer : IBaseItemComparer
+ public class AiredEpisodeOrderComparer : IBaseItemComparer
{
/// <summary>
/// Compares the specified x.
diff --git a/Emby.Server.Implementations/Sorting/SeriesSortNameComparer.cs b/Emby.Server.Implementations/Sorting/SeriesSortNameComparer.cs
index 942e84704..46e0dd918 100644
--- a/Emby.Server.Implementations/Sorting/SeriesSortNameComparer.cs
+++ b/Emby.Server.Implementations/Sorting/SeriesSortNameComparer.cs
@@ -5,7 +5,7 @@ using MediaBrowser.Model.Querying;
namespace Emby.Server.Implementations.Sorting
{
- class SeriesSortNameComparer : IBaseItemComparer
+ public class SeriesSortNameComparer : IBaseItemComparer
{
/// <summary>
/// Compares the specified x.
diff --git a/Jellyfin.Drawing.Skia/SkiaEncoder.cs b/Jellyfin.Drawing.Skia/SkiaEncoder.cs
index dc714ed18..5060476ba 100644
--- a/Jellyfin.Drawing.Skia/SkiaEncoder.cs
+++ b/Jellyfin.Drawing.Skia/SkiaEncoder.cs
@@ -282,7 +282,7 @@ namespace Jellyfin.Drawing.Skia
var bitmap = new SKBitmap(codec.Info.Width, codec.Info.Height, !requiresTransparencyHack);
// decode
- var _ = codec.GetPixels(bitmap.Info, bitmap.GetPixels());
+ _ = codec.GetPixels(bitmap.Info, bitmap.GetPixels());
origin = codec.EncodedOrigin;
diff --git a/Jellyfin.Server/CoreAppHost.cs b/Jellyfin.Server/CoreAppHost.cs
index 315e34a04..126cb467f 100644
--- a/Jellyfin.Server/CoreAppHost.cs
+++ b/Jellyfin.Server/CoreAppHost.cs
@@ -18,15 +18,17 @@ namespace Jellyfin.Server
public override bool CanSelfRestart => StartupOptions.RestartPath != null;
+ protected override bool SupportsDualModeSockets => true;
+
protected override void RestartInternal() => Program.Restart();
protected override IEnumerable<Assembly> GetAssembliesWithPartsInternal()
- => new[] { typeof(CoreAppHost).Assembly };
+ {
+ yield return typeof(CoreAppHost).Assembly;
+ }
protected override void ShutdownInternal() => Program.Shutdown();
- protected override bool SupportsDualModeSockets => true;
-
protected override IHttpListener CreateHttpListener()
=> new WebSocketSharpListener(
Logger,
@@ -37,7 +39,6 @@ namespace Jellyfin.Server
CryptographyProvider,
SupportsDualModeSockets,
FileSystemManager,
- EnvironmentInfo
- );
+ EnvironmentInfo);
}
}
diff --git a/Jellyfin.Server/Jellyfin.Server.csproj b/Jellyfin.Server/Jellyfin.Server.csproj
index b1515df43..bd670df52 100644
--- a/Jellyfin.Server/Jellyfin.Server.csproj
+++ b/Jellyfin.Server/Jellyfin.Server.csproj
@@ -5,11 +5,14 @@
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp2.1</TargetFramework>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
+ <GenerateDocumentationFile>true</GenerateDocumentationFile>
</PropertyGroup>
<PropertyGroup>
<!-- We need C# 7.1 for async main-->
<LangVersion>latest</LangVersion>
+ <!-- Disable documentation warnings (for now) -->
+ <NoWarn>SA1600;CS1591</NoWarn>
</PropertyGroup>
<ItemGroup>
@@ -20,6 +23,10 @@
<EmbeddedResource Include="Resources/Configuration/*" />
</ItemGroup>
+ <PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
+ <TreatWarningsAsErrors>true</TreatWarningsAsErrors>
+ </PropertyGroup>
+
<!-- Code analysers-->
<ItemGroup Condition=" '$(Configuration)' == 'Debug' ">
<PackageReference Include="Microsoft.CodeAnalysis.FxCopAnalyzers" Version="2.6.3" />
diff --git a/Jellyfin.Server/Program.cs b/Jellyfin.Server/Program.cs
index 7826fde35..ac5aab460 100644
--- a/Jellyfin.Server/Program.cs
+++ b/Jellyfin.Server/Program.cs
@@ -21,6 +21,7 @@ using MediaBrowser.Controller.Drawing;
using MediaBrowser.Model.Globalization;
using MediaBrowser.Model.IO;
using Microsoft.Extensions.Configuration;
+using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Serilog;
using Serilog.AspNetCore;
@@ -56,13 +57,28 @@ namespace Jellyfin.Server
errs => Task.FromResult(0)).ConfigureAwait(false);
}
+ public static void Shutdown()
+ {
+ if (!_tokenSource.IsCancellationRequested)
+ {
+ _tokenSource.Cancel();
+ }
+ }
+
+ public static void Restart()
+ {
+ _restartOnShutdown = true;
+
+ Shutdown();
+ }
+
private static async Task StartApp(StartupOptions options)
{
ServerApplicationPaths appPaths = CreateApplicationPaths(options);
// $JELLYFIN_LOG_DIR needs to be set for the logger configuration manager
Environment.SetEnvironmentVariable("JELLYFIN_LOG_DIR", appPaths.LogDirectoryPath);
- await CreateLogger(appPaths);
+ await CreateLogger(appPaths).ConfigureAwait(false);
_logger = _loggerFactory.CreateLogger("Main");
AppDomain.CurrentDomain.UnhandledException += (sender, e)
@@ -75,6 +91,7 @@ namespace Jellyfin.Server
{
return; // Already shutting down
}
+
e.Cancel = true;
_logger.LogInformation("Ctrl+C, shutting down");
Environment.ExitCode = 128 + 2;
@@ -88,6 +105,7 @@ namespace Jellyfin.Server
{
return; // Already shutting down
}
+
_logger.LogInformation("Received a SIGTERM signal, shutting down");
Environment.ExitCode = 128 + 15;
Shutdown();
@@ -101,7 +119,7 @@ namespace Jellyfin.Server
SQLitePCL.Batteries_V2.Init();
// Allow all https requests
- ServicePointManager.ServerCertificateValidationCallback = new RemoteCertificateValidationCallback(delegate { return true; });
+ ServicePointManager.ServerCertificateValidationCallback = new RemoteCertificateValidationCallback(delegate { return true; } );
var fileSystem = new ManagedFileSystem(_loggerFactory, environmentInfo, null, appPaths.TempDirectory, true);
@@ -114,18 +132,18 @@ namespace Jellyfin.Server
new NullImageEncoder(),
new NetworkManager(_loggerFactory, environmentInfo)))
{
- await appHost.Init();
+ await appHost.Init(new ServiceCollection()).ConfigureAwait(false);
appHost.ImageProcessor.ImageEncoder = GetImageEncoder(fileSystem, appPaths, appHost.LocalizationManager);
- await appHost.RunStartupTasks();
+ await appHost.RunStartupTasks().ConfigureAwait(false);
// TODO: read input for a stop command
try
{
// Block main thread until shutdown
- await Task.Delay(-1, _tokenSource.Token);
+ await Task.Delay(-1, _tokenSource.Token).ConfigureAwait(false);
}
catch (TaskCanceledException)
{
@@ -139,112 +157,156 @@ namespace Jellyfin.Server
}
}
+ /// <summary>
+ /// Create the data, config and log paths from the variety of inputs(command line args,
+ /// environment variables) or decide on what default to use. For Windows it's %AppPath%
+ /// for everything else the XDG approach is followed:
+ /// https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html
+ /// </summary>
+ /// <param name="options">StartupOptions</param>
+ /// <returns>ServerApplicationPaths</returns>
private static ServerApplicationPaths CreateApplicationPaths(StartupOptions options)
{
- string programDataPath = Environment.GetEnvironmentVariable("JELLYFIN_DATA_PATH");
- if (string.IsNullOrEmpty(programDataPath))
+ // dataDir
+ // IF --datadir
+ // ELSE IF $JELLYFIN_DATA_PATH
+ // ELSE IF windows, use <%APPDATA%>/jellyfin
+ // ELSE IF $XDG_DATA_HOME then use $XDG_DATA_HOME/jellyfin
+ // ELSE use $HOME/.local/share/jellyfin
+ var dataDir = options.DataDir;
+
+ if (string.IsNullOrEmpty(dataDir))
{
- if (options.DataDir != null)
- {
- programDataPath = options.DataDir;
- }
- else
+ dataDir = Environment.GetEnvironmentVariable("JELLYFIN_DATA_PATH");
+
+ if (string.IsNullOrEmpty(dataDir))
{
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
- programDataPath = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData);
+ dataDir = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData);
}
else
{
// $XDG_DATA_HOME defines the base directory relative to which user specific data files should be stored.
- programDataPath = Environment.GetEnvironmentVariable("XDG_DATA_HOME");
- // If $XDG_DATA_HOME is either not set or empty, $HOME/.local/share should be used.
- if (string.IsNullOrEmpty(programDataPath))
+ dataDir = Environment.GetEnvironmentVariable("XDG_DATA_HOME");
+
+ // If $XDG_DATA_HOME is either not set or empty, a default equal to $HOME/.local/share should be used.
+ if (string.IsNullOrEmpty(dataDir))
{
- programDataPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), ".local", "share");
+ dataDir = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), ".local", "share");
}
}
- programDataPath = Path.Combine(programDataPath, "jellyfin");
+ dataDir = Path.Combine(dataDir, "jellyfin");
}
}
- if (string.IsNullOrEmpty(programDataPath))
- {
- Console.WriteLine("Cannot continue without path to program data folder (try -programdata)");
- Environment.Exit(1);
- }
- else
- {
- Directory.CreateDirectory(programDataPath);
- }
+ // configDir
+ // IF --configdir
+ // ELSE IF $JELLYFIN_CONFIG_DIR
+ // ELSE IF --datadir, use <datadir>/config (assume portable run)
+ // ELSE IF <datadir>/config exists, use that
+ // ELSE IF windows, use <datadir>/config
+ // ELSE IF $XDG_CONFIG_HOME use $XDG_CONFIG_HOME/jellyfin
+ // ELSE $HOME/.config/jellyfin
+ var configDir = options.ConfigDir;
- string configDir = Environment.GetEnvironmentVariable("JELLYFIN_CONFIG_DIR");
if (string.IsNullOrEmpty(configDir))
{
- if (options.ConfigDir != null)
- {
- configDir = options.ConfigDir;
- }
- else
+ configDir = Environment.GetEnvironmentVariable("JELLYFIN_CONFIG_DIR");
+
+ if (string.IsNullOrEmpty(configDir))
{
- // Let BaseApplicationPaths set up the default value
- configDir = null;
+ if (options.DataDir != null || Directory.Exists(Path.Combine(dataDir, "config")) || RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
+ {
+ // Hang config folder off already set dataDir
+ configDir = Path.Combine(dataDir, "config");
+ }
+ else
+ {
+ // $XDG_CONFIG_HOME defines the base directory relative to which user specific configuration files should be stored.
+ configDir = Environment.GetEnvironmentVariable("XDG_CONFIG_HOME");
+
+ // If $XDG_CONFIG_HOME is either not set or empty, a default equal to $HOME /.config should be used.
+ if (string.IsNullOrEmpty(configDir))
+ {
+ configDir = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), ".config");
+ }
+
+ configDir = Path.Combine(configDir, "jellyfin");
+ }
}
}
- if (configDir != null)
- {
- Directory.CreateDirectory(configDir);
- }
+ // cacheDir
+ // IF --cachedir
+ // ELSE IF $JELLYFIN_CACHE_DIR
+ // ELSE IF windows, use <datadir>/cache
+ // ELSE IF XDG_CACHE_HOME, use $XDG_CACHE_HOME/jellyfin
+ // ELSE HOME/.cache/jellyfin
+ var cacheDir = options.CacheDir;
- string cacheDir = Environment.GetEnvironmentVariable("JELLYFIN_CACHE_DIR");
if (string.IsNullOrEmpty(cacheDir))
{
- if (options.CacheDir != null)
- {
- cacheDir = options.CacheDir;
- }
- else if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
+ cacheDir = Environment.GetEnvironmentVariable("JELLYFIN_CACHE_DIR");
+
+ if (string.IsNullOrEmpty(cacheDir))
{
- // $XDG_CACHE_HOME defines the base directory relative to which user specific non-essential data files should be stored.
- cacheDir = Environment.GetEnvironmentVariable("XDG_CACHE_HOME");
- // If $XDG_CACHE_HOME is either not set or empty, $HOME/.cache should be used.
- if (string.IsNullOrEmpty(cacheDir))
+ if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
- cacheDir = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), ".cache");
+ // Hang cache folder off already set dataDir
+ cacheDir = Path.Combine(dataDir, "cache");
+ }
+ else
+ {
+ // $XDG_CACHE_HOME defines the base directory relative to which user specific non-essential data files should be stored.
+ cacheDir = Environment.GetEnvironmentVariable("XDG_CACHE_HOME");
+
+ // If $XDG_CACHE_HOME is either not set or empty, a default equal to $HOME/.cache should be used.
+ if (string.IsNullOrEmpty(cacheDir))
+ {
+ cacheDir = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), ".cache");
+ }
+
+ cacheDir = Path.Combine(cacheDir, "jellyfin");
}
- cacheDir = Path.Combine(cacheDir, "jellyfin");
}
}
- if (cacheDir != null)
- {
- Directory.CreateDirectory(cacheDir);
- }
+ // logDir
+ // IF --logdir
+ // ELSE IF $JELLYFIN_LOG_DIR
+ // ELSE IF --datadir, use <datadir>/log (assume portable run)
+ // ELSE <datadir>/log
+ var logDir = options.LogDir;
- string logDir = Environment.GetEnvironmentVariable("JELLYFIN_LOG_DIR");
if (string.IsNullOrEmpty(logDir))
{
- if (options.LogDir != null)
- {
- logDir = options.LogDir;
- }
- else
+ logDir = Environment.GetEnvironmentVariable("JELLYFIN_LOG_DIR");
+
+ if (string.IsNullOrEmpty(logDir))
{
- // Let BaseApplicationPaths set up the default value
- logDir = null;
+ // Hang log folder off already set dataDir
+ logDir = Path.Combine(dataDir, "log");
}
}
- if (logDir != null)
+ // Ensure the main folders exist before we continue
+ try
{
+ Directory.CreateDirectory(dataDir);
Directory.CreateDirectory(logDir);
+ Directory.CreateDirectory(configDir);
+ Directory.CreateDirectory(cacheDir);
+ }
+ catch (IOException ex)
+ {
+ Console.Error.WriteLine("Error whilst attempting to create folder");
+ Console.Error.WriteLine(ex.ToString());
+ Environment.Exit(1);
}
- string appPath = AppContext.BaseDirectory;
-
- return new ServerApplicationPaths(programDataPath, appPath, appPath, logDir, configDir, cacheDir);
+ return new ServerApplicationPaths(dataDir, logDir, configDir, cacheDir);
}
private static async Task CreateLogger(IApplicationPaths appPaths)
@@ -263,6 +325,7 @@ namespace Jellyfin.Server
await rscstr.CopyToAsync(fstr).ConfigureAwait(false);
}
}
+
var configuration = new ConfigurationBuilder()
.SetBasePath(appPaths.ConfigurationDirectoryPath)
.AddJsonFile("logging.json")
@@ -290,7 +353,7 @@ namespace Jellyfin.Server
}
}
- public static IImageEncoder GetImageEncoder(
+ private static IImageEncoder GetImageEncoder(
IFileSystem fileSystem,
IApplicationPaths appPaths,
ILocalizationManager localizationManager)
@@ -331,26 +394,12 @@ namespace Jellyfin.Server
{
return MediaBrowser.Model.System.OperatingSystem.BSD;
}
+
throw new Exception($"Can't resolve OS with description: '{osDescription}'");
}
}
}
- public static void Shutdown()
- {
- if (!_tokenSource.IsCancellationRequested)
- {
- _tokenSource.Cancel();
- }
- }
-
- public static void Restart()
- {
- _restartOnShutdown = true;
-
- Shutdown();
- }
-
private static void StartNewInstance(StartupOptions options)
{
_logger.LogInformation("Starting new instance");
diff --git a/Jellyfin.Server/SocketSharp/RequestMono.cs b/Jellyfin.Server/SocketSharp/RequestMono.cs
index a8ba4cdb5..24cf994ea 100644
--- a/Jellyfin.Server/SocketSharp/RequestMono.cs
+++ b/Jellyfin.Server/SocketSharp/RequestMono.cs
@@ -13,7 +13,7 @@ namespace Jellyfin.Server.SocketSharp
{
internal static string GetParameter(string header, string attr)
{
- int ap = header.IndexOf(attr);
+ int ap = header.IndexOf(attr, StringComparison.Ordinal);
if (ap == -1)
{
return null;
@@ -82,9 +82,7 @@ namespace Jellyfin.Server.SocketSharp
}
else
{
- //
// We use a substream, as in 2.x we will support large uploads streamed to disk,
- //
var sub = new HttpPostedFile(e.Filename, e.ContentType, input, e.Start, e.Length);
files[e.Name] = sub;
}
@@ -127,8 +125,12 @@ namespace Jellyfin.Server.SocketSharp
public string Authorization => string.IsNullOrEmpty(request.Headers["Authorization"]) ? null : request.Headers["Authorization"];
- protected bool validate_cookies, validate_query_string, validate_form;
- protected bool checked_cookies, checked_query_string, checked_form;
+ protected bool validate_cookies { get; set; }
+ protected bool validate_query_string { get; set; }
+ protected bool validate_form { get; set; }
+ protected bool checked_cookies { get; set; }
+ protected bool checked_query_string { get; set; }
+ protected bool checked_form { get; set; }
private static void ThrowValidationException(string name, string key, string value)
{
@@ -138,8 +140,12 @@ namespace Jellyfin.Server.SocketSharp
v = v.Substring(0, 16) + "...\"";
}
- string msg = string.Format("A potentially dangerous Request.{0} value was " +
- "detected from the client ({1}={2}).", name, key, v);
+ string msg = string.Format(
+ CultureInfo.InvariantCulture,
+ "A potentially dangerous Request.{0} value was detected from the client ({1}={2}).",
+ name,
+ key,
+ v);
throw new Exception(msg);
}
@@ -179,6 +185,7 @@ namespace Jellyfin.Server.SocketSharp
for (int idx = 1; idx < len; idx++)
{
char next = val[idx];
+
// See http://secunia.com/advisories/14325
if (current == '<' || current == '\xff1c')
{
@@ -256,6 +263,7 @@ namespace Jellyfin.Server.SocketSharp
value.Append((char)c);
}
}
+
if (c == -1)
{
AddRawKeyValue(form, key, value);
@@ -271,6 +279,7 @@ namespace Jellyfin.Server.SocketSharp
key.Append((char)c);
}
}
+
if (c == -1)
{
AddRawKeyValue(form, key, value);
@@ -308,6 +317,7 @@ namespace Jellyfin.Server.SocketSharp
result.Append(key);
result.Append('=');
}
+
result.Append(pair.Value);
}
@@ -429,13 +439,13 @@ namespace Jellyfin.Server.SocketSharp
real = position + d;
break;
default:
- throw new ArgumentException(nameof(origin));
+ throw new ArgumentException("Unknown SeekOrigin value", nameof(origin));
}
long virt = real - offset;
if (virt < 0 || virt > Length)
{
- throw new ArgumentException();
+ throw new ArgumentException("Invalid position", nameof(d));
}
position = s.Seek(real, SeekOrigin.Begin);
@@ -491,11 +501,6 @@ namespace Jellyfin.Server.SocketSharp
public Stream InputStream => stream;
}
- private class Helpers
- {
- public static readonly CultureInfo InvariantCulture = CultureInfo.InvariantCulture;
- }
-
internal static class StrUtils
{
public static bool StartsWith(string str1, string str2, bool ignore_case)
@@ -533,12 +538,17 @@ namespace Jellyfin.Server.SocketSharp
public class Element
{
- public string ContentType;
- public string Name;
- public string Filename;
- public Encoding Encoding;
- public long Start;
- public long Length;
+ public string ContentType { get; set; }
+
+ public string Name { get; set; }
+
+ public string Filename { get; set; }
+
+ public Encoding Encoding { get; set; }
+
+ public long Start { get; set; }
+
+ public long Length { get; set; }
public override string ToString()
{
@@ -547,15 +557,23 @@ namespace Jellyfin.Server.SocketSharp
}
}
+ private const byte LF = (byte)'\n';
+
+ private const byte CR = (byte)'\r';
+
private Stream data;
+
private string boundary;
- private byte[] boundary_bytes;
+
+ private byte[] boundaryBytes;
+
private byte[] buffer;
- private bool at_eof;
+
+ private bool atEof;
+
private Encoding encoding;
- private StringBuilder sb;
- private const byte LF = (byte)'\n', CR = (byte)'\r';
+ private StringBuilder sb;
// See RFC 2046
// In the case of multipart entities, in which one or more different
@@ -570,18 +588,48 @@ namespace Jellyfin.Server.SocketSharp
public HttpMultipart(Stream data, string b, Encoding encoding)
{
this.data = data;
- //DB: 30/01/11: cannot set or read the Position in HttpListener in Win.NET
- //var ms = new MemoryStream(32 * 1024);
- //data.CopyTo(ms);
- //this.data = ms;
-
boundary = b;
- boundary_bytes = encoding.GetBytes(b);
- buffer = new byte[boundary_bytes.Length + 2]; // CRLF or '--'
+ boundaryBytes = encoding.GetBytes(b);
+ buffer = new byte[boundaryBytes.Length + 2]; // CRLF or '--'
this.encoding = encoding;
sb = new StringBuilder();
}
+ public Element ReadNextElement()
+ {
+ if (atEof || ReadBoundary())
+ {
+ return null;
+ }
+
+ var elem = new Element();
+ string header;
+ while ((header = ReadHeaders()) != null)
+ {
+ if (StrUtils.StartsWith(header, "Content-Disposition:", true))
+ {
+ elem.Name = GetContentDispositionAttribute(header, "name");
+ elem.Filename = StripPath(GetContentDispositionAttributeWithEncoding(header, "filename"));
+ }
+ else if (StrUtils.StartsWith(header, "Content-Type:", true))
+ {
+ elem.ContentType = header.Substring("Content-Type:".Length).Trim();
+ elem.Encoding = GetEncoding(elem.ContentType);
+ }
+ }
+
+ long start = data.Position;
+ elem.Start = start;
+ long pos = MoveToNextBoundary();
+ if (pos == -1)
+ {
+ return null;
+ }
+
+ elem.Length = pos - start;
+ return elem;
+ }
+
private string ReadLine()
{
// CRLF or LF are ok as line endings.
@@ -600,6 +648,7 @@ namespace Jellyfin.Server.SocketSharp
{
break;
}
+
got_cr = b == CR;
sb.Append((char)b);
}
@@ -769,7 +818,7 @@ namespace Jellyfin.Server.SocketSharp
return -1;
}
- if (!CompareBytes(boundary_bytes, buffer))
+ if (!CompareBytes(boundaryBytes, buffer))
{
state = 0;
data.Position = retval + 2;
@@ -785,7 +834,7 @@ namespace Jellyfin.Server.SocketSharp
if (buffer[bl - 2] == '-' && buffer[bl - 1] == '-')
{
- at_eof = true;
+ atEof = true;
}
else if (buffer[bl - 2] != CR || buffer[bl - 1] != LF)
{
@@ -800,6 +849,7 @@ namespace Jellyfin.Server.SocketSharp
c = data.ReadByte();
continue;
}
+
data.Position = retval + 2;
if (got_cr)
{
@@ -818,42 +868,6 @@ namespace Jellyfin.Server.SocketSharp
return retval;
}
- public Element ReadNextElement()
- {
- if (at_eof || ReadBoundary())
- {
- return null;
- }
-
- var elem = new Element();
- string header;
- while ((header = ReadHeaders()) != null)
- {
- if (StrUtils.StartsWith(header, "Content-Disposition:", true))
- {
- elem.Name = GetContentDispositionAttribute(header, "name");
- elem.Filename = StripPath(GetContentDispositionAttributeWithEncoding(header, "filename"));
- }
- else if (StrUtils.StartsWith(header, "Content-Type:", true))
- {
- elem.ContentType = header.Substring("Content-Type:".Length).Trim();
- elem.Encoding = GetEncoding(elem.ContentType);
- }
- }
-
- long start = 0;
- start = data.Position;
- elem.Start = start;
- long pos = MoveToNextBoundary();
- if (pos == -1)
- {
- return null;
- }
-
- elem.Length = pos - start;
- return elem;
- }
-
private static string StripPath(string path)
{
if (path == null || path.Length == 0)
diff --git a/Jellyfin.Server/SocketSharp/SharpWebSocket.cs b/Jellyfin.Server/SocketSharp/SharpWebSocket.cs
index f371cb25a..6eee4cd12 100644
--- a/Jellyfin.Server/SocketSharp/SharpWebSocket.cs
+++ b/Jellyfin.Server/SocketSharp/SharpWebSocket.cs
@@ -24,6 +24,7 @@ namespace Jellyfin.Server.SocketSharp
private TaskCompletionSource<bool> _taskCompletionSource = new TaskCompletionSource<bool>();
private readonly CancellationTokenSource _cancellationTokenSource = new CancellationTokenSource();
+ private bool _disposed = false;
public SharpWebSocket(SocketHttpListener.WebSocket socket, ILogger logger)
{
@@ -40,9 +41,9 @@ namespace Jellyfin.Server.SocketSharp
_logger = logger;
WebSocket = socket;
- socket.OnMessage += socket_OnMessage;
- socket.OnClose += socket_OnClose;
- socket.OnError += socket_OnError;
+ socket.OnMessage += OnSocketMessage;
+ socket.OnClose += OnSocketClose;
+ socket.OnError += OnSocketError;
WebSocket.ConnectAsServer();
}
@@ -52,29 +53,22 @@ namespace Jellyfin.Server.SocketSharp
return _taskCompletionSource.Task;
}
- void socket_OnError(object sender, SocketHttpListener.ErrorEventArgs e)
+ private void OnSocketError(object sender, SocketHttpListener.ErrorEventArgs e)
{
_logger.LogError("Error in SharpWebSocket: {Message}", e.Message ?? string.Empty);
- //Closed?.Invoke(this, EventArgs.Empty);
+
+ // Closed?.Invoke(this, EventArgs.Empty);
}
- void socket_OnClose(object sender, SocketHttpListener.CloseEventArgs e)
+ private void OnSocketClose(object sender, SocketHttpListener.CloseEventArgs e)
{
_taskCompletionSource.TrySetResult(true);
Closed?.Invoke(this, EventArgs.Empty);
}
- void socket_OnMessage(object sender, SocketHttpListener.MessageEventArgs e)
+ private void OnSocketMessage(object sender, SocketHttpListener.MessageEventArgs e)
{
- //if (!string.IsNullOrEmpty(e.Data))
- //{
- // if (OnReceive != null)
- // {
- // OnReceive(e.Data);
- // }
- // return;
- //}
if (OnReceiveBytes != null)
{
OnReceiveBytes(e.RawData);
@@ -117,6 +111,7 @@ namespace Jellyfin.Server.SocketSharp
public void Dispose()
{
Dispose(true);
+ GC.SuppressFinalize(this);
}
/// <summary>
@@ -125,16 +120,23 @@ namespace Jellyfin.Server.SocketSharp
/// <param name="dispose"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
protected virtual void Dispose(bool dispose)
{
+ if (_disposed)
+ {
+ return;
+ }
+
if (dispose)
{
- WebSocket.OnMessage -= socket_OnMessage;
- WebSocket.OnClose -= socket_OnClose;
- WebSocket.OnError -= socket_OnError;
+ WebSocket.OnMessage -= OnSocketMessage;
+ WebSocket.OnClose -= OnSocketClose;
+ WebSocket.OnError -= OnSocketError;
_cancellationTokenSource.Cancel();
WebSocket.Close();
}
+
+ _disposed = true;
}
/// <summary>
@@ -142,11 +144,5 @@ namespace Jellyfin.Server.SocketSharp
/// </summary>
/// <value>The receive action.</value>
public Action<byte[]> OnReceiveBytes { get; set; }
-
- /// <summary>
- /// Gets or sets the on receive.
- /// </summary>
- /// <value>The on receive.</value>
- public Action<string> OnReceive { get; set; }
}
}
diff --git a/Jellyfin.Server/SocketSharp/WebSocketSharpListener.cs b/Jellyfin.Server/SocketSharp/WebSocketSharpListener.cs
index a44343ab2..58c4d38a2 100644
--- a/Jellyfin.Server/SocketSharp/WebSocketSharpListener.cs
+++ b/Jellyfin.Server/SocketSharp/WebSocketSharpListener.cs
@@ -34,9 +34,16 @@ namespace Jellyfin.Server.SocketSharp
private CancellationTokenSource _disposeCancellationTokenSource = new CancellationTokenSource();
private CancellationToken _disposeCancellationToken;
- public WebSocketSharpListener(ILogger logger, X509Certificate certificate, IStreamHelper streamHelper,
- INetworkManager networkManager, ISocketFactory socketFactory, ICryptoProvider cryptoProvider,
- bool enableDualMode, IFileSystem fileSystem, IEnvironmentInfo environment)
+ public WebSocketSharpListener(
+ ILogger logger,
+ X509Certificate certificate,
+ IStreamHelper streamHelper,
+ INetworkManager networkManager,
+ ISocketFactory socketFactory,
+ ICryptoProvider cryptoProvider,
+ bool enableDualMode,
+ IFileSystem fileSystem,
+ IEnvironmentInfo environment)
{
_logger = logger;
_certificate = certificate;
@@ -61,7 +68,9 @@ namespace Jellyfin.Server.SocketSharp
public void Start(IEnumerable<string> urlPrefixes)
{
if (_listener == null)
+ {
_listener = new HttpListener(_logger, _cryptoProvider, _socketFactory, _networkManager, _streamHelper, _fileSystem, _environment);
+ }
_listener.EnableDualMode = _enableDualMode;
@@ -83,15 +92,18 @@ namespace Jellyfin.Server.SocketSharp
private void ProcessContext(HttpListenerContext context)
{
- var _ = Task.Run(async () => await InitTask(context, _disposeCancellationToken));
+ _ = Task.Run(async () => await InitTask(context, _disposeCancellationToken).ConfigureAwait(false));
}
private static void LogRequest(ILogger logger, HttpListenerRequest request)
{
var url = request.Url.ToString();
- logger.LogInformation("{0} {1}. UserAgent: {2}",
- request.IsWebSocketRequest ? "WS" : "HTTP " + request.HttpMethod, url, request.UserAgent ?? string.Empty);
+ logger.LogInformation(
+ "{0} {1}. UserAgent: {2}",
+ request.IsWebSocketRequest ? "WS" : "HTTP " + request.HttpMethod,
+ url,
+ request.UserAgent ?? string.Empty);
}
private Task InitTask(HttpListenerContext context, CancellationToken cancellationToken)
@@ -201,7 +213,7 @@ namespace Jellyfin.Server.SocketSharp
}
catch (ObjectDisposedException)
{
- //TODO Investigate and properly fix.
+ // TODO: Investigate and properly fix.
}
catch (Exception ex)
{
@@ -223,38 +235,39 @@ namespace Jellyfin.Server.SocketSharp
public Task Stop()
{
_disposeCancellationTokenSource.Cancel();
-
- if (_listener != null)
- {
- _listener.Close();
- }
+ _listener?.Close();
return Task.CompletedTask;
}
+ /// <summary>
+ /// Releases the unmanaged resources and disposes of the managed resources used.
+ /// </summary>
public void Dispose()
{
Dispose(true);
+ GC.SuppressFinalize(this);
}
private bool _disposed;
- private readonly object _disposeLock = new object();
+
+ /// <summary>
+ /// Releases the unmanaged resources and disposes of the managed resources used.
+ /// </summary>
+ /// <param name="disposing">Whether or not the managed resources should be disposed</param>
protected virtual void Dispose(bool disposing)
{
- if (_disposed) return;
-
- lock (_disposeLock)
+ if (_disposed)
{
- if (_disposed) return;
-
- if (disposing)
- {
- Stop();
- }
+ return;
+ }
- //release unmanaged resources here...
- _disposed = true;
+ if (disposing)
+ {
+ Stop().GetAwaiter().GetResult();
}
+
+ _disposed = true;
}
}
}
diff --git a/Jellyfin.Server/SocketSharp/WebSocketSharpRequest.cs b/Jellyfin.Server/SocketSharp/WebSocketSharpRequest.cs
index c2b71e9ef..6458707d9 100644
--- a/Jellyfin.Server/SocketSharp/WebSocketSharpRequest.cs
+++ b/Jellyfin.Server/SocketSharp/WebSocketSharpRequest.cs
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
+using System.Globalization;
using System.IO;
using System.Text;
using Emby.Server.Implementations.HttpServer;
@@ -24,7 +25,7 @@ namespace Jellyfin.Server.SocketSharp
this.request = httpContext.Request;
this.response = new WebSocketSharpResponse(logger, httpContext.Response, this);
- //HandlerFactoryPath = GetHandlerPathIfAny(UrlPrefixes[0]);
+ // HandlerFactoryPath = GetHandlerPathIfAny(UrlPrefixes[0]);
}
public HttpListenerRequest HttpRequest => request;
@@ -45,9 +46,11 @@ namespace Jellyfin.Server.SocketSharp
public string UserHostAddress => request.UserHostAddress;
- public string XForwardedFor => string.IsNullOrEmpty(request.Headers["X-Forwarded-For"]) ? null : request.Headers["X-Forwarded-For"];
+ public string XForwardedFor
+ => string.IsNullOrEmpty(request.Headers["X-Forwarded-For"]) ? null : request.Headers["X-Forwarded-For"];
- public int? XForwardedPort => string.IsNullOrEmpty(request.Headers["X-Forwarded-Port"]) ? (int?)null : int.Parse(request.Headers["X-Forwarded-Port"]);
+ public int? XForwardedPort
+ => string.IsNullOrEmpty(request.Headers["X-Forwarded-Port"]) ? (int?)null : int.Parse(request.Headers["X-Forwarded-Port"], CultureInfo.InvariantCulture);
public string XForwardedProtocol => string.IsNullOrEmpty(request.Headers["X-Forwarded-Proto"]) ? null : request.Headers["X-Forwarded-Proto"];
@@ -83,6 +86,7 @@ namespace Jellyfin.Server.SocketSharp
switch (crlf)
{
case 0:
+ {
if (c == '\r')
{
crlf = 1;
@@ -97,29 +101,39 @@ namespace Jellyfin.Server.SocketSharp
{
throw new ArgumentException("net_WebHeaderInvalidControlChars");
}
+
break;
+ }
case 1:
+ {
if (c == '\n')
{
crlf = 2;
break;
}
+
throw new ArgumentException("net_WebHeaderInvalidCRLFChars");
+ }
case 2:
+ {
if (c == ' ' || c == '\t')
{
crlf = 0;
break;
}
+
throw new ArgumentException("net_WebHeaderInvalidCRLFChars");
+ }
}
}
+
if (crlf != 0)
{
throw new ArgumentException("net_WebHeaderInvalidCRLFChars");
}
+
return name;
}
@@ -132,6 +146,7 @@ namespace Jellyfin.Server.SocketSharp
return true;
}
}
+
return false;
}
@@ -337,6 +352,7 @@ namespace Jellyfin.Server.SocketSharp
this.pathInfo = System.Net.WebUtility.UrlDecode(pathInfo);
this.pathInfo = NormalizePathInfo(pathInfo, mode);
}
+
return this.pathInfo;
}
}
@@ -438,7 +454,7 @@ namespace Jellyfin.Server.SocketSharp
public string ContentType => request.ContentType;
- public Encoding contentEncoding;
+ private Encoding contentEncoding;
public Encoding ContentEncoding
{
get => contentEncoding ?? request.ContentEncoding;
@@ -496,6 +512,7 @@ namespace Jellyfin.Server.SocketSharp
i++;
}
}
+
return httpFiles;
}
}
diff --git a/Jellyfin.Server/SocketSharp/WebSocketSharpResponse.cs b/Jellyfin.Server/SocketSharp/WebSocketSharpResponse.cs
index cabc96b23..56e5c73d6 100644
--- a/Jellyfin.Server/SocketSharp/WebSocketSharpResponse.cs
+++ b/Jellyfin.Server/SocketSharp/WebSocketSharpResponse.cs
@@ -13,12 +13,12 @@ using HttpListenerResponse = SocketHttpListener.Net.HttpListenerResponse;
using IHttpResponse = MediaBrowser.Model.Services.IHttpResponse;
using IRequest = MediaBrowser.Model.Services.IRequest;
-
namespace Jellyfin.Server.SocketSharp
{
public class WebSocketSharpResponse : IHttpResponse
{
private readonly ILogger _logger;
+
private readonly HttpListenerResponse _response;
public WebSocketSharpResponse(ILogger logger, HttpListenerResponse response, IRequest request)
@@ -30,7 +30,9 @@ namespace Jellyfin.Server.SocketSharp
}
public IRequest Request { get; private set; }
+
public Dictionary<string, object> Items { get; private set; }
+
public object OriginalResponse => _response;
public int StatusCode
@@ -51,7 +53,7 @@ namespace Jellyfin.Server.SocketSharp
set => _response.ContentType = value;
}
- //public ICookies Cookies { get; set; }
+ public QueryParamCollection Headers => _response.Headers;
public void AddHeader(string name, string value)
{
@@ -64,8 +66,6 @@ namespace Jellyfin.Server.SocketSharp
_response.AddHeader(name, value);
}
- public QueryParamCollection Headers => _response.Headers;
-
public string GetHeader(string name)
{
return _response.Headers[name];
@@ -114,9 +114,9 @@ namespace Jellyfin.Server.SocketSharp
public void SetContentLength(long contentLength)
{
- //you can happily set the Content-Length header in Asp.Net
- //but HttpListener will complain if you do - you have to set ContentLength64 on the response.
- //workaround: HttpListener throws "The parameter is incorrect" exceptions when we try to set the Content-Length header
+ // you can happily set the Content-Length header in Asp.Net
+ // but HttpListener will complain if you do - you have to set ContentLength64 on the response.
+ // workaround: HttpListener throws "The parameter is incorrect" exceptions when we try to set the Content-Length header
_response.ContentLength64 = contentLength;
}
@@ -147,15 +147,12 @@ namespace Jellyfin.Server.SocketSharp
{
sb.Append($";domain={cookie.Domain}");
}
- //else if (restrictAllCookiesToDomain != null)
- //{
- // sb.Append($";domain={restrictAllCookiesToDomain}");
- //}
if (cookie.Secure)
{
sb.Append(";Secure");
}
+
if (cookie.HttpOnly)
{
sb.Append(";HttpOnly");
@@ -164,7 +161,6 @@ namespace Jellyfin.Server.SocketSharp
return sb.ToString();
}
-
public bool SendChunked
{
get => _response.SendChunked;
diff --git a/MediaBrowser.Api/Session/SessionInfoWebSocketListener.cs b/MediaBrowser.Api/Session/SessionInfoWebSocketListener.cs
index 387ccad25..beb2fb11d 100644
--- a/MediaBrowser.Api/Session/SessionInfoWebSocketListener.cs
+++ b/MediaBrowser.Api/Session/SessionInfoWebSocketListener.cs
@@ -11,7 +11,7 @@ namespace MediaBrowser.Api.Session
/// <summary>
/// Class SessionInfoWebSocketListener
/// </summary>
- class SessionInfoWebSocketListener : BasePeriodicWebSocketListener<IEnumerable<SessionInfo>, WebSocketListenerState>
+ public class SessionInfoWebSocketListener : BasePeriodicWebSocketListener<IEnumerable<SessionInfo>, WebSocketListenerState>
{
/// <summary>
/// Gets the name.
diff --git a/MediaBrowser.Api/System/ActivityLogWebSocketListener.cs b/MediaBrowser.Api/System/ActivityLogWebSocketListener.cs
index 0df46c399..43f3c5a22 100644
--- a/MediaBrowser.Api/System/ActivityLogWebSocketListener.cs
+++ b/MediaBrowser.Api/System/ActivityLogWebSocketListener.cs
@@ -11,7 +11,7 @@ namespace MediaBrowser.Api.System
/// <summary>
/// Class SessionInfoWebSocketListener
/// </summary>
- class ActivityLogWebSocketListener : BasePeriodicWebSocketListener<List<ActivityLogEntry>, WebSocketListenerState>
+ public class ActivityLogWebSocketListener : BasePeriodicWebSocketListener<List<ActivityLogEntry>, WebSocketListenerState>
{
/// <summary>
/// Gets the name.
diff --git a/MediaBrowser.Common/IApplicationHost.cs b/MediaBrowser.Common/IApplicationHost.cs
index 59e3c1767..6891152ee 100644
--- a/MediaBrowser.Common/IApplicationHost.cs
+++ b/MediaBrowser.Common/IApplicationHost.cs
@@ -5,6 +5,7 @@ using System.Threading.Tasks;
using MediaBrowser.Common.Plugins;
using MediaBrowser.Model.Events;
using MediaBrowser.Model.Updates;
+using Microsoft.Extensions.DependencyInjection;
namespace MediaBrowser.Common
{
@@ -14,12 +15,6 @@ namespace MediaBrowser.Common
public interface IApplicationHost
{
/// <summary>
- /// Gets the display name of the operating system.
- /// </summary>
- /// <value>The display name of the operating system.</value>
- string OperatingSystemDisplayName { get; }
-
- /// <summary>
/// Gets the name.
/// </summary>
/// <value>The name.</value>
@@ -105,13 +100,6 @@ namespace MediaBrowser.Common
T Resolve<T>();
/// <summary>
- /// Resolves this instance.
- /// </summary>
- /// <typeparam name="T"></typeparam>
- /// <returns>``0.</returns>
- T TryResolve<T>();
-
- /// <summary>
/// Shuts down.
/// </summary>
Task Shutdown();
@@ -131,7 +119,7 @@ namespace MediaBrowser.Common
/// <summary>
/// Inits this instance.
/// </summary>
- Task Init();
+ Task Init(IServiceCollection serviceCollection);
/// <summary>
/// Creates the instance.
diff --git a/MediaBrowser.Common/MediaBrowser.Common.csproj b/MediaBrowser.Common/MediaBrowser.Common.csproj
index 2220d4661..715f4fccd 100644
--- a/MediaBrowser.Common/MediaBrowser.Common.csproj
+++ b/MediaBrowser.Common/MediaBrowser.Common.csproj
@@ -12,6 +12,10 @@
</ItemGroup>
<ItemGroup>
+ <PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="2.2.0" />
+ </ItemGroup>
+
+ <ItemGroup>
<Compile Include="..\SharedVersion.cs" />
</ItemGroup>
diff --git a/MediaBrowser.LocalMetadata/Providers/PlaylistXmlProvider.cs b/MediaBrowser.LocalMetadata/Providers/PlaylistXmlProvider.cs
index 4180a4f15..442a18cb9 100644
--- a/MediaBrowser.LocalMetadata/Providers/PlaylistXmlProvider.cs
+++ b/MediaBrowser.LocalMetadata/Providers/PlaylistXmlProvider.cs
@@ -9,7 +9,7 @@ using Microsoft.Extensions.Logging;
namespace MediaBrowser.LocalMetadata.Providers
{
- class PlaylistXmlProvider : BaseXmlProvider<Playlist>
+ public class PlaylistXmlProvider : BaseXmlProvider<Playlist>
{
private readonly ILogger _logger;
private readonly IProviderManager _providerManager;
diff --git a/MediaBrowser.Model/System/PublicSystemInfo.cs b/MediaBrowser.Model/System/PublicSystemInfo.cs
index accdc9e60..d97eda352 100644
--- a/MediaBrowser.Model/System/PublicSystemInfo.cs
+++ b/MediaBrowser.Model/System/PublicSystemInfo.cs
@@ -24,12 +24,12 @@ namespace MediaBrowser.Model.System
/// Gets or sets the server version.
/// </summary>
/// <value>The version.</value>
- public string Version { get; set; }
+ public string Version { get; set; }
/// <summary>
- /// Gets or sets the operating sytem.
+ /// Gets or sets the operating system.
/// </summary>
- /// <value>The operating sytem.</value>
+ /// <value>The operating system.</value>
public string OperatingSystem { get; set; }
/// <summary>
diff --git a/MediaBrowser.Model/System/SystemInfo.cs b/MediaBrowser.Model/System/SystemInfo.cs
index 26f735330..d9ed68b27 100644
--- a/MediaBrowser.Model/System/SystemInfo.cs
+++ b/MediaBrowser.Model/System/SystemInfo.cs
@@ -1,3 +1,4 @@
+using System;
using System.Runtime.InteropServices;
using MediaBrowser.Model.Updates;
@@ -136,7 +137,7 @@ namespace MediaBrowser.Model.System
/// </summary>
public SystemInfo()
{
- CompletedInstallations = new InstallationInfo[] { };
+ CompletedInstallations = Array.Empty<InstallationInfo>();
}
}
}
diff --git a/MediaBrowser.Providers/BoxSets/MovieDbBoxSetImageProvider.cs b/MediaBrowser.Providers/BoxSets/MovieDbBoxSetImageProvider.cs
index c6c1a2a94..4d12b2f4a 100644
--- a/MediaBrowser.Providers/BoxSets/MovieDbBoxSetImageProvider.cs
+++ b/MediaBrowser.Providers/BoxSets/MovieDbBoxSetImageProvider.cs
@@ -14,7 +14,7 @@ using MediaBrowser.Providers.Movies;
namespace MediaBrowser.Providers.BoxSets
{
- class MovieDbBoxSetImageProvider : IRemoteImageProvider, IHasOrder
+ public class MovieDbBoxSetImageProvider : IRemoteImageProvider, IHasOrder
{
private readonly IHttpClient _httpClient;
diff --git a/MediaBrowser.Providers/Movies/MovieDbImageProvider.cs b/MediaBrowser.Providers/Movies/MovieDbImageProvider.cs
index b9c5d7ce5..20b53d58a 100644
--- a/MediaBrowser.Providers/Movies/MovieDbImageProvider.cs
+++ b/MediaBrowser.Providers/Movies/MovieDbImageProvider.cs
@@ -16,7 +16,7 @@ using MediaBrowser.Model.Serialization;
namespace MediaBrowser.Providers.Movies
{
- class MovieDbImageProvider : IRemoteImageProvider, IHasOrder
+ public class MovieDbImageProvider : IRemoteImageProvider, IHasOrder
{
private readonly IJsonSerializer _jsonSerializer;
private readonly IHttpClient _httpClient;
diff --git a/MediaBrowser.Providers/Music/MusicVideoMetadataService.cs b/MediaBrowser.Providers/Music/MusicVideoMetadataService.cs
index 8b01ff342..93412306f 100644
--- a/MediaBrowser.Providers/Music/MusicVideoMetadataService.cs
+++ b/MediaBrowser.Providers/Music/MusicVideoMetadataService.cs
@@ -9,7 +9,7 @@ using Microsoft.Extensions.Logging;
namespace MediaBrowser.Providers.Music
{
- class MusicVideoMetadataService : MetadataService<MusicVideo, MusicVideoInfo>
+ public class MusicVideoMetadataService : MetadataService<MusicVideo, MusicVideoInfo>
{
protected override void MergeData(MetadataResult<MusicVideo> source, MetadataResult<MusicVideo> target, MetadataFields[] lockedFields, bool replaceData, bool mergeMetadataSettings)
{
diff --git a/MediaBrowser.Providers/Photos/PhotoAlbumMetadataService.cs b/MediaBrowser.Providers/Photos/PhotoAlbumMetadataService.cs
index fd969c7c2..993581cca 100644
--- a/MediaBrowser.Providers/Photos/PhotoAlbumMetadataService.cs
+++ b/MediaBrowser.Providers/Photos/PhotoAlbumMetadataService.cs
@@ -9,7 +9,7 @@ using Microsoft.Extensions.Logging;
namespace MediaBrowser.Providers.Photos
{
- class PhotoAlbumMetadataService : MetadataService<PhotoAlbum, ItemLookupInfo>
+ public class PhotoAlbumMetadataService : MetadataService<PhotoAlbum, ItemLookupInfo>
{
protected override void MergeData(MetadataResult<PhotoAlbum> source, MetadataResult<PhotoAlbum> target, MetadataFields[] lockedFields, bool replaceData, bool mergeMetadataSettings)
{
diff --git a/MediaBrowser.Providers/Photos/PhotoMetadataService.cs b/MediaBrowser.Providers/Photos/PhotoMetadataService.cs
index a430e1041..b739c5765 100644
--- a/MediaBrowser.Providers/Photos/PhotoMetadataService.cs
+++ b/MediaBrowser.Providers/Photos/PhotoMetadataService.cs
@@ -9,7 +9,7 @@ using Microsoft.Extensions.Logging;
namespace MediaBrowser.Providers.Photos
{
- class PhotoMetadataService : MetadataService<Photo, ItemLookupInfo>
+ public class PhotoMetadataService : MetadataService<Photo, ItemLookupInfo>
{
protected override void MergeData(MetadataResult<Photo> source, MetadataResult<Photo> target, MetadataFields[] lockedFields, bool replaceData, bool mergeMetadataSettings)
{
diff --git a/MediaBrowser.Providers/Playlists/PlaylistMetadataService.cs b/MediaBrowser.Providers/Playlists/PlaylistMetadataService.cs
index b28d2a548..30ce5c64c 100644
--- a/MediaBrowser.Providers/Playlists/PlaylistMetadataService.cs
+++ b/MediaBrowser.Providers/Playlists/PlaylistMetadataService.cs
@@ -11,7 +11,7 @@ using Microsoft.Extensions.Logging;
namespace MediaBrowser.Providers.Playlists
{
- class PlaylistMetadataService : MetadataService<Playlist, ItemLookupInfo>
+ public class PlaylistMetadataService : MetadataService<Playlist, ItemLookupInfo>
{
protected override IList<BaseItem> GetChildrenForMetadataUpdates(Playlist item)
{
diff --git a/MediaBrowser.Providers/TV/Omdb/OmdbEpisodeProvider.cs b/MediaBrowser.Providers/TV/Omdb/OmdbEpisodeProvider.cs
index d0749405b..dee3030af 100644
--- a/MediaBrowser.Providers/TV/Omdb/OmdbEpisodeProvider.cs
+++ b/MediaBrowser.Providers/TV/Omdb/OmdbEpisodeProvider.cs
@@ -16,7 +16,7 @@ using Microsoft.Extensions.Logging;
namespace MediaBrowser.Providers.TV.Omdb
{
- class OmdbEpisodeProvider :
+ public class OmdbEpisodeProvider :
IRemoteMetadataProvider<Episode, EpisodeInfo>,
IHasOrder
{
diff --git a/MediaBrowser.Providers/TV/TheMovieDb/MovieDbEpisodeProvider.cs b/MediaBrowser.Providers/TV/TheMovieDb/MovieDbEpisodeProvider.cs
index 44590515e..3d7745085 100644
--- a/MediaBrowser.Providers/TV/TheMovieDb/MovieDbEpisodeProvider.cs
+++ b/MediaBrowser.Providers/TV/TheMovieDb/MovieDbEpisodeProvider.cs
@@ -20,7 +20,7 @@ using Microsoft.Extensions.Logging;
namespace MediaBrowser.Providers.TV.TheMovieDb
{
- class MovieDbEpisodeProvider :
+ public class MovieDbEpisodeProvider :
MovieDbProviderBase,
IRemoteMetadataProvider<Episode, EpisodeInfo>,
IHasOrder
diff --git a/MediaBrowser.WebDashboard/jellyfin-web b/MediaBrowser.WebDashboard/jellyfin-web
-Subproject 094c1deae91c51b8bbf8ebb16a55758af110f04
+Subproject c7ce1ac8eccd50f1bd759b30fbe60ea797ffe86
diff --git a/MediaBrowser.XbmcMetadata/Parsers/MovieNfoParser.cs b/MediaBrowser.XbmcMetadata/Parsers/MovieNfoParser.cs
index 21ee8f92f..a3e48d30d 100644
--- a/MediaBrowser.XbmcMetadata/Parsers/MovieNfoParser.cs
+++ b/MediaBrowser.XbmcMetadata/Parsers/MovieNfoParser.cs
@@ -13,7 +13,7 @@ using Microsoft.Extensions.Logging;
namespace MediaBrowser.XbmcMetadata.Parsers
{
- class MovieNfoParser : BaseNfoParser<Video>
+ public class MovieNfoParser : BaseNfoParser<Video>
{
protected override bool SupportsUrlAfterClosingXmlTag => true;
diff --git a/jellyfin.ruleset b/jellyfin.ruleset
index 0f8c9aa02..4381349ca 100644
--- a/jellyfin.ruleset
+++ b/jellyfin.ruleset
@@ -3,11 +3,19 @@
<Rules AnalyzerId="StyleCop.Analyzers" RuleNamespace="StyleCop.Analyzers">
<!-- disable warning SA1101: Prefix local calls with 'this.' -->
<Rule Id="SA1101" Action="None" />
+ <!-- disable warning SA1130: Use lambda syntax -->
+ <Rule Id="SA1130" Action="None" />
<!-- disable warning SA1200: 'using' directive must appear within a namespace declaration -->
<Rule Id="SA1200" Action="None" />
<!-- disable warning SA1309: Fields must not begin with an underscore -->
<Rule Id="SA1309" Action="None" />
+ <!-- disable warning SA1512: Single-line comments must not be followed by blank line -->
+ <Rule Id="SA1512" Action="None" />
<!-- disable warning SA1633: The file header is missing or not located at the top of the file -->
<Rule Id="SA1633" Action="None" />
</Rules>
+ <Rules AnalyzerId="Microsoft.CodeAnalysis.FxCopAnalyzers" RuleNamespace="Microsoft.Design">
+ <!-- disable warning CA1054: Change the type of parameter url from string to System.Uri -->
+ <Rule Id="CA1054" Action="None" />
+ </Rules>
</RuleSet>