update to current master to resolve merge conflict

This commit is contained in:
herby2212
2023-10-21 01:20:59 +02:00
226 changed files with 5626 additions and 3298 deletions

View File

@@ -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%";

View File

@@ -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>
/// <inheritdoc />
public bool HasPendingRestart { get; private set; }
/// <inheritdoc />
public bool IsShuttingDown { 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);

View File

@@ -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)

View File

@@ -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) + ")");

View File

@@ -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();

View File

@@ -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' ">

View File

@@ -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);
}
}

View File

@@ -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

View File

@@ -210,7 +210,6 @@ namespace Emby.Server.Implementations.IO
DisposeTimer();
_disposed = true;
GC.SuppressFinalize(this);
}
}
}

View File

@@ -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)
{

View File

@@ -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))

View File

@@ -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);
}
}

View File

@@ -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);
}

View File

@@ -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;
}

View File

@@ -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>

View File

@@ -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

View File

@@ -1,5 +1,4 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Emby.Naming.Common;

View File

@@ -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);

View File

@@ -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))
{

View File

@@ -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>();

View File

@@ -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

View File

@@ -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
});
}
}
}

View File

@@ -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)

View File

@@ -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;

View File

@@ -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",

View File

@@ -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"
}

View File

@@ -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": " 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"
}

View File

@@ -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 Košanas Mapi",
"TasksChannelsCategory": "Interneta Kanāli",
"TaskCleanCache": "Iztīrīt katmiņ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."
}

View File

@@ -121,5 +121,7 @@
"TaskOptimizeDatabaseDescription": "ഡാറ്റാബേസ് ചുരുക്കുകയും സ്വതന്ത്ര ഇടം വെട്ടിച്ചുരുക്കുകയും ചെയ്യുന്നു. ലൈബ്രറി സ്‌കാൻ ചെയ്‌തതിനുശേഷം അല്ലെങ്കിൽ ഡാറ്റാബേസ് പരിഷ്‌ക്കരണങ്ങളെ സൂചിപ്പിക്കുന്ന മറ്റ് മാറ്റങ്ങൾ ചെയ്‌തതിന് ശേഷം ഈ ടാസ്‌ക് പ്രവർത്തിപ്പിക്കുന്നത് പ്രകടനം മെച്ചപ്പെടുത്തും.",
"TaskOptimizeDatabase": "ഡാറ്റാബേസ് ഒപ്റ്റിമൈസ് ചെയ്യുക",
"HearingImpaired": "കേൾവി തകരാറുകൾ",
"External": "പുറമേയുള്ള"
"External": "പുറമേയുള്ള",
"TaskKeyframeExtractorDescription": "കൂടുതൽ കൃത്യമായ HLS പ്ലേലിസ്റ്റുകൾ സൃഷ്‌ടിക്കുന്നതിന് വീഡിയോ ഫയലുകളിൽ നിന്ന് കീഫ്രെയിമുകൾ എക്‌സ്‌ട്രാക്‌റ്റ് ചെയ്യുന്നു. ഈ പ്രവർത്തനം പൂർത്തിയാവാൻ കുറച്ചധികം സമയം എടുത്തേക്കാം.",
"TaskKeyframeExtractor": "കീഫ്രെയിം എക്സ്ട്രാക്റ്റർ"
}

View File

@@ -0,0 +1 @@
{}

View File

@@ -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í"
}

View File

@@ -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;
}
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);
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);
}
}
}

View File

@@ -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)

View File

@@ -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;
}

View File

@@ -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())

View File

@@ -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))

View File

@@ -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++;

View File

@@ -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();
});
}
}

View File

@@ -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;
}
}
}

View File

@@ -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);
}