aboutsummaryrefslogtreecommitdiff
path: root/Emby.Server.Implementations
diff options
context:
space:
mode:
Diffstat (limited to 'Emby.Server.Implementations')
-rw-r--r--Emby.Server.Implementations/ApplicationHost.cs315
-rw-r--r--Emby.Server.Implementations/Channels/ChannelManager.cs8
-rw-r--r--Emby.Server.Implementations/Cryptography/CryptographyProvider.cs90
-rw-r--r--Emby.Server.Implementations/Data/ManagedConnection.cs2
-rw-r--r--Emby.Server.Implementations/Dto/DtoService.cs24
-rw-r--r--Emby.Server.Implementations/Emby.Server.Implementations.csproj14
-rw-r--r--Emby.Server.Implementations/EntryPoints/ExternalPortForwarding.cs6
-rw-r--r--Emby.Server.Implementations/HttpServer/Security/AuthService.cs2
-rw-r--r--Emby.Server.Implementations/HttpServer/WebSocketManager.cs7
-rw-r--r--Emby.Server.Implementations/IO/FileRefresher.cs24
-rw-r--r--Emby.Server.Implementations/IO/LibraryMonitor.cs10
-rw-r--r--Emby.Server.Implementations/IO/ManagedFileSystem.cs43
-rw-r--r--Emby.Server.Implementations/Library/CoreResolutionIgnoreRule.cs13
-rw-r--r--Emby.Server.Implementations/Library/LibraryManager.cs64
-rw-r--r--Emby.Server.Implementations/Library/MediaSourceManager.cs37
-rw-r--r--Emby.Server.Implementations/Library/Resolvers/Audio/AudioResolver.cs67
-rw-r--r--Emby.Server.Implementations/Library/Resolvers/Audio/MusicAlbumResolver.cs30
-rw-r--r--Emby.Server.Implementations/Library/Resolvers/Audio/MusicArtistResolver.cs19
-rw-r--r--Emby.Server.Implementations/Library/Resolvers/BaseVideoResolver.cs13
-rw-r--r--Emby.Server.Implementations/Library/Resolvers/GenericVideoResolver.cs6
-rw-r--r--Emby.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs23
-rw-r--r--Emby.Server.Implementations/Library/Resolvers/PhotoAlbumResolver.cs11
-rw-r--r--Emby.Server.Implementations/Library/Resolvers/PhotoResolver.cs23
-rw-r--r--Emby.Server.Implementations/Library/Resolvers/TV/EpisodeResolver.cs7
-rw-r--r--Emby.Server.Implementations/Library/Resolvers/TV/SeasonResolver.cs30
-rw-r--r--Emby.Server.Implementations/Library/Resolvers/TV/SeriesResolver.cs38
-rw-r--r--Emby.Server.Implementations/Library/Validators/CollectionPostScanTask.cs156
-rw-r--r--Emby.Server.Implementations/Library/Validators/PeopleValidator.cs2
-rw-r--r--Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs12
-rw-r--r--Emby.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs19
-rw-r--r--Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs19
-rw-r--r--Emby.Server.Implementations/LiveTv/LiveTvDtoService.cs2
-rw-r--r--Emby.Server.Implementations/LiveTv/LiveTvManager.cs6
-rw-r--r--Emby.Server.Implementations/LiveTv/LiveTvMediaSourceProvider.cs2
-rw-r--r--Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunManager.cs2
-rw-r--r--Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunUdpStream.cs4
-rw-r--r--Emby.Server.Implementations/LiveTv/TunerHosts/M3uParser.cs2
-rw-r--r--Emby.Server.Implementations/LiveTv/TunerHosts/SharedHttpStream.cs2
-rw-r--r--Emby.Server.Implementations/Localization/Core/as.json1
-rw-r--r--Emby.Server.Implementations/Localization/Core/be.json4
-rw-r--r--Emby.Server.Implementations/Localization/Core/ca.json4
-rw-r--r--Emby.Server.Implementations/Localization/Core/cs.json2
-rw-r--r--Emby.Server.Implementations/Localization/Core/en-GB.json2
-rw-r--r--Emby.Server.Implementations/Localization/Core/en-US.json4
-rw-r--r--Emby.Server.Implementations/Localization/Core/eo.json18
-rw-r--r--Emby.Server.Implementations/Localization/Core/es_419.json14
-rw-r--r--Emby.Server.Implementations/Localization/Core/et.json25
-rw-r--r--Emby.Server.Implementations/Localization/Core/fa.json4
-rw-r--r--Emby.Server.Implementations/Localization/Core/fr.json2
-rw-r--r--Emby.Server.Implementations/Localization/Core/hu.json4
-rw-r--r--Emby.Server.Implementations/Localization/Core/id.json12
-rw-r--r--Emby.Server.Implementations/Localization/Core/it.json2
-rw-r--r--Emby.Server.Implementations/Localization/Core/kk.json2
-rw-r--r--Emby.Server.Implementations/Localization/Core/mk.json2
-rw-r--r--Emby.Server.Implementations/Localization/Core/mn.json14
-rw-r--r--Emby.Server.Implementations/Localization/Core/ms.json2
-rw-r--r--Emby.Server.Implementations/Localization/Core/pl.json6
-rw-r--r--Emby.Server.Implementations/Localization/Core/pr.json4
-rw-r--r--Emby.Server.Implementations/Localization/Core/pt-PT.json8
-rw-r--r--Emby.Server.Implementations/Localization/Core/ru.json4
-rw-r--r--Emby.Server.Implementations/Localization/Core/sk.json2
-rw-r--r--Emby.Server.Implementations/Localization/Core/sq.json4
-rw-r--r--Emby.Server.Implementations/Localization/Core/sr.json2
-rw-r--r--Emby.Server.Implementations/Localization/Core/sv.json2
-rw-r--r--Emby.Server.Implementations/Localization/Core/ta.json2
-rw-r--r--Emby.Server.Implementations/Localization/Core/te.json23
-rw-r--r--Emby.Server.Implementations/Localization/Core/tr.json2
-rw-r--r--Emby.Server.Implementations/Localization/Core/uk.json2
-rw-r--r--Emby.Server.Implementations/Localization/Core/vi.json12
-rw-r--r--Emby.Server.Implementations/Localization/Core/zh-CN.json2
-rw-r--r--Emby.Server.Implementations/Localization/Core/zu.json1
-rw-r--r--Emby.Server.Implementations/Localization/LocalizationManager.cs103
-rw-r--r--Emby.Server.Implementations/Localization/countries.json2
-rw-r--r--Emby.Server.Implementations/Localization/iso6392.txt3
-rw-r--r--Emby.Server.Implementations/Playlists/PlaylistManager.cs2
-rw-r--r--Emby.Server.Implementations/Plugins/PluginManager.cs5
-rw-r--r--Emby.Server.Implementations/ScheduledTasks/ScheduledTaskWorker.cs10
-rw-r--r--Emby.Server.Implementations/ScheduledTasks/Tasks/DeleteCacheFileTask.cs8
-rw-r--r--Emby.Server.Implementations/ScheduledTasks/Tasks/DeleteTranscodeFileTask.cs8
-rw-r--r--Emby.Server.Implementations/Sorting/AlbumArtistComparer.cs2
-rw-r--r--Emby.Server.Implementations/Sorting/AlbumComparer.cs6
-rw-r--r--Emby.Server.Implementations/Sorting/ArtistComparer.cs2
-rw-r--r--Emby.Server.Implementations/Sorting/NameComparer.cs2
-rw-r--r--Emby.Server.Implementations/Sorting/SeriesSortNameComparer.cs2
-rw-r--r--Emby.Server.Implementations/Sorting/SortNameComparer.cs2
-rw-r--r--Emby.Server.Implementations/Udp/UdpServer.cs6
-rw-r--r--Emby.Server.Implementations/Updates/InstallationManager.cs2
87 files changed, 838 insertions, 675 deletions
diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs
index 6fd152a42..903c31133 100644
--- a/Emby.Server.Implementations/ApplicationHost.cs
+++ b/Emby.Server.Implementations/ApplicationHost.cs
@@ -3,6 +3,7 @@
#pragma warning disable CS1591
using System;
+using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
@@ -18,6 +19,7 @@ using Emby.Dlna;
using Emby.Dlna.Main;
using Emby.Dlna.Ssdp;
using Emby.Drawing;
+using Emby.Naming.Common;
using Emby.Notifications;
using Emby.Photos;
using Emby.Server.Implementations.Archiving;
@@ -56,6 +58,7 @@ using MediaBrowser.Common.Updates;
using MediaBrowser.Controller;
using MediaBrowser.Controller.Channels;
using MediaBrowser.Controller.Chapters;
+using MediaBrowser.Controller.ClientEvent;
using MediaBrowser.Controller.Collections;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Dlna;
@@ -117,7 +120,7 @@ namespace Emby.Server.Implementations
/// <summary>
/// The disposable parts.
/// </summary>
- private readonly List<IDisposable> _disposableParts = new List<IDisposable>();
+ private readonly ConcurrentDictionary<IDisposable, byte> _disposableParts = new ();
private readonly IFileSystem _fileSystemManager;
private readonly IConfiguration _startupConfig;
@@ -128,7 +131,6 @@ namespace Emby.Server.Implementations
private List<Type> _creatingInstances;
private IMediaEncoder _mediaEncoder;
private ISessionManager _sessionManager;
- private string[] _urlPrefixes;
/// <summary>
/// Gets or sets all concrete types.
@@ -147,25 +149,20 @@ namespace Emby.Server.Implementations
/// <param name="loggerFactory">Instance of the <see cref="ILoggerFactory"/> interface.</param>
/// <param name="options">Instance of the <see cref="IStartupOptions"/> interface.</param>
/// <param name="startupConfig">The <see cref="IConfiguration" /> interface.</param>
- /// <param name="fileSystem">Instance of the <see cref="IFileSystem"/> interface.</param>
- /// <param name="serviceCollection">Instance of the <see cref="IServiceCollection"/> interface.</param>
public ApplicationHost(
IServerApplicationPaths applicationPaths,
ILoggerFactory loggerFactory,
IStartupOptions options,
- IConfiguration startupConfig,
- IFileSystem fileSystem,
- IServiceCollection serviceCollection)
+ IConfiguration startupConfig)
{
ApplicationPaths = applicationPaths;
LoggerFactory = loggerFactory;
_startupOptions = options;
_startupConfig = startupConfig;
- _fileSystemManager = fileSystem;
- ServiceCollection = serviceCollection;
+ _fileSystemManager = new ManagedFileSystem(LoggerFactory.CreateLogger<ManagedFileSystem>(), applicationPaths);
Logger = LoggerFactory.CreateLogger<ApplicationHost>();
- fileSystem.AddShortcutHandler(new MbLinkShortcutHandler(fileSystem));
+ _fileSystemManager.AddShortcutHandler(new MbLinkShortcutHandler(_fileSystemManager));
ApplicationVersion = typeof(ApplicationHost).Assembly.GetName().Version;
ApplicationVersionString = ApplicationVersion.ToString(3);
@@ -214,7 +211,7 @@ namespace Emby.Server.Implementations
/// <summary>
/// Gets the <see cref="INetworkManager"/> singleton instance.
/// </summary>
- public INetworkManager NetManager { get; internal set; }
+ public INetworkManager NetManager { get; private set; }
/// <summary>
/// Gets a value indicating whether this instance has changes that require the entire application to restart.
@@ -230,24 +227,22 @@ namespace Emby.Server.Implementations
/// </summary>
protected ILogger<ApplicationHost> Logger { get; }
- protected IServiceCollection ServiceCollection { get; }
-
/// <summary>
/// Gets the logger factory.
/// </summary>
protected ILoggerFactory LoggerFactory { get; }
/// <summary>
- /// Gets or sets the application paths.
+ /// Gets the application paths.
/// </summary>
/// <value>The application paths.</value>
- protected IServerApplicationPaths ApplicationPaths { get; set; }
+ protected IServerApplicationPaths ApplicationPaths { get; }
/// <summary>
- /// Gets or sets the configuration manager.
+ /// Gets the configuration manager.
/// </summary>
/// <value>The configuration manager.</value>
- public ServerConfigurationManager ConfigurationManager { get; set; }
+ public ServerConfigurationManager ConfigurationManager { get; }
/// <summary>
/// Gets or sets the service provider.
@@ -351,22 +346,6 @@ namespace Emby.Server.Implementations
}
/// <summary>
- /// Creates an instance of type and resolves all constructor dependencies.
- /// </summary>
- /// <param name="type">The type.</param>
- /// <returns>System.Object.</returns>
- public object CreateInstance(Type type)
- => ActivatorUtilities.CreateInstance(ServiceProvider, type);
-
- /// <summary>
- /// Creates an instance of type and resolves all constructor dependencies.
- /// </summary>
- /// <typeparam name="T">The type.</typeparam>
- /// <returns>T.</returns>
- public T CreateInstance<T>()
- => ActivatorUtilities.CreateInstance<T>(ServiceProvider);
-
- /// <summary>
/// Creates the instance safe.
/// </summary>
/// <param name="type">The type.</param>
@@ -375,7 +354,7 @@ namespace Emby.Server.Implementations
{
_creatingInstances ??= new List<Type>();
- if (_creatingInstances.IndexOf(type) != -1)
+ if (_creatingInstances.Contains(type))
{
Logger.LogError("DI Loop detected in the attempted creation of {Type}", type.FullName);
foreach (var entry in _creatingInstances)
@@ -385,7 +364,7 @@ namespace Emby.Server.Implementations
_pluginManager.FailPlugin(type.Assembly);
- throw new ExternalException("DI Loop detected.");
+ throw new TypeLoadException("DI Loop detected");
}
try
@@ -418,8 +397,15 @@ namespace Emby.Server.Implementations
public IEnumerable<Type> GetExportTypes<T>()
{
var currentType = typeof(T);
-
- return _allConcreteTypes.Where(i => currentType.IsAssignableFrom(i));
+ var numberOfConcreteTypes = _allConcreteTypes.Length;
+ for (var i = 0; i < numberOfConcreteTypes; i++)
+ {
+ var type = _allConcreteTypes[i];
+ if (currentType.IsAssignableFrom(type))
+ {
+ yield return type;
+ }
+ }
}
/// <inheritdoc />
@@ -434,9 +420,9 @@ namespace Emby.Server.Implementations
if (manageLifetime)
{
- lock (_disposableParts)
+ foreach (var part in parts.OfType<IDisposable>())
{
- _disposableParts.AddRange(parts.OfType<IDisposable>());
+ _disposableParts.TryAdd(part, byte.MinValue);
}
}
@@ -455,9 +441,9 @@ namespace Emby.Server.Implementations
if (manageLifetime)
{
- lock (_disposableParts)
+ foreach (var part in parts.OfType<IDisposable>())
{
- _disposableParts.AddRange(parts.OfType<IDisposable>());
+ _disposableParts.TryAdd(part, byte.MinValue);
}
}
@@ -521,7 +507,7 @@ namespace Emby.Server.Implementations
}
/// <inheritdoc/>
- public void Init()
+ public void Init(IServiceCollection serviceCollection)
{
DiscoverTypes();
@@ -551,128 +537,130 @@ namespace Emby.Server.Implementations
CertificatePath = networkConfiguration.CertificatePath;
Certificate = GetCertificate(CertificatePath, networkConfiguration.CertificatePassword);
- RegisterServices();
+ RegisterServices(serviceCollection);
- _pluginManager.RegisterServices(ServiceCollection);
+ _pluginManager.RegisterServices(serviceCollection);
}
/// <summary>
/// Registers services/resources with the service collection that will be available via DI.
/// </summary>
- protected virtual void RegisterServices()
+ /// <param name="serviceCollection">Instance of the <see cref="IServiceCollection"/> interface.</param>
+ protected virtual void RegisterServices(IServiceCollection serviceCollection)
{
- ServiceCollection.AddSingleton(_startupOptions);
+ serviceCollection.AddSingleton(_startupOptions);
- ServiceCollection.AddMemoryCache();
+ serviceCollection.AddMemoryCache();
- ServiceCollection.AddSingleton<IServerConfigurationManager>(ConfigurationManager);
- ServiceCollection.AddSingleton<IConfigurationManager>(ConfigurationManager);
- ServiceCollection.AddSingleton<IApplicationHost>(this);
- ServiceCollection.AddSingleton<IPluginManager>(_pluginManager);
- ServiceCollection.AddSingleton<IApplicationPaths>(ApplicationPaths);
+ serviceCollection.AddSingleton<IServerConfigurationManager>(ConfigurationManager);
+ serviceCollection.AddSingleton<IConfigurationManager>(ConfigurationManager);
+ serviceCollection.AddSingleton<IApplicationHost>(this);
+ serviceCollection.AddSingleton(_pluginManager);
+ serviceCollection.AddSingleton<IApplicationPaths>(ApplicationPaths);
- ServiceCollection.AddSingleton(_fileSystemManager);
- ServiceCollection.AddSingleton<TmdbClientManager>();
+ serviceCollection.AddSingleton(_fileSystemManager);
+ serviceCollection.AddSingleton<TmdbClientManager>();
- ServiceCollection.AddSingleton(NetManager);
+ serviceCollection.AddSingleton(NetManager);
- ServiceCollection.AddSingleton<ITaskManager, TaskManager>();
+ serviceCollection.AddSingleton<ITaskManager, TaskManager>();
- ServiceCollection.AddSingleton(_xmlSerializer);
+ serviceCollection.AddSingleton(_xmlSerializer);
- ServiceCollection.AddSingleton<IStreamHelper, StreamHelper>();
+ serviceCollection.AddSingleton<IStreamHelper, StreamHelper>();
- ServiceCollection.AddSingleton<ICryptoProvider, CryptographyProvider>();
+ serviceCollection.AddSingleton<ICryptoProvider, CryptographyProvider>();
- ServiceCollection.AddSingleton<ISocketFactory, SocketFactory>();
+ serviceCollection.AddSingleton<ISocketFactory, SocketFactory>();
- ServiceCollection.AddSingleton<IInstallationManager, InstallationManager>();
+ serviceCollection.AddSingleton<IInstallationManager, InstallationManager>();
- ServiceCollection.AddSingleton<IZipClient, ZipClient>();
+ serviceCollection.AddSingleton<IZipClient, ZipClient>();
- ServiceCollection.AddSingleton<IServerApplicationHost>(this);
- ServiceCollection.AddSingleton<IServerApplicationPaths>(ApplicationPaths);
+ serviceCollection.AddSingleton<IServerApplicationHost>(this);
+ serviceCollection.AddSingleton(ApplicationPaths);
- ServiceCollection.AddSingleton<ILocalizationManager, LocalizationManager>();
+ serviceCollection.AddSingleton<ILocalizationManager, LocalizationManager>();
- ServiceCollection.AddSingleton<IBlurayExaminer, BdInfoExaminer>();
+ serviceCollection.AddSingleton<IBlurayExaminer, BdInfoExaminer>();
- ServiceCollection.AddSingleton<IUserDataRepository, SqliteUserDataRepository>();
- ServiceCollection.AddSingleton<IUserDataManager, UserDataManager>();
+ serviceCollection.AddSingleton<IUserDataRepository, SqliteUserDataRepository>();
+ serviceCollection.AddSingleton<IUserDataManager, UserDataManager>();
- ServiceCollection.AddSingleton<IItemRepository, SqliteItemRepository>();
+ serviceCollection.AddSingleton<IItemRepository, SqliteItemRepository>();
- ServiceCollection.AddSingleton<IMediaEncoder, MediaBrowser.MediaEncoding.Encoder.MediaEncoder>();
- ServiceCollection.AddSingleton<EncodingHelper>();
+ serviceCollection.AddSingleton<IMediaEncoder, MediaBrowser.MediaEncoding.Encoder.MediaEncoder>();
+ serviceCollection.AddSingleton<EncodingHelper>();
// TODO: Refactor to eliminate the circular dependencies here so that Lazy<T> isn't required
- ServiceCollection.AddTransient(provider => new Lazy<ILibraryMonitor>(provider.GetRequiredService<ILibraryMonitor>));
- ServiceCollection.AddTransient(provider => new Lazy<IProviderManager>(provider.GetRequiredService<IProviderManager>));
- ServiceCollection.AddTransient(provider => new Lazy<IUserViewManager>(provider.GetRequiredService<IUserViewManager>));
- ServiceCollection.AddSingleton<ILibraryManager, LibraryManager>();
+ serviceCollection.AddTransient(provider => new Lazy<ILibraryMonitor>(provider.GetRequiredService<ILibraryMonitor>));
+ serviceCollection.AddTransient(provider => new Lazy<IProviderManager>(provider.GetRequiredService<IProviderManager>));
+ serviceCollection.AddTransient(provider => new Lazy<IUserViewManager>(provider.GetRequiredService<IUserViewManager>));
+ serviceCollection.AddSingleton<ILibraryManager, LibraryManager>();
+ serviceCollection.AddSingleton<NamingOptions>();
- ServiceCollection.AddSingleton<IMusicManager, MusicManager>();
+ serviceCollection.AddSingleton<IMusicManager, MusicManager>();
- ServiceCollection.AddSingleton<ILibraryMonitor, LibraryMonitor>();
+ serviceCollection.AddSingleton<ILibraryMonitor, LibraryMonitor>();
- ServiceCollection.AddSingleton<ISearchEngine, SearchEngine>();
+ serviceCollection.AddSingleton<ISearchEngine, SearchEngine>();
- ServiceCollection.AddSingleton<IWebSocketManager, WebSocketManager>();
+ serviceCollection.AddSingleton<IWebSocketManager, WebSocketManager>();
- ServiceCollection.AddSingleton<IImageProcessor, ImageProcessor>();
+ serviceCollection.AddSingleton<IImageProcessor, ImageProcessor>();
- ServiceCollection.AddSingleton<ITVSeriesManager, TVSeriesManager>();
+ serviceCollection.AddSingleton<ITVSeriesManager, TVSeriesManager>();
- ServiceCollection.AddSingleton<IMediaSourceManager, MediaSourceManager>();
+ serviceCollection.AddSingleton<IMediaSourceManager, MediaSourceManager>();
- ServiceCollection.AddSingleton<ISubtitleManager, SubtitleManager>();
+ serviceCollection.AddSingleton<ISubtitleManager, SubtitleManager>();
- ServiceCollection.AddSingleton<IProviderManager, ProviderManager>();
+ serviceCollection.AddSingleton<IProviderManager, ProviderManager>();
// TODO: Refactor to eliminate the circular dependency here so that Lazy<T> isn't required
- ServiceCollection.AddTransient(provider => new Lazy<ILiveTvManager>(provider.GetRequiredService<ILiveTvManager>));
- ServiceCollection.AddSingleton<IDtoService, DtoService>();
-
- ServiceCollection.AddSingleton<IChannelManager, ChannelManager>();
+ serviceCollection.AddTransient(provider => new Lazy<ILiveTvManager>(provider.GetRequiredService<ILiveTvManager>));
+ serviceCollection.AddSingleton<IDtoService, DtoService>();
- ServiceCollection.AddSingleton<ISessionManager, SessionManager>();
+ serviceCollection.AddSingleton<IChannelManager, ChannelManager>();
- ServiceCollection.AddSingleton<IDlnaManager, DlnaManager>();
+ serviceCollection.AddSingleton<ISessionManager, SessionManager>();
- ServiceCollection.AddSingleton<ICollectionManager, CollectionManager>();
+ serviceCollection.AddSingleton<IDlnaManager, DlnaManager>();
- ServiceCollection.AddSingleton<IPlaylistManager, PlaylistManager>();
+ serviceCollection.AddSingleton<ICollectionManager, CollectionManager>();
- ServiceCollection.AddSingleton<ISyncPlayManager, SyncPlayManager>();
+ serviceCollection.AddSingleton<IPlaylistManager, PlaylistManager>();
- ServiceCollection.AddSingleton<LiveTvDtoService>();
- ServiceCollection.AddSingleton<ILiveTvManager, LiveTvManager>();
+ serviceCollection.AddSingleton<ISyncPlayManager, SyncPlayManager>();
- ServiceCollection.AddSingleton<IUserViewManager, UserViewManager>();
+ serviceCollection.AddSingleton<LiveTvDtoService>();
+ serviceCollection.AddSingleton<ILiveTvManager, LiveTvManager>();
- ServiceCollection.AddSingleton<INotificationManager, NotificationManager>();
+ serviceCollection.AddSingleton<IUserViewManager, UserViewManager>();
- ServiceCollection.AddSingleton<IDeviceDiscovery, DeviceDiscovery>();
+ serviceCollection.AddSingleton<INotificationManager, NotificationManager>();
- ServiceCollection.AddSingleton<IChapterManager, ChapterManager>();
+ serviceCollection.AddSingleton<IDeviceDiscovery, DeviceDiscovery>();
- ServiceCollection.AddSingleton<IEncodingManager, MediaEncoder.EncodingManager>();
+ serviceCollection.AddSingleton<IChapterManager, ChapterManager>();
- ServiceCollection.AddScoped<ISessionContext, SessionContext>();
+ serviceCollection.AddSingleton<IEncodingManager, MediaEncoder.EncodingManager>();
- ServiceCollection.AddSingleton<IAuthService, AuthService>();
- ServiceCollection.AddSingleton<IQuickConnect, QuickConnectManager>();
+ serviceCollection.AddScoped<ISessionContext, SessionContext>();
- ServiceCollection.AddSingleton<ISubtitleEncoder, MediaBrowser.MediaEncoding.Subtitles.SubtitleEncoder>();
+ serviceCollection.AddSingleton<IAuthService, AuthService>();
+ serviceCollection.AddSingleton<IQuickConnect, QuickConnectManager>();
- ServiceCollection.AddSingleton<IAttachmentExtractor, MediaBrowser.MediaEncoding.Attachments.AttachmentExtractor>();
+ serviceCollection.AddSingleton<ISubtitleEncoder, MediaBrowser.MediaEncoding.Subtitles.SubtitleEncoder>();
- ServiceCollection.AddSingleton<TranscodingJobHelper>();
- ServiceCollection.AddScoped<MediaInfoHelper>();
- ServiceCollection.AddScoped<AudioHelper>();
- ServiceCollection.AddScoped<DynamicHlsHelper>();
+ serviceCollection.AddSingleton<IAttachmentExtractor, MediaBrowser.MediaEncoding.Attachments.AttachmentExtractor>();
- ServiceCollection.AddSingleton<IDirectoryService, DirectoryService>();
+ serviceCollection.AddSingleton<TranscodingJobHelper>();
+ serviceCollection.AddScoped<MediaInfoHelper>();
+ serviceCollection.AddScoped<AudioHelper>();
+ serviceCollection.AddScoped<DynamicHlsHelper>();
+ serviceCollection.AddScoped<IClientEventLogger, ClientEventLogger>();
+ serviceCollection.AddSingleton<IDirectoryService, DirectoryService>();
}
/// <summary>
@@ -795,8 +783,6 @@ namespace Emby.Server.Implementations
_pluginManager.CreatePlugins();
- _urlPrefixes = GetUrlPrefixes().ToArray();
-
Resolve<ILibraryManager>().AddParts(
GetExports<IResolverIgnoreRule>(),
GetExports<IItemResolver>(),
@@ -864,32 +850,12 @@ namespace Emby.Server.Implementations
}
}
- private IEnumerable<string> GetUrlPrefixes()
- {
- var hosts = new[] { "+" };
-
- return hosts.SelectMany(i =>
- {
- var prefixes = new List<string>
- {
- "http://" + i + ":" + HttpPort + "/"
- };
-
- if (Certificate != null)
- {
- prefixes.Add("https://" + i + ":" + HttpsPort + "/");
- }
-
- return prefixes;
- });
- }
-
/// <summary>
/// Called when [configuration updated].
/// </summary>
/// <param name="sender">The sender.</param>
/// <param name="e">The <see cref="EventArgs"/> instance containing the event data.</param>
- protected void OnConfigurationUpdated(object sender, EventArgs e)
+ private void OnConfigurationUpdated(object sender, EventArgs e)
{
var requiresRestart = false;
var networkConfiguration = ConfigurationManager.GetNetworkConfiguration();
@@ -898,8 +864,8 @@ namespace Emby.Server.Implementations
if (HttpPort != 0 && HttpsPort != 0)
{
// Need to restart if ports have changed
- if (networkConfiguration.HttpServerPortNumber != HttpPort ||
- networkConfiguration.HttpsPortNumber != HttpsPort)
+ if (networkConfiguration.HttpServerPortNumber != HttpPort
+ || networkConfiguration.HttpsPortNumber != HttpsPort)
{
if (ConfigurationManager.Configuration.IsPortAuthorized)
{
@@ -911,11 +877,6 @@ namespace Emby.Server.Implementations
}
}
- if (!_urlPrefixes.SequenceEqual(GetUrlPrefixes(), StringComparer.OrdinalIgnoreCase))
- {
- requiresRestart = true;
- }
-
if (ValidateSslCertificate(networkConfiguration))
{
requiresRestart = true;
@@ -957,7 +918,7 @@ namespace Emby.Server.Implementations
}
/// <summary>
- /// Notifies that the kernel that a change has been made that requires a restart.
+ /// Notifies the kernel that a change has been made that requires a restart.
/// </summary>
public void NotifyPendingRestart()
{
@@ -1098,11 +1059,6 @@ namespace Emby.Server.Implementations
};
}
- public IEnumerable<WakeOnLanInfo> GetWakeOnLanInfo()
- => NetManager.GetMacAddresses()
- .Select(i => new WakeOnLanInfo(i))
- .ToList();
-
public PublicSystemInfo GetPublicSystemInfo(HttpRequest request)
{
return new PublicSystemInfo
@@ -1118,7 +1074,7 @@ namespace Emby.Server.Implementations
}
/// <inheritdoc/>
- public string GetSmartApiUrl(IPAddress remoteAddr, int? port = null)
+ public string GetSmartApiUrl(IPAddress remoteAddr)
{
// Published server ends with a /
if (!string.IsNullOrEmpty(PublishedServerUrl))
@@ -1127,18 +1083,12 @@ namespace Emby.Server.Implementations
return PublishedServerUrl.Trim('/');
}
- string smart = NetManager.GetBindInterface(remoteAddr, out port);
- // If the smartAPI doesn't start with http then treat it as a host or ip.
- if (smart.StartsWith("http", StringComparison.OrdinalIgnoreCase))
- {
- return smart.Trim('/');
- }
-
+ string smart = NetManager.GetBindInterface(remoteAddr, out var port);
return GetLocalApiUrl(smart.Trim('/'), null, port);
}
/// <inheritdoc/>
- public string GetSmartApiUrl(HttpRequest request, int? port = null)
+ public string GetSmartApiUrl(HttpRequest request)
{
// Return the host in the HTTP request as the API url
if (ConfigurationManager.GetNetworkConfiguration().EnablePublishedServerUriByRequest)
@@ -1159,18 +1109,12 @@ namespace Emby.Server.Implementations
return PublishedServerUrl.Trim('/');
}
- string smart = NetManager.GetBindInterface(request, out port);
- // If the smartAPI doesn't start with http then treat it as a host or ip.
- if (smart.StartsWith("http", StringComparison.OrdinalIgnoreCase))
- {
- return smart.Trim('/');
- }
-
+ string smart = NetManager.GetBindInterface(request, out var port);
return GetLocalApiUrl(smart.Trim('/'), request.Scheme, port);
}
/// <inheritdoc/>
- public string GetSmartApiUrl(string hostname, int? port = null)
+ public string GetSmartApiUrl(string hostname)
{
// Published server ends with a /
if (!string.IsNullOrEmpty(PublishedServerUrl))
@@ -1179,31 +1123,29 @@ namespace Emby.Server.Implementations
return PublishedServerUrl.Trim('/');
}
- string smart = NetManager.GetBindInterface(hostname, out port);
-
- // If the smartAPI doesn't start with http then treat it as a host or ip.
- if (smart.StartsWith("http", StringComparison.OrdinalIgnoreCase))
- {
- return smart.Trim('/');
- }
-
+ string smart = NetManager.GetBindInterface(hostname, out var port);
return GetLocalApiUrl(smart.Trim('/'), null, port);
}
/// <inheritdoc/>
- public string GetLoopbackHttpApiUrl()
+ public string GetApiUrlForLocalAccess(bool allowHttps = true)
{
- if (NetManager.IsIP6Enabled)
- {
- return GetLocalApiUrl("::1", Uri.UriSchemeHttp, HttpPort);
- }
-
- return GetLocalApiUrl("127.0.0.1", Uri.UriSchemeHttp, HttpPort);
+ // With an empty source, the port will be null
+ string smart = NetManager.GetBindInterface(string.Empty, out _);
+ var scheme = !allowHttps ? Uri.UriSchemeHttp : null;
+ int? port = !allowHttps ? HttpPort : null;
+ return GetLocalApiUrl(smart.Trim('/'), scheme, port);
}
/// <inheritdoc/>
public string GetLocalApiUrl(string hostname, string scheme = null, int? port = null)
{
+ // If the smartAPI doesn't start with http then treat it as a host or ip.
+ if (hostname.StartsWith("http", StringComparison.OrdinalIgnoreCase))
+ {
+ return hostname.TrimEnd('/');
+ }
+
// NOTE: If no BaseUrl is set then UriBuilder appends a trailing slash, but if there is no BaseUrl it does
// not. For consistency, always trim the trailing slash.
return new UriBuilder
@@ -1277,12 +1219,15 @@ namespace Emby.Server.Implementations
Logger.LogInformation("Disposing {Type}", type.Name);
- var parts = _disposableParts.Distinct().Where(i => i.GetType() != type).ToList();
- _disposableParts.Clear();
-
- foreach (var part in parts)
+ foreach (var (part, _) in _disposableParts)
{
- Logger.LogInformation("Disposing {Type}", part.GetType().Name);
+ var partType = part.GetType();
+ if (partType == type)
+ {
+ continue;
+ }
+
+ Logger.LogInformation("Disposing {Type}", partType.Name);
try
{
@@ -1290,9 +1235,11 @@ namespace Emby.Server.Implementations
}
catch (Exception ex)
{
- Logger.LogError(ex, "Error disposing {Type}", part.GetType().Name);
+ Logger.LogError(ex, "Error disposing {Type}", partType.Name);
}
}
+
+ _disposableParts.Clear();
}
_disposed = true;
diff --git a/Emby.Server.Implementations/Channels/ChannelManager.cs b/Emby.Server.Implementations/Channels/ChannelManager.cs
index 09aee602a..f65eaec1c 100644
--- a/Emby.Server.Implementations/Channels/ChannelManager.cs
+++ b/Emby.Server.Implementations/Channels/ChannelManager.cs
@@ -1075,14 +1075,6 @@ namespace Emby.Server.Implementations.Channels
forceUpdate = true;
}
- // was used for status
- // if (!string.Equals(item.ExternalEtag ?? string.Empty, info.Etag ?? string.Empty, StringComparison.Ordinal))
- // {
- // item.ExternalEtag = info.Etag;
- // forceUpdate = true;
- // _logger.LogDebug("Forcing update due to ExternalEtag {0}", item.Name);
- // }
-
if (!internalChannelId.Equals(item.ChannelId))
{
forceUpdate = true;
diff --git a/Emby.Server.Implementations/Cryptography/CryptographyProvider.cs b/Emby.Server.Implementations/Cryptography/CryptographyProvider.cs
index 673810c49..e9c005cea 100644
--- a/Emby.Server.Implementations/Cryptography/CryptographyProvider.cs
+++ b/Emby.Server.Implementations/Cryptography/CryptographyProvider.cs
@@ -1,9 +1,11 @@
using System;
using System.Collections.Generic;
+using System.Globalization;
using System.Security.Cryptography;
+using System.Text;
using MediaBrowser.Common.Extensions;
using MediaBrowser.Model.Cryptography;
-using static MediaBrowser.Common.Cryptography.Constants;
+using static MediaBrowser.Model.Cryptography.Constants;
namespace Emby.Server.Implementations.Cryptography
{
@@ -12,10 +14,7 @@ namespace Emby.Server.Implementations.Cryptography
/// </summary>
public class CryptographyProvider : ICryptoProvider
{
- // FIXME: When we get DotNet Standard 2.1 we need to revisit how we do the crypto
- // Currently supported hash methods from https://docs.microsoft.com/en-us/dotnet/api/system.security.cryptography.cryptoconfig?view=netcore-2.1
- // there might be a better way to autogenerate this list as dotnet updates, but I couldn't find one
- // Please note the default method of PBKDF2 is not included, it cannot be used to generate hashes cleanly as it is actually a pbkdf with sha1
+ // TODO: remove when not needed for backwards compat
private static readonly HashSet<string> _supportedHashMethods = new HashSet<string>()
{
"MD5",
@@ -35,60 +34,81 @@ namespace Emby.Server.Implementations.Cryptography
};
/// <inheritdoc />
- public string DefaultHashMethod => "PBKDF2";
+ public string DefaultHashMethod => "PBKDF2-SHA512";
/// <inheritdoc />
- public IEnumerable<string> GetSupportedHashMethods()
- => _supportedHashMethods;
-
- private byte[] PBKDF2(string method, byte[] bytes, byte[] salt, int iterations)
+ public PasswordHash CreatePasswordHash(ReadOnlySpan<char> password)
{
- // downgrading for now as we need this library to be dotnetstandard compliant
- // with this downgrade we'll add a check to make sure we're on the downgrade method at the moment
- if (method != DefaultHashMethod)
- {
- throw new CryptographicException($"Cannot currently use PBKDF2 with requested hash method: {method}");
- }
-
- using var r = new Rfc2898DeriveBytes(bytes, salt, iterations);
- return r.GetBytes(32);
+ byte[] salt = GenerateSalt();
+ return new PasswordHash(
+ DefaultHashMethod,
+ Rfc2898DeriveBytes.Pbkdf2(
+ password,
+ salt,
+ DefaultIterations,
+ HashAlgorithmName.SHA512,
+ DefaultOutputLength),
+ salt,
+ new Dictionary<string, string>
+ {
+ { "iterations", DefaultIterations.ToString(CultureInfo.InvariantCulture) }
+ });
}
/// <inheritdoc />
- public byte[] ComputeHash(string hashMethod, byte[] bytes, byte[] salt)
+ public bool Verify(PasswordHash hash, ReadOnlySpan<char> password)
{
- if (hashMethod == DefaultHashMethod)
+ if (string.Equals(hash.Id, "PBKDF2", StringComparison.Ordinal))
{
- return PBKDF2(hashMethod, bytes, salt, DefaultIterations);
+ return hash.Hash.SequenceEqual(
+ Rfc2898DeriveBytes.Pbkdf2(
+ password,
+ hash.Salt,
+ int.Parse(hash.Parameters["iterations"], CultureInfo.InvariantCulture),
+ HashAlgorithmName.SHA1,
+ 32));
}
- if (!_supportedHashMethods.Contains(hashMethod))
+ if (string.Equals(hash.Id, "PBKDF2-SHA512", StringComparison.Ordinal))
{
- throw new CryptographicException($"Requested hash method is not supported: {hashMethod}");
+ return hash.Hash.SequenceEqual(
+ Rfc2898DeriveBytes.Pbkdf2(
+ password,
+ hash.Salt,
+ int.Parse(hash.Parameters["iterations"], CultureInfo.InvariantCulture),
+ HashAlgorithmName.SHA512,
+ DefaultOutputLength));
}
- using var h = HashAlgorithm.Create(hashMethod) ?? throw new ResourceNotFoundException($"Unknown hash method: {hashMethod}.");
- if (salt.Length == 0)
+ if (!_supportedHashMethods.Contains(hash.Id))
{
- return h.ComputeHash(bytes);
+ throw new CryptographicException($"Requested hash method is not supported: {hash.Id}");
}
- byte[] salted = new byte[bytes.Length + salt.Length];
+ using var h = HashAlgorithm.Create(hash.Id) ?? throw new ResourceNotFoundException($"Unknown hash method: {hash.Id}.");
+ var bytes = Encoding.UTF8.GetBytes(password.ToArray());
+ if (hash.Salt.Length == 0)
+ {
+ return hash.Hash.SequenceEqual(h.ComputeHash(bytes));
+ }
+
+ byte[] salted = new byte[bytes.Length + hash.Salt.Length];
Array.Copy(bytes, salted, bytes.Length);
- Array.Copy(salt, 0, salted, bytes.Length, salt.Length);
- return h.ComputeHash(salted);
+ hash.Salt.CopyTo(salted.AsSpan(bytes.Length));
+ return hash.Hash.SequenceEqual(h.ComputeHash(salted));
}
/// <inheritdoc />
- public byte[] ComputeHashWithDefaultMethod(byte[] bytes, byte[] salt)
- => PBKDF2(DefaultHashMethod, bytes, salt, DefaultIterations);
-
- /// <inheritdoc />
public byte[] GenerateSalt()
=> GenerateSalt(DefaultSaltLength);
/// <inheritdoc />
public byte[] GenerateSalt(int length)
- => RandomNumberGenerator.GetBytes(length);
+ {
+ var salt = new byte[length];
+ using var rng = RandomNumberGenerator.Create();
+ rng.GetNonZeroBytes(salt);
+ return salt;
+ }
}
}
diff --git a/Emby.Server.Implementations/Data/ManagedConnection.cs b/Emby.Server.Implementations/Data/ManagedConnection.cs
index 44dad5b17..11e33278d 100644
--- a/Emby.Server.Implementations/Data/ManagedConnection.cs
+++ b/Emby.Server.Implementations/Data/ManagedConnection.cs
@@ -7,7 +7,7 @@ using SQLitePCL.pretty;
namespace Emby.Server.Implementations.Data
{
- public class ManagedConnection : IDisposable
+ public sealed class ManagedConnection : IDisposable
{
private readonly SemaphoreSlim _writeLock;
diff --git a/Emby.Server.Implementations/Dto/DtoService.cs b/Emby.Server.Implementations/Dto/DtoService.cs
index ad76f3d6d..b91ff6408 100644
--- a/Emby.Server.Implementations/Dto/DtoService.cs
+++ b/Emby.Server.Implementations/Dto/DtoService.cs
@@ -134,14 +134,11 @@ namespace Emby.Server.Implementations.Dto
var dto = GetBaseItemDtoInternal(item, options, user, owner);
if (item is LiveTvChannel tvChannel)
{
- var list = new List<(BaseItemDto, LiveTvChannel)>(1) { (dto, tvChannel) };
- LivetvManager.AddChannelInfo(list, options, user);
+ LivetvManager.AddChannelInfo(new[] { (dto, tvChannel) }, options, user);
}
else if (item is LiveTvProgram)
{
- var list = new List<(BaseItem, BaseItemDto)>(1) { (item, dto) };
- var task = LivetvManager.AddInfoToProgramDto(list, options.Fields, user);
- Task.WaitAll(task);
+ LivetvManager.AddInfoToProgramDto(new[] { (item, dto) }, options.Fields, user).GetAwaiter().GetResult();
}
if (item is IItemByName itemByName
@@ -373,6 +370,12 @@ namespace Emby.Server.Implementations.Dto
if (item is MusicAlbum || item is Season || item is Playlist)
{
dto.ChildCount = dto.RecursiveItemCount;
+ var folderChildCount = folder.LinkedChildren.Length;
+ // The default is an empty array, so we can't reliably use the count when it's empty
+ if (folderChildCount > 0)
+ {
+ dto.ChildCount ??= folderChildCount;
+ }
}
if (options.ContainsField(ItemFields.ChildCount))
@@ -497,7 +500,7 @@ namespace Emby.Server.Implementations.Dto
}
catch (Exception ex)
{
- _logger.LogError(ex, "Error getting {imageType} image info for {path}", image.Type, image.Path);
+ _logger.LogError(ex, "Error getting {ImageType} image info for {Path}", image.Type, image.Path);
return null;
}
}
@@ -755,15 +758,6 @@ namespace Emby.Server.Implementations.Dto
dto.BackdropImageTags = GetTagsAndFillBlurhashes(dto, item, ImageType.Backdrop, backdropLimit);
}
- if (options.ContainsField(ItemFields.ScreenshotImageTags))
- {
- var screenshotLimit = options.GetImageLimit(ImageType.Screenshot);
- if (screenshotLimit > 0)
- {
- dto.ScreenshotImageTags = GetTagsAndFillBlurhashes(dto, item, ImageType.Screenshot, screenshotLimit);
- }
- }
-
if (options.ContainsField(ItemFields.Genres))
{
dto.Genres = item.Genres;
diff --git a/Emby.Server.Implementations/Emby.Server.Implementations.csproj b/Emby.Server.Implementations/Emby.Server.Implementations.csproj
index c1ce4b557..95acd216d 100644
--- a/Emby.Server.Implementations/Emby.Server.Implementations.csproj
+++ b/Emby.Server.Implementations/Emby.Server.Implementations.csproj
@@ -25,14 +25,14 @@
<ItemGroup>
<PackageReference Include="DiscUtils.Udf" Version="0.16.13" />
<PackageReference Include="Jellyfin.XmlTv" Version="10.6.2" />
- <PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="6.0.0-rc.2*" />
- <PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="6.0.0-rc.2*" />
- <PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="6.0.0-rc.2*" />
- <PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="6.0.0-rc.2*" />
- <PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="6.0.0-rc.2*" />
- <PackageReference Include="Mono.Nat" Version="3.0.1" />
+ <PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="6.0.0" />
+ <PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="6.0.0" />
+ <PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="6.0.0" />
+ <PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="6.0.0" />
+ <PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="6.0.0" />
+ <PackageReference Include="Mono.Nat" Version="3.0.2" />
<PackageReference Include="prometheus-net.DotNetRuntime" Version="4.2.2" />
- <PackageReference Include="sharpcompress" Version="0.30.0" />
+ <PackageReference Include="sharpcompress" Version="0.30.1" />
<PackageReference Include="SQLitePCL.pretty.netstandard" Version="3.1.0" />
<PackageReference Include="DotNet.Glob" Version="3.1.3" />
</ItemGroup>
diff --git a/Emby.Server.Implementations/EntryPoints/ExternalPortForwarding.cs b/Emby.Server.Implementations/EntryPoints/ExternalPortForwarding.cs
index 640754af4..d325fa14f 100644
--- a/Emby.Server.Implementations/EntryPoints/ExternalPortForwarding.cs
+++ b/Emby.Server.Implementations/EntryPoints/ExternalPortForwarding.cs
@@ -27,7 +27,6 @@ namespace Emby.Server.Implementations.EntryPoints
private readonly IServerApplicationHost _appHost;
private readonly ILogger<ExternalPortForwarding> _logger;
private readonly IServerConfigurationManager _config;
- private readonly IDeviceDiscovery _deviceDiscovery;
private readonly ConcurrentDictionary<IPEndPoint, byte> _createdRules = new ConcurrentDictionary<IPEndPoint, byte>();
@@ -42,17 +41,14 @@ namespace Emby.Server.Implementations.EntryPoints
/// <param name="logger">The logger.</param>
/// <param name="appHost">The application host.</param>
/// <param name="config">The configuration manager.</param>
- /// <param name="deviceDiscovery">The device discovery.</param>
public ExternalPortForwarding(
ILogger<ExternalPortForwarding> logger,
IServerApplicationHost appHost,
- IServerConfigurationManager config,
- IDeviceDiscovery deviceDiscovery)
+ IServerConfigurationManager config)
{
_logger = logger;
_appHost = appHost;
_config = config;
- _deviceDiscovery = deviceDiscovery;
}
private string GetConfigIdentifier()
diff --git a/Emby.Server.Implementations/HttpServer/Security/AuthService.cs b/Emby.Server.Implementations/HttpServer/Security/AuthService.cs
index e2ad07177..e7103ec95 100644
--- a/Emby.Server.Implementations/HttpServer/Security/AuthService.cs
+++ b/Emby.Server.Implementations/HttpServer/Security/AuthService.cs
@@ -24,7 +24,7 @@ namespace Emby.Server.Implementations.HttpServer.Security
if (!auth.HasToken)
{
- throw new AuthenticationException("Request does not contain a token.");
+ return auth;
}
if (!auth.IsAuthenticated)
diff --git a/Emby.Server.Implementations/HttpServer/WebSocketManager.cs b/Emby.Server.Implementations/HttpServer/WebSocketManager.cs
index f86bfd755..e99876dce 100644
--- a/Emby.Server.Implementations/HttpServer/WebSocketManager.cs
+++ b/Emby.Server.Implementations/HttpServer/WebSocketManager.cs
@@ -35,7 +35,12 @@ namespace Emby.Server.Implementations.HttpServer
/// <inheritdoc />
public async Task WebSocketRequestHandler(HttpContext context)
{
- _ = await _authService.Authenticate(context.Request).ConfigureAwait(false);
+ var authorizationInfo = await _authService.Authenticate(context.Request).ConfigureAwait(false);
+ if (!authorizationInfo.IsAuthenticated)
+ {
+ throw new SecurityException("Token is required");
+ }
+
try
{
_logger.LogInformation("WS {IP} request", context.Connection.RemoteIpAddress);
diff --git a/Emby.Server.Implementations/IO/FileRefresher.cs b/Emby.Server.Implementations/IO/FileRefresher.cs
index 47a83d77c..e62361c1e 100644
--- a/Emby.Server.Implementations/IO/FileRefresher.cs
+++ b/Emby.Server.Implementations/IO/FileRefresher.cs
@@ -1,5 +1,3 @@
-#nullable disable
-
#pragma warning disable CS1591
using System;
@@ -14,7 +12,7 @@ using Microsoft.Extensions.Logging;
namespace Emby.Server.Implementations.IO
{
- public class FileRefresher : IDisposable
+ public sealed class FileRefresher : IDisposable
{
private readonly ILogger _logger;
private readonly ILibraryManager _libraryManager;
@@ -22,7 +20,7 @@ namespace Emby.Server.Implementations.IO
private readonly List<string> _affectedPaths = new List<string>();
private readonly object _timerLock = new object();
- private Timer _timer;
+ private Timer? _timer;
private bool _disposed;
public FileRefresher(string path, IServerConfigurationManager configurationManager, ILibraryManager libraryManager, ILogger logger)
@@ -36,7 +34,7 @@ namespace Emby.Server.Implementations.IO
AddPath(path);
}
- public event EventHandler<EventArgs> Completed;
+ public event EventHandler<EventArgs>? Completed;
public string Path { get; private set; }
@@ -111,7 +109,7 @@ namespace Emby.Server.Implementations.IO
RestartTimer();
}
- private void OnTimerCallback(object state)
+ private void OnTimerCallback(object? state)
{
List<string> paths;
@@ -127,7 +125,7 @@ namespace Emby.Server.Implementations.IO
try
{
- ProcessPathChanges(paths.ToList());
+ ProcessPathChanges(paths);
}
catch (Exception ex)
{
@@ -137,12 +135,12 @@ namespace Emby.Server.Implementations.IO
private void ProcessPathChanges(List<string> paths)
{
- var itemsToRefresh = paths
+ IEnumerable<BaseItem> itemsToRefresh = paths
.Distinct(StringComparer.OrdinalIgnoreCase)
.Select(GetAffectedBaseItem)
.Where(item => item != null)
- .GroupBy(x => x.Id)
- .Select(x => x.First());
+ .GroupBy(x => x!.Id) // Removed null values in the previous .Where()
+ .Select(x => x.First())!;
foreach (var item in itemsToRefresh)
{
@@ -176,15 +174,15 @@ namespace Emby.Server.Implementations.IO
/// </summary>
/// <param name="path">The path.</param>
/// <returns>BaseItem.</returns>
- private BaseItem GetAffectedBaseItem(string path)
+ private BaseItem? GetAffectedBaseItem(string path)
{
- BaseItem item = null;
+ BaseItem? item = null;
while (item == null && !string.IsNullOrEmpty(path))
{
item = _libraryManager.FindByPath(path, null);
- path = System.IO.Path.GetDirectoryName(path);
+ path = System.IO.Path.GetDirectoryName(path) ?? string.Empty;
}
if (item != null)
diff --git a/Emby.Server.Implementations/IO/LibraryMonitor.cs b/Emby.Server.Implementations/IO/LibraryMonitor.cs
index e9d069cd3..9fcc7fe59 100644
--- a/Emby.Server.Implementations/IO/LibraryMonitor.cs
+++ b/Emby.Server.Implementations/IO/LibraryMonitor.cs
@@ -267,7 +267,7 @@ namespace Emby.Server.Implementations.IO
if (_fileSystemWatchers.TryAdd(path, newWatcher))
{
newWatcher.EnableRaisingEvents = true;
- _logger.LogInformation("Watching directory " + path);
+ _logger.LogInformation("Watching directory {Path}", path);
}
else
{
@@ -276,7 +276,7 @@ namespace Emby.Server.Implementations.IO
}
catch (Exception ex)
{
- _logger.LogError(ex, "Error watching path: {path}", path);
+ _logger.LogError(ex, "Error watching path: {Path}", path);
}
});
}
@@ -449,12 +449,12 @@ namespace Emby.Server.Implementations.IO
}
var newRefresher = new FileRefresher(path, _configurationManager, _libraryManager, _logger);
- newRefresher.Completed += NewRefresher_Completed;
+ newRefresher.Completed += OnNewRefresherCompleted;
_activeRefreshers.Add(newRefresher);
}
}
- private void NewRefresher_Completed(object sender, EventArgs e)
+ private void OnNewRefresherCompleted(object sender, EventArgs e)
{
var refresher = (FileRefresher)sender;
DisposeRefresher(refresher);
@@ -481,6 +481,7 @@ namespace Emby.Server.Implementations.IO
{
lock (_activeRefreshers)
{
+ refresher.Completed -= OnNewRefresherCompleted;
refresher.Dispose();
_activeRefreshers.Remove(refresher);
}
@@ -492,6 +493,7 @@ namespace Emby.Server.Implementations.IO
{
foreach (var refresher in _activeRefreshers.ToList())
{
+ refresher.Completed -= OnNewRefresherCompleted;
refresher.Dispose();
}
diff --git a/Emby.Server.Implementations/IO/ManagedFileSystem.cs b/Emby.Server.Implementations/IO/ManagedFileSystem.cs
index eeee28842..777cd2cd4 100644
--- a/Emby.Server.Implementations/IO/ManagedFileSystem.cs
+++ b/Emby.Server.Implementations/IO/ManagedFileSystem.cs
@@ -1,5 +1,3 @@
-#pragma warning disable CS1591
-
using System;
using System.Collections.Generic;
using System.Globalization;
@@ -23,6 +21,11 @@ namespace Emby.Server.Implementations.IO
private readonly string _tempPath;
private static readonly bool _isEnvironmentCaseInsensitive = OperatingSystem.IsWindows();
+ /// <summary>
+ /// Initializes a new instance of the <see cref="ManagedFileSystem"/> class.
+ /// </summary>
+ /// <param name="logger">The <see cref="ILogger"/> instance to use.</param>
+ /// <param name="applicationPaths">The <see cref="IApplicationPaths"/> instance to use.</param>
public ManagedFileSystem(
ILogger<ManagedFileSystem> logger,
IApplicationPaths applicationPaths)
@@ -31,6 +34,7 @@ namespace Emby.Server.Implementations.IO
_tempPath = applicationPaths.TempDirectory;
}
+ /// <inheritdoc />
public virtual void AddShortcutHandler(IShortcutHandler handler)
{
_shortcutHandlers.Add(handler);
@@ -72,6 +76,7 @@ namespace Emby.Server.Implementations.IO
return handler?.Resolve(filename);
}
+ /// <inheritdoc />
public virtual string MakeAbsolutePath(string folderPath, string filePath)
{
// path is actually a stream
@@ -358,11 +363,13 @@ namespace Emby.Server.Implementations.IO
return GetCreationTimeUtc(GetFileSystemInfo(path));
}
+ /// <inheritdoc />
public virtual DateTime GetCreationTimeUtc(FileSystemMetadata info)
{
return info.CreationTimeUtc;
}
+ /// <inheritdoc />
public virtual DateTime GetLastWriteTimeUtc(FileSystemMetadata info)
{
return info.LastWriteTimeUtc;
@@ -397,6 +404,7 @@ namespace Emby.Server.Implementations.IO
return GetLastWriteTimeUtc(GetFileSystemInfo(path));
}
+ /// <inheritdoc />
public virtual void SetHidden(string path, bool isHidden)
{
if (!OperatingSystem.IsWindows())
@@ -421,6 +429,7 @@ namespace Emby.Server.Implementations.IO
}
}
+ /// <inheritdoc />
public virtual void SetAttributes(string path, bool isHidden, bool readOnly)
{
if (!OperatingSystem.IsWindows())
@@ -444,7 +453,7 @@ namespace Emby.Server.Implementations.IO
if (readOnly)
{
- attributes = attributes | FileAttributes.ReadOnly;
+ attributes |= FileAttributes.ReadOnly;
}
else
{
@@ -453,7 +462,7 @@ namespace Emby.Server.Implementations.IO
if (isHidden)
{
- attributes = attributes | FileAttributes.Hidden;
+ attributes |= FileAttributes.Hidden;
}
else
{
@@ -498,6 +507,7 @@ namespace Emby.Server.Implementations.IO
File.Copy(temp1, file2, true);
}
+ /// <inheritdoc />
public virtual bool ContainsSubPath(string parentPath, string path)
{
if (string.IsNullOrEmpty(parentPath))
@@ -515,6 +525,7 @@ namespace Emby.Server.Implementations.IO
_isEnvironmentCaseInsensitive ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal);
}
+ /// <inheritdoc />
public virtual string NormalizePath(string path)
{
if (string.IsNullOrEmpty(path))
@@ -530,6 +541,7 @@ namespace Emby.Server.Implementations.IO
return Path.TrimEndingDirectorySeparator(path);
}
+ /// <inheritdoc />
public virtual bool AreEqual(string path1, string path2)
{
if (path1 == null && path2 == null)
@@ -548,6 +560,7 @@ namespace Emby.Server.Implementations.IO
_isEnvironmentCaseInsensitive ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal);
}
+ /// <inheritdoc />
public virtual string GetFileNameWithoutExtension(FileSystemMetadata info)
{
if (info.IsDirectory)
@@ -558,11 +571,11 @@ namespace Emby.Server.Implementations.IO
return Path.GetFileNameWithoutExtension(info.FullName);
}
+ /// <inheritdoc />
public virtual bool IsPathFile(string path)
{
- // Cannot use Path.IsPathRooted because it returns false under mono when using windows-based paths, e.g. C:\\
- if (path.IndexOf("://", StringComparison.OrdinalIgnoreCase) != -1 &&
- !path.StartsWith("file://", StringComparison.OrdinalIgnoreCase))
+ if (path.Contains("://", StringComparison.OrdinalIgnoreCase)
+ && !path.StartsWith("file://", StringComparison.OrdinalIgnoreCase))
{
return false;
}
@@ -570,17 +583,23 @@ namespace Emby.Server.Implementations.IO
return true;
}
+ /// <inheritdoc />
public virtual void DeleteFile(string path)
{
SetAttributes(path, false, false);
File.Delete(path);
}
+ /// <inheritdoc />
public virtual List<FileSystemMetadata> GetDrives()
{
// check for ready state to avoid waiting for drives to timeout
// 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)
+ return DriveInfo.GetDrives()
+ .Where(
+ d => (d.DriveType == DriveType.Fixed || d.DriveType == DriveType.Network || d.DriveType == DriveType.Removable)
+ && d.IsReady
+ && d.TotalSize != 0)
.Select(d => new FileSystemMetadata
{
Name = d.Name,
@@ -589,16 +608,19 @@ namespace Emby.Server.Implementations.IO
}).ToList();
}
+ /// <inheritdoc />
public virtual IEnumerable<FileSystemMetadata> GetDirectories(string path, bool recursive = false)
{
return ToMetadata(new DirectoryInfo(path).EnumerateDirectories("*", GetEnumerationOptions(recursive)));
}
+ /// <inheritdoc />
public virtual IEnumerable<FileSystemMetadata> GetFiles(string path, bool recursive = false)
{
return GetFiles(path, null, false, recursive);
}
+ /// <inheritdoc />
public virtual IEnumerable<FileSystemMetadata> GetFiles(string path, IReadOnlyList<string>? extensions, bool enableCaseSensitiveExtensions, bool recursive = false)
{
var enumerationOptions = GetEnumerationOptions(recursive);
@@ -629,6 +651,7 @@ namespace Emby.Server.Implementations.IO
return ToMetadata(files);
}
+ /// <inheritdoc />
public virtual IEnumerable<FileSystemMetadata> GetFileSystemEntries(string path, bool recursive = false)
{
var directoryInfo = new DirectoryInfo(path);
@@ -642,16 +665,19 @@ namespace Emby.Server.Implementations.IO
return infos.Select(GetFileSystemMetadata);
}
+ /// <inheritdoc />
public virtual IEnumerable<string> GetDirectoryPaths(string path, bool recursive = false)
{
return Directory.EnumerateDirectories(path, "*", GetEnumerationOptions(recursive));
}
+ /// <inheritdoc />
public virtual IEnumerable<string> GetFilePaths(string path, bool recursive = false)
{
return GetFilePaths(path, null, false, recursive);
}
+ /// <inheritdoc />
public virtual IEnumerable<string> GetFilePaths(string path, string[]? extensions, bool enableCaseSensitiveExtensions, bool recursive = false)
{
var enumerationOptions = GetEnumerationOptions(recursive);
@@ -682,6 +708,7 @@ namespace Emby.Server.Implementations.IO
return files;
}
+ /// <inheritdoc />
public virtual IEnumerable<string> GetFileSystemEntryPaths(string path, bool recursive = false)
{
return Directory.EnumerateFileSystemEntries(path, "*", GetEnumerationOptions(recursive));
diff --git a/Emby.Server.Implementations/Library/CoreResolutionIgnoreRule.cs b/Emby.Server.Implementations/Library/CoreResolutionIgnoreRule.cs
index bc5b4499f..29758a078 100644
--- a/Emby.Server.Implementations/Library/CoreResolutionIgnoreRule.cs
+++ b/Emby.Server.Implementations/Library/CoreResolutionIgnoreRule.cs
@@ -1,8 +1,9 @@
using System;
using System.IO;
+using Emby.Naming.Audio;
+using Emby.Naming.Common;
using MediaBrowser.Controller;
using MediaBrowser.Controller.Entities;
-using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Resolvers;
using MediaBrowser.Model.IO;
@@ -13,17 +14,17 @@ namespace Emby.Server.Implementations.Library
/// </summary>
public class CoreResolutionIgnoreRule : IResolverIgnoreRule
{
- private readonly ILibraryManager _libraryManager;
+ private readonly NamingOptions _namingOptions;
private readonly IServerApplicationPaths _serverApplicationPaths;
/// <summary>
/// Initializes a new instance of the <see cref="CoreResolutionIgnoreRule"/> class.
/// </summary>
- /// <param name="libraryManager">The library manager.</param>
+ /// <param name="namingOptions">The naming options.</param>
/// <param name="serverApplicationPaths">The server application paths.</param>
- public CoreResolutionIgnoreRule(ILibraryManager libraryManager, IServerApplicationPaths serverApplicationPaths)
+ public CoreResolutionIgnoreRule(NamingOptions namingOptions, IServerApplicationPaths serverApplicationPaths)
{
- _libraryManager = libraryManager;
+ _namingOptions = namingOptions;
_serverApplicationPaths = serverApplicationPaths;
}
@@ -78,7 +79,7 @@ namespace Emby.Server.Implementations.Library
{
// Don't resolve these into audio files
if (Path.GetFileNameWithoutExtension(filename.AsSpan()).Equals(BaseItem.ThemeSongFileName, StringComparison.Ordinal)
- && _libraryManager.IsAudioFile(filename))
+ && AudioFileParser.IsAudioFile(filename, _namingOptions))
{
return true;
}
diff --git a/Emby.Server.Implementations/Library/LibraryManager.cs b/Emby.Server.Implementations/Library/LibraryManager.cs
index 1326f60fe..778b6225e 100644
--- a/Emby.Server.Implementations/Library/LibraryManager.cs
+++ b/Emby.Server.Implementations/Library/LibraryManager.cs
@@ -79,6 +79,7 @@ namespace Emby.Server.Implementations.Library
private readonly IFileSystem _fileSystem;
private readonly IItemRepository _itemRepository;
private readonly IImageProcessor _imageProcessor;
+ private readonly NamingOptions _namingOptions;
/// <summary>
/// The _root folder sync lock.
@@ -88,9 +89,6 @@ namespace Emby.Server.Implementations.Library
private readonly TimeSpan _viewRefreshInterval = TimeSpan.FromHours(24);
- private NamingOptions _namingOptions;
- private string[] _videoFileExtensions;
-
/// <summary>
/// The _root folder.
/// </summary>
@@ -116,6 +114,7 @@ namespace Emby.Server.Implementations.Library
/// <param name="itemRepository">The item repository.</param>
/// <param name="imageProcessor">The image processor.</param>
/// <param name="memoryCache">The memory cache.</param>
+ /// <param name="namingOptions">The naming options.</param>
public LibraryManager(
IServerApplicationHost appHost,
ILogger<LibraryManager> logger,
@@ -130,7 +129,8 @@ namespace Emby.Server.Implementations.Library
IMediaEncoder mediaEncoder,
IItemRepository itemRepository,
IImageProcessor imageProcessor,
- IMemoryCache memoryCache)
+ IMemoryCache memoryCache,
+ NamingOptions namingOptions)
{
_appHost = appHost;
_logger = logger;
@@ -146,6 +146,7 @@ namespace Emby.Server.Implementations.Library
_itemRepository = itemRepository;
_imageProcessor = imageProcessor;
_memoryCache = memoryCache;
+ _namingOptions = namingOptions;
_configurationManager.ConfigurationUpdated += ConfigurationUpdated;
@@ -333,8 +334,7 @@ namespace Emby.Server.Implementations.Library
{
try
{
- var task = BaseItem.ChannelManager.DeleteItem(item);
- Task.WaitAll(task);
+ BaseItem.ChannelManager.DeleteItem(item).GetAwaiter().GetResult();
}
catch (ArgumentException)
{
@@ -492,7 +492,7 @@ namespace Emby.Server.Implementations.Library
}
catch (Exception ex)
{
- _logger.LogError(ex, "Error in {resolver} resolving {path}", resolver.GetType().Name, args.Path);
+ _logger.LogError(ex, "Error in {Resolver} resolving {Path}", resolver.GetType().Name, args.Path);
return null;
}
}
@@ -799,7 +799,7 @@ namespace Emby.Server.Implementations.Library
{
var userRootPath = _configurationManager.ApplicationPaths.DefaultUserViewsPath;
- _logger.LogDebug("Creating userRootPath at {path}", userRootPath);
+ _logger.LogDebug("Creating userRootPath at {Path}", userRootPath);
Directory.CreateDirectory(userRootPath);
var newItemId = GetNewItemId(userRootPath, typeof(UserRootFolder));
@@ -810,7 +810,7 @@ namespace Emby.Server.Implementations.Library
}
catch (Exception ex)
{
- _logger.LogError(ex, "Error creating UserRootFolder {path}", newItemId);
+ _logger.LogError(ex, "Error creating UserRootFolder {Path}", newItemId);
}
if (tmpItem == null)
@@ -827,7 +827,7 @@ namespace Emby.Server.Implementations.Library
}
_userRootFolder = tmpItem;
- _logger.LogDebug("Setting userRootFolder: {folder}", _userRootFolder);
+ _logger.LogDebug("Setting userRootFolder: {Folder}", _userRootFolder);
}
}
}
@@ -1213,7 +1213,7 @@ namespace Emby.Server.Implementations.Library
}
catch (Exception ex)
{
- _logger.LogError(ex, "Error resolving shortcut file {file}", i);
+ _logger.LogError(ex, "Error resolving shortcut file {File}", i);
return null;
}
})
@@ -1698,7 +1698,7 @@ namespace Emby.Server.Implementations.Library
if (video == null)
{
- _logger.LogError("Intro resolver returned null for {path}.", info.Path);
+ _logger.LogError("Intro resolver returned null for {Path}.", info.Path);
}
else
{
@@ -1717,7 +1717,7 @@ namespace Emby.Server.Implementations.Library
}
catch (Exception ex)
{
- _logger.LogError(ex, "Error resolving path {path}.", info.Path);
+ _logger.LogError(ex, "Error resolving path {Path}.", info.Path);
}
}
else
@@ -2502,16 +2502,6 @@ namespace Emby.Server.Implementations.Library
}
/// <inheritdoc />
- public bool IsVideoFile(string path)
- {
- return VideoResolver.IsVideoFile(path, GetNamingOptions());
- }
-
- /// <inheritdoc />
- public bool IsAudioFile(string path)
- => AudioFileParser.IsAudioFile(path, GetNamingOptions());
-
- /// <inheritdoc />
public int? GetSeasonNumberFromPath(string path)
=> SeasonPathParser.Parse(path, true, true).SeasonNumber;
@@ -2526,7 +2516,7 @@ namespace Emby.Server.Implementations.Library
isAbsoluteNaming = null;
}
- var resolver = new EpisodeResolver(GetNamingOptions());
+ var resolver = new EpisodeResolver(_namingOptions);
var isFolder = episode.VideoType == VideoType.BluRay || episode.VideoType == VideoType.Dvd;
@@ -2683,21 +2673,9 @@ namespace Emby.Server.Implementations.Library
return changed;
}
- /// <inheritdoc />
- public NamingOptions GetNamingOptions()
- {
- if (_namingOptions == null)
- {
- _namingOptions = new NamingOptions();
- _videoFileExtensions = _namingOptions.VideoFileExtensions;
- }
-
- return _namingOptions;
- }
-
public ItemLookupInfo ParseName(string name)
{
- var namingOptions = GetNamingOptions();
+ var namingOptions = _namingOptions;
var result = VideoResolver.CleanDateTime(name, namingOptions);
return new ItemLookupInfo
@@ -2709,11 +2687,11 @@ namespace Emby.Server.Implementations.Library
public IEnumerable<Video> FindTrailers(BaseItem owner, List<FileSystemMetadata> fileSystemChildren, IDirectoryService directoryService)
{
- var namingOptions = GetNamingOptions();
+ var namingOptions = _namingOptions;
var files = owner.IsInMixedFolder ? new List<FileSystemMetadata>() : fileSystemChildren.Where(i => i.IsDirectory)
.Where(i => string.Equals(i.Name, BaseItem.TrailersFolderName, StringComparison.OrdinalIgnoreCase))
- .SelectMany(i => _fileSystem.GetFiles(i.FullName, _videoFileExtensions, false, false))
+ .SelectMany(i => _fileSystem.GetFiles(i.FullName, namingOptions.VideoFileExtensions, false, false))
.ToList();
var videos = VideoListResolver.Resolve(fileSystemChildren, namingOptions);
@@ -2727,7 +2705,7 @@ namespace Emby.Server.Implementations.Library
var resolvers = new IItemResolver[]
{
- new GenericVideoResolver<Trailer>(this)
+ new GenericVideoResolver<Trailer>(_namingOptions)
};
return ResolvePaths(files, directoryService, null, new LibraryOptions(), null, resolvers)
@@ -2753,11 +2731,11 @@ namespace Emby.Server.Implementations.Library
public IEnumerable<Video> FindExtras(BaseItem owner, List<FileSystemMetadata> fileSystemChildren, IDirectoryService directoryService)
{
- var namingOptions = GetNamingOptions();
+ var namingOptions = _namingOptions;
var files = owner.IsInMixedFolder ? new List<FileSystemMetadata>() : fileSystemChildren.Where(i => i.IsDirectory)
.Where(i => BaseItem.AllExtrasTypesFolderNames.ContainsKey(i.Name ?? string.Empty))
- .SelectMany(i => _fileSystem.GetFiles(i.FullName, _videoFileExtensions, false, false))
+ .SelectMany(i => _fileSystem.GetFiles(i.FullName, namingOptions.VideoFileExtensions, false, false))
.ToList();
var videos = VideoListResolver.Resolve(fileSystemChildren, namingOptions);
@@ -2841,7 +2819,7 @@ namespace Emby.Server.Implementations.Library
private void SetExtraTypeFromFilename(Video item)
{
- var resolver = new ExtraResolver(GetNamingOptions());
+ var resolver = new ExtraResolver(_namingOptions);
var result = resolver.GetExtraInfo(item.Path);
diff --git a/Emby.Server.Implementations/Library/MediaSourceManager.cs b/Emby.Server.Implementations/Library/MediaSourceManager.cs
index 351fced34..972d4ebbb 100644
--- a/Emby.Server.Implementations/Library/MediaSourceManager.cs
+++ b/Emby.Server.Implementations/Library/MediaSourceManager.cs
@@ -45,6 +45,7 @@ namespace Emby.Server.Implementations.Library
private readonly IMediaEncoder _mediaEncoder;
private readonly ILocalizationManager _localizationManager;
private readonly IApplicationPaths _appPaths;
+ private readonly IDirectoryService _directoryService;
private readonly ConcurrentDictionary<string, ILiveStream> _openStreams = new ConcurrentDictionary<string, ILiveStream>(StringComparer.OrdinalIgnoreCase);
private readonly SemaphoreSlim _liveStreamSemaphore = new SemaphoreSlim(1, 1);
@@ -61,7 +62,8 @@ namespace Emby.Server.Implementations.Library
ILogger<MediaSourceManager> logger,
IFileSystem fileSystem,
IUserDataManager userDataManager,
- IMediaEncoder mediaEncoder)
+ IMediaEncoder mediaEncoder,
+ IDirectoryService directoryService)
{
_itemRepo = itemRepo;
_userManager = userManager;
@@ -72,6 +74,7 @@ namespace Emby.Server.Implementations.Library
_mediaEncoder = mediaEncoder;
_localizationManager = localizationManager;
_appPaths = applicationPaths;
+ _directoryService = directoryService;
}
public void AddParts(IEnumerable<IMediaSourceProvider> providers)
@@ -106,16 +109,6 @@ namespace Emby.Server.Implementations.Library
return false;
}
- public List<MediaStream> GetMediaStreams(string mediaSourceId)
- {
- var list = GetMediaStreams(new MediaStreamQuery
- {
- ItemId = new Guid(mediaSourceId)
- });
-
- return GetMediaStreamsForItem(list);
- }
-
public List<MediaStream> GetMediaStreams(Guid itemId)
{
var list = GetMediaStreams(new MediaStreamQuery
@@ -161,7 +154,7 @@ namespace Emby.Server.Implementations.Library
if (allowMediaProbe && mediaSources[0].Type != MediaSourceType.Placeholder && !mediaSources[0].MediaStreams.Any(i => i.Type == MediaStreamType.Audio || i.Type == MediaStreamType.Video))
{
await item.RefreshMetadata(
- new MetadataRefreshOptions(new DirectoryService(_fileSystem))
+ new MetadataRefreshOptions(_directoryService)
{
EnableRemoteContentProbe = true,
MetadataRefreshMode = MetadataRefreshMode.FullRefresh
@@ -212,6 +205,7 @@ namespace Emby.Server.Implementations.Library
return SortMediaSources(list);
}
+ /// <inheritdoc />>
public MediaProtocol GetPathProtocol(string path)
{
if (path.StartsWith("Rtsp", StringComparison.OrdinalIgnoreCase))
@@ -258,7 +252,7 @@ namespace Emby.Server.Implementations.Library
{
if (path != null)
{
- if (path.IndexOf(".m3u", StringComparison.OrdinalIgnoreCase) != -1)
+ if (path.Contains(".m3u", StringComparison.OrdinalIgnoreCase))
{
return false;
}
@@ -297,7 +291,7 @@ namespace Emby.Server.Implementations.Library
catch (Exception ex)
{
_logger.LogError(ex, "Error getting media sources");
- return new List<MediaSourceInfo>();
+ return Enumerable.Empty<MediaSourceInfo>();
}
}
@@ -494,14 +488,11 @@ namespace Emby.Server.Implementations.Library
_liveStreamSemaphore.Release();
}
- // TODO: Don't hardcode this
- const bool isAudio = false;
-
try
{
if (mediaSource.MediaStreams.Any(i => i.Index != -1) || !mediaSource.SupportsProbing)
{
- AddMediaInfo(mediaSource, isAudio);
+ AddMediaInfo(mediaSource);
}
else
{
@@ -509,14 +500,14 @@ namespace Emby.Server.Implementations.Library
string cacheKey = request.OpenToken;
await new LiveStreamHelper(_mediaEncoder, _logger, _appPaths)
- .AddMediaInfoWithProbe(mediaSource, isAudio, cacheKey, true, cancellationToken)
+ .AddMediaInfoWithProbe(mediaSource, false, cacheKey, true, cancellationToken)
.ConfigureAwait(false);
}
}
catch (Exception ex)
{
_logger.LogError(ex, "Error probing live tv stream");
- AddMediaInfo(mediaSource, isAudio);
+ AddMediaInfo(mediaSource);
}
// TODO: @bond Fix
@@ -536,7 +527,7 @@ namespace Emby.Server.Implementations.Library
return new Tuple<LiveStreamResponse, IDirectStreamProvider>(new LiveStreamResponse(clone), liveStream as IDirectStreamProvider);
}
- private static void AddMediaInfo(MediaSourceInfo mediaSource, bool isAudio)
+ private static void AddMediaInfo(MediaSourceInfo mediaSource)
{
mediaSource.DefaultSubtitleStreamIndex = null;
@@ -855,9 +846,7 @@ namespace Emby.Server.Implementations.Library
return (provider, keyId);
}
- /// <summary>
- /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
- /// </summary>
+ /// <inheritdoc />
public void Dispose()
{
Dispose(true);
diff --git a/Emby.Server.Implementations/Library/Resolvers/Audio/AudioResolver.cs b/Emby.Server.Implementations/Library/Resolvers/Audio/AudioResolver.cs
index fd9747b4b..7a6aea9c1 100644
--- a/Emby.Server.Implementations/Library/Resolvers/Audio/AudioResolver.cs
+++ b/Emby.Server.Implementations/Library/Resolvers/Audio/AudioResolver.cs
@@ -6,7 +6,10 @@ using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
+using Emby.Naming.Audio;
using Emby.Naming.AudioBook;
+using Emby.Naming.Common;
+using Emby.Naming.Video;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Providers;
@@ -21,11 +24,11 @@ namespace Emby.Server.Implementations.Library.Resolvers.Audio
/// </summary>
public class AudioResolver : ItemResolver<MediaBrowser.Controller.Entities.Audio.Audio>, IMultiItemResolver
{
- private readonly ILibraryManager _libraryManager;
+ private readonly NamingOptions _namingOptions;
- public AudioResolver(ILibraryManager libraryManager)
+ public AudioResolver(NamingOptions namingOptions)
{
- _libraryManager = libraryManager;
+ _namingOptions = namingOptions;
}
/// <summary>
@@ -40,7 +43,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.Audio
string collectionType,
IDirectoryService directoryService)
{
- var result = ResolveMultipleInternal(parent, files, collectionType, directoryService);
+ var result = ResolveMultipleInternal(parent, files, collectionType);
if (result != null)
{
@@ -56,12 +59,11 @@ namespace Emby.Server.Implementations.Library.Resolvers.Audio
private MultiItemResolverResult ResolveMultipleInternal(
Folder parent,
List<FileSystemMetadata> files,
- string collectionType,
- IDirectoryService directoryService)
+ string collectionType)
{
if (string.Equals(collectionType, CollectionType.Books, StringComparison.OrdinalIgnoreCase))
{
- return ResolveMultipleAudio<AudioBook>(parent, files, directoryService, false, collectionType, true);
+ return ResolveMultipleAudio(parent, files, true);
}
return null;
@@ -87,14 +89,10 @@ namespace Emby.Server.Implementations.Library.Resolvers.Audio
return null;
}
- var files = args.FileSystemChildren
- .Where(i => !_libraryManager.IgnoreFile(i, args.Parent))
- .ToList();
-
- return FindAudio<AudioBook>(args, args.Path, args.Parent, files, args.DirectoryService, collectionType, false);
+ return FindAudioBook(args, false);
}
- if (_libraryManager.IsAudioFile(args.Path))
+ if (AudioFileParser.IsAudioFile(args.Path, _namingOptions))
{
var extension = Path.GetExtension(args.Path);
@@ -107,7 +105,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))
+ if (isMixedCollectionType && VideoResolver.IsVideoFile(args.Path, _namingOptions))
{
return null;
}
@@ -141,29 +139,23 @@ namespace Emby.Server.Implementations.Library.Resolvers.Audio
return null;
}
- private T FindAudio<T>(ItemResolveArgs args, string path, Folder parent, List<FileSystemMetadata> fileSystemEntries, IDirectoryService directoryService, string collectionType, bool parseName)
- where T : MediaBrowser.Controller.Entities.Audio.Audio, new()
+ private AudioBook FindAudioBook(ItemResolveArgs args, bool parseName)
{
// TODO: Allow GetMultiDiscMovie in here
- const bool supportsMultiVersion = false;
+ var result = ResolveMultipleAudio(args.Parent, args.GetActualFileSystemChildren(), parseName);
- var result = ResolveMultipleAudio<T>(parent, fileSystemEntries, directoryService, supportsMultiVersion, collectionType, parseName) ??
- new MultiItemResolverResult();
-
- if (result.Items.Count == 1)
+ if (result == null || result.Items.Count != 1 || result.Items[0] is not AudioBook item)
{
- // If we were supporting this we'd be checking filesFromOtherItems
- var item = (T)result.Items[0];
- item.IsInMixedFolder = false;
- item.Name = Path.GetFileName(item.ContainingFolderPath);
- return item;
+ return null;
}
- return null;
+ // If we were supporting this we'd be checking filesFromOtherItems
+ item.IsInMixedFolder = false;
+ item.Name = Path.GetFileName(item.ContainingFolderPath);
+ return item;
}
- private MultiItemResolverResult ResolveMultipleAudio<T>(Folder parent, IEnumerable<FileSystemMetadata> fileSystemEntries, IDirectoryService directoryService, bool suppportMultiEditions, string collectionType, bool parseName)
- where T : MediaBrowser.Controller.Entities.Audio.Audio, new()
+ private MultiItemResolverResult ResolveMultipleAudio(Folder parent, IEnumerable<FileSystemMetadata> fileSystemEntries, bool parseName)
{
var files = new List<FileSystemMetadata>();
var items = new List<BaseItem>();
@@ -176,15 +168,13 @@ namespace Emby.Server.Implementations.Library.Resolvers.Audio
{
leftOver.Add(child);
}
- else if (!IsIgnored(child.Name))
+ else
{
files.Add(child);
}
}
- var namingOptions = ((LibraryManager)_libraryManager).GetNamingOptions();
-
- var resolver = new AudioBookListResolver(namingOptions);
+ var resolver = new AudioBookListResolver(_namingOptions);
var resolverResult = resolver.Resolve(files).ToList();
var result = new MultiItemResolverResult
@@ -210,7 +200,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.Audio
var firstMedia = resolvedItem.Files[0];
- var libraryItem = new T
+ var libraryItem = new AudioBook
{
Path = firstMedia.Path,
IsInMixedFolder = isInMixedFolder,
@@ -230,12 +220,12 @@ namespace Emby.Server.Implementations.Library.Resolvers.Audio
return result;
}
- private bool ContainsFile(List<AudioBookInfo> result, FileSystemMetadata file)
+ private static bool ContainsFile(IEnumerable<AudioBookInfo> result, FileSystemMetadata file)
{
return result.Any(i => ContainsFile(i, file));
}
- private bool ContainsFile(AudioBookInfo result, FileSystemMetadata file)
+ private static bool ContainsFile(AudioBookInfo result, FileSystemMetadata file)
{
return result.Files.Any(i => ContainsFile(i, file)) ||
result.AlternateVersions.Any(i => ContainsFile(i, file)) ||
@@ -246,10 +236,5 @@ namespace Emby.Server.Implementations.Library.Resolvers.Audio
{
return string.Equals(result.Path, file.FullName, StringComparison.OrdinalIgnoreCase);
}
-
- private static bool IsIgnored(string filename)
- {
- return false;
- }
}
}
diff --git a/Emby.Server.Implementations/Library/Resolvers/Audio/MusicAlbumResolver.cs b/Emby.Server.Implementations/Library/Resolvers/Audio/MusicAlbumResolver.cs
index 60720dd2f..a9819a364 100644
--- a/Emby.Server.Implementations/Library/Resolvers/Audio/MusicAlbumResolver.cs
+++ b/Emby.Server.Implementations/Library/Resolvers/Audio/MusicAlbumResolver.cs
@@ -6,6 +6,7 @@ using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Emby.Naming.Audio;
+using Emby.Naming.Common;
using MediaBrowser.Controller.Entities.Audio;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Providers;
@@ -22,20 +23,17 @@ namespace Emby.Server.Implementations.Library.Resolvers.Audio
public class MusicAlbumResolver : ItemResolver<MusicAlbum>
{
private readonly ILogger<MusicAlbumResolver> _logger;
- private readonly IFileSystem _fileSystem;
- private readonly ILibraryManager _libraryManager;
+ private readonly NamingOptions _namingOptions;
/// <summary>
/// Initializes a new instance of the <see cref="MusicAlbumResolver"/> class.
/// </summary>
/// <param name="logger">The logger.</param>
- /// <param name="fileSystem">The file system.</param>
- /// <param name="libraryManager">The library manager.</param>
- public MusicAlbumResolver(ILogger<MusicAlbumResolver> logger, IFileSystem fileSystem, ILibraryManager libraryManager)
+ /// <param name="namingOptions">The naming options.</param>
+ public MusicAlbumResolver(ILogger<MusicAlbumResolver> logger, NamingOptions namingOptions)
{
_logger = logger;
- _fileSystem = fileSystem;
- _libraryManager = libraryManager;
+ _namingOptions = namingOptions;
}
/// <summary>
@@ -87,7 +85,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.Audio
/// <returns><c>true</c> if the provided path points to a music album, <c>false</c> otherwise.</returns>
public bool IsMusicAlbum(string path, IDirectoryService directoryService)
{
- return ContainsMusic(directoryService.GetFileSystemEntries(path), true, directoryService, _logger, _fileSystem, _libraryManager);
+ return ContainsMusic(directoryService.GetFileSystemEntries(path), true, directoryService);
}
/// <summary>
@@ -101,7 +99,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, _libraryManager))
+ if (ContainsMusic(args.FileSystemChildren, true, args.DirectoryService))
{
return true;
}
@@ -116,13 +114,10 @@ namespace Emby.Server.Implementations.Library.Resolvers.Audio
private bool ContainsMusic(
IEnumerable<FileSystemMetadata> list,
bool allowSubfolders,
- IDirectoryService directoryService,
- ILogger<MusicAlbumResolver> logger,
- IFileSystem fileSystem,
- ILibraryManager libraryManager)
+ IDirectoryService directoryService)
{
// check for audio files before digging down into directories
- var foundAudioFile = list.Any(fileSystemInfo => !fileSystemInfo.IsDirectory && libraryManager.IsAudioFile(fileSystemInfo.FullName));
+ var foundAudioFile = list.Any(fileSystemInfo => !fileSystemInfo.IsDirectory && AudioFileParser.IsAudioFile(fileSystemInfo.FullName, _namingOptions));
if (foundAudioFile)
{
// at least one audio file exists
@@ -137,21 +132,20 @@ namespace Emby.Server.Implementations.Library.Resolvers.Audio
var discSubfolderCount = 0;
- var namingOptions = ((LibraryManager)_libraryManager).GetNamingOptions();
- var parser = new AlbumParser(namingOptions);
+ var parser = new AlbumParser(_namingOptions);
var directories = list.Where(fileSystemInfo => fileSystemInfo.IsDirectory);
var result = Parallel.ForEach(directories, (fileSystemInfo, state) =>
{
var path = fileSystemInfo.FullName;
- var hasMusic = ContainsMusic(directoryService.GetFileSystemEntries(path), false, directoryService, logger, fileSystem, libraryManager);
+ var hasMusic = ContainsMusic(directoryService.GetFileSystemEntries(path), false, directoryService);
if (hasMusic)
{
if (parser.IsMultiPart(path))
{
- logger.LogDebug("Found multi-disc folder: " + path);
+ _logger.LogDebug("Found multi-disc folder: {Path}", path);
Interlocked.Increment(ref discSubfolderCount);
}
else
diff --git a/Emby.Server.Implementations/Library/Resolvers/Audio/MusicArtistResolver.cs b/Emby.Server.Implementations/Library/Resolvers/Audio/MusicArtistResolver.cs
index 3d2ae95d2..27e18be42 100644
--- a/Emby.Server.Implementations/Library/Resolvers/Audio/MusicArtistResolver.cs
+++ b/Emby.Server.Implementations/Library/Resolvers/Audio/MusicArtistResolver.cs
@@ -3,6 +3,7 @@
using System;
using System.Linq;
using System.Threading.Tasks;
+using Emby.Naming.Common;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Entities.Audio;
using MediaBrowser.Controller.Library;
@@ -19,27 +20,19 @@ namespace Emby.Server.Implementations.Library.Resolvers.Audio
public class MusicArtistResolver : ItemResolver<MusicArtist>
{
private readonly ILogger<MusicAlbumResolver> _logger;
- private readonly IFileSystem _fileSystem;
- private readonly ILibraryManager _libraryManager;
- private readonly IServerConfigurationManager _config;
+ private NamingOptions _namingOptions;
/// <summary>
/// Initializes a new instance of the <see cref="MusicArtistResolver"/> class.
/// </summary>
/// <param name="logger">The logger for the created <see cref="MusicAlbumResolver"/> instances.</param>
- /// <param name="fileSystem">The file system.</param>
- /// <param name="libraryManager">The library manager.</param>
- /// <param name="config">The configuration manager.</param>
+ /// <param name="namingOptions">The naming options.</param>
public MusicArtistResolver(
ILogger<MusicAlbumResolver> logger,
- IFileSystem fileSystem,
- ILibraryManager libraryManager,
- IServerConfigurationManager config)
+ NamingOptions namingOptions)
{
_logger = logger;
- _fileSystem = fileSystem;
- _libraryManager = libraryManager;
- _config = config;
+ _namingOptions = namingOptions;
}
/// <summary>
@@ -89,7 +82,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.Audio
var directoryService = args.DirectoryService;
- var albumResolver = new MusicAlbumResolver(_logger, _fileSystem, _libraryManager);
+ var albumResolver = new MusicAlbumResolver(_logger, _namingOptions);
// If we contain an album assume we are an artist folder
var directories = args.FileSystemChildren.Where(i => i.IsDirectory);
diff --git a/Emby.Server.Implementations/Library/Resolvers/BaseVideoResolver.cs b/Emby.Server.Implementations/Library/Resolvers/BaseVideoResolver.cs
index 9ff99fa43..0ebf0e530 100644
--- a/Emby.Server.Implementations/Library/Resolvers/BaseVideoResolver.cs
+++ b/Emby.Server.Implementations/Library/Resolvers/BaseVideoResolver.cs
@@ -6,6 +6,7 @@ using System;
using System.IO;
using System.Linq;
using DiscUtils.Udf;
+using Emby.Naming.Common;
using Emby.Naming.Video;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Library;
@@ -21,12 +22,12 @@ namespace Emby.Server.Implementations.Library.Resolvers
public abstract class BaseVideoResolver<T> : MediaBrowser.Controller.Resolvers.ItemResolver<T>
where T : Video, new()
{
- protected BaseVideoResolver(ILibraryManager libraryManager)
+ protected BaseVideoResolver(NamingOptions namingOptions)
{
- LibraryManager = libraryManager;
+ NamingOptions = namingOptions;
}
- protected ILibraryManager LibraryManager { get; }
+ protected NamingOptions NamingOptions { get; }
/// <summary>
/// Resolves the specified args.
@@ -48,7 +49,7 @@ namespace Emby.Server.Implementations.Library.Resolvers
protected virtual TVideoType ResolveVideo<TVideoType>(ItemResolveArgs args, bool parseName)
where TVideoType : Video, new()
{
- var namingOptions = LibraryManager.GetNamingOptions();
+ var namingOptions = NamingOptions;
// If the path is a file check for a matching extensions
if (args.IsDirectory)
@@ -138,7 +139,7 @@ namespace Emby.Server.Implementations.Library.Resolvers
return null;
}
- if (LibraryManager.IsVideoFile(args.Path) || videoInfo.IsStub)
+ if (VideoResolver.IsVideoFile(args.Path, NamingOptions) || videoInfo.IsStub)
{
var path = args.Path;
@@ -267,7 +268,7 @@ namespace Emby.Server.Implementations.Library.Resolvers
protected void Set3DFormat(Video video)
{
- var result = Format3DParser.Parse(video.Path, LibraryManager.GetNamingOptions());
+ var result = Format3DParser.Parse(video.Path, NamingOptions);
Set3DFormat(video, result.Is3D, result.Format3D);
}
diff --git a/Emby.Server.Implementations/Library/Resolvers/GenericVideoResolver.cs b/Emby.Server.Implementations/Library/Resolvers/GenericVideoResolver.cs
index 9599faea4..72341d9db 100644
--- a/Emby.Server.Implementations/Library/Resolvers/GenericVideoResolver.cs
+++ b/Emby.Server.Implementations/Library/Resolvers/GenericVideoResolver.cs
@@ -2,16 +2,16 @@
#pragma warning disable CS1591
+using Emby.Naming.Common;
using MediaBrowser.Controller.Entities;
-using MediaBrowser.Controller.Library;
namespace Emby.Server.Implementations.Library.Resolvers
{
public class GenericVideoResolver<T> : BaseVideoResolver<T>
where T : Video, new()
{
- public GenericVideoResolver(ILibraryManager libraryManager)
- : base(libraryManager)
+ public GenericVideoResolver(NamingOptions namingOptions)
+ : base(namingOptions)
{
}
}
diff --git a/Emby.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs b/Emby.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs
index f3b6ef0a2..732be0fe5 100644
--- a/Emby.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs
+++ b/Emby.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs
@@ -5,6 +5,7 @@ using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text.RegularExpressions;
+using Emby.Naming.Common;
using Emby.Naming.Video;
using Jellyfin.Extensions;
using MediaBrowser.Controller.Drawing;
@@ -25,6 +26,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.Movies
public class MovieResolver : BaseVideoResolver<Video>, IMultiItemResolver
{
private readonly IImageProcessor _imageProcessor;
+ private readonly StackResolver _stackResolver;
private string[] _validCollectionTypes = new[]
{
@@ -38,12 +40,13 @@ namespace Emby.Server.Implementations.Library.Resolvers.Movies
/// <summary>
/// Initializes a new instance of the <see cref="MovieResolver"/> class.
/// </summary>
- /// <param name="libraryManager">The library manager.</param>
/// <param name="imageProcessor">The image processor.</param>
- public MovieResolver(ILibraryManager libraryManager, IImageProcessor imageProcessor)
- : base(libraryManager)
+ /// <param name="namingOptions">The naming options.</param>
+ public MovieResolver(IImageProcessor imageProcessor, NamingOptions namingOptions)
+ : base(namingOptions)
{
_imageProcessor = imageProcessor;
+ _stackResolver = new StackResolver(NamingOptions);
}
/// <summary>
@@ -89,9 +92,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.Movies
return null;
}
- var files = args.FileSystemChildren
- .Where(i => !LibraryManager.IgnoreFile(i, args.Parent))
- .ToList();
+ var files = args.GetActualFileSystemChildren().ToList();
if (string.Equals(collectionType, CollectionType.MusicVideos, StringComparison.OrdinalIgnoreCase))
{
@@ -258,9 +259,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.Movies
}
}
- var namingOptions = LibraryManager.GetNamingOptions();
-
- var resolverResult = VideoListResolver.Resolve(files, namingOptions, suppportMultiEditions).ToList();
+ var resolverResult = VideoListResolver.Resolve(files, NamingOptions, suppportMultiEditions).ToList();
var result = new MultiItemResolverResult
{
@@ -438,7 +437,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, videoPath, i.Name));
+ var hasPhotos = photos.Any(i => !PhotoResolver.IsOwnedByResolvedMedia(videoPath, i.Name));
if (!hasPhotos)
{
@@ -511,9 +510,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.Movies
return null;
}
- var namingOptions = ((LibraryManager)LibraryManager).GetNamingOptions();
-
- var result = new StackResolver(namingOptions).ResolveDirectories(folderPaths).ToList();
+ var result = _stackResolver.ResolveDirectories(folderPaths).ToList();
if (result.Count != 1)
{
diff --git a/Emby.Server.Implementations/Library/Resolvers/PhotoAlbumResolver.cs b/Emby.Server.Implementations/Library/Resolvers/PhotoAlbumResolver.cs
index 1c560e8a6..7dd0ab185 100644
--- a/Emby.Server.Implementations/Library/Resolvers/PhotoAlbumResolver.cs
+++ b/Emby.Server.Implementations/Library/Resolvers/PhotoAlbumResolver.cs
@@ -1,6 +1,7 @@
#nullable disable
using System;
+using Emby.Naming.Common;
using MediaBrowser.Controller.Drawing;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Library;
@@ -15,17 +16,17 @@ namespace Emby.Server.Implementations.Library.Resolvers
public class PhotoAlbumResolver : GenericFolderResolver<PhotoAlbum>
{
private readonly IImageProcessor _imageProcessor;
- private readonly ILibraryManager _libraryManager;
+ private readonly NamingOptions _namingOptions;
/// <summary>
/// Initializes a new instance of the <see cref="PhotoAlbumResolver"/> class.
/// </summary>
/// <param name="imageProcessor">The image processor.</param>
- /// <param name="libraryManager">The library manager.</param>
- public PhotoAlbumResolver(IImageProcessor imageProcessor, ILibraryManager libraryManager)
+ /// <param name="namingOptions">The naming options.</param>
+ public PhotoAlbumResolver(IImageProcessor imageProcessor, NamingOptions namingOptions)
{
_imageProcessor = imageProcessor;
- _libraryManager = libraryManager;
+ _namingOptions = namingOptions;
}
/// <inheritdoc />
@@ -73,7 +74,7 @@ namespace Emby.Server.Implementations.Library.Resolvers
foreach (var siblingFile in files)
{
- if (PhotoResolver.IsOwnedByMedia(_libraryManager, siblingFile.FullName, filename))
+ if (PhotoResolver.IsOwnedByMedia(_namingOptions, 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 57bf40e9e..51d819303 100644
--- a/Emby.Server.Implementations/Library/Resolvers/PhotoResolver.cs
+++ b/Emby.Server.Implementations/Library/Resolvers/PhotoResolver.cs
@@ -6,6 +6,8 @@ using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
+using Emby.Naming.Common;
+using Emby.Naming.Video;
using MediaBrowser.Controller.Drawing;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Library;
@@ -16,7 +18,8 @@ namespace Emby.Server.Implementations.Library.Resolvers
public class PhotoResolver : ItemResolver<Photo>
{
private readonly IImageProcessor _imageProcessor;
- private readonly ILibraryManager _libraryManager;
+ private readonly NamingOptions _namingOptions;
+
private static readonly HashSet<string> _ignoreFiles = new HashSet<string>(StringComparer.OrdinalIgnoreCase)
{
"folder",
@@ -30,10 +33,11 @@ namespace Emby.Server.Implementations.Library.Resolvers
"default"
};
- public PhotoResolver(IImageProcessor imageProcessor, ILibraryManager libraryManager)
+
+ public PhotoResolver(IImageProcessor imageProcessor, NamingOptions namingOptions)
{
_imageProcessor = imageProcessor;
- _libraryManager = libraryManager;
+ _namingOptions = namingOptions;
}
/// <summary>
@@ -60,7 +64,7 @@ namespace Emby.Server.Implementations.Library.Resolvers
foreach (var file in files)
{
- if (IsOwnedByMedia(_libraryManager, file.FullName, filename))
+ if (IsOwnedByMedia(_namingOptions, file.FullName, filename))
{
return null;
}
@@ -77,17 +81,12 @@ namespace Emby.Server.Implementations.Library.Resolvers
return null;
}
- internal static bool IsOwnedByMedia(ILibraryManager libraryManager, string file, string imageFilename)
+ internal static bool IsOwnedByMedia(NamingOptions namingOptions, string file, string imageFilename)
{
- if (libraryManager.IsVideoFile(file))
- {
- return IsOwnedByResolvedMedia(libraryManager, file, imageFilename);
- }
-
- return false;
+ return VideoResolver.IsVideoFile(file, namingOptions) && IsOwnedByResolvedMedia(file, imageFilename);
}
- internal static bool IsOwnedByResolvedMedia(ILibraryManager libraryManager, string file, string imageFilename)
+ internal static bool IsOwnedByResolvedMedia(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/TV/EpisodeResolver.cs b/Emby.Server.Implementations/Library/Resolvers/TV/EpisodeResolver.cs
index cf2d22f4d..f72da3617 100644
--- a/Emby.Server.Implementations/Library/Resolvers/TV/EpisodeResolver.cs
+++ b/Emby.Server.Implementations/Library/Resolvers/TV/EpisodeResolver.cs
@@ -2,6 +2,7 @@
using System;
using System.Linq;
+using Emby.Naming.Common;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.TV;
using MediaBrowser.Controller.Library;
@@ -17,9 +18,9 @@ namespace Emby.Server.Implementations.Library.Resolvers.TV
/// <summary>
/// Initializes a new instance of the <see cref="EpisodeResolver"/> class.
/// </summary>
- /// <param name="libraryManager">The library manager.</param>
- public EpisodeResolver(ILibraryManager libraryManager)
- : base(libraryManager)
+ /// <param name="namingOptions">The naming options.</param>
+ public EpisodeResolver(NamingOptions namingOptions)
+ : base(namingOptions)
{
}
diff --git a/Emby.Server.Implementations/Library/Resolvers/TV/SeasonResolver.cs b/Emby.Server.Implementations/Library/Resolvers/TV/SeasonResolver.cs
index 063f67543..ea4851458 100644
--- a/Emby.Server.Implementations/Library/Resolvers/TV/SeasonResolver.cs
+++ b/Emby.Server.Implementations/Library/Resolvers/TV/SeasonResolver.cs
@@ -1,6 +1,7 @@
#nullable disable
using System.Globalization;
+using Emby.Naming.Common;
using Emby.Naming.TV;
using MediaBrowser.Controller.Entities.TV;
using MediaBrowser.Controller.Library;
@@ -14,22 +15,22 @@ namespace Emby.Server.Implementations.Library.Resolvers.TV
/// </summary>
public class SeasonResolver : GenericFolderResolver<Season>
{
- private readonly ILibraryManager _libraryManager;
private readonly ILocalizationManager _localization;
private readonly ILogger<SeasonResolver> _logger;
+ private readonly NamingOptions _namingOptions;
/// <summary>
/// Initializes a new instance of the <see cref="SeasonResolver"/> class.
/// </summary>
- /// <param name="libraryManager">The library manager.</param>
+ /// <param name="namingOptions">The naming options.</param>
/// <param name="localization">The localization.</param>
/// <param name="logger">The logger.</param>
public SeasonResolver(
- ILibraryManager libraryManager,
+ NamingOptions namingOptions,
ILocalizationManager localization,
ILogger<SeasonResolver> logger)
{
- _libraryManager = libraryManager;
+ _namingOptions = namingOptions;
_localization = localization;
_logger = logger;
}
@@ -43,7 +44,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.TV
{
if (args.Parent is Series series && args.IsDirectory)
{
- var namingOptions = ((LibraryManager)_libraryManager).GetNamingOptions();
+ var namingOptions = _namingOptions;
var path = args.Path;
@@ -65,18 +66,15 @@ namespace Emby.Server.Implementations.Library.Resolvers.TV
var episodeInfo = resolver.Resolve(testPath, true);
- if (episodeInfo != null)
+ if (episodeInfo?.EpisodeNumber != null && episodeInfo.SeasonNumber.HasValue)
{
- if (episodeInfo.EpisodeNumber.HasValue && episodeInfo.SeasonNumber.HasValue)
- {
- _logger.LogDebug(
- "Found folder underneath series with episode number: {0}. Season {1}. Episode {2}",
- path,
- episodeInfo.SeasonNumber.Value,
- episodeInfo.EpisodeNumber.Value);
-
- return null;
- }
+ _logger.LogDebug(
+ "Found folder underneath series with episode number: {0}. Season {1}. Episode {2}",
+ path,
+ episodeInfo.SeasonNumber.Value,
+ episodeInfo.EpisodeNumber.Value);
+
+ return null;
}
}
diff --git a/Emby.Server.Implementations/Library/Resolvers/TV/SeriesResolver.cs b/Emby.Server.Implementations/Library/Resolvers/TV/SeriesResolver.cs
index b7fbe01c5..4e15acd18 100644
--- a/Emby.Server.Implementations/Library/Resolvers/TV/SeriesResolver.cs
+++ b/Emby.Server.Implementations/Library/Resolvers/TV/SeriesResolver.cs
@@ -5,7 +5,12 @@
using System;
using System.Collections.Generic;
using System.IO;
+using System.Linq;
+using Emby.Naming.Common;
using Emby.Naming.TV;
+using Emby.Naming.Video;
+using MediaBrowser.Common.Configuration;
+using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Entities.TV;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Resolvers;
@@ -21,17 +26,17 @@ namespace Emby.Server.Implementations.Library.Resolvers.TV
public class SeriesResolver : GenericFolderResolver<Series>
{
private readonly ILogger<SeriesResolver> _logger;
- private readonly ILibraryManager _libraryManager;
+ private readonly NamingOptions _namingOptions;
/// <summary>
/// Initializes a new instance of the <see cref="SeriesResolver"/> class.
/// </summary>
/// <param name="logger">The logger.</param>
- /// <param name="libraryManager">The library manager.</param>
- public SeriesResolver(ILogger<SeriesResolver> logger, ILibraryManager libraryManager)
+ /// <param name="namingOptions">The naming options.</param>
+ public SeriesResolver(ILogger<SeriesResolver> logger, NamingOptions namingOptions)
{
_logger = logger;
- _libraryManager = libraryManager;
+ _namingOptions = namingOptions;
}
/// <summary>
@@ -54,16 +59,19 @@ namespace Emby.Server.Implementations.Library.Resolvers.TV
return null;
}
+ var seriesInfo = Naming.TV.SeriesResolver.Resolve(_namingOptions, args.Path);
+
var collectionType = args.GetCollectionType();
if (string.Equals(collectionType, CollectionType.TvShows, StringComparison.OrdinalIgnoreCase))
{
- var configuredContentType = _libraryManager.GetConfiguredContentType(args.Path);
+ // TODO refactor into separate class or something, this is copied from LibraryManager.GetConfiguredContentType
+ var configuredContentType = args.GetConfiguredContentType();
if (!string.Equals(configuredContentType, CollectionType.TvShows, StringComparison.OrdinalIgnoreCase))
{
return new Series
{
Path = args.Path,
- Name = Path.GetFileName(args.Path)
+ Name = seriesInfo.Name
};
}
}
@@ -80,7 +88,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.TV
return new Series
{
Path = args.Path,
- Name = Path.GetFileName(args.Path)
+ Name = seriesInfo.Name
};
}
@@ -89,12 +97,12 @@ namespace Emby.Server.Implementations.Library.Resolvers.TV
return null;
}
- if (IsSeriesFolder(args.Path, args.FileSystemChildren, _logger, _libraryManager, false))
+ if (IsSeriesFolder(args.Path, args.FileSystemChildren, false))
{
return new Series
{
Path = args.Path,
- Name = Path.GetFileName(args.Path)
+ Name = seriesInfo.Name
};
}
}
@@ -103,11 +111,9 @@ namespace Emby.Server.Implementations.Library.Resolvers.TV
return null;
}
- public static bool IsSeriesFolder(
+ private bool IsSeriesFolder(
string path,
IEnumerable<FileSystemMetadata> fileSystemChildren,
- ILogger<SeriesResolver> logger,
- ILibraryManager libraryManager,
bool isTvContentType)
{
foreach (var child in fileSystemChildren)
@@ -116,21 +122,21 @@ namespace Emby.Server.Implementations.Library.Resolvers.TV
{
if (IsSeasonFolder(child.FullName, isTvContentType))
{
- logger.LogDebug("{Path} is a series because of season folder {Dir}.", path, child.FullName);
+ _logger.LogDebug("{Path} is a series because of season folder {Dir}.", path, child.FullName);
return true;
}
}
else
{
string fullName = child.FullName;
- if (libraryManager.IsVideoFile(fullName))
+ if (VideoResolver.IsVideoFile(path, _namingOptions))
{
if (isTvContentType)
{
return true;
}
- var namingOptions = ((LibraryManager)libraryManager).GetNamingOptions();
+ var namingOptions = _namingOptions;
var episodeResolver = new Naming.TV.EpisodeResolver(namingOptions);
@@ -143,7 +149,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.TV
}
}
- logger.LogDebug("{Path} is not a series folder.", path);
+ _logger.LogDebug("{Path} is not a series folder.", path);
return false;
}
diff --git a/Emby.Server.Implementations/Library/Validators/CollectionPostScanTask.cs b/Emby.Server.Implementations/Library/Validators/CollectionPostScanTask.cs
new file mode 100644
index 000000000..945b559ad
--- /dev/null
+++ b/Emby.Server.Implementations/Library/Validators/CollectionPostScanTask.cs
@@ -0,0 +1,156 @@
+using System;
+using System.Linq;
+using System.Threading;
+using System.Threading.Tasks;
+using System.Collections.Generic;
+using MediaBrowser.Controller.Collections;
+using MediaBrowser.Controller.Entities;
+using MediaBrowser.Controller.Entities.Movies;
+using MediaBrowser.Controller.Library;
+using MediaBrowser.Model.Querying;
+using Jellyfin.Data.Enums;
+using Microsoft.Extensions.Logging;
+using MediaBrowser.Model.Entities;
+
+namespace Emby.Server.Implementations.Library.Validators
+{
+ /// <summary>
+ /// Class CollectionPostScanTask.
+ /// </summary>
+ public class CollectionPostScanTask : ILibraryPostScanTask
+ {
+ private readonly ILibraryManager _libraryManager;
+ private readonly ICollectionManager _collectionManager;
+ private readonly ILogger<CollectionPostScanTask> _logger;
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="CollectionPostScanTask" /> class.
+ /// </summary>
+ /// <param name="libraryManager">The library manager.</param>
+ /// <param name="collectionManager">The collection manager.</param>
+ /// <param name="logger">The logger.</param>
+ public CollectionPostScanTask(
+ ILibraryManager libraryManager,
+ ICollectionManager collectionManager,
+ ILogger<CollectionPostScanTask> logger)
+ {
+ _libraryManager = libraryManager;
+ _collectionManager = collectionManager;
+ _logger = logger;
+ }
+
+ /// <summary>
+ /// Runs the specified progress.
+ /// </summary>
+ /// <param name="progress">The progress.</param>
+ /// <param name="cancellationToken">The cancellation token.</param>
+ /// <returns>Task.</returns>
+ public async Task Run(IProgress<double> progress, CancellationToken cancellationToken)
+ {
+ var collectionNameMoviesMap = new Dictionary<string, HashSet<Guid>>();
+
+ foreach (var library in _libraryManager.RootFolder.Children)
+ {
+ if (!_libraryManager.GetLibraryOptions(library).AutomaticallyAddToCollection)
+ {
+ continue;
+ }
+
+ var startIndex = 0;
+ var pagesize = 1000;
+
+ while (true)
+ {
+ var movies = _libraryManager.GetItemList(new InternalItemsQuery
+ {
+ MediaTypes = new string[] { MediaType.Video },
+ IncludeItemTypes = new[] { nameof(Movie) },
+ IsVirtualItem = false,
+ OrderBy = new[] { (ItemSortBy.SortName, SortOrder.Ascending) },
+ Parent = library,
+ StartIndex = startIndex,
+ Limit = pagesize,
+ Recursive = true
+ });
+
+ foreach (var m in movies)
+ {
+ if (m is Movie movie && !string.IsNullOrEmpty(movie.CollectionName))
+ {
+ if (collectionNameMoviesMap.TryGetValue(movie.CollectionName, out var movieList))
+ {
+ movieList.Add(movie.Id);
+ }
+ else
+ {
+ collectionNameMoviesMap[movie.CollectionName] = new HashSet<Guid> { movie.Id };
+ }
+ }
+ }
+
+ if (movies.Count < pagesize)
+ {
+ break;
+ }
+
+ startIndex += pagesize;
+ }
+ }
+
+ var numComplete = 0;
+ var count = collectionNameMoviesMap.Count;
+
+ if (count == 0)
+ {
+ progress.Report(100);
+ return;
+ }
+
+ var boxSets = _libraryManager.GetItemList(new InternalItemsQuery
+ {
+ IncludeItemTypes = new[] { nameof(BoxSet) },
+ CollapseBoxSetItems = false,
+ Recursive = true
+ });
+
+ foreach (var (collectionName, movieIds) in collectionNameMoviesMap)
+ {
+ try
+ {
+ var boxSet = boxSets.FirstOrDefault(b => b?.Name == collectionName) as BoxSet;
+ if (boxSet == null)
+ {
+ // won't automatically create collection if only one movie in it
+ if (movieIds.Count >= 2)
+ {
+ boxSet = await _collectionManager.CreateCollectionAsync(new CollectionCreationOptions
+ {
+ Name = collectionName,
+ IsLocked = true
+ });
+
+ await _collectionManager.AddToCollectionAsync(boxSet.Id, movieIds);
+ }
+ }
+ else
+ {
+ await _collectionManager.AddToCollectionAsync(boxSet.Id, movieIds);
+ }
+
+ numComplete++;
+ double percent = numComplete;
+ percent /= count;
+ percent *= 100;
+
+ progress.Report(percent);
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError(ex, "Error refreshing {CollectionName} with {@MovieIds}", collectionName, movieIds);
+ }
+ }
+
+ progress.Report(100);
+ }
+ }
+}
diff --git a/Emby.Server.Implementations/Library/Validators/PeopleValidator.cs b/Emby.Server.Implementations/Library/Validators/PeopleValidator.cs
index 8739a9e1b..8a9a4b865 100644
--- a/Emby.Server.Implementations/Library/Validators/PeopleValidator.cs
+++ b/Emby.Server.Implementations/Library/Validators/PeopleValidator.cs
@@ -78,7 +78,7 @@ namespace Emby.Server.Implementations.Library.Validators
}
catch (Exception ex)
{
- _logger.LogError(ex, "Error validating IBN entry {person}", person);
+ _logger.LogError(ex, "Error validating IBN entry {Person}", person);
}
// Update progress
diff --git a/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs b/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs
index 980b42729..644f9050d 100644
--- a/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs
+++ b/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs
@@ -957,7 +957,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
public async Task<ILiveStream> GetChannelStreamWithDirectStreamProvider(string channelId, string streamId, List<ILiveStream> currentLiveStreams, CancellationToken cancellationToken)
{
- _logger.LogInformation("Streaming Channel " + channelId);
+ _logger.LogInformation("Streaming Channel {Id}", channelId);
var result = string.IsNullOrEmpty(streamId) ?
null :
@@ -1027,7 +1027,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
{
var stream = new MediaSourceInfo
{
- EncoderPath = _appHost.GetLoopbackHttpApiUrl() + "/LiveTv/LiveRecordings/" + info.Id + "/stream",
+ EncoderPath = _appHost.GetApiUrlForLocalAccess() + "/LiveTv/LiveRecordings/" + info.Id + "/stream",
EncoderProtocol = MediaProtocol.Http,
Path = info.Path,
Protocol = MediaProtocol.File,
@@ -1308,16 +1308,16 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
await recorder.Record(directStreamProvider, mediaStreamInfo, recordPath, duration, onStarted, activeRecordingInfo.CancellationTokenSource.Token).ConfigureAwait(false);
recordingStatus = RecordingStatus.Completed;
- _logger.LogInformation("Recording completed: {recordPath}", recordPath);
+ _logger.LogInformation("Recording completed: {RecordPath}", recordPath);
}
catch (OperationCanceledException)
{
- _logger.LogInformation("Recording stopped: {recordPath}", recordPath);
+ _logger.LogInformation("Recording stopped: {RecordPath}", recordPath);
recordingStatus = RecordingStatus.Completed;
}
catch (Exception ex)
{
- _logger.LogError(ex, "Error recording to {recordPath}", recordPath);
+ _logger.LogError(ex, "Error recording to {RecordPath}", recordPath);
recordingStatus = RecordingStatus.Error;
}
@@ -1404,7 +1404,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
}
catch (Exception ex)
{
- _logger.LogError(ex, "Error deleting 0-byte failed recording file {path}", path);
+ _logger.LogError(ex, "Error deleting 0-byte failed recording file {Path}", path);
}
}
}
diff --git a/Emby.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs b/Emby.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs
index 835028b92..a88a1fe84 100644
--- a/Emby.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs
+++ b/Emby.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs
@@ -87,8 +87,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
ErrorDialog = false
};
- var commandLineLogMessage = processStartInfo.FileName + " " + processStartInfo.Arguments;
- _logger.LogInformation(commandLineLogMessage);
+ _logger.LogInformation("{Filename} {Arguments}", processStartInfo.FileName, processStartInfo.Arguments);
var logFilePath = Path.Combine(_appPaths.LogDirectoryPath, "record-transcode-" + Guid.NewGuid() + ".txt");
Directory.CreateDirectory(Path.GetDirectoryName(logFilePath));
@@ -97,7 +96,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
_logFileStream = new FileStream(logFilePath, FileMode.CreateNew, FileAccess.Write, FileShare.Read, IODefaults.FileStreamBufferSize, FileOptions.Asynchronous);
await JsonSerializer.SerializeAsync(_logFileStream, mediaSource, _jsonOptions, cancellationToken).ConfigureAwait(false);
- await _logFileStream.WriteAsync(Encoding.UTF8.GetBytes(Environment.NewLine + Environment.NewLine + commandLineLogMessage + Environment.NewLine + Environment.NewLine), cancellationToken).ConfigureAwait(false);
+ await _logFileStream.WriteAsync(Encoding.UTF8.GetBytes(Environment.NewLine + Environment.NewLine + processStartInfo.FileName + " " + processStartInfo.Arguments + Environment.NewLine + Environment.NewLine), cancellationToken).ConfigureAwait(false);
_process = new Process
{
@@ -188,7 +187,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
CultureInfo.InvariantCulture,
"-i \"{0}\" {2} -map_metadata -1 -threads {6} {3}{4}{5} -y \"{1}\"",
inputTempFile,
- targetFile.Replace("\"", "\\\""), // Escape quotes in filename
+ targetFile.Replace("\"", "\\\"", StringComparison.Ordinal), // Escape quotes in filename
videoArgs,
GetAudioArgs(mediaSource),
subtitleArgs,
@@ -225,13 +224,13 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
{
try
{
- _logger.LogInformation("Stopping ffmpeg recording process for {path}", _targetPath);
+ _logger.LogInformation("Stopping ffmpeg recording process for {Path}", _targetPath);
_process.StandardInput.WriteLine("q");
}
catch (Exception ex)
{
- _logger.LogError(ex, "Error stopping recording transcoding job for {path}", _targetPath);
+ _logger.LogError(ex, "Error stopping recording transcoding job for {Path}", _targetPath);
}
if (_hasExited)
@@ -241,7 +240,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
try
{
- _logger.LogInformation("Calling recording process.WaitForExit for {path}", _targetPath);
+ _logger.LogInformation("Calling recording process.WaitForExit for {Path}", _targetPath);
if (_process.WaitForExit(10000))
{
@@ -250,7 +249,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
}
catch (Exception ex)
{
- _logger.LogError(ex, "Error waiting for recording process to exit for {path}", _targetPath);
+ _logger.LogError(ex, "Error waiting for recording process to exit for {Path}", _targetPath);
}
if (_hasExited)
@@ -260,13 +259,13 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
try
{
- _logger.LogInformation("Killing ffmpeg recording process for {path}", _targetPath);
+ _logger.LogInformation("Killing ffmpeg recording process for {Path}", _targetPath);
_process.Kill();
}
catch (Exception ex)
{
- _logger.LogError(ex, "Error killing recording transcoding job for {path}", _targetPath);
+ _logger.LogError(ex, "Error killing recording transcoding job for {Path}", _targetPath);
}
}
}
diff --git a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs b/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs
index 1f963e4a2..08aa0cfd7 100644
--- a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs
+++ b/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs
@@ -11,6 +11,7 @@ using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Net.Mime;
+using System.Security.Cryptography;
using System.Text;
using System.Text.Json;
using System.Threading;
@@ -20,7 +21,6 @@ using Jellyfin.Extensions;
using Jellyfin.Extensions.Json;
using MediaBrowser.Common.Net;
using MediaBrowser.Controller.LiveTv;
-using MediaBrowser.Model.Cryptography;
using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.LiveTv;
@@ -35,7 +35,6 @@ namespace Emby.Server.Implementations.LiveTv.Listings
private readonly ILogger<SchedulesDirect> _logger;
private readonly IHttpClientFactory _httpClientFactory;
private readonly SemaphoreSlim _tokenSemaphore = new SemaphoreSlim(1, 1);
- private readonly ICryptoProvider _cryptoProvider;
private readonly ConcurrentDictionary<string, NameValuePair> _tokens = new ConcurrentDictionary<string, NameValuePair>();
private readonly JsonSerializerOptions _jsonOptions = JsonDefaults.Options;
@@ -43,12 +42,10 @@ namespace Emby.Server.Implementations.LiveTv.Listings
public SchedulesDirect(
ILogger<SchedulesDirect> logger,
- IHttpClientFactory httpClientFactory,
- ICryptoProvider cryptoProvider)
+ IHttpClientFactory httpClientFactory)
{
_logger = logger;
_httpClientFactory = httpClientFactory;
- _cryptoProvider = cryptoProvider;
}
/// <inheritdoc />
@@ -169,12 +166,12 @@ namespace Emby.Server.Implementations.LiveTv.Listings
const double DesiredAspect = 2.0 / 3;
- programEntry.PrimaryImage = GetProgramImage(ApiUrl, imagesWithText, true, DesiredAspect) ??
- GetProgramImage(ApiUrl, allImages, true, DesiredAspect);
+ programEntry.PrimaryImage = GetProgramImage(ApiUrl, imagesWithText, DesiredAspect) ??
+ GetProgramImage(ApiUrl, allImages, DesiredAspect);
const double WideAspect = 16.0 / 9;
- programEntry.ThumbImage = GetProgramImage(ApiUrl, imagesWithText, true, WideAspect);
+ programEntry.ThumbImage = GetProgramImage(ApiUrl, imagesWithText, WideAspect);
// Don't supply the same image twice
if (string.Equals(programEntry.PrimaryImage, programEntry.ThumbImage, StringComparison.Ordinal))
@@ -182,7 +179,7 @@ namespace Emby.Server.Implementations.LiveTv.Listings
programEntry.ThumbImage = null;
}
- programEntry.BackdropImage = GetProgramImage(ApiUrl, imagesWithoutText, true, WideAspect);
+ programEntry.BackdropImage = GetProgramImage(ApiUrl, imagesWithoutText, WideAspect);
// programEntry.bannerImage = GetProgramImage(ApiUrl, data, "Banner", false) ??
// GetProgramImage(ApiUrl, data, "Banner-L1", false) ??
@@ -403,7 +400,7 @@ namespace Emby.Server.Implementations.LiveTv.Listings
return info;
}
- private string GetProgramImage(string apiUrl, IEnumerable<ImageDataDto> images, bool returnDefaultImage, double desiredAspect)
+ private static string GetProgramImage(string apiUrl, IEnumerable<ImageDataDto> images, double desiredAspect)
{
var match = images
.OrderBy(i => Math.Abs(desiredAspect - GetAspectRatio(i)))
@@ -648,7 +645,7 @@ namespace Emby.Server.Implementations.LiveTv.Listings
CancellationToken cancellationToken)
{
using var options = new HttpRequestMessage(HttpMethod.Post, ApiUrl + "/token");
- var hashedPasswordBytes = _cryptoProvider.ComputeHash("SHA1", Encoding.ASCII.GetBytes(password), Array.Empty<byte>());
+ var hashedPasswordBytes = SHA1.HashData(Encoding.ASCII.GetBytes(password));
// TODO: remove ToLower when Convert.ToHexString supports lowercase
// Schedules Direct requires the hex to be lowercase
string hashedPassword = Convert.ToHexString(hashedPasswordBytes).ToLowerInvariant();
diff --git a/Emby.Server.Implementations/LiveTv/LiveTvDtoService.cs b/Emby.Server.Implementations/LiveTv/LiveTvDtoService.cs
index 21e1409ac..598e3f88a 100644
--- a/Emby.Server.Implementations/LiveTv/LiveTvDtoService.cs
+++ b/Emby.Server.Implementations/LiveTv/LiveTvDtoService.cs
@@ -393,7 +393,7 @@ namespace Emby.Server.Implementations.LiveTv
}
catch (Exception ex)
{
- _logger.LogError(ex, "Error getting image info for {name}", info.Name);
+ _logger.LogError(ex, "Error getting image info for {Name}", info.Name);
}
return null;
diff --git a/Emby.Server.Implementations/LiveTv/LiveTvManager.cs b/Emby.Server.Implementations/LiveTv/LiveTvManager.cs
index ea1a28fe8..a41b63f28 100644
--- a/Emby.Server.Implementations/LiveTv/LiveTvManager.cs
+++ b/Emby.Server.Implementations/LiveTv/LiveTvManager.cs
@@ -1054,7 +1054,7 @@ namespace Emby.Server.Implementations.LiveTv
{
cancellationToken.ThrowIfCancellationRequested();
- _logger.LogDebug("Refreshing guide from {name}", service.Name);
+ _logger.LogDebug("Refreshing guide from {Name}", service.Name);
try
{
@@ -1135,7 +1135,7 @@ namespace Emby.Server.Implementations.LiveTv
}
catch (Exception ex)
{
- _logger.LogError(ex, "Error getting channel information for {name}", channelInfo.Item2.Name);
+ _logger.LogError(ex, "Error getting channel information for {Name}", channelInfo.Item2.Name);
}
numComplete++;
@@ -1248,7 +1248,7 @@ namespace Emby.Server.Implementations.LiveTv
}
catch (Exception ex)
{
- _logger.LogError(ex, "Error getting programs for channel {name}", currentChannel.Name);
+ _logger.LogError(ex, "Error getting programs for channel {Name}", currentChannel.Name);
}
numComplete++;
diff --git a/Emby.Server.Implementations/LiveTv/LiveTvMediaSourceProvider.cs b/Emby.Server.Implementations/LiveTv/LiveTvMediaSourceProvider.cs
index ecd28097d..4b7584af3 100644
--- a/Emby.Server.Implementations/LiveTv/LiveTvMediaSourceProvider.cs
+++ b/Emby.Server.Implementations/LiveTv/LiveTvMediaSourceProvider.cs
@@ -104,7 +104,7 @@ namespace Emby.Server.Implementations.LiveTv
// Dummy this up so that direct play checks can still run
if (string.IsNullOrEmpty(source.Path) && source.Protocol == MediaProtocol.Http)
{
- source.Path = _appHost.GetSmartApiUrl(string.Empty);
+ source.Path = _appHost.GetApiUrlForLocalAccess();
}
}
diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunManager.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunManager.cs
index f9d151a81..9ab4cc628 100644
--- a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunManager.cs
+++ b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunManager.cs
@@ -51,7 +51,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
public async Task<bool> CheckTunerAvailability(IPAddress remoteIp, int tuner, CancellationToken cancellationToken)
{
using var client = new TcpClient();
- client.Connect(remoteIp, HdHomeRunPort);
+ await client.ConnectAsync(remoteIp, HdHomeRunPort).ConfigureAwait(false);
using var stream = client.GetStream();
return await CheckTunerAvailability(stream, tuner, cancellationToken).ConfigureAwait(false);
diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunUdpStream.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunUdpStream.cs
index 31445e1ec..9fba17ca3 100644
--- a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunUdpStream.cs
+++ b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunUdpStream.cs
@@ -82,7 +82,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
Directory.CreateDirectory(Path.GetDirectoryName(TempFilePath));
- Logger.LogInformation("Opening HDHR UDP Live stream from {host}", uri.Host);
+ Logger.LogInformation("Opening HDHR UDP Live stream from {Host}", uri.Host);
var remoteAddress = IPAddress.Parse(uri.Host);
IPAddress localAddress = null;
@@ -147,7 +147,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
// OpenedMediaSource.Path = tempFile;
// OpenedMediaSource.ReadAtNativeFramerate = true;
- MediaSource.Path = _appHost.GetLoopbackHttpApiUrl() + "/LiveTv/LiveStreamFiles/" + UniqueId + "/stream.ts";
+ MediaSource.Path = _appHost.GetApiUrlForLocalAccess() + "/LiveTv/LiveStreamFiles/" + UniqueId + "/stream.ts";
MediaSource.Protocol = MediaProtocol.Http;
// OpenedMediaSource.SupportsDirectPlay = false;
// OpenedMediaSource.SupportsDirectStream = true;
diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/M3uParser.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/M3uParser.cs
index 506ef5548..708ff52d7 100644
--- a/Emby.Server.Implementations/LiveTv/TunerHosts/M3uParser.cs
+++ b/Emby.Server.Implementations/LiveTv/TunerHosts/M3uParser.cs
@@ -283,7 +283,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
// #EXTINF:0,84.0 - VOX Schweiz
if (!string.IsNullOrWhiteSpace(nameInExtInf))
{
- var numberIndex = nameInExtInf.IndexOf(' ');
+ var numberIndex = nameInExtInf.IndexOf(' ', StringComparison.Ordinal);
if (numberIndex > 0)
{
var numberPart = nameInExtInf.Substring(0, numberIndex).Trim(new[] { ' ', '.' });
diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/SharedHttpStream.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/SharedHttpStream.cs
index 3b69e55b0..b1ce7b2b3 100644
--- a/Emby.Server.Implementations/LiveTv/TunerHosts/SharedHttpStream.cs
+++ b/Emby.Server.Implementations/LiveTv/TunerHosts/SharedHttpStream.cs
@@ -83,7 +83,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
// OpenedMediaSource.Path = tempFile;
// OpenedMediaSource.ReadAtNativeFramerate = true;
- MediaSource.Path = _appHost.GetLoopbackHttpApiUrl() + "/LiveTv/LiveStreamFiles/" + UniqueId + "/stream.ts";
+ MediaSource.Path = _appHost.GetApiUrlForLocalAccess() + "/LiveTv/LiveStreamFiles/" + UniqueId + "/stream.ts";
MediaSource.Protocol = MediaProtocol.Http;
// OpenedMediaSource.Path = TempFilePath;
diff --git a/Emby.Server.Implementations/Localization/Core/as.json b/Emby.Server.Implementations/Localization/Core/as.json
new file mode 100644
index 000000000..0967ef424
--- /dev/null
+++ b/Emby.Server.Implementations/Localization/Core/as.json
@@ -0,0 +1 @@
+{}
diff --git a/Emby.Server.Implementations/Localization/Core/be.json b/Emby.Server.Implementations/Localization/Core/be.json
new file mode 100644
index 000000000..56c4e7d39
--- /dev/null
+++ b/Emby.Server.Implementations/Localization/Core/be.json
@@ -0,0 +1,4 @@
+{
+ "Sync": "Сінхранізацыя",
+ "Playlists": "Плэйліст"
+}
diff --git a/Emby.Server.Implementations/Localization/Core/ca.json b/Emby.Server.Implementations/Localization/Core/ca.json
index db3c13d80..2dee5e327 100644
--- a/Emby.Server.Implementations/Localization/Core/ca.json
+++ b/Emby.Server.Implementations/Localization/Core/ca.json
@@ -25,7 +25,7 @@
"HeaderLiveTV": "TV en Directe",
"HeaderNextUp": "A continuació",
"HeaderRecordingGroups": "Grups d'Enregistrament",
- "HomeVideos": "Vídeos domèstics",
+ "HomeVideos": "Vídeos Domèstics",
"Inherit": "Hereta",
"ItemAddedWithName": "{0} ha estat afegit a la biblioteca",
"ItemRemovedWithName": "{0} ha estat eliminat de la biblioteca",
@@ -39,7 +39,7 @@
"MixedContent": "Contingut barrejat",
"Movies": "Pel·lícules",
"Music": "Música",
- "MusicVideos": "Vídeos musicals",
+ "MusicVideos": "Vídeos Musicals",
"NameInstallFailed": "Instalació de {0} fallida",
"NameSeasonNumber": "Temporada {0}",
"NameSeasonUnknown": "Temporada Desconeguda",
diff --git a/Emby.Server.Implementations/Localization/Core/cs.json b/Emby.Server.Implementations/Localization/Core/cs.json
index 1ea378321..25f51db16 100644
--- a/Emby.Server.Implementations/Localization/Core/cs.json
+++ b/Emby.Server.Implementations/Localization/Core/cs.json
@@ -15,7 +15,7 @@
"Favorites": "Oblíbené",
"Folders": "Složky",
"Genres": "Žánry",
- "HeaderAlbumArtists": "Album umělce",
+ "HeaderAlbumArtists": "Umělci alba",
"HeaderContinueWatching": "Pokračovat ve sledování",
"HeaderFavoriteAlbums": "Oblíbená alba",
"HeaderFavoriteArtists": "Oblíbení interpreti",
diff --git a/Emby.Server.Implementations/Localization/Core/en-GB.json b/Emby.Server.Implementations/Localization/Core/en-GB.json
index 86ce9240e..add578376 100644
--- a/Emby.Server.Implementations/Localization/Core/en-GB.json
+++ b/Emby.Server.Implementations/Localization/Core/en-GB.json
@@ -15,7 +15,7 @@
"Favorites": "Favourites",
"Folders": "Folders",
"Genres": "Genres",
- "HeaderAlbumArtists": "Artist's Album",
+ "HeaderAlbumArtists": "Album artists",
"HeaderContinueWatching": "Continue Watching",
"HeaderFavoriteAlbums": "Favourite Albums",
"HeaderFavoriteArtists": "Favourite Artists",
diff --git a/Emby.Server.Implementations/Localization/Core/en-US.json b/Emby.Server.Implementations/Localization/Core/en-US.json
index ca127cdb8..568a8e447 100644
--- a/Emby.Server.Implementations/Localization/Core/en-US.json
+++ b/Emby.Server.Implementations/Localization/Core/en-US.json
@@ -12,12 +12,12 @@
"Default": "Default",
"DeviceOfflineWithName": "{0} has disconnected",
"DeviceOnlineWithName": "{0} is connected",
- "FailedLoginAttemptWithUserName": "Failed login attempt from {0}",
+ "FailedLoginAttemptWithUserName": "Failed login try from {0}",
"Favorites": "Favorites",
"Folders": "Folders",
"Forced": "Forced",
"Genres": "Genres",
- "HeaderAlbumArtists": "Artist's Album",
+ "HeaderAlbumArtists": "Album artists",
"HeaderContinueWatching": "Continue Watching",
"HeaderFavoriteAlbums": "Favorite Albums",
"HeaderFavoriteArtists": "Favorite Artists",
diff --git a/Emby.Server.Implementations/Localization/Core/eo.json b/Emby.Server.Implementations/Localization/Core/eo.json
index f92b5f673..3cadde0a0 100644
--- a/Emby.Server.Implementations/Localization/Core/eo.json
+++ b/Emby.Server.Implementations/Localization/Core/eo.json
@@ -1,5 +1,5 @@
{
- "NotificationOptionInstallationFailed": "Instalada fiasko",
+ "NotificationOptionInstallationFailed": "Instalada malsukceso",
"NotificationOptionAudioPlaybackStopped": "Ludado de sono haltis",
"NotificationOptionAudioPlayback": "Ludado de sono lanĉis",
"NameSeasonUnknown": "Sezono Nekonata",
@@ -11,7 +11,7 @@
"ItemAddedWithName": "{0} aldonis al la plurmediteko",
"HeaderLiveTV": "TV-etero",
"HeaderContinueWatching": "Daŭrigi Spektadon",
- "HeaderAlbumArtists": "Albumo de artisto",
+ "HeaderAlbumArtists": "Artistoj de albumo",
"Folders": "Dosierujoj",
"DeviceOnlineWithName": "{0} estas konektita",
"Default": "Defaŭlte",
@@ -48,17 +48,17 @@
"Shows": "Serioj",
"HeaderFavoriteShows": "Favorataj Serioj",
"TvShows": "TV-serioj",
- "Favorites": "Favoratoj",
+ "Favorites": "Favorataj",
"TaskCleanLogs": "Purigi Ĵurnalan Katalogon",
- "TaskRefreshLibrary": "Skanu Plurmeditekon",
+ "TaskRefreshLibrary": "Skani Plurmeditekon",
"ValueSpecialEpisodeName": "Speciala - {0}",
- "TaskOptimizeDatabase": "Optimigi datumbazon",
+ "TaskOptimizeDatabase": "Optimumigi datenbazon",
"TaskRefreshChannels": "Refreŝigi Kanalojn",
"TaskUpdatePlugins": "Ĝisdatigi Kromprogramojn",
"TaskRefreshPeople": "Refreŝigi Homojn",
"TasksChannelsCategory": "Interretaj Kanaloj",
"ProviderValue": "Provizanto: {0}",
- "NotificationOptionPluginError": "Kromprograma malsukceso",
+ "NotificationOptionPluginError": "Kromprogramo malsukcesis",
"MixedContent": "Miksita enhavo",
"TasksApplicationCategory": "Aplikaĵo",
"TasksMaintenanceCategory": "Prizorgado",
@@ -75,7 +75,7 @@
"ServerNameNeedsToBeRestarted": "{0} devas esti relanĉita",
"NotificationOptionVideoPlayback": "La videoludado lanĉis",
"NotificationOptionServerRestartRequired": "Servila relanĉigo bezonata",
- "TaskOptimizeDatabaseDescription": "Kompaktigas datenbazon kaj trunkas liberan lokon. Lanĉi ĉi tiun taskon post la teka skanado aŭ fari aliajn ŝanĝojn, kiuj implicas datenbazajn modifojn, povus plibonigi rendimenton.",
+ "TaskOptimizeDatabaseDescription": "Kompaktigas datenbazon kaj trunkas liberan lokon. Lanĉi ĉi tiun taskon post la plurmediteka skanado aŭ fari aliajn ŝanĝojn, kiuj implicas datenbazajn modifojn, povus plibonigi rendimenton.",
"TaskUpdatePluginsDescription": "Elŝutas kaj instalas ĝisdatigojn por kromprogramojn, kiuj estas agorditaj por ĝisdatigi aŭtomate.",
"TaskDownloadMissingSubtitlesDescription": "Serĉas en interreto mankantajn subtekstojn surbaze de metadatena agordaro.",
"TaskRefreshPeopleDescription": "Ĝisdatigas metadatenojn por aktoroj kaj reĵisoroj en via plurmediteko.",
@@ -102,9 +102,9 @@
"MessageApplicationUpdatedTo": "Jellyfin Server estis ĝisdatigita al {0}",
"MessageApplicationUpdated": "Jellyfin Server estis ĝisdatigita",
"TaskRefreshChannelsDescription": "Refreŝigas informon pri interretaj kanaloj.",
- "TaskDownloadMissingSubtitles": "Elŝutu mankantajn subtekstojn",
+ "TaskDownloadMissingSubtitles": "Elŝuti mankantajn subtekstojn",
"TaskCleanTranscode": "Malplenigi Transkodadan Katalogon",
- "TaskRefreshChapterImages": "Eltiru Ĉapitro-Bildojn",
+ "TaskRefreshChapterImages": "Eltiri Ĉapitraj Bildojn",
"TaskCleanCache": "Malplenigi Staplan Katalogon",
"TaskCleanActivityLog": "Malplenigi Aktivecan Ĵurnalon",
"PluginUpdatedWithName": "{0} estis ĝisdatigita",
diff --git a/Emby.Server.Implementations/Localization/Core/es_419.json b/Emby.Server.Implementations/Localization/Core/es_419.json
index a968c6dab..2ca736ad9 100644
--- a/Emby.Server.Implementations/Localization/Core/es_419.json
+++ b/Emby.Server.Implementations/Localization/Core/es_419.json
@@ -15,7 +15,7 @@
"HeaderFavoriteEpisodes": "Episodios favoritos",
"HeaderFavoriteShows": "Programas favoritos",
"HeaderContinueWatching": "Continuar viendo",
- "HeaderAlbumArtists": "Artistas del álbum",
+ "HeaderAlbumArtists": "Artistas de álbum",
"Genres": "Géneros",
"Folders": "Carpetas",
"Favorites": "Favoritos",
@@ -29,7 +29,7 @@
"TaskRefreshChannelsDescription": "Actualiza la información de canales de Internet.",
"TaskRefreshChannels": "Actualizar canales",
"TaskCleanTranscodeDescription": "Elimina archivos transcodificados que tengan más de un día.",
- "TaskCleanTranscode": "Limpiar directorio de transcodificado",
+ "TaskCleanTranscode": "Limpiar el directorio de transcodificaciones",
"TaskUpdatePluginsDescription": "Descarga e instala actualizaciones para complementos que están configurados para actualizarse automáticamente.",
"TaskUpdatePlugins": "Actualizar complementos",
"TaskRefreshPeopleDescription": "Actualiza metadatos de actores y directores en tu biblioteca de medios.",
@@ -105,7 +105,7 @@
"Inherit": "Heredar",
"HomeVideos": "Videos caseros",
"HeaderRecordingGroups": "Grupos de grabación",
- "FailedLoginAttemptWithUserName": "Intento fallido de inicio de sesión desde {0}",
+ "FailedLoginAttemptWithUserName": "Intento de inicio de sesión fallido desde {0}",
"DeviceOnlineWithName": "{0} está conectado",
"DeviceOfflineWithName": "{0} se ha desconectado",
"ChapterNameValue": "Capítulo {0}",
@@ -114,10 +114,10 @@
"Application": "Aplicación",
"AppDeviceValues": "App: {0}, Dispositivo: {1}",
"TaskCleanActivityLogDescription": "Elimina las entradas del registro de actividad anteriores al periodo configurado.",
- "TaskCleanActivityLog": "Limpiar Registro de Actividades",
+ "TaskCleanActivityLog": "Limpiar registro de actividades",
"Undefined": "Sin definir",
"Forced": "Forzado",
- "Default": "Por Defecto",
- "TaskOptimizeDatabaseDescription": "Compacta la base de datos y restaura el espacio libre. Ejecutar esta tarea después de actualizar las librerías o realizar otros cambios que impliquen modificar las bases de datos puede mejorar la performance.",
- "TaskOptimizeDatabase": "Optimización de base de datos"
+ "Default": "Por defecto",
+ "TaskOptimizeDatabaseDescription": "Compacta la base de datos y libera espacio. Ejecutar esta tarea después de escanear la biblioteca o hacer otros cambios que impliquen modificaciones en la base de datos puede mejorar el rendimiento.",
+ "TaskOptimizeDatabase": "Optimizar base de datos"
}
diff --git a/Emby.Server.Implementations/Localization/Core/et.json b/Emby.Server.Implementations/Localization/Core/et.json
index c3596ecf1..8db6a0b38 100644
--- a/Emby.Server.Implementations/Localization/Core/et.json
+++ b/Emby.Server.Implementations/Localization/Core/et.json
@@ -17,7 +17,7 @@
"TaskCleanLogsDescription": "Kustutab logifailid, mis on vanemad kui {0} päeva.",
"TaskCleanLogs": "Puhasta logikataloog",
"TaskRefreshLibraryDescription": "Otsib meedikogust uusi faile ja värskendab metaandmeid.",
- "Collections": "Kollektsioonid",
+ "Collections": "Kogumikud",
"TaskRefreshLibrary": "Skaneeri meediakogu",
"TaskRefreshChapterImagesDescription": "Loob peatükkidega videote jaoks pisipildid.",
"TaskRefreshChapterImages": "Eralda peatükipildid",
@@ -32,9 +32,9 @@
"ValueSpecialEpisodeName": "Eriepisood - {0}",
"ValueHasBeenAddedToLibrary": "{0} lisati meediakogusse",
"UserStartedPlayingItemWithValues": "{0} taasesitab {1} serveris {2}",
- "UserPasswordChangedWithName": "Kasutaja {0} parooli on muudetud",
- "UserLockedOutWithName": "Kasutaja {0} on lukustatud",
- "UserDeletedWithName": "Kasutaja {0} on kustutatud",
+ "UserPasswordChangedWithName": "Kasutaja {0} parool muudeti",
+ "UserLockedOutWithName": "Kasutaja {0} lukustati",
+ "UserDeletedWithName": "Kasutaja {0} kustutati",
"UserCreatedWithName": "Kasutaja {0} on loodud",
"ScheduledTaskStartedWithName": "{0} käivitati",
"ProviderValue": "Allikas: {0}",
@@ -54,9 +54,9 @@
"Plugin": "Plugin",
"Playlists": "Pleilistid",
"Photos": "Fotod",
- "NotificationOptionVideoPlaybackStopped": "Video taasesitus on peatatud",
+ "NotificationOptionVideoPlaybackStopped": "Video taasesitus lõppes",
"NotificationOptionVideoPlayback": "Video taasesitus algas",
- "NotificationOptionUserLockedOut": "Kasutaja on lukustatud",
+ "NotificationOptionUserLockedOut": "Kasutaja lukustati",
"NotificationOptionTaskFailed": "Ajastatud ülesanne nurjus",
"NotificationOptionServerRestartRequired": "Vajalik on serveri taaskäivitamine",
"NotificationOptionPluginUpdateInstalled": "Paigaldati plugina uuendus",
@@ -66,7 +66,7 @@
"NotificationOptionNewLibraryContent": "Lisati uut sisu",
"NotificationOptionInstallationFailed": "Paigaldamine nurjus",
"NotificationOptionCameraImageUploaded": "Kaamera pilt on üles laaditud",
- "NotificationOptionAudioPlaybackStopped": "Heli taasesitus peatati",
+ "NotificationOptionAudioPlaybackStopped": "Heli taasesitus lõppes",
"NotificationOptionAudioPlayback": "Heli taasesitus algas",
"NotificationOptionApplicationUpdateInstalled": "Rakenduse uuendus paigaldati",
"NotificationOptionApplicationUpdateAvailable": "Rakenduse uuendus on saadaval",
@@ -97,12 +97,12 @@
"HeaderFavoriteArtists": "Lemmikesitajad",
"HeaderFavoriteAlbums": "Lemmikalbumid",
"HeaderContinueWatching": "Jätka vaatamist",
- "HeaderAlbumArtists": "Albumi esitaja",
+ "HeaderAlbumArtists": "Albumi esitajad",
"Genres": "Žanrid",
"Forced": "Sunnitud",
"Folders": "Kaustad",
"Favorites": "Lemmikud",
- "FailedLoginAttemptWithUserName": "Ebaõnnestunud sisselogimiskatse kasutajalt {0}",
+ "FailedLoginAttemptWithUserName": "{0} - sisselogimine nurjus",
"DeviceOnlineWithName": "{0} on ühendatud",
"DeviceOfflineWithName": "{0} katkestas ühenduse",
"Default": "Vaikimisi",
@@ -114,5 +114,10 @@
"Artists": "Esitajad",
"Application": "Rakendus",
"AppDeviceValues": "Rakendus: {0}, seade: {1}",
- "Albums": "Albumid"
+ "Albums": "Albumid",
+ "UserOfflineFromDevice": "{0} katkestas ühenduse seadmega {1}",
+ "SubtitleDownloadFailureFromForItem": "Subtiitrite allalaadimine {0} > {1} nurjus",
+ "UserPolicyUpdatedWithName": "Kasutaja {0} õigusi värskendati",
+ "UserStoppedPlayingItemWithValues": "{0} lõpetas {1} taasesituse seadmes {2}",
+ "UserOnlineFromDevice": "{0} on ühendatud seadmest {1}"
}
diff --git a/Emby.Server.Implementations/Localization/Core/fa.json b/Emby.Server.Implementations/Localization/Core/fa.json
index 8ab657e5b..3d3b3533f 100644
--- a/Emby.Server.Implementations/Localization/Core/fa.json
+++ b/Emby.Server.Implementations/Localization/Core/fa.json
@@ -6,7 +6,7 @@
"AuthenticationSucceededWithUserName": "{0} با موفقیت تایید اعتبار شد",
"Books": "کتاب‌ها",
"CameraImageUploadedFrom": "یک عکس جدید از دوربین ارسال شده است {0}",
- "Channels": "کانال‌ها",
+ "Channels": "کانالها",
"ChapterNameValue": "قسمت {0}",
"Collections": "مجموعه‌ها",
"DeviceOfflineWithName": "ارتباط {0} قطع شد",
@@ -37,7 +37,7 @@
"MessageNamedServerConfigurationUpdatedWithValue": "پکربندی بخش {0} سرور بروزرسانی شد",
"MessageServerConfigurationUpdated": "پیکربندی سرور بروزرسانی شد",
"MixedContent": "محتوای مخلوط",
- "Movies": "فیلم‌ها",
+ "Movies": "فیلم ها",
"Music": "موسیقی",
"MusicVideos": "موزیک ویدیوها",
"NameInstallFailed": "{0} نصب با مشکل مواجه شد",
diff --git a/Emby.Server.Implementations/Localization/Core/fr.json b/Emby.Server.Implementations/Localization/Core/fr.json
index 1265b6ef5..d60955d5f 100644
--- a/Emby.Server.Implementations/Localization/Core/fr.json
+++ b/Emby.Server.Implementations/Localization/Core/fr.json
@@ -15,7 +15,7 @@
"Favorites": "Favoris",
"Folders": "Dossiers",
"Genres": "Genres",
- "HeaderAlbumArtists": "Artistes de l'album",
+ "HeaderAlbumArtists": "Artistes d'album",
"HeaderContinueWatching": "Continuer à regarder",
"HeaderFavoriteAlbums": "Albums favoris",
"HeaderFavoriteArtists": "Artistes préférés",
diff --git a/Emby.Server.Implementations/Localization/Core/hu.json b/Emby.Server.Implementations/Localization/Core/hu.json
index 85ab1511a..acde84aaf 100644
--- a/Emby.Server.Implementations/Localization/Core/hu.json
+++ b/Emby.Server.Implementations/Localization/Core/hu.json
@@ -11,11 +11,11 @@
"Collections": "Gyűjtemények",
"DeviceOfflineWithName": "{0} kijelentkezett",
"DeviceOnlineWithName": "{0} belépett",
- "FailedLoginAttemptWithUserName": "Sikertelen bejelentkezési kísérlet tőle: {0}",
+ "FailedLoginAttemptWithUserName": "Sikertelen bejelentkezési kísérlet innen: {0}",
"Favorites": "Kedvencek",
"Folders": "Könyvtárak",
"Genres": "Műfajok",
- "HeaderAlbumArtists": "Előadó albumai",
+ "HeaderAlbumArtists": "Album előadó(k)",
"HeaderContinueWatching": "Megtekintés folytatása",
"HeaderFavoriteAlbums": "Kedvenc albumok",
"HeaderFavoriteArtists": "Kedvenc előadók",
diff --git a/Emby.Server.Implementations/Localization/Core/id.json b/Emby.Server.Implementations/Localization/Core/id.json
index ba3513870..37d59abd9 100644
--- a/Emby.Server.Implementations/Localization/Core/id.json
+++ b/Emby.Server.Implementations/Localization/Core/id.json
@@ -7,10 +7,10 @@
"MessageApplicationUpdated": "Jellyfin Server sudah diperbarui",
"Latest": "Terbaru",
"LabelIpAddressValue": "Alamat IP: {0}",
- "ItemRemovedWithName": "{0} sudah dikeluarkan dari pustaka",
+ "ItemRemovedWithName": "{0} sudah dihapus dari pustaka",
"ItemAddedWithName": "{0} telah dimasukkan ke dalam pustaka",
- "Inherit": "Warisan",
- "HomeVideos": "Video Rumah",
+ "Inherit": "Warisi",
+ "HomeVideos": "Video Rumahan",
"HeaderRecordingGroups": "Grup Rekaman",
"HeaderNextUp": "Selanjutnya",
"HeaderLiveTV": "TV Live",
@@ -73,7 +73,7 @@
"NotificationOptionCameraImageUploaded": "Gambar kamera terunggah",
"NotificationOptionApplicationUpdateInstalled": "Pembaruan aplikasi terpasang",
"NotificationOptionApplicationUpdateAvailable": "Pembaruan aplikasi tersedia",
- "NewVersionIsAvailable": "Versi baru dari Jellyfin Server tersedia untuk diunduh.",
+ "NewVersionIsAvailable": "Versi baru dari Jellyfin Server sudah tersedia untuk diunduh.",
"NameSeasonUnknown": "Musim tak diketahui",
"NameSeasonNumber": "Musim {0}",
"NameInstallFailed": "{0} penginstalan gagal",
@@ -117,5 +117,7 @@
"TaskCleanActivityLog": "Bersihkan Log Aktivitas",
"Undefined": "Tidak terdefinisi",
"Forced": "Dipaksa",
- "Default": "Bawaan"
+ "Default": "Bawaan",
+ "TaskOptimizeDatabaseDescription": "Rapihkan basis data dan membersihkan ruang kosong. Menjalankan tugas ini setelah memindai pustaka atau melakukan perubahan lain yang menyiratkan modifikasi basis data dapat meningkatkan kinerja.",
+ "TaskOptimizeDatabase": "Optimalkan basis data"
}
diff --git a/Emby.Server.Implementations/Localization/Core/it.json b/Emby.Server.Implementations/Localization/Core/it.json
index 5e28cf09f..4c4de4999 100644
--- a/Emby.Server.Implementations/Localization/Core/it.json
+++ b/Emby.Server.Implementations/Localization/Core/it.json
@@ -15,7 +15,7 @@
"Favorites": "Preferiti",
"Folders": "Cartelle",
"Genres": "Generi",
- "HeaderAlbumArtists": "Artisti dell'Album",
+ "HeaderAlbumArtists": "Artisti dell'album",
"HeaderContinueWatching": "Continua a guardare",
"HeaderFavoriteAlbums": "Album Preferiti",
"HeaderFavoriteArtists": "Artisti Preferiti",
diff --git a/Emby.Server.Implementations/Localization/Core/kk.json b/Emby.Server.Implementations/Localization/Core/kk.json
index d28564a7c..1b4a18deb 100644
--- a/Emby.Server.Implementations/Localization/Core/kk.json
+++ b/Emby.Server.Implementations/Localization/Core/kk.json
@@ -15,7 +15,7 @@
"Favorites": "Tañdaulylar",
"Folders": "Qaltalar",
"Genres": "Janrlar",
- "HeaderAlbumArtists": "Oryndauşynyñ älbomy",
+ "HeaderAlbumArtists": "Älbom oryndauşylary",
"HeaderContinueWatching": "Qaraudy jalğastyru",
"HeaderFavoriteAlbums": "Tañdauly älbomdar",
"HeaderFavoriteArtists": "Tañdauly oryndauşylar",
diff --git a/Emby.Server.Implementations/Localization/Core/mk.json b/Emby.Server.Implementations/Localization/Core/mk.json
index b780ef498..6baedcb2d 100644
--- a/Emby.Server.Implementations/Localization/Core/mk.json
+++ b/Emby.Server.Implementations/Localization/Core/mk.json
@@ -50,7 +50,7 @@
"HeaderFavoriteEpisodes": "Омилени Епизоди",
"HeaderFavoriteArtists": "Омилени Изведувачи",
"HeaderFavoriteAlbums": "Омилени Албуми",
- "HeaderContinueWatching": "Продолжи со гледање",
+ "HeaderContinueWatching": "Продолжи со Гледање",
"HeaderAlbumArtists": "Изведувачи од Албуми",
"Genres": "Жанрови",
"Folders": "Папки",
diff --git a/Emby.Server.Implementations/Localization/Core/mn.json b/Emby.Server.Implementations/Localization/Core/mn.json
new file mode 100644
index 000000000..7421d42fb
--- /dev/null
+++ b/Emby.Server.Implementations/Localization/Core/mn.json
@@ -0,0 +1,14 @@
+{
+ "Books": "Номууд",
+ "HeaderNextUp": "Дараах",
+ "HeaderContinueWatching": "Үргэлжлүүлэн үзэх",
+ "Songs": "Дуунууд",
+ "Playlists": "Тоглуулах жагсаалт",
+ "Movies": "Кино",
+ "Latest": "Сүүлийн үеийн",
+ "Genres": "Төрөл зүйл",
+ "Favorites": "Дуртай",
+ "Collections": "Багц",
+ "Artists": "Зураачуд",
+ "Albums": "Цомгууд"
+}
diff --git a/Emby.Server.Implementations/Localization/Core/ms.json b/Emby.Server.Implementations/Localization/Core/ms.json
index b2dcf270c..4dcb99293 100644
--- a/Emby.Server.Implementations/Localization/Core/ms.json
+++ b/Emby.Server.Implementations/Localization/Core/ms.json
@@ -39,7 +39,7 @@
"MixedContent": "Kandungan campuran",
"Movies": "Filem",
"Music": "Muzik",
- "MusicVideos": "Muzik video",
+ "MusicVideos": "",
"NameInstallFailed": "{0} pemasangan gagal",
"NameSeasonNumber": "Musim {0}",
"NameSeasonUnknown": "Musim Tidak Diketahui",
diff --git a/Emby.Server.Implementations/Localization/Core/pl.json b/Emby.Server.Implementations/Localization/Core/pl.json
index e8a32a13e..4fa8d2bb4 100644
--- a/Emby.Server.Implementations/Localization/Core/pl.json
+++ b/Emby.Server.Implementations/Localization/Core/pl.json
@@ -15,7 +15,7 @@
"Favorites": "Ulubione",
"Folders": "Foldery",
"Genres": "Gatunki",
- "HeaderAlbumArtists": "Album artysty",
+ "HeaderAlbumArtists": "Wykonawcy albumów",
"HeaderContinueWatching": "Kontynuuj odtwarzanie",
"HeaderFavoriteAlbums": "Ulubione albumy",
"HeaderFavoriteArtists": "Ulubieni wykonawcy",
@@ -47,7 +47,7 @@
"NotificationOptionApplicationUpdateAvailable": "Dostępna aktualizacja aplikacji",
"NotificationOptionApplicationUpdateInstalled": "Zaktualizowano aplikację",
"NotificationOptionAudioPlayback": "Rozpoczęto odtwarzanie muzyki",
- "NotificationOptionAudioPlaybackStopped": "Odtwarzane dźwięku zatrzymane",
+ "NotificationOptionAudioPlaybackStopped": "Odtwarzanie dźwięku zatrzymane",
"NotificationOptionCameraImageUploaded": "Przekazano obraz z urządzenia przenośnego",
"NotificationOptionInstallationFailed": "Nieudana instalacja",
"NotificationOptionNewLibraryContent": "Dodano nową zawartość",
@@ -98,7 +98,7 @@
"TaskRefreshChannels": "Odśwież kanały",
"TaskCleanTranscodeDescription": "Usuwa transkodowane pliki starsze niż 1 dzień.",
"TaskCleanTranscode": "Wyczyść folder transkodowania",
- "TaskUpdatePluginsDescription": "Pobiera i instaluje aktualizacje dla pluginów które są skonfigurowane do automatycznej aktualizacji.",
+ "TaskUpdatePluginsDescription": "Pobiera i instaluje aktualizacje dla pluginów, które są skonfigurowane do automatycznej aktualizacji.",
"TaskUpdatePlugins": "Aktualizuj pluginy",
"TaskRefreshPeopleDescription": "Odświeża metadane o aktorów i reżyserów w Twojej bibliotece mediów.",
"TaskRefreshPeople": "Odśwież obsadę",
diff --git a/Emby.Server.Implementations/Localization/Core/pr.json b/Emby.Server.Implementations/Localization/Core/pr.json
index e3a3bfaf1..81aa996d9 100644
--- a/Emby.Server.Implementations/Localization/Core/pr.json
+++ b/Emby.Server.Implementations/Localization/Core/pr.json
@@ -1,5 +1,7 @@
{
"Books": "Libros",
"AuthenticationSucceededWithUserName": "{0} autentificado correctamente",
- "Artists": "Artistas"
+ "Artists": "Artistas",
+ "Songs": "Shantees",
+ "Albums": "Ships"
}
diff --git a/Emby.Server.Implementations/Localization/Core/pt-PT.json b/Emby.Server.Implementations/Localization/Core/pt-PT.json
index 8c41edf96..8870de168 100644
--- a/Emby.Server.Implementations/Localization/Core/pt-PT.json
+++ b/Emby.Server.Implementations/Localization/Core/pt-PT.json
@@ -15,7 +15,7 @@
"Favorites": "Favoritos",
"Folders": "Pastas",
"Genres": "Géneros",
- "HeaderAlbumArtists": "Artistas do Álbum",
+ "HeaderAlbumArtists": "Álbum do Artista",
"HeaderContinueWatching": "Continuar a Ver",
"HeaderFavoriteAlbums": "Álbuns Favoritos",
"HeaderFavoriteArtists": "Artistas Favoritos",
@@ -39,7 +39,7 @@
"MixedContent": "Conteúdo Misto",
"Movies": "Filmes",
"Music": "Música",
- "MusicVideos": "Videoclips",
+ "MusicVideos": "Videoclipes",
"NameInstallFailed": "{0} falha na instalação",
"NameSeasonNumber": "Temporada {0}",
"NameSeasonUnknown": "Temporada Desconhecida",
@@ -118,5 +118,7 @@
"TaskCleanActivityLog": "Limpar registo de atividade",
"Undefined": "Indefinido",
"Forced": "Forçado",
- "Default": "Padrão"
+ "Default": "Padrão",
+ "TaskOptimizeDatabaseDescription": "Base de dados compacta e corta espaço livre. A execução desta tarefa depois de digitalizar a biblioteca ou de fazer outras alterações que impliquem modificações na base de dados pode melhorar o desempenho.",
+ "TaskOptimizeDatabase": "Otimizar base de dados"
}
diff --git a/Emby.Server.Implementations/Localization/Core/ru.json b/Emby.Server.Implementations/Localization/Core/ru.json
index cd016b51b..2d7163275 100644
--- a/Emby.Server.Implementations/Localization/Core/ru.json
+++ b/Emby.Server.Implementations/Localization/Core/ru.json
@@ -31,7 +31,7 @@
"ItemRemovedWithName": "{0} - изъято из медиатеки",
"LabelIpAddressValue": "IP-адрес: {0}",
"LabelRunningTimeValue": "Длительность: {0}",
- "Latest": "Последнее",
+ "Latest": "Крайнее",
"MessageApplicationUpdated": "Jellyfin Server был обновлён",
"MessageApplicationUpdatedTo": "Jellyfin Server был обновлён до {0}",
"MessageNamedServerConfigurationUpdatedWithValue": "Конфигурация сервера (раздел {0}) была обновлена",
@@ -119,6 +119,6 @@
"Undefined": "Не определено",
"Forced": "Форсир-ые",
"Default": "По умолчанию",
- "TaskOptimizeDatabaseDescription": "Сжимает базу данных и обрезает свободное место. Выполнение этой задачи после сканирования библиотеки или внесения других изменений, предполагающих модификации базы данных, может повысить производительность.",
+ "TaskOptimizeDatabaseDescription": "Сжимает базу данных и вырезает свободные места. Выполнение этой задачи после сканирования библиотеки или внесения других изменений, предполагающих модификации базы данных, может повысить производительность.",
"TaskOptimizeDatabase": "Оптимизировать базу данных"
}
diff --git a/Emby.Server.Implementations/Localization/Core/sk.json b/Emby.Server.Implementations/Localization/Core/sk.json
index ad90bd813..37da7d5ab 100644
--- a/Emby.Server.Implementations/Localization/Core/sk.json
+++ b/Emby.Server.Implementations/Localization/Core/sk.json
@@ -39,7 +39,7 @@
"MixedContent": "Zmiešaný obsah",
"Movies": "Filmy",
"Music": "Hudba",
- "MusicVideos": "Hudobné videá",
+ "MusicVideos": "Hudobné videoklipy",
"NameInstallFailed": "Inštalácia {0} zlyhala",
"NameSeasonNumber": "Séria {0}",
"NameSeasonUnknown": "Neznáma séria",
diff --git a/Emby.Server.Implementations/Localization/Core/sq.json b/Emby.Server.Implementations/Localization/Core/sq.json
index 066ef5587..2766dab06 100644
--- a/Emby.Server.Implementations/Localization/Core/sq.json
+++ b/Emby.Server.Implementations/Localization/Core/sq.json
@@ -74,7 +74,7 @@
"NameSeasonUnknown": "Sezon i panjohur",
"NameSeasonNumber": "Sezoni {0}",
"NameInstallFailed": "Instalimi i {0} dështoi",
- "MusicVideos": "Video muzikore",
+ "MusicVideos": "Video Muzikore",
"Music": "Muzikë",
"Movies": "Filmat",
"MixedContent": "Përmbajtje e përzier",
@@ -96,7 +96,7 @@
"HeaderFavoriteArtists": "Artistët e preferuar",
"HeaderFavoriteAlbums": "Albumet e preferuar",
"HeaderContinueWatching": "Vazhdo të shikosh",
- "HeaderAlbumArtists": "Artistët e Albumeve",
+ "HeaderAlbumArtists": "Artistët e albumeve",
"Genres": "Zhanret",
"Folders": "Skedarët",
"Favorites": "Të preferuarat",
diff --git a/Emby.Server.Implementations/Localization/Core/sr.json b/Emby.Server.Implementations/Localization/Core/sr.json
index 15fb34186..2d6f3d53d 100644
--- a/Emby.Server.Implementations/Localization/Core/sr.json
+++ b/Emby.Server.Implementations/Localization/Core/sr.json
@@ -50,7 +50,7 @@
"NameSeasonUnknown": "Непозната сезона",
"NameSeasonNumber": "Сезона {0}",
"NameInstallFailed": "Инсталација {0} није успела",
- "MusicVideos": "Музички спотови",
+ "MusicVideos": "Музички видео",
"Music": "Музика",
"Movies": "Филмови",
"MixedContent": "Мешовит садржај",
diff --git a/Emby.Server.Implementations/Localization/Core/sv.json b/Emby.Server.Implementations/Localization/Core/sv.json
index 6c772c6a2..1cef30b6c 100644
--- a/Emby.Server.Implementations/Localization/Core/sv.json
+++ b/Emby.Server.Implementations/Localization/Core/sv.json
@@ -15,7 +15,7 @@
"Favorites": "Favoriter",
"Folders": "Mappar",
"Genres": "Genrer",
- "HeaderAlbumArtists": "Artistens album",
+ "HeaderAlbumArtists": "Albumsartister",
"HeaderContinueWatching": "Fortsätt kolla",
"HeaderFavoriteAlbums": "Favoritalbum",
"HeaderFavoriteArtists": "Favoritartister",
diff --git a/Emby.Server.Implementations/Localization/Core/ta.json b/Emby.Server.Implementations/Localization/Core/ta.json
index d6e9aa8e5..e3a993625 100644
--- a/Emby.Server.Implementations/Localization/Core/ta.json
+++ b/Emby.Server.Implementations/Localization/Core/ta.json
@@ -85,7 +85,7 @@
"HeaderFavoriteArtists": "பிடித்த கலைஞர்கள்",
"HeaderFavoriteAlbums": "பிடித்த ஆல்பங்கள்",
"HeaderContinueWatching": "தொடர்ந்து பார்",
- "HeaderAlbumArtists": "இசைக் கலைஞர்கள்",
+ "HeaderAlbumArtists": "கலைஞரின் ஆல்பம்",
"Genres": "வகைகள்",
"Favorites": "பிடித்தவை",
"ChapterNameValue": "அத்தியாயம் {0}",
diff --git a/Emby.Server.Implementations/Localization/Core/te.json b/Emby.Server.Implementations/Localization/Core/te.json
new file mode 100644
index 000000000..a9a8ceae0
--- /dev/null
+++ b/Emby.Server.Implementations/Localization/Core/te.json
@@ -0,0 +1,23 @@
+{
+ "ValueSpecialEpisodeName": "ప్రత్యేక - {0}",
+ "Sync": "సమకాలీకరించు",
+ "Songs": "పాటలు",
+ "Shows": "ప్రదర్శనలు",
+ "Playlists": "ప్లేజాబితాలు",
+ "Photos": "ఫోటోలు",
+ "MusicVideos": "మ్యూజిక్ వీడియోలు",
+ "Music": "సంగీతం",
+ "Movies": "సినిమాలు",
+ "HeaderContinueWatching": "చూడటం కొనసాగించండి",
+ "HeaderAlbumArtists": "ఆల్బమ్ కళాకారులు",
+ "Genres": "శైలులు",
+ "Forced": "బలవంతంగా",
+ "Folders": "ఫోల్డర్లు",
+ "Favorites": "ఇష్టమైనవి",
+ "Default": "డిఫాల్ట్",
+ "Collections": "సేకరణలు",
+ "Channels": "ఛానెల్‌లు",
+ "Books": "పుస్తకాలు",
+ "Artists": "కళాకారులు",
+ "Albums": "ఆల్బమ్‌లు"
+}
diff --git a/Emby.Server.Implementations/Localization/Core/tr.json b/Emby.Server.Implementations/Localization/Core/tr.json
index e661299c4..8fadb88ac 100644
--- a/Emby.Server.Implementations/Localization/Core/tr.json
+++ b/Emby.Server.Implementations/Localization/Core/tr.json
@@ -8,7 +8,7 @@
"CameraImageUploadedFrom": "{0} 'den yeni bir kamera resmi yüklendi",
"Channels": "Kanallar",
"ChapterNameValue": "Bölüm {0}",
- "Collections": "Koleksiyon",
+ "Collections": "Koleksiyonlar",
"DeviceOfflineWithName": "{0} bağlantısı kesildi",
"DeviceOnlineWithName": "{0} bağlı",
"FailedLoginAttemptWithUserName": "{0} adresinden giriş başarısız oldu",
diff --git a/Emby.Server.Implementations/Localization/Core/uk.json b/Emby.Server.Implementations/Localization/Core/uk.json
index e99ed6f0b..1c7d73615 100644
--- a/Emby.Server.Implementations/Localization/Core/uk.json
+++ b/Emby.Server.Implementations/Localization/Core/uk.json
@@ -16,7 +16,7 @@
"HeaderFavoriteArtists": "Улюблені виконавці",
"HeaderFavoriteAlbums": "Улюблені альбоми",
"HeaderContinueWatching": "Продовжити перегляд",
- "HeaderAlbumArtists": "Виконавці альбомів",
+ "HeaderAlbumArtists": "Виконавці альбому",
"Genres": "Жанри",
"Folders": "Каталоги",
"Favorites": "Улюблені",
diff --git a/Emby.Server.Implementations/Localization/Core/vi.json b/Emby.Server.Implementations/Localization/Core/vi.json
index 33aa0eea0..b7ece8d5f 100644
--- a/Emby.Server.Implementations/Localization/Core/vi.json
+++ b/Emby.Server.Implementations/Localization/Core/vi.json
@@ -3,7 +3,7 @@
"Favorites": "Yêu Thích",
"Folders": "Thư Mục",
"Genres": "Thể Loại",
- "HeaderAlbumArtists": "Album Nghệ sĩ",
+ "HeaderAlbumArtists": "Album nghệ sĩ",
"HeaderContinueWatching": "Xem Tiếp",
"HeaderLiveTV": "TV Trực Tiếp",
"Movies": "Phim",
@@ -62,11 +62,11 @@
"PluginUninstalledWithName": "{0} đã được gỡ bỏ",
"PluginInstalledWithName": "{0} đã được cài đặt",
"Plugin": "Plugin",
- "NotificationOptionVideoPlaybackStopped": "Phát lại video đã dừng",
+ "NotificationOptionVideoPlaybackStopped": "Đã dừng phát lại video",
"NotificationOptionVideoPlayback": "Đã bắt đầu phát lại video",
"NotificationOptionUserLockedOut": "Người dùng bị khóa",
"NotificationOptionTaskFailed": "Lỗi tác vụ đã lên lịch",
- "NotificationOptionServerRestartRequired": "Yêu cầu khởi động lại Server",
+ "NotificationOptionServerRestartRequired": "Yêu cầu khởi động lại máy chủ",
"NotificationOptionPluginUpdateInstalled": "Cập nhật Plugin đã được cài đặt",
"NotificationOptionPluginUninstalled": "Đã gỡ bỏ Plugin",
"NotificationOptionPluginInstalled": "Đã cài đặt Plugin",
@@ -75,7 +75,7 @@
"NotificationOptionInstallationFailed": "Cài đặt thất bại",
"NotificationOptionCameraImageUploaded": "Đã tải lên hình ảnh máy ảnh",
"NotificationOptionAudioPlaybackStopped": "Phát lại âm thanh đã dừng",
- "NotificationOptionAudioPlayback": "Phát lại âm thanh đã bắt đầu",
+ "NotificationOptionAudioPlayback": "Đã bắt đầu phát lại âm thanh",
"NotificationOptionApplicationUpdateInstalled": "Bản cập nhật ứng dụng đã được cài đặt",
"NotificationOptionApplicationUpdateAvailable": "Bản cập nhật ứng dụng hiện sẵn có",
"NewVersionIsAvailable": "Một phiên bản mới của Jellyfin Server sẵn có để tải.",
@@ -95,7 +95,7 @@
"ItemRemovedWithName": "{0} đã xóa khỏi thư viện",
"ItemAddedWithName": "{0} được thêm vào thư viện",
"Inherit": "Thừa hưởng",
- "HomeVideos": "Video nhà",
+ "HomeVideos": "Video Nhà",
"HeaderRecordingGroups": "Nhóm Ghi Video",
"HeaderNextUp": "Tiếp Theo",
"HeaderFavoriteSongs": "Bài Hát Yêu Thích",
@@ -103,7 +103,7 @@
"HeaderFavoriteEpisodes": "Tập Phim Yêu Thích",
"HeaderFavoriteArtists": "Nghệ Sĩ Yêu Thích",
"HeaderFavoriteAlbums": "Album Ưa Thích",
- "FailedLoginAttemptWithUserName": "Nỗ lực đăng nhập thất bại từ {0}",
+ "FailedLoginAttemptWithUserName": "Đăng nhập không thành công thử từ {0}",
"DeviceOnlineWithName": "{0} đã kết nối",
"DeviceOfflineWithName": "{0} đã ngắt kết nối",
"ChapterNameValue": "Phân Cảnh {0}",
diff --git a/Emby.Server.Implementations/Localization/Core/zh-CN.json b/Emby.Server.Implementations/Localization/Core/zh-CN.json
index f9df62724..ac4eb644b 100644
--- a/Emby.Server.Implementations/Localization/Core/zh-CN.json
+++ b/Emby.Server.Implementations/Localization/Core/zh-CN.json
@@ -11,7 +11,7 @@
"Collections": "合集",
"DeviceOfflineWithName": "{0} 已断开",
"DeviceOnlineWithName": "{0} 已连接",
- "FailedLoginAttemptWithUserName": "来自 {0} 的失败登入",
+ "FailedLoginAttemptWithUserName": "从 {0} 尝试登录失败",
"Favorites": "我的最爱",
"Folders": "文件夹",
"Genres": "风格",
diff --git a/Emby.Server.Implementations/Localization/Core/zu.json b/Emby.Server.Implementations/Localization/Core/zu.json
new file mode 100644
index 000000000..0967ef424
--- /dev/null
+++ b/Emby.Server.Implementations/Localization/Core/zu.json
@@ -0,0 +1 @@
+{}
diff --git a/Emby.Server.Implementations/Localization/LocalizationManager.cs b/Emby.Server.Implementations/Localization/LocalizationManager.cs
index 03919197e..9cdbbb6a3 100644
--- a/Emby.Server.Implementations/Localization/LocalizationManager.cs
+++ b/Emby.Server.Implementations/Localization/LocalizationManager.cs
@@ -372,43 +372,76 @@ namespace Emby.Server.Implementations.Localization
/// <inheritdoc />
public IEnumerable<LocalizationOption> GetLocalizationOptions()
{
- yield return new LocalizationOption("Arabic", "ar");
- yield return new LocalizationOption("Bulgarian (Bulgaria)", "bg-BG");
- yield return new LocalizationOption("Catalan", "ca");
- yield return new LocalizationOption("Chinese Simplified", "zh-CN");
- yield return new LocalizationOption("Chinese Traditional", "zh-TW");
- yield return new LocalizationOption("Croatian", "hr");
- yield return new LocalizationOption("Czech", "cs");
- yield return new LocalizationOption("Danish", "da");
- yield return new LocalizationOption("Dutch", "nl");
+ yield return new LocalizationOption("Afrikaans", "af");
+ yield return new LocalizationOption("العربية", "ar");
+ yield return new LocalizationOption("Беларуская", "be");
+ yield return new LocalizationOption("Български", "bg-BG");
+ yield return new LocalizationOption("বাংলা (বাংলাদেশ)", "bn");
+ yield return new LocalizationOption("Català", "ca");
+ yield return new LocalizationOption("Čeština", "cs");
+ yield return new LocalizationOption("Cymraeg", "cy");
+ yield return new LocalizationOption("Dansk", "da");
+ yield return new LocalizationOption("Deutsch", "de");
yield return new LocalizationOption("English (United Kingdom)", "en-GB");
- yield return new LocalizationOption("English (United States)", "en-US");
- yield return new LocalizationOption("French", "fr");
- yield return new LocalizationOption("French (Canada)", "fr-CA");
- yield return new LocalizationOption("German", "de");
- yield return new LocalizationOption("Greek", "el");
- yield return new LocalizationOption("Hebrew", "he");
- yield return new LocalizationOption("Hungarian", "hu");
- yield return new LocalizationOption("Italian", "it");
- yield return new LocalizationOption("Kazakh", "kk");
- yield return new LocalizationOption("Korean", "ko");
- yield return new LocalizationOption("Lithuanian", "lt-LT");
- yield return new LocalizationOption("Malay", "ms");
- yield return new LocalizationOption("Norwegian Bokmål", "nb");
- yield return new LocalizationOption("Persian", "fa");
- yield return new LocalizationOption("Polish", "pl");
- yield return new LocalizationOption("Portuguese (Brazil)", "pt-BR");
- yield return new LocalizationOption("Portuguese (Portugal)", "pt-PT");
- yield return new LocalizationOption("Russian", "ru");
- yield return new LocalizationOption("Slovak", "sk");
- yield return new LocalizationOption("Slovenian (Slovenia)", "sl-SI");
- yield return new LocalizationOption("Spanish", "es");
- yield return new LocalizationOption("Spanish (Argentina)", "es-AR");
- yield return new LocalizationOption("Spanish (Mexico)", "es-MX");
- yield return new LocalizationOption("Swedish", "sv");
- yield return new LocalizationOption("Swiss German", "gsw");
- yield return new LocalizationOption("Turkish", "tr");
+ yield return new LocalizationOption("English", "en-US");
+ yield return new LocalizationOption("Ελληνικά", "el");
+ yield return new LocalizationOption("Esperanto", "eo");
+ yield return new LocalizationOption("Español", "es");
+ yield return new LocalizationOption("Español americano", "es_419");
+ yield return new LocalizationOption("Español (Argentina)", "es-AR");
+ yield return new LocalizationOption("Español (Dominicana)", "es_DO");
+ yield return new LocalizationOption("Español (México)", "es-MX");
+ yield return new LocalizationOption("Eesti", "et");
+ yield return new LocalizationOption("فارسی", "fa");
+ yield return new LocalizationOption("Suomi", "fi");
+ yield return new LocalizationOption("Filipino", "fil");
+ yield return new LocalizationOption("Français", "fr");
+ yield return new LocalizationOption("Français (Canada)", "fr-CA");
+ yield return new LocalizationOption("Galego", "gl");
+ yield return new LocalizationOption("Schwiizerdütsch", "gsw");
+ yield return new LocalizationOption("עִבְרִית", "he");
+ yield return new LocalizationOption("हिन्दी", "hi");
+ yield return new LocalizationOption("Hrvatski", "hr");
+ yield return new LocalizationOption("Magyar", "hu");
+ yield return new LocalizationOption("Bahasa Indonesia", "id");
+ yield return new LocalizationOption("Íslenska", "is");
+ yield return new LocalizationOption("Italiano", "it");
+ yield return new LocalizationOption("日本語", "ja");
+ yield return new LocalizationOption("Qazaqşa", "kk");
+ yield return new LocalizationOption("한국어", "ko");
+ yield return new LocalizationOption("Lietuvių", "lt");
+ yield return new LocalizationOption("Latviešu", "lv");
+ yield return new LocalizationOption("Македонски", "mk");
+ yield return new LocalizationOption("മലയാളം", "ml");
+ yield return new LocalizationOption("मराठी", "mr");
+ yield return new LocalizationOption("Bahasa Melayu", "ms");
+ yield return new LocalizationOption("Norsk bokmål", "nb");
+ yield return new LocalizationOption("नेपाली", "ne");
+ yield return new LocalizationOption("Nederlands", "nl");
+ yield return new LocalizationOption("Norsk nynorsk", "nn");
+ yield return new LocalizationOption("ਪੰਜਾਬੀ", "pa");
+ yield return new LocalizationOption("Polski", "pl");
+ yield return new LocalizationOption("Pirate", "pr");
+ yield return new LocalizationOption("Português", "pt");
+ yield return new LocalizationOption("Português (Brasil)", "pt-BR");
+ yield return new LocalizationOption("Português (Portugal)", "pt-PT");
+ yield return new LocalizationOption("Românește", "ro");
+ yield return new LocalizationOption("Русский", "ru");
+ yield return new LocalizationOption("Slovenčina", "sk");
+ yield return new LocalizationOption("Slovenščina", "sl-SI");
+ yield return new LocalizationOption("Shqip", "sq");
+ yield return new LocalizationOption("Српски", "sr");
+ yield return new LocalizationOption("Svenska", "sv");
+ yield return new LocalizationOption("தமிழ்", "ta");
+ yield return new LocalizationOption("తెలుగు", "te");
+ yield return new LocalizationOption("ภาษาไทย", "th");
+ yield return new LocalizationOption("Türkçe", "tr");
+ yield return new LocalizationOption("Українська", "uk");
+ yield return new LocalizationOption("اُردُو", "ur_PK");
yield return new LocalizationOption("Tiếng Việt", "vi");
+ yield return new LocalizationOption("汉语 (简化字)", "zh-CN");
+ yield return new LocalizationOption("漢語 (繁体字)", "zh-TW");
+ yield return new LocalizationOption("廣東話 (香港)", "zh-HK");
}
}
}
diff --git a/Emby.Server.Implementations/Localization/countries.json b/Emby.Server.Implementations/Localization/countries.json
index b08a3ae79..22ffc5e09 100644
--- a/Emby.Server.Implementations/Localization/countries.json
+++ b/Emby.Server.Implementations/Localization/countries.json
@@ -630,7 +630,7 @@
"TwoLetterISORegionName": "MD"
},
{
- "DisplayName": "Réunion",
+ "DisplayName": "Réunion",
"Name": "RE",
"ThreeLetterISORegionName": "REU",
"TwoLetterISORegionName": "RE"
diff --git a/Emby.Server.Implementations/Localization/iso6392.txt b/Emby.Server.Implementations/Localization/iso6392.txt
index 488901822..66fba3330 100644
--- a/Emby.Server.Implementations/Localization/iso6392.txt
+++ b/Emby.Server.Implementations/Localization/iso6392.txt
@@ -349,7 +349,8 @@ pli||pi|Pali|pali
pol||pl|Polish|polonais
pon|||Pohnpeian|pohnpei
por||pt|Portuguese|portugais
-pob||pt-br|Portuguese (Brazil)|portugais
+pop||pt-pt|Portuguese (Portugal)|portugais (pt-pt)
+pob||pt-br|Portuguese (Brazil)|portugais (pt-br)
pra|||Prakrit languages|prâkrit, langues
pro|||Provençal, Old (to 1500)|provençal ancien (jusqu'à 1500)
pus||ps|Pushto; Pashto|pachto
diff --git a/Emby.Server.Implementations/Playlists/PlaylistManager.cs b/Emby.Server.Implementations/Playlists/PlaylistManager.cs
index b07798fa4..9481e26f7 100644
--- a/Emby.Server.Implementations/Playlists/PlaylistManager.cs
+++ b/Emby.Server.Implementations/Playlists/PlaylistManager.cs
@@ -527,7 +527,7 @@ namespace Emby.Server.Implementations.Playlists
var relativeUri = folderUri.MakeRelativeUri(fileAbsoluteUri);
string relativePath = Uri.UnescapeDataString(relativeUri.ToString());
- if (fileAbsoluteUri.Scheme.Equals("file", StringComparison.CurrentCultureIgnoreCase))
+ if (fileAbsoluteUri.Scheme.Equals("file", StringComparison.OrdinalIgnoreCase))
{
relativePath = relativePath.Replace(Path.AltDirectorySeparatorChar, Path.DirectorySeparatorChar);
}
diff --git a/Emby.Server.Implementations/Plugins/PluginManager.cs b/Emby.Server.Implementations/Plugins/PluginManager.cs
index d52c0b2a1..d70a15dbc 100644
--- a/Emby.Server.Implementations/Plugins/PluginManager.cs
+++ b/Emby.Server.Implementations/Plugins/PluginManager.cs
@@ -126,7 +126,8 @@ namespace Emby.Server.Implementations.Plugins
{
assembly = Assembly.LoadFrom(file);
- assembly.GetExportedTypes();
+ // Load all required types to verify that the plugin will load
+ assembly.GetTypes();
}
catch (FileLoadException ex)
{
@@ -134,7 +135,7 @@ namespace Emby.Server.Implementations.Plugins
ChangePluginState(plugin, PluginStatus.Malfunctioned);
continue;
}
- catch (TypeLoadException ex) // Undocumented exception
+ catch (SystemException ex) when (ex is TypeLoadException or ReflectionTypeLoadException) // Undocumented exception
{
_logger.LogError(ex, "Failed to load assembly {Path}. This error occurs when a plugin references an incompatible version of one of the shared libraries. Disabling plugin.", file);
ChangePluginState(plugin, PluginStatus.NotSupported);
diff --git a/Emby.Server.Implementations/ScheduledTasks/ScheduledTaskWorker.cs b/Emby.Server.Implementations/ScheduledTasks/ScheduledTaskWorker.cs
index f2cdfeb16..21a7f4f5f 100644
--- a/Emby.Server.Implementations/ScheduledTasks/ScheduledTaskWorker.cs
+++ b/Emby.Server.Implementations/ScheduledTasks/ScheduledTaskWorker.cs
@@ -638,7 +638,7 @@ namespace Emby.Server.Implementations.ScheduledTasks
{
try
{
- _logger.LogInformation(Name + ": Cancelling");
+ _logger.LogInformation("{Name}: Cancelling", Name);
token.Cancel();
}
catch (Exception ex)
@@ -652,16 +652,16 @@ namespace Emby.Server.Implementations.ScheduledTasks
{
try
{
- _logger.LogInformation(Name + ": Waiting on Task");
+ _logger.LogInformation("{Name}: Waiting on Task", Name);
var exited = task.Wait(2000);
if (exited)
{
- _logger.LogInformation(Name + ": Task exited");
+ _logger.LogInformation("{Name}: Task exited", Name);
}
else
{
- _logger.LogInformation(Name + ": Timed out waiting for task to stop");
+ _logger.LogInformation("{Name}: Timed out waiting for task to stop", Name);
}
}
catch (Exception ex)
@@ -674,7 +674,7 @@ namespace Emby.Server.Implementations.ScheduledTasks
{
try
{
- _logger.LogDebug(Name + ": Disposing CancellationToken");
+ _logger.LogDebug("{Name}: Disposing CancellationToken", Name);
token.Dispose();
}
catch (Exception ex)
diff --git a/Emby.Server.Implementations/ScheduledTasks/Tasks/DeleteCacheFileTask.cs b/Emby.Server.Implementations/ScheduledTasks/Tasks/DeleteCacheFileTask.cs
index a575b260c..0941902fc 100644
--- a/Emby.Server.Implementations/ScheduledTasks/Tasks/DeleteCacheFileTask.cs
+++ b/Emby.Server.Implementations/ScheduledTasks/Tasks/DeleteCacheFileTask.cs
@@ -161,11 +161,11 @@ namespace Emby.Server.Implementations.ScheduledTasks.Tasks
}
catch (UnauthorizedAccessException ex)
{
- _logger.LogError(ex, "Error deleting directory {path}", directory);
+ _logger.LogError(ex, "Error deleting directory {Path}", directory);
}
catch (IOException ex)
{
- _logger.LogError(ex, "Error deleting directory {path}", directory);
+ _logger.LogError(ex, "Error deleting directory {Path}", directory);
}
}
}
@@ -179,11 +179,11 @@ namespace Emby.Server.Implementations.ScheduledTasks.Tasks
}
catch (UnauthorizedAccessException ex)
{
- _logger.LogError(ex, "Error deleting file {path}", path);
+ _logger.LogError(ex, "Error deleting file {Path}", path);
}
catch (IOException ex)
{
- _logger.LogError(ex, "Error deleting file {path}", path);
+ _logger.LogError(ex, "Error deleting file {Path}", path);
}
}
}
diff --git a/Emby.Server.Implementations/ScheduledTasks/Tasks/DeleteTranscodeFileTask.cs b/Emby.Server.Implementations/ScheduledTasks/Tasks/DeleteTranscodeFileTask.cs
index b13fc7fc6..099d781cd 100644
--- a/Emby.Server.Implementations/ScheduledTasks/Tasks/DeleteTranscodeFileTask.cs
+++ b/Emby.Server.Implementations/ScheduledTasks/Tasks/DeleteTranscodeFileTask.cs
@@ -141,11 +141,11 @@ namespace Emby.Server.Implementations.ScheduledTasks.Tasks
}
catch (UnauthorizedAccessException ex)
{
- _logger.LogError(ex, "Error deleting directory {path}", directory);
+ _logger.LogError(ex, "Error deleting directory {Path}", directory);
}
catch (IOException ex)
{
- _logger.LogError(ex, "Error deleting directory {path}", directory);
+ _logger.LogError(ex, "Error deleting directory {Path}", directory);
}
}
}
@@ -159,11 +159,11 @@ namespace Emby.Server.Implementations.ScheduledTasks.Tasks
}
catch (UnauthorizedAccessException ex)
{
- _logger.LogError(ex, "Error deleting file {path}", path);
+ _logger.LogError(ex, "Error deleting file {Path}", path);
}
catch (IOException ex)
{
- _logger.LogError(ex, "Error deleting file {path}", path);
+ _logger.LogError(ex, "Error deleting file {Path}", path);
}
}
}
diff --git a/Emby.Server.Implementations/Sorting/AlbumArtistComparer.cs b/Emby.Server.Implementations/Sorting/AlbumArtistComparer.cs
index bd1966623..67a9fbd3b 100644
--- a/Emby.Server.Implementations/Sorting/AlbumArtistComparer.cs
+++ b/Emby.Server.Implementations/Sorting/AlbumArtistComparer.cs
@@ -26,7 +26,7 @@ namespace Emby.Server.Implementations.Sorting
/// <returns>System.Int32.</returns>
public int Compare(BaseItem? x, BaseItem? y)
{
- return string.Compare(GetValue(x), GetValue(y), StringComparison.CurrentCultureIgnoreCase);
+ return string.Compare(GetValue(x), GetValue(y), StringComparison.OrdinalIgnoreCase);
}
/// <summary>
diff --git a/Emby.Server.Implementations/Sorting/AlbumComparer.cs b/Emby.Server.Implementations/Sorting/AlbumComparer.cs
index fe7dc84cb..4d09dda84 100644
--- a/Emby.Server.Implementations/Sorting/AlbumComparer.cs
+++ b/Emby.Server.Implementations/Sorting/AlbumComparer.cs
@@ -25,7 +25,7 @@ namespace Emby.Server.Implementations.Sorting
/// <returns>System.Int32.</returns>
public int Compare(BaseItem? x, BaseItem? y)
{
- return string.Compare(GetValue(x), GetValue(y), StringComparison.CurrentCultureIgnoreCase);
+ return string.Compare(GetValue(x), GetValue(y), StringComparison.OrdinalIgnoreCase);
}
/// <summary>
@@ -35,9 +35,7 @@ namespace Emby.Server.Implementations.Sorting
/// <returns>System.String.</returns>
private static string? GetValue(BaseItem? x)
{
- var audio = x as Audio;
-
- return audio == null ? string.Empty : audio.Album;
+ return x is Audio audio ? audio.Album : string.Empty;
}
}
}
diff --git a/Emby.Server.Implementations/Sorting/ArtistComparer.cs b/Emby.Server.Implementations/Sorting/ArtistComparer.cs
index 7b7ba5753..a8bb55e2b 100644
--- a/Emby.Server.Implementations/Sorting/ArtistComparer.cs
+++ b/Emby.Server.Implementations/Sorting/ArtistComparer.cs
@@ -17,7 +17,7 @@ namespace Emby.Server.Implementations.Sorting
/// <inheritdoc />
public int Compare(BaseItem? x, BaseItem? y)
{
- return string.Compare(GetValue(x), GetValue(y), StringComparison.CurrentCultureIgnoreCase);
+ return string.Compare(GetValue(x), GetValue(y), StringComparison.OrdinalIgnoreCase);
}
/// <summary>
diff --git a/Emby.Server.Implementations/Sorting/NameComparer.cs b/Emby.Server.Implementations/Sorting/NameComparer.cs
index 8f87717f4..c2875eeb9 100644
--- a/Emby.Server.Implementations/Sorting/NameComparer.cs
+++ b/Emby.Server.Implementations/Sorting/NameComparer.cs
@@ -34,7 +34,7 @@ namespace Emby.Server.Implementations.Sorting
throw new ArgumentNullException(nameof(y));
}
- return string.Compare(x.Name, y.Name, StringComparison.CurrentCultureIgnoreCase);
+ return string.Compare(x.Name, y.Name, StringComparison.OrdinalIgnoreCase);
}
}
}
diff --git a/Emby.Server.Implementations/Sorting/SeriesSortNameComparer.cs b/Emby.Server.Implementations/Sorting/SeriesSortNameComparer.cs
index 4123a59f8..0bd9600b9 100644
--- a/Emby.Server.Implementations/Sorting/SeriesSortNameComparer.cs
+++ b/Emby.Server.Implementations/Sorting/SeriesSortNameComparer.cs
@@ -25,7 +25,7 @@ namespace Emby.Server.Implementations.Sorting
/// <returns>System.Int32.</returns>
public int Compare(BaseItem x, BaseItem y)
{
- return string.Compare(GetValue(x), GetValue(y), StringComparison.CurrentCultureIgnoreCase);
+ return string.Compare(GetValue(x), GetValue(y), StringComparison.OrdinalIgnoreCase);
}
private static string GetValue(BaseItem item)
diff --git a/Emby.Server.Implementations/Sorting/SortNameComparer.cs b/Emby.Server.Implementations/Sorting/SortNameComparer.cs
index fb97a0349..79be9a89a 100644
--- a/Emby.Server.Implementations/Sorting/SortNameComparer.cs
+++ b/Emby.Server.Implementations/Sorting/SortNameComparer.cs
@@ -36,7 +36,7 @@ namespace Emby.Server.Implementations.Sorting
throw new ArgumentNullException(nameof(y));
}
- return string.Compare(x.SortName, y.SortName, StringComparison.CurrentCultureIgnoreCase);
+ return string.Compare(x.SortName, y.SortName, StringComparison.OrdinalIgnoreCase);
}
}
}
diff --git a/Emby.Server.Implementations/Udp/UdpServer.cs b/Emby.Server.Implementations/Udp/UdpServer.cs
index bf51c3968..33e4e5651 100644
--- a/Emby.Server.Implementations/Udp/UdpServer.cs
+++ b/Emby.Server.Implementations/Udp/UdpServer.cs
@@ -58,7 +58,7 @@ namespace Emby.Server.Implementations.Udp
_udpSocket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
}
- private async Task RespondToV2Message(string messageText, EndPoint endpoint, CancellationToken cancellationToken)
+ private async Task RespondToV2Message(EndPoint endpoint, CancellationToken cancellationToken)
{
string? localUrl = _config[AddressOverrideConfigKey];
if (string.IsNullOrEmpty(localUrl))
@@ -76,7 +76,7 @@ namespace Emby.Server.Implementations.Udp
try
{
- await _udpSocket.SendToAsync(JsonSerializer.SerializeToUtf8Bytes(response), SocketFlags.None, endpoint).ConfigureAwait(false);
+ await _udpSocket.SendToAsync(JsonSerializer.SerializeToUtf8Bytes(response), SocketFlags.None, endpoint, cancellationToken).ConfigureAwait(false);
}
catch (SocketException ex)
{
@@ -115,7 +115,7 @@ namespace Emby.Server.Implementations.Udp
var text = Encoding.UTF8.GetString(_receiveBuffer, 0, result.ReceivedBytes);
if (text.Contains("who is JellyfinServer?", StringComparison.OrdinalIgnoreCase))
{
- await RespondToV2Message(text, result.RemoteEndPoint, cancellationToken).ConfigureAwait(false);
+ await RespondToV2Message(result.RemoteEndPoint, cancellationToken).ConfigureAwait(false);
}
}
catch (SocketException ex)
diff --git a/Emby.Server.Implementations/Updates/InstallationManager.cs b/Emby.Server.Implementations/Updates/InstallationManager.cs
index 4a022c5db..ef95ebf94 100644
--- a/Emby.Server.Implementations/Updates/InstallationManager.cs
+++ b/Emby.Server.Implementations/Updates/InstallationManager.cs
@@ -571,7 +571,7 @@ namespace Emby.Server.Implementations.Updates
?? _pluginManager.Plugins.FirstOrDefault(p => p.Name.Equals(package.Name, StringComparison.OrdinalIgnoreCase) && p.Version.Equals(package.Version));
await PerformPackageInstallation(package, plugin?.Manifest.Status ?? PluginStatus.Active, cancellationToken).ConfigureAwait(false);
- _logger.LogInformation(plugin == null ? "New plugin installed: {PluginName} {PluginVersion}" : "Plugin updated: {PluginName} {PluginVersion}", package.Name, package.Version);
+ _logger.LogInformation("Plugin {Action}: {PluginName} {PluginVersion}", plugin == null ? "installed" : "updated", package.Name, package.Version);
return plugin != null;
}