aboutsummaryrefslogtreecommitdiff
path: root/Emby.Server.Implementations
diff options
context:
space:
mode:
Diffstat (limited to 'Emby.Server.Implementations')
-rw-r--r--Emby.Server.Implementations/AppBase/BaseApplicationPaths.cs6
-rw-r--r--Emby.Server.Implementations/ApplicationHost.cs94
-rw-r--r--Emby.Server.Implementations/Channels/ChannelManager.cs9
-rw-r--r--Emby.Server.Implementations/Data/SqliteItemRepository.cs7
-rw-r--r--Emby.Server.Implementations/Dto/DtoService.cs11
-rw-r--r--Emby.Server.Implementations/Emby.Server.Implementations.csproj2
-rw-r--r--Emby.Server.Implementations/EntryPoints/UdpServerEntryPoint.cs35
-rw-r--r--Emby.Server.Implementations/HttpServer/WebSocketConnection.cs1
-rw-r--r--Emby.Server.Implementations/IO/FileRefresher.cs1
-rw-r--r--Emby.Server.Implementations/IO/ManagedFileSystem.cs8
-rw-r--r--Emby.Server.Implementations/Library/LibraryManager.cs33
-rw-r--r--Emby.Server.Implementations/Library/LiveStreamHelper.cs18
-rw-r--r--Emby.Server.Implementations/Library/MediaSourceManager.cs15
-rw-r--r--Emby.Server.Implementations/Library/Resolvers/Audio/AudioResolver.cs6
-rw-r--r--Emby.Server.Implementations/Library/Resolvers/BaseVideoResolver.cs2
-rw-r--r--Emby.Server.Implementations/Library/Resolvers/Books/BookResolver.cs9
-rw-r--r--Emby.Server.Implementations/Library/Resolvers/PhotoResolver.cs1
-rw-r--r--Emby.Server.Implementations/Library/Resolvers/TV/SeasonResolver.cs2
-rw-r--r--Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs14
-rw-r--r--Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs25
-rw-r--r--Emby.Server.Implementations/LiveTv/TunerHosts/BaseTunerHost.cs1
-rw-r--r--Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs62
-rw-r--r--Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunManager.cs2
-rw-r--r--Emby.Server.Implementations/LiveTv/TunerHosts/M3UTunerHost.cs2
-rw-r--r--Emby.Server.Implementations/Localization/Core/en-US.json2
-rw-r--r--Emby.Server.Implementations/Localization/Core/fo.json18
-rw-r--r--Emby.Server.Implementations/Localization/Core/is.json63
-rw-r--r--Emby.Server.Implementations/Localization/Core/lv.json76
-rw-r--r--Emby.Server.Implementations/Localization/Core/ml.json4
-rw-r--r--Emby.Server.Implementations/Localization/Core/si.json1
-rw-r--r--Emby.Server.Implementations/Localization/Core/sk.json2
-rw-r--r--Emby.Server.Implementations/Localization/LocalizationManager.cs37
-rw-r--r--Emby.Server.Implementations/MediaEncoder/EncodingManager.cs2
-rw-r--r--Emby.Server.Implementations/Net/SocketFactory.cs39
-rw-r--r--Emby.Server.Implementations/Playlists/PlaylistManager.cs16
-rw-r--r--Emby.Server.Implementations/Plugins/PluginManager.cs18
-rw-r--r--Emby.Server.Implementations/ScheduledTasks/Tasks/ChapterImagesTask.cs4
-rw-r--r--Emby.Server.Implementations/SystemManager.cs104
-rw-r--r--Emby.Server.Implementations/Udp/UdpServer.cs22
-rw-r--r--Emby.Server.Implementations/Updates/InstallationManager.cs8
40 files changed, 436 insertions, 346 deletions
diff --git a/Emby.Server.Implementations/AppBase/BaseApplicationPaths.cs b/Emby.Server.Implementations/AppBase/BaseApplicationPaths.cs
index 6edfad575..39524be1d 100644
--- a/Emby.Server.Implementations/AppBase/BaseApplicationPaths.cs
+++ b/Emby.Server.Implementations/AppBase/BaseApplicationPaths.cs
@@ -10,8 +10,6 @@ namespace Emby.Server.Implementations.AppBase
/// </summary>
public abstract class BaseApplicationPaths : IApplicationPaths
{
- private string _dataPath;
-
/// <summary>
/// Initializes a new instance of the <see cref="BaseApplicationPaths"/> class.
/// </summary>
@@ -33,7 +31,7 @@ namespace Emby.Server.Implementations.AppBase
CachePath = cacheDirectoryPath;
WebPath = webDirectoryPath;
- _dataPath = Directory.CreateDirectory(Path.Combine(ProgramDataPath, "data")).FullName;
+ DataPath = Directory.CreateDirectory(Path.Combine(ProgramDataPath, "data")).FullName;
}
/// <summary>
@@ -55,7 +53,7 @@ namespace Emby.Server.Implementations.AppBase
/// Gets the folder path to the data directory.
/// </summary>
/// <value>The data directory.</value>
- public string DataPath => _dataPath;
+ public string DataPath { get; }
/// <inheritdoc />
public string VirtualDataPath => "%AppDataPath%";
diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs
index 8518b1352..c9bf7f085 100644
--- a/Emby.Server.Implementations/ApplicationHost.cs
+++ b/Emby.Server.Implementations/ApplicationHost.cs
@@ -13,9 +13,7 @@ using System.Net;
using System.Reflection;
using System.Security.Cryptography.X509Certificates;
using System.Threading.Tasks;
-using Emby.Dlna;
using Emby.Dlna.Main;
-using Emby.Dlna.Ssdp;
using Emby.Naming.Common;
using Emby.Photos;
using Emby.Server.Implementations.Channels;
@@ -58,7 +56,6 @@ using MediaBrowser.Controller.Chapters;
using MediaBrowser.Controller.ClientEvent;
using MediaBrowser.Controller.Collections;
using MediaBrowser.Controller.Configuration;
-using MediaBrowser.Controller.Dlna;
using MediaBrowser.Controller.Drawing;
using MediaBrowser.Controller.Dto;
using MediaBrowser.Controller.Entities;
@@ -82,7 +79,6 @@ using MediaBrowser.LocalMetadata.Savers;
using MediaBrowser.MediaEncoding.BdInfo;
using MediaBrowser.MediaEncoding.Subtitles;
using MediaBrowser.Model.Cryptography;
-using MediaBrowser.Model.Dlna;
using MediaBrowser.Model.Globalization;
using MediaBrowser.Model.IO;
using MediaBrowser.Model.MediaInfo;
@@ -101,7 +97,6 @@ using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
-using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Prometheus.DotNetRuntime;
using static MediaBrowser.Controller.Extensions.ConfigurationExtensions;
@@ -133,7 +128,7 @@ namespace Emby.Server.Implementations
/// <value>All concrete types.</value>
private Type[] _allConcreteTypes;
- private bool _disposed = false;
+ private bool _disposed;
/// <summary>
/// Initializes a new instance of the <see cref="ApplicationHost"/> class.
@@ -184,26 +179,16 @@ namespace Emby.Server.Implementations
public bool CoreStartupHasCompleted { get; private set; }
- public virtual bool CanLaunchWebBrowser => Environment.UserInteractive
- && !_startupOptions.IsService
- && (OperatingSystem.IsWindows() || OperatingSystem.IsMacOS());
-
/// <summary>
/// Gets the <see cref="INetworkManager"/> singleton instance.
/// </summary>
public INetworkManager NetManager { get; private set; }
- /// <summary>
- /// Gets a value indicating whether this instance has changes that require the entire application to restart.
- /// </summary>
- /// <value><c>true</c> if this instance has pending application restart; otherwise, <c>false</c>.</value>
- public bool HasPendingRestart { get; private set; }
-
/// <inheritdoc />
- public bool IsShuttingDown { get; private set; }
+ public bool HasPendingRestart { get; private set; }
/// <inheritdoc />
- public bool ShouldRestart { get; private set; }
+ public bool ShouldRestart { get; set; }
/// <summary>
/// Gets the logger.
@@ -461,7 +446,7 @@ namespace Emby.Server.Implementations
ConfigurationManager.AddParts(GetExports<IConfigurationFactory>());
- NetManager = new NetworkManager(ConfigurationManager, LoggerFactory.CreateLogger<NetworkManager>());
+ NetManager = new NetworkManager(ConfigurationManager, _startupConfig, LoggerFactory.CreateLogger<NetworkManager>());
// Initialize runtime stat collection
if (ConfigurationManager.Configuration.EnableMetrics)
@@ -507,6 +492,8 @@ namespace Emby.Server.Implementations
serviceCollection.AddSingleton<IFileSystem, ManagedFileSystem>();
serviceCollection.AddSingleton<IShortcutHandler, MbLinkShortcutHandler>();
+ serviceCollection.AddScoped<ISystemManager, SystemManager>();
+
serviceCollection.AddSingleton<TmdbClientManager>();
serviceCollection.AddSingleton(NetManager);
@@ -572,8 +559,6 @@ namespace Emby.Server.Implementations
serviceCollection.AddSingleton<ISessionManager, SessionManager>();
- serviceCollection.AddSingleton<IDlnaManager, DlnaManager>();
-
serviceCollection.AddSingleton<ICollectionManager, CollectionManager>();
serviceCollection.AddSingleton<IPlaylistManager, PlaylistManager>();
@@ -585,8 +570,6 @@ namespace Emby.Server.Implementations
serviceCollection.AddSingleton<IUserViewManager, UserViewManager>();
- serviceCollection.AddSingleton<IDeviceDiscovery, DeviceDiscovery>();
-
serviceCollection.AddSingleton<IChapterManager, ChapterManager>();
serviceCollection.AddSingleton<IEncodingManager, MediaEncoder.EncodingManager>();
@@ -850,24 +833,6 @@ namespace Emby.Server.Implementations
}
}
- /// <inheritdoc />
- public void Restart()
- {
- ShouldRestart = true;
- Shutdown();
- }
-
- /// <inheritdoc />
- public void Shutdown()
- {
- Task.Run(async () =>
- {
- await Task.Delay(100).ConfigureAwait(false);
- IsShuttingDown = true;
- Resolve<IHostApplicationLifetime>().StopApplication();
- });
- }
-
/// <summary>
/// Gets the composable part assemblies.
/// </summary>
@@ -923,49 +888,6 @@ namespace Emby.Server.Implementations
protected abstract IEnumerable<Assembly> GetAssembliesWithPartsInternal();
- /// <summary>
- /// Gets the system status.
- /// </summary>
- /// <param name="request">Where this request originated.</param>
- /// <returns>SystemInfo.</returns>
- public SystemInfo GetSystemInfo(HttpRequest request)
- {
- return new SystemInfo
- {
- HasPendingRestart = HasPendingRestart,
- IsShuttingDown = IsShuttingDown,
- Version = ApplicationVersionString,
- WebSocketPortNumber = HttpPort,
- CompletedInstallations = Resolve<IInstallationManager>().CompletedInstallations.ToArray(),
- Id = SystemId,
- ProgramDataPath = ApplicationPaths.ProgramDataPath,
- WebPath = ApplicationPaths.WebPath,
- LogPath = ApplicationPaths.LogDirectoryPath,
- ItemsByNamePath = ApplicationPaths.InternalMetadataPath,
- InternalMetadataPath = ApplicationPaths.InternalMetadataPath,
- CachePath = ApplicationPaths.CachePath,
- CanLaunchWebBrowser = CanLaunchWebBrowser,
- TranscodingTempPath = ConfigurationManager.GetTranscodePath(),
- ServerName = FriendlyName,
- LocalAddress = GetSmartApiUrl(request),
- SupportsLibraryMonitor = true,
- PackageName = _startupOptions.PackageName
- };
- }
-
- public PublicSystemInfo GetPublicSystemInfo(HttpRequest request)
- {
- return new PublicSystemInfo
- {
- Version = ApplicationVersionString,
- ProductName = ApplicationProductName,
- Id = SystemId,
- ServerName = FriendlyName,
- LocalAddress = GetSmartApiUrl(request),
- StartupWizardCompleted = ConfigurationManager.CommonConfiguration.IsStartupWizardCompleted
- };
- }
-
/// <inheritdoc/>
public string GetSmartApiUrl(IPAddress remoteAddr)
{
@@ -983,7 +905,7 @@ namespace Emby.Server.Implementations
/// <inheritdoc/>
public string GetSmartApiUrl(HttpRequest request)
{
- // Return the host in the HTTP request as the API url
+ // Return the host in the HTTP request as the API URL if not configured otherwise
if (ConfigurationManager.GetNetworkConfiguration().EnablePublishedServerUriByRequest)
{
int? requestPort = request.Host.Port;
@@ -1018,7 +940,7 @@ namespace Emby.Server.Implementations
public string GetApiUrlForLocalAccess(IPAddress ipAddress = null, bool allowHttps = true)
{
// With an empty source, the port will be null
- var smart = NetManager.GetBindAddress(ipAddress, out _, true);
+ var smart = NetManager.GetBindAddress(ipAddress, out _, false);
var scheme = !allowHttps ? Uri.UriSchemeHttp : null;
int? port = !allowHttps ? HttpPort : null;
return GetLocalApiUrl(smart, scheme, port);
diff --git a/Emby.Server.Implementations/Channels/ChannelManager.cs b/Emby.Server.Implementations/Channels/ChannelManager.cs
index 961e225e9..8279acb05 100644
--- a/Emby.Server.Implementations/Channels/ChannelManager.cs
+++ b/Emby.Server.Implementations/Channels/ChannelManager.cs
@@ -371,8 +371,11 @@ namespace Emby.Server.Implementations.Channels
Directory.CreateDirectory(Path.GetDirectoryName(path));
- await using FileStream createStream = File.Create(path);
- await JsonSerializer.SerializeAsync(createStream, mediaSources, _jsonOptions).ConfigureAwait(false);
+ FileStream createStream = File.Create(path);
+ await using (createStream.ConfigureAwait(false))
+ {
+ await JsonSerializer.SerializeAsync(createStream, mediaSources, _jsonOptions).ConfigureAwait(false);
+ }
}
/// <inheritdoc />
@@ -1156,7 +1159,7 @@ namespace Emby.Server.Implementations.Channels
if (info.People is not null && info.People.Count > 0)
{
- _libraryManager.UpdatePeople(item, info.People);
+ await _libraryManager.UpdatePeopleAsync(item, info.People, cancellationToken).ConfigureAwait(false);
}
}
else if (forceUpdate)
diff --git a/Emby.Server.Implementations/Data/SqliteItemRepository.cs b/Emby.Server.Implementations/Data/SqliteItemRepository.cs
index 77cf4089b..e519364c2 100644
--- a/Emby.Server.Implementations/Data/SqliteItemRepository.cs
+++ b/Emby.Server.Implementations/Data/SqliteItemRepository.cs
@@ -3540,10 +3540,7 @@ namespace Emby.Server.Implementations.Data
.Append(paramName)
.Append("))) OR ");
- if (statement is not null)
- {
- statement.TryBind(paramName, query.PersonIds[i]);
- }
+ statement?.TryBind(paramName, query.PersonIds[i]);
}
clauseBuilder.Length -= Or.Length;
@@ -4382,7 +4379,7 @@ namespace Emby.Server.Implementations.Data
foreach (var videoType in query.VideoTypes)
{
- videoTypes.Add("data like '%\"VideoType\":\"" + videoType.ToString() + "\"%'");
+ videoTypes.Add("data like '%\"VideoType\":\"" + videoType + "\"%'");
}
whereClauses.Add("(" + string.Join(" OR ", videoTypes) + ")");
diff --git a/Emby.Server.Implementations/Dto/DtoService.cs b/Emby.Server.Implementations/Dto/DtoService.cs
index 6d27703bd..44b97e8b8 100644
--- a/Emby.Server.Implementations/Dto/DtoService.cs
+++ b/Emby.Server.Implementations/Dto/DtoService.cs
@@ -22,6 +22,7 @@ using MediaBrowser.Controller.Lyrics;
using MediaBrowser.Controller.Persistence;
using MediaBrowser.Controller.Playlists;
using MediaBrowser.Controller.Providers;
+using MediaBrowser.Controller.Trickplay;
using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Querying;
@@ -52,6 +53,7 @@ namespace Emby.Server.Implementations.Dto
private readonly Lazy<ILiveTvManager> _livetvManagerFactory;
private readonly ILyricManager _lyricManager;
+ private readonly ITrickplayManager _trickplayManager;
public DtoService(
ILogger<DtoService> logger,
@@ -63,7 +65,8 @@ namespace Emby.Server.Implementations.Dto
IApplicationHost appHost,
IMediaSourceManager mediaSourceManager,
Lazy<ILiveTvManager> livetvManagerFactory,
- ILyricManager lyricManager)
+ ILyricManager lyricManager,
+ ITrickplayManager trickplayManager)
{
_logger = logger;
_libraryManager = libraryManager;
@@ -75,6 +78,7 @@ namespace Emby.Server.Implementations.Dto
_mediaSourceManager = mediaSourceManager;
_livetvManagerFactory = livetvManagerFactory;
_lyricManager = lyricManager;
+ _trickplayManager = trickplayManager;
}
private ILiveTvManager LivetvManager => _livetvManagerFactory.Value;
@@ -1059,6 +1063,11 @@ namespace Emby.Server.Implementations.Dto
dto.Chapters = _itemRepo.GetChapters(item);
}
+ if (options.ContainsField(ItemFields.Trickplay))
+ {
+ dto.Trickplay = _trickplayManager.GetTrickplayManifest(item).GetAwaiter().GetResult();
+ }
+
if (video.ExtraType.HasValue)
{
dto.ExtraType = video.ExtraType.Value.ToString();
diff --git a/Emby.Server.Implementations/Emby.Server.Implementations.csproj b/Emby.Server.Implementations/Emby.Server.Implementations.csproj
index 3aab0a5e9..80263c139 100644
--- a/Emby.Server.Implementations/Emby.Server.Implementations.csproj
+++ b/Emby.Server.Implementations/Emby.Server.Implementations.csproj
@@ -43,8 +43,6 @@
<TargetFramework>net7.0</TargetFramework>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
- <!-- https://github.com/microsoft/ApplicationInsights-dotnet/issues/2047 -->
- <NoWarn>AD0001</NoWarn>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
diff --git a/Emby.Server.Implementations/EntryPoints/UdpServerEntryPoint.cs b/Emby.Server.Implementations/EntryPoints/UdpServerEntryPoint.cs
index 54191649d..7e4994f1a 100644
--- a/Emby.Server.Implementations/EntryPoints/UdpServerEntryPoint.cs
+++ b/Emby.Server.Implementations/EntryPoints/UdpServerEntryPoint.cs
@@ -18,7 +18,7 @@ using Microsoft.Extensions.Logging;
namespace Emby.Server.Implementations.EntryPoints
{
/// <summary>
- /// Class UdpServerEntryPoint.
+ /// Class responsible for registering all UDP broadcast endpoints and their handlers.
/// </summary>
public sealed class UdpServerEntryPoint : IServerEntryPoint
{
@@ -35,14 +35,13 @@ namespace Emby.Server.Implementations.EntryPoints
private readonly IConfiguration _config;
private readonly IConfigurationManager _configurationManager;
private readonly INetworkManager _networkManager;
- private readonly bool _enableMultiSocketBinding;
/// <summary>
/// The UDP server.
/// </summary>
- private List<UdpServer> _udpServers;
- private CancellationTokenSource _cancellationTokenSource = new CancellationTokenSource();
- private bool _disposed = false;
+ private readonly List<UdpServer> _udpServers;
+ private readonly CancellationTokenSource _cancellationTokenSource = new CancellationTokenSource();
+ private bool _disposed;
/// <summary>
/// Initializes a new instance of the <see cref="UdpServerEntryPoint" /> class.
@@ -65,7 +64,6 @@ namespace Emby.Server.Implementations.EntryPoints
_configurationManager = configurationManager;
_networkManager = networkManager;
_udpServers = new List<UdpServer>();
- _enableMultiSocketBinding = OperatingSystem.IsWindows() || OperatingSystem.IsLinux();
}
/// <inheritdoc />
@@ -80,14 +78,16 @@ namespace Emby.Server.Implementations.EntryPoints
try
{
- if (_enableMultiSocketBinding)
+ // Linux needs to bind to the broadcast addresses to get broadcast traffic
+ // Windows receives broadcast fine when binding to just the interface, it is unable to bind to broadcast addresses
+ if (OperatingSystem.IsLinux())
{
- // Add global broadcast socket
+ // Add global broadcast listener
var server = new UdpServer(_logger, _appHost, _config, IPAddress.Broadcast, PortNumber);
server.Start(_cancellationTokenSource.Token);
_udpServers.Add(server);
- // Add bind address specific broadcast sockets
+ // Add bind address specific broadcast listeners
// IPv6 is currently unsupported
var validInterfaces = _networkManager.GetInternalBindAddresses().Where(i => i.AddressFamily == AddressFamily.InterNetwork);
foreach (var intf in validInterfaces)
@@ -102,9 +102,18 @@ namespace Emby.Server.Implementations.EntryPoints
}
else
{
- var server = new UdpServer(_logger, _appHost, _config, IPAddress.Any, PortNumber);
- server.Start(_cancellationTokenSource.Token);
- _udpServers.Add(server);
+ // Add bind address specific broadcast listeners
+ // IPv6 is currently unsupported
+ var validInterfaces = _networkManager.GetInternalBindAddresses().Where(i => i.AddressFamily == AddressFamily.InterNetwork);
+ foreach (var intf in validInterfaces)
+ {
+ var intfAddress = intf.Address;
+ _logger.LogDebug("Binding UDP server to {Address} on port {PortNumber}", intfAddress, PortNumber);
+
+ var server = new UdpServer(_logger, _appHost, _config, intfAddress, PortNumber);
+ server.Start(_cancellationTokenSource.Token);
+ _udpServers.Add(server);
+ }
}
}
catch (SocketException ex)
@@ -119,7 +128,7 @@ namespace Emby.Server.Implementations.EntryPoints
{
if (_disposed)
{
- throw new ObjectDisposedException(this.GetType().Name);
+ throw new ObjectDisposedException(GetType().Name);
}
}
diff --git a/Emby.Server.Implementations/HttpServer/WebSocketConnection.cs b/Emby.Server.Implementations/HttpServer/WebSocketConnection.cs
index 7f620d666..f83da566b 100644
--- a/Emby.Server.Implementations/HttpServer/WebSocketConnection.cs
+++ b/Emby.Server.Implementations/HttpServer/WebSocketConnection.cs
@@ -12,7 +12,6 @@ using MediaBrowser.Controller.Net;
using MediaBrowser.Controller.Net.WebSocketMessages;
using MediaBrowser.Controller.Net.WebSocketMessages.Outbound;
using MediaBrowser.Model.Session;
-using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
namespace Emby.Server.Implementations.HttpServer
diff --git a/Emby.Server.Implementations/IO/FileRefresher.cs b/Emby.Server.Implementations/IO/FileRefresher.cs
index 15b1836eb..e75cab64c 100644
--- a/Emby.Server.Implementations/IO/FileRefresher.cs
+++ b/Emby.Server.Implementations/IO/FileRefresher.cs
@@ -210,7 +210,6 @@ namespace Emby.Server.Implementations.IO
DisposeTimer();
_disposed = true;
- GC.SuppressFinalize(this);
}
}
}
diff --git a/Emby.Server.Implementations/IO/ManagedFileSystem.cs b/Emby.Server.Implementations/IO/ManagedFileSystem.cs
index 18b00ce0b..c380d67db 100644
--- a/Emby.Server.Implementations/IO/ManagedFileSystem.cs
+++ b/Emby.Server.Implementations/IO/ManagedFileSystem.cs
@@ -91,7 +91,7 @@ namespace Emby.Server.Implementations.IO
}
// unc path
- if (filePath.StartsWith("\\\\", StringComparison.Ordinal))
+ if (filePath.StartsWith(@"\\", StringComparison.Ordinal))
{
return filePath;
}
@@ -103,15 +103,17 @@ namespace Emby.Server.Implementations.IO
return filePath;
}
+ var filePathSpan = filePath.AsSpan();
+
// relative path
if (firstChar == '\\')
{
- filePath = filePath.Substring(1);
+ filePathSpan = filePathSpan.Slice(1);
}
try
{
- return Path.GetFullPath(Path.Combine(folderPath, filePath));
+ return Path.GetFullPath(Path.Join(folderPath, filePathSpan));
}
catch (ArgumentException)
{
diff --git a/Emby.Server.Implementations/Library/LibraryManager.cs b/Emby.Server.Implementations/Library/LibraryManager.cs
index b0a4a4151..4f0983564 100644
--- a/Emby.Server.Implementations/Library/LibraryManager.cs
+++ b/Emby.Server.Implementations/Library/LibraryManager.cs
@@ -46,7 +46,6 @@ using MediaBrowser.Model.IO;
using MediaBrowser.Model.Library;
using MediaBrowser.Model.Querying;
using MediaBrowser.Model.Tasks;
-using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.Logging;
using Episode = MediaBrowser.Controller.Entities.TV.Episode;
using EpisodeInfo = Emby.Naming.TV.EpisodeInfo;
@@ -839,19 +838,12 @@ namespace Emby.Server.Implementations.Library
{
var path = Person.GetPath(name);
var id = GetItemByNameId<Person>(path);
- if (GetItemById(id) is not Person item)
+ if (GetItemById(id) is Person item)
{
- item = new Person
- {
- Name = name,
- Id = id,
- DateCreated = DateTime.UtcNow,
- DateModified = DateTime.UtcNow,
- Path = path
- };
+ return item;
}
- return item;
+ return null;
}
/// <summary>
@@ -1162,7 +1154,7 @@ namespace Emby.Server.Implementations.Library
Name = Path.GetFileName(dir),
Locations = _fileSystem.GetFilePaths(dir, false)
- .Where(i => string.Equals(ShortcutFileExtension, Path.GetExtension(i), StringComparison.OrdinalIgnoreCase))
+ .Where(i => Path.GetExtension(i.AsSpan()).Equals(ShortcutFileExtension, StringComparison.OrdinalIgnoreCase))
.Select(i =>
{
try
@@ -2858,7 +2850,7 @@ namespace Emby.Server.Implementations.Library
{
var path = Path.Combine(virtualFolderPath, collectionType.ToString().ToLowerInvariant() + ".collection");
- File.WriteAllBytes(path, Array.Empty<byte>());
+ await File.WriteAllBytesAsync(path, Array.Empty<byte>()).ConfigureAwait(false);
}
CollectionFolder.SaveLibraryOptions(virtualFolderPath, options);
@@ -2900,9 +2892,18 @@ namespace Emby.Server.Implementations.Library
var saveEntity = false;
var personEntity = GetPerson(person.Name);
- // if PresentationUniqueKey is empty it's likely a new item.
- if (string.IsNullOrEmpty(personEntity.PresentationUniqueKey))
+ if (personEntity is null)
{
+ var path = Person.GetPath(person.Name);
+ personEntity = new Person()
+ {
+ Name = person.Name,
+ Id = GetItemByNameId<Person>(path),
+ DateCreated = DateTime.UtcNow,
+ DateModified = DateTime.UtcNow,
+ Path = path
+ };
+
personEntity.PresentationUniqueKey = personEntity.CreatePresentationUniqueKey();
saveEntity = true;
}
@@ -3135,7 +3136,7 @@ namespace Emby.Server.Implementations.Library
}
var shortcut = _fileSystem.GetFilePaths(virtualFolderPath, true)
- .Where(i => string.Equals(ShortcutFileExtension, Path.GetExtension(i), StringComparison.OrdinalIgnoreCase))
+ .Where(i => Path.GetExtension(i.AsSpan()).Equals(ShortcutFileExtension, StringComparison.OrdinalIgnoreCase))
.FirstOrDefault(f => _appHost.ExpandVirtualPath(_fileSystem.ResolveShortcut(f)).Equals(mediaPath, StringComparison.OrdinalIgnoreCase));
if (!string.IsNullOrEmpty(shortcut))
diff --git a/Emby.Server.Implementations/Library/LiveStreamHelper.cs b/Emby.Server.Implementations/Library/LiveStreamHelper.cs
index 936a08da8..59d705ace 100644
--- a/Emby.Server.Implementations/Library/LiveStreamHelper.cs
+++ b/Emby.Server.Implementations/Library/LiveStreamHelper.cs
@@ -48,15 +48,20 @@ namespace Emby.Server.Implementations.Library
if (!string.IsNullOrEmpty(cacheKey))
{
+ FileStream jsonStream = AsyncFile.OpenRead(cacheFilePath);
try
{
- await using FileStream jsonStream = AsyncFile.OpenRead(cacheFilePath);
mediaInfo = await JsonSerializer.DeserializeAsync<MediaInfo>(jsonStream, _jsonOptions, cancellationToken).ConfigureAwait(false);
// _logger.LogDebug("Found cached media info");
}
- catch
+ catch (Exception ex)
{
+ _logger.LogError(ex, "Error deserializing mediainfo cache");
+ }
+ finally
+ {
+ await jsonStream.DisposeAsync().ConfigureAwait(false);
}
}
@@ -84,10 +89,13 @@ namespace Emby.Server.Implementations.Library
if (cacheFilePath is not null)
{
Directory.CreateDirectory(Path.GetDirectoryName(cacheFilePath));
- await using FileStream createStream = AsyncFile.OpenWrite(cacheFilePath);
- await JsonSerializer.SerializeAsync(createStream, mediaInfo, _jsonOptions, cancellationToken).ConfigureAwait(false);
+ FileStream createStream = AsyncFile.OpenWrite(cacheFilePath);
+ await using (createStream.ConfigureAwait(false))
+ {
+ await JsonSerializer.SerializeAsync(createStream, mediaInfo, _jsonOptions, cancellationToken).ConfigureAwait(false);
+ }
- // _logger.LogDebug("Saved media info to {0}", cacheFilePath);
+ _logger.LogDebug("Saved media info to {0}", cacheFilePath);
}
}
diff --git a/Emby.Server.Implementations/Library/MediaSourceManager.cs b/Emby.Server.Implementations/Library/MediaSourceManager.cs
index c9a26a30f..91469dba9 100644
--- a/Emby.Server.Implementations/Library/MediaSourceManager.cs
+++ b/Emby.Server.Implementations/Library/MediaSourceManager.cs
@@ -625,17 +625,19 @@ namespace Emby.Server.Implementations.Library
if (!string.IsNullOrEmpty(cacheKey))
{
+ FileStream jsonStream = AsyncFile.OpenRead(cacheFilePath);
try
{
- await using FileStream jsonStream = AsyncFile.OpenRead(cacheFilePath);
mediaInfo = await JsonSerializer.DeserializeAsync<MediaInfo>(jsonStream, _jsonOptions, cancellationToken).ConfigureAwait(false);
-
- // _logger.LogDebug("Found cached media info");
}
catch (Exception ex)
{
_logger.LogDebug(ex, "_jsonSerializer.DeserializeFromFile threw an exception.");
}
+ finally
+ {
+ await jsonStream.DisposeAsync().ConfigureAwait(false);
+ }
}
if (mediaInfo is null)
@@ -664,8 +666,11 @@ namespace Emby.Server.Implementations.Library
if (cacheFilePath is not null)
{
Directory.CreateDirectory(Path.GetDirectoryName(cacheFilePath));
- await using FileStream createStream = File.Create(cacheFilePath);
- await JsonSerializer.SerializeAsync(createStream, mediaInfo, _jsonOptions, cancellationToken).ConfigureAwait(false);
+ FileStream createStream = File.Create(cacheFilePath);
+ await using (createStream.ConfigureAwait(false))
+ {
+ await JsonSerializer.SerializeAsync(createStream, mediaInfo, _jsonOptions, cancellationToken).ConfigureAwait(false);
+ }
// _logger.LogDebug("Saved media info to {0}", cacheFilePath);
}
diff --git a/Emby.Server.Implementations/Library/Resolvers/Audio/AudioResolver.cs b/Emby.Server.Implementations/Library/Resolvers/Audio/AudioResolver.cs
index a74f82475..862f144e6 100644
--- a/Emby.Server.Implementations/Library/Resolvers/Audio/AudioResolver.cs
+++ b/Emby.Server.Implementations/Library/Resolvers/Audio/AudioResolver.cs
@@ -94,9 +94,9 @@ namespace Emby.Server.Implementations.Library.Resolvers.Audio
if (AudioFileParser.IsAudioFile(args.Path, _namingOptions))
{
- var extension = Path.GetExtension(args.Path);
+ var extension = Path.GetExtension(args.Path.AsSpan());
- if (string.Equals(extension, ".cue", StringComparison.OrdinalIgnoreCase))
+ if (extension.Equals(".cue", StringComparison.OrdinalIgnoreCase))
{
// if audio file exists of same name, return null
return null;
@@ -128,7 +128,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.Audio
if (item is not null)
{
- item.IsShortcut = string.Equals(extension, ".strm", StringComparison.OrdinalIgnoreCase);
+ item.IsShortcut = extension.Equals(".strm", StringComparison.OrdinalIgnoreCase);
item.IsInMixedFolder = true;
}
diff --git a/Emby.Server.Implementations/Library/Resolvers/BaseVideoResolver.cs b/Emby.Server.Implementations/Library/Resolvers/BaseVideoResolver.cs
index 381796d0e..779cfd5be 100644
--- a/Emby.Server.Implementations/Library/Resolvers/BaseVideoResolver.cs
+++ b/Emby.Server.Implementations/Library/Resolvers/BaseVideoResolver.cs
@@ -263,7 +263,7 @@ namespace Emby.Server.Implementations.Library.Resolvers
return false;
}
- return directoryService.GetFilePaths(fullPath).Any(i => string.Equals(Path.GetExtension(i), ".vob", StringComparison.OrdinalIgnoreCase));
+ return directoryService.GetFilePaths(fullPath).Any(i => Path.GetExtension(i.AsSpan()).Equals(".vob", StringComparison.OrdinalIgnoreCase));
}
/// <summary>
diff --git a/Emby.Server.Implementations/Library/Resolvers/Books/BookResolver.cs b/Emby.Server.Implementations/Library/Resolvers/Books/BookResolver.cs
index 042422c6f..73861ff59 100644
--- a/Emby.Server.Implementations/Library/Resolvers/Books/BookResolver.cs
+++ b/Emby.Server.Implementations/Library/Resolvers/Books/BookResolver.cs
@@ -32,9 +32,9 @@ namespace Emby.Server.Implementations.Library.Resolvers.Books
return GetBook(args);
}
- var extension = Path.GetExtension(args.Path);
+ var extension = Path.GetExtension(args.Path.AsSpan());
- if (extension is not null && _validExtensions.Contains(extension, StringComparison.OrdinalIgnoreCase))
+ if (_validExtensions.Contains(extension, StringComparison.OrdinalIgnoreCase))
{
// It's a book
return new Book
@@ -51,12 +51,11 @@ namespace Emby.Server.Implementations.Library.Resolvers.Books
{
var bookFiles = args.FileSystemChildren.Where(f =>
{
- var fileExtension = Path.GetExtension(f.FullName)
- ?? string.Empty;
+ var fileExtension = Path.GetExtension(f.FullName.AsSpan());
return _validExtensions.Contains(
fileExtension,
- StringComparer.OrdinalIgnoreCase);
+ StringComparison.OrdinalIgnoreCase);
}).ToList();
// Don't return a Book if there is more (or less) than one document in the directory
diff --git a/Emby.Server.Implementations/Library/Resolvers/PhotoResolver.cs b/Emby.Server.Implementations/Library/Resolvers/PhotoResolver.cs
index b77c6b204..c860391fc 100644
--- a/Emby.Server.Implementations/Library/Resolvers/PhotoResolver.cs
+++ b/Emby.Server.Implementations/Library/Resolvers/PhotoResolver.cs
@@ -1,5 +1,4 @@
using System;
-using System.Collections.Generic;
using System.IO;
using System.Linq;
using Emby.Naming.Common;
diff --git a/Emby.Server.Implementations/Library/Resolvers/TV/SeasonResolver.cs b/Emby.Server.Implementations/Library/Resolvers/TV/SeasonResolver.cs
index e9538a5c9..858c5b281 100644
--- a/Emby.Server.Implementations/Library/Resolvers/TV/SeasonResolver.cs
+++ b/Emby.Server.Implementations/Library/Resolvers/TV/SeasonResolver.cs
@@ -62,7 +62,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.TV
var resolver = new Naming.TV.EpisodeResolver(namingOptions);
var folderName = System.IO.Path.GetFileName(path);
- var testPath = "\\\\test\\" + folderName;
+ var testPath = @"\\test\" + folderName;
var episodeInfo = resolver.Resolve(testPath, true);
diff --git a/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs b/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs
index b9d0f170a..74b62ca3f 100644
--- a/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs
+++ b/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs
@@ -1851,7 +1851,8 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
return;
}
- await using (var stream = new FileStream(nfoPath, FileMode.CreateNew, FileAccess.Write, FileShare.None))
+ var stream = new FileStream(nfoPath, FileMode.CreateNew, FileAccess.Write, FileShare.None);
+ await using (stream.ConfigureAwait(false))
{
var settings = new XmlWriterSettings
{
@@ -1860,7 +1861,8 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
Async = true
};
- await using (var writer = XmlWriter.Create(stream, settings))
+ var writer = XmlWriter.Create(stream, settings);
+ await using (writer.ConfigureAwait(false))
{
await writer.WriteStartDocumentAsync(true).ConfigureAwait(false);
await writer.WriteStartElementAsync(null, "tvshow", null).ConfigureAwait(false);
@@ -1914,7 +1916,8 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
return;
}
- await using (var stream = new FileStream(nfoPath, FileMode.CreateNew, FileAccess.Write, FileShare.None))
+ var stream = new FileStream(nfoPath, FileMode.CreateNew, FileAccess.Write, FileShare.None);
+ await using (stream.ConfigureAwait(false))
{
var settings = new XmlWriterSettings
{
@@ -1927,7 +1930,8 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
var isSeriesEpisode = timer.IsProgramSeries;
- await using (var writer = XmlWriter.Create(stream, settings))
+ var writer = XmlWriter.Create(stream, settings);
+ await using (writer.ConfigureAwait(false))
{
await writer.WriteStartDocumentAsync(true).ConfigureAwait(false);
@@ -1965,7 +1969,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
}
else
{
- await writer.WriteStartElementAsync(null, "movie", null);
+ await writer.WriteStartElementAsync(null, "movie", null).ConfigureAwait(false);
if (!string.IsNullOrWhiteSpace(item.Name))
{
diff --git a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs b/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs
index 7645c6c52..6b0520ad0 100644
--- a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs
+++ b/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs
@@ -106,8 +106,7 @@ namespace Emby.Server.Implementations.LiveTv.Listings
options.Content = JsonContent.Create(requestList, options: _jsonOptions);
options.Headers.TryAddWithoutValidation("token", token);
using var response = await Send(options, true, info, cancellationToken).ConfigureAwait(false);
- await using var responseStream = await response.Content.ReadAsStreamAsync(cancellationToken).ConfigureAwait(false);
- var dailySchedules = await JsonSerializer.DeserializeAsync<IReadOnlyList<DayDto>>(responseStream, _jsonOptions, cancellationToken).ConfigureAwait(false);
+ var dailySchedules = await response.Content.ReadFromJsonAsync<IReadOnlyList<DayDto>>(_jsonOptions, cancellationToken).ConfigureAwait(false);
if (dailySchedules is null)
{
return Array.Empty<ProgramInfo>();
@@ -122,8 +121,7 @@ namespace Emby.Server.Implementations.LiveTv.Listings
programRequestOptions.Content = JsonContent.Create(programIds, options: _jsonOptions);
using var innerResponse = await Send(programRequestOptions, true, info, cancellationToken).ConfigureAwait(false);
- await using var innerResponseStream = await innerResponse.Content.ReadAsStreamAsync(cancellationToken).ConfigureAwait(false);
- var programDetails = await JsonSerializer.DeserializeAsync<IReadOnlyList<ProgramDetailsDto>>(innerResponseStream, _jsonOptions, cancellationToken).ConfigureAwait(false);
+ var programDetails = await innerResponse.Content.ReadFromJsonAsync<IReadOnlyList<ProgramDetailsDto>>(_jsonOptions, cancellationToken).ConfigureAwait(false);
if (programDetails is null)
{
return Array.Empty<ProgramInfo>();
@@ -482,8 +480,7 @@ namespace Emby.Server.Implementations.LiveTv.Listings
try
{
using var innerResponse2 = await Send(message, true, info, cancellationToken).ConfigureAwait(false);
- await using var response = await innerResponse2.Content.ReadAsStreamAsync(cancellationToken).ConfigureAwait(false);
- return await JsonSerializer.DeserializeAsync<IReadOnlyList<ShowImagesDto>>(response, _jsonOptions, cancellationToken).ConfigureAwait(false);
+ return await innerResponse2.Content.ReadFromJsonAsync<IReadOnlyList<ShowImagesDto>>(_jsonOptions, cancellationToken).ConfigureAwait(false);
}
catch (Exception ex)
{
@@ -510,10 +507,7 @@ namespace Emby.Server.Implementations.LiveTv.Listings
try
{
using var httpResponse = await Send(options, false, info, cancellationToken).ConfigureAwait(false);
- await using var response = await httpResponse.Content.ReadAsStreamAsync(cancellationToken).ConfigureAwait(false);
-
- var root = await JsonSerializer.DeserializeAsync<IReadOnlyList<HeadendsDto>>(response, _jsonOptions, cancellationToken).ConfigureAwait(false);
-
+ var root = await httpResponse.Content.ReadFromJsonAsync<IReadOnlyList<HeadendsDto>>(_jsonOptions, cancellationToken).ConfigureAwait(false);
if (root is not null)
{
foreach (HeadendsDto headend in root)
@@ -649,8 +643,7 @@ namespace Emby.Server.Implementations.LiveTv.Listings
using var response = await Send(options, false, null, cancellationToken).ConfigureAwait(false);
response.EnsureSuccessStatusCode();
- await using var stream = await response.Content.ReadAsStreamAsync(cancellationToken).ConfigureAwait(false);
- var root = await JsonSerializer.DeserializeAsync<TokenDto>(stream, _jsonOptions, cancellationToken).ConfigureAwait(false);
+ var root = await response.Content.ReadFromJsonAsync<TokenDto>(_jsonOptions, cancellationToken).ConfigureAwait(false);
if (string.Equals(root?.Message, "OK", StringComparison.Ordinal))
{
_logger.LogInformation("Authenticated with Schedules Direct token: {Token}", root.Token);
@@ -691,10 +684,7 @@ namespace Emby.Server.Implementations.LiveTv.Listings
{
using var httpResponse = await Send(options, false, null, cancellationToken).ConfigureAwait(false);
httpResponse.EnsureSuccessStatusCode();
- await using var stream = await httpResponse.Content.ReadAsStreamAsync(cancellationToken).ConfigureAwait(false);
- using var response = httpResponse.Content;
- var root = await JsonSerializer.DeserializeAsync<LineupsDto>(stream, _jsonOptions, cancellationToken).ConfigureAwait(false);
-
+ var root = await httpResponse.Content.ReadFromJsonAsync<LineupsDto>(_jsonOptions, cancellationToken).ConfigureAwait(false);
return root?.Lineups.Any(i => string.Equals(info.ListingsId, i.Lineup, StringComparison.OrdinalIgnoreCase)) ?? false;
}
catch (HttpRequestException ex)
@@ -748,8 +738,7 @@ namespace Emby.Server.Implementations.LiveTv.Listings
options.Headers.TryAddWithoutValidation("token", token);
using var httpResponse = await Send(options, true, info, cancellationToken).ConfigureAwait(false);
- await using var stream = await httpResponse.Content.ReadAsStreamAsync(cancellationToken).ConfigureAwait(false);
- var root = await JsonSerializer.DeserializeAsync<ChannelDto>(stream, _jsonOptions, cancellationToken).ConfigureAwait(false);
+ var root = await httpResponse.Content.ReadFromJsonAsync<ChannelDto>(_jsonOptions, cancellationToken).ConfigureAwait(false);
if (root is null)
{
return new List<ChannelInfo>();
diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/BaseTunerHost.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/BaseTunerHost.cs
index 1721be9e2..ff25ee585 100644
--- a/Emby.Server.Implementations/LiveTv/TunerHosts/BaseTunerHost.cs
+++ b/Emby.Server.Implementations/LiveTv/TunerHosts/BaseTunerHost.cs
@@ -17,7 +17,6 @@ using MediaBrowser.Controller.LiveTv;
using MediaBrowser.Model.Dto;
using MediaBrowser.Model.IO;
using MediaBrowser.Model.LiveTv;
-using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.Logging;
namespace Emby.Server.Implementations.LiveTv.TunerHosts
diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs
index 7e588f681..8cd0c4ffb 100644
--- a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs
+++ b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs
@@ -9,6 +9,7 @@ using System.IO;
using System.Linq;
using System.Net;
using System.Net.Http;
+using System.Net.Http.Json;
using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;
@@ -27,7 +28,6 @@ using MediaBrowser.Model.IO;
using MediaBrowser.Model.LiveTv;
using MediaBrowser.Model.MediaInfo;
using MediaBrowser.Model.Net;
-using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.Logging;
namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
@@ -76,13 +76,10 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
var model = await GetModelInfo(info, false, cancellationToken).ConfigureAwait(false);
using var response = await _httpClientFactory.CreateClient(NamedClient.Default).GetAsync(model.LineupURL ?? model.BaseURL + "/lineup.json", HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false);
- await using var stream = await response.Content.ReadAsStreamAsync(cancellationToken).ConfigureAwait(false);
- var lineup = await JsonSerializer.DeserializeAsync<List<Channels>>(stream, _jsonOptions, cancellationToken)
- .ConfigureAwait(false) ?? new List<Channels>();
-
+ var lineup = await response.Content.ReadFromJsonAsync<IEnumerable<Channels>>(_jsonOptions, cancellationToken).ConfigureAwait(false) ?? Enumerable.Empty<Channels>();
if (info.ImportFavoritesOnly)
{
- lineup = lineup.Where(i => i.Favorite).ToList();
+ lineup = lineup.Where(i => i.Favorite);
}
return lineup.Where(i => !i.DRM).ToList();
@@ -129,9 +126,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
.GetAsync(GetApiUrl(info) + "/discover.json", HttpCompletionOption.ResponseHeadersRead, cancellationToken)
.ConfigureAwait(false);
response.EnsureSuccessStatusCode();
- await using var stream = await response.Content.ReadAsStreamAsync(cancellationToken).ConfigureAwait(false);
- var discoverResponse = await JsonSerializer.DeserializeAsync<DiscoverResponse>(stream, _jsonOptions, cancellationToken)
- .ConfigureAwait(false);
+ var discoverResponse = await response.Content.ReadFromJsonAsync<DiscoverResponse>(_jsonOptions, cancellationToken).ConfigureAwait(false);
if (!string.IsNullOrEmpty(cacheKey))
{
@@ -175,34 +170,37 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
using var response = await _httpClientFactory.CreateClient(NamedClient.Default)
.GetAsync(string.Format(CultureInfo.InvariantCulture, "{0}/tuners.html", GetApiUrl(info)), HttpCompletionOption.ResponseHeadersRead, cancellationToken)
.ConfigureAwait(false);
- await using var stream = await response.Content.ReadAsStreamAsync(cancellationToken).ConfigureAwait(false);
- using var sr = new StreamReader(stream, System.Text.Encoding.UTF8);
var tuners = new List<LiveTvTunerInfo>();
- await foreach (var line in sr.ReadAllLinesAsync().ConfigureAwait(false))
+ var stream = await response.Content.ReadAsStreamAsync(cancellationToken).ConfigureAwait(false);
+ await using (stream.ConfigureAwait(false))
{
- string stripedLine = StripXML(line);
- if (stripedLine.Contains("Channel", StringComparison.Ordinal))
+ using var sr = new StreamReader(stream, System.Text.Encoding.UTF8);
+ await foreach (var line in sr.ReadAllLinesAsync().ConfigureAwait(false))
{
- LiveTvTunerStatus status;
- var index = stripedLine.IndexOf("Channel", StringComparison.OrdinalIgnoreCase);
- var name = stripedLine.Substring(0, index - 1);
- var currentChannel = stripedLine.Substring(index + 7);
- if (string.Equals(currentChannel, "none", StringComparison.Ordinal))
+ string stripedLine = StripXML(line);
+ if (stripedLine.Contains("Channel", StringComparison.Ordinal))
{
- status = LiveTvTunerStatus.LiveTv;
- }
- else
- {
- status = LiveTvTunerStatus.Available;
- }
+ LiveTvTunerStatus status;
+ var index = stripedLine.IndexOf("Channel", StringComparison.OrdinalIgnoreCase);
+ var name = stripedLine.Substring(0, index - 1);
+ var currentChannel = stripedLine.Substring(index + 7);
+ if (string.Equals(currentChannel, "none", StringComparison.Ordinal))
+ {
+ status = LiveTvTunerStatus.LiveTv;
+ }
+ else
+ {
+ status = LiveTvTunerStatus.Available;
+ }
- tuners.Add(new LiveTvTunerInfo
- {
- Name = name,
- SourceType = string.IsNullOrWhiteSpace(model.ModelNumber) ? Name : model.ModelNumber,
- ProgramName = currentChannel,
- Status = status
- });
+ tuners.Add(new LiveTvTunerInfo
+ {
+ Name = name,
+ SourceType = string.IsNullOrWhiteSpace(model.ModelNumber) ? Name : model.ModelNumber,
+ ProgramName = currentChannel,
+ Status = status
+ });
+ }
}
}
diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunManager.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunManager.cs
index a8b090635..68383a554 100644
--- a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunManager.cs
+++ b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunManager.cs
@@ -44,8 +44,6 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
StopStreaming(socket).GetAwaiter().GetResult();
}
}
-
- GC.SuppressFinalize(this);
}
public async Task<bool> CheckTunerAvailability(IPAddress remoteIP, int tuner, CancellationToken cancellationToken)
diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/M3UTunerHost.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/M3UTunerHost.cs
index 613ea117f..db5e81df5 100644
--- a/Emby.Server.Implementations/LiveTv/TunerHosts/M3UTunerHost.cs
+++ b/Emby.Server.Implementations/LiveTv/TunerHosts/M3UTunerHost.cs
@@ -5,7 +5,6 @@
using System;
using System.Collections.Generic;
using System.Globalization;
-using System.IO;
using System.Linq;
using System.Net.Http;
using System.Threading;
@@ -22,7 +21,6 @@ using MediaBrowser.Model.Entities;
using MediaBrowser.Model.IO;
using MediaBrowser.Model.LiveTv;
using MediaBrowser.Model.MediaInfo;
-using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.Logging;
using Microsoft.Net.Http.Headers;
diff --git a/Emby.Server.Implementations/Localization/Core/en-US.json b/Emby.Server.Implementations/Localization/Core/en-US.json
index 15088384c..496ecabd3 100644
--- a/Emby.Server.Implementations/Localization/Core/en-US.json
+++ b/Emby.Server.Implementations/Localization/Core/en-US.json
@@ -112,6 +112,8 @@
"TaskCleanLogsDescription": "Deletes log files that are more than {0} days old.",
"TaskRefreshPeople": "Refresh People",
"TaskRefreshPeopleDescription": "Updates metadata for actors and directors in your media library.",
+ "TaskRefreshTrickplayImages": "Generate Trickplay Images",
+ "TaskRefreshTrickplayImagesDescription": "Creates trickplay previews for videos in enabled libraries.",
"TaskUpdatePlugins": "Update Plugins",
"TaskUpdatePluginsDescription": "Downloads and installs updates for plugins that are configured to update automatically.",
"TaskCleanTranscode": "Clean Transcode Directory",
diff --git a/Emby.Server.Implementations/Localization/Core/fo.json b/Emby.Server.Implementations/Localization/Core/fo.json
new file mode 100644
index 000000000..40aa5f71a
--- /dev/null
+++ b/Emby.Server.Implementations/Localization/Core/fo.json
@@ -0,0 +1,18 @@
+{
+ "Artists": "Listafólk",
+ "Collections": "Søvn",
+ "Default": "Sjálvgildi",
+ "DeviceOfflineWithName": "{0} hevur slitið sambandið",
+ "External": "Ytri",
+ "Genres": "Greinar",
+ "Albums": "Album",
+ "AppDeviceValues": "App: {0}, Eind: {1}",
+ "Application": "Nýtsluskipan",
+ "Books": "Bøkur",
+ "Channels": "Rásir",
+ "ChapterNameValue": "Kapittul {0}",
+ "DeviceOnlineWithName": "{0} er sambundið",
+ "Favorites": "Yndis",
+ "Folders": "Mappur",
+ "Forced": "Kravt"
+}
diff --git a/Emby.Server.Implementations/Localization/Core/is.json b/Emby.Server.Implementations/Localization/Core/is.json
index a40f49506..0f1f0b3d2 100644
--- a/Emby.Server.Implementations/Localization/Core/is.json
+++ b/Emby.Server.Implementations/Localization/Core/is.json
@@ -13,8 +13,8 @@
"HeaderFavoriteArtists": "Uppáhalds Listamenn",
"HeaderFavoriteAlbums": "Uppáhalds Plötur",
"HeaderContinueWatching": "Halda áfram að horfa",
- "HeaderAlbumArtists": "Höfundur plötu",
- "Genres": "Tegundir",
+ "HeaderAlbumArtists": "Listamaður á umslagi",
+ "Genres": "Stefnur",
"Folders": "Möppur",
"Favorites": "Uppáhalds",
"FailedLoginAttemptWithUserName": "{0} reyndi að auðkenna sig",
@@ -22,32 +22,32 @@
"DeviceOfflineWithName": "{0} hefur aftengst",
"Collections": "Söfn",
"ChapterNameValue": "Kafli {0}",
- "Channels": "Stöðvar",
- "CameraImageUploadedFrom": "Ný ljósmynd frá myndavél hefur verið hlaðið upp frá {0}",
+ "Channels": "Rásir",
+ "CameraImageUploadedFrom": "{0} hefur hlaðið upp nýrri ljósmynd úr myndavél sinni",
"Books": "Bækur",
- "AuthenticationSucceededWithUserName": "{0} auðkenning tókst",
- "Artists": "Listamaður",
+ "AuthenticationSucceededWithUserName": "Auðkenning fyrir {0} tókst",
+ "Artists": "Listamenn",
"Application": "Forrit",
"AppDeviceValues": "Snjallforrit: {0}, Tæki: {1}",
"Albums": "Plötur",
- "Plugin": "Viðbót",
- "Photos": "Myndir",
- "NotificationOptionVideoPlaybackStopped": "Myndbandafspilun stöðvuð",
- "NotificationOptionVideoPlayback": "Myndbandafspilun hafin",
+ "Plugin": "Viðbótarvirkni",
+ "Photos": "Ljósmyndir",
+ "NotificationOptionVideoPlaybackStopped": "Myndbandsafspilun stöðvuð",
+ "NotificationOptionVideoPlayback": "Myndbandsafspilun hafin",
"NotificationOptionUserLockedOut": "Notandi læstur úti",
- "NotificationOptionServerRestartRequired": "Endurræsing þjóns er nauðsynileg",
- "NotificationOptionPluginUpdateInstalled": "Viðbótar uppfærsla uppsett",
- "NotificationOptionPluginUninstalled": "Viðbót fjarlægð",
- "NotificationOptionPluginInstalled": "Viðbót sett upp",
+ "NotificationOptionServerRestartRequired": "Endurræsing þjóns er nauðsynleg",
+ "NotificationOptionPluginUpdateInstalled": "Uppfærslu á viðbótarvirkni lokið",
+ "NotificationOptionPluginUninstalled": "Viðbótarvirkni fjarlægð",
+ "NotificationOptionPluginInstalled": "Viðbótarvirkni sett upp",
"NotificationOptionPluginError": "Bilun í viðbót",
"NotificationOptionInstallationFailed": "Uppsetning tókst ekki",
- "NotificationOptionCameraImageUploaded": "Myndavélarmynd hlaðið upp",
+ "NotificationOptionCameraImageUploaded": "Ljósmynd hlaðið upp",
"NotificationOptionAudioPlaybackStopped": "Hljóðafspilun stöðvuð",
"NotificationOptionAudioPlayback": "Hljóðafspilun hafin",
"NotificationOptionApplicationUpdateInstalled": "Uppfærsla uppsett",
"NotificationOptionApplicationUpdateAvailable": "Uppfærsla í boði",
- "NameSeasonUnknown": "Sería óþekkt",
- "NameSeasonNumber": "Sería {0}",
+ "NameSeasonUnknown": "Þáttaröð óþekkt",
+ "NameSeasonNumber": "Þáttaröð {0}",
"MixedContent": "Blandað efni",
"MessageServerConfigurationUpdated": "Stillingar þjóns hafa verið uppfærðar",
"MessageApplicationUpdatedTo": "Jellyfin þjónn hefur verið uppfærður í {0}",
@@ -57,24 +57,24 @@
"User": "Notandi",
"System": "Kerfi",
"NotificationOptionNewLibraryContent": "Nýju efni bætt við",
- "NewVersionIsAvailable": "Ný útgáfa af Jellyfin þjón er fáanleg til niðurhals.",
+ "NewVersionIsAvailable": "Ný útgáfa af Jellyfin þjón er tilbúin til niðurhals.",
"NameInstallFailed": "{0} uppsetning mistókst",
"MusicVideos": "Tónlistarmyndbönd",
"Music": "Tónlist",
"Movies": "Kvikmyndir",
"UserDeletedWithName": "Notanda {0} hefur verið eytt",
"UserCreatedWithName": "Notandi {0} hefur verið stofnaður",
- "TvShows": "Þættir",
+ "TvShows": "Sjónvarpsþættir",
"Sync": "Samstilla",
"Songs": "Lög",
- "ServerNameNeedsToBeRestarted": "{0} þarf að endurræsa",
+ "ServerNameNeedsToBeRestarted": "{0} þarf að vera endurræstur",
"ScheduledTaskStartedWithName": "{0} hafin",
"ScheduledTaskFailedWithName": "{0} mistókst",
"PluginUpdatedWithName": "{0} var uppfært",
"PluginUninstalledWithName": "{0} var fjarlægt",
"PluginInstalledWithName": "{0} var sett upp",
"NotificationOptionTaskFailed": "Tímasett verkefni mistókst",
- "StartupEmbyServerIsLoading": "Jellyfin netþjónnin er að hlaðast. Vinsamlega prufaðu aftur fljótlega.",
+ "StartupEmbyServerIsLoading": "Jellyfin netþjónnin er að ræsa sig upp. Vinsamlegast reyndu aftur fljótlega.",
"VersionNumber": "Útgáfa {0}",
"ValueHasBeenAddedToLibrary": "{0} hefur verið bætt við í gagnasafnið þitt",
"UserStoppedPlayingItemWithValues": "{0} hefur lokið spilunar af {1} á {2}",
@@ -83,14 +83,14 @@
"UserPasswordChangedWithName": "Lykilorði fyrir notandann {0} hefur verið breytt",
"UserOnlineFromDevice": "{0} hefur verið virkur síðan {1}",
"UserOfflineFromDevice": "{0} hefur aftengst frá {1}",
- "UserLockedOutWithName": "Notanda {0} hefur verið heflaður aðgangur",
- "UserDownloadingItemWithValues": "{0} Hleður niður {1}",
+ "UserLockedOutWithName": "Notandi {0} hefur verið læstur úti",
+ "UserDownloadingItemWithValues": "{0} hleður niður {1}",
"SubtitleDownloadFailureFromForItem": "Tókst ekki að hala niður skjátextum frá {0} til {1}",
- "ProviderValue": "Veitandi: {0}",
+ "ProviderValue": "Efnisveita: {0}",
"MessageNamedServerConfigurationUpdatedWithValue": "Stilling {0} hefur verið uppfærð á netþjón",
- "ValueSpecialEpisodeName": "Sérstakt - {0}",
- "Shows": "Sýningar",
- "Playlists": "Spilunarlisti",
+ "ValueSpecialEpisodeName": "Sérstaktur - {0}",
+ "Shows": "Þættir",
+ "Playlists": "Efnisskrár",
"TaskRefreshChannelsDescription": "Endurhlaða upplýsingum netrása.",
"TaskRefreshChannels": "Endurhlaða Rásir",
"TaskCleanTranscodeDescription": "Eyða umkóðuðum skrám sem eru meira en einum degi eldri.",
@@ -116,5 +116,12 @@
"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"
+ "HearingImpaired": "Heyrnarskertur",
+ "TaskOptimizeDatabaseDescription": "Þjappar gagnagrunni og bætir við lausu diskaplássi. Að keyra þessa aðgerð eftir skönnun safnsins, eða eftir einhverjar breytingar sem fela í sér gagnagrunnsbreytingar, gætu aukið hraðvirkni.",
+ "TaskKeyframeExtractor": "Lykilrammaplokkari",
+ "TaskKeyframeExtractorDescription": "Plokkar lykilramma úr myndbandsskrám til að búa til nákvæmari HLS uppskiptingarlista. Þetta verk getur tekið langan tíma.",
+ "TaskRefreshChapterImages": "Plokka kafla-myndir",
+ "TaskCleanActivityLogDescription": "Eyðir virkniskráningarfærslum sem hafa náð settum hámarksaldri.",
+ "Forced": "Þvingað",
+ "External": "Útvær"
}
diff --git a/Emby.Server.Implementations/Localization/Core/lv.json b/Emby.Server.Implementations/Localization/Core/lv.json
index f7b24412a..83a000014 100644
--- a/Emby.Server.Implementations/Localization/Core/lv.json
+++ b/Emby.Server.Implementations/Localization/Core/lv.json
@@ -1,7 +1,7 @@
{
"ServerNameNeedsToBeRestarted": "{0} ir vajadzīgs restarts",
"NotificationOptionTaskFailed": "Plānota uzdevuma kļūme",
- "HeaderRecordingGroups": "Ierakstu Grupas",
+ "HeaderRecordingGroups": "Ierakstu grupas",
"UserPolicyUpdatedWithName": "Lietotāju politika atjaunota priekš {0}",
"SubtitleDownloadFailureFromForItem": "Subtitru lejupielāde no {0} priekš {1} neizdevās",
"NotificationOptionVideoPlaybackStopped": "Video atskaņošana apturēta",
@@ -14,7 +14,7 @@
"Photos": "Attēli",
"NotificationOptionUserLockedOut": "Lietotājs bloķēts",
"LabelRunningTimeValue": "Garums: {0}",
- "Inherit": "Mantot",
+ "Inherit": "Pārmantot",
"AppDeviceValues": "Lietotne: {0}, Ierīce: {1}",
"VersionNumber": "Versija {0}",
"ValueHasBeenAddedToLibrary": "{0} ir ticis pievienots jūsu multvides bibliotēkai",
@@ -28,7 +28,7 @@
"UserDeletedWithName": "Lietotājs {0} ir izdzēsts",
"UserCreatedWithName": "Lietotājs {0} ir ticis izveidots",
"User": "Lietotājs",
- "TvShows": "TV Raidījumi",
+ "TvShows": "TV raidījumi",
"Sync": "Sinhronizācija",
"System": "Sistēma",
"StartupEmbyServerIsLoading": "Jellyfin Serveris lādējas. Lūdzu mēģiniet vēlreiz pēc brīža.",
@@ -38,11 +38,11 @@
"PluginUninstalledWithName": "{0} tika noņemts",
"PluginInstalledWithName": "{0} tika uzstādīts",
"Plugin": "Paplašinājums",
- "Playlists": "Atskaņošanas Saraksti",
+ "Playlists": "Atskaņošanas saraksti",
"MixedContent": "Jaukts saturs",
- "HomeVideos": "Mājas Video",
+ "HomeVideos": "Mājas video",
"HeaderNextUp": "Nākamais",
- "ChapterNameValue": "Nodaļa {0}",
+ "ChapterNameValue": "{0}. nodaļa",
"Application": "Lietotne",
"NotificationOptionServerRestartRequired": "Vajadzīgs servera restarts",
"NotificationOptionPluginUpdateInstalled": "Paplašinājuma atjauninājums uzstādīts",
@@ -56,14 +56,14 @@
"NotificationOptionApplicationUpdateInstalled": "Lietotnes atjauninājums uzstādīts",
"NotificationOptionApplicationUpdateAvailable": "Lietotnes atjauninājums pieejams",
"NewVersionIsAvailable": "Lejupielādei ir pieejama jauna Jellyfin Server versija.",
- "NameSeasonUnknown": "Nezināma Sezona",
- "NameSeasonNumber": "Sezona {0}",
+ "NameSeasonUnknown": "Nezināma sezona",
+ "NameSeasonNumber": "{0}. sezona",
"NameInstallFailed": "{0} instalācija neizdevās",
"MusicVideos": "Mūzikas video",
"Music": "Mūzika",
"Movies": "Filmas",
"MessageServerConfigurationUpdated": "Servera konfigurācija ir tikusi atjaunota",
- "MessageNamedServerConfigurationUpdatedWithValue": "Servera konfigurācijas sadaļa {0} ir tikusi atjaunota",
+ "MessageNamedServerConfigurationUpdatedWithValue": "Servera konfigurācijas sadaļa {0} tika atjaunota",
"MessageApplicationUpdatedTo": "Jellyfin Server ir ticis atjaunots uz {0}",
"MessageApplicationUpdated": "Jellyfin Server ir ticis atjaunots",
"Latest": "Jaunākais",
@@ -71,57 +71,57 @@
"ItemRemovedWithName": "{0} tika noņemts no bibliotēkas",
"ItemAddedWithName": "{0} tika pievienots bibliotēkai",
"HeaderLiveTV": "Tiešraides TV",
- "HeaderContinueWatching": "Turpināt Skatīšanos",
- "HeaderAlbumArtists": "Albumu Izpildītāji",
+ "HeaderContinueWatching": "Turpināt skatīšanos",
+ "HeaderAlbumArtists": "Albumu izpildītāji",
"Genres": "Žanri",
"Folders": "Mapes",
- "Favorites": "Favorīti",
- "FailedLoginAttemptWithUserName": "Neizdevies pieslēgšanās mēģinājums no {0}",
- "DeviceOnlineWithName": "{0} ir pievienojies",
- "DeviceOfflineWithName": "{0} ir atvienojies",
+ "Favorites": "Izlase",
+ "FailedLoginAttemptWithUserName": "Neizdevies ieiešanas mēģinājums no {0}",
+ "DeviceOnlineWithName": "Savienojums ar {0} ir izveidots",
+ "DeviceOfflineWithName": "Savienojums ar {0} ir pārtraukts",
"Collections": "Kolekcijas",
"Channels": "Kanāli",
- "CameraImageUploadedFrom": "Jauns kameras attēls ir ticis augšupielādēts no {0}",
+ "CameraImageUploadedFrom": "Jauns kameras attēls tika augšupielādēts no {0}",
"Books": "Grāmatas",
"Artists": "Izpildītāji",
"Albums": "Albumi",
"ProviderValue": "Provider: {0}",
- "HeaderFavoriteSongs": "Dziesmu Favorīti",
- "HeaderFavoriteShows": "Raidījumu Favorīti",
- "HeaderFavoriteEpisodes": "Episožu Favorīti",
- "HeaderFavoriteArtists": "Izpildītāju Favorīti",
- "HeaderFavoriteAlbums": "Albumu Favorīti",
- "TaskCleanCacheDescription": "Nodzēš keša datnes, kas vairs nav sistēmai vajadzīgas.",
- "TaskRefreshChapterImages": "Izvilkt Nodaļu Attēlus",
+ "HeaderFavoriteSongs": "Dziesmu izlase",
+ "HeaderFavoriteShows": "Raidījumu izlase",
+ "HeaderFavoriteEpisodes": "Sēriju izlase",
+ "HeaderFavoriteArtists": "Izpildītāju izlase",
+ "HeaderFavoriteAlbums": "Albumu izlase",
+ "TaskCleanCacheDescription": "Nodzēš kešatmiņas datnes, kas vairs nav sistēmai vajadzīgas.",
+ "TaskRefreshChapterImages": "Izvilkt nodaļu attēlus",
"TasksApplicationCategory": "Lietotne",
"TasksLibraryCategory": "Bibliotēka",
"TaskDownloadMissingSubtitlesDescription": "Internetā meklē trūkstošus subtitrus balstoties uz metadatu uzstādījumiem.",
- "TaskDownloadMissingSubtitles": "Lejupielādēt trūkstošus subtitrus",
+ "TaskDownloadMissingSubtitles": "Lejupielādēt trūkstošos subtitrus",
"TaskRefreshChannelsDescription": "Atjauno interneta kanālu informāciju.",
- "TaskRefreshChannels": "Atjaunot Kanālus",
- "TaskCleanTranscodeDescription": "Izdzēš trans-kodēšanas datnes, kas ir vecākas par vienu dienu.",
- "TaskCleanTranscode": "Iztīrīt Trans-kodēšanas Mapi",
+ "TaskRefreshChannels": "Atjaunot kanālus",
+ "TaskCleanTranscodeDescription": "Izdzēš transkodēšanas datnes, kas ir senākas par vienu dienu.",
+ "TaskCleanTranscode": "Iztīrīt transkodēšanas mapi",
"TaskUpdatePluginsDescription": "Lejupielādē un uzstāda atjauninājumus paplašinājumiem, kam ir uzstādīta automātiskā atjaunināšana.",
- "TaskUpdatePlugins": "Atjaunot Paplašinājumus",
+ "TaskUpdatePlugins": "Atjaunot paplašinājumus",
"TaskRefreshPeopleDescription": "Atjauno metadatus aktieriem un direktoriem jūsu multivides bibliotēkā.",
- "TaskRefreshPeople": "Atjaunot Cilvēkus",
- "TaskCleanLogsDescription": "Nodzēš log datnes, kas ir vairāk par {0} dienām vecas.",
- "TaskCleanLogs": "Iztīrīt Logdatņu Mapi",
+ "TaskRefreshPeople": "Atjaunot cilvēkus",
+ "TaskCleanLogsDescription": "Nodzēš logdatnes, kas ir senākas par {0} dienām.",
+ "TaskCleanLogs": "Iztīrīt logdatņu mapi",
"TaskRefreshLibraryDescription": "Skenē jūsu multivides bibliotēku, lai atrastu jaunas datnes, un atsvaidzina metadatus.",
- "TaskRefreshLibrary": "Skenēt Multivides Bibliotēku",
+ "TaskRefreshLibrary": "Skenēt multivides bibliotēku",
"TaskRefreshChapterImagesDescription": "Izveido sīktēlus priekš video ar sadaļām.",
- "TaskCleanCache": "Iztīrīt Kešošanas Mapi",
- "TasksChannelsCategory": "Interneta Kanāli",
+ "TaskCleanCache": "Iztīrīt kešatmiņas mapi",
+ "TasksChannelsCategory": "Interneta kanāli",
"TasksMaintenanceCategory": "Apkope",
- "Forced": "Piespiests",
+ "Forced": "Piespiedu",
"TaskCleanActivityLogDescription": "Nodzēš darbību žurnāla ierakstus, kuri ir vecāki par doto vecumu.",
- "TaskCleanActivityLog": "Notīrīt Darbību Žurnālu",
+ "TaskCleanActivityLog": "Notīrīt darbību žurnālu",
"Undefined": "Nenoteikts",
"Default": "Noklusējuma",
- "TaskOptimizeDatabaseDescription": "Saspiež datubāzi un atbrīvo atmiņu. Uzdevum palaišana pēc bibliotēku skenēšanas vai citām, ar datubāzi saistītām, izmaiņām iespējams uzlabos ātrdarbību.",
+ "TaskOptimizeDatabaseDescription": "Saspiež datubāzi un atbrīvo atmiņu. Šī uzdevuma palaišana pēc bibliotēku skenēšanas vai citām, ar datubāzi saistītām, izmaiņām iespējams uzlabos ātrdarbību.",
"TaskOptimizeDatabase": "Optimizēt datubāzi",
"External": "Ārējais",
"HearingImpaired": "Ar dzirdes traucējumiem",
- "TaskKeyframeExtractor": "Atslēgkadru Ekstraktors",
+ "TaskKeyframeExtractor": "Atslēgkadru ekstraktors",
"TaskKeyframeExtractorDescription": "Ekstraktē atslēgkadrus no video failiem lai izveidotu precīzākus HLS atskaņošanas sarakstus. Šis process var būt ilgs."
}
diff --git a/Emby.Server.Implementations/Localization/Core/ml.json b/Emby.Server.Implementations/Localization/Core/ml.json
index 0620fbcdb..0b50fa529 100644
--- a/Emby.Server.Implementations/Localization/Core/ml.json
+++ b/Emby.Server.Implementations/Localization/Core/ml.json
@@ -121,5 +121,7 @@
"TaskOptimizeDatabaseDescription": "ഡാറ്റാബേസ് ചുരുക്കുകയും സ്വതന്ത്ര ഇടം വെട്ടിച്ചുരുക്കുകയും ചെയ്യുന്നു. ലൈബ്രറി സ്‌കാൻ ചെയ്‌തതിനുശേഷം അല്ലെങ്കിൽ ഡാറ്റാബേസ് പരിഷ്‌ക്കരണങ്ങളെ സൂചിപ്പിക്കുന്ന മറ്റ് മാറ്റങ്ങൾ ചെയ്‌തതിന് ശേഷം ഈ ടാസ്‌ക് പ്രവർത്തിപ്പിക്കുന്നത് പ്രകടനം മെച്ചപ്പെടുത്തും.",
"TaskOptimizeDatabase": "ഡാറ്റാബേസ് ഒപ്റ്റിമൈസ് ചെയ്യുക",
"HearingImpaired": "കേൾവി തകരാറുകൾ",
- "External": "പുറമേയുള്ള"
+ "External": "പുറമേയുള്ള",
+ "TaskKeyframeExtractorDescription": "കൂടുതൽ കൃത്യമായ HLS പ്ലേലിസ്റ്റുകൾ സൃഷ്‌ടിക്കുന്നതിന് വീഡിയോ ഫയലുകളിൽ നിന്ന് കീഫ്രെയിമുകൾ എക്‌സ്‌ട്രാക്‌റ്റ് ചെയ്യുന്നു. ഈ പ്രവർത്തനം പൂർത്തിയാവാൻ കുറച്ചധികം സമയം എടുത്തേക്കാം.",
+ "TaskKeyframeExtractor": "കീഫ്രെയിം എക്സ്ട്രാക്റ്റർ"
}
diff --git a/Emby.Server.Implementations/Localization/Core/si.json b/Emby.Server.Implementations/Localization/Core/si.json
new file mode 100644
index 000000000..0967ef424
--- /dev/null
+++ b/Emby.Server.Implementations/Localization/Core/si.json
@@ -0,0 +1 @@
+{}
diff --git a/Emby.Server.Implementations/Localization/Core/sk.json b/Emby.Server.Implementations/Localization/Core/sk.json
index 858cc40dd..c231d76fe 100644
--- a/Emby.Server.Implementations/Localization/Core/sk.json
+++ b/Emby.Server.Implementations/Localization/Core/sk.json
@@ -124,5 +124,5 @@
"TaskKeyframeExtractorDescription": "Extrahuje kľúčové snímky z video súborov na vytvorenie presnejších HLS playlistov. Táto úloha môže trvať dlhšiu dobu.",
"TaskKeyframeExtractor": "Extraktor kľúčových snímkov",
"External": "Externé",
- "HearingImpaired": "Sluchovo Postihnutý"
+ "HearingImpaired": "Sluchovo postihnutí"
}
diff --git a/Emby.Server.Implementations/Localization/LocalizationManager.cs b/Emby.Server.Implementations/Localization/LocalizationManager.cs
index 96f435399..16776b6bd 100644
--- a/Emby.Server.Implementations/Localization/LocalizationManager.cs
+++ b/Emby.Server.Implementations/Localization/LocalizationManager.cs
@@ -71,25 +71,28 @@ namespace Emby.Server.Implementations.Localization
string countryCode = resource.Substring(RatingsPath.Length, 2);
var dict = new Dictionary<string, ParentalRating>(StringComparer.OrdinalIgnoreCase);
- await using var stream = _assembly.GetManifestResourceStream(resource);
- using var reader = new StreamReader(stream!); // shouldn't be null here, we just got the resource path from Assembly.GetManifestResourceNames()
- await foreach (var line in reader.ReadAllLinesAsync().ConfigureAwait(false))
+ var stream = _assembly.GetManifestResourceStream(resource);
+ await using (stream!.ConfigureAwait(false)) // shouldn't be null here, we just got the resource path from Assembly.GetManifestResourceNames()
{
- if (string.IsNullOrWhiteSpace(line))
+ using var reader = new StreamReader(stream!);
+ await foreach (var line in reader.ReadAllLinesAsync().ConfigureAwait(false))
{
- continue;
- }
-
- string[] parts = line.Split(',');
- if (parts.Length == 2
- && int.TryParse(parts[1], NumberStyles.Integer, CultureInfo.InvariantCulture, out var value))
- {
- var name = parts[0];
- dict.Add(name, new ParentalRating(name, value));
- }
- else
- {
- _logger.LogWarning("Malformed line in ratings file for country {CountryCode}", countryCode);
+ if (string.IsNullOrWhiteSpace(line))
+ {
+ continue;
+ }
+
+ string[] parts = line.Split(',');
+ if (parts.Length == 2
+ && int.TryParse(parts[1], NumberStyles.Integer, CultureInfo.InvariantCulture, out var value))
+ {
+ var name = parts[0];
+ dict.Add(name, new ParentalRating(name, value));
+ }
+ else
+ {
+ _logger.LogWarning("Malformed line in ratings file for country {CountryCode}", countryCode);
+ }
}
}
diff --git a/Emby.Server.Implementations/MediaEncoder/EncodingManager.cs b/Emby.Server.Implementations/MediaEncoder/EncodingManager.cs
index 7732e32d0..896f47923 100644
--- a/Emby.Server.Implementations/MediaEncoder/EncodingManager.cs
+++ b/Emby.Server.Implementations/MediaEncoder/EncodingManager.cs
@@ -222,7 +222,7 @@ namespace Emby.Server.Implementations.MediaEncoder
{
var deadImages = images
.Except(chapters.Select(i => i.ImagePath).Where(i => !string.IsNullOrEmpty(i)), StringComparer.OrdinalIgnoreCase)
- .Where(i => BaseItem.SupportedImageExtensions.Contains(Path.GetExtension(i), StringComparison.OrdinalIgnoreCase))
+ .Where(i => BaseItem.SupportedImageExtensions.Contains(Path.GetExtension(i.AsSpan()), StringComparison.OrdinalIgnoreCase))
.ToList();
foreach (var image in deadImages)
diff --git a/Emby.Server.Implementations/Net/SocketFactory.cs b/Emby.Server.Implementations/Net/SocketFactory.cs
index 51e92953d..2bcd5eab2 100644
--- a/Emby.Server.Implementations/Net/SocketFactory.cs
+++ b/Emby.Server.Implementations/Net/SocketFactory.cs
@@ -1,12 +1,15 @@
-#pragma warning disable CS1591
-
using System;
+using System.Linq;
using System.Net;
+using System.Net.NetworkInformation;
using System.Net.Sockets;
using MediaBrowser.Model.Net;
namespace Emby.Server.Implementations.Net
{
+ /// <summary>
+ /// Factory class to create different kinds of sockets.
+ /// </summary>
public class SocketFactory : ISocketFactory
{
/// <inheritdoc />
@@ -29,7 +32,7 @@ namespace Emby.Server.Implementations.Net
}
catch
{
- socket?.Dispose();
+ socket.Dispose();
throw;
}
@@ -38,7 +41,8 @@ namespace Emby.Server.Implementations.Net
/// <inheritdoc />
public Socket CreateSsdpUdpSocket(IPData bindInterface, int localPort)
{
- ArgumentNullException.ThrowIfNull(bindInterface.Address);
+ var interfaceAddress = bindInterface.Address;
+ ArgumentNullException.ThrowIfNull(interfaceAddress);
if (localPort < 0)
{
@@ -49,13 +53,13 @@ namespace Emby.Server.Implementations.Net
try
{
socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
- socket.Bind(new IPEndPoint(bindInterface.Address, localPort));
+ socket.Bind(new IPEndPoint(interfaceAddress, localPort));
return socket;
}
catch
{
- socket?.Dispose();
+ socket.Dispose();
throw;
}
@@ -82,22 +86,31 @@ namespace Emby.Server.Implementations.Net
try
{
- var interfaceIndex = bindInterface.Index;
- var interfaceIndexSwapped = (int)IPAddress.HostToNetworkOrder(interfaceIndex);
-
socket.MulticastLoopback = false;
socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
socket.SetSocketOption(SocketOptionLevel.IP, SocketOptionName.PacketInformation, true);
socket.SetSocketOption(SocketOptionLevel.IP, SocketOptionName.MulticastTimeToLive, multicastTimeToLive);
- socket.SetSocketOption(SocketOptionLevel.IP, SocketOptionName.MulticastInterface, interfaceIndexSwapped);
- socket.SetSocketOption(SocketOptionLevel.IP, SocketOptionName.AddMembership, new MulticastOption(multicastAddress, interfaceIndex));
- socket.Bind(new IPEndPoint(multicastAddress, localPort));
+
+ if (OperatingSystem.IsLinux() || OperatingSystem.IsMacOS())
+ {
+ socket.SetSocketOption(SocketOptionLevel.IP, SocketOptionName.AddMembership, new MulticastOption(multicastAddress));
+ socket.Bind(new IPEndPoint(multicastAddress, localPort));
+ }
+ else
+ {
+ // Only create socket if interface supports multicast
+ var interfaceIndex = bindInterface.Index;
+ var interfaceIndexSwapped = IPAddress.HostToNetworkOrder(interfaceIndex);
+
+ socket.SetSocketOption(SocketOptionLevel.IP, SocketOptionName.AddMembership, new MulticastOption(multicastAddress, interfaceIndex));
+ socket.Bind(new IPEndPoint(bindIPAddress, localPort));
+ }
return socket;
}
catch
{
- socket?.Dispose();
+ socket.Dispose();
throw;
}
diff --git a/Emby.Server.Implementations/Playlists/PlaylistManager.cs b/Emby.Server.Implementations/Playlists/PlaylistManager.cs
index 0cb450cdf..649c49924 100644
--- a/Emby.Server.Implementations/Playlists/PlaylistManager.cs
+++ b/Emby.Server.Implementations/Playlists/PlaylistManager.cs
@@ -327,9 +327,9 @@ namespace Emby.Server.Implementations.Playlists
// this is probably best done as a metadata provider
// saving a file over itself will require some work to prevent this from happening when not needed
var playlistPath = item.Path;
- var extension = Path.GetExtension(playlistPath);
+ var extension = Path.GetExtension(playlistPath.AsSpan());
- if (string.Equals(".wpl", extension, StringComparison.OrdinalIgnoreCase))
+ if (extension.Equals(".wpl", StringComparison.OrdinalIgnoreCase))
{
var playlist = new WplPlaylist();
foreach (var child in item.GetLinkedChildren())
@@ -362,8 +362,7 @@ namespace Emby.Server.Implementations.Playlists
string text = new WplContent().ToText(playlist);
File.WriteAllText(playlistPath, text);
}
-
- if (string.Equals(".zpl", extension, StringComparison.OrdinalIgnoreCase))
+ else if (extension.Equals(".zpl", StringComparison.OrdinalIgnoreCase))
{
var playlist = new ZplPlaylist();
foreach (var child in item.GetLinkedChildren())
@@ -396,8 +395,7 @@ namespace Emby.Server.Implementations.Playlists
string text = new ZplContent().ToText(playlist);
File.WriteAllText(playlistPath, text);
}
-
- if (string.Equals(".m3u", extension, StringComparison.OrdinalIgnoreCase))
+ else if (extension.Equals(".m3u", StringComparison.OrdinalIgnoreCase))
{
var playlist = new M3uPlaylist
{
@@ -428,8 +426,7 @@ namespace Emby.Server.Implementations.Playlists
string text = new M3uContent().ToText(playlist);
File.WriteAllText(playlistPath, text);
}
-
- if (string.Equals(".m3u8", extension, StringComparison.OrdinalIgnoreCase))
+ else if (extension.Equals(".m3u8", StringComparison.OrdinalIgnoreCase))
{
var playlist = new M3uPlaylist();
playlist.IsExtended = true;
@@ -458,8 +455,7 @@ namespace Emby.Server.Implementations.Playlists
string text = new M3uContent().ToText(playlist);
File.WriteAllText(playlistPath, text);
}
-
- if (string.Equals(".pls", extension, StringComparison.OrdinalIgnoreCase))
+ else if (extension.Equals(".pls", StringComparison.OrdinalIgnoreCase))
{
var playlist = new PlsPlaylist();
foreach (var child in item.GetLinkedChildren())
diff --git a/Emby.Server.Implementations/Plugins/PluginManager.cs b/Emby.Server.Implementations/Plugins/PluginManager.cs
index d7189ef0c..20793ee39 100644
--- a/Emby.Server.Implementations/Plugins/PluginManager.cs
+++ b/Emby.Server.Implementations/Plugins/PluginManager.cs
@@ -386,11 +386,11 @@ namespace Emby.Server.Implementations.Plugins
var url = new Uri(packageInfo.ImageUrl);
imagePath = Path.Join(path, url.Segments[^1]);
- await using var fileStream = AsyncFile.OpenWrite(imagePath);
-
+ var fileStream = AsyncFile.OpenWrite(imagePath);
+ Stream? downloadStream = null;
try
{
- await using var downloadStream = await HttpClientFactory
+ downloadStream = await HttpClientFactory
.CreateClient(NamedClient.Default)
.GetStreamAsync(url)
.ConfigureAwait(false);
@@ -402,6 +402,14 @@ namespace Emby.Server.Implementations.Plugins
_logger.LogError(ex, "Failed to download image to path {Path} on disk.", imagePath);
imagePath = string.Empty;
}
+ finally
+ {
+ await fileStream.DisposeAsync().ConfigureAwait(false);
+ if (downloadStream is not null)
+ {
+ await downloadStream.DisposeAsync().ConfigureAwait(false);
+ }
+ }
}
var manifest = new PluginManifest
@@ -421,7 +429,7 @@ namespace Emby.Server.Implementations.Plugins
ImagePath = imagePath
};
- if (!await ReconcileManifest(manifest, path))
+ if (!await ReconcileManifest(manifest, path).ConfigureAwait(false))
{
// An error occurred during reconciliation and saving could be undesirable.
return false;
@@ -458,7 +466,7 @@ namespace Emby.Server.Implementations.Plugins
}
using var metaStream = File.OpenRead(metafile);
- var localManifest = await JsonSerializer.DeserializeAsync<PluginManifest>(metaStream, _jsonOptions);
+ var localManifest = await JsonSerializer.DeserializeAsync<PluginManifest>(metaStream, _jsonOptions).ConfigureAwait(false);
localManifest ??= new PluginManifest();
if (!Equals(localManifest.Id, manifest.Id))
diff --git a/Emby.Server.Implementations/ScheduledTasks/Tasks/ChapterImagesTask.cs b/Emby.Server.Implementations/ScheduledTasks/Tasks/ChapterImagesTask.cs
index 6ad6c4cbd..5d15c3a21 100644
--- a/Emby.Server.Implementations/ScheduledTasks/Tasks/ChapterImagesTask.cs
+++ b/Emby.Server.Implementations/ScheduledTasks/Tasks/ChapterImagesTask.cs
@@ -115,7 +115,7 @@ namespace Emby.Server.Implementations.ScheduledTasks.Tasks
{
try
{
- previouslyFailedImages = File.ReadAllText(failHistoryPath)
+ previouslyFailedImages = (await File.ReadAllTextAsync(failHistoryPath, cancellationToken).ConfigureAwait(false))
.Split('|', StringSplitOptions.RemoveEmptyEntries)
.ToList();
}
@@ -156,7 +156,7 @@ namespace Emby.Server.Implementations.ScheduledTasks.Tasks
}
string text = string.Join('|', previouslyFailedImages);
- File.WriteAllText(failHistoryPath, text);
+ await File.WriteAllTextAsync(failHistoryPath, text, cancellationToken).ConfigureAwait(false);
}
numComplete++;
diff --git a/Emby.Server.Implementations/SystemManager.cs b/Emby.Server.Implementations/SystemManager.cs
new file mode 100644
index 000000000..2c477218f
--- /dev/null
+++ b/Emby.Server.Implementations/SystemManager.cs
@@ -0,0 +1,104 @@
+using System;
+using System.Linq;
+using System.Threading.Tasks;
+using MediaBrowser.Common.Configuration;
+using MediaBrowser.Common.Updates;
+using MediaBrowser.Controller;
+using MediaBrowser.Controller.Configuration;
+using MediaBrowser.Model.System;
+using Microsoft.AspNetCore.Http;
+using Microsoft.Extensions.Hosting;
+
+namespace Emby.Server.Implementations;
+
+/// <inheritdoc />
+public class SystemManager : ISystemManager
+{
+ private readonly IHostApplicationLifetime _applicationLifetime;
+ private readonly IServerApplicationHost _applicationHost;
+ private readonly IServerApplicationPaths _applicationPaths;
+ private readonly IServerConfigurationManager _configurationManager;
+ private readonly IStartupOptions _startupOptions;
+ private readonly IInstallationManager _installationManager;
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="SystemManager"/> class.
+ /// </summary>
+ /// <param name="applicationLifetime">Instance of <see cref="IHostApplicationLifetime"/>.</param>
+ /// <param name="applicationHost">Instance of <see cref="IServerApplicationHost"/>.</param>
+ /// <param name="applicationPaths">Instance of <see cref="IServerApplicationPaths"/>.</param>
+ /// <param name="configurationManager">Instance of <see cref="IServerConfigurationManager"/>.</param>
+ /// <param name="startupOptions">Instance of <see cref="IStartupOptions"/>.</param>
+ /// <param name="installationManager">Instance of <see cref="IInstallationManager"/>.</param>
+ public SystemManager(
+ IHostApplicationLifetime applicationLifetime,
+ IServerApplicationHost applicationHost,
+ IServerApplicationPaths applicationPaths,
+ IServerConfigurationManager configurationManager,
+ IStartupOptions startupOptions,
+ IInstallationManager installationManager)
+ {
+ _applicationLifetime = applicationLifetime;
+ _applicationHost = applicationHost;
+ _applicationPaths = applicationPaths;
+ _configurationManager = configurationManager;
+ _startupOptions = startupOptions;
+ _installationManager = installationManager;
+ }
+
+ /// <inheritdoc />
+ public SystemInfo GetSystemInfo(HttpRequest request)
+ {
+ return new SystemInfo
+ {
+ HasPendingRestart = _applicationHost.HasPendingRestart,
+ IsShuttingDown = _applicationLifetime.ApplicationStopping.IsCancellationRequested,
+ Version = _applicationHost.ApplicationVersionString,
+ WebSocketPortNumber = _applicationHost.HttpPort,
+ CompletedInstallations = _installationManager.CompletedInstallations.ToArray(),
+ Id = _applicationHost.SystemId,
+ ProgramDataPath = _applicationPaths.ProgramDataPath,
+ WebPath = _applicationPaths.WebPath,
+ LogPath = _applicationPaths.LogDirectoryPath,
+ ItemsByNamePath = _applicationPaths.InternalMetadataPath,
+ InternalMetadataPath = _applicationPaths.InternalMetadataPath,
+ CachePath = _applicationPaths.CachePath,
+ TranscodingTempPath = _configurationManager.GetTranscodePath(),
+ ServerName = _applicationHost.FriendlyName,
+ LocalAddress = _applicationHost.GetSmartApiUrl(request),
+ SupportsLibraryMonitor = true,
+ PackageName = _startupOptions.PackageName,
+ CastReceiverApplications = _configurationManager.Configuration.CastReceiverApplications
+ };
+ }
+
+ /// <inheritdoc />
+ public PublicSystemInfo GetPublicSystemInfo(HttpRequest request)
+ {
+ return new PublicSystemInfo
+ {
+ Version = _applicationHost.ApplicationVersionString,
+ ProductName = _applicationHost.Name,
+ Id = _applicationHost.SystemId,
+ ServerName = _applicationHost.FriendlyName,
+ LocalAddress = _applicationHost.GetSmartApiUrl(request),
+ StartupWizardCompleted = _configurationManager.CommonConfiguration.IsStartupWizardCompleted
+ };
+ }
+
+ /// <inheritdoc />
+ public void Restart() => ShutdownInternal(true);
+
+ /// <inheritdoc />
+ public void Shutdown() => ShutdownInternal(false);
+
+ private void ShutdownInternal(bool restart)
+ {
+ Task.Run(async () =>
+ {
+ await Task.Delay(100).ConfigureAwait(false);
+ _applicationHost.ShouldRestart = restart;
+ _applicationLifetime.StopApplication();
+ });
+ }
+}
diff --git a/Emby.Server.Implementations/Udp/UdpServer.cs b/Emby.Server.Implementations/Udp/UdpServer.cs
index a3bbd6df0..2d806c146 100644
--- a/Emby.Server.Implementations/Udp/UdpServer.cs
+++ b/Emby.Server.Implementations/Udp/UdpServer.cs
@@ -27,9 +27,9 @@ namespace Emby.Server.Implementations.Udp
private readonly byte[] _receiveBuffer = new byte[8192];
- private Socket _udpSocket;
- private IPEndPoint _endpoint;
- private bool _disposed = false;
+ private readonly Socket _udpSocket;
+ private readonly IPEndPoint _endpoint;
+ private bool _disposed;
/// <summary>
/// Initializes a new instance of the <see cref="UdpServer" /> class.
@@ -52,7 +52,10 @@ namespace Emby.Server.Implementations.Udp
_endpoint = new IPEndPoint(bindAddress, port);
- _udpSocket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
+ _udpSocket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp)
+ {
+ MulticastLoopback = false,
+ };
_udpSocket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
}
@@ -74,6 +77,7 @@ namespace Emby.Server.Implementations.Udp
try
{
+ _logger.LogDebug("Sending AutoDiscovery response");
await _udpSocket.SendToAsync(JsonSerializer.SerializeToUtf8Bytes(response), SocketFlags.None, endpoint, cancellationToken).ConfigureAwait(false);
}
catch (SocketException ex)
@@ -99,7 +103,8 @@ namespace Emby.Server.Implementations.Udp
{
try
{
- var result = await _udpSocket.ReceiveFromAsync(_receiveBuffer, SocketFlags.None, _endpoint, cancellationToken).ConfigureAwait(false);
+ var endpoint = (EndPoint)new IPEndPoint(IPAddress.Any, 0);
+ var result = await _udpSocket.ReceiveFromAsync(_receiveBuffer, endpoint, cancellationToken).ConfigureAwait(false);
var text = Encoding.UTF8.GetString(_receiveBuffer, 0, result.ReceivedBytes);
if (text.Contains("who is JellyfinServer?", StringComparison.OrdinalIgnoreCase))
{
@@ -112,7 +117,7 @@ namespace Emby.Server.Implementations.Udp
}
catch (OperationCanceledException)
{
- // Don't throw
+ _logger.LogDebug("Broadcast socket operation cancelled");
}
}
}
@@ -125,9 +130,8 @@ namespace Emby.Server.Implementations.Udp
return;
}
- _udpSocket?.Dispose();
-
- GC.SuppressFinalize(this);
+ _udpSocket.Dispose();
+ _disposed = true;
}
}
}
diff --git a/Emby.Server.Implementations/Updates/InstallationManager.cs b/Emby.Server.Implementations/Updates/InstallationManager.cs
index 6c198b6f9..c717744b1 100644
--- a/Emby.Server.Implementations/Updates/InstallationManager.cs
+++ b/Emby.Server.Implementations/Updates/InstallationManager.cs
@@ -504,8 +504,7 @@ namespace Emby.Server.Implementations.Updates
private async Task PerformPackageInstallation(InstallationInfo package, PluginStatus status, CancellationToken cancellationToken)
{
- var extension = Path.GetExtension(package.SourceUrl);
- if (!string.Equals(extension, ".zip", StringComparison.OrdinalIgnoreCase))
+ if (!Path.GetExtension(package.SourceUrl.AsSpan()).Equals(".zip", StringComparison.OrdinalIgnoreCase))
{
_logger.LogError("Only zip packages are supported. {SourceUrl} is not a zip archive.", package.SourceUrl);
return;
@@ -521,10 +520,9 @@ namespace Emby.Server.Implementations.Updates
// CA5351: Do Not Use Broken Cryptographic Algorithms
#pragma warning disable CA5351
- using var md5 = MD5.Create();
cancellationToken.ThrowIfCancellationRequested();
- var hash = Convert.ToHexString(md5.ComputeHash(stream));
+ var hash = Convert.ToHexString(await MD5.HashDataAsync(stream, cancellationToken).ConfigureAwait(false));
if (!string.Equals(package.Checksum, hash, StringComparison.OrdinalIgnoreCase))
{
_logger.LogError(
@@ -557,7 +555,7 @@ namespace Emby.Server.Implementations.Updates
reader.ExtractToDirectory(targetDir, true);
// Ensure we create one or populate existing ones with missing data.
- await _pluginManager.PopulateManifest(package.PackageInfo, package.Version, targetDir, status);
+ await _pluginManager.PopulateManifest(package.PackageInfo, package.Version, targetDir, status).ConfigureAwait(false);
_pluginManager.ImportPluginFrom(targetDir);
}