From 493de3297a415061f8d6a69ff9f62261c3159a2a Mon Sep 17 00:00:00 2001 From: Patrick Barron Date: Fri, 22 Sep 2023 21:10:49 -0400 Subject: Use IHostLifetime to handle restarting and shutting down --- Emby.Server.Implementations/ApplicationHost.cs | 124 ++------------------- .../Plugins/PluginManager.cs | 22 ++-- .../Session/SessionManager.cs | 117 +++++++++---------- 3 files changed, 72 insertions(+), 191 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs index 8b13ccadab..86721ace61 100644 --- a/Emby.Server.Implementations/ApplicationHost.cs +++ b/Emby.Server.Implementations/ApplicationHost.cs @@ -12,7 +12,6 @@ using System.Linq; using System.Net; using System.Reflection; using System.Security.Cryptography.X509Certificates; -using System.Threading; using System.Threading.Tasks; using Emby.Dlna; using Emby.Dlna.Main; @@ -112,7 +111,7 @@ namespace Emby.Server.Implementations /// /// Class CompositionRoot. /// - public abstract class ApplicationHost : IServerApplicationHost, IAsyncDisposable, IDisposable + public abstract class ApplicationHost : IServerApplicationHost, IDisposable { /// /// The disposable parts. @@ -127,7 +126,6 @@ namespace Emby.Server.Implementations private readonly IPluginManager _pluginManager; private List _creatingInstances; - private ISessionManager _sessionManager; /// /// Gets or sets all concrete types. @@ -172,6 +170,8 @@ namespace Emby.Server.Implementations ConfigurationManager.Configuration, ApplicationPaths.PluginsPath, ApplicationVersion); + + _disposableParts.TryAdd((PluginManager)_pluginManager, byte.MinValue); } /// @@ -202,7 +202,10 @@ namespace Emby.Server.Implementations public bool HasPendingRestart { get; private set; } /// - public bool IsShuttingDown { get; private set; } + public bool IsShuttingDown { get; set; } + + /// + public bool ShouldRestart { get; set; } /// /// Gets the logger. @@ -406,11 +409,9 @@ namespace Emby.Server.Implementations /// /// Runs the startup tasks. /// - /// The cancellation token. /// . - public async Task RunStartupTasksAsync(CancellationToken cancellationToken) + public async Task RunStartupTasksAsync() { - cancellationToken.ThrowIfCancellationRequested(); Logger.LogInformation("Running startup tasks"); Resolve().AddTasks(GetExports(false)); @@ -424,8 +425,6 @@ namespace Emby.Server.Implementations var entryPoints = GetExports(); - cancellationToken.ThrowIfCancellationRequested(); - var stopWatch = new Stopwatch(); stopWatch.Start(); @@ -435,8 +434,6 @@ namespace Emby.Server.Implementations Logger.LogInformation("Core startup complete"); CoreStartupHasCompleted = true; - cancellationToken.ThrowIfCancellationRequested(); - stopWatch.Restart(); await Task.WhenAll(StartEntryPoints(entryPoints, false)).ConfigureAwait(false); @@ -633,8 +630,6 @@ namespace Emby.Server.Implementations var localizationManager = (LocalizationManager)Resolve(); await localizationManager.LoadAll().ConfigureAwait(false); - _sessionManager = Resolve(); - SetStaticProperties(); FindParts(); @@ -855,38 +850,6 @@ namespace Emby.Server.Implementations } } - /// - /// Restarts this instance. - /// - public void Restart() - { - if (IsShuttingDown) - { - return; - } - - IsShuttingDown = true; - _pluginManager.UnloadAssemblies(); - - Task.Run(async () => - { - try - { - await _sessionManager.SendServerRestartNotification(CancellationToken.None).ConfigureAwait(false); - } - catch (Exception ex) - { - Logger.LogError(ex, "Error sending server restart notification"); - } - - Logger.LogInformation("Calling RestartInternal"); - - RestartInternal(); - }); - } - - protected abstract void RestartInternal(); - /// /// Gets the composable part assemblies. /// @@ -1065,30 +1028,6 @@ namespace Emby.Server.Implementations }.ToString().TrimEnd('/'); } - /// - public async Task Shutdown() - { - if (IsShuttingDown) - { - return; - } - - IsShuttingDown = true; - - try - { - await _sessionManager.SendServerShutdownNotification(CancellationToken.None).ConfigureAwait(false); - } - catch (Exception ex) - { - Logger.LogError(ex, "Error sending server shutdown notification"); - } - - ShutdownInternal(); - } - - protected abstract void ShutdownInternal(); - public IEnumerable GetApiPluginAssemblies() { var assemblies = _allConcreteTypes @@ -1152,52 +1091,5 @@ namespace Emby.Server.Implementations _disposed = true; } - - public async ValueTask DisposeAsync() - { - await DisposeAsyncCore().ConfigureAwait(false); - Dispose(false); - GC.SuppressFinalize(this); - } - - /// - /// Used to perform asynchronous cleanup of managed resources or for cascading calls to . - /// - /// A ValueTask. - protected virtual async ValueTask DisposeAsyncCore() - { - var type = GetType(); - - Logger.LogInformation("Disposing {Type}", type.Name); - - foreach (var (part, _) in _disposableParts) - { - var partType = part.GetType(); - if (partType == type) - { - continue; - } - - Logger.LogInformation("Disposing {Type}", partType.Name); - - try - { - part.Dispose(); - } - catch (Exception ex) - { - Logger.LogError(ex, "Error disposing {Type}", partType.Name); - } - } - - if (_sessionManager is not null) - { - // used for closing websockets - foreach (var session in _sessionManager.Sessions) - { - await session.DisposeAsync().ConfigureAwait(false); - } - } - } } } diff --git a/Emby.Server.Implementations/Plugins/PluginManager.cs b/Emby.Server.Implementations/Plugins/PluginManager.cs index 1303012e1a..d7189ef0ca 100644 --- a/Emby.Server.Implementations/Plugins/PluginManager.cs +++ b/Emby.Server.Implementations/Plugins/PluginManager.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using System.Data; using System.Globalization; using System.IO; using System.Linq; @@ -11,7 +10,6 @@ using System.Text; using System.Text.Json; using System.Threading.Tasks; using Emby.Server.Implementations.Library; -using Jellyfin.Extensions; using Jellyfin.Extensions.Json; using Jellyfin.Extensions.Json.Converters; using MediaBrowser.Common; @@ -30,7 +28,7 @@ namespace Emby.Server.Implementations.Plugins /// /// Defines the . /// - public class PluginManager : IPluginManager + public sealed class PluginManager : IPluginManager, IDisposable { private const string MetafileName = "meta.json"; @@ -191,15 +189,6 @@ namespace Emby.Server.Implementations.Plugins } } - /// - public void UnloadAssemblies() - { - foreach (var assemblyLoadContext in _assemblyLoadContexts) - { - assemblyLoadContext.Unload(); - } - } - /// /// Creates all the plugin instances. /// @@ -441,6 +430,15 @@ namespace Emby.Server.Implementations.Plugins return SaveManifest(manifest, path); } + /// + public void Dispose() + { + foreach (var assemblyLoadContext in _assemblyLoadContexts) + { + assemblyLoadContext.Unload(); + } + } + /// /// Reconciles the manifest against any properties that exist locally in a pre-packaged meta.json found at the path. /// If no file is found, no reconciliation occurs. diff --git a/Emby.Server.Implementations/Session/SessionManager.cs b/Emby.Server.Implementations/Session/SessionManager.cs index b4a622ccf4..902d46a906 100644 --- a/Emby.Server.Implementations/Session/SessionManager.cs +++ b/Emby.Server.Implementations/Session/SessionManager.cs @@ -36,6 +36,7 @@ using MediaBrowser.Model.Querying; using MediaBrowser.Model.Session; using MediaBrowser.Model.SyncPlay; using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; using Episode = MediaBrowser.Controller.Entities.TV.Episode; @@ -44,7 +45,7 @@ namespace Emby.Server.Implementations.Session /// /// Class SessionManager. /// - public class SessionManager : ISessionManager, IDisposable + public sealed class SessionManager : ISessionManager, IAsyncDisposable { private readonly IUserDataManager _userDataManager; private readonly ILogger _logger; @@ -57,11 +58,9 @@ namespace Emby.Server.Implementations.Session private readonly IMediaSourceManager _mediaSourceManager; private readonly IServerApplicationHost _appHost; private readonly IDeviceManager _deviceManager; - - /// - /// The active connections. - /// - private readonly ConcurrentDictionary _activeConnections = new(StringComparer.OrdinalIgnoreCase); + private readonly CancellationTokenRegistration _shutdownCallback; + private readonly ConcurrentDictionary _activeConnections + = new(StringComparer.OrdinalIgnoreCase); private Timer _idleTimer; @@ -79,7 +78,8 @@ namespace Emby.Server.Implementations.Session IImageProcessor imageProcessor, IServerApplicationHost appHost, IDeviceManager deviceManager, - IMediaSourceManager mediaSourceManager) + IMediaSourceManager mediaSourceManager, + IHostApplicationLifetime hostApplicationLifetime) { _logger = logger; _eventManager = eventManager; @@ -92,6 +92,7 @@ namespace Emby.Server.Implementations.Session _appHost = appHost; _deviceManager = deviceManager; _mediaSourceManager = mediaSourceManager; + _shutdownCallback = hostApplicationLifetime.ApplicationStopping.Register(OnApplicationStopping); _deviceManager.DeviceOptionsUpdated += OnDeviceManagerDeviceOptionsUpdated; } @@ -151,36 +152,6 @@ namespace Emby.Server.Implementations.Session } } - /// - public void Dispose() - { - Dispose(true); - GC.SuppressFinalize(this); - } - - /// - /// Releases unmanaged and optionally managed resources. - /// - /// true to release both managed and unmanaged resources; false to release only unmanaged resources. - protected virtual void Dispose(bool disposing) - { - if (_disposed) - { - return; - } - - if (disposing) - { - _idleTimer?.Dispose(); - } - - _idleTimer = null; - - _deviceManager.DeviceOptionsUpdated -= OnDeviceManagerDeviceOptionsUpdated; - - _disposed = true; - } - private void CheckDisposed() { if (_disposed) @@ -1330,32 +1301,6 @@ namespace Emby.Server.Implementations.Session return SendMessageToSessions(Sessions, SessionMessageType.RestartRequired, string.Empty, cancellationToken); } - /// - /// Sends the server shutdown notification. - /// - /// The cancellation token. - /// Task. - public Task SendServerShutdownNotification(CancellationToken cancellationToken) - { - CheckDisposed(); - - return SendMessageToSessions(Sessions, SessionMessageType.ServerShuttingDown, string.Empty, cancellationToken); - } - - /// - /// Sends the server restart notification. - /// - /// The cancellation token. - /// Task. - public Task SendServerRestartNotification(CancellationToken cancellationToken) - { - CheckDisposed(); - - _logger.LogDebug("Beginning SendServerRestartNotification"); - - return SendMessageToSessions(Sessions, SessionMessageType.ServerRestarting, string.Empty, cancellationToken); - } - /// /// Adds the additional user. /// @@ -1833,5 +1778,51 @@ namespace Emby.Server.Implementations.Session return SendMessageToSessions(sessions, name, data, cancellationToken); } + + /// + public async ValueTask DisposeAsync() + { + if (_disposed) + { + return; + } + + foreach (var session in _activeConnections.Values) + { + await session.DisposeAsync().ConfigureAwait(false); + } + + if (_idleTimer is not null) + { + await _idleTimer.DisposeAsync().ConfigureAwait(false); + _idleTimer = null; + } + + await _shutdownCallback.DisposeAsync().ConfigureAwait(false); + + _deviceManager.DeviceOptionsUpdated -= OnDeviceManagerDeviceOptionsUpdated; + _disposed = true; + } + + private async void OnApplicationStopping() + { + _logger.LogInformation("Sending shutdown notifications"); + try + { + var messageType = _appHost.ShouldRestart ? SessionMessageType.ServerRestarting : SessionMessageType.ServerShuttingDown; + + await SendMessageToSessions(Sessions, messageType, string.Empty, CancellationToken.None).ConfigureAwait(false); + } + catch (Exception ex) + { + _logger.LogError(ex, "Error sending server shutdown notifications"); + } + + // Close open websockets to allow Kestrel to shut down cleanly + foreach (var session in _activeConnections.Values) + { + await session.DisposeAsync().ConfigureAwait(false); + } + } } } -- cgit v1.2.3 From 0eddc3e6adb65bea097115694469ecc1cd14c845 Mon Sep 17 00:00:00 2001 From: trailfullideal Date: Sun, 24 Sep 2023 11:18:28 -0400 Subject: Added translation using Weblate (Cherokee) --- Emby.Server.Implementations/Localization/Core/chr.json | 1 + 1 file changed, 1 insertion(+) create mode 100644 Emby.Server.Implementations/Localization/Core/chr.json (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/chr.json b/Emby.Server.Implementations/Localization/Core/chr.json new file mode 100644 index 0000000000..0967ef424b --- /dev/null +++ b/Emby.Server.Implementations/Localization/Core/chr.json @@ -0,0 +1 @@ +{} -- cgit v1.2.3 From 79976b6b48ccbc8ba828051a8751984a969f6fbc Mon Sep 17 00:00:00 2001 From: trailfullideal Date: Sun, 24 Sep 2023 15:14:19 +0000 Subject: Translated using Weblate (Zulu) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/zu/ --- Emby.Server.Implementations/Localization/Core/zu.json | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/zu.json b/Emby.Server.Implementations/Localization/Core/zu.json index b5f4b920f3..aa056d4498 100644 --- a/Emby.Server.Implementations/Localization/Core/zu.json +++ b/Emby.Server.Implementations/Localization/Core/zu.json @@ -25,5 +25,14 @@ "Channels": "Amashaneli", "Books": "Izincwadi", "Artists": "Abadlali", - "Albums": "Ama-albhamu" + "Albums": "Ama-albhamu", + "CameraImageUploadedFrom": "Kulandelayo lwesithonjana sekhamera selithunyelwe kusuka ku {0}", + "HeaderFavoriteArtists": "Abasethi Abathandekayo", + "HeaderFavoriteEpisodes": "Izilimi Ezithandekayo", + "HeaderFavoriteShows": "Izisho Ezithandekayo", + "External": "Kwezifungo", + "FailedLoginAttemptWithUserName": "Ukushayiswa kwesithombe sokungena okungekho {0}", + "HeaderContinueWatching": "Buyela Ukubona", + "HeaderFavoriteAlbums": "Izimpahla Ezithandwayo", + "HeaderAlbumArtists": "Abasethi wenkulumo" } -- cgit v1.2.3 From 3229d3ba0236243fd2116b78376400a76d9d70dd Mon Sep 17 00:00:00 2001 From: trailfullideal Date: Sun, 24 Sep 2023 14:52:43 +0000 Subject: Translated using Weblate (Assamese) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/as/ --- .../Localization/Core/as.json | 44 +++++++++++++++++++++- 1 file changed, 43 insertions(+), 1 deletion(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/as.json b/Emby.Server.Implementations/Localization/Core/as.json index 0967ef424b..7c7dd26e92 100644 --- a/Emby.Server.Implementations/Localization/Core/as.json +++ b/Emby.Server.Implementations/Localization/Core/as.json @@ -1 +1,43 @@ -{} +{ + "Albums": "এলবাম", + "Application": "আবেদন", + "AppDeviceValues": "এপ্‌: {0}, ডিভাইচ: {1}", + "Artists": "শিল্পী", + "Channels": "চেনেলস", + "Default": "ডিফল্ট", + "AuthenticationSucceededWithUserName": "{0} সফলভাবে প্রমাণিত", + "Books": "পুস্তক", + "Movies": "চলচ্চিত্ৰ", + "CameraImageUploadedFrom": "একটি নতুন ক্যামেরা চিত্র আপলোড করা হয়েছে {0}", + "Collections": "সংগ্রহ", + "HeaderFavoriteShows": "প্রিয় শোসমূহ", + "Latest": "শেহতীয়া", + "MessageApplicationUpdated": "জেলিফিন চাইভাৰ আপডেট কৰা হৈছে", + "MixedContent": "মিশ্ৰিত সমগ্ৰতা", + "NewVersionIsAvailable": "ডাউনলোড কৰিবলৈ জেলিফিন চাইভাৰৰ এটা নতুন সংস্কৰণ উপলব্ধ আছে.", + "NotificationOptionCameraImageUploaded": "কেমেৰাৰ চিত্ৰ আপল'ড কৰা হ'ল", + "External": "বাহ্যিক", + "Favorites": "পছন্দসই", + "Folders": "ফোল্ডাৰ", + "Forced": "বলপূর্বক", + "Genres": "শ্রেণী", + "HeaderAlbumArtists": "অ্যালবাম শিল্পী", + "HeaderContinueWatching": "দেখা চালিয়ে যান", + "FailedLoginAttemptWithUserName": "লগইন ব্যর্থ চেষ্টা কৰা হৈছে থেকে {0}", + "HeaderFavoriteAlbums": "প্রিয় অ্যালবামসমূহ", + "HeaderFavoriteArtists": "প্রিয় শিল্পীসমূহ", + "HeaderFavoriteEpisodes": "প্রিয় পর্বসমূহ", + "HeaderFavoriteSongs": "প্ৰিয় গীত", + "HeaderLiveTV": "প্ৰতিবেদন টিভি", + "HeaderNextUp": "পৰৱৰ্তী অংশ", + "HeaderRecordingGroups": "অলংকৰণ গোষ্ঠীসমূহ", + "HearingImpaired": "শ্ৰবণ অক্ষম", + "HomeVideos": "ঘৰৰ ভিডিঅ'সমূহ", + "Inherit": "উত্তপ্ত কৰা", + "MessageServerConfigurationUpdated": "চাইভাৰ কনফিগাৰেশ্যন আপডেট কৰা হৈছে", + "NotificationOptionApplicationUpdateAvailable": "অ্যাপ্লিকেশ্যন আপডেট উপলব্ধ", + "NotificationOptionApplicationUpdateInstalled": "অ্যাপ্লিকেশ্যন আপডেট ইনষ্টল কৰা হ'ল", + "NotificationOptionAudioPlayback": "অডিঅ' প্লেবেক আৰম্ভ হ'ল", + "NotificationOptionAudioPlaybackStopped": "অডিঅ' প্লেবেক আঁতৰ হ'ল", + "NotificationOptionInstallationFailed": "ইনষ্টলেশ্যন ব্যৰ্থতা" +} -- cgit v1.2.3 From b17ee80652237102aa87e407648efaddbf032962 Mon Sep 17 00:00:00 2001 From: trailfullideal Date: Sun, 24 Sep 2023 15:22:17 +0000 Subject: Translated using Weblate (Cherokee) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/chr/ --- .../Localization/Core/chr.json | 53 +++++++++++++++++++++- 1 file changed, 52 insertions(+), 1 deletion(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/chr.json b/Emby.Server.Implementations/Localization/Core/chr.json index 0967ef424b..85d1f4c881 100644 --- a/Emby.Server.Implementations/Localization/Core/chr.json +++ b/Emby.Server.Implementations/Localization/Core/chr.json @@ -1 +1,52 @@ -{} +{ + "ChapterNameValue": "Didanedi {0}", + "HeaderAlbumArtists": "Didanidanolisgisgi", + "HeaderFavoriteAlbums": "Dvganidi didanidisgisgi", + "HeaderLiveTV": "Anigadi didanidisgosgi", + "HeaderRecordingGroups": "Didanisquodiisgisgi", + "HomeVideos": "Diganadi dinagadisgisgi", + "Inherit": "Anigwe", + "MessageApplicationUpdatedTo": "Tsenigwidinonvhi Jellyfin Server tsadanidigwe anigadi {0}", + "MixedContent": "Ganinidi dininoladisgisgi", + "Movies": "Anidvnisgisgi", + "MusicVideos": "Danodisgisgi didanidisgosgi", + "NotificationOptionAudioPlayback": "Didanidigwe diganuyisgisgi anigadi", + "NotificationOptionInstallationFailed": "Diudvdi anadvnatisgisgi", + "NotificationOptionPluginUninstalled": "Ditsigvhnidv anawvdisgisgi", + "Albums": "Anigawidaniyv", + "Application": "Didanvyi", + "Artists": "Dinidaniyi", + "AuthenticationSucceededWithUserName": "{0} Sesoquonisdi nagadani", + "Books": "Didanedi", + "CameraImageUploadedFrom": "Anigawidaniyv nasgi didagwalanvyi {0}", + "Channels": "Diganadasgi", + "Collections": "Diganadisgi", + "Default": "Dinadi", + "DeviceOfflineWithName": "{0} Aniyvolehvi nasgi", + "External": "Amohdi", + "Favorites": "Nvdayelvdisgi", + "Folders": "Didanididisgi", + "Forced": "Ganedi", + "Genres": "Diganadisgi", + "HeaderContinueWatching": "Uwoditsu asdanidisgisgi", + "HeaderFavoriteArtists": "Dvganidi dinidanolisgisgi", + "HeaderFavoriteEpisodes": "Dvganidi didanidilisgadisgisgi", + "HeaderFavoriteShows": "Dvganidi didanididanolisgisgi)", + "HeaderFavoriteSongs": "Dvganidi danodisgisgi", + "HeaderNextUp": "Anidvli uwodoli", + "HearingImpaired": "Anitsunidi talunidisgisgi", + "ItemAddedWithName": "{0} Dinigwe anididanidisgi", + "Latest": "Uwodoli", + "MessageApplicationUpdated": "Tsenigwidinonvhi Jellyfin Server tsadanidigwe", + "MessageServerConfigurationUpdated": "Sedanidvdi anigadi diganidinonvhi", + "Music": "Danodisgisgi", + "NameSeasonUnknown": "Tsunita anidvdisgi", + "NewVersionIsAvailable": "Danodigwe anigadi Jellyfin Server tsadanidigwe adisdi uwodvdi diganidinonvhi.", + "NotificationOptionApplicationUpdateAvailable": "Disisdi tsadanidigwe udvdi", + "NotificationOptionApplicationUpdateInstalled": "Disisdi tsadanidigwe digawvdi", + "NotificationOptionAudioPlaybackStopped": "Didanidigwe diganuyisgisgi digawvdi", + "NotificationOptionCameraImageUploaded": "Asdayi adininisgisgi diganuyisgisgi", + "NotificationOptionNewLibraryContent": "Danodisgisgi anigadi digawvdi", + "NotificationOptionPluginError": "Ditsigvhnidv anadvnatisgisgi", + "NotificationOptionPluginInstalled": "Ditsigvhnidv digawvdi" +} -- cgit v1.2.3 From 57891e763972705f2db88a46e03e4e9b74d4621b Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Wed, 13 Sep 2023 23:36:46 +0200 Subject: PhotoResolver: change how generated images are detected Backdrops/fanart are generated as (backdrop)|(fanart)[0-9]*.extension Fixes #7830 --- .../Library/Resolvers/PhotoResolver.cs | 27 +++++++++++----------- MediaBrowser.Controller/Resolvers/IItemResolver.cs | 2 +- MediaBrowser.Controller/Resolvers/ItemResolver.cs | 6 ++--- 3 files changed, 16 insertions(+), 19 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Library/Resolvers/PhotoResolver.cs b/Emby.Server.Implementations/Library/Resolvers/PhotoResolver.cs index 9026160ff0..b77c6b204b 100644 --- a/Emby.Server.Implementations/Library/Resolvers/PhotoResolver.cs +++ b/Emby.Server.Implementations/Library/Resolvers/PhotoResolver.cs @@ -1,5 +1,3 @@ -#nullable disable - using System; using System.Collections.Generic; using System.IO; @@ -25,7 +23,7 @@ namespace Emby.Server.Implementations.Library.Resolvers private readonly NamingOptions _namingOptions; private readonly IDirectoryService _directoryService; - private static readonly HashSet _ignoreFiles = new HashSet(StringComparer.OrdinalIgnoreCase) + private static readonly string[] _ignoreFiles = new[] { "folder", "thumb", @@ -56,7 +54,7 @@ namespace Emby.Server.Implementations.Library.Resolvers /// /// The args. /// Trailer. - protected override Photo Resolve(ItemResolveArgs args) + protected override Photo? Resolve(ItemResolveArgs args) { if (!args.IsDirectory) { @@ -68,10 +66,11 @@ namespace Emby.Server.Implementations.Library.Resolvers { if (IsImageFile(args.Path, _imageProcessor)) { - var filename = Path.GetFileNameWithoutExtension(args.Path); + var filename = Path.GetFileNameWithoutExtension(args.Path.AsSpan()); // Make sure the image doesn't belong to a video file - var files = _directoryService.GetFiles(Path.GetDirectoryName(args.Path)); + var files = _directoryService.GetFiles(Path.GetDirectoryName(args.Path) + ?? throw new InvalidOperationException("Path can't be a root directory.")); foreach (var file in files) { @@ -92,32 +91,32 @@ namespace Emby.Server.Implementations.Library.Resolvers return null; } - internal static bool IsOwnedByMedia(NamingOptions namingOptions, string file, string imageFilename) + internal static bool IsOwnedByMedia(NamingOptions namingOptions, string file, ReadOnlySpan imageFilename) { return VideoResolver.IsVideoFile(file, namingOptions) && IsOwnedByResolvedMedia(file, imageFilename); } - internal static bool IsOwnedByResolvedMedia(string file, string imageFilename) + internal static bool IsOwnedByResolvedMedia(ReadOnlySpan file, ReadOnlySpan imageFilename) => imageFilename.StartsWith(Path.GetFileNameWithoutExtension(file), StringComparison.OrdinalIgnoreCase); internal static bool IsImageFile(string path, IImageProcessor imageProcessor) { ArgumentNullException.ThrowIfNull(path); - var filename = Path.GetFileNameWithoutExtension(path); - - if (_ignoreFiles.Contains(filename)) + var extension = Path.GetExtension(path.AsSpan()).TrimStart('.'); + if (!imageProcessor.SupportedInputFormats.Contains(extension, StringComparison.OrdinalIgnoreCase)) { return false; } - if (_ignoreFiles.Any(i => filename.IndexOf(i, StringComparison.OrdinalIgnoreCase) != -1)) + var filename = Path.GetFileNameWithoutExtension(path); + + if (_ignoreFiles.Any(i => filename.StartsWith(i, StringComparison.OrdinalIgnoreCase))) { return false; } - string extension = Path.GetExtension(path).TrimStart('.'); - return imageProcessor.SupportedInputFormats.Contains(extension, StringComparison.OrdinalIgnoreCase); + return true; } } } diff --git a/MediaBrowser.Controller/Resolvers/IItemResolver.cs b/MediaBrowser.Controller/Resolvers/IItemResolver.cs index b95d00aa3c..282aa721e8 100644 --- a/MediaBrowser.Controller/Resolvers/IItemResolver.cs +++ b/MediaBrowser.Controller/Resolvers/IItemResolver.cs @@ -24,7 +24,7 @@ namespace MediaBrowser.Controller.Resolvers /// /// The args. /// BaseItem. - BaseItem ResolvePath(ItemResolveArgs args); + BaseItem? ResolvePath(ItemResolveArgs args); } public interface IMultiItemResolver diff --git a/MediaBrowser.Controller/Resolvers/ItemResolver.cs b/MediaBrowser.Controller/Resolvers/ItemResolver.cs index a6da8384e8..5c9dd6f07c 100644 --- a/MediaBrowser.Controller/Resolvers/ItemResolver.cs +++ b/MediaBrowser.Controller/Resolvers/ItemResolver.cs @@ -1,5 +1,3 @@ -#nullable disable - using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Library; @@ -23,7 +21,7 @@ namespace MediaBrowser.Controller.Resolvers /// /// The args. /// `0. - protected internal virtual T Resolve(ItemResolveArgs args) + protected internal virtual T? Resolve(ItemResolveArgs args) { return null; } @@ -42,7 +40,7 @@ namespace MediaBrowser.Controller.Resolvers /// /// The args. /// BaseItem. - public BaseItem ResolvePath(ItemResolveArgs args) + public BaseItem? ResolvePath(ItemResolveArgs args) { var item = Resolve(args); -- cgit v1.2.3 From cc15ea7f65da7f32e52c909fbf1d6757c20e9e4e Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Wed, 13 Sep 2023 18:12:29 +0200 Subject: Ignore .zfs folder Maybe helps with #10215 ? --- Emby.Server.Implementations/Library/IgnorePatterns.cs | 4 ++++ .../Library/IgnorePatternsTests.cs | 1 + 2 files changed, 5 insertions(+) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Library/IgnorePatterns.cs b/Emby.Server.Implementations/Library/IgnorePatterns.cs index 5384c04b3b..cf6fc18456 100644 --- a/Emby.Server.Implementations/Library/IgnorePatterns.cs +++ b/Emby.Server.Implementations/Library/IgnorePatterns.cs @@ -89,6 +89,10 @@ namespace Emby.Server.Implementations.Library // bts sync files "**/*.bts", "**/*.sync", + + // zfs + "**/.zfs/**", + "**/.zfs" }; private static readonly GlobOptions _globOptions = new GlobOptions diff --git a/tests/Jellyfin.Server.Implementations.Tests/Library/IgnorePatternsTests.cs b/tests/Jellyfin.Server.Implementations.Tests/Library/IgnorePatternsTests.cs index 09eb223288..07061cfc77 100644 --- a/tests/Jellyfin.Server.Implementations.Tests/Library/IgnorePatternsTests.cs +++ b/tests/Jellyfin.Server.Implementations.Tests/Library/IgnorePatternsTests.cs @@ -31,6 +31,7 @@ namespace Jellyfin.Server.Implementations.Tests.Library [InlineData("/media/music/Foo B.A.R./epic.flac", false)] [InlineData("/media/music/Foo B.A.R", false)] [InlineData("/media/music/Foo B.A.R.", false)] + [InlineData("/movies/.zfs/snapshot/AutoM-2023-09", true)] public void PathIgnored(string path, bool expected) { Assert.Equal(expected, IgnorePatterns.ShouldIgnore(path)); -- cgit v1.2.3 From ea56b3edb7739e4c12dd2734da854f724d0fdb1b Mon Sep 17 00:00:00 2001 From: Anand CU Date: Wed, 27 Sep 2023 17:51:43 +0000 Subject: Translated using Weblate (Kannada) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/kn/ --- .../Localization/Core/kn.json | 122 ++++++++++++++++++++- 1 file changed, 121 insertions(+), 1 deletion(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/kn.json b/Emby.Server.Implementations/Localization/Core/kn.json index 3c8c38ed4f..5e2b3756bb 100644 --- a/Emby.Server.Implementations/Localization/Core/kn.json +++ b/Emby.Server.Implementations/Localization/Core/kn.json @@ -3,5 +3,125 @@ "TaskOptimizeDatabase": "ಡೇಟಾಬೇಸ್ ಅನ್ನು ಆಪ್ಟಿಮೈಜ್ ಮಾಡಿ", "TaskOptimizeDatabaseDescription": "ಡೇಟಾಬೇಸ್ ಅನ್ನು ಕಾಂಪ್ಯಾಕ್ಟ್ ಮಾಡುತ್ತದೆ ಮತ್ತು ಮುಕ್ತ ಜಾಗವನ್ನು ಮೊಟಕುಗೊಳಿಸುತ್ತದೆ. ಲೈಬ್ರರಿಯನ್ನು ಸ್ಕ್ಯಾನ್ ಮಾಡಿದ ನಂತರ ಈ ಕಾರ್ಯವನ್ನು ನಡೆಸುವುದು ಅಥವಾ ಡೇಟಾಬೇಸ್ ಮಾರ್ಪಾಡುಗಳನ್ನು ಸೂಚಿಸುವ ಇತರ ಬದಲಾವಣೆಗಳನ್ನು ಮಾಡುವುದರಿಂದ ಕಾರ್ಯಕ್ಷಮತೆಯನ್ನು ಸುಧಾರಿಸಬಹುದು.", "TaskKeyframeExtractor": "ಕೀಫ್ರೇಮ್ ಎಕ್ಸ್‌ಟ್ರಾಕ್ಟರ್", - "TaskKeyframeExtractorDescription": "ಹೆಚ್ಚು ನಿಖರವಾದ HLS ಪ್ಲೇಪಟ್ಟಿಗಳನ್ನು ರಚಿಸಲು ವೀಡಿಯೊ ಫೈಲ್‌ಗಳಿಂದ ಕೀಫ್ರೇಮ್‌ಗಳನ್ನು ಹೊರತೆಗೆಯುತ್ತದೆ. ಈ ಕಾರ್ಯವು ದೀರ್ಘಕಾಲದವರೆಗೆ ನಡೆಯಬಹುದು." + "TaskKeyframeExtractorDescription": "ಹೆಚ್ಚು ನಿಖರವಾದ HLS ಪ್ಲೇಪಟ್ಟಿಗಳನ್ನು ರಚಿಸಲು ವೀಡಿಯೊ ಫೈಲ್‌ಗಳಿಂದ ಕೀಫ್ರೇಮ್‌ಗಳನ್ನು ಹೊರತೆಗೆಯುತ್ತದೆ. ಈ ಕಾರ್ಯವು ದೀರ್ಘಕಾಲದವರೆಗೆ ನಡೆಯಬಹುದು.", + "ValueHasBeenAddedToLibrary": "{0} ಅನ್ನು ನಿಮ್ಮ ಮಾಧ್ಯಮ ಲೈಬ್ರರಿಗೆ ಸೇರಿಸಲಾಗಿದೆ", + "ValueSpecialEpisodeName": "ವಿಶೇಷ - {0}", + "TasksLibraryCategory": "ಸಮೊಹ", + "TasksApplicationCategory": "ಅಪ್ಲಿಕೇಶನ್", + "TasksChannelsCategory": "ಇಂಟರ್ನೆಟ್ ಚಾನೆಲ್ಗಳು", + "TaskCleanCache": "ಕ್ಲೀನ್ ಕ್ಯಾಶ ಡೈರೆಕ್ಟರಿ", + "TaskCleanCacheDescription": "ಸಿಸ್ಟಮ್‌ಗೆ ಇನ್ನು ಮುಂದೆ ಅಗತ್ಯವಿಲ್ಲದ ಸಂಗ್ರಹ ಫೈಲ್‌ಗಳನ್ನು ಅಳಿಸುತ್ತದೆ.", + "TaskRefreshLibrary": "ಸ್ಕ್ಯಾನ್ ಮೀಡಿಯಾ ಲೈಬ್ರರಿ", + "UserOfflineFromDevice": "{1} ನಿಂದ {0} ಸಂಪರ್ಕ ಕಡಿತಗೊಂಡಿದೆ", + "Albums": "ಸಂಪುಟ", + "Application": "ಅಪ್ಲಿಕೇಶನ್", + "AppDeviceValues": "ಅಪ್ಲಿಕೇಶನ್: {0}, ಸಾಧನ: {1}", + "Artists": "ಕಲಾವಿದರು", + "AuthenticationSucceededWithUserName": "{0} ಯಶಸ್ವಿಯಾಗಿ ದೃಢೀಕರಿಸಲಾಗಿದೆ", + "Books": "ಪುಸ್ತಕಗಳು", + "ChapterNameValue": "ಅಧ್ಯಾಯ {0}", + "Collections": "ಸಂಗ್ರಹಣೆಗಳು", + "Default": "ಪೂರ್ವನಿಯೋಜಿತ", + "DeviceOfflineWithName": "{0} ಸಂಪರ್ಕ ಕಡಿತಗೊಂಡಿದೆ", + "DeviceOnlineWithName": "{0} ಸಂಪರ್ಕಗೊಂಡಿದೆ", + "External": "ಹೊರಗಿನ", + "FailedLoginAttemptWithUserName": "{0} ರಿಂದ ವಿಫಲ ಲಾಗಿನ್ ಪ್ರಯತ್ನ", + "Favorites": "ಮೆಚ್ಚಿನವುಗಳು", + "Folders": "ಫೋಲ್ಡರ್‌ಗಳು", + "Forced": "ಬಲವಂತವಾಗಿ", + "Genres": "ಪ್ರಕಾರಗಳು", + "HeaderContinueWatching": "ನೋಡುವುದನ್ನು ಮುಂದುವರಿಸಿ", + "HeaderFavoriteAlbums": "ಮೆಚ್ಚಿನ ಸಂಪುಟಗಳು", + "HeaderFavoriteArtists": "ಮೆಚ್ಚಿನ ಕಲಾವಿದರು", + "HeaderFavoriteShows": "ಮೆಚ್ಚಿನ ಪ್ರದರ್ಶನಗಳು", + "HeaderFavoriteSongs": "ಮೆಚ್ಚಿನ ಹಾಡುಗಳು", + "HeaderLiveTV": "ನೇರ ದೂರದರ್ಶನ", + "HeaderNextUp": "ಮುಂದೆ", + "HeaderRecordingGroups": "ರೆಕಾರ್ಡಿಂಗ್ ಗುಂಪುಗಳು", + "MessageApplicationUpdated": "ಜೆಲ್ಲಿಫಿನ್ ಸರ್ವರ್ ಅನ್ನು ನವೀಕರಿಸಲಾಗಿದೆ", + "CameraImageUploadedFrom": "ಹೊಸ ಕ್ಯಾಮರಾ ಚಿತ್ರವನ್ನು {0} ನಿಂದ ಅಪ್‌ಲೋಡ್ ಮಾಡಲಾಗಿದೆ", + "Channels": "ಮೂಲಗಳು", + "HeaderAlbumArtists": "ಸಂಪುಟ ಕಲಾವಿದರು", + "HeaderFavoriteEpisodes": "ಮೆಚ್ಚಿನ ಸಂಚಿಕೆಗಳು", + "HearingImpaired": "ಮೂಗ", + "ItemAddedWithName": "{0} ಅನ್ನು ಸಂಕಲನಕ್ಕೆ ಸೇರಿಸಲಾಗಿದೆ", + "MessageApplicationUpdatedTo": "ಜೆಲ್ಲಿಫಿನ್ ಸರ್ವರ್ ಅನ್ನು {0} ಗೆ ನವೀಕರಿಸಲಾಗಿದೆ", + "MessageNamedServerConfigurationUpdatedWithValue": "ಸರ್ವರ್ ಕಾನ್ಫಿಗರೇಶನ್ ವಿಭಾಗ {0} ಅನ್ನು ನವೀಕರಿಸಲಾಗಿದೆ", + "NewVersionIsAvailable": "ಜೆಲ್ಲಿಫಿನ್ ಸರ್ವರ್‌ನ ಹೊಸ ಆವೃತ್ತಿಯು ಡೌನ್‌ಲೋಡ್‌ಗೆ ಲಭ್ಯವಿದೆ.", + "NotificationOptionAudioPlayback": "ಆಡಿಯೋ ಪ್ಲೇಬ್ಯಾಕ್ ಪ್ರಾರಂಭವಾಗಿದೆ", + "NotificationOptionCameraImageUploaded": "ಕ್ಯಾಮರಾ ಚಿತ್ರವನ್ನು ಅಪ್ಲೋಡ್ ಮಾಡಲಾಗಿದೆ", + "NotificationOptionPluginUninstalled": "ಪ್ಲಗಿನ್ ಅನ್‌ಇನ್‌ಸ್ಟಾಲ್ ಮಾಡಲಾಗಿದೆ", + "NotificationOptionUserLockedOut": "ಬಳಕೆದಾರರು ಲಾಕ್ ಔಟ್ ಆಗಿದ್ದಾರೆ", + "NotificationOptionVideoPlaybackStopped": "ವೀಡಿಯೊ ಪ್ಲೇಬ್ಯಾಕ್ ನಿಲ್ಲಿಸಲಾಗಿದೆ", + "PluginUninstalledWithName": "{0} ಅನ್ನು ಅನ್‌ಇನ್‌ಸ್ಟಾಲ್ ಮಾಡಲಾಗಿದೆ", + "ScheduledTaskFailedWithName": "{0} ವಿಫಲವಾಗಿದೆ", + "ScheduledTaskStartedWithName": "{0} ಪ್ರಾರಂಭವಾಯಿತು", + "ServerNameNeedsToBeRestarted": "{0} ಅನ್ನು ಮರುಪ್ರಾರಂಭಿಸಬೇಕಾಗಿದೆ", + "UserCreatedWithName": "ಬಳಕೆದಾರ {0} ಅನ್ನು ರಚಿಸಲಾಗಿದೆ", + "UserLockedOutWithName": "ಬಳಕೆದಾರ {0} ಅನ್ನು ಲಾಕ್ ಮಾಡಲಾಗಿದೆ", + "UserOnlineFromDevice": "{1} ನಿಂದ {0} ಆನ್‌ಲೈನ್‌ನಲ್ಲಿದೆ", + "UserPasswordChangedWithName": "{0} ಬಳಕೆದಾರರಿಗಾಗಿ ಪಾಸ್‌ವರ್ಡ್ ಅನ್ನು ಬದಲಾಯಿಸಲಾಗಿದೆ", + "UserPolicyUpdatedWithName": "ಬಳಕೆದಾರರ ನೀತಿಯನ್ನು {0} ಗೆ ನವೀಕರಿಸಲಾಗಿದೆ", + "UserStartedPlayingItemWithValues": "{2} ರಂದು {0} ಆಡುತ್ತಿದೆ {1}", + "UserStoppedPlayingItemWithValues": "{0} ಅವರು {1} ಅನ್ನು {2} ನಲ್ಲಿ ಆಡುವುದನ್ನು ಮುಗಿಸಿದ್ದಾರೆ", + "VersionNumber": "ಆವೃತ್ತಿ {0}", + "TasksMaintenanceCategory": "ನಿರ್ವಹಣೆ", + "TaskCleanActivityLog": "ಕ್ಲೀನ್ ಚಟುವಟಿಕೆ ಲಾಗ್", + "TaskCleanActivityLogDescription": "ಕಾನ್ಫಿಗರ್ ಮಾಡಿದ ವಯಸ್ಸಿಗಿಂತ ಹಳೆಯದಾದ ಚಟುವಟಿಕೆ ಲಾಗ್ ನಮೂದುಗಳನ್ನು ಅಳಿಸುತ್ತದೆ.", + "TaskRefreshChapterImages": "ಅಧ್ಯಾಯ ಚಿತ್ರಗಳನ್ನು ಹೊರತೆಗೆಯಿರಿ", + "TaskRefreshChapterImagesDescription": "ಅಧ್ಯಾಯಗಳನ್ನು ಹೊಂದಿರುವ ವೀಡಿಯೊಗಳಿಗಾಗಿ ಥಂಬ್‌ನೇಲ್‌ಗಳನ್ನು ರಚಿಸುತ್ತದೆ.", + "TaskRefreshLibraryDescription": "ಹೊಸ ಫೈಲ್‌ಗಳಿಗಾಗಿ ನಿಮ್ಮ ಮೀಡಿಯಾ ಲೈಬ್ರರಿಯನ್ನು ಸ್ಕ್ಯಾನ್ ಮಾಡುತ್ತದೆ ಮತ್ತು ಮೆಟಾಡೇಟಾವನ್ನು ರಿಫ್ರೆಶ್ ಮಾಡುತ್ತದೆ.", + "TaskCleanLogsDescription": "{0} ದಿನಗಳಿಗಿಂತ ಹಳೆಯದಾದ ಲಾಗ್ ಫೈಲ್‌ಗಳನ್ನು ಅಳಿಸುತ್ತದೆ.", + "TaskUpdatePluginsDescription": "ಸ್ವಯಂಚಾಲಿತವಾಗಿ ನವೀಕರಿಸಲು ಕಾನ್ಫಿಗರ್ ಮಾಡಲಾದ ಪ್ಲಗಿನ್‌ಗಳಿಗಾಗಿ ನವೀಕರಣಗಳನ್ನು ಡೌನ್‌ಲೋಡ್ ಮಾಡುತ್ತದೆ ಮತ್ತು ಸ್ಥಾಪಿಸುತ್ತದೆ.", + "TaskCleanTranscodeDescription": "ಒಂದು ದಿನಕ್ಕಿಂತ ಹಳೆಯದಾದ ಟ್ರಾನ್ಸ್‌ಕೋಡ್ ಫೈಲ್‌ಗಳನ್ನು ಅಳಿಸುತ್ತದೆ.", + "TaskDownloadMissingSubtitles": "ಕಾಣೆಯಾದ ಉಪಶೀರ್ಷಿಕೆಗಳನ್ನು ಡೌನ್‌ಲೋಡ್ ಮಾಡಿ", + "Shows": "ಧಾರವಾಹಿಗಳು", + "Songs": "ಹಾಡುಗಳು", + "StartupEmbyServerIsLoading": "ಜೆಲ್ಲಿಫಿನ್ ಸರ್ವರ್ ಲೋಡ್ ಆಗುತ್ತಿದೆ. ದಯವಿಟ್ಟು ಸ್ವಲ್ಪ ಸಮಯದ ನಂತರ ಮತ್ತೆ ಪ್ರಯತ್ನಿಸಿ.", + "UserDeletedWithName": "ಬಳಕೆದಾರ {0} ಅನ್ನು ಅಳಿಸಲಾಗಿದೆ", + "UserDownloadingItemWithValues": "{0} ಡೌನ್‌ಲೋಡ್ ಆಗುತ್ತಿದೆ {1}", + "SubtitleDownloadFailureFromForItem": "ಉಪಶೀರ್ಷಿಕೆಗಳು {0} ನಿಂದ {1} ಗಾಗಿ ಡೌನ್‌ಲೋಡ್ ಮಾಡಲು ವಿಫಲವಾಗಿವೆ", + "Sync": "ಹೊಂದಿಕೆ", + "System": "ವ್ಯವಸ್ಥೆ", + "TvShows": "ದೂರದರ್ಶನ ಕಾರ್ಯಕ್ರಮಗಳು", + "Undefined": "ವ್ಯಾಖ್ಯಾನಿಸಲಾಗಿಲ್ಲ", + "User": "ಬಳಕೆದಾರ", + "HomeVideos": "ಮುಖಪುಟ ವೀಡಿಯೊಗಳು", + "Inherit": "ಪಾರಂಪರ್ಯವಾಗಿ", + "ItemRemovedWithName": "{0} ಅನ್ನು ಸಂಕಲನದಿಂದ ತೆಗೆದುಹಾಕಲಾಗಿದೆ", + "LabelIpAddressValue": "IP ವಿಳಾಸ: {0}", + "LabelRunningTimeValue": "ಅವಧಿ: {0}", + "Latest": "ಹೊಸದಾದ", + "MessageServerConfigurationUpdated": "ಸರ್ವರ್ ಕಾನ್ಫಿಗರೇಶನ್ ಅನ್ನು ನವೀಕರಿಸಲಾಗಿದೆ", + "MixedContent": "ಮಿಶ್ರ ವಿಷಯ", + "Movies": "ಚಲನಚಿತ್ರಗಳು", + "Music": "ಸಂಗೀತ", + "MusicVideos": "ಸಂಗೀತ ವೀಡಿಯೊಗಳು", + "NameInstallFailed": "{0} ಸ್ಥಾಪನೆ ವಿಫಲವಾಗಿದೆ", + "NameSeasonNumber": "ಸೀಸನ್ {0}", + "NameSeasonUnknown": "ಸೀಸನ್ ತಿಳಿದಿಲ್ಲ", + "NotificationOptionApplicationUpdateAvailable": "ಅಪ್ಲಿಕೇಶನ್ ನವೀಕರಣ ಲಭ್ಯವಿದೆ", + "NotificationOptionApplicationUpdateInstalled": "ಅಪ್ಲಿಕೇಶನ್ ನವೀಕರಣವನ್ನು ಸ್ಥಾಪಿಸಲಾಗಿದೆ", + "NotificationOptionAudioPlaybackStopped": "ಆಡಿಯೋ ಪ್ಲೇಬ್ಯಾಕ್ ನಿಲ್ಲಿಸಲಾಗಿದೆ", + "NotificationOptionInstallationFailed": "ಸ್ಥಾಪನ ವೈಫಲ್ಯ", + "NotificationOptionNewLibraryContent": "ಹೊಸ ವಿಷಯವನ್ನು ಒಳಗೊಂಡಿದೆ", + "NotificationOptionPluginError": "ಪ್ಲಗಿನ್ ವೈಫಲ್ಯ", + "NotificationOptionPluginInstalled": "ಪ್ಲಗಿನ್ ವೈಫಲ್ಯ", + "NotificationOptionPluginUpdateInstalled": "ಪ್ಲಗಿನ್ ನವೀಕರಣವನ್ನು ಸ್ಥಾಪಿಸಲಾಗಿದೆ", + "NotificationOptionServerRestartRequired": "ಸರ್ವರ್ ಮರುಪ್ರಾರಂಭದ ಅಗತ್ಯವಿದೆ", + "NotificationOptionTaskFailed": "ನಿಗದಿತ ಕಾರ್ಯ ವೈಫಲ್ಯ", + "NotificationOptionVideoPlayback": "ವೀಡಿಯೊ ಪ್ಲೇಬ್ಯಾಕ್ ಪ್ರಾರಂಭವಾಗಿದೆ", + "Photos": "ಚಿತ್ರಗಳು", + "Playlists": "ಪ್ಲೇಪಟ್ಟಿಗಳು", + "Plugin": "ಪ್ಲಗಿನ್", + "PluginInstalledWithName": "{0} ಅನ್ನು ಸ್ಥಾಪಿಸಲಾಗಿದೆ", + "PluginUpdatedWithName": "{0} ಅನ್ನು ನವೀಕರಿಸಲಾಗಿದೆ", + "ProviderValue": "ಒದಗಿಸುವವರು: {0}", + "TaskCleanLogs": "ಕ್ಲೀನ್ ಲಾಗ್ ಡೈರೆಕ್ಟರಿ", + "TaskRefreshPeople": "ಜನರನ್ನು ರಿಫ್ರೆಶ್ ಮಾಡಿ", + "TaskRefreshPeopleDescription": "ನಿಮ್ಮ ಮಾಧ್ಯಮ ಲೈಬ್ರರಿಯಲ್ಲಿ ನಟರು ಮತ್ತು ನಿರ್ದೇಶಕರಿಗಾಗಿ ಮೆಟಾಡೇಟಾವನ್ನು ನವೀಕರಿಸಿ.", + "TaskUpdatePlugins": "ಪ್ಲಗಿನ್‌ಗಳನ್ನು ನವೀಕರಿಸಿ", + "TaskCleanTranscode": "ಟ್ರಾನ್ಸ್‌ಕೋಡ್ ಡೈರೆಕ್ಟರಿಯನ್ನು ಸ್ವಚ್ಛಗೊಳಿಸಿ", + "TaskRefreshChannels": "ಚಾನಲ್‌ಗಳನ್ನು ರಿಫ್ರೆಶ್ ಮಾಡಿ", + "TaskRefreshChannelsDescription": "ಇಂಟರ್ನೆಟ್ ಚಾನಲ್ ಮಾಹಿತಿಯನ್ನು ರಿಫ್ರೆಶ್ ಮಾಡುತ್ತದೆ." } -- cgit v1.2.3 From 59ec06c35c3b958a1778a56334bdf91a2f0ccf3f Mon Sep 17 00:00:00 2001 From: Patrick Barron Date: Fri, 29 Sep 2023 12:43:49 -0400 Subject: Clear active sessions on application stopping --- Emby.Server.Implementations/Session/SessionManager.cs | 2 ++ 1 file changed, 2 insertions(+) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Session/SessionManager.cs b/Emby.Server.Implementations/Session/SessionManager.cs index 902d46a906..50d3e8e46d 100644 --- a/Emby.Server.Implementations/Session/SessionManager.cs +++ b/Emby.Server.Implementations/Session/SessionManager.cs @@ -1823,6 +1823,8 @@ namespace Emby.Server.Implementations.Session { await session.DisposeAsync().ConfigureAwait(false); } + + _activeConnections.Clear(); } } } -- cgit v1.2.3 From 34c4c9f034b316030245d86e23502bca5488f88a Mon Sep 17 00:00:00 2001 From: Hagay Goshen Date: Mon, 2 Oct 2023 13:48:32 +0300 Subject: allow repeated same tv guide m3u channels , issue 6527 --- Emby.Server.Implementations/LiveTv/TunerHosts/M3uParser.cs | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/M3uParser.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/M3uParser.cs index df9101f48c..341782d9d3 100644 --- a/Emby.Server.Implementations/LiveTv/TunerHosts/M3uParser.cs +++ b/Emby.Server.Implementations/LiveTv/TunerHosts/M3uParser.cs @@ -94,14 +94,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts else if (!string.IsNullOrWhiteSpace(extInf) && !trimmedLine.StartsWith('#')) { var channel = GetChannelnfo(extInf, tunerHostId, trimmedLine); - if (string.IsNullOrWhiteSpace(channel.Id)) - { - channel.Id = channelIdPrefix + trimmedLine.GetMD5().ToString("N", CultureInfo.InvariantCulture); - } - else - { - channel.Id = channelIdPrefix + channel.Id.GetMD5().ToString("N", CultureInfo.InvariantCulture); - } + channel.Id = channelIdPrefix + trimmedLine.GetMD5().ToString("N", CultureInfo.InvariantCulture); channel.Path = trimmedLine; channels.Add(channel); -- cgit v1.2.3 From 6f464a3ac6253a5280d89f1f51d2aee93ad9a016 Mon Sep 17 00:00:00 2001 From: Oliver Bastholm Date: Sun, 1 Oct 2023 23:57:26 +0000 Subject: Translated using Weblate (Danish) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/da/ --- .../Localization/Core/da.json | 48 +++++++++++----------- 1 file changed, 24 insertions(+), 24 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/da.json b/Emby.Server.Implementations/Localization/Core/da.json index 1b6eecdcfe..837172a5b9 100644 --- a/Emby.Server.Implementations/Localization/Core/da.json +++ b/Emby.Server.Implementations/Localization/Core/da.json @@ -15,13 +15,13 @@ "Favorites": "Favoritter", "Folders": "Mapper", "Genres": "Genrer", - "HeaderAlbumArtists": "Albums kunstnere", + "HeaderAlbumArtists": "Albumkunstnere", "HeaderContinueWatching": "Fortsæt afspilning", - "HeaderFavoriteAlbums": "Favorit albummer", - "HeaderFavoriteArtists": "Favorit kunstnere", - "HeaderFavoriteEpisodes": "Favorit afsnit", - "HeaderFavoriteShows": "Favorit serier", - "HeaderFavoriteSongs": "Favorit sange", + "HeaderFavoriteAlbums": "Favoritalbummer", + "HeaderFavoriteArtists": "Favoritkunstnere", + "HeaderFavoriteEpisodes": "Yndlingsafsnit", + "HeaderFavoriteShows": "Yndlingsserier", + "HeaderFavoriteSongs": "Yndlingssange", "HeaderLiveTV": "Live-TV", "HeaderNextUp": "Næste", "HeaderRecordingGroups": "Optagelsesgrupper", @@ -34,8 +34,8 @@ "Latest": "Seneste", "MessageApplicationUpdated": "Jellyfin Server er blevet opdateret", "MessageApplicationUpdatedTo": "Jellyfin Server er blevet opdateret til {0}", - "MessageNamedServerConfigurationUpdatedWithValue": "Server konfiguration sektion {0} er blevet opdateret", - "MessageServerConfigurationUpdated": "Server konfigurationen er blevet opdateret", + "MessageNamedServerConfigurationUpdatedWithValue": "Serverkonfiguration sektion {0} er blevet opdateret", + "MessageServerConfigurationUpdated": "Serverkonfigurationen er blevet opdateret", "MixedContent": "Blandet indhold", "Movies": "Film", "Music": "Musik", @@ -51,7 +51,7 @@ "NotificationOptionCameraImageUploaded": "Kamerabillede uploadet", "NotificationOptionInstallationFailed": "Installationen mislykkedes", "NotificationOptionNewLibraryContent": "Nyt indhold tilføjet", - "NotificationOptionPluginError": "Plugin fejl", + "NotificationOptionPluginError": "Plugin-fejl", "NotificationOptionPluginInstalled": "Plugin blev installeret", "NotificationOptionPluginUninstalled": "Plugin blev afinstalleret", "NotificationOptionPluginUpdateInstalled": "Opdatering til plugin blev installeret", @@ -92,26 +92,26 @@ "ValueHasBeenAddedToLibrary": "{0} er blevet tilføjet til dit mediebibliotek", "ValueSpecialEpisodeName": "Special - {0}", "VersionNumber": "Version {0}", - "TaskDownloadMissingSubtitlesDescription": "Søger på internettet efter manglende undertekster baseret på metadata konfigurationen.", + "TaskDownloadMissingSubtitlesDescription": "Søger på internettet efter manglende undertekster baseret på metadata-konfigurationen.", "TaskDownloadMissingSubtitles": "Hent manglende undertekster", "TaskUpdatePluginsDescription": "Henter og installerer opdateringer for plugins, som er indstillet til at blive opdateret automatisk.", "TaskUpdatePlugins": "Opdater Plugins", - "TaskCleanLogsDescription": "Sletter log filer som er mere end {0} dage gamle.", - "TaskCleanLogs": "Ryd Log mappe", - "TaskRefreshLibraryDescription": "Scanner dit medie bibliotek for nye filer og opdateret metadata.", - "TaskRefreshLibrary": "Scan Medie Bibliotek", - "TaskCleanCacheDescription": "Sletter cache filer som systemet ikke længere bruger.", - "TaskCleanCache": "Ryd Cache mappe", - "TasksChannelsCategory": "Internet Kanaler", + "TaskCleanLogsDescription": "Sletter log-filer som er mere end {0} dage gamle.", + "TaskCleanLogs": "Ryd Log-mappe", + "TaskRefreshLibraryDescription": "Scanner dit mediebibliotek for nye filer og opdateret metadata.", + "TaskRefreshLibrary": "Scan Mediebibliotek", + "TaskCleanCacheDescription": "Sletter cache-filer som systemet ikke længere bruger.", + "TaskCleanCache": "Ryd Cache-mappe", + "TasksChannelsCategory": "Internetkanaler", "TasksApplicationCategory": "Applikation", "TasksLibraryCategory": "Bibliotek", "TasksMaintenanceCategory": "Vedligeholdelse", - "TaskRefreshChapterImages": "Udtræk kapitel billeder", - "TaskRefreshChapterImagesDescription": "Lav miniaturebilleder for videoer der har kapitler.", - "TaskRefreshChannelsDescription": "Opdater internet kanal information.", + "TaskRefreshChapterImages": "Udtræk kapitelbilleder", + "TaskRefreshChapterImagesDescription": "Laver miniaturebilleder for videoer, der har kapitler.", + "TaskRefreshChannelsDescription": "Opdaterer information for internetkanal.", "TaskRefreshChannels": "Opdater Kanaler", - "TaskCleanTranscodeDescription": "Fjern transcode filer som er mere end 1 dag gammel.", - "TaskCleanTranscode": "Tøm Transcode mappen", + "TaskCleanTranscodeDescription": "Fjerner transcode-filer, som er mere end 1 dag gammel.", + "TaskCleanTranscode": "Tøm Transcode-mappen", "TaskRefreshPeople": "Opdater Personer", "TaskRefreshPeopleDescription": "Opdaterer metadata for skuespillere og instruktører i dit mediebibliotek.", "TaskCleanActivityLogDescription": "Sletter linjer i aktivitetsloggen ældre end den konfigurerede alder.", @@ -121,8 +121,8 @@ "Default": "Standard", "TaskOptimizeDatabaseDescription": "Komprimerer databasen og frigør plads. Denne handling køres efter at have scannet mediebiblioteket, eller efter at have lavet ændringer til databasen, for at højne ydeevnen.", "TaskOptimizeDatabase": "Optimér database", - "TaskKeyframeExtractorDescription": "Udtrækker billeder fra videofiler for at lave mere præcise HLS playlister. Denne opgave kan tage lang tid.", - "TaskKeyframeExtractor": "Nøglebillede udtræk", + "TaskKeyframeExtractorDescription": "Udtrækker billeder fra videofiler for at lave mere præcise HLS-playlister. Denne opgave kan tage lang tid.", + "TaskKeyframeExtractor": "Udtræk af nøglebillede", "External": "Ekstern", "HearingImpaired": "Hørehæmmet" } -- cgit v1.2.3 From 5153e9259b9695a2619c1dbd34f11ce21b4068ca Mon Sep 17 00:00:00 2001 From: officialdanielamani Date: Sun, 1 Oct 2023 23:19:21 +0000 Subject: Translated using Weblate (Malay) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/ms/ --- Emby.Server.Implementations/Localization/Core/ms.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/ms.json b/Emby.Server.Implementations/Localization/Core/ms.json index 904443bea1..a07222975b 100644 --- a/Emby.Server.Implementations/Localization/Core/ms.json +++ b/Emby.Server.Implementations/Localization/Core/ms.json @@ -1,5 +1,5 @@ { - "Albums": "Albums", + "Albums": "Album", "AppDeviceValues": "Apl: {0}, Peranti: {1}", "Application": "Aplikasi", "Artists": "Artis-artis", -- cgit v1.2.3 From f746db9a54caef36bb3b32cf22812a78d97c865d Mon Sep 17 00:00:00 2001 From: Patrick Barron Date: Mon, 2 Oct 2023 15:55:26 -0400 Subject: Re-add shutdown/restart methods --- Emby.Server.Implementations/ApplicationHost.cs | 19 +++++++++++++++++++ Jellyfin.Api/Controllers/SystemController.cs | 23 +++-------------------- MediaBrowser.Common/IApplicationHost.cs | 18 ++++++++++++++---- 3 files changed, 36 insertions(+), 24 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs index 86721ace61..e2d2719e5d 100644 --- a/Emby.Server.Implementations/ApplicationHost.cs +++ b/Emby.Server.Implementations/ApplicationHost.cs @@ -101,6 +101,7 @@ 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; @@ -850,6 +851,24 @@ namespace Emby.Server.Implementations } } + /// + public void Restart() + { + ShouldRestart = true; + Shutdown(); + } + + /// + public void Shutdown() + { + Task.Run(async () => + { + await Task.Delay(100).ConfigureAwait(false); + IsShuttingDown = true; + Resolve().StopApplication(); + }); + } + /// /// Gets the composable part assemblies. /// diff --git a/Jellyfin.Api/Controllers/SystemController.cs b/Jellyfin.Api/Controllers/SystemController.cs index 4cc0f0ced4..42ac4a9b4b 100644 --- a/Jellyfin.Api/Controllers/SystemController.cs +++ b/Jellyfin.Api/Controllers/SystemController.cs @@ -4,7 +4,6 @@ using System.ComponentModel.DataAnnotations; using System.IO; using System.Linq; using System.Net.Mime; -using System.Threading.Tasks; using Jellyfin.Api.Attributes; using Jellyfin.Api.Constants; using MediaBrowser.Common.Configuration; @@ -18,7 +17,6 @@ using MediaBrowser.Model.System; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; -using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; namespace Jellyfin.Api.Controllers; @@ -33,7 +31,6 @@ public class SystemController : BaseJellyfinApiController private readonly IFileSystem _fileSystem; private readonly INetworkManager _network; private readonly ILogger _logger; - private readonly IHostApplicationLifetime _hostApplicationLifetime; /// /// Initializes a new instance of the class. @@ -43,21 +40,18 @@ public class SystemController : BaseJellyfinApiController /// Instance of interface. /// Instance of interface. /// Instance of interface. - /// Instance of interface. public SystemController( IServerConfigurationManager serverConfigurationManager, IServerApplicationHost appHost, IFileSystem fileSystem, INetworkManager network, - ILogger logger, - IHostApplicationLifetime hostApplicationLifetime) + ILogger logger) { _appPaths = serverConfigurationManager.ApplicationPaths; _appHost = appHost; _fileSystem = fileSystem; _network = network; _logger = logger; - _hostApplicationLifetime = hostApplicationLifetime; } /// @@ -112,13 +106,7 @@ public class SystemController : BaseJellyfinApiController [ProducesResponseType(StatusCodes.Status403Forbidden)] public ActionResult RestartApplication() { - Task.Run(async () => - { - await Task.Delay(100).ConfigureAwait(false); - _appHost.ShouldRestart = true; - _appHost.IsShuttingDown = true; - _hostApplicationLifetime.StopApplication(); - }); + _appHost.Restart(); return NoContent(); } @@ -134,12 +122,7 @@ public class SystemController : BaseJellyfinApiController [ProducesResponseType(StatusCodes.Status403Forbidden)] public ActionResult ShutdownApplication() { - Task.Run(async () => - { - await Task.Delay(100).ConfigureAwait(false); - _appHost.IsShuttingDown = true; - _hostApplicationLifetime.StopApplication(); - }); + _appHost.Shutdown(); return NoContent(); } diff --git a/MediaBrowser.Common/IApplicationHost.cs b/MediaBrowser.Common/IApplicationHost.cs index c1ea6e87cc..5985d3dd82 100644 --- a/MediaBrowser.Common/IApplicationHost.cs +++ b/MediaBrowser.Common/IApplicationHost.cs @@ -41,15 +41,15 @@ namespace MediaBrowser.Common bool HasPendingRestart { get; } /// - /// Gets or sets a value indicating whether this instance is currently shutting down. + /// Gets a value indicating whether this instance is currently shutting down. /// /// true if this instance is shutting down; otherwise, false. - bool IsShuttingDown { get; set; } + bool IsShuttingDown { get; } /// - /// Gets or sets a value indicating whether the application should restart. + /// Gets a value indicating whether the application should restart. /// - bool ShouldRestart { get; set; } + bool ShouldRestart { get; } /// /// Gets the application version. @@ -91,6 +91,11 @@ namespace MediaBrowser.Common /// void NotifyPendingRestart(); + /// + /// Restarts this instance. + /// + void Restart(); + /// /// Gets the exports. /// @@ -122,6 +127,11 @@ namespace MediaBrowser.Common /// ``0. T Resolve(); + /// + /// Shuts down. + /// + void Shutdown(); + /// /// Initializes this instance. /// -- cgit v1.2.3 From ab0790271aac8846430c9689dff647eb4ce55078 Mon Sep 17 00:00:00 2001 From: Patrick Barron Date: Mon, 2 Oct 2023 16:54:19 -0400 Subject: Apply suggestions from code review Co-authored-by: Claus Vium --- Emby.Server.Implementations/ApplicationHost.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs index e2d2719e5d..a8b8e5fb94 100644 --- a/Emby.Server.Implementations/ApplicationHost.cs +++ b/Emby.Server.Implementations/ApplicationHost.cs @@ -203,10 +203,10 @@ namespace Emby.Server.Implementations public bool HasPendingRestart { get; private set; } /// - public bool IsShuttingDown { get; set; } + public bool IsShuttingDown { get; private set; } /// - public bool ShouldRestart { get; set; } + public bool ShouldRestart { get; private set; } /// /// Gets the logger. -- cgit v1.2.3 From 78e00578c20d0127b540bc69290972104f12ac84 Mon Sep 17 00:00:00 2001 From: Patrick Barron Date: Tue, 3 Oct 2023 10:25:14 -0400 Subject: Use DI for IFileSystem --- .../AppBase/BaseConfigurationManager.cs | 20 +++++++------------- Emby.Server.Implementations/ApplicationHost.cs | 11 +++++------ .../Configuration/ServerConfigurationManager.cs | 11 ++++++----- Emby.Server.Implementations/IO/ManagedFileSystem.cs | 19 ++++++++----------- MediaBrowser.Model/IO/IFileSystem.cs | 2 -- 5 files changed, 26 insertions(+), 37 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/AppBase/BaseConfigurationManager.cs b/Emby.Server.Implementations/AppBase/BaseConfigurationManager.cs index a4deeddb78..a2f38c8c2d 100644 --- a/Emby.Server.Implementations/AppBase/BaseConfigurationManager.cs +++ b/Emby.Server.Implementations/AppBase/BaseConfigurationManager.cs @@ -8,7 +8,6 @@ using MediaBrowser.Common.Configuration; using MediaBrowser.Common.Events; using MediaBrowser.Common.Extensions; using MediaBrowser.Model.Configuration; -using MediaBrowser.Model.IO; using MediaBrowser.Model.Serialization; using Microsoft.Extensions.Logging; @@ -19,14 +18,8 @@ namespace Emby.Server.Implementations.AppBase /// public abstract class BaseConfigurationManager : IConfigurationManager { - private readonly IFileSystem _fileSystem; - - private readonly ConcurrentDictionary _configurations = new ConcurrentDictionary(); - - /// - /// The _configuration sync lock. - /// - private readonly object _configurationSyncLock = new object(); + private readonly ConcurrentDictionary _configurations = new(); + private readonly object _configurationSyncLock = new(); private ConfigurationStore[] _configurationStores = Array.Empty(); private IConfigurationFactory[] _configurationFactories = Array.Empty(); @@ -42,12 +35,13 @@ namespace Emby.Server.Implementations.AppBase /// The application paths. /// The logger factory. /// The XML serializer. - /// The file system. - protected BaseConfigurationManager(IApplicationPaths applicationPaths, ILoggerFactory loggerFactory, IXmlSerializer xmlSerializer, IFileSystem fileSystem) + protected BaseConfigurationManager( + IApplicationPaths applicationPaths, + ILoggerFactory loggerFactory, + IXmlSerializer xmlSerializer) { CommonApplicationPaths = applicationPaths; XmlSerializer = xmlSerializer; - _fileSystem = fileSystem; Logger = loggerFactory.CreateLogger(); UpdateCachePath(); @@ -272,7 +266,7 @@ namespace Emby.Server.Implementations.AppBase { var file = Path.Combine(path, Guid.NewGuid().ToString()); File.WriteAllText(file, string.Empty); - _fileSystem.DeleteFile(file); + File.Delete(file); } private string GetConfigurationFile(string key) diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs index 985ab0db5e..8518b13521 100644 --- a/Emby.Server.Implementations/ApplicationHost.cs +++ b/Emby.Server.Implementations/ApplicationHost.cs @@ -120,7 +120,6 @@ namespace Emby.Server.Implementations private readonly ConcurrentDictionary _disposableParts = new(); private readonly DeviceId _deviceId; - private readonly IFileSystem _fileSystemManager; private readonly IConfiguration _startupConfig; private readonly IXmlSerializer _xmlSerializer; private readonly IStartupOptions _startupOptions; @@ -153,10 +152,8 @@ namespace Emby.Server.Implementations LoggerFactory = loggerFactory; _startupOptions = options; _startupConfig = startupConfig; - _fileSystemManager = new ManagedFileSystem(LoggerFactory.CreateLogger(), applicationPaths); Logger = LoggerFactory.CreateLogger(); - _fileSystemManager.AddShortcutHandler(new MbLinkShortcutHandler()); _deviceId = new DeviceId(ApplicationPaths, LoggerFactory); ApplicationVersion = typeof(ApplicationHost).Assembly.GetName().Version; @@ -164,7 +161,7 @@ namespace Emby.Server.Implementations ApplicationUserAgent = Name.Replace(' ', '-') + "/" + ApplicationVersionString; _xmlSerializer = new MyXmlSerializer(); - ConfigurationManager = new ServerConfigurationManager(ApplicationPaths, LoggerFactory, _xmlSerializer, _fileSystemManager); + ConfigurationManager = new ServerConfigurationManager(ApplicationPaths, LoggerFactory, _xmlSerializer); _pluginManager = new PluginManager( LoggerFactory.CreateLogger(), this, @@ -507,7 +504,9 @@ namespace Emby.Server.Implementations serviceCollection.AddSingleton(_pluginManager); serviceCollection.AddSingleton(ApplicationPaths); - serviceCollection.AddSingleton(_fileSystemManager); + serviceCollection.AddSingleton(); + serviceCollection.AddSingleton(); + serviceCollection.AddSingleton(); serviceCollection.AddSingleton(NetManager); @@ -681,7 +680,7 @@ namespace Emby.Server.Implementations BaseItem.ProviderManager = Resolve(); BaseItem.LocalizationManager = Resolve(); BaseItem.ItemRepository = Resolve(); - BaseItem.FileSystem = _fileSystemManager; + BaseItem.FileSystem = Resolve(); BaseItem.UserDataManager = Resolve(); BaseItem.ChannelManager = Resolve(); Video.LiveTvManager = Resolve(); diff --git a/Emby.Server.Implementations/Configuration/ServerConfigurationManager.cs b/Emby.Server.Implementations/Configuration/ServerConfigurationManager.cs index 6b8b1a620f..0ee43ce0a3 100644 --- a/Emby.Server.Implementations/Configuration/ServerConfigurationManager.cs +++ b/Emby.Server.Implementations/Configuration/ServerConfigurationManager.cs @@ -7,7 +7,6 @@ using MediaBrowser.Common.Configuration; using MediaBrowser.Controller; using MediaBrowser.Controller.Configuration; using MediaBrowser.Model.Configuration; -using MediaBrowser.Model.IO; using MediaBrowser.Model.Serialization; using Microsoft.Extensions.Logging; @@ -22,11 +21,13 @@ namespace Emby.Server.Implementations.Configuration /// Initializes a new instance of the class. /// /// The application paths. - /// The paramref name="loggerFactory" factory. + /// The logger factory. /// The XML serializer. - /// The file system. - public ServerConfigurationManager(IApplicationPaths applicationPaths, ILoggerFactory loggerFactory, IXmlSerializer xmlSerializer, IFileSystem fileSystem) - : base(applicationPaths, loggerFactory, xmlSerializer, fileSystem) + public ServerConfigurationManager( + IApplicationPaths applicationPaths, + ILoggerFactory loggerFactory, + IXmlSerializer xmlSerializer) + : base(applicationPaths, loggerFactory, xmlSerializer) { UpdateMetadataPath(); } diff --git a/Emby.Server.Implementations/IO/ManagedFileSystem.cs b/Emby.Server.Implementations/IO/ManagedFileSystem.cs index 3aa5233ed1..18b00ce0b2 100644 --- a/Emby.Server.Implementations/IO/ManagedFileSystem.cs +++ b/Emby.Server.Implementations/IO/ManagedFileSystem.cs @@ -15,10 +15,6 @@ namespace Emby.Server.Implementations.IO /// public class ManagedFileSystem : IFileSystem { - private readonly ILogger _logger; - - private readonly List _shortcutHandlers = new List(); - private readonly string _tempPath; private static readonly bool _isEnvironmentCaseInsensitive = OperatingSystem.IsWindows(); private static readonly char[] _invalidPathCharacters = { @@ -29,23 +25,24 @@ namespace Emby.Server.Implementations.IO (char)31, ':', '*', '?', '\\', '/' }; + private readonly ILogger _logger; + private readonly List _shortcutHandlers; + private readonly string _tempPath; + /// /// Initializes a new instance of the class. /// /// The instance to use. /// The instance to use. + /// the 's to use. public ManagedFileSystem( ILogger logger, - IApplicationPaths applicationPaths) + IApplicationPaths applicationPaths, + IEnumerable shortcutHandlers) { _logger = logger; _tempPath = applicationPaths.TempDirectory; - } - - /// - public virtual void AddShortcutHandler(IShortcutHandler handler) - { - _shortcutHandlers.Add(handler); + _shortcutHandlers = shortcutHandlers.ToList(); } /// diff --git a/MediaBrowser.Model/IO/IFileSystem.cs b/MediaBrowser.Model/IO/IFileSystem.cs index 5bdda2b240..ec381d4231 100644 --- a/MediaBrowser.Model/IO/IFileSystem.cs +++ b/MediaBrowser.Model/IO/IFileSystem.cs @@ -10,8 +10,6 @@ namespace MediaBrowser.Model.IO /// public interface IFileSystem { - void AddShortcutHandler(IShortcutHandler handler); - /// /// Determines whether the specified filename is shortcut. /// -- cgit v1.2.3 From 12b51cf2ba537a6f56882277dc856ab39ace94a0 Mon Sep 17 00:00:00 2001 From: Patrick Barron Date: Tue, 3 Oct 2023 10:31:55 -0400 Subject: Reduce nesting in SessionManager.OnPlaybackStopped --- .../Session/SessionManager.cs | 36 +++++++++++----------- 1 file changed, 18 insertions(+), 18 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Session/SessionManager.cs b/Emby.Server.Implementations/Session/SessionManager.cs index 50d3e8e46d..e935f7e5e5 100644 --- a/Emby.Server.Implementations/Session/SessionManager.cs +++ b/Emby.Server.Implementations/Session/SessionManager.cs @@ -951,27 +951,27 @@ namespace Emby.Server.Implementations.Session private bool OnPlaybackStopped(User user, BaseItem item, long? positionTicks, bool playbackFailed) { - bool playedToCompletion = false; - - if (!playbackFailed) + if (playbackFailed) { - var data = _userDataManager.GetUserData(user, item); - - if (positionTicks.HasValue) - { - playedToCompletion = _userDataManager.UpdatePlayState(item, data, positionTicks.Value); - } - else - { - // If the client isn't able to report this, then we'll just have to make an assumption - data.PlayCount++; - data.Played = item.SupportsPlayedStatus; - data.PlaybackPositionTicks = 0; - playedToCompletion = true; - } + return false; + } - _userDataManager.SaveUserData(user, item, data, UserDataSaveReason.PlaybackFinished, CancellationToken.None); + var data = _userDataManager.GetUserData(user, item); + bool playedToCompletion; + if (positionTicks.HasValue) + { + playedToCompletion = _userDataManager.UpdatePlayState(item, data, positionTicks.Value); } + else + { + // If the client isn't able to report this, then we'll just have to make an assumption + data.PlayCount++; + data.Played = item.SupportsPlayedStatus; + data.PlaybackPositionTicks = 0; + playedToCompletion = true; + } + + _userDataManager.SaveUserData(user, item, data, UserDataSaveReason.PlaybackFinished, CancellationToken.None); return playedToCompletion; } -- cgit v1.2.3 From 35e09cf203550ca101b85be92e7aa4014902ffd3 Mon Sep 17 00:00:00 2001 From: Piranha Date: Tue, 3 Oct 2023 14:30:03 +0000 Subject: Translated using Weblate (Spanish) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/es/ --- Emby.Server.Implementations/Localization/Core/es.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/es.json b/Emby.Server.Implementations/Localization/Core/es.json index f5636a0af0..4c56f789d3 100644 --- a/Emby.Server.Implementations/Localization/Core/es.json +++ b/Emby.Server.Implementations/Localization/Core/es.json @@ -3,9 +3,9 @@ "AppDeviceValues": "Aplicación: {0}, Dispositivo: {1}", "Application": "Aplicación", "Artists": "Artistas", - "AuthenticationSucceededWithUserName": "{0} identificado correctamente", + "AuthenticationSucceededWithUserName": "{0} autenticado correctamente", "Books": "Libros", - "CameraImageUploadedFrom": "Se ha subido una nueva imagen de cámara desde {0}", + "CameraImageUploadedFrom": "Se ha subido una nueva imagen por cámara desde {0}", "Channels": "Canales", "ChapterNameValue": "Capítulo {0}", "Collections": "Colecciones", -- cgit v1.2.3 From c9ce246c173339c00b4131f3e8b2c9240584d6ae Mon Sep 17 00:00:00 2001 From: Artnal Date: Tue, 3 Oct 2023 11:29:38 +0000 Subject: Translated using Weblate (French) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/fr/ --- Emby.Server.Implementations/Localization/Core/fr.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/fr.json b/Emby.Server.Implementations/Localization/Core/fr.json index 4877bcd7a7..a2b429dcdd 100644 --- a/Emby.Server.Implementations/Localization/Core/fr.json +++ b/Emby.Server.Implementations/Localization/Core/fr.json @@ -105,8 +105,8 @@ "TaskRefreshPeople": "Actualiser les acteurs", "TaskCleanLogsDescription": "Supprime les journaux de plus de {0} jours.", "TaskCleanLogs": "Nettoyer le répertoire des journaux", - "TaskRefreshLibraryDescription": "Scanne votre médiathèque pour trouver les nouveaux fichiers et actualise les métadonnées.", - "TaskRefreshLibrary": "Scanner la médiathèque", + "TaskRefreshLibraryDescription": "Analyser sa médiathèque pour trouver les nouveaux fichiers et actualiser les métadonnées.", + "TaskRefreshLibrary": "Analyser la médiathèque", "TaskRefreshChapterImagesDescription": "Crée des vignettes pour les vidéos ayant des chapitres.", "TaskRefreshChapterImages": "Extraire les images de chapitre", "TaskCleanCacheDescription": "Supprime les fichiers de cache dont le système n'a plus besoin.", -- cgit v1.2.3 From 6f7413812fb4654d1b1ca065a177a1bb19408386 Mon Sep 17 00:00:00 2001 From: Patrick Barron Date: Wed, 4 Oct 2023 14:34:53 -0400 Subject: Add SystemManager service --- Emby.Server.Implementations/ApplicationHost.cs | 80 +--------------- Emby.Server.Implementations/SystemManager.cs | 108 ++++++++++++++++++++++ Jellyfin.Api/Controllers/SystemController.cs | 47 +++++----- MediaBrowser.Common/IApplicationHost.cs | 24 +---- MediaBrowser.Controller/IServerApplicationHost.cs | 12 --- MediaBrowser.Controller/ISystemManager.cs | 34 +++++++ 6 files changed, 173 insertions(+), 132 deletions(-) create mode 100644 Emby.Server.Implementations/SystemManager.cs create mode 100644 MediaBrowser.Controller/ISystemManager.cs (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs index 8518b13521..3253ea0267 100644 --- a/Emby.Server.Implementations/ApplicationHost.cs +++ b/Emby.Server.Implementations/ApplicationHost.cs @@ -101,7 +101,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 +132,7 @@ namespace Emby.Server.Implementations /// All concrete types. private Type[] _allConcreteTypes; - private bool _disposed = false; + private bool _disposed; /// /// Initializes a new instance of the class. @@ -184,26 +183,16 @@ namespace Emby.Server.Implementations public bool CoreStartupHasCompleted { get; private set; } - public virtual bool CanLaunchWebBrowser => Environment.UserInteractive - && !_startupOptions.IsService - && (OperatingSystem.IsWindows() || OperatingSystem.IsMacOS()); - /// /// Gets the singleton instance. /// public INetworkManager NetManager { get; private set; } - /// - /// Gets a value indicating whether this instance has changes that require the entire application to restart. - /// - /// true if this instance has pending application restart; otherwise, false. - public bool HasPendingRestart { get; private set; } - /// - public bool IsShuttingDown { get; private set; } + public bool HasPendingRestart { get; private set; } /// - public bool ShouldRestart { get; private set; } + public bool ShouldRestart { get; set; } /// /// Gets the logger. @@ -507,6 +496,8 @@ namespace Emby.Server.Implementations serviceCollection.AddSingleton(); serviceCollection.AddSingleton(); + serviceCollection.AddScoped(); + serviceCollection.AddSingleton(); serviceCollection.AddSingleton(NetManager); @@ -850,24 +841,6 @@ namespace Emby.Server.Implementations } } - /// - public void Restart() - { - ShouldRestart = true; - Shutdown(); - } - - /// - public void Shutdown() - { - Task.Run(async () => - { - await Task.Delay(100).ConfigureAwait(false); - IsShuttingDown = true; - Resolve().StopApplication(); - }); - } - /// /// Gets the composable part assemblies. /// @@ -923,49 +896,6 @@ namespace Emby.Server.Implementations protected abstract IEnumerable GetAssembliesWithPartsInternal(); - /// - /// Gets the system status. - /// - /// Where this request originated. - /// SystemInfo. - public SystemInfo GetSystemInfo(HttpRequest request) - { - return new SystemInfo - { - HasPendingRestart = HasPendingRestart, - IsShuttingDown = IsShuttingDown, - Version = ApplicationVersionString, - WebSocketPortNumber = HttpPort, - CompletedInstallations = Resolve().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 - }; - } - /// public string GetSmartApiUrl(IPAddress remoteAddr) { diff --git a/Emby.Server.Implementations/SystemManager.cs b/Emby.Server.Implementations/SystemManager.cs new file mode 100644 index 0000000000..5e9c424e9a --- /dev/null +++ b/Emby.Server.Implementations/SystemManager.cs @@ -0,0 +1,108 @@ +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; + +/// +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; + + /// + /// Initializes a new instance of the class. + /// + /// Instance of . + /// Instance of . + /// Instance of . + /// Instance of . + /// Instance of . + /// Instance of . + 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; + } + + private bool CanLaunchWebBrowser => Environment.UserInteractive + && !_startupOptions.IsService + && (OperatingSystem.IsWindows() || OperatingSystem.IsMacOS()); + + /// + 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, + CanLaunchWebBrowser = CanLaunchWebBrowser, + TranscodingTempPath = _configurationManager.GetTranscodePath(), + ServerName = _applicationHost.FriendlyName, + LocalAddress = _applicationHost.GetSmartApiUrl(request), + SupportsLibraryMonitor = true, + PackageName = _startupOptions.PackageName + }; + } + + /// + 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 + }; + } + + /// + public void Restart() => ShutdownInternal(true); + + /// + 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/Jellyfin.Api/Controllers/SystemController.cs b/Jellyfin.Api/Controllers/SystemController.cs index 42ac4a9b4b..11095a97f0 100644 --- a/Jellyfin.Api/Controllers/SystemController.cs +++ b/Jellyfin.Api/Controllers/SystemController.cs @@ -10,7 +10,6 @@ using MediaBrowser.Common.Configuration; using MediaBrowser.Common.Extensions; using MediaBrowser.Common.Net; using MediaBrowser.Controller; -using MediaBrowser.Controller.Configuration; using MediaBrowser.Model.IO; using MediaBrowser.Model.Net; using MediaBrowser.Model.System; @@ -26,32 +25,36 @@ namespace Jellyfin.Api.Controllers; /// public class SystemController : BaseJellyfinApiController { + private readonly ILogger _logger; private readonly IServerApplicationHost _appHost; private readonly IApplicationPaths _appPaths; private readonly IFileSystem _fileSystem; - private readonly INetworkManager _network; - private readonly ILogger _logger; + private readonly INetworkManager _networkManager; + private readonly ISystemManager _systemManager; /// /// Initializes a new instance of the class. /// - /// Instance of interface. + /// Instance of interface. + /// Instance of interface. /// Instance of interface. /// Instance of interface. - /// Instance of interface. - /// Instance of interface. + /// Instance of interface. + /// Instance of interface. public SystemController( - IServerConfigurationManager serverConfigurationManager, + ILogger logger, IServerApplicationHost appHost, + IServerApplicationPaths appPaths, IFileSystem fileSystem, - INetworkManager network, - ILogger logger) + INetworkManager networkManager, + ISystemManager systemManager) { - _appPaths = serverConfigurationManager.ApplicationPaths; + _logger = logger; _appHost = appHost; + _appPaths = appPaths; _fileSystem = fileSystem; - _network = network; - _logger = logger; + _networkManager = networkManager; + _systemManager = systemManager; } /// @@ -65,9 +68,7 @@ public class SystemController : BaseJellyfinApiController [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status403Forbidden)] public ActionResult GetSystemInfo() - { - return _appHost.GetSystemInfo(Request); - } + => _systemManager.GetSystemInfo(Request); /// /// Gets public information about the server. @@ -77,9 +78,7 @@ public class SystemController : BaseJellyfinApiController [HttpGet("Info/Public")] [ProducesResponseType(StatusCodes.Status200OK)] public ActionResult GetPublicSystemInfo() - { - return _appHost.GetPublicSystemInfo(Request); - } + => _systemManager.GetPublicSystemInfo(Request); /// /// Pings the system. @@ -90,9 +89,7 @@ public class SystemController : BaseJellyfinApiController [HttpPost("Ping", Name = "PostPingSystem")] [ProducesResponseType(StatusCodes.Status200OK)] public ActionResult PingSystem() - { - return _appHost.Name; - } + => _appHost.Name; /// /// Restarts the application. @@ -106,7 +103,7 @@ public class SystemController : BaseJellyfinApiController [ProducesResponseType(StatusCodes.Status403Forbidden)] public ActionResult RestartApplication() { - _appHost.Restart(); + _systemManager.Restart(); return NoContent(); } @@ -122,7 +119,7 @@ public class SystemController : BaseJellyfinApiController [ProducesResponseType(StatusCodes.Status403Forbidden)] public ActionResult ShutdownApplication() { - _appHost.Shutdown(); + _systemManager.Shutdown(); return NoContent(); } @@ -180,7 +177,7 @@ public class SystemController : BaseJellyfinApiController return new EndPointInfo { IsLocal = HttpContext.IsLocal(), - IsInNetwork = _network.IsInLocalNetwork(HttpContext.GetNormalizedRemoteIP()) + IsInNetwork = _networkManager.IsInLocalNetwork(HttpContext.GetNormalizedRemoteIP()) }; } @@ -218,7 +215,7 @@ public class SystemController : BaseJellyfinApiController [ProducesResponseType(StatusCodes.Status200OK)] public ActionResult> GetWakeOnLanInfo() { - var result = _network.GetMacAddresses() + var result = _networkManager.GetMacAddresses() .Select(i => new WakeOnLanInfo(i)); return Ok(result); } diff --git a/MediaBrowser.Common/IApplicationHost.cs b/MediaBrowser.Common/IApplicationHost.cs index 5985d3dd82..23795c6be8 100644 --- a/MediaBrowser.Common/IApplicationHost.cs +++ b/MediaBrowser.Common/IApplicationHost.cs @@ -35,21 +35,15 @@ namespace MediaBrowser.Common string SystemId { get; } /// - /// Gets a value indicating whether this instance has pending kernel reload. + /// Gets a value indicating whether this instance has pending changes requiring a restart. /// - /// true if this instance has pending kernel reload; otherwise, false. + /// true if this instance has a pending restart; otherwise, false. bool HasPendingRestart { get; } /// - /// Gets a value indicating whether this instance is currently shutting down. + /// Gets or sets a value indicating whether the application should restart. /// - /// true if this instance is shutting down; otherwise, false. - bool IsShuttingDown { get; } - - /// - /// Gets a value indicating whether the application should restart. - /// - bool ShouldRestart { get; } + bool ShouldRestart { get; set; } /// /// Gets the application version. @@ -91,11 +85,6 @@ namespace MediaBrowser.Common /// void NotifyPendingRestart(); - /// - /// Restarts this instance. - /// - void Restart(); - /// /// Gets the exports. /// @@ -127,11 +116,6 @@ namespace MediaBrowser.Common /// ``0. T Resolve(); - /// - /// Shuts down. - /// - void Shutdown(); - /// /// Initializes this instance. /// diff --git a/MediaBrowser.Controller/IServerApplicationHost.cs b/MediaBrowser.Controller/IServerApplicationHost.cs index 45ac5c3a85..e9c4d9e19a 100644 --- a/MediaBrowser.Controller/IServerApplicationHost.cs +++ b/MediaBrowser.Controller/IServerApplicationHost.cs @@ -4,7 +4,6 @@ using System.Net; using MediaBrowser.Common; -using MediaBrowser.Model.System; using Microsoft.AspNetCore.Http; namespace MediaBrowser.Controller @@ -16,8 +15,6 @@ namespace MediaBrowser.Controller { bool CoreStartupHasCompleted { get; } - bool CanLaunchWebBrowser { get; } - /// /// Gets the HTTP server port. /// @@ -41,15 +38,6 @@ namespace MediaBrowser.Controller /// The name of the friendly. string FriendlyName { get; } - /// - /// Gets the system info. - /// - /// The HTTP request. - /// SystemInfo. - SystemInfo GetSystemInfo(HttpRequest request); - - PublicSystemInfo GetPublicSystemInfo(HttpRequest request); - /// /// Gets a URL specific for the request. /// diff --git a/MediaBrowser.Controller/ISystemManager.cs b/MediaBrowser.Controller/ISystemManager.cs new file mode 100644 index 0000000000..ef3034d2f5 --- /dev/null +++ b/MediaBrowser.Controller/ISystemManager.cs @@ -0,0 +1,34 @@ +using MediaBrowser.Model.System; +using Microsoft.AspNetCore.Http; + +namespace MediaBrowser.Controller; + +/// +/// A service for managing the application instance. +/// +public interface ISystemManager +{ + /// + /// Gets the system info. + /// + /// The HTTP request. + /// The . + SystemInfo GetSystemInfo(HttpRequest request); + + /// + /// Gets the public system info. + /// + /// The HTTP request. + /// The . + PublicSystemInfo GetPublicSystemInfo(HttpRequest request); + + /// + /// Starts the application restart process. + /// + void Restart(); + + /// + /// Starts the application shutdown process. + /// + void Shutdown(); +} -- cgit v1.2.3 From 852f1dc0c1e3178955e2d1b2791b1e45f3f956b3 Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Thu, 5 Oct 2023 23:16:17 +0200 Subject: Don't create non existent persons in LibraryManager.GetPerson return null instead. GetStudio, GetGenre, GetMusicGenre, GetYear, GetArtist still create a new one when the requested one doesn't exist Fixes #3901 --- .../Library/LibraryManager.cs | 27 +++++++++++----------- .../Controllers/PersonsControllerTests.cs | 26 +++++++++++++++++++++ 2 files changed, 40 insertions(+), 13 deletions(-) create mode 100644 tests/Jellyfin.Server.Integration.Tests/Controllers/PersonsControllerTests.cs (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Library/LibraryManager.cs b/Emby.Server.Implementations/Library/LibraryManager.cs index b0a4a4151a..bdbf5f703c 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(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; } /// @@ -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(path), + DateCreated = DateTime.UtcNow, + DateModified = DateTime.UtcNow, + Path = path + }; + personEntity.PresentationUniqueKey = personEntity.CreatePresentationUniqueKey(); saveEntity = true; } diff --git a/tests/Jellyfin.Server.Integration.Tests/Controllers/PersonsControllerTests.cs b/tests/Jellyfin.Server.Integration.Tests/Controllers/PersonsControllerTests.cs new file mode 100644 index 0000000000..38c64547c2 --- /dev/null +++ b/tests/Jellyfin.Server.Integration.Tests/Controllers/PersonsControllerTests.cs @@ -0,0 +1,26 @@ +using System.Net; +using System.Threading.Tasks; +using Xunit; + +namespace Jellyfin.Server.Integration.Tests.Controllers; + +public class PersonsControllerTests : IClassFixture +{ + private readonly JellyfinApplicationFactory _factory; + private static string? _accessToken; + + public PersonsControllerTests(JellyfinApplicationFactory factory) + { + _factory = factory; + } + + [Fact] + public async Task GetPerson_DoesntExist_NotFound() + { + var client = _factory.CreateClient(); + client.DefaultRequestHeaders.AddAuthHeader(_accessToken ??= await AuthHelper.CompleteStartupAsync(client)); + + using var response = await client.GetAsync($"Persons/DoesntExist"); + Assert.Equal(HttpStatusCode.NotFound, response.StatusCode); + } +} -- cgit v1.2.3 From b176beb88e22a36cc056439ac2a4df4fbe68f2c1 Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Fri, 6 Oct 2023 00:40:09 +0200 Subject: Reduce string allocations Some simple changes to reduce the number of allocated strings --- Emby.Dlna/DlnaManager.cs | 2 +- Emby.Naming/ExternalFiles/ExternalPathParser.cs | 2 +- Emby.Naming/Video/StubResolver.cs | 7 ++--- Emby.Photos/PhotoProvider.cs | 2 +- .../IO/ManagedFileSystem.cs | 6 ++-- .../Library/LibraryManager.cs | 4 +-- .../Library/Resolvers/Audio/AudioResolver.cs | 6 ++-- .../Library/Resolvers/BaseVideoResolver.cs | 2 +- .../Library/Resolvers/Books/BookResolver.cs | 9 +++--- .../MediaEncoder/EncodingManager.cs | 2 +- .../Playlists/PlaylistManager.cs | 16 ++++------ .../Updates/InstallationManager.cs | 3 +- Jellyfin.Api/Controllers/DynamicHlsController.cs | 4 +-- Jellyfin.Api/Controllers/HlsSegmentController.cs | 9 +++--- Jellyfin.Api/Helpers/StreamingHelpers.cs | 10 +++--- Jellyfin.Api/Helpers/TranscodingJobHelper.cs | 2 +- .../MediaEncoding/EncodingHelper.cs | 18 +++++------ .../Attachments/AttachmentExtractor.cs | 36 +++------------------- MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs | 6 ++-- src/Jellyfin.Drawing.Skia/SkiaEncoder.cs | 2 +- src/Jellyfin.Drawing.Skia/StripCollageBuilder.cs | 12 ++++---- 21 files changed, 63 insertions(+), 97 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Dlna/DlnaManager.cs b/Emby.Dlna/DlnaManager.cs index 99b3e6e7ef..d67cb67b54 100644 --- a/Emby.Dlna/DlnaManager.cs +++ b/Emby.Dlna/DlnaManager.cs @@ -228,7 +228,7 @@ namespace Emby.Dlna try { return _fileSystem.GetFilePaths(path) - .Where(i => string.Equals(Path.GetExtension(i), ".xml", StringComparison.OrdinalIgnoreCase)) + .Where(i => Path.GetExtension(i.AsSpan()).Equals(".xml", StringComparison.OrdinalIgnoreCase)) .Select(i => ParseProfileFile(i, type)) .Where(i => i is not null) .ToList()!; // We just filtered out all the nulls diff --git a/Emby.Naming/ExternalFiles/ExternalPathParser.cs b/Emby.Naming/ExternalFiles/ExternalPathParser.cs index 9531296711..4080ba10d3 100644 --- a/Emby.Naming/ExternalFiles/ExternalPathParser.cs +++ b/Emby.Naming/ExternalFiles/ExternalPathParser.cs @@ -43,7 +43,7 @@ namespace Emby.Naming.ExternalFiles return null; } - var extension = Path.GetExtension(path); + var extension = Path.GetExtension(path.AsSpan()); if (!(_type == DlnaProfileType.Subtitle && _namingOptions.SubtitleFileExtensions.Contains(extension, StringComparison.OrdinalIgnoreCase)) && !(_type == DlnaProfileType.Audio && _namingOptions.AudioFileExtensions.Contains(extension, StringComparison.OrdinalIgnoreCase))) { diff --git a/Emby.Naming/Video/StubResolver.cs b/Emby.Naming/Video/StubResolver.cs index f7ba606e3e..4b9df19b08 100644 --- a/Emby.Naming/Video/StubResolver.cs +++ b/Emby.Naming/Video/StubResolver.cs @@ -26,19 +26,18 @@ namespace Emby.Naming.Video return false; } - var extension = Path.GetExtension(path); + var extension = Path.GetExtension(path.AsSpan()); if (!options.StubFileExtensions.Contains(extension, StringComparison.OrdinalIgnoreCase)) { return false; } - path = Path.GetFileNameWithoutExtension(path); - var token = Path.GetExtension(path).TrimStart('.'); + var token = Path.GetExtension(Path.GetFileNameWithoutExtension(path.AsSpan())).TrimStart('.'); foreach (var rule in options.StubTypes) { - if (string.Equals(rule.Token, token, StringComparison.OrdinalIgnoreCase)) + if (token.Equals(rule.Token, StringComparison.OrdinalIgnoreCase)) { stubType = rule.StubType; return true; diff --git a/Emby.Photos/PhotoProvider.cs b/Emby.Photos/PhotoProvider.cs index f54066c57f..27329a7f2f 100644 --- a/Emby.Photos/PhotoProvider.cs +++ b/Emby.Photos/PhotoProvider.cs @@ -61,7 +61,7 @@ namespace Emby.Photos item.SetImagePath(ImageType.Primary, item.Path); // Examples: https://github.com/mono/taglib-sharp/blob/a5f6949a53d09ce63ee7495580d6802921a21f14/tests/fixtures/TagLib.Tests.Images/NullOrientationTest.cs - if (_includeExtensions.Contains(Path.GetExtension(item.Path), StringComparison.OrdinalIgnoreCase)) + if (_includeExtensions.Contains(Path.GetExtension(item.Path.AsSpan()), StringComparison.OrdinalIgnoreCase)) { try { diff --git a/Emby.Server.Implementations/IO/ManagedFileSystem.cs b/Emby.Server.Implementations/IO/ManagedFileSystem.cs index 18b00ce0b2..4178936ce2 100644 --- a/Emby.Server.Implementations/IO/ManagedFileSystem.cs +++ b/Emby.Server.Implementations/IO/ManagedFileSystem.cs @@ -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 b0a4a4151a..845e7e3512 100644 --- a/Emby.Server.Implementations/Library/LibraryManager.cs +++ b/Emby.Server.Implementations/Library/LibraryManager.cs @@ -1162,7 +1162,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 @@ -3135,7 +3135,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/Resolvers/Audio/AudioResolver.cs b/Emby.Server.Implementations/Library/Resolvers/Audio/AudioResolver.cs index a74f824752..862f144e68 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 381796d0e3..779cfd5be4 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)); } /// diff --git a/Emby.Server.Implementations/Library/Resolvers/Books/BookResolver.cs b/Emby.Server.Implementations/Library/Resolvers/Books/BookResolver.cs index 042422c6f4..73861ff599 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/MediaEncoder/EncodingManager.cs b/Emby.Server.Implementations/MediaEncoder/EncodingManager.cs index 7732e32d0a..896f47923f 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/Playlists/PlaylistManager.cs b/Emby.Server.Implementations/Playlists/PlaylistManager.cs index 0cb450cdf3..649c499247 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/Updates/InstallationManager.cs b/Emby.Server.Implementations/Updates/InstallationManager.cs index 6c198b6f99..77d385ba1c 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; diff --git a/Jellyfin.Api/Controllers/DynamicHlsController.cs b/Jellyfin.Api/Controllers/DynamicHlsController.cs index 065a4ce5c6..4026a38f8d 100644 --- a/Jellyfin.Api/Controllers/DynamicHlsController.cs +++ b/Jellyfin.Api/Controllers/DynamicHlsController.cs @@ -2041,9 +2041,9 @@ public class DynamicHlsController : BaseJellyfinApiController return null; } - var playlistFilename = Path.GetFileNameWithoutExtension(playlist); + var playlistFilename = Path.GetFileNameWithoutExtension(playlist.AsSpan()); - var indexString = Path.GetFileNameWithoutExtension(file.Name).Substring(playlistFilename.Length); + var indexString = Path.GetFileNameWithoutExtension(file.Name.AsSpan()).Slice(playlistFilename.Length); return int.Parse(indexString, NumberStyles.Integer, CultureInfo.InvariantCulture); } diff --git a/Jellyfin.Api/Controllers/HlsSegmentController.cs b/Jellyfin.Api/Controllers/HlsSegmentController.cs index d7cec865e1..6eedfd8c7f 100644 --- a/Jellyfin.Api/Controllers/HlsSegmentController.cs +++ b/Jellyfin.Api/Controllers/HlsSegmentController.cs @@ -59,7 +59,7 @@ public class HlsSegmentController : BaseJellyfinApiController public ActionResult GetHlsAudioSegmentLegacy([FromRoute, Required] string itemId, [FromRoute, Required] string segmentId) { // TODO: Deprecate with new iOS app - var file = segmentId + Path.GetExtension(Request.Path); + var file = string.Concat(segmentId, Path.GetExtension(Request.Path.Value.AsSpan())); var transcodePath = _serverConfigurationManager.GetTranscodePath(); file = Path.GetFullPath(Path.Combine(transcodePath, file)); var fileDir = Path.GetDirectoryName(file); @@ -85,11 +85,12 @@ public class HlsSegmentController : BaseJellyfinApiController [SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "itemId", Justification = "Required for ServiceStack")] public ActionResult GetHlsPlaylistLegacy([FromRoute, Required] string itemId, [FromRoute, Required] string playlistId) { - var file = playlistId + Path.GetExtension(Request.Path); + var file = string.Concat(playlistId, Path.GetExtension(Request.Path.Value.AsSpan())); var transcodePath = _serverConfigurationManager.GetTranscodePath(); file = Path.GetFullPath(Path.Combine(transcodePath, file)); var fileDir = Path.GetDirectoryName(file); - if (string.IsNullOrEmpty(fileDir) || !fileDir.StartsWith(transcodePath, StringComparison.InvariantCulture) || Path.GetExtension(file) != ".m3u8") + if (string.IsNullOrEmpty(fileDir) || !fileDir.StartsWith(transcodePath, StringComparison.InvariantCulture) + || Path.GetExtension(file.AsSpan()).Equals(".m3u8", StringComparison.OrdinalIgnoreCase)) { return BadRequest("Invalid segment."); } @@ -138,7 +139,7 @@ public class HlsSegmentController : BaseJellyfinApiController [FromRoute, Required] string segmentId, [FromRoute, Required] string segmentContainer) { - var file = segmentId + Path.GetExtension(Request.Path); + var file = string.Concat(segmentId, Path.GetExtension(Request.Path.Value.AsSpan())); var transcodeFolderPath = _serverConfigurationManager.GetTranscodePath(); file = Path.GetFullPath(Path.Combine(transcodeFolderPath, file)); diff --git a/Jellyfin.Api/Helpers/StreamingHelpers.cs b/Jellyfin.Api/Helpers/StreamingHelpers.cs index 782cd65685..d15fe4fab2 100644 --- a/Jellyfin.Api/Helpers/StreamingHelpers.cs +++ b/Jellyfin.Api/Helpers/StreamingHelpers.cs @@ -243,7 +243,7 @@ public static class StreamingHelpers ? GetOutputFileExtension(state, mediaSource) : ("." + state.OutputContainer); - state.OutputFilePath = GetOutputFilePath(state, ext!, serverConfigurationManager, streamingRequest.DeviceId, streamingRequest.PlaySessionId); + state.OutputFilePath = GetOutputFilePath(state, ext, serverConfigurationManager, streamingRequest.DeviceId, streamingRequest.PlaySessionId); return state; } @@ -418,11 +418,11 @@ public static class StreamingHelpers /// System.String. private static string? GetOutputFileExtension(StreamState state, MediaSourceInfo? mediaSource) { - var ext = Path.GetExtension(state.RequestedUrl); + var ext = Path.GetExtension(state.RequestedUrl.AsSpan()); - if (!string.IsNullOrEmpty(ext)) + if (ext.IsEmpty) { - return ext; + return null; } // Try to infer based on the desired video codec @@ -504,7 +504,7 @@ public static class StreamingHelpers /// The device id. /// The play session id. /// The complete file path, including the folder, for the transcoding file. - private static string GetOutputFilePath(StreamState state, string outputFileExtension, IServerConfigurationManager serverConfigurationManager, string? deviceId, string? playSessionId) + private static string GetOutputFilePath(StreamState state, string? outputFileExtension, IServerConfigurationManager serverConfigurationManager, string? deviceId, string? playSessionId) { var data = $"{state.MediaPath}-{state.UserAgent}-{deviceId!}-{playSessionId!}"; diff --git a/Jellyfin.Api/Helpers/TranscodingJobHelper.cs b/Jellyfin.Api/Helpers/TranscodingJobHelper.cs index 73ebb396d0..c16a586d60 100644 --- a/Jellyfin.Api/Helpers/TranscodingJobHelper.cs +++ b/Jellyfin.Api/Helpers/TranscodingJobHelper.cs @@ -538,7 +538,7 @@ public class TranscodingJobHelper : IDisposable await _attachmentExtractor.ExtractAllAttachments(state.MediaPath, state.MediaSource, attachmentPath, cancellationTokenSource.Token).ConfigureAwait(false); } - if (state.SubtitleStream.IsExternal && string.Equals(Path.GetExtension(state.SubtitleStream.Path), ".mks", StringComparison.OrdinalIgnoreCase)) + if (state.SubtitleStream.IsExternal && Path.GetExtension(state.SubtitleStream.Path.AsSpan()).Equals(".mks", StringComparison.OrdinalIgnoreCase)) { string subtitlePath = state.SubtitleStream.Path; string subtitlePathArgument = string.Format(CultureInfo.InvariantCulture, "file:\"{0}\"", subtitlePath.Replace("\"", "\\\"", StringComparison.Ordinal)); diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs index c311d3b8ab..3be7a4252b 100644 --- a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs +++ b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs @@ -548,25 +548,25 @@ namespace MediaBrowser.Controller.MediaEncoding /// System.Nullable{VideoCodecs}. public string InferVideoCodec(string url) { - var ext = Path.GetExtension(url); + var ext = Path.GetExtension(url.AsSpan()); - if (string.Equals(ext, ".asf", StringComparison.OrdinalIgnoreCase)) + if (ext.Equals(".asf", StringComparison.OrdinalIgnoreCase)) { return "wmv"; } - if (string.Equals(ext, ".webm", StringComparison.OrdinalIgnoreCase)) + if (ext.Equals(".webm", StringComparison.OrdinalIgnoreCase)) { // TODO: this may not always mean VP8, as the codec ages return "vp8"; } - if (string.Equals(ext, ".ogg", StringComparison.OrdinalIgnoreCase) || string.Equals(ext, ".ogv", StringComparison.OrdinalIgnoreCase)) + if (ext.Equals(".ogg", StringComparison.OrdinalIgnoreCase) || ext.Equals(".ogv", StringComparison.OrdinalIgnoreCase)) { return "theora"; } - if (string.Equals(ext, ".m3u8", StringComparison.OrdinalIgnoreCase) || string.Equals(ext, ".ts", StringComparison.OrdinalIgnoreCase)) + if (ext.Equals(".m3u8", StringComparison.OrdinalIgnoreCase) || ext.Equals(".ts", StringComparison.OrdinalIgnoreCase)) { return "h264"; } @@ -1080,10 +1080,10 @@ namespace MediaBrowser.Controller.MediaEncoding && state.SubtitleStream.IsExternal) { var subtitlePath = state.SubtitleStream.Path; - var subtitleExtension = Path.GetExtension(subtitlePath); + var subtitleExtension = Path.GetExtension(subtitlePath.AsSpan()); - if (string.Equals(subtitleExtension, ".sub", StringComparison.OrdinalIgnoreCase) - || string.Equals(subtitleExtension, ".sup", StringComparison.OrdinalIgnoreCase)) + if (subtitleExtension.Equals(".sub", StringComparison.OrdinalIgnoreCase) + || subtitleExtension.Equals(".sup", StringComparison.OrdinalIgnoreCase)) { var idxFile = Path.ChangeExtension(subtitlePath, ".idx"); if (File.Exists(idxFile)) @@ -6033,7 +6033,7 @@ namespace MediaBrowser.Controller.MediaEncoding var format = string.Empty; var keyFrame = string.Empty; - if (string.Equals(Path.GetExtension(outputPath), ".mp4", StringComparison.OrdinalIgnoreCase) + if (Path.GetExtension(outputPath.AsSpan()).Equals(".mp4", StringComparison.OrdinalIgnoreCase) && state.BaseRequest.Context == EncodingContext.Streaming) { // Comparison: https://github.com/jansmolders86/mediacenterjs/blob/master/lib/transcoding/desktop.js diff --git a/MediaBrowser.MediaEncoding/Attachments/AttachmentExtractor.cs b/MediaBrowser.MediaEncoding/Attachments/AttachmentExtractor.cs index 989e386a51..0ec0c84d41 100644 --- a/MediaBrowser.MediaEncoding/Attachments/AttachmentExtractor.cs +++ b/MediaBrowser.MediaEncoding/Attachments/AttachmentExtractor.cs @@ -1,4 +1,3 @@ -#nullable disable #pragma warning disable CS1591 using System; @@ -23,7 +22,7 @@ using Microsoft.Extensions.Logging; namespace MediaBrowser.MediaEncoding.Attachments { - public class AttachmentExtractor : IAttachmentExtractor, IDisposable + public sealed class AttachmentExtractor : IAttachmentExtractor { private readonly ILogger _logger; private readonly IApplicationPaths _appPaths; @@ -34,8 +33,6 @@ namespace MediaBrowser.MediaEncoding.Attachments private readonly ConcurrentDictionary _semaphoreLocks = new ConcurrentDictionary(); - private bool _disposed = false; - public AttachmentExtractor( ILogger logger, IApplicationPaths appPaths, @@ -296,7 +293,7 @@ namespace MediaBrowser.MediaEncoding.Attachments ArgumentException.ThrowIfNullOrEmpty(outputPath); - Directory.CreateDirectory(Path.GetDirectoryName(outputPath)); + Directory.CreateDirectory(Path.GetDirectoryName(outputPath) ?? throw new ArgumentException("Path can't be a root directory.", nameof(outputPath))); var processArgs = string.Format( CultureInfo.InvariantCulture, @@ -391,33 +388,8 @@ namespace MediaBrowser.MediaEncoding.Attachments filename = (mediaPath + attachmentStreamIndex.ToString(CultureInfo.InvariantCulture)).GetMD5().ToString("D", CultureInfo.InvariantCulture); } - var prefix = filename.Substring(0, 1); - return Path.Combine(_appPaths.DataPath, "attachments", prefix, filename); - } - - /// - public void Dispose() - { - Dispose(true); - GC.SuppressFinalize(this); - } - - /// - /// Releases unmanaged and - optionally - managed resources. - /// - /// true to release both managed and unmanaged resources; false to release only unmanaged resources. - protected virtual void Dispose(bool disposing) - { - if (_disposed) - { - return; - } - - if (disposing) - { - } - - _disposed = true; + var prefix = filename.AsSpan(0, 1); + return Path.Join(_appPaths.DataPath, "attachments", prefix, filename); } } } diff --git a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs index 346e97ae12..59c3505616 100644 --- a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs +++ b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs @@ -316,10 +316,8 @@ namespace MediaBrowser.MediaEncoding.Encoder { var files = _fileSystem.GetFilePaths(path, recursive); - var excludeExtensions = new[] { ".c" }; - - return files.FirstOrDefault(i => string.Equals(Path.GetFileNameWithoutExtension(i), filename, StringComparison.OrdinalIgnoreCase) - && !excludeExtensions.Contains(Path.GetExtension(i) ?? string.Empty)); + return files.FirstOrDefault(i => Path.GetFileNameWithoutExtension(i.AsSpan()).Equals(filename, StringComparison.OrdinalIgnoreCase) + && !Path.GetExtension(i.AsSpan()).Equals(".c", StringComparison.OrdinalIgnoreCase)); } catch (Exception) { diff --git a/src/Jellyfin.Drawing.Skia/SkiaEncoder.cs b/src/Jellyfin.Drawing.Skia/SkiaEncoder.cs index 126c0503e0..416f602179 100644 --- a/src/Jellyfin.Drawing.Skia/SkiaEncoder.cs +++ b/src/Jellyfin.Drawing.Skia/SkiaEncoder.cs @@ -188,7 +188,7 @@ public class SkiaEncoder : IImageEncoder return path; } - var tempPath = Path.Combine(_appPaths.TempDirectory, Guid.NewGuid() + Path.GetExtension(path)); + var tempPath = Path.Combine(_appPaths.TempDirectory, string.Concat(Guid.NewGuid().ToString(), Path.GetExtension(path.AsSpan()))); var directory = Path.GetDirectoryName(tempPath) ?? throw new ResourceNotFoundException($"Provided path ({tempPath}) is not valid."); Directory.CreateDirectory(directory); File.Copy(path, tempPath, true); diff --git a/src/Jellyfin.Drawing.Skia/StripCollageBuilder.cs b/src/Jellyfin.Drawing.Skia/StripCollageBuilder.cs index 6dff7aa9b4..4aff26c16b 100644 --- a/src/Jellyfin.Drawing.Skia/StripCollageBuilder.cs +++ b/src/Jellyfin.Drawing.Skia/StripCollageBuilder.cs @@ -38,25 +38,25 @@ public partial class StripCollageBuilder { ArgumentNullException.ThrowIfNull(outputPath); - var ext = Path.GetExtension(outputPath); + var ext = Path.GetExtension(outputPath.AsSpan()); - if (string.Equals(ext, ".jpg", StringComparison.OrdinalIgnoreCase) - || string.Equals(ext, ".jpeg", StringComparison.OrdinalIgnoreCase)) + if (ext.Equals(".jpg", StringComparison.OrdinalIgnoreCase) + || ext.Equals(".jpeg", StringComparison.OrdinalIgnoreCase)) { return SKEncodedImageFormat.Jpeg; } - if (string.Equals(ext, ".webp", StringComparison.OrdinalIgnoreCase)) + if (ext.Equals(".webp", StringComparison.OrdinalIgnoreCase)) { return SKEncodedImageFormat.Webp; } - if (string.Equals(ext, ".gif", StringComparison.OrdinalIgnoreCase)) + if (ext.Equals(".gif", StringComparison.OrdinalIgnoreCase)) { return SKEncodedImageFormat.Gif; } - if (string.Equals(ext, ".bmp", StringComparison.OrdinalIgnoreCase)) + if (ext.Equals(".bmp", StringComparison.OrdinalIgnoreCase)) { return SKEncodedImageFormat.Bmp; } -- cgit v1.2.3 From e9e1fd214b608007735b2a2a3ed722d0e4937da8 Mon Sep 17 00:00:00 2001 From: "[ ]" Date: Thu, 5 Oct 2023 07:08:18 +0000 Subject: Translated using Weblate (Pirate (pr)) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/pr/ --- Emby.Server.Implementations/Localization/Core/pr.json | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/pr.json b/Emby.Server.Implementations/Localization/Core/pr.json index d2446a7fdc..26dc5ce82f 100644 --- a/Emby.Server.Implementations/Localization/Core/pr.json +++ b/Emby.Server.Implementations/Localization/Core/pr.json @@ -29,5 +29,8 @@ "Forced": "Pressed", "External": "Outboard", "HeaderFavoriteEpisodes": "Treasured Tales", - "HeaderFavoriteShows": "Treasured Tales" + "HeaderFavoriteShows": "Treasured Tales", + "ChapterNameValue": "Piece {0}", + "HeaderFavoriteSongs": "Treasured Chimes", + "HeaderNextUp": "Incoming" } -- cgit v1.2.3 From 8466e53b8f0e885e75e3f3f5e9c81fceb0a61379 Mon Sep 17 00:00:00 2001 From: tellmeY18 Date: Sat, 7 Oct 2023 21:49:16 +0000 Subject: Translated using Weblate (Malayalam) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/ml/ --- Emby.Server.Implementations/Localization/Core/ml.json | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/ml.json b/Emby.Server.Implementations/Localization/Core/ml.json index 0620fbcdb0..0b50fa5298 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": "കീഫ്രെയിം എക്സ്ട്രാക്റ്റർ" } -- cgit v1.2.3