diff options
| author | dkanada <dkanada@users.noreply.github.com> | 2020-04-11 20:10:16 +0900 |
|---|---|---|
| committer | dkanada <dkanada@users.noreply.github.com> | 2020-04-11 20:10:16 +0900 |
| commit | bd55bdb4e3ff4555a722321f92a4783f913204f3 (patch) | |
| tree | 1cdfcd2c8da8bfc79922c3bacf17d87ae448b701 /Emby.Server.Implementations | |
| parent | cbe1fe2c8f82af2233201203367ba8a532dbec8b (diff) | |
| parent | 299541f1b26136ef89741f28c7949cda4e5e485f (diff) | |
merge branch master into plugin
Diffstat (limited to 'Emby.Server.Implementations')
175 files changed, 2162 insertions, 1494 deletions
diff --git a/Emby.Server.Implementations/Activity/ActivityLogEntryPoint.cs b/Emby.Server.Implementations/Activity/ActivityLogEntryPoint.cs index c91506b85..13733b60f 100644 --- a/Emby.Server.Implementations/Activity/ActivityLogEntryPoint.cs +++ b/Emby.Server.Implementations/Activity/ActivityLogEntryPoint.cs @@ -1,5 +1,4 @@ #pragma warning disable CS1591 -#pragma warning disable SA1600 using System; using System.Collections.Generic; @@ -9,7 +8,6 @@ using System.Text; using System.Threading.Tasks; using MediaBrowser.Common.Plugins; using MediaBrowser.Common.Updates; -using MediaBrowser.Controller; using MediaBrowser.Controller.Authentication; using MediaBrowser.Controller.Devices; using MediaBrowser.Controller.Entities; @@ -29,7 +27,7 @@ using Microsoft.Extensions.Logging; namespace Emby.Server.Implementations.Activity { - public class ActivityLogEntryPoint : IServerEntryPoint + public sealed class ActivityLogEntryPoint : IServerEntryPoint { private readonly ILogger _logger; private readonly IInstallationManager _installationManager; @@ -39,7 +37,6 @@ namespace Emby.Server.Implementations.Activity private readonly ILocalizationManager _localization; private readonly ISubtitleManager _subManager; private readonly IUserManager _userManager; - private readonly IServerApplicationHost _appHost; private readonly IDeviceManager _deviceManager; /// <summary> @@ -64,8 +61,7 @@ namespace Emby.Server.Implementations.Activity ILocalizationManager localization, IInstallationManager installationManager, ISubtitleManager subManager, - IUserManager userManager, - IServerApplicationHost appHost) + IUserManager userManager) { _logger = logger; _sessionManager = sessionManager; @@ -76,7 +72,6 @@ namespace Emby.Server.Implementations.Activity _installationManager = installationManager; _subManager = subManager; _userManager = userManager; - _appHost = appHost; } public Task RunAsync() @@ -141,7 +136,7 @@ namespace Emby.Server.Implementations.Activity CultureInfo.InvariantCulture, _localization.GetLocalizedString("SubtitleDownloadFailureFromForItem"), e.Provider, - Notifications.Notifications.GetItemName(e.Item)), + Emby.Notifications.NotificationEntryPoint.GetItemName(e.Item)), Type = "SubtitleDownloadFailure", ItemId = e.Item.Id.ToString("N", CultureInfo.InvariantCulture), ShortOverview = e.Exception.Message @@ -533,6 +528,7 @@ namespace Emby.Server.Implementations.Activity private void CreateLogEntry(ActivityLogEntry entry) => _activityManager.Create(entry); + /// <inheritdoc /> public void Dispose() { _taskManager.TaskCompleted -= OnTaskCompleted; diff --git a/Emby.Server.Implementations/Activity/ActivityManager.cs b/Emby.Server.Implementations/Activity/ActivityManager.cs index 6712c4782..ee10845cf 100644 --- a/Emby.Server.Implementations/Activity/ActivityManager.cs +++ b/Emby.Server.Implementations/Activity/ActivityManager.cs @@ -1,5 +1,4 @@ #pragma warning disable CS1591 -#pragma warning disable SA1600 using System; using MediaBrowser.Controller.Library; diff --git a/Emby.Server.Implementations/Activity/ActivityRepository.cs b/Emby.Server.Implementations/Activity/ActivityRepository.cs index 633343bb6..7be72319e 100644 --- a/Emby.Server.Implementations/Activity/ActivityRepository.cs +++ b/Emby.Server.Implementations/Activity/ActivityRepository.cs @@ -1,5 +1,4 @@ #pragma warning disable CS1591 -#pragma warning disable SA1600 using System; using System.Collections.Generic; diff --git a/Emby.Server.Implementations/AppBase/BaseApplicationPaths.cs b/Emby.Server.Implementations/AppBase/BaseApplicationPaths.cs index c3cdcc222..bc4781743 100644 --- a/Emby.Server.Implementations/AppBase/BaseApplicationPaths.cs +++ b/Emby.Server.Implementations/AppBase/BaseApplicationPaths.cs @@ -5,7 +5,7 @@ using MediaBrowser.Common.Configuration; namespace Emby.Server.Implementations.AppBase { /// <summary> - /// Provides a base class to hold common application paths used by both the Ui and Server. + /// Provides a base class to hold common application paths used by both the UI and Server. /// This can be subclassed to add application-specific paths. /// </summary> public abstract class BaseApplicationPaths : IApplicationPaths @@ -37,10 +37,7 @@ namespace Emby.Server.Implementations.AppBase /// <value>The program data path.</value> public string ProgramDataPath { get; } - /// <summary> - /// Gets the path to the web UI resources folder. - /// </summary> - /// <value>The web UI resources path.</value> + /// <inheritdoc/> public string WebPath { get; } /// <summary> diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs index 3cf4d6c6d..c926896cf 100644 --- a/Emby.Server.Implementations/ApplicationHost.cs +++ b/Emby.Server.Implementations/ApplicationHost.cs @@ -1,5 +1,4 @@ #pragma warning disable CS1591 -#pragma warning disable SA1600 using System; using System.Collections.Concurrent; @@ -44,6 +43,7 @@ using Emby.Server.Implementations.Playlists; using Emby.Server.Implementations.ScheduledTasks; using Emby.Server.Implementations.Security; using Emby.Server.Implementations.Serialization; +using Emby.Server.Implementations.Services; using Emby.Server.Implementations.Session; using Emby.Server.Implementations.SocketSharp; using Emby.Server.Implementations.TV; @@ -100,8 +100,8 @@ using MediaBrowser.Model.Tasks; using MediaBrowser.Model.Updates; using MediaBrowser.Providers.Chapters; using MediaBrowser.Providers.Manager; +using MediaBrowser.Providers.Plugins.TheTvdb; using MediaBrowser.Providers.Subtitles; -using MediaBrowser.Providers.TV.TheTVDB; using MediaBrowser.WebDashboard.Api; using MediaBrowser.XbmcMetadata.Providers; using Microsoft.AspNetCore.Http; @@ -118,8 +118,12 @@ namespace Emby.Server.Implementations /// </summary> public abstract class ApplicationHost : IServerApplicationHost, IDisposable { - private SqliteUserRepository _userRepository; + /// <summary> + /// The environment variable prefixes to log at server startup. + /// </summary> + private static readonly string[] _relevantEnvVarPrefixes = { "JELLYFIN_", "DOTNET_", "ASPNETCORE_" }; + private SqliteUserRepository _userRepository; private SqliteDisplayPreferencesRepository _displayPreferencesRepository; /// <summary> @@ -167,10 +171,9 @@ namespace Emby.Server.Implementations public bool IsShuttingDown { get; private set; } /// <summary> - /// Gets or sets the logger. + /// Gets the logger. /// </summary> - /// <value>The logger.</value> - protected ILogger Logger { get; set; } + protected ILogger Logger { get; } private IPlugin[] _plugins; @@ -181,10 +184,9 @@ namespace Emby.Server.Implementations public IReadOnlyList<IPlugin> Plugins => _plugins; /// <summary> - /// Gets or sets the logger factory. + /// Gets the logger factory. /// </summary> - /// <value>The logger factory.</value> - public ILoggerFactory LoggerFactory { get; protected set; } + protected ILoggerFactory LoggerFactory { get; } /// <summary> /// Gets or sets the application paths. @@ -227,11 +229,6 @@ namespace Emby.Server.Implementations public int HttpsPort { get; private set; } /// <summary> - /// Gets the content root for the webhost. - /// </summary> - public string ContentRoot { get; private set; } - - /// <summary> /// Gets the server configuration manager. /// </summary> /// <value>The server configuration manager.</value> @@ -315,8 +312,6 @@ namespace Emby.Server.Implementations private IMediaSourceManager MediaSourceManager { get; set; } - private readonly IConfiguration _configuration; - /// <summary> /// Gets the installation manager. /// </summary> @@ -354,11 +349,8 @@ namespace Emby.Server.Implementations IStartupOptions options, IFileSystem fileSystem, IImageEncoder imageEncoder, - INetworkManager networkManager, - IConfiguration configuration) + INetworkManager networkManager) { - _configuration = configuration; - XmlSerializer = new MyXmlSerializer(); NetworkManager = networkManager; @@ -574,7 +566,8 @@ namespace Emby.Server.Implementations } } - public async Task InitAsync(IServiceCollection serviceCollection) + /// <inheritdoc/> + public async Task InitAsync(IServiceCollection serviceCollection, IConfiguration startupConfig) { HttpPort = ServerConfigurationManager.Configuration.HttpServerPortNumber; HttpsPort = ServerConfigurationManager.Configuration.HttpsPortNumber; @@ -607,13 +600,7 @@ namespace Emby.Server.Implementations DiscoverTypes(); - await RegisterResources(serviceCollection).ConfigureAwait(false); - - ContentRoot = ServerConfigurationManager.Configuration.DashboardSourcePath; - if (string.IsNullOrEmpty(ContentRoot)) - { - ContentRoot = ServerConfigurationManager.ApplicationPaths.WebPath; - } + await RegisterServices(serviceCollection, startupConfig).ConfigureAwait(false); } public async Task ExecuteWebsocketHandlerAsync(HttpContext context, Func<Task> next) @@ -639,14 +626,14 @@ namespace Emby.Server.Implementations var response = context.Response; var localPath = context.Request.Path.ToString(); - var req = new WebSocketSharpRequest(request, response, request.Path, Logger); + var req = new WebSocketSharpRequest(request, response, request.Path, LoggerFactory.CreateLogger<WebSocketSharpRequest>()); await HttpServer.RequestHandler(req, request.GetDisplayUrl(), request.Host.ToString(), localPath, context.RequestAborted).ConfigureAwait(false); } /// <summary> - /// Registers resources that classes will depend on + /// Registers services/resources with the service collection that will be available via DI. /// </summary> - protected async Task RegisterResources(IServiceCollection serviceCollection) + protected async Task RegisterServices(IServiceCollection serviceCollection, IConfiguration startupConfig) { serviceCollection.AddMemoryCache(); @@ -655,16 +642,13 @@ namespace Emby.Server.Implementations serviceCollection.AddSingleton<IApplicationPaths>(ApplicationPaths); - serviceCollection.AddSingleton<IConfiguration>(_configuration); - serviceCollection.AddSingleton(JsonSerializer); - serviceCollection.AddSingleton(LoggerFactory); - serviceCollection.AddLogging(); - serviceCollection.AddSingleton(Logger); + // TODO: Support for injecting ILogger should be deprecated in favour of ILogger<T> and this removed + serviceCollection.AddSingleton<ILogger>(Logger); serviceCollection.AddSingleton(FileSystemManager); - serviceCollection.AddSingleton<TvDbClientManager>(); + serviceCollection.AddSingleton<TvdbClientManager>(); HttpClient = new HttpClientManager.HttpClientManager( ApplicationPaths, @@ -749,7 +733,7 @@ namespace Emby.Server.Implementations ProcessFactory, LocalizationManager, () => SubtitleEncoder, - _configuration, + startupConfig, StartupOptions.FFmpegPath); serviceCollection.AddSingleton(MediaEncoder); @@ -767,20 +751,9 @@ namespace Emby.Server.Implementations CertificateInfo = GetCertificateInfo(true); Certificate = GetCertificate(CertificateInfo); - HttpServer = new HttpListenerHost( - this, - LoggerFactory.CreateLogger<HttpListenerHost>(), - ServerConfigurationManager, - _configuration, - NetworkManager, - JsonSerializer, - XmlSerializer, - CreateHttpListener()) - { - GlobalResponse = LocalizationManager.GetLocalizedString("StartupEmbyServerIsLoading") - }; - - serviceCollection.AddSingleton(HttpServer); + serviceCollection.AddSingleton<ServiceController>(); + serviceCollection.AddSingleton<IHttpListener, WebSocketSharpListener>(); + serviceCollection.AddSingleton<IHttpServer, HttpListenerHost>(); ImageProcessor = new ImageProcessor(LoggerFactory.CreateLogger<ImageProcessor>(), ServerConfigurationManager.ApplicationPaths, FileSystemManager, ImageEncoder, () => LibraryManager, () => MediaEncoder); serviceCollection.AddSingleton(ImageProcessor); @@ -806,7 +779,18 @@ namespace Emby.Server.Implementations ChannelManager = new ChannelManager(UserManager, DtoService, LibraryManager, LoggerFactory, ServerConfigurationManager, FileSystemManager, UserDataManager, JsonSerializer, ProviderManager); serviceCollection.AddSingleton(ChannelManager); - SessionManager = new SessionManager(UserDataManager, LoggerFactory, LibraryManager, UserManager, musicManager, DtoService, ImageProcessor, this, AuthenticationRepository, DeviceManager, MediaSourceManager); + SessionManager = new SessionManager( + LoggerFactory.CreateLogger<SessionManager>(), + UserDataManager, + LibraryManager, + UserManager, + musicManager, + DtoService, + ImageProcessor, + this, + AuthenticationRepository, + DeviceManager, + MediaSourceManager); serviceCollection.AddSingleton(SessionManager); serviceCollection.AddSingleton<IDlnaManager>( @@ -823,15 +807,23 @@ namespace Emby.Server.Implementations UserViewManager = new UserViewManager(LibraryManager, LocalizationManager, UserManager, ChannelManager, LiveTvManager, ServerConfigurationManager); serviceCollection.AddSingleton(UserViewManager); - NotificationManager = new NotificationManager(LoggerFactory, UserManager, ServerConfigurationManager); + NotificationManager = new NotificationManager( + LoggerFactory.CreateLogger<NotificationManager>(), + UserManager, + ServerConfigurationManager); serviceCollection.AddSingleton(NotificationManager); serviceCollection.AddSingleton<IDeviceDiscovery>(new DeviceDiscovery(ServerConfigurationManager)); - ChapterManager = new ChapterManager(LibraryManager, LoggerFactory, ServerConfigurationManager, ItemRepository); + ChapterManager = new ChapterManager(ItemRepository); serviceCollection.AddSingleton(ChapterManager); - EncodingManager = new MediaEncoder.EncodingManager(FileSystemManager, LoggerFactory, MediaEncoder, ChapterManager, LibraryManager); + EncodingManager = new MediaEncoder.EncodingManager( + LoggerFactory.CreateLogger<MediaEncoder.EncodingManager>(), + FileSystemManager, + MediaEncoder, + ChapterManager, + LibraryManager); serviceCollection.AddSingleton(EncodingManager); var activityLogRepo = GetActivityLogRepository(); @@ -874,6 +866,14 @@ namespace Emby.Server.Implementations ((LibraryManager)LibraryManager).ItemRepository = ItemRepository; } + /// <summary> + /// Create services registered with the service container that need to be initialized at application startup. + /// </summary> + public void InitializeServices() + { + HttpServer = Resolve<IHttpServer>(); + } + public static void LogEnvironmentInfo(ILogger logger, IApplicationPaths appPaths) { // Distinct these to prevent users from reporting problems that aren't actually problems @@ -881,6 +881,18 @@ namespace Emby.Server.Implementations .GetCommandLineArgs() .Distinct(); + // Get all relevant environment variables + var allEnvVars = Environment.GetEnvironmentVariables(); + var relevantEnvVars = new Dictionary<object, object>(); + foreach (var key in allEnvVars.Keys) + { + if (_relevantEnvVarPrefixes.Any(prefix => key.ToString().StartsWith(prefix, StringComparison.OrdinalIgnoreCase))) + { + relevantEnvVars.Add(key, allEnvVars[key]); + } + } + + logger.LogInformation("Environment Variables: {EnvVars}", relevantEnvVars); logger.LogInformation("Arguments: {Args}", commandLineArgs); logger.LogInformation("Operating system: {OS}", OperatingSystem.Name); logger.LogInformation("Architecture: {Architecture}", RuntimeInformation.OSArchitecture); @@ -990,48 +1002,12 @@ namespace Emby.Server.Implementations AuthenticatedAttribute.AuthService = AuthService; } - private async void PluginInstalled(object sender, GenericEventArgs<VersionInfo> args) - { - string dir = Path.Combine(ApplicationPaths.PluginsPath, args.Argument.name); - var types = Directory.EnumerateFiles(dir, "*.dll", SearchOption.AllDirectories) - .Select(Assembly.LoadFrom) - .SelectMany(x => x.ExportedTypes) - .Where(x => x.IsClass && !x.IsAbstract && !x.IsInterface && !x.IsGenericType) - .ToArray(); - - int oldLen = _allConcreteTypes.Length; - Array.Resize(ref _allConcreteTypes, oldLen + types.Length); - types.CopyTo(_allConcreteTypes, oldLen); - - var plugins = types.Where(x => x.IsAssignableFrom(typeof(IPlugin))) - .Select(CreateInstanceSafe) - .Where(x => x != null) - .Cast<IPlugin>() - .Select(LoadPlugin) - .Where(x => x != null) - .ToArray(); - - oldLen = _plugins.Length; - Array.Resize(ref _plugins, oldLen + plugins.Length); - plugins.CopyTo(_plugins, oldLen); - - var entries = types.Where(x => x.IsAssignableFrom(typeof(IServerEntryPoint))) - .Select(CreateInstanceSafe) - .Where(x => x != null) - .Cast<IServerEntryPoint>() - .ToList(); - - await Task.WhenAll(StartEntryPoints(entries, true)).ConfigureAwait(false); - await Task.WhenAll(StartEntryPoints(entries, false)).ConfigureAwait(false); - } - /// <summary> /// Finds the parts. /// </summary> public void FindParts() { InstallationManager = ServiceProvider.GetService<IInstallationManager>(); - InstallationManager.PluginInstalled += PluginInstalled; if (!ServerConfigurationManager.Configuration.IsPortAuthorized) { @@ -1045,7 +1021,7 @@ namespace Emby.Server.Implementations .Where(i => i != null) .ToArray(); - HttpServer.Init(GetExports<IService>(false), GetExports<IWebSocketListener>(), GetUrlPrefixes()); + HttpServer.Init(GetExportTypes<IService>(), GetExports<IWebSocketListener>(), GetUrlPrefixes()); LibraryManager.AddParts( GetExports<IResolverIgnoreRule>(), @@ -1139,7 +1115,7 @@ namespace Emby.Server.Implementations { exportedTypes = ass.GetExportedTypes(); } - catch (TypeLoadException ex) + catch (FileNotFoundException ex) { Logger.LogError(ex, "Error getting exported types from {Assembly}", ass.FullName); continue; @@ -1179,8 +1155,6 @@ namespace Emby.Server.Implementations }); } - protected IHttpListener CreateHttpListener() => new WebSocketSharpListener(Logger); - private CertificateInfo GetCertificateInfo(bool generateCertificate) { // Custom cert @@ -1490,18 +1464,10 @@ namespace Emby.Server.Implementations public string GetLocalApiUrl(ReadOnlySpan<char> host) { var url = new StringBuilder(64); - if (EnableHttps) - { - url.Append("https://"); - } - else - { - url.Append("http://"); - } - - url.Append(host) + url.Append(EnableHttps ? "https://" : "http://") + .Append(host) .Append(':') - .Append(HttpPort); + .Append(EnableHttps ? HttpsPort : HttpPort); string baseUrl = ServerConfigurationManager.Configuration.BaseUrl; if (baseUrl.Length != 0) @@ -1774,7 +1740,7 @@ namespace Emby.Server.Implementations } _userRepository?.Dispose(); - _displayPreferencesRepository.Dispose(); + _displayPreferencesRepository?.Dispose(); } _userRepository = null; diff --git a/Emby.Server.Implementations/Branding/BrandingConfigurationFactory.cs b/Emby.Server.Implementations/Branding/BrandingConfigurationFactory.cs index 15aee63a0..93000ae12 100644 --- a/Emby.Server.Implementations/Branding/BrandingConfigurationFactory.cs +++ b/Emby.Server.Implementations/Branding/BrandingConfigurationFactory.cs @@ -1,5 +1,4 @@ #pragma warning disable CS1591 -#pragma warning disable SA1600 using System.Collections.Generic; using MediaBrowser.Common.Configuration; diff --git a/Emby.Server.Implementations/Browser/BrowserLauncher.cs b/Emby.Server.Implementations/Browser/BrowserLauncher.cs index f5da0d018..96096e142 100644 --- a/Emby.Server.Implementations/Browser/BrowserLauncher.cs +++ b/Emby.Server.Implementations/Browser/BrowserLauncher.cs @@ -1,51 +1,48 @@ using System; using MediaBrowser.Controller; +using Microsoft.Extensions.Logging; namespace Emby.Server.Implementations.Browser { /// <summary> - /// Class BrowserLauncher. + /// Assists in opening application URLs in an external browser. /// </summary> public static class BrowserLauncher { /// <summary> - /// Opens the dashboard page. + /// Opens the home page of the web client. /// </summary> - /// <param name="page">The page.</param> /// <param name="appHost">The app host.</param> - private static void OpenDashboardPage(string page, IServerApplicationHost appHost) + public static void OpenWebApp(IServerApplicationHost appHost) { - var url = appHost.GetLocalApiUrl("localhost") + "/web/" + page; - - OpenUrl(appHost, url); + TryOpenUrl(appHost, "/web/index.html"); } /// <summary> - /// Opens the web client. + /// Opens the swagger API page. /// </summary> /// <param name="appHost">The app host.</param> - public static void OpenWebApp(IServerApplicationHost appHost) + public static void OpenSwaggerPage(IServerApplicationHost appHost) { - OpenDashboardPage("index.html", appHost); + TryOpenUrl(appHost, "/swagger/index.html"); } /// <summary> - /// Opens the URL. + /// Opens the specified URL in an external browser window. Any exceptions will be logged, but ignored. /// </summary> - /// <param name="appHost">The application host instance.</param> + /// <param name="appHost">The application host.</param> /// <param name="url">The URL.</param> - private static void OpenUrl(IServerApplicationHost appHost, string url) + private static void TryOpenUrl(IServerApplicationHost appHost, string url) { try { - appHost.LaunchUrl(url); - } - catch (NotSupportedException) - { - + string baseUrl = appHost.GetLocalApiUrl("localhost"); + appHost.LaunchUrl(baseUrl + url); } - catch (Exception) + catch (Exception ex) { + var logger = appHost.Resolve<ILogger>(); + logger?.LogError(ex, "Failed to open browser window with URL {URL}", url); } } } diff --git a/Emby.Server.Implementations/Channels/ChannelDynamicMediaSourceProvider.cs b/Emby.Server.Implementations/Channels/ChannelDynamicMediaSourceProvider.cs index aae416b37..6016fed07 100644 --- a/Emby.Server.Implementations/Channels/ChannelDynamicMediaSourceProvider.cs +++ b/Emby.Server.Implementations/Channels/ChannelDynamicMediaSourceProvider.cs @@ -1,5 +1,4 @@ #pragma warning disable CS1591 -#pragma warning disable SA1600 using System; using System.Collections.Generic; diff --git a/Emby.Server.Implementations/Channels/ChannelImageProvider.cs b/Emby.Server.Implementations/Channels/ChannelImageProvider.cs index fe64f1b15..62aeb9bcb 100644 --- a/Emby.Server.Implementations/Channels/ChannelImageProvider.cs +++ b/Emby.Server.Implementations/Channels/ChannelImageProvider.cs @@ -1,5 +1,4 @@ #pragma warning disable CS1591 -#pragma warning disable SA1600 using System.Collections.Generic; using System.Linq; diff --git a/Emby.Server.Implementations/Channels/ChannelManager.cs b/Emby.Server.Implementations/Channels/ChannelManager.cs index de2e123af..6e1baddfe 100644 --- a/Emby.Server.Implementations/Channels/ChannelManager.cs +++ b/Emby.Server.Implementations/Channels/ChannelManager.cs @@ -1,5 +1,4 @@ #pragma warning disable CS1591 -#pragma warning disable SA1600 using System; using System.Collections.Concurrent; diff --git a/Emby.Server.Implementations/Channels/ChannelPostScanTask.cs b/Emby.Server.Implementations/Channels/ChannelPostScanTask.cs index 6cbd04fea..266d539d0 100644 --- a/Emby.Server.Implementations/Channels/ChannelPostScanTask.cs +++ b/Emby.Server.Implementations/Channels/ChannelPostScanTask.cs @@ -1,5 +1,4 @@ #pragma warning disable CS1591 -#pragma warning disable SA1600 using System; using System.Linq; diff --git a/Emby.Server.Implementations/Channels/RefreshChannelsScheduledTask.cs b/Emby.Server.Implementations/Channels/RefreshChannelsScheduledTask.cs index 03e6abcfb..367efcb13 100644 --- a/Emby.Server.Implementations/Channels/RefreshChannelsScheduledTask.cs +++ b/Emby.Server.Implementations/Channels/RefreshChannelsScheduledTask.cs @@ -1,5 +1,4 @@ #pragma warning disable CS1591 -#pragma warning disable SA1600 using System; using System.Collections.Generic; @@ -10,6 +9,7 @@ using MediaBrowser.Controller.Channels; using MediaBrowser.Controller.Library; using MediaBrowser.Model.Tasks; using Microsoft.Extensions.Logging; +using MediaBrowser.Model.Globalization; namespace Emby.Server.Implementations.Channels { @@ -19,23 +19,30 @@ namespace Emby.Server.Implementations.Channels private readonly IUserManager _userManager; private readonly ILogger _logger; private readonly ILibraryManager _libraryManager; + private readonly ILocalizationManager _localization; - public RefreshChannelsScheduledTask(IChannelManager channelManager, IUserManager userManager, ILogger logger, ILibraryManager libraryManager) + public RefreshChannelsScheduledTask( + IChannelManager channelManager, + IUserManager userManager, + ILogger<RefreshChannelsScheduledTask> logger, + ILibraryManager libraryManager, + ILocalizationManager localization) { _channelManager = channelManager; _userManager = userManager; _logger = logger; _libraryManager = libraryManager; + _localization = localization; } /// <inheritdoc /> - public string Name => "Refresh Channels"; + public string Name => _localization.GetLocalizedString("TasksRefreshChannels"); /// <inheritdoc /> - public string Description => "Refreshes internet channel information."; + public string Description => _localization.GetLocalizedString("TasksRefreshChannelsDescription"); /// <inheritdoc /> - public string Category => "Internet Channels"; + public string Category => _localization.GetLocalizedString("TasksChannelsCategory"); /// <inheritdoc /> public bool IsHidden => ((ChannelManager)_channelManager).Channels.Length == 0; diff --git a/Emby.Server.Implementations/Collections/CollectionImageProvider.cs b/Emby.Server.Implementations/Collections/CollectionImageProvider.cs index 8b1407984..21ba0288e 100644 --- a/Emby.Server.Implementations/Collections/CollectionImageProvider.cs +++ b/Emby.Server.Implementations/Collections/CollectionImageProvider.cs @@ -1,5 +1,4 @@ #pragma warning disable CS1591 -#pragma warning disable SA1600 using System.Collections.Generic; using System.Linq; diff --git a/Emby.Server.Implementations/Collections/CollectionManager.cs b/Emby.Server.Implementations/Collections/CollectionManager.cs index efdef8481..321952874 100644 --- a/Emby.Server.Implementations/Collections/CollectionManager.cs +++ b/Emby.Server.Implementations/Collections/CollectionManager.cs @@ -1,5 +1,4 @@ #pragma warning disable CS1591 -#pragma warning disable SA1600 using System; using System.Collections.Generic; @@ -348,7 +347,10 @@ namespace Emby.Server.Implementations.Collections private readonly IServerConfigurationManager _config; private readonly ILogger _logger; - public CollectionManagerEntryPoint(ICollectionManager collectionManager, IServerConfigurationManager config, ILogger logger) + public CollectionManagerEntryPoint( + ICollectionManager collectionManager, + IServerConfigurationManager config, + ILogger<CollectionManagerEntryPoint> logger) { _collectionManager = (CollectionManager)collectionManager; _config = config; diff --git a/Emby.Server.Implementations/Configuration/ServerConfigurationManager.cs b/Emby.Server.Implementations/Configuration/ServerConfigurationManager.cs index 30b654886..f407317ec 100644 --- a/Emby.Server.Implementations/Configuration/ServerConfigurationManager.cs +++ b/Emby.Server.Implementations/Configuration/ServerConfigurationManager.cs @@ -132,7 +132,7 @@ namespace Emby.Server.Implementations.Configuration var newPath = newConfig.MetadataPath; if (!string.IsNullOrWhiteSpace(newPath) - && !string.Equals(Configuration.MetadataPath, newPath, StringComparison.Ordinal)) + && !string.Equals(Configuration.MetadataPath, newPath, StringComparison.Ordinal)) { // Validate if (!Directory.Exists(newPath)) diff --git a/Emby.Server.Implementations/ConfigurationOptions.cs b/Emby.Server.Implementations/ConfigurationOptions.cs index 2ea7ff6e9..20bdd18e7 100644 --- a/Emby.Server.Implementations/ConfigurationOptions.cs +++ b/Emby.Server.Implementations/ConfigurationOptions.cs @@ -1,16 +1,27 @@ using System.Collections.Generic; +using Emby.Server.Implementations.HttpServer; +using Emby.Server.Implementations.Updates; +using MediaBrowser.Providers.Music; using static MediaBrowser.Controller.Extensions.ConfigurationExtensions; namespace Emby.Server.Implementations { + /// <summary> + /// Static class containing the default configuration options for the web server. + /// </summary> public static class ConfigurationOptions { - public static Dictionary<string, string> Configuration => new Dictionary<string, string> + /// <summary> + /// Gets a new copy of the default configuration options. + /// </summary> + public static Dictionary<string, string> DefaultConfiguration => new Dictionary<string, string> { - { "HttpListenerHost:DefaultRedirectPath", "web/index.html" }, - { "MusicBrainz:BaseUrl", "https://www.musicbrainz.org" }, + { HostWebClientKey, bool.TrueString }, + { HttpListenerHost.DefaultRedirectKey, "web/index.html" }, + { InstallationManager.PluginManifestUrlKey, "https://repo.jellyfin.org/releases/plugin/manifest.json" }, { FfmpegProbeSizeKey, "1G" }, - { FfmpegAnalyzeDurationKey, "200M" } + { FfmpegAnalyzeDurationKey, "200M" }, + { PlaylistsAllowDuplicatesKey, bool.TrueString } }; } } diff --git a/Emby.Server.Implementations/Data/BaseSqliteRepository.cs b/Emby.Server.Implementations/Data/BaseSqliteRepository.cs index b7f643819..0654132f4 100644 --- a/Emby.Server.Implementations/Data/BaseSqliteRepository.cs +++ b/Emby.Server.Implementations/Data/BaseSqliteRepository.cs @@ -1,5 +1,4 @@ #pragma warning disable CS1591 -#pragma warning disable SA1600 using System; using System.Collections.Generic; diff --git a/Emby.Server.Implementations/Data/CleanDatabaseScheduledTask.cs b/Emby.Server.Implementations/Data/CleanDatabaseScheduledTask.cs index 8a5387e9b..37c678a5d 100644 --- a/Emby.Server.Implementations/Data/CleanDatabaseScheduledTask.cs +++ b/Emby.Server.Implementations/Data/CleanDatabaseScheduledTask.cs @@ -1,5 +1,4 @@ #pragma warning disable CS1591 -#pragma warning disable SA1600 using System; using System.Threading; @@ -15,7 +14,7 @@ namespace Emby.Server.Implementations.Data private readonly ILibraryManager _libraryManager; private readonly ILogger _logger; - public CleanDatabaseScheduledTask(ILibraryManager libraryManager, ILogger logger) + public CleanDatabaseScheduledTask(ILibraryManager libraryManager, ILogger<CleanDatabaseScheduledTask> logger) { _libraryManager = libraryManager; _logger = logger; diff --git a/Emby.Server.Implementations/Data/ManagedConnection.cs b/Emby.Server.Implementations/Data/ManagedConnection.cs index 2c2f19cd3..5c094ddd2 100644 --- a/Emby.Server.Implementations/Data/ManagedConnection.cs +++ b/Emby.Server.Implementations/Data/ManagedConnection.cs @@ -1,5 +1,4 @@ #pragma warning disable CS1591 -#pragma warning disable SA1600 using System; using System.Collections.Generic; diff --git a/Emby.Server.Implementations/Data/SqliteDisplayPreferencesRepository.cs b/Emby.Server.Implementations/Data/SqliteDisplayPreferencesRepository.cs index 8087419ce..d474f1c6b 100644 --- a/Emby.Server.Implementations/Data/SqliteDisplayPreferencesRepository.cs +++ b/Emby.Server.Implementations/Data/SqliteDisplayPreferencesRepository.cs @@ -1,5 +1,4 @@ #pragma warning disable CS1591 -#pragma warning disable SA1600 using System; using System.Collections.Generic; diff --git a/Emby.Server.Implementations/Data/SqliteExtensions.cs b/Emby.Server.Implementations/Data/SqliteExtensions.cs index 55c24ccc0..e7c5270b9 100644 --- a/Emby.Server.Implementations/Data/SqliteExtensions.cs +++ b/Emby.Server.Implementations/Data/SqliteExtensions.cs @@ -1,5 +1,4 @@ #pragma warning disable CS1591 -#pragma warning disable SA1600 using System; using System.Collections.Generic; @@ -288,7 +287,7 @@ namespace Emby.Server.Implementations.Data } } - public static void TryBind(this IStatement statement, string name, byte[] value) + public static void TryBind(this IStatement statement, string name, ReadOnlySpan<byte> value) { if (statement.BindParameters.TryGetValue(name, out IBindParameter bindParam)) { diff --git a/Emby.Server.Implementations/Data/SqliteItemRepository.cs b/Emby.Server.Implementations/Data/SqliteItemRepository.cs index c514846e5..33ff74bb5 100644 --- a/Emby.Server.Implementations/Data/SqliteItemRepository.cs +++ b/Emby.Server.Implementations/Data/SqliteItemRepository.cs @@ -454,7 +454,7 @@ namespace Emby.Server.Implementations.Data private static string GetSaveItemCommandText() { - var saveColumns = new [] + var saveColumns = new[] { "guid", "type", @@ -560,7 +560,7 @@ namespace Emby.Server.Implementations.Data throw new ArgumentNullException(nameof(item)); } - SaveItems(new [] { item }, cancellationToken); + SaveItems(new[] { item }, cancellationToken); } public void SaveImages(BaseItem item) @@ -1622,7 +1622,7 @@ namespace Emby.Server.Implementations.Data { IEnumerable<MetadataFields> GetLockedFields(string s) { - foreach (var i in s.Split(new [] { '|' }, StringSplitOptions.RemoveEmptyEntries)) + foreach (var i in s.Split(new[] { '|' }, StringSplitOptions.RemoveEmptyEntries)) { if (Enum.TryParse(i, true, out MetadataFields parsedValue)) { @@ -1818,7 +1818,7 @@ namespace Emby.Server.Implementations.Data { if (!reader.IsDBNull(index)) { - item.ProductionLocations = reader.GetString(index).Split(new [] { '|' }, StringSplitOptions.RemoveEmptyEntries).ToArray(); + item.ProductionLocations = reader.GetString(index).Split(new[] { '|' }, StringSplitOptions.RemoveEmptyEntries).ToArray(); } index++; } @@ -2006,7 +2006,7 @@ namespace Emby.Server.Implementations.Data /// <summary> /// Saves the chapters. /// </summary> - public void SaveChapters(Guid id, List<ChapterInfo> chapters) + public void SaveChapters(Guid id, IReadOnlyList<ChapterInfo> chapters) { CheckDisposed(); @@ -2035,22 +2035,24 @@ namespace Emby.Server.Implementations.Data } } - private void InsertChapters(byte[] idBlob, List<ChapterInfo> chapters, IDatabaseConnection db) + private void InsertChapters(byte[] idBlob, IReadOnlyList<ChapterInfo> chapters, IDatabaseConnection db) { var startIndex = 0; var limit = 100; var chapterIndex = 0; + const string StartInsertText = "insert into " + ChaptersTableName + " (ItemId, ChapterIndex, StartPositionTicks, Name, ImagePath, ImageDateModified) values "; + var insertText = new StringBuilder(StartInsertText, 256); + while (startIndex < chapters.Count) { - var insertText = new StringBuilder("insert into " + ChaptersTableName + " (ItemId, ChapterIndex, StartPositionTicks, Name, ImagePath, ImageDateModified) values "); - var endIndex = Math.Min(chapters.Count, startIndex + limit); for (var i = startIndex; i < endIndex; i++) { insertText.AppendFormat("(@ItemId, @ChapterIndex{0}, @StartPositionTicks{0}, @Name{0}, @ImagePath{0}, @ImageDateModified{0}),", i.ToString(CultureInfo.InvariantCulture)); } + insertText.Length -= 1; // Remove last , using (var statement = PrepareStatement(db, insertText.ToString())) @@ -2077,6 +2079,7 @@ namespace Emby.Server.Implementations.Data } startIndex += limit; + insertText.Length = StartInsertText.Length; } } @@ -2897,8 +2900,8 @@ namespace Emby.Server.Implementations.Data BindSimilarParams(query, statement); BindSearchParams(query, statement); - // Running this again will bind the params - GetWhereClauses(query, statement); + // Running this again will bind the params + GetWhereClauses(query, statement); result.TotalRecordCount = statement.ExecuteQuery().SelectScalarInt().First(); } @@ -2914,29 +2917,30 @@ namespace Emby.Server.Implementations.Data private string GetOrderByText(InternalItemsQuery query) { var orderBy = query.OrderBy; - if (string.IsNullOrEmpty(query.SearchTerm)) + bool hasSimilar = query.SimilarTo != null; + bool hasSearch = !string.IsNullOrEmpty(query.SearchTerm); + + if (hasSimilar || hasSearch) { - int oldLen = orderBy.Count; - if (oldLen == 0 && query.SimilarTo != null) + List<(string, SortOrder)> prepend = new List<(string, SortOrder)>(4); + if (hasSearch) { - var arr = new (string, SortOrder)[oldLen + 2]; - orderBy.CopyTo(arr, 0); - arr[oldLen] = ("SimilarityScore", SortOrder.Descending); - arr[oldLen + 1] = (ItemSortBy.Random, SortOrder.Ascending); - query.OrderBy = arr; + prepend.Add(("SearchScore", SortOrder.Descending)); + prepend.Add((ItemSortBy.SortName, SortOrder.Ascending)); } - } - else - { - query.OrderBy = new[] - { - ("SearchScore", SortOrder.Descending), - (ItemSortBy.SortName, SortOrder.Ascending) - }; - } + if (hasSimilar) + { + prepend.Add(("SimilarityScore", SortOrder.Descending)); + prepend.Add((ItemSortBy.Random, SortOrder.Ascending)); + } - if (orderBy.Count == 0) + var arr = new (string, SortOrder)[prepend.Count + orderBy.Count]; + prepend.CopyTo(arr, 0); + orderBy.CopyTo(arr, prepend.Count); + orderBy = query.OrderBy = arr; + } + else if (orderBy.Count == 0) { return string.Empty; } @@ -3265,8 +3269,8 @@ namespace Emby.Server.Implementations.Data BindSimilarParams(query, statement); BindSearchParams(query, statement); - // Running this again will bind the params - GetWhereClauses(query, statement); + // Running this again will bind the params + GetWhereClauses(query, statement); foreach (var row in statement.ExecuteQuery()) { @@ -3287,8 +3291,8 @@ namespace Emby.Server.Implementations.Data BindSimilarParams(query, statement); BindSearchParams(query, statement); - // Running this again will bind the params - GetWhereClauses(query, statement); + // Running this again will bind the params + GetWhereClauses(query, statement); result.TotalRecordCount = statement.ExecuteQuery().SelectScalarInt().First(); } @@ -3311,7 +3315,7 @@ namespace Emby.Server.Implementations.Data for (int i = 0; i < str.Length; i++) { - if (!(char.IsLetter(str[i])) && (!(char.IsNumber(str[i])))) + if (!char.IsLetter(str[i]) && !char.IsNumber(str[i])) { return false; } @@ -3335,7 +3339,7 @@ namespace Emby.Server.Implementations.Data return IsAlphaNumeric(value); } - private List<string> GetWhereClauses(InternalItemsQuery query, IStatement statement, string paramSuffix = "") + private List<string> GetWhereClauses(InternalItemsQuery query, IStatement statement) { if (query.IsResumable ?? false) { @@ -3347,27 +3351,27 @@ namespace Emby.Server.Implementations.Data if (query.IsHD.HasValue) { - var threshold = 1200; + const int Threshold = 1200; if (query.IsHD.Value) { - minWidth = threshold; + minWidth = Threshold; } else { - maxWidth = threshold - 1; + maxWidth = Threshold - 1; } } if (query.Is4K.HasValue) { - var threshold = 3800; + const int Threshold = 3800; if (query.Is4K.Value) { - minWidth = threshold; + minWidth = Threshold; } else { - maxWidth = threshold - 1; + maxWidth = Threshold - 1; } } @@ -3376,93 +3380,61 @@ namespace Emby.Server.Implementations.Data if (minWidth.HasValue) { whereClauses.Add("Width>=@MinWidth"); - if (statement != null) - { - statement.TryBind("@MinWidth", minWidth); - } + statement?.TryBind("@MinWidth", minWidth); } + if (query.MinHeight.HasValue) { whereClauses.Add("Height>=@MinHeight"); - if (statement != null) - { - statement.TryBind("@MinHeight", query.MinHeight); - } + statement?.TryBind("@MinHeight", query.MinHeight); } + if (maxWidth.HasValue) { whereClauses.Add("Width<=@MaxWidth"); - if (statement != null) - { - statement.TryBind("@MaxWidth", maxWidth); - } + statement?.TryBind("@MaxWidth", maxWidth); } + if (query.MaxHeight.HasValue) { whereClauses.Add("Height<=@MaxHeight"); - if (statement != null) - { - statement.TryBind("@MaxHeight", query.MaxHeight); - } + statement?.TryBind("@MaxHeight", query.MaxHeight); } if (query.IsLocked.HasValue) { whereClauses.Add("IsLocked=@IsLocked"); - if (statement != null) - { - statement.TryBind("@IsLocked", query.IsLocked); - } + statement?.TryBind("@IsLocked", query.IsLocked); } var tags = query.Tags.ToList(); var excludeTags = query.ExcludeTags.ToList(); - if (query.IsMovie ?? false) + if (query.IsMovie == true) { - var alternateTypes = new List<string>(); - if (query.IncludeItemTypes.Length == 0 || query.IncludeItemTypes.Contains(typeof(Movie).Name)) - { - alternateTypes.Add(typeof(Movie).FullName); - } - if (query.IncludeItemTypes.Length == 0 || query.IncludeItemTypes.Contains(typeof(Trailer).Name)) + if (query.IncludeItemTypes.Length == 0 + || query.IncludeItemTypes.Contains(nameof(Movie)) + || query.IncludeItemTypes.Contains(nameof(Trailer))) { - alternateTypes.Add(typeof(Trailer).FullName); - } - - var programAttribtues = new List<string>(); - if (alternateTypes.Count == 0) - { - programAttribtues.Add("IsMovie=@IsMovie"); + whereClauses.Add("(IsMovie is null OR IsMovie=@IsMovie)"); } else { - programAttribtues.Add("(IsMovie is null OR IsMovie=@IsMovie)"); - } - - if (statement != null) - { - statement.TryBind("@IsMovie", true); + whereClauses.Add("IsMovie=@IsMovie"); } - whereClauses.Add("(" + string.Join(" OR ", programAttribtues) + ")"); + statement?.TryBind("@IsMovie", true); } else if (query.IsMovie.HasValue) { whereClauses.Add("IsMovie=@IsMovie"); - if (statement != null) - { - statement.TryBind("@IsMovie", query.IsMovie); - } + statement?.TryBind("@IsMovie", query.IsMovie); } if (query.IsSeries.HasValue) { whereClauses.Add("IsSeries=@IsSeries"); - if (statement != null) - { - statement.TryBind("@IsSeries", query.IsSeries); - } + statement?.TryBind("@IsSeries", query.IsSeries); } if (query.IsSports.HasValue) @@ -3514,27 +3486,10 @@ namespace Emby.Server.Implementations.Data if (query.IsFolder.HasValue) { whereClauses.Add("IsFolder=@IsFolder"); - if (statement != null) - { - statement.TryBind("@IsFolder", query.IsFolder); - } + statement?.TryBind("@IsFolder", query.IsFolder); } var includeTypes = query.IncludeItemTypes.SelectMany(MapIncludeItemTypes).ToArray(); - if (includeTypes.Length == 1) - { - whereClauses.Add("type=@type"); - if (statement != null) - { - statement.TryBind("@type", includeTypes[0]); - } - } - else if (includeTypes.Length > 1) - { - var inClause = string.Join(",", includeTypes.Select(i => "'" + i + "'")); - whereClauses.Add($"type in ({inClause})"); - } - // Only specify excluded types if no included types are specified if (includeTypes.Length == 0) { @@ -3542,10 +3497,7 @@ namespace Emby.Server.Implementations.Data if (excludeTypes.Length == 1) { whereClauses.Add("type<>@type"); - if (statement != null) - { - statement.TryBind("@type", excludeTypes[0]); - } + statement?.TryBind("@type", excludeTypes[0]); } else if (excludeTypes.Length > 1) { @@ -3553,14 +3505,21 @@ namespace Emby.Server.Implementations.Data whereClauses.Add($"type not in ({inClause})"); } } + else if (includeTypes.Length == 1) + { + whereClauses.Add("type=@type"); + statement?.TryBind("@type", includeTypes[0]); + } + else if (includeTypes.Length > 1) + { + var inClause = string.Join(",", includeTypes.Select(i => "'" + i + "'")); + whereClauses.Add($"type in ({inClause})"); + } if (query.ChannelIds.Length == 1) { whereClauses.Add("ChannelId=@ChannelId"); - if (statement != null) - { - statement.TryBind("@ChannelId", query.ChannelIds[0].ToString("N", CultureInfo.InvariantCulture)); - } + statement?.TryBind("@ChannelId", query.ChannelIds[0].ToString("N", CultureInfo.InvariantCulture)); } else if (query.ChannelIds.Length > 1) { @@ -3571,98 +3530,65 @@ namespace Emby.Server.Implementations.Data if (!query.ParentId.Equals(Guid.Empty)) { whereClauses.Add("ParentId=@ParentId"); - if (statement != null) - { - statement.TryBind("@ParentId", query.ParentId); - } + statement?.TryBind("@ParentId", query.ParentId); } if (!string.IsNullOrWhiteSpace(query.Path)) { whereClauses.Add("Path=@Path"); - if (statement != null) - { - statement.TryBind("@Path", GetPathToSave(query.Path)); - } + statement?.TryBind("@Path", GetPathToSave(query.Path)); } if (!string.IsNullOrWhiteSpace(query.PresentationUniqueKey)) { whereClauses.Add("PresentationUniqueKey=@PresentationUniqueKey"); - if (statement != null) - { - statement.TryBind("@PresentationUniqueKey", query.PresentationUniqueKey); - } + statement?.TryBind("@PresentationUniqueKey", query.PresentationUniqueKey); } if (query.MinCommunityRating.HasValue) { whereClauses.Add("CommunityRating>=@MinCommunityRating"); - if (statement != null) - { - statement.TryBind("@MinCommunityRating", query.MinCommunityRating.Value); - } + statement?.TryBind("@MinCommunityRating", query.MinCommunityRating.Value); } if (query.MinIndexNumber.HasValue) { whereClauses.Add("IndexNumber>=@MinIndexNumber"); - if (statement != null) - { - statement.TryBind("@MinIndexNumber", query.MinIndexNumber.Value); - } + statement?.TryBind("@MinIndexNumber", query.MinIndexNumber.Value); } if (query.MinDateCreated.HasValue) { whereClauses.Add("DateCreated>=@MinDateCreated"); - if (statement != null) - { - statement.TryBind("@MinDateCreated", query.MinDateCreated.Value); - } + statement?.TryBind("@MinDateCreated", query.MinDateCreated.Value); } if (query.MinDateLastSaved.HasValue) { whereClauses.Add("(DateLastSaved not null and DateLastSaved>=@MinDateLastSavedForUser)"); - if (statement != null) - { - statement.TryBind("@MinDateLastSaved", query.MinDateLastSaved.Value); - } + statement?.TryBind("@MinDateLastSaved", query.MinDateLastSaved.Value); } if (query.MinDateLastSavedForUser.HasValue) { whereClauses.Add("(DateLastSaved not null and DateLastSaved>=@MinDateLastSavedForUser)"); - if (statement != null) - { - statement.TryBind("@MinDateLastSavedForUser", query.MinDateLastSavedForUser.Value); - } + statement?.TryBind("@MinDateLastSavedForUser", query.MinDateLastSavedForUser.Value); } if (query.IndexNumber.HasValue) { whereClauses.Add("IndexNumber=@IndexNumber"); - if (statement != null) - { - statement.TryBind("@IndexNumber", query.IndexNumber.Value); - } + statement?.TryBind("@IndexNumber", query.IndexNumber.Value); } if (query.ParentIndexNumber.HasValue) { whereClauses.Add("ParentIndexNumber=@ParentIndexNumber"); - if (statement != null) - { - statement.TryBind("@ParentIndexNumber", query.ParentIndexNumber.Value); - } + statement?.TryBind("@ParentIndexNumber", query.ParentIndexNumber.Value); } if (query.ParentIndexNumberNotEquals.HasValue) { whereClauses.Add("(ParentIndexNumber<>@ParentIndexNumberNotEquals or ParentIndexNumber is null)"); - if (statement != null) - { - statement.TryBind("@ParentIndexNumberNotEquals", query.ParentIndexNumberNotEquals.Value); - } + statement?.TryBind("@ParentIndexNumberNotEquals", query.ParentIndexNumberNotEquals.Value); } var minEndDate = query.MinEndDate; @@ -3683,73 +3609,59 @@ namespace Emby.Server.Implementations.Data if (minEndDate.HasValue) { whereClauses.Add("EndDate>=@MinEndDate"); - if (statement != null) - { - statement.TryBind("@MinEndDate", minEndDate.Value); - } + statement?.TryBind("@MinEndDate", minEndDate.Value); } if (maxEndDate.HasValue) { whereClauses.Add("EndDate<=@MaxEndDate"); - if (statement != null) - { - statement.TryBind("@MaxEndDate", maxEndDate.Value); - } + statement?.TryBind("@MaxEndDate", maxEndDate.Value); } if (query.MinStartDate.HasValue) { whereClauses.Add("StartDate>=@MinStartDate"); - if (statement != null) - { - statement.TryBind("@MinStartDate", query.MinStartDate.Value); - } + statement?.TryBind("@MinStartDate", query.MinStartDate.Value); } if (query.MaxStartDate.HasValue) { whereClauses.Add("StartDate<=@MaxStartDate"); - if (statement != null) - { - statement.TryBind("@MaxStartDate", query.MaxStartDate.Value); - } + statement?.TryBind("@MaxStartDate", query.MaxStartDate.Value); } if (query.MinPremiereDate.HasValue) { whereClauses.Add("PremiereDate>=@MinPremiereDate"); - if (statement != null) - { - statement.TryBind("@MinPremiereDate", query.MinPremiereDate.Value); - } + statement?.TryBind("@MinPremiereDate", query.MinPremiereDate.Value); } + if (query.MaxPremiereDate.HasValue) { whereClauses.Add("PremiereDate<=@MaxPremiereDate"); - if (statement != null) - { - statement.TryBind("@MaxPremiereDate", query.MaxPremiereDate.Value); - } + statement?.TryBind("@MaxPremiereDate", query.MaxPremiereDate.Value); } - if (query.TrailerTypes.Length > 0) + var trailerTypes = query.TrailerTypes; + int trailerTypesLen = trailerTypes.Length; + if (trailerTypesLen > 0) { - var clauses = new List<string>(); - var index = 0; - foreach (var type in query.TrailerTypes) + const string Or = " OR "; + StringBuilder clause = new StringBuilder("(", trailerTypesLen * 32); + for (int i = 0; i < trailerTypesLen; i++) { - var paramName = "@TrailerTypes" + index; - - clauses.Add("TrailerTypes like " + paramName); - if (statement != null) - { - statement.TryBind(paramName, "%" + type + "%"); - } - index++; + var paramName = "@TrailerTypes" + i; + clause.Append("TrailerTypes like ") + .Append(paramName) + .Append(Or); + statement?.TryBind(paramName, "%" + trailerTypes[i] + "%"); } - var clause = "(" + string.Join(" OR ", clauses) + ")"; - whereClauses.Add(clause); + + // Remove last " OR " + clause.Length -= Or.Length; + clause.Append(')'); + + whereClauses.Add(clause.ToString()); } if (query.IsAiring.HasValue) @@ -3757,24 +3669,15 @@ namespace Emby.Server.Implementations.Data if (query.IsAiring.Value) { whereClauses.Add("StartDate<=@MaxStartDate"); - if (statement != null) - { - statement.TryBind("@MaxStartDate", DateTime.UtcNow); - } + statement?.TryBind("@MaxStartDate", DateTime.UtcNow); whereClauses.Add("EndDate>=@MinEndDate"); - if (statement != null) - { - statement.TryBind("@MinEndDate", DateTime.UtcNow); - } + statement?.TryBind("@MinEndDate", DateTime.UtcNow); } else { whereClauses.Add("(StartDate>@IsAiringDate OR EndDate < @IsAiringDate)"); - if (statement != null) - { - statement.TryBind("@IsAiringDate", DateTime.UtcNow); - } + statement?.TryBind("@IsAiringDate", DateTime.UtcNow); } } @@ -3789,13 +3692,10 @@ namespace Emby.Server.Implementations.Data var paramName = "@PersonId" + index; clauses.Add("(guid in (select itemid from People where Name = (select Name from TypedBaseItems where guid=" + paramName + ")))"); - - if (statement != null) - { - statement.TryBind(paramName, personId.ToByteArray()); - } + statement?.TryBind(paramName, personId.ToByteArray()); index++; } + var clause = "(" + string.Join(" OR ", clauses) + ")"; whereClauses.Add(clause); } @@ -3803,47 +3703,31 @@ namespace Emby.Server.Implementations.Data if (!string.IsNullOrWhiteSpace(query.Person)) { whereClauses.Add("Guid in (select ItemId from People where Name=@PersonName)"); - if (statement != null) - { - statement.TryBind("@PersonName", query.Person); - } + statement?.TryBind("@PersonName", query.Person); } if (!string.IsNullOrWhiteSpace(query.MinSortName)) { whereClauses.Add("SortName>=@MinSortName"); - if (statement != null) - { - statement.TryBind("@MinSortName", query.MinSortName); - } + statement?.TryBind("@MinSortName", query.MinSortName); } if (!string.IsNullOrWhiteSpace(query.ExternalSeriesId)) { whereClauses.Add("ExternalSeriesId=@ExternalSeriesId"); - if (statement != null) - { - statement.TryBind("@ExternalSeriesId", query.ExternalSeriesId); - } + statement?.TryBind("@ExternalSeriesId", query.ExternalSeriesId); } if (!string.IsNullOrWhiteSpace(query.ExternalId)) { whereClauses.Add("ExternalId=@ExternalId"); - if (statement != null) - { - statement.TryBind("@ExternalId", query.ExternalId); - } + statement?.TryBind("@ExternalId", query.ExternalId); } if (!string.IsNullOrWhiteSpace(query.Name)) { whereClauses.Add("CleanName=@Name"); - - if (statement != null) - { - statement.TryBind("@Name", GetCleanValue(query.Name)); - } + statement?.TryBind("@Name", GetCleanValue(query.Name)); } // These are the same, for now @@ -3862,28 +3746,21 @@ namespace Emby.Server.Implementations.Data if (!string.IsNullOrWhiteSpace(query.NameStartsWith)) { whereClauses.Add("SortName like @NameStartsWith"); - if (statement != null) - { - statement.TryBind("@NameStartsWith", query.NameStartsWith + "%"); - } + statement?.TryBind("@NameStartsWith", query.NameStartsWith + "%"); } + if (!string.IsNullOrWhiteSpace(query.NameStartsWithOrGreater)) { whereClauses.Add("SortName >= @NameStartsWithOrGreater"); // lowercase this because SortName is stored as lowercase - if (statement != null) - { - statement.TryBind("@NameStartsWithOrGreater", query.NameStartsWithOrGreater.ToLowerInvariant()); - } + statement?.TryBind("@NameStartsWithOrGreater", query.NameStartsWithOrGreater.ToLowerInvariant()); } + if (!string.IsNullOrWhiteSpace(query.NameLessThan)) { whereClauses.Add("SortName < @NameLessThan"); // lowercase this because SortName is stored as lowercase - if (statement != null) - { - statement.TryBind("@NameLessThan", query.NameLessThan.ToLowerInvariant()); - } + statement?.TryBind("@NameLessThan", query.NameLessThan.ToLowerInvariant()); } if (query.ImageTypes.Length > 0) @@ -3899,18 +3776,12 @@ namespace Emby.Server.Implementations.Data if (query.IsLiked.Value) { whereClauses.Add("rating>=@UserRating"); - if (statement != null) - { - statement.TryBind("@UserRating", UserItemData.MinLikeValue); - } + statement?.TryBind("@UserRating", UserItemData.MinLikeValue); } else { whereClauses.Add("(rating is null or rating<@UserRating)"); - if (statement != null) - { - statement.TryBind("@UserRating", UserItemData.MinLikeValue); - } + statement?.TryBind("@UserRating", UserItemData.MinLikeValue); } } @@ -3924,10 +3795,8 @@ namespace Emby.Server.Implementations.Data { whereClauses.Add("(IsFavorite is null or IsFavorite=@IsFavoriteOrLiked)"); } - if (statement != null) - { - statement.TryBind("@IsFavoriteOrLiked", query.IsFavoriteOrLiked.Value); - } + + statement?.TryBind("@IsFavoriteOrLiked", query.IsFavoriteOrLiked.Value); } if (query.IsFavorite.HasValue) @@ -3940,10 +3809,8 @@ namespace Emby.Server.Implementations.Data { whereClauses.Add("(IsFavorite is null or IsFavorite=@IsFavorite)"); } - if (statement != null) - { - statement.TryBind("@IsFavorite", query.IsFavorite.Value); - } + + statement?.TryBind("@IsFavorite", query.IsFavorite.Value); } if (EnableJoinUserData(query)) @@ -3972,10 +3839,8 @@ namespace Emby.Server.Implementations.Data { whereClauses.Add("(played is null or played=@IsPlayed)"); } - if (statement != null) - { - statement.TryBind("@IsPlayed", query.IsPlayed.Value); - } + + statement?.TryBind("@IsPlayed", query.IsPlayed.Value); } } } @@ -4007,6 +3872,7 @@ namespace Emby.Server.Implementations.Data } index++; } + var clause = "(" + string.Join(" OR ", clauses) + ")"; whereClauses.Add(clause); } @@ -4026,6 +3892,7 @@ namespace Emby.Server.Implementations.Data } index++; } + var clause = "(" + string.Join(" OR ", clauses) + ")"; whereClauses.Add(clause); } @@ -4759,18 +4626,22 @@ namespace Emby.Server.Implementations.Data { list.Add(typeof(Person).Name); } + if (IsTypeInQuery(typeof(Genre).Name, query)) { list.Add(typeof(Genre).Name); } + if (IsTypeInQuery(typeof(MusicGenre).Name, query)) { list.Add(typeof(MusicGenre).Name); } + if (IsTypeInQuery(typeof(MusicArtist).Name, query)) { list.Add(typeof(MusicArtist).Name); } + if (IsTypeInQuery(typeof(Studio).Name, query)) { list.Add(typeof(Studio).Name); @@ -4844,7 +4715,7 @@ namespace Emby.Server.Implementations.Data return false; } - private static readonly Type[] KnownTypes = + private static readonly Type[] _knownTypes = { typeof(LiveTvProgram), typeof(LiveTvChannel), @@ -4913,7 +4784,7 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type { var dict = new Dictionary<string, string[]>(StringComparer.OrdinalIgnoreCase); - foreach (var t in KnownTypes) + foreach (var t in _knownTypes) { dict[t.Name] = new[] { t.FullName }; } @@ -4925,9 +4796,9 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type } // Not crazy about having this all the way down here, but at least it's in one place - readonly Dictionary<string, string[]> _types = GetTypeMapDictionary(); + private readonly Dictionary<string, string[]> _types = GetTypeMapDictionary(); - private IEnumerable<string> MapIncludeItemTypes(string value) + private string[] MapIncludeItemTypes(string value) { if (_types.TryGetValue(value, out string[] result)) { @@ -4942,7 +4813,7 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type return Array.Empty<string>(); } - public void DeleteItem(Guid id, CancellationToken cancellationToken) + public void DeleteItem(Guid id) { if (id == Guid.Empty) { @@ -4978,7 +4849,7 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type } } - private void ExecuteWithSingleParam(IDatabaseConnection db, string query, byte[] value) + private void ExecuteWithSingleParam(IDatabaseConnection db, string query, ReadOnlySpan<byte> value) { using (var statement = PrepareStatement(db, query)) { @@ -5008,6 +4879,11 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type commandText += " order by ListOrder"; + if (query.Limit > 0) + { + commandText += " LIMIT " + query.Limit; + } + using (var connection = GetConnection(true)) { var list = new List<string>(); @@ -5046,6 +4922,11 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type commandText += " order by ListOrder"; + if (query.Limit > 0) + { + commandText += " LIMIT " + query.Limit; + } + using (var connection = GetConnection(true)) { var list = new List<PersonInfo>(); @@ -5528,6 +5409,7 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type { GetWhereClauses(typeSubQuery, null); } + BindSimilarParams(query, statement); BindSearchParams(query, statement); GetWhereClauses(innerQuery, statement); @@ -5569,7 +5451,7 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type } var allTypes = typeString.Split(new[] { '|' }, StringSplitOptions.RemoveEmptyEntries) - .ToLookup(i => i); + .ToLookup(x => x); foreach (var type in allTypes) { @@ -5611,32 +5493,32 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type return counts; } - private List<Tuple<int, string>> GetItemValuesToSave(BaseItem item, List<string> inheritedTags) + private List<(int, string)> GetItemValuesToSave(BaseItem item, List<string> inheritedTags) { - var list = new List<Tuple<int, string>>(); + var list = new List<(int, string)>(); if (item is IHasArtist hasArtist) { - list.AddRange(hasArtist.Artists.Select(i => new Tuple<int, string>(0, i))); + list.AddRange(hasArtist.Artists.Select(i => (0, i))); } if (item is IHasAlbumArtist hasAlbumArtist) { - list.AddRange(hasAlbumArtist.AlbumArtists.Select(i => new Tuple<int, string>(1, i))); + list.AddRange(hasAlbumArtist.AlbumArtists.Select(i => (1, i))); } - list.AddRange(item.Genres.Select(i => new Tuple<int, string>(2, i))); - list.AddRange(item.Studios.Select(i => new Tuple<int, string>(3, i))); - list.AddRange(item.Tags.Select(i => new Tuple<int, string>(4, i))); + list.AddRange(item.Genres.Select(i => (2, i))); + list.AddRange(item.Studios.Select(i => (3, i))); + list.AddRange(item.Tags.Select(i => (4, i))); // keywords was 5 - list.AddRange(inheritedTags.Select(i => new Tuple<int, string>(6, i))); + list.AddRange(inheritedTags.Select(i => (6, i))); return list; } - private void UpdateItemValues(Guid itemId, List<Tuple<int, string>> values, IDatabaseConnection db) + private void UpdateItemValues(Guid itemId, List<(int, string)> values, IDatabaseConnection db) { if (itemId.Equals(Guid.Empty)) { @@ -5658,32 +5540,28 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type InsertItemValues(guidBlob, values, db); } - private void InsertItemValues(byte[] idBlob, List<Tuple<int, string>> values, IDatabaseConnection db) + private void InsertItemValues(byte[] idBlob, List<(int, string)> values, IDatabaseConnection db) { + const int Limit = 100; var startIndex = 0; - var limit = 100; while (startIndex < values.Count) { var insertText = new StringBuilder("insert into ItemValues (ItemId, Type, Value, CleanValue) values "); - var endIndex = Math.Min(values.Count, startIndex + limit); - var isSubsequentRow = false; + var endIndex = Math.Min(values.Count, startIndex + Limit); for (var i = startIndex; i < endIndex; i++) { - if (isSubsequentRow) - { - insertText.Append(','); - } - insertText.AppendFormat( CultureInfo.InvariantCulture, - "(@ItemId, @Type{0}, @Value{0}, @CleanValue{0})", + "(@ItemId, @Type{0}, @Value{0}, @CleanValue{0}),", i); - isSubsequentRow = true; } + // Remove last comma + insertText.Length--; + using (var statement = PrepareStatement(db, insertText.ToString())) { statement.TryBind("@ItemId", idBlob); @@ -5711,7 +5589,7 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type statement.MoveNext(); } - startIndex += limit; + startIndex += Limit; } } @@ -5746,28 +5624,23 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type private void InsertPeople(byte[] idBlob, List<PersonInfo> people, IDatabaseConnection db) { + const int Limit = 100; var startIndex = 0; - var limit = 100; var listIndex = 0; while (startIndex < people.Count) { var insertText = new StringBuilder("insert into People (ItemId, Name, Role, PersonType, SortOrder, ListOrder) values "); - var endIndex = Math.Min(people.Count, startIndex + limit); - var isSubsequentRow = false; - + var endIndex = Math.Min(people.Count, startIndex + Limit); for (var i = startIndex; i < endIndex; i++) { - if (isSubsequentRow) - { - insertText.Append(','); - } - - insertText.AppendFormat("(@ItemId, @Name{0}, @Role{0}, @PersonType{0}, @SortOrder{0}, @ListOrder{0})", i.ToString(CultureInfo.InvariantCulture)); - isSubsequentRow = true; + insertText.AppendFormat("(@ItemId, @Name{0}, @Role{0}, @PersonType{0}, @SortOrder{0}, @ListOrder{0}),", i.ToString(CultureInfo.InvariantCulture)); } + // Remove last comma + insertText.Length--; + using (var statement = PrepareStatement(db, insertText.ToString())) { statement.TryBind("@ItemId", idBlob); @@ -5791,16 +5664,17 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type statement.MoveNext(); } - startIndex += limit; + startIndex += Limit; } } private PersonInfo GetPerson(IReadOnlyList<IResultSetValue> reader) { - var item = new PersonInfo(); - - item.ItemId = reader.GetGuid(0); - item.Name = reader.GetString(1); + var item = new PersonInfo + { + ItemId = reader.GetGuid(0), + Name = reader.GetString(1) + }; if (!reader.IsDBNull(2)) { @@ -5907,20 +5781,28 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type private void InsertMediaStreams(byte[] idBlob, List<MediaStream> streams, IDatabaseConnection db) { + const int Limit = 10; var startIndex = 0; - var limit = 10; while (startIndex < streams.Count) { - var insertText = new StringBuilder(string.Format("insert into mediastreams ({0}) values ", string.Join(",", _mediaStreamSaveColumns))); + var insertText = new StringBuilder("insert into mediastreams ("); + foreach (var column in _mediaStreamSaveColumns) + { + insertText.Append(column).Append(','); + } - var endIndex = Math.Min(streams.Count, startIndex + limit); + // Remove last comma + insertText.Length--; + insertText.Append(") values "); + + var endIndex = Math.Min(streams.Count, startIndex + Limit); for (var i = startIndex; i < endIndex; i++) { if (i != startIndex) { - insertText.Append(","); + insertText.Append(','); } var index = i.ToString(CultureInfo.InvariantCulture); @@ -5928,11 +5810,12 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type foreach (var column in _mediaStreamSaveColumns.Skip(1)) { - insertText.Append("@" + column + index + ","); + insertText.Append('@').Append(column).Append(index).Append(','); } + insertText.Length -= 1; // Remove the last comma - insertText.Append(")"); + insertText.Append(')'); } using (var statement = PrepareStatement(db, insertText.ToString())) @@ -5994,7 +5877,7 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type statement.MoveNext(); } - startIndex += limit; + startIndex += Limit; } } @@ -6011,7 +5894,7 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type Index = reader[1].ToInt() }; - item.Type = (MediaStreamType)Enum.Parse(typeof(MediaStreamType), reader[2].ToString(), true); + item.Type = Enum.Parse<MediaStreamType>(reader[2].ToString(), true); if (reader[3].SQLiteType != SQLiteType.Null) { @@ -6159,7 +6042,8 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type item.ColorTransfer = reader[34].ToString(); } - if (item.Type == MediaStreamType.Subtitle){ + if (item.Type == MediaStreamType.Subtitle) + { item.localizedUndefined = _localization.GetLocalizedString("Undefined"); item.localizedDefault = _localization.GetLocalizedString("Default"); item.localizedForced = _localization.GetLocalizedString("Forced"); @@ -6288,8 +6172,8 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type statement.TryBind("@Codec" + index, attachment.Codec); statement.TryBind("@CodecTag" + index, attachment.CodecTag); statement.TryBind("@Comment" + index, attachment.Comment); - statement.TryBind("@FileName" + index, attachment.FileName); - statement.TryBind("@MimeType" + index, attachment.MimeType); + statement.TryBind("@Filename" + index, attachment.FileName); + statement.TryBind("@MIMEType" + index, attachment.MimeType); } statement.Reset(); diff --git a/Emby.Server.Implementations/Data/SqliteUserDataRepository.cs b/Emby.Server.Implementations/Data/SqliteUserDataRepository.cs index f6c37e4e5..22955850a 100644 --- a/Emby.Server.Implementations/Data/SqliteUserDataRepository.cs +++ b/Emby.Server.Implementations/Data/SqliteUserDataRepository.cs @@ -1,5 +1,4 @@ #pragma warning disable CS1591 -#pragma warning disable SA1600 using System; using System.Collections.Generic; diff --git a/Emby.Server.Implementations/Data/SqliteUserRepository.cs b/Emby.Server.Implementations/Data/SqliteUserRepository.cs index c82c93ffc..0c3f26974 100644 --- a/Emby.Server.Implementations/Data/SqliteUserRepository.cs +++ b/Emby.Server.Implementations/Data/SqliteUserRepository.cs @@ -1,5 +1,4 @@ #pragma warning disable CS1591 -#pragma warning disable SA1600 using System; using System.Collections.Generic; @@ -26,7 +25,7 @@ namespace Emby.Server.Implementations.Data IServerApplicationPaths appPaths) : base(logger) { - _jsonOptions = JsonDefaults.GetOptions();; + _jsonOptions = JsonDefaults.GetOptions(); DbFilePath = Path.Combine(appPaths.DataPath, "users.db"); } diff --git a/Emby.Server.Implementations/Devices/DeviceId.cs b/Emby.Server.Implementations/Devices/DeviceId.cs index ff75efa59..f0d43e665 100644 --- a/Emby.Server.Implementations/Devices/DeviceId.cs +++ b/Emby.Server.Implementations/Devices/DeviceId.cs @@ -1,5 +1,4 @@ #pragma warning disable CS1591 -#pragma warning disable SA1600 using System; using System.Globalization; diff --git a/Emby.Server.Implementations/Devices/DeviceManager.cs b/Emby.Server.Implementations/Devices/DeviceManager.cs index 2bd0b840a..adb8e793d 100644 --- a/Emby.Server.Implementations/Devices/DeviceManager.cs +++ b/Emby.Server.Implementations/Devices/DeviceManager.cs @@ -1,5 +1,4 @@ #pragma warning disable CS1591 -#pragma warning disable SA1600 using System; using System.Collections.Generic; @@ -142,11 +141,10 @@ namespace Emby.Server.Implementations.Devices public QueryResult<DeviceInfo> GetDevices(DeviceQuery query) { - var sessions = _authRepo.Get(new AuthenticationInfoQuery + IEnumerable<AuthenticationInfo> sessions = _authRepo.Get(new AuthenticationInfoQuery { //UserId = query.UserId HasUser = true - }).Items; // TODO: DeviceQuery doesn't seem to be used from client. Not even Swagger. @@ -154,23 +152,19 @@ namespace Emby.Server.Implementations.Devices { var val = query.SupportsSync.Value; - sessions = sessions.Where(i => GetCapabilities(i.DeviceId).SupportsSync == val).ToArray(); + sessions = sessions.Where(i => GetCapabilities(i.DeviceId).SupportsSync == val); } if (!query.UserId.Equals(Guid.Empty)) { var user = _userManager.GetUserById(query.UserId); - sessions = sessions.Where(i => CanAccessDevice(user, i.DeviceId)).ToArray(); + sessions = sessions.Where(i => CanAccessDevice(user, i.DeviceId)); } var array = sessions.Select(ToDeviceInfo).ToArray(); - return new QueryResult<DeviceInfo> - { - Items = array, - TotalRecordCount = array.Length - }; + return new QueryResult<DeviceInfo>(array); } private DeviceInfo ToDeviceInfo(AuthenticationInfo authInfo) @@ -186,7 +180,7 @@ namespace Emby.Server.Implementations.Devices LastUserName = authInfo.UserName, Name = authInfo.DeviceName, DateLastActivity = authInfo.DateLastActivity, - IconUrl = caps == null ? null : caps.IconUrl + IconUrl = caps?.IconUrl }; } @@ -412,7 +406,10 @@ namespace Emby.Server.Implementations.Devices private readonly IServerConfigurationManager _config; private ILogger _logger; - public DeviceManagerEntryPoint(IDeviceManager deviceManager, IServerConfigurationManager config, ILogger logger) + public DeviceManagerEntryPoint( + IDeviceManager deviceManager, + IServerConfigurationManager config, + ILogger<DeviceManagerEntryPoint> logger) { _deviceManager = (DeviceManager)deviceManager; _config = config; diff --git a/Emby.Server.Implementations/Diagnostics/CommonProcess.cs b/Emby.Server.Implementations/Diagnostics/CommonProcess.cs index f8b754151..bfa49ac5f 100644 --- a/Emby.Server.Implementations/Diagnostics/CommonProcess.cs +++ b/Emby.Server.Implementations/Diagnostics/CommonProcess.cs @@ -1,5 +1,4 @@ #pragma warning disable CS1591 -#pragma warning disable SA1600 using System; using System.Diagnostics; diff --git a/Emby.Server.Implementations/Diagnostics/ProcessFactory.cs b/Emby.Server.Implementations/Diagnostics/ProcessFactory.cs index 219f73c78..02ad3c1a8 100644 --- a/Emby.Server.Implementations/Diagnostics/ProcessFactory.cs +++ b/Emby.Server.Implementations/Diagnostics/ProcessFactory.cs @@ -1,5 +1,4 @@ #pragma warning disable CS1591 -#pragma warning disable SA1600 using MediaBrowser.Model.Diagnostics; diff --git a/Emby.Server.Implementations/Dto/DtoService.cs b/Emby.Server.Implementations/Dto/DtoService.cs index 960f3f2d6..34a342cf7 100644 --- a/Emby.Server.Implementations/Dto/DtoService.cs +++ b/Emby.Server.Implementations/Dto/DtoService.cs @@ -1,5 +1,4 @@ #pragma warning disable CS1591 -#pragma warning disable SA1600 using System; using System.Collections.Generic; @@ -1057,30 +1056,19 @@ namespace Emby.Server.Implementations.Dto if (options.ContainsField(ItemFields.SpecialFeatureCount)) { - if (allExtras == null) - { - allExtras = item.GetExtras().ToArray(); - } - + allExtras = item.GetExtras().ToArray(); dto.SpecialFeatureCount = allExtras.Count(i => i.ExtraType.HasValue && BaseItem.DisplayExtraTypes.Contains(i.ExtraType.Value)); } if (options.ContainsField(ItemFields.LocalTrailerCount)) { - int trailerCount = 0; - if (allExtras == null) - { - allExtras = item.GetExtras().ToArray(); - } - - trailerCount += allExtras.Count(i => i.ExtraType.HasValue && i.ExtraType.Value == ExtraType.Trailer); + allExtras ??= item.GetExtras().ToArray(); + dto.LocalTrailerCount = allExtras.Count(i => i.ExtraType == ExtraType.Trailer); if (item is IHasTrailers hasTrailers) { - trailerCount += hasTrailers.GetTrailerCount(); + dto.LocalTrailerCount += hasTrailers.GetTrailerCount(); } - - dto.LocalTrailerCount = trailerCount; } // Add EpisodeInfo diff --git a/Emby.Server.Implementations/Emby.Server.Implementations.csproj b/Emby.Server.Implementations/Emby.Server.Implementations.csproj index f8560ca85..d46b9507b 100644 --- a/Emby.Server.Implementations/Emby.Server.Implementations.csproj +++ b/Emby.Server.Implementations/Emby.Server.Implementations.csproj @@ -29,14 +29,14 @@ <PackageReference Include="Microsoft.AspNetCore.ResponseCompression" Version="2.2.0" /> <PackageReference Include="Microsoft.AspNetCore.Server.Kestrel" Version="2.2.0" /> <PackageReference Include="Microsoft.AspNetCore.WebSockets" Version="2.2.1" /> - <PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="3.1.1" /> - <PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="3.1.1" /> - <PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="3.1.1" /> + <PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="3.1.3" /> + <PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="3.1.3" /> + <PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="3.1.3" /> + <PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="3.1.3" /> <PackageReference Include="Mono.Nat" Version="2.0.0" /> <PackageReference Include="ServiceStack.Text.Core" Version="5.8.0" /> - <PackageReference Include="sharpcompress" Version="0.24.0" /> + <PackageReference Include="sharpcompress" Version="0.25.0" /> <PackageReference Include="SQLitePCL.pretty.netstandard" Version="2.1.0" /> - <PackageReference Include="System.Interactive.Async" Version="4.0.0" /> </ItemGroup> <ItemGroup> diff --git a/Emby.Server.Implementations/EntryPoints/ExternalPortForwarding.cs b/Emby.Server.Implementations/EntryPoints/ExternalPortForwarding.cs index 4e4ef3be0..e290c62e1 100644 --- a/Emby.Server.Implementations/EntryPoints/ExternalPortForwarding.cs +++ b/Emby.Server.Implementations/EntryPoints/ExternalPortForwarding.cs @@ -1,5 +1,4 @@ #pragma warning disable CS1591 -#pragma warning disable SA1600 using System; using System.Collections.Generic; diff --git a/Emby.Server.Implementations/EntryPoints/LibraryChangedNotifier.cs b/Emby.Server.Implementations/EntryPoints/LibraryChangedNotifier.cs index 06458baed..8e3236407 100644 --- a/Emby.Server.Implementations/EntryPoints/LibraryChangedNotifier.cs +++ b/Emby.Server.Implementations/EntryPoints/LibraryChangedNotifier.cs @@ -1,5 +1,4 @@ #pragma warning disable CS1591 -#pragma warning disable SA1600 using System; using System.Collections.Generic; @@ -56,7 +55,12 @@ namespace Emby.Server.Implementations.EntryPoints private readonly IProviderManager _providerManager; - public LibraryChangedNotifier(ILibraryManager libraryManager, ISessionManager sessionManager, IUserManager userManager, ILogger logger, IProviderManager providerManager) + public LibraryChangedNotifier( + ILibraryManager libraryManager, + ISessionManager sessionManager, + IUserManager userManager, + ILogger<LibraryChangedNotifier> logger, + IProviderManager providerManager) { _libraryManager = libraryManager; _sessionManager = sessionManager; diff --git a/Emby.Server.Implementations/EntryPoints/RecordingNotifier.cs b/Emby.Server.Implementations/EntryPoints/RecordingNotifier.cs index e0aa18e89..41c0c5115 100644 --- a/Emby.Server.Implementations/EntryPoints/RecordingNotifier.cs +++ b/Emby.Server.Implementations/EntryPoints/RecordingNotifier.cs @@ -1,5 +1,4 @@ #pragma warning disable CS1591 -#pragma warning disable SA1600 using System; using System.Linq; @@ -13,14 +12,18 @@ using Microsoft.Extensions.Logging; namespace Emby.Server.Implementations.EntryPoints { - public class RecordingNotifier : IServerEntryPoint + public sealed class RecordingNotifier : IServerEntryPoint { private readonly ILiveTvManager _liveTvManager; private readonly ISessionManager _sessionManager; private readonly IUserManager _userManager; private readonly ILogger _logger; - public RecordingNotifier(ISessionManager sessionManager, IUserManager userManager, ILogger logger, ILiveTvManager liveTvManager) + public RecordingNotifier( + ISessionManager sessionManager, + IUserManager userManager, + ILogger<RecordingNotifier> logger, + ILiveTvManager liveTvManager) { _sessionManager = sessionManager; _userManager = userManager; @@ -28,32 +31,33 @@ namespace Emby.Server.Implementations.EntryPoints _liveTvManager = liveTvManager; } + /// <inheritdoc /> public Task RunAsync() { - _liveTvManager.TimerCancelled += _liveTvManager_TimerCancelled; - _liveTvManager.SeriesTimerCancelled += _liveTvManager_SeriesTimerCancelled; - _liveTvManager.TimerCreated += _liveTvManager_TimerCreated; - _liveTvManager.SeriesTimerCreated += _liveTvManager_SeriesTimerCreated; + _liveTvManager.TimerCancelled += OnLiveTvManagerTimerCancelled; + _liveTvManager.SeriesTimerCancelled += OnLiveTvManagerSeriesTimerCancelled; + _liveTvManager.TimerCreated += OnLiveTvManagerTimerCreated; + _liveTvManager.SeriesTimerCreated += OnLiveTvManagerSeriesTimerCreated; return Task.CompletedTask; } - private void _liveTvManager_SeriesTimerCreated(object sender, MediaBrowser.Model.Events.GenericEventArgs<TimerEventInfo> e) + private void OnLiveTvManagerSeriesTimerCreated(object sender, MediaBrowser.Model.Events.GenericEventArgs<TimerEventInfo> e) { SendMessage("SeriesTimerCreated", e.Argument); } - private void _liveTvManager_TimerCreated(object sender, MediaBrowser.Model.Events.GenericEventArgs<TimerEventInfo> e) + private void OnLiveTvManagerTimerCreated(object sender, MediaBrowser.Model.Events.GenericEventArgs<TimerEventInfo> e) { SendMessage("TimerCreated", e.Argument); } - private void _liveTvManager_SeriesTimerCancelled(object sender, MediaBrowser.Model.Events.GenericEventArgs<TimerEventInfo> e) + private void OnLiveTvManagerSeriesTimerCancelled(object sender, MediaBrowser.Model.Events.GenericEventArgs<TimerEventInfo> e) { SendMessage("SeriesTimerCancelled", e.Argument); } - private void _liveTvManager_TimerCancelled(object sender, MediaBrowser.Model.Events.GenericEventArgs<TimerEventInfo> e) + private void OnLiveTvManagerTimerCancelled(object sender, MediaBrowser.Model.Events.GenericEventArgs<TimerEventInfo> e) { SendMessage("TimerCancelled", e.Argument); } @@ -64,11 +68,7 @@ namespace Emby.Server.Implementations.EntryPoints try { - await _sessionManager.SendMessageToUserSessions(users, name, info, CancellationToken.None); - } - catch (ObjectDisposedException) - { - // TODO Log exception or Investigate and properly fix. + await _sessionManager.SendMessageToUserSessions(users, name, info, CancellationToken.None).ConfigureAwait(false); } catch (Exception ex) { @@ -76,12 +76,13 @@ namespace Emby.Server.Implementations.EntryPoints } } + /// <inheritdoc /> public void Dispose() { - _liveTvManager.TimerCancelled -= _liveTvManager_TimerCancelled; - _liveTvManager.SeriesTimerCancelled -= _liveTvManager_SeriesTimerCancelled; - _liveTvManager.TimerCreated -= _liveTvManager_TimerCreated; - _liveTvManager.SeriesTimerCreated -= _liveTvManager_SeriesTimerCreated; + _liveTvManager.TimerCancelled -= OnLiveTvManagerTimerCancelled; + _liveTvManager.SeriesTimerCancelled -= OnLiveTvManagerSeriesTimerCancelled; + _liveTvManager.TimerCreated -= OnLiveTvManagerTimerCreated; + _liveTvManager.SeriesTimerCreated -= OnLiveTvManagerSeriesTimerCreated; } } } diff --git a/Emby.Server.Implementations/EntryPoints/RefreshUsersMetadata.cs b/Emby.Server.Implementations/EntryPoints/RefreshUsersMetadata.cs index f00996b5f..54f4b67e6 100644 --- a/Emby.Server.Implementations/EntryPoints/RefreshUsersMetadata.cs +++ b/Emby.Server.Implementations/EntryPoints/RefreshUsersMetadata.cs @@ -6,7 +6,6 @@ using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Providers; using MediaBrowser.Model.IO; using MediaBrowser.Model.Tasks; -using Microsoft.Extensions.Logging; namespace Emby.Server.Implementations.EntryPoints { @@ -15,21 +14,17 @@ namespace Emby.Server.Implementations.EntryPoints /// </summary> public class RefreshUsersMetadata : IScheduledTask, IConfigurableScheduledTask { - private readonly ILogger _logger; - /// <summary> /// The user manager. /// </summary> private readonly IUserManager _userManager; - - private IFileSystem _fileSystem; + private readonly IFileSystem _fileSystem; /// <summary> /// Initializes a new instance of the <see cref="RefreshUsersMetadata" /> class. /// </summary> - public RefreshUsersMetadata(ILogger logger, IUserManager userManager, IFileSystem fileSystem) + public RefreshUsersMetadata(IUserManager userManager, IFileSystem fileSystem) { - _logger = logger; _userManager = userManager; _fileSystem = fileSystem; } diff --git a/Emby.Server.Implementations/EntryPoints/StartupWizard.cs b/Emby.Server.Implementations/EntryPoints/StartupWizard.cs index 161788c63..8e9771931 100644 --- a/Emby.Server.Implementations/EntryPoints/StartupWizard.cs +++ b/Emby.Server.Implementations/EntryPoints/StartupWizard.cs @@ -2,38 +2,30 @@ using System.Threading.Tasks; using Emby.Server.Implementations.Browser; using MediaBrowser.Controller; using MediaBrowser.Controller.Configuration; +using MediaBrowser.Controller.Extensions; using MediaBrowser.Controller.Plugins; -using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Configuration; namespace Emby.Server.Implementations.EntryPoints { /// <summary> /// Class StartupWizard. /// </summary> - public class StartupWizard : IServerEntryPoint + public sealed class StartupWizard : IServerEntryPoint { - /// <summary> - /// The app host. - /// </summary> private readonly IServerApplicationHost _appHost; - - /// <summary> - /// The user manager. - /// </summary> - private readonly ILogger _logger; - - private IServerConfigurationManager _config; + private readonly IConfiguration _appConfig; + private readonly IServerConfigurationManager _config; /// <summary> /// Initializes a new instance of the <see cref="StartupWizard"/> class. /// </summary> /// <param name="appHost">The application host.</param> - /// <param name="logger">The logger.</param> /// <param name="config">The configuration manager.</param> - public StartupWizard(IServerApplicationHost appHost, ILogger logger, IServerConfigurationManager config) + public StartupWizard(IServerApplicationHost appHost, IConfiguration appConfig, IServerConfigurationManager config) { _appHost = appHost; - _logger = logger; + _appConfig = appConfig; _config = config; } @@ -45,7 +37,11 @@ namespace Emby.Server.Implementations.EntryPoints return Task.CompletedTask; } - if (!_config.Configuration.IsStartupWizardCompleted) + if (!_appConfig.HostWebClient()) + { + BrowserLauncher.OpenSwaggerPage(_appHost); + } + else if (!_config.Configuration.IsStartupWizardCompleted) { BrowserLauncher.OpenWebApp(_appHost); } diff --git a/Emby.Server.Implementations/EntryPoints/UdpServerEntryPoint.cs b/Emby.Server.Implementations/EntryPoints/UdpServerEntryPoint.cs index 529f83560..50ba0f8fa 100644 --- a/Emby.Server.Implementations/EntryPoints/UdpServerEntryPoint.cs +++ b/Emby.Server.Implementations/EntryPoints/UdpServerEntryPoint.cs @@ -3,8 +3,6 @@ using System.Threading.Tasks; using Emby.Server.Implementations.Udp; using MediaBrowser.Controller; using MediaBrowser.Controller.Plugins; -using MediaBrowser.Model.Net; -using MediaBrowser.Model.Serialization; using Microsoft.Extensions.Logging; namespace Emby.Server.Implementations.EntryPoints @@ -23,9 +21,7 @@ namespace Emby.Server.Implementations.EntryPoints /// The logger. /// </summary> private readonly ILogger _logger; - private readonly ISocketFactory _socketFactory; private readonly IServerApplicationHost _appHost; - private readonly IJsonSerializer _json; /// <summary> /// The UDP server. @@ -64,7 +60,7 @@ namespace Emby.Server.Implementations.EntryPoints _cancellationTokenSource.Cancel(); _udpServer.Dispose(); - + _cancellationTokenSource.Dispose(); _cancellationTokenSource = null; _udpServer = null; diff --git a/Emby.Server.Implementations/EntryPoints/UserDataChangeNotifier.cs b/Emby.Server.Implementations/EntryPoints/UserDataChangeNotifier.cs index 3e22080fc..3618b88c5 100644 --- a/Emby.Server.Implementations/EntryPoints/UserDataChangeNotifier.cs +++ b/Emby.Server.Implementations/EntryPoints/UserDataChangeNotifier.cs @@ -1,5 +1,4 @@ #pragma warning disable CS1591 -#pragma warning disable SA1600 using System; using System.Collections.Generic; @@ -13,39 +12,38 @@ using MediaBrowser.Controller.Plugins; using MediaBrowser.Controller.Session; using MediaBrowser.Model.Entities; using MediaBrowser.Model.Session; -using Microsoft.Extensions.Logging; namespace Emby.Server.Implementations.EntryPoints { - public class UserDataChangeNotifier : IServerEntryPoint + public sealed class UserDataChangeNotifier : IServerEntryPoint { + private const int UpdateDuration = 500; + private readonly ISessionManager _sessionManager; - private readonly ILogger _logger; private readonly IUserDataManager _userDataManager; private readonly IUserManager _userManager; + private readonly Dictionary<Guid, List<BaseItem>> _changedItems = new Dictionary<Guid, List<BaseItem>>(); + private readonly object _syncLock = new object(); - private Timer UpdateTimer { get; set; } - private const int UpdateDuration = 500; + private Timer _updateTimer; - private readonly Dictionary<Guid, List<BaseItem>> _changedItems = new Dictionary<Guid, List<BaseItem>>(); - public UserDataChangeNotifier(IUserDataManager userDataManager, ISessionManager sessionManager, ILogger logger, IUserManager userManager) + public UserDataChangeNotifier(IUserDataManager userDataManager, ISessionManager sessionManager, IUserManager userManager) { _userDataManager = userDataManager; _sessionManager = sessionManager; - _logger = logger; _userManager = userManager; } public Task RunAsync() { - _userDataManager.UserDataSaved += _userDataManager_UserDataSaved; + _userDataManager.UserDataSaved += OnUserDataManagerUserDataSaved; return Task.CompletedTask; } - void _userDataManager_UserDataSaved(object sender, UserDataSaveEventArgs e) + void OnUserDataManagerUserDataSaved(object sender, UserDataSaveEventArgs e) { if (e.SaveReason == UserDataSaveReason.PlaybackProgress) { @@ -54,14 +52,17 @@ namespace Emby.Server.Implementations.EntryPoints lock (_syncLock) { - if (UpdateTimer == null) + if (_updateTimer == null) { - UpdateTimer = new Timer(UpdateTimerCallback, null, UpdateDuration, - Timeout.Infinite); + _updateTimer = new Timer( + UpdateTimerCallback, + null, + UpdateDuration, + Timeout.Infinite); } else { - UpdateTimer.Change(UpdateDuration, Timeout.Infinite); + _updateTimer.Change(UpdateDuration, Timeout.Infinite); } if (!_changedItems.TryGetValue(e.UserId, out List<BaseItem> keys)) @@ -97,10 +98,10 @@ namespace Emby.Server.Implementations.EntryPoints var task = SendNotifications(changes, CancellationToken.None); - if (UpdateTimer != null) + if (_updateTimer != null) { - UpdateTimer.Dispose(); - UpdateTimer = null; + _updateTimer.Dispose(); + _updateTimer = null; } } } @@ -145,13 +146,13 @@ namespace Emby.Server.Implementations.EntryPoints public void Dispose() { - if (UpdateTimer != null) + if (_updateTimer != null) { - UpdateTimer.Dispose(); - UpdateTimer = null; + _updateTimer.Dispose(); + _updateTimer = null; } - _userDataManager.UserDataSaved -= _userDataManager_UserDataSaved; + _userDataManager.UserDataSaved -= OnUserDataManagerUserDataSaved; } } } diff --git a/Emby.Server.Implementations/HttpClientManager/HttpClientManager.cs b/Emby.Server.Implementations/HttpClientManager/HttpClientManager.cs index 8a2bc83fb..882bfe2f6 100644 --- a/Emby.Server.Implementations/HttpClientManager/HttpClientManager.cs +++ b/Emby.Server.Implementations/HttpClientManager/HttpClientManager.cs @@ -78,7 +78,7 @@ namespace Emby.Server.Implementations.HttpClientManager if (!string.IsNullOrWhiteSpace(userInfo)) { _logger.LogWarning("Found userInfo in url: {0} ... url: {1}", userInfo, url); - url = url.Replace(userInfo + '@', string.Empty); + url = url.Replace(userInfo + '@', string.Empty, StringComparison.Ordinal); } var request = new HttpRequestMessage(method, url); @@ -96,13 +96,13 @@ namespace Emby.Server.Implementations.HttpClientManager switch (options.DecompressionMethod) { - case CompressionMethod.Deflate | CompressionMethod.Gzip: + case CompressionMethods.Deflate | CompressionMethods.Gzip: request.Headers.Add(HeaderNames.AcceptEncoding, new[] { "gzip", "deflate" }); break; - case CompressionMethod.Deflate: + case CompressionMethods.Deflate: request.Headers.Add(HeaderNames.AcceptEncoding, "deflate"); break; - case CompressionMethod.Gzip: + case CompressionMethods.Gzip: request.Headers.Add(HeaderNames.AcceptEncoding, "gzip"); break; default: @@ -239,15 +239,10 @@ namespace Emby.Server.Implementations.HttpClientManager var httpWebRequest = GetRequestMessage(options, httpMethod); - if (options.RequestContentBytes != null - || !string.IsNullOrEmpty(options.RequestContent) + if (!string.IsNullOrEmpty(options.RequestContent) || httpMethod == HttpMethod.Post) { - if (options.RequestContentBytes != null) - { - httpWebRequest.Content = new ByteArrayContent(options.RequestContentBytes); - } - else if (options.RequestContent != null) + if (options.RequestContent != null) { httpWebRequest.Content = new StringContent( options.RequestContent, diff --git a/Emby.Server.Implementations/HttpServer/FileWriter.cs b/Emby.Server.Implementations/HttpServer/FileWriter.cs index d36f230d6..0b61e40b0 100644 --- a/Emby.Server.Implementations/HttpServer/FileWriter.cs +++ b/Emby.Server.Implementations/HttpServer/FileWriter.cs @@ -1,5 +1,4 @@ #pragma warning disable CS1591 -#pragma warning disable SA1600 using System; using System.Collections.Generic; @@ -12,8 +11,8 @@ using System.Threading; using System.Threading.Tasks; using MediaBrowser.Model.IO; using MediaBrowser.Model.Services; -using Microsoft.Extensions.Logging; using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.Logging; using Microsoft.Net.Http.Headers; namespace Emby.Server.Implementations.HttpServer diff --git a/Emby.Server.Implementations/HttpServer/HttpListenerHost.cs b/Emby.Server.Implementations/HttpServer/HttpListenerHost.cs index b0126f7fa..72667a314 100644 --- a/Emby.Server.Implementations/HttpServer/HttpListenerHost.cs +++ b/Emby.Server.Implementations/HttpServer/HttpListenerHost.cs @@ -1,5 +1,4 @@ #pragma warning disable CS1591 -#pragma warning disable SA1600 using System; using System.Collections.Generic; @@ -18,11 +17,13 @@ using MediaBrowser.Controller; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Net; using MediaBrowser.Model.Events; +using MediaBrowser.Model.Globalization; using MediaBrowser.Model.Serialization; using MediaBrowser.Model.Services; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.WebUtilities; using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; using ServiceStack.Text.Jsv; @@ -30,6 +31,12 @@ namespace Emby.Server.Implementations.HttpServer { public class HttpListenerHost : IHttpServer, IDisposable { + /// <summary> + /// The key for a setting that specifies the default redirect path + /// to use for requests where the URL base prefix is invalid or missing. + /// </summary> + public const string DefaultRedirectKey = "HttpListenerHost:DefaultRedirectPath"; + private readonly ILogger _logger; private readonly IServerConfigurationManager _config; private readonly INetworkManager _networkManager; @@ -40,9 +47,11 @@ namespace Emby.Server.Implementations.HttpServer private readonly Func<Type, Func<string, object>> _funcParseFn; private readonly string _defaultRedirectPath; private readonly string _baseUrlPrefix; - private readonly Dictionary<Type, Type> ServiceOperationsMap = new Dictionary<Type, Type>(); - private IWebSocketListener[] _webSocketListeners = Array.Empty<IWebSocketListener>(); + private readonly Dictionary<Type, Type> _serviceOperationsMap = new Dictionary<Type, Type>(); private readonly List<IWebSocketConnection> _webSocketConnections = new List<IWebSocketConnection>(); + private readonly IHostEnvironment _hostEnvironment; + + private IWebSocketListener[] _webSocketListeners = Array.Empty<IWebSocketListener>(); private bool _disposed = false; public HttpListenerHost( @@ -53,25 +62,34 @@ namespace Emby.Server.Implementations.HttpServer INetworkManager networkManager, IJsonSerializer jsonSerializer, IXmlSerializer xmlSerializer, - IHttpListener socketListener) + IHttpListener socketListener, + ILocalizationManager localizationManager, + ServiceController serviceController, + IHostEnvironment hostEnvironment) { _appHost = applicationHost; _logger = logger; _config = config; - _defaultRedirectPath = configuration["HttpListenerHost:DefaultRedirectPath"]; + _defaultRedirectPath = configuration[DefaultRedirectKey]; _baseUrlPrefix = _config.Configuration.BaseUrl; _networkManager = networkManager; _jsonSerializer = jsonSerializer; _xmlSerializer = xmlSerializer; _socketListener = socketListener; + ServiceController = serviceController; + _socketListener.WebSocketConnected = OnWebSocketConnected; + _hostEnvironment = hostEnvironment; _funcParseFn = t => s => JsvReader.GetParseFn(t)(s); Instance = this; ResponseFilters = Array.Empty<Action<IRequest, HttpResponse, object>>(); + GlobalResponse = localizationManager.GetLocalizedString("StartupEmbyServerIsLoading"); } + public event EventHandler<GenericEventArgs<IWebSocketConnection>> WebSocketConnected; + public Action<IRequest, HttpResponse, object>[] ResponseFilters { get; set; } public static HttpListenerHost Instance { get; protected set; } @@ -80,9 +98,7 @@ namespace Emby.Server.Implementations.HttpServer public string GlobalResponse { get; set; } - public ServiceController ServiceController { get; private set; } - - public event EventHandler<GenericEventArgs<IWebSocketConnection>> WebSocketConnected; + public ServiceController ServiceController { get; } public object CreateInstance(Type type) { @@ -91,7 +107,7 @@ namespace Emby.Server.Implementations.HttpServer private static string NormalizeUrlPath(string path) { - if (path.StartsWith("/")) + if (path.Length > 0 && path[0] == '/') { // If the path begins with a leading slash, just return it as-is return path; @@ -131,13 +147,13 @@ namespace Emby.Server.Implementations.HttpServer public Type GetServiceTypeByRequest(Type requestType) { - ServiceOperationsMap.TryGetValue(requestType, out var serviceType); + _serviceOperationsMap.TryGetValue(requestType, out var serviceType); return serviceType; } public void AddServiceInfo(Type serviceType, Type requestType) { - ServiceOperationsMap[requestType] = serviceType; + _serviceOperationsMap[requestType] = serviceType; } private List<IHasRequestFilter> GetRequestFilterAttributes(Type requestDtoType) @@ -199,7 +215,7 @@ namespace Emby.Server.Implementations.HttpServer else { var inners = agg.InnerExceptions; - if (inners != null && inners.Count > 0) + if (inners.Count > 0) { return GetActualException(inners[0]); } @@ -362,7 +378,7 @@ namespace Emby.Server.Implementations.HttpServer return true; } - host = host ?? string.Empty; + host ??= string.Empty; if (_networkManager.IsInPrivateAddressSpace(host)) { @@ -433,7 +449,7 @@ namespace Emby.Server.Implementations.HttpServer } /// <summary> - /// Overridable method that can be used to implement a custom hnandler + /// Overridable method that can be used to implement a custom handler. /// </summary> public async Task RequestHandler(IHttpRequest httpReq, string urlString, string host, string localPath, CancellationToken cancellationToken) { @@ -492,7 +508,7 @@ namespace Emby.Server.Implementations.HttpServer || string.Equals(localPath, _baseUrlPrefix, StringComparison.OrdinalIgnoreCase) || string.Equals(localPath, "/", StringComparison.OrdinalIgnoreCase) || string.IsNullOrEmpty(localPath) - || !localPath.StartsWith(_baseUrlPrefix)) + || !localPath.StartsWith(_baseUrlPrefix, StringComparison.OrdinalIgnoreCase)) { // Always redirect back to the default path if the base prefix is invalid or missing _logger.LogDebug("Normalizing a URL at {0}", localPath); @@ -519,22 +535,25 @@ namespace Emby.Server.Implementations.HttpServer } else { - await ErrorHandler(new FileNotFoundException(), httpReq, false).ConfigureAwait(false); + throw new FileNotFoundException(); } } - catch (Exception ex) when (ex is SocketException || ex is IOException || ex is OperationCanceledException) - { - await ErrorHandler(ex, httpReq, false).ConfigureAwait(false); - } - catch (SecurityException ex) - { - await ErrorHandler(ex, httpReq, false).ConfigureAwait(false); - } catch (Exception ex) { - var logException = !string.Equals(ex.GetType().Name, "SocketException", StringComparison.OrdinalIgnoreCase); + // Do not handle exceptions manually when in development mode + // The framework-defined development exception page will be returned instead + if (_hostEnvironment.IsDevelopment()) + { + throw; + } - await ErrorHandler(ex, httpReq, logException).ConfigureAwait(false); + bool ignoreStackTrace = + ex is SocketException + || ex is IOException + || ex is OperationCanceledException + || ex is SecurityException + || ex is FileNotFoundException; + await ErrorHandler(ex, httpReq, ignoreStackTrace).ConfigureAwait(false); } finally { @@ -586,17 +605,15 @@ namespace Emby.Server.Implementations.HttpServer /// <summary> /// Adds the rest handlers. /// </summary> - /// <param name="services">The services.</param> - /// <param name="listeners"></param> - /// <param name="urlPrefixes"></param> - public void Init(IEnumerable<IService> services, IEnumerable<IWebSocketListener> listeners, IEnumerable<string> urlPrefixes) + /// <param name="serviceTypes">The service types to register with the <see cref="ServiceController"/>.</param> + /// <param name="listeners">The web socket listeners.</param> + /// <param name="urlPrefixes">The URL prefixes. See <see cref="UrlPrefixes"/>.</param> + public void Init(IEnumerable<Type> serviceTypes, IEnumerable<IWebSocketListener> listeners, IEnumerable<string> urlPrefixes) { _webSocketListeners = listeners.ToArray(); UrlPrefixes = urlPrefixes.ToArray(); - ServiceController = new ServiceController(); - var types = services.Select(r => r.GetType()); - ServiceController.Init(this, types); + ServiceController.Init(this, serviceTypes); ResponseFilters = new Action<IRequest, HttpResponse, object>[] { @@ -693,7 +710,10 @@ namespace Emby.Server.Implementations.HttpServer protected virtual void Dispose(bool disposing) { - if (_disposed) return; + if (_disposed) + { + return; + } if (disposing) { diff --git a/Emby.Server.Implementations/HttpServer/HttpResultFactory.cs b/Emby.Server.Implementations/HttpServer/HttpResultFactory.cs index 98a4f140e..b42662420 100644 --- a/Emby.Server.Implementations/HttpServer/HttpResultFactory.cs +++ b/Emby.Server.Implementations/HttpServer/HttpResultFactory.cs @@ -1,5 +1,4 @@ #pragma warning disable CS1591 -#pragma warning disable SA1600 using System; using System.Collections.Generic; diff --git a/Emby.Server.Implementations/HttpServer/IHttpListener.cs b/Emby.Server.Implementations/HttpServer/IHttpListener.cs index 1c3496e5d..501593725 100644 --- a/Emby.Server.Implementations/HttpServer/IHttpListener.cs +++ b/Emby.Server.Implementations/HttpServer/IHttpListener.cs @@ -1,5 +1,4 @@ #pragma warning disable CS1591 -#pragma warning disable SA1600 using System; using System.Threading; diff --git a/Emby.Server.Implementations/HttpServer/RangeRequestWriter.cs b/Emby.Server.Implementations/HttpServer/RangeRequestWriter.cs index 7cb113a58..8b9028f6b 100644 --- a/Emby.Server.Implementations/HttpServer/RangeRequestWriter.cs +++ b/Emby.Server.Implementations/HttpServer/RangeRequestWriter.cs @@ -1,5 +1,4 @@ #pragma warning disable CS1591 -#pragma warning disable SA1600 using System; using System.Collections.Generic; diff --git a/Emby.Server.Implementations/HttpServer/Security/AuthService.cs b/Emby.Server.Implementations/HttpServer/Security/AuthService.cs index 03b5b748d..58421aaf1 100644 --- a/Emby.Server.Implementations/HttpServer/Security/AuthService.cs +++ b/Emby.Server.Implementations/HttpServer/Security/AuthService.cs @@ -1,5 +1,4 @@ #pragma warning disable CS1591 -#pragma warning disable SA1600 using System; using System.Linq; diff --git a/Emby.Server.Implementations/HttpServer/Security/AuthorizationContext.cs b/Emby.Server.Implementations/HttpServer/Security/AuthorizationContext.cs index e8884bca0..129faeaab 100644 --- a/Emby.Server.Implementations/HttpServer/Security/AuthorizationContext.cs +++ b/Emby.Server.Implementations/HttpServer/Security/AuthorizationContext.cs @@ -1,5 +1,4 @@ #pragma warning disable CS1591 -#pragma warning disable SA1600 using System; using System.Collections.Generic; diff --git a/Emby.Server.Implementations/HttpServer/Security/SessionContext.cs b/Emby.Server.Implementations/HttpServer/Security/SessionContext.cs index a6a0f5b03..166952c64 100644 --- a/Emby.Server.Implementations/HttpServer/Security/SessionContext.cs +++ b/Emby.Server.Implementations/HttpServer/Security/SessionContext.cs @@ -1,5 +1,4 @@ #pragma warning disable CS1591 -#pragma warning disable SA1600 using System; using MediaBrowser.Controller.Entities; diff --git a/Emby.Server.Implementations/IO/ExtendedFileSystemInfo.cs b/Emby.Server.Implementations/IO/ExtendedFileSystemInfo.cs index 5be144452..545d73e05 100644 --- a/Emby.Server.Implementations/IO/ExtendedFileSystemInfo.cs +++ b/Emby.Server.Implementations/IO/ExtendedFileSystemInfo.cs @@ -1,12 +1,13 @@ #pragma warning disable CS1591 -#pragma warning disable SA1600 namespace Emby.Server.Implementations.IO { public class ExtendedFileSystemInfo { public bool IsHidden { get; set; } + public bool IsReadOnly { get; set; } + public bool Exists { get; set; } } } diff --git a/Emby.Server.Implementations/IO/FileRefresher.cs b/Emby.Server.Implementations/IO/FileRefresher.cs index cf92ddbcd..ef93779aa 100644 --- a/Emby.Server.Implementations/IO/FileRefresher.cs +++ b/Emby.Server.Implementations/IO/FileRefresher.cs @@ -1,5 +1,4 @@ #pragma warning disable CS1591 -#pragma warning disable SA1600 using System; using System.Collections.Generic; @@ -15,27 +14,29 @@ namespace Emby.Server.Implementations.IO { public class FileRefresher : IDisposable { - private ILogger Logger { get; set; } - private ILibraryManager LibraryManager { get; set; } - private IServerConfigurationManager ConfigurationManager { get; set; } + private readonly ILogger _logger; + private readonly ILibraryManager _libraryManager; + private readonly IServerConfigurationManager _configurationManager; + 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 Timer _timer; public FileRefresher(string path, IServerConfigurationManager configurationManager, ILibraryManager libraryManager, ILogger logger) { logger.LogDebug("New file refresher created for {0}", path); Path = path; - ConfigurationManager = configurationManager; - LibraryManager = libraryManager; - Logger = logger; + _configurationManager = configurationManager; + _libraryManager = libraryManager; + _logger = logger; AddPath(path); } + public event EventHandler<EventArgs> Completed; + + public string Path { get; private set; } + private void AddAffectedPath(string path) { if (string.IsNullOrEmpty(path)) @@ -80,11 +81,11 @@ namespace Emby.Server.Implementations.IO if (_timer == null) { - _timer = new Timer(OnTimerCallback, null, TimeSpan.FromSeconds(ConfigurationManager.Configuration.LibraryMonitorDelay), TimeSpan.FromMilliseconds(-1)); + _timer = new Timer(OnTimerCallback, null, TimeSpan.FromSeconds(_configurationManager.Configuration.LibraryMonitorDelay), TimeSpan.FromMilliseconds(-1)); } else { - _timer.Change(TimeSpan.FromSeconds(ConfigurationManager.Configuration.LibraryMonitorDelay), TimeSpan.FromMilliseconds(-1)); + _timer.Change(TimeSpan.FromSeconds(_configurationManager.Configuration.LibraryMonitorDelay), TimeSpan.FromMilliseconds(-1)); } } } @@ -93,7 +94,7 @@ namespace Emby.Server.Implementations.IO { lock (_timerLock) { - Logger.LogDebug("Resetting file refresher from {0} to {1}", Path, path); + _logger.LogDebug("Resetting file refresher from {0} to {1}", Path, path); Path = path; AddAffectedPath(path); @@ -116,7 +117,7 @@ namespace Emby.Server.Implementations.IO paths = _affectedPaths.ToList(); } - Logger.LogDebug("Timer stopped."); + _logger.LogDebug("Timer stopped."); DisposeTimer(); Completed?.Invoke(this, EventArgs.Empty); @@ -127,7 +128,7 @@ namespace Emby.Server.Implementations.IO } catch (Exception ex) { - Logger.LogError(ex, "Error processing directory changes"); + _logger.LogError(ex, "Error processing directory changes"); } } @@ -147,7 +148,7 @@ namespace Emby.Server.Implementations.IO continue; } - Logger.LogInformation("{name} ({path}) will be refreshed.", item.Name, item.Path); + _logger.LogInformation("{name} ({path}) will be refreshed.", item.Name, item.Path); try { @@ -158,11 +159,11 @@ namespace Emby.Server.Implementations.IO // For now swallow and log. // Research item: If an IOException occurs, the item may be in a disconnected state (media unavailable) // Should we remove it from it's parent? - Logger.LogError(ex, "Error refreshing {name}", item.Name); + _logger.LogError(ex, "Error refreshing {name}", item.Name); } catch (Exception ex) { - Logger.LogError(ex, "Error refreshing {name}", item.Name); + _logger.LogError(ex, "Error refreshing {name}", item.Name); } } } @@ -178,7 +179,7 @@ namespace Emby.Server.Implementations.IO while (item == null && !string.IsNullOrEmpty(path)) { - item = LibraryManager.FindByPath(path, null); + item = _libraryManager.FindByPath(path, null); path = System.IO.Path.GetDirectoryName(path); } diff --git a/Emby.Server.Implementations/IO/LibraryMonitor.cs b/Emby.Server.Implementations/IO/LibraryMonitor.cs index 7777efc3b..b1fb8cc63 100644 --- a/Emby.Server.Implementations/IO/LibraryMonitor.cs +++ b/Emby.Server.Implementations/IO/LibraryMonitor.cs @@ -1,5 +1,4 @@ #pragma warning disable CS1591 -#pragma warning disable SA1600 using System; using System.Collections.Concurrent; diff --git a/Emby.Server.Implementations/IO/ManagedFileSystem.cs b/Emby.Server.Implementations/IO/ManagedFileSystem.cs index da5a4d50e..7461ec4f1 100644 --- a/Emby.Server.Implementations/IO/ManagedFileSystem.cs +++ b/Emby.Server.Implementations/IO/ManagedFileSystem.cs @@ -1,5 +1,4 @@ #pragma warning disable CS1591 -#pragma warning disable SA1600 using System; using System.Collections.Generic; @@ -588,11 +587,11 @@ namespace Emby.Server.Implementations.IO // some drives on linux have no actual size or are used for other purposes return DriveInfo.GetDrives().Where(d => d.IsReady && d.TotalSize != 0 && d.DriveType != DriveType.Ram) .Select(d => new FileSystemMetadata - { - Name = d.Name, - FullName = d.RootDirectory.FullName, - IsDirectory = true - }).ToList(); + { + Name = d.Name, + FullName = d.RootDirectory.FullName, + IsDirectory = true + }).ToList(); } public virtual IEnumerable<FileSystemMetadata> GetDirectories(string path, bool recursive = false) diff --git a/Emby.Server.Implementations/IO/MbLinkShortcutHandler.cs b/Emby.Server.Implementations/IO/MbLinkShortcutHandler.cs index 574b63ae6..e6696b8c4 100644 --- a/Emby.Server.Implementations/IO/MbLinkShortcutHandler.cs +++ b/Emby.Server.Implementations/IO/MbLinkShortcutHandler.cs @@ -1,5 +1,4 @@ #pragma warning disable CS1591 -#pragma warning disable SA1600 using System; using System.IO; diff --git a/Emby.Server.Implementations/IO/StreamHelper.cs b/Emby.Server.Implementations/IO/StreamHelper.cs index c99018e40..40b397edc 100644 --- a/Emby.Server.Implementations/IO/StreamHelper.cs +++ b/Emby.Server.Implementations/IO/StreamHelper.cs @@ -1,5 +1,4 @@ #pragma warning disable CS1591 -#pragma warning disable SA1600 using System; using System.Buffers; diff --git a/Emby.Server.Implementations/IStartupOptions.cs b/Emby.Server.Implementations/IStartupOptions.cs index 6e915de3d..16b68170b 100644 --- a/Emby.Server.Implementations/IStartupOptions.cs +++ b/Emby.Server.Implementations/IStartupOptions.cs @@ -3,33 +3,38 @@ namespace Emby.Server.Implementations public interface IStartupOptions { /// <summary> - /// --ffmpeg + /// Gets the value of the --ffmpeg command line option. /// </summary> string FFmpegPath { get; } /// <summary> - /// --service + /// Gets the value of the --service command line option. /// </summary> bool IsService { get; } /// <summary> - /// --noautorunwebapp + /// Gets the value of the --noautorunwebapp command line option. /// </summary> bool NoAutoRunWebApp { get; } /// <summary> - /// --package-name + /// Gets the value of the --package-name command line option. /// </summary> string PackageName { get; } /// <summary> - /// --restartpath + /// Gets the value of the --restartpath command line option. /// </summary> string RestartPath { get; } /// <summary> - /// --restartargs + /// Gets the value of the --restartargs command line option. /// </summary> string RestartArgs { get; } + + /// <summary> + /// Gets the value of the --plugin-manifest-url command line option. + /// </summary> + string PluginManifestUrl { get; } } } diff --git a/Emby.Server.Implementations/Images/BaseDynamicImageProvider.cs b/Emby.Server.Implementations/Images/BaseDynamicImageProvider.cs index acf3a3b23..fd50f156a 100644 --- a/Emby.Server.Implementations/Images/BaseDynamicImageProvider.cs +++ b/Emby.Server.Implementations/Images/BaseDynamicImageProvider.cs @@ -1,5 +1,4 @@ #pragma warning disable CS1591 -#pragma warning disable SA1600 using System; using System.Collections.Generic; diff --git a/Emby.Server.Implementations/Library/ExclusiveLiveStream.cs b/Emby.Server.Implementations/Library/ExclusiveLiveStream.cs index 3eb64c29c..9a7186898 100644 --- a/Emby.Server.Implementations/Library/ExclusiveLiveStream.cs +++ b/Emby.Server.Implementations/Library/ExclusiveLiveStream.cs @@ -1,5 +1,4 @@ #pragma warning disable CS1591 -#pragma warning disable SA1600 using System; using System.Globalization; diff --git a/Emby.Server.Implementations/Library/LibraryManager.cs b/Emby.Server.Implementations/Library/LibraryManager.cs index 55566ed51..4e2cf334c 100644 --- a/Emby.Server.Implementations/Library/LibraryManager.cs +++ b/Emby.Server.Implementations/Library/LibraryManager.cs @@ -1,5 +1,4 @@ #pragma warning disable CS1591 -#pragma warning disable SA1600 using System; using System.Collections.Concurrent; @@ -438,10 +437,10 @@ namespace Emby.Server.Implementations.Library item.SetParent(null); - ItemRepository.DeleteItem(item.Id, CancellationToken.None); + ItemRepository.DeleteItem(item.Id); foreach (var child in children) { - ItemRepository.DeleteItem(child.Id, CancellationToken.None); + ItemRepository.DeleteItem(child.Id); } _libraryItemsCache.TryRemove(item.Id, out BaseItem removed); @@ -944,7 +943,6 @@ namespace Emby.Server.Implementations.Library IncludeItemTypes = new[] { typeof(T).Name }, Name = name, DtoOptions = options - }).Cast<MusicArtist>() .OrderBy(i => i.IsAccessedByName ? 1 : 0) .Cast<T>() @@ -1080,7 +1078,7 @@ namespace Emby.Server.Implementations.Library var innerProgress = new ActionableProgress<double>(); - innerProgress.RegisterAction(pct => progress.Report(pct * pct * 0.96)); + innerProgress.RegisterAction(pct => progress.Report(pct * 0.96)); // Validate the entire media library await RootFolder.ValidateChildren(innerProgress, cancellationToken, new MetadataRefreshOptions(new DirectoryService(_fileSystem)), recursive: true).ConfigureAwait(false); @@ -1174,7 +1172,6 @@ namespace Emby.Server.Implementations.Library return _fileSystem.GetDirectoryPaths(ConfigurationManager.ApplicationPaths.DefaultUserViewsPath) .Select(dir => GetVirtualFolderInfo(dir, topLibraryFolders, refreshQueue)) - .OrderBy(i => i.Name) .ToList(); } @@ -1406,25 +1403,32 @@ namespace Emby.Server.Implementations.Library private void SetTopParentOrAncestorIds(InternalItemsQuery query) { - if (query.AncestorIds.Length == 0) + var ancestorIds = query.AncestorIds; + int len = ancestorIds.Length; + if (len == 0) { return; } - var parents = query.AncestorIds.Select(i => GetItemById(i)).ToList(); - - if (parents.All(i => i is ICollectionFolder || i is UserView)) + var parents = new BaseItem[len]; + for (int i = 0; i < len; i++) { - // Optimize by querying against top level views - query.TopParentIds = parents.SelectMany(i => GetTopParentIdsForQuery(i, query.User)).ToArray(); - query.AncestorIds = Array.Empty<Guid>(); - - // Prevent searching in all libraries due to empty filter - if (query.TopParentIds.Length == 0) + parents[i] = GetItemById(ancestorIds[i]); + if (!(parents[i] is ICollectionFolder || parents[i] is UserView)) { - query.TopParentIds = new[] { Guid.NewGuid() }; + return; } } + + // Optimize by querying against top level views + query.TopParentIds = parents.SelectMany(i => GetTopParentIdsForQuery(i, query.User)).ToArray(); + query.AncestorIds = Array.Empty<Guid>(); + + // Prevent searching in all libraries due to empty filter + if (query.TopParentIds.Length == 0) + { + query.TopParentIds = new[] { Guid.NewGuid() }; + } } public QueryResult<(BaseItem, ItemCounts)> GetAlbumArtists(InternalItemsQuery query) @@ -1585,7 +1589,7 @@ namespace Emby.Server.Implementations.Library public async Task<IEnumerable<Video>> GetIntros(BaseItem item, User user) { var tasks = IntroProviders - .OrderBy(i => i.GetType().Name.IndexOf("Default", StringComparison.OrdinalIgnoreCase) == -1 ? 0 : 1) + .OrderBy(i => i.GetType().Name.Contains("Default", StringComparison.OrdinalIgnoreCase) ? 1 : 0) .Take(1) .Select(i => GetIntros(i, item, user)); @@ -2363,33 +2367,22 @@ namespace Emby.Server.Implementations.Library new SubtitleResolver(BaseItem.LocalizationManager, _fileSystem).AddExternalSubtitleStreams(streams, videoPath, streams.Count, files); } - public bool IsVideoFile(string path, LibraryOptions libraryOptions) + /// <inheritdoc /> + public bool IsVideoFile(string path) { var resolver = new VideoResolver(GetNamingOptions()); return resolver.IsVideoFile(path); } - public bool IsVideoFile(string path) - { - return IsVideoFile(path, new LibraryOptions()); - } - - public bool IsAudioFile(string path, LibraryOptions libraryOptions) - { - var parser = new AudioFileParser(GetNamingOptions()); - return parser.IsAudioFile(path); - } - + /// <inheritdoc /> public bool IsAudioFile(string path) - { - return IsAudioFile(path, new LibraryOptions()); - } + => AudioFileParser.IsAudioFile(path, GetNamingOptions()); + /// <inheritdoc /> public int? GetSeasonNumberFromPath(string path) - { - return SeasonPathParser.Parse(path, true, true).SeasonNumber; - } + => SeasonPathParser.Parse(path, true, true).SeasonNumber; + /// <inheritdoc /> public bool FillMissingEpisodeNumbersFromPath(Episode episode, bool forceRefresh) { var series = episode.Series; @@ -2616,14 +2609,12 @@ namespace Emby.Server.Implementations.Library }).OrderBy(i => i.Path); } - private static readonly string[] ExtrasSubfolderNames = new[] { "extras", "specials", "shorts", "scenes", "featurettes", "behind the scenes", "deleted scenes", "interviews" }; - public IEnumerable<Video> FindExtras(BaseItem owner, List<FileSystemMetadata> fileSystemChildren, IDirectoryService directoryService) { var namingOptions = GetNamingOptions(); var files = owner.IsInMixedFolder ? new List<FileSystemMetadata>() : fileSystemChildren.Where(i => i.IsDirectory) - .Where(i => ExtrasSubfolderNames.Contains(i.Name ?? string.Empty, StringComparer.OrdinalIgnoreCase)) + .Where(i => BaseItem.AllExtrasTypesFolderNames.Contains(i.Name ?? string.Empty, StringComparer.OrdinalIgnoreCase)) .SelectMany(i => _fileSystem.GetFiles(i.FullName, _videoFileExtensions, false, false)) .ToList(); diff --git a/Emby.Server.Implementations/Library/LiveStreamHelper.cs b/Emby.Server.Implementations/Library/LiveStreamHelper.cs index f28f4a538..ed7d8aa40 100644 --- a/Emby.Server.Implementations/Library/LiveStreamHelper.cs +++ b/Emby.Server.Implementations/Library/LiveStreamHelper.cs @@ -1,5 +1,4 @@ #pragma warning disable CS1591 -#pragma warning disable SA1600 using System; using System.Collections.Generic; diff --git a/Emby.Server.Implementations/Library/MediaSourceManager.cs b/Emby.Server.Implementations/Library/MediaSourceManager.cs index e310065b2..70d5bd9f4 100644 --- a/Emby.Server.Implementations/Library/MediaSourceManager.cs +++ b/Emby.Server.Implementations/Library/MediaSourceManager.cs @@ -1,5 +1,4 @@ #pragma warning disable CS1591 -#pragma warning disable SA1600 using System; using System.Collections.Generic; diff --git a/Emby.Server.Implementations/Library/MediaStreamSelector.cs b/Emby.Server.Implementations/Library/MediaStreamSelector.cs index 1652ad974..6b9f4d052 100644 --- a/Emby.Server.Implementations/Library/MediaStreamSelector.cs +++ b/Emby.Server.Implementations/Library/MediaStreamSelector.cs @@ -1,5 +1,4 @@ #pragma warning disable CS1591 -#pragma warning disable SA1600 using System; using System.Collections.Generic; diff --git a/Emby.Server.Implementations/Library/MusicManager.cs b/Emby.Server.Implementations/Library/MusicManager.cs index 29af6670b..1ec578371 100644 --- a/Emby.Server.Implementations/Library/MusicManager.cs +++ b/Emby.Server.Implementations/Library/MusicManager.cs @@ -1,5 +1,4 @@ #pragma warning disable CS1591 -#pragma warning disable SA1600 using System; using System.Collections.Generic; diff --git a/Emby.Server.Implementations/Library/ResolverHelper.cs b/Emby.Server.Implementations/Library/ResolverHelper.cs index 96d1bff92..34dcbbe28 100644 --- a/Emby.Server.Implementations/Library/ResolverHelper.cs +++ b/Emby.Server.Implementations/Library/ResolverHelper.cs @@ -9,7 +9,7 @@ using MediaBrowser.Model.IO; namespace Emby.Server.Implementations.Library { /// <summary> - /// Class ResolverHelper + /// Class ResolverHelper. /// </summary> public static class ResolverHelper { diff --git a/Emby.Server.Implementations/Library/Resolvers/Audio/AudioResolver.cs b/Emby.Server.Implementations/Library/Resolvers/Audio/AudioResolver.cs index 7e3b27a12..fefc8e789 100644 --- a/Emby.Server.Implementations/Library/Resolvers/Audio/AudioResolver.cs +++ b/Emby.Server.Implementations/Library/Resolvers/Audio/AudioResolver.cs @@ -1,5 +1,4 @@ #pragma warning disable CS1591 -#pragma warning disable SA1600 using System; using System.Collections.Generic; @@ -73,7 +72,6 @@ namespace Emby.Server.Implementations.Library.Resolvers.Audio { // Return audio if the path is a file and has a matching extension - var libraryOptions = args.GetLibraryOptions(); var collectionType = args.GetCollectionType(); var isBooksCollectionType = string.Equals(collectionType, CollectionType.Books, StringComparison.OrdinalIgnoreCase); @@ -92,7 +90,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.Audio return FindAudio<AudioBook>(args, args.Path, args.Parent, files, args.DirectoryService, collectionType, false); } - if (LibraryManager.IsAudioFile(args.Path, libraryOptions)) + if (LibraryManager.IsAudioFile(args.Path)) { var extension = Path.GetExtension(args.Path); @@ -105,7 +103,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.Audio var isMixedCollectionType = string.IsNullOrEmpty(collectionType); // For conflicting extensions, give priority to videos - if (isMixedCollectionType && LibraryManager.IsVideoFile(args.Path, libraryOptions)) + if (isMixedCollectionType && LibraryManager.IsVideoFile(args.Path)) { return null; } @@ -121,7 +119,6 @@ namespace Emby.Server.Implementations.Library.Resolvers.Audio { item = new MediaBrowser.Controller.Entities.Audio.Audio(); } - else if (isBooksCollectionType) { item = new AudioBook(); diff --git a/Emby.Server.Implementations/Library/Resolvers/Audio/MusicAlbumResolver.cs b/Emby.Server.Implementations/Library/Resolvers/Audio/MusicAlbumResolver.cs index 9f858f98d..85b1b6e32 100644 --- a/Emby.Server.Implementations/Library/Resolvers/Audio/MusicAlbumResolver.cs +++ b/Emby.Server.Implementations/Library/Resolvers/Audio/MusicAlbumResolver.cs @@ -5,7 +5,6 @@ using MediaBrowser.Controller.Entities.Audio; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Providers; using MediaBrowser.Controller.Resolvers; -using MediaBrowser.Model.Configuration; using MediaBrowser.Model.Entities; using MediaBrowser.Model.IO; using Microsoft.Extensions.Logging; @@ -78,9 +77,9 @@ namespace Emby.Server.Implementations.Library.Resolvers.Audio /// <summary> /// Determine if the supplied file data points to a music album. /// </summary> - public bool IsMusicAlbum(string path, IDirectoryService directoryService, LibraryOptions libraryOptions) + public bool IsMusicAlbum(string path, IDirectoryService directoryService) { - return ContainsMusic(directoryService.GetFileSystemEntries(path), true, directoryService, _logger, _fileSystem, libraryOptions, _libraryManager); + return ContainsMusic(directoryService.GetFileSystemEntries(path), true, directoryService, _logger, _fileSystem, _libraryManager); } /// <summary> @@ -94,7 +93,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.Audio if (args.IsDirectory) { // if (args.Parent is MusicArtist) return true; //saves us from testing children twice - if (ContainsMusic(args.FileSystemChildren, true, args.DirectoryService, _logger, _fileSystem, args.GetLibraryOptions(), _libraryManager)) + if (ContainsMusic(args.FileSystemChildren, true, args.DirectoryService, _logger, _fileSystem, _libraryManager)) { return true; } @@ -112,7 +111,6 @@ namespace Emby.Server.Implementations.Library.Resolvers.Audio IDirectoryService directoryService, ILogger logger, IFileSystem fileSystem, - LibraryOptions libraryOptions, ILibraryManager libraryManager) { var discSubfolderCount = 0; @@ -132,7 +130,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.Audio } var path = fileSystemInfo.FullName; - var hasMusic = ContainsMusic(directoryService.GetFileSystemEntries(path), false, directoryService, logger, fileSystem, libraryOptions, libraryManager); + var hasMusic = ContainsMusic(directoryService.GetFileSystemEntries(path), false, directoryService, logger, fileSystem, libraryManager); if (hasMusic) { @@ -153,7 +151,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.Audio { var fullName = fileSystemInfo.FullName; - if (libraryManager.IsAudioFile(fullName, libraryOptions)) + if (libraryManager.IsAudioFile(fullName)) { return true; } diff --git a/Emby.Server.Implementations/Library/Resolvers/Audio/MusicArtistResolver.cs b/Emby.Server.Implementations/Library/Resolvers/Audio/MusicArtistResolver.cs index ee7e84929..681db4896 100644 --- a/Emby.Server.Implementations/Library/Resolvers/Audio/MusicArtistResolver.cs +++ b/Emby.Server.Implementations/Library/Resolvers/Audio/MusicArtistResolver.cs @@ -27,7 +27,11 @@ namespace Emby.Server.Implementations.Library.Resolvers.Audio /// <param name="fileSystem">The file system.</param> /// <param name="libraryManager">The library manager.</param> /// <param name="config">The configuration manager.</param> - public MusicArtistResolver(ILogger logger, IFileSystem fileSystem, ILibraryManager libraryManager, IServerConfigurationManager config) + public MusicArtistResolver( + ILogger<MusicArtistResolver> logger, + IFileSystem fileSystem, + ILibraryManager libraryManager, + IServerConfigurationManager config) { _logger = logger; _fileSystem = fileSystem; @@ -80,14 +84,17 @@ namespace Emby.Server.Implementations.Library.Resolvers.Audio } // Avoid mis-identifying top folders - if (args.Parent.IsRoot) return null; + if (args.Parent.IsRoot) + { + return null; + } var directoryService = args.DirectoryService; var albumResolver = new MusicAlbumResolver(_logger, _fileSystem, _libraryManager); // If we contain an album assume we are an artist folder - return args.FileSystemChildren.Where(i => i.IsDirectory).Any(i => albumResolver.IsMusicAlbum(i.FullName, directoryService, args.GetLibraryOptions())) ? new MusicArtist() : null; + return args.FileSystemChildren.Where(i => i.IsDirectory).Any(i => albumResolver.IsMusicAlbum(i.FullName, directoryService)) ? new MusicArtist() : null; } } } diff --git a/Emby.Server.Implementations/Library/Resolvers/BaseVideoResolver.cs b/Emby.Server.Implementations/Library/Resolvers/BaseVideoResolver.cs index 43302bb3f..fb75593bd 100644 --- a/Emby.Server.Implementations/Library/Resolvers/BaseVideoResolver.cs +++ b/Emby.Server.Implementations/Library/Resolvers/BaseVideoResolver.cs @@ -1,5 +1,4 @@ #pragma warning disable CS1591 -#pragma warning disable SA1600 using System; using System.IO; @@ -80,6 +79,7 @@ namespace Emby.Server.Implementations.Library.Resolvers }; break; } + if (IsBluRayDirectory(child.FullName, filename, args.DirectoryService)) { videoInfo = parser.ResolveDirectory(args.Path); @@ -137,7 +137,7 @@ namespace Emby.Server.Implementations.Library.Resolvers return null; } - if (LibraryManager.IsVideoFile(args.Path, args.GetLibraryOptions()) || videoInfo.IsStub) + if (LibraryManager.IsVideoFile(args.Path) || videoInfo.IsStub) { var path = args.Path; diff --git a/Emby.Server.Implementations/Library/Resolvers/Books/BookResolver.cs b/Emby.Server.Implementations/Library/Resolvers/Books/BookResolver.cs index 1e2e0704c..0b93ebeb8 100644 --- a/Emby.Server.Implementations/Library/Resolvers/Books/BookResolver.cs +++ b/Emby.Server.Implementations/Library/Resolvers/Books/BookResolver.cs @@ -1,5 +1,4 @@ #pragma warning disable CS1591 -#pragma warning disable SA1600 using System; using System.IO; diff --git a/Emby.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs b/Emby.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs index 08db168bc..cb67c8aa7 100644 --- a/Emby.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs +++ b/Emby.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs @@ -436,7 +436,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.Movies if (result.Items.Count == 1) { var videoPath = result.Items[0].Path; - var hasPhotos = photos.Any(i => !PhotoResolver.IsOwnedByResolvedMedia(LibraryManager, libraryOptions, videoPath, i.Name)); + var hasPhotos = photos.Any(i => !PhotoResolver.IsOwnedByResolvedMedia(LibraryManager, videoPath, i.Name)); if (!hasPhotos) { @@ -446,8 +446,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.Movies return movie; } } - - if (result.Items.Count == 0 && multiDiscFolders.Count > 0) + else if (result.Items.Count == 0 && multiDiscFolders.Count > 0) { return GetMultiDiscMovie<T>(multiDiscFolders, directoryService); } @@ -519,14 +518,15 @@ namespace Emby.Server.Implementations.Library.Resolvers.Movies return null; } + int additionalPartsLen = folderPaths.Count - 1; + var additionalParts = new string[additionalPartsLen]; + folderPaths.CopyTo(1, additionalParts, 0, additionalPartsLen); + var returnVideo = new T { Path = folderPaths[0], - - AdditionalParts = folderPaths.Skip(1).ToArray(), - + AdditionalParts = additionalParts, VideoType = videoTypes[0], - Name = result[0].Name }; diff --git a/Emby.Server.Implementations/Library/Resolvers/PhotoAlbumResolver.cs b/Emby.Server.Implementations/Library/Resolvers/PhotoAlbumResolver.cs index 4536b0aaa..3ac837057 100644 --- a/Emby.Server.Implementations/Library/Resolvers/PhotoAlbumResolver.cs +++ b/Emby.Server.Implementations/Library/Resolvers/PhotoAlbumResolver.cs @@ -63,13 +63,12 @@ namespace Emby.Server.Implementations.Library.Resolvers { if (!file.IsDirectory && PhotoResolver.IsImageFile(file.FullName, _imageProcessor)) { - var libraryOptions = args.GetLibraryOptions(); var filename = file.Name; var ownedByMedia = false; foreach (var siblingFile in files) { - if (PhotoResolver.IsOwnedByMedia(_libraryManager, libraryOptions, siblingFile.FullName, filename)) + if (PhotoResolver.IsOwnedByMedia(_libraryManager, siblingFile.FullName, filename)) { ownedByMedia = true; break; diff --git a/Emby.Server.Implementations/Library/Resolvers/PhotoResolver.cs b/Emby.Server.Implementations/Library/Resolvers/PhotoResolver.cs index e1eb23652..bcfcee9c6 100644 --- a/Emby.Server.Implementations/Library/Resolvers/PhotoResolver.cs +++ b/Emby.Server.Implementations/Library/Resolvers/PhotoResolver.cs @@ -1,5 +1,4 @@ #pragma warning disable CS1591 -#pragma warning disable SA1600 using System; using System.Collections.Generic; @@ -8,7 +7,6 @@ using System.Linq; using MediaBrowser.Controller.Drawing; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Library; -using MediaBrowser.Model.Configuration; using MediaBrowser.Model.Entities; namespace Emby.Server.Implementations.Library.Resolvers @@ -57,11 +55,10 @@ namespace Emby.Server.Implementations.Library.Resolvers // Make sure the image doesn't belong to a video file var files = args.DirectoryService.GetFiles(Path.GetDirectoryName(args.Path)); - var libraryOptions = args.GetLibraryOptions(); foreach (var file in files) { - if (IsOwnedByMedia(_libraryManager, libraryOptions, file.FullName, filename)) + if (IsOwnedByMedia(_libraryManager, file.FullName, filename)) { return null; } @@ -78,17 +75,17 @@ namespace Emby.Server.Implementations.Library.Resolvers return null; } - internal static bool IsOwnedByMedia(ILibraryManager libraryManager, LibraryOptions libraryOptions, string file, string imageFilename) + internal static bool IsOwnedByMedia(ILibraryManager libraryManager, string file, string imageFilename) { - if (libraryManager.IsVideoFile(file, libraryOptions)) + if (libraryManager.IsVideoFile(file)) { - return IsOwnedByResolvedMedia(libraryManager, libraryOptions, file, imageFilename); + return IsOwnedByResolvedMedia(libraryManager, file, imageFilename); } return false; } - internal static bool IsOwnedByResolvedMedia(ILibraryManager libraryManager, LibraryOptions libraryOptions, string file, string imageFilename) + internal static bool IsOwnedByResolvedMedia(ILibraryManager libraryManager, string file, string imageFilename) => imageFilename.StartsWith(Path.GetFileNameWithoutExtension(file), StringComparison.OrdinalIgnoreCase); internal static bool IsImageFile(string path, IImageProcessor imageProcessor) diff --git a/Emby.Server.Implementations/Library/Resolvers/PlaylistResolver.cs b/Emby.Server.Implementations/Library/Resolvers/PlaylistResolver.cs index 5e672f221..41561916f 100644 --- a/Emby.Server.Implementations/Library/Resolvers/PlaylistResolver.cs +++ b/Emby.Server.Implementations/Library/Resolvers/PlaylistResolver.cs @@ -1,65 +1,70 @@ #pragma warning disable CS1591 -#pragma warning disable SA1600 using System; using System.IO; using System.Linq; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Playlists; +using MediaBrowser.Controller.Resolvers; +using MediaBrowser.LocalMetadata.Savers; using MediaBrowser.Model.Entities; namespace Emby.Server.Implementations.Library.Resolvers { + /// <summary> + /// <see cref="IItemResolver"/> for <see cref="Playlist"/> library items. + /// </summary> public class PlaylistResolver : FolderResolver<Playlist> { - private string[] SupportedCollectionTypes = new string[] { - + private string[] _musicPlaylistCollectionTypes = new string[] { string.Empty, CollectionType.Music }; - /// <summary> - /// Resolves the specified args. - /// </summary> - /// <param name="args">The args.</param> - /// <returns>BoxSet.</returns> + /// <inheritdoc/> protected override Playlist Resolve(ItemResolveArgs args) { - // It's a boxset if all of the following conditions are met: - // Is a Directory - // Contains [playlist] in the path if (args.IsDirectory) { - var filename = Path.GetFileName(args.Path); - - if (string.IsNullOrEmpty(filename)) + // It's a boxset if the path is a directory with [playlist] in it's the name + // TODO: Should this use Path.GetDirectoryName() instead? + bool isBoxSet = Path.GetFileName(args.Path) + ?.Contains("[playlist]", StringComparison.OrdinalIgnoreCase) + ?? false; + if (isBoxSet) { - return null; + return new Playlist + { + Path = args.Path, + Name = Path.GetFileName(args.Path).Replace("[playlist]", string.Empty, StringComparison.OrdinalIgnoreCase).Trim() + }; } - if (filename.IndexOf("[playlist]", StringComparison.OrdinalIgnoreCase) != -1) + // It's a directory-based playlist if the directory contains a playlist file + var filePaths = Directory.EnumerateFiles(args.Path); + if (filePaths.Any(f => f.EndsWith(PlaylistXmlSaver.DefaultPlaylistFilename, StringComparison.OrdinalIgnoreCase))) { return new Playlist { Path = args.Path, - Name = Path.GetFileName(args.Path).Replace("[playlist]", string.Empty, StringComparison.OrdinalIgnoreCase).Trim() + Name = Path.GetFileName(args.Path) }; } } - else + + // Check if this is a music playlist file + // It should have the correct collection type and a supported file extension + else if (_musicPlaylistCollectionTypes.Contains(args.CollectionType ?? string.Empty, StringComparer.OrdinalIgnoreCase)) { - if (SupportedCollectionTypes.Contains(args.CollectionType ?? string.Empty, StringComparer.OrdinalIgnoreCase)) + var extension = Path.GetExtension(args.Path); + if (Playlist.SupportedExtensions.Contains(extension ?? string.Empty, StringComparer.OrdinalIgnoreCase)) { - var extension = Path.GetExtension(args.Path); - if (Playlist.SupportedExtensions.Contains(extension ?? string.Empty, StringComparer.OrdinalIgnoreCase)) + return new Playlist { - return new Playlist - { - Path = args.Path, - Name = Path.GetFileNameWithoutExtension(args.Path), - IsInMixedFolder = true - }; - } + Path = args.Path, + Name = Path.GetFileNameWithoutExtension(args.Path), + IsInMixedFolder = true + }; } } diff --git a/Emby.Server.Implementations/Library/Resolvers/SpecialFolderResolver.cs b/Emby.Server.Implementations/Library/Resolvers/SpecialFolderResolver.cs index eca60b133..1030ed39d 100644 --- a/Emby.Server.Implementations/Library/Resolvers/SpecialFolderResolver.cs +++ b/Emby.Server.Implementations/Library/Resolvers/SpecialFolderResolver.cs @@ -1,5 +1,4 @@ #pragma warning disable CS1591 -#pragma warning disable SA1600 using System; using System.IO; diff --git a/Emby.Server.Implementations/Library/Resolvers/TV/SeasonResolver.cs b/Emby.Server.Implementations/Library/Resolvers/TV/SeasonResolver.cs index 3e88c0287..18145b7f1 100644 --- a/Emby.Server.Implementations/Library/Resolvers/TV/SeasonResolver.cs +++ b/Emby.Server.Implementations/Library/Resolvers/TV/SeasonResolver.cs @@ -25,7 +25,11 @@ namespace Emby.Server.Implementations.Library.Resolvers.TV /// <param name="libraryManager">The library manager.</param> /// <param name="localization">The localization</param> /// <param name="logger">The logger</param> - public SeasonResolver(IServerConfigurationManager config, ILibraryManager libraryManager, ILocalizationManager localization, ILogger logger) + public SeasonResolver( + IServerConfigurationManager config, + ILibraryManager libraryManager, + ILocalizationManager localization, + ILogger<SeasonResolver> logger) { _config = config; _libraryManager = libraryManager; diff --git a/Emby.Server.Implementations/Library/Resolvers/TV/SeriesResolver.cs b/Emby.Server.Implementations/Library/Resolvers/TV/SeriesResolver.cs index 4ee30b475..dd6bd8ee8 100644 --- a/Emby.Server.Implementations/Library/Resolvers/TV/SeriesResolver.cs +++ b/Emby.Server.Implementations/Library/Resolvers/TV/SeriesResolver.cs @@ -1,5 +1,4 @@ #pragma warning disable CS1591 -#pragma warning disable SA1600 using System; using System.Collections.Generic; @@ -9,7 +8,6 @@ using MediaBrowser.Controller.Entities.TV; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Providers; using MediaBrowser.Controller.Resolvers; -using MediaBrowser.Model.Configuration; using MediaBrowser.Model.Entities; using MediaBrowser.Model.IO; using Microsoft.Extensions.Logging; @@ -31,7 +29,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.TV /// <param name="fileSystem">The file system.</param> /// <param name="logger">The logger.</param> /// <param name="libraryManager">The library manager.</param> - public SeriesResolver(IFileSystem fileSystem, ILogger logger, ILibraryManager libraryManager) + public SeriesResolver(IFileSystem fileSystem, ILogger<SeriesResolver> logger, ILibraryManager libraryManager) { _fileSystem = fileSystem; _logger = logger; @@ -102,7 +100,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.TV return null; } - if (IsSeriesFolder(args.Path, args.FileSystemChildren, args.DirectoryService, _fileSystem, _logger, _libraryManager, args.GetLibraryOptions(), false)) + if (IsSeriesFolder(args.Path, args.FileSystemChildren, args.DirectoryService, _fileSystem, _logger, _libraryManager, false)) { return new Series { @@ -123,24 +121,10 @@ namespace Emby.Server.Implementations.Library.Resolvers.TV IFileSystem fileSystem, ILogger logger, ILibraryManager libraryManager, - LibraryOptions libraryOptions, bool isTvContentType) { foreach (var child in fileSystemChildren) { - //if ((attributes & FileAttributes.Hidden) == FileAttributes.Hidden) - //{ - // //logger.LogDebug("Igoring series file or folder marked hidden: {0}", child.FullName); - // continue; - //} - - // Can't enforce this because files saved by Bitcasa are always marked System - //if ((attributes & FileAttributes.System) == FileAttributes.System) - //{ - // logger.LogDebug("Igoring series subfolder marked system: {0}", child.FullName); - // continue; - //} - if (child.IsDirectory) { if (IsSeasonFolder(child.FullName, isTvContentType, libraryManager)) @@ -152,7 +136,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.TV else { string fullName = child.FullName; - if (libraryManager.IsVideoFile(fullName, libraryOptions)) + if (libraryManager.IsVideoFile(fullName)) { if (isTvContentType) { diff --git a/Emby.Server.Implementations/Library/Resolvers/VideoResolver.cs b/Emby.Server.Implementations/Library/Resolvers/VideoResolver.cs index 6404d6476..62268fce9 100644 --- a/Emby.Server.Implementations/Library/Resolvers/VideoResolver.cs +++ b/Emby.Server.Implementations/Library/Resolvers/VideoResolver.cs @@ -1,5 +1,4 @@ #pragma warning disable CS1591 -#pragma warning disable SA1600 using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Library; diff --git a/Emby.Server.Implementations/Library/SearchEngine.cs b/Emby.Server.Implementations/Library/SearchEngine.cs index 76ae14720..11d6c737a 100644 --- a/Emby.Server.Implementations/Library/SearchEngine.cs +++ b/Emby.Server.Implementations/Library/SearchEngine.cs @@ -1,5 +1,4 @@ #pragma warning disable CS1591 -#pragma warning disable SA1600 using System; using System.Collections.Generic; diff --git a/Emby.Server.Implementations/Library/UserDataManager.cs b/Emby.Server.Implementations/Library/UserDataManager.cs index f1fb35d9a..071681b08 100644 --- a/Emby.Server.Implementations/Library/UserDataManager.cs +++ b/Emby.Server.Implementations/Library/UserDataManager.cs @@ -1,5 +1,4 @@ #pragma warning disable CS1591 -#pragma warning disable SA1600 using System; using System.Collections.Concurrent; diff --git a/Emby.Server.Implementations/Library/UserManager.cs b/Emby.Server.Implementations/Library/UserManager.cs index 6e203f894..7b17cc913 100644 --- a/Emby.Server.Implementations/Library/UserManager.cs +++ b/Emby.Server.Implementations/Library/UserManager.cs @@ -1,5 +1,4 @@ #pragma warning disable CS1591 -#pragma warning disable SA1600 using System; using System.Collections.Concurrent; @@ -806,17 +805,17 @@ namespace Emby.Server.Implementations.Library // Delete user config dir lock (_configSyncLock) - lock (_policySyncLock) - { - try - { - Directory.Delete(user.ConfigurationDirectoryPath, true); - } - catch (IOException ex) + lock (_policySyncLock) { - _logger.LogError(ex, "Error deleting user config dir: {Path}", user.ConfigurationDirectoryPath); + try + { + Directory.Delete(user.ConfigurationDirectoryPath, true); + } + catch (IOException ex) + { + _logger.LogError(ex, "Error deleting user config dir: {Path}", user.ConfigurationDirectoryPath); + } } - } _users.TryRemove(user.Id, out _); diff --git a/Emby.Server.Implementations/Library/UserViewManager.cs b/Emby.Server.Implementations/Library/UserViewManager.cs index 935deb71c..322819b05 100644 --- a/Emby.Server.Implementations/Library/UserViewManager.cs +++ b/Emby.Server.Implementations/Library/UserViewManager.cs @@ -1,5 +1,4 @@ #pragma warning disable CS1591 -#pragma warning disable SA1600 using System; using System.Collections.Generic; diff --git a/Emby.Server.Implementations/Library/Validators/ArtistsPostScanTask.cs b/Emby.Server.Implementations/Library/Validators/ArtistsPostScanTask.cs index 61a07d0d6..2af8ff5cb 100644 --- a/Emby.Server.Implementations/Library/Validators/ArtistsPostScanTask.cs +++ b/Emby.Server.Implementations/Library/Validators/ArtistsPostScanTask.cs @@ -25,7 +25,10 @@ namespace Emby.Server.Implementations.Library.Validators /// <param name="libraryManager">The library manager.</param> /// <param name="logger">The logger.</param> /// <param name="itemRepo">The item repository.</param> - public ArtistsPostScanTask(ILibraryManager libraryManager, ILogger logger, IItemRepository itemRepo) + public ArtistsPostScanTask( + ILibraryManager libraryManager, + ILogger<ArtistsPostScanTask> logger, + IItemRepository itemRepo) { _libraryManager = libraryManager; _logger = logger; diff --git a/Emby.Server.Implementations/Library/Validators/GenresPostScanTask.cs b/Emby.Server.Implementations/Library/Validators/GenresPostScanTask.cs index 06d1dd89d..251785dfd 100644 --- a/Emby.Server.Implementations/Library/Validators/GenresPostScanTask.cs +++ b/Emby.Server.Implementations/Library/Validators/GenresPostScanTask.cs @@ -25,7 +25,10 @@ namespace Emby.Server.Implementations.Library.Validators /// <param name="libraryManager">The library manager.</param> /// <param name="logger">The logger.</param> /// <param name="itemRepo">The item repository.</param> - public GenresPostScanTask(ILibraryManager libraryManager, ILogger logger, IItemRepository itemRepo) + public GenresPostScanTask( + ILibraryManager libraryManager, + ILogger<GenresPostScanTask> logger, + IItemRepository itemRepo) { _libraryManager = libraryManager; _logger = logger; diff --git a/Emby.Server.Implementations/Library/Validators/MusicGenresPostScanTask.cs b/Emby.Server.Implementations/Library/Validators/MusicGenresPostScanTask.cs index 58549e9d7..9d8690116 100644 --- a/Emby.Server.Implementations/Library/Validators/MusicGenresPostScanTask.cs +++ b/Emby.Server.Implementations/Library/Validators/MusicGenresPostScanTask.cs @@ -25,7 +25,10 @@ namespace Emby.Server.Implementations.Library.Validators /// <param name="libraryManager">The library manager.</param> /// <param name="logger">The logger.</param> /// <param name="itemRepo">The item repository.</param> - public MusicGenresPostScanTask(ILibraryManager libraryManager, ILogger logger, IItemRepository itemRepo) + public MusicGenresPostScanTask( + ILibraryManager libraryManager, + ILogger<MusicGenresPostScanTask> logger, + IItemRepository itemRepo) { _libraryManager = libraryManager; _logger = logger; diff --git a/Emby.Server.Implementations/Library/Validators/StudiosPostScanTask.cs b/Emby.Server.Implementations/Library/Validators/StudiosPostScanTask.cs index 00899c336..2f8f906b9 100644 --- a/Emby.Server.Implementations/Library/Validators/StudiosPostScanTask.cs +++ b/Emby.Server.Implementations/Library/Validators/StudiosPostScanTask.cs @@ -26,7 +26,10 @@ namespace Emby.Server.Implementations.Library.Validators /// <param name="libraryManager">The library manager.</param> /// <param name="logger">The logger.</param> /// <param name="itemRepo">The item repository.</param> - public StudiosPostScanTask(ILibraryManager libraryManager, ILogger logger, IItemRepository itemRepo) + public StudiosPostScanTask( + ILibraryManager libraryManager, + ILogger<StudiosPostScanTask> logger, + IItemRepository itemRepo) { _libraryManager = libraryManager; _logger = logger; diff --git a/Emby.Server.Implementations/LiveTv/EmbyTV/DirectRecorder.cs b/Emby.Server.Implementations/LiveTv/EmbyTV/DirectRecorder.cs index 161cf6051..2e13a3bb3 100644 --- a/Emby.Server.Implementations/LiveTv/EmbyTV/DirectRecorder.cs +++ b/Emby.Server.Implementations/LiveTv/EmbyTV/DirectRecorder.cs @@ -1,3 +1,5 @@ +#pragma warning disable CS1591 + using System; using System.IO; using System.Net.Http; @@ -70,7 +72,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV UserAgent = "Emby/3.0", // Shouldn't matter but may cause issues - DecompressionMethod = CompressionMethod.None + DecompressionMethod = CompressionMethods.None }; using (var response = await _httpClient.SendAsync(httpRequestOptions, HttpMethod.Get).ConfigureAwait(false)) diff --git a/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs b/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs index 4ac48e537..139aa19a4 100644 --- a/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs +++ b/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs @@ -1,4 +1,3 @@ -#pragma warning disable SA1600 #pragma warning disable CS1591 using System; @@ -30,7 +29,6 @@ using MediaBrowser.Model.Diagnostics; using MediaBrowser.Model.Dto; using MediaBrowser.Model.Entities; using MediaBrowser.Model.Events; -using MediaBrowser.Model.Extensions; using MediaBrowser.Model.IO; using MediaBrowser.Model.LiveTv; using MediaBrowser.Model.MediaInfo; @@ -81,7 +79,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV IServerApplicationHost appHost, IStreamHelper streamHelper, IMediaSourceManager mediaSourceManager, - ILogger logger, + ILogger<EmbyTV> logger, IJsonSerializer jsonSerializer, IHttpClient httpClient, IServerConfigurationManager config, diff --git a/Emby.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs b/Emby.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs index 6e4ac2fec..d24fc6792 100644 --- a/Emby.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs +++ b/Emby.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs @@ -1,3 +1,5 @@ +#pragma warning disable CS1591 + using System; using System.Collections.Generic; using System.Globalization; diff --git a/Emby.Server.Implementations/LiveTv/EmbyTV/EntryPoint.cs b/Emby.Server.Implementations/LiveTv/EmbyTV/EntryPoint.cs index 9c9ba09f5..69a9cb78a 100644 --- a/Emby.Server.Implementations/LiveTv/EmbyTV/EntryPoint.cs +++ b/Emby.Server.Implementations/LiveTv/EmbyTV/EntryPoint.cs @@ -1,3 +1,5 @@ +#pragma warning disable CS1591 + using System.Threading.Tasks; using MediaBrowser.Controller.Plugins; @@ -5,11 +7,13 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV { public class EntryPoint : IServerEntryPoint { + /// <inheritdoc /> public Task RunAsync() { return EmbyTV.Current.Start(); } + /// <inheritdoc /> public void Dispose() { } diff --git a/Emby.Server.Implementations/LiveTv/EmbyTV/EpgChannelData.cs b/Emby.Server.Implementations/LiveTv/EmbyTV/EpgChannelData.cs index 498aa3c26..463d0ed0a 100644 --- a/Emby.Server.Implementations/LiveTv/EmbyTV/EpgChannelData.cs +++ b/Emby.Server.Implementations/LiveTv/EmbyTV/EpgChannelData.cs @@ -1,4 +1,3 @@ -#pragma warning disable SA1600 #pragma warning disable CS1591 using System; diff --git a/Emby.Server.Implementations/LiveTv/EmbyTV/IRecorder.cs b/Emby.Server.Implementations/LiveTv/EmbyTV/IRecorder.cs index 6eced3050..4712724d6 100644 --- a/Emby.Server.Implementations/LiveTv/EmbyTV/IRecorder.cs +++ b/Emby.Server.Implementations/LiveTv/EmbyTV/IRecorder.cs @@ -1,3 +1,5 @@ +#pragma warning disable CS1591 + using System; using System.Threading; using System.Threading.Tasks; diff --git a/Emby.Server.Implementations/LiveTv/EmbyTV/ItemDataProvider.cs b/Emby.Server.Implementations/LiveTv/EmbyTV/ItemDataProvider.cs index 9055a70a6..fc543dc55 100644 --- a/Emby.Server.Implementations/LiveTv/EmbyTV/ItemDataProvider.cs +++ b/Emby.Server.Implementations/LiveTv/EmbyTV/ItemDataProvider.cs @@ -1,3 +1,5 @@ +#pragma warning disable CS1591 + using System; using System.Collections.Generic; using System.IO; diff --git a/Emby.Server.Implementations/LiveTv/EmbyTV/RecordingHelper.cs b/Emby.Server.Implementations/LiveTv/EmbyTV/RecordingHelper.cs index ded3c7607..0b0ff6cb3 100644 --- a/Emby.Server.Implementations/LiveTv/EmbyTV/RecordingHelper.cs +++ b/Emby.Server.Implementations/LiveTv/EmbyTV/RecordingHelper.cs @@ -1,3 +1,5 @@ +#pragma warning disable CS1591 + using System; using System.Globalization; using MediaBrowser.Controller.LiveTv; @@ -21,7 +23,11 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV if (info.SeasonNumber.HasValue && info.EpisodeNumber.HasValue) { - name += string.Format(" S{0}E{1}", info.SeasonNumber.Value.ToString("00", CultureInfo.InvariantCulture), info.EpisodeNumber.Value.ToString("00", CultureInfo.InvariantCulture)); + name += string.Format( + CultureInfo.InvariantCulture, + " S{0}E{1}", + info.SeasonNumber.Value.ToString("00", CultureInfo.InvariantCulture), + info.EpisodeNumber.Value.ToString("00", CultureInfo.InvariantCulture)); addHyphen = false; } else if (info.OriginalAirDate.HasValue) @@ -32,7 +38,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV } else { - name += " " + info.OriginalAirDate.Value.ToLocalTime().ToString("yyyy-MM-dd"); + name += " " + info.OriginalAirDate.Value.ToLocalTime().ToString("yyyy-MM-dd", CultureInfo.InvariantCulture); } } else @@ -67,14 +73,15 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV { date = date.ToLocalTime(); - return string.Format("{0}_{1}_{2}_{3}_{4}_{5}", + return string.Format( + CultureInfo.InvariantCulture, + "{0}_{1}_{2}_{3}_{4}_{5}", date.Year.ToString("0000", CultureInfo.InvariantCulture), date.Month.ToString("00", CultureInfo.InvariantCulture), date.Day.ToString("00", CultureInfo.InvariantCulture), date.Hour.ToString("00", CultureInfo.InvariantCulture), date.Minute.ToString("00", CultureInfo.InvariantCulture), - date.Second.ToString("00", CultureInfo.InvariantCulture) - ); + date.Second.ToString("00", CultureInfo.InvariantCulture)); } } } diff --git a/Emby.Server.Implementations/LiveTv/EmbyTV/SeriesTimerManager.cs b/Emby.Server.Implementations/LiveTv/EmbyTV/SeriesTimerManager.cs index 520b44404..194e4606d 100644 --- a/Emby.Server.Implementations/LiveTv/EmbyTV/SeriesTimerManager.cs +++ b/Emby.Server.Implementations/LiveTv/EmbyTV/SeriesTimerManager.cs @@ -1,3 +1,5 @@ +#pragma warning disable CS1591 + using System; using MediaBrowser.Controller.LiveTv; using MediaBrowser.Model.Serialization; @@ -12,6 +14,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV { } + /// <inheritdoc /> public override void Add(SeriesTimerInfo item) { if (string.IsNullOrEmpty(item.Id)) diff --git a/Emby.Server.Implementations/LiveTv/EmbyTV/TimerManager.cs b/Emby.Server.Implementations/LiveTv/EmbyTV/TimerManager.cs index d09b281d4..7ebb043d8 100644 --- a/Emby.Server.Implementations/LiveTv/EmbyTV/TimerManager.cs +++ b/Emby.Server.Implementations/LiveTv/EmbyTV/TimerManager.cs @@ -1,3 +1,5 @@ +#pragma warning disable CS1591 + using System; using System.Collections.Concurrent; using System.Globalization; diff --git a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs b/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs index 1dd794da0..89b81fd96 100644 --- a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs +++ b/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs @@ -1,3 +1,5 @@ +#pragma warning disable CS1591 + using System; using System.Collections.Concurrent; using System.Collections.Generic; @@ -30,7 +32,11 @@ namespace Emby.Server.Implementations.LiveTv.Listings private const string ApiUrl = "https://json.schedulesdirect.org/20141201"; - public SchedulesDirect(ILogger logger, IJsonSerializer jsonSerializer, IHttpClient httpClient, IApplicationHost appHost) + public SchedulesDirect( + ILogger<SchedulesDirect> logger, + IJsonSerializer jsonSerializer, + IHttpClient httpClient, + IApplicationHost appHost) { _logger = logger; _jsonSerializer = jsonSerializer; @@ -629,7 +635,7 @@ namespace Emby.Server.Implementations.LiveTv.Listings ListingsProviderInfo providerInfo) { // Schedules direct requires that the client support compression and will return a 400 response without it - options.DecompressionMethod = CompressionMethod.Deflate; + options.DecompressionMethod = CompressionMethods.Deflate; try { @@ -659,7 +665,7 @@ namespace Emby.Server.Implementations.LiveTv.Listings ListingsProviderInfo providerInfo) { // Schedules direct requires that the client support compression and will return a 400 response without it - options.DecompressionMethod = CompressionMethod.Deflate; + options.DecompressionMethod = CompressionMethods.Deflate; try { diff --git a/Emby.Server.Implementations/LiveTv/Listings/XmlTvListingsProvider.cs b/Emby.Server.Implementations/LiveTv/Listings/XmlTvListingsProvider.cs index 1f38de2d8..07f8539c5 100644 --- a/Emby.Server.Implementations/LiveTv/Listings/XmlTvListingsProvider.cs +++ b/Emby.Server.Implementations/LiveTv/Listings/XmlTvListingsProvider.cs @@ -1,3 +1,5 @@ +#pragma warning disable CS1591 + using System; using System.Collections.Generic; using System.Globalization; @@ -31,7 +33,7 @@ namespace Emby.Server.Implementations.LiveTv.Listings public XmlTvListingsProvider( IServerConfigurationManager config, IHttpClient httpClient, - ILogger logger, + ILogger<XmlTvListingsProvider> logger, IFileSystem fileSystem, IZipClient zipClient) { @@ -81,7 +83,7 @@ namespace Emby.Server.Implementations.LiveTv.Listings { CancellationToken = cancellationToken, Url = path, - DecompressionMethod = CompressionMethod.Gzip, + DecompressionMethod = CompressionMethods.Gzip, }, HttpMethod.Get).ConfigureAwait(false)) using (var stream = res.Content) @@ -91,12 +93,12 @@ namespace Emby.Server.Implementations.LiveTv.Listings { using (var gzStream = new GZipStream(stream, CompressionMode.Decompress)) { - await gzStream.CopyToAsync(fileStream).ConfigureAwait(false); + await gzStream.CopyToAsync(fileStream, cancellationToken).ConfigureAwait(false); } } else { - await stream.CopyToAsync(fileStream).ConfigureAwait(false); + await stream.CopyToAsync(fileStream, cancellationToken).ConfigureAwait(false); } } diff --git a/Emby.Server.Implementations/LiveTv/LiveTvConfigurationFactory.cs b/Emby.Server.Implementations/LiveTv/LiveTvConfigurationFactory.cs index f9b274acb..ba916af38 100644 --- a/Emby.Server.Implementations/LiveTv/LiveTvConfigurationFactory.cs +++ b/Emby.Server.Implementations/LiveTv/LiveTvConfigurationFactory.cs @@ -1,3 +1,5 @@ +#pragma warning disable CS1591 + using System.Collections.Generic; using MediaBrowser.Common.Configuration; using MediaBrowser.Model.LiveTv; diff --git a/Emby.Server.Implementations/LiveTv/LiveTvDtoService.cs b/Emby.Server.Implementations/LiveTv/LiveTvDtoService.cs index e584664c9..6e903a18e 100644 --- a/Emby.Server.Implementations/LiveTv/LiveTvDtoService.cs +++ b/Emby.Server.Implementations/LiveTv/LiveTvDtoService.cs @@ -1,3 +1,5 @@ +#pragma warning disable CS1591 + using System; using System.Globalization; using System.Linq; diff --git a/Emby.Server.Implementations/LiveTv/LiveTvManager.cs b/Emby.Server.Implementations/LiveTv/LiveTvManager.cs index e3f9df35a..b64fe8634 100644 --- a/Emby.Server.Implementations/LiveTv/LiveTvManager.cs +++ b/Emby.Server.Implementations/LiveTv/LiveTvManager.cs @@ -1,4 +1,3 @@ -#pragma warning disable SA1600 #pragma warning disable CS1591 using System; diff --git a/Emby.Server.Implementations/LiveTv/LiveTvMediaSourceProvider.cs b/Emby.Server.Implementations/LiveTv/LiveTvMediaSourceProvider.cs index 52d60c004..7f63991d0 100644 --- a/Emby.Server.Implementations/LiveTv/LiveTvMediaSourceProvider.cs +++ b/Emby.Server.Implementations/LiveTv/LiveTvMediaSourceProvider.cs @@ -1,52 +1,47 @@ +#pragma warning disable CS1591 + using System; using System.Collections.Generic; using System.Globalization; using System.Linq; using System.Threading; using System.Threading.Tasks; -using MediaBrowser.Common.Configuration; using MediaBrowser.Controller; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.LiveTv; -using MediaBrowser.Controller.MediaEncoding; using MediaBrowser.Model.Dto; using MediaBrowser.Model.MediaInfo; -using MediaBrowser.Model.Serialization; using Microsoft.Extensions.Logging; namespace Emby.Server.Implementations.LiveTv { public class LiveTvMediaSourceProvider : IMediaSourceProvider { + // Do not use a pipe here because Roku http requests to the server will fail, without any explicit error message. + private const char StreamIdDelimeter = '_'; + private const string StreamIdDelimeterString = "_"; + private readonly ILiveTvManager _liveTvManager; - private readonly IJsonSerializer _jsonSerializer; private readonly ILogger _logger; private readonly IMediaSourceManager _mediaSourceManager; - private readonly IMediaEncoder _mediaEncoder; private readonly IServerApplicationHost _appHost; - private IApplicationPaths _appPaths; - public LiveTvMediaSourceProvider(ILiveTvManager liveTvManager, IApplicationPaths appPaths, IJsonSerializer jsonSerializer, ILoggerFactory loggerFactory, IMediaSourceManager mediaSourceManager, IMediaEncoder mediaEncoder, IServerApplicationHost appHost) + public LiveTvMediaSourceProvider(ILiveTvManager liveTvManager, ILogger<LiveTvMediaSourceProvider> logger, IMediaSourceManager mediaSourceManager, IServerApplicationHost appHost) { _liveTvManager = liveTvManager; - _jsonSerializer = jsonSerializer; + _logger = logger; _mediaSourceManager = mediaSourceManager; - _mediaEncoder = mediaEncoder; _appHost = appHost; - _logger = loggerFactory.CreateLogger(GetType().Name); - _appPaths = appPaths; } public Task<IEnumerable<MediaSourceInfo>> GetMediaSources(BaseItem item, CancellationToken cancellationToken) { - var baseItem = (BaseItem)item; - - if (baseItem.SourceType == SourceType.LiveTV) + if (item.SourceType == SourceType.LiveTV) { var activeRecordingInfo = _liveTvManager.GetActiveRecordingInfo(item.Path); - if (string.IsNullOrEmpty(baseItem.Path) || activeRecordingInfo != null) + if (string.IsNullOrEmpty(item.Path) || activeRecordingInfo != null) { return GetMediaSourcesInternal(item, activeRecordingInfo, cancellationToken); } @@ -55,10 +50,6 @@ namespace Emby.Server.Implementations.LiveTv return Task.FromResult<IEnumerable<MediaSourceInfo>>(Array.Empty<MediaSourceInfo>()); } - // Do not use a pipe here because Roku http requests to the server will fail, without any explicit error message. - private const char StreamIdDelimeter = '_'; - private const string StreamIdDelimeterString = "_"; - private async Task<IEnumerable<MediaSourceInfo>> GetMediaSourcesInternal(BaseItem item, ActiveRecordingInfo activeRecordingInfo, CancellationToken cancellationToken) { IEnumerable<MediaSourceInfo> sources; @@ -91,7 +82,7 @@ namespace Emby.Server.Implementations.LiveTv foreach (var source in list) { source.Type = MediaSourceType.Default; - source.BufferMs = source.BufferMs ?? 1500; + source.BufferMs ??= 1500; if (source.RequiresOpening || forceRequireOpening) { @@ -100,11 +91,14 @@ namespace Emby.Server.Implementations.LiveTv if (source.RequiresOpening) { - var openKeys = new List<string>(); - openKeys.Add(item.GetType().Name); - openKeys.Add(item.Id.ToString("N", CultureInfo.InvariantCulture)); - openKeys.Add(source.Id ?? string.Empty); - source.OpenToken = string.Join(StreamIdDelimeterString, openKeys.ToArray()); + var openKeys = new List<string> + { + item.GetType().Name, + item.Id.ToString("N", CultureInfo.InvariantCulture), + source.Id ?? string.Empty + }; + + source.OpenToken = string.Join(StreamIdDelimeterString, openKeys); } // Dummy this up so that direct play checks can still run @@ -114,11 +108,12 @@ namespace Emby.Server.Implementations.LiveTv } } - _logger.LogDebug("MediaSources: {0}", _jsonSerializer.SerializeToString(list)); + _logger.LogDebug("MediaSources: {@MediaSources}", list); return list; } + /// <inheritdoc /> public async Task<ILiveStream> OpenMediaSource(string openToken, List<ILiveStream> currentLiveStreams, CancellationToken cancellationToken) { var keys = openToken.Split(new[] { StreamIdDelimeter }, 3); diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/BaseTunerHost.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/BaseTunerHost.cs index 715f600a1..80ee1ee33 100644 --- a/Emby.Server.Implementations/LiveTv/TunerHosts/BaseTunerHost.cs +++ b/Emby.Server.Implementations/LiveTv/TunerHosts/BaseTunerHost.cs @@ -1,3 +1,5 @@ +#pragma warning disable CS1591 + using System; using System.Collections.Concurrent; using System.Collections.Generic; diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs index 06f27fa3e..25b2c674c 100644 --- a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs +++ b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs @@ -1,3 +1,5 @@ +#pragma warning disable CS1591 + using System; using System.Collections.Generic; using System.Globalization; @@ -36,7 +38,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun public HdHomerunHost( IServerConfigurationManager config, - ILogger logger, + ILogger<HdHomerunHost> logger, IJsonSerializer jsonSerializer, IFileSystem fileSystem, IHttpClient httpClient, diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunManager.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunManager.cs index 9702392b2..57c5b7500 100644 --- a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunManager.cs +++ b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunManager.cs @@ -1,3 +1,5 @@ +#pragma warning disable CS1591 + using System; using System.Buffers; using System.Collections.Generic; diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunUdpStream.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunUdpStream.cs index 649becbd3..03ee5bfb6 100644 --- a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunUdpStream.cs +++ b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunUdpStream.cs @@ -1,3 +1,5 @@ +#pragma warning disable CS1591 + using System; using System.Collections.Generic; using System.IO; @@ -5,8 +7,8 @@ using System.Net; using System.Net.Sockets; using System.Threading; using System.Threading.Tasks; -using MediaBrowser.Common.Net; using MediaBrowser.Common.Configuration; +using MediaBrowser.Common.Net; using MediaBrowser.Controller; using MediaBrowser.Controller.Library; using MediaBrowser.Model.Dto; diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/LiveStream.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/LiveStream.cs index 862b9fdfe..4e4f1d7f6 100644 --- a/Emby.Server.Implementations/LiveTv/TunerHosts/LiveStream.cs +++ b/Emby.Server.Implementations/LiveTv/TunerHosts/LiveStream.cs @@ -1,3 +1,5 @@ +#pragma warning disable CS1591 + using System; using System.Collections.Generic; using System.Globalization; @@ -5,8 +7,8 @@ using System.IO; using System.Linq; using System.Threading; using System.Threading.Tasks; -using MediaBrowser.Controller.Library; using MediaBrowser.Common.Configuration; +using MediaBrowser.Controller.Library; using MediaBrowser.Model.Dto; using MediaBrowser.Model.IO; using MediaBrowser.Model.LiveTv; diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/M3UTunerHost.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/M3UTunerHost.cs index df054f1eb..f5dda79db 100644 --- a/Emby.Server.Implementations/LiveTv/TunerHosts/M3UTunerHost.cs +++ b/Emby.Server.Implementations/LiveTv/TunerHosts/M3UTunerHost.cs @@ -1,3 +1,5 @@ +#pragma warning disable CS1591 + using System; using System.Collections.Generic; using System.Globalization; diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/M3uParser.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/M3uParser.cs index 51f61bac7..59451fccd 100644 --- a/Emby.Server.Implementations/LiveTv/TunerHosts/M3uParser.cs +++ b/Emby.Server.Implementations/LiveTv/TunerHosts/M3uParser.cs @@ -1,3 +1,5 @@ +#pragma warning disable CS1591 + using System; using System.Collections.Generic; using System.Globalization; diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/SharedHttpStream.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/SharedHttpStream.cs index 99244eb62..d63588bbd 100644 --- a/Emby.Server.Implementations/LiveTv/TunerHosts/SharedHttpStream.cs +++ b/Emby.Server.Implementations/LiveTv/TunerHosts/SharedHttpStream.cs @@ -1,3 +1,5 @@ +#pragma warning disable CS1591 + using System; using System.Collections.Generic; using System.IO; @@ -57,7 +59,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts Url = url, CancellationToken = CancellationToken.None, BufferContent = false, - DecompressionMethod = CompressionMethod.None + DecompressionMethod = CompressionMethods.None }; foreach (var header in mediaSource.RequiredHttpHeaders) diff --git a/Emby.Server.Implementations/Localization/Core/af.json b/Emby.Server.Implementations/Localization/Core/af.json index dcec26801..1363eaf85 100644 --- a/Emby.Server.Implementations/Localization/Core/af.json +++ b/Emby.Server.Implementations/Localization/Core/af.json @@ -41,7 +41,6 @@ "User": "Gebruiker", "TvShows": "TV Programme", "System": "Stelsel", - "SubtitlesDownloadedForItem": "Ondertitels afgelaai vir {0}", "SubtitleDownloadFailureFromForItem": "Ondertitels het misluk om af te laai van {0} vir {1}", "StartupEmbyServerIsLoading": "Jellyfin Bediener is besig om te laai. Probeer weer in 'n kort tyd.", "ServerNameNeedsToBeRestarted": "{0} moet herbegin word", diff --git a/Emby.Server.Implementations/Localization/Core/ar.json b/Emby.Server.Implementations/Localization/Core/ar.json index f0f165b22..f313039a6 100644 --- a/Emby.Server.Implementations/Localization/Core/ar.json +++ b/Emby.Server.Implementations/Localization/Core/ar.json @@ -1,23 +1,23 @@ { "Albums": "ألبومات", "AppDeviceValues": "تطبيق: {0}, جهاز: {1}", - "Application": "التطبيق", - "Artists": "الفنان", + "Application": "تطبيق", + "Artists": "الفنانين", "AuthenticationSucceededWithUserName": "{0} سجل الدخول بنجاح", - "Books": "كتب", + "Books": "الكتب", "CameraImageUploadedFrom": "صورة كاميرا جديدة تم رفعها من {0}", "Channels": "القنوات", - "ChapterNameValue": "الباب {0}", + "ChapterNameValue": "الفصل {0}", "Collections": "مجموعات", - "DeviceOfflineWithName": "تم قطع اتصال {0}", + "DeviceOfflineWithName": "قُطِع الاتصال بـ{0}", "DeviceOnlineWithName": "{0} متصل", "FailedLoginAttemptWithUserName": "عملية تسجيل الدخول فشلت من {0}", - "Favorites": "التفضيلات", + "Favorites": "المفضلة", "Folders": "المجلدات", - "Genres": "أنواع الأفلام", + "Genres": "الأنواع", "HeaderAlbumArtists": "فناني الألبومات", "HeaderCameraUploads": "تحميلات الكاميرا", - "HeaderContinueWatching": "استئناف المشاهدة", + "HeaderContinueWatching": "استئناف", "HeaderFavoriteAlbums": "الألبومات المفضلة", "HeaderFavoriteArtists": "الفنانون المفضلون", "HeaderFavoriteEpisodes": "الحلقات المفضلة", @@ -31,28 +31,28 @@ "ItemAddedWithName": "تم إضافة {0} للمكتبة", "ItemRemovedWithName": "تم إزالة {0} من المكتبة", "LabelIpAddressValue": "عنوان الآي بي: {0}", - "LabelRunningTimeValue": "وقت التشغيل: {0}", + "LabelRunningTimeValue": "المدة: {0}", "Latest": "الأحدث", - "MessageApplicationUpdated": "لقد تم تحديث خادم أمبي", + "MessageApplicationUpdated": "لقد تم تحديث خادم Jellyfin", "MessageApplicationUpdatedTo": "تم تحديث سيرفر Jellyfin الى {0}", "MessageNamedServerConfigurationUpdatedWithValue": "تم تحديث إعدادات الخادم في قسم {0}", "MessageServerConfigurationUpdated": "تم تحديث إعدادات الخادم", - "MixedContent": "محتوى مخلوط", + "MixedContent": "محتوى مختلط", "Movies": "الأفلام", "Music": "الموسيقى", "MusicVideos": "الفيديوهات الموسيقية", "NameInstallFailed": "فشل التثبيت {0}", "NameSeasonNumber": "الموسم {0}", "NameSeasonUnknown": "الموسم غير معروف", - "NewVersionIsAvailable": "نسخة حديثة من سيرفر Jellyfin متوفرة للتحميل .", + "NewVersionIsAvailable": "نسخة جديدة من سيرفر Jellyfin متوفرة للتحميل.", "NotificationOptionApplicationUpdateAvailable": "يوجد تحديث للتطبيق", "NotificationOptionApplicationUpdateInstalled": "تم تحديث التطبيق", "NotificationOptionAudioPlayback": "بدأ تشغيل المقطع الصوتي", "NotificationOptionAudioPlaybackStopped": "تم إيقاف تشغيل المقطع الصوتي", - "NotificationOptionCameraImageUploaded": "تم رقع صورة الكاميرا", + "NotificationOptionCameraImageUploaded": "تم رفع صورة الكاميرا", "NotificationOptionInstallationFailed": "فشل في التثبيت", "NotificationOptionNewLibraryContent": "تم إضافة محتوى جديد", - "NotificationOptionPluginError": "فشل في الملحق", + "NotificationOptionPluginError": "فشل في البرنامج المضاف", "NotificationOptionPluginInstalled": "تم تثبيت الملحق", "NotificationOptionPluginUninstalled": "تمت إزالة الملحق", "NotificationOptionPluginUpdateInstalled": "تم تثبيت تحديثات الملحق", @@ -76,7 +76,6 @@ "StartupEmbyServerIsLoading": "سيرفر Jellyfin قيد التشغيل . الرجاء المحاولة بعد قليل.", "SubtitleDownloadFailureForItem": "عملية إنزال الترجمة فشلت لـ{0}", "SubtitleDownloadFailureFromForItem": "الترجمات فشلت في التحميل من {0} الى {1}", - "SubtitlesDownloadedForItem": "تم تحميل الترجمات الى {0}", "Sync": "مزامنة", "System": "النظام", "TvShows": "البرامج التلفزيونية", @@ -91,7 +90,29 @@ "UserPolicyUpdatedWithName": "تم تحديث سياسة المستخدم {0}", "UserStartedPlayingItemWithValues": "قام {0} ببدء تشغيل {1} على {2}", "UserStoppedPlayingItemWithValues": "قام {0} بإيقاف تشغيل {1} على {2}", - "ValueHasBeenAddedToLibrary": "{0} تم اضافتها الى مكتبة الوسائط", - "ValueSpecialEpisodeName": "مميز - {0}", - "VersionNumber": "الإصدار رقم {0}" + "ValueHasBeenAddedToLibrary": "تمت اضافت {0} إلى مكتبة الوسائط", + "ValueSpecialEpisodeName": "خاص - {0}", + "VersionNumber": "النسخة {0}", + "TaskCleanCacheDescription": "يحذف ملفات ذاكرة التخزين المؤقت التي لم يعد النظام بحاجة إليها.", + "TaskCleanCache": "احذف مجلد ذاكرة التخزين المؤقت", + "TasksChannelsCategory": "قنوات الإنترنت", + "TasksLibraryCategory": "مكتبة", + "TasksMaintenanceCategory": "صيانة", + "TaskRefreshLibraryDescription": "يقوم بفصح مكتبة الوسائط الخاصة بك بحثًا عن ملفات جديدة وتحديث البيانات الوصفية.", + "TaskRefreshLibrary": "افحص مكتبة الوسائط", + "TaskRefreshChapterImagesDescription": "إنشاء صور مصغرة لمقاطع الفيديو ذات فصول.", + "TaskRefreshChapterImages": "استخراج صور الفصل", + "TasksApplicationCategory": "تطبيق", + "TaskDownloadMissingSubtitlesDescription": "ابحث في الإنترنت على الترجمات المفقودة إستنادا على الميتاداتا.", + "TaskDownloadMissingSubtitles": "تحميل الترجمات المفقودة", + "TaskRefreshChannelsDescription": "تحديث معلومات قنوات الإنترنت.", + "TaskRefreshChannels": "إعادة تحديث القنوات", + "TaskCleanTranscodeDescription": "حذف ملفات الترميز الأقدم من يوم واحد.", + "TaskCleanTranscode": "حذف سجلات الترميز", + "TaskUpdatePluginsDescription": "تحميل وتثبيت الإضافات التي تم تفعيل التحديث التلقائي لها.", + "TaskUpdatePlugins": "تحديث الإضافات", + "TaskRefreshPeopleDescription": "تحديث البيانات الوصفية للممثلين والمخرجين في مكتبة الوسائط الخاصة بك.", + "TaskRefreshPeople": "إعادة تحميل الأشخاص", + "TaskCleanLogsDescription": "حذف السجلات الأقدم من {0} يوم.", + "TaskCleanLogs": "حذف دليل السجل" } diff --git a/Emby.Server.Implementations/Localization/Core/bg-BG.json b/Emby.Server.Implementations/Localization/Core/bg-BG.json index 9441da591..3fc7c7dc0 100644 --- a/Emby.Server.Implementations/Localization/Core/bg-BG.json +++ b/Emby.Server.Implementations/Localization/Core/bg-BG.json @@ -1,8 +1,8 @@ { "Albums": "Албуми", - "AppDeviceValues": "Програма: {0}, устройство: {1}", + "AppDeviceValues": "Програма: {0}, Устройство: {1}", "Application": "Програма", - "Artists": "Изпълнители", + "Artists": "Артисти", "AuthenticationSucceededWithUserName": "{0} се удостовери успешно", "Books": "Книги", "CameraImageUploadedFrom": "Нова снимка от камера беше качена от {0}", @@ -31,20 +31,20 @@ "ItemAddedWithName": "{0} е добавено към библиотеката", "ItemRemovedWithName": "{0} е премахнато от библиотеката", "LabelIpAddressValue": "ИП адрес: {0}", - "LabelRunningTimeValue": "", + "LabelRunningTimeValue": "Стартирано от: {0}", "Latest": "Последни", "MessageApplicationUpdated": "Сървърът е обновен", "MessageApplicationUpdatedTo": "Сървърът е обновен до {0}", - "MessageNamedServerConfigurationUpdatedWithValue": "", - "MessageServerConfigurationUpdated": "", + "MessageNamedServerConfigurationUpdatedWithValue": "Секцията {0} от сървърната конфигурация се актуализира", + "MessageServerConfigurationUpdated": "Конфигурацията на сървъра се актуализира", "MixedContent": "Смесено съдържание", "Movies": "Филми", "Music": "Музика", "MusicVideos": "Музикални клипове", - "NameInstallFailed": "", + "NameInstallFailed": "{0} не можа да се инсталира", "NameSeasonNumber": "Сезон {0}", "NameSeasonUnknown": "Неразпознат сезон", - "NewVersionIsAvailable": "", + "NewVersionIsAvailable": "Нова версия на Jellyfin сървъра е достъпна за сваляне.", "NotificationOptionApplicationUpdateAvailable": "Налично е обновление на програмата", "NotificationOptionApplicationUpdateInstalled": "Обновлението на програмата е инсталирано", "NotificationOptionAudioPlayback": "Възпроизвеждането на звук започна", @@ -58,7 +58,7 @@ "NotificationOptionPluginUpdateInstalled": "Обновлението на приставката е инсталирано", "NotificationOptionServerRestartRequired": "Нужно е повторно пускане на сървъра", "NotificationOptionTaskFailed": "Грешка в планирана задача", - "NotificationOptionUserLockedOut": "", + "NotificationOptionUserLockedOut": "Потребителя е заключен", "NotificationOptionVideoPlayback": "Възпроизвеждането на видео започна", "NotificationOptionVideoPlaybackStopped": "Възпроизвеждането на видео е спряно", "Photos": "Снимки", @@ -70,28 +70,49 @@ "ProviderValue": "Доставчик: {0}", "ScheduledTaskFailedWithName": "{0} се провали", "ScheduledTaskStartedWithName": "{0} започна", - "ServerNameNeedsToBeRestarted": "", + "ServerNameNeedsToBeRestarted": "{0} е нужно да се рестартира", "Shows": "Сериали", "Songs": "Песни", "StartupEmbyServerIsLoading": "Сървърът зарежда. Моля, опитайте отново след малко.", "SubtitleDownloadFailureForItem": "Неуспешно изтегляне на субтитри за {0}", - "SubtitleDownloadFailureFromForItem": "", - "SubtitlesDownloadedForItem": "Изтеглени са субтитри за {0}", + "SubtitleDownloadFailureFromForItem": "Поднадписите за {1} от {0} не можаха да се изтеглят", "Sync": "Синхронизиране", "System": "Система", "TvShows": "Телевизионни сериали", "User": "Потребител", "UserCreatedWithName": "Потребителят {0} е създаден", "UserDeletedWithName": "Потребителят {0} е изтрит", - "UserDownloadingItemWithValues": "", - "UserLockedOutWithName": "", + "UserDownloadingItemWithValues": "{0} изтегля {1}", + "UserLockedOutWithName": "Потребител {0} се заключи", "UserOfflineFromDevice": "{0} се разкачи от {1}", "UserOnlineFromDevice": "{0} е на линия от {1}", "UserPasswordChangedWithName": "Паролата на потребителя {0} е променена", - "UserPolicyUpdatedWithName": "", + "UserPolicyUpdatedWithName": "Потребителската политика за {0} се актуализира", "UserStartedPlayingItemWithValues": "{0} пусна {1}", "UserStoppedPlayingItemWithValues": "{0} спря {1}", - "ValueHasBeenAddedToLibrary": "", + "ValueHasBeenAddedToLibrary": "{0} беше добавен във Вашата библиотека", "ValueSpecialEpisodeName": "Специални - {0}", - "VersionNumber": "Версия {0}" + "VersionNumber": "Версия {0}", + "TaskDownloadMissingSubtitlesDescription": "Търси Интернет за липсващи поднадписи, на база конфигурацията за мета-данни.", + "TaskDownloadMissingSubtitles": "Изтегляне на липсващи поднадписи", + "TaskRefreshChannelsDescription": "Обновява информацията за интернет канала.", + "TaskRefreshChannels": "Обновяване на Канали", + "TaskCleanTranscodeDescription": "Изтрива прекодирани файлове по-стари от един ден.", + "TaskCleanTranscode": "Изчиства директорията за прекодиране", + "TaskUpdatePluginsDescription": "Изтегля и инсталира актуализации за добавките, които са настроени за автоматична актуализация.", + "TaskUpdatePlugins": "Актуализира добавките", + "TaskRefreshPeopleDescription": "Актуализира мета-данните за артистите и режисьорите за Вашата медийна библиотека.", + "TaskRefreshPeople": "Обновяване на участниците", + "TaskCleanLogsDescription": "Изтрива лог файлове по-стари от {0} дни.", + "TaskCleanLogs": "Изчисти директорията с логове", + "TaskRefreshLibraryDescription": "Сканира Вашата библиотека с медия за нови файлове и обновява мета-данните.", + "TaskRefreshLibrary": "Сканиране на библиотеката с медия", + "TaskRefreshChapterImagesDescription": "Създава иконки за видеа, които имат епизоди.", + "TaskRefreshChapterImages": "Извличане на изображения за епизода", + "TaskCleanCacheDescription": "Изтриване на ненужните от системата файлове.", + "TaskCleanCache": "Изчистване на Кеш-директорията", + "TasksChannelsCategory": "Интернет Канали", + "TasksApplicationCategory": "Приложение", + "TasksLibraryCategory": "Библиотека", + "TasksMaintenanceCategory": "Поддръжка" } diff --git a/Emby.Server.Implementations/Localization/Core/bn.json b/Emby.Server.Implementations/Localization/Core/bn.json index a7219a725..ef7792356 100644 --- a/Emby.Server.Implementations/Localization/Core/bn.json +++ b/Emby.Server.Implementations/Localization/Core/bn.json @@ -38,7 +38,6 @@ "TvShows": "টিভি শোগুলো", "System": "সিস্টেম", "Sync": "সিংক", - "SubtitlesDownloadedForItem": "{0} এর জন্য সাবটাইটেল ডাউনলোড করা হয়েছে", "SubtitleDownloadFailureFromForItem": "{2} থেকে {1} এর জন্য সাবটাইটেল ডাউনলোড ব্যর্থ", "StartupEmbyServerIsLoading": "জেলিফিন সার্ভার লোড হচ্ছে। দয়া করে একটু পরে আবার চেষ্টা করুন।", "Songs": "গানগুলো", diff --git a/Emby.Server.Implementations/Localization/Core/ca.json b/Emby.Server.Implementations/Localization/Core/ca.json index 44e7cf0ce..2d8299367 100644 --- a/Emby.Server.Implementations/Localization/Core/ca.json +++ b/Emby.Server.Implementations/Localization/Core/ca.json @@ -76,7 +76,6 @@ "StartupEmbyServerIsLoading": "El Servidor d'Jellyfin està carregant. Si et plau, prova de nou en breus.", "SubtitleDownloadFailureForItem": "Subtitles failed to download for {0}", "SubtitleDownloadFailureFromForItem": "Els subtítols no s'han pogut baixar de {0} per {1}", - "SubtitlesDownloadedForItem": "Subtítols descarregats per a {0}", "Sync": "Sincronitzar", "System": "System", "TvShows": "Espectacles de TV", diff --git a/Emby.Server.Implementations/Localization/Core/cs.json b/Emby.Server.Implementations/Localization/Core/cs.json index 86fbac380..992bb9df3 100644 --- a/Emby.Server.Implementations/Localization/Core/cs.json +++ b/Emby.Server.Implementations/Localization/Core/cs.json @@ -5,7 +5,7 @@ "Artists": "Umělci", "AuthenticationSucceededWithUserName": "{0} úspěšně ověřen", "Books": "Knihy", - "CameraImageUploadedFrom": "Z {0} byla nahrána nová fotografie", + "CameraImageUploadedFrom": "Z {0} byla nahrána nová fotografie z fotoaparátu", "Channels": "Kanály", "ChapterNameValue": "Kapitola {0}", "Collections": "Kolekce", @@ -76,7 +76,6 @@ "StartupEmbyServerIsLoading": "Jellyfin Server je spouštěn. Zkuste to prosím v brzké době znovu.", "SubtitleDownloadFailureForItem": "Stahování titulků selhalo pro {0}", "SubtitleDownloadFailureFromForItem": "Stažení titulků pro {1} z {0} selhalo", - "SubtitlesDownloadedForItem": "Staženy titulky pro {0}", "Sync": "Synchronizace", "System": "Systém", "TvShows": "TV seriály", @@ -93,5 +92,27 @@ "UserStoppedPlayingItemWithValues": "{0} zastavil přehrávání {1}", "ValueHasBeenAddedToLibrary": "{0} byl přidán do vaší knihovny médií", "ValueSpecialEpisodeName": "Speciál - {0}", - "VersionNumber": "Verze {0}" + "VersionNumber": "Verze {0}", + "TaskDownloadMissingSubtitlesDescription": "Vyhledá na internetu chybějící titulky na základě nastavení metadat.", + "TaskDownloadMissingSubtitles": "Stáhnout chybějící titulky", + "TaskRefreshChannelsDescription": "Obnoví informace o internetových kanálech.", + "TaskRefreshChannels": "Obnovit kanály", + "TaskCleanTranscodeDescription": "Odstraní více než 1 den staré transkódované soubory.", + "TaskCleanTranscode": "Vyčistit adresář s transkódovaným obsahem", + "TaskUpdatePluginsDescription": "Stáhne a nainstaluje aktualizace zásuvných modulů, které mají nastavenou automatickou aktualizaci.", + "TaskUpdatePlugins": "Aktualizovat zásuvné moduly", + "TaskRefreshPeopleDescription": "Aktualizuje metadata umělců a režisérů ve Vaší knihovně médií.", + "TaskRefreshPeople": "Obnovit umělce", + "TaskCleanLogsDescription": "Odstraní soubory protokolu, které jsou starší více než {0} dní.", + "TaskCleanLogs": "Vyčistit adresář se souborem protokolu", + "TaskRefreshLibraryDescription": "Prohledá Vaši knihovnu médií zda neobsahuje nové soubory a obnoví metadatada.", + "TaskRefreshLibrary": "Prohledat knihovnu médií", + "TaskRefreshChapterImagesDescription": "Vytvoří náhledy videí, které obsahují kapitoly.", + "TaskRefreshChapterImages": "Extrahovat obrázky kapitol", + "TaskCleanCacheDescription": "Odstraní soubory mezipaměti, které systém již nebude potřebovat.", + "TaskCleanCache": "Vyčistit složku s mezipamětí", + "TasksChannelsCategory": "Internetové kanály", + "TasksApplicationCategory": "Aplikace", + "TasksLibraryCategory": "Knihovna", + "TasksMaintenanceCategory": "Údržba" } diff --git a/Emby.Server.Implementations/Localization/Core/da.json b/Emby.Server.Implementations/Localization/Core/da.json index cc8b7dbd5..92719a9fd 100644 --- a/Emby.Server.Implementations/Localization/Core/da.json +++ b/Emby.Server.Implementations/Localization/Core/da.json @@ -3,7 +3,7 @@ "AppDeviceValues": "App: {0}, Enhed: {1}", "Application": "Applikation", "Artists": "Kunstnere", - "AuthenticationSucceededWithUserName": "{0} bekræftet med succes", + "AuthenticationSucceededWithUserName": "{0} succesfuldt autentificeret", "Books": "Bøger", "CameraImageUploadedFrom": "Et nyt kamerabillede er blevet uploadet fra {0}", "Channels": "Kanaler", @@ -76,7 +76,6 @@ "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": "Undertekster kunne ikke downloades fra {0} til {1}", - "SubtitlesDownloadedForItem": "Undertekster downloadet for {0}", "Sync": "Synk", "System": "System", "TvShows": "TV serier", @@ -93,5 +92,19 @@ "UserStoppedPlayingItemWithValues": "{0} har afsluttet afspilning af {1} på {2}", "ValueHasBeenAddedToLibrary": "{0} er blevet tilføjet til dit mediebibliotek", "ValueSpecialEpisodeName": "Special - {0}", - "VersionNumber": "Version {0}" + "VersionNumber": "Version {0}", + "TaskDownloadMissingSubtitlesDescription": "Søger på internettet efter manglende undertekster baseret på metadata konfiration.", + "TaskDownloadMissingSubtitles": "Download manglende undertekster", + "TaskUpdatePluginsDescription": "Downloader og installere opdateringer for plugins som er konfigureret til at opdatere automatisk.", + "TaskUpdatePlugins": "Opdater Plugins", + "TaskCleanLogsDescription": "Sletter log filer som er mere end {0} dage gammle.", + "TaskCleanLogs": "Ryd Log Mappe", + "TaskRefreshLibraryDescription": "Scanner dit medie bibliotek for nye filer og opdatere metadata.", + "TaskRefreshLibrary": "Scan Medie Bibliotek", + "TaskCleanCacheDescription": "Sletter cache filer som systemet ikke har brug for længere.", + "TaskCleanCache": "Ryd Cache Mappe", + "TasksChannelsCategory": "Internet Kanaler", + "TasksApplicationCategory": "Applikation", + "TasksLibraryCategory": "Bibliotek", + "TasksMaintenanceCategory": "Vedligeholdelse" } diff --git a/Emby.Server.Implementations/Localization/Core/de.json b/Emby.Server.Implementations/Localization/Core/de.json index 019736c47..414430ff7 100644 --- a/Emby.Server.Implementations/Localization/Core/de.json +++ b/Emby.Server.Implementations/Localization/Core/de.json @@ -3,13 +3,13 @@ "AppDeviceValues": "App: {0}, Gerät: {1}", "Application": "Anwendung", "Artists": "Interpreten", - "AuthenticationSucceededWithUserName": "{0} hat sich erfolgreich angemeldet", + "AuthenticationSucceededWithUserName": "{0} hat sich erfolgreich authentifziert", "Books": "Bücher", - "CameraImageUploadedFrom": "Ein neues Foto wurde hochgeladen von {0}", + "CameraImageUploadedFrom": "Ein neues Foto wurde von {0} hochgeladen", "Channels": "Kanäle", "ChapterNameValue": "Kapitel {0}", "Collections": "Sammlungen", - "DeviceOfflineWithName": "{0} wurde getrennt", + "DeviceOfflineWithName": "{0} hat die Verbindung getrennt", "DeviceOnlineWithName": "{0} ist verbunden", "FailedLoginAttemptWithUserName": "Fehlgeschlagener Anmeldeversuch von {0}", "Favorites": "Favoriten", @@ -17,7 +17,7 @@ "Genres": "Genres", "HeaderAlbumArtists": "Album-Interpreten", "HeaderCameraUploads": "Kamera-Uploads", - "HeaderContinueWatching": "Weiterschauen", + "HeaderContinueWatching": "Fortsetzen", "HeaderFavoriteAlbums": "Lieblingsalben", "HeaderFavoriteArtists": "Lieblings-Interpreten", "HeaderFavoriteEpisodes": "Lieblingsepisoden", @@ -50,7 +50,7 @@ "NotificationOptionAudioPlayback": "Audiowiedergabe gestartet", "NotificationOptionAudioPlaybackStopped": "Audiowiedergabe gestoppt", "NotificationOptionCameraImageUploaded": "Foto hochgeladen", - "NotificationOptionInstallationFailed": "Installationsfehler", + "NotificationOptionInstallationFailed": "Installation fehlgeschlagen", "NotificationOptionNewLibraryContent": "Neuer Inhalt hinzugefügt", "NotificationOptionPluginError": "Plugin-Fehler", "NotificationOptionPluginInstalled": "Plugin installiert", @@ -76,7 +76,6 @@ "StartupEmbyServerIsLoading": "Jellyfin-Server startet, bitte versuche es gleich noch einmal.", "SubtitleDownloadFailureForItem": "Download der Untertitel fehlgeschlagen für {0}", "SubtitleDownloadFailureFromForItem": "Untertitel von {0} für {1} konnten nicht heruntergeladen werden", - "SubtitlesDownloadedForItem": "Untertitel heruntergeladen für {0}", "Sync": "Synchronisation", "System": "System", "TvShows": "TV-Sendungen", @@ -93,5 +92,27 @@ "UserStoppedPlayingItemWithValues": "{0} hat die Wiedergabe von {1} auf {2} beendet", "ValueHasBeenAddedToLibrary": "{0} wurde deiner Bibliothek hinzugefügt", "ValueSpecialEpisodeName": "Extra - {0}", - "VersionNumber": "Version {0}" + "VersionNumber": "Version {0}", + "TaskDownloadMissingSubtitlesDescription": "Durchsucht das Internet nach fehlenden Untertiteln, basierend auf den Meta Einstellungen.", + "TaskDownloadMissingSubtitles": "Lade fehlende Untertitel herunter", + "TaskRefreshChannelsDescription": "Erneuere Internet Kanal Informationen.", + "TaskRefreshChannels": "Erneuere Kanäle", + "TaskCleanTranscodeDescription": "Löscht Transkodierdateien welche älter als ein Tag sind.", + "TaskCleanTranscode": "Lösche Transkodier Pfad", + "TaskUpdatePluginsDescription": "Läd Updates für Plugins herunter, welche dazu eingestellt sind automatisch zu updaten und installiert sie.", + "TaskUpdatePlugins": "Update Plugins", + "TaskRefreshPeopleDescription": "Erneuert Metadaten für Schausteller und Regisseure in deinen Bibliotheken.", + "TaskRefreshPeople": "Erneuere Schausteller", + "TaskCleanLogsDescription": "Lösche Log Datein die älter als {0} Tage sind.", + "TaskCleanLogs": "Lösche Log Pfad", + "TaskRefreshLibraryDescription": "Scanne alle Bibliotheken für hinzugefügte Datein und erneuere Metadaten.", + "TaskRefreshLibrary": "Scanne alle Bibliotheken", + "TaskRefreshChapterImagesDescription": "Kreiert Vorschaubilder für Videos welche Kapitel haben.", + "TaskRefreshChapterImages": "Extrahiert Kapitel-Bilder", + "TaskCleanCacheDescription": "Löscht Zwischenspeicherdatein die nicht länger von System gebraucht werden.", + "TaskCleanCache": "Leere Cache Pfad", + "TasksChannelsCategory": "Internet Kanäle", + "TasksApplicationCategory": "Anwendung", + "TasksLibraryCategory": "Bibliothek", + "TasksMaintenanceCategory": "Wartung" } diff --git a/Emby.Server.Implementations/Localization/Core/el.json b/Emby.Server.Implementations/Localization/Core/el.json index 580b42330..53e2f58de 100644 --- a/Emby.Server.Implementations/Localization/Core/el.json +++ b/Emby.Server.Implementations/Localization/Core/el.json @@ -76,7 +76,6 @@ "StartupEmbyServerIsLoading": "Ο Jellyfin Server φορτώνει. Παρακαλώ δοκιμάστε σε λίγο.", "SubtitleDownloadFailureForItem": "Οι υπότιτλοι απέτυχαν να κατέβουν για {0}", "SubtitleDownloadFailureFromForItem": "Αποτυχίες μεταφόρτωσης υποτίτλων από {0} για {1}", - "SubtitlesDownloadedForItem": "Οι υπότιτλοι κατέβηκαν για {0}", "Sync": "Συγχρονισμός", "System": "Σύστημα", "TvShows": "Τηλεοπτικές Σειρές", diff --git a/Emby.Server.Implementations/Localization/Core/en-GB.json b/Emby.Server.Implementations/Localization/Core/en-GB.json index 67d4068cf..dc4f0b212 100644 --- a/Emby.Server.Implementations/Localization/Core/en-GB.json +++ b/Emby.Server.Implementations/Localization/Core/en-GB.json @@ -76,7 +76,6 @@ "StartupEmbyServerIsLoading": "Jellyfin Server is loading. Please try again shortly.", "SubtitleDownloadFailureForItem": "Subtitles failed to download for {0}", "SubtitleDownloadFailureFromForItem": "Subtitles failed to download from {0} for {1}", - "SubtitlesDownloadedForItem": "Subtitles downloaded for {0}", "Sync": "Sync", "System": "System", "TvShows": "TV Shows", diff --git a/Emby.Server.Implementations/Localization/Core/en-US.json b/Emby.Server.Implementations/Localization/Core/en-US.json index aa855ed21..97a843160 100644 --- a/Emby.Server.Implementations/Localization/Core/en-US.json +++ b/Emby.Server.Implementations/Localization/Core/en-US.json @@ -75,7 +75,6 @@ "Songs": "Songs", "StartupEmbyServerIsLoading": "Jellyfin Server is loading. Please try again shortly.", "SubtitleDownloadFailureFromForItem": "Subtitles failed to download from {0} for {1}", - "SubtitlesDownloadedForItem": "Subtitles downloaded for {0}", "Sync": "Sync", "System": "System", "TvShows": "TV Shows", @@ -92,5 +91,27 @@ "UserStoppedPlayingItemWithValues": "{0} has finished playing {1} on {2}", "ValueHasBeenAddedToLibrary": "{0} has been added to your media library", "ValueSpecialEpisodeName": "Special - {0}", - "VersionNumber": "Version {0}" + "VersionNumber": "Version {0}", + "TasksMaintenanceCategory": "Maintenance", + "TasksLibraryCategory": "Library", + "TasksApplicationCategory": "Application", + "TasksChannelsCategory": "Internet Channels", + "TaskCleanCache": "Clean Cache Directory", + "TaskCleanCacheDescription": "Deletes cache files no longer needed by the system.", + "TaskRefreshChapterImages": "Extract Chapter Images", + "TaskRefreshChapterImagesDescription": "Creates thumbnails for videos that have chapters.", + "TaskRefreshLibrary": "Scan Media Library", + "TaskRefreshLibraryDescription": "Scans your media library for new files and refreshes metadata.", + "TaskCleanLogs": "Clean Log Directory", + "TaskCleanLogsDescription": "Deletes log files that are more than {0} days old.", + "TaskRefreshPeople": "Refresh People", + "TaskRefreshPeopleDescription": "Updates metadata for actors and directors in your media library.", + "TaskUpdatePlugins": "Update Plugins", + "TaskUpdatePluginsDescription": "Downloads and installs updates for plugins that are configured to update automatically.", + "TaskCleanTranscode": "Clean Transcode Directory", + "TaskCleanTranscodeDescription": "Deletes transcode files more than one day old.", + "TaskRefreshChannels": "Refresh Channels", + "TaskRefreshChannelsDescription": "Refreshes internet channel information.", + "TaskDownloadMissingSubtitles": "Download missing subtitles", + "TaskDownloadMissingSubtitlesDescription": "Searches the internet for missing subtitles based on metadata configuration." } diff --git a/Emby.Server.Implementations/Localization/Core/es-AR.json b/Emby.Server.Implementations/Localization/Core/es-AR.json index dc73ba6b3..1211eef54 100644 --- a/Emby.Server.Implementations/Localization/Core/es-AR.json +++ b/Emby.Server.Implementations/Localization/Core/es-AR.json @@ -11,11 +11,11 @@ "Collections": "Colecciones", "DeviceOfflineWithName": "{0} se ha desconectado", "DeviceOnlineWithName": "{0} está conectado", - "FailedLoginAttemptWithUserName": "Error al intentar iniciar sesión desde {0}", + "FailedLoginAttemptWithUserName": "Error al intentar iniciar sesión de {0}", "Favorites": "Favoritos", "Folders": "Carpetas", "Genres": "Géneros", - "HeaderAlbumArtists": "Artistas de álbumes", + "HeaderAlbumArtists": "Artistas de álbum", "HeaderCameraUploads": "Subidas de cámara", "HeaderContinueWatching": "Continuar viendo", "HeaderFavoriteAlbums": "Álbumes favoritos", @@ -24,7 +24,7 @@ "HeaderFavoriteShows": "Programas favoritos", "HeaderFavoriteSongs": "Canciones favoritas", "HeaderLiveTV": "TV en vivo", - "HeaderNextUp": "Continuar Viendo", + "HeaderNextUp": "A Continuación", "HeaderRecordingGroups": "Grupos de grabación", "HomeVideos": "Videos caseros", "Inherit": "Heredar", @@ -35,48 +35,47 @@ "Latest": "Últimos", "MessageApplicationUpdated": "El servidor Jellyfin fue actualizado", "MessageApplicationUpdatedTo": "Se ha actualizado el servidor Jellyfin a la versión {0}", - "MessageNamedServerConfigurationUpdatedWithValue": "Fue actualizada la sección {0} de la configuración del servidor", - "MessageServerConfigurationUpdated": "Fue actualizada la configuración del servidor", - "MixedContent": "Contenido mixto", + "MessageNamedServerConfigurationUpdatedWithValue": "Se ha actualizado la sección {0} de la configuración del servidor", + "MessageServerConfigurationUpdated": "Se ha actualizado la configuración del servidor", + "MixedContent": "Contenido mezclado", "Movies": "Películas", "Music": "Música", "MusicVideos": "Videos musicales", - "NameInstallFailed": "{0} error de instalación", + "NameInstallFailed": "{0} instalación fallida", "NameSeasonNumber": "Temporada {0}", "NameSeasonUnknown": "Temporada desconocida", - "NewVersionIsAvailable": "Disponible una nueva versión de Jellyfin para descargar.", + "NewVersionIsAvailable": "Una nueva versión del Servidor Jellyfin está disponible 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", "NotificationOptionAudioPlaybackStopped": "Se detuvo la reproducción de audio", - "NotificationOptionCameraImageUploaded": "Imagen de la cámara cargada", + "NotificationOptionCameraImageUploaded": "Imagen de la cámara subida", "NotificationOptionInstallationFailed": "Error de instalación", "NotificationOptionNewLibraryContent": "Nuevo contenido añadido", - "NotificationOptionPluginError": "Error en plugin", - "NotificationOptionPluginInstalled": "Plugin instalado", - "NotificationOptionPluginUninstalled": "Plugin desinstalado", - "NotificationOptionPluginUpdateInstalled": "Actualización del complemento instalada", - "NotificationOptionServerRestartRequired": "Se requiere reinicio del servidor", - "NotificationOptionTaskFailed": "Error de tarea programada", + "NotificationOptionPluginError": "Falla de complemento", + "NotificationOptionPluginInstalled": "Complemento instalado", + "NotificationOptionPluginUninstalled": "Complemento desinstalado", + "NotificationOptionPluginUpdateInstalled": "Actualización de complemento instalada", + "NotificationOptionServerRestartRequired": "Se necesita reiniciar el Servidor", + "NotificationOptionTaskFailed": "Falla de tarea programada", "NotificationOptionUserLockedOut": "Usuario bloqueado", "NotificationOptionVideoPlayback": "Se inició la reproducción de video", "NotificationOptionVideoPlaybackStopped": "Reproducción de video detenida", "Photos": "Fotos", "Playlists": "Listas de reproducción", - "Plugin": "Plugin", + "Plugin": "Complemento", "PluginInstalledWithName": "{0} fue instalado", "PluginUninstalledWithName": "{0} fue desinstalado", "PluginUpdatedWithName": "{0} fue actualizado", "ProviderValue": "Proveedor: {0}", "ScheduledTaskFailedWithName": "{0} falló", - "ScheduledTaskStartedWithName": "{0} iniciada", + "ScheduledTaskStartedWithName": "{0} iniciado", "ServerNameNeedsToBeRestarted": "{0} necesita ser reiniciado", "Shows": "Series", "Songs": "Canciones", - "StartupEmbyServerIsLoading": "Jellyfin Server se está cargando. Vuelve a intentarlo en breve.", + "StartupEmbyServerIsLoading": "El servidor Jellyfin se está cargando. Vuelve a intentarlo en breve.", "SubtitleDownloadFailureForItem": "Subtitles failed to download for {0}", - "SubtitleDownloadFailureFromForItem": "Fallo de descarga de subtítulos desde {0} para {1}", - "SubtitlesDownloadedForItem": "Descargar subtítulos para {0}", + "SubtitleDownloadFailureFromForItem": "Falló la descarga de subtitulos desde {0} para {1}", "Sync": "Sincronizar", "System": "Sistema", "TvShows": "Series de TV", @@ -88,10 +87,32 @@ "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": "Actualizada política de usuario para {0}", + "UserPolicyUpdatedWithName": "Las política de usuario ha sido actualizada 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}" + "VersionNumber": "Versión {0}", + "TaskDownloadMissingSubtitlesDescription": "Busca en internet los subtítulos que falten basándose en la configuración de los metadatos.", + "TaskDownloadMissingSubtitles": "Descargar subtítulos extraviados", + "TaskRefreshChannelsDescription": "Actualizar información de canales de internet.", + "TaskRefreshChannels": "Actualizar canales", + "TaskCleanTranscodeDescription": "Eliminar archivos transcodificados con mas de un día de antigüedad.", + "TaskCleanTranscode": "Limpiar directorio de Transcodificado", + "TaskUpdatePluginsDescription": "Descargar e instalar actualizaciones para complementos que estén configurados en actualizar automáticamente.", + "TaskUpdatePlugins": "Actualizar complementos", + "TaskRefreshPeopleDescription": "Actualizar metadatos de actores y directores en su librería multimedia.", + "TaskRefreshPeople": "Actualizar personas", + "TaskCleanLogsDescription": "Eliminar archivos de registro que tengan mas de {0} días de antigüedad.", + "TaskCleanLogs": "Limpiar directorio de registros", + "TaskRefreshLibraryDescription": "Escanear su librería multimedia por nuevos archivos y refrescar metadatos.", + "TaskRefreshLibrary": "Escanear librería multimedia", + "TaskRefreshChapterImagesDescription": "Crear miniaturas de videos que tengan capítulos.", + "TaskRefreshChapterImages": "Extraer imágenes de capitulo", + "TaskCleanCacheDescription": "Eliminar archivos de cache que no se necesiten en el sistema.", + "TaskCleanCache": "Limpiar directorio Cache", + "TasksChannelsCategory": "Canales de Internet", + "TasksApplicationCategory": "Solicitud", + "TasksLibraryCategory": "Biblioteca", + "TasksMaintenanceCategory": "Mantenimiento" } diff --git a/Emby.Server.Implementations/Localization/Core/es-MX.json b/Emby.Server.Implementations/Localization/Core/es-MX.json index 99fda7aa6..e0bbe90b3 100644 --- a/Emby.Server.Implementations/Localization/Core/es-MX.json +++ b/Emby.Server.Implementations/Localization/Core/es-MX.json @@ -76,7 +76,6 @@ "StartupEmbyServerIsLoading": "El servidor Jellyfin esta cargando. Por favor intente de nuevo dentro de poco.", "SubtitleDownloadFailureForItem": "Falló la descarga de subtítulos para {0}", "SubtitleDownloadFailureFromForItem": "Falló la descarga de subtitulos desde {0} para {1}", - "SubtitlesDownloadedForItem": "Subtítulos descargados para {0}", "Sync": "Sincronizar", "System": "Sistema", "TvShows": "Programas de TV", @@ -93,5 +92,27 @@ "UserStoppedPlayingItemWithValues": "{0} ha terminado de reproducirse {1} en {2}", "ValueHasBeenAddedToLibrary": "{0} se han añadido a su biblioteca de medios", "ValueSpecialEpisodeName": "Especial - {0}", - "VersionNumber": "Versión {0}" + "VersionNumber": "Versión {0}", + "TaskDownloadMissingSubtitlesDescription": "Buscar subtítulos de internet basado en configuración de metadatos.", + "TaskDownloadMissingSubtitles": "Descargar subtítulos perdidos", + "TaskRefreshChannelsDescription": "Refrescar información de canales de internet.", + "TaskRefreshChannels": "Actualizar canales", + "TaskCleanTranscodeDescription": "Eliminar archivos transcodificados que tengan mas de un día.", + "TaskCleanTranscode": "Limpiar directorio de transcodificado", + "TaskUpdatePluginsDescription": "Descargar y actualizar complementos que están configurados para actualizarse automáticamente.", + "TaskUpdatePlugins": "Actualizar complementos", + "TaskRefreshPeopleDescription": "Actualizar datos de actores y directores en su librería multimedia.", + "TaskRefreshPeople": "Refrescar persona", + "TaskCleanLogsDescription": "Eliminar archivos de registro con mas de {0} días.", + "TaskCleanLogs": "Directorio de logo limpio", + "TaskRefreshLibraryDescription": "Escanear su librería multimedia para nuevos archivos y refrescar metadatos.", + "TaskRefreshLibrary": "Escanear librería multimerdia", + "TaskRefreshChapterImagesDescription": "Crear miniaturas para videos con capítulos.", + "TaskRefreshChapterImages": "Extraer imágenes de capítulos", + "TaskCleanCacheDescription": "Eliminar archivos cache que ya no se necesiten por el sistema.", + "TaskCleanCache": "Limpiar directorio cache", + "TasksChannelsCategory": "Canales de Internet", + "TasksApplicationCategory": "Aplicación", + "TasksLibraryCategory": "Biblioteca", + "TasksMaintenanceCategory": "Mantenimiento" } diff --git a/Emby.Server.Implementations/Localization/Core/es.json b/Emby.Server.Implementations/Localization/Core/es.json index 2dcc2c1c8..de1baada8 100644 --- a/Emby.Server.Implementations/Localization/Core/es.json +++ b/Emby.Server.Implementations/Localization/Core/es.json @@ -76,7 +76,6 @@ "StartupEmbyServerIsLoading": "Jellyfin Server se está cargando. Vuelve a intentarlo en breve.", "SubtitleDownloadFailureForItem": "Error al descargar subtítulos para {0}", "SubtitleDownloadFailureFromForItem": "Fallo de descarga de subtítulos desde {0} para {1}", - "SubtitlesDownloadedForItem": "Descargar subtítulos para {0}", "Sync": "Sincronizar", "System": "Sistema", "TvShows": "Programas de televisión", @@ -93,5 +92,27 @@ "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}" + "VersionNumber": "Versión {0}", + "TasksMaintenanceCategory": "Mantenimiento", + "TasksLibraryCategory": "Librería", + "TasksApplicationCategory": "Aplicación", + "TasksChannelsCategory": "Canales de internet", + "TaskCleanCache": "Eliminar archivos temporales", + "TaskCleanCacheDescription": "Elimina los archivos temporales que ya no son necesarios para el servidor.", + "TaskRefreshChapterImages": "Extraer imágenes de los capítulos", + "TaskRefreshChapterImagesDescription": "Crea las miniaturas de los vídeos que tengan capítulos.", + "TaskRefreshLibrary": "Escanear la biblioteca", + "TaskRefreshLibraryDescription": "Añade los archivos que se hayan añadido a la biblioteca y actualiza las etiquetas de los ya presentes.", + "TaskCleanLogs": "Limpiar registros", + "TaskCleanLogsDescription": "Elimina los archivos de registro que tengan más de {0} días.", + "TaskRefreshPeople": "Actualizar personas", + "TaskRefreshPeopleDescription": "Actualiza las etiquetas de los intérpretes y directores presentes en tus bibliotecas.", + "TaskUpdatePlugins": "Actualizar extensiones", + "TaskUpdatePluginsDescription": "Actualiza las extensiones que están configuradas para actualizarse automáticamente.", + "TaskCleanTranscode": "Limpiar las transcodificaciones", + "TaskCleanTranscodeDescription": "Elimina los archivos temporales de transcodificación anteriores a un día de antigüedad.", + "TaskRefreshChannels": "Actualizar canales", + "TaskRefreshChannelsDescription": "Actualiza la información de los canales de internet.", + "TaskDownloadMissingSubtitles": "Descargar los subtítulos que faltan", + "TaskDownloadMissingSubtitlesDescription": "Busca en internet los subtítulos que falten en el contenido de tus bibliotecas, basándose en la configuración de los metadatos." } diff --git a/Emby.Server.Implementations/Localization/Core/es_DO.json b/Emby.Server.Implementations/Localization/Core/es_DO.json index 1ac8d3ae4..1a7b57c53 100644 --- a/Emby.Server.Implementations/Localization/Core/es_DO.json +++ b/Emby.Server.Implementations/Localization/Core/es_DO.json @@ -10,5 +10,12 @@ "CameraImageUploadedFrom": "Se ha subido una nueva imagen de cámara desde {0}", "AuthenticationSucceededWithUserName": "{0} autenticado con éxito", "Application": "Aplicación", - "AppDeviceValues": "App: {0}, Dispositivo: {1}" + "AppDeviceValues": "App: {0}, Dispositivo: {1}", + "HeaderContinueWatching": "Continuar Viendo", + "HeaderCameraUploads": "Subidas de Cámara", + "HeaderAlbumArtists": "Artistas del Álbum", + "Genres": "Géneros", + "Folders": "Carpetas", + "Favorites": "Favoritos", + "FailedLoginAttemptWithUserName": "Intento de inicio de sesión fallido de {0}" } diff --git a/Emby.Server.Implementations/Localization/Core/fa.json b/Emby.Server.Implementations/Localization/Core/fa.json index faa658ed5..45e74b8eb 100644 --- a/Emby.Server.Implementations/Localization/Core/fa.json +++ b/Emby.Server.Implementations/Localization/Core/fa.json @@ -1,56 +1,56 @@ { - "Albums": "آلبوم ها", + "Albums": "آلبومها", "AppDeviceValues": "برنامه: {0} ، دستگاه: {1}", "Application": "برنامه", "Artists": "هنرمندان", "AuthenticationSucceededWithUserName": "{0} با موفقیت تایید اعتبار شد", - "Books": "کتاب ها", - "CameraImageUploadedFrom": "یک عکس جدید از دوربین ارسال شده {0}", - "Channels": "کانال ها", - "ChapterNameValue": "فصل {0}", - "Collections": "کلکسیون ها", + "Books": "کتابها", + "CameraImageUploadedFrom": "یک عکس جدید از دوربین ارسال شده است {0}", + "Channels": "کانالها", + "ChapterNameValue": "قسمت {0}", + "Collections": "مجموعهها", "DeviceOfflineWithName": "ارتباط {0} قطع شد", - "DeviceOnlineWithName": "{0} متصل شده", + "DeviceOnlineWithName": "{0} متصل شد", "FailedLoginAttemptWithUserName": "تلاش برای ورود از {0} ناموفق بود", - "Favorites": "مورد علاقه ها", - "Folders": "پوشه ها", + "Favorites": "مورد علاقهها", + "Folders": "پوشهها", "Genres": "ژانرها", "HeaderAlbumArtists": "هنرمندان آلبوم", "HeaderCameraUploads": "آپلودهای دوربین", "HeaderContinueWatching": "ادامه تماشا", - "HeaderFavoriteAlbums": "آلبوم های مورد علاقه", + "HeaderFavoriteAlbums": "آلبومهای مورد علاقه", "HeaderFavoriteArtists": "هنرمندان مورد علاقه", - "HeaderFavoriteEpisodes": "قسمت های مورد علاقه", - "HeaderFavoriteShows": "سریال های مورد علاقه", - "HeaderFavoriteSongs": "آهنگ های مورد علاقه", + "HeaderFavoriteEpisodes": "قسمتهای مورد علاقه", + "HeaderFavoriteShows": "سریالهای مورد علاقه", + "HeaderFavoriteSongs": "آهنگهای مورد علاقه", "HeaderLiveTV": "پخش زنده تلویزیون", - "HeaderNextUp": "بعدی چیه", - "HeaderRecordingGroups": "گروه های ضبط", + "HeaderNextUp": "قسمت بعدی", + "HeaderRecordingGroups": "گروههای ضبط", "HomeVideos": "ویدیوهای خانگی", "Inherit": "به ارث برده", "ItemAddedWithName": "{0} به کتابخانه افزوده شد", "ItemRemovedWithName": "{0} از کتابخانه حذف شد", "LabelIpAddressValue": "آدرس آی پی: {0}", "LabelRunningTimeValue": "زمان اجرا: {0}", - "Latest": "آخرین", + "Latest": "جدیدترینها", "MessageApplicationUpdated": "سرور Jellyfin بروزرسانی شد", - "MessageApplicationUpdatedTo": "سرور جلیفین آپدیت شده به نسخه {0}", + "MessageApplicationUpdatedTo": "سرور Jellyfin به نسخه {0} بروزرسانی شد", "MessageNamedServerConfigurationUpdatedWithValue": "پکربندی بخش {0} سرور بروزرسانی شد", "MessageServerConfigurationUpdated": "پیکربندی سرور بروزرسانی شد", - "MixedContent": "محتوای درهم", - "Movies": "فیلم های سینمایی", + "MixedContent": "محتوای مخلوط", + "Movies": "فیلمها", "Music": "موسیقی", "MusicVideos": "موزیک ویدیوها", - "NameInstallFailed": "{0} نصب با مشکل مواجه شده", + "NameInstallFailed": "{0} نصب با مشکل مواجه شد", "NameSeasonNumber": "فصل {0}", - "NameSeasonUnknown": "فصل های ناشناخته", - "NewVersionIsAvailable": "یک نسخه جدید جلیفین برای بروزرسانی آماده میباشد.", + "NameSeasonUnknown": "فصل ناشناخته", + "NewVersionIsAvailable": "یک نسخه جدید Jellyfin برای بروزرسانی آماده میباشد.", "NotificationOptionApplicationUpdateAvailable": "بروزرسانی برنامه موجود است", "NotificationOptionApplicationUpdateInstalled": "بروزرسانی برنامه نصب شد", "NotificationOptionAudioPlayback": "پخش صدا آغاز شد", "NotificationOptionAudioPlaybackStopped": "پخش صدا متوقف شد", "NotificationOptionCameraImageUploaded": "تصاویر دوربین آپلود شد", - "NotificationOptionInstallationFailed": "شکست نصب", + "NotificationOptionInstallationFailed": "نصب شکست خورد", "NotificationOptionNewLibraryContent": "محتوای جدید افزوده شد", "NotificationOptionPluginError": "خرابی افزونه", "NotificationOptionPluginInstalled": "افزونه نصب شد", @@ -58,40 +58,39 @@ "NotificationOptionPluginUpdateInstalled": "بروزرسانی افزونه نصب شد", "NotificationOptionServerRestartRequired": "شروع مجدد سرور نیاز است", "NotificationOptionTaskFailed": "شکست وظیفه برنامه ریزی شده", - "NotificationOptionUserLockedOut": "کاربر از سیستم خارج شد", + "NotificationOptionUserLockedOut": "کاربر قفل شد", "NotificationOptionVideoPlayback": "پخش ویدیو آغاز شد", "NotificationOptionVideoPlaybackStopped": "پخش ویدیو متوقف شد", - "Photos": "عکس ها", - "Playlists": "لیست های پخش", + "Photos": "عکسها", + "Playlists": "لیستهای پخش", "Plugin": "افزونه", "PluginInstalledWithName": "{0} نصب شد", "PluginUninstalledWithName": "{0} حذف شد", "PluginUpdatedWithName": "{0} آپدیت شد", "ProviderValue": "ارائه دهنده: {0}", - "ScheduledTaskFailedWithName": "{0} ناموفق بود", + "ScheduledTaskFailedWithName": "{0} شکست خورد", "ScheduledTaskStartedWithName": "{0} شروع شد", - "ServerNameNeedsToBeRestarted": "{0} احتیاج به راه اندازی مجدد", - "Shows": "سریال ها", - "Songs": "آهنگ ها", + "ServerNameNeedsToBeRestarted": "{0} نیاز به راه اندازی مجدد دارد", + "Shows": "سریالها", + "Songs": "موسیقیها", "StartupEmbyServerIsLoading": "سرور Jellyfin در حال بارگیری است. لطفا کمی بعد دوباره تلاش کنید.", "SubtitleDownloadFailureForItem": "دانلود زیرنویس برای {0} ناموفق بود", - "SubtitleDownloadFailureFromForItem": "زیرنویس برای دانلود با مشکل مواجه شده از {0} برای {1}", - "SubtitlesDownloadedForItem": "زیرنویس {0} دانلود شد", - "Sync": "همگامسازی", + "SubtitleDownloadFailureFromForItem": "بارگیری زیرنویس برای {1} از {0} شکست خورد", + "Sync": "همگامسازی", "System": "سیستم", - "TvShows": "سریال های تلویزیونی", + "TvShows": "سریالهای تلویزیونی", "User": "کاربر", "UserCreatedWithName": "کاربر {0} ایجاد شد", "UserDeletedWithName": "کاربر {0} حذف شد", - "UserDownloadingItemWithValues": "{0} در حال دانلود است {1}", - "UserLockedOutWithName": "کاربر {0} از سیستم خارج شد", + "UserDownloadingItemWithValues": "{0} در حال بارگیری {1} میباشد", + "UserLockedOutWithName": "کاربر {0} قفل شده است", "UserOfflineFromDevice": "ارتباط {0} از {1} قطع شد", - "UserOnlineFromDevice": "{0}از {1} آنلاین میباشد", - "UserPasswordChangedWithName": "رمز برای کاربر {0} تغییر یافت", + "UserOnlineFromDevice": "{0} از {1} آنلاین میباشد", + "UserPasswordChangedWithName": "گذرواژه برای کاربر {0} تغییر کرد", "UserPolicyUpdatedWithName": "سیاست کاربری برای {0} بروزرسانی شد", - "UserStartedPlayingItemWithValues": "{0} شروع به پخش {1} کرد", - "UserStoppedPlayingItemWithValues": "{0} پخش {1} را متوقف کرد", - "ValueHasBeenAddedToLibrary": "{0} اضافه شده به کتابخانه رسانه شما", - "ValueSpecialEpisodeName": "ویژه- {0}", + "UserStartedPlayingItemWithValues": "{0} در حال پخش {1} بر روی {2} است", + "UserStoppedPlayingItemWithValues": "{0} پخش {1} را بر روی {2} به پایان رساند", + "ValueHasBeenAddedToLibrary": "{0} به کتابخانهی رسانهی شما افزوده شد", + "ValueSpecialEpisodeName": "ویژه - {0}", "VersionNumber": "نسخه {0}" } diff --git a/Emby.Server.Implementations/Localization/Core/fi.json b/Emby.Server.Implementations/Localization/Core/fi.json index d02f841fd..bf5fc05c4 100644 --- a/Emby.Server.Implementations/Localization/Core/fi.json +++ b/Emby.Server.Implementations/Localization/Core/fi.json @@ -17,31 +17,31 @@ "LabelIpAddressValue": "IP-osoite: {0}", "ItemRemovedWithName": "{0} poistettiin kirjastosta", "ItemAddedWithName": "{0} lisättiin kirjastoon", - "Inherit": "Periä", + "Inherit": "Periytyä", "HomeVideos": "Kotivideot", - "HeaderRecordingGroups": "Äänitysryhmät", + "HeaderRecordingGroups": "Nauhoitusryhmät", "HeaderNextUp": "Seuraavaksi", "HeaderFavoriteSongs": "Lempikappaleet", "HeaderFavoriteShows": "Lempisarjat", "HeaderFavoriteEpisodes": "Lempijaksot", - "HeaderCameraUploads": "Kamerasta lähetetyt", + "HeaderCameraUploads": "Kameralataukset", "HeaderFavoriteArtists": "Lempiartistit", "HeaderFavoriteAlbums": "Lempialbumit", "HeaderContinueWatching": "Jatka katsomista", - "HeaderAlbumArtists": "Albumin artistit", - "Genres": "Tyylilaji", + "HeaderAlbumArtists": "Albumin esittäjä", + "Genres": "Tyylilajit", "Folders": "Kansiot", "Favorites": "Suosikit", - "FailedLoginAttemptWithUserName": "Epäonnistunut kirjautumisyritys kohteesta {0}", - "DeviceOnlineWithName": "{0} on yhdistynyt", + "FailedLoginAttemptWithUserName": "Kirjautuminen epäonnistui kohteesta {0}", + "DeviceOnlineWithName": "{0} on yhdistetty", "DeviceOfflineWithName": "{0} on katkaissut yhteytensä", "Collections": "Kokoelmat", "ChapterNameValue": "Luku: {0}", "Channels": "Kanavat", - "CameraImageUploadedFrom": "Uusi kamerakuva on lähetetty kohteesta {0}", + "CameraImageUploadedFrom": "Uusi kamerakuva on ladattu {0}", "Books": "Kirjat", - "AuthenticationSucceededWithUserName": "{0} todennettu onnistuneesti", - "Artists": "Artistit", + "AuthenticationSucceededWithUserName": "{0} todennus onnistui", + "Artists": "Esiintyjät", "Application": "Sovellus", "AppDeviceValues": "Sovellus: {0}, Laite: {1}", "Albums": "Albumit", @@ -69,28 +69,27 @@ "UserCreatedWithName": "Luotiin käyttäjä {0}", "TvShows": "TV-Ohjelmat", "Sync": "Synkronoi", - "SubtitlesDownloadedForItem": "Tekstitys ladattu {0}", "SubtitleDownloadFailureFromForItem": "Tekstityksen lataaminen epäonnistui {0} - {1}", "StartupEmbyServerIsLoading": "Jellyfin palvelin latautuu. Kokeile hetken kuluttua uudelleen.", "Songs": "Kappaleet", "Shows": "Ohjelmat", "ServerNameNeedsToBeRestarted": "{0} vaatii uudelleenkäynnistyksen", "ProviderValue": "Palveluntarjoaja: {0}", - "Plugin": "Laajennus", + "Plugin": "Liitännäinen", "NotificationOptionVideoPlaybackStopped": "Videon toistaminen pysäytetty", "NotificationOptionVideoPlayback": "Videon toistaminen aloitettu", "NotificationOptionUserLockedOut": "Käyttäjä kirjautui ulos", "NotificationOptionTaskFailed": "Ajastetun tehtävän ongelma", "NotificationOptionServerRestartRequired": "Palvelimen uudelleenkäynnistys vaaditaan", - "NotificationOptionPluginUpdateInstalled": "Laajennuksen päivitys asennettu", - "NotificationOptionPluginUninstalled": "Laajennus poistettu", - "NotificationOptionPluginInstalled": "Laajennus asennettu", - "NotificationOptionPluginError": "Ongelma laajennuksessa", - "NotificationOptionNewLibraryContent": "Uusi sisältö lisätty", - "NotificationOptionInstallationFailed": "Asennusvirhe", - "NotificationOptionCameraImageUploaded": "Kameran kuva lisätty", - "NotificationOptionAudioPlaybackStopped": "Äänen toistaminen pysäytetty", - "NotificationOptionAudioPlayback": "Äänen toistaminen aloitettu", + "NotificationOptionPluginUpdateInstalled": "Liitännäinen päivitetty", + "NotificationOptionPluginUninstalled": "Liitännäinen poistettu", + "NotificationOptionPluginInstalled": "Liitännäinen asennettu", + "NotificationOptionPluginError": "Ongelma liitännäisessä", + "NotificationOptionNewLibraryContent": "Uutta sisältöä lisätty", + "NotificationOptionInstallationFailed": "Asennus epäonnistui", + "NotificationOptionCameraImageUploaded": "Kuva ladattu kamerasta", + "NotificationOptionAudioPlaybackStopped": "Audion toisto pysäytetty", + "NotificationOptionAudioPlayback": "Audion toisto aloitettu", "NotificationOptionApplicationUpdateInstalled": "Ohjelmistopäivitys asennettu", "NotificationOptionApplicationUpdateAvailable": "Ohjelmistopäivitys saatavilla" } diff --git a/Emby.Server.Implementations/Localization/Core/fil.json b/Emby.Server.Implementations/Localization/Core/fil.json index 66db059d9..47daf2044 100644 --- a/Emby.Server.Implementations/Localization/Core/fil.json +++ b/Emby.Server.Implementations/Localization/Core/fil.json @@ -16,7 +16,6 @@ "TvShows": "Pelikula", "System": "Sistema", "Sync": "Pag-sync", - "SubtitlesDownloadedForItem": "Naidownload na ang subtitles {0}", "SubtitleDownloadFailureFromForItem": "Hindi naidownload ang subtitles {0} para sa {1}", "StartupEmbyServerIsLoading": "Nagloload ang Jellyfin Server. Sandaling maghintay.", "Songs": "Kanta", @@ -91,5 +90,13 @@ "Artists": "Artista", "Application": "Aplikasyon", "AppDeviceValues": "Aplikasyon: {0}, Aparato: {1}", - "Albums": "Albums" + "Albums": "Albums", + "TaskRefreshLibrary": "Suriin ang nasa librerya", + "TaskRefreshChapterImagesDescription": "Gumawa ng larawan para sa mga pelikula na may kabanata", + "TaskRefreshChapterImages": "Kunin ang mga larawan ng kabanata", + "TaskCleanCacheDescription": "Tanggalin ang mga cache file na hindi na kailangan ng systema.", + "TasksChannelsCategory": "Palabas sa internet", + "TasksLibraryCategory": "Librerya", + "TasksMaintenanceCategory": "Pagpapanatili", + "HomeVideos": "Sariling pelikula" } diff --git a/Emby.Server.Implementations/Localization/Core/fr-CA.json b/Emby.Server.Implementations/Localization/Core/fr-CA.json index 4b4db39a8..2c9dae6a1 100644 --- a/Emby.Server.Implementations/Localization/Core/fr-CA.json +++ b/Emby.Server.Implementations/Localization/Core/fr-CA.json @@ -76,7 +76,6 @@ "StartupEmbyServerIsLoading": "Le serveur Jellyfin est en cours de chargement. Veuillez réessayer dans quelques instants.", "SubtitleDownloadFailureForItem": "Subtitles failed to download for {0}", "SubtitleDownloadFailureFromForItem": "Échec du téléchargement des sous-titres depuis {0} pour {1}", - "SubtitlesDownloadedForItem": "Les sous-titres de {0} ont été téléchargés", "Sync": "Synchroniser", "System": "Système", "TvShows": "Séries Télé", @@ -93,5 +92,7 @@ "UserStoppedPlayingItemWithValues": "{0} vient d'arrêter la lecture de {1} sur {2}", "ValueHasBeenAddedToLibrary": "{0} a été ajouté à votre médiathèque", "ValueSpecialEpisodeName": "Spécial - {0}", - "VersionNumber": "Version {0}" + "VersionNumber": "Version {0}", + "TasksLibraryCategory": "Bibliothèque", + "TasksMaintenanceCategory": "Entretien" } diff --git a/Emby.Server.Implementations/Localization/Core/fr.json b/Emby.Server.Implementations/Localization/Core/fr.json index 90754e415..88a7ac190 100644 --- a/Emby.Server.Implementations/Localization/Core/fr.json +++ b/Emby.Server.Implementations/Localization/Core/fr.json @@ -3,7 +3,7 @@ "AppDeviceValues": "Application : {0}, Appareil : {1}", "Application": "Application", "Artists": "Artistes", - "AuthenticationSucceededWithUserName": "{0} s'est authentifié avec succès", + "AuthenticationSucceededWithUserName": "{0} authentifié avec succès", "Books": "Livres", "CameraImageUploadedFrom": "Une nouvelle photo a été chargée depuis {0}", "Channels": "Chaînes", @@ -76,7 +76,6 @@ "StartupEmbyServerIsLoading": "Le serveur Jellyfin est en cours de chargement. Veuillez réessayer dans quelques instants.", "SubtitleDownloadFailureForItem": "Le téléchargement des sous-titres pour {0} a échoué.", "SubtitleDownloadFailureFromForItem": "Échec du téléchargement des sous-titres depuis {0} pour {1}", - "SubtitlesDownloadedForItem": "Les sous-titres de {0} ont été téléchargés", "Sync": "Synchroniser", "System": "Système", "TvShows": "Séries Télé", @@ -93,5 +92,27 @@ "UserStoppedPlayingItemWithValues": "{0} vient d'arrêter la lecture de {1} sur {2}", "ValueHasBeenAddedToLibrary": "{0} a été ajouté à votre médiathèque", "ValueSpecialEpisodeName": "Spécial - {0}", - "VersionNumber": "Version {0}" + "VersionNumber": "Version {0}", + "TasksChannelsCategory": "Chaines en ligne", + "TaskDownloadMissingSubtitlesDescription": "Cherche les sous-titres manquant sur internet en se basant sur la configuration des métadonnées.", + "TaskDownloadMissingSubtitles": "Télécharge les sous-titres manquant", + "TaskRefreshChannelsDescription": "Rafraîchit les informations des chaines en ligne.", + "TaskRefreshChannels": "Rafraîchit les chaines", + "TaskCleanTranscodeDescription": "Supprime les fichiers transcodés de plus d'un jour.", + "TaskCleanTranscode": "Nettoie les dossier des transcodages", + "TaskUpdatePluginsDescription": "Télécharge et installe les mises à jours des plugins configurés pour être mis à jour automatiquement.", + "TaskUpdatePlugins": "Mettre à jour les plugins", + "TaskRefreshPeopleDescription": "Met à jour les métadonnées pour les acteurs et directeurs dans votre bibliothèque.", + "TaskRefreshPeople": "Rafraîchit les acteurs", + "TaskCleanLogsDescription": "Supprime les journaux de plus de {0} jours.", + "TaskCleanLogs": "Nettoie le répertoire des journaux", + "TaskRefreshLibraryDescription": "Scanne toute les bibliothèques pour trouver les nouveaux fichiers et rafraîchit les métadonnées.", + "TaskRefreshLibrary": "Scanne toute les Bibliothèques", + "TaskRefreshChapterImagesDescription": "Crée des images de miniature pour les vidéos ayant des chapitres.", + "TaskRefreshChapterImages": "Extrait les images de chapitre", + "TaskCleanCacheDescription": "Supprime les fichiers de cache dont le système n'a plus besoin.", + "TaskCleanCache": "Vider le répertoire cache", + "TasksApplicationCategory": "Application", + "TasksLibraryCategory": "Bibliothèque", + "TasksMaintenanceCategory": "Maintenance" } diff --git a/Emby.Server.Implementations/Localization/Core/gsw.json b/Emby.Server.Implementations/Localization/Core/gsw.json index 69c157401..9611e33f5 100644 --- a/Emby.Server.Implementations/Localization/Core/gsw.json +++ b/Emby.Server.Implementations/Localization/Core/gsw.json @@ -76,7 +76,6 @@ "StartupEmbyServerIsLoading": "Jellyfin Server ladt. Bitte grad noeinisch probiere.", "SubtitleDownloadFailureForItem": "Subtitles failed to download for {0}", "SubtitleDownloadFailureFromForItem": "Ondertetle vo {0} för {1} hend ned chönne abeglade wärde", - "SubtitlesDownloadedForItem": "Ondertetle abeglade för {0}", "Sync": "Synchronisation", "System": "System", "TvShows": "Färnsehserie", diff --git a/Emby.Server.Implementations/Localization/Core/he.json b/Emby.Server.Implementations/Localization/Core/he.json index b08c8966e..1ce8b08a0 100644 --- a/Emby.Server.Implementations/Localization/Core/he.json +++ b/Emby.Server.Implementations/Localization/Core/he.json @@ -2,33 +2,33 @@ "Albums": "אלבומים", "AppDeviceValues": "יישום: {0}, מכשיר: {1}", "Application": "אפליקציה", - "Artists": "אמנים", - "AuthenticationSucceededWithUserName": "{0} זוהה בהצלחה", + "Artists": "אומנים", + "AuthenticationSucceededWithUserName": "{0} אומת בהצלחה", "Books": "ספרים", - "CameraImageUploadedFrom": "תמונה חדשה הועלתה מ{0}", + "CameraImageUploadedFrom": "תמונת מצלמה חדשה הועלתה מ {0}", "Channels": "ערוצים", "ChapterNameValue": "פרק {0}", - "Collections": "קולקציות", + "Collections": "אוספים", "DeviceOfflineWithName": "{0} התנתק", "DeviceOnlineWithName": "{0} מחובר", "FailedLoginAttemptWithUserName": "ניסיון כניסה שגוי מ{0}", - "Favorites": "אהובים", + "Favorites": "מועדפים", "Folders": "תיקיות", "Genres": "ז'אנרים", "HeaderAlbumArtists": "אמני האלבום", "HeaderCameraUploads": "העלאות ממצלמה", "HeaderContinueWatching": "המשך לצפות", "HeaderFavoriteAlbums": "אלבומים שאהבתי", - "HeaderFavoriteArtists": "אמנים שאהבתי", - "HeaderFavoriteEpisodes": "פרקים אהובים", - "HeaderFavoriteShows": "תוכניות אהובות", - "HeaderFavoriteSongs": "שירים שאהבתי", - "HeaderLiveTV": "טלוויזיה בשידור חי", + "HeaderFavoriteArtists": "אמנים מועדפים", + "HeaderFavoriteEpisodes": "פרקים מועדפים", + "HeaderFavoriteShows": "סדרות מועדפות", + "HeaderFavoriteSongs": "שירים מועדפים", + "HeaderLiveTV": "שידורים חיים", "HeaderNextUp": "הבא", "HeaderRecordingGroups": "קבוצות הקלטה", "HomeVideos": "סרטונים בייתים", "Inherit": "הורש", - "ItemAddedWithName": "{0} was added to the library", + "ItemAddedWithName": "{0} הוסף לספרייה", "ItemRemovedWithName": "{0} נמחק מהספרייה", "LabelIpAddressValue": "Ip כתובת: {0}", "LabelRunningTimeValue": "משך צפייה: {0}", @@ -36,15 +36,15 @@ "MessageApplicationUpdated": "שרת הJellyfin עודכן", "MessageApplicationUpdatedTo": "שרת הJellyfin עודכן לגרסא {0}", "MessageNamedServerConfigurationUpdatedWithValue": "הגדרת השרת {0} שונתה", - "MessageServerConfigurationUpdated": "Server configuration has been updated", + "MessageServerConfigurationUpdated": "תצורת השרת עודכנה", "MixedContent": "תוכן מעורב", "Movies": "סרטים", "Music": "מוזיקה", - "MusicVideos": "Music videos", - "NameInstallFailed": "{0} installation failed", - "NameSeasonNumber": "Season {0}", - "NameSeasonUnknown": "Season Unknown", - "NewVersionIsAvailable": "A new version of Jellyfin Server is available for download.", + "MusicVideos": "קליפים", + "NameInstallFailed": "התקנת {0} נכשלה", + "NameSeasonNumber": "עונה {0}", + "NameSeasonUnknown": "עונה לא ידועה", + "NewVersionIsAvailable": "גרסה חדשה של שרת Jellyfin זמינה להורדה.", "NotificationOptionApplicationUpdateAvailable": "Application update available", "NotificationOptionApplicationUpdateInstalled": "Application update installed", "NotificationOptionAudioPlayback": "Audio playback started", @@ -53,10 +53,10 @@ "NotificationOptionInstallationFailed": "התקנה נכשלה", "NotificationOptionNewLibraryContent": "New content added", "NotificationOptionPluginError": "Plugin failure", - "NotificationOptionPluginInstalled": "Plugin installed", - "NotificationOptionPluginUninstalled": "Plugin uninstalled", - "NotificationOptionPluginUpdateInstalled": "Plugin update installed", - "NotificationOptionServerRestartRequired": "Server restart required", + "NotificationOptionPluginInstalled": "התוסף הותקן", + "NotificationOptionPluginUninstalled": "התוסף הוסר", + "NotificationOptionPluginUpdateInstalled": "העדכון לתוסף הותקן", + "NotificationOptionServerRestartRequired": "יש לאתחל את השרת", "NotificationOptionTaskFailed": "Scheduled task failure", "NotificationOptionUserLockedOut": "User locked out", "NotificationOptionVideoPlayback": "Video playback started", @@ -71,26 +71,25 @@ "ScheduledTaskFailedWithName": "{0} failed", "ScheduledTaskStartedWithName": "{0} started", "ServerNameNeedsToBeRestarted": "{0} needs to be restarted", - "Shows": "Shows", - "Songs": "Songs", - "StartupEmbyServerIsLoading": "Jellyfin Server is loading. Please try again shortly.", + "Shows": "סדרות", + "Songs": "שירים", + "StartupEmbyServerIsLoading": "שרת Jellyfin בהליכי טעינה. אנא נסה שנית בעוד זמן קצר.", "SubtitleDownloadFailureForItem": "Subtitles failed to download for {0}", "SubtitleDownloadFailureFromForItem": "Subtitles failed to download from {0} for {1}", - "SubtitlesDownloadedForItem": "Subtitles downloaded for {0}", "Sync": "סנכרן", "System": "System", - "TvShows": "TV Shows", + "TvShows": "סדרות טלוויזיה", "User": "User", - "UserCreatedWithName": "User {0} has been created", - "UserDeletedWithName": "User {0} has been deleted", - "UserDownloadingItemWithValues": "{0} is downloading {1}", + "UserCreatedWithName": "המשתמש {0} נוצר", + "UserDeletedWithName": "המשתמש {0} הוסר", + "UserDownloadingItemWithValues": "{0} מוריד את {1}", "UserLockedOutWithName": "User {0} has been locked out", "UserOfflineFromDevice": "{0} has disconnected from {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} is playing {1} on {2}", - "UserStoppedPlayingItemWithValues": "{0} has finished playing {1} on {2}", + "UserStartedPlayingItemWithValues": "{0} מנגן את {1} על {2}", + "UserStoppedPlayingItemWithValues": "{0} סיים לנגן את {1} על {2}", "ValueHasBeenAddedToLibrary": "{0} has been added to your media library", "ValueSpecialEpisodeName": "מיוחד- {0}", "VersionNumber": "Version {0}" diff --git a/Emby.Server.Implementations/Localization/Core/hr.json b/Emby.Server.Implementations/Localization/Core/hr.json index f284b3cd9..6947178d7 100644 --- a/Emby.Server.Implementations/Localization/Core/hr.json +++ b/Emby.Server.Implementations/Localization/Core/hr.json @@ -76,7 +76,6 @@ "StartupEmbyServerIsLoading": "Jellyfin Server se učitava. Pokušajte ponovo kasnije.", "SubtitleDownloadFailureForItem": "Titlovi prijevoda nisu preuzeti za {0}", "SubtitleDownloadFailureFromForItem": "Subtitles failed to download from {0} for {1}", - "SubtitlesDownloadedForItem": "Titlovi prijevoda preuzeti za {0}", "Sync": "Sink.", "System": "Sistem", "TvShows": "TV Shows", diff --git a/Emby.Server.Implementations/Localization/Core/hu.json b/Emby.Server.Implementations/Localization/Core/hu.json index 950be68d0..6f226fe99 100644 --- a/Emby.Server.Implementations/Localization/Core/hu.json +++ b/Emby.Server.Implementations/Localization/Core/hu.json @@ -7,11 +7,11 @@ "Books": "Könyvek", "CameraImageUploadedFrom": "Új kamerakép került feltöltésre innen: {0}", "Channels": "Csatornák", - "ChapterNameValue": "Jelenet {0}", + "ChapterNameValue": "{0}. jelenet", "Collections": "Gyűjtemények", "DeviceOfflineWithName": "{0} kijelentkezett", "DeviceOnlineWithName": "{0} belépett", - "FailedLoginAttemptWithUserName": "Sikertelen bejelentkezési kísérlet {0}", + "FailedLoginAttemptWithUserName": "Sikertelen bejelentkezési kísérlet tőle: {0}", "Favorites": "Kedvencek", "Folders": "Könyvtárak", "Genres": "Műfajok", @@ -27,7 +27,7 @@ "HeaderNextUp": "Következik", "HeaderRecordingGroups": "Felvételi csoportok", "HomeVideos": "Házi videók", - "Inherit": "Öröklés", + "Inherit": "Örökölt", "ItemAddedWithName": "{0} hozzáadva a könyvtárhoz", "ItemRemovedWithName": "{0} eltávolítva a könyvtárból", "LabelIpAddressValue": "IP cím: {0}", @@ -73,10 +73,9 @@ "ServerNameNeedsToBeRestarted": "{0}-t újra kell indítani", "Shows": "Műsorok", "Songs": "Dalok", - "StartupEmbyServerIsLoading": "A Jellyfin Szerver betöltődik. Kérlek próbáld újra később.", + "StartupEmbyServerIsLoading": "A Jellyfin Szerver betöltődik. Kérlek, próbáld újra hamarosan.", "SubtitleDownloadFailureForItem": "Subtitles failed to download 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", @@ -86,12 +85,34 @@ "UserDownloadingItemWithValues": "{0} letölti {1}", "UserLockedOutWithName": "{0} felhasználó zárolva van", "UserOfflineFromDevice": "{0} kijelentkezett innen: {1}", - "UserOnlineFromDevice": "{0} online itt: {1}", + "UserOnlineFromDevice": "{0} online innen: {1}", "UserPasswordChangedWithName": "Jelszó megváltozott a következő felhasználó számára: {0}", "UserPolicyUpdatedWithName": "A felhasználói házirend frissítve lett neki: {0}", "UserStartedPlayingItemWithValues": "{0} elkezdte játszani a következőt: {1} itt: {2}", - "UserStoppedPlayingItemWithValues": "{0} befejezte a következőt: {1} itt: {2}", + "UserStoppedPlayingItemWithValues": "{0} befejezte {1} lejátászását itt: {2}", "ValueHasBeenAddedToLibrary": "{0} hozzáadva a médiatárhoz", "ValueSpecialEpisodeName": "Special - {0}", - "VersionNumber": "Verzió: {0}" + "VersionNumber": "Verzió: {0}", + "TaskCleanTranscode": "Átkódolási könyvtár ürítése", + "TaskUpdatePluginsDescription": "Letölti és telepíti a frissítéseket azokhoz a bővítményekhez, amelyeknél az automatikus frissítés engedélyezve van.", + "TaskUpdatePlugins": "Bővítmények frissítése", + "TaskRefreshPeopleDescription": "Frissíti a szereplők és a stábok metaadatait a könyvtáradban.", + "TaskRefreshPeople": "Személyek frissítése", + "TaskCleanLogsDescription": "Törli azokat a naplófájlokat, amelyek {0} napnál régebbiek.", + "TaskCleanLogs": "Naplózási könyvtár ürítése", + "TaskRefreshLibraryDescription": "Átvizsgálja a könyvtáraidat új fájlokért és frissíti a metaadatokat.", + "TaskRefreshLibrary": "Média könyvtár beolvasása", + "TaskRefreshChapterImagesDescription": "Miniatűröket generál olyan videókhoz, amely tartalmaz fejezeteket.", + "TaskRefreshChapterImages": "Fejezetek képeinek generálása", + "TaskCleanCacheDescription": "Törli azokat a gyorsítótárazott fájlokat, amikre a rendszernek már nincs szüksége.", + "TaskCleanCache": "Gyorsítótár könyvtárának ürítése", + "TasksChannelsCategory": "Internetes csatornák", + "TasksApplicationCategory": "Alkalmazás", + "TasksLibraryCategory": "Könyvtár", + "TasksMaintenanceCategory": "Karbantartás", + "TaskDownloadMissingSubtitlesDescription": "A metaadat konfiguráció alapján ellenőrzi és letölti a hiányzó feliratokat az internetről.", + "TaskDownloadMissingSubtitles": "Hiányzó feliratok letöltése", + "TaskRefreshChannelsDescription": "Frissíti az internetes csatornák adatait.", + "TaskRefreshChannels": "Csatornák frissítése", + "TaskCleanTranscodeDescription": "Törli az egy napnál régebbi átkódolási fájlokat." } diff --git a/Emby.Server.Implementations/Localization/Core/id.json b/Emby.Server.Implementations/Localization/Core/id.json index 38c6cf54f..eabdb9138 100644 --- a/Emby.Server.Implementations/Localization/Core/id.json +++ b/Emby.Server.Implementations/Localization/Core/id.json @@ -54,7 +54,6 @@ "User": "Pengguna", "System": "Sistem", "Sync": "Sinkron", - "SubtitlesDownloadedForItem": "Talop telah diunduh untuk {0}", "Shows": "Tayangan", "ServerNameNeedsToBeRestarted": "{0} perlu dimuat ulang", "ScheduledTaskStartedWithName": "{0} dimulai", @@ -91,5 +90,6 @@ "NotificationOptionVideoPlayback": "Pemutaran video dimulai", "NotificationOptionAudioPlaybackStopped": "Pemutaran audio berhenti", "NotificationOptionAudioPlayback": "Pemutaran audio dimulai", - "MixedContent": "Konten campur" + "MixedContent": "Konten campur", + "PluginUninstalledWithName": "{0} telah dihapus" } diff --git a/Emby.Server.Implementations/Localization/Core/is.json b/Emby.Server.Implementations/Localization/Core/is.json index c3b5211b8..ef2a57e8e 100644 --- a/Emby.Server.Implementations/Localization/Core/is.json +++ b/Emby.Server.Implementations/Localization/Core/is.json @@ -3,7 +3,7 @@ "ItemRemovedWithName": "{0} var fjarlægt úr safninu", "ItemAddedWithName": "{0} var bætt í safnið", "Inherit": "Erfa", - "HomeVideos": "Myndbönd að heiman", + "HomeVideos": "Heimamyndbönd", "HeaderRecordingGroups": "Upptökuhópar", "HeaderNextUp": "Næst á dagskrá", "HeaderLiveTV": "Sjónvarp í beinni útsendingu", @@ -36,10 +36,10 @@ "NotificationOptionVideoPlaybackStopped": "Myndbandafspilun stöðvuð", "NotificationOptionVideoPlayback": "Myndbandafspilun hafin", "NotificationOptionUserLockedOut": "Notandi læstur úti", - "NotificationOptionServerRestartRequired": "Endurræsing miðlara nauðsynileg", + "NotificationOptionServerRestartRequired": "Endurræsing þjóns er nauðsynileg", "NotificationOptionPluginUpdateInstalled": "Viðbótar uppfærsla uppsett", "NotificationOptionPluginUninstalled": "Viðbót fjarlægð", - "NotificationOptionPluginInstalled": "Viðbót settur upp", + "NotificationOptionPluginInstalled": "Viðbót sett upp", "NotificationOptionPluginError": "Bilun í viðbót", "NotificationOptionInstallationFailed": "Uppsetning tókst ekki", "NotificationOptionCameraImageUploaded": "Myndavélarmynd hlaðið upp", @@ -50,15 +50,15 @@ "NameSeasonUnknown": "Sería óþekkt", "NameSeasonNumber": "Sería {0}", "MixedContent": "Blandað efni", - "MessageServerConfigurationUpdated": "Stillingar miðlarans hefur verið uppfærð", - "MessageApplicationUpdatedTo": "Jellyfin Server hefur verið uppfærður í {0}", - "MessageApplicationUpdated": "Jellyfin Server hefur verið uppfærður", + "MessageServerConfigurationUpdated": "Stillingar þjóns hafa verið uppfærðar", + "MessageApplicationUpdatedTo": "Jellyfin þjónn hefur verið uppfærður í {0}", + "MessageApplicationUpdated": "Jellyfin þjónn hefur verið uppfærður", "Latest": "Nýjasta", - "LabelRunningTimeValue": "Keyrslutími kerfis: {0}", + "LabelRunningTimeValue": "spilunartími: {0}", "User": "Notandi", "System": "Kerfi", "NotificationOptionNewLibraryContent": "Nýju efni bætt við", - "NewVersionIsAvailable": "Ný útgáfa af Jellyfin Server er fáanleg til niðurhals.", + "NewVersionIsAvailable": "Ný útgáfa af Jellyfin þjón er fáanleg til niðurhals.", "NameInstallFailed": "{0} uppsetning mistókst", "MusicVideos": "Tónlistarmyndbönd", "Music": "Tónlist", @@ -74,5 +74,22 @@ "PluginUpdatedWithName": "{0} var uppfært", "PluginUninstalledWithName": "{0} var fjarlægt", "PluginInstalledWithName": "{0} var sett upp", - "NotificationOptionTaskFailed": "Tímasett verkefni mistókst" + "NotificationOptionTaskFailed": "Tímasett verkefni mistókst", + "StartupEmbyServerIsLoading": "Jellyfin netþjónnin er að hlaðast. Vinsamlega prufaðu aftur fljótlega.", + "VersionNumber": "Útgáfa {0}", + "ValueHasBeenAddedToLibrary": "{0} hefur verið bætt við í gagnasafnið þitt", + "UserStoppedPlayingItemWithValues": "{0} hefur lokið spilunar af {1} á {2}", + "UserStartedPlayingItemWithValues": "{0} er að spila {1} á {2}", + "UserPolicyUpdatedWithName": "Notandaregla hefur verið uppfærð fyrir notanda {0}", + "UserPasswordChangedWithName": "Lykilorði fyrir notandann {0} hefur verið breytt", + "UserOnlineFromDevice": "{0} hefur verið virkur síðan {1}", + "UserOfflineFromDevice": "{0} hefur aftengst frá {1}", + "UserLockedOutWithName": "Notanda {0} hefur verið hindraður aðgangur", + "UserDownloadingItemWithValues": "{0} Hleður niður {1}", + "SubtitleDownloadFailureFromForItem": "Tókst ekki að hala niður skjátextum frá {0} til {1}", + "ProviderValue": "Veitandi: {0}", + "MessageNamedServerConfigurationUpdatedWithValue": "Stilling {0} hefur verið uppfærð á netþjón", + "ValueSpecialEpisodeName": "Sérstakt - {0}", + "Shows": "Þættir", + "Playlists": "Spilunarlisti" } diff --git a/Emby.Server.Implementations/Localization/Core/it.json b/Emby.Server.Implementations/Localization/Core/it.json index c1c40c18e..0758bbe9c 100644 --- a/Emby.Server.Implementations/Localization/Core/it.json +++ b/Emby.Server.Implementations/Localization/Core/it.json @@ -5,7 +5,7 @@ "Artists": "Artisti", "AuthenticationSucceededWithUserName": "{0} autenticato con successo", "Books": "Libri", - "CameraImageUploadedFrom": "È stata caricata una nuova immagine della fotocamera {0}", + "CameraImageUploadedFrom": "È stata caricata una nuova immagine della fotocamera dal device {0}", "Channels": "Canali", "ChapterNameValue": "Capitolo {0}", "Collections": "Collezioni", @@ -15,10 +15,10 @@ "Favorites": "Preferiti", "Folders": "Cartelle", "Genres": "Generi", - "HeaderAlbumArtists": "Artisti dell' Album", + "HeaderAlbumArtists": "Artisti degli Album", "HeaderCameraUploads": "Caricamenti Fotocamera", "HeaderContinueWatching": "Continua a guardare", - "HeaderFavoriteAlbums": "Album preferiti", + "HeaderFavoriteAlbums": "Album Preferiti", "HeaderFavoriteArtists": "Artisti Preferiti", "HeaderFavoriteEpisodes": "Episodi Preferiti", "HeaderFavoriteShows": "Serie TV Preferite", @@ -76,7 +76,6 @@ "StartupEmbyServerIsLoading": "Jellyfin server si sta avviando. Per favore riprova più tardi.", "SubtitleDownloadFailureForItem": "Impossibile scaricare i sottotitoli per {0}", "SubtitleDownloadFailureFromForItem": "Impossibile scaricare i sottotitoli da {0} per {1}", - "SubtitlesDownloadedForItem": "Sottotitoli scaricati per {0}", "Sync": "Sincronizza", "System": "Sistema", "TvShows": "Serie TV", @@ -93,5 +92,27 @@ "UserStoppedPlayingItemWithValues": "{0} ha interrotto la riproduzione di {1} su {2}", "ValueHasBeenAddedToLibrary": "{0} è stato aggiunto alla tua libreria multimediale", "ValueSpecialEpisodeName": "Speciale - {0}", - "VersionNumber": "Versione {0}" + "VersionNumber": "Versione {0}", + "TaskRefreshChannelsDescription": "Aggiorna le informazioni dei canali Internet.", + "TaskDownloadMissingSubtitlesDescription": "Cerca su internet i sottotitoli mancanti basandosi sulle configurazioni dei metadati.", + "TaskDownloadMissingSubtitles": "Scarica i sottotitoli mancanti", + "TaskRefreshChannels": "Aggiorna i canali", + "TaskCleanTranscodeDescription": "Cancella i file di transcode più vecchi di un giorno.", + "TaskCleanTranscode": "Svuota la cartella del transcoding", + "TaskUpdatePluginsDescription": "Scarica e installa gli aggiornamenti per i plugin che sono stati configurati per essere aggiornati contemporaneamente.", + "TaskUpdatePlugins": "Aggiorna i Plugin", + "TaskRefreshPeopleDescription": "Aggiorna i metadati per gli attori e registi nella tua libreria multimediale.", + "TaskRefreshPeople": "Aggiorna persone", + "TaskCleanLogsDescription": "Rimuovi i file di log più vecchi di {0} giorni.", + "TaskCleanLogs": "Pulisci la cartella dei log", + "TaskRefreshLibraryDescription": "Analizza la tua libreria multimediale per nuovi file e rinnova i metadati.", + "TaskRefreshLibrary": "Analizza la libreria dei contenuti multimediali", + "TaskRefreshChapterImagesDescription": "Crea le thumbnail per i video che hanno capitoli.", + "TaskRefreshChapterImages": "Estrai immagini capitolo", + "TaskCleanCacheDescription": "Cancella i file di cache non più necessari al sistema.", + "TaskCleanCache": "Pulisci la directory della cache", + "TasksChannelsCategory": "Canali su Internet", + "TasksApplicationCategory": "Applicazione", + "TasksLibraryCategory": "Libreria", + "TasksMaintenanceCategory": "Manutenzione" } diff --git a/Emby.Server.Implementations/Localization/Core/ja.json b/Emby.Server.Implementations/Localization/Core/ja.json index 4aa0637c5..d0daed7a3 100644 --- a/Emby.Server.Implementations/Localization/Core/ja.json +++ b/Emby.Server.Implementations/Localization/Core/ja.json @@ -75,7 +75,6 @@ "Songs": "曲", "StartupEmbyServerIsLoading": "Jellyfin Server は現在読み込み中です。しばらくしてからもう一度お試しください。", "SubtitleDownloadFailureFromForItem": "{0} から {1}の字幕のダウンロードに失敗しました", - "SubtitlesDownloadedForItem": "{0} の字幕がダウンロードされました", "Sync": "同期", "System": "システム", "TvShows": "テレビ番組", @@ -92,5 +91,23 @@ "UserStoppedPlayingItemWithValues": "{0} は{2}で{1} の再生が終わりました", "ValueHasBeenAddedToLibrary": "{0}はあなたのメディアライブラリに追加されました", "ValueSpecialEpisodeName": "スペシャル - {0}", - "VersionNumber": "バージョン {0}" + "VersionNumber": "バージョン {0}", + "TaskCleanLogsDescription": "{0} 日以上前のログを消去します。", + "TaskCleanLogs": "ログの掃除", + "TaskRefreshLibraryDescription": "メディアライブラリをスキャンして新しいファイルを探し、メタデータをリフレッシュします。", + "TaskRefreshLibrary": "メディアライブラリのスキャン", + "TaskCleanCacheDescription": "不要なキャッシュを消去します。", + "TaskCleanCache": "キャッシュの掃除", + "TasksChannelsCategory": "ネットチャンネル", + "TasksApplicationCategory": "アプリケーション", + "TasksLibraryCategory": "ライブラリ", + "TasksMaintenanceCategory": "メンテナンス", + "TaskRefreshChannelsDescription": "ネットチャンネルの情報をリフレッシュします。", + "TaskRefreshChannels": "チャンネルのリフレッシュ", + "TaskCleanTranscodeDescription": "一日以上前のトランスコードを消去します。", + "TaskCleanTranscode": "トランスコード用のディレクトリの掃除", + "TaskUpdatePluginsDescription": "自動更新可能なプラグインのアップデートをダウンロードしてインストールします。", + "TaskUpdatePlugins": "プラグインの更新", + "TaskRefreshPeopleDescription": "メディアライブラリで俳優や監督のメタデータをリフレッシュします。", + "TaskRefreshPeople": "俳優や監督のデータのリフレッシュ" } diff --git a/Emby.Server.Implementations/Localization/Core/kk.json b/Emby.Server.Implementations/Localization/Core/kk.json index cbee71155..5618ff4a8 100644 --- a/Emby.Server.Implementations/Localization/Core/kk.json +++ b/Emby.Server.Implementations/Localization/Core/kk.json @@ -76,7 +76,6 @@ "StartupEmbyServerIsLoading": "Jellyfin Server júktelýde. Áreketti kóp uzamaı qaıtalańyz.", "SubtitleDownloadFailureForItem": "Субтитрлер {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", diff --git a/Emby.Server.Implementations/Localization/Core/ko.json b/Emby.Server.Implementations/Localization/Core/ko.json index 0320a0a1b..9e3ecd5a8 100644 --- a/Emby.Server.Implementations/Localization/Core/ko.json +++ b/Emby.Server.Implementations/Localization/Core/ko.json @@ -76,7 +76,6 @@ "StartupEmbyServerIsLoading": "Jellyfin 서버를 불러오고 있습니다. 잠시 후에 다시 시도하십시오.", "SubtitleDownloadFailureForItem": "Subtitles failed to download for {0}", "SubtitleDownloadFailureFromForItem": "{0}에서 {1} 자막 다운로드에 실패했습니다", - "SubtitlesDownloadedForItem": "{0} 자막 다운로드 완료", "Sync": "동기화", "System": "시스템", "TvShows": "TV 쇼", @@ -93,5 +92,27 @@ "UserStoppedPlayingItemWithValues": "{2}에서 {0}이 {1} 재생을 마침", "ValueHasBeenAddedToLibrary": "{0}가 미디어 라이브러리에 추가되었습니다", "ValueSpecialEpisodeName": "스페셜 - {0}", - "VersionNumber": "버전 {0}" + "VersionNumber": "버전 {0}", + "TasksApplicationCategory": "어플리케이션", + "TasksMaintenanceCategory": "유지 보수", + "TaskDownloadMissingSubtitlesDescription": "메타 데이터 기반으로 누락 된 자막이 있는지 인터넷을 검색합니다.", + "TaskDownloadMissingSubtitles": "누락 된 자막 다운로드", + "TaskRefreshChannelsDescription": "인터넷 채널 정보를 새로 고칩니다.", + "TaskRefreshChannels": "채널 새로고침", + "TaskCleanTranscodeDescription": "하루 이상 지난 트랜스 코드 파일을 삭제합니다.", + "TaskCleanTranscode": "트랜스코드 폴더 청소", + "TaskUpdatePluginsDescription": "자동으로 업데이트되도록 구성된 플러그인 업데이트를 다운로드하여 설치합니다.", + "TaskUpdatePlugins": "플러그인 업데이트", + "TaskRefreshPeopleDescription": "미디어 라이브러리에서 배우 및 감독의 메타 데이터를 업데이트합니다.", + "TaskRefreshPeople": "인물 새로고침", + "TaskCleanLogsDescription": "{0} 일이 지난 로그 파일을 삭제합니다.", + "TaskCleanLogs": "로그 폴더 청소", + "TaskRefreshLibraryDescription": "미디어 라이브러리에서 새 파일을 검색하고 메타 데이터를 새로 고칩니다.", + "TaskRefreshLibrary": "미디어 라이브러리 스캔", + "TaskRefreshChapterImagesDescription": "챕터가있는 비디오의 썸네일을 만듭니다.", + "TaskRefreshChapterImages": "챕터 이미지 추출", + "TaskCleanCacheDescription": "시스템에서 더 이상 필요하지 않은 캐시 파일을 삭제합니다.", + "TaskCleanCache": "캐시 폴더 청소", + "TasksChannelsCategory": "인터넷 채널", + "TasksLibraryCategory": "라이브러리" } diff --git a/Emby.Server.Implementations/Localization/Core/lt-LT.json b/Emby.Server.Implementations/Localization/Core/lt-LT.json index e8e1b7740..01a740187 100644 --- a/Emby.Server.Implementations/Localization/Core/lt-LT.json +++ b/Emby.Server.Implementations/Localization/Core/lt-LT.json @@ -76,7 +76,6 @@ "StartupEmbyServerIsLoading": "Jellyfin Server kraunasi. Netrukus pabandykite dar kartą.", "SubtitleDownloadFailureForItem": "Subtitles failed to download for {0}", "SubtitleDownloadFailureFromForItem": "{1} subtitrai buvo nesėkmingai parsiųsti iš {0}", - "SubtitlesDownloadedForItem": "{0} subtitrai parsiųsti", "Sync": "Sinchronizuoti", "System": "System", "TvShows": "TV Serialai", diff --git a/Emby.Server.Implementations/Localization/Core/lv.json b/Emby.Server.Implementations/Localization/Core/lv.json new file mode 100644 index 000000000..dbcf17287 --- /dev/null +++ b/Emby.Server.Implementations/Localization/Core/lv.json @@ -0,0 +1,117 @@ +{ + "ServerNameNeedsToBeRestarted": "{0} ir vajadzīgs restarts", + "NotificationOptionTaskFailed": "Plānota uzdevuma kļūme", + "HeaderRecordingGroups": "Ierakstu Grupas", + "UserPolicyUpdatedWithName": "Lietotāju politika atjaunota priekš {0}", + "SubtitleDownloadFailureFromForItem": "Subtitru lejupielāde no {0} priekš {1} neizdevās", + "NotificationOptionVideoPlaybackStopped": "Video atskaņošana apturēta", + "NotificationOptionVideoPlayback": "Video atskaņošana sākta", + "NotificationOptionInstallationFailed": "Instalācija neizdevās", + "AuthenticationSucceededWithUserName": "{0} veiksmīgi autentificējies", + "ValueSpecialEpisodeName": "Speciālais - {0}", + "ScheduledTaskStartedWithName": "{0} iesākts", + "ScheduledTaskFailedWithName": "{0} neizdevās", + "Photos": "Attēli", + "NotificationOptionUserLockedOut": "Lietotājs bloķēts", + "LabelRunningTimeValue": "Garums: {0}", + "Inherit": "Mantot", + "AppDeviceValues": "Lietotne:{0}, Ierīce:{1}", + "VersionNumber": "Versija {0}", + "ValueHasBeenAddedToLibrary": "{0} ir ticis pievienots tavai multvides bibliotēkai", + "UserStoppedPlayingItemWithValues": "{0} ir beidzis atskaņot {1} uz {2}", + "UserStartedPlayingItemWithValues": "{0} atskaņo {1} uz {2}", + "UserPasswordChangedWithName": "Parole nomainīta lietotājam {0}", + "UserOnlineFromDevice": "{0} ir tiešsaistē no {1}", + "UserOfflineFromDevice": "{0} ir atvienojies no {1}", + "UserLockedOutWithName": "Lietotājs {0} ir ticis bloķēts", + "UserDownloadingItemWithValues": "{0} lejupielādē {1}", + "UserDeletedWithName": "Lietotājs {0} ir izdzēsts", + "UserCreatedWithName": "Lietotājs {0} ir ticis izveidots", + "User": "Lietotājs", + "TvShows": "TV Raidījumi", + "Sync": "Sinhronizācija", + "System": "Sistēma", + "StartupEmbyServerIsLoading": "Jellyfin Serveris lādējas. Lūdzu mēģiniet vēlreiz pēc brīža.", + "Songs": "Dziesmas", + "Shows": "Raidījumi", + "PluginUpdatedWithName": "{0} tika atjaunots", + "PluginUninstalledWithName": "{0} tika noņemts", + "PluginInstalledWithName": "{0} tika uzstādīts", + "Plugin": "Paplašinājums", + "Playlists": "Atskaņošanas Saraksti", + "MixedContent": "Jaukts saturs", + "HomeVideos": "Mājas Video", + "HeaderNextUp": "Nākamais", + "ChapterNameValue": "Nodaļa {0}", + "Application": "Lietotne", + "NotificationOptionServerRestartRequired": "Vajadzīgs servera restarts", + "NotificationOptionPluginUpdateInstalled": "Paplašinājuma atjauninājums uzstādīts", + "NotificationOptionPluginUninstalled": "Paplašinājums noņemts", + "NotificationOptionPluginInstalled": "Paplašinājums uzstādīts", + "NotificationOptionPluginError": "Paplašinājuma kļūda", + "NotificationOptionNewLibraryContent": "Jauns saturs pievienots", + "NotificationOptionCameraImageUploaded": "Kameras attēls augšupielādēts", + "NotificationOptionAudioPlaybackStopped": "Audio atskaņošana apturēta", + "NotificationOptionAudioPlayback": "Audio atskaņošana sākta", + "NotificationOptionApplicationUpdateInstalled": "Lietotnes atjauninājums uzstādīts", + "NotificationOptionApplicationUpdateAvailable": "Lietotnes atjauninājums pieejams", + "NewVersionIsAvailable": "Lejupielādei ir pieejama jauna Jellyfin Server versija.", + "NameSeasonUnknown": "Nezināma Sezona", + "NameSeasonNumber": "Sezona {0}", + "NameInstallFailed": "{0} instalācija neizdevās", + "MusicVideos": "Mūzikas video", + "Music": "Mūzika", + "Movies": "Filmas", + "MessageServerConfigurationUpdated": "Servera konfigurācija ir tikusi atjaunota", + "MessageNamedServerConfigurationUpdatedWithValue": "Servera konfigurācijas sadaļa {0} ir tikusi atjaunota", + "MessageApplicationUpdatedTo": "Jellyfin Server ir ticis atjaunots uz {0}", + "MessageApplicationUpdated": "Jellyfin Server ir ticis atjaunots", + "Latest": "Jaunākais", + "LabelIpAddressValue": "IP adrese: {0}", + "ItemRemovedWithName": "{0} tika noņemts no bibliotēkas", + "ItemAddedWithName": "{0} tika pievienots bibliotēkai", + "HeaderLiveTV": "Tiešraides TV", + "HeaderContinueWatching": "Turpināt Skatīšanos", + "HeaderCameraUploads": "Kameras augšupielādes", + "HeaderAlbumArtists": "Albumu Izpildītāji", + "Genres": "Žanri", + "Folders": "Mapes", + "Favorites": "Favorīti", + "FailedLoginAttemptWithUserName": "Neizdevies pieslēgšanās mēģinājums no {0}", + "DeviceOnlineWithName": "{0} ir pievienojies", + "DeviceOfflineWithName": "{0} ir atvienojies", + "Collections": "Kolekcijas", + "Channels": "Kanāli", + "CameraImageUploadedFrom": "Jauns kameras attēls ir ticis augšupielādēts no {0}", + "Books": "Grāmatas", + "Artists": "Izpildītāji", + "Albums": "Albumi", + "ProviderValue": "Provider: {0}", + "HeaderFavoriteSongs": "Dziesmu Favorīti", + "HeaderFavoriteShows": "Raidījumu Favorīti", + "HeaderFavoriteEpisodes": "Episožu Favorīti", + "HeaderFavoriteArtists": "Izpildītāju Favorīti", + "HeaderFavoriteAlbums": "Albumu Favorīti", + "TaskCleanCacheDescription": "Nodzēš keša datnes, kas vairs nav sistēmai vajadzīgas.", + "TaskRefreshChapterImages": "Izvilkt Nodaļu Attēlus", + "TasksApplicationCategory": "Lietotne", + "TasksLibraryCategory": "Bibliotēka", + "TaskDownloadMissingSubtitlesDescription": "Internetā meklē trūkstošus subtitrus pēc metadatu uzstādījumiem.", + "TaskDownloadMissingSubtitles": "Lejupielādēt trūkstošus subtitrus", + "TaskRefreshChannelsDescription": "Atjauno interneta kanālu informāciju.", + "TaskRefreshChannels": "Atjaunot Kanālus", + "TaskCleanTranscodeDescription": "Izdzēš trans-kodēšanas datnes, kas ir vecākas par vienu dienu.", + "TaskCleanTranscode": "Iztīrīt Trans-kodēšanas Mapi", + "TaskUpdatePluginsDescription": "Lejupielādē un uzstāda atjauninājumus paplašinājumiem, kam ir uzstādīta automātiskā atjaunināšana.", + "TaskUpdatePlugins": "Atjaunot Paplašinājumus", + "TaskRefreshPeopleDescription": "Atjauno metadatus priekš aktieriem un direktoriem tavā mediju bibliotēkā.", + "TaskRefreshPeople": "Atjaunot Cilvēkus", + "TaskCleanLogsDescription": "Nodzēš log datnes, kas ir vairāk par {0} dienām vecas.", + "TaskCleanLogs": "Iztīrīt Logdatņu Mapi", + "TaskRefreshLibraryDescription": "Skenē tavas mediju bibliotēkas priekš jaunām datnēm un atjauno metadatus.", + "TaskRefreshLibrary": "Skanēt Mediju Bibliotēku", + "TaskRefreshChapterImagesDescription": "Izveido sīktēlus priekš video ar sadaļām.", + "TaskCleanCache": "Iztīrīt Kešošanas Mapi", + "TasksChannelsCategory": "Interneta Kanāli", + "TasksMaintenanceCategory": "Apkope" +} diff --git a/Emby.Server.Implementations/Localization/Core/mk.json b/Emby.Server.Implementations/Localization/Core/mk.json new file mode 100644 index 000000000..8df137302 --- /dev/null +++ b/Emby.Server.Implementations/Localization/Core/mk.json @@ -0,0 +1,95 @@ +{ + "ScheduledTaskFailedWithName": "{0} неуспешно", + "ProviderValue": "Провајдер: {0}", + "PluginUpdatedWithName": "{0} беше надоградено", + "PluginUninstalledWithName": "{0} беше успешно деинсталирано", + "PluginInstalledWithName": "{0} беше успешно инсталирано", + "Plugin": "Додатоци", + "Playlists": "Листи", + "Photos": "Слики", + "NotificationOptionVideoPlaybackStopped": "Видео стопирано", + "NotificationOptionVideoPlayback": "Видео пуштено", + "NotificationOptionUserLockedOut": "Корисникот е ослободен", + "NotificationOptionTaskFailed": "Закажани задачи неуспешно", + "NotificationOptionServerRestartRequired": "Задолжително рестартирање на серверот", + "NotificationOptionPluginUpdateInstalled": "Надоградба на Додаток успешна", + "NotificationOptionPluginUninstalled": "Додаток успешно деинсталиран", + "NotificationOptionPluginInstalled": "Додаток успешно инсталиран", + "NotificationOptionPluginError": "Грешка на додаток", + "NotificationOptionNewLibraryContent": "Додадена нова содржина", + "NotificationOptionInstallationFailed": "Неуспешна Инсталација", + "NotificationOptionCameraImageUploaded": "Слика од камера поставена", + "NotificationOptionAudioPlaybackStopped": "Аудио стопирано", + "NotificationOptionAudioPlayback": "Аудио стартувано", + "NotificationOptionApplicationUpdateInstalled": "Надоградбата на Апликацијата е иснталирана", + "NotificationOptionApplicationUpdateAvailable": "Возможна надоградба на Апликацијата", + "NewVersionIsAvailable": "Нова верзија од Jellyfin е возможна за спуштање.", + "NameSeasonUnknown": "Непозната Сезона", + "NameSeasonNumber": "Сезона {0}", + "NameInstallFailed": "{0} неуспешна инсталација", + "MusicVideos": "Музички видеа", + "Music": "Музика", + "Movies": "Филмови", + "MixedContent": "Мешана содржина", + "MessageServerConfigurationUpdated": "Серверската конфигурација беше надградена", + "MessageNamedServerConfigurationUpdatedWithValue": "Секцијата на конфигурација на сервер {0} беше надоградена", + "MessageApplicationUpdatedTo": "Jellyfin беше надограден до {0}", + "MessageApplicationUpdated": "Jellyfin Серверот беше надограден", + "Latest": "Последно", + "LabelRunningTimeValue": "Време на работа: {0}", + "LabelIpAddressValue": "ИП Адреса: {0}", + "ItemRemovedWithName": "{0} е избришано до Библиотеката", + "ItemAddedWithName": "{0} беше додадено во Библиотеката", + "Inherit": "Следно", + "HomeVideos": "Домашни Видеа", + "HeaderRecordingGroups": "Групи на снимање", + "HeaderNextUp": "Следно", + "HeaderLiveTV": "ТВ", + "HeaderFavoriteSongs": "Омилени Песни", + "HeaderFavoriteShows": "Омилени Серии", + "HeaderFavoriteEpisodes": "Омилени Епизоди", + "HeaderFavoriteArtists": "Омилени Изведувачи", + "HeaderFavoriteAlbums": "Омилени Албуми", + "HeaderContinueWatching": "Продолжи со гледање", + "HeaderCameraUploads": "Поставувања од камера", + "HeaderAlbumArtists": "Изведувачи од Албуми", + "Genres": "Жанрови", + "Folders": "Папки", + "Favorites": "Омилени", + "FailedLoginAttemptWithUserName": "Неуспешно поврзување од {0}", + "DeviceOnlineWithName": "{0} е приклучен", + "DeviceOfflineWithName": "{0} се исклучи", + "Collections": "Колекции", + "ChapterNameValue": "Дел {0}", + "Channels": "Канали", + "CameraImageUploadedFrom": "Нова слика од камера беше поставена од {0}", + "Books": "Книги", + "AuthenticationSucceededWithUserName": "{0} успешно поврзан", + "Artists": "Изведувач", + "Application": "Апликација", + "AppDeviceValues": "Аплиакција: {0}, Уред: {1}", + "Albums": "Албуми", + "VersionNumber": "Верзија {0}", + "ValueSpecialEpisodeName": "Специјално - {0}", + "ValueHasBeenAddedToLibrary": "{0} е додадено во твојата библиотека", + "UserStoppedPlayingItemWithValues": "{0} заврши со репродукција {1} во {2}", + "UserStartedPlayingItemWithValues": "{0} пушти {1} на {2}", + "UserPolicyUpdatedWithName": "Полисата на користење беше надоградена за {0}", + "UserPasswordChangedWithName": "Лозинката е сменета за корисникот {0}", + "UserOnlineFromDevice": "{0} е приклучен од {1}", + "UserOfflineFromDevice": "{0} е дисконектиран од {1}", + "UserLockedOutWithName": "Корисникот {0} е заклучен", + "UserDownloadingItemWithValues": "{0} се спушта {1}", + "UserDeletedWithName": "Корисникот {0} е избришан", + "UserCreatedWithName": "Корисникот {0} е креиран", + "User": "Корисник", + "TvShows": "ТВ Серии", + "System": "Систем", + "Sync": "Синхронизација", + "SubtitleDownloadFailureFromForItem": "Преводот неуспешно се спушти од {0} за {1}", + "StartupEmbyServerIsLoading": "Jellyfin Server се пушта. Ве молиме причекајте.", + "Songs": "Песни", + "Shows": "Серии", + "ServerNameNeedsToBeRestarted": "{0} треба да се рестартира", + "ScheduledTaskStartedWithName": "{0} започна" +} diff --git a/Emby.Server.Implementations/Localization/Core/mr.json b/Emby.Server.Implementations/Localization/Core/mr.json new file mode 100644 index 000000000..50b6360d8 --- /dev/null +++ b/Emby.Server.Implementations/Localization/Core/mr.json @@ -0,0 +1,61 @@ +{ + "Books": "पुस्तकं", + "Artists": "संगीतकार", + "Albums": "अल्बम", + "Playlists": "प्लेलिस्ट", + "HeaderAlbumArtists": "अल्बम संगीतकार", + "Folders": "फोल्डर", + "HeaderFavoriteEpisodes": "आवडते भाग", + "HeaderFavoriteSongs": "आवडती गाणी", + "Movies": "चित्रपट", + "HeaderFavoriteArtists": "आवडते संगीतकार", + "Shows": "कार्यक्रम", + "HeaderFavoriteAlbums": "आवडते अल्बम", + "Channels": "वाहिन्या", + "ValueSpecialEpisodeName": "विशेष - {0}", + "HeaderFavoriteShows": "आवडते कार्यक्रम", + "Favorites": "आवडीचे", + "HeaderNextUp": "यानंतर", + "Songs": "गाणी", + "HeaderLiveTV": "लाइव्ह टीव्ही", + "Genres": "जाँनरे", + "Photos": "चित्र", + "TaskDownloadMissingSubtitles": "नसलेले सबटायटल डाउनलोड करा", + "TaskCleanTranscodeDescription": "एक दिवसापेक्षा जुन्या ट्रान्सकोड फायली काढून टाका.", + "TaskCleanTranscode": "ट्रान्सकोड डिरेक्टरी साफ करून टाका", + "TaskUpdatePlugins": "प्लगइन अपडेट करा", + "TaskCleanLogs": "लॉग डिरेक्टरी साफ करून टाका", + "TaskCleanCache": "कॅश डिरेक्टरी साफ करून टाका", + "TasksChannelsCategory": "इंटरनेट वाहिन्या", + "TasksApplicationCategory": "अॅप्लिकेशन", + "TasksLibraryCategory": "संग्रहालय", + "VersionNumber": "आवृत्ती {0}", + "UserPasswordChangedWithName": "{0} या प्रयोक्त्याचे पासवर्ड बदलण्यात आले आहे", + "UserOnlineFromDevice": "{0} हे {1} येथून ऑनलाइन आहेत", + "UserDeletedWithName": "प्रयोक्ता {0} काढून टाकण्यात आले आहे", + "UserCreatedWithName": "प्रयोक्ता {0} बनवण्यात आले आहे", + "User": "प्रयोक्ता", + "TvShows": "टीव्ही कार्यक्रम", + "StartupEmbyServerIsLoading": "जेलिफिन सर्व्हर लोड होत आहे. कृपया थोड्या वेळात पुन्हा प्रयत्न करा.", + "Plugin": "प्लगइन", + "NotificationOptionCameraImageUploaded": "कॅमेरा चित्र अपलोड केले आहे", + "NotificationOptionApplicationUpdateInstalled": "अॅप्लिकेशन अपडेट इन्स्टॉल केले आहे", + "NotificationOptionApplicationUpdateAvailable": "अॅप्लिकेशन अपडेट उपलब्ध आहे", + "NewVersionIsAvailable": "जेलिफिन सर्व्हरची एक नवीन आवृत्ती डाउनलोड करण्यास उपलब्ध आहे.", + "NameSeasonUnknown": "अज्ञात सीझन", + "NameSeasonNumber": "सीझन {0}", + "MusicVideos": "संगीत व्हिडीयो", + "Music": "संगीत", + "MessageApplicationUpdatedTo": "जेलिफिन सर्व्हर अपडेट होऊन {0} आवृत्तीवर पोहोचला आहे", + "MessageApplicationUpdated": "जेलिफिन सर्व्हर अपडेट केला गेला आहे", + "Latest": "नवीनतम", + "LabelIpAddressValue": "आयपी पत्ता: {0}", + "ItemRemovedWithName": "{0} हे संग्रहालयातून काढून टाकण्यात आले", + "ItemAddedWithName": "{0} हे संग्रहालयात जोडले गेले", + "HomeVideos": "घरचे व्हिडीयो", + "HeaderRecordingGroups": "रेकॉर्डिंग गट", + "HeaderCameraUploads": "कॅमेरा अपलोड", + "CameraImageUploadedFrom": "एक नवीन कॅमेरा चित्र {0} येथून अपलोड केले आहे", + "Application": "अॅप्लिकेशन", + "AppDeviceValues": "अॅप: {0}, यंत्र: {1}" +} diff --git a/Emby.Server.Implementations/Localization/Core/ms.json b/Emby.Server.Implementations/Localization/Core/ms.json index b91c98ca0..79d078d4a 100644 --- a/Emby.Server.Implementations/Localization/Core/ms.json +++ b/Emby.Server.Implementations/Localization/Core/ms.json @@ -1,9 +1,9 @@ { "Albums": "Album-album", - "AppDeviceValues": "App: {0}, Device: {1}", + "AppDeviceValues": "Apl: {0}, Peranti: {1}", "Application": "Aplikasi", - "Artists": "Artis-artis", - "AuthenticationSucceededWithUserName": "{0} successfully authenticated", + "Artists": "Artis", + "AuthenticationSucceededWithUserName": "{0} berjaya disahkan", "Books": "Buku-buku", "CameraImageUploadedFrom": "A new camera image has been uploaded from {0}", "Channels": "Saluran", @@ -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": "Alamat IP: {0}", "LabelRunningTimeValue": "Running time: {0}", "Latest": "Latest", "MessageApplicationUpdated": "Jellyfin Server has been updated", @@ -50,7 +50,7 @@ "NotificationOptionAudioPlayback": "Audio playback started", "NotificationOptionAudioPlaybackStopped": "Audio playback stopped", "NotificationOptionCameraImageUploaded": "Camera image uploaded", - "NotificationOptionInstallationFailed": "Installation failure", + "NotificationOptionInstallationFailed": "Pemasangan gagal", "NotificationOptionNewLibraryContent": "New content added", "NotificationOptionPluginError": "Plugin failure", "NotificationOptionPluginInstalled": "Plugin installed", @@ -76,7 +76,6 @@ "StartupEmbyServerIsLoading": "Jellyfin Server is loading. Please try again shortly.", "SubtitleDownloadFailureForItem": "Subtitles failed to download for {0}", "SubtitleDownloadFailureFromForItem": "Subtitles failed to download from {0} for {1}", - "SubtitlesDownloadedForItem": "Subtitles downloaded for {0}", "Sync": "Sync", "System": "Sistem", "TvShows": "TV Shows", diff --git a/Emby.Server.Implementations/Localization/Core/nb.json b/Emby.Server.Implementations/Localization/Core/nb.json index f9fa1b68c..e523ae90b 100644 --- a/Emby.Server.Implementations/Localization/Core/nb.json +++ b/Emby.Server.Implementations/Localization/Core/nb.json @@ -76,7 +76,6 @@ "StartupEmbyServerIsLoading": "Jellyfin Server laster. Prøv igjen snart.", "SubtitleDownloadFailureForItem": "En feil oppstå under nedlasting av undertekster for {0}", "SubtitleDownloadFailureFromForItem": "Kunne ikke laste ned undertekster fra {0} for {1}", - "SubtitlesDownloadedForItem": "Undertekster lastet ned for {0}", "Sync": "Synkroniser", "System": "System", "TvShows": "TV-serier", @@ -93,5 +92,9 @@ "UserStoppedPlayingItemWithValues": "{0} har stoppet avspilling {1}", "ValueHasBeenAddedToLibrary": "{0} har blitt lagt til i mediebiblioteket ditt", "ValueSpecialEpisodeName": "Spesialepisode - {0}", - "VersionNumber": "Versjon {0}" + "VersionNumber": "Versjon {0}", + "TasksChannelsCategory": "Internett kanaler", + "TasksApplicationCategory": "Applikasjon", + "TasksLibraryCategory": "Bibliotek", + "TasksMaintenanceCategory": "Vedlikehold" } diff --git a/Emby.Server.Implementations/Localization/Core/nl.json b/Emby.Server.Implementations/Localization/Core/nl.json index 4423b7f98..3bc9c2a77 100644 --- a/Emby.Server.Implementations/Localization/Core/nl.json +++ b/Emby.Server.Implementations/Localization/Core/nl.json @@ -3,13 +3,13 @@ "AppDeviceValues": "App: {0}, Apparaat: {1}", "Application": "Applicatie", "Artists": "Artiesten", - "AuthenticationSucceededWithUserName": "{0} is succesvol geverifieerd", + "AuthenticationSucceededWithUserName": "{0} succesvol geauthenticeerd", "Books": "Boeken", "CameraImageUploadedFrom": "Er is een nieuwe foto toegevoegd van {0}", "Channels": "Kanalen", "ChapterNameValue": "Hoofdstuk {0}", "Collections": "Verzamelingen", - "DeviceOfflineWithName": "{0} heeft de verbinding verbroken", + "DeviceOfflineWithName": "Verbinding met {0} is verbroken", "DeviceOnlineWithName": "{0} is verbonden", "FailedLoginAttemptWithUserName": "Mislukte aanmeld poging van {0}", "Favorites": "Favorieten", @@ -76,7 +76,6 @@ "StartupEmbyServerIsLoading": "Jellyfin Server is aan het laden, probeer het later opnieuw.", "SubtitleDownloadFailureForItem": "Downloaden van ondertiteling voor {0} is mislukt", "SubtitleDownloadFailureFromForItem": "Ondertitels konden niet gedownload worden van {0} voor {1}", - "SubtitlesDownloadedForItem": "Ondertiteling voor {0} is gedownload", "Sync": "Synchronisatie", "System": "Systeem", "TvShows": "TV-series", @@ -93,5 +92,27 @@ "UserStoppedPlayingItemWithValues": "{0} heeft afspelen van {1} gestopt op {2}", "ValueHasBeenAddedToLibrary": "{0} is toegevoegd aan je mediabibliotheek", "ValueSpecialEpisodeName": "Speciaal - {0}", - "VersionNumber": "Versie {0}" + "VersionNumber": "Versie {0}", + "TaskDownloadMissingSubtitlesDescription": "Zoekt op het internet naar missende ondertitels gebaseerd op metadata configuratie.", + "TaskDownloadMissingSubtitles": "Download missende ondertitels", + "TaskRefreshChannelsDescription": "Vernieuwt informatie van internet kanalen.", + "TaskRefreshChannels": "Vernieuw Kanalen", + "TaskCleanTranscodeDescription": "Verwijder transcode bestanden ouder dan 1 dag.", + "TaskCleanLogs": "Log Folder Opschonen", + "TaskCleanTranscode": "Transcode Folder Opschonen", + "TaskUpdatePluginsDescription": "Download en installeert updates voor plugins waar automatisch updaten aan staat.", + "TaskUpdatePlugins": "Update Plugins", + "TaskRefreshPeopleDescription": "Update metadata for acteurs en regisseurs in de media bibliotheek.", + "TaskRefreshPeople": "Vernieuw Personen", + "TaskCleanLogsDescription": "Verwijdert log bestanden ouder dan {0} dagen.", + "TaskRefreshLibraryDescription": "Scant de media bibliotheek voor nieuwe bestanden en vernieuwt de metadata.", + "TaskRefreshLibrary": "Scan Media Bibliotheek", + "TaskRefreshChapterImagesDescription": "Maakt thumbnails aan voor videos met hoofdstukken.", + "TaskRefreshChapterImages": "Hoofdstukafbeeldingen Uitpakken", + "TaskCleanCacheDescription": "Verwijder gecachte bestanden die het systeem niet langer nodig heeft.", + "TaskCleanCache": "Cache Folder Opschonen", + "TasksChannelsCategory": "Internet Kanalen", + "TasksApplicationCategory": "Applicatie", + "TasksLibraryCategory": "Bibliotheek", + "TasksMaintenanceCategory": "Onderhoud" } diff --git a/Emby.Server.Implementations/Localization/Core/nn.json b/Emby.Server.Implementations/Localization/Core/nn.json new file mode 100644 index 000000000..281cadac5 --- /dev/null +++ b/Emby.Server.Implementations/Localization/Core/nn.json @@ -0,0 +1,60 @@ +{ + "MessageServerConfigurationUpdated": "Tenar konfigurasjonen har blitt oppdatert", + "MessageNamedServerConfigurationUpdatedWithValue": "Tenar konfigurasjon seksjon {0} har blitt oppdatert", + "MessageApplicationUpdatedTo": "Jellyfin Tenaren har blitt oppdatert til {0}", + "MessageApplicationUpdated": "Jellyfin Tenaren har blitt oppdatert", + "Latest": "Nyaste", + "LabelRunningTimeValue": "Speletid: {0}", + "LabelIpAddressValue": "IP adresse: {0}", + "ItemRemovedWithName": "{0} vart fjerna frå biblioteket", + "ItemAddedWithName": "{0} vart lagt til i biblioteket", + "Inherit": "Arv", + "HomeVideos": "Heime Videoar", + "HeaderRecordingGroups": "Innspelingsgrupper", + "HeaderNextUp": "Neste", + "HeaderLiveTV": "Direkte TV", + "HeaderFavoriteSongs": "Favoritt Songar", + "HeaderFavoriteShows": "Favoritt Seriar", + "HeaderFavoriteEpisodes": "Favoritt Episodar", + "HeaderFavoriteArtists": "Favoritt Artistar", + "HeaderFavoriteAlbums": "Favoritt Album", + "HeaderContinueWatching": "Fortsett å sjå", + "HeaderCameraUploads": "Kamera Opplastingar", + "HeaderAlbumArtists": "Album Artist", + "Genres": "Sjangrar", + "Folders": "Mapper", + "Favorites": "Favorittar", + "FailedLoginAttemptWithUserName": "Mislukka påloggingsforsøk frå {0}", + "DeviceOnlineWithName": "{0} er tilkopla", + "DeviceOfflineWithName": "{0} har kopla frå", + "Collections": "Samlingar", + "ChapterNameValue": "Kapittel {0}", + "Channels": "Kanalar", + "CameraImageUploadedFrom": "Eit nytt kamera bilete har blitt lasta opp frå {0}", + "Books": "Bøker", + "AuthenticationSucceededWithUserName": "{0} Har logga inn", + "Artists": "Artistar", + "Application": "Program", + "AppDeviceValues": "App: {0}, Einheit: {1}", + "Albums": "Album", + "NotificationOptionServerRestartRequired": "Tenaren krev omstart", + "NotificationOptionPluginUpdateInstalled": "Tilleggsprogram-oppdatering vart installert", + "NotificationOptionPluginUninstalled": "Tilleggsprogram avinstallert", + "NotificationOptionPluginInstalled": "Tilleggsprogram installert", + "NotificationOptionPluginError": "Tilleggsprogram feila", + "NotificationOptionNewLibraryContent": "Nytt innhald er lagt til", + "NotificationOptionInstallationFailed": "Installasjonen feila", + "NotificationOptionCameraImageUploaded": "Kamerabilde vart lasta opp", + "NotificationOptionAudioPlaybackStopped": "Lydavspilling stoppa", + "NotificationOptionAudioPlayback": "Lydavspilling påbyrja", + "NotificationOptionApplicationUpdateInstalled": "Applikasjonsoppdatering er installert", + "NotificationOptionApplicationUpdateAvailable": "Applikasjonsoppdatering er tilgjengeleg", + "NewVersionIsAvailable": "Ein ny versjon av Jellyfin serveren er tilgjengeleg for nedlasting.", + "NameSeasonUnknown": "Ukjend sesong", + "NameSeasonNumber": "Sesong {0}", + "NameInstallFailed": "{0} Installasjonen feila", + "MusicVideos": "Musikkvideoar", + "Music": "Musikk", + "Movies": "Filmar", + "MixedContent": "Blanda innhald" +} diff --git a/Emby.Server.Implementations/Localization/Core/pl.json b/Emby.Server.Implementations/Localization/Core/pl.json index e72f1a262..e9d9bbf2e 100644 --- a/Emby.Server.Implementations/Localization/Core/pl.json +++ b/Emby.Server.Implementations/Localization/Core/pl.json @@ -76,7 +76,6 @@ "StartupEmbyServerIsLoading": "Trwa wczytywanie serwera Jellyfin. Spróbuj ponownie za chwilę.", "SubtitleDownloadFailureForItem": "Pobieranie napisów dla {0} zakończone niepowodzeniem", "SubtitleDownloadFailureFromForItem": "Nieudane pobieranie napisów z {0} dla {1}", - "SubtitlesDownloadedForItem": "Pobrano napisy dla {0}", "Sync": "Synchronizacja", "System": "System", "TvShows": "Seriale", diff --git a/Emby.Server.Implementations/Localization/Core/pt-BR.json b/Emby.Server.Implementations/Localization/Core/pt-BR.json index 41a389e3b..3a69b6d7a 100644 --- a/Emby.Server.Implementations/Localization/Core/pt-BR.json +++ b/Emby.Server.Implementations/Localization/Core/pt-BR.json @@ -76,7 +76,6 @@ "StartupEmbyServerIsLoading": "O Servidor Jellyfin está carregando. Por favor, tente novamente mais tarde.", "SubtitleDownloadFailureForItem": "Download de legendas falhou para {0}", "SubtitleDownloadFailureFromForItem": "Houve um problema ao baixar as legendas de {0} para {1}", - "SubtitlesDownloadedForItem": "Legendas baixadas para {0}", "Sync": "Sincronizar", "System": "Sistema", "TvShows": "Séries", @@ -93,5 +92,27 @@ "UserStoppedPlayingItemWithValues": "{0} parou de reproduzir {1} em {2}", "ValueHasBeenAddedToLibrary": "{0} foi adicionado à sua biblioteca de mídia", "ValueSpecialEpisodeName": "Especial - {0}", - "VersionNumber": "Versão {0}" + "VersionNumber": "Versão {0}", + "TaskDownloadMissingSubtitlesDescription": "Procurar na internet por legendas faltando baseado na configuração de metadados.", + "TaskDownloadMissingSubtitles": "Baixar legendas que estão faltando", + "TaskRefreshChannelsDescription": "Atualizar informação de canais da internet .", + "TaskRefreshChannels": "Atualizar Canais", + "TaskCleanTranscodeDescription": "Deletar arquivos de transcodificação com mais de um dia de criação.", + "TaskCleanTranscode": "Limpar pasta de transcodificação", + "TaskUpdatePluginsDescription": "Baixa e instala atualizações para plugins que estão configurados para atualizar automaticamente.", + "TaskUpdatePlugins": "Atualizar Plugins", + "TaskRefreshPeopleDescription": "Atualiza metadados para atores e diretores na sua biblioteca de mídia.", + "TaskRefreshPeople": "Atualizar pessoas", + "TaskCleanLogsDescription": "Deletar arquivos temporários com mais de {0} dias.", + "TaskCleanLogs": "Limpar pasta de logs", + "TaskRefreshLibraryDescription": "Escaneie a sua biblioteca de mídia para arquivos novos e atualize os metadados.", + "TaskRefreshLibrary": "Escanear a Biblioteca de Mídia", + "TaskRefreshChapterImagesDescription": "Criar miniaturas para vídeos que tem capítulos.", + "TaskRefreshChapterImages": "Extrair imagens dos capítulos", + "TaskCleanCacheDescription": "Deletar arquivos temporários que não são mais necessários para o sistema.", + "TaskCleanCache": "Limpar Arquivos Temporários", + "TasksChannelsCategory": "Canais da Internet", + "TasksApplicationCategory": "Aplicativo", + "TasksLibraryCategory": "Biblioteca", + "TasksMaintenanceCategory": "Manutenção" } diff --git a/Emby.Server.Implementations/Localization/Core/pt-PT.json b/Emby.Server.Implementations/Localization/Core/pt-PT.json index b12d391c1..ebf35c492 100644 --- a/Emby.Server.Implementations/Localization/Core/pt-PT.json +++ b/Emby.Server.Implementations/Localization/Core/pt-PT.json @@ -76,7 +76,6 @@ "StartupEmbyServerIsLoading": "O servidor Jellyfin está a iniciar. Tente novamente mais tarde.", "SubtitleDownloadFailureForItem": "Subtitles failed to download for {0}", "SubtitleDownloadFailureFromForItem": "Falha na transferência de legendas a partir de {0} para {1}", - "SubtitlesDownloadedForItem": "Transferidas legendas para {0}", "Sync": "Sincronização", "System": "Sistema", "TvShows": "Programas TV", diff --git a/Emby.Server.Implementations/Localization/Core/pt.json b/Emby.Server.Implementations/Localization/Core/pt.json index ef8d988c8..3d5f7cab2 100644 --- a/Emby.Server.Implementations/Localization/Core/pt.json +++ b/Emby.Server.Implementations/Localization/Core/pt.json @@ -1,5 +1,5 @@ { - "HeaderLiveTV": "TV ao Vivo", + "HeaderLiveTV": "TV em Directo", "Collections": "Colecções", "Books": "Livros", "Artists": "Artistas", @@ -10,13 +10,13 @@ "HeaderFavoriteAlbums": "Álbuns Favoritos", "HeaderFavoriteEpisodes": "Episódios Favoritos", "HeaderFavoriteShows": "Séries Favoritas", - "HeaderContinueWatching": "Continuar a Ver", + "HeaderContinueWatching": "Continuar a Assistir", "HeaderAlbumArtists": "Artistas do Álbum", "Genres": "Géneros", - "Folders": "Pastas", + "Folders": "Directórios", "Favorites": "Favoritos", "Channels": "Canais", - "UserDownloadingItemWithValues": "{0} está a transferir {1}", + "UserDownloadingItemWithValues": "{0} está a ser transferido {1}", "VersionNumber": "Versão {0}", "ValueHasBeenAddedToLibrary": "{0} foi adicionado à sua biblioteca multimédia", "UserStoppedPlayingItemWithValues": "{0} terminou a reprodução de {1} em {2}", @@ -24,36 +24,35 @@ "UserPolicyUpdatedWithName": "A política do utilizador {0} foi alterada", "UserPasswordChangedWithName": "A palavra-passe do utilizador {0} foi alterada", "UserOnlineFromDevice": "{0} ligou-se a partir de {1}", - "UserOfflineFromDevice": "{0} desligou-se a partir de {1}", - "UserLockedOutWithName": "Utilizador {0} bloqueado", - "UserDeletedWithName": "Utilizador {0} removido", - "UserCreatedWithName": "Utilizador {0} criado", + "UserOfflineFromDevice": "{0} desconectou-se a partir de {1}", + "UserLockedOutWithName": "O utilizador {0} foi bloqueado", + "UserDeletedWithName": "O utilizador {0} foi removido", + "UserCreatedWithName": "O utilizador {0} foi criado", "User": "Utilizador", - "TvShows": "Programas", + "TvShows": "Séries", "System": "Sistema", - "SubtitlesDownloadedForItem": "Legendas transferidas para {0}", "SubtitleDownloadFailureFromForItem": "Falha na transferência de legendas de {0} para {1}", "StartupEmbyServerIsLoading": "O servidor Jellyfin está a iniciar. Tente novamente dentro de momentos.", "ServerNameNeedsToBeRestarted": "{0} necessita ser reiniciado", "ScheduledTaskStartedWithName": "{0} iniciou", "ScheduledTaskFailedWithName": "{0} falhou", "ProviderValue": "Fornecedor: {0}", - "PluginUpdatedWithName": "{0} foi actualizado", + "PluginUpdatedWithName": "{0} foi atualizado", "PluginUninstalledWithName": "{0} foi desinstalado", "PluginInstalledWithName": "{0} foi instalado", - "Plugin": "Extensão", + "Plugin": "Plugin", "NotificationOptionVideoPlaybackStopped": "Reprodução de vídeo parada", "NotificationOptionVideoPlayback": "Reprodução de vídeo iniciada", "NotificationOptionUserLockedOut": "Utilizador bloqueado", "NotificationOptionTaskFailed": "Falha em tarefa agendada", "NotificationOptionServerRestartRequired": "É necessário reiniciar o servidor", - "NotificationOptionPluginUpdateInstalled": "Extensão actualizada", - "NotificationOptionPluginUninstalled": "Extensão desinstalada", - "NotificationOptionPluginInstalled": "Extensão instalada", - "NotificationOptionPluginError": "Falha na extensão", + "NotificationOptionPluginUpdateInstalled": "Plugin actualizado", + "NotificationOptionPluginUninstalled": "Plugin desinstalado", + "NotificationOptionPluginInstalled": "Plugin instalado", + "NotificationOptionPluginError": "Falha no plugin", "NotificationOptionNewLibraryContent": "Novo conteúdo adicionado", "NotificationOptionInstallationFailed": "Falha de instalação", - "NotificationOptionCameraImageUploaded": "Imagem da câmara enviada", + "NotificationOptionCameraImageUploaded": "Imagem de câmara enviada", "NotificationOptionAudioPlaybackStopped": "Reprodução Parada", "NotificationOptionAudioPlayback": "Reprodução Iniciada", "NotificationOptionApplicationUpdateInstalled": "A actualização da aplicação foi instalada", @@ -66,30 +65,30 @@ "Music": "Música", "MixedContent": "Conteúdo Misto", "MessageServerConfigurationUpdated": "A configuração do servidor foi actualizada", - "MessageNamedServerConfigurationUpdatedWithValue": "Configurações do servidor na secção {0} foram atualizadas", - "MessageApplicationUpdatedTo": "O servidor Jellyfin foi actualizado para a versão {0}", + "MessageNamedServerConfigurationUpdatedWithValue": "As configurações do servidor na secção {0} foram atualizadas", + "MessageApplicationUpdatedTo": "O servidor Jellyfin foi atualizado para a versão {0}", "MessageApplicationUpdated": "O servidor Jellyfin foi actualizado", "Latest": "Mais Recente", "LabelRunningTimeValue": "Duração: {0}", - "LabelIpAddressValue": "Endereço IP: {0}", + "LabelIpAddressValue": "Endereço de IP: {0}", "ItemRemovedWithName": "{0} foi removido da biblioteca", "ItemAddedWithName": "{0} foi adicionado à biblioteca", "Inherit": "Herdar", "HomeVideos": "Vídeos Caseiros", "HeaderRecordingGroups": "Grupos de Gravação", - "ValueSpecialEpisodeName": "Especial - {0}", + "ValueSpecialEpisodeName": "Episódio Especial - {0}", "Sync": "Sincronização", "Songs": "Músicas", "Shows": "Séries", "Playlists": "Listas de Reprodução", "Photos": "Fotografias", "Movies": "Filmes", - "HeaderCameraUploads": "Envios a partir da câmara", - "FailedLoginAttemptWithUserName": "Tentativa de ligação a partir de {0} falhou", - "DeviceOnlineWithName": "{0} ligou-se", - "DeviceOfflineWithName": "{0} desligou-se", + "HeaderCameraUploads": "Carregamentos a partir da câmara", + "FailedLoginAttemptWithUserName": "Tentativa de ligação falhada a partir de {0}", + "DeviceOnlineWithName": "{0} está connectado", + "DeviceOfflineWithName": "{0} desconectou-se", "ChapterNameValue": "Capítulo {0}", - "CameraImageUploadedFrom": "Uma nova imagem de câmara foi enviada a partir de {0}", + "CameraImageUploadedFrom": "Uma nova imagem da câmara foi enviada a partir de {0}", "AuthenticationSucceededWithUserName": "{0} autenticado com sucesso", "Application": "Aplicação", "AppDeviceValues": "Aplicação {0}, Dispositivo: {1}" diff --git a/Emby.Server.Implementations/Localization/Core/ro.json b/Emby.Server.Implementations/Localization/Core/ro.json index 71bffffc6..699dd26da 100644 --- a/Emby.Server.Implementations/Localization/Core/ro.json +++ b/Emby.Server.Implementations/Localization/Core/ro.json @@ -17,7 +17,6 @@ "TvShows": "Spectacole TV", "System": "Sistem", "Sync": "Sincronizare", - "SubtitlesDownloadedForItem": "Subtitrari descarcate pentru {0}", "SubtitleDownloadFailureFromForItem": "Subtitrările nu au putut fi descărcate de la {0} pentru {1}", "StartupEmbyServerIsLoading": "Se încarcă serverul Jellyfin. Încercați din nou în scurt timp.", "Songs": "Melodii", @@ -92,5 +91,27 @@ "Artists": "Artiști", "Application": "Aplicație", "AppDeviceValues": "Aplicație: {0}, Dispozitiv: {1}", - "Albums": "Albume" + "Albums": "Albume", + "TaskDownloadMissingSubtitlesDescription": "Caută pe internet subtitrările lipsă pe baza configurației metadatelor.", + "TaskDownloadMissingSubtitles": "Descarcă subtitrările lipsă", + "TaskRefreshChannelsDescription": "Actualizează informațiile despre canalul de internet.", + "TaskRefreshChannels": "Actualizează canale", + "TaskCleanTranscodeDescription": "Șterge fișierele de transcodare mai vechi de o zi.", + "TaskCleanTranscode": "Curățați directorul de transcodare", + "TaskUpdatePluginsDescription": "Descarcă și instalează actualizări pentru pluginuri care sunt configurate să se actualizeze automat.", + "TaskUpdatePlugins": "Actualizați plugin-uri", + "TaskRefreshPeopleDescription": "Actualizează metadatele pentru actori și regizori din biblioteca media.", + "TaskRefreshPeople": "Actualizează oamenii", + "TaskCleanLogsDescription": "Șterge fișierele jurnal care au mai mult de {0} zile.", + "TaskCleanLogs": "Curățare director jurnal", + "TaskRefreshLibraryDescription": "Scanează biblioteca media pentru fișiere noi și reîmprospătează metadatele.", + "TaskRefreshLibrary": "Scanează Biblioteca Media", + "TaskRefreshChapterImagesDescription": "Creează miniaturi pentru videourile care au capitole.", + "TaskRefreshChapterImages": "Extrage Imaginile de Capitol", + "TaskCleanCacheDescription": "Șterge fișierele cache care nu mai sunt necesare sistemului.", + "TaskCleanCache": "Curățați directorul cache", + "TasksChannelsCategory": "Canale de pe Internet", + "TasksApplicationCategory": "Aplicație", + "TasksLibraryCategory": "Librărie", + "TasksMaintenanceCategory": "Mentenanță" } diff --git a/Emby.Server.Implementations/Localization/Core/ru.json b/Emby.Server.Implementations/Localization/Core/ru.json index 7cf957a94..c46aa5c30 100644 --- a/Emby.Server.Implementations/Localization/Core/ru.json +++ b/Emby.Server.Implementations/Localization/Core/ru.json @@ -76,7 +76,6 @@ "StartupEmbyServerIsLoading": "Jellyfin Server загружается. Повторите попытку в ближайшее время.", "SubtitleDownloadFailureForItem": "Субтитры к {0} не удалось загрузить", "SubtitleDownloadFailureFromForItem": "Субтитры к {1} не удалось загрузить с {0}", - "SubtitlesDownloadedForItem": "Субтитры к {0} загружены", "Sync": "Синхро", "System": "Система", "TvShows": "ТВ", diff --git a/Emby.Server.Implementations/Localization/Core/sk.json b/Emby.Server.Implementations/Localization/Core/sk.json index 1988bda52..0ee652637 100644 --- a/Emby.Server.Implementations/Localization/Core/sk.json +++ b/Emby.Server.Implementations/Localization/Core/sk.json @@ -76,7 +76,6 @@ "StartupEmbyServerIsLoading": "Jellyfin Server sa spúšťa. Prosím, skúste to o chvíľu znova.", "SubtitleDownloadFailureForItem": "Sťahovanie titulkov pre {0} zlyhalo", "SubtitleDownloadFailureFromForItem": "Sťahovanie titulkov z {0} pre {1} zlyhalo", - "SubtitlesDownloadedForItem": "Titulky pre {0} stiahnuté", "Sync": "Synchronizácia", "System": "Systém", "TvShows": "TV seriály", @@ -93,5 +92,27 @@ "UserStoppedPlayingItemWithValues": "{0} ukončil prehrávanie {1} na {2}", "ValueHasBeenAddedToLibrary": "{0} bol pridané do vašej knižnice médií", "ValueSpecialEpisodeName": "Špeciál - {0}", - "VersionNumber": "Verzia {0}" + "VersionNumber": "Verzia {0}", + "TaskDownloadMissingSubtitlesDescription": "Vyhľadá na internete chýbajúce titulky podľa toho, ako sú nakonfigurované metadáta.", + "TaskDownloadMissingSubtitles": "Stiahnuť chýbajúce titulky", + "TaskRefreshChannelsDescription": "Obnoví informácie o internetových kanáloch.", + "TaskRefreshChannels": "Obnoviť kanály", + "TaskCleanTranscodeDescription": "Vymaže súbory transkódovania, ktoré sú staršie ako jeden deň.", + "TaskCleanTranscode": "Vyčistiť priečinok pre transkódovanie", + "TaskUpdatePluginsDescription": "Stiahne a nainštaluje aktualizácie pre zásuvné moduly, ktoré sú nastavené tak, aby sa aktualizovali automaticky.", + "TaskUpdatePlugins": "Aktualizovať zásuvné moduly", + "TaskRefreshPeopleDescription": "Aktualizuje metadáta pre hercov a režisérov vo vašej mediálnej knižnici.", + "TaskRefreshPeople": "Obnoviť osoby", + "TaskCleanLogsDescription": "Vymaže log súbory, ktoré su staršie ako {0} deň/dni/dní.", + "TaskCleanLogs": "Vyčistiť priečinok s logmi", + "TaskRefreshLibraryDescription": "Hľadá vo vašej mediálnej knižnici nové súbory a obnovuje metadáta.", + "TaskRefreshLibrary": "Prehľadávať knižnicu medií", + "TaskRefreshChapterImagesDescription": "Vytvorí náhľady pre videá, ktoré majú kapitoly.", + "TaskRefreshChapterImages": "Extrahovať obrázky kapitol", + "TaskCleanCacheDescription": "Vymaže cache súbory, ktoré nie sú už potrebné pre systém.", + "TaskCleanCache": "Vyčistiť Cache priečinok", + "TasksChannelsCategory": "Internetové kanály", + "TasksApplicationCategory": "Aplikácia", + "TasksLibraryCategory": "Knižnica", + "TasksMaintenanceCategory": "Údržba" } diff --git a/Emby.Server.Implementations/Localization/Core/sl-SI.json b/Emby.Server.Implementations/Localization/Core/sl-SI.json index b43cfbb74..b60dd33bd 100644 --- a/Emby.Server.Implementations/Localization/Core/sl-SI.json +++ b/Emby.Server.Implementations/Localization/Core/sl-SI.json @@ -40,7 +40,7 @@ "MixedContent": "Razne vsebine", "Movies": "Filmi", "Music": "Glasba", - "MusicVideos": "Glasbeni posnetki", + "MusicVideos": "Glasbeni videi", "NameInstallFailed": "{0} namestitev neuspešna", "NameSeasonNumber": "Sezona {0}", "NameSeasonUnknown": "Season neznana", @@ -76,7 +76,6 @@ "StartupEmbyServerIsLoading": "Jellyfin Server se nalaga. Poskusi ponovno kasneje.", "SubtitleDownloadFailureForItem": "Subtitles failed to download for {0}", "SubtitleDownloadFailureFromForItem": "Neuspešen prenos podnapisov iz {0} za {1}", - "SubtitlesDownloadedForItem": "Podnapisi preneseni za {0}", "Sync": "Sinhroniziraj", "System": "System", "TvShows": "TV serije", diff --git a/Emby.Server.Implementations/Localization/Core/sr.json b/Emby.Server.Implementations/Localization/Core/sr.json index da0088991..5f3cbb1c8 100644 --- a/Emby.Server.Implementations/Localization/Core/sr.json +++ b/Emby.Server.Implementations/Localization/Core/sr.json @@ -17,7 +17,6 @@ "TvShows": "ТВ серије", "System": "Систем", "Sync": "Усклади", - "SubtitlesDownloadedForItem": "Титлови преузети за {0}", "SubtitleDownloadFailureFromForItem": "Неуспело преузимање титлова за {1} са {0}", "StartupEmbyServerIsLoading": "Џелифин сервер се подиже. Покушајте поново убрзо.", "Songs": "Песме", @@ -82,7 +81,7 @@ "Favorites": "Омиљено", "FailedLoginAttemptWithUserName": "Неуспела пријава са {0}", "DeviceOnlineWithName": "{0} се повезао", - "DeviceOfflineWithName": "{0} се одвезао", + "DeviceOfflineWithName": "{0} је прекинуо везу", "Collections": "Колекције", "ChapterNameValue": "Поглавље {0}", "Channels": "Канали", @@ -92,5 +91,27 @@ "Artists": "Извођач", "Application": "Апликација", "AppDeviceValues": "Апл: {0}, уређај: {1}", - "Albums": "Албуми" + "Albums": "Албуми", + "TaskDownloadMissingSubtitlesDescription": "Претражује интернет за недостајуће титлове на основу конфигурације метаподатака.", + "TaskDownloadMissingSubtitles": "Преузмите недостајуће титлове", + "TaskRefreshChannelsDescription": "Освежава информације о интернет каналу.", + "TaskRefreshChannels": "Освежи канале", + "TaskCleanTranscodeDescription": "Брише датотеке за кодирање старије од једног дана.", + "TaskCleanTranscode": "Очистите директоријум преноса", + "TaskUpdatePluginsDescription": "Преузима и инсталира исправке за додатке који су конфигурисани за аутоматско ажурирање.", + "TaskUpdatePlugins": "Ажурирајте додатке", + "TaskRefreshPeopleDescription": "Ажурира метаподатке за глумце и редитеље у вашој медијској библиотеци.", + "TaskRefreshPeople": "Освежите људе", + "TaskCleanLogsDescription": "Брише логове старије од {0} дана.", + "TaskCleanLogs": "Очистите директоријум логова", + "TaskRefreshLibraryDescription": "Скенира вашу медијску библиотеку за нове датотеке и освежава метаподатке.", + "TaskRefreshLibrary": "Скенирај Библиотеку Медија", + "TaskRefreshChapterImagesDescription": "Ствара сличице за видео записе који имају поглавља.", + "TaskRefreshChapterImages": "Издвоји слике из поглавља", + "TaskCleanCacheDescription": "Брише Кеш фајлове који више нису потребни систему.", + "TaskCleanCache": "Очистите Кеш Директоријум", + "TasksChannelsCategory": "Интернет канали", + "TasksApplicationCategory": "Апликација", + "TasksLibraryCategory": "Библиотека", + "TasksMaintenanceCategory": "Одржавање" } diff --git a/Emby.Server.Implementations/Localization/Core/sv.json b/Emby.Server.Implementations/Localization/Core/sv.json index 744b0e2d3..b7c50394a 100644 --- a/Emby.Server.Implementations/Localization/Core/sv.json +++ b/Emby.Server.Implementations/Localization/Core/sv.json @@ -1,7 +1,7 @@ { "Albums": "Album", - "AppDeviceValues": "App: {0}, Enhet: {1}", - "Application": "App", + "AppDeviceValues": "Applikation: {0}, Enhet: {1}", + "Application": "Applikation", "Artists": "Artister", "AuthenticationSucceededWithUserName": "{0} har autentiserats", "Books": "Böcker", @@ -16,15 +16,15 @@ "Folders": "Mappar", "Genres": "Genrer", "HeaderAlbumArtists": "Albumartister", - "HeaderCameraUploads": "Kamera Uppladdningar", - "HeaderContinueWatching": "Fortsätt kolla på", + "HeaderCameraUploads": "Kamerauppladdningar", + "HeaderContinueWatching": "Fortsätt kolla", "HeaderFavoriteAlbums": "Favoritalbum", "HeaderFavoriteArtists": "Favoritartister", "HeaderFavoriteEpisodes": "Favoritavsnitt", "HeaderFavoriteShows": "Favoritserier", "HeaderFavoriteSongs": "Favoritlåtar", "HeaderLiveTV": "Live-TV", - "HeaderNextUp": "Nästa på tur", + "HeaderNextUp": "Nästa", "HeaderRecordingGroups": "Inspelningsgrupper", "HomeVideos": "Hemvideor", "Inherit": "Ärv", @@ -34,9 +34,9 @@ "LabelRunningTimeValue": "Speltid: {0}", "Latest": "Senaste", "MessageApplicationUpdated": "Jellyfin Server har uppdaterats", - "MessageApplicationUpdatedTo": "Jellyfin Server har uppgraderats till {0}", + "MessageApplicationUpdatedTo": "Jellyfin Server har uppdaterats till {0}", "MessageNamedServerConfigurationUpdatedWithValue": "Serverinställningarna {0} har uppdaterats", - "MessageServerConfigurationUpdated": "Server konfigurationen har uppdaterats", + "MessageServerConfigurationUpdated": "Serverkonfigurationen har uppdaterats", "MixedContent": "Blandat innehåll", "Movies": "Filmer", "Music": "Musik", @@ -44,11 +44,11 @@ "NameInstallFailed": "{0} installationen misslyckades", "NameSeasonNumber": "Säsong {0}", "NameSeasonUnknown": "Okänd säsong", - "NewVersionIsAvailable": "En ny version av Jellyfin Server är klar för nedladdning.", + "NewVersionIsAvailable": "En ny version av Jellyfin Server är tillgänglig att hämta.", "NotificationOptionApplicationUpdateAvailable": "Ny programversion tillgänglig", "NotificationOptionApplicationUpdateInstalled": "Programuppdatering installerad", "NotificationOptionAudioPlayback": "Ljuduppspelning har påbörjats", - "NotificationOptionAudioPlaybackStopped": "Ljuduppspelning stoppad", + "NotificationOptionAudioPlaybackStopped": "Ljuduppspelning stoppades", "NotificationOptionCameraImageUploaded": "Kamerabild har laddats upp", "NotificationOptionInstallationFailed": "Fel vid installation", "NotificationOptionNewLibraryContent": "Nytt innehåll har lagts till", @@ -60,7 +60,7 @@ "NotificationOptionTaskFailed": "Schemalagd aktivitet har misslyckats", "NotificationOptionUserLockedOut": "Användare har låsts ut", "NotificationOptionVideoPlayback": "Videouppspelning har påbörjats", - "NotificationOptionVideoPlaybackStopped": "Videouppspelning stoppad", + "NotificationOptionVideoPlaybackStopped": "Videouppspelning stoppades", "Photos": "Bilder", "Playlists": "Spellistor", "Plugin": "Tillägg", @@ -69,14 +69,13 @@ "PluginUpdatedWithName": "{0} uppdaterades", "ProviderValue": "Källa: {0}", "ScheduledTaskFailedWithName": "{0} misslyckades", - "ScheduledTaskStartedWithName": "{0} startad", + "ScheduledTaskStartedWithName": "{0} startades", "ServerNameNeedsToBeRestarted": "{0} behöver startas om", "Shows": "Serier", "Songs": "Låtar", - "StartupEmbyServerIsLoading": "Jellyfin server arbetar. Pröva igen inom kort.", + "StartupEmbyServerIsLoading": "Jellyfin Server arbetar. Pröva igen snart.", "SubtitleDownloadFailureForItem": "Nerladdning av undertexter för {0} misslyckades", - "SubtitleDownloadFailureFromForItem": "Undertexter misslyckades att ladda ner {0} för {1}", - "SubtitlesDownloadedForItem": "Undertexter har laddats ner till {0}", + "SubtitleDownloadFailureFromForItem": "Undertexter kunde inte laddas ner från {0} för {1}", "Sync": "Synk", "System": "System", "TvShows": "TV-serier", @@ -89,9 +88,30 @@ "UserOnlineFromDevice": "{0} är uppkopplad från {1}", "UserPasswordChangedWithName": "Lösenordet för {0} har ändrats", "UserPolicyUpdatedWithName": "Användarpolicyn har uppdaterats för {0}", - "UserStartedPlayingItemWithValues": "{0} har börjat spela upp {1}", - "UserStoppedPlayingItemWithValues": "{0} har avslutat uppspelningen av {1}", - "ValueHasBeenAddedToLibrary": "{0} har blivit tillagd till ditt mediabibliotek", + "UserStartedPlayingItemWithValues": "{0} spelar upp {1} på {2}", + "UserStoppedPlayingItemWithValues": "{0} har avslutat uppspelningen av {1} på {2}", + "ValueHasBeenAddedToLibrary": "{0} har lagts till i ditt mediebibliotek", "ValueSpecialEpisodeName": "Specialavsnitt - {0}", - "VersionNumber": "Version {0}" + "VersionNumber": "Version {0}", + "TaskDownloadMissingSubtitlesDescription": "Söker på internet efter saknade undertexter baserad på metadatas konfiguration.", + "TaskDownloadMissingSubtitles": "Ladda ned saknade undertexter", + "TaskRefreshChannelsDescription": "Uppdaterar information för internetkanaler.", + "TaskRefreshChannels": "Uppdatera kanaler", + "TaskCleanTranscodeDescription": "Raderar transkodningsfiler som är mer än en dag gamla.", + "TaskCleanTranscode": "Töm transkodningskatalog", + "TaskUpdatePluginsDescription": "Laddar ned och installerar uppdateringar till insticksprogram som är konfigurerade att uppdateras automatiskt.", + "TaskUpdatePlugins": "Uppdatera insticksprogram", + "TaskRefreshPeopleDescription": "Uppdaterar metadata för skådespelare och regissörer i ditt mediabibliotek.", + "TaskCleanLogsDescription": "Raderar loggfiler som är mer än {0} dagar gamla.", + "TaskCleanLogs": "Töm loggkatalog", + "TaskRefreshLibraryDescription": "Söker igenom ditt mediabibliotek efter nya filer och förnyar metadata.", + "TaskRefreshLibrary": "Genomsök mediabibliotek", + "TaskRefreshChapterImagesDescription": "Skapa miniatyrbilder för videor med kapitel.", + "TaskRefreshChapterImages": "Extrahera kapitelbilder", + "TaskCleanCacheDescription": "Radera cachade filer som systemet inte längre behöver.", + "TaskCleanCache": "Rensa cachekatalog", + "TasksChannelsCategory": "Internetkanaler", + "TasksApplicationCategory": "Applikation", + "TasksLibraryCategory": "Bibliotek", + "TasksMaintenanceCategory": "Underhåll" } diff --git a/Emby.Server.Implementations/Localization/Core/tr.json b/Emby.Server.Implementations/Localization/Core/tr.json index eb1c2623f..62d205516 100644 --- a/Emby.Server.Implementations/Localization/Core/tr.json +++ b/Emby.Server.Implementations/Localization/Core/tr.json @@ -34,8 +34,8 @@ "LabelRunningTimeValue": "Çalışma süresi: {0}", "Latest": "En son", "MessageApplicationUpdated": "Jellyfin Sunucusu güncellendi", - "MessageApplicationUpdatedTo": "Jellyfin Sunucusu {0} olarak güncellendi", - "MessageNamedServerConfigurationUpdatedWithValue": "Sunucu ayarları kısım {0} güncellendi", + "MessageApplicationUpdatedTo": "Jellyfin Sunucusu {0} sürümüne güncellendi", + "MessageNamedServerConfigurationUpdatedWithValue": "Sunucu ayar kısmı {0} güncellendi", "MessageServerConfigurationUpdated": "Sunucu ayarları güncellendi", "MixedContent": "Karışık içerik", "Movies": "Filmler", @@ -76,7 +76,6 @@ "StartupEmbyServerIsLoading": "Jellyfin Sunucusu yükleniyor. Lütfen kısa süre sonra tekrar deneyin.", "SubtitleDownloadFailureForItem": "Subtitles failed to download for {0}", "SubtitleDownloadFailureFromForItem": "{1} için alt yazılar {0} 'dan indirilemedi", - "SubtitlesDownloadedForItem": "{0} için altyazılar indirildi", "Sync": "Eşitle", "System": "Sistem", "TvShows": "Diziler", @@ -93,5 +92,10 @@ "UserStoppedPlayingItemWithValues": "{0}, {2} cihazında {1} izlemeyi bitirdi", "ValueHasBeenAddedToLibrary": "Medya kitaplığınıza {0} eklendi", "ValueSpecialEpisodeName": "Özel - {0}", - "VersionNumber": "Versiyon {0}" + "VersionNumber": "Versiyon {0}", + "TaskCleanCache": "Geçici dosya klasörünü temizle", + "TasksChannelsCategory": "İnternet kanalları", + "TasksApplicationCategory": "Yazılım", + "TasksLibraryCategory": "Kütüphane", + "TasksMaintenanceCategory": "Onarım" } diff --git a/Emby.Server.Implementations/Localization/Core/ur_PK.json b/Emby.Server.Implementations/Localization/Core/ur_PK.json new file mode 100644 index 000000000..0967ef424 --- /dev/null +++ b/Emby.Server.Implementations/Localization/Core/ur_PK.json @@ -0,0 +1 @@ +{} diff --git a/Emby.Server.Implementations/Localization/Core/zh-CN.json b/Emby.Server.Implementations/Localization/Core/zh-CN.json index dd6168614..6b563a9b1 100644 --- a/Emby.Server.Implementations/Localization/Core/zh-CN.json +++ b/Emby.Server.Implementations/Localization/Core/zh-CN.json @@ -3,11 +3,11 @@ "AppDeviceValues": "应用: {0}, 设备: {1}", "Application": "应用程序", "Artists": "艺术家", - "AuthenticationSucceededWithUserName": "{0} 验证成功", + "AuthenticationSucceededWithUserName": "{0} 认证成功", "Books": "书籍", "CameraImageUploadedFrom": "新的相机图像已从 {0} 上传", "Channels": "频道", - "ChapterNameValue": "章节 {0}", + "ChapterNameValue": "第 {0} 集", "Collections": "合集", "DeviceOfflineWithName": "{0} 已断开", "DeviceOnlineWithName": "{0} 已连接", @@ -17,14 +17,14 @@ "Genres": "风格", "HeaderAlbumArtists": "专辑作家", "HeaderCameraUploads": "相机上传", - "HeaderContinueWatching": "继续观看", + "HeaderContinueWatching": "继续观影", "HeaderFavoriteAlbums": "收藏的专辑", "HeaderFavoriteArtists": "最爱的艺术家", "HeaderFavoriteEpisodes": "最爱的剧集", "HeaderFavoriteShows": "最爱的节目", "HeaderFavoriteSongs": "最爱的歌曲", "HeaderLiveTV": "电视直播", - "HeaderNextUp": "下一步", + "HeaderNextUp": "接下来", "HeaderRecordingGroups": "录制组", "HomeVideos": "家庭视频", "Inherit": "继承", @@ -76,7 +76,6 @@ "StartupEmbyServerIsLoading": "Jellyfin 服务器加载中。请稍后再试。", "SubtitleDownloadFailureForItem": "为 {0} 下载字幕失败", "SubtitleDownloadFailureFromForItem": "无法从 {0} 下载 {1} 的字幕", - "SubtitlesDownloadedForItem": "已为 {0} 下载了字幕", "Sync": "同步", "System": "系统", "TvShows": "电视剧", @@ -93,5 +92,27 @@ "UserStoppedPlayingItemWithValues": "{0} 已在 {2} 上停止播放 {1}", "ValueHasBeenAddedToLibrary": "{0} 已添加至您的媒体库中", "ValueSpecialEpisodeName": "特典 - {0}", - "VersionNumber": "版本 {0}" + "VersionNumber": "版本 {0}", + "TaskUpdatePluginsDescription": "为已设置为自动更新的插件下载和安装更新。", + "TaskRefreshPeople": "刷新人员", + "TasksChannelsCategory": "互联网频道", + "TasksLibraryCategory": "媒体库", + "TaskDownloadMissingSubtitlesDescription": "根据元数据设置在互联网上搜索缺少的字幕。", + "TaskDownloadMissingSubtitles": "下载缺少的字幕", + "TaskRefreshChannelsDescription": "刷新互联网频道信息。", + "TaskRefreshChannels": "刷新频道", + "TaskCleanTranscodeDescription": "删除存在超过 1 天的转码文件。", + "TaskCleanTranscode": "清理转码目录", + "TaskUpdatePlugins": "更新插件", + "TaskRefreshPeopleDescription": "更新媒体库中演员和导演的元数据。", + "TaskCleanLogsDescription": "删除存在超过 {0} 天的的日志文件。", + "TaskCleanLogs": "清理日志目录", + "TaskRefreshLibraryDescription": "扫描你的媒体库以获取新文件并刷新元数据。", + "TaskRefreshLibrary": "扫描媒体库", + "TaskRefreshChapterImagesDescription": "为包含剧集的视频提取缩略图。", + "TaskRefreshChapterImages": "提取剧集图片", + "TaskCleanCacheDescription": "删除系统不再需要的缓存文件。", + "TaskCleanCache": "清理缓存目录", + "TasksApplicationCategory": "应用程序", + "TasksMaintenanceCategory": "维护" } diff --git a/Emby.Server.Implementations/Localization/Core/zh-HK.json b/Emby.Server.Implementations/Localization/Core/zh-HK.json index f3d9e5fce..224748e61 100644 --- a/Emby.Server.Implementations/Localization/Core/zh-HK.json +++ b/Emby.Server.Implementations/Localization/Core/zh-HK.json @@ -76,7 +76,6 @@ "StartupEmbyServerIsLoading": "Jellyfin 伺服器載入中,請稍後再試。", "SubtitleDownloadFailureForItem": "Subtitles failed to download for {0}", "SubtitleDownloadFailureFromForItem": "無法從 {0} 下載 {1} 的字幕", - "SubtitlesDownloadedForItem": "已為 {0} 下載了字幕", "Sync": "同步", "System": "System", "TvShows": "電視節目", diff --git a/Emby.Server.Implementations/Localization/Core/zh-TW.json b/Emby.Server.Implementations/Localization/Core/zh-TW.json index acd211f22..21034b76f 100644 --- a/Emby.Server.Implementations/Localization/Core/zh-TW.json +++ b/Emby.Server.Implementations/Localization/Core/zh-TW.json @@ -72,7 +72,6 @@ "Shows": "節目", "Songs": "歌曲", "StartupEmbyServerIsLoading": "Jellyfin Server正在啟動,請稍後再試一次。", - "SubtitlesDownloadedForItem": "已為 {0} 下載字幕", "Sync": "同步", "System": "系統", "TvShows": "電視節目", diff --git a/Emby.Server.Implementations/MediaEncoder/EncodingManager.cs b/Emby.Server.Implementations/MediaEncoder/EncodingManager.cs index 840aca7a6..677d68b4c 100644 --- a/Emby.Server.Implementations/MediaEncoder/EncodingManager.cs +++ b/Emby.Server.Implementations/MediaEncoder/EncodingManager.cs @@ -26,14 +26,20 @@ namespace Emby.Server.Implementations.MediaEncoder private readonly IChapterManager _chapterManager; private readonly ILibraryManager _libraryManager; + /// <summary> + /// The first chapter ticks. + /// </summary> + private static readonly long _firstChapterTicks = TimeSpan.FromSeconds(15).Ticks; + public EncodingManager( + ILogger<EncodingManager> logger, IFileSystem fileSystem, - ILoggerFactory loggerFactory, IMediaEncoder encoder, - IChapterManager chapterManager, ILibraryManager libraryManager) + IChapterManager chapterManager, + ILibraryManager libraryManager) { + _logger = logger; _fileSystem = fileSystem; - _logger = loggerFactory.CreateLogger(nameof(EncodingManager)); _encoder = encoder; _chapterManager = chapterManager; _libraryManager = libraryManager; @@ -97,12 +103,7 @@ namespace Emby.Server.Implementations.MediaEncoder return video.DefaultVideoStreamIndex.HasValue; } - /// <summary> - /// The first chapter ticks - /// </summary> - private static readonly long FirstChapterTicks = TimeSpan.FromSeconds(15).Ticks; - - public async Task<bool> RefreshChapterImages(Video video, IDirectoryService directoryService, List<ChapterInfo> chapters, bool extractImages, bool saveChapters, CancellationToken cancellationToken) + public async Task<bool> RefreshChapterImages(Video video, IDirectoryService directoryService, IReadOnlyList<ChapterInfo> chapters, bool extractImages, bool saveChapters, CancellationToken cancellationToken) { if (!IsEligibleForChapterImageExtraction(video)) { @@ -135,7 +136,7 @@ namespace Emby.Server.Implementations.MediaEncoder try { // Add some time for the first chapter to make sure we don't end up with a black image - var time = chapter.StartPositionTicks == 0 ? TimeSpan.FromTicks(Math.Min(FirstChapterTicks, video.RunTimeTicks ?? 0)) : TimeSpan.FromTicks(chapter.StartPositionTicks); + var time = chapter.StartPositionTicks == 0 ? TimeSpan.FromTicks(Math.Min(_firstChapterTicks, video.RunTimeTicks ?? 0)) : TimeSpan.FromTicks(chapter.StartPositionTicks); var protocol = MediaProtocol.File; @@ -152,9 +153,9 @@ namespace Emby.Server.Implementations.MediaEncoder { _fileSystem.DeleteFile(tempFile); } - catch + catch (IOException ex) { - + _logger.LogError(ex, "Error deleting temporary chapter image encoding file {Path}", tempFile); } chapter.ImagePath = path; @@ -184,7 +185,7 @@ namespace Emby.Server.Implementations.MediaEncoder if (saveChapters && changesMade) { - _chapterManager.SaveChapters(video.Id.ToString(), chapters); + _chapterManager.SaveChapters(video.Id, chapters); } DeleteDeadImages(currentImages, chapters); @@ -199,22 +200,21 @@ namespace Emby.Server.Implementations.MediaEncoder return Path.Combine(GetChapterImagesPath(video), filename); } - private static List<string> GetSavedChapterImages(Video video, IDirectoryService directoryService) + private static IReadOnlyList<string> GetSavedChapterImages(Video video, IDirectoryService directoryService) { var path = GetChapterImagesPath(video); if (!Directory.Exists(path)) { - return new List<string>(); + return Array.Empty<string>(); } try { - return directoryService.GetFilePaths(path) - .ToList(); + return directoryService.GetFilePaths(path); } catch (IOException) { - return new List<string>(); + return Array.Empty<string>(); } } @@ -227,7 +227,7 @@ namespace Emby.Server.Implementations.MediaEncoder foreach (var image in deadImages) { - _logger.LogDebug("Deleting dead chapter image {path}", image); + _logger.LogDebug("Deleting dead chapter image {Path}", image); try { @@ -235,7 +235,7 @@ namespace Emby.Server.Implementations.MediaEncoder } catch (IOException ex) { - _logger.LogError(ex, "Error deleting {path}.", image); + _logger.LogError(ex, "Error deleting {Path}.", image); } } } diff --git a/Emby.Server.Implementations/Networking/NetworkManager.cs b/Emby.Server.Implementations/Networking/NetworkManager.cs index 1d8d3cf39..b3e88b667 100644 --- a/Emby.Server.Implementations/Networking/NetworkManager.cs +++ b/Emby.Server.Implementations/Networking/NetworkManager.cs @@ -500,7 +500,7 @@ namespace Emby.Server.Implementations.Networking { if (ip.Address.Equals(address) && ip.IPv4Mask != null) { - return ip.IPv4Mask; + return ip.IPv4Mask; } } } diff --git a/Emby.Server.Implementations/Playlists/PlaylistManager.cs b/Emby.Server.Implementations/Playlists/PlaylistManager.cs index b26f4026c..9b1510ac9 100644 --- a/Emby.Server.Implementations/Playlists/PlaylistManager.cs +++ b/Emby.Server.Implementations/Playlists/PlaylistManager.cs @@ -8,12 +8,14 @@ using System.Threading.Tasks; using MediaBrowser.Controller.Dto; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities.Audio; +using MediaBrowser.Controller.Extensions; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Playlists; using MediaBrowser.Controller.Providers; using MediaBrowser.Model.Entities; using MediaBrowser.Model.IO; using MediaBrowser.Model.Playlists; +using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Logging; using PlaylistsNET.Content; using PlaylistsNET.Models; @@ -28,21 +30,24 @@ namespace Emby.Server.Implementations.Playlists private readonly ILogger _logger; private readonly IUserManager _userManager; private readonly IProviderManager _providerManager; + private readonly IConfiguration _appConfig; public PlaylistManager( ILibraryManager libraryManager, IFileSystem fileSystem, ILibraryMonitor iLibraryMonitor, - ILoggerFactory loggerFactory, + ILogger<PlaylistManager> logger, IUserManager userManager, - IProviderManager providerManager) + IProviderManager providerManager, + IConfiguration appConfig) { _libraryManager = libraryManager; _fileSystem = fileSystem; _iLibraryMonitor = iLibraryMonitor; - _logger = loggerFactory.CreateLogger(nameof(PlaylistManager)); + _logger = logger; _userManager = userManager; _providerManager = providerManager; + _appConfig = appConfig; } public IEnumerable<Playlist> GetPlaylists(Guid userId) @@ -177,7 +182,7 @@ namespace Emby.Server.Implementations.Playlists return Playlist.GetPlaylistItems(playlistMediaType, items, user, options); } - public void AddToPlaylist(string playlistId, IEnumerable<Guid> itemIds, Guid userId) + public void AddToPlaylist(string playlistId, ICollection<Guid> itemIds, Guid userId) { var user = userId.Equals(Guid.Empty) ? null : _userManager.GetUserById(userId); @@ -187,37 +192,59 @@ namespace Emby.Server.Implementations.Playlists }); } - private void AddToPlaylistInternal(string playlistId, IEnumerable<Guid> itemIds, User user, DtoOptions options) + private void AddToPlaylistInternal(string playlistId, ICollection<Guid> newItemIds, User user, DtoOptions options) { - var playlist = _libraryManager.GetItemById(playlistId) as Playlist; + // Retrieve the existing playlist + var playlist = _libraryManager.GetItemById(playlistId) as Playlist + ?? throw new ArgumentException("No Playlist exists with Id " + playlistId); - if (playlist == null) + // Retrieve all the items to be added to the playlist + var newItems = GetPlaylistItems(newItemIds, playlist.MediaType, user, options) + .Where(i => i.SupportsAddingToPlaylist); + + // Filter out duplicate items, if necessary + if (!_appConfig.DoPlaylistsAllowDuplicates()) { - throw new ArgumentException("No Playlist exists with the supplied Id"); + var existingIds = playlist.LinkedChildren.Select(c => c.ItemId).ToHashSet(); + newItems = newItems + .Where(i => !existingIds.Contains(i.Id)) + .Distinct(); } - var list = new List<LinkedChild>(); - - var items = GetPlaylistItems(itemIds, playlist.MediaType, user, options) - .Where(i => i.SupportsAddingToPlaylist) + // Create a list of the new linked children to add to the playlist + var childrenToAdd = newItems + .Select(i => LinkedChild.Create(i)) .ToList(); - foreach (var item in items) + // Log duplicates that have been ignored, if any + int numDuplicates = newItemIds.Count - childrenToAdd.Count; + if (numDuplicates > 0) { - list.Add(LinkedChild.Create(item)); + _logger.LogWarning("Ignored adding {DuplicateCount} duplicate items to playlist {PlaylistName}.", numDuplicates, playlist.Name); } - var newList = playlist.LinkedChildren.ToList(); - newList.AddRange(list); - playlist.LinkedChildren = newList.ToArray(); + // Do nothing else if there are no items to add to the playlist + if (childrenToAdd.Count == 0) + { + return; + } + + // Create a new array with the updated playlist items + var newLinkedChildren = new LinkedChild[playlist.LinkedChildren.Length + childrenToAdd.Count]; + playlist.LinkedChildren.CopyTo(newLinkedChildren, 0); + childrenToAdd.CopyTo(newLinkedChildren, playlist.LinkedChildren.Length); + // Update the playlist in the repository + playlist.LinkedChildren = newLinkedChildren; playlist.UpdateToRepository(ItemUpdateType.MetadataEdit, CancellationToken.None); + // Update the playlist on disk if (playlist.IsFile) { SavePlaylistFile(playlist); } + // Refresh playlist metadata _providerManager.QueueRefresh( playlist.Id, new MetadataRefreshOptions(new DirectoryService(_fileSystem)) diff --git a/Emby.Server.Implementations/ScheduledTasks/Tasks/ChapterImagesTask.cs b/Emby.Server.Implementations/ScheduledTasks/Tasks/ChapterImagesTask.cs index 5822c467b..ea6a70615 100644 --- a/Emby.Server.Implementations/ScheduledTasks/Tasks/ChapterImagesTask.cs +++ b/Emby.Server.Implementations/ScheduledTasks/Tasks/ChapterImagesTask.cs @@ -15,6 +15,7 @@ using MediaBrowser.Model.Entities; using MediaBrowser.Model.IO; using MediaBrowser.Model.Tasks; using Microsoft.Extensions.Logging; +using MediaBrowser.Model.Globalization; namespace Emby.Server.Implementations.ScheduledTasks { @@ -39,11 +40,19 @@ namespace Emby.Server.Implementations.ScheduledTasks private readonly IEncodingManager _encodingManager; private readonly IFileSystem _fileSystem; + private readonly ILocalizationManager _localization; /// <summary> /// Initializes a new instance of the <see cref="ChapterImagesTask" /> class. /// </summary> - public ChapterImagesTask(ILoggerFactory loggerFactory, ILibraryManager libraryManager, IItemRepository itemRepo, IApplicationPaths appPaths, IEncodingManager encodingManager, IFileSystem fileSystem) + public ChapterImagesTask( + ILoggerFactory loggerFactory, + ILibraryManager libraryManager, + IItemRepository itemRepo, + IApplicationPaths appPaths, + IEncodingManager encodingManager, + IFileSystem fileSystem, + ILocalizationManager localization) { _logger = loggerFactory.CreateLogger(GetType().Name); _libraryManager = libraryManager; @@ -51,6 +60,7 @@ namespace Emby.Server.Implementations.ScheduledTasks _appPaths = appPaths; _encodingManager = encodingManager; _fileSystem = fileSystem; + _localization = localization; } /// <summary> @@ -159,11 +169,11 @@ namespace Emby.Server.Implementations.ScheduledTasks } } - public string Name => "Extract Chapter Images"; + public string Name => _localization.GetLocalizedString("TaskRefreshChapterImages"); - public string Description => "Creates thumbnails for videos that have chapters."; + public string Description => _localization.GetLocalizedString("TaskRefreshChapterImagesDescription"); - public string Category => "Library"; + public string Category => _localization.GetLocalizedString("TasksLibraryCategory"); public string Key => "RefreshChapterImages"; diff --git a/Emby.Server.Implementations/ScheduledTasks/Tasks/DeleteCacheFileTask.cs b/Emby.Server.Implementations/ScheduledTasks/Tasks/DeleteCacheFileTask.cs index b55a59f05..9df7c538b 100644 --- a/Emby.Server.Implementations/ScheduledTasks/Tasks/DeleteCacheFileTask.cs +++ b/Emby.Server.Implementations/ScheduledTasks/Tasks/DeleteCacheFileTask.cs @@ -8,6 +8,7 @@ using MediaBrowser.Common.Configuration; using MediaBrowser.Model.IO; using MediaBrowser.Model.Tasks; using Microsoft.Extensions.Logging; +using MediaBrowser.Model.Globalization; namespace Emby.Server.Implementations.ScheduledTasks.Tasks { @@ -25,15 +26,21 @@ namespace Emby.Server.Implementations.ScheduledTasks.Tasks private readonly ILogger _logger; private readonly IFileSystem _fileSystem; + private readonly ILocalizationManager _localization; /// <summary> /// Initializes a new instance of the <see cref="DeleteCacheFileTask" /> class. /// </summary> - public DeleteCacheFileTask(IApplicationPaths appPaths, ILogger logger, IFileSystem fileSystem) + public DeleteCacheFileTask( + IApplicationPaths appPaths, + ILogger<DeleteCacheFileTask> logger, + IFileSystem fileSystem, + ILocalizationManager localization) { ApplicationPaths = appPaths; _logger = logger; _fileSystem = fileSystem; + _localization = localization; } /// <summary> @@ -158,11 +165,11 @@ namespace Emby.Server.Implementations.ScheduledTasks.Tasks } } - public string Name => "Clean Cache Directory"; + public string Name => _localization.GetLocalizedString("TaskCleanCache"); - public string Description => "Deletes cache files no longer needed by the system."; + public string Description => _localization.GetLocalizedString("TaskCleanCacheDescription"); - public string Category => "Maintenance"; + public string Category => _localization.GetLocalizedString("TasksMaintenanceCategory"); public string Key => "DeleteCacheFiles"; diff --git a/Emby.Server.Implementations/ScheduledTasks/Tasks/DeleteLogFileTask.cs b/Emby.Server.Implementations/ScheduledTasks/Tasks/DeleteLogFileTask.cs index 9f9c6353a..3140aa489 100644 --- a/Emby.Server.Implementations/ScheduledTasks/Tasks/DeleteLogFileTask.cs +++ b/Emby.Server.Implementations/ScheduledTasks/Tasks/DeleteLogFileTask.cs @@ -6,6 +6,7 @@ using System.Threading.Tasks; using MediaBrowser.Common.Configuration; using MediaBrowser.Model.IO; using MediaBrowser.Model.Tasks; +using MediaBrowser.Model.Globalization; namespace Emby.Server.Implementations.ScheduledTasks.Tasks { @@ -21,15 +22,17 @@ namespace Emby.Server.Implementations.ScheduledTasks.Tasks private IConfigurationManager ConfigurationManager { get; set; } private readonly IFileSystem _fileSystem; + private readonly ILocalizationManager _localization; /// <summary> /// Initializes a new instance of the <see cref="DeleteLogFileTask" /> class. /// </summary> /// <param name="configurationManager">The configuration manager.</param> - public DeleteLogFileTask(IConfigurationManager configurationManager, IFileSystem fileSystem) + public DeleteLogFileTask(IConfigurationManager configurationManager, IFileSystem fileSystem, ILocalizationManager localization) { ConfigurationManager = configurationManager; _fileSystem = fileSystem; + _localization = localization; } /// <summary> @@ -79,11 +82,11 @@ namespace Emby.Server.Implementations.ScheduledTasks.Tasks return Task.CompletedTask; } - public string Name => "Clean Log Directory"; + public string Name => _localization.GetLocalizedString("TaskCleanLogs"); - public string Description => string.Format("Deletes log files that are more than {0} days old.", ConfigurationManager.CommonConfiguration.LogFileRetentionDays); + public string Description => string.Format(_localization.GetLocalizedString("TaskCleanLogsDescription"), ConfigurationManager.CommonConfiguration.LogFileRetentionDays); - public string Category => "Maintenance"; + public string Category => _localization.GetLocalizedString("TasksMaintenanceCategory"); public string Key => "CleanLogFiles"; diff --git a/Emby.Server.Implementations/ScheduledTasks/Tasks/DeleteTranscodeFileTask.cs b/Emby.Server.Implementations/ScheduledTasks/Tasks/DeleteTranscodeFileTask.cs index 9ff490f18..1d133dcda 100644 --- a/Emby.Server.Implementations/ScheduledTasks/Tasks/DeleteTranscodeFileTask.cs +++ b/Emby.Server.Implementations/ScheduledTasks/Tasks/DeleteTranscodeFileTask.cs @@ -8,6 +8,7 @@ using MediaBrowser.Common.Configuration; using MediaBrowser.Model.IO; using MediaBrowser.Model.Tasks; using Microsoft.Extensions.Logging; +using MediaBrowser.Model.Globalization; namespace Emby.Server.Implementations.ScheduledTasks.Tasks { @@ -19,15 +20,21 @@ namespace Emby.Server.Implementations.ScheduledTasks.Tasks private readonly ILogger _logger; private readonly IConfigurationManager _configurationManager; private readonly IFileSystem _fileSystem; + private readonly ILocalizationManager _localization; /// <summary> /// Initializes a new instance of the <see cref="DeleteTranscodeFileTask" /> class. /// </summary> - public DeleteTranscodeFileTask(ILogger logger, IFileSystem fileSystem, IConfigurationManager configurationManager) + public DeleteTranscodeFileTask( + ILogger<DeleteTranscodeFileTask> logger, + IFileSystem fileSystem, + IConfigurationManager configurationManager, + ILocalizationManager localization) { _logger = logger; _fileSystem = fileSystem; _configurationManager = configurationManager; + _localization = localization; } /// <summary> @@ -125,11 +132,11 @@ namespace Emby.Server.Implementations.ScheduledTasks.Tasks } } - public string Name => "Clean Transcode Directory"; + public string Name => _localization.GetLocalizedString("TaskCleanTranscode"); - public string Description => "Deletes transcode files more than one day old."; + public string Description => _localization.GetLocalizedString("TaskCleanTranscodeDescription"); - public string Category => "Maintenance"; + public string Category => _localization.GetLocalizedString("TasksMaintenanceCategory"); public string Key => "DeleteTranscodeFiles"; diff --git a/Emby.Server.Implementations/ScheduledTasks/Tasks/PeopleValidationTask.cs b/Emby.Server.Implementations/ScheduledTasks/Tasks/PeopleValidationTask.cs index eaf17aace..63f867bf6 100644 --- a/Emby.Server.Implementations/ScheduledTasks/Tasks/PeopleValidationTask.cs +++ b/Emby.Server.Implementations/ScheduledTasks/Tasks/PeopleValidationTask.cs @@ -5,6 +5,7 @@ using System.Threading.Tasks; using MediaBrowser.Controller; using MediaBrowser.Controller.Library; using MediaBrowser.Model.Tasks; +using MediaBrowser.Model.Globalization; namespace Emby.Server.Implementations.ScheduledTasks { @@ -19,16 +20,18 @@ namespace Emby.Server.Implementations.ScheduledTasks private readonly ILibraryManager _libraryManager; private readonly IServerApplicationHost _appHost; + private readonly ILocalizationManager _localization; /// <summary> /// Initializes a new instance of the <see cref="PeopleValidationTask" /> class. /// </summary> /// <param name="libraryManager">The library manager.</param> /// <param name="appHost">The server application host</param> - public PeopleValidationTask(ILibraryManager libraryManager, IServerApplicationHost appHost) + public PeopleValidationTask(ILibraryManager libraryManager, IServerApplicationHost appHost, ILocalizationManager localization) { _libraryManager = libraryManager; _appHost = appHost; + _localization = localization; } /// <summary> @@ -57,11 +60,11 @@ namespace Emby.Server.Implementations.ScheduledTasks return _libraryManager.ValidatePeople(cancellationToken, progress); } - public string Name => "Refresh People"; + public string Name => _localization.GetLocalizedString("TaskRefreshPeople"); - public string Description => "Updates metadata for actors and directors in your media library."; + public string Description => _localization.GetLocalizedString("TaskRefreshPeopleDescription"); - public string Category => "Library"; + public string Category => _localization.GetLocalizedString("TasksLibraryCategory"); public string Key => "RefreshPeople"; diff --git a/Emby.Server.Implementations/ScheduledTasks/Tasks/PluginUpdateTask.cs b/Emby.Server.Implementations/ScheduledTasks/Tasks/PluginUpdateTask.cs index 992b77c25..6a1afced7 100644 --- a/Emby.Server.Implementations/ScheduledTasks/Tasks/PluginUpdateTask.cs +++ b/Emby.Server.Implementations/ScheduledTasks/Tasks/PluginUpdateTask.cs @@ -8,6 +8,7 @@ using MediaBrowser.Common.Updates; using MediaBrowser.Model.Net; using MediaBrowser.Model.Tasks; using Microsoft.Extensions.Logging; +using MediaBrowser.Model.Globalization; namespace Emby.Server.Implementations.ScheduledTasks { @@ -22,11 +23,13 @@ namespace Emby.Server.Implementations.ScheduledTasks private readonly ILogger _logger; private readonly IInstallationManager _installationManager; + private readonly ILocalizationManager _localization; - public PluginUpdateTask(ILogger logger, IInstallationManager installationManager) + public PluginUpdateTask(ILogger<PluginUpdateTask> logger, IInstallationManager installationManager, ILocalizationManager localization) { _logger = logger; _installationManager = installationManager; + _localization = localization; } /// <summary> @@ -52,9 +55,8 @@ namespace Emby.Server.Implementations.ScheduledTasks { progress.Report(0); - var packagesToInstall = await _installationManager.GetAvailablePluginUpdates(cancellationToken) - .ToListAsync(cancellationToken) - .ConfigureAwait(false); + var packageFetchTask = _installationManager.GetAvailablePluginUpdates(cancellationToken); + var packagesToInstall = (await packageFetchTask.ConfigureAwait(false)).ToList(); progress.Report(10); @@ -96,13 +98,13 @@ namespace Emby.Server.Implementations.ScheduledTasks } /// <inheritdoc /> - public string Name => "Update Plugins"; + public string Name => _localization.GetLocalizedString("TaskUpdatePlugins"); /// <inheritdoc /> - public string Description => "Downloads and installs updates for plugins that are configured to update automatically."; + public string Description => _localization.GetLocalizedString("TaskUpdatePluginsDescription"); /// <inheritdoc /> - public string Category => "Application"; + public string Category => _localization.GetLocalizedString("TasksApplicationCategory"); /// <inheritdoc /> public string Key => "PluginUpdates"; diff --git a/Emby.Server.Implementations/ScheduledTasks/Tasks/RefreshMediaLibraryTask.cs b/Emby.Server.Implementations/ScheduledTasks/Tasks/RefreshMediaLibraryTask.cs index 073678019..74cb01444 100644 --- a/Emby.Server.Implementations/ScheduledTasks/Tasks/RefreshMediaLibraryTask.cs +++ b/Emby.Server.Implementations/ScheduledTasks/Tasks/RefreshMediaLibraryTask.cs @@ -6,6 +6,7 @@ using Emby.Server.Implementations.Library; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Library; using MediaBrowser.Model.Tasks; +using MediaBrowser.Model.Globalization; namespace Emby.Server.Implementations.ScheduledTasks { @@ -19,15 +20,17 @@ namespace Emby.Server.Implementations.ScheduledTasks /// </summary> private readonly ILibraryManager _libraryManager; private readonly IServerConfigurationManager _config; + private readonly ILocalizationManager _localization; /// <summary> /// Initializes a new instance of the <see cref="RefreshMediaLibraryTask" /> class. /// </summary> /// <param name="libraryManager">The library manager.</param> - public RefreshMediaLibraryTask(ILibraryManager libraryManager, IServerConfigurationManager config) + public RefreshMediaLibraryTask(ILibraryManager libraryManager, IServerConfigurationManager config, ILocalizationManager localization) { _libraryManager = libraryManager; _config = config; + _localization = localization; } /// <summary> @@ -38,7 +41,8 @@ namespace Emby.Server.Implementations.ScheduledTasks { yield return new TaskTriggerInfo { - Type = TaskTriggerInfo.TriggerInterval, IntervalTicks = TimeSpan.FromHours(12).Ticks + Type = TaskTriggerInfo.TriggerInterval, + IntervalTicks = TimeSpan.FromHours(12).Ticks }; } @@ -57,11 +61,11 @@ namespace Emby.Server.Implementations.ScheduledTasks return ((LibraryManager)_libraryManager).ValidateMediaLibraryInternal(progress, cancellationToken); } - public string Name => "Scan Media Library"; + public string Name => _localization.GetLocalizedString("TaskRefreshLibrary"); - public string Description => "Scans your media library for new files and refreshes metadata."; + public string Description => _localization.GetLocalizedString("TaskRefreshLibraryDescription"); - public string Category => "Library"; + public string Category => _localization.GetLocalizedString("TasksLibraryCategory"); public string Key => "RefreshLibrary"; diff --git a/Emby.Server.Implementations/Services/ServiceController.cs b/Emby.Server.Implementations/Services/ServiceController.cs index d963f9043..e24a95dbb 100644 --- a/Emby.Server.Implementations/Services/ServiceController.cs +++ b/Emby.Server.Implementations/Services/ServiceController.cs @@ -3,14 +3,27 @@ using System.Collections.Generic; using System.Threading.Tasks; using Emby.Server.Implementations.HttpServer; using MediaBrowser.Model.Services; +using Microsoft.Extensions.Logging; namespace Emby.Server.Implementations.Services { public delegate object ActionInvokerFn(object intance, object request); + public delegate void VoidActionInvokerFn(object intance, object request); public class ServiceController { + private readonly ILogger _logger; + + /// <summary> + /// Initializes a new instance of the <see cref="ServiceController"/> class. + /// </summary> + /// <param name="logger">The <see cref="ServiceController"/> logger.</param> + public ServiceController(ILogger<ServiceController> logger) + { + _logger = logger; + } + public void Init(HttpListenerHost appHost, IEnumerable<Type> serviceTypes) { foreach (var serviceType in serviceTypes) @@ -21,6 +34,13 @@ namespace Emby.Server.Implementations.Services public void RegisterService(HttpListenerHost appHost, Type serviceType) { + // Make sure the provided type implements IService + if (!typeof(IService).IsAssignableFrom(serviceType)) + { + _logger.LogWarning("Tried to register a service that does not implement IService: {ServiceType}", serviceType); + return; + } + var processedReqs = new HashSet<Type>(); var actions = ServiceExecGeneral.Reset(serviceType); diff --git a/Emby.Server.Implementations/Services/SwaggerService.cs b/Emby.Server.Implementations/Services/SwaggerService.cs index c30f32af9..5177251c3 100644 --- a/Emby.Server.Implementations/Services/SwaggerService.cs +++ b/Emby.Server.Implementations/Services/SwaggerService.cs @@ -1,9 +1,9 @@ using System; using System.Collections.Generic; using System.Linq; +using Emby.Server.Implementations.HttpServer; using MediaBrowser.Controller.Net; using MediaBrowser.Model.Services; -using Emby.Server.Implementations.HttpServer; namespace Emby.Server.Implementations.Services { @@ -241,7 +241,7 @@ namespace Emby.Server.Implementations.Services responses = responses, - security = new [] { apiKeySecurity } + security = new[] { apiKeySecurity } }; } diff --git a/Emby.Server.Implementations/Session/SessionManager.cs b/Emby.Server.Implementations/Session/SessionManager.cs index e2cee5b03..de768333d 100644 --- a/Emby.Server.Implementations/Session/SessionManager.cs +++ b/Emby.Server.Implementations/Session/SessionManager.cs @@ -62,8 +62,43 @@ namespace Emby.Server.Implementations.Session private readonly ConcurrentDictionary<string, SessionInfo> _activeConnections = new ConcurrentDictionary<string, SessionInfo>(StringComparer.OrdinalIgnoreCase); + private Timer _idleTimer; + + private DtoOptions _itemInfoDtoOptions; + private bool _disposed = false; + + public SessionManager( + ILogger<SessionManager> logger, + IUserDataManager userDataManager, + ILibraryManager libraryManager, + IUserManager userManager, + IMusicManager musicManager, + IDtoService dtoService, + IImageProcessor imageProcessor, + IServerApplicationHost appHost, + IAuthenticationRepository authRepo, + IDeviceManager deviceManager, + IMediaSourceManager mediaSourceManager) + { + _logger = logger; + _userDataManager = userDataManager; + _libraryManager = libraryManager; + _userManager = userManager; + _musicManager = musicManager; + _dtoService = dtoService; + _imageProcessor = imageProcessor; + _appHost = appHost; + _authRepo = authRepo; + _deviceManager = deviceManager; + _mediaSourceManager = mediaSourceManager; + + _deviceManager.DeviceOptionsUpdated += OnDeviceManagerDeviceOptionsUpdated; + } + + /// <inheritdoc /> public event EventHandler<GenericEventArgs<AuthenticationRequest>> AuthenticationFailed; + /// <inheritdoc /> public event EventHandler<GenericEventArgs<AuthenticationResult>> AuthenticationSucceeded; /// <summary> @@ -81,40 +116,23 @@ namespace Emby.Server.Implementations.Session /// </summary> public event EventHandler<PlaybackStopEventArgs> PlaybackStopped; + /// <inheritdoc /> public event EventHandler<SessionEventArgs> SessionStarted; + /// <inheritdoc /> public event EventHandler<SessionEventArgs> CapabilitiesChanged; + /// <inheritdoc /> public event EventHandler<SessionEventArgs> SessionEnded; + /// <inheritdoc /> public event EventHandler<SessionEventArgs> SessionActivity; - public SessionManager( - IUserDataManager userDataManager, - ILoggerFactory loggerFactory, - ILibraryManager libraryManager, - IUserManager userManager, - IMusicManager musicManager, - IDtoService dtoService, - IImageProcessor imageProcessor, - IServerApplicationHost appHost, - IAuthenticationRepository authRepo, - IDeviceManager deviceManager, - IMediaSourceManager mediaSourceManager) - { - _userDataManager = userDataManager; - _logger = loggerFactory.CreateLogger(nameof(SessionManager)); - _libraryManager = libraryManager; - _userManager = userManager; - _musicManager = musicManager; - _dtoService = dtoService; - _imageProcessor = imageProcessor; - _appHost = appHost; - _authRepo = authRepo; - _deviceManager = deviceManager; - _mediaSourceManager = mediaSourceManager; - _deviceManager.DeviceOptionsUpdated += OnDeviceManagerDeviceOptionsUpdated; - } + /// <summary> + /// Gets all connections. + /// </summary> + /// <value>All connections.</value> + public IEnumerable<SessionInfo> Sessions => _activeConnections.Values.OrderByDescending(c => c.LastActivityDate); private void OnDeviceManagerDeviceOptionsUpdated(object sender, GenericEventArgs<Tuple<string, DeviceOptions>> e) { @@ -135,14 +153,17 @@ namespace Emby.Server.Implementations.Session } } - private bool _disposed = false; - + /// <inheritdoc /> public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } + /// <summary> + /// Releases unmanaged and optionally managed resources. + /// </summary> + /// <param name="disposing"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param> protected virtual void Dispose(bool disposing) { if (_disposed) @@ -152,15 +173,17 @@ namespace Emby.Server.Implementations.Session if (disposing) { - // TODO: dispose stuff + _idleTimer?.Dispose(); } + _idleTimer = null; + _deviceManager.DeviceOptionsUpdated -= OnDeviceManagerDeviceOptionsUpdated; _disposed = true; } - public void CheckDisposed() + private void CheckDisposed() { if (_disposed) { @@ -168,12 +191,6 @@ namespace Emby.Server.Implementations.Session } } - /// <summary> - /// Gets all connections. - /// </summary> - /// <value>All connections.</value> - public IEnumerable<SessionInfo> Sessions => _activeConnections.Values.OrderByDescending(c => c.LastActivityDate); - private void OnSessionStarted(SessionInfo info) { if (!string.IsNullOrEmpty(info.DeviceId)) @@ -204,13 +221,13 @@ namespace Emby.Server.Implementations.Session new SessionEventArgs { SessionInfo = info - }, _logger); info.Dispose(); } + /// <inheritdoc /> public void UpdateDeviceName(string sessionId, string deviceName) { var session = GetSession(sessionId); @@ -230,7 +247,6 @@ namespace Emby.Server.Implementations.Session /// <param name="remoteEndPoint">The remote end point.</param> /// <param name="user">The user.</param> /// <returns>SessionInfo.</returns> - /// <exception cref="ArgumentNullException">user</exception> public SessionInfo LogSessionActivity( string appName, string appVersion, @@ -268,14 +284,7 @@ namespace Emby.Server.Implementations.Session if ((activityDate - userLastActivityDate).TotalSeconds > 60) { - try - { - _userManager.UpdateUser(user); - } - catch (Exception ex) - { - _logger.LogError("Error updating user", ex); - } + _userManager.UpdateUser(user); } } @@ -292,18 +301,20 @@ namespace Emby.Server.Implementations.Session return session; } + /// <inheritdoc /> public void CloseIfNeeded(SessionInfo session) { if (!session.SessionControllers.Any(i => i.IsSessionActive)) { var key = GetSessionKey(session.Client, session.DeviceId); - _activeConnections.TryRemove(key, out var removed); + _activeConnections.TryRemove(key, out _); OnSessionEnded(session); } } + /// <inheritdoc /> public void ReportSessionEnded(string sessionId) { CheckDisposed(); @@ -313,7 +324,7 @@ namespace Emby.Server.Implementations.Session { var key = GetSessionKey(session.Client, session.DeviceId); - _activeConnections.TryRemove(key, out var removed); + _activeConnections.TryRemove(key, out _); OnSessionEnded(session); } @@ -344,7 +355,7 @@ namespace Emby.Server.Implementations.Session var runtimeTicks = libraryItem.RunTimeTicks; MediaSourceInfo mediaSource = null; - if (libraryItem is IHasMediaSources hasMediaSources) + if (libraryItem is IHasMediaSources) { mediaSource = await GetMediaSource(libraryItem, info.MediaSourceId, info.LiveStreamId).ConfigureAwait(false); @@ -396,7 +407,6 @@ namespace Emby.Server.Implementations.Session /// Removes the now playing item id. /// </summary> /// <param name="session">The session.</param> - /// <exception cref="ArgumentNullException">item</exception> private void RemoveNowPlayingItem(SessionInfo session) { session.NowPlayingItem = null; @@ -409,9 +419,7 @@ namespace Emby.Server.Implementations.Session } private static string GetSessionKey(string appName, string deviceId) - { - return appName + deviceId; - } + => appName + deviceId; /// <summary> /// Gets the connection. @@ -431,6 +439,7 @@ namespace Emby.Server.Implementations.Session { throw new ArgumentNullException(nameof(deviceId)); } + var key = GetSessionKey(appName, deviceId); CheckDisposed(); @@ -503,7 +512,7 @@ namespace Emby.Server.Implementations.Session { var users = new List<User>(); - if (!session.UserId.Equals(Guid.Empty)) + if (session.UserId != Guid.Empty) { var user = _userManager.GetUserById(session.UserId); @@ -522,8 +531,6 @@ namespace Emby.Server.Implementations.Session return users; } - private Timer _idleTimer; - private void StartIdleCheckTimer() { if (_idleTimer == null) @@ -599,11 +606,11 @@ namespace Emby.Server.Implementations.Session } /// <summary> - /// Used to report that playback has started for an item + /// Used to report that playback has started for an item. /// </summary> /// <param name="info">The info.</param> /// <returns>Task.</returns> - /// <exception cref="ArgumentNullException">info</exception> + /// <exception cref="ArgumentNullException"><c>info</c> is <c>null</c>.</exception> public async Task OnPlaybackStart(PlaybackStartInfo info) { CheckDisposed(); @@ -615,7 +622,7 @@ namespace Emby.Server.Implementations.Session var session = GetSession(info.SessionId); - var libraryItem = info.ItemId.Equals(Guid.Empty) + var libraryItem = info.ItemId == Guid.Empty ? null : GetNowPlayingItem(session, info.ItemId); @@ -653,7 +660,6 @@ namespace Emby.Server.Implementations.Session ClientName = session.Client, DeviceId = session.DeviceId, Session = session - }, _logger); @@ -684,6 +690,7 @@ namespace Emby.Server.Implementations.Session _userDataManager.SaveUserData(user, item, data, UserDataSaveReason.PlaybackStart, CancellationToken.None); } + /// <inheritdoc /> public Task OnPlaybackProgress(PlaybackProgressInfo info) { return OnPlaybackProgress(info, false); @@ -857,7 +864,7 @@ namespace Emby.Server.Implementations.Session { MediaSourceInfo mediaSource = null; - if (libraryItem is IHasMediaSources hasMediaSources) + if (libraryItem is IHasMediaSources) { mediaSource = await GetMediaSource(libraryItem, info.MediaSourceId, info.LiveStreamId).ConfigureAwait(false); } @@ -966,13 +973,17 @@ namespace Emby.Server.Implementations.Session /// <param name="sessionId">The session identifier.</param> /// <param name="throwOnMissing">if set to <c>true</c> [throw on missing].</param> /// <returns>SessionInfo.</returns> - /// <exception cref="ResourceNotFoundException">sessionId</exception> + /// <exception cref="ResourceNotFoundException"> + /// No session with an Id equal to <c>sessionId</c> was found + /// and <c>throwOnMissing</c> is <c>true</c>. + /// </exception> private SessionInfo GetSession(string sessionId, bool throwOnMissing = true) { var session = Sessions.FirstOrDefault(i => string.Equals(i.Id, sessionId, StringComparison.Ordinal)); if (session == null && throwOnMissing) { - throw new ResourceNotFoundException(string.Format("Session {0} not found.", sessionId)); + throw new ResourceNotFoundException( + string.Format(CultureInfo.InvariantCulture, "Session {0} not found.", sessionId)); } return session; @@ -985,12 +996,14 @@ namespace Emby.Server.Implementations.Session if (session == null) { - throw new ResourceNotFoundException(string.Format("Session {0} not found.", sessionId)); + throw new ResourceNotFoundException( + string.Format(CultureInfo.InvariantCulture, "Session {0} not found.", sessionId)); } return session; } + /// <inheritdoc /> public Task SendMessageCommand(string controllingSessionId, string sessionId, MessageCommand command, CancellationToken cancellationToken) { CheckDisposed(); @@ -1011,6 +1024,7 @@ namespace Emby.Server.Implementations.Session return SendGeneralCommand(controllingSessionId, sessionId, generalCommand, cancellationToken); } + /// <inheritdoc /> public Task SendGeneralCommand(string controllingSessionId, string sessionId, GeneralCommand command, CancellationToken cancellationToken) { CheckDisposed(); @@ -1055,6 +1069,7 @@ namespace Emby.Server.Implementations.Session return Task.WhenAll(GetTasks()); } + /// <inheritdoc /> public async Task SendPlayCommand(string controllingSessionId, string sessionId, PlayRequest command, CancellationToken cancellationToken) { CheckDisposed(); @@ -1096,7 +1111,8 @@ namespace Emby.Server.Implementations.Session { if (items.Any(i => i.GetPlayAccess(user) != PlayAccess.Full)) { - throw new ArgumentException(string.Format("{0} is not allowed to play media.", user.Name)); + throw new ArgumentException( + string.Format(CultureInfo.InvariantCulture, "{0} is not allowed to play media.", user.Name)); } } @@ -1204,6 +1220,7 @@ namespace Emby.Server.Implementations.Session return _musicManager.GetInstantMixFromItem(item, user, new DtoOptions(false) { EnableImages = false }); } + /// <inheritdoc /> public Task SendBrowseCommand(string controllingSessionId, string sessionId, BrowseRequest command, CancellationToken cancellationToken) { var generalCommand = new GeneralCommand @@ -1220,6 +1237,7 @@ namespace Emby.Server.Implementations.Session return SendGeneralCommand(controllingSessionId, sessionId, generalCommand, cancellationToken); } + /// <inheritdoc /> public Task SendPlaystateCommand(string controllingSessionId, string sessionId, PlaystateRequest command, CancellationToken cancellationToken) { CheckDisposed(); @@ -1303,12 +1321,12 @@ namespace Emby.Server.Implementations.Session var session = GetSession(sessionId); - if (session.UserId.Equals(userId)) + if (session.UserId == userId) { throw new ArgumentException("The requested user is already the primary user of the session."); } - if (session.AdditionalUsers.All(i => !i.UserId.Equals(userId))) + if (session.AdditionalUsers.All(i => i.UserId != userId)) { var user = _userManager.GetUserById(userId); @@ -1383,6 +1401,16 @@ namespace Emby.Server.Implementations.Session user = _userManager.GetUserByName(request.Username); } + if (enforcePassword) + { + user = await _userManager.AuthenticateUser( + request.Username, + request.Password, + request.PasswordSha1, + request.RemoteEndPoint, + true).ConfigureAwait(false); + } + if (user == null) { AuthenticationFailed?.Invoke(this, new GenericEventArgs<AuthenticationRequest>(request)); @@ -1395,16 +1423,6 @@ namespace Emby.Server.Implementations.Session throw new SecurityException("User is not allowed access from this device."); } - if (enforcePassword) - { - user = await _userManager.AuthenticateUser( - request.Username, - request.Password, - request.PasswordSha1, - request.RemoteEndPoint, - true).ConfigureAwait(false); - } - var token = GetAuthorizationToken(user, request.DeviceId, request.App, request.AppVersion, request.DeviceName); var session = LogSessionActivity( @@ -1430,12 +1448,13 @@ namespace Emby.Server.Implementations.Session private string GetAuthorizationToken(User user, string deviceId, string app, string appVersion, string deviceName) { - var existing = _authRepo.Get(new AuthenticationInfoQuery - { - DeviceId = deviceId, - UserId = user.Id, - Limit = 1 - }).Items.FirstOrDefault(); + var existing = _authRepo.Get( + new AuthenticationInfoQuery + { + DeviceId = deviceId, + UserId = user.Id, + Limit = 1 + }).Items.FirstOrDefault(); var allExistingForDevice = _authRepo.Get( new AuthenticationInfoQuery @@ -1460,7 +1479,7 @@ namespace Emby.Server.Implementations.Session if (existing != null) { - _logger.LogInformation("Reissuing access token: " + existing.AccessToken); + _logger.LogInformation("Reissuing access token: {Token}", existing.AccessToken); return existing.AccessToken; } @@ -1485,6 +1504,7 @@ namespace Emby.Server.Implementations.Session return newToken.AccessToken; } + /// <inheritdoc /> public void Logout(string accessToken) { CheckDisposed(); @@ -1494,18 +1514,20 @@ namespace Emby.Server.Implementations.Session throw new ArgumentNullException(nameof(accessToken)); } - var existing = _authRepo.Get(new AuthenticationInfoQuery - { - Limit = 1, - AccessToken = accessToken - }).Items.FirstOrDefault(); + var existing = _authRepo.Get( + new AuthenticationInfoQuery + { + Limit = 1, + AccessToken = accessToken + }).Items; - if (existing != null) + if (existing.Count > 0) { - Logout(existing); + Logout(existing[0]); } } + /// <inheritdoc /> public void Logout(AuthenticationInfo existing) { CheckDisposed(); @@ -1531,6 +1553,7 @@ namespace Emby.Server.Implementations.Session } } + /// <inheritdoc /> public void RevokeUserTokens(Guid userId, string currentAccessToken) { CheckDisposed(); @@ -1549,6 +1572,7 @@ namespace Emby.Server.Implementations.Session } } + /// <inheritdoc /> public void RevokeToken(string token) { Logout(token); @@ -1605,8 +1629,6 @@ namespace Emby.Server.Implementations.Session _deviceManager.SaveCapabilities(deviceId, capabilities); } - private DtoOptions _itemInfoDtoOptions; - /// <summary> /// Converts a BaseItem to a BaseItemInfo. /// </summary> @@ -1683,6 +1705,7 @@ namespace Emby.Server.Implementations.Session } } + /// <inheritdoc /> public void ReportNowViewingItem(string sessionId, string itemId) { if (string.IsNullOrEmpty(itemId)) @@ -1697,6 +1720,7 @@ namespace Emby.Server.Implementations.Session ReportNowViewingItem(sessionId, info); } + /// <inheritdoc /> public void ReportNowViewingItem(string sessionId, BaseItemDto item) { var session = GetSession(sessionId); @@ -1704,6 +1728,7 @@ namespace Emby.Server.Implementations.Session session.NowViewingItem = item; } + /// <inheritdoc /> public void ReportTranscodingInfo(string deviceId, TranscodingInfo info) { var session = Sessions.FirstOrDefault(i => @@ -1715,11 +1740,13 @@ namespace Emby.Server.Implementations.Session } } + /// <inheritdoc /> public void ClearTranscodingInfo(string deviceId) { ReportTranscodingInfo(deviceId, null); } + /// <inheritdoc /> public SessionInfo GetSession(string deviceId, string client, string version) { return Sessions.FirstOrDefault(i => @@ -1727,6 +1754,7 @@ namespace Emby.Server.Implementations.Session && string.Equals(i.Client, client, StringComparison.OrdinalIgnoreCase)); } + /// <inheritdoc /> public SessionInfo GetSessionByAuthenticationToken(AuthenticationInfo info, string deviceId, string remoteEndpoint, string appVersion) { if (info == null) @@ -1759,23 +1787,24 @@ namespace Emby.Server.Implementations.Session return LogSessionActivity(appName, appVersion, deviceId, deviceName, remoteEndpoint, user); } + /// <inheritdoc /> public SessionInfo GetSessionByAuthenticationToken(string token, string deviceId, string remoteEndpoint) { - var result = _authRepo.Get(new AuthenticationInfoQuery + var items = _authRepo.Get(new AuthenticationInfoQuery { - AccessToken = token - }); - - var info = result.Items.FirstOrDefault(); + AccessToken = token, + Limit = 1 + }).Items; - if (info == null) + if (items.Count == 0) { return null; } - return GetSessionByAuthenticationToken(info, deviceId, remoteEndpoint, null); + return GetSessionByAuthenticationToken(items[0], deviceId, remoteEndpoint, null); } + /// <inheritdoc /> public Task SendMessageToAdminSessions<T>(string name, T data, CancellationToken cancellationToken) { CheckDisposed(); @@ -1785,6 +1814,7 @@ namespace Emby.Server.Implementations.Session return SendMessageToUserSessions(adminUserIds, name, data, cancellationToken); } + /// <inheritdoc /> public Task SendMessageToUserSessions<T>(List<Guid> userIds, string name, Func<T> dataFn, CancellationToken cancellationToken) { CheckDisposed(); @@ -1796,11 +1826,10 @@ namespace Emby.Server.Implementations.Session return Task.CompletedTask; } - var data = dataFn(); - - return SendMessageToSessions(sessions, name, data, cancellationToken); + return SendMessageToSessions(sessions, name, dataFn(), cancellationToken); } + /// <inheritdoc /> public Task SendMessageToUserSessions<T>(List<Guid> userIds, string name, T data, CancellationToken cancellationToken) { CheckDisposed(); @@ -1809,6 +1838,7 @@ namespace Emby.Server.Implementations.Session return SendMessageToSessions(sessions, name, data, cancellationToken); } + /// <inheritdoc /> public Task SendMessageToUserDeviceSessions<T>(string deviceId, string name, T data, CancellationToken cancellationToken) { CheckDisposed(); @@ -1817,22 +1847,5 @@ namespace Emby.Server.Implementations.Session return SendMessageToSessions(sessions, name, data, cancellationToken); } - - public Task SendMessageToUserDeviceAndAdminSessions<T>(string deviceId, string name, T data, CancellationToken cancellationToken) - { - CheckDisposed(); - - var sessions = Sessions - .Where(i => string.Equals(i.DeviceId, deviceId, StringComparison.OrdinalIgnoreCase) || IsAdminSession(i)); - - return SendMessageToSessions(sessions, name, data, cancellationToken); - } - - private bool IsAdminSession(SessionInfo s) - { - var user = _userManager.GetUserById(s.UserId); - - return user != null && user.Policy.IsAdministrator; - } } } diff --git a/Emby.Server.Implementations/SocketSharp/WebSocketSharpListener.cs b/Emby.Server.Implementations/SocketSharp/WebSocketSharpListener.cs index 2e12a19fd..b85750c9b 100644 --- a/Emby.Server.Implementations/SocketSharp/WebSocketSharpListener.cs +++ b/Emby.Server.Implementations/SocketSharp/WebSocketSharpListener.cs @@ -21,15 +21,14 @@ namespace Emby.Server.Implementations.SocketSharp private CancellationTokenSource _disposeCancellationTokenSource = new CancellationTokenSource(); private CancellationToken _disposeCancellationToken; - public WebSocketSharpListener( - ILogger logger) + public WebSocketSharpListener(ILogger<WebSocketSharpListener> logger) { _logger = logger; - _disposeCancellationToken = _disposeCancellationTokenSource.Token; } public Func<Exception, IRequest, bool, bool, Task> ErrorHandler { get; set; } + public Func<IHttpRequest, string, string, string, CancellationToken, Task> RequestHandler { get; set; } public Action<WebSocketConnectEventArgs> WebSocketConnected { get; set; } diff --git a/Emby.Server.Implementations/Sorting/AlphanumComparator.cs b/Emby.Server.Implementations/Sorting/AlphanumComparator.cs deleted file mode 100644 index 2e00c24d7..000000000 --- a/Emby.Server.Implementations/Sorting/AlphanumComparator.cs +++ /dev/null @@ -1,99 +0,0 @@ -using System.Collections.Generic; -using System.Text; -using MediaBrowser.Controller.Sorting; - -namespace Emby.Server.Implementations.Sorting -{ - public class AlphanumComparator : IComparer<string> - { - public static int CompareValues(string s1, string s2) - { - if (s1 == null || s2 == null) - { - return 0; - } - - int thisMarker = 0, thisNumericChunk = 0; - int thatMarker = 0, thatNumericChunk = 0; - - while ((thisMarker < s1.Length) || (thatMarker < s2.Length)) - { - if (thisMarker >= s1.Length) - { - return -1; - } - else if (thatMarker >= s2.Length) - { - return 1; - } - char thisCh = s1[thisMarker]; - char thatCh = s2[thatMarker]; - - var thisChunk = new StringBuilder(); - var thatChunk = new StringBuilder(); - - while ((thisMarker < s1.Length) && (thisChunk.Length == 0 || SortHelper.InChunk(thisCh, thisChunk[0]))) - { - thisChunk.Append(thisCh); - thisMarker++; - - if (thisMarker < s1.Length) - { - thisCh = s1[thisMarker]; - } - } - - while ((thatMarker < s2.Length) && (thatChunk.Length == 0 || SortHelper.InChunk(thatCh, thatChunk[0]))) - { - thatChunk.Append(thatCh); - thatMarker++; - - if (thatMarker < s2.Length) - { - thatCh = s2[thatMarker]; - } - } - - int result = 0; - // If both chunks contain numeric characters, sort them numerically - if (char.IsDigit(thisChunk[0]) && char.IsDigit(thatChunk[0])) - { - if (!int.TryParse(thisChunk.ToString(), out thisNumericChunk)) - { - return 0; - } - if (!int.TryParse(thatChunk.ToString(), out thatNumericChunk)) - { - return 0; - } - - if (thisNumericChunk < thatNumericChunk) - { - result = -1; - } - - if (thisNumericChunk > thatNumericChunk) - { - result = 1; - } - } - else - { - result = thisChunk.ToString().CompareTo(thatChunk.ToString()); - } - - if (result != 0) - { - return result; - } - } - - return 0; - } - - public int Compare(string x, string y) - { - return CompareValues(x, y); - } - } -} diff --git a/Emby.Server.Implementations/Updates/InstallationManager.cs b/Emby.Server.Implementations/Updates/InstallationManager.cs index 6a34ca74b..0b2309889 100644 --- a/Emby.Server.Implementations/Updates/InstallationManager.cs +++ b/Emby.Server.Implementations/Updates/InstallationManager.cs @@ -3,8 +3,10 @@ using System.Collections.Concurrent; using System.Collections.Generic; using System.IO; using System.Linq; +using System.Net; using System.Net.Http; using System.Runtime.CompilerServices; +using System.Runtime.Serialization; using System.Security.Cryptography; using System.Threading; using System.Threading.Tasks; @@ -18,6 +20,7 @@ using MediaBrowser.Model.Events; using MediaBrowser.Model.IO; using MediaBrowser.Model.Serialization; using MediaBrowser.Model.Updates; +using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Logging; namespace Emby.Server.Implementations.Updates @@ -28,6 +31,11 @@ namespace Emby.Server.Implementations.Updates public class InstallationManager : IInstallationManager { /// <summary> + /// The key for a setting that specifies a URL for the plugin repository JSON manifest. + /// </summary> + public const string PluginManifestUrlKey = "InstallationManager:PluginManifestUrl"; + + /// <summary> /// The logger. /// </summary> private readonly ILogger _logger; @@ -44,6 +52,7 @@ namespace Emby.Server.Implementations.Updates private readonly IApplicationHost _applicationHost; private readonly IZipClient _zipClient; + private readonly IConfiguration _appConfig; private readonly object _currentInstallationsLock = new object(); @@ -65,7 +74,8 @@ namespace Emby.Server.Implementations.Updates IJsonSerializer jsonSerializer, IServerConfigurationManager config, IFileSystem fileSystem, - IZipClient zipClient) + IZipClient zipClient, + IConfiguration appConfig) { if (logger == null) { @@ -83,6 +93,7 @@ namespace Emby.Server.Implementations.Updates _config = config; _fileSystem = fileSystem; _zipClient = zipClient; + _appConfig = appConfig; } /// <inheritdoc /> @@ -112,19 +123,43 @@ namespace Emby.Server.Implementations.Updates /// <inheritdoc /> public async Task<IReadOnlyList<PackageInfo>> GetAvailablePackages(CancellationToken cancellationToken = default) { - using (var response = await _httpClient.SendAsync( - new HttpRequestOptions + var manifestUrl = _appConfig.GetValue<string>(PluginManifestUrlKey); + + try + { + using (var response = await _httpClient.SendAsync( + new HttpRequestOptions + { + Url = manifestUrl, + CancellationToken = cancellationToken, + CacheMode = CacheMode.Unconditional, + CacheLength = TimeSpan.FromMinutes(3) + }, + HttpMethod.Get).ConfigureAwait(false)) + using (Stream stream = response.Content) { - Url = "https://repo.jellyfin.org/releases/plugin/manifest-water.json", - CancellationToken = cancellationToken, - CacheMode = CacheMode.Unconditional, - CacheLength = TimeSpan.FromMinutes(3) - }, - HttpMethod.Get).ConfigureAwait(false)) - using (Stream stream = response.Content) + try + { + return await _jsonSerializer.DeserializeFromStreamAsync<IReadOnlyList<PackageInfo>>(stream).ConfigureAwait(false); + } + catch (SerializationException ex) + { + const string LogTemplate = + "Failed to deserialize the plugin manifest retrieved from {PluginManifestUrl}. If you " + + "have specified a custom plugin repository manifest URL with --plugin-manifest-url or " + + PluginManifestUrlKey + ", please ensure that it is correct."; + _logger.LogError(ex, LogTemplate, manifestUrl); + throw; + } + } + } + catch (UriFormatException ex) { - return await _jsonSerializer.DeserializeFromStreamAsync<IReadOnlyList<PackageInfo>>( - stream).ConfigureAwait(false); + const string LogTemplate = + "The URL configured for the plugin repository manifest URL is not valid: {PluginManifestUrl}. " + + "Please check the URL configured by --plugin-manifest-url or " + PluginManifestUrlKey; + _logger.LogError(ex, LogTemplate, manifestUrl); + throw; } } @@ -185,17 +220,19 @@ namespace Emby.Server.Implementations.Updates } /// <inheritdoc /> - public async IAsyncEnumerable<VersionInfo> GetAvailablePluginUpdates([EnumeratorCancellation] CancellationToken cancellationToken = default) + public async Task<IEnumerable<VersionInfo>> GetAvailablePluginUpdates(CancellationToken cancellationToken = default) { var catalog = await GetAvailablePackages(cancellationToken).ConfigureAwait(false); + return GetAvailablePluginUpdates(catalog); + } - // Figure out what needs to be installed + private IEnumerable<VersionInfo> GetAvailablePluginUpdates(IReadOnlyList<PackageInfo> pluginCatalog) + { foreach (var plugin in _applicationHost.Plugins) { - var compatibleVersions = GetCompatibleVersions(catalog, plugin.Name, plugin.Id, plugin.Version); - var version = compatibleVersions.FirstOrDefault(y => y.version > plugin.Version); - if (version != null - && !CompletedInstallations.Any(x => string.Equals(x.Guid, version.guid, StringComparison.OrdinalIgnoreCase))) + var compatibleversions = GetCompatibleVersions(pluginCatalog, plugin.Name, plugin.Id, plugin.Version); + var version = compatibleversions.FirstOrDefault(y => y.version > plugin.Version); + if (version != null && !CompletedInstallations.Any(x => string.Equals(x.Guid, version.guid, StringComparison.OrdinalIgnoreCase))) { yield return version; } |
