aboutsummaryrefslogtreecommitdiff
path: root/Emby.Server.Implementations
diff options
context:
space:
mode:
Diffstat (limited to 'Emby.Server.Implementations')
-rw-r--r--Emby.Server.Implementations/AppBase/BaseConfigurationManager.cs52
-rw-r--r--Emby.Server.Implementations/ApplicationHost.cs114
-rw-r--r--Emby.Server.Implementations/Channels/ChannelManager.cs39
-rw-r--r--Emby.Server.Implementations/Collections/CollectionManager.cs18
-rw-r--r--Emby.Server.Implementations/Configuration/ServerConfigurationManager.cs4
-rw-r--r--Emby.Server.Implementations/Data/SqliteItemRepository.cs305
-rw-r--r--Emby.Server.Implementations/Dto/DtoService.cs19
-rw-r--r--Emby.Server.Implementations/Emby.Server.Implementations.csproj31
-rw-r--r--Emby.Server.Implementations/EntryPoints/LibraryChangedNotifier.cs18
-rw-r--r--Emby.Server.Implementations/EntryPoints/UserDataChangeNotifier.cs13
-rw-r--r--Emby.Server.Implementations/IStartupOptions.cs10
-rw-r--r--Emby.Server.Implementations/Images/BaseFolderImageProvider.cs2
-rw-r--r--Emby.Server.Implementations/Images/FolderImageProvider.cs2
-rw-r--r--Emby.Server.Implementations/Images/GenreImageProvider.cs2
-rw-r--r--Emby.Server.Implementations/Library/LibraryManager.cs41
-rw-r--r--Emby.Server.Implementations/Library/Resolvers/Audio/AudioResolver.cs6
-rw-r--r--Emby.Server.Implementations/Library/Resolvers/Audio/MusicAlbumResolver.cs7
-rw-r--r--Emby.Server.Implementations/Library/Resolvers/Audio/MusicArtistResolver.cs15
-rw-r--r--Emby.Server.Implementations/Library/Resolvers/BaseVideoResolver.cs23
-rw-r--r--Emby.Server.Implementations/Library/Resolvers/ExtraResolver.cs8
-rw-r--r--Emby.Server.Implementations/Library/Resolvers/GenericVideoResolver.cs6
-rw-r--r--Emby.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs22
-rw-r--r--Emby.Server.Implementations/Library/Resolvers/PhotoResolver.cs18
-rw-r--r--Emby.Server.Implementations/Library/Resolvers/TV/EpisodeResolver.cs6
-rw-r--r--Emby.Server.Implementations/Library/UserViewManager.cs6
-rw-r--r--Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs12
-rw-r--r--Emby.Server.Implementations/LiveTv/Listings/XmlTvListingsProvider.cs23
-rw-r--r--Emby.Server.Implementations/LiveTv/LiveTvManager.cs32
-rw-r--r--Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs4
-rw-r--r--Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/LegacyHdHomerunChannelCommands.cs3
-rw-r--r--Emby.Server.Implementations/LiveTv/TunerHosts/M3uParser.cs46
-rw-r--r--Emby.Server.Implementations/Localization/Core/be.json127
-rw-r--r--Emby.Server.Implementations/Localization/Core/bg-BG.json3
-rw-r--r--Emby.Server.Implementations/Localization/Core/es-AR.json4
-rw-r--r--Emby.Server.Implementations/Localization/Core/es.json2
-rw-r--r--Emby.Server.Implementations/Localization/Core/es_419.json3
-rw-r--r--Emby.Server.Implementations/Localization/Core/fa.json3
-rw-r--r--Emby.Server.Implementations/Localization/Core/gsw.json12
-rw-r--r--Emby.Server.Implementations/Localization/Core/hi.json8
-rw-r--r--Emby.Server.Implementations/Localization/Core/id.json2
-rw-r--r--Emby.Server.Implementations/Localization/Core/is.json11
-rw-r--r--Emby.Server.Implementations/Localization/Core/nl.json16
-rw-r--r--Emby.Server.Implementations/Localization/Core/pr.json13
-rw-r--r--Emby.Server.Implementations/Localization/Core/pt.json3
-rw-r--r--Emby.Server.Implementations/Localization/Core/ru.json6
-rw-r--r--Emby.Server.Implementations/Localization/Core/sl-SI.json3
-rw-r--r--Emby.Server.Implementations/Localization/Core/sv.json2
-rw-r--r--Emby.Server.Implementations/Localization/Core/uk.json2
-rw-r--r--Emby.Server.Implementations/Localization/Core/zh-HK.json12
-rw-r--r--Emby.Server.Implementations/Localization/LocalizationManager.cs144
-rw-r--r--Emby.Server.Implementations/Localization/Ratings/0-prefer.csv11
-rw-r--r--Emby.Server.Implementations/Localization/Ratings/au.csv20
-rw-r--r--Emby.Server.Implementations/Localization/Ratings/be.csv17
-rw-r--r--Emby.Server.Implementations/Localization/Ratings/br.csv14
-rw-r--r--Emby.Server.Implementations/Localization/Ratings/ca.csv26
-rw-r--r--Emby.Server.Implementations/Localization/Ratings/co.csv15
-rw-r--r--Emby.Server.Implementations/Localization/Ratings/de.csv22
-rw-r--r--Emby.Server.Implementations/Localization/Ratings/dk.csv11
-rw-r--r--Emby.Server.Implementations/Localization/Ratings/es.csv30
-rw-r--r--Emby.Server.Implementations/Localization/Ratings/fi.csv20
-rw-r--r--Emby.Server.Implementations/Localization/Ratings/fr.csv17
-rw-r--r--Emby.Server.Implementations/Localization/Ratings/gb.csv29
-rw-r--r--Emby.Server.Implementations/Localization/Ratings/ie.csv15
-rw-r--r--Emby.Server.Implementations/Localization/Ratings/jp.csv15
-rw-r--r--Emby.Server.Implementations/Localization/Ratings/kz.csv13
-rw-r--r--Emby.Server.Implementations/Localization/Ratings/mx.csv12
-rw-r--r--Emby.Server.Implementations/Localization/Ratings/nl.csv14
-rw-r--r--Emby.Server.Implementations/Localization/Ratings/no.csv15
-rw-r--r--Emby.Server.Implementations/Localization/Ratings/nz.csv26
-rw-r--r--Emby.Server.Implementations/Localization/Ratings/ro.csv7
-rw-r--r--Emby.Server.Implementations/Localization/Ratings/ru.csv11
-rw-r--r--Emby.Server.Implementations/Localization/Ratings/se.csv15
-rw-r--r--Emby.Server.Implementations/Localization/Ratings/uk.csv29
-rw-r--r--Emby.Server.Implementations/Localization/Ratings/us.csv73
-rw-r--r--Emby.Server.Implementations/Plugins/PluginLoadContext.cs33
-rw-r--r--Emby.Server.Implementations/Plugins/PluginManager.cs63
-rw-r--r--Emby.Server.Implementations/ScheduledTasks/ScheduledTaskWorker.cs9
-rw-r--r--Emby.Server.Implementations/ScheduledTasks/TaskManager.cs13
-rw-r--r--Emby.Server.Implementations/ScheduledTasks/Tasks/OptimizeDatabaseTask.cs4
-rw-r--r--Emby.Server.Implementations/Session/SessionWebSocketListener.cs13
-rw-r--r--Emby.Server.Implementations/Sorting/RuntimeComparer.cs5
-rw-r--r--Emby.Server.Implementations/Sorting/SeriesSortNameComparer.cs7
-rw-r--r--Emby.Server.Implementations/Sorting/SortNameComparer.cs5
-rw-r--r--Emby.Server.Implementations/Sorting/StartDateComparer.cs6
-rw-r--r--Emby.Server.Implementations/Sorting/StudioComparer.cs5
-rw-r--r--Emby.Server.Implementations/TV/TVSeriesManager.cs12
86 files changed, 1124 insertions, 811 deletions
diff --git a/Emby.Server.Implementations/AppBase/BaseConfigurationManager.cs b/Emby.Server.Implementations/AppBase/BaseConfigurationManager.cs
index 985a127d5..a4deeddb7 100644
--- a/Emby.Server.Implementations/AppBase/BaseConfigurationManager.cs
+++ b/Emby.Server.Implementations/AppBase/BaseConfigurationManager.cs
@@ -1,5 +1,3 @@
-#nullable disable
-
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
@@ -34,14 +32,9 @@ namespace Emby.Server.Implementations.AppBase
private IConfigurationFactory[] _configurationFactories = Array.Empty<IConfigurationFactory>();
/// <summary>
- /// The _configuration loaded.
- /// </summary>
- private bool _configurationLoaded;
-
- /// <summary>
/// The _configuration.
/// </summary>
- private BaseApplicationConfiguration _configuration;
+ private BaseApplicationConfiguration? _configuration;
/// <summary>
/// Initializes a new instance of the <see cref="BaseConfigurationManager" /> class.
@@ -63,17 +56,17 @@ namespace Emby.Server.Implementations.AppBase
/// <summary>
/// Occurs when [configuration updated].
/// </summary>
- public event EventHandler<EventArgs> ConfigurationUpdated;
+ public event EventHandler<EventArgs>? ConfigurationUpdated;
/// <summary>
/// Occurs when [configuration updating].
/// </summary>
- public event EventHandler<ConfigurationUpdateEventArgs> NamedConfigurationUpdating;
+ public event EventHandler<ConfigurationUpdateEventArgs>? NamedConfigurationUpdating;
/// <summary>
/// Occurs when [named configuration updated].
/// </summary>
- public event EventHandler<ConfigurationUpdateEventArgs> NamedConfigurationUpdated;
+ public event EventHandler<ConfigurationUpdateEventArgs>? NamedConfigurationUpdated;
/// <summary>
/// Gets the type of the configuration.
@@ -107,31 +100,25 @@ namespace Emby.Server.Implementations.AppBase
{
get
{
- if (_configurationLoaded)
+ if (_configuration is not null)
{
return _configuration;
}
lock (_configurationSyncLock)
{
- if (_configurationLoaded)
+ if (_configuration is not null)
{
return _configuration;
}
- _configuration = (BaseApplicationConfiguration)ConfigurationHelper.GetXmlConfiguration(ConfigurationType, CommonApplicationPaths.SystemConfigurationFilePath, XmlSerializer);
-
- _configurationLoaded = true;
-
- return _configuration;
+ return _configuration = (BaseApplicationConfiguration)ConfigurationHelper.GetXmlConfiguration(ConfigurationType, CommonApplicationPaths.SystemConfigurationFilePath, XmlSerializer);
}
}
protected set
{
_configuration = value;
-
- _configurationLoaded = value is not null;
}
}
@@ -183,7 +170,7 @@ namespace Emby.Server.Implementations.AppBase
Logger.LogInformation("Saving system configuration");
var path = CommonApplicationPaths.SystemConfigurationFilePath;
- Directory.CreateDirectory(Path.GetDirectoryName(path));
+ Directory.CreateDirectory(Path.GetDirectoryName(path) ?? throw new InvalidOperationException("Path can't be a root directory."));
lock (_configurationSyncLock)
{
@@ -323,25 +310,20 @@ namespace Emby.Server.Implementations.AppBase
private object LoadConfiguration(string path, Type configurationType)
{
- if (!File.Exists(path))
- {
- return Activator.CreateInstance(configurationType);
- }
-
try
{
- return XmlSerializer.DeserializeFromFile(configurationType, path);
- }
- catch (IOException)
- {
- return Activator.CreateInstance(configurationType);
+ if (File.Exists(path))
+ {
+ return XmlSerializer.DeserializeFromFile(configurationType, path);
+ }
}
- catch (Exception ex)
+ catch (Exception ex) when (ex is not IOException)
{
Logger.LogError(ex, "Error loading configuration file: {Path}", path);
-
- return Activator.CreateInstance(configurationType);
}
+
+ return Activator.CreateInstance(configurationType)
+ ?? throw new InvalidOperationException("Configuration type can't be Nullable<T>.");
}
/// <inheritdoc />
@@ -367,7 +349,7 @@ namespace Emby.Server.Implementations.AppBase
_configurations.AddOrUpdate(key, configuration, (_, _) => configuration);
var path = GetConfigurationFile(key);
- Directory.CreateDirectory(Path.GetDirectoryName(path));
+ Directory.CreateDirectory(Path.GetDirectoryName(path) ?? throw new InvalidOperationException("Path can't be a root directory."));
lock (_configurationSyncLock)
{
diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs
index 7b3d07dfc..080c44829 100644
--- a/Emby.Server.Implementations/ApplicationHost.cs
+++ b/Emby.Server.Implementations/ApplicationHost.cs
@@ -11,7 +11,6 @@ using System.IO;
using System.Linq;
using System.Net;
using System.Reflection;
-using System.Runtime.InteropServices;
using System.Security.Cryptography.X509Certificates;
using System.Threading;
using System.Threading.Tasks;
@@ -19,7 +18,6 @@ using Emby.Dlna;
using Emby.Dlna.Main;
using Emby.Dlna.Ssdp;
using Emby.Naming.Common;
-using Emby.Notifications;
using Emby.Photos;
using Emby.Server.Implementations.Channels;
using Emby.Server.Implementations.Collections;
@@ -70,7 +68,6 @@ using MediaBrowser.Controller.LiveTv;
using MediaBrowser.Controller.Lyrics;
using MediaBrowser.Controller.MediaEncoding;
using MediaBrowser.Controller.Net;
-using MediaBrowser.Controller.Notifications;
using MediaBrowser.Controller.Persistence;
using MediaBrowser.Controller.Playlists;
using MediaBrowser.Controller.Plugins;
@@ -118,14 +115,10 @@ namespace Emby.Server.Implementations
public abstract class ApplicationHost : IServerApplicationHost, IAsyncDisposable, IDisposable
{
/// <summary>
- /// The environment variable prefixes to log at server startup.
- /// </summary>
- private static readonly string[] _relevantEnvVarPrefixes = { "JELLYFIN_", "DOTNET_", "ASPNETCORE_" };
-
- /// <summary>
/// The disposable parts.
/// </summary>
private readonly ConcurrentDictionary<IDisposable, byte> _disposableParts = new();
+ private readonly DeviceId _deviceId;
private readonly IFileSystem _fileSystemManager;
private readonly IConfiguration _startupConfig;
@@ -134,7 +127,6 @@ namespace Emby.Server.Implementations
private readonly IPluginManager _pluginManager;
private List<Type> _creatingInstances;
- private IMediaEncoder _mediaEncoder;
private ISessionManager _sessionManager;
/// <summary>
@@ -143,8 +135,6 @@ namespace Emby.Server.Implementations
/// <value>All concrete types.</value>
private Type[] _allConcreteTypes;
- private DeviceId _deviceId;
-
private bool _disposed = false;
/// <summary>
@@ -168,6 +158,7 @@ namespace Emby.Server.Implementations
Logger = LoggerFactory.CreateLogger<ApplicationHost>();
_fileSystemManager.AddShortcutHandler(new MbLinkShortcutHandler(_fileSystemManager));
+ _deviceId = new DeviceId(ApplicationPaths, LoggerFactory);
ApplicationVersion = typeof(ApplicationHost).Assembly.GetName().Version;
ApplicationVersionString = ApplicationVersion.ToString(3);
@@ -193,30 +184,11 @@ namespace Emby.Server.Implementations
/// </summary>
private string PublishedServerUrl => _startupConfig[AddressOverrideKey];
- /// <summary>
- /// Gets a value indicating whether this instance can self restart.
- /// </summary>
- public bool CanSelfRestart => _startupOptions.RestartPath is not null;
-
public bool CoreStartupHasCompleted { get; private set; }
- public virtual bool CanLaunchWebBrowser
- {
- get
- {
- if (!Environment.UserInteractive)
- {
- return false;
- }
-
- if (_startupOptions.IsService)
- {
- return false;
- }
-
- return OperatingSystem.IsWindows() || OperatingSystem.IsMacOS();
- }
- }
+ public virtual bool CanLaunchWebBrowser => Environment.UserInteractive
+ && !_startupOptions.IsService
+ && (OperatingSystem.IsWindows() || OperatingSystem.IsMacOS());
/// <summary>
/// Gets the <see cref="INetworkManager"/> singleton instance.
@@ -293,15 +265,7 @@ namespace Emby.Server.Implementations
/// <value>The application name.</value>
public string ApplicationProductName { get; } = FileVersionInfo.GetVersionInfo(Assembly.GetEntryAssembly().Location).ProductName;
- public string SystemId
- {
- get
- {
- _deviceId ??= new DeviceId(ApplicationPaths, LoggerFactory);
-
- return _deviceId.Value;
- }
- }
+ public string SystemId => _deviceId.Value;
/// <inheritdoc/>
public string Name => ApplicationProductName;
@@ -454,7 +418,7 @@ namespace Emby.Server.Implementations
ConfigurationManager.ConfigurationUpdated += OnConfigurationUpdated;
ConfigurationManager.NamedConfigurationUpdated += OnConfigurationUpdated;
- _mediaEncoder.SetFFmpegPath();
+ Resolve<IMediaEncoder>().SetFFmpegPath();
Logger.LogInformation("ServerId: {ServerId}", SystemId);
@@ -624,8 +588,6 @@ namespace Emby.Server.Implementations
serviceCollection.AddSingleton<IUserViewManager, UserViewManager>();
- serviceCollection.AddSingleton<INotificationManager, NotificationManager>();
-
serviceCollection.AddSingleton<IDeviceDiscovery, DeviceDiscovery>();
serviceCollection.AddSingleton<IChapterManager, ChapterManager>();
@@ -654,7 +616,7 @@ namespace Emby.Server.Implementations
/// <returns>A task representing the service initialization operation.</returns>
public async Task InitializeServices()
{
- var jellyfinDb = await Resolve<IDbContextFactory<JellyfinDb>>().CreateDbContextAsync().ConfigureAwait(false);
+ var jellyfinDb = await Resolve<IDbContextFactory<JellyfinDbContext>>().CreateDbContextAsync().ConfigureAwait(false);
await using (jellyfinDb.ConfigureAwait(false))
{
if ((await jellyfinDb.Database.GetPendingMigrationsAsync().ConfigureAwait(false)).Any())
@@ -668,7 +630,6 @@ namespace Emby.Server.Implementations
var localizationManager = (LocalizationManager)Resolve<ILocalizationManager>();
await localizationManager.LoadAll().ConfigureAwait(false);
- _mediaEncoder = Resolve<IMediaEncoder>();
_sessionManager = Resolve<ISessionManager>();
SetStaticProperties();
@@ -679,36 +640,6 @@ namespace Emby.Server.Implementations
FindParts();
}
- public static void LogEnvironmentInfo(ILogger logger, IApplicationPaths appPaths)
- {
- // Distinct these to prevent users from reporting problems that aren't actually problems
- var commandLineArgs = Environment
- .GetCommandLineArgs()
- .Distinct();
-
- // Get all relevant environment variables
- var allEnvVars = Environment.GetEnvironmentVariables();
- var relevantEnvVars = new Dictionary<object, object>();
- foreach (var key in allEnvVars.Keys)
- {
- if (_relevantEnvVarPrefixes.Any(prefix => key.ToString().StartsWith(prefix, StringComparison.OrdinalIgnoreCase)))
- {
- relevantEnvVars.Add(key, allEnvVars[key]);
- }
- }
-
- logger.LogInformation("Environment Variables: {EnvVars}", relevantEnvVars);
- logger.LogInformation("Arguments: {Args}", commandLineArgs);
- logger.LogInformation("Operating system: {OS}", MediaBrowser.Common.System.OperatingSystem.Name);
- logger.LogInformation("Architecture: {Architecture}", RuntimeInformation.OSArchitecture);
- logger.LogInformation("64-Bit Process: {Is64Bit}", Environment.Is64BitProcess);
- logger.LogInformation("User Interactive: {IsUserInteractive}", Environment.UserInteractive);
- logger.LogInformation("Processor count: {ProcessorCount}", Environment.ProcessorCount);
- logger.LogInformation("Program data path: {ProgramDataPath}", appPaths.ProgramDataPath);
- logger.LogInformation("Web resources path: {WebPath}", appPaths.WebPath);
- logger.LogInformation("Application directory: {ApplicationPath}", appPaths.ProgramSystemPath);
- }
-
private X509Certificate2 GetCertificate(string path, string password)
{
if (string.IsNullOrWhiteSpace(path))
@@ -795,13 +726,7 @@ namespace Emby.Server.Implementations
Resolve<ILiveTvManager>().AddParts(GetExports<ILiveTvService>(), GetExports<ITunerHost>(), GetExports<IListingsProvider>());
- Resolve<ISubtitleManager>().AddParts(GetExports<ISubtitleProvider>());
-
- Resolve<IChannelManager>().AddParts(GetExports<IChannel>());
-
Resolve<IMediaSourceManager>().AddParts(GetExports<IMediaSourceProvider>());
-
- Resolve<INotificationManager>().AddParts(GetExports<INotificationService>(), GetExports<INotificationTypeFactory>());
}
/// <summary>
@@ -935,17 +860,13 @@ namespace Emby.Server.Implementations
/// </summary>
public void Restart()
{
- if (!CanSelfRestart)
- {
- throw new PlatformNotSupportedException("The server is unable to self-restart. Please restart manually.");
- }
-
if (IsShuttingDown)
{
return;
}
IsShuttingDown = true;
+ _pluginManager.UnloadAssemblies();
Task.Run(async () =>
{
@@ -1004,9 +925,6 @@ namespace Emby.Server.Implementations
// Local metadata
yield return typeof(BoxSetXmlSaver).Assembly;
- // Notifications
- yield return typeof(NotificationManager).Assembly;
-
// Xbmc
yield return typeof(ArtistNfoProvider).Assembly;
@@ -1045,15 +963,11 @@ namespace Emby.Server.Implementations
ItemsByNamePath = ApplicationPaths.InternalMetadataPath,
InternalMetadataPath = ApplicationPaths.InternalMetadataPath,
CachePath = ApplicationPaths.CachePath,
- OperatingSystem = MediaBrowser.Common.System.OperatingSystem.Id.ToString(),
- OperatingSystemDisplayName = MediaBrowser.Common.System.OperatingSystem.Name,
- CanSelfRestart = CanSelfRestart,
CanLaunchWebBrowser = CanLaunchWebBrowser,
TranscodingTempPath = ConfigurationManager.GetTranscodePath(),
ServerName = FriendlyName,
LocalAddress = GetSmartApiUrl(request),
SupportsLibraryMonitor = true,
- SystemArchitecture = RuntimeInformation.OSArchitecture,
PackageName = _startupOptions.PackageName
};
}
@@ -1065,7 +979,6 @@ namespace Emby.Server.Implementations
Version = ApplicationVersionString,
ProductName = ApplicationProductName,
Id = SystemId,
- OperatingSystem = MediaBrowser.Common.System.OperatingSystem.Id.ToString(),
ServerName = FriendlyName,
LocalAddress = GetSmartApiUrl(request),
StartupWizardCompleted = ConfigurationManager.CommonConfiguration.IsStartupWizardCompleted
@@ -1275,10 +1188,13 @@ namespace Emby.Server.Implementations
}
}
- // used for closing websockets
- foreach (var session in _sessionManager.Sessions)
+ if (_sessionManager != null)
{
- await session.DisposeAsync().ConfigureAwait(false);
+ // used for closing websockets
+ foreach (var session in _sessionManager.Sessions)
+ {
+ await session.DisposeAsync().ConfigureAwait(false);
+ }
}
}
}
diff --git a/Emby.Server.Implementations/Channels/ChannelManager.cs b/Emby.Server.Implementations/Channels/ChannelManager.cs
index 85ccbc028..961e225e9 100644
--- a/Emby.Server.Implementations/Channels/ChannelManager.cs
+++ b/Emby.Server.Implementations/Channels/ChannelManager.cs
@@ -66,6 +66,7 @@ namespace Emby.Server.Implementations.Channels
/// <param name="userDataManager">The user data manager.</param>
/// <param name="providerManager">The provider manager.</param>
/// <param name="memoryCache">The memory cache.</param>
+ /// <param name="channels">The channels.</param>
public ChannelManager(
IUserManager userManager,
IDtoService dtoService,
@@ -75,7 +76,8 @@ namespace Emby.Server.Implementations.Channels
IFileSystem fileSystem,
IUserDataManager userDataManager,
IProviderManager providerManager,
- IMemoryCache memoryCache)
+ IMemoryCache memoryCache,
+ IEnumerable<IChannel> channels)
{
_userManager = userManager;
_dtoService = dtoService;
@@ -86,19 +88,14 @@ namespace Emby.Server.Implementations.Channels
_userDataManager = userDataManager;
_providerManager = providerManager;
_memoryCache = memoryCache;
+ Channels = channels.ToArray();
}
- internal IChannel[] Channels { get; private set; }
+ internal IChannel[] Channels { get; }
private static TimeSpan CacheLength => TimeSpan.FromHours(3);
/// <inheritdoc />
- public void AddParts(IEnumerable<IChannel> channels)
- {
- Channels = channels.ToArray();
- }
-
- /// <inheritdoc />
public bool EnableMediaSourceDisplay(BaseItem item)
{
var internalChannel = _libraryManager.GetItemById(item.ChannelId);
@@ -160,16 +157,16 @@ namespace Emby.Server.Implementations.Channels
}
/// <inheritdoc />
- public QueryResult<Channel> GetChannelsInternal(ChannelQuery query)
+ public async Task<QueryResult<Channel>> GetChannelsInternalAsync(ChannelQuery query)
{
var user = query.UserId.Equals(default)
? null
: _userManager.GetUserById(query.UserId);
- var channels = GetAllChannels()
- .Select(GetChannelEntity)
+ var channels = await GetAllChannelEntitiesAsync()
.OrderBy(i => i.SortName)
- .ToList();
+ .ToListAsync()
+ .ConfigureAwait(false);
if (query.IsRecordingsFolder.HasValue)
{
@@ -229,6 +226,7 @@ namespace Emby.Server.Implementations.Channels
if (user is not null)
{
+ var userId = user.Id.ToString("N", CultureInfo.InvariantCulture);
channels = channels.Where(i =>
{
if (!i.IsVisible(user))
@@ -238,7 +236,7 @@ namespace Emby.Server.Implementations.Channels
try
{
- return GetChannelProvider(i).IsEnabledFor(user.Id.ToString("N", CultureInfo.InvariantCulture));
+ return GetChannelProvider(i).IsEnabledFor(userId);
}
catch
{
@@ -261,7 +259,7 @@ namespace Emby.Server.Implementations.Channels
{
foreach (var item in all)
{
- RefreshLatestChannelItems(GetChannelProvider(item), CancellationToken.None).GetAwaiter().GetResult();
+ await RefreshLatestChannelItems(GetChannelProvider(item), CancellationToken.None).ConfigureAwait(false);
}
}
@@ -272,13 +270,13 @@ namespace Emby.Server.Implementations.Channels
}
/// <inheritdoc />
- public QueryResult<BaseItemDto> GetChannels(ChannelQuery query)
+ public async Task<QueryResult<BaseItemDto>> GetChannelsAsync(ChannelQuery query)
{
var user = query.UserId.Equals(default)
? null
: _userManager.GetUserById(query.UserId);
- var internalResult = GetChannelsInternal(query);
+ var internalResult = await GetChannelsInternalAsync(query).ConfigureAwait(false);
var dtoOptions = new DtoOptions();
@@ -330,9 +328,12 @@ namespace Emby.Server.Implementations.Channels
progress.Report(100);
}
- private Channel GetChannelEntity(IChannel channel)
+ private async IAsyncEnumerable<Channel> GetAllChannelEntitiesAsync()
{
- return GetChannel(GetInternalChannelId(channel.Name)) ?? GetChannel(channel, CancellationToken.None).GetAwaiter().GetResult();
+ foreach (IChannel channel in GetAllChannels())
+ {
+ yield return GetChannel(GetInternalChannelId(channel.Name)) ?? await GetChannel(channel, CancellationToken.None).ConfigureAwait(false);
+ }
}
private MediaSourceInfo[] GetSavedMediaSources(BaseItem item)
@@ -404,7 +405,7 @@ namespace Emby.Server.Implementations.Channels
}
else
{
- results = new List<MediaSourceInfo>();
+ results = Enumerable.Empty<MediaSourceInfo>();
}
return results
diff --git a/Emby.Server.Implementations/Collections/CollectionManager.cs b/Emby.Server.Implementations/Collections/CollectionManager.cs
index b53c8ca51..179683055 100644
--- a/Emby.Server.Implementations/Collections/CollectionManager.cs
+++ b/Emby.Server.Implementations/Collections/CollectionManager.cs
@@ -206,8 +206,7 @@ namespace Emby.Server.Implementations.Collections
throw new ArgumentException("No collection exists with the supplied Id");
}
- var list = new List<LinkedChild>();
- var itemList = new List<BaseItem>();
+ List<BaseItem>? itemList = null;
var linkedChildrenList = collection.GetLinkedChildren();
var currentLinkedChildrenIds = linkedChildrenList.Select(i => i.Id).ToList();
@@ -223,18 +222,23 @@ namespace Emby.Server.Implementations.Collections
if (!currentLinkedChildrenIds.Contains(id))
{
- itemList.Add(item);
+ (itemList ??= new()).Add(item);
- list.Add(LinkedChild.Create(item));
linkedChildrenList.Add(item);
}
}
- if (list.Count > 0)
+ if (itemList is not null)
{
- LinkedChild[] newChildren = new LinkedChild[collection.LinkedChildren.Length + list.Count];
+ var originalLen = collection.LinkedChildren.Length;
+ var newItemCount = itemList.Count;
+ LinkedChild[] newChildren = new LinkedChild[originalLen + newItemCount];
collection.LinkedChildren.CopyTo(newChildren, 0);
- list.CopyTo(newChildren, collection.LinkedChildren.Length);
+ for (int i = 0; i < newItemCount; i++)
+ {
+ newChildren[originalLen + i] = LinkedChild.Create(itemList[i]);
+ }
+
collection.LinkedChildren = newChildren;
collection.UpdateRatingToItems(linkedChildrenList);
diff --git a/Emby.Server.Implementations/Configuration/ServerConfigurationManager.cs b/Emby.Server.Implementations/Configuration/ServerConfigurationManager.cs
index ff5602f24..6b8b1a620 100644
--- a/Emby.Server.Implementations/Configuration/ServerConfigurationManager.cs
+++ b/Emby.Server.Implementations/Configuration/ServerConfigurationManager.cs
@@ -1,5 +1,3 @@
-#nullable disable
-
using System;
using System.Globalization;
using System.IO;
@@ -36,7 +34,7 @@ namespace Emby.Server.Implementations.Configuration
/// <summary>
/// Configuration updating event.
/// </summary>
- public event EventHandler<GenericEventArgs<ServerConfiguration>> ConfigurationUpdating;
+ public event EventHandler<GenericEventArgs<ServerConfiguration>>? ConfigurationUpdating;
/// <summary>
/// Gets the type of the configuration.
diff --git a/Emby.Server.Implementations/Data/SqliteItemRepository.cs b/Emby.Server.Implementations/Data/SqliteItemRepository.cs
index bc703fe90..3bf4d07c5 100644
--- a/Emby.Server.Implementations/Data/SqliteItemRepository.cs
+++ b/Emby.Server.Implementations/Data/SqliteItemRepository.cs
@@ -586,7 +586,7 @@ namespace Emby.Server.Implementations.Data
/// <exception cref="ArgumentNullException">
/// <paramref name="items"/> or <paramref name="cancellationToken"/> is <c>null</c>.
/// </exception>
- public void SaveItems(IEnumerable<BaseItem> items, CancellationToken cancellationToken)
+ public void SaveItems(IReadOnlyList<BaseItem> items, CancellationToken cancellationToken)
{
ArgumentNullException.ThrowIfNull(items);
@@ -594,9 +594,11 @@ namespace Emby.Server.Implementations.Data
CheckDisposed();
- var tuples = new List<(BaseItem, List<Guid>, BaseItem, string, List<string>)>();
- foreach (var item in items)
+ var itemsLen = items.Count;
+ var tuples = new ValueTuple<BaseItem, List<Guid>, BaseItem, string, List<string>>[itemsLen];
+ for (int i = 0; i < itemsLen; i++)
{
+ var item = items[i];
var ancestorIds = item.SupportsAncestors ?
item.GetAncestorIds().Distinct().ToList() :
null;
@@ -606,7 +608,7 @@ namespace Emby.Server.Implementations.Data
var userdataKey = item.GetUserDataKeys().FirstOrDefault();
var inheritedTags = item.GetInheritedTags();
- tuples.Add((item, ancestorIds, topParent, userdataKey, inheritedTags));
+ tuples[i] = (item, ancestorIds, topParent, userdataKey, inheritedTags);
}
using (var connection = GetConnection())
@@ -1195,7 +1197,7 @@ namespace Emby.Server.Implementations.Data
Path = RestorePath(path.ToString())
};
- if (long.TryParse(dateModified, NumberStyles.Any, CultureInfo.InvariantCulture, out var ticks)
+ if (long.TryParse(dateModified, CultureInfo.InvariantCulture, out var ticks)
&& ticks >= DateTime.MinValue.Ticks
&& ticks <= DateTime.MaxValue.Ticks)
{
@@ -3202,7 +3204,8 @@ namespace Emby.Server.Implementations.Data
return IsAlphaNumeric(value);
}
- private List<string> GetWhereClauses(InternalItemsQuery query, IStatement statement)
+#nullable enable
+ private List<string> GetWhereClauses(InternalItemsQuery query, IStatement? statement)
{
if (query.IsResumable ?? false)
{
@@ -3677,7 +3680,6 @@ namespace Emby.Server.Implementations.Data
if (statement is not null)
{
nameContains = FixUnicodeChars(nameContains);
-
statement.TryBind("@NameContains", "%" + GetCleanValue(nameContains) + "%");
}
}
@@ -3803,13 +3805,8 @@ namespace Emby.Server.Implementations.Data
foreach (var artistId in query.ArtistIds)
{
var paramName = "@ArtistIds" + index;
-
clauses.Add("(guid in (select itemid from ItemValues where CleanValue = (select CleanName from TypedBaseItems where guid=" + paramName + ") and Type<=1))");
- if (statement is not null)
- {
- statement.TryBind(paramName, artistId);
- }
-
+ statement?.TryBind(paramName, artistId);
index++;
}
@@ -3824,13 +3821,8 @@ namespace Emby.Server.Implementations.Data
foreach (var artistId in query.AlbumArtistIds)
{
var paramName = "@ArtistIds" + index;
-
clauses.Add("(guid in (select itemid from ItemValues where CleanValue = (select CleanName from TypedBaseItems where guid=" + paramName + ") and Type=1))");
- if (statement is not null)
- {
- statement.TryBind(paramName, artistId);
- }
-
+ statement?.TryBind(paramName, artistId);
index++;
}
@@ -3845,13 +3837,8 @@ namespace Emby.Server.Implementations.Data
foreach (var artistId in query.ContributingArtistIds)
{
var paramName = "@ArtistIds" + index;
-
clauses.Add("((select CleanName from TypedBaseItems where guid=" + paramName + ") in (select CleanValue from ItemValues where ItemId=Guid and Type=0) AND (select CleanName from TypedBaseItems where guid=" + paramName + ") not in (select CleanValue from ItemValues where ItemId=Guid and Type=1))");
- if (statement is not null)
- {
- statement.TryBind(paramName, artistId);
- }
-
+ statement?.TryBind(paramName, artistId);
index++;
}
@@ -3866,13 +3853,8 @@ namespace Emby.Server.Implementations.Data
foreach (var albumId in query.AlbumIds)
{
var paramName = "@AlbumIds" + index;
-
clauses.Add("Album in (select Name from typedbaseitems where guid=" + paramName + ")");
- if (statement is not null)
- {
- statement.TryBind(paramName, albumId);
- }
-
+ statement?.TryBind(paramName, albumId);
index++;
}
@@ -3887,13 +3869,8 @@ namespace Emby.Server.Implementations.Data
foreach (var artistId in query.ExcludeArtistIds)
{
var paramName = "@ExcludeArtistId" + index;
-
clauses.Add("(guid not in (select itemid from ItemValues where CleanValue = (select CleanName from TypedBaseItems where guid=" + paramName + ") and Type<=1))");
- if (statement is not null)
- {
- statement.TryBind(paramName, artistId);
- }
-
+ statement?.TryBind(paramName, artistId);
index++;
}
@@ -3908,13 +3885,8 @@ namespace Emby.Server.Implementations.Data
foreach (var genreId in query.GenreIds)
{
var paramName = "@GenreId" + index;
-
clauses.Add("(guid in (select itemid from ItemValues where CleanValue = (select CleanName from TypedBaseItems where guid=" + paramName + ") and Type=2))");
- if (statement is not null)
- {
- statement.TryBind(paramName, genreId);
- }
-
+ statement?.TryBind(paramName, genreId);
index++;
}
@@ -3929,11 +3901,7 @@ namespace Emby.Server.Implementations.Data
foreach (var item in query.Genres)
{
clauses.Add("@Genre" + index + " in (select CleanValue from ItemValues where ItemId=Guid and Type=2)");
- if (statement is not null)
- {
- statement.TryBind("@Genre" + index, GetCleanValue(item));
- }
-
+ statement?.TryBind("@Genre" + index, GetCleanValue(item));
index++;
}
@@ -3948,11 +3916,7 @@ namespace Emby.Server.Implementations.Data
foreach (var item in tags)
{
clauses.Add("@Tag" + index + " in (select CleanValue from ItemValues where ItemId=Guid and Type=4)");
- if (statement is not null)
- {
- statement.TryBind("@Tag" + index, GetCleanValue(item));
- }
-
+ statement?.TryBind("@Tag" + index, GetCleanValue(item));
index++;
}
@@ -3967,11 +3931,7 @@ namespace Emby.Server.Implementations.Data
foreach (var item in excludeTags)
{
clauses.Add("@ExcludeTag" + index + " not in (select CleanValue from ItemValues where ItemId=Guid and Type=4)");
- if (statement is not null)
- {
- statement.TryBind("@ExcludeTag" + index, GetCleanValue(item));
- }
-
+ statement?.TryBind("@ExcludeTag" + index, GetCleanValue(item));
index++;
}
@@ -3986,14 +3946,8 @@ namespace Emby.Server.Implementations.Data
foreach (var studioId in query.StudioIds)
{
var paramName = "@StudioId" + index;
-
clauses.Add("(guid in (select itemid from ItemValues where CleanValue = (select CleanName from TypedBaseItems where guid=" + paramName + ") and Type=3))");
-
- if (statement is not null)
- {
- statement.TryBind(paramName, studioId);
- }
-
+ statement?.TryBind(paramName, studioId);
index++;
}
@@ -4008,11 +3962,7 @@ namespace Emby.Server.Implementations.Data
foreach (var item in query.OfficialRatings)
{
clauses.Add("OfficialRating=@OfficialRating" + index);
- if (statement is not null)
- {
- statement.TryBind("@OfficialRating" + index, item);
- }
-
+ statement?.TryBind("@OfficialRating" + index, item);
index++;
}
@@ -4020,35 +3970,97 @@ namespace Emby.Server.Implementations.Data
whereClauses.Add(clause);
}
- if (query.MinParentalRating.HasValue)
+ var ratingClauseBuilder = new StringBuilder("(");
+ if (query.HasParentalRating ?? false)
{
- whereClauses.Add("InheritedParentalRatingValue>=@MinParentalRating");
- if (statement is not null)
+ ratingClauseBuilder.Append("InheritedParentalRatingValue not null");
+ if (query.MinParentalRating.HasValue)
{
- statement.TryBind("@MinParentalRating", query.MinParentalRating.Value);
+ ratingClauseBuilder.Append(" AND InheritedParentalRatingValue >= @MinParentalRating");
+ statement?.TryBind("@MinParentalRating", query.MinParentalRating.Value);
}
- }
- if (query.MaxParentalRating.HasValue)
+ if (query.MaxParentalRating.HasValue)
+ {
+ ratingClauseBuilder.Append(" AND InheritedParentalRatingValue <= @MaxParentalRating");
+ statement?.TryBind("@MaxParentalRating", query.MaxParentalRating.Value);
+ }
+ }
+ else if (query.BlockUnratedItems.Length > 0)
{
- whereClauses.Add("InheritedParentalRatingValue<=@MaxParentalRating");
+ var paramName = "@UnratedType";
+ var index = 0;
+ string blockedUnratedItems = string.Join(',', query.BlockUnratedItems.Select(_ => paramName + index++));
+ ratingClauseBuilder.Append("(InheritedParentalRatingValue is null AND UnratedType not in (" + blockedUnratedItems + "))");
+
if (statement is not null)
{
- statement.TryBind("@MaxParentalRating", query.MaxParentalRating.Value);
+ for (var ind = 0; ind < query.BlockUnratedItems.Length; ind++)
+ {
+ statement.TryBind(paramName + ind, query.BlockUnratedItems[ind].ToString());
+ }
+ }
+
+ if (query.MinParentalRating.HasValue || query.MaxParentalRating.HasValue)
+ {
+ ratingClauseBuilder.Append(" OR (");
}
- }
- if (query.HasParentalRating.HasValue)
- {
- if (query.HasParentalRating.Value)
+ if (query.MinParentalRating.HasValue)
{
- whereClauses.Add("InheritedParentalRatingValue > 0");
+ ratingClauseBuilder.Append("InheritedParentalRatingValue >= @MinParentalRating");
+ statement?.TryBind("@MinParentalRating", query.MinParentalRating.Value);
}
- else
+
+ if (query.MaxParentalRating.HasValue)
{
- whereClauses.Add("InheritedParentalRatingValue = 0");
+ if (query.MinParentalRating.HasValue)
+ {
+ ratingClauseBuilder.Append(" AND ");
+ }
+
+ ratingClauseBuilder.Append("InheritedParentalRatingValue <= @MaxParentalRating");
+ statement?.TryBind("@MaxParentalRating", query.MaxParentalRating.Value);
+ }
+
+ if (query.MinParentalRating.HasValue || query.MaxParentalRating.HasValue)
+ {
+ ratingClauseBuilder.Append(")");
+ }
+
+ if (!(query.MinParentalRating.HasValue || query.MaxParentalRating.HasValue))
+ {
+ ratingClauseBuilder.Append(" OR InheritedParentalRatingValue not null");
}
}
+ else if (query.MinParentalRating.HasValue)
+ {
+ ratingClauseBuilder.Append("InheritedParentalRatingValue is null OR (InheritedParentalRatingValue >= @MinParentalRating");
+ statement?.TryBind("@MinParentalRating", query.MinParentalRating.Value);
+
+ if (query.MaxParentalRating.HasValue)
+ {
+ ratingClauseBuilder.Append(" AND InheritedParentalRatingValue <= @MaxParentalRating");
+ statement?.TryBind("@MaxParentalRating", query.MaxParentalRating.Value);
+ }
+
+ ratingClauseBuilder.Append(")");
+ }
+ else if (query.MaxParentalRating.HasValue)
+ {
+ ratingClauseBuilder.Append("InheritedParentalRatingValue is null OR InheritedParentalRatingValue <= @MaxParentalRating");
+ statement?.TryBind("@MaxParentalRating", query.MaxParentalRating.Value);
+ }
+ else if (!query.HasParentalRating ?? false)
+ {
+ ratingClauseBuilder.Append("InheritedParentalRatingValue is null");
+ }
+
+ var ratingClauseString = ratingClauseBuilder.ToString();
+ if (!string.Equals(ratingClauseString, "(", StringComparison.OrdinalIgnoreCase))
+ {
+ whereClauses.Add(ratingClauseString + ")");
+ }
if (query.HasOfficialRating.HasValue)
{
@@ -4089,37 +4101,25 @@ namespace Emby.Server.Implementations.Data
if (!string.IsNullOrWhiteSpace(query.HasNoAudioTrackWithLanguage))
{
whereClauses.Add("((select language from MediaStreams where MediaStreams.ItemId=A.Guid and MediaStreams.StreamType='Audio' and MediaStreams.Language=@HasNoAudioTrackWithLanguage limit 1) is null)");
- if (statement is not null)
- {
- statement.TryBind("@HasNoAudioTrackWithLanguage", query.HasNoAudioTrackWithLanguage);
- }
+ statement?.TryBind("@HasNoAudioTrackWithLanguage", query.HasNoAudioTrackWithLanguage);
}
if (!string.IsNullOrWhiteSpace(query.HasNoInternalSubtitleTrackWithLanguage))
{
whereClauses.Add("((select language from MediaStreams where MediaStreams.ItemId=A.Guid and MediaStreams.StreamType='Subtitle' and MediaStreams.IsExternal=0 and MediaStreams.Language=@HasNoInternalSubtitleTrackWithLanguage limit 1) is null)");
- if (statement is not null)
- {
- statement.TryBind("@HasNoInternalSubtitleTrackWithLanguage", query.HasNoInternalSubtitleTrackWithLanguage);
- }
+ statement?.TryBind("@HasNoInternalSubtitleTrackWithLanguage", query.HasNoInternalSubtitleTrackWithLanguage);
}
if (!string.IsNullOrWhiteSpace(query.HasNoExternalSubtitleTrackWithLanguage))
{
whereClauses.Add("((select language from MediaStreams where MediaStreams.ItemId=A.Guid and MediaStreams.StreamType='Subtitle' and MediaStreams.IsExternal=1 and MediaStreams.Language=@HasNoExternalSubtitleTrackWithLanguage limit 1) is null)");
- if (statement is not null)
- {
- statement.TryBind("@HasNoExternalSubtitleTrackWithLanguage", query.HasNoExternalSubtitleTrackWithLanguage);
- }
+ statement?.TryBind("@HasNoExternalSubtitleTrackWithLanguage", query.HasNoExternalSubtitleTrackWithLanguage);
}
if (!string.IsNullOrWhiteSpace(query.HasNoSubtitleTrackWithLanguage))
{
whereClauses.Add("((select language from MediaStreams where MediaStreams.ItemId=A.Guid and MediaStreams.StreamType='Subtitle' and MediaStreams.Language=@HasNoSubtitleTrackWithLanguage limit 1) is null)");
- if (statement is not null)
- {
- statement.TryBind("@HasNoSubtitleTrackWithLanguage", query.HasNoSubtitleTrackWithLanguage);
- }
+ statement?.TryBind("@HasNoSubtitleTrackWithLanguage", query.HasNoSubtitleTrackWithLanguage);
}
if (query.HasSubtitles.HasValue)
@@ -4169,15 +4169,11 @@ namespace Emby.Server.Implementations.Data
if (query.Years.Length == 1)
{
whereClauses.Add("ProductionYear=@Years");
- if (statement is not null)
- {
- statement.TryBind("@Years", query.Years[0].ToString(CultureInfo.InvariantCulture));
- }
+ statement?.TryBind("@Years", query.Years[0].ToString(CultureInfo.InvariantCulture));
}
else if (query.Years.Length > 1)
{
var val = string.Join(',', query.Years);
-
whereClauses.Add("ProductionYear in (" + val + ")");
}
@@ -4185,10 +4181,7 @@ namespace Emby.Server.Implementations.Data
if (isVirtualItem.HasValue)
{
whereClauses.Add("IsVirtualItem=@IsVirtualItem");
- if (statement is not null)
- {
- statement.TryBind("@IsVirtualItem", isVirtualItem.Value);
- }
+ statement?.TryBind("@IsVirtualItem", isVirtualItem.Value);
}
if (query.IsSpecialSeason.HasValue)
@@ -4219,31 +4212,22 @@ namespace Emby.Server.Implementations.Data
if (queryMediaTypes.Length == 1)
{
whereClauses.Add("MediaType=@MediaTypes");
- if (statement is not null)
- {
- statement.TryBind("@MediaTypes", queryMediaTypes[0]);
- }
+ statement?.TryBind("@MediaTypes", queryMediaTypes[0]);
}
else if (queryMediaTypes.Length > 1)
{
var val = string.Join(',', queryMediaTypes.Select(i => "'" + i + "'"));
-
whereClauses.Add("MediaType in (" + val + ")");
}
if (query.ItemIds.Length > 0)
{
var includeIds = new List<string>();
-
var index = 0;
foreach (var id in query.ItemIds)
{
includeIds.Add("Guid = @IncludeId" + index);
- if (statement is not null)
- {
- statement.TryBind("@IncludeId" + index, id);
- }
-
+ statement?.TryBind("@IncludeId" + index, id);
index++;
}
@@ -4253,16 +4237,11 @@ namespace Emby.Server.Implementations.Data
if (query.ExcludeItemIds.Length > 0)
{
var excludeIds = new List<string>();
-
var index = 0;
foreach (var id in query.ExcludeItemIds)
{
excludeIds.Add("Guid <> @ExcludeId" + index);
- if (statement is not null)
- {
- statement.TryBind("@ExcludeId" + index, id);
- }
-
+ statement?.TryBind("@ExcludeId" + index, id);
index++;
}
@@ -4283,11 +4262,7 @@ namespace Emby.Server.Implementations.Data
var paramName = "@ExcludeProviderId" + index;
excludeIds.Add("(ProviderIds is null or ProviderIds not like " + paramName + ")");
- if (statement is not null)
- {
- statement.TryBind(paramName, "%" + pair.Key + "=" + pair.Value + "%");
- }
-
+ statement?.TryBind(paramName, "%" + pair.Key + "=" + pair.Value + "%");
index++;
break;
@@ -4312,7 +4287,7 @@ namespace Emby.Server.Implementations.Data
}
// TODO this seems to be an idea for a better schema where ProviderIds are their own table
- // buut this is not implemented
+ // but this is not implemented
// hasProviderIds.Add("(COALESCE((select value from ProviderIds where ItemId=Guid and Name = '" + pair.Key + "'), '') <> " + paramName + ")");
// TODO this is a really BAD way to do it since the pair:
@@ -4326,11 +4301,7 @@ namespace Emby.Server.Implementations.Data
hasProviderIds.Add("ProviderIds like " + paramName);
// this replaces the placeholder with a value, here: %key=val%
- if (statement is not null)
- {
- statement.TryBind(paramName, "%" + pair.Key + "=" + pair.Value + "%");
- }
-
+ statement?.TryBind(paramName, "%" + pair.Key + "=" + pair.Value + "%");
index++;
break;
@@ -4407,11 +4378,7 @@ namespace Emby.Server.Implementations.Data
if (query.AncestorIds.Length == 1)
{
whereClauses.Add("Guid in (select itemId from AncestorIds where AncestorId=@AncestorId)");
-
- if (statement is not null)
- {
- statement.TryBind("@AncestorId", query.AncestorIds[0]);
- }
+ statement?.TryBind("@AncestorId", query.AncestorIds[0]);
}
if (query.AncestorIds.Length > 1)
@@ -4424,39 +4391,13 @@ namespace Emby.Server.Implementations.Data
{
var inClause = "select guid from TypedBaseItems where PresentationUniqueKey=@AncestorWithPresentationUniqueKey";
whereClauses.Add(string.Format(CultureInfo.InvariantCulture, "Guid in (select itemId from AncestorIds where AncestorId in ({0}))", inClause));
- if (statement is not null)
- {
- statement.TryBind("@AncestorWithPresentationUniqueKey", query.AncestorWithPresentationUniqueKey);
- }
+ statement?.TryBind("@AncestorWithPresentationUniqueKey", query.AncestorWithPresentationUniqueKey);
}
if (!string.IsNullOrWhiteSpace(query.SeriesPresentationUniqueKey))
{
whereClauses.Add("SeriesPresentationUniqueKey=@SeriesPresentationUniqueKey");
-
- if (statement is not null)
- {
- statement.TryBind("@SeriesPresentationUniqueKey", query.SeriesPresentationUniqueKey);
- }
- }
-
- if (query.BlockUnratedItems.Length == 1)
- {
- whereClauses.Add("(InheritedParentalRatingValue > 0 or UnratedType <> @UnratedType)");
- if (statement is not null)
- {
- statement.TryBind("@UnratedType", query.BlockUnratedItems[0].ToString());
- }
- }
-
- if (query.BlockUnratedItems.Length > 1)
- {
- var inClause = string.Join(',', query.BlockUnratedItems.Select(i => "'" + i.ToString() + "'"));
- whereClauses.Add(
- string.Format(
- CultureInfo.InvariantCulture,
- "(InheritedParentalRatingValue > 0 or UnratedType not in ({0}))",
- inClause));
+ statement?.TryBind("@SeriesPresentationUniqueKey", query.SeriesPresentationUniqueKey);
}
if (query.ExcludeInheritedTags.Length > 0)
@@ -4477,6 +4418,24 @@ namespace Emby.Server.Implementations.Data
}
}
+ if (query.IncludeInheritedTags.Length > 0)
+ {
+ var paramName = "@IncludeInheritedTags";
+ if (statement is null)
+ {
+ int index = 0;
+ string includedTags = string.Join(',', query.IncludeInheritedTags.Select(_ => paramName + index++));
+ whereClauses.Add("((select CleanValue from ItemValues where ItemId=Guid and Type=6 and cleanvalue in (" + includedTags + ")) is not null)");
+ }
+ else
+ {
+ for (int index = 0; index < query.IncludeInheritedTags.Length; index++)
+ {
+ statement.TryBind(paramName + index, GetCleanValue(query.IncludeInheritedTags[index]));
+ }
+ }
+ }
+
if (query.SeriesStatuses.Length > 0)
{
var statuses = new List<string>();
@@ -4587,6 +4546,7 @@ namespace Emby.Server.Implementations.Data
return whereClauses;
}
+#nullable disable
/// <summary>
/// Formats a where clause for the specified provider.
@@ -5440,6 +5400,9 @@ AND Type = @InternalPersonType)");
list.AddRange(inheritedTags.Select(i => (6, i)));
+ // Remove all invalid values.
+ list.RemoveAll(i => string.IsNullOrEmpty(i.Item2));
+
return list;
}
diff --git a/Emby.Server.Implementations/Dto/DtoService.cs b/Emby.Server.Implementations/Dto/DtoService.cs
index 5103b1fbf..45270de89 100644
--- a/Emby.Server.Implementations/Dto/DtoService.cs
+++ b/Emby.Server.Implementations/Dto/DtoService.cs
@@ -83,22 +83,23 @@ namespace Emby.Server.Implementations.Dto
/// <inheritdoc />
public IReadOnlyList<BaseItemDto> GetBaseItemDtos(IReadOnlyList<BaseItem> items, DtoOptions options, User user = null, BaseItem owner = null)
{
- var returnItems = new BaseItemDto[items.Count];
- var programTuples = new List<(BaseItem, BaseItemDto)>();
- var channelTuples = new List<(BaseItemDto, LiveTvChannel)>();
+ var accessibleItems = user is null ? items : items.Where(x => x.IsVisible(user)).ToList();
+ var returnItems = new BaseItemDto[accessibleItems.Count];
+ List<(BaseItem, BaseItemDto)> programTuples = null;
+ List<(BaseItemDto, LiveTvChannel)> channelTuples = null;
- for (int index = 0; index < items.Count; index++)
+ for (int index = 0; index < accessibleItems.Count; index++)
{
- var item = items[index];
+ var item = accessibleItems[index];
var dto = GetBaseItemDtoInternal(item, options, user, owner);
if (item is LiveTvChannel tvChannel)
{
- channelTuples.Add((dto, tvChannel));
+ (channelTuples ??= new()).Add((dto, tvChannel));
}
else if (item is LiveTvProgram)
{
- programTuples.Add((item, dto));
+ (programTuples ??= new()).Add((item, dto));
}
if (item is IItemByName byName)
@@ -121,12 +122,12 @@ namespace Emby.Server.Implementations.Dto
returnItems[index] = dto;
}
- if (programTuples.Count > 0)
+ if (programTuples is not null)
{
LivetvManager.AddInfoToProgramDto(programTuples, options.Fields, user).GetAwaiter().GetResult();
}
- if (channelTuples.Count > 0)
+ if (channelTuples is not null)
{
LivetvManager.AddChannelInfo(channelTuples, options, user);
}
diff --git a/Emby.Server.Implementations/Emby.Server.Implementations.csproj b/Emby.Server.Implementations/Emby.Server.Implementations.csproj
index 7accc3b8b..b8655c760 100644
--- a/Emby.Server.Implementations/Emby.Server.Implementations.csproj
+++ b/Emby.Server.Implementations/Emby.Server.Implementations.csproj
@@ -7,7 +7,6 @@
<ItemGroup>
<ProjectReference Include="..\Emby.Naming\Emby.Naming.csproj" />
- <ProjectReference Include="..\Emby.Notifications\Emby.Notifications.csproj" />
<ProjectReference Include="..\Jellyfin.Api\Jellyfin.Api.csproj" />
<ProjectReference Include="..\Jellyfin.Server.Implementations\Jellyfin.Server.Implementations.csproj" />
<ProjectReference Include="..\MediaBrowser.Model\MediaBrowser.Model.csproj" />
@@ -23,17 +22,17 @@
</ItemGroup>
<ItemGroup>
- <PackageReference Include="DiscUtils.Udf" Version="0.16.13" />
- <PackageReference Include="Jellyfin.XmlTv" Version="10.8.0" />
- <PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="7.0.0" />
- <PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="7.0.0" />
- <PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="7.0.0" />
- <PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="7.0.0" />
- <PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="7.0.2" />
- <PackageReference Include="Mono.Nat" Version="3.0.4" />
- <PackageReference Include="prometheus-net.DotNetRuntime" Version="4.4.0" />
- <PackageReference Include="SQLitePCL.pretty.netstandard" Version="3.1.0" />
- <PackageReference Include="DotNet.Glob" Version="3.1.3" />
+ <PackageReference Include="DiscUtils.Udf" />
+ <PackageReference Include="Jellyfin.XmlTv" />
+ <PackageReference Include="Microsoft.Extensions.DependencyInjection" />
+ <PackageReference Include="Microsoft.Extensions.Caching.Memory" />
+ <PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" />
+ <PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" />
+ <PackageReference Include="Microsoft.EntityFrameworkCore.Relational" />
+ <PackageReference Include="Mono.Nat" />
+ <PackageReference Include="prometheus-net.DotNetRuntime" />
+ <PackageReference Include="SQLitePCL.pretty.netstandard" />
+ <PackageReference Include="DotNet.Glob" />
</ItemGroup>
<ItemGroup>
@@ -54,13 +53,13 @@
<!-- Code Analyzers-->
<ItemGroup Condition=" '$(Configuration)' == 'Debug' ">
- <PackageReference Include="Microsoft.CodeAnalysis.BannedApiAnalyzers" Version="3.3.3">
+ <PackageReference Include="Microsoft.CodeAnalysis.BannedApiAnalyzers">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
</PackageReference>
- <PackageReference Include="SerilogAnalyzer" Version="0.15.0" PrivateAssets="All" />
- <PackageReference Include="StyleCop.Analyzers" Version="1.2.0-beta.435" PrivateAssets="All" />
- <PackageReference Include="SmartAnalyzers.MultithreadingAnalyzer" Version="1.1.31" PrivateAssets="All" />
+ <PackageReference Include="SerilogAnalyzer" PrivateAssets="All" />
+ <PackageReference Include="StyleCop.Analyzers" PrivateAssets="All" />
+ <PackageReference Include="SmartAnalyzers.MultithreadingAnalyzer" PrivateAssets="All" />
</ItemGroup>
<ItemGroup>
diff --git a/Emby.Server.Implementations/EntryPoints/LibraryChangedNotifier.cs b/Emby.Server.Implementations/EntryPoints/LibraryChangedNotifier.cs
index 05d0a9b79..2e3988f9e 100644
--- a/Emby.Server.Implementations/EntryPoints/LibraryChangedNotifier.cs
+++ b/Emby.Server.Implementations/EntryPoints/LibraryChangedNotifier.cs
@@ -276,25 +276,31 @@ namespace Emby.Server.Implementations.EntryPoints
/// Libraries the update timer callback.
/// </summary>
/// <param name="state">The state.</param>
- private void LibraryUpdateTimerCallback(object state)
+ private async void LibraryUpdateTimerCallback(object state)
{
+ List<Folder> foldersAddedTo;
+ List<Folder> foldersRemovedFrom;
+ List<BaseItem> itemsUpdated;
+ List<BaseItem> itemsAdded;
+ List<BaseItem> itemsRemoved;
lock (_libraryChangedSyncLock)
{
// Remove dupes in case some were saved multiple times
- var foldersAddedTo = _foldersAddedTo
+ foldersAddedTo = _foldersAddedTo
.DistinctBy(x => x.Id)
.ToList();
- var foldersRemovedFrom = _foldersRemovedFrom
+ foldersRemovedFrom = _foldersRemovedFrom
.DistinctBy(x => x.Id)
.ToList();
- var itemsUpdated = _itemsUpdated
+ itemsUpdated = _itemsUpdated
.Where(i => !_itemsAdded.Contains(i))
.DistinctBy(x => x.Id)
.ToList();
- SendChangeNotifications(_itemsAdded.ToList(), itemsUpdated, _itemsRemoved.ToList(), foldersAddedTo, foldersRemovedFrom, CancellationToken.None).GetAwaiter().GetResult();
+ itemsAdded = _itemsAdded.ToList();
+ itemsRemoved = _itemsRemoved.ToList();
if (LibraryUpdateTimer is not null)
{
@@ -308,6 +314,8 @@ namespace Emby.Server.Implementations.EntryPoints
_foldersAddedTo.Clear();
_foldersRemovedFrom.Clear();
}
+
+ await SendChangeNotifications(itemsAdded, itemsUpdated, itemsRemoved, foldersAddedTo, foldersRemovedFrom, CancellationToken.None).ConfigureAwait(false);
}
/// <summary>
diff --git a/Emby.Server.Implementations/EntryPoints/UserDataChangeNotifier.cs b/Emby.Server.Implementations/EntryPoints/UserDataChangeNotifier.cs
index e724618b3..d32759017 100644
--- a/Emby.Server.Implementations/EntryPoints/UserDataChangeNotifier.cs
+++ b/Emby.Server.Implementations/EntryPoints/UserDataChangeNotifier.cs
@@ -87,29 +87,30 @@ namespace Emby.Server.Implementations.EntryPoints
}
}
- private void UpdateTimerCallback(object? state)
+ private async void UpdateTimerCallback(object? state)
{
+ List<KeyValuePair<Guid, List<BaseItem>>> changes;
lock (_syncLock)
{
// Remove dupes in case some were saved multiple times
- var changes = _changedItems.ToList();
+ changes = _changedItems.ToList();
_changedItems.Clear();
- SendNotifications(changes, CancellationToken.None).GetAwaiter().GetResult();
-
if (_updateTimer is not null)
{
_updateTimer.Dispose();
_updateTimer = null;
}
}
+
+ await SendNotifications(changes, CancellationToken.None).ConfigureAwait(false);
}
private async Task SendNotifications(List<KeyValuePair<Guid, List<BaseItem>>> changes, CancellationToken cancellationToken)
{
- foreach (var pair in changes)
+ foreach ((var key, var value) in changes)
{
- await SendNotifications(pair.Key, pair.Value, cancellationToken).ConfigureAwait(false);
+ await SendNotifications(key, value, cancellationToken).ConfigureAwait(false);
}
}
diff --git a/Emby.Server.Implementations/IStartupOptions.cs b/Emby.Server.Implementations/IStartupOptions.cs
index 3769ae4dd..b7bcaace1 100644
--- a/Emby.Server.Implementations/IStartupOptions.cs
+++ b/Emby.Server.Implementations/IStartupOptions.cs
@@ -21,16 +21,6 @@ namespace Emby.Server.Implementations
string? PackageName { get; }
/// <summary>
- /// Gets the value of the --restartpath command line option.
- /// </summary>
- string? RestartPath { get; }
-
- /// <summary>
- /// Gets the value of the --restartargs command line option.
- /// </summary>
- string? RestartArgs { get; }
-
- /// <summary>
/// Gets the value of the --published-server-url command line option.
/// </summary>
string? PublishedServerUrl { get; }
diff --git a/Emby.Server.Implementations/Images/BaseFolderImageProvider.cs b/Emby.Server.Implementations/Images/BaseFolderImageProvider.cs
index 6fc7f1ac3..84c21931c 100644
--- a/Emby.Server.Implementations/Images/BaseFolderImageProvider.cs
+++ b/Emby.Server.Implementations/Images/BaseFolderImageProvider.cs
@@ -1,5 +1,3 @@
-#nullable disable
-
#pragma warning disable CS1591
using System.Collections.Generic;
diff --git a/Emby.Server.Implementations/Images/FolderImageProvider.cs b/Emby.Server.Implementations/Images/FolderImageProvider.cs
index 4376bd356..90f7568a9 100644
--- a/Emby.Server.Implementations/Images/FolderImageProvider.cs
+++ b/Emby.Server.Implementations/Images/FolderImageProvider.cs
@@ -1,5 +1,3 @@
-#nullable disable
-
#pragma warning disable CS1591
using MediaBrowser.Common.Configuration;
diff --git a/Emby.Server.Implementations/Images/GenreImageProvider.cs b/Emby.Server.Implementations/Images/GenreImageProvider.cs
index 968bf5fa3..c9b41f819 100644
--- a/Emby.Server.Implementations/Images/GenreImageProvider.cs
+++ b/Emby.Server.Implementations/Images/GenreImageProvider.cs
@@ -1,5 +1,3 @@
-#nullable disable
-
#pragma warning disable CS1591
using System.Collections.Generic;
diff --git a/Emby.Server.Implementations/Library/LibraryManager.cs b/Emby.Server.Implementations/Library/LibraryManager.cs
index a3c66dc79..e5c520ca2 100644
--- a/Emby.Server.Implementations/Library/LibraryManager.cs
+++ b/Emby.Server.Implementations/Library/LibraryManager.cs
@@ -113,6 +113,7 @@ namespace Emby.Server.Implementations.Library
/// <param name="imageProcessor">The image processor.</param>
/// <param name="memoryCache">The memory cache.</param>
/// <param name="namingOptions">The naming options.</param>
+ /// <param name="directoryService">The directory service.</param>
public LibraryManager(
IServerApplicationHost appHost,
ILoggerFactory loggerFactory,
@@ -128,7 +129,8 @@ namespace Emby.Server.Implementations.Library
IItemRepository itemRepository,
IImageProcessor imageProcessor,
IMemoryCache memoryCache,
- NamingOptions namingOptions)
+ NamingOptions namingOptions,
+ IDirectoryService directoryService)
{
_appHost = appHost;
_logger = loggerFactory.CreateLogger<LibraryManager>();
@@ -146,7 +148,7 @@ namespace Emby.Server.Implementations.Library
_memoryCache = memoryCache;
_namingOptions = namingOptions;
- _extraResolver = new ExtraResolver(loggerFactory.CreateLogger<ExtraResolver>(), namingOptions);
+ _extraResolver = new ExtraResolver(loggerFactory.CreateLogger<ExtraResolver>(), namingOptions, directoryService);
_configurationManager.ConfigurationUpdated += ConfigurationUpdated;
@@ -356,8 +358,8 @@ namespace Emby.Server.Implementations.Library
}
var children = item.IsFolder
- ? ((Folder)item).GetRecursiveChildren(false).ToList()
- : new List<BaseItem>();
+ ? ((Folder)item).GetRecursiveChildren(false)
+ : Enumerable.Empty<BaseItem>();
foreach (var metadataPath in GetMetadataPaths(item, children))
{
@@ -537,7 +539,7 @@ namespace Emby.Server.Implementations.Library
collectionType = GetContentTypeOverride(fullPath, true);
}
- var args = new ItemResolveArgs(_configurationManager.ApplicationPaths, directoryService)
+ var args = new ItemResolveArgs(_configurationManager.ApplicationPaths, this)
{
Parent = parent,
FileInfo = fileInfo,
@@ -1253,7 +1255,7 @@ namespace Emby.Server.Implementations.Library
var parent = GetItemById(query.ParentId);
if (parent is not null)
{
- SetTopParentIdsOrAncestors(query, new List<BaseItem> { parent });
+ SetTopParentIdsOrAncestors(query, new[] { parent });
}
}
@@ -1277,7 +1279,7 @@ namespace Emby.Server.Implementations.Library
var parent = GetItemById(query.ParentId);
if (parent is not null)
{
- SetTopParentIdsOrAncestors(query, new List<BaseItem> { parent });
+ SetTopParentIdsOrAncestors(query, new[] { parent });
}
}
@@ -1435,7 +1437,7 @@ namespace Emby.Server.Implementations.Library
var parent = GetItemById(query.ParentId);
if (parent is not null)
{
- SetTopParentIdsOrAncestors(query, new List<BaseItem> { parent });
+ SetTopParentIdsOrAncestors(query, new[] { parent });
}
}
@@ -1455,7 +1457,7 @@ namespace Emby.Server.Implementations.Library
_itemRepository.GetItemList(query));
}
- private void SetTopParentIdsOrAncestors(InternalItemsQuery query, List<BaseItem> parents)
+ private void SetTopParentIdsOrAncestors(InternalItemsQuery query, IReadOnlyCollection<BaseItem> parents)
{
if (parents.All(i => i is ICollectionFolder || i is UserView))
{
@@ -1602,7 +1604,7 @@ namespace Emby.Server.Implementations.Library
{
_logger.LogError(ex, "Error getting intros");
- return new List<IntroInfo>();
+ return Enumerable.Empty<IntroInfo>();
}
}
@@ -2876,7 +2878,7 @@ namespace Emby.Server.Implementations.Library
private async Task SavePeopleMetadataAsync(IEnumerable<PersonInfo> people, CancellationToken cancellationToken)
{
- var personsToSave = new List<BaseItem>();
+ List<BaseItem> personsToSave = null;
foreach (var person in people)
{
@@ -2918,12 +2920,12 @@ namespace Emby.Server.Implementations.Library
if (saveEntity)
{
- personsToSave.Add(personEntity);
+ (personsToSave ??= new()).Add(personEntity);
await RunMetadataSavers(personEntity, itemUpdateType).ConfigureAwait(false);
}
}
- if (personsToSave.Count > 0)
+ if (personsToSave is not null)
{
CreateItems(personsToSave, null, CancellationToken.None);
}
@@ -3085,22 +3087,19 @@ namespace Emby.Server.Implementations.Library
throw new ArgumentNullException(nameof(path));
}
- var removeList = new List<NameValuePair>();
+ List<NameValuePair> removeList = null;
foreach (var contentType in _configurationManager.Configuration.ContentTypes)
{
- if (string.IsNullOrWhiteSpace(contentType.Name))
- {
- removeList.Add(contentType);
- }
- else if (_fileSystem.AreEqual(path, contentType.Name)
+ if (string.IsNullOrWhiteSpace(contentType.Name)
+ || _fileSystem.AreEqual(path, contentType.Name)
|| _fileSystem.ContainsSubPath(path, contentType.Name))
{
- removeList.Add(contentType);
+ (removeList ??= new()).Add(contentType);
}
}
- if (removeList.Count > 0)
+ if (removeList is not null)
{
_configurationManager.Configuration.ContentTypes = _configurationManager.Configuration.ContentTypes
.Except(removeList)
diff --git a/Emby.Server.Implementations/Library/Resolvers/Audio/AudioResolver.cs b/Emby.Server.Implementations/Library/Resolvers/Audio/AudioResolver.cs
index 06621700a..a74f82475 100644
--- a/Emby.Server.Implementations/Library/Resolvers/Audio/AudioResolver.cs
+++ b/Emby.Server.Implementations/Library/Resolvers/Audio/AudioResolver.cs
@@ -158,7 +158,6 @@ namespace Emby.Server.Implementations.Library.Resolvers.Audio
private MultiItemResolverResult ResolveMultipleAudio(Folder parent, IEnumerable<FileSystemMetadata> fileSystemEntries, bool parseName)
{
var files = new List<FileSystemMetadata>();
- var items = new List<BaseItem>();
var leftOver = new List<FileSystemMetadata>();
// Loop through each child file/folder and see if we find a video
@@ -180,7 +179,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.Audio
var result = new MultiItemResolverResult
{
ExtraFiles = leftOver,
- Items = items
+ Items = new List<BaseItem>()
};
var isInMixedFolder = resolverResult.Count > 1 || (parent is not null && parent.IsTopParent);
@@ -193,7 +192,8 @@ namespace Emby.Server.Implementations.Library.Resolvers.Audio
continue;
}
- if (resolvedItem.Files.Count == 0)
+ // Until multi-part books are handled letting files stack hides them from browsing in the client
+ if (resolvedItem.Files.Count == 0 || resolvedItem.Extras.Count > 0 || resolvedItem.AlternateVersions.Count > 0)
{
continue;
}
diff --git a/Emby.Server.Implementations/Library/Resolvers/Audio/MusicAlbumResolver.cs b/Emby.Server.Implementations/Library/Resolvers/Audio/MusicAlbumResolver.cs
index a922e3685..bbc70701c 100644
--- a/Emby.Server.Implementations/Library/Resolvers/Audio/MusicAlbumResolver.cs
+++ b/Emby.Server.Implementations/Library/Resolvers/Audio/MusicAlbumResolver.cs
@@ -25,16 +25,19 @@ namespace Emby.Server.Implementations.Library.Resolvers.Audio
{
private readonly ILogger<MusicAlbumResolver> _logger;
private readonly NamingOptions _namingOptions;
+ private readonly IDirectoryService _directoryService;
/// <summary>
/// Initializes a new instance of the <see cref="MusicAlbumResolver"/> class.
/// </summary>
/// <param name="logger">The logger.</param>
/// <param name="namingOptions">The naming options.</param>
- public MusicAlbumResolver(ILogger<MusicAlbumResolver> logger, NamingOptions namingOptions)
+ /// <param name="directoryService">The directory service.</param>
+ public MusicAlbumResolver(ILogger<MusicAlbumResolver> logger, NamingOptions namingOptions, IDirectoryService directoryService)
{
_logger = logger;
_namingOptions = namingOptions;
+ _directoryService = directoryService;
}
/// <summary>
@@ -109,7 +112,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.Audio
}
// If args contains music it's a music album
- if (ContainsMusic(args.FileSystemChildren, true, args.DirectoryService))
+ if (ContainsMusic(args.FileSystemChildren, true, _directoryService))
{
return true;
}
diff --git a/Emby.Server.Implementations/Library/Resolvers/Audio/MusicArtistResolver.cs b/Emby.Server.Implementations/Library/Resolvers/Audio/MusicArtistResolver.cs
index 2538c2b5b..c858dc53d 100644
--- a/Emby.Server.Implementations/Library/Resolvers/Audio/MusicArtistResolver.cs
+++ b/Emby.Server.Implementations/Library/Resolvers/Audio/MusicArtistResolver.cs
@@ -6,6 +6,7 @@ using System.Threading.Tasks;
using Emby.Naming.Common;
using MediaBrowser.Controller.Entities.Audio;
using MediaBrowser.Controller.Library;
+using MediaBrowser.Controller.Providers;
using MediaBrowser.Controller.Resolvers;
using MediaBrowser.Model.Entities;
using Microsoft.Extensions.Logging;
@@ -18,19 +19,23 @@ namespace Emby.Server.Implementations.Library.Resolvers.Audio
public class MusicArtistResolver : ItemResolver<MusicArtist>
{
private readonly ILogger<MusicAlbumResolver> _logger;
- private NamingOptions _namingOptions;
+ private readonly NamingOptions _namingOptions;
+ private readonly IDirectoryService _directoryService;
/// <summary>
/// Initializes a new instance of the <see cref="MusicArtistResolver"/> class.
/// </summary>
/// <param name="logger">Instance of the <see cref="MusicAlbumResolver"/> interface.</param>
/// <param name="namingOptions">The <see cref="NamingOptions"/>.</param>
+ /// <param name="directoryService">The directory service.</param>
public MusicArtistResolver(
ILogger<MusicAlbumResolver> logger,
- NamingOptions namingOptions)
+ NamingOptions namingOptions,
+ IDirectoryService directoryService)
{
_logger = logger;
_namingOptions = namingOptions;
+ _directoryService = directoryService;
}
/// <summary>
@@ -78,9 +83,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.Audio
return null;
}
- var directoryService = args.DirectoryService;
-
- var albumResolver = new MusicAlbumResolver(_logger, _namingOptions);
+ var albumResolver = new MusicAlbumResolver(_logger, _namingOptions, _directoryService);
var directories = args.FileSystemChildren.Where(i => i.IsDirectory);
@@ -97,7 +100,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.Audio
}
// If we contain a music album assume we are an artist folder
- if (albumResolver.IsMusicAlbum(fileSystemInfo.FullName, directoryService))
+ if (albumResolver.IsMusicAlbum(fileSystemInfo.FullName, _directoryService))
{
// Stop once we see a music album
state.Stop();
diff --git a/Emby.Server.Implementations/Library/Resolvers/BaseVideoResolver.cs b/Emby.Server.Implementations/Library/Resolvers/BaseVideoResolver.cs
index e8615e7db..4fac91bf1 100644
--- a/Emby.Server.Implementations/Library/Resolvers/BaseVideoResolver.cs
+++ b/Emby.Server.Implementations/Library/Resolvers/BaseVideoResolver.cs
@@ -25,14 +25,17 @@ namespace Emby.Server.Implementations.Library.Resolvers
{
private readonly ILogger _logger;
- protected BaseVideoResolver(ILogger logger, NamingOptions namingOptions)
+ protected BaseVideoResolver(ILogger logger, NamingOptions namingOptions, IDirectoryService directoryService)
{
_logger = logger;
NamingOptions = namingOptions;
+ DirectoryService = directoryService;
}
protected NamingOptions NamingOptions { get; }
+ protected IDirectoryService DirectoryService { get; }
+
/// <summary>
/// Resolves the specified args.
/// </summary>
@@ -65,13 +68,25 @@ namespace Emby.Server.Implementations.Library.Resolvers
var filename = child.Name;
if (child.IsDirectory)
{
- if (IsDvdDirectory(child.FullName, filename, args.DirectoryService))
+ if (IsDvdDirectory(child.FullName, filename, DirectoryService))
{
- videoType = VideoType.Dvd;
+ var videoTmp = new TVideoType
+ {
+ Path = args.Path,
+ VideoType = VideoType.Dvd
+ };
+ Set3DFormat(videoTmp);
+ return videoTmp;
}
else if (IsBluRayDirectory(filename))
{
- videoType = VideoType.BluRay;
+ var videoTmp = new TVideoType
+ {
+ Path = args.Path,
+ VideoType = VideoType.BluRay
+ };
+ Set3DFormat(videoTmp);
+ return videoTmp;
}
}
else if (IsDvdFile(filename))
diff --git a/Emby.Server.Implementations/Library/Resolvers/ExtraResolver.cs b/Emby.Server.Implementations/Library/Resolvers/ExtraResolver.cs
index 30c52e19d..0b255f673 100644
--- a/Emby.Server.Implementations/Library/Resolvers/ExtraResolver.cs
+++ b/Emby.Server.Implementations/Library/Resolvers/ExtraResolver.cs
@@ -4,6 +4,7 @@ using System.IO;
using Emby.Naming.Common;
using Emby.Naming.Video;
using MediaBrowser.Controller.Entities;
+using MediaBrowser.Controller.Providers;
using MediaBrowser.Controller.Resolvers;
using MediaBrowser.Model.Entities;
using Microsoft.Extensions.Logging;
@@ -25,11 +26,12 @@ namespace Emby.Server.Implementations.Library.Resolvers
/// </summary>
/// <param name="logger">The logger.</param>
/// <param name="namingOptions">An instance of <see cref="NamingOptions"/>.</param>
- public ExtraResolver(ILogger<ExtraResolver> logger, NamingOptions namingOptions)
+ /// <param name="directoryService">The directory service.</param>
+ public ExtraResolver(ILogger<ExtraResolver> logger, NamingOptions namingOptions, IDirectoryService directoryService)
{
_namingOptions = namingOptions;
- _trailerResolvers = new IItemResolver[] { new GenericVideoResolver<Trailer>(logger, namingOptions) };
- _videoResolvers = new IItemResolver[] { new GenericVideoResolver<Video>(logger, namingOptions) };
+ _trailerResolvers = new IItemResolver[] { new GenericVideoResolver<Trailer>(logger, namingOptions, directoryService) };
+ _videoResolvers = new IItemResolver[] { new GenericVideoResolver<Video>(logger, namingOptions, directoryService) };
}
/// <summary>
diff --git a/Emby.Server.Implementations/Library/Resolvers/GenericVideoResolver.cs b/Emby.Server.Implementations/Library/Resolvers/GenericVideoResolver.cs
index 5e33b402d..ba320266a 100644
--- a/Emby.Server.Implementations/Library/Resolvers/GenericVideoResolver.cs
+++ b/Emby.Server.Implementations/Library/Resolvers/GenericVideoResolver.cs
@@ -2,6 +2,7 @@
using Emby.Naming.Common;
using MediaBrowser.Controller.Entities;
+using MediaBrowser.Controller.Providers;
using Microsoft.Extensions.Logging;
namespace Emby.Server.Implementations.Library.Resolvers
@@ -18,8 +19,9 @@ namespace Emby.Server.Implementations.Library.Resolvers
/// </summary>
/// <param name="logger">The logger.</param>
/// <param name="namingOptions">The naming options.</param>
- public GenericVideoResolver(ILogger logger, NamingOptions namingOptions)
- : base(logger, namingOptions)
+ /// <param name="directoryService">The directory service.</param>
+ public GenericVideoResolver(ILogger logger, NamingOptions namingOptions, IDirectoryService directoryService)
+ : base(logger, namingOptions, directoryService)
{
}
}
diff --git a/Emby.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs b/Emby.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs
index 1522cd3ae..ea980b992 100644
--- a/Emby.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs
+++ b/Emby.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs
@@ -43,8 +43,9 @@ namespace Emby.Server.Implementations.Library.Resolvers.Movies
/// <param name="imageProcessor">The image processor.</param>
/// <param name="logger">The logger.</param>
/// <param name="namingOptions">The naming options.</param>
- public MovieResolver(IImageProcessor imageProcessor, ILogger<MovieResolver> logger, NamingOptions namingOptions)
- : base(logger, namingOptions)
+ /// <param name="directoryService">The directory service.</param>
+ public MovieResolver(IImageProcessor imageProcessor, ILogger<MovieResolver> logger, NamingOptions namingOptions, IDirectoryService directoryService)
+ : base(logger, namingOptions, directoryService)
{
_imageProcessor = imageProcessor;
}
@@ -97,12 +98,12 @@ namespace Emby.Server.Implementations.Library.Resolvers.Movies
if (string.Equals(collectionType, CollectionType.MusicVideos, StringComparison.OrdinalIgnoreCase))
{
- movie = FindMovie<MusicVideo>(args, args.Path, args.Parent, files, args.DirectoryService, collectionType, false);
+ movie = FindMovie<MusicVideo>(args, args.Path, args.Parent, files, DirectoryService, collectionType, false);
}
if (string.Equals(collectionType, CollectionType.HomeVideos, StringComparison.OrdinalIgnoreCase))
{
- movie = FindMovie<Video>(args, args.Path, args.Parent, files, args.DirectoryService, collectionType, false);
+ movie = FindMovie<Video>(args, args.Path, args.Parent, files, DirectoryService, collectionType, false);
}
if (string.IsNullOrEmpty(collectionType))
@@ -118,12 +119,12 @@ namespace Emby.Server.Implementations.Library.Resolvers.Movies
return null;
}
- movie = FindMovie<Movie>(args, args.Path, args.Parent, files, args.DirectoryService, collectionType, true);
+ movie = FindMovie<Movie>(args, args.Path, args.Parent, files, DirectoryService, collectionType, true);
}
if (string.Equals(collectionType, CollectionType.Movies, StringComparison.OrdinalIgnoreCase))
{
- movie = FindMovie<Movie>(args, args.Path, args.Parent, files, args.DirectoryService, collectionType, true);
+ movie = FindMovie<Movie>(args, args.Path, args.Parent, files, DirectoryService, collectionType, true);
}
// ignore extras
@@ -313,13 +314,8 @@ namespace Emby.Server.Implementations.Library.Resolvers.Movies
return result;
}
- private static bool IsIgnored(string filename)
- {
- // Ignore samples
- Match m = Regex.Match(filename, @"\bsample\b", RegexOptions.IgnoreCase | RegexOptions.Compiled);
-
- return m.Success;
- }
+ private static bool IsIgnored(ReadOnlySpan<char> filename)
+ => Regex.IsMatch(filename, @"\bsample\b", RegexOptions.IgnoreCase | RegexOptions.Compiled);
private static bool ContainsFile(IReadOnlyList<VideoInfo> result, FileSystemMetadata file)
{
diff --git a/Emby.Server.Implementations/Library/Resolvers/PhotoResolver.cs b/Emby.Server.Implementations/Library/Resolvers/PhotoResolver.cs
index e11fb262e..9026160ff 100644
--- a/Emby.Server.Implementations/Library/Resolvers/PhotoResolver.cs
+++ b/Emby.Server.Implementations/Library/Resolvers/PhotoResolver.cs
@@ -1,7 +1,5 @@
#nullable disable
-#pragma warning disable CS1591
-
using System;
using System.Collections.Generic;
using System.IO;
@@ -12,15 +10,20 @@ using Jellyfin.Extensions;
using MediaBrowser.Controller.Drawing;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Library;
+using MediaBrowser.Controller.Providers;
using MediaBrowser.Controller.Resolvers;
using MediaBrowser.Model.Entities;
namespace Emby.Server.Implementations.Library.Resolvers
{
+ /// <summary>
+ /// Class PhotoResolver.
+ /// </summary>
public class PhotoResolver : ItemResolver<Photo>
{
private readonly IImageProcessor _imageProcessor;
private readonly NamingOptions _namingOptions;
+ private readonly IDirectoryService _directoryService;
private static readonly HashSet<string> _ignoreFiles = new HashSet<string>(StringComparer.OrdinalIgnoreCase)
{
@@ -35,10 +38,17 @@ namespace Emby.Server.Implementations.Library.Resolvers
"default"
};
- public PhotoResolver(IImageProcessor imageProcessor, NamingOptions namingOptions)
+ /// <summary>
+ /// Initializes a new instance of the <see cref="PhotoResolver"/> class.
+ /// </summary>
+ /// <param name="imageProcessor">The image processor.</param>
+ /// <param name="namingOptions">The naming options.</param>
+ /// <param name="directoryService">The directory service.</param>
+ public PhotoResolver(IImageProcessor imageProcessor, NamingOptions namingOptions, IDirectoryService directoryService)
{
_imageProcessor = imageProcessor;
_namingOptions = namingOptions;
+ _directoryService = directoryService;
}
/// <summary>
@@ -61,7 +71,7 @@ namespace Emby.Server.Implementations.Library.Resolvers
var filename = Path.GetFileNameWithoutExtension(args.Path);
// Make sure the image doesn't belong to a video file
- var files = args.DirectoryService.GetFiles(Path.GetDirectoryName(args.Path));
+ var files = _directoryService.GetFiles(Path.GetDirectoryName(args.Path));
foreach (var file in files)
{
diff --git a/Emby.Server.Implementations/Library/Resolvers/TV/EpisodeResolver.cs b/Emby.Server.Implementations/Library/Resolvers/TV/EpisodeResolver.cs
index 0fcc5070b..392ee4c77 100644
--- a/Emby.Server.Implementations/Library/Resolvers/TV/EpisodeResolver.cs
+++ b/Emby.Server.Implementations/Library/Resolvers/TV/EpisodeResolver.cs
@@ -5,6 +5,7 @@ using System.Linq;
using Emby.Naming.Common;
using MediaBrowser.Controller.Entities.TV;
using MediaBrowser.Controller.Library;
+using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Entities;
using Microsoft.Extensions.Logging;
@@ -20,8 +21,9 @@ namespace Emby.Server.Implementations.Library.Resolvers.TV
/// </summary>
/// <param name="logger">The logger.</param>
/// <param name="namingOptions">The naming options.</param>
- public EpisodeResolver(ILogger<EpisodeResolver> logger, NamingOptions namingOptions)
- : base(logger, namingOptions)
+ /// <param name="directoryService">The directory service.</param>
+ public EpisodeResolver(ILogger<EpisodeResolver> logger, NamingOptions namingOptions, IDirectoryService directoryService)
+ : base(logger, namingOptions, directoryService)
{
}
diff --git a/Emby.Server.Implementations/Library/UserViewManager.cs b/Emby.Server.Implementations/Library/UserViewManager.cs
index 1137625f4..17f1d1905 100644
--- a/Emby.Server.Implementations/Library/UserViewManager.cs
+++ b/Emby.Server.Implementations/Library/UserViewManager.cs
@@ -111,10 +111,10 @@ namespace Emby.Server.Implementations.Library
if (query.IncludeExternalContent)
{
- var channelResult = _channelManager.GetChannelsInternal(new ChannelQuery
+ var channelResult = _channelManager.GetChannelsInternalAsync(new ChannelQuery
{
UserId = query.UserId
- });
+ }).GetAwaiter().GetResult();
var channels = channelResult.Items;
@@ -286,7 +286,7 @@ namespace Emby.Server.Implementations.Library
if (parents.Count == 0)
{
- return new List<BaseItem>();
+ return Array.Empty<BaseItem>();
}
if (includeItemTypes.Length == 0)
diff --git a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs b/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs
index 3f7914d3b..b5e742f98 100644
--- a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs
+++ b/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs
@@ -570,15 +570,13 @@ namespace Emby.Server.Implementations.LiveTv.Listings
_tokens.TryAdd(username, savedToken);
}
- if (!string.IsNullOrEmpty(savedToken.Name) && !string.IsNullOrEmpty(savedToken.Value))
+ if (!string.IsNullOrEmpty(savedToken.Name)
+ && long.TryParse(savedToken.Value, CultureInfo.InvariantCulture, out long ticks))
{
- if (long.TryParse(savedToken.Value, NumberStyles.Any, CultureInfo.InvariantCulture, out long ticks))
+ // If it's under 24 hours old we can still use it
+ if (DateTime.UtcNow.Ticks - ticks < TimeSpan.FromHours(20).Ticks)
{
- // If it's under 24 hours old we can still use it
- if (DateTime.UtcNow.Ticks - ticks < TimeSpan.FromHours(20).Ticks)
- {
- return savedToken.Name;
- }
+ return savedToken.Name;
}
}
diff --git a/Emby.Server.Implementations/LiveTv/Listings/XmlTvListingsProvider.cs b/Emby.Server.Implementations/LiveTv/Listings/XmlTvListingsProvider.cs
index e874990da..066afb956 100644
--- a/Emby.Server.Implementations/LiveTv/Listings/XmlTvListingsProvider.cs
+++ b/Emby.Server.Implementations/LiveTv/Listings/XmlTvListingsProvider.cs
@@ -137,32 +137,33 @@ namespace Emby.Server.Implementations.LiveTv.Listings
private static ProgramInfo GetProgramInfo(XmlTvProgram program, ListingsProviderInfo info)
{
- string episodeTitle = program.Episode?.Title;
+ string episodeTitle = program.Episode.Title;
+ var programCategories = program.Categories.Where(c => !string.IsNullOrWhiteSpace(c)).ToList();
var programInfo = new ProgramInfo
{
ChannelId = program.ChannelId,
EndDate = program.EndDate.UtcDateTime,
- EpisodeNumber = program.Episode?.Episode,
+ EpisodeNumber = program.Episode.Episode,
EpisodeTitle = episodeTitle,
- Genres = program.Categories,
+ Genres = programCategories,
StartDate = program.StartDate.UtcDateTime,
Name = program.Title,
Overview = program.Description,
ProductionYear = program.CopyrightDate?.Year,
- SeasonNumber = program.Episode?.Series,
- IsSeries = program.Episode is not null,
+ SeasonNumber = program.Episode.Series,
+ IsSeries = program.Episode.Series is not null,
IsRepeat = program.IsPreviouslyShown && !program.IsNew,
IsPremiere = program.Premiere is not null,
- IsKids = program.Categories.Any(c => info.KidsCategories.Contains(c, StringComparison.OrdinalIgnoreCase)),
- IsMovie = program.Categories.Any(c => info.MovieCategories.Contains(c, StringComparison.OrdinalIgnoreCase)),
- IsNews = program.Categories.Any(c => info.NewsCategories.Contains(c, StringComparison.OrdinalIgnoreCase)),
- IsSports = program.Categories.Any(c => info.SportsCategories.Contains(c, StringComparison.OrdinalIgnoreCase)),
+ IsKids = programCategories.Any(c => info.KidsCategories.Contains(c, StringComparison.OrdinalIgnoreCase)),
+ IsMovie = programCategories.Any(c => info.MovieCategories.Contains(c, StringComparison.OrdinalIgnoreCase)),
+ IsNews = programCategories.Any(c => info.NewsCategories.Contains(c, StringComparison.OrdinalIgnoreCase)),
+ IsSports = programCategories.Any(c => info.SportsCategories.Contains(c, StringComparison.OrdinalIgnoreCase)),
ImageUrl = string.IsNullOrEmpty(program.Icon?.Source) ? null : program.Icon.Source,
HasImage = !string.IsNullOrEmpty(program.Icon?.Source),
OfficialRating = string.IsNullOrEmpty(program.Rating?.Value) ? null : program.Rating.Value,
CommunityRating = program.StarRating,
- SeriesId = program.Episode is null ? null : program.Title?.GetMD5().ToString("N", CultureInfo.InvariantCulture)
+ SeriesId = program.Episode.Episode is null ? null : program.Title?.GetMD5().ToString("N", CultureInfo.InvariantCulture)
};
if (string.IsNullOrWhiteSpace(program.ProgramId))
@@ -243,7 +244,7 @@ namespace Emby.Server.Implementations.LiveTv.Listings
{
Id = c.Id,
Name = c.DisplayName,
- ImageUrl = string.IsNullOrEmpty(c.Icon.Source) ? null : c.Icon.Source,
+ ImageUrl = string.IsNullOrEmpty(c.Icon?.Source) ? null : c.Icon.Source,
Number = string.IsNullOrWhiteSpace(c.Number) ? c.Id : c.Number
}).ToList();
}
diff --git a/Emby.Server.Implementations/LiveTv/LiveTvManager.cs b/Emby.Server.Implementations/LiveTv/LiveTvManager.cs
index 4003468d0..ee039ff0f 100644
--- a/Emby.Server.Implementations/LiveTv/LiveTvManager.cs
+++ b/Emby.Server.Implementations/LiveTv/LiveTvManager.cs
@@ -1312,20 +1312,19 @@ namespace Emby.Server.Implementations.LiveTv
return 7;
}
- private QueryResult<BaseItem> GetEmbyRecordings(RecordingQuery query, DtoOptions dtoOptions, User user)
+ private async Task<QueryResult<BaseItem>> GetEmbyRecordingsAsync(RecordingQuery query, DtoOptions dtoOptions, User user)
{
if (user is null)
{
return new QueryResult<BaseItem>();
}
- var folderIds = GetRecordingFolders(user, true)
- .Select(i => i.Id)
- .ToList();
+ var folders = await GetRecordingFoldersAsync(user, true).ConfigureAwait(false);
+ var folderIds = Array.ConvertAll(folders, x => x.Id);
var excludeItemTypes = new List<BaseItemKind>();
- if (folderIds.Count == 0)
+ if (folderIds.Length == 0)
{
return new QueryResult<BaseItem>();
}
@@ -1392,7 +1391,7 @@ namespace Emby.Server.Implementations.LiveTv
{
MediaTypes = new[] { MediaType.Video },
Recursive = true,
- AncestorIds = folderIds.ToArray(),
+ AncestorIds = folderIds,
IsFolder = false,
IsVirtualItem = false,
Limit = limit,
@@ -1528,7 +1527,7 @@ namespace Emby.Server.Implementations.LiveTv
}
}
- public QueryResult<BaseItemDto> GetRecordings(RecordingQuery query, DtoOptions options)
+ public async Task<QueryResult<BaseItemDto>> GetRecordingsAsync(RecordingQuery query, DtoOptions options)
{
var user = query.UserId.Equals(default)
? null
@@ -1536,7 +1535,7 @@ namespace Emby.Server.Implementations.LiveTv
RemoveFields(options);
- var internalResult = GetEmbyRecordings(query, options, user);
+ var internalResult = await GetEmbyRecordingsAsync(query, options, user).ConfigureAwait(false);
var returnArray = _dtoService.GetBaseItemDtos(internalResult.Items, options, user);
@@ -2379,12 +2378,11 @@ namespace Emby.Server.Implementations.LiveTv
return _tvDtoService.GetInternalProgramId(externalId);
}
- public List<BaseItem> GetRecordingFolders(User user)
- {
- return GetRecordingFolders(user, false);
- }
+ /// <inheritdoc />
+ public Task<BaseItem[]> GetRecordingFoldersAsync(User user)
+ => GetRecordingFoldersAsync(user, false);
- private List<BaseItem> GetRecordingFolders(User user, bool refreshChannels)
+ private async Task<BaseItem[]> GetRecordingFoldersAsync(User user, bool refreshChannels)
{
var folders = EmbyTV.EmbyTV.Current.GetRecordingFolders()
.SelectMany(i => i.Locations)
@@ -2396,14 +2394,16 @@ namespace Emby.Server.Implementations.LiveTv
.OrderBy(i => i.SortName)
.ToList();
- folders.AddRange(_channelManager.GetChannelsInternal(new MediaBrowser.Model.Channels.ChannelQuery
+ var channels = await _channelManager.GetChannelsInternalAsync(new MediaBrowser.Model.Channels.ChannelQuery
{
UserId = user.Id,
IsRecordingsFolder = true,
RefreshLatestChannelItems = refreshChannels
- }).Items);
+ }).ConfigureAwait(false);
+
+ folders.AddRange(channels.Items);
- return folders.Cast<BaseItem>().ToList();
+ return folders.Cast<BaseItem>().ToArray();
}
}
}
diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs
index 5327b3d74..98bbc1540 100644
--- a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs
+++ b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs
@@ -14,6 +14,7 @@ using System.Threading;
using System.Threading.Tasks;
using Jellyfin.Extensions;
using Jellyfin.Extensions.Json;
+using Jellyfin.Extensions.Json.Converters;
using MediaBrowser.Common.Extensions;
using MediaBrowser.Common.Net;
using MediaBrowser.Controller;
@@ -58,7 +59,8 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
_socketFactory = socketFactory;
_streamHelper = streamHelper;
- _jsonOptions = JsonDefaults.Options;
+ _jsonOptions = new JsonSerializerOptions(JsonDefaults.Options);
+ _jsonOptions.Converters.Add(new JsonBoolNumberConverter());
}
public string Name => "HD Homerun";
diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/LegacyHdHomerunChannelCommands.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/LegacyHdHomerunChannelCommands.cs
index 80d9d0724..3450f971f 100644
--- a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/LegacyHdHomerunChannelCommands.cs
+++ b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/LegacyHdHomerunChannelCommands.cs
@@ -13,8 +13,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
public LegacyHdHomerunChannelCommands(string url)
{
// parse url for channel and program
- var regExp = new Regex(@"\/ch([0-9]+)-?([0-9]*)");
- var match = regExp.Match(url);
+ var match = Regex.Match(url, @"\/ch([0-9]+)-?([0-9]*)");
if (match.Success)
{
_channel = match.Groups[1].Value;
diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/M3uParser.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/M3uParser.cs
index a423ec8f4..f2020e05f 100644
--- a/Emby.Server.Implementations/LiveTv/TunerHosts/M3uParser.cs
+++ b/Emby.Server.Implementations/LiveTv/TunerHosts/M3uParser.cs
@@ -122,9 +122,13 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
var attributes = ParseExtInf(extInf, out string remaining);
extInf = remaining;
- if (attributes.TryGetValue("tvg-logo", out string value))
+ if (attributes.TryGetValue("tvg-logo", out string tvgLogo))
{
- channel.ImageUrl = value;
+ channel.ImageUrl = tvgLogo;
+ }
+ else if (attributes.TryGetValue("logo", out string logo))
+ {
+ channel.ImageUrl = logo;
}
if (attributes.TryGetValue("group-title", out string groupTitle))
@@ -168,28 +172,24 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
string numberString = null;
string attributeValue;
- if (attributes.TryGetValue("tvg-chno", out attributeValue))
+ if (attributes.TryGetValue("tvg-chno", out attributeValue)
+ && double.TryParse(attributeValue, CultureInfo.InvariantCulture, out _))
{
- if (double.TryParse(attributeValue, NumberStyles.Any, CultureInfo.InvariantCulture, out _))
- {
- numberString = attributeValue;
- }
+ numberString = attributeValue;
}
if (!IsValidChannelNumber(numberString))
{
if (attributes.TryGetValue("tvg-id", out attributeValue))
{
- if (double.TryParse(attributeValue, NumberStyles.Any, CultureInfo.InvariantCulture, out _))
+ if (double.TryParse(attributeValue, CultureInfo.InvariantCulture, out _))
{
numberString = attributeValue;
}
- else if (attributes.TryGetValue("channel-id", out attributeValue))
+ else if (attributes.TryGetValue("channel-id", out attributeValue)
+ && double.TryParse(attributeValue, CultureInfo.InvariantCulture, out _))
{
- if (double.TryParse(attributeValue, NumberStyles.Any, CultureInfo.InvariantCulture, out _))
- {
- numberString = attributeValue;
- }
+ numberString = attributeValue;
}
}
@@ -207,7 +207,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
{
var numberPart = nameInExtInf.Slice(0, numberIndex).Trim(new[] { ' ', '.' });
- if (double.TryParse(numberPart, NumberStyles.Any, CultureInfo.InvariantCulture, out _))
+ if (double.TryParse(numberPart, CultureInfo.InvariantCulture, out _))
{
numberString = numberPart.ToString();
}
@@ -255,19 +255,14 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
private static bool IsValidChannelNumber(string numberString)
{
- if (string.IsNullOrWhiteSpace(numberString) ||
- string.Equals(numberString, "-1", StringComparison.OrdinalIgnoreCase) ||
- string.Equals(numberString, "0", StringComparison.OrdinalIgnoreCase))
- {
- return false;
- }
-
- if (!double.TryParse(numberString, NumberStyles.Any, CultureInfo.InvariantCulture, out _))
+ if (string.IsNullOrWhiteSpace(numberString)
+ || string.Equals(numberString, "-1", StringComparison.Ordinal)
+ || string.Equals(numberString, "0", StringComparison.Ordinal))
{
return false;
}
- return true;
+ return double.TryParse(numberString, CultureInfo.InvariantCulture, out _);
}
private static string GetChannelName(string extInf, Dictionary<string, string> attributes)
@@ -285,7 +280,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
{
var numberPart = nameInExtInf.Substring(0, numberIndex).Trim(new[] { ' ', '.' });
- if (double.TryParse(numberPart, NumberStyles.Any, CultureInfo.InvariantCulture, out _))
+ if (double.TryParse(numberPart, CultureInfo.InvariantCulture, out _))
{
// channel.Number = number.ToString();
nameInExtInf = nameInExtInf.Substring(numberIndex + 1).Trim(new[] { ' ', '-' });
@@ -317,8 +312,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
{
var dict = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
- var reg = new Regex(@"([a-z0-9\-_]+)=\""([^""]+)\""", RegexOptions.IgnoreCase);
- var matches = reg.Matches(line);
+ var matches = Regex.Matches(line, @"([a-z0-9\-_]+)=\""([^""]+)\""", RegexOptions.IgnoreCase);
remaining = line;
diff --git a/Emby.Server.Implementations/Localization/Core/be.json b/Emby.Server.Implementations/Localization/Core/be.json
index 56c4e7d39..3af124678 100644
--- a/Emby.Server.Implementations/Localization/Core/be.json
+++ b/Emby.Server.Implementations/Localization/Core/be.json
@@ -1,4 +1,127 @@
{
- "Sync": "Сінхранізацыя",
- "Playlists": "Плэйліст"
+ "Sync": "Сінхранізаваць",
+ "Playlists": "Плэйлісты",
+ "Latest": "Апошні",
+ "LabelIpAddressValue": "IP-адрас: {0}",
+ "ItemAddedWithName": "{0} быў дададзены ў бібліятэку",
+ "MessageApplicationUpdated": "Сервер Jellyfin абноўлены",
+ "NotificationOptionApplicationUpdateInstalled": "Абнаўленне прыкладання ўсталявана",
+ "PluginInstalledWithName": "{0} быў усталяваны",
+ "UserCreatedWithName": "Карыстальнік {0} быў створаны",
+ "Albums": "Альбомы",
+ "Application": "Прыкладанне",
+ "AuthenticationSucceededWithUserName": "{0} паспяхова аўтэнтыфікаваны",
+ "Channels": "Каналы",
+ "ChapterNameValue": "Раздзел {0}",
+ "Collections": "Калекцыі",
+ "Default": "Па змаўчанні",
+ "FailedLoginAttemptWithUserName": "Няўдалая спроба ўваходу з {0}",
+ "Folders": "Папкі",
+ "Favorites": "Абранае",
+ "External": "Знешні",
+ "Genres": "Жанры",
+ "HeaderContinueWatching": "Працягнуць прагляд",
+ "HeaderFavoriteAlbums": "Абраныя альбомы",
+ "HeaderFavoriteEpisodes": "Абраныя серыі",
+ "HeaderFavoriteShows": "Абраныя шоу",
+ "HeaderFavoriteSongs": "Абраныя песні",
+ "HeaderLiveTV": "Прамы эфір",
+ "HeaderAlbumArtists": "Выканаўцы альбома",
+ "LabelRunningTimeValue": "Працягласць: {0}",
+ "HomeVideos": "Хатнія відэа",
+ "ItemRemovedWithName": "{0} быў выдалены з бібліятэкі",
+ "MessageApplicationUpdatedTo": "Сервер Jellyfin абноўлены да {0}",
+ "Movies": "Фільмы",
+ "Music": "Музыка",
+ "MusicVideos": "Музычныя кліпы",
+ "NameInstallFailed": "Устаноўка {0} не атрымалася",
+ "NameSeasonNumber": "Сезон {0}",
+ "NotificationOptionApplicationUpdateAvailable": "Даступна абнаўленне прыкладання",
+ "NotificationOptionPluginInstalled": "Плагін усталяваны",
+ "NotificationOptionPluginUpdateInstalled": "Абнаўленне плагіна усталявана",
+ "NotificationOptionServerRestartRequired": "Патрабуецца перазапуск сервера",
+ "Photos": "Фатаграфіі",
+ "Plugin": "Плагін",
+ "PluginUninstalledWithName": "{0} быў выдалены",
+ "PluginUpdatedWithName": "{0} быў абноўлены",
+ "ProviderValue": "Пастаўшчык: {0}",
+ "Songs": "Песні",
+ "System": "Сістэма",
+ "User": "Карыстальнік",
+ "UserDeletedWithName": "Карыстальнік {0} быў выдалены",
+ "UserDownloadingItemWithValues": "{0} спампоўваецца {1}",
+ "TaskOptimizeDatabase": "Аптымізаваць базу дадзеных",
+ "Artists": "Выканаўцы",
+ "UserOfflineFromDevice": "{0} адключыўся ад {1}",
+ "UserPolicyUpdatedWithName": "Палітыка карыстальніка абноўлена для {0}",
+ "TaskCleanActivityLogDescription": "Выдаляе старэйшыя за зададзены ўзрост запісы ў журнале актыўнасці.",
+ "TaskRefreshChapterImagesDescription": "Стварае мініяцюры для відэа, якія маюць раздзелы.",
+ "TaskCleanLogsDescription": "Выдаляе файлы журналу, якім больш за {0} дзён.",
+ "TaskUpdatePluginsDescription": "Спампоўвае і ўсталёўвае абнаўленні для плагінаў, якія настроены на аўтаматычнае абнаўленне.",
+ "TaskRefreshChannelsDescription": "Абнаўляе інфармацыю аб інтэрнэт-канале.",
+ "TaskDownloadMissingSubtitlesDescription": "Шукае ў інтэрнэце адсутныя субтытры на аснове канфігурацыі метададзеных.",
+ "TaskOptimizeDatabaseDescription": "Ушчыльняе базу дадзеных і скарачае вольную прастору. Выкананне гэтай задачы пасля сканавання бібліятэкі або ўнясення іншых змяненняў, якія прадугледжваюць мадыфікацыю базы дадзеных, можа палепшыць прадукцыйнасць.",
+ "TaskKeyframeExtractor": "Экстрактар ключавых кадраў",
+ "TasksApplicationCategory": "Прыкладанне",
+ "AppDeviceValues": "Прыкладанне: {0}, Прылада: {1}",
+ "Books": "Кнігі",
+ "CameraImageUploadedFrom": "Новая выява камеры была загружана з {0}",
+ "DeviceOfflineWithName": "{0} адключыўся",
+ "DeviceOnlineWithName": "{0} падлучаны",
+ "Forced": "Прымусова",
+ "HeaderRecordingGroups": "Групы запісаў",
+ "HeaderNextUp": "Наступнае",
+ "HeaderFavoriteArtists": "Абраныя выканаўцы",
+ "HearingImpaired": "Са слабым слыхам",
+ "Inherit": "Атрымаць у спадчыну",
+ "MessageNamedServerConfigurationUpdatedWithValue": "Канфігурацыя сервера {0} абноўлена",
+ "MessageServerConfigurationUpdated": "Канфігурацыя сервера абноўлена",
+ "MixedContent": "Змешаны змест",
+ "NameSeasonUnknown": "Невядомы сезон",
+ "NotificationOptionInstallationFailed": "Збой усталёўкі",
+ "NewVersionIsAvailable": "Новая версія сервера Jellyfin даступная для cпампоўкі.",
+ "NotificationOptionCameraImageUploaded": "Выява камеры запампавана",
+ "NotificationOptionAudioPlaybackStopped": "Прайграванне аўдыё спынена",
+ "NotificationOptionAudioPlayback": "Прайграванне аўдыё пачалося",
+ "NotificationOptionNewLibraryContent": "Дададзены новы кантэнт",
+ "NotificationOptionPluginError": "Збой плагіна",
+ "NotificationOptionPluginUninstalled": "Плагін выдалены",
+ "NotificationOptionTaskFailed": "Збой запланаванага задання",
+ "NotificationOptionUserLockedOut": "Карыстальнік заблакіраваны",
+ "NotificationOptionVideoPlayback": "Пачалося прайграванне відэа",
+ "NotificationOptionVideoPlaybackStopped": "Прайграванне відэа спынена",
+ "ScheduledTaskFailedWithName": "{0} не атрымалася",
+ "ScheduledTaskStartedWithName": "{0} пачалося",
+ "ServerNameNeedsToBeRestarted": "{0} трэба перазапусціць",
+ "Shows": "Шоу",
+ "StartupEmbyServerIsLoading": "Jellyfin Server загружаецца. Калі ласка, паўтарыце спробу крыху пазней.",
+ "SubtitleDownloadFailureFromForItem": "Не атрымалася спампаваць субтытры з {0} для {1}",
+ "TvShows": "ТБ-шоу",
+ "Undefined": "Нявызначана",
+ "UserLockedOutWithName": "Карыстальнік {0} быў заблакіраваны",
+ "UserOnlineFromDevice": "{0} падключаны з {1}",
+ "UserPasswordChangedWithName": "Пароль быў зменены для карыстальніка {0}",
+ "UserStartedPlayingItemWithValues": "{0} грае {1} на {2}",
+ "UserStoppedPlayingItemWithValues": "{0} скончыў прайграванне {1} на {2}",
+ "ValueHasBeenAddedToLibrary": "{0} быў дададзены ў вашу медыятэку",
+ "ValueSpecialEpisodeName": "Спецэпізод - {0}",
+ "VersionNumber": "Версія {0}",
+ "TasksMaintenanceCategory": "Абслугоўванне",
+ "TasksLibraryCategory": "Медыятэка",
+ "TasksChannelsCategory": "Інтэрнэт-каналы",
+ "TaskCleanActivityLog": "Ачысціць журнал актыўнасці",
+ "TaskCleanCache": "Ачысціць кэш",
+ "TaskCleanCacheDescription": "Выдаляе файлы кэша, якія больш не патрэбныя сістэме.",
+ "TaskRefreshChapterImages": "Выняць выявы раздзелаў",
+ "TaskRefreshLibrary": "Сканіраваць медыятэку",
+ "TaskRefreshLibraryDescription": "Сканіруе вашу медыятэку на наяўнасць новых файлаў і абнаўляе метададзеныя.",
+ "TaskCleanLogs": "Ачысціць часопіс",
+ "TaskRefreshPeople": "Абнавіць людзей",
+ "TaskRefreshPeopleDescription": "Абнаўленне метаданых для акцёраў і рэжысёраў у вашай медыятэцы.",
+ "TaskUpdatePlugins": "Абнавіць плагіны",
+ "TaskCleanTranscode": "Ачысціць каталог перакадзіравання",
+ "TaskCleanTranscodeDescription": "Выдаляе перакадзіраваныя файлы, старэйшыя за адзін дзень.",
+ "TaskRefreshChannels": "Абнавіць каналы",
+ "TaskDownloadMissingSubtitles": "Спампаваць адсутныя субтытры",
+ "TaskKeyframeExtractorDescription": "Выдае ключавыя кадры з відэафайлаў для стварэння больш дакладных спісаў прайгравання HLS. Гэта задача можа працаваць у працягу доўгага часу."
}
diff --git a/Emby.Server.Implementations/Localization/Core/bg-BG.json b/Emby.Server.Implementations/Localization/Core/bg-BG.json
index 64cb36fd8..13b99cc99 100644
--- a/Emby.Server.Implementations/Localization/Core/bg-BG.json
+++ b/Emby.Server.Implementations/Localization/Core/bg-BG.json
@@ -123,5 +123,6 @@
"TaskOptimizeDatabase": "Оптимизирай базата данни",
"TaskKeyframeExtractorDescription": "Извличат се ключови кадри от видеофайловете ,за да се създаде по точен ХЛС списък . Задачата може да отнеме много време.",
"TaskKeyframeExtractor": "Извличане на ключови кадри",
- "External": "Външен"
+ "External": "Външен",
+ "HearingImpaired": "Увреден слух"
}
diff --git a/Emby.Server.Implementations/Localization/Core/es-AR.json b/Emby.Server.Implementations/Localization/Core/es-AR.json
index 8ad9e8c71..8bd3c5def 100644
--- a/Emby.Server.Implementations/Localization/Core/es-AR.json
+++ b/Emby.Server.Implementations/Localization/Core/es-AR.json
@@ -118,11 +118,11 @@
"TaskCleanActivityLog": "Borrar log de actividades",
"Undefined": "Indefinido",
"Forced": "Forzado",
- "Default": "Por Defecto",
+ "Default": "Predeterminado",
"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",
"External": "Externo",
"TaskKeyframeExtractorDescription": "Extrae Fotogramas Clave de los archivos de vídeo para crear Listas de Reprodución HLS más precisas. Esta tarea puede durar mucho tiempo.",
"TaskKeyframeExtractor": "Extractor de Fotogramas Clave",
- "HearingImpaired": "Personas con discapacidad auditiva"
+ "HearingImpaired": "Discapacidad Auditiva"
}
diff --git a/Emby.Server.Implementations/Localization/Core/es.json b/Emby.Server.Implementations/Localization/Core/es.json
index afffdf3bf..5e41462db 100644
--- a/Emby.Server.Implementations/Localization/Core/es.json
+++ b/Emby.Server.Implementations/Localization/Core/es.json
@@ -31,7 +31,7 @@
"ItemRemovedWithName": "{0} ha sido eliminado de la biblioteca",
"LabelIpAddressValue": "Dirección IP: {0}",
"LabelRunningTimeValue": "Tiempo de funcionamiento: {0}",
- "Latest": "Últimos",
+ "Latest": "Último contenido en",
"MessageApplicationUpdated": "Se ha actualizado el servidor Jellyfin",
"MessageApplicationUpdatedTo": "Se ha actualizado el servidor Jellyfin a la versión {0}",
"MessageNamedServerConfigurationUpdatedWithValue": "La sección {0} de configuración del servidor ha sido actualizada",
diff --git a/Emby.Server.Implementations/Localization/Core/es_419.json b/Emby.Server.Implementations/Localization/Core/es_419.json
index d6078c9c6..3d5c04633 100644
--- a/Emby.Server.Implementations/Localization/Core/es_419.json
+++ b/Emby.Server.Implementations/Localization/Core/es_419.json
@@ -122,5 +122,6 @@
"TaskOptimizeDatabase": "Optimizar base de datos",
"External": "Externo",
"TaskKeyframeExtractorDescription": "Extrae Fotogramas Clave de los archivos de vídeo para crear Listas de Reproducción HLS más precisas. Esta tarea puede durar mucho tiempo.",
- "TaskKeyframeExtractor": "Extractor de Fotogramas Clave"
+ "TaskKeyframeExtractor": "Extractor de Fotogramas Clave",
+ "HearingImpaired": "Discapacidad auditiva"
}
diff --git a/Emby.Server.Implementations/Localization/Core/fa.json b/Emby.Server.Implementations/Localization/Core/fa.json
index 026648af4..8e4bba25b 100644
--- a/Emby.Server.Implementations/Localization/Core/fa.json
+++ b/Emby.Server.Implementations/Localization/Core/fa.json
@@ -123,5 +123,6 @@
"TaskOptimizeDatabaseDescription": "فشرده سازی پایگاه داده و باز کردن فضای آزاد.اجرای این گزینه بعد از اسکن کردن کتابخانه یا تغییرات دیگر که روی پایگاه داده تأثیر میگذارند میتواند کارایی را بهبود ببخشد.",
"TaskKeyframeExtractorDescription": "فریم های کلیدی را از فایل های ویدئویی استخراج می کند تا لیست های پخش HLS دقیق تری ایجاد کند. این کار ممکن است برای مدت طولانی اجرا شود.",
"TaskKeyframeExtractor": "استخراج کننده فریم کلیدی",
- "External": "خارجی"
+ "External": "خارجی",
+ "HearingImpaired": "مشکل شنوایی"
}
diff --git a/Emby.Server.Implementations/Localization/Core/gsw.json b/Emby.Server.Implementations/Localization/Core/gsw.json
index bd8cec710..ac9da1dd1 100644
--- a/Emby.Server.Implementations/Localization/Core/gsw.json
+++ b/Emby.Server.Implementations/Localization/Core/gsw.json
@@ -1,7 +1,7 @@
{
"Albums": "Alben",
"AppDeviceValues": "App: {0}, Gerät: {1}",
- "Application": "Anwendung",
+ "Application": "Applikation",
"Artists": "Künstler",
"AuthenticationSucceededWithUserName": "{0} hat sich angemeldet",
"Books": "Bücher",
@@ -14,7 +14,7 @@
"FailedLoginAttemptWithUserName": "Fehlgeschlagener Anmeldeversuch von {0}",
"Favorites": "Favoriten",
"Folders": "Ordner",
- "Genres": "Genres",
+ "Genres": "Genre",
"HeaderAlbumArtists": "Album-Künstler",
"HeaderContinueWatching": "weiter schauen",
"HeaderFavoriteAlbums": "Lieblingsalben",
@@ -49,7 +49,7 @@
"NotificationOptionAudioPlayback": "Audiowedergab gstartet",
"NotificationOptionAudioPlaybackStopped": "Audiwedergab gstoppt",
"NotificationOptionCameraImageUploaded": "Foti ueglade",
- "NotificationOptionInstallationFailed": "Installationsfehler",
+ "NotificationOptionInstallationFailed": "Installationsfähler",
"NotificationOptionNewLibraryContent": "Nöie Inhaut hinzuegfüegt",
"NotificationOptionPluginError": "Plugin-Fäuer",
"NotificationOptionPluginInstalled": "Plugin installiert",
@@ -120,5 +120,9 @@
"Forced": "Erzwungen",
"Default": "Standard",
"TaskOptimizeDatabase": "Datenbank optimieren",
- "External": "Extern"
+ "External": "Extern",
+ "TaskOptimizeDatabaseDescription": "Kompromiert d Datenbank und trennt freie Speicherplatz. Durch die Ufagb cha d Leistig nach em ne Scan vor Bibliothek oder andere Ufgabe verbesseret werde.",
+ "HearingImpaired": "Hörgschädigti",
+ "TaskKeyframeExtractor": "Keyframe-Extraktor",
+ "TaskKeyframeExtractorDescription": "Extrahiert Keyframes us Videodateien zum erstelle vo genauere HLS Playliste. Die Ufgab cha für e langi Zyt laufe."
}
diff --git a/Emby.Server.Implementations/Localization/Core/hi.json b/Emby.Server.Implementations/Localization/Core/hi.json
index 182b43ffc..a0e2f04a1 100644
--- a/Emby.Server.Implementations/Localization/Core/hi.json
+++ b/Emby.Server.Implementations/Localization/Core/hi.json
@@ -67,5 +67,11 @@
"Plugin": "प्लग-इन",
"Playlists": "प्लेलिस्ट",
"Photos": "तस्वीरें",
- "External": "बाहरी"
+ "External": "बाहरी",
+ "PluginUpdatedWithName": "{0} अपडेट हुए",
+ "ScheduledTaskStartedWithName": "{0} शुरू हुए",
+ "Songs": "गाने",
+ "UserStartedPlayingItemWithValues": "{0} {2} पर {1} खेल रहे हैं",
+ "UserStoppedPlayingItemWithValues": "{0} ने {2} पर {1} खेलना खत्म किया",
+ "StartupEmbyServerIsLoading": "जेलीफ़िन सर्वर लोड हो रहा है। कृपया शीघ्र ही पुन: प्रयास करें।"
}
diff --git a/Emby.Server.Implementations/Localization/Core/id.json b/Emby.Server.Implementations/Localization/Core/id.json
index 695c0f404..87ce07da3 100644
--- a/Emby.Server.Implementations/Localization/Core/id.json
+++ b/Emby.Server.Implementations/Localization/Core/id.json
@@ -82,7 +82,7 @@
"MessageServerConfigurationUpdated": "Konfigurasi server telah diperbarui",
"MessageNamedServerConfigurationUpdatedWithValue": "Bagian konfigurasi server {0} telah diperbarui",
"FailedLoginAttemptWithUserName": "Gagal melakukan login dari {0}",
- "CameraImageUploadedFrom": "Gambar kamera baru telah diunggah dari {0}",
+ "CameraImageUploadedFrom": "Sebuah gambar kamera baru telah diunggah dari {0}",
"DeviceOfflineWithName": "{0} telah terputus",
"DeviceOnlineWithName": "{0} telah terhubung",
"NotificationOptionVideoPlaybackStopped": "Pemutaran video berhenti",
diff --git a/Emby.Server.Implementations/Localization/Core/is.json b/Emby.Server.Implementations/Localization/Core/is.json
index b262a8b42..a40f49506 100644
--- a/Emby.Server.Implementations/Localization/Core/is.json
+++ b/Emby.Server.Implementations/Localization/Core/is.json
@@ -107,5 +107,14 @@
"TasksApplicationCategory": "Forrit",
"TasksLibraryCategory": "Miðlasafn",
"TasksMaintenanceCategory": "Viðhald",
- "Default": "Sjálfgefið"
+ "Default": "Sjálfgefið",
+ "TaskCleanActivityLog": "Hreinsa athafnaskrá",
+ "TaskRefreshPeople": "Endurnýja fólk",
+ "TaskDownloadMissingSubtitles": "Sækja texta sem vantar",
+ "TaskOptimizeDatabase": "Fínstilla gagnagrunn",
+ "Undefined": "Óskilgreint",
+ "TaskCleanLogsDescription": "Eyðir færslu skrám sem eru meira en {0} gömul.",
+ "TaskCleanLogs": "Hreinsa færslu skrá",
+ "TaskDownloadMissingSubtitlesDescription": "Leitar á netinu að texta sem vantar miðað við uppsetningu lýsigagna.",
+ "HearingImpaired": "Heyrnarskertur"
}
diff --git a/Emby.Server.Implementations/Localization/Core/nl.json b/Emby.Server.Implementations/Localization/Core/nl.json
index e03747cbe..01a2ab273 100644
--- a/Emby.Server.Implementations/Localization/Core/nl.json
+++ b/Emby.Server.Implementations/Localization/Core/nl.json
@@ -58,8 +58,8 @@
"NotificationOptionServerRestartRequired": "Server herstart nodig",
"NotificationOptionTaskFailed": "Geplande taak mislukt",
"NotificationOptionUserLockedOut": "Gebruiker is vergrendeld",
- "NotificationOptionVideoPlayback": "Video gestart",
- "NotificationOptionVideoPlaybackStopped": "Video gestopt",
+ "NotificationOptionVideoPlayback": "Afspelen van video gestart",
+ "NotificationOptionVideoPlaybackStopped": "Afspelen van video gestopt",
"Photos": "Foto's",
"Playlists": "Afspeellijsten",
"Plugin": "Plug-in",
@@ -95,26 +95,26 @@
"TaskDownloadMissingSubtitlesDescription": "Zoekt op het internet naar ontbrekende ondertiteling gebaseerd op metadataconfiguratie.",
"TaskDownloadMissingSubtitles": "Ontbrekende ondertiteling downloaden",
"TaskRefreshChannelsDescription": "Vernieuwt informatie van internet kanalen.",
- "TaskRefreshChannels": "Vernieuw Kanalen",
+ "TaskRefreshChannels": "Kanalen vernieuwen",
"TaskCleanTranscodeDescription": "Verwijdert transcode bestanden ouder dan 1 dag.",
"TaskCleanLogs": "Logboekmap opschonen",
"TaskCleanTranscode": "Transcoderingsmap opschonen",
"TaskUpdatePluginsDescription": "Downloadt en installeert updates van plug-ins waarvoor automatisch bijwerken is ingeschakeld.",
"TaskUpdatePlugins": "Plug-ins bijwerken",
- "TaskRefreshPeopleDescription": "Update metadata for acteurs en regisseurs in de media bibliotheek.",
+ "TaskRefreshPeopleDescription": "Updatet metadata voor acteurs en regisseurs in je mediabibliotheek.",
"TaskRefreshPeople": "Personen vernieuwen",
"TaskCleanLogsDescription": "Verwijdert log bestanden ouder dan {0} dagen.",
"TaskRefreshLibraryDescription": "Scant de mediabibliotheek op nieuwe bestanden en vernieuwt de metadata.",
"TaskRefreshLibrary": "Mediabibliotheek scannen",
- "TaskRefreshChapterImagesDescription": "Maakt thumbnails aan voor videos met hoofdstukken.",
- "TaskRefreshChapterImages": "Hoofdstukafbeeldingen uitpakken",
+ "TaskRefreshChapterImagesDescription": "Maakt voorbeeldafbeedingen aan voor video's met hoofdstukken.",
+ "TaskRefreshChapterImages": "Hoofdstukafbeeldingen extraheren",
"TaskCleanCacheDescription": "Verwijdert gecachte bestanden die het systeem niet langer nodig heeft.",
"TaskCleanCache": "Cache-map opschonen",
- "TasksChannelsCategory": "Internet Kanalen",
+ "TasksChannelsCategory": "Internetkanalen",
"TasksApplicationCategory": "Toepassing",
"TasksLibraryCategory": "Bibliotheek",
"TasksMaintenanceCategory": "Onderhoud",
- "TaskCleanActivityLogDescription": "Verwijdert activiteiten logs ouder dan de ingestelde tijd.",
+ "TaskCleanActivityLogDescription": "Verwijdert activiteitenlogs ouder dan de ingestelde leeftijd.",
"TaskCleanActivityLog": "Activiteitenlogboek legen",
"Undefined": "Niet gedefinieerd",
"Forced": "Geforceerd",
diff --git a/Emby.Server.Implementations/Localization/Core/pr.json b/Emby.Server.Implementations/Localization/Core/pr.json
index 506c14fdc..87800a2fe 100644
--- a/Emby.Server.Implementations/Localization/Core/pr.json
+++ b/Emby.Server.Implementations/Localization/Core/pr.json
@@ -13,5 +13,16 @@
"DeviceOfflineWithName": "{0} abandoned ship",
"AppDeviceValues": "Captain: {0}, Ship: {1}",
"CameraImageUploadedFrom": "Yer looking glass has glimpsed another painting from {0}",
- "Collections": "Barrels"
+ "Collections": "Barrels",
+ "ItemAddedWithName": "{0} is now with yer treasure",
+ "Default": "Normal-like",
+ "FailedLoginAttemptWithUserName": "Ye failed to get in, try from {0}",
+ "Favorites": "Finest Loot",
+ "ItemRemovedWithName": "{0} was taken from yer treasure",
+ "LabelIpAddressValue": "Ship's coordinates: {0}",
+ "Genres": "types o' booty",
+ "TaskDownloadMissingSubtitlesDescription": "Scours the seven seas o' the internet for subtitles that be missin' based on the captain's map o' metadata.",
+ "HeaderAlbumArtists": "Buccaneers o' the musical arts",
+ "HeaderFavoriteAlbums": "Beloved booty o' musical adventures",
+ "HeaderFavoriteArtists": "Treasured scallywags o' the creative seas"
}
diff --git a/Emby.Server.Implementations/Localization/Core/pt.json b/Emby.Server.Implementations/Localization/Core/pt.json
index 39229f45f..92e0d34ae 100644
--- a/Emby.Server.Implementations/Localization/Core/pt.json
+++ b/Emby.Server.Implementations/Localization/Core/pt.json
@@ -121,5 +121,6 @@
"TaskOptimizeDatabase": "Otimizar base de dados",
"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.",
"External": "Externo",
- "HearingImpaired": "Problemas auditivos"
+ "HearingImpaired": "Problemas auditivos",
+ "TaskKeyframeExtractor": "Extrator de quadro-chave"
}
diff --git a/Emby.Server.Implementations/Localization/Core/ru.json b/Emby.Server.Implementations/Localization/Core/ru.json
index 65cf29e80..839bbcb6d 100644
--- a/Emby.Server.Implementations/Localization/Core/ru.json
+++ b/Emby.Server.Implementations/Localization/Core/ru.json
@@ -16,14 +16,14 @@
"Folders": "Папки",
"Genres": "Жанры",
"HeaderAlbumArtists": "Исполнители альбома",
- "HeaderContinueWatching": "Продолжение просмотра",
+ "HeaderContinueWatching": "Продолжить просмотр",
"HeaderFavoriteAlbums": "Избранные альбомы",
"HeaderFavoriteArtists": "Избранные исполнители",
"HeaderFavoriteEpisodes": "Избранные эпизоды",
"HeaderFavoriteShows": "Избранные сериалы",
"HeaderFavoriteSongs": "Избранные композиции",
"HeaderLiveTV": "Эфир",
- "HeaderNextUp": "Очередное",
+ "HeaderNextUp": "Следующий",
"HeaderRecordingGroups": "Группы записей",
"HomeVideos": "Домашние видео",
"Inherit": "Наследуемое",
@@ -70,7 +70,7 @@
"ScheduledTaskFailedWithName": "{0} - неудачна",
"ScheduledTaskStartedWithName": "{0} - запущена",
"ServerNameNeedsToBeRestarted": "Необходим перезапуск {0}",
- "Shows": "Передачи",
+ "Shows": "Телешоу",
"Songs": "Композиции",
"StartupEmbyServerIsLoading": "Jellyfin Server загружается. Повторите попытку в ближайшее время.",
"SubtitleDownloadFailureForItem": "Субтитры к {0} не удалось загрузить",
diff --git a/Emby.Server.Implementations/Localization/Core/sl-SI.json b/Emby.Server.Implementations/Localization/Core/sl-SI.json
index d845accac..4c23f71ef 100644
--- a/Emby.Server.Implementations/Localization/Core/sl-SI.json
+++ b/Emby.Server.Implementations/Localization/Core/sl-SI.json
@@ -123,5 +123,6 @@
"TaskOptimizeDatabase": "Optimiziraj bazo podatkov",
"TaskKeyframeExtractor": "Ekstraktor ključnih sličic",
"External": "Zunanji",
- "TaskKeyframeExtractorDescription": "Iz video datoteke Izvleče ključne sličice, da ustvari bolj natančne sezname predvajanja HLS. Proces lahko traja dolgo časa."
+ "TaskKeyframeExtractorDescription": "Iz video datoteke Izvleče ključne sličice, da ustvari bolj natančne sezname predvajanja HLS. Proces lahko traja dolgo časa.",
+ "HearingImpaired": "Oslabljen sluh"
}
diff --git a/Emby.Server.Implementations/Localization/Core/sv.json b/Emby.Server.Implementations/Localization/Core/sv.json
index 318a0f3cf..785e6b226 100644
--- a/Emby.Server.Implementations/Localization/Core/sv.json
+++ b/Emby.Server.Implementations/Localization/Core/sv.json
@@ -66,7 +66,7 @@
"PluginInstalledWithName": "{0} installerades",
"PluginUninstalledWithName": "{0} avinstallerades",
"PluginUpdatedWithName": "{0} uppdaterades",
- "ProviderValue": "Källa: {0}",
+ "ProviderValue": "Leverantör: {0}",
"ScheduledTaskFailedWithName": "{0} misslyckades",
"ScheduledTaskStartedWithName": "{0} startades",
"ServerNameNeedsToBeRestarted": "{0} behöver startas om",
diff --git a/Emby.Server.Implementations/Localization/Core/uk.json b/Emby.Server.Implementations/Localization/Core/uk.json
index 92ce616f2..ff77fb8c5 100644
--- a/Emby.Server.Implementations/Localization/Core/uk.json
+++ b/Emby.Server.Implementations/Localization/Core/uk.json
@@ -86,7 +86,7 @@
"Shows": "Шоу",
"ServerNameNeedsToBeRestarted": "{0} потрібно перезапустити",
"ScheduledTaskStartedWithName": "{0} розпочато",
- "ScheduledTaskFailedWithName": "Помилка {0}",
+ "ScheduledTaskFailedWithName": "{0} незавершено, збій",
"ProviderValue": "Постачальник: {0}",
"PluginUpdatedWithName": "{0} оновлено",
"PluginUninstalledWithName": "{0} видалено",
diff --git a/Emby.Server.Implementations/Localization/Core/zh-HK.json b/Emby.Server.Implementations/Localization/Core/zh-HK.json
index baa9ecc1c..cdc25ec7c 100644
--- a/Emby.Server.Implementations/Localization/Core/zh-HK.json
+++ b/Emby.Server.Implementations/Localization/Core/zh-HK.json
@@ -9,15 +9,15 @@
"Channels": "頻道",
"ChapterNameValue": "章節 {0}",
"Collections": "合輯",
- "DeviceOfflineWithName": "{0} 已經斷開連結",
+ "DeviceOfflineWithName": "{0} 已經斷開連接",
"DeviceOnlineWithName": "{0} 已經連接",
- "FailedLoginAttemptWithUserName": "來自 {0} 的登入失敗",
+ "FailedLoginAttemptWithUserName": "{0} 登入失敗",
"Favorites": "我的最愛",
"Folders": "資料夾",
"Genres": "風格",
"HeaderAlbumArtists": "專輯藝人",
"HeaderContinueWatching": "繼續觀看",
- "HeaderFavoriteAlbums": "最愛專輯",
+ "HeaderFavoriteAlbums": "最愛的專輯",
"HeaderFavoriteArtists": "最愛的藝人",
"HeaderFavoriteEpisodes": "最愛的劇集",
"HeaderFavoriteShows": "最愛的節目",
@@ -44,10 +44,10 @@
"NameSeasonNumber": "第 {0} 季",
"NameSeasonUnknown": "未知季數",
"NewVersionIsAvailable": "新版本的 Jellyfin 伺服器可供下載。",
- "NotificationOptionApplicationUpdateAvailable": "有可用的應用程式更新",
+ "NotificationOptionApplicationUpdateAvailable": "有可用的更新",
"NotificationOptionApplicationUpdateInstalled": "應用程式已更新",
- "NotificationOptionAudioPlayback": "開始播放音頻",
- "NotificationOptionAudioPlaybackStopped": "已停止播放音頻",
+ "NotificationOptionAudioPlayback": "開始播放音訊",
+ "NotificationOptionAudioPlaybackStopped": "已停止播放音訊",
"NotificationOptionCameraImageUploaded": "相片已上傳",
"NotificationOptionInstallationFailed": "安裝失敗",
"NotificationOptionNewLibraryContent": "已添加新内容",
diff --git a/Emby.Server.Implementations/Localization/LocalizationManager.cs b/Emby.Server.Implementations/Localization/LocalizationManager.cs
index b418c7877..166b71b4a 100644
--- a/Emby.Server.Implementations/Localization/LocalizationManager.cs
+++ b/Emby.Server.Implementations/Localization/LocalizationManager.cs
@@ -3,6 +3,7 @@ using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
+using System.Linq;
using System.Reflection;
using System.Text.Json;
using System.Threading.Tasks;
@@ -25,7 +26,7 @@ namespace Emby.Server.Implementations.Localization
private const string CulturesPath = "Emby.Server.Implementations.Localization.iso6392.txt";
private const string CountriesPath = "Emby.Server.Implementations.Localization.countries.json";
private static readonly Assembly _assembly = typeof(LocalizationManager).Assembly;
- private static readonly string[] _unratedValues = { "n/a", "unrated", "not rated" };
+ private static readonly string[] _unratedValues = { "n/a", "unrated", "not rated", "nr" };
private readonly IServerConfigurationManager _configurationManager;
private readonly ILogger<LocalizationManager> _logger;
@@ -86,12 +87,10 @@ namespace Emby.Server.Implementations.Localization
var name = parts[0];
dict.Add(name, new ParentalRating(name, value));
}
-#if DEBUG
else
{
_logger.LogWarning("Malformed line in ratings file for country {CountryCode}", countryCode);
}
-#endif
}
_allParentalRatings[countryCode] = dict;
@@ -184,80 +183,149 @@ namespace Emby.Server.Implementations.Localization
/// <inheritdoc />
public IEnumerable<ParentalRating> GetParentalRatings()
- => GetParentalRatingsDictionary().Values;
-
- /// <summary>
- /// Gets the parental ratings dictionary.
- /// </summary>
- /// <returns><see cref="Dictionary{String, ParentalRating}" />.</returns>
- private Dictionary<string, ParentalRating> GetParentalRatingsDictionary()
{
- var countryCode = _configurationManager.Configuration.MetadataCountryCode;
+ // Use server default language for ratings
+ // Fall back to empty list if there are no parental ratings for that language
+ var ratings = GetParentalRatingsDictionary()?.Values.ToList()
+ ?? new List<ParentalRating>();
+
+ // Add common ratings to ensure them being available for selection
+ // Based on the US rating system due to it being the main source of rating in the metadata providers
+ // Unrated
+ if (!ratings.Any(x => x.Value is null))
+ {
+ ratings.Add(new ParentalRating("Unrated", null));
+ }
- if (string.IsNullOrEmpty(countryCode))
+ // Minimum rating possible
+ if (!ratings.Any(x => x.Value == 0))
+ {
+ ratings.Add(new ParentalRating("Approved", 0));
+ }
+
+ // Matches PG (this has different age restrictions depending on country)
+ if (!ratings.Any(x => x.Value == 10))
+ {
+ ratings.Add(new ParentalRating("10", 10));
+ }
+
+ // Matches PG-13
+ if (!ratings.Any(x => x.Value == 13))
+ {
+ ratings.Add(new ParentalRating("13", 13));
+ }
+
+ // Matches TV-14
+ if (!ratings.Any(x => x.Value == 14))
+ {
+ ratings.Add(new ParentalRating("14", 14));
+ }
+
+ // Catchall if max rating of country is less than 21
+ // Using 21 instead of 18 to be sure to allow access to all rated content except adult and banned
+ if (!ratings.Any(x => x.Value >= 21))
+ {
+ ratings.Add(new ParentalRating("21", 21));
+ }
+
+ // A lot of countries don't excplicitly have a seperate rating for adult content
+ if (!ratings.Any(x => x.Value == 1000))
+ {
+ ratings.Add(new ParentalRating("XXX", 1000));
+ }
+
+ // A lot of countries don't excplicitly have a seperate rating for banned content
+ if (!ratings.Any(x => x.Value == 1001))
{
- countryCode = "us";
+ ratings.Add(new ParentalRating("Banned", 1001));
}
- return GetRatings(countryCode)
- ?? GetRatings("us")
- ?? throw new InvalidOperationException($"Invalid resource path: '{CountriesPath}'");
+ return ratings.OrderBy(r => r.Value);
}
/// <summary>
- /// Gets the ratings.
+ /// Gets the parental ratings dictionary.
/// </summary>
- /// <param name="countryCode">The country code.</param>
- /// <returns>The ratings.</returns>
- private Dictionary<string, ParentalRating>? GetRatings(string countryCode)
+ /// <param name="countryCode">The optional two letter ISO language string.</param>
+ /// <returns><see cref="Dictionary{String, ParentalRating}" />.</returns>
+ private Dictionary<string, ParentalRating>? GetParentalRatingsDictionary(string? countryCode = null)
{
- _allParentalRatings.TryGetValue(countryCode, out var value);
+ // Fallback to server default if no country code is specified.
+ if (string.IsNullOrEmpty(countryCode))
+ {
+ countryCode = _configurationManager.Configuration.MetadataCountryCode;
+ }
- return value;
+ if (_allParentalRatings.TryGetValue(countryCode, out var countryValue))
+ {
+ return countryValue;
+ }
+
+ return null;
}
/// <inheritdoc />
- public int? GetRatingLevel(string rating)
+ public int? GetRatingLevel(string rating, string? countryCode = null)
{
ArgumentException.ThrowIfNullOrEmpty(rating);
+ // Handle unrated content
if (_unratedValues.Contains(rating.AsSpan(), StringComparison.OrdinalIgnoreCase))
{
return null;
}
// Fairly common for some users to have "Rated R" in their rating field
+ rating = rating.Replace("Rated :", string.Empty, StringComparison.OrdinalIgnoreCase);
rating = rating.Replace("Rated ", string.Empty, StringComparison.OrdinalIgnoreCase);
- var ratingsDictionary = GetParentalRatingsDictionary();
-
- if (ratingsDictionary.TryGetValue(rating, out ParentalRating? value))
+ // Use rating system matching the language
+ if (!string.IsNullOrEmpty(countryCode))
{
- return value.Value;
+ var ratingsDictionary = GetParentalRatingsDictionary(countryCode);
+ if (ratingsDictionary is not null && ratingsDictionary.TryGetValue(rating, out ParentalRating? value))
+ {
+ return value.Value;
+ }
+ }
+ else
+ {
+ // Fall back to server default language for ratings check
+ // If it has no ratings, use the US ratings
+ var ratingsDictionary = GetParentalRatingsDictionary() ?? GetParentalRatingsDictionary("us");
+ if (ratingsDictionary is not null && ratingsDictionary.TryGetValue(rating, out ParentalRating? value))
+ {
+ return value.Value;
+ }
}
- // If we don't find anything check all ratings systems
+ // If we don't find anything, check all ratings systems
foreach (var dictionary in _allParentalRatings.Values)
{
- if (dictionary.TryGetValue(rating, out value))
+ if (dictionary.TryGetValue(rating, out var value))
{
return value.Value;
}
}
- // Try splitting by : to handle "Germany: FSK 18"
- var index = rating.IndexOf(':', StringComparison.Ordinal);
- if (index != -1)
+ // Try splitting by : to handle "Germany: FSK-18"
+ if (rating.Contains(':', StringComparison.OrdinalIgnoreCase))
{
- var trimmedRating = rating.AsSpan(index).TrimStart(':').Trim();
+ return GetRatingLevel(rating.AsSpan().RightPart(':').ToString());
+ }
- if (!trimmedRating.IsEmpty)
- {
- return GetRatingLevel(trimmedRating.ToString());
- }
+ // Handle prefix country code to handle "DE-18"
+ if (rating.Contains('-', StringComparison.OrdinalIgnoreCase))
+ {
+ var ratingSpan = rating.AsSpan();
+
+ // Extract culture from country prefix
+ var culture = FindLanguageInfo(ratingSpan.LeftPart('-').ToString());
+
+ // Check rating system of culture
+ return GetRatingLevel(ratingSpan.RightPart('-').ToString(), culture?.TwoLetterISOLanguageName);
}
- // TODO: Further improve by normalizing out all spaces and dashes
return null;
}
diff --git a/Emby.Server.Implementations/Localization/Ratings/0-prefer.csv b/Emby.Server.Implementations/Localization/Ratings/0-prefer.csv
new file mode 100644
index 000000000..36886ba76
--- /dev/null
+++ b/Emby.Server.Implementations/Localization/Ratings/0-prefer.csv
@@ -0,0 +1,11 @@
+E,0
+EC,0
+T,7
+M,18
+AO,18
+UR,18
+RP,18
+X,1000
+XX,1000
+XXX,1000
+XXXX,1000
diff --git a/Emby.Server.Implementations/Localization/Ratings/au.csv b/Emby.Server.Implementations/Localization/Ratings/au.csv
index 11f4ed94c..4ab808ae9 100644
--- a/Emby.Server.Implementations/Localization/Ratings/au.csv
+++ b/Emby.Server.Implementations/Localization/Ratings/au.csv
@@ -1,7 +1,13 @@
-AU-G,1
-AU-PG,5
-AU-M,6
-AU-MA15+,7
-AU-R18+,9
-AU-X18+,10
-AU-RC,11
+Exempt,0
+G,0
+7+,7
+M,15
+MA,15
+MA15+,15
+PG,16
+16+,16
+R,18
+R18+,18
+X18+,18
+18+,18
+X,1000
diff --git a/Emby.Server.Implementations/Localization/Ratings/be.csv b/Emby.Server.Implementations/Localization/Ratings/be.csv
index d3937caf7..d171a7132 100644
--- a/Emby.Server.Implementations/Localization/Ratings/be.csv
+++ b/Emby.Server.Implementations/Localization/Ratings/be.csv
@@ -1,6 +1,11 @@
-BE-AL,1
-BE-MG6,2
-BE-6,3
-BE-9,5
-BE-12,6
-BE-16,8
+AL,0
+KT,0
+TOUS,0
+MG6,6
+6,6
+9,9
+KNT,12
+12,12
+14,14
+16,16
+18,18
diff --git a/Emby.Server.Implementations/Localization/Ratings/br.csv b/Emby.Server.Implementations/Localization/Ratings/br.csv
index e5edaf62c..5ec1eb262 100644
--- a/Emby.Server.Implementations/Localization/Ratings/br.csv
+++ b/Emby.Server.Implementations/Localization/Ratings/br.csv
@@ -1,6 +1,8 @@
-BR-L,1
-BR-10,5
-BR-12,7
-BR-14,8
-BR-16,8
-BR-18,9
+Livre,0
+L,0
+ER,9
+10,10
+12,12
+14,14
+16,16
+18,18
diff --git a/Emby.Server.Implementations/Localization/Ratings/ca.csv b/Emby.Server.Implementations/Localization/Ratings/ca.csv
index 5aef0580f..336ee2806 100644
--- a/Emby.Server.Implementations/Localization/Ratings/ca.csv
+++ b/Emby.Server.Implementations/Localization/Ratings/ca.csv
@@ -1,6 +1,20 @@
-CA-G,1
-CA-PG,5
-CA-14A,7
-CA-A,8
-CA-18A,9
-CA-R,10
+E,0
+G,0
+TV-Y,0
+TV-G,0
+TV-Y7,7
+TV-Y7-FV,7
+PG,9
+TV-PG,9
+PG-13,13
+13+,13
+TV-14,14
+14A,14
+16+,16
+NC-17,17
+R,18
+TV-MA,18
+18A,18
+18+,18
+A,1000
+Prohibited,1001
diff --git a/Emby.Server.Implementations/Localization/Ratings/co.csv b/Emby.Server.Implementations/Localization/Ratings/co.csv
index 9684fa052..e1e96c590 100644
--- a/Emby.Server.Implementations/Localization/Ratings/co.csv
+++ b/Emby.Server.Implementations/Localization/Ratings/co.csv
@@ -1,8 +1,7 @@
-CO-T,1
-CO-7,5
-CO-12,7
-CO-15,8
-CO-18,10
-CO-X,100
-CO-BANNED,15
-CO-E,15
+T,0
+7,7
+12,12
+15,15
+18,18
+X,1000
+Prohibited,1001
diff --git a/Emby.Server.Implementations/Localization/Ratings/de.csv b/Emby.Server.Implementations/Localization/Ratings/de.csv
index f944a140d..d633a5dab 100644
--- a/Emby.Server.Implementations/Localization/Ratings/de.csv
+++ b/Emby.Server.Implementations/Localization/Ratings/de.csv
@@ -1,10 +1,12 @@
-DE-0,1
-FSK-0,1
-DE-6,5
-FSK-6,5
-DE-12,7
-FSK-12,7
-DE-16,8
-FSK-16,8
-DE-18,9
-FSK-18,9
+Educational,0
+Infoprogramm,0
+FSK-0,0
+0,0
+FSK-6,6
+6,6
+FSK-12,12
+12,12
+FSK-16,16
+16,16
+FSK-18,18
+18,18
diff --git a/Emby.Server.Implementations/Localization/Ratings/dk.csv b/Emby.Server.Implementations/Localization/Ratings/dk.csv
index 5364ae1f2..4ef63b2ea 100644
--- a/Emby.Server.Implementations/Localization/Ratings/dk.csv
+++ b/Emby.Server.Implementations/Localization/Ratings/dk.csv
@@ -1,4 +1,7 @@
-DA-A,1
-DA-7,5
-DA-11,6
-DA-15,8
+F,0
+A,0
+7,7
+11,11
+12,12
+15,15
+16,16
diff --git a/Emby.Server.Implementations/Localization/Ratings/es.csv b/Emby.Server.Implementations/Localization/Ratings/es.csv
index 887d91ba6..0bc1d3f7d 100644
--- a/Emby.Server.Implementations/Localization/Ratings/es.csv
+++ b/Emby.Server.Implementations/Localization/Ratings/es.csv
@@ -1,6 +1,24 @@
-ES-A,1
-ES-APTA,1
-ES-7,3
-ES-12,6
-ES-16,8
-ES-18,11
+A,0
+A/fig,0
+A/i,0
+A/fig/i,0
+APTA,0
+TP,0
+0+,0
+6+,6
+7/fig,7
+7/i,7
+7/i/fig,7
+7,7
+9+,9
+10,10
+12,12
+12/fig,12
+13,13
+14,14
+16,16
+16/fig,16
+18,18
+18/fig,18
+X,1000
+Banned,1001
diff --git a/Emby.Server.Implementations/Localization/Ratings/fi.csv b/Emby.Server.Implementations/Localization/Ratings/fi.csv
index 782785890..7ff92f259 100644
--- a/Emby.Server.Implementations/Localization/Ratings/fi.csv
+++ b/Emby.Server.Implementations/Localization/Ratings/fi.csv
@@ -1,10 +1,10 @@
-FI-S,1
-FI-T,1
-FI-7,4
-FI-12,5
-FI-16,8
-FI-18,9
-FI-K7,4
-FI-K12,5
-FI-K16,8
-FI-K18,9
+S,0
+T,0
+K7,7
+7,7
+K12,12
+12,12
+K16,16
+16,16
+K18,18
+18,18
diff --git a/Emby.Server.Implementations/Localization/Ratings/fr.csv b/Emby.Server.Implementations/Localization/Ratings/fr.csv
index f586a3fa9..774a70589 100644
--- a/Emby.Server.Implementations/Localization/Ratings/fr.csv
+++ b/Emby.Server.Implementations/Localization/Ratings/fr.csv
@@ -1,5 +1,12 @@
-FR-U,1
-FR-10,5
-FR-12,7
-FR-16,9
-FR-18,10
+Public Averti,0
+Tous Publics,0
+U,0
+0+,0
+6+,6
+9+,9
+10,10
+12,12
+14+,14
+16,16
+18,18
+X,1000
diff --git a/Emby.Server.Implementations/Localization/Ratings/gb.csv b/Emby.Server.Implementations/Localization/Ratings/gb.csv
index c1f7d0452..75b1c2058 100644
--- a/Emby.Server.Implementations/Localization/Ratings/gb.csv
+++ b/Emby.Server.Implementations/Localization/Ratings/gb.csv
@@ -1,7 +1,22 @@
-GB-U,1
-GB-PG,5
-GB-12,6
-GB-12A,7
-GB-15,8
-GB-18,9
-GB-R18,15
+All,0
+E,0
+G,0
+U,0
+0+,0
+6+,6
+7+,7
+PG,8
+9+,9
+12,12
+12+,12
+12A,12
+Teen,13
+13+,13
+14+,14
+15,15
+16,16
+Caution,18
+18,18
+Mature,1000
+Adult,1000
+R18,1000
diff --git a/Emby.Server.Implementations/Localization/Ratings/ie.csv b/Emby.Server.Implementations/Localization/Ratings/ie.csv
index e42be5cd4..6ef2e5012 100644
--- a/Emby.Server.Implementations/Localization/Ratings/ie.csv
+++ b/Emby.Server.Implementations/Localization/Ratings/ie.csv
@@ -1,6 +1,9 @@
-IE-G,1
-IE-PG,5
-IE-12A,7
-IE-15A,8
-IE-16,9
-IE-18,10
+G,4
+PG,12
+12,12
+12A,12
+12PG,12
+15,15
+15A,15
+16,16
+18,18
diff --git a/Emby.Server.Implementations/Localization/Ratings/jp.csv b/Emby.Server.Implementations/Localization/Ratings/jp.csv
index a8fc2d143..bfb5fdaae 100644
--- a/Emby.Server.Implementations/Localization/Ratings/jp.csv
+++ b/Emby.Server.Implementations/Localization/Ratings/jp.csv
@@ -1,4 +1,11 @@
-JP-G,1
-JP-PG12,7
-JP-15+,8
-JP-18+,10
+A,0
+G,0
+B,12
+PG12,12
+C,15
+15+,15
+R15+,15
+16+,16
+D,17
+Z,18
+18+,18
diff --git a/Emby.Server.Implementations/Localization/Ratings/kz.csv b/Emby.Server.Implementations/Localization/Ratings/kz.csv
index d546bff53..e26b32b67 100644
--- a/Emby.Server.Implementations/Localization/Ratings/kz.csv
+++ b/Emby.Server.Implementations/Localization/Ratings/kz.csv
@@ -1,7 +1,6 @@
-KZ-6-,0
-KZ-6+,6
-KZ-12+,12
-KZ-14+,14
-KZ-16+,16
-KZ-18+,18
-KZ-21+,21
+K,0
+БА,12
+Б14,14
+E16,16
+E18,18
+HA,18
diff --git a/Emby.Server.Implementations/Localization/Ratings/mx.csv b/Emby.Server.Implementations/Localization/Ratings/mx.csv
index 785a8ba22..305912f23 100644
--- a/Emby.Server.Implementations/Localization/Ratings/mx.csv
+++ b/Emby.Server.Implementations/Localization/Ratings/mx.csv
@@ -1,6 +1,6 @@
-MX-AA,1
-MX-A,5
-MX-B,7
-MX-B-15,8
-MX-C,9
-MX-D,10
+A,0
+AA,0
+B,12
+B-15,15
+C,18
+D,1000
diff --git a/Emby.Server.Implementations/Localization/Ratings/nl.csv b/Emby.Server.Implementations/Localization/Ratings/nl.csv
index 8c005092e..44f372b2d 100644
--- a/Emby.Server.Implementations/Localization/Ratings/nl.csv
+++ b/Emby.Server.Implementations/Localization/Ratings/nl.csv
@@ -1,6 +1,8 @@
-NL-AL,1
-NL-MG6,2
-NL-6,3
-NL-9,5
-NL-12,6
-NL-16,8
+AL,0
+MG6,6
+6,6
+9,9
+12,12
+14,14
+16,16
+18,18
diff --git a/Emby.Server.Implementations/Localization/Ratings/no.csv b/Emby.Server.Implementations/Localization/Ratings/no.csv
index 127407be8..c8f8e93db 100644
--- a/Emby.Server.Implementations/Localization/Ratings/no.csv
+++ b/Emby.Server.Implementations/Localization/Ratings/no.csv
@@ -1,6 +1,9 @@
-NO-A,1
-NO-6,3
-NO-9,4
-NO-12,5
-NO-15,8
-NO-18,9
+A,0
+6,6
+7,7
+9,9
+11,11
+12,12
+15,15
+18,18
+Not approved,1001
diff --git a/Emby.Server.Implementations/Localization/Ratings/nz.csv b/Emby.Server.Implementations/Localization/Ratings/nz.csv
index bba99b764..f617f0c39 100644
--- a/Emby.Server.Implementations/Localization/Ratings/nz.csv
+++ b/Emby.Server.Implementations/Localization/Ratings/nz.csv
@@ -1,11 +1,15 @@
-NZ-G,1
-NZ-PG,5
-NZ-M,6
-NZ-R13,7
-NZ-RP13,7
-NZ-R15,8
-NZ-RP16,9
-NZ-R16,9
-NZ-R18,10
-NZ-R,10
-NZ-MA,10
+Exempt,0
+G,0
+GY,13
+PG,13
+R13,13
+RP13,13
+R15,15
+M,16
+R16,16
+RP16,16
+GA,18
+R18,18
+MA,1000
+R,1001
+Objectionable,1001
diff --git a/Emby.Server.Implementations/Localization/Ratings/ro.csv b/Emby.Server.Implementations/Localization/Ratings/ro.csv
index 4089b282f..44c23e248 100644
--- a/Emby.Server.Implementations/Localization/Ratings/ro.csv
+++ b/Emby.Server.Implementations/Localization/Ratings/ro.csv
@@ -1 +1,6 @@
-RO-AG,1
+AG,0
+AP-12,12
+N-15,15
+IM-18,18
+IM-18-XXX,1000
+IC,1001
diff --git a/Emby.Server.Implementations/Localization/Ratings/ru.csv b/Emby.Server.Implementations/Localization/Ratings/ru.csv
index 1bc94affd..8b264070b 100644
--- a/Emby.Server.Implementations/Localization/Ratings/ru.csv
+++ b/Emby.Server.Implementations/Localization/Ratings/ru.csv
@@ -1,5 +1,6 @@
-RU-0+,1
-RU-6+,3
-RU-12+,7
-RU-16+,9
-RU-18+,10
+0+,0
+6+,6
+12+,12
+16+,16
+18+,18
+Refused classification,1001
diff --git a/Emby.Server.Implementations/Localization/Ratings/se.csv b/Emby.Server.Implementations/Localization/Ratings/se.csv
index 1443c07df..e129c3561 100644
--- a/Emby.Server.Implementations/Localization/Ratings/se.csv
+++ b/Emby.Server.Implementations/Localization/Ratings/se.csv
@@ -1,5 +1,10 @@
-SE-Btl,1
-SE-Barntillåten,1
-SE-7,3
-SE-11,5
-SE-15,8
+Alla,0
+Barntillåten,0
+Btl,0
+0+,0
+7,7
+9+,9
+10+,10
+11,11
+14,14
+15,15
diff --git a/Emby.Server.Implementations/Localization/Ratings/uk.csv b/Emby.Server.Implementations/Localization/Ratings/uk.csv
index 6c8005b3f..75b1c2058 100644
--- a/Emby.Server.Implementations/Localization/Ratings/uk.csv
+++ b/Emby.Server.Implementations/Localization/Ratings/uk.csv
@@ -1,7 +1,22 @@
-UK-U,1
-UK-PG,5
-UK-12,7
-UK-12A,7
-UK-15,9
-UK-18,10
-UK-R18,15
+All,0
+E,0
+G,0
+U,0
+0+,0
+6+,6
+7+,7
+PG,8
+9+,9
+12,12
+12+,12
+12A,12
+Teen,13
+13+,13
+14+,14
+15,15
+16,16
+Caution,18
+18,18
+Mature,1000
+Adult,1000
+R18,1000
diff --git a/Emby.Server.Implementations/Localization/Ratings/us.csv b/Emby.Server.Implementations/Localization/Ratings/us.csv
index 34c897fe3..d103ddf42 100644
--- a/Emby.Server.Implementations/Localization/Ratings/us.csv
+++ b/Emby.Server.Implementations/Localization/Ratings/us.csv
@@ -1,23 +1,50 @@
-TV-Y,1
-APPROVED,1
-G,1
-E,1
-EC,1
-TV-G,1
-TV-Y7,3
-TV-Y7-FV,4
-PG,5
-TV-PG,5
-PG-13,7
-T,7
-TV-14,8
-R,9
-M,9
-TV-MA,9
-NC-17,10
-AO,15
-RP,15
-UR,15
-NR,15
-X,15
-XXX,100
+Approved,0
+G,0
+TV-G,0
+TV-Y,0
+TV-Y7,7
+TV-Y7-FV,7
+PG,10
+PG-13,13
+TV-PG,13
+TV-PG-D,13
+TV-PG-L,13
+TV-PG-S,13
+TV-PG-V,13
+TV-PG-DL,13
+TV-PG-DS,13
+TV-PG-DV,13
+TV-PG-LS,13
+TV-PG-LV,13
+TV-PG-SV,13
+TV-PG-DLS,13
+TV-PG-DLV,13
+TV-PG-DSV,13
+TV-PG-LSV,13
+TV-PG-DLSV,13
+TV-14,14
+TV-14-D,14
+TV-14-L,14
+TV-14-S,14
+TV-14-V,14
+TV-14-DL,14
+TV-14-DS,14
+TV-14-DV,14
+TV-14-LS,14
+TV-14-LV,14
+TV-14-SV,14
+TV-14-DLS,14
+TV-14-DLV,14
+TV-14-DSV,14
+TV-14-LSV,14
+TV-14-DLSV,14
+NC-17,17
+R,17
+TV-MA,17
+TV-MA-L,17
+TV-MA-S,17
+TV-MA-V,17
+TV-MA-LS,17
+TV-MA-LV,17
+TV-MA-SV,17
+TV-MA-LSV,17
diff --git a/Emby.Server.Implementations/Plugins/PluginLoadContext.cs b/Emby.Server.Implementations/Plugins/PluginLoadContext.cs
new file mode 100644
index 000000000..d04e9cf68
--- /dev/null
+++ b/Emby.Server.Implementations/Plugins/PluginLoadContext.cs
@@ -0,0 +1,33 @@
+using System.Reflection;
+using System.Runtime.Loader;
+
+namespace Emby.Server.Implementations.Plugins;
+
+/// <summary>
+/// A custom <see cref="AssemblyLoadContext"/> for loading Jellyfin plugins.
+/// </summary>
+public class PluginLoadContext : AssemblyLoadContext
+{
+ private readonly AssemblyDependencyResolver _resolver;
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="PluginLoadContext"/> class.
+ /// </summary>
+ /// <param name="path">The path of the plugin assembly.</param>
+ public PluginLoadContext(string path) : base(true)
+ {
+ _resolver = new AssemblyDependencyResolver(path);
+ }
+
+ /// <inheritdoc />
+ protected override Assembly? Load(AssemblyName assemblyName)
+ {
+ var assemblyPath = _resolver.ResolveAssemblyToPath(assemblyName);
+ if (assemblyPath is not null)
+ {
+ return LoadFromAssemblyPath(assemblyPath);
+ }
+
+ return null;
+ }
+}
diff --git a/Emby.Server.Implementations/Plugins/PluginManager.cs b/Emby.Server.Implementations/Plugins/PluginManager.cs
index 14e7c2269..7c23254a1 100644
--- a/Emby.Server.Implementations/Plugins/PluginManager.cs
+++ b/Emby.Server.Implementations/Plugins/PluginManager.cs
@@ -5,6 +5,7 @@ using System.IO;
using System.Linq;
using System.Net.Http;
using System.Reflection;
+using System.Runtime.Loader;
using System.Text;
using System.Text.Json;
using System.Threading.Tasks;
@@ -30,6 +31,7 @@ namespace Emby.Server.Implementations.Plugins
{
private readonly string _pluginsPath;
private readonly Version _appVersion;
+ private readonly List<AssemblyLoadContext> _assemblyLoadContexts;
private readonly JsonSerializerOptions _jsonOptions;
private readonly ILogger<PluginManager> _logger;
private readonly IApplicationHost _appHost;
@@ -76,6 +78,8 @@ namespace Emby.Server.Implementations.Plugins
_appHost = appHost;
_minimumVersion = new Version(0, 0, 0, 1);
_plugins = Directory.Exists(_pluginsPath) ? DiscoverPlugins().ToList() : new List<LocalPlugin>();
+
+ _assemblyLoadContexts = new List<AssemblyLoadContext>();
}
private IHttpClientFactory HttpClientFactory
@@ -119,43 +123,78 @@ namespace Emby.Server.Implementations.Plugins
continue;
}
+ var assemblyLoadContext = new PluginLoadContext(plugin.Path);
+ _assemblyLoadContexts.Add(assemblyLoadContext);
+
+ var assemblies = new List<Assembly>(plugin.DllFiles.Count);
+ var loadedAll = true;
+
foreach (var file in plugin.DllFiles)
{
- Assembly assembly;
try
{
- assembly = Assembly.LoadFrom(file);
-
- // Load all required types to verify that the plugin will load
- assembly.GetTypes();
+ assemblies.Add(assemblyLoadContext.LoadFromAssemblyPath(file));
}
catch (FileLoadException ex)
{
- _logger.LogError(ex, "Failed to load assembly {Path}. Disabling plugin.", file);
+ _logger.LogError(ex, "Failed to load assembly {Path}. Disabling plugin", file);
ChangePluginState(plugin, PluginStatus.Malfunctioned);
- continue;
+ loadedAll = false;
+ break;
+ }
+#pragma warning disable CA1031 // Do not catch general exception types
+ catch (Exception ex)
+#pragma warning restore CA1031 // Do not catch general exception types
+ {
+ _logger.LogError(ex, "Failed to load assembly {Path}. Unknown exception was thrown. Disabling plugin", file);
+ ChangePluginState(plugin, PluginStatus.Malfunctioned);
+ loadedAll = false;
+ break;
+ }
+ }
+
+ if (!loadedAll)
+ {
+ continue;
+ }
+
+ foreach (var assembly in assemblies)
+ {
+ try
+ {
+ // Load all required types to verify that the plugin will load
+ assembly.GetTypes();
}
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);
+ _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", assembly.Location);
ChangePluginState(plugin, PluginStatus.NotSupported);
- continue;
+ break;
}
#pragma warning disable CA1031 // Do not catch general exception types
catch (Exception ex)
#pragma warning restore CA1031 // Do not catch general exception types
{
- _logger.LogError(ex, "Failed to load assembly {Path}. Unknown exception was thrown. Disabling plugin.", file);
+ _logger.LogError(ex, "Failed to load assembly {Path}. Unknown exception was thrown. Disabling plugin", assembly.Location);
ChangePluginState(plugin, PluginStatus.Malfunctioned);
- continue;
+ break;
}
- _logger.LogInformation("Loaded assembly {Assembly} from {Path}", assembly.FullName, file);
+ _logger.LogInformation("Loaded assembly {Assembly} from {Path}", assembly.FullName, assembly.Location);
yield return assembly;
}
}
}
+ /// <inheritdoc />
+ public void UnloadAssemblies()
+ {
+ foreach (var assemblyLoadContext in _assemblyLoadContexts)
+ {
+ assemblyLoadContext.Unload();
+ }
+ }
+
/// <summary>
/// Creates all the plugin instances.
/// </summary>
diff --git a/Emby.Server.Implementations/ScheduledTasks/ScheduledTaskWorker.cs b/Emby.Server.Implementations/ScheduledTasks/ScheduledTaskWorker.cs
index ee9aa8569..1af2c96d2 100644
--- a/Emby.Server.Implementations/ScheduledTasks/ScheduledTaskWorker.cs
+++ b/Emby.Server.Implementations/ScheduledTasks/ScheduledTaskWorker.cs
@@ -93,11 +93,8 @@ namespace Emby.Server.Implementations.ScheduledTasks
public ScheduledTaskWorker(IScheduledTask scheduledTask, IApplicationPaths applicationPaths, ITaskManager taskManager, ILogger logger)
{
ArgumentNullException.ThrowIfNull(scheduledTask);
-
ArgumentNullException.ThrowIfNull(applicationPaths);
-
ArgumentNullException.ThrowIfNull(taskManager);
-
ArgumentNullException.ThrowIfNull(logger);
ScheduledTask = scheduledTask;
@@ -332,7 +329,7 @@ namespace Emby.Server.Implementations.ScheduledTasks
return;
}
- _logger.LogInformation("{0} fired for task: {1}", trigger.GetType().Name, Name);
+ _logger.LogDebug("{0} fired for task: {1}", trigger.GetType().Name, Name);
trigger.Stop();
@@ -378,7 +375,7 @@ namespace Emby.Server.Implementations.ScheduledTasks
CurrentCancellationTokenSource = new CancellationTokenSource();
- _logger.LogInformation("Executing {0}", Name);
+ _logger.LogDebug("Executing {0}", Name);
((TaskManager)_taskManager).OnTaskExecuting(this);
@@ -406,7 +403,7 @@ namespace Emby.Server.Implementations.ScheduledTasks
}
catch (Exception ex)
{
- _logger.LogError(ex, "Error");
+ _logger.LogError(ex, "Error executing Scheduled Task");
failureException = ex;
diff --git a/Emby.Server.Implementations/ScheduledTasks/TaskManager.cs b/Emby.Server.Implementations/ScheduledTasks/TaskManager.cs
index 63f0beb10..42c30c959 100644
--- a/Emby.Server.Implementations/ScheduledTasks/TaskManager.cs
+++ b/Emby.Server.Implementations/ScheduledTasks/TaskManager.cs
@@ -1,5 +1,3 @@
-#nullable disable
-
#pragma warning disable CS1591
using System;
@@ -43,9 +41,9 @@ namespace Emby.Server.Implementations.ScheduledTasks
ScheduledTasks = Array.Empty<IScheduledTaskWorker>();
}
- public event EventHandler<GenericEventArgs<IScheduledTaskWorker>> TaskExecuting;
+ public event EventHandler<GenericEventArgs<IScheduledTaskWorker>>? TaskExecuting;
- public event EventHandler<TaskCompletionEventArgs> TaskCompleted;
+ public event EventHandler<TaskCompletionEventArgs>? TaskCompleted;
/// <summary>
/// Gets the list of Scheduled Tasks.
@@ -134,7 +132,7 @@ namespace Emby.Server.Implementations.ScheduledTasks
{
var type = scheduledTask.ScheduledTask.GetType();
- _logger.LogInformation("Queuing task {0}", type.Name);
+ _logger.LogDebug("Queuing task {0}", type.Name);
lock (_taskQueue)
{
@@ -174,7 +172,7 @@ namespace Emby.Server.Implementations.ScheduledTasks
{
var type = task.ScheduledTask.GetType();
- _logger.LogInformation("Queuing task {0}", type.Name);
+ _logger.LogDebug("Queuing task {0}", type.Name);
lock (_taskQueue)
{
@@ -256,9 +254,6 @@ namespace Emby.Server.Implementations.ScheduledTasks
/// </summary>
private void ExecuteQueuedTasks()
{
- _logger.LogInformation("ExecuteQueuedTasks");
-
- // Execute queued tasks
lock (_taskQueue)
{
var list = new List<Tuple<Type, TaskOptions>>();
diff --git a/Emby.Server.Implementations/ScheduledTasks/Tasks/OptimizeDatabaseTask.cs b/Emby.Server.Implementations/ScheduledTasks/Tasks/OptimizeDatabaseTask.cs
index 1efacd856..1f3cb9b63 100644
--- a/Emby.Server.Implementations/ScheduledTasks/Tasks/OptimizeDatabaseTask.cs
+++ b/Emby.Server.Implementations/ScheduledTasks/Tasks/OptimizeDatabaseTask.cs
@@ -17,7 +17,7 @@ namespace Emby.Server.Implementations.ScheduledTasks.Tasks
{
private readonly ILogger<OptimizeDatabaseTask> _logger;
private readonly ILocalizationManager _localization;
- private readonly IDbContextFactory<JellyfinDb> _provider;
+ private readonly IDbContextFactory<JellyfinDbContext> _provider;
/// <summary>
/// Initializes a new instance of the <see cref="OptimizeDatabaseTask" /> class.
@@ -28,7 +28,7 @@ namespace Emby.Server.Implementations.ScheduledTasks.Tasks
public OptimizeDatabaseTask(
ILogger<OptimizeDatabaseTask> logger,
ILocalizationManager localization,
- IDbContextFactory<JellyfinDb> provider)
+ IDbContextFactory<JellyfinDbContext> provider)
{
_logger = logger;
_localization = localization;
diff --git a/Emby.Server.Implementations/Session/SessionWebSocketListener.cs b/Emby.Server.Implementations/Session/SessionWebSocketListener.cs
index aebb55907..4e427b1a4 100644
--- a/Emby.Server.Implementations/Session/SessionWebSocketListener.cs
+++ b/Emby.Server.Implementations/Session/SessionWebSocketListener.cs
@@ -1,5 +1,3 @@
-#nullable disable
-
using System;
using System.Collections.Generic;
using System.Linq;
@@ -58,7 +56,7 @@ namespace Emby.Server.Implementations.Session
/// <summary>
/// The KeepAlive cancellation token.
/// </summary>
- private CancellationTokenSource _keepAliveCancellationToken;
+ private CancellationTokenSource? _keepAliveCancellationToken;
/// <summary>
/// Initializes a new instance of the <see cref="SessionWebSocketListener" /> class.
@@ -105,7 +103,7 @@ namespace Emby.Server.Implementations.Session
}
}
- private async Task<SessionInfo> GetSession(HttpContext httpContext, string remoteEndpoint)
+ private async Task<SessionInfo?> GetSession(HttpContext httpContext, string? remoteEndpoint)
{
if (!httpContext.User.Identity?.IsAuthenticated ?? false)
{
@@ -138,8 +136,13 @@ namespace Emby.Server.Implementations.Session
/// </summary>
/// <param name="sender">The WebSocket.</param>
/// <param name="e">The event arguments.</param>
- private void OnWebSocketClosed(object sender, EventArgs e)
+ private void OnWebSocketClosed(object? sender, EventArgs e)
{
+ if (sender is null)
+ {
+ return;
+ }
+
var webSocket = (IWebSocketConnection)sender;
_logger.LogDebug("WebSocket {0} is closed.", webSocket);
RemoveWebSocket(webSocket);
diff --git a/Emby.Server.Implementations/Sorting/RuntimeComparer.cs b/Emby.Server.Implementations/Sorting/RuntimeComparer.cs
index 646bafbb5..753e58324 100644
--- a/Emby.Server.Implementations/Sorting/RuntimeComparer.cs
+++ b/Emby.Server.Implementations/Sorting/RuntimeComparer.cs
@@ -1,5 +1,3 @@
-#nullable disable
-
using System;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Sorting;
@@ -24,10 +22,9 @@ namespace Emby.Server.Implementations.Sorting
/// <param name="x">The x.</param>
/// <param name="y">The y.</param>
/// <returns>System.Int32.</returns>
- public int Compare(BaseItem x, BaseItem y)
+ public int Compare(BaseItem? x, BaseItem? y)
{
ArgumentNullException.ThrowIfNull(x);
-
ArgumentNullException.ThrowIfNull(y);
return (x.RunTimeTicks ?? 0).CompareTo(y.RunTimeTicks ?? 0);
diff --git a/Emby.Server.Implementations/Sorting/SeriesSortNameComparer.cs b/Emby.Server.Implementations/Sorting/SeriesSortNameComparer.cs
index 0bd9600b9..5b6c64f63 100644
--- a/Emby.Server.Implementations/Sorting/SeriesSortNameComparer.cs
+++ b/Emby.Server.Implementations/Sorting/SeriesSortNameComparer.cs
@@ -1,5 +1,3 @@
-#nullable disable
-
#pragma warning disable CS1591
using System;
@@ -23,15 +21,14 @@ namespace Emby.Server.Implementations.Sorting
/// <param name="x">The x.</param>
/// <param name="y">The y.</param>
/// <returns>System.Int32.</returns>
- public int Compare(BaseItem x, BaseItem y)
+ public int Compare(BaseItem? x, BaseItem? y)
{
return string.Compare(GetValue(x), GetValue(y), StringComparison.OrdinalIgnoreCase);
}
- private static string GetValue(BaseItem item)
+ private static string? GetValue(BaseItem? item)
{
var hasSeries = item as IHasSeries;
-
return hasSeries?.FindSeriesSortName();
}
}
diff --git a/Emby.Server.Implementations/Sorting/SortNameComparer.cs b/Emby.Server.Implementations/Sorting/SortNameComparer.cs
index 628b9b3dd..19abafe19 100644
--- a/Emby.Server.Implementations/Sorting/SortNameComparer.cs
+++ b/Emby.Server.Implementations/Sorting/SortNameComparer.cs
@@ -1,5 +1,3 @@
-#nullable disable
-
using System;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Sorting;
@@ -24,10 +22,9 @@ namespace Emby.Server.Implementations.Sorting
/// <param name="x">The x.</param>
/// <param name="y">The y.</param>
/// <returns>System.Int32.</returns>
- public int Compare(BaseItem x, BaseItem y)
+ public int Compare(BaseItem? x, BaseItem? y)
{
ArgumentNullException.ThrowIfNull(x);
-
ArgumentNullException.ThrowIfNull(y);
return string.Compare(x.SortName, y.SortName, StringComparison.OrdinalIgnoreCase);
diff --git a/Emby.Server.Implementations/Sorting/StartDateComparer.cs b/Emby.Server.Implementations/Sorting/StartDateComparer.cs
index c3df7c47e..2759d20de 100644
--- a/Emby.Server.Implementations/Sorting/StartDateComparer.cs
+++ b/Emby.Server.Implementations/Sorting/StartDateComparer.cs
@@ -1,5 +1,3 @@
-#nullable disable
-
#pragma warning disable CS1591
using System;
@@ -24,7 +22,7 @@ namespace Emby.Server.Implementations.Sorting
/// <param name="x">The x.</param>
/// <param name="y">The y.</param>
/// <returns>System.Int32.</returns>
- public int Compare(BaseItem x, BaseItem y)
+ public int Compare(BaseItem? x, BaseItem? y)
{
return GetDate(x).CompareTo(GetDate(y));
}
@@ -34,7 +32,7 @@ namespace Emby.Server.Implementations.Sorting
/// </summary>
/// <param name="x">The x.</param>
/// <returns>DateTime.</returns>
- private static DateTime GetDate(BaseItem x)
+ private static DateTime GetDate(BaseItem? x)
{
if (x is LiveTvProgram hasStartDate)
{
diff --git a/Emby.Server.Implementations/Sorting/StudioComparer.cs b/Emby.Server.Implementations/Sorting/StudioComparer.cs
index 457c06271..89d10f3d2 100644
--- a/Emby.Server.Implementations/Sorting/StudioComparer.cs
+++ b/Emby.Server.Implementations/Sorting/StudioComparer.cs
@@ -1,5 +1,3 @@
-#nullable disable
-
#pragma warning disable CS1591
using System;
@@ -24,10 +22,9 @@ namespace Emby.Server.Implementations.Sorting
/// <param name="x">The x.</param>
/// <param name="y">The y.</param>
/// <returns>System.Int32.</returns>
- public int Compare(BaseItem x, BaseItem y)
+ public int Compare(BaseItem? x, BaseItem? y)
{
ArgumentNullException.ThrowIfNull(x);
-
ArgumentNullException.ThrowIfNull(y);
return AlphanumericComparator.CompareValues(x.Studios.FirstOrDefault(), y.Studios.FirstOrDefault());
diff --git a/Emby.Server.Implementations/TV/TVSeriesManager.cs b/Emby.Server.Implementations/TV/TVSeriesManager.cs
index 967f90b55..f0e173f0b 100644
--- a/Emby.Server.Implementations/TV/TVSeriesManager.cs
+++ b/Emby.Server.Implementations/TV/TVSeriesManager.cs
@@ -1,5 +1,3 @@
-#nullable disable
-
#pragma warning disable CS1591
using System;
@@ -42,7 +40,7 @@ namespace Emby.Server.Implementations.TV
throw new ArgumentException("User not found");
}
- string presentationUniqueKey = null;
+ string? presentationUniqueKey = null;
if (query.SeriesId.HasValue && !query.SeriesId.Value.Equals(default))
{
if (_libraryManager.GetItemById(query.SeriesId.Value) is Series series)
@@ -91,7 +89,7 @@ namespace Emby.Server.Implementations.TV
throw new ArgumentException("User not found");
}
- string presentationUniqueKey = null;
+ string? presentationUniqueKey = null;
int? limit = null;
if (request.SeriesId.HasValue && !request.SeriesId.Value.Equals(default))
{
@@ -168,7 +166,7 @@ namespace Emby.Server.Implementations.TV
return !anyFound && i.LastWatchedDate == DateTime.MinValue;
})
.Select(i => i.GetEpisodeFunction())
- .Where(i => i is not null);
+ .Where(i => i is not null)!;
}
private static string GetUniqueSeriesKey(Episode episode)
@@ -185,7 +183,7 @@ namespace Emby.Server.Implementations.TV
/// Gets the next up.
/// </summary>
/// <returns>Task{Episode}.</returns>
- private (DateTime LastWatchedDate, Func<Episode> GetEpisodeFunction) GetNextUp(string seriesKey, User user, DtoOptions dtoOptions, bool rewatching)
+ private (DateTime LastWatchedDate, Func<Episode?> GetEpisodeFunction) GetNextUp(string seriesKey, User user, DtoOptions dtoOptions, bool rewatching)
{
var lastQuery = new InternalItemsQuery(user)
{
@@ -209,7 +207,7 @@ namespace Emby.Server.Implementations.TV
var lastWatchedEpisode = _libraryManager.GetItemList(lastQuery).Cast<Episode>().FirstOrDefault();
- Episode GetEpisode()
+ Episode? GetEpisode()
{
var nextQuery = new InternalItemsQuery(user)
{