aboutsummaryrefslogtreecommitdiff
path: root/Emby.Server.Implementations
diff options
context:
space:
mode:
Diffstat (limited to 'Emby.Server.Implementations')
-rw-r--r--Emby.Server.Implementations/AppBase/BaseApplicationPaths.cs6
-rw-r--r--Emby.Server.Implementations/AppBase/BaseConfigurationManager.cs2
-rw-r--r--Emby.Server.Implementations/AppBase/ConfigurationHelper.cs4
-rw-r--r--Emby.Server.Implementations/ApplicationHost.cs609
-rw-r--r--Emby.Server.Implementations/Archiving/ZipClient.cs6
-rw-r--r--Emby.Server.Implementations/Channels/RefreshChannelsScheduledTask.cs2
-rw-r--r--Emby.Server.Implementations/Collections/CollectionImageProvider.cs8
-rw-r--r--Emby.Server.Implementations/Collections/CollectionManager.cs4
-rw-r--r--Emby.Server.Implementations/ConfigurationOptions.cs12
-rw-r--r--Emby.Server.Implementations/Data/BaseSqliteRepository.cs27
-rw-r--r--Emby.Server.Implementations/Data/CleanDatabaseScheduledTask.cs11
-rw-r--r--Emby.Server.Implementations/Data/SqliteItemRepository.cs287
-rw-r--r--Emby.Server.Implementations/Devices/DeviceId.cs12
-rw-r--r--Emby.Server.Implementations/Devices/DeviceManager.cs12
-rw-r--r--Emby.Server.Implementations/Dto/DtoService.cs12
-rw-r--r--Emby.Server.Implementations/Emby.Server.Implementations.csproj4
-rw-r--r--Emby.Server.Implementations/EntryPoints/UserDataChangeNotifier.cs2
-rw-r--r--Emby.Server.Implementations/FFMpeg/FFMpegLoader.cs13
-rw-r--r--Emby.Server.Implementations/HttpClientManager/HttpClientManager.cs55
-rw-r--r--Emby.Server.Implementations/HttpServer/HttpListenerHost.cs94
-rw-r--r--Emby.Server.Implementations/HttpServer/HttpResultFactory.cs134
-rw-r--r--Emby.Server.Implementations/HttpServer/LoggerUtils.cs55
-rw-r--r--Emby.Server.Implementations/HttpServer/StreamWriter.cs8
-rw-r--r--Emby.Server.Implementations/IO/FileRefresher.cs10
-rw-r--r--Emby.Server.Implementations/IO/LibraryMonitor.cs21
-rw-r--r--Emby.Server.Implementations/IO/ManagedFileSystem.cs59
-rw-r--r--Emby.Server.Implementations/Library/CoreResolutionIgnoreRule.cs14
-rw-r--r--Emby.Server.Implementations/Library/LibraryManager.cs48
-rw-r--r--Emby.Server.Implementations/Library/Resolvers/BaseVideoResolver.cs5
-rw-r--r--Emby.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs3
-rw-r--r--Emby.Server.Implementations/Library/Resolvers/PhotoResolver.cs8
-rw-r--r--Emby.Server.Implementations/Library/Resolvers/SpecialFolderResolver.cs2
-rw-r--r--Emby.Server.Implementations/Library/Resolvers/TV/EpisodeResolver.cs4
-rw-r--r--Emby.Server.Implementations/Library/Resolvers/VideoResolver.cs4
-rw-r--r--Emby.Server.Implementations/Library/UserManager.cs9
-rw-r--r--Emby.Server.Implementations/Library/Validators/PeopleValidator.cs4
-rw-r--r--Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs6
-rw-r--r--Emby.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs15
-rw-r--r--Emby.Server.Implementations/LiveTv/EmbyTV/ItemDataProvider.cs13
-rw-r--r--Emby.Server.Implementations/LiveTv/EmbyTV/SeriesTimerManager.cs5
-rw-r--r--Emby.Server.Implementations/LiveTv/EmbyTV/TimerManager.cs5
-rw-r--r--Emby.Server.Implementations/LiveTv/LiveTvManager.cs2
-rw-r--r--Emby.Server.Implementations/LiveTv/TunerHosts/BaseTunerHost.cs5
-rw-r--r--Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs13
-rw-r--r--Emby.Server.Implementations/LiveTv/TunerHosts/M3UTunerHost.cs9
-rw-r--r--Emby.Server.Implementations/LiveTv/TunerHosts/M3uParser.cs78
-rw-r--r--Emby.Server.Implementations/LiveTv/TunerHosts/SharedHttpStream.cs2
-rw-r--r--Emby.Server.Implementations/Localization/Core/da.json50
-rw-r--r--Emby.Server.Implementations/Localization/Core/de.json54
-rw-r--r--Emby.Server.Implementations/Localization/Core/en-GB.json2
-rw-r--r--Emby.Server.Implementations/Localization/Core/en-US.json2
-rw-r--r--Emby.Server.Implementations/Localization/Core/es.json46
-rw-r--r--Emby.Server.Implementations/Localization/Core/fr.json2
-rw-r--r--Emby.Server.Implementations/Localization/Core/hu.json70
-rw-r--r--Emby.Server.Implementations/Localization/Core/it.json8
-rw-r--r--Emby.Server.Implementations/Localization/Core/kk.json188
-rw-r--r--Emby.Server.Implementations/Localization/Core/ms.json6
-rw-r--r--Emby.Server.Implementations/Localization/Core/nl.json36
-rw-r--r--Emby.Server.Implementations/Localization/Core/ru.json10
-rw-r--r--Emby.Server.Implementations/Localization/Ratings/kz.csv13
-rw-r--r--Emby.Server.Implementations/ScheduledTasks/Tasks/ChapterImagesTask.cs2
-rw-r--r--Emby.Server.Implementations/ScheduledTasks/Triggers/DailyTrigger.cs2
-rw-r--r--Emby.Server.Implementations/Serialization/JsonSerializer.cs21
-rw-r--r--Emby.Server.Implementations/Sorting/AiredEpisodeOrderComparer.cs2
-rw-r--r--Emby.Server.Implementations/Sorting/SeriesSortNameComparer.cs2
-rw-r--r--Emby.Server.Implementations/Updates/InstallationManager.cs40
66 files changed, 841 insertions, 1438 deletions
diff --git a/Emby.Server.Implementations/AppBase/BaseApplicationPaths.cs b/Emby.Server.Implementations/AppBase/BaseApplicationPaths.cs
index f26cc4f62..65cdccfa5 100644
--- a/Emby.Server.Implementations/AppBase/BaseApplicationPaths.cs
+++ b/Emby.Server.Implementations/AppBase/BaseApplicationPaths.cs
@@ -73,12 +73,6 @@ namespace Emby.Server.Implementations.AppBase
public string PluginConfigurationsPath => Path.Combine(PluginsPath, "configurations");
/// <summary>
- /// Gets the path to where temporary update files will be stored
- /// </summary>
- /// <value>The plugin configurations path.</value>
- public string TempUpdatePath => Path.Combine(ProgramDataPath, "updates");
-
- /// <summary>
/// Gets the path to the log directory
/// </summary>
/// <value>The log directory path.</value>
diff --git a/Emby.Server.Implementations/AppBase/BaseConfigurationManager.cs b/Emby.Server.Implementations/AppBase/BaseConfigurationManager.cs
index 5feac1adf..af60a8dce 100644
--- a/Emby.Server.Implementations/AppBase/BaseConfigurationManager.cs
+++ b/Emby.Server.Implementations/AppBase/BaseConfigurationManager.cs
@@ -79,7 +79,7 @@ namespace Emby.Server.Implementations.AppBase
get
{
// Lazy load
- LazyInitializer.EnsureInitialized(ref _configuration, ref _configurationLoaded, ref _configurationSyncLock, () => (BaseApplicationConfiguration)ConfigurationHelper.GetXmlConfiguration(ConfigurationType, CommonApplicationPaths.SystemConfigurationFilePath, XmlSerializer, FileSystem));
+ LazyInitializer.EnsureInitialized(ref _configuration, ref _configurationLoaded, ref _configurationSyncLock, () => (BaseApplicationConfiguration)ConfigurationHelper.GetXmlConfiguration(ConfigurationType, CommonApplicationPaths.SystemConfigurationFilePath, XmlSerializer));
return _configuration;
}
protected set
diff --git a/Emby.Server.Implementations/AppBase/ConfigurationHelper.cs b/Emby.Server.Implementations/AppBase/ConfigurationHelper.cs
index 3faad76e7..90b97061f 100644
--- a/Emby.Server.Implementations/AppBase/ConfigurationHelper.cs
+++ b/Emby.Server.Implementations/AppBase/ConfigurationHelper.cs
@@ -1,7 +1,6 @@
using System;
using System.IO;
using System.Linq;
-using MediaBrowser.Model.IO;
using MediaBrowser.Model.Serialization;
namespace Emby.Server.Implementations.AppBase
@@ -18,9 +17,8 @@ namespace Emby.Server.Implementations.AppBase
/// <param name="type">The type.</param>
/// <param name="path">The path.</param>
/// <param name="xmlSerializer">The XML serializer.</param>
- /// <param name="fileSystem">The file system</param>
/// <returns>System.Object.</returns>
- public static object GetXmlConfiguration(Type type, string path, IXmlSerializer xmlSerializer, IFileSystem fileSystem)
+ public static object GetXmlConfiguration(Type type, string path, IXmlSerializer xmlSerializer)
{
object configuration;
diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs
index bb475eb2c..45a819f26 100644
--- a/Emby.Server.Implementations/ApplicationHost.cs
+++ b/Emby.Server.Implementations/ApplicationHost.cs
@@ -102,11 +102,13 @@ using MediaBrowser.Model.Xml;
using MediaBrowser.Providers.Chapters;
using MediaBrowser.Providers.Manager;
using MediaBrowser.Providers.Subtitles;
+using MediaBrowser.Providers.TV.TheTVDB;
using MediaBrowser.WebDashboard.Api;
using MediaBrowser.XbmcMetadata.Providers;
+using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
+using Microsoft.Extensions.DependencyInjection;
using ServiceStack;
-using ServiceStack.Text.Jsv;
using X509Certificate = System.Security.Cryptography.X509Certificates.X509Certificate;
namespace Emby.Server.Implementations
@@ -122,12 +124,6 @@ namespace Emby.Server.Implementations
/// <value><c>true</c> if this instance can self restart; otherwise, <c>false</c>.</value>
public abstract bool CanSelfRestart { get; }
- /// <summary>
- /// Gets or sets a value indicating whether this instance can self update.
- /// </summary>
- /// <value><c>true</c> if this instance can self update; otherwise, <c>false</c>.</value>
- public virtual bool CanSelfUpdate => false;
-
public virtual bool CanLaunchWebBrowser
{
get
@@ -202,7 +198,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 +215,6 @@ namespace Emby.Server.Implementations
protected IEnvironmentInfo EnvironmentInfo { get; set; }
- private IBlurayExaminer BlurayExaminer { get; set; }
-
public PackageVersionClass SystemUpdateLevel
{
get
@@ -232,12 +226,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 +298,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; }
@@ -325,6 +313,8 @@ namespace Emby.Server.Implementations
private IMediaSourceManager MediaSourceManager { get; set; }
private IPlaylistManager PlaylistManager { get; set; }
+ private readonly IConfiguration _configuration;
+
/// <summary>
/// Gets or sets the installation manager.
/// </summary>
@@ -363,8 +353,10 @@ namespace Emby.Server.Implementations
IFileSystem fileSystem,
IEnvironmentInfo environmentInfo,
IImageEncoder imageEncoder,
- INetworkManager networkManager)
+ INetworkManager networkManager,
+ IConfiguration configuration)
{
+ _configuration = configuration;
// hack alert, until common can target .net core
BaseExtensions.CryptographyProvider = CryptographyProvider;
@@ -440,7 +432,7 @@ namespace Emby.Server.Implementations
{
if (_deviceId == null)
{
- _deviceId = new DeviceId(ApplicationPaths, LoggerFactory, FileSystemManager);
+ _deviceId = new DeviceId(ApplicationPaths, LoggerFactory);
}
return _deviceId.Value;
@@ -453,138 +445,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.LogDebug("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 +508,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 +524,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>
@@ -664,16 +550,18 @@ namespace Emby.Server.Implementations
var entryPoints = GetExports<IServerEntryPoint>();
- var now = DateTime.UtcNow;
+ var stopWatch = new Stopwatch();
+ stopWatch.Start();
await Task.WhenAll(StartEntryPoints(entryPoints, true));
- Logger.LogInformation("Executed all pre-startup entry points in {Elapsed:fff} ms", DateTime.Now - now);
+ Logger.LogInformation("Executed all pre-startup entry points in {Elapsed:g}", stopWatch.Elapsed);
Logger.LogInformation("Core startup complete");
HttpServer.GlobalResponse = null;
- now = DateTime.UtcNow;
+ stopWatch.Restart();
await Task.WhenAll(StartEntryPoints(entryPoints, false));
- Logger.LogInformation("Executed all post-startup entry points in {Elapsed:fff} ms", DateTime.Now - now);
+ Logger.LogInformation("Executed all post-startup entry points in {Elapsed:g}", stopWatch.Elapsed);
+ stopWatch.Stop();
}
private IEnumerable<Task> StartEntryPoints(IEnumerable<IServerEntryPoint> entryPoints, bool isBeforeStartup)
@@ -691,7 +579,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 +609,7 @@ namespace Emby.Server.Implementations
SetHttpLimit();
- await RegisterResources();
+ await RegisterResources(serviceCollection);
FindParts();
}
@@ -736,104 +624,105 @@ 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.AddMemoryCache();
+
+ serviceCollection.AddSingleton(ConfigurationManager);
+ serviceCollection.AddSingleton<IApplicationHost>(this);
- RegisterSingleInstance<IApplicationPaths>(ApplicationPaths);
+ serviceCollection.AddSingleton<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);
+ serviceCollection.AddSingleton<TvDbClientManager>();
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);
+ InstallationManager = new InstallationManager(LoggerFactory, this, ApplicationPaths, HttpClient, JsonSerializer, ServerConfigurationManager, FileSystemManager, CryptographyProvider, ZipClient, PackageRuntime);
+ serviceCollection.AddSingleton(InstallationManager);
- ZipClient = new ZipClient(FileSystemManager);
- RegisterSingleInstance(ZipClient);
+ ZipClient = new 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);
+ UserManager = new UserManager(LoggerFactory, ServerConfigurationManager, UserRepository, XmlSerializer, NetworkManager, () => ImageProcessor, () => DtoService, this, JsonSerializer, FileSystemManager);
+ 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);
+ LibraryMonitor = new LibraryMonitor(LoggerFactory, LibraryManager, ServerConfigurationManager, FileSystemManager, EnvironmentInfo);
+ serviceCollection.AddSingleton(LibraryMonitor);
- RegisterSingleInstance<ISearchEngine>(() => new SearchEngine(LoggerFactory, LibraryManager, UserManager));
+ serviceCollection.AddSingleton<ISearchEngine>(new SearchEngine(LoggerFactory, LibraryManager, UserManager));
CertificateInfo = GetCertificateInfo(true);
Certificate = GetCertificate(CertificateInfo);
@@ -841,88 +730,88 @@ namespace Emby.Server.Implementations
HttpServer = new HttpListenerHost(this,
LoggerFactory,
ServerConfigurationManager,
- "web/index.html",
+ _configuration,
NetworkManager,
JsonSerializer,
- XmlSerializer,
- GetParseFn);
+ XmlSerializer);
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);
+ DeviceManager = new DeviceManager(AuthenticationRepository, JsonSerializer, LibraryManager, LocalizationManager, UserManager, FileSystemManager, LibraryMonitor, ServerConfigurationManager);
+ 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);
+ DtoService = new DtoService(LoggerFactory, LibraryManager, UserDataManager, ItemRepository, ImageProcessor, ProviderManager, this, () => MediaSourceManager, () => LiveTvManager);
+ 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 +824,8 @@ namespace Emby.Server.Implementations
((UserDataManager)UserDataManager).Repository = userDataRepo;
ItemRepository.Initialize(userDataRepo, UserManager);
((LibraryManager)LibraryManager).ItemRepository = ItemRepository;
+
+ _serviceProvider = serviceCollection.BuildServiceProvider();
}
protected virtual IBrotliCompressor CreateBrotliCompressor()
@@ -942,11 +833,6 @@ namespace Emby.Server.Implementations
return null;
}
- private static Func<string, object> GetParseFn(Type propertyType)
- {
- return s => JsvReader.GetParseFn(propertyType)(s);
- }
-
public virtual string PackageRuntime => "netcore";
public static void LogEnvironmentInfo(ILogger logger, IApplicationPaths appPaths, EnvironmentInfo.EnvironmentInfo environmentInfo)
@@ -1058,7 +944,7 @@ namespace Emby.Server.Implementations
protected virtual FFMpegInfo GetFFMpegInfo()
{
- return new FFMpegLoader(Logger, ApplicationPaths, HttpClient, ZipClient, FileSystemManager, GetFfmpegInstallInfo())
+ return new FFMpegLoader(ApplicationPaths, FileSystemManager, GetFfmpegInstallInfo())
.GetFFMpegInfo(StartupOptions);
}
@@ -1066,7 +952,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 +984,7 @@ namespace Emby.Server.Implementations
5000);
MediaEncoder = mediaEncoder;
- RegisterSingleInstance(MediaEncoder);
+ serviceCollection.AddSingleton(MediaEncoder);
}
/// <summary>
@@ -1174,7 +1060,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 +1097,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 +1149,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)
+ AllConcreteTypes = GetComposablePartAssemblies()
+ .SelectMany(x => x.ExportedTypes)
+ .Where(type =>
{
- Logger.LogInformation("Loading {assemblyName}", assembly.FullName);
- }
- else
- {
- 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 +1368,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)));
+ yield return typeof(ArtistNfoProvider).Assembly;
- list.AddRange(GetAssembliesWithPartsInternal().Select(i => new Tuple<Assembly, string>(i, null)));
-
- return list.ToList();
- }
-
- protected abstract IEnumerable<Assembly> GetAssembliesWithPartsInternal();
-
- private List<Tuple<Assembly, string>> GetPluginAssemblies(string path)
- {
- try
- {
- return FilterAssembliesToLoad(Directory.EnumerateFiles(path, "*.dll", SearchOption.TopDirectoryOnly))
- .Select(LoadAssembly)
- .Where(a => a != null)
- .ToList();
- }
- catch (DirectoryNotFoundException)
+ foreach (var i in GetAssembliesWithPartsInternal())
{
- 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,9 +1453,8 @@ namespace Emby.Server.Implementations
SupportsHttps = SupportsHttps,
HttpsPortNumber = HttpsPort,
OperatingSystem = EnvironmentInfo.OperatingSystem.ToString(),
- OperatingSystemDisplayName = OperatingSystemDisplayName,
+ OperatingSystemDisplayName = EnvironmentInfo.OperatingSystemName,
CanSelfRestart = CanSelfRestart,
- CanSelfUpdate = CanSelfUpdate,
CanLaunchWebBrowser = CanLaunchWebBrowser,
WanAddress = wanAddress,
HasUpdateAvailable = HasUpdateAvailable,
@@ -1788,7 +1522,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
@@ -2020,21 +1754,6 @@ namespace Emby.Server.Implementations
}
/// <summary>
- /// Updates the application.
- /// </summary>
- /// <param name="package">The package that contains the update</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <param name="progress">The progress.</param>
- public async Task UpdateApplication(PackageVersionInfo package, CancellationToken cancellationToken, IProgress<double> progress)
- {
- await InstallationManager.InstallPackage(package, false, progress, cancellationToken).ConfigureAwait(false);
-
- HasUpdateAvailable = false;
-
- OnApplicationUpdated(package);
- }
-
- /// <summary>
/// This returns localhost in the case of no external dns, and the hostname if the
/// dns is prefixed with a valid Uri prefix.
/// </summary>
diff --git a/Emby.Server.Implementations/Archiving/ZipClient.cs b/Emby.Server.Implementations/Archiving/ZipClient.cs
index 1135cf694..6b0fd2dc6 100644
--- a/Emby.Server.Implementations/Archiving/ZipClient.cs
+++ b/Emby.Server.Implementations/Archiving/ZipClient.cs
@@ -14,11 +14,9 @@ namespace Emby.Server.Implementations.Archiving
/// </summary>
public class ZipClient : IZipClient
{
- private readonly IFileSystem _fileSystem;
-
- public ZipClient(IFileSystem fileSystem)
+ public ZipClient()
{
- _fileSystem = fileSystem;
+
}
/// <summary>
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/Collections/CollectionImageProvider.cs b/Emby.Server.Implementations/Collections/CollectionImageProvider.cs
index 6aeadda2f..cdfb5cadf 100644
--- a/Emby.Server.Implementations/Collections/CollectionImageProvider.cs
+++ b/Emby.Server.Implementations/Collections/CollectionImageProvider.cs
@@ -10,14 +10,18 @@ using MediaBrowser.Controller.Entities.Movies;
using MediaBrowser.Controller.Entities.TV;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Entities;
-using MediaBrowser.Model.Extensions;
using MediaBrowser.Model.IO;
namespace Emby.Server.Implementations.Collections
{
public class CollectionImageProvider : BaseDynamicImageProvider<BoxSet>
{
- public CollectionImageProvider(IFileSystem fileSystem, IProviderManager providerManager, IApplicationPaths applicationPaths, IImageProcessor imageProcessor) : base(fileSystem, providerManager, applicationPaths, imageProcessor)
+ public CollectionImageProvider(
+ IFileSystem fileSystem,
+ IProviderManager providerManager,
+ IApplicationPaths applicationPaths,
+ IImageProcessor imageProcessor)
+ : base(fileSystem, providerManager, applicationPaths, imageProcessor)
{
}
diff --git a/Emby.Server.Implementations/Collections/CollectionManager.cs b/Emby.Server.Implementations/Collections/CollectionManager.cs
index 812e48a1f..2b99e0ddf 100644
--- a/Emby.Server.Implementations/Collections/CollectionManager.cs
+++ b/Emby.Server.Implementations/Collections/CollectionManager.cs
@@ -342,14 +342,12 @@ namespace Emby.Server.Implementations.Collections
{
private readonly CollectionManager _collectionManager;
private readonly IServerConfigurationManager _config;
- private readonly IFileSystem _fileSystem;
private ILogger _logger;
- public CollectionManagerEntryPoint(ICollectionManager collectionManager, IServerConfigurationManager config, IFileSystem fileSystem, ILogger logger)
+ public CollectionManagerEntryPoint(ICollectionManager collectionManager, IServerConfigurationManager config, ILogger logger)
{
_collectionManager = (CollectionManager)collectionManager;
_config = config;
- _fileSystem = fileSystem;
_logger = logger;
}
diff --git a/Emby.Server.Implementations/ConfigurationOptions.cs b/Emby.Server.Implementations/ConfigurationOptions.cs
new file mode 100644
index 000000000..30bfd8749
--- /dev/null
+++ b/Emby.Server.Implementations/ConfigurationOptions.cs
@@ -0,0 +1,12 @@
+using System.Collections.Generic;
+
+namespace Emby.Server.Implementations
+{
+ public static class ConfigurationOptions
+ {
+ public static readonly Dictionary<string, string> Configuration = new Dictionary<string, string>
+ {
+ {"HttpListenerHost:DefaultRedirectPath", "web/index.html"}
+ };
+ }
+}
diff --git a/Emby.Server.Implementations/Data/BaseSqliteRepository.cs b/Emby.Server.Implementations/Data/BaseSqliteRepository.cs
index 0f432c36c..fba81306b 100644
--- a/Emby.Server.Implementations/Data/BaseSqliteRepository.cs
+++ b/Emby.Server.Implementations/Data/BaseSqliteRepository.cs
@@ -224,7 +224,7 @@ namespace Emby.Server.Implementations.Data
});
}
- db.ExecuteAll(string.Join(";", queries.ToArray()));
+ db.ExecuteAll(string.Join(";", queries));
Logger.LogInformation("PRAGMA synchronous=" + db.Query("PRAGMA synchronous").SelectScalarString().First());
}
@@ -232,23 +232,6 @@ namespace Emby.Server.Implementations.Data
protected virtual int? CacheSize => null;
- internal static void CheckOk(int rc)
- {
- string msg = "";
-
- if (raw.SQLITE_OK != rc)
- {
- throw CreateException((ErrorCode)rc, msg);
- }
- }
-
- internal static Exception CreateException(ErrorCode rc, string msg)
- {
- var exp = new Exception(msg);
-
- return exp;
- }
-
private bool _disposed;
protected void CheckDisposed()
{
@@ -375,13 +358,6 @@ namespace Emby.Server.Implementations.Data
}
}
- public class DummyToken : IDisposable
- {
- public void Dispose()
- {
- }
- }
-
public static IDisposable Read(this ReaderWriterLockSlim obj)
{
//if (BaseSqliteRepository.ThreadSafeMode > 0)
@@ -390,6 +366,7 @@ namespace Emby.Server.Implementations.Data
//}
return new WriteLockToken(obj);
}
+
public static IDisposable Write(this ReaderWriterLockSlim obj)
{
//if (BaseSqliteRepository.ThreadSafeMode > 0)
diff --git a/Emby.Server.Implementations/Data/CleanDatabaseScheduledTask.cs b/Emby.Server.Implementations/Data/CleanDatabaseScheduledTask.cs
index dcfe14943..f7743a3c2 100644
--- a/Emby.Server.Implementations/Data/CleanDatabaseScheduledTask.cs
+++ b/Emby.Server.Implementations/Data/CleanDatabaseScheduledTask.cs
@@ -1,11 +1,8 @@
using System;
using System.Threading;
using System.Threading.Tasks;
-using MediaBrowser.Common.Configuration;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Library;
-using MediaBrowser.Controller.Persistence;
-using MediaBrowser.Model.IO;
using Microsoft.Extensions.Logging;
namespace Emby.Server.Implementations.Data
@@ -13,18 +10,12 @@ namespace Emby.Server.Implementations.Data
public class CleanDatabaseScheduledTask : ILibraryPostScanTask
{
private readonly ILibraryManager _libraryManager;
- private readonly IItemRepository _itemRepo;
private readonly ILogger _logger;
- private readonly IFileSystem _fileSystem;
- private readonly IApplicationPaths _appPaths;
- public CleanDatabaseScheduledTask(ILibraryManager libraryManager, IItemRepository itemRepo, ILogger logger, IFileSystem fileSystem, IApplicationPaths appPaths)
+ public CleanDatabaseScheduledTask(ILibraryManager libraryManager, ILogger logger)
{
_libraryManager = libraryManager;
- _itemRepo = itemRepo;
_logger = logger;
- _fileSystem = fileSystem;
- _appPaths = appPaths;
}
public Task Run(IProgress<double> progress, CancellationToken cancellationToken)
diff --git a/Emby.Server.Implementations/Data/SqliteItemRepository.cs b/Emby.Server.Implementations/Data/SqliteItemRepository.cs
index 3014e482d..70e5fa640 100644
--- a/Emby.Server.Implementations/Data/SqliteItemRepository.cs
+++ b/Emby.Server.Implementations/Data/SqliteItemRepository.cs
@@ -536,7 +536,7 @@ namespace Emby.Server.Implementations.Data
throw new ArgumentNullException(nameof(item));
}
- SaveItems(new List<BaseItem> { item }, cancellationToken);
+ SaveItems(new [] { item }, cancellationToken);
}
public void SaveImages(BaseItem item)
@@ -576,7 +576,7 @@ namespace Emby.Server.Implementations.Data
/// or
/// cancellationToken
/// </exception>
- public void SaveItems(List<BaseItem> items, CancellationToken cancellationToken)
+ public void SaveItems(IEnumerable<BaseItem> items, CancellationToken cancellationToken)
{
if (items == null)
{
@@ -587,7 +587,7 @@ namespace Emby.Server.Implementations.Data
CheckDisposed();
- var tuples = new List<Tuple<BaseItem, List<Guid>, BaseItem, string, List<string>>>();
+ var tuples = new List<(BaseItem, List<Guid>, BaseItem, string, List<string>)>();
foreach (var item in items)
{
var ancestorIds = item.SupportsAncestors ?
@@ -599,7 +599,7 @@ namespace Emby.Server.Implementations.Data
var userdataKey = item.GetUserDataKeys().FirstOrDefault();
var inheritedTags = item.GetInheritedTags();
- tuples.Add(new Tuple<BaseItem, List<Guid>, BaseItem, string, List<string>>(item, ancestorIds, topParent, userdataKey, inheritedTags));
+ tuples.Add((item, ancestorIds, topParent, userdataKey, inheritedTags));
}
using (WriteLock.Write())
@@ -615,7 +615,7 @@ namespace Emby.Server.Implementations.Data
}
}
- private void SaveItemsInTranscation(IDatabaseConnection db, List<Tuple<BaseItem, List<Guid>, BaseItem, string, List<string>>> tuples)
+ private void SaveItemsInTranscation(IDatabaseConnection db, IEnumerable<(BaseItem, List<Guid>, BaseItem, string, List<string>)> tuples)
{
var statements = PrepareAllSafe(db, new string[]
{
@@ -966,7 +966,7 @@ namespace Emby.Server.Implementations.Data
if (item.ExtraIds.Length > 0)
{
- saveItemStatement.TryBind("@ExtraIds", string.Join("|", item.ExtraIds.ToArray()));
+ saveItemStatement.TryBind("@ExtraIds", string.Join("|", item.ExtraIds));
}
else
{
@@ -1183,9 +1183,9 @@ namespace Emby.Server.Implementations.Data
/// <exception cref="ArgumentException"></exception>
public BaseItem RetrieveItem(Guid id)
{
- if (id.Equals(Guid.Empty))
+ if (id == Guid.Empty)
{
- throw new ArgumentNullException(nameof(id));
+ throw new ArgumentException(nameof(id), "Guid can't be empty");
}
CheckDisposed();
@@ -2079,14 +2079,14 @@ namespace Emby.Server.Implementations.Data
return false;
}
- var sortingFields = query.OrderBy.Select(i => i.Item1);
+ var sortingFields = new HashSet<string>(query.OrderBy.Select(i => i.Item1), StringComparer.OrdinalIgnoreCase);
- return sortingFields.Contains(ItemSortBy.IsFavoriteOrLiked, StringComparer.OrdinalIgnoreCase)
- || sortingFields.Contains(ItemSortBy.IsPlayed, StringComparer.OrdinalIgnoreCase)
- || sortingFields.Contains(ItemSortBy.IsUnplayed, StringComparer.OrdinalIgnoreCase)
- || sortingFields.Contains(ItemSortBy.PlayCount, StringComparer.OrdinalIgnoreCase)
- || sortingFields.Contains(ItemSortBy.DatePlayed, StringComparer.OrdinalIgnoreCase)
- || sortingFields.Contains(ItemSortBy.SeriesDatePlayed, StringComparer.OrdinalIgnoreCase)
+ return sortingFields.Contains(ItemSortBy.IsFavoriteOrLiked)
+ || sortingFields.Contains(ItemSortBy.IsPlayed)
+ || sortingFields.Contains(ItemSortBy.IsUnplayed)
+ || sortingFields.Contains(ItemSortBy.PlayCount)
+ || sortingFields.Contains(ItemSortBy.DatePlayed)
+ || sortingFields.Contains(ItemSortBy.SeriesDatePlayed)
|| query.IsFavoriteOrLiked.HasValue
|| query.IsFavorite.HasValue
|| query.IsResumable.HasValue
@@ -2094,9 +2094,9 @@ namespace Emby.Server.Implementations.Data
|| query.IsLiked.HasValue;
}
- private readonly List<ItemFields> allFields = Enum.GetNames(typeof(ItemFields))
+ private readonly ItemFields[] _allFields = Enum.GetNames(typeof(ItemFields))
.Select(i => (ItemFields)Enum.Parse(typeof(ItemFields), i, true))
- .ToList();
+ .ToArray();
private string[] GetColumnNamesFromField(ItemFields field)
{
@@ -2151,18 +2151,26 @@ namespace Emby.Server.Implementations.Data
}
}
- private bool HasProgramAttributes(InternalItemsQuery query)
+ private static readonly HashSet<string> _programExcludeParentTypes = new HashSet<string>(StringComparer.OrdinalIgnoreCase)
{
- var excludeParentTypes = new string[]
- {
- "Series",
- "Season",
- "MusicAlbum",
- "MusicArtist",
- "PhotoAlbum"
- };
+ "Series",
+ "Season",
+ "MusicAlbum",
+ "MusicArtist",
+ "PhotoAlbum"
+ };
+
+ private static readonly HashSet<string> _programTypes = new HashSet<string>(StringComparer.OrdinalIgnoreCase)
+ {
+ "Program",
+ "TvChannel",
+ "LiveTvProgram",
+ "LiveTvTvChannel"
+ };
- if (excludeParentTypes.Contains(query.ParentType ?? string.Empty, StringComparer.OrdinalIgnoreCase))
+ private bool HasProgramAttributes(InternalItemsQuery query)
+ {
+ if (_programExcludeParentTypes.Contains(query.ParentType))
{
return false;
}
@@ -2172,29 +2180,18 @@ namespace Emby.Server.Implementations.Data
return true;
}
- var types = new string[]
- {
- "Program",
- "TvChannel",
- "LiveTvProgram",
- "LiveTvTvChannel"
- };
-
- return types.Any(i => query.IncludeItemTypes.Contains(i, StringComparer.OrdinalIgnoreCase));
+ return query.IncludeItemTypes.Any(x => _programTypes.Contains(x));
}
- private bool HasServiceName(InternalItemsQuery query)
+ private static readonly HashSet<string> _serviceTypes = new HashSet<string>(StringComparer.OrdinalIgnoreCase)
{
- var excludeParentTypes = new string[]
- {
- "Series",
- "Season",
- "MusicAlbum",
- "MusicArtist",
- "PhotoAlbum"
- };
+ "TvChannel",
+ "LiveTvTvChannel"
+ };
- if (excludeParentTypes.Contains(query.ParentType ?? string.Empty, StringComparer.OrdinalIgnoreCase))
+ private bool HasServiceName(InternalItemsQuery query)
+ {
+ if (_programExcludeParentTypes.Contains(query.ParentType))
{
return false;
}
@@ -2204,27 +2201,18 @@ namespace Emby.Server.Implementations.Data
return true;
}
- var types = new string[]
- {
- "TvChannel",
- "LiveTvTvChannel"
- };
-
- return types.Any(i => query.IncludeItemTypes.Contains(i, StringComparer.OrdinalIgnoreCase));
+ return query.IncludeItemTypes.Any(x => _serviceTypes.Contains(x));
}
- private bool HasStartDate(InternalItemsQuery query)
+ private static readonly HashSet<string> _startDateTypes = new HashSet<string>(StringComparer.OrdinalIgnoreCase)
{
- var excludeParentTypes = new string[]
- {
- "Series",
- "Season",
- "MusicAlbum",
- "MusicArtist",
- "PhotoAlbum"
- };
+ "Program",
+ "LiveTvProgram"
+ };
- if (excludeParentTypes.Contains(query.ParentType ?? string.Empty, StringComparer.OrdinalIgnoreCase))
+ private bool HasStartDate(InternalItemsQuery query)
+ {
+ if (_programExcludeParentTypes.Contains(query.ParentType))
{
return false;
}
@@ -2234,13 +2222,7 @@ namespace Emby.Server.Implementations.Data
return true;
}
- var types = new string[]
- {
- "Program",
- "LiveTvProgram"
- };
-
- return types.Any(i => query.IncludeItemTypes.Contains(i, StringComparer.OrdinalIgnoreCase));
+ return query.IncludeItemTypes.Any(x => _startDateTypes.Contains(x));
}
private bool HasEpisodeAttributes(InternalItemsQuery query)
@@ -2263,16 +2245,26 @@ namespace Emby.Server.Implementations.Data
return query.IncludeItemTypes.Contains("Trailer", StringComparer.OrdinalIgnoreCase);
}
- private bool HasArtistFields(InternalItemsQuery query)
+
+ private static readonly HashSet<string> _artistExcludeParentTypes = new HashSet<string>(StringComparer.OrdinalIgnoreCase)
{
- var excludeParentTypes = new string[]
- {
- "Series",
- "Season",
- "PhotoAlbum"
- };
+ "Series",
+ "Season",
+ "PhotoAlbum"
+ };
+
+ private static readonly HashSet<string> _artistsTypes = new HashSet<string>(StringComparer.OrdinalIgnoreCase)
+ {
+ "Audio",
+ "MusicAlbum",
+ "MusicVideo",
+ "AudioBook",
+ "AudioPodcast"
+ };
- if (excludeParentTypes.Contains(query.ParentType ?? string.Empty, StringComparer.OrdinalIgnoreCase))
+ private bool HasArtistFields(InternalItemsQuery query)
+ {
+ if (_artistExcludeParentTypes.Contains(query.ParentType))
{
return false;
}
@@ -2282,18 +2274,18 @@ namespace Emby.Server.Implementations.Data
return true;
}
- var types = new string[]
- {
- "Audio",
- "MusicAlbum",
- "MusicVideo",
- "AudioBook",
- "AudioPodcast"
- };
-
- return types.Any(i => query.IncludeItemTypes.Contains(i, StringComparer.OrdinalIgnoreCase));
+ return query.IncludeItemTypes.Any(x => _artistsTypes.Contains(x));
}
+ private static readonly HashSet<string> _seriesTypes = new HashSet<string>(StringComparer.OrdinalIgnoreCase)
+ {
+ "Audio",
+ "MusicAlbum",
+ "MusicVideo",
+ "AudioBook",
+ "AudioPodcast"
+ };
+
private bool HasSeriesFields(InternalItemsQuery query)
{
if (string.Equals(query.ParentType, "PhotoAlbum", StringComparison.OrdinalIgnoreCase))
@@ -2306,26 +2298,18 @@ namespace Emby.Server.Implementations.Data
return true;
}
- var types = new string[]
- {
- "Book",
- "AudioBook",
- "Episode",
- "Season"
- };
-
- return types.Any(i => query.IncludeItemTypes.Contains(i, StringComparer.OrdinalIgnoreCase));
+ return query.IncludeItemTypes.Any(x => _seriesTypes.Contains(x));
}
- private string[] GetFinalColumnsToSelect(InternalItemsQuery query, string[] startColumns)
+ private List<string> GetFinalColumnsToSelect(InternalItemsQuery query, IEnumerable<string> startColumns)
{
var list = startColumns.ToList();
- foreach (var field in allFields)
+ foreach (var field in _allFields)
{
if (!HasField(query, field))
{
- foreach (var fieldToRemove in GetColumnNamesFromField(field).ToList())
+ foreach (var fieldToRemove in GetColumnNamesFromField(field))
{
list.Remove(fieldToRemove);
}
@@ -2419,11 +2403,14 @@ namespace Emby.Server.Implementations.Data
list.Add(builder.ToString());
- var excludeIds = query.ExcludeItemIds.ToList();
- excludeIds.Add(item.Id);
- excludeIds.AddRange(item.ExtraIds);
+ var oldLen = query.ExcludeItemIds.Length;
+ var newLen = oldLen + item.ExtraIds.Length + 1;
+ var excludeIds = new Guid[newLen];
+ query.ExcludeItemIds.CopyTo(excludeIds, 0);
+ excludeIds[oldLen] = item.Id;
+ item.ExtraIds.CopyTo(excludeIds, oldLen + 1);
- query.ExcludeItemIds = excludeIds.ToArray();
+ query.ExcludeItemIds = excludeIds;
query.ExcludeProviderIds = item.ProviderIds;
}
@@ -2444,7 +2431,7 @@ namespace Emby.Server.Implementations.Data
list.Add(builder.ToString());
}
- return list.ToArray();
+ return list;
}
private void BindSearchParams(InternalItemsQuery query, IStatement statement)
@@ -2723,18 +2710,17 @@ namespace Emby.Server.Implementations.Data
private void AddItem(List<BaseItem> items, BaseItem newItem)
{
- var providerIds = newItem.ProviderIds.ToList();
-
for (var i = 0; i < items.Count; i++)
{
var item = items[i];
- foreach (var providerId in providerIds)
+ foreach (var providerId in newItem.ProviderIds)
{
if (providerId.Key == MetadataProviders.TmdbCollection.ToString())
{
continue;
}
+
if (item.GetProviderId(providerId.Key) == providerId.Value)
{
if (newItem.SourceType == SourceType.Library)
@@ -2753,15 +2739,15 @@ namespace Emby.Server.Implementations.Data
{
var elapsed = (DateTime.UtcNow - startDate).TotalMilliseconds;
- int slowThreshold = 1000;
+ int slowThreshold = 100;
#if DEBUG
- slowThreshold = 250;
+ slowThreshold = 10;
#endif
if (elapsed >= slowThreshold)
{
- Logger.LogWarning("{0} query time (slow): {1}ms. Query: {2}",
+ Logger.LogWarning("{0} query time (slow): {1:g}. Query: {2}",
methodName,
elapsed,
commandText);
@@ -2806,7 +2792,7 @@ namespace Emby.Server.Implementations.Data
var whereText = whereClauses.Count == 0 ?
string.Empty :
- " where " + string.Join(" AND ", whereClauses.ToArray());
+ " where " + string.Join(" AND ", whereClauses);
commandText += whereText
+ GetGroupBy(query)
@@ -2930,25 +2916,31 @@ namespace Emby.Server.Implementations.Data
private string GetOrderByText(InternalItemsQuery query)
{
- var orderBy = query.OrderBy.ToList();
- var enableOrderInversion = false;
-
- if (query.SimilarTo != null && orderBy.Count == 0)
+ if (string.IsNullOrEmpty(query.SearchTerm))
{
- orderBy.Add(new ValueTuple<string, SortOrder>("SimilarityScore", SortOrder.Descending));
- orderBy.Add(new ValueTuple<string, SortOrder>(ItemSortBy.Random, SortOrder.Ascending));
- }
+ int oldLen = query.OrderBy.Length;
- if (!string.IsNullOrEmpty(query.SearchTerm))
+ if (query.SimilarTo != null && oldLen == 0)
+ {
+ var arr = new (string, SortOrder)[oldLen + 2];
+ query.OrderBy.CopyTo(arr, 0);
+ arr[oldLen] = ("SimilarityScore", SortOrder.Descending);
+ arr[oldLen + 1] = (ItemSortBy.Random, SortOrder.Ascending);
+ query.OrderBy = arr;
+ }
+ }
+ else
{
- orderBy = new List<(string, SortOrder)>();
- orderBy.Add(new ValueTuple<string, SortOrder>("SearchScore", SortOrder.Descending));
- orderBy.Add(new ValueTuple<string, SortOrder>(ItemSortBy.SortName, SortOrder.Ascending));
+ query.OrderBy = new []
+ {
+ ("SearchScore", SortOrder.Descending),
+ (ItemSortBy.SortName, SortOrder.Ascending)
+ };
}
- query.OrderBy = orderBy.ToArray();
+ var orderBy = query.OrderBy;
- if (orderBy.Count == 0)
+ if (orderBy.Length == 0)
{
return string.Empty;
}
@@ -2957,6 +2949,7 @@ namespace Emby.Server.Implementations.Data
{
var columnMap = MapOrderByField(i.Item1, query);
var columnAscending = i.Item2 == SortOrder.Ascending;
+ const bool enableOrderInversion = false;
if (columnMap.Item2 && enableOrderInversion)
{
columnAscending = !columnAscending;
@@ -2968,7 +2961,7 @@ namespace Emby.Server.Implementations.Data
}));
}
- private ValueTuple<string, bool> MapOrderByField(string name, InternalItemsQuery query)
+ private (string, bool) MapOrderByField(string name, InternalItemsQuery query)
{
if (string.Equals(name, ItemSortBy.AirTime, StringComparison.OrdinalIgnoreCase))
{
@@ -3218,7 +3211,7 @@ namespace Emby.Server.Implementations.Data
var whereText = whereClauses.Count == 0 ?
string.Empty :
- " where " + string.Join(" AND ", whereClauses.ToArray());
+ " where " + string.Join(" AND ", whereClauses);
commandText += whereText
+ GetGroupBy(query)
@@ -4378,7 +4371,7 @@ namespace Emby.Server.Implementations.Data
}
else if (query.Years.Length > 1)
{
- var val = string.Join(",", query.Years.ToArray());
+ var val = string.Join(",", query.Years);
whereClauses.Add("ProductionYear in (" + val + ")");
}
@@ -4952,7 +4945,12 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type
return result;
}
- return new[] { value }.Where(IsValidType);
+ if (IsValidType(value))
+ {
+ return new[] { value };
+ }
+
+ return Array.Empty<string>();
}
public void DeleteItem(Guid id, CancellationToken cancellationToken)
@@ -5215,32 +5213,32 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type
}
}
- public QueryResult<Tuple<BaseItem, ItemCounts>> GetAllArtists(InternalItemsQuery query)
+ public QueryResult<(BaseItem, ItemCounts)> GetAllArtists(InternalItemsQuery query)
{
return GetItemValues(query, new[] { 0, 1 }, typeof(MusicArtist).FullName);
}
- public QueryResult<Tuple<BaseItem, ItemCounts>> GetArtists(InternalItemsQuery query)
+ public QueryResult<(BaseItem, ItemCounts)> GetArtists(InternalItemsQuery query)
{
return GetItemValues(query, new[] { 0 }, typeof(MusicArtist).FullName);
}
- public QueryResult<Tuple<BaseItem, ItemCounts>> GetAlbumArtists(InternalItemsQuery query)
+ public QueryResult<(BaseItem, ItemCounts)> GetAlbumArtists(InternalItemsQuery query)
{
return GetItemValues(query, new[] { 1 }, typeof(MusicArtist).FullName);
}
- public QueryResult<Tuple<BaseItem, ItemCounts>> GetStudios(InternalItemsQuery query)
+ public QueryResult<(BaseItem, ItemCounts)> GetStudios(InternalItemsQuery query)
{
return GetItemValues(query, new[] { 3 }, typeof(Studio).FullName);
}
- public QueryResult<Tuple<BaseItem, ItemCounts>> GetGenres(InternalItemsQuery query)
+ public QueryResult<(BaseItem, ItemCounts)> GetGenres(InternalItemsQuery query)
{
return GetItemValues(query, new[] { 2 }, typeof(Genre).FullName);
}
- public QueryResult<Tuple<BaseItem, ItemCounts>> GetMusicGenres(InternalItemsQuery query)
+ public QueryResult<(BaseItem, ItemCounts)> GetMusicGenres(InternalItemsQuery query)
{
return GetItemValues(query, new[] { 2 }, typeof(MusicGenre).FullName);
}
@@ -5317,7 +5315,7 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type
}
}
- private QueryResult<Tuple<BaseItem, ItemCounts>> GetItemValues(InternalItemsQuery query, int[] itemValueTypes, string returnType)
+ private QueryResult<(BaseItem, ItemCounts)> GetItemValues(InternalItemsQuery query, int[] itemValueTypes, string returnType)
{
if (query == null)
{
@@ -5335,7 +5333,7 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type
var typeClause = itemValueTypes.Length == 1 ?
("Type=" + itemValueTypes[0].ToString(CultureInfo.InvariantCulture)) :
- ("Type in (" + string.Join(",", itemValueTypes.Select(i => i.ToString(CultureInfo.InvariantCulture)).ToArray()) + ")");
+ ("Type in (" + string.Join(",", itemValueTypes.Select(i => i.ToString(CultureInfo.InvariantCulture))) + ")");
InternalItemsQuery typeSubQuery = null;
@@ -5363,11 +5361,7 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type
whereClauses.Add("guid in (select ItemId from ItemValues where ItemValues.CleanValue=A.CleanName AND " + typeClause + ")");
- var typeWhereText = whereClauses.Count == 0 ?
- string.Empty :
- " where " + string.Join(" AND ", whereClauses);
-
- itemCountColumnQuery += typeWhereText;
+ itemCountColumnQuery += " where " + string.Join(" AND ", whereClauses);
itemCountColumns = new Dictionary<string, string>()
{
@@ -5400,7 +5394,7 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type
IsSeries = query.IsSeries
};
- columns = GetFinalColumnsToSelect(query, columns.ToArray()).ToList();
+ columns = GetFinalColumnsToSelect(query, columns);
var commandText = "select "
+ string.Join(",", columns)
@@ -5492,8 +5486,8 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type
{
return connection.RunInTransaction(db =>
{
- var list = new List<Tuple<BaseItem, ItemCounts>>();
- var result = new QueryResult<Tuple<BaseItem, ItemCounts>>();
+ var list = new List<(BaseItem, ItemCounts)>();
+ var result = new QueryResult<(BaseItem, ItemCounts)>();
var statements = PrepareAllSafe(db, statementTexts);
@@ -5531,7 +5525,7 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type
{
var countStartColumn = columns.Count - 1;
- list.Add(new Tuple<BaseItem, ItemCounts>(item, GetItemCounts(row, countStartColumn, typesToCount)));
+ list.Add((item, GetItemCounts(row, countStartColumn, typesToCount)));
}
}
@@ -6198,6 +6192,5 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type
return item;
}
-
}
}
diff --git a/Emby.Server.Implementations/Devices/DeviceId.cs b/Emby.Server.Implementations/Devices/DeviceId.cs
index 866bd137f..495c3436a 100644
--- a/Emby.Server.Implementations/Devices/DeviceId.cs
+++ b/Emby.Server.Implementations/Devices/DeviceId.cs
@@ -11,7 +11,6 @@ namespace Emby.Server.Implementations.Devices
{
private readonly IApplicationPaths _appPaths;
private readonly ILogger _logger;
- private readonly IFileSystem _fileSystem;
private readonly object _syncLock = new object();
@@ -86,19 +85,10 @@ namespace Emby.Server.Implementations.Devices
private string _id;
- public DeviceId(
- IApplicationPaths appPaths,
- ILoggerFactory loggerFactory,
- IFileSystem fileSystem)
+ public DeviceId(IApplicationPaths appPaths, ILoggerFactory loggerFactory)
{
- if (fileSystem == null)
- {
- throw new ArgumentNullException(nameof(fileSystem));
- }
-
_appPaths = appPaths;
_logger = loggerFactory.CreateLogger("SystemId");
- _fileSystem = fileSystem;
}
public string Value => _id ?? (_id = GetDeviceId());
diff --git a/Emby.Server.Implementations/Devices/DeviceManager.cs b/Emby.Server.Implementations/Devices/DeviceManager.cs
index ec3649bca..7d6529a67 100644
--- a/Emby.Server.Implementations/Devices/DeviceManager.cs
+++ b/Emby.Server.Implementations/Devices/DeviceManager.cs
@@ -34,8 +34,6 @@ namespace Emby.Server.Implementations.Devices
private readonly IFileSystem _fileSystem;
private readonly ILibraryMonitor _libraryMonitor;
private readonly IServerConfigurationManager _config;
- private readonly ILogger _logger;
- private readonly INetworkManager _network;
private readonly ILibraryManager _libraryManager;
private readonly ILocalizationManager _localizationManager;
@@ -55,17 +53,13 @@ namespace Emby.Server.Implementations.Devices
IUserManager userManager,
IFileSystem fileSystem,
ILibraryMonitor libraryMonitor,
- IServerConfigurationManager config,
- ILoggerFactory loggerFactory,
- INetworkManager network)
+ IServerConfigurationManager config)
{
_json = json;
_userManager = userManager;
_fileSystem = fileSystem;
_libraryMonitor = libraryMonitor;
_config = config;
- _logger = loggerFactory.CreateLogger(nameof(DeviceManager));
- _network = network;
_libraryManager = libraryManager;
_localizationManager = localizationManager;
_authRepo = authRepo;
@@ -414,14 +408,12 @@ namespace Emby.Server.Implementations.Devices
{
private readonly DeviceManager _deviceManager;
private readonly IServerConfigurationManager _config;
- private readonly IFileSystem _fileSystem;
private ILogger _logger;
- public DeviceManagerEntryPoint(IDeviceManager deviceManager, IServerConfigurationManager config, IFileSystem fileSystem, ILogger logger)
+ public DeviceManagerEntryPoint(IDeviceManager deviceManager, IServerConfigurationManager config, ILogger logger)
{
_deviceManager = (DeviceManager)deviceManager;
_config = config;
- _fileSystem = fileSystem;
_logger = logger;
}
diff --git a/Emby.Server.Implementations/Dto/DtoService.cs b/Emby.Server.Implementations/Dto/DtoService.cs
index 983eb51e6..2233d3d40 100644
--- a/Emby.Server.Implementations/Dto/DtoService.cs
+++ b/Emby.Server.Implementations/Dto/DtoService.cs
@@ -36,13 +36,9 @@ namespace Emby.Server.Implementations.Dto
private readonly IItemRepository _itemRepo;
private readonly IImageProcessor _imageProcessor;
- private readonly IServerConfigurationManager _config;
- private readonly IFileSystem _fileSystem;
private readonly IProviderManager _providerManager;
- private readonly Func<IChannelManager> _channelManagerFactory;
private readonly IApplicationHost _appHost;
- private readonly Func<IDeviceManager> _deviceManager;
private readonly Func<IMediaSourceManager> _mediaSourceManager;
private readonly Func<ILiveTvManager> _livetvManager;
@@ -52,12 +48,8 @@ namespace Emby.Server.Implementations.Dto
IUserDataManager userDataRepository,
IItemRepository itemRepo,
IImageProcessor imageProcessor,
- IServerConfigurationManager config,
- IFileSystem fileSystem,
IProviderManager providerManager,
- Func<IChannelManager> channelManagerFactory,
IApplicationHost appHost,
- Func<IDeviceManager> deviceManager,
Func<IMediaSourceManager> mediaSourceManager,
Func<ILiveTvManager> livetvManager)
{
@@ -66,12 +58,8 @@ namespace Emby.Server.Implementations.Dto
_userDataRepository = userDataRepository;
_itemRepo = itemRepo;
_imageProcessor = imageProcessor;
- _config = config;
- _fileSystem = fileSystem;
_providerManager = providerManager;
- _channelManagerFactory = channelManagerFactory;
_appHost = appHost;
- _deviceManager = deviceManager;
_mediaSourceManager = mediaSourceManager;
_livetvManager = livetvManager;
}
diff --git a/Emby.Server.Implementations/Emby.Server.Implementations.csproj b/Emby.Server.Implementations/Emby.Server.Implementations.csproj
index 8356a9501..bbf165d62 100644
--- a/Emby.Server.Implementations/Emby.Server.Implementations.csproj
+++ b/Emby.Server.Implementations/Emby.Server.Implementations.csproj
@@ -22,9 +22,11 @@
</ItemGroup>
<ItemGroup>
+ <PackageReference Include="Microsoft.Extensions.Logging" Version="2.2.0" />
+ <PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="2.2.0" />
+ <PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" 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/FFMpeg/FFMpegLoader.cs b/Emby.Server.Implementations/FFMpeg/FFMpegLoader.cs
index 6167d1eaa..bbf51dd24 100644
--- a/Emby.Server.Implementations/FFMpeg/FFMpegLoader.cs
+++ b/Emby.Server.Implementations/FFMpeg/FFMpegLoader.cs
@@ -3,27 +3,19 @@ using System.Collections.Generic;
using System.IO;
using System.Linq;
using MediaBrowser.Common.Configuration;
-using MediaBrowser.Common.Net;
using MediaBrowser.Model.IO;
-using Microsoft.Extensions.Logging;
namespace Emby.Server.Implementations.FFMpeg
{
public class FFMpegLoader
{
- private readonly IHttpClient _httpClient;
private readonly IApplicationPaths _appPaths;
- private readonly ILogger _logger;
- private readonly IZipClient _zipClient;
private readonly IFileSystem _fileSystem;
private readonly FFMpegInstallInfo _ffmpegInstallInfo;
- public FFMpegLoader(ILogger logger, IApplicationPaths appPaths, IHttpClient httpClient, IZipClient zipClient, IFileSystem fileSystem, FFMpegInstallInfo ffmpegInstallInfo)
+ public FFMpegLoader(IApplicationPaths appPaths, IFileSystem fileSystem, FFMpegInstallInfo ffmpegInstallInfo)
{
- _logger = logger;
_appPaths = appPaths;
- _httpClient = httpClient;
- _zipClient = zipClient;
_fileSystem = fileSystem;
_ffmpegInstallInfo = ffmpegInstallInfo;
}
@@ -115,8 +107,7 @@ namespace Emby.Server.Implementations.FFMpeg
var encoderFilename = Path.GetFileName(info.EncoderPath);
var probeFilename = Path.GetFileName(info.ProbePath);
- foreach (var directory in _fileSystem.GetDirectoryPaths(rootEncoderPath)
- .ToList())
+ foreach (var directory in _fileSystem.GetDirectoryPaths(rootEncoderPath))
{
var allFiles = _fileSystem.GetFilePaths(directory, true).ToList();
diff --git a/Emby.Server.Implementations/HttpClientManager/HttpClientManager.cs b/Emby.Server.Implementations/HttpClientManager/HttpClientManager.cs
index 6ea1bd08e..2e0728136 100644
--- a/Emby.Server.Implementations/HttpClientManager/HttpClientManager.cs
+++ b/Emby.Server.Implementations/HttpClientManager/HttpClientManager.cs
@@ -66,11 +66,6 @@ namespace Emby.Server.Implementations.HttpClientManager
// http://stackoverflow.com/questions/566437/http-post-returns-the-error-417-expectation-failed-c
ServicePointManager.Expect100Continue = false;
-
-#if NET46
-// Trakt requests sometimes fail without this
- ServicePointManager.SecurityProtocol = SecurityProtocolType.Ssl3 | SecurityProtocolType.Tls;
-#endif
}
/// <summary>
@@ -106,23 +101,6 @@ namespace Emby.Server.Implementations.HttpClientManager
return client;
}
- private static WebRequest CreateWebRequest(string url)
- {
- try
- {
- return WebRequest.Create(url);
- }
- catch (NotSupportedException)
- {
- //Webrequest creation does fail on MONO randomly when using WebRequest.Create
- //the issue occurs in the GetCreator method here: http://www.oschina.net/code/explore/mono-2.8.1/mcs/class/System/System.Net/WebRequest.cs
-
- var type = Type.GetType("System.Net.HttpRequestCreator, System, Version=4.0.0.0,Culture=neutral, PublicKeyToken=b77a5c561934e089");
- var creator = Activator.CreateInstance(type, nonPublic: true) as IWebRequestCreate;
- return creator.Create(new Uri(url)) as HttpWebRequest;
- }
- }
-
private WebRequest GetRequest(HttpRequestOptions options, string method)
{
string url = options.Url;
@@ -135,7 +113,7 @@ namespace Emby.Server.Implementations.HttpClientManager
url = url.Replace(userInfo + "@", string.Empty);
}
- var request = CreateWebRequest(url);
+ var request = WebRequest.Create(url);
if (request is HttpWebRequest httpWebRequest)
{
@@ -561,21 +539,10 @@ namespace Emby.Server.Implementations.HttpClientManager
var contentLength = GetContentLength(httpResponse);
- if (contentLength.HasValue)
- {
- using (var fs = _fileSystem.GetFileStream(tempFile, FileOpenMode.Create, FileAccessMode.Write, FileShareMode.Read, true))
- {
- await httpResponse.GetResponseStream().CopyToAsync(fs, StreamDefaults.DefaultCopyToBufferSize, options.CancellationToken).ConfigureAwait(false);
- }
- }
- else
+ using (var stream = httpResponse.GetResponseStream())
+ using (var fs = _fileSystem.GetFileStream(tempFile, FileOpenMode.Create, FileAccessMode.Write, FileShareMode.Read, true))
{
- // We're not able to track progress
- using (var stream = httpResponse.GetResponseStream())
- using (var fs = _fileSystem.GetFileStream(tempFile, FileOpenMode.Create, FileAccessMode.Write, FileShareMode.Read, true))
- {
- await stream.CopyToAsync(fs, StreamDefaults.DefaultCopyToBufferSize, options.CancellationToken).ConfigureAwait(false);
- }
+ await stream.CopyToAsync(fs, StreamDefaults.DefaultCopyToBufferSize, options.CancellationToken).ConfigureAwait(false);
}
options.Progress.Report(100);
@@ -627,14 +594,16 @@ namespace Emby.Server.Implementations.HttpClientManager
var exception = new HttpException(webException.Message, webException);
- var response = webException.Response as HttpWebResponse;
- if (response != null)
+ using (var response = webException.Response as HttpWebResponse)
{
- exception.StatusCode = response.StatusCode;
-
- if ((int)response.StatusCode == 429)
+ if (response != null)
{
- client.LastTimeout = DateTime.UtcNow;
+ exception.StatusCode = response.StatusCode;
+
+ if ((int)response.StatusCode == 429)
+ {
+ client.LastTimeout = DateTime.UtcNow;
+ }
}
}
diff --git a/Emby.Server.Implementations/HttpServer/HttpListenerHost.cs b/Emby.Server.Implementations/HttpServer/HttpListenerHost.cs
index 834ffb130..ee746c669 100644
--- a/Emby.Server.Implementations/HttpServer/HttpListenerHost.cs
+++ b/Emby.Server.Implementations/HttpServer/HttpListenerHost.cs
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
+using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
@@ -19,7 +20,9 @@ using MediaBrowser.Model.Events;
using MediaBrowser.Model.Extensions;
using MediaBrowser.Model.Serialization;
using MediaBrowser.Model.Services;
+using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
+using ServiceStack.Text.Jsv;
namespace Emby.Server.Implementations.HttpServer
{
@@ -53,20 +56,20 @@ namespace Emby.Server.Implementations.HttpServer
IServerApplicationHost applicationHost,
ILoggerFactory loggerFactory,
IServerConfigurationManager config,
- string defaultRedirectPath,
+ IConfiguration configuration,
INetworkManager networkManager,
IJsonSerializer jsonSerializer,
- IXmlSerializer xmlSerializer,
- Func<Type, Func<string, object>> funcParseFn)
+ IXmlSerializer xmlSerializer)
{
_appHost = applicationHost;
_logger = loggerFactory.CreateLogger("HttpServer");
_config = config;
- DefaultRedirectPath = defaultRedirectPath;
+ DefaultRedirectPath = configuration["HttpListenerHost:DefaultRedirectPath"];
_networkManager = networkManager;
_jsonSerializer = jsonSerializer;
_xmlSerializer = xmlSerializer;
- _funcParseFn = funcParseFn;
+
+ _funcParseFn = t => s => JsvReader.GetParseFn(t)(s);
Instance = this;
ResponseFilters = Array.Empty<Action<IRequest, IResponse, object>>();
@@ -284,31 +287,6 @@ namespace Emby.Server.Implementations.HttpServer
}
}
- private static readonly string[] _skipLogExtensions =
- {
- ".js",
- ".css",
- ".woff",
- ".woff2",
- ".ttf",
- ".html"
- };
-
- private bool EnableLogging(string url, string localPath)
- {
- var extension = GetExtension(url);
-
- return ((string.IsNullOrEmpty(extension) || !_skipLogExtensions.Contains(extension))
- && (string.IsNullOrEmpty(localPath) || localPath.IndexOf("system/ping", StringComparison.OrdinalIgnoreCase) == -1));
- }
-
- private static string GetExtension(string url)
- {
- var parts = url.Split(new[] { '?' }, 2);
-
- return Path.GetExtension(parts[0]);
- }
-
public static string RemoveQueryStringByKey(string url, string key)
{
var uri = new Uri(url);
@@ -446,10 +424,9 @@ namespace Emby.Server.Implementations.HttpServer
/// </summary>
protected async Task RequestHandler(IHttpRequest httpReq, string urlString, string host, string localPath, CancellationToken cancellationToken)
{
- var date = DateTime.Now;
+ var stopWatch = new Stopwatch();
+ stopWatch.Start();
var httpRes = httpReq.Response;
- bool enableLog = false;
- bool logHeaders = false;
string urlToLog = null;
string remoteIp = httpReq.RemoteIp;
@@ -496,18 +473,8 @@ namespace Emby.Server.Implementations.HttpServer
return;
}
- var operationName = httpReq.OperationName;
-
- enableLog = EnableLogging(urlString, localPath);
- urlToLog = urlString;
- logHeaders = enableLog && urlToLog.IndexOf("/videos/", StringComparison.OrdinalIgnoreCase) != -1;
-
- if (enableLog)
- {
- urlToLog = GetUrlToLog(urlString);
-
- LoggerUtils.LogRequest(_logger, urlToLog, httpReq.HttpMethod, httpReq.UserAgent, logHeaders ? httpReq.Headers : null);
- }
+ urlToLog = GetUrlToLog(urlString);
+ Logger.LogDebug("HTTP {HttpMethod} {Url} UserAgent: {UserAgent} \nHeaders: {@Headers}", urlToLog, httpReq.UserAgent ?? string.Empty, httpReq.HttpMethod, httpReq.Headers);
if (string.Equals(localPath, "/emby/", StringComparison.OrdinalIgnoreCase) ||
string.Equals(localPath, "/mediabrowser/", StringComparison.OrdinalIgnoreCase))
@@ -515,6 +482,7 @@ namespace Emby.Server.Implementations.HttpServer
RedirectToUrl(httpRes, DefaultRedirectPath);
return;
}
+
if (string.Equals(localPath, "/emby", StringComparison.OrdinalIgnoreCase) ||
string.Equals(localPath, "/mediabrowser", StringComparison.OrdinalIgnoreCase))
{
@@ -560,16 +528,19 @@ namespace Emby.Server.Implementations.HttpServer
RedirectToUrl(httpRes, DefaultRedirectPath);
return;
}
+
if (string.Equals(localPath, "/web/", StringComparison.OrdinalIgnoreCase))
{
RedirectToUrl(httpRes, "../" + DefaultRedirectPath);
return;
}
+
if (string.Equals(localPath, "/", StringComparison.OrdinalIgnoreCase))
{
RedirectToUrl(httpRes, DefaultRedirectPath);
return;
}
+
if (string.IsNullOrEmpty(localPath))
{
RedirectToUrl(httpRes, "/" + DefaultRedirectPath);
@@ -605,33 +576,21 @@ namespace Emby.Server.Implementations.HttpServer
if (handler != null)
{
- await handler.ProcessRequestAsync(this, httpReq, httpRes, Logger, operationName, cancellationToken).ConfigureAwait(false);
+ await handler.ProcessRequestAsync(this, httpReq, httpRes, Logger, httpReq.OperationName, cancellationToken).ConfigureAwait(false);
}
else
{
await ErrorHandler(new FileNotFoundException(), httpReq, false, false).ConfigureAwait(false);
}
}
- catch (OperationCanceledException ex)
- {
- await ErrorHandler(ex, httpReq, false, false).ConfigureAwait(false);
- }
-
- catch (IOException ex)
- {
- await ErrorHandler(ex, httpReq, false, false).ConfigureAwait(false);
- }
-
- catch (SocketException ex)
+ catch (Exception ex) when (ex is SocketException || ex is IOException || ex is OperationCanceledException)
{
await ErrorHandler(ex, httpReq, false, false).ConfigureAwait(false);
}
-
catch (SecurityException ex)
{
await ErrorHandler(ex, httpReq, false, true).ConfigureAwait(false);
}
-
catch (Exception ex)
{
var logException = !string.Equals(ex.GetType().Name, "SocketException", StringComparison.OrdinalIgnoreCase);
@@ -642,13 +601,15 @@ namespace Emby.Server.Implementations.HttpServer
{
httpRes.Close();
- if (enableLog)
+ stopWatch.Stop();
+ var elapsed = stopWatch.Elapsed;
+ if (elapsed.TotalMilliseconds > 500)
{
- var statusCode = httpRes.StatusCode;
-
- var duration = DateTime.Now - date;
-
- LoggerUtils.LogResponse(_logger, statusCode, urlToLog, remoteIp, duration, logHeaders ? httpRes.Headers : null);
+ _logger.LogWarning("HTTP Response {StatusCode} to {RemoteIp}. Time (slow): {Elapsed:g}. {Url}", httpRes.StatusCode, remoteIp, elapsed, urlToLog);
+ }
+ else
+ {
+ _logger.LogDebug("HTTP Response {StatusCode} to {RemoteIp}. Time: {Elapsed:g}. {Url}", httpRes.StatusCode, remoteIp, elapsed, urlToLog);
}
}
}
@@ -661,12 +622,11 @@ namespace Emby.Server.Implementations.HttpServer
var pathParts = pathInfo.TrimStart('/').Split('/');
if (pathParts.Length == 0)
{
- _logger.LogError("Path parts empty for PathInfo: {pathInfo}, Url: {RawUrl}", pathInfo, httpReq.RawUrl);
+ _logger.LogError("Path parts empty for PathInfo: {PathInfo}, Url: {RawUrl}", pathInfo, httpReq.RawUrl);
return null;
}
var restPath = ServiceHandler.FindMatchingRestPath(httpReq.HttpMethod, pathInfo, out string contentType);
-
if (restPath != null)
{
return new ServiceHandler
diff --git a/Emby.Server.Implementations/HttpServer/HttpResultFactory.cs b/Emby.Server.Implementations/HttpServer/HttpResultFactory.cs
index 7445fd3c2..070717d48 100644
--- a/Emby.Server.Implementations/HttpServer/HttpResultFactory.cs
+++ b/Emby.Server.Implementations/HttpServer/HttpResultFactory.cs
@@ -90,7 +90,7 @@ namespace Emby.Server.Implementations.HttpServer
/// </summary>
private IHasHeaders GetHttpResult(IRequest requestContext, Stream content, string contentType, bool addCachePrevention, IDictionary<string, string> responseHeaders = null)
{
- var result = new StreamWriter(content, contentType, _logger);
+ var result = new StreamWriter(content, contentType);
if (responseHeaders == null)
{
@@ -131,7 +131,7 @@ namespace Emby.Server.Implementations.HttpServer
content = Array.Empty<byte>();
}
- result = new StreamWriter(content, contentType, contentLength, _logger);
+ result = new StreamWriter(content, contentType, contentLength);
}
else
{
@@ -143,7 +143,7 @@ namespace Emby.Server.Implementations.HttpServer
responseHeaders = new Dictionary<string, string>();
}
- if (addCachePrevention && !responseHeaders.TryGetValue("Expires", out string expires))
+ if (addCachePrevention && !responseHeaders.TryGetValue("Expires", out string _))
{
responseHeaders["Expires"] = "-1";
}
@@ -175,7 +175,7 @@ namespace Emby.Server.Implementations.HttpServer
bytes = Array.Empty<byte>();
}
- result = new StreamWriter(bytes, contentType, contentLength, _logger);
+ result = new StreamWriter(bytes, contentType, contentLength);
}
else
{
@@ -187,7 +187,7 @@ namespace Emby.Server.Implementations.HttpServer
responseHeaders = new Dictionary<string, string>();
}
- if (addCachePrevention && !responseHeaders.TryGetValue("Expires", out string expires))
+ if (addCachePrevention && !responseHeaders.TryGetValue("Expires", out string _))
{
responseHeaders["Expires"] = "-1";
}
@@ -277,9 +277,10 @@ namespace Emby.Server.Implementations.HttpServer
private object ToOptimizedResultInternal<T>(IRequest request, T dto, IDictionary<string, string> responseHeaders = null)
{
- var contentType = request.ResponseContentType;
+ // TODO: @bond use Span and .Equals
+ var contentType = request.ResponseContentType?.Split(';')[0].Trim().ToLowerInvariant();
- switch (GetRealContentType(contentType))
+ switch (contentType)
{
case "application/xml":
case "text/xml":
@@ -333,13 +334,13 @@ namespace Emby.Server.Implementations.HttpServer
if (isHeadRequest)
{
- var result = new StreamWriter(Array.Empty<byte>(), contentType, contentLength, _logger);
+ var result = new StreamWriter(Array.Empty<byte>(), contentType, contentLength);
AddResponseHeaders(result, responseHeaders);
return result;
}
else
{
- var result = new StreamWriter(content, contentType, contentLength, _logger);
+ var result = new StreamWriter(content, contentType, contentLength);
AddResponseHeaders(result, responseHeaders);
return result;
}
@@ -348,13 +349,19 @@ namespace Emby.Server.Implementations.HttpServer
private byte[] Compress(byte[] bytes, string compressionType)
{
if (string.Equals(compressionType, "br", StringComparison.OrdinalIgnoreCase))
+ {
return CompressBrotli(bytes);
+ }
if (string.Equals(compressionType, "deflate", StringComparison.OrdinalIgnoreCase))
+ {
return Deflate(bytes);
+ }
if (string.Equals(compressionType, "gzip", StringComparison.OrdinalIgnoreCase))
+ {
return GZip(bytes);
+ }
throw new NotSupportedException(compressionType);
}
@@ -390,13 +397,6 @@ namespace Emby.Server.Implementations.HttpServer
}
}
- public static string GetRealContentType(string contentType)
- {
- return contentType == null
- ? null
- : contentType.Split(';')[0].ToLowerInvariant().Trim();
- }
-
private static string SerializeToXmlString(object from)
{
using (var ms = new MemoryStream())
@@ -422,18 +422,20 @@ namespace Emby.Server.Implementations.HttpServer
/// <summary>
/// Pres the process optimized result.
/// </summary>
- private object GetCachedResult(IRequest requestContext, IDictionary<string, string> responseHeaders, Guid cacheKey, string cacheKeyString, DateTime? lastDateModified, TimeSpan? cacheDuration, string contentType)
+ private object GetCachedResult(IRequest requestContext, IDictionary<string, string> responseHeaders, StaticResultOptions options)
{
bool noCache = (requestContext.Headers.Get("Cache-Control") ?? string.Empty).IndexOf("no-cache", StringComparison.OrdinalIgnoreCase) != -1;
+ AddCachingHeaders(responseHeaders, options.CacheDuration, noCache, options.DateLastModified);
if (!noCache)
{
- if (IsNotModified(requestContext, cacheKey))
+ DateTime.TryParse(requestContext.Headers.Get("If-Modified-Since"), out var ifModifiedSinceHeader);
+
+ if (IsNotModified(ifModifiedSinceHeader, options.CacheDuration, options.DateLastModified))
{
- AddAgeHeader(responseHeaders, lastDateModified);
- AddExpiresHeader(responseHeaders, cacheKeyString, cacheDuration);
+ AddAgeHeader(responseHeaders, options.DateLastModified);
- var result = new HttpResult(Array.Empty<byte>(), contentType ?? "text/html", HttpStatusCode.NotModified);
+ var result = new HttpResult(Array.Empty<byte>(), options.ContentType ?? "text/html", HttpStatusCode.NotModified);
AddResponseHeaders(result, responseHeaders);
@@ -441,8 +443,6 @@ namespace Emby.Server.Implementations.HttpServer
}
}
- AddCachingHeaders(responseHeaders, cacheKeyString, cacheDuration);
-
return null;
}
@@ -487,9 +487,6 @@ namespace Emby.Server.Implementations.HttpServer
options.DateLastModified = _fileSystem.GetLastWriteTimeUtc(path);
}
- var cacheKey = path + options.DateLastModified.Value.Ticks;
-
- options.CacheKey = cacheKey.GetMD5();
options.ContentFactory = () => Task.FromResult(GetFileStream(path, fileShare));
options.ResponseHeaders = options.ResponseHeaders ?? new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
@@ -520,7 +517,6 @@ namespace Emby.Server.Implementations.HttpServer
return GetStaticResult(requestContext, new StaticResultOptions
{
CacheDuration = cacheDuration,
- CacheKey = cacheKey,
ContentFactory = factoryFn,
ContentType = contentType,
DateLastModified = lastDateModified,
@@ -534,14 +530,10 @@ namespace Emby.Server.Implementations.HttpServer
options.ResponseHeaders = options.ResponseHeaders ?? new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
var contentType = options.ContentType;
- var etag = requestContext.Headers.Get("If-None-Match");
- var cacheKey = etag != null ? new Guid(etag.Trim('\"')) : Guid.Empty;
- if (!cacheKey.Equals(Guid.Empty))
+ if (!string.IsNullOrEmpty(requestContext.Headers.Get("If-Modified-Since")))
{
- var key = cacheKey.ToString("N");
-
// See if the result is already cached in the browser
- var result = GetCachedResult(requestContext, options.ResponseHeaders, cacheKey, key, options.DateLastModified, options.CacheDuration, contentType);
+ var result = GetCachedResult(requestContext, options.ResponseHeaders, options);
if (result != null)
{
@@ -553,6 +545,8 @@ namespace Emby.Server.Implementations.HttpServer
var isHeadRequest = options.IsHeadRequest || string.Equals(requestContext.Verb, "HEAD", StringComparison.OrdinalIgnoreCase);
var factoryFn = options.ContentFactory;
var responseHeaders = options.ResponseHeaders;
+ AddCachingHeaders(responseHeaders, options.CacheDuration, false, options.DateLastModified);
+ AddAgeHeader(responseHeaders, options.DateLastModified);
var rangeHeader = requestContext.Headers.Get("Range");
@@ -566,21 +560,10 @@ namespace Emby.Server.Implementations.HttpServer
};
AddResponseHeaders(hasHeaders, options.ResponseHeaders);
- // Generate an ETag based on identifying information - TODO read contents from filesystem instead?
- var responseId = $"{hasHeaders.ContentType}{options.Path}{hasHeaders.TotalContentLength}";
- var hashedId = MD5.Create().ComputeHash(Encoding.Default.GetBytes(responseId));
- hasHeaders.Headers["ETag"] = new Guid(hashedId).ToString("N");
-
return hasHeaders;
}
var stream = await factoryFn().ConfigureAwait(false);
- // Generate an etag based on stream content
- var streamHash = MD5.Create().ComputeHash(stream);
- var newEtag = new Guid(streamHash).ToString("N");
-
- // reset position so the response can re-use it -- TODO is this ok?
- stream.Position = 0;
var totalContentLength = options.ContentLength;
if (!totalContentLength.HasValue)
@@ -603,7 +586,6 @@ namespace Emby.Server.Implementations.HttpServer
};
AddResponseHeaders(hasHeaders, options.ResponseHeaders);
- hasHeaders.Headers["ETag"] = newEtag;
return hasHeaders;
}
else
@@ -621,14 +603,13 @@ namespace Emby.Server.Implementations.HttpServer
}
}
- var hasHeaders = new StreamWriter(stream, contentType, _logger)
+ var hasHeaders = new StreamWriter(stream, contentType)
{
OnComplete = options.OnComplete,
OnError = options.OnError
};
AddResponseHeaders(hasHeaders, options.ResponseHeaders);
- hasHeaders.Headers["ETag"] = newEtag;
return hasHeaders;
}
}
@@ -641,37 +622,28 @@ namespace Emby.Server.Implementations.HttpServer
/// <summary>
/// Adds the caching responseHeaders.
/// </summary>
- private void AddCachingHeaders(IDictionary<string, string> responseHeaders, string cacheKey, TimeSpan? cacheDuration)
+ private void AddCachingHeaders(IDictionary<string, string> responseHeaders, TimeSpan? cacheDuration,
+ bool noCache, DateTime? lastModifiedDate)
{
- if (cacheDuration.HasValue)
- {
- responseHeaders["Cache-Control"] = "public, max-age=" + Convert.ToInt32(cacheDuration.Value.TotalSeconds);
- }
- else if (!string.IsNullOrEmpty(cacheKey))
- {
- responseHeaders["Cache-Control"] = "public";
- }
- else
+ if (noCache)
{
responseHeaders["Cache-Control"] = "no-cache, no-store, must-revalidate";
responseHeaders["pragma"] = "no-cache, no-store, must-revalidate";
+ return;
}
- AddExpiresHeader(responseHeaders, cacheKey, cacheDuration);
- }
-
- /// <summary>
- /// Adds the expires header.
- /// </summary>
- private static void AddExpiresHeader(IDictionary<string, string> responseHeaders, string cacheKey, TimeSpan? cacheDuration)
- {
if (cacheDuration.HasValue)
{
- responseHeaders["Expires"] = DateTime.UtcNow.Add(cacheDuration.Value).ToString("r");
+ responseHeaders["Cache-Control"] = "public, max-age=" + cacheDuration.Value.TotalSeconds;
}
- else if (string.IsNullOrEmpty(cacheKey))
+ else
{
- responseHeaders["Expires"] = "-1";
+ responseHeaders["Cache-Control"] = "public";
+ }
+
+ if (lastModifiedDate.HasValue)
+ {
+ responseHeaders["Last-Modified"] = lastModifiedDate.ToString();
}
}
@@ -687,32 +659,6 @@ namespace Emby.Server.Implementations.HttpServer
responseHeaders["Age"] = Convert.ToInt64((DateTime.UtcNow - lastDateModified.Value).TotalSeconds).ToString(CultureInfo.InvariantCulture);
}
}
- /// <summary>
- /// Determines whether [is not modified] [the specified cache key].
- /// </summary>
- /// <param name="requestContext">The request context.</param>
- /// <param name="cacheKey">The cache key.</param>
- /// <param name="lastDateModified">The last date modified.</param>
- /// <param name="cacheDuration">Duration of the cache.</param>
- /// <returns><c>true</c> if [is not modified] [the specified cache key]; otherwise, <c>false</c>.</returns>
- private bool IsNotModified(IRequest requestContext, Guid cacheKey)
- {
- var ifNoneMatchHeader = requestContext.Headers.Get("If-None-Match");
-
- bool hasCacheKey = !cacheKey.Equals(Guid.Empty);
-
- // Validate If-None-Match
- if (hasCacheKey && !string.IsNullOrEmpty(ifNoneMatchHeader))
- {
- if (Guid.TryParse(ifNoneMatchHeader, out var ifNoneMatch)
- && cacheKey.Equals(ifNoneMatch))
- {
- return true;
- }
- }
-
- return false;
- }
/// <summary>
/// Determines whether [is not modified] [the specified if modified since].
diff --git a/Emby.Server.Implementations/HttpServer/LoggerUtils.cs b/Emby.Server.Implementations/HttpServer/LoggerUtils.cs
deleted file mode 100644
index d22d9db26..000000000
--- a/Emby.Server.Implementations/HttpServer/LoggerUtils.cs
+++ /dev/null
@@ -1,55 +0,0 @@
-using System;
-using System.Globalization;
-using MediaBrowser.Model.Services;
-using Microsoft.Extensions.Logging;
-
-namespace Emby.Server.Implementations.HttpServer
-{
- public static class LoggerUtils
- {
- public static void LogRequest(ILogger logger, string url, string method, string userAgent, QueryParamCollection headers)
- {
- if (headers == null)
- {
- logger.LogInformation("{0} {1}. UserAgent: {2}", "HTTP " + method, url, userAgent ?? string.Empty);
- }
- else
- {
- var headerText = string.Empty;
- var index = 0;
-
- foreach (var i in headers)
- {
- if (index > 0)
- {
- headerText += ", ";
- }
-
- headerText += i.Name + "=" + i.Value;
-
- index++;
- }
-
- logger.LogInformation("HTTP {0} {1}. {2}", method, url, headerText);
- }
- }
-
- /// <summary>
- /// Logs the response.
- /// </summary>
- /// <param name="logger">The logger.</param>
- /// <param name="statusCode">The status code.</param>
- /// <param name="url">The URL.</param>
- /// <param name="endPoint">The end point.</param>
- /// <param name="duration">The duration.</param>
- public static void LogResponse(ILogger logger, int statusCode, string url, string endPoint, TimeSpan duration, QueryParamCollection headers)
- {
- var durationMs = duration.TotalMilliseconds;
- var logSuffix = durationMs >= 1000 && durationMs < 60000 ? "ms (slow)" : "ms";
-
- //var headerText = headers == null ? string.Empty : "Headers: " + string.Join(", ", headers.Where(i => i.Name.IndexOf("Access-", StringComparison.OrdinalIgnoreCase) == -1).Select(i => i.Name + "=" + i.Value).ToArray());
- var headerText = string.Empty;
- logger.LogInformation("HTTP Response {0} to {1}. Time: {2}{3}. {4} {5}", statusCode, endPoint, Convert.ToInt32(durationMs).ToString(CultureInfo.InvariantCulture), logSuffix, url, headerText);
- }
- }
-}
diff --git a/Emby.Server.Implementations/HttpServer/StreamWriter.cs b/Emby.Server.Implementations/HttpServer/StreamWriter.cs
index 3269d44cf..cb2e3580b 100644
--- a/Emby.Server.Implementations/HttpServer/StreamWriter.cs
+++ b/Emby.Server.Implementations/HttpServer/StreamWriter.cs
@@ -14,8 +14,6 @@ namespace Emby.Server.Implementations.HttpServer
/// </summary>
public class StreamWriter : IAsyncStreamWriter, IHasHeaders
{
- private ILogger Logger { get; set; }
-
private static readonly CultureInfo UsCulture = new CultureInfo("en-US");
/// <summary>
@@ -45,7 +43,7 @@ namespace Emby.Server.Implementations.HttpServer
/// <param name="source">The source.</param>
/// <param name="contentType">Type of the content.</param>
/// <param name="logger">The logger.</param>
- public StreamWriter(Stream source, string contentType, ILogger logger)
+ public StreamWriter(Stream source, string contentType)
{
if (string.IsNullOrEmpty(contentType))
{
@@ -53,7 +51,6 @@ namespace Emby.Server.Implementations.HttpServer
}
SourceStream = source;
- Logger = logger;
Headers["Content-Type"] = contentType;
@@ -69,7 +66,7 @@ namespace Emby.Server.Implementations.HttpServer
/// <param name="source">The source.</param>
/// <param name="contentType">Type of the content.</param>
/// <param name="logger">The logger.</param>
- public StreamWriter(byte[] source, string contentType, int contentLength, ILogger logger)
+ public StreamWriter(byte[] source, string contentType, int contentLength)
{
if (string.IsNullOrEmpty(contentType))
{
@@ -77,7 +74,6 @@ namespace Emby.Server.Implementations.HttpServer
}
SourceBytes = source;
- Logger = logger;
Headers["Content-Type"] = contentType;
diff --git a/Emby.Server.Implementations/IO/FileRefresher.cs b/Emby.Server.Implementations/IO/FileRefresher.cs
index 3668f6a7a..73242d0ad 100644
--- a/Emby.Server.Implementations/IO/FileRefresher.cs
+++ b/Emby.Server.Implementations/IO/FileRefresher.cs
@@ -17,31 +17,23 @@ namespace Emby.Server.Implementations.IO
public class FileRefresher : IDisposable
{
private ILogger Logger { get; set; }
- private ITaskManager TaskManager { get; set; }
private ILibraryManager LibraryManager { get; set; }
private IServerConfigurationManager ConfigurationManager { get; set; }
- private readonly IFileSystem _fileSystem;
private readonly List<string> _affectedPaths = new List<string>();
private Timer _timer;
private readonly object _timerLock = new object();
public string Path { get; private set; }
public event EventHandler<EventArgs> Completed;
- private readonly IEnvironmentInfo _environmentInfo;
- private readonly ILibraryManager _libraryManager;
- public FileRefresher(string path, IFileSystem fileSystem, IServerConfigurationManager configurationManager, ILibraryManager libraryManager, ITaskManager taskManager, ILogger logger, IEnvironmentInfo environmentInfo, ILibraryManager libraryManager1)
+ public FileRefresher(string path, IServerConfigurationManager configurationManager, ILibraryManager libraryManager, ILogger logger)
{
logger.LogDebug("New file refresher created for {0}", path);
Path = path;
- _fileSystem = fileSystem;
ConfigurationManager = configurationManager;
LibraryManager = libraryManager;
- TaskManager = taskManager;
Logger = logger;
- _environmentInfo = environmentInfo;
- _libraryManager = libraryManager1;
AddPath(path);
}
diff --git a/Emby.Server.Implementations/IO/LibraryMonitor.cs b/Emby.Server.Implementations/IO/LibraryMonitor.cs
index 607a4d333..d47342511 100644
--- a/Emby.Server.Implementations/IO/LibraryMonitor.cs
+++ b/Emby.Server.Implementations/IO/LibraryMonitor.cs
@@ -34,7 +34,7 @@ namespace Emby.Server.Implementations.IO
/// <summary>
/// Any file name ending in any of these will be ignored by the watchers
/// </summary>
- private readonly HashSet<string> _alwaysIgnoreFiles = new HashSet<string>(StringComparer.OrdinalIgnoreCase)
+ private static readonly HashSet<string> _alwaysIgnoreFiles = new HashSet<string>(StringComparer.OrdinalIgnoreCase)
{
"small.jpg",
"albumart.jpg",
@@ -44,7 +44,7 @@ namespace Emby.Server.Implementations.IO
"TempSBE"
};
- private readonly string[] _alwaysIgnoreSubstrings = new string[]
+ private static readonly string[] _alwaysIgnoreSubstrings = new string[]
{
// Synology
"eaDir",
@@ -53,7 +53,7 @@ namespace Emby.Server.Implementations.IO
".actors"
};
- private readonly HashSet<string> _alwaysIgnoreExtensions = new HashSet<string>(StringComparer.OrdinalIgnoreCase)
+ private static readonly HashSet<string> _alwaysIgnoreExtensions = new HashSet<string>(StringComparer.OrdinalIgnoreCase)
{
// thumbs.db
".db",
@@ -123,12 +123,6 @@ namespace Emby.Server.Implementations.IO
/// <value>The logger.</value>
private ILogger Logger { get; set; }
- /// <summary>
- /// Gets or sets the task manager.
- /// </summary>
- /// <value>The task manager.</value>
- private ITaskManager TaskManager { get; set; }
-
private ILibraryManager LibraryManager { get; set; }
private IServerConfigurationManager ConfigurationManager { get; set; }
@@ -140,19 +134,12 @@ namespace Emby.Server.Implementations.IO
/// </summary>
public LibraryMonitor(
ILoggerFactory loggerFactory,
- ITaskManager taskManager,
ILibraryManager libraryManager,
IServerConfigurationManager configurationManager,
IFileSystem fileSystem,
IEnvironmentInfo environmentInfo)
{
- if (taskManager == null)
- {
- throw new ArgumentNullException(nameof(taskManager));
- }
-
LibraryManager = libraryManager;
- TaskManager = taskManager;
Logger = loggerFactory.CreateLogger(GetType().Name);
ConfigurationManager = configurationManager;
_fileSystem = fileSystem;
@@ -541,7 +528,7 @@ namespace Emby.Server.Implementations.IO
}
}
- var newRefresher = new FileRefresher(path, _fileSystem, ConfigurationManager, LibraryManager, TaskManager, Logger, _environmentInfo, LibraryManager);
+ var newRefresher = new FileRefresher(path, ConfigurationManager, LibraryManager, Logger);
newRefresher.Completed += NewRefresher_Completed;
_activeRefreshers.Add(newRefresher);
}
diff --git a/Emby.Server.Implementations/IO/ManagedFileSystem.cs b/Emby.Server.Implementations/IO/ManagedFileSystem.cs
index 7c44878ec..a64dfb607 100644
--- a/Emby.Server.Implementations/IO/ManagedFileSystem.cs
+++ b/Emby.Server.Implementations/IO/ManagedFileSystem.cs
@@ -4,8 +4,10 @@ using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text;
+using MediaBrowser.Common.Configuration;
using MediaBrowser.Model.IO;
using MediaBrowser.Model.System;
+using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
namespace Emby.Server.Implementations.IO
@@ -20,61 +22,27 @@ namespace Emby.Server.Implementations.IO
private readonly bool _supportsAsyncFileStreams;
private char[] _invalidFileNameChars;
private readonly List<IShortcutHandler> _shortcutHandlers = new List<IShortcutHandler>();
- private bool EnableSeparateFileAndDirectoryQueries;
- private string _tempPath;
+ private readonly string _tempPath;
- private IEnvironmentInfo _environmentInfo;
- private bool _isEnvironmentCaseInsensitive;
-
- private string _defaultDirectory;
+ private readonly IEnvironmentInfo _environmentInfo;
+ private readonly bool _isEnvironmentCaseInsensitive;
public ManagedFileSystem(
ILoggerFactory loggerFactory,
IEnvironmentInfo environmentInfo,
- string defaultDirectory,
- string tempPath,
- bool enableSeparateFileAndDirectoryQueries)
+ IApplicationPaths applicationPaths)
{
Logger = loggerFactory.CreateLogger("FileSystem");
_supportsAsyncFileStreams = true;
- _tempPath = tempPath;
+ _tempPath = applicationPaths.TempDirectory;
_environmentInfo = environmentInfo;
- _defaultDirectory = defaultDirectory;
-
- // On Linux with mono, this needs to be true or symbolic links are ignored
- EnableSeparateFileAndDirectoryQueries = enableSeparateFileAndDirectoryQueries;
SetInvalidFileNameChars(environmentInfo.OperatingSystem == MediaBrowser.Model.System.OperatingSystem.Windows);
_isEnvironmentCaseInsensitive = environmentInfo.OperatingSystem == MediaBrowser.Model.System.OperatingSystem.Windows;
}
- public virtual string DefaultDirectory
- {
- get
- {
- var value = _defaultDirectory;
-
- if (!string.IsNullOrEmpty(value))
- {
- try
- {
- if (Directory.Exists(value))
- {
- return value;
- }
- }
- catch
- {
-
- }
- }
-
- return null;
- }
- }
-
public virtual void AddShortcutHandler(IShortcutHandler handler)
{
_shortcutHandlers.Add(handler);
@@ -718,7 +686,7 @@ namespace Emby.Server.Implementations.IO
SetAttributes(path, false, false);
File.Delete(path);
}
-
+
public virtual List<FileSystemMetadata> GetDrives()
{
// Only include drives in the ready state or this method could end up being very slow, waiting for drives to timeout
@@ -777,20 +745,15 @@ namespace Emby.Server.Implementations.IO
var directoryInfo = new DirectoryInfo(path);
var searchOption = recursive ? SearchOption.AllDirectories : SearchOption.TopDirectoryOnly;
- if (EnableSeparateFileAndDirectoryQueries)
- {
- return ToMetadata(directoryInfo.EnumerateDirectories("*", searchOption))
- .Concat(ToMetadata(directoryInfo.EnumerateFiles("*", searchOption)));
- }
-
- return ToMetadata(directoryInfo.EnumerateFileSystemInfos("*", searchOption));
+ return ToMetadata(directoryInfo.EnumerateDirectories("*", searchOption))
+ .Concat(ToMetadata(directoryInfo.EnumerateFiles("*", searchOption)));
}
private IEnumerable<FileSystemMetadata> ToMetadata(IEnumerable<FileSystemInfo> infos)
{
return infos.Select(GetFileSystemMetadata);
}
-
+
public virtual IEnumerable<string> GetDirectoryPaths(string path, bool recursive = false)
{
var searchOption = recursive ? SearchOption.AllDirectories : SearchOption.TopDirectoryOnly;
diff --git a/Emby.Server.Implementations/Library/CoreResolutionIgnoreRule.cs b/Emby.Server.Implementations/Library/CoreResolutionIgnoreRule.cs
index 80f746c7a..c644d13ea 100644
--- a/Emby.Server.Implementations/Library/CoreResolutionIgnoreRule.cs
+++ b/Emby.Server.Implementations/Library/CoreResolutionIgnoreRule.cs
@@ -1,5 +1,4 @@
using System;
-using System.Collections.Generic;
using System.IO;
using System.Linq;
using MediaBrowser.Controller.Entities;
@@ -7,7 +6,6 @@ using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Resolvers;
using MediaBrowser.Model.Extensions;
using MediaBrowser.Model.IO;
-using Microsoft.Extensions.Logging;
namespace Emby.Server.Implementations.Library
{
@@ -16,16 +14,14 @@ namespace Emby.Server.Implementations.Library
/// </summary>
public class CoreResolutionIgnoreRule : IResolverIgnoreRule
{
- private readonly IFileSystem _fileSystem;
private readonly ILibraryManager _libraryManager;
- private readonly ILogger _logger;
private bool _ignoreDotPrefix;
/// <summary>
/// Any folder named in this list will be ignored - can be added to at runtime for extensibility
/// </summary>
- public static readonly Dictionary<string, string> IgnoreFolders = new List<string>
+ public static readonly string[] IgnoreFolders =
{
"metadata",
"ps3_update",
@@ -50,13 +46,11 @@ namespace Emby.Server.Implementations.Library
// macos
".AppleDouble"
- }.ToDictionary(i => i, StringComparer.OrdinalIgnoreCase);
+ };
- public CoreResolutionIgnoreRule(IFileSystem fileSystem, ILibraryManager libraryManager, ILogger logger)
+ public CoreResolutionIgnoreRule(ILibraryManager libraryManager)
{
- _fileSystem = fileSystem;
_libraryManager = libraryManager;
- _logger = logger;
_ignoreDotPrefix = Environment.OSVersion.Platform != PlatformID.Win32NT;
}
@@ -117,7 +111,7 @@ namespace Emby.Server.Implementations.Library
if (fileInfo.IsDirectory)
{
// Ignore any folders in our list
- if (IgnoreFolders.ContainsKey(filename))
+ if (IgnoreFolders.Contains(filename, StringComparer.OrdinalIgnoreCase))
{
return true;
}
diff --git a/Emby.Server.Implementations/Library/LibraryManager.cs b/Emby.Server.Implementations/Library/LibraryManager.cs
index 064006ebd..3c2272b56 100644
--- a/Emby.Server.Implementations/Library/LibraryManager.cs
+++ b/Emby.Server.Implementations/Library/LibraryManager.cs
@@ -986,7 +986,7 @@ namespace Emby.Server.Implementations.Library
// Ensure the location is available.
Directory.CreateDirectory(ConfigurationManager.ApplicationPaths.PeoplePath);
- return new PeopleValidator(this, _logger, ConfigurationManager, _fileSystem).ValidatePeople(cancellationToken, progress);
+ return new PeopleValidator(this, _logger, _fileSystem).ValidatePeople(cancellationToken, progress);
}
/// <summary>
@@ -1225,9 +1225,9 @@ namespace Emby.Server.Implementations.Library
/// <exception cref="ArgumentNullException">id</exception>
public BaseItem GetItemById(Guid id)
{
- if (id.Equals(Guid.Empty))
+ if (id == Guid.Empty)
{
- throw new ArgumentNullException(nameof(id));
+ throw new ArgumentException(nameof(id), "Guid can't be empty");
}
if (LibraryItemsCache.TryGetValue(id, out BaseItem item))
@@ -1237,8 +1237,6 @@ namespace Emby.Server.Implementations.Library
item = RetrieveItem(id);
- //_logger.LogDebug("GetitemById {0}", id);
-
if (item != null)
{
RegisterItem(item);
@@ -1333,7 +1331,7 @@ namespace Emby.Server.Implementations.Library
return ItemRepository.GetItemIdsList(query);
}
- public QueryResult<Tuple<BaseItem, ItemCounts>> GetStudios(InternalItemsQuery query)
+ public QueryResult<(BaseItem, ItemCounts)> GetStudios(InternalItemsQuery query)
{
if (query.User != null)
{
@@ -1344,7 +1342,7 @@ namespace Emby.Server.Implementations.Library
return ItemRepository.GetStudios(query);
}
- public QueryResult<Tuple<BaseItem, ItemCounts>> GetGenres(InternalItemsQuery query)
+ public QueryResult<(BaseItem, ItemCounts)> GetGenres(InternalItemsQuery query)
{
if (query.User != null)
{
@@ -1355,7 +1353,7 @@ namespace Emby.Server.Implementations.Library
return ItemRepository.GetGenres(query);
}
- public QueryResult<Tuple<BaseItem, ItemCounts>> GetMusicGenres(InternalItemsQuery query)
+ public QueryResult<(BaseItem, ItemCounts)> GetMusicGenres(InternalItemsQuery query)
{
if (query.User != null)
{
@@ -1366,7 +1364,7 @@ namespace Emby.Server.Implementations.Library
return ItemRepository.GetMusicGenres(query);
}
- public QueryResult<Tuple<BaseItem, ItemCounts>> GetAllArtists(InternalItemsQuery query)
+ public QueryResult<(BaseItem, ItemCounts)> GetAllArtists(InternalItemsQuery query)
{
if (query.User != null)
{
@@ -1377,7 +1375,7 @@ namespace Emby.Server.Implementations.Library
return ItemRepository.GetAllArtists(query);
}
- public QueryResult<Tuple<BaseItem, ItemCounts>> GetArtists(InternalItemsQuery query)
+ public QueryResult<(BaseItem, ItemCounts)> GetArtists(InternalItemsQuery query)
{
if (query.User != null)
{
@@ -1421,7 +1419,7 @@ namespace Emby.Server.Implementations.Library
}
}
- public QueryResult<Tuple<BaseItem, ItemCounts>> GetAlbumArtists(InternalItemsQuery query)
+ public QueryResult<(BaseItem, ItemCounts)> GetAlbumArtists(InternalItemsQuery query)
{
if (query.User != null)
{
@@ -1808,18 +1806,16 @@ namespace Emby.Server.Implementations.Library
/// <returns>Task.</returns>
public void CreateItems(IEnumerable<BaseItem> items, BaseItem parent, CancellationToken cancellationToken)
{
- var list = items.ToList();
-
- ItemRepository.SaveItems(list, cancellationToken);
+ ItemRepository.SaveItems(items, cancellationToken);
- foreach (var item in list)
+ foreach (var item in items)
{
RegisterItem(item);
}
if (ItemAdded != null)
{
- foreach (var item in list)
+ foreach (var item in items)
{
// With the live tv guide this just creates too much noise
if (item.SourceType != SourceType.Library)
@@ -1853,7 +1849,7 @@ namespace Emby.Server.Implementations.Library
/// <summary>
/// Updates the item.
/// </summary>
- public void UpdateItems(List<BaseItem> items, BaseItem parent, ItemUpdateType updateReason, CancellationToken cancellationToken)
+ public void UpdateItems(IEnumerable<BaseItem> items, BaseItem parent, ItemUpdateType updateReason, CancellationToken cancellationToken)
{
foreach (var item in items)
{
@@ -1908,7 +1904,7 @@ namespace Emby.Server.Implementations.Library
/// <returns>Task.</returns>
public void UpdateItem(BaseItem item, BaseItem parent, ItemUpdateType updateReason, CancellationToken cancellationToken)
{
- UpdateItems(new List<BaseItem> { item }, parent, updateReason, cancellationToken);
+ UpdateItems(new [] { item }, parent, updateReason, cancellationToken);
}
/// <summary>
@@ -2005,9 +2001,7 @@ namespace Emby.Server.Implementations.Library
.FirstOrDefault();
}
- var options = collectionFolder == null ? new LibraryOptions() : collectionFolder.GetLibraryOptions();
-
- return options;
+ return collectionFolder == null ? new LibraryOptions() : collectionFolder.GetLibraryOptions();
}
public string GetContentType(BaseItem item)
@@ -2017,11 +2011,13 @@ namespace Emby.Server.Implementations.Library
{
return configuredContentType;
}
+
configuredContentType = GetConfiguredContentType(item, true);
if (!string.IsNullOrEmpty(configuredContentType))
{
return configuredContentType;
}
+
return GetInheritedContentType(item);
}
@@ -2056,6 +2052,7 @@ namespace Emby.Server.Implementations.Library
{
return collectionFolder.CollectionType;
}
+
return GetContentTypeOverride(item.ContainingFolderPath, inheritConfiguredPath);
}
@@ -2066,6 +2063,7 @@ namespace Emby.Server.Implementations.Library
{
return nameValuePair.Value;
}
+
return null;
}
@@ -2108,9 +2106,9 @@ namespace Emby.Server.Implementations.Library
string viewType,
string sortName)
{
- var path = Path.Combine(ConfigurationManager.ApplicationPaths.InternalMetadataPath, "views");
-
- path = Path.Combine(path, _fileSystem.GetValidFilename(viewType));
+ var path = Path.Combine(ConfigurationManager.ApplicationPaths.InternalMetadataPath,
+ "views",
+ _fileSystem.GetValidFilename(viewType));
var id = GetNewItemId(path + "_namedview_" + name, typeof(UserView));
@@ -2543,7 +2541,7 @@ namespace Emby.Server.Implementations.Library
var resolvers = new IItemResolver[]
{
- new GenericVideoResolver<Trailer>(this, _fileSystem)
+ new GenericVideoResolver<Trailer>(this)
};
return ResolvePaths(files, directoryService, null, new LibraryOptions(), null, resolvers)
diff --git a/Emby.Server.Implementations/Library/Resolvers/BaseVideoResolver.cs b/Emby.Server.Implementations/Library/Resolvers/BaseVideoResolver.cs
index d992f8d03..541b13cbe 100644
--- a/Emby.Server.Implementations/Library/Resolvers/BaseVideoResolver.cs
+++ b/Emby.Server.Implementations/Library/Resolvers/BaseVideoResolver.cs
@@ -6,7 +6,6 @@ using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Entities;
-using MediaBrowser.Model.IO;
namespace Emby.Server.Implementations.Library.Resolvers
{
@@ -18,11 +17,9 @@ namespace Emby.Server.Implementations.Library.Resolvers
where T : Video, new()
{
protected readonly ILibraryManager LibraryManager;
- protected readonly IFileSystem FileSystem;
- protected BaseVideoResolver(ILibraryManager libraryManager, IFileSystem fileSystem)
+ protected BaseVideoResolver(ILibraryManager libraryManager)
{
- FileSystem = fileSystem;
LibraryManager = libraryManager;
}
diff --git a/Emby.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs b/Emby.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs
index 472a3f105..848563679 100644
--- a/Emby.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs
+++ b/Emby.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs
@@ -548,7 +548,8 @@ namespace Emby.Server.Implementations.Library.Resolvers.Movies
private IImageProcessor _imageProcessor;
- public MovieResolver(ILibraryManager libraryManager, IFileSystem fileSystem, IImageProcessor imageProcessor) : base(libraryManager, fileSystem)
+ public MovieResolver(ILibraryManager libraryManager, IImageProcessor imageProcessor)
+ : base(libraryManager)
{
_imageProcessor = imageProcessor;
}
diff --git a/Emby.Server.Implementations/Library/Resolvers/PhotoResolver.cs b/Emby.Server.Implementations/Library/Resolvers/PhotoResolver.cs
index a3298c580..db270c398 100644
--- a/Emby.Server.Implementations/Library/Resolvers/PhotoResolver.cs
+++ b/Emby.Server.Implementations/Library/Resolvers/PhotoResolver.cs
@@ -7,7 +7,6 @@ using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Library;
using MediaBrowser.Model.Configuration;
using MediaBrowser.Model.Entities;
-using MediaBrowser.Model.IO;
namespace Emby.Server.Implementations.Library.Resolvers
{
@@ -15,13 +14,11 @@ namespace Emby.Server.Implementations.Library.Resolvers
{
private readonly IImageProcessor _imageProcessor;
private readonly ILibraryManager _libraryManager;
- private readonly IFileSystem _fileSystem;
- public PhotoResolver(IImageProcessor imageProcessor, ILibraryManager libraryManager, IFileSystem fileSystem)
+ public PhotoResolver(IImageProcessor imageProcessor, ILibraryManager libraryManager)
{
_imageProcessor = imageProcessor;
_libraryManager = libraryManager;
- _fileSystem = fileSystem;
}
/// <summary>
@@ -113,8 +110,7 @@ namespace Emby.Server.Implementations.Library.Resolvers
return false;
}
- return imageProcessor.SupportedInputFormats.Contains((Path.GetExtension(path) ?? string.Empty).TrimStart('.'));
+ return imageProcessor.SupportedInputFormats.Contains(Path.GetExtension(path).TrimStart('.'), StringComparer.Ordinal);
}
-
}
}
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/Library/Resolvers/TV/EpisodeResolver.cs b/Emby.Server.Implementations/Library/Resolvers/TV/EpisodeResolver.cs
index fed0904d1..a6d18c9b5 100644
--- a/Emby.Server.Implementations/Library/Resolvers/TV/EpisodeResolver.cs
+++ b/Emby.Server.Implementations/Library/Resolvers/TV/EpisodeResolver.cs
@@ -3,7 +3,6 @@ using System.Linq;
using MediaBrowser.Controller.Entities.TV;
using MediaBrowser.Controller.Library;
using MediaBrowser.Model.Entities;
-using MediaBrowser.Model.IO;
namespace Emby.Server.Implementations.Library.Resolvers.TV
{
@@ -74,7 +73,8 @@ namespace Emby.Server.Implementations.Library.Resolvers.TV
return null;
}
- public EpisodeResolver(ILibraryManager libraryManager, IFileSystem fileSystem) : base(libraryManager, fileSystem)
+ public EpisodeResolver(ILibraryManager libraryManager)
+ : base(libraryManager)
{
}
}
diff --git a/Emby.Server.Implementations/Library/Resolvers/VideoResolver.cs b/Emby.Server.Implementations/Library/Resolvers/VideoResolver.cs
index 60752a85d..68d5d8b2d 100644
--- a/Emby.Server.Implementations/Library/Resolvers/VideoResolver.cs
+++ b/Emby.Server.Implementations/Library/Resolvers/VideoResolver.cs
@@ -1,13 +1,13 @@
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Library;
-using MediaBrowser.Model.IO;
namespace Emby.Server.Implementations.Library.Resolvers
{
public class GenericVideoResolver<T> : BaseVideoResolver<T>
where T : Video, new()
{
- public GenericVideoResolver(ILibraryManager libraryManager, IFileSystem fileSystem) : base(libraryManager, fileSystem)
+ public GenericVideoResolver(ILibraryManager libraryManager)
+ : base(libraryManager)
{
}
}
diff --git a/Emby.Server.Implementations/Library/UserManager.cs b/Emby.Server.Implementations/Library/UserManager.cs
index 3ff84382f..dfef8e997 100644
--- a/Emby.Server.Implementations/Library/UserManager.cs
+++ b/Emby.Server.Implementations/Library/UserManager.cs
@@ -74,7 +74,6 @@ namespace Emby.Server.Implementations.Library
private readonly Func<IDtoService> _dtoServiceFactory;
private readonly IServerApplicationHost _appHost;
private readonly IFileSystem _fileSystem;
- private readonly ICryptoProvider _cryptographyProvider;
private IAuthenticationProvider[] _authenticationProviders;
private DefaultAuthenticationProvider _defaultAuthenticationProvider;
@@ -89,8 +88,7 @@ namespace Emby.Server.Implementations.Library
Func<IDtoService> dtoServiceFactory,
IServerApplicationHost appHost,
IJsonSerializer jsonSerializer,
- IFileSystem fileSystem,
- ICryptoProvider cryptographyProvider)
+ IFileSystem fileSystem)
{
_logger = loggerFactory.CreateLogger(nameof(UserManager));
UserRepository = userRepository;
@@ -101,7 +99,6 @@ namespace Emby.Server.Implementations.Library
_appHost = appHost;
_jsonSerializer = jsonSerializer;
_fileSystem = fileSystem;
- _cryptographyProvider = cryptographyProvider;
ConfigurationManager = configurationManager;
_users = Array.Empty<User>();
@@ -171,9 +168,9 @@ namespace Emby.Server.Implementations.Library
/// <exception cref="ArgumentNullException"></exception>
public User GetUserById(Guid id)
{
- if (id.Equals(Guid.Empty))
+ if (id == Guid.Empty)
{
- throw new ArgumentNullException(nameof(id));
+ throw new ArgumentException(nameof(id), "Guid can't be empty");
}
return Users.FirstOrDefault(u => u.Id == id);
diff --git a/Emby.Server.Implementations/Library/Validators/PeopleValidator.cs b/Emby.Server.Implementations/Library/Validators/PeopleValidator.cs
index 0ea543ba0..7899cf01b 100644
--- a/Emby.Server.Implementations/Library/Validators/PeopleValidator.cs
+++ b/Emby.Server.Implementations/Library/Validators/PeopleValidator.cs
@@ -24,7 +24,6 @@ namespace Emby.Server.Implementations.Library.Validators
/// </summary>
private readonly ILogger _logger;
- private readonly IServerConfigurationManager _config;
private readonly IFileSystem _fileSystem;
/// <summary>
@@ -32,11 +31,10 @@ namespace Emby.Server.Implementations.Library.Validators
/// </summary>
/// <param name="libraryManager">The library manager.</param>
/// <param name="logger">The logger.</param>
- public PeopleValidator(ILibraryManager libraryManager, ILogger logger, IServerConfigurationManager config, IFileSystem fileSystem)
+ public PeopleValidator(ILibraryManager libraryManager, ILogger logger, IFileSystem fileSystem)
{
_libraryManager = libraryManager;
_logger = logger;
- _config = config;
_fileSystem = fileSystem;
}
diff --git a/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs b/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs
index 84ca130b7..fceb82ba1 100644
--- a/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs
+++ b/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs
@@ -105,8 +105,8 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
_mediaSourceManager = mediaSourceManager;
_streamHelper = streamHelper;
- _seriesTimerProvider = new SeriesTimerManager(fileSystem, jsonSerializer, _logger, Path.Combine(DataPath, "seriestimers"));
- _timerProvider = new TimerManager(fileSystem, jsonSerializer, _logger, Path.Combine(DataPath, "timers"), _logger);
+ _seriesTimerProvider = new SeriesTimerManager(jsonSerializer, _logger, Path.Combine(DataPath, "seriestimers"));
+ _timerProvider = new TimerManager(jsonSerializer, _logger, Path.Combine(DataPath, "timers"), _logger);
_timerProvider.TimerFired += _timerProvider_TimerFired;
_config.NamedConfigurationUpdated += _config_NamedConfigurationUpdated;
@@ -1708,7 +1708,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
{
if (mediaSource.RequiresLooping || !(mediaSource.Container ?? string.Empty).EndsWith("ts", StringComparison.OrdinalIgnoreCase) || (mediaSource.Protocol != MediaProtocol.File && mediaSource.Protocol != MediaProtocol.Http))
{
- return new EncodedRecorder(_logger, _fileSystem, _mediaEncoder, _config.ApplicationPaths, _jsonSerializer, _httpClient, _processFactory, _config, _assemblyInfo);
+ return new EncodedRecorder(_logger, _fileSystem, _mediaEncoder, _config.ApplicationPaths, _jsonSerializer, _processFactory, _config);
}
return new DirectRecorder(_logger, _httpClient, _fileSystem, _streamHelper);
diff --git a/Emby.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs b/Emby.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs
index eed239514..9a9bae215 100644
--- a/Emby.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs
+++ b/Emby.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs
@@ -7,7 +7,6 @@ using System.Text;
using System.Threading;
using System.Threading.Tasks;
using MediaBrowser.Common.Configuration;
-using MediaBrowser.Common.Net;
using MediaBrowser.Controller;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Library;
@@ -17,7 +16,6 @@ using MediaBrowser.Model.Diagnostics;
using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.IO;
-using MediaBrowser.Model.Reflection;
using MediaBrowser.Model.Serialization;
using Microsoft.Extensions.Logging;
@@ -27,7 +25,6 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
{
private readonly ILogger _logger;
private readonly IFileSystem _fileSystem;
- private readonly IHttpClient _httpClient;
private readonly IMediaEncoder _mediaEncoder;
private readonly IServerApplicationPaths _appPaths;
private bool _hasExited;
@@ -38,19 +35,23 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
private readonly IJsonSerializer _json;
private readonly TaskCompletionSource<bool> _taskCompletionSource = new TaskCompletionSource<bool>();
private readonly IServerConfigurationManager _config;
- private readonly IAssemblyInfo _assemblyInfo;
- public EncodedRecorder(ILogger logger, IFileSystem fileSystem, IMediaEncoder mediaEncoder, IServerApplicationPaths appPaths, IJsonSerializer json, IHttpClient httpClient, IProcessFactory processFactory, IServerConfigurationManager config, IAssemblyInfo assemblyInfo)
+ public EncodedRecorder(
+ ILogger logger,
+ IFileSystem fileSystem,
+ IMediaEncoder mediaEncoder,
+ IServerApplicationPaths appPaths,
+ IJsonSerializer json,
+ IProcessFactory processFactory,
+ IServerConfigurationManager config)
{
_logger = logger;
_fileSystem = fileSystem;
_mediaEncoder = mediaEncoder;
_appPaths = appPaths;
_json = json;
- _httpClient = httpClient;
_processFactory = processFactory;
_config = config;
- _assemblyInfo = assemblyInfo;
}
private static bool CopySubtitles => false;
diff --git a/Emby.Server.Implementations/LiveTv/EmbyTV/ItemDataProvider.cs b/Emby.Server.Implementations/LiveTv/EmbyTV/ItemDataProvider.cs
index 6b02eaea8..a2ac60b31 100644
--- a/Emby.Server.Implementations/LiveTv/EmbyTV/ItemDataProvider.cs
+++ b/Emby.Server.Implementations/LiveTv/EmbyTV/ItemDataProvider.cs
@@ -17,15 +17,13 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
protected readonly ILogger Logger;
private readonly string _dataPath;
protected readonly Func<T, T, bool> EqualityComparer;
- private readonly IFileSystem _fileSystem;
- public ItemDataProvider(IFileSystem fileSystem, IJsonSerializer jsonSerializer, ILogger logger, string dataPath, Func<T, T, bool> equalityComparer)
+ public ItemDataProvider(IJsonSerializer jsonSerializer, ILogger logger, string dataPath, Func<T, T, bool> equalityComparer)
{
Logger = logger;
_dataPath = dataPath;
EqualityComparer = equalityComparer;
_jsonSerializer = jsonSerializer;
- _fileSystem = fileSystem;
}
public IReadOnlyList<T> GetAll()
@@ -45,12 +43,14 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
{
var jsonFile = path + ".json";
- try
+ if (!File.Exists(jsonFile))
{
- return _jsonSerializer.DeserializeFromFile<List<T>>(jsonFile) ?? new List<T>();
+ return new List<T>();
}
- catch (FileNotFoundException)
+
+ try
{
+ return _jsonSerializer.DeserializeFromFile<List<T>>(jsonFile) ?? new List<T>();
}
catch (IOException)
{
@@ -59,6 +59,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
{
Logger.LogError(ex, "Error deserializing {jsonFile}", jsonFile);
}
+
return new List<T>();
}
diff --git a/Emby.Server.Implementations/LiveTv/EmbyTV/SeriesTimerManager.cs b/Emby.Server.Implementations/LiveTv/EmbyTV/SeriesTimerManager.cs
index d2ad65a1e..520b44404 100644
--- a/Emby.Server.Implementations/LiveTv/EmbyTV/SeriesTimerManager.cs
+++ b/Emby.Server.Implementations/LiveTv/EmbyTV/SeriesTimerManager.cs
@@ -1,6 +1,5 @@
using System;
using MediaBrowser.Controller.LiveTv;
-using MediaBrowser.Model.IO;
using MediaBrowser.Model.Serialization;
using Microsoft.Extensions.Logging;
@@ -8,8 +7,8 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
{
public class SeriesTimerManager : ItemDataProvider<SeriesTimerInfo>
{
- public SeriesTimerManager(IFileSystem fileSystem, IJsonSerializer jsonSerializer, ILogger logger, string dataPath)
- : base(fileSystem, jsonSerializer, logger, dataPath, (r1, r2) => string.Equals(r1.Id, r2.Id, StringComparison.OrdinalIgnoreCase))
+ public SeriesTimerManager(IJsonSerializer jsonSerializer, ILogger logger, string dataPath)
+ : base(jsonSerializer, logger, dataPath, (r1, r2) => string.Equals(r1.Id, r2.Id, StringComparison.OrdinalIgnoreCase))
{
}
diff --git a/Emby.Server.Implementations/LiveTv/EmbyTV/TimerManager.cs b/Emby.Server.Implementations/LiveTv/EmbyTV/TimerManager.cs
index 1dcb02f43..3c807a8ea 100644
--- a/Emby.Server.Implementations/LiveTv/EmbyTV/TimerManager.cs
+++ b/Emby.Server.Implementations/LiveTv/EmbyTV/TimerManager.cs
@@ -5,7 +5,6 @@ using System.Linq;
using System.Threading;
using MediaBrowser.Controller.LiveTv;
using MediaBrowser.Model.Events;
-using MediaBrowser.Model.IO;
using MediaBrowser.Model.LiveTv;
using MediaBrowser.Model.Serialization;
using Microsoft.Extensions.Logging;
@@ -19,8 +18,8 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
public event EventHandler<GenericEventArgs<TimerInfo>> TimerFired;
- public TimerManager(IFileSystem fileSystem, IJsonSerializer jsonSerializer, ILogger logger, string dataPath, ILogger logger1)
- : base(fileSystem, jsonSerializer, logger, dataPath, (r1, r2) => string.Equals(r1.Id, r2.Id, StringComparison.OrdinalIgnoreCase))
+ public TimerManager(IJsonSerializer jsonSerializer, ILogger logger, string dataPath, ILogger logger1)
+ : base(jsonSerializer, logger, dataPath, (r1, r2) => string.Equals(r1.Id, r2.Id, StringComparison.OrdinalIgnoreCase))
{
_logger = logger1;
}
diff --git a/Emby.Server.Implementations/LiveTv/LiveTvManager.cs b/Emby.Server.Implementations/LiveTv/LiveTvManager.cs
index a36302876..f7ef16fb0 100644
--- a/Emby.Server.Implementations/LiveTv/LiveTvManager.cs
+++ b/Emby.Server.Implementations/LiveTv/LiveTvManager.cs
@@ -184,7 +184,7 @@ namespace Emby.Server.Implementations.LiveTv
public QueryResult<BaseItem> GetInternalChannels(LiveTvChannelQuery query, DtoOptions dtoOptions, CancellationToken cancellationToken)
{
- var user = query.UserId.Equals(Guid.Empty) ? null : _userManager.GetUserById(query.UserId);
+ var user = query.UserId == Guid.Empty ? null : _userManager.GetUserById(query.UserId);
var topFolder = GetInternalLiveTvFolder(cancellationToken);
diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/BaseTunerHost.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/BaseTunerHost.cs
index 6d1eff187..715f600a1 100644
--- a/Emby.Server.Implementations/LiveTv/TunerHosts/BaseTunerHost.cs
+++ b/Emby.Server.Implementations/LiveTv/TunerHosts/BaseTunerHost.cs
@@ -9,7 +9,6 @@ using MediaBrowser.Common.Configuration;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.LiveTv;
-using MediaBrowser.Controller.MediaEncoding;
using MediaBrowser.Model.Dto;
using MediaBrowser.Model.IO;
using MediaBrowser.Model.LiveTv;
@@ -23,18 +22,16 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
protected readonly IServerConfigurationManager Config;
protected readonly ILogger Logger;
protected IJsonSerializer JsonSerializer;
- protected readonly IMediaEncoder MediaEncoder;
protected readonly IFileSystem FileSystem;
private readonly ConcurrentDictionary<string, ChannelCache> _channelCache =
new ConcurrentDictionary<string, ChannelCache>(StringComparer.OrdinalIgnoreCase);
- protected BaseTunerHost(IServerConfigurationManager config, ILogger logger, IJsonSerializer jsonSerializer, IMediaEncoder mediaEncoder, IFileSystem fileSystem)
+ protected BaseTunerHost(IServerConfigurationManager config, ILogger logger, IJsonSerializer jsonSerializer, IFileSystem fileSystem)
{
Config = config;
Logger = logger;
JsonSerializer = jsonSerializer;
- MediaEncoder = mediaEncoder;
FileSystem = fileSystem;
}
diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs
index 77b09a83d..24b100edd 100644
--- a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs
+++ b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs
@@ -31,15 +31,22 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
private readonly IServerApplicationHost _appHost;
private readonly ISocketFactory _socketFactory;
private readonly INetworkManager _networkManager;
- private readonly IEnvironmentInfo _environment;
- public HdHomerunHost(IServerConfigurationManager config, ILogger logger, IJsonSerializer jsonSerializer, IMediaEncoder mediaEncoder, IFileSystem fileSystem, IHttpClient httpClient, IServerApplicationHost appHost, ISocketFactory socketFactory, INetworkManager networkManager, IEnvironmentInfo environment) : base(config, logger, jsonSerializer, mediaEncoder, fileSystem)
+ public HdHomerunHost(
+ IServerConfigurationManager config,
+ ILogger logger,
+ IJsonSerializer jsonSerializer,
+ IFileSystem fileSystem,
+ IHttpClient httpClient,
+ IServerApplicationHost appHost,
+ ISocketFactory socketFactory,
+ INetworkManager networkManager)
+ : base(config, logger, jsonSerializer, fileSystem)
{
_httpClient = httpClient;
_appHost = appHost;
_socketFactory = socketFactory;
_networkManager = networkManager;
- _environment = environment;
}
public string Name => "HD Homerun";
diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/M3UTunerHost.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/M3UTunerHost.cs
index 638796e2e..fdaaf0bae 100644
--- a/Emby.Server.Implementations/LiveTv/TunerHosts/M3UTunerHost.cs
+++ b/Emby.Server.Implementations/LiveTv/TunerHosts/M3UTunerHost.cs
@@ -26,15 +26,14 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
{
private readonly IHttpClient _httpClient;
private readonly IServerApplicationHost _appHost;
- private readonly IEnvironmentInfo _environment;
private readonly INetworkManager _networkManager;
private readonly IMediaSourceManager _mediaSourceManager;
- public M3UTunerHost(IServerConfigurationManager config, IMediaSourceManager mediaSourceManager, ILogger logger, IJsonSerializer jsonSerializer, IMediaEncoder mediaEncoder, IFileSystem fileSystem, IHttpClient httpClient, IServerApplicationHost appHost, IEnvironmentInfo environment, INetworkManager networkManager) : base(config, logger, jsonSerializer, mediaEncoder, fileSystem)
+ public M3UTunerHost(IServerConfigurationManager config, IMediaSourceManager mediaSourceManager, ILogger logger, IJsonSerializer jsonSerializer, IFileSystem fileSystem, IHttpClient httpClient, IServerApplicationHost appHost, INetworkManager networkManager)
+ : base(config, logger, jsonSerializer, fileSystem)
{
_httpClient = httpClient;
_appHost = appHost;
- _environment = environment;
_networkManager = networkManager;
_mediaSourceManager = mediaSourceManager;
}
@@ -52,7 +51,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
{
var channelIdPrefix = GetFullChannelIdPrefix(info);
- var result = await new M3uParser(Logger, FileSystem, _httpClient, _appHost).Parse(info.Url, channelIdPrefix, info.Id, cancellationToken).ConfigureAwait(false);
+ var result = await new M3uParser(Logger, _httpClient, _appHost).Parse(info.Url, channelIdPrefix, info.Id, cancellationToken).ConfigureAwait(false);
return result.Cast<ChannelInfo>().ToList();
}
@@ -115,7 +114,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
public async Task Validate(TunerHostInfo info)
{
- using (var stream = await new M3uParser(Logger, FileSystem, _httpClient, _appHost).GetListingsStream(info.Url, CancellationToken.None).ConfigureAwait(false))
+ using (var stream = await new M3uParser(Logger, _httpClient, _appHost).GetListingsStream(info.Url, CancellationToken.None).ConfigureAwait(false))
{
}
diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/M3uParser.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/M3uParser.cs
index 9a01c42d3..ad124bb0f 100644
--- a/Emby.Server.Implementations/LiveTv/TunerHosts/M3uParser.cs
+++ b/Emby.Server.Implementations/LiveTv/TunerHosts/M3uParser.cs
@@ -19,14 +19,12 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
public class M3uParser
{
private readonly ILogger _logger;
- private readonly IFileSystem _fileSystem;
private readonly IHttpClient _httpClient;
private readonly IServerApplicationHost _appHost;
- public M3uParser(ILogger logger, IFileSystem fileSystem, IHttpClient httpClient, IServerApplicationHost appHost)
+ public M3uParser(ILogger logger, IHttpClient httpClient, IServerApplicationHost appHost)
{
_logger = logger;
- _fileSystem = fileSystem;
_httpClient = httpClient;
_appHost = appHost;
}
@@ -157,56 +155,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 +212,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/Localization/Core/da.json b/Emby.Server.Implementations/Localization/Core/da.json
index 7004d44db..9d4d74099 100644
--- a/Emby.Server.Implementations/Localization/Core/da.json
+++ b/Emby.Server.Implementations/Localization/Core/da.json
@@ -2,10 +2,10 @@
"Albums": "Album",
"AppDeviceValues": "App: {0}, Enhed: {1}",
"Application": "Applikation",
- "Artists": "Kunstner",
+ "Artists": "Kunstnere",
"AuthenticationSucceededWithUserName": "{0} bekræftet med succes",
"Books": "Bøger",
- "CameraImageUploadedFrom": "A new camera image has been uploaded from {0}",
+ "CameraImageUploadedFrom": "Et nyt kamerabillede er blevet uploadet fra {0}",
"Channels": "Kanaler",
"ChapterNameValue": "Kapitel {0}",
"Collections": "Samlinger",
@@ -14,41 +14,41 @@
"FailedLoginAttemptWithUserName": "Fejlet loginforsøg fra {0}",
"Favorites": "Favoritter",
"Folders": "Mapper",
- "Genres": "Genre",
+ "Genres": "Genrer",
"HeaderAlbumArtists": "Albumkunstnere",
- "HeaderCameraUploads": "Camera Uploads",
+ "HeaderCameraUploads": "Kamera Uploads",
"HeaderContinueWatching": "Fortsæt Afspilning",
"HeaderFavoriteAlbums": "Favoritalbum",
"HeaderFavoriteArtists": "Favoritkunstnere",
- "HeaderFavoriteEpisodes": "Favoritepisoder",
- "HeaderFavoriteShows": "Favorit serier",
- "HeaderFavoriteSongs": "Favoritsange",
+ "HeaderFavoriteEpisodes": "Favorit-afsnit",
+ "HeaderFavoriteShows": "Favorit-serier",
+ "HeaderFavoriteSongs": "Favorit-sange",
"HeaderLiveTV": "Live TV",
"HeaderNextUp": "Næste",
- "HeaderRecordingGroups": "Optagegrupper",
+ "HeaderRecordingGroups": "Optagelsesgrupper",
"HomeVideos": "Hjemmevideoer",
- "Inherit": "Arv",
+ "Inherit": "Nedarv",
"ItemAddedWithName": "{0} blev tilføjet til biblioteket",
"ItemRemovedWithName": "{0} blev fjernet fra biblioteket",
"LabelIpAddressValue": "IP-adresse: {0}",
"LabelRunningTimeValue": "Spilletid: {0}",
"Latest": "Seneste",
"MessageApplicationUpdated": "Jellyfin Server er blevet opdateret",
- "MessageApplicationUpdatedTo": "Jellyfin Server has been updated to {0}",
- "MessageNamedServerConfigurationUpdatedWithValue": "Server konfigurationssektion {0} er blevet opdateret",
- "MessageServerConfigurationUpdated": "Serverkonfiguration er blevet opdateret",
+ "MessageApplicationUpdatedTo": "Jellyfin Server er blevet opdateret til {0}",
+ "MessageNamedServerConfigurationUpdatedWithValue": "Serverkonfigurationsafsnit {0} er blevet opdateret",
+ "MessageServerConfigurationUpdated": "Serverkonfigurationen er blevet opdateret",
"MixedContent": "Blandet indhold",
"Movies": "Film",
"Music": "Musik",
"MusicVideos": "Musikvideoer",
- "NameInstallFailed": "{0} installation failed",
+ "NameInstallFailed": "{0} installationen mislykkedes",
"NameSeasonNumber": "Sæson {0}",
- "NameSeasonUnknown": "Season Unknown",
- "NewVersionIsAvailable": "A new version of Jellyfin Server is available for download.",
+ "NameSeasonUnknown": "Ukendt Sæson",
+ "NewVersionIsAvailable": "En ny version af Jellyfin Server er tilgængelig til download.",
"NotificationOptionApplicationUpdateAvailable": "Opdatering til applikation tilgængelig",
"NotificationOptionApplicationUpdateInstalled": "Opdatering til applikation installeret",
- "NotificationOptionAudioPlayback": "Audioafspilning påbegyndt",
- "NotificationOptionAudioPlaybackStopped": "Audioafspilning stoppet",
+ "NotificationOptionAudioPlayback": "Lydafspilning påbegyndt",
+ "NotificationOptionAudioPlaybackStopped": "Lydafspilning stoppet",
"NotificationOptionCameraImageUploaded": "Kamerabillede uploadet",
"NotificationOptionInstallationFailed": "Installationsfejl",
"NotificationOptionNewLibraryContent": "Nyt indhold tilføjet",
@@ -70,16 +70,16 @@
"ProviderValue": "Udbyder: {0}",
"ScheduledTaskFailedWithName": "{0} fejlet",
"ScheduledTaskStartedWithName": "{0} påbegyndt",
- "ServerNameNeedsToBeRestarted": "{0} needs to be restarted",
- "Shows": "Shows",
+ "ServerNameNeedsToBeRestarted": "{0} skal genstartes",
+ "Shows": "Serier",
"Songs": "Sange",
- "StartupEmbyServerIsLoading": "Jellyfin Server indlæser. Prøv venligst igen om kort tid.",
+ "StartupEmbyServerIsLoading": "Jellyfin Server er i gang med at starte op. Prøv venligst igen om lidt.",
"SubtitleDownloadFailureForItem": "Fejlet i download af undertekster for {0}",
- "SubtitleDownloadFailureFromForItem": "Subtitles failed to download from {0} for {1}",
+ "SubtitleDownloadFailureFromForItem": "Undertekster kunne ikke downloades fra {0} til {1}",
"SubtitlesDownloadedForItem": "Undertekster downloadet for {0}",
"Sync": "Synk",
"System": "System",
- "TvShows": "TV Shows",
+ "TvShows": "TV serier",
"User": "Bruger",
"UserCreatedWithName": "Bruger {0} er blevet oprettet",
"UserDeletedWithName": "Brugeren {0} er blevet slettet",
@@ -88,10 +88,10 @@
"UserOfflineFromDevice": "{0} har afbrudt fra {1}",
"UserOnlineFromDevice": "{0} er online fra {1}",
"UserPasswordChangedWithName": "Adgangskode er ændret for bruger {0}",
- "UserPolicyUpdatedWithName": "User policy has been updated for {0}",
+ "UserPolicyUpdatedWithName": "Brugerpolitik er blevet opdateret for {0}",
"UserStartedPlayingItemWithValues": "{0} har påbegyndt afspilning af {1}",
- "UserStoppedPlayingItemWithValues": "{0} har afsluttet afspilning af {1}",
- "ValueHasBeenAddedToLibrary": "{0} has been added to your media library",
+ "UserStoppedPlayingItemWithValues": "{0} har afsluttet afspilning af {1} på {2}",
+ "ValueHasBeenAddedToLibrary": "{0} er blevet tilføjet til dit mediebibliotek",
"ValueSpecialEpisodeName": "Special - {0}",
"VersionNumber": "Version {0}"
}
diff --git a/Emby.Server.Implementations/Localization/Core/de.json b/Emby.Server.Implementations/Localization/Core/de.json
index 7bd2e90fe..6fd63a514 100644
--- a/Emby.Server.Implementations/Localization/Core/de.json
+++ b/Emby.Server.Implementations/Localization/Core/de.json
@@ -3,61 +3,61 @@
"AppDeviceValues": "App: {0}, Gerät: {1}",
"Application": "Anwendung",
"Artists": "Interpreten",
- "AuthenticationSucceededWithUserName": "{0} erfolgreich authentifiziert",
+ "AuthenticationSucceededWithUserName": "{0} hat sich angemeldet",
"Books": "Bücher",
- "CameraImageUploadedFrom": "Ein neues Bild wurde hochgeladen von {0}",
+ "CameraImageUploadedFrom": "Ein neues Foto wurde hochgeladen von {0}",
"Channels": "Kanäle",
"ChapterNameValue": "Kapitel {0}",
"Collections": "Sammlungen",
"DeviceOfflineWithName": "{0} wurde getrennt",
- "DeviceOnlineWithName": "{0} ist verbunden",
+ "DeviceOnlineWithName": "{0} hat sich verbunden",
"FailedLoginAttemptWithUserName": "Fehlgeschlagener Anmeldeversuch von {0}",
"Favorites": "Favoriten",
"Folders": "Verzeichnisse",
"Genres": "Genres",
- "HeaderAlbumArtists": "Album-Künstler",
- "HeaderCameraUploads": "Kamera Uploads",
+ "HeaderAlbumArtists": "Album-Interpreten",
+ "HeaderCameraUploads": "Kamera-Uploads",
"HeaderContinueWatching": "Weiterschauen",
"HeaderFavoriteAlbums": "Lieblingsalben",
- "HeaderFavoriteArtists": "Interpreten Favoriten",
+ "HeaderFavoriteArtists": "Lieblings-Interpreten",
"HeaderFavoriteEpisodes": "Lieblingsepisoden",
"HeaderFavoriteShows": "Lieblingsserien",
- "HeaderFavoriteSongs": "Lieder Favoriten",
- "HeaderLiveTV": "Live TV",
+ "HeaderFavoriteSongs": "Lieblingslieder",
+ "HeaderLiveTV": "Live-TV",
"HeaderNextUp": "Als Nächstes",
"HeaderRecordingGroups": "Aufnahme-Gruppen",
"HomeVideos": "Heimvideos",
"Inherit": "Übernehmen",
"ItemAddedWithName": "{0} wurde der Bibliothek hinzugefügt",
"ItemRemovedWithName": "{0} wurde aus der Bibliothek entfernt",
- "LabelIpAddressValue": "IP Adresse: {0}",
+ "LabelIpAddressValue": "IP-Adresse: {0}",
"LabelRunningTimeValue": "Laufzeit: {0}",
"Latest": "Neueste",
- "MessageApplicationUpdated": "Jellyfin Server wurde auf den neusten Stand gebracht.",
- "MessageApplicationUpdatedTo": "Jellyfin Server wurde auf Version {0} aktualisiert",
+ "MessageApplicationUpdated": "Jellyfin-Server wurde aktualisiert",
+ "MessageApplicationUpdatedTo": "Jellyfin-Server wurde auf Version {0} aktualisiert",
"MessageNamedServerConfigurationUpdatedWithValue": "Der Server Einstellungsbereich {0} wurde aktualisiert",
- "MessageServerConfigurationUpdated": "Server Einstellungen wurden aktualisiert",
+ "MessageServerConfigurationUpdated": "Servereinstellungen wurden aktualisiert",
"MixedContent": "Gemischte Inhalte",
"Movies": "Filme",
"Music": "Musik",
"MusicVideos": "Musikvideos",
- "NameInstallFailed": "{0} Installation fehlgeschlagen",
+ "NameInstallFailed": "Installation von {0} fehlgeschlagen",
"NameSeasonNumber": "Staffel {0}",
"NameSeasonUnknown": "Staffel unbekannt",
- "NewVersionIsAvailable": "Eine neue Version von Jellyfin Server steht zum Download bereit.",
+ "NewVersionIsAvailable": "Eine neue Version von Jellyfin-Server steht zum Download bereit.",
"NotificationOptionApplicationUpdateAvailable": "Anwendungsaktualisierung verfügbar",
"NotificationOptionApplicationUpdateInstalled": "Anwendungsaktualisierung installiert",
"NotificationOptionAudioPlayback": "Audiowiedergabe gestartet",
"NotificationOptionAudioPlaybackStopped": "Audiowiedergabe gestoppt",
- "NotificationOptionCameraImageUploaded": "Kamera Bild hochgeladen",
+ "NotificationOptionCameraImageUploaded": "Foto hochgeladen",
"NotificationOptionInstallationFailed": "Installationsfehler",
"NotificationOptionNewLibraryContent": "Neuer Inhalt hinzugefügt",
- "NotificationOptionPluginError": "Plugin Fehler",
+ "NotificationOptionPluginError": "Plugin-Fehler",
"NotificationOptionPluginInstalled": "Plugin installiert",
"NotificationOptionPluginUninstalled": "Plugin deinstalliert",
"NotificationOptionPluginUpdateInstalled": "Pluginaktualisierung installiert",
"NotificationOptionServerRestartRequired": "Serverneustart notwendig",
- "NotificationOptionTaskFailed": "Geplante Aufgaben fehlgeschlagen",
+ "NotificationOptionTaskFailed": "Geplante Aufgabe fehlgeschlagen",
"NotificationOptionUserLockedOut": "Benutzer ausgeschlossen",
"NotificationOptionVideoPlayback": "Videowiedergabe gestartet",
"NotificationOptionVideoPlaybackStopped": "Videowiedergabe gestoppt",
@@ -68,18 +68,18 @@
"PluginUninstalledWithName": "{0} wurde deinstalliert",
"PluginUpdatedWithName": "{0} wurde aktualisiert",
"ProviderValue": "Anbieter: {0}",
- "ScheduledTaskFailedWithName": "{0} fehlgeschlagen",
- "ScheduledTaskStartedWithName": "{0} gestartet",
+ "ScheduledTaskFailedWithName": "{0} ist fehlgeschlagen",
+ "ScheduledTaskStartedWithName": "{0} wurde gestartet",
"ServerNameNeedsToBeRestarted": "{0} muss neu gestartet werden",
"Shows": "Serien",
"Songs": "Songs",
- "StartupEmbyServerIsLoading": "Jellyfin Server startet, bitte versuche es gleich noch einmal.",
+ "StartupEmbyServerIsLoading": "Jellyfin-Server startet, bitte versuche es gleich noch einmal.",
"SubtitleDownloadFailureForItem": "Download der Untertitel fehlgeschlagen für {0}",
- "SubtitleDownloadFailureFromForItem": "Subtitles failed to download from {0} for {1}",
+ "SubtitleDownloadFailureFromForItem": "Untertitel von {0} für {1} konnten nicht heruntergeladen werden",
"SubtitlesDownloadedForItem": "Untertitel heruntergeladen für {0}",
"Sync": "Synchronisation",
"System": "System",
- "TvShows": "TV Sendungen",
+ "TvShows": "TV-Sendungen",
"User": "Benutzer",
"UserCreatedWithName": "Benutzer {0} wurde erstellt",
"UserDeletedWithName": "Benutzer {0} wurde gelöscht",
@@ -88,10 +88,10 @@
"UserOfflineFromDevice": "{0} wurde getrennt von {1}",
"UserOnlineFromDevice": "{0} ist online von {1}",
"UserPasswordChangedWithName": "Das Passwort für Benutzer {0} wurde geändert",
- "UserPolicyUpdatedWithName": "Benutzerrichtlinie wurde für {0} aktualisiert",
- "UserStartedPlayingItemWithValues": "{0} hat die Wiedergabe von {1} gestartet",
- "UserStoppedPlayingItemWithValues": "{0} hat die Wiedergabe von {1} beendet",
- "ValueHasBeenAddedToLibrary": "{0} wurde ihrer Bibliothek hinzugefügt",
- "ValueSpecialEpisodeName": "Special - {0}",
+ "UserPolicyUpdatedWithName": "Benutzerrichtlinie von {0} wurde aktualisiert",
+ "UserStartedPlayingItemWithValues": "{0} hat die Wiedergabe von {1} auf {2} gestartet",
+ "UserStoppedPlayingItemWithValues": "{0} hat die Wiedergabe von {1} auf {2} beendet",
+ "ValueHasBeenAddedToLibrary": "{0} wurde deiner Bibliothek hinzugefügt",
+ "ValueSpecialEpisodeName": "Extra - {0}",
"VersionNumber": "Version {0}"
}
diff --git a/Emby.Server.Implementations/Localization/Core/en-GB.json b/Emby.Server.Implementations/Localization/Core/en-GB.json
index 332902292..67d4068cf 100644
--- a/Emby.Server.Implementations/Localization/Core/en-GB.json
+++ b/Emby.Server.Implementations/Localization/Core/en-GB.json
@@ -90,7 +90,7 @@
"UserPasswordChangedWithName": "Password has been changed for user {0}",
"UserPolicyUpdatedWithName": "User policy has been updated for {0}",
"UserStartedPlayingItemWithValues": "{0} has started playing {1}",
- "UserStoppedPlayingItemWithValues": "{0} has stopped playing {1}",
+ "UserStoppedPlayingItemWithValues": "{0} has finished playing {1} on {2}",
"ValueHasBeenAddedToLibrary": "{0} has been added to your media library",
"ValueSpecialEpisodeName": "Special - {0}",
"VersionNumber": "Version {0}"
diff --git a/Emby.Server.Implementations/Localization/Core/en-US.json b/Emby.Server.Implementations/Localization/Core/en-US.json
index f19cd532b..aa855ed21 100644
--- a/Emby.Server.Implementations/Localization/Core/en-US.json
+++ b/Emby.Server.Implementations/Localization/Core/en-US.json
@@ -30,7 +30,7 @@
"Inherit": "Inherit",
"ItemAddedWithName": "{0} was added to the library",
"ItemRemovedWithName": "{0} was removed from the library",
- "LabelIpAddressValue": "Ip address: {0}",
+ "LabelIpAddressValue": "IP address: {0}",
"LabelRunningTimeValue": "Running time: {0}",
"Latest": "Latest",
"MessageApplicationUpdated": "Jellyfin Server has been updated",
diff --git a/Emby.Server.Implementations/Localization/Core/es.json b/Emby.Server.Implementations/Localization/Core/es.json
index 5d118d21f..1850b8f25 100644
--- a/Emby.Server.Implementations/Localization/Core/es.json
+++ b/Emby.Server.Implementations/Localization/Core/es.json
@@ -5,46 +5,46 @@
"Artists": "Artistas",
"AuthenticationSucceededWithUserName": "{0} autenticado correctamente",
"Books": "Libros",
- "CameraImageUploadedFrom": "A new camera image has been uploaded from {0}",
+ "CameraImageUploadedFrom": "Se ha subido una nueva imagen de cámara desde {0}",
"Channels": "Canales",
"ChapterNameValue": "Capítulo {0}",
"Collections": "Colecciones",
"DeviceOfflineWithName": "{0} se ha desconectado",
"DeviceOnlineWithName": "{0} está conectado",
- "FailedLoginAttemptWithUserName": "Error al intentar iniciar sesión a partir de {0}",
+ "FailedLoginAttemptWithUserName": "Error al intentar iniciar sesión desde {0}",
"Favorites": "Favoritos",
"Folders": "Carpetas",
"Genres": "Géneros",
- "HeaderAlbumArtists": "Artistas del Álbum",
- "HeaderCameraUploads": "Camera Uploads",
+ "HeaderAlbumArtists": "Artistas del álbum",
+ "HeaderCameraUploads": "Subidas desde cámara",
"HeaderContinueWatching": "Continuar viendo",
"HeaderFavoriteAlbums": "Álbumes favoritos",
"HeaderFavoriteArtists": "Artistas favoritos",
"HeaderFavoriteEpisodes": "Episodios favoritos",
"HeaderFavoriteShows": "Programas favoritos",
"HeaderFavoriteSongs": "Canciones favoritas",
- "HeaderLiveTV": "TV en vivo",
+ "HeaderLiveTV": "TV en directo",
"HeaderNextUp": "Siguiendo",
"HeaderRecordingGroups": "Grupos de grabación",
- "HomeVideos": "Vídeos de inicio",
+ "HomeVideos": "Vídeos caseros",
"Inherit": "Heredar",
"ItemAddedWithName": "{0} se ha añadido a la biblioteca",
- "ItemRemovedWithName": "{0} se elimina de la biblioteca",
+ "ItemRemovedWithName": "{0} ha sido eliminado de la biblioteca",
"LabelIpAddressValue": "Dirección IP: {0}",
"LabelRunningTimeValue": "Tiempo de funcionamiento: {0}",
"Latest": "Últimos",
"MessageApplicationUpdated": "Se ha actualizado el servidor Jellyfin",
- "MessageApplicationUpdatedTo": "Jellyfin Server has been updated to {0}",
- "MessageNamedServerConfigurationUpdatedWithValue": "La sección de configuración del servidor {0} ha sido actualizado",
+ "MessageApplicationUpdatedTo": "Se ha actualizado el servidor Jellyfin a la versión {0}",
+ "MessageNamedServerConfigurationUpdatedWithValue": "La sección {0} de configuración del servidor ha sido actualizada",
"MessageServerConfigurationUpdated": "Se ha actualizado la configuración del servidor",
"MixedContent": "Contenido mixto",
- "Movies": "Peliculas",
+ "Movies": "Películas",
"Music": "Música",
- "MusicVideos": "Videos musicales",
- "NameInstallFailed": "{0} installation failed",
+ "MusicVideos": "Vídeos musicales",
+ "NameInstallFailed": "{0} error de instalación",
"NameSeasonNumber": "Temporada {0}",
- "NameSeasonUnknown": "Season Unknown",
- "NewVersionIsAvailable": "A new version of Jellyfin Server is available for download.",
+ "NameSeasonUnknown": "Temporada desconocida",
+ "NewVersionIsAvailable": "Disponible una nueva versión de Jellyfin para descargar.",
"NotificationOptionApplicationUpdateAvailable": "Actualización de la aplicación disponible",
"NotificationOptionApplicationUpdateInstalled": "Actualización de la aplicación instalada",
"NotificationOptionAudioPlayback": "Se inició la reproducción de audio",
@@ -56,13 +56,13 @@
"NotificationOptionPluginInstalled": "Plugin instalado",
"NotificationOptionPluginUninstalled": "Plugin desinstalado",
"NotificationOptionPluginUpdateInstalled": "Actualización del complemento instalada",
- "NotificationOptionServerRestartRequired": "Requiere reinicio del servidor",
+ "NotificationOptionServerRestartRequired": "Se requiere reinicio del servidor",
"NotificationOptionTaskFailed": "Error de tarea programada",
"NotificationOptionUserLockedOut": "Usuario bloqueado",
"NotificationOptionVideoPlayback": "Se inició la reproducción de vídeo",
"NotificationOptionVideoPlaybackStopped": "Reproducción de vídeo detenida",
"Photos": "Fotos",
- "Playlists": "Listas reproducción",
+ "Playlists": "Listas de reproducción",
"Plugin": "Plugin",
"PluginInstalledWithName": "{0} se ha instalado",
"PluginUninstalledWithName": "{0} se ha desinstalado",
@@ -70,16 +70,16 @@
"ProviderValue": "Proveedor: {0}",
"ScheduledTaskFailedWithName": "{0} falló",
"ScheduledTaskStartedWithName": "{0} iniciada",
- "ServerNameNeedsToBeRestarted": "{0} needs to be restarted",
+ "ServerNameNeedsToBeRestarted": "{0} necesita ser reiniciado",
"Shows": "Series",
"Songs": "Canciones",
"StartupEmbyServerIsLoading": "Jellyfin Server se está cargando. Vuelve a intentarlo en breve.",
"SubtitleDownloadFailureForItem": "Error al descargar subtítulos para {0}",
- "SubtitleDownloadFailureFromForItem": "Subtitles failed to download from {0} for {1}",
+ "SubtitleDownloadFailureFromForItem": "Fallo de descarga de subtítulos desde {0} para {1}",
"SubtitlesDownloadedForItem": "Descargar subtítulos para {0}",
"Sync": "Sincronizar",
"System": "Sistema",
- "TvShows": "Series TV",
+ "TvShows": "Series de TV",
"User": "Usuario",
"UserCreatedWithName": "El usuario {0} ha sido creado",
"UserDeletedWithName": "El usuario {0} ha sido borrado",
@@ -88,10 +88,10 @@
"UserOfflineFromDevice": "{0} se ha desconectado de {1}",
"UserOnlineFromDevice": "{0} está en línea desde {1}",
"UserPasswordChangedWithName": "Se ha cambiado la contraseña para el usuario {0}",
- "UserPolicyUpdatedWithName": "User policy has been updated for {0}",
- "UserStartedPlayingItemWithValues": "{0} ha comenzado reproducir {1}",
- "UserStoppedPlayingItemWithValues": "{0} ha parado de reproducir {1}",
- "ValueHasBeenAddedToLibrary": "{0} has been added to your media library",
+ "UserPolicyUpdatedWithName": "Actualizada política de usuario para {0}",
+ "UserStartedPlayingItemWithValues": "{0} está reproduciendo {1} en {2}",
+ "UserStoppedPlayingItemWithValues": "{0} ha terminado de reproducir {1} en {2}",
+ "ValueHasBeenAddedToLibrary": "{0} ha sido añadido a tu biblioteca multimedia",
"ValueSpecialEpisodeName": "Especial - {0}",
"VersionNumber": "Versión {0}"
}
diff --git a/Emby.Server.Implementations/Localization/Core/fr.json b/Emby.Server.Implementations/Localization/Core/fr.json
index aa9a1add3..52afb4e49 100644
--- a/Emby.Server.Implementations/Localization/Core/fr.json
+++ b/Emby.Server.Implementations/Localization/Core/fr.json
@@ -36,7 +36,7 @@
"MessageApplicationUpdated": "Le serveur Jellyfin a été mis à jour",
"MessageApplicationUpdatedTo": "Jellyfin Serveur a été mis à jour en version {0}",
"MessageNamedServerConfigurationUpdatedWithValue": "La configuration de la section {0} du serveur a été mise à jour",
- "MessageServerConfigurationUpdated": "La configuration du serveur a été mise à jour.",
+ "MessageServerConfigurationUpdated": "La configuration du serveur a été mise à jour",
"MixedContent": "Contenu mixte",
"Movies": "Films",
"Music": "Musique",
diff --git a/Emby.Server.Implementations/Localization/Core/hu.json b/Emby.Server.Implementations/Localization/Core/hu.json
index d2d16b18f..c0f988abe 100644
--- a/Emby.Server.Implementations/Localization/Core/hu.json
+++ b/Emby.Server.Implementations/Localization/Core/hu.json
@@ -5,48 +5,48 @@
"Artists": "Előadók",
"AuthenticationSucceededWithUserName": "{0} sikeresen azonosítva",
"Books": "Könyvek",
- "CameraImageUploadedFrom": "A new camera image has been uploaded from {0}",
+ "CameraImageUploadedFrom": "Új kamerakép került feltöltésre {0}",
"Channels": "Csatornák",
"ChapterNameValue": "Jelenet {0}",
"Collections": "Gyűjtemények",
"DeviceOfflineWithName": "{0} kijelentkezett",
"DeviceOnlineWithName": "{0} belépett",
- "FailedLoginAttemptWithUserName": "Failed login attempt from {0}",
+ "FailedLoginAttemptWithUserName": "Sikertelen bejelentkezési kísérlet {0}",
"Favorites": "Kedvencek",
"Folders": "Könyvtárak",
"Genres": "Műfajok",
"HeaderAlbumArtists": "Album Előadók",
- "HeaderCameraUploads": "Camera Uploads",
- "HeaderContinueWatching": "Vetítés(ek) folytatása",
+ "HeaderCameraUploads": "Kamera feltöltések",
+ "HeaderContinueWatching": "Folyamatban lévő filmek",
"HeaderFavoriteAlbums": "Kedvenc Albumok",
"HeaderFavoriteArtists": "Kedvenc Művészek",
"HeaderFavoriteEpisodes": "Kedvenc Epizódok",
"HeaderFavoriteShows": "Kedvenc Műsorok",
"HeaderFavoriteSongs": "Kedvenc Dalok",
- "HeaderLiveTV": "Live TV",
+ "HeaderLiveTV": "Élő TV",
"HeaderNextUp": "Következik",
- "HeaderRecordingGroups": "Recording Groups",
+ "HeaderRecordingGroups": "Felvételi csoportok",
"HomeVideos": "Házi videók",
"Inherit": "Inherit",
- "ItemAddedWithName": "{0} was added to the library",
- "ItemRemovedWithName": "{0} was removed from the library",
- "LabelIpAddressValue": "Ip cím: {0}",
- "LabelRunningTimeValue": "Running time: {0}",
+ "ItemAddedWithName": "{0} hozzáadva a könyvtárhoz",
+ "ItemRemovedWithName": "{0} eltávolítva a könyvtárból",
+ "LabelIpAddressValue": "IP cím: {0}",
+ "LabelRunningTimeValue": "Futási idő: {0}",
"Latest": "Legújabb",
"MessageApplicationUpdated": "Jellyfin Szerver frissítve",
- "MessageApplicationUpdatedTo": "Jellyfin Server has been updated to {0}",
+ "MessageApplicationUpdatedTo": "Jellyfin Szerver frissítve lett a következőre {0}",
"MessageNamedServerConfigurationUpdatedWithValue": "Szerver konfigurációs rész {0} frissítve",
"MessageServerConfigurationUpdated": "Szerver konfiguráció frissítve",
"MixedContent": "Vegyes tartalom",
"Movies": "Filmek",
"Music": "Zene",
"MusicVideos": "Zenei Videók",
- "NameInstallFailed": "{0} installation failed",
- "NameSeasonNumber": "Season {0}",
- "NameSeasonUnknown": "Season Unknown",
- "NewVersionIsAvailable": "A new version of Jellyfin Server is available for download.",
- "NotificationOptionApplicationUpdateAvailable": "Program frissítés elérhető",
- "NotificationOptionApplicationUpdateInstalled": "Program frissítés telepítve",
+ "NameInstallFailed": "{0} sikertelen telepítés",
+ "NameSeasonNumber": "Évad {0}",
+ "NameSeasonUnknown": "Ismeretlen évad",
+ "NewVersionIsAvailable": "Letölthető a Jellyfin Szerver új verziója.",
+ "NotificationOptionApplicationUpdateAvailable": "Új programfrissítés érhető el",
+ "NotificationOptionApplicationUpdateInstalled": "Programfrissítés telepítve",
"NotificationOptionAudioPlayback": "Audió lejátszás elkezdve",
"NotificationOptionAudioPlaybackStopped": "Audió lejátszás befejezve",
"NotificationOptionCameraImageUploaded": "Kamera kép feltöltve",
@@ -57,7 +57,7 @@
"NotificationOptionPluginUninstalled": "Bővítmény eltávolítva",
"NotificationOptionPluginUpdateInstalled": "Bővítmény frissítés telepítve",
"NotificationOptionServerRestartRequired": "Szerver újraindítás szükséges",
- "NotificationOptionTaskFailed": "Scheduled task failure",
+ "NotificationOptionTaskFailed": "Ütemezett feladat hiba",
"NotificationOptionUserLockedOut": "Felhasználó tiltva",
"NotificationOptionVideoPlayback": "Videó lejátszás elkezdve",
"NotificationOptionVideoPlaybackStopped": "Videó lejátszás befejezve",
@@ -68,30 +68,30 @@
"PluginUninstalledWithName": "{0} eltávolítva",
"PluginUpdatedWithName": "{0} frissítve",
"ProviderValue": "Provider: {0}",
- "ScheduledTaskFailedWithName": "{0} failed",
- "ScheduledTaskStartedWithName": "{0} started",
- "ServerNameNeedsToBeRestarted": "{0} needs to be restarted",
+ "ScheduledTaskFailedWithName": "{0} hiba",
+ "ScheduledTaskStartedWithName": "{0} elkezdve",
+ "ServerNameNeedsToBeRestarted": "{0}-t újra kell indítani",
"Shows": "Műsorok",
"Songs": "Dalok",
- "StartupEmbyServerIsLoading": "Jellyfin Szerver betöltődik. Kérjük, próbáld meg újra később.",
+ "StartupEmbyServerIsLoading": "A Jellyfin Szerver betöltődik. Kérlek próbáld újra később.",
"SubtitleDownloadFailureForItem": "Subtitles failed to download for {0}",
- "SubtitleDownloadFailureFromForItem": "Subtitles failed to download from {0} for {1}",
- "SubtitlesDownloadedForItem": "Subtitles downloaded for {0}",
+ "SubtitleDownloadFailureFromForItem": "Nem sikerült a felirat letöltése innen: {0} ehhez: {1}",
+ "SubtitlesDownloadedForItem": "Letöltött feliratok a következőhöz {0}",
"Sync": "Szinkronizál",
"System": "Rendszer",
"TvShows": "TV Műsorok",
"User": "Felhasználó",
- "UserCreatedWithName": "User {0} has been created",
- "UserDeletedWithName": "User {0} has been deleted",
+ "UserCreatedWithName": "{0} felhasználó létrehozva",
+ "UserDeletedWithName": "{0} felhasználó törölve",
"UserDownloadingItemWithValues": "{0} letölti {1}",
- "UserLockedOutWithName": "User {0} has been locked out",
- "UserOfflineFromDevice": "{0} kijelentkezett innen {1}",
- "UserOnlineFromDevice": "{0} is online from {1}",
- "UserPasswordChangedWithName": "Password has been changed for user {0}",
- "UserPolicyUpdatedWithName": "User policy has been updated for {0}",
- "UserStartedPlayingItemWithValues": "{0} elkezdte játszani a következőt {1}",
- "UserStoppedPlayingItemWithValues": "{0} befejezte a következőt {1}",
- "ValueHasBeenAddedToLibrary": "{0} has been added to your media library",
+ "UserLockedOutWithName": "{0} felhasználó zárolva van",
+ "UserOfflineFromDevice": "{0} kijelentkezett innen: {1}",
+ "UserOnlineFromDevice": "{0} online itt: {1}",
+ "UserPasswordChangedWithName": "Jelszó megváltozott a következő felhasználó számára: {0}",
+ "UserPolicyUpdatedWithName": "A felhasználói házirend frissítve lett {0}",
+ "UserStartedPlayingItemWithValues": "{0} elkezdte játszani a következőt: {1} itt: {2}",
+ "UserStoppedPlayingItemWithValues": "{0} befejezte a következőt: {1} itt: {2}",
+ "ValueHasBeenAddedToLibrary": "{0} hozzáadva a médiatárhoz",
"ValueSpecialEpisodeName": "Special - {0}",
- "VersionNumber": "Verzió {0}"
+ "VersionNumber": "Verzió: {0}"
}
diff --git a/Emby.Server.Implementations/Localization/Core/it.json b/Emby.Server.Implementations/Localization/Core/it.json
index b3d9c16cf..a5f1e8f94 100644
--- a/Emby.Server.Implementations/Localization/Core/it.json
+++ b/Emby.Server.Implementations/Localization/Core/it.json
@@ -5,13 +5,13 @@
"Artists": "Artisti",
"AuthenticationSucceededWithUserName": "{0} autenticato con successo",
"Books": "Libri",
- "CameraImageUploadedFrom": "A new camera image has been uploaded from {0}",
+ "CameraImageUploadedFrom": "È stata caricata una nuova immagine della fotocamera {0}",
"Channels": "Canali",
"ChapterNameValue": "Capitolo {0}",
"Collections": "Collezioni",
"DeviceOfflineWithName": "{0} è stato disconnesso",
"DeviceOnlineWithName": "{0} è connesso",
- "FailedLoginAttemptWithUserName": "Tentativo di accesso fallito da {0}",
+ "FailedLoginAttemptWithUserName": "Tentativo di accesso fallito da {0}",
"Favorites": "Preferiti",
"Folders": "Cartelle",
"Genres": "Generi",
@@ -19,9 +19,9 @@
"HeaderCameraUploads": "Caricamenti Fotocamera",
"HeaderContinueWatching": "Continua a guardare",
"HeaderFavoriteAlbums": "Album preferiti",
- "HeaderFavoriteArtists": "Artisti preferiti",
+ "HeaderFavoriteArtists": "Artisti Preferiti",
"HeaderFavoriteEpisodes": "Episodi Preferiti",
- "HeaderFavoriteShows": "Show preferiti",
+ "HeaderFavoriteShows": "Serie TV Preferite",
"HeaderFavoriteSongs": "Brani Preferiti",
"HeaderLiveTV": "Diretta TV",
"HeaderNextUp": "Prossimo",
diff --git a/Emby.Server.Implementations/Localization/Core/kk.json b/Emby.Server.Implementations/Localization/Core/kk.json
index ae256f79d..658d168e9 100644
--- a/Emby.Server.Implementations/Localization/Core/kk.json
+++ b/Emby.Server.Implementations/Localization/Core/kk.json
@@ -1,97 +1,97 @@
{
- "Albums": "Альбомдар",
- "AppDeviceValues": "Қолданба: {0}, Құрылғы: {1}",
- "Application": "Қолданба",
- "Artists": "Орындаушылар",
- "AuthenticationSucceededWithUserName": "{0} түпнұсқалығын расталуы сәтті",
- "Books": "Кітаптар",
- "CameraImageUploadedFrom": "Жаңа сурет {0} камерасынан жүктеп алынды",
- "Channels": "Арналар",
- "ChapterNameValue": "{0}-сахна",
- "Collections": "Жиынтықтар",
- "DeviceOfflineWithName": "{0} ажыратылған",
- "DeviceOnlineWithName": "{0} қосылған",
- "FailedLoginAttemptWithUserName": "{0} тарапынан кіру әрекеті сәтсіз",
- "Favorites": "Таңдаулылар",
- "Folders": "Қалталар",
- "Genres": "Жанрлар",
- "HeaderAlbumArtists": "Альбом орындаушылары",
- "HeaderCameraUploads": "Камерадан жүктелгендер",
- "HeaderContinueWatching": "Қарауды жалғастыру",
- "HeaderFavoriteAlbums": "Таңдаулы альбомдар",
- "HeaderFavoriteArtists": "Таңдаулы орындаушылар",
- "HeaderFavoriteEpisodes": "Таңдаулы бөлімдер",
- "HeaderFavoriteShows": "Таңдаулы көрсетімдер",
- "HeaderFavoriteSongs": "Таңдаулы әуендер",
- "HeaderLiveTV": "Эфир",
- "HeaderNextUp": "Кезекті",
- "HeaderRecordingGroups": "Жазба топтары",
- "HomeVideos": "Үйлік бейнелер",
- "Inherit": "Мұраға иелену",
- "ItemAddedWithName": "{0} тасығышханаға үстелінді",
- "ItemRemovedWithName": "{0} тасығышханадан аласталды",
- "LabelIpAddressValue": "IP-мекенжайы: {0}",
- "LabelRunningTimeValue": "Іске қосылу уақыты: {0}",
- "Latest": "Ең кейінгі",
- "MessageApplicationUpdated": "Jellyfin Server жаңартылды.",
- "MessageApplicationUpdatedTo": "Jellyfin Server {0} үшін жаңартылды",
- "MessageNamedServerConfigurationUpdatedWithValue": "Сервер теңшелімі ({0} бөлімі) жаңартылды",
- "MessageServerConfigurationUpdated": "Сервер теңшелімі жаңартылды",
- "MixedContent": "Аралас мазмұн",
- "Movies": "Фильмдер",
- "Music": "Музыка",
- "MusicVideos": "Музыкалық бейнелер",
- "NameInstallFailed": "{0} орнатылуы сәтсіз",
- "NameSeasonNumber": "{0}-маусым",
- "NameSeasonUnknown": "Белгісіз маусым",
- "NewVersionIsAvailable": "Жаңа Jellyfin Server нұсқасы жүктеп алуға қолжетімді.",
- "NotificationOptionApplicationUpdateAvailable": "Қолданба жаңартуы қолжетімді",
- "NotificationOptionApplicationUpdateInstalled": "Қолданба жаңартуы орнатылды",
- "NotificationOptionAudioPlayback": "Дыбыс ойнатуы басталды",
- "NotificationOptionAudioPlaybackStopped": "Дыбыс ойнатуы тоқтатылды",
- "NotificationOptionCameraImageUploaded": "Камерадан фотосурет кері қотарылған",
- "NotificationOptionInstallationFailed": "Орнату сәтсіздігі",
- "NotificationOptionNewLibraryContent": "Жаңа мазмұн үстелген",
- "NotificationOptionPluginError": "Плагин сәтсіздігі",
- "NotificationOptionPluginInstalled": "Плагин орнатылды",
- "NotificationOptionPluginUninstalled": "Плагин орнатуы болдырылмады",
- "NotificationOptionPluginUpdateInstalled": "Плагин жаңартуы орнатылды",
- "NotificationOptionServerRestartRequired": "Серверді қайта іске қосу қажет",
- "NotificationOptionTaskFailed": "Жоспарлаған тапсырма сәтсіздігі",
- "NotificationOptionUserLockedOut": "Пайдаланушы құрсаулы",
- "NotificationOptionVideoPlayback": "Бейне ойнатуы басталды",
- "NotificationOptionVideoPlaybackStopped": "Бейне ойнатуы тоқтатылды",
- "Photos": "Фотосуреттер",
- "Playlists": "Ойнату тізімдері",
- "Plugin": "Плагин",
- "PluginInstalledWithName": "{0} орнатылды",
- "PluginUninstalledWithName": "{0} жойылды",
- "PluginUpdatedWithName": "{0} жаңартылды",
- "ProviderValue": "Жеткізуші: {0}",
- "ScheduledTaskFailedWithName": "{0} сәтсіз",
- "ScheduledTaskStartedWithName": "{0} іске қосылды",
- "ServerNameNeedsToBeRestarted": "{0} қайта іске қосу қажет",
- "Shows": "Көрсетімдер",
- "Songs": "Әуендер",
- "StartupEmbyServerIsLoading": "Jellyfin Server жүктелуде. Әрекетті көп ұзамай қайталаңыз.",
+ "Albums": "Álbomdar",
+ "AppDeviceValues": "Qoldanba: {0}, Qurylǵy: {1}",
+ "Application": "Qoldanba",
+ "Artists": "Oryndaýshylar",
+ "AuthenticationSucceededWithUserName": "{0} túpnusqalyǵyn rastalýy sátti",
+ "Books": "Kitaptar",
+ "CameraImageUploadedFrom": "Jańa sýret {0} kamerasynan júktep alyndy",
+ "Channels": "Arnalar",
+ "ChapterNameValue": "{0}-sahna",
+ "Collections": "Jıyntyqtar",
+ "DeviceOfflineWithName": "{0} ajyratylǵan",
+ "DeviceOnlineWithName": "{0} qosylǵan",
+ "FailedLoginAttemptWithUserName": "{0} tarapynan kirý áreketi sátsiz",
+ "Favorites": "Tańdaýlylar",
+ "Folders": "Qaltalar",
+ "Genres": "Janrlar",
+ "HeaderAlbumArtists": "Álbom oryndaýshylary",
+ "HeaderCameraUploads": "Kameradan júktelgender",
+ "HeaderContinueWatching": "Qaraýdy jalǵastyrý",
+ "HeaderFavoriteAlbums": "Tańdaýly álbomdar",
+ "HeaderFavoriteArtists": "Tańdaýly oryndaýshylar",
+ "HeaderFavoriteEpisodes": "Tańdaýly bólimder",
+ "HeaderFavoriteShows": "Tańdaýly kórsetimder",
+ "HeaderFavoriteSongs": "Tańdaýly áýender",
+ "HeaderLiveTV": "Efır",
+ "HeaderNextUp": "Kezekti",
+ "HeaderRecordingGroups": "Jazba toptary",
+ "HomeVideos": "Úılik beıneler",
+ "Inherit": "Muraǵa ıelený",
+ "ItemAddedWithName": "{0} tasyǵyshhanaǵa ústelindi",
+ "ItemRemovedWithName": "{0} tasyǵyshhanadan alastaldy",
+ "LabelIpAddressValue": "IP-mekenjaıy: {0}",
+ "LabelRunningTimeValue": "Oınatý ýaqyty: {0}",
+ "Latest": "Eń keıingi",
+ "MessageApplicationUpdated": "Jellyfin Serveri jańartyldy",
+ "MessageApplicationUpdatedTo": "Jellyfin Serveri {0} deńgeıge jańartyldy",
+ "MessageNamedServerConfigurationUpdatedWithValue": "Server teńsheliminiń {0} bólimi jańartyldy",
+ "MessageServerConfigurationUpdated": "Server teńshelimi jańartyldy",
+ "MixedContent": "Aralas mazmun",
+ "Movies": "Fılmder",
+ "Music": "Mýzyka",
+ "MusicVideos": "Mýzykalyq beıneler",
+ "NameInstallFailed": "{0} ornatylýy sátsiz",
+ "NameSeasonNumber": "{0}-maýsym",
+ "NameSeasonUnknown": "Belgisiz maýsym",
+ "NewVersionIsAvailable": "Jańa Jellyfin Server nusqasy júktep alýǵa qoljetimdi.",
+ "NotificationOptionApplicationUpdateAvailable": "Qoldanba jańartýy qoljetimdi",
+ "NotificationOptionApplicationUpdateInstalled": "Qoldanba jańartýy ornatyldy",
+ "NotificationOptionAudioPlayback": "Dybys oınatýy bastaldy",
+ "NotificationOptionAudioPlaybackStopped": "Dybys oınatýy toqtatyldy",
+ "NotificationOptionCameraImageUploaded": "Kameradan fotosýret keri qotarylǵan",
+ "NotificationOptionInstallationFailed": "Ornatý sátsizdigi",
+ "NotificationOptionNewLibraryContent": "Jańa mazmun ústelgen",
+ "NotificationOptionPluginError": "Plagın sátsizdigi",
+ "NotificationOptionPluginInstalled": "Plagın ornatyldy",
+ "NotificationOptionPluginUninstalled": "Plagın ornatýy boldyrylmady",
+ "NotificationOptionPluginUpdateInstalled": "Plagın jańartýy ornatyldy",
+ "NotificationOptionServerRestartRequired": "Serverdi qaıta iske qosý qajet",
+ "NotificationOptionTaskFailed": "Josparlaǵan tapsyrma sátsizdigi",
+ "NotificationOptionUserLockedOut": "Paıdalanýshy qursaýly",
+ "NotificationOptionVideoPlayback": "Beıne oınatýy bastaldy",
+ "NotificationOptionVideoPlaybackStopped": "Beıne oınatýy toqtatyldy",
+ "Photos": "Fotosýretter",
+ "Playlists": "Oınatý tizimderi",
+ "Plugin": "Plagın",
+ "PluginInstalledWithName": "{0} ornatyldy",
+ "PluginUninstalledWithName": "{0} joıyldy",
+ "PluginUpdatedWithName": "{0} jańartyldy",
+ "ProviderValue": "Jetkizýshi: {0}",
+ "ScheduledTaskFailedWithName": "{0} sátsiz",
+ "ScheduledTaskStartedWithName": "{0} iske qosyldy",
+ "ServerNameNeedsToBeRestarted": "{0} qaıta iske qosý qajet",
+ "Shows": "Kórsetimder",
+ "Songs": "Áýender",
+ "StartupEmbyServerIsLoading": "Jellyfin Server júktelýde. Áreketti kóp uzamaı qaıtalańyz.",
"SubtitleDownloadFailureForItem": "Субтитрлер {0} үшін жүктеліп алынуы сәтсіз",
- "SubtitleDownloadFailureFromForItem": "Subtitles failed to download from {0} for {1}",
- "SubtitlesDownloadedForItem": "{0} үшін субтитрлер жүктеліп алынды",
- "Sync": "Үндестіру",
- "System": "Жүйе",
- "TvShows": "ТД-көрсетімдер",
- "User": "Пайдаланушы",
- "UserCreatedWithName": "Пайдаланушы {0} жасалған",
- "UserDeletedWithName": "Пайдаланушы {0} жойылған",
- "UserDownloadingItemWithValues": "{0} мынаны жүктеп алуда: {1}",
- "UserLockedOutWithName": "Пайдаланушы {0} құрсаулы",
- "UserOfflineFromDevice": "{0} - {1} тарапынан ажыратылған",
- "UserOnlineFromDevice": "{0} - {1} арқылы қосылған",
- "UserPasswordChangedWithName": "Пайдаланушы {0} үшін құпия сөз өзгертілді",
- "UserPolicyUpdatedWithName": "Пайдаланушы {0} үшін саясаттары жаңартылды",
- "UserStartedPlayingItemWithValues": "{0} - {1} ойнатуын {2} бастады",
- "UserStoppedPlayingItemWithValues": "{0} - {1} ойнатуын {2} тоқтатты",
- "ValueHasBeenAddedToLibrary": "{0} (тасығышханаға үстелінді)",
- "ValueSpecialEpisodeName": "Арнайы - {0}",
- "VersionNumber": "Нұсқасы: {0}"
+ "SubtitleDownloadFailureFromForItem": "{1} úshin sýbtıtrlerdi {0} kózinen júktep alý sátsiz",
+ "SubtitlesDownloadedForItem": "{0} úshin sýbtıtrler júktelip alyndy",
+ "Sync": "Úndestirý",
+ "System": "Júıe",
+ "TvShows": "TD-kórsetimder",
+ "User": "Paıdalanýshy",
+ "UserCreatedWithName": "Paıdalanýshy {0} jasalǵan",
+ "UserDeletedWithName": "Paıdalanýshy {0} joıylǵan",
+ "UserDownloadingItemWithValues": "{0} mynany júktep alýda: {1}",
+ "UserLockedOutWithName": "Paıdalanýshy {0} qursaýly",
+ "UserOfflineFromDevice": "{0} - {1} tarapynan ajyratylǵan",
+ "UserOnlineFromDevice": "{0} - {1} arqyly qosylǵan",
+ "UserPasswordChangedWithName": "Paıdalanýshy {0} úshin paról ózgertildi",
+ "UserPolicyUpdatedWithName": "Paıdalanýshy {0} úshin saıasattary jańartyldy",
+ "UserStartedPlayingItemWithValues": "{0} - {1} oınatýyn {2} bastady",
+ "UserStoppedPlayingItemWithValues": "{0} - {1} oınatýyn {2} toqtatty",
+ "ValueHasBeenAddedToLibrary": "{0} (tasyǵyshhanaǵa ústelindi)",
+ "ValueSpecialEpisodeName": "Arnaıy - {0}",
+ "VersionNumber": "Nusqasy {0}"
}
diff --git a/Emby.Server.Implementations/Localization/Core/ms.json b/Emby.Server.Implementations/Localization/Core/ms.json
index c01bb0c50..c10fbe58f 100644
--- a/Emby.Server.Implementations/Localization/Core/ms.json
+++ b/Emby.Server.Implementations/Localization/Core/ms.json
@@ -1,10 +1,10 @@
{
- "Albums": "Albums",
+ "Albums": "Album-album",
"AppDeviceValues": "App: {0}, Device: {1}",
"Application": "Application",
- "Artists": "Artists",
+ "Artists": "Artis-artis",
"AuthenticationSucceededWithUserName": "{0} successfully authenticated",
- "Books": "Books",
+ "Books": "Buku-buku",
"CameraImageUploadedFrom": "A new camera image has been uploaded from {0}",
"Channels": "Channels",
"ChapterNameValue": "Chapter {0}",
diff --git a/Emby.Server.Implementations/Localization/Core/nl.json b/Emby.Server.Implementations/Localization/Core/nl.json
index 589753c44..24af1839f 100644
--- a/Emby.Server.Implementations/Localization/Core/nl.json
+++ b/Emby.Server.Implementations/Localization/Core/nl.json
@@ -5,28 +5,28 @@
"Artists": "Artiesten",
"AuthenticationSucceededWithUserName": "{0} is succesvol geverifieerd",
"Books": "Boeken",
- "CameraImageUploadedFrom": "A new camera image has been uploaded from {0}",
+ "CameraImageUploadedFrom": "Er is een nieuwe foto toegevoegd via {0}",
"Channels": "Kanalen",
"ChapterNameValue": "Hoofdstuk {0}",
"Collections": "Collecties",
- "DeviceOfflineWithName": "{0} is losgekoppeld",
+ "DeviceOfflineWithName": "{0} heeft de verbinding verbroken",
"DeviceOnlineWithName": "{0} is verbonden",
"FailedLoginAttemptWithUserName": "Mislukte aanmeld poging van {0}",
"Favorites": "Favorieten",
"Folders": "Mappen",
"Genres": "Genres",
- "HeaderAlbumArtists": "Album artiesten",
- "HeaderCameraUploads": "Camera uploads",
+ "HeaderAlbumArtists": "Albumartiesten",
+ "HeaderCameraUploads": "Camera-uploads",
"HeaderContinueWatching": "Kijken hervatten",
"HeaderFavoriteAlbums": "Favoriete albums",
"HeaderFavoriteArtists": "Favoriete artiesten",
"HeaderFavoriteEpisodes": "Favoriete afleveringen",
"HeaderFavoriteShows": "Favoriete shows",
- "HeaderFavoriteSongs": "Favoriete titels",
+ "HeaderFavoriteSongs": "Favoriete nummers",
"HeaderLiveTV": "Live TV",
"HeaderNextUp": "Volgende",
"HeaderRecordingGroups": "Opnamegroepen",
- "HomeVideos": "Thuis video's",
+ "HomeVideos": "Start video's",
"Inherit": "Overerven",
"ItemAddedWithName": "{0} is toegevoegd aan de bibliotheek",
"ItemRemovedWithName": "{0} is verwijderd uit de bibliotheek",
@@ -34,22 +34,22 @@
"LabelRunningTimeValue": "Looptijd: {0}",
"Latest": "Nieuwste",
"MessageApplicationUpdated": "Jellyfin Server is bijgewerkt",
- "MessageApplicationUpdatedTo": "Jellyfin Server has been updated to {0}",
+ "MessageApplicationUpdatedTo": "Jellyfin Server is bijgewerkt naar {0}",
"MessageNamedServerConfigurationUpdatedWithValue": "Sectie {0} van de server configuratie is bijgewerkt",
"MessageServerConfigurationUpdated": "Server configuratie is bijgewerkt",
"MixedContent": "Gemengde inhoud",
"Movies": "Films",
"Music": "Muziek",
"MusicVideos": "Muziekvideo's",
- "NameInstallFailed": "{0} installation failed",
+ "NameInstallFailed": "{0} installatie mislukt",
"NameSeasonNumber": "Seizoen {0}",
"NameSeasonUnknown": "Seizoen onbekend",
- "NewVersionIsAvailable": "A new version of Jellyfin Server is available for download.",
+ "NewVersionIsAvailable": "Een nieuwe versie van Jellyfin Server is beschikbaar om te downloaden.",
"NotificationOptionApplicationUpdateAvailable": "Programma-update beschikbaar",
"NotificationOptionApplicationUpdateInstalled": "Programma-update geïnstalleerd",
- "NotificationOptionAudioPlayback": "Geluid gestart",
- "NotificationOptionAudioPlaybackStopped": "Geluid gestopt",
- "NotificationOptionCameraImageUploaded": "Camera afbeelding geüpload",
+ "NotificationOptionAudioPlayback": "Muziek gestart",
+ "NotificationOptionAudioPlaybackStopped": "Muziek gestopt",
+ "NotificationOptionCameraImageUploaded": "Camera-afbeelding geüpload",
"NotificationOptionInstallationFailed": "Installatie mislukt",
"NotificationOptionNewLibraryContent": "Nieuwe content toegevoegd",
"NotificationOptionPluginError": "Plug-in fout",
@@ -70,12 +70,12 @@
"ProviderValue": "Aanbieder: {0}",
"ScheduledTaskFailedWithName": "{0} is mislukt",
"ScheduledTaskStartedWithName": "{0} is gestart",
- "ServerNameNeedsToBeRestarted": "{0} needs to be restarted",
+ "ServerNameNeedsToBeRestarted": "{0} moet herstart worden",
"Shows": "Series",
- "Songs": "Titels",
+ "Songs": "Nummers",
"StartupEmbyServerIsLoading": "Jellyfin Server is aan het laden, probeer het later opnieuw.",
"SubtitleDownloadFailureForItem": "Downloaden van ondertiteling voor {0} is mislukt",
- "SubtitleDownloadFailureFromForItem": "Subtitles failed to download from {0} for {1}",
+ "SubtitleDownloadFailureFromForItem": "Ondertitels konden niet gedownload worden van {0} voor {1}",
"SubtitlesDownloadedForItem": "Ondertiteling voor {0} is gedownload",
"Sync": "Synchronisatie",
"System": "Systeem",
@@ -89,9 +89,9 @@
"UserOnlineFromDevice": "{0} heeft verbinding met {1}",
"UserPasswordChangedWithName": "Wachtwoord voor {0} is gewijzigd",
"UserPolicyUpdatedWithName": "Gebruikersbeleid gewijzigd voor {0}",
- "UserStartedPlayingItemWithValues": "{0} heeft afspelen van {1} gestart",
- "UserStoppedPlayingItemWithValues": "{0} heeft afspelen van {1} gestopt",
- "ValueHasBeenAddedToLibrary": "{0} has been added to your media library",
+ "UserStartedPlayingItemWithValues": "{0} heeft afspelen van {1} gestart op {2}",
+ "UserStoppedPlayingItemWithValues": "{0} heeft afspelen van {1} gestopt op {2}",
+ "ValueHasBeenAddedToLibrary": "{0} is toegevoegd aan je mediabibliotheek",
"ValueSpecialEpisodeName": "Speciaal - {0}",
"VersionNumber": "Versie {0}"
}
diff --git a/Emby.Server.Implementations/Localization/Core/ru.json b/Emby.Server.Implementations/Localization/Core/ru.json
index d799fa50b..c0465def8 100644
--- a/Emby.Server.Implementations/Localization/Core/ru.json
+++ b/Emby.Server.Implementations/Localization/Core/ru.json
@@ -5,7 +5,7 @@
"Artists": "Исполнители",
"AuthenticationSucceededWithUserName": "{0} - авторизация успешна",
"Books": "Литература",
- "CameraImageUploadedFrom": "Новое фото было выложено с {0}",
+ "CameraImageUploadedFrom": "Новое фото было выложено с камеры {0}",
"Channels": "Каналы",
"ChapterNameValue": "Сцена {0}",
"Collections": "Коллекции",
@@ -31,20 +31,20 @@
"ItemAddedWithName": "{0} - добавлено в медиатеку",
"ItemRemovedWithName": "{0} - изъято из медиатеки",
"LabelIpAddressValue": "IP-адрес: {0}",
- "LabelRunningTimeValue": "Время выполнения: {0}",
+ "LabelRunningTimeValue": "Длительность: {0}",
"Latest": "Новейшее",
"MessageApplicationUpdated": "Jellyfin Server был обновлён",
"MessageApplicationUpdatedTo": "Jellyfin Server был обновлён до {0}",
"MessageNamedServerConfigurationUpdatedWithValue": "Конфиг-ия сервера (раздел {0}) была обновлена",
"MessageServerConfigurationUpdated": "Конфиг-ия сервера была обновлена",
- "MixedContent": "Смешанное содержание",
+ "MixedContent": "Смешанное содержимое",
"Movies": "Кино",
"Music": "Музыка",
"MusicVideos": "Муз. видео",
"NameInstallFailed": "Установка {0} неудачна",
"NameSeasonNumber": "Сезон {0}",
"NameSeasonUnknown": "Сезон неопознан",
- "NewVersionIsAvailable": "Имеется новая версия Jellyfin Server",
+ "NewVersionIsAvailable": "Новая версия Jellyfin Server доступна для загрузки.",
"NotificationOptionApplicationUpdateAvailable": "Имеется обновление приложения",
"NotificationOptionApplicationUpdateInstalled": "Обновление приложения установлено",
"NotificationOptionAudioPlayback": "Воспр-ие аудио зап-но",
@@ -75,7 +75,7 @@
"Songs": "Композиции",
"StartupEmbyServerIsLoading": "Jellyfin Server загружается. Повторите попытку в ближайшее время.",
"SubtitleDownloadFailureForItem": "Субтитры к {0} не удалось загрузить",
- "SubtitleDownloadFailureFromForItem": "Subtitles failed to download from {0} for {1}",
+ "SubtitleDownloadFailureFromForItem": "Субтитры к {1} не удалось загрузить с {0}",
"SubtitlesDownloadedForItem": "Субтитры к {0} загружены",
"Sync": "Синхро",
"System": "Система",
diff --git a/Emby.Server.Implementations/Localization/Ratings/kz.csv b/Emby.Server.Implementations/Localization/Ratings/kz.csv
index 4441c5650..d546bff53 100644
--- a/Emby.Server.Implementations/Localization/Ratings/kz.csv
+++ b/Emby.Server.Implementations/Localization/Ratings/kz.csv
@@ -1,6 +1,7 @@
-KZ-К,1
-KZ-БА,6
-KZ-Б14,7
-KZ-Е16,8
-KZ-Е18,10
-KZ-НА,15
+KZ-6-,0
+KZ-6+,6
+KZ-12+,12
+KZ-14+,14
+KZ-16+,16
+KZ-18+,18
+KZ-21+,21
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/ScheduledTasks/Triggers/DailyTrigger.cs b/Emby.Server.Implementations/ScheduledTasks/Triggers/DailyTrigger.cs
index 98685cebe..ec9466c4a 100644
--- a/Emby.Server.Implementations/ScheduledTasks/Triggers/DailyTrigger.cs
+++ b/Emby.Server.Implementations/ScheduledTasks/Triggers/DailyTrigger.cs
@@ -44,7 +44,7 @@ namespace Emby.Server.Implementations.ScheduledTasks
var dueTime = triggerDate - now;
- logger.LogInformation("Daily trigger for {0} set to fire at {1}, which is {2} minutes from now.", taskName, triggerDate.ToString(), dueTime.TotalMinutes.ToString(CultureInfo.InvariantCulture));
+ logger.LogInformation("Daily trigger for {Task} set to fire at {TriggerDate:g}, which is {DueTime:g} from now.", taskName, triggerDate, dueTime);
Timer = new Timer(state => OnTriggered(), null, dueTime, TimeSpan.FromMilliseconds(-1));
}
diff --git a/Emby.Server.Implementations/Serialization/JsonSerializer.cs b/Emby.Server.Implementations/Serialization/JsonSerializer.cs
index 44898d498..8ae7fd90c 100644
--- a/Emby.Server.Implementations/Serialization/JsonSerializer.cs
+++ b/Emby.Server.Implementations/Serialization/JsonSerializer.cs
@@ -42,6 +42,27 @@ namespace Emby.Server.Implementations.Serialization
}
/// <summary>
+ /// Serializes to stream.
+ /// </summary>
+ /// <param name="obj">The obj.</param>
+ /// <param name="stream">The stream.</param>
+ /// <exception cref="ArgumentNullException">obj</exception>
+ public void SerializeToStream<T>(T obj, Stream stream)
+ {
+ if (obj == null)
+ {
+ throw new ArgumentNullException(nameof(obj));
+ }
+
+ if (stream == null)
+ {
+ throw new ArgumentNullException(nameof(stream));
+ }
+
+ ServiceStack.Text.JsonSerializer.SerializeToStream<T>(obj, stream);
+ }
+
+ /// <summary>
/// Serializes to file.
/// </summary>
/// <param name="obj">The obj.</param>
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/Emby.Server.Implementations/Updates/InstallationManager.cs b/Emby.Server.Implementations/Updates/InstallationManager.cs
index dc7f57f27..301802b8a 100644
--- a/Emby.Server.Implementations/Updates/InstallationManager.cs
+++ b/Emby.Server.Implementations/Updates/InstallationManager.cs
@@ -116,6 +116,7 @@ namespace Emby.Server.Implementations.Updates
private readonly IApplicationHost _applicationHost;
private readonly ICryptoProvider _cryptographyProvider;
+ private readonly IZipClient _zipClient;
// netframework or netcore
private readonly string _packageRuntime;
@@ -129,6 +130,7 @@ namespace Emby.Server.Implementations.Updates
IServerConfigurationManager config,
IFileSystem fileSystem,
ICryptoProvider cryptographyProvider,
+ IZipClient zipClient,
string packageRuntime)
{
if (loggerFactory == null)
@@ -146,6 +148,7 @@ namespace Emby.Server.Implementations.Updates
_config = config;
_fileSystem = fileSystem;
_cryptographyProvider = cryptographyProvider;
+ _zipClient = zipClient;
_packageRuntime = packageRuntime;
_logger = loggerFactory.CreateLogger(nameof(InstallationManager));
}
@@ -526,14 +529,18 @@ namespace Emby.Server.Implementations.Updates
private async Task PerformPackageInstallation(IProgress<double> progress, string target, PackageVersionInfo package, CancellationToken cancellationToken)
{
- // Target based on if it is an archive or single assembly
- // zip archives are assumed to contain directory structures relative to our ProgramDataPath
var extension = Path.GetExtension(package.targetFilename);
- var isArchive = string.Equals(extension, ".zip", StringComparison.OrdinalIgnoreCase) || string.Equals(extension, ".rar", StringComparison.OrdinalIgnoreCase) || string.Equals(extension, ".7z", StringComparison.OrdinalIgnoreCase);
+ var isArchive = string.Equals(extension, ".zip", StringComparison.OrdinalIgnoreCase);
+
+ if (!isArchive)
+ {
+ _logger.LogError("Only zip packages are supported. {Filename} is not a zip archive.", package.targetFilename);
+ return;
+ }
if (target == null)
{
- target = Path.Combine(isArchive ? _appPaths.TempUpdatePath : _appPaths.PluginsPath, package.targetFilename);
+ target = Path.Combine(_appPaths.PluginsPath, Path.GetFileNameWithoutExtension(package.targetFilename));
}
// Download to temporary file so that, if interrupted, it won't destroy the existing installation
@@ -547,36 +554,19 @@ namespace Emby.Server.Implementations.Updates
cancellationToken.ThrowIfCancellationRequested();
- // Validate with a checksum
- var packageChecksum = string.IsNullOrWhiteSpace(package.checksum) ? Guid.Empty : new Guid(package.checksum);
- if (!packageChecksum.Equals(Guid.Empty)) // support for legacy uploads for now
- {
- using (var stream = File.OpenRead(tempFile))
- {
- var check = Guid.Parse(BitConverter.ToString(_cryptographyProvider.ComputeMD5(stream)).Replace("-", string.Empty));
- if (check != packageChecksum)
- {
- throw new Exception(string.Format("Download validation failed for {0}. Probably corrupted during transfer.", package.name));
- }
- }
- }
-
- cancellationToken.ThrowIfCancellationRequested();
+ // TODO: Validate with a checksum, *properly*
// Success - move it to the real target
try
{
- Directory.CreateDirectory(Path.GetDirectoryName(target));
- File.Copy(tempFile, target, true);
- //If it is an archive - write out a version file so we know what it is
- if (isArchive)
+ using (var stream = File.OpenRead(tempFile))
{
- File.WriteAllText(target + ".ver", package.versionStr);
+ _zipClient.ExtractAllFromZip(stream, target, true);
}
}
catch (IOException ex)
{
- _logger.LogError(ex, "Error attempting to move file from {TempFile} to {TargetFile}", tempFile, target);
+ _logger.LogError(ex, "Error attempting to extract {TempFile} to {TargetFile}", tempFile, target);
throw;
}