diff options
| author | Cody Robibero <cody@robibe.ro> | 2024-02-28 17:09:23 -0700 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2024-02-28 17:09:23 -0700 |
| commit | f3c333f4d5bb272b5ffcff29af337ca31e8c374b (patch) | |
| tree | 008525e157be39a25e013fd3b039d4680760eb68 /Emby.Server.Implementations | |
| parent | 54eb81395ef8d3d4cb064b56361ce94fc72b38b5 (diff) | |
| parent | 4f0f364ac941dc4a856512c9bf0e6b93fdf7b3ab (diff) | |
Merge branch 'master' into bhowe34/fix-replace-missing-metadata-for-music
Diffstat (limited to 'Emby.Server.Implementations')
19 files changed, 167 insertions, 241 deletions
diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs index 5870fed76..745753440 100644 --- a/Emby.Server.Implementations/ApplicationHost.cs +++ b/Emby.Server.Implementations/ApplicationHost.cs @@ -62,7 +62,6 @@ using MediaBrowser.Controller.MediaEncoding; using MediaBrowser.Controller.Net; using MediaBrowser.Controller.Persistence; using MediaBrowser.Controller.Playlists; -using MediaBrowser.Controller.Plugins; using MediaBrowser.Controller.Providers; using MediaBrowser.Controller.QuickConnect; using MediaBrowser.Controller.Resolvers; @@ -393,7 +392,7 @@ namespace Emby.Server.Implementations /// Runs the startup tasks. /// </summary> /// <returns><see cref="Task" />.</returns> - public async Task RunStartupTasksAsync() + public Task RunStartupTasksAsync() { Logger.LogInformation("Running startup tasks"); @@ -405,38 +404,10 @@ namespace Emby.Server.Implementations Resolve<IMediaEncoder>().SetFFmpegPath(); Logger.LogInformation("ServerId: {ServerId}", SystemId); - - var entryPoints = GetExports<IServerEntryPoint>(); - - var stopWatch = new Stopwatch(); - stopWatch.Start(); - - await Task.WhenAll(StartEntryPoints(entryPoints, true)).ConfigureAwait(false); - Logger.LogInformation("Executed all pre-startup entry points in {Elapsed:g}", stopWatch.Elapsed); - Logger.LogInformation("Core startup complete"); CoreStartupHasCompleted = true; - stopWatch.Restart(); - - await Task.WhenAll(StartEntryPoints(entryPoints, false)).ConfigureAwait(false); - Logger.LogInformation("Executed all post-startup entry points in {Elapsed:g}", stopWatch.Elapsed); - stopWatch.Stop(); - } - - private IEnumerable<Task> StartEntryPoints(IEnumerable<IServerEntryPoint> entryPoints, bool isBeforeStartup) - { - foreach (var entryPoint in entryPoints) - { - if (isBeforeStartup != (entryPoint is IRunBeforeStartup)) - { - continue; - } - - Logger.LogDebug("Starting entry point {Type}", entryPoint.GetType()); - - yield return entryPoint.RunAsync(); - } + return Task.CompletedTask; } /// <inheritdoc/> @@ -659,7 +630,7 @@ namespace Emby.Server.Implementations BaseItem.FileSystem = Resolve<IFileSystem>(); BaseItem.UserDataManager = Resolve<IUserDataManager>(); BaseItem.ChannelManager = Resolve<IChannelManager>(); - Video.LiveTvManager = Resolve<ILiveTvManager>(); + Video.RecordingsManager = Resolve<IRecordingsManager>(); Folder.UserViewManager = Resolve<IUserViewManager>(); UserView.TVSeriesManager = Resolve<ITVSeriesManager>(); UserView.CollectionManager = Resolve<ICollectionManager>(); @@ -695,8 +666,6 @@ namespace Emby.Server.Implementations GetExports<IMetadataSaver>(), GetExports<IExternalId>()); - Resolve<ILiveTvManager>().AddParts(GetExports<ILiveTvService>(), GetExports<IListingsProvider>()); - Resolve<IMediaSourceManager>().AddParts(GetExports<IMediaSourceProvider>()); } diff --git a/Emby.Server.Implementations/Dto/DtoService.cs b/Emby.Server.Implementations/Dto/DtoService.cs index d0d5bb81c..7812687ea 100644 --- a/Emby.Server.Implementations/Dto/DtoService.cs +++ b/Emby.Server.Implementations/Dto/DtoService.cs @@ -18,7 +18,6 @@ using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities.Audio; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.LiveTv; -using MediaBrowser.Controller.Lyrics; using MediaBrowser.Controller.Persistence; using MediaBrowser.Controller.Playlists; using MediaBrowser.Controller.Providers; @@ -47,12 +46,12 @@ namespace Emby.Server.Implementations.Dto private readonly IImageProcessor _imageProcessor; private readonly IProviderManager _providerManager; + private readonly IRecordingsManager _recordingsManager; private readonly IApplicationHost _appHost; private readonly IMediaSourceManager _mediaSourceManager; private readonly Lazy<ILiveTvManager> _livetvManagerFactory; - private readonly ILyricManager _lyricManager; private readonly ITrickplayManager _trickplayManager; public DtoService( @@ -62,10 +61,10 @@ namespace Emby.Server.Implementations.Dto IItemRepository itemRepo, IImageProcessor imageProcessor, IProviderManager providerManager, + IRecordingsManager recordingsManager, IApplicationHost appHost, IMediaSourceManager mediaSourceManager, Lazy<ILiveTvManager> livetvManagerFactory, - ILyricManager lyricManager, ITrickplayManager trickplayManager) { _logger = logger; @@ -74,10 +73,10 @@ namespace Emby.Server.Implementations.Dto _itemRepo = itemRepo; _imageProcessor = imageProcessor; _providerManager = providerManager; + _recordingsManager = recordingsManager; _appHost = appHost; _mediaSourceManager = mediaSourceManager; _livetvManagerFactory = livetvManagerFactory; - _lyricManager = lyricManager; _trickplayManager = trickplayManager; } @@ -149,10 +148,6 @@ namespace Emby.Server.Implementations.Dto { LivetvManager.AddInfoToProgramDto(new[] { (item, dto) }, options.Fields, user).GetAwaiter().GetResult(); } - else if (item is Audio) - { - dto.HasLyrics = _lyricManager.HasLyricFile(item); - } if (item is IItemByName itemByName && options.ContainsField(ItemFields.ItemCounts)) @@ -256,8 +251,7 @@ namespace Emby.Server.Implementations.Dto dto.Etag = item.GetEtag(user); } - var liveTvManager = LivetvManager; - var activeRecording = liveTvManager.GetActiveRecordingInfo(item.Path); + var activeRecording = _recordingsManager.GetActiveRecordingInfo(item.Path); if (activeRecording is not null) { dto.Type = BaseItemKind.Recording; @@ -270,7 +264,12 @@ namespace Emby.Server.Implementations.Dto dto.Name = dto.SeriesName; } - liveTvManager.AddInfoToRecordingDto(item, dto, activeRecording, user); + LivetvManager.AddInfoToRecordingDto(item, dto, activeRecording, user); + } + + if (item is Audio audio) + { + dto.HasLyrics = audio.GetMediaStreams().Any(s => s.Type == MediaStreamType.Lyric); } return dto; diff --git a/Emby.Server.Implementations/EntryPoints/LibraryChangedNotifier.cs b/Emby.Server.Implementations/EntryPoints/LibraryChangedNotifier.cs index 83e7b230d..4c668379c 100644 --- a/Emby.Server.Implementations/EntryPoints/LibraryChangedNotifier.cs +++ b/Emby.Server.Implementations/EntryPoints/LibraryChangedNotifier.cs @@ -13,19 +13,19 @@ using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities.Audio; using MediaBrowser.Controller.Library; -using MediaBrowser.Controller.Plugins; using MediaBrowser.Controller.Providers; using MediaBrowser.Controller.Session; using MediaBrowser.Model.Entities; using MediaBrowser.Model.Session; +using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; namespace Emby.Server.Implementations.EntryPoints; /// <summary> -/// A <see cref="IServerEntryPoint"/> that notifies users when libraries are updated. +/// A <see cref="IHostedService"/> responsible for notifying users when libraries are updated. /// </summary> -public sealed class LibraryChangedNotifier : IServerEntryPoint +public sealed class LibraryChangedNotifier : IHostedService, IDisposable { private readonly ILibraryManager _libraryManager; private readonly IServerConfigurationManager _configurationManager; @@ -70,7 +70,7 @@ public sealed class LibraryChangedNotifier : IServerEntryPoint } /// <inheritdoc /> - public Task RunAsync() + public Task StartAsync(CancellationToken cancellationToken) { _libraryManager.ItemAdded += OnLibraryItemAdded; _libraryManager.ItemUpdated += OnLibraryItemUpdated; @@ -83,6 +83,20 @@ public sealed class LibraryChangedNotifier : IServerEntryPoint return Task.CompletedTask; } + /// <inheritdoc /> + public Task StopAsync(CancellationToken cancellationToken) + { + _libraryManager.ItemAdded -= OnLibraryItemAdded; + _libraryManager.ItemUpdated -= OnLibraryItemUpdated; + _libraryManager.ItemRemoved -= OnLibraryItemRemoved; + + _providerManager.RefreshCompleted -= OnProviderRefreshCompleted; + _providerManager.RefreshStarted -= OnProviderRefreshStarted; + _providerManager.RefreshProgress -= OnProviderRefreshProgress; + + return Task.CompletedTask; + } + private void OnProviderRefreshProgress(object? sender, GenericEventArgs<Tuple<BaseItem, double>> e) { var item = e.Argument.Item1; @@ -137,9 +151,7 @@ public sealed class LibraryChangedNotifier : IServerEntryPoint } private void OnProviderRefreshStarted(object? sender, GenericEventArgs<BaseItem> e) - { - OnProviderRefreshProgress(sender, new GenericEventArgs<Tuple<BaseItem, double>>(new Tuple<BaseItem, double>(e.Argument, 0))); - } + => OnProviderRefreshProgress(sender, new GenericEventArgs<Tuple<BaseItem, double>>(new Tuple<BaseItem, double>(e.Argument, 0))); private void OnProviderRefreshCompleted(object? sender, GenericEventArgs<BaseItem> e) { @@ -342,7 +354,7 @@ public sealed class LibraryChangedNotifier : IServerEntryPoint return item.SourceType == SourceType.Library; } - private IEnumerable<string> GetTopParentIds(List<BaseItem> items, List<Folder> allUserRootChildren) + private static IEnumerable<string> GetTopParentIds(List<BaseItem> items, List<Folder> allUserRootChildren) { var list = new List<string>(); @@ -363,7 +375,7 @@ public sealed class LibraryChangedNotifier : IServerEntryPoint return list.Distinct(StringComparer.Ordinal); } - private IEnumerable<T> TranslatePhysicalItemToUserLibrary<T>(T item, User user, bool includeIfNotFound = false) + private T[] TranslatePhysicalItemToUserLibrary<T>(T item, User user, bool includeIfNotFound = false) where T : BaseItem { // If the physical root changed, return the user root @@ -384,18 +396,7 @@ public sealed class LibraryChangedNotifier : IServerEntryPoint /// <inheritdoc /> public void Dispose() { - _libraryManager.ItemAdded -= OnLibraryItemAdded; - _libraryManager.ItemUpdated -= OnLibraryItemUpdated; - _libraryManager.ItemRemoved -= OnLibraryItemRemoved; - - _providerManager.RefreshCompleted -= OnProviderRefreshCompleted; - _providerManager.RefreshStarted -= OnProviderRefreshStarted; - _providerManager.RefreshProgress -= OnProviderRefreshProgress; - - if (_libraryUpdateTimer is not null) - { - _libraryUpdateTimer.Dispose(); - _libraryUpdateTimer = null; - } + _libraryUpdateTimer?.Dispose(); + _libraryUpdateTimer = null; } } diff --git a/Emby.Server.Implementations/EntryPoints/UserDataChangeNotifier.cs b/Emby.Server.Implementations/EntryPoints/UserDataChangeNotifier.cs index d32759017..957ad9c01 100644 --- a/Emby.Server.Implementations/EntryPoints/UserDataChangeNotifier.cs +++ b/Emby.Server.Implementations/EntryPoints/UserDataChangeNotifier.cs @@ -1,5 +1,3 @@ -#pragma warning disable CS1591 - using System; using System.Collections.Generic; using System.Globalization; @@ -8,14 +6,17 @@ using System.Threading; using System.Threading.Tasks; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Library; -using MediaBrowser.Controller.Plugins; using MediaBrowser.Controller.Session; using MediaBrowser.Model.Entities; using MediaBrowser.Model.Session; +using Microsoft.Extensions.Hosting; namespace Emby.Server.Implementations.EntryPoints { - public sealed class UserDataChangeNotifier : IServerEntryPoint + /// <summary> + /// <see cref="IHostedService"/> responsible for notifying users when associated item data is updated. + /// </summary> + public sealed class UserDataChangeNotifier : IHostedService, IDisposable { private const int UpdateDuration = 500; @@ -23,25 +24,43 @@ namespace Emby.Server.Implementations.EntryPoints private readonly IUserDataManager _userDataManager; private readonly IUserManager _userManager; - private readonly Dictionary<Guid, List<BaseItem>> _changedItems = new Dictionary<Guid, List<BaseItem>>(); + private readonly Dictionary<Guid, List<BaseItem>> _changedItems = new(); + private readonly object _syncLock = new(); - private readonly object _syncLock = new object(); private Timer? _updateTimer; - public UserDataChangeNotifier(IUserDataManager userDataManager, ISessionManager sessionManager, IUserManager userManager) + /// <summary> + /// Initializes a new instance of the <see cref="UserDataChangeNotifier"/> class. + /// </summary> + /// <param name="userDataManager">The <see cref="IUserDataManager"/>.</param> + /// <param name="sessionManager">The <see cref="ISessionManager"/>.</param> + /// <param name="userManager">The <see cref="IUserManager"/>.</param> + public UserDataChangeNotifier( + IUserDataManager userDataManager, + ISessionManager sessionManager, + IUserManager userManager) { _userDataManager = userDataManager; _sessionManager = sessionManager; _userManager = userManager; } - public Task RunAsync() + /// <inheritdoc /> + public Task StartAsync(CancellationToken cancellationToken) { _userDataManager.UserDataSaved += OnUserDataManagerUserDataSaved; return Task.CompletedTask; } + /// <inheritdoc /> + public Task StopAsync(CancellationToken cancellationToken) + { + _userDataManager.UserDataSaved -= OnUserDataManagerUserDataSaved; + + return Task.CompletedTask; + } + private void OnUserDataManagerUserDataSaved(object? sender, UserDataSaveEventArgs e) { if (e.SaveReason == UserDataSaveReason.PlaybackProgress) @@ -103,55 +122,40 @@ namespace Emby.Server.Implementations.EntryPoints } } - await SendNotifications(changes, CancellationToken.None).ConfigureAwait(false); - } - - private async Task SendNotifications(List<KeyValuePair<Guid, List<BaseItem>>> changes, CancellationToken cancellationToken) - { - foreach ((var key, var value) in changes) + foreach (var (userId, changedItems) in changes) { - await SendNotifications(key, value, cancellationToken).ConfigureAwait(false); + await _sessionManager.SendMessageToUserSessions( + [userId], + SessionMessageType.UserDataChanged, + () => GetUserDataChangeInfo(userId, changedItems), + default).ConfigureAwait(false); } } - private Task SendNotifications(Guid userId, List<BaseItem> changedItems, CancellationToken cancellationToken) - { - return _sessionManager.SendMessageToUserSessions(new List<Guid> { userId }, SessionMessageType.UserDataChanged, () => GetUserDataChangeInfo(userId, changedItems), cancellationToken); - } - private UserDataChangeInfo GetUserDataChangeInfo(Guid userId, List<BaseItem> changedItems) { var user = _userManager.GetUserById(userId); - var dtoList = changedItems - .DistinctBy(x => x.Id) - .Select(i => - { - var dto = _userDataManager.GetUserDataDto(i, user); - dto.ItemId = i.Id.ToString("N", CultureInfo.InvariantCulture); - return dto; - }) - .ToArray(); - - var userIdString = userId.ToString("N", CultureInfo.InvariantCulture); - return new UserDataChangeInfo { - UserId = userIdString, - - UserDataList = dtoList + UserId = userId.ToString("N", CultureInfo.InvariantCulture), + UserDataList = changedItems + .DistinctBy(x => x.Id) + .Select(i => + { + var dto = _userDataManager.GetUserDataDto(i, user); + dto.ItemId = i.Id.ToString("N", CultureInfo.InvariantCulture); + return dto; + }) + .ToArray() }; } + /// <inheritdoc /> public void Dispose() { - if (_updateTimer is not null) - { - _updateTimer.Dispose(); - _updateTimer = null; - } - - _userDataManager.UserDataSaved -= OnUserDataManagerUserDataSaved; + _updateTimer?.Dispose(); + _updateTimer = null; } } } diff --git a/Emby.Server.Implementations/IO/LibraryMonitor.cs b/Emby.Server.Implementations/IO/LibraryMonitor.cs index dde38906f..31617d1a5 100644 --- a/Emby.Server.Implementations/IO/LibraryMonitor.cs +++ b/Emby.Server.Implementations/IO/LibraryMonitor.cs @@ -1,5 +1,3 @@ -#pragma warning disable CS1591 - using System; using System.Collections.Concurrent; using System.Collections.Generic; @@ -11,11 +9,13 @@ using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Library; using MediaBrowser.Model.IO; +using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; namespace Emby.Server.Implementations.IO { - public class LibraryMonitor : ILibraryMonitor + /// <inheritdoc cref="ILibraryMonitor" /> + public sealed class LibraryMonitor : ILibraryMonitor, IDisposable { private readonly ILogger<LibraryMonitor> _logger; private readonly ILibraryManager _libraryManager; @@ -25,19 +25,19 @@ namespace Emby.Server.Implementations.IO /// <summary> /// The file system watchers. /// </summary> - private readonly ConcurrentDictionary<string, FileSystemWatcher> _fileSystemWatchers = new ConcurrentDictionary<string, FileSystemWatcher>(StringComparer.OrdinalIgnoreCase); + private readonly ConcurrentDictionary<string, FileSystemWatcher> _fileSystemWatchers = new(StringComparer.OrdinalIgnoreCase); /// <summary> /// The affected paths. /// </summary> - private readonly List<FileRefresher> _activeRefreshers = new List<FileRefresher>(); + private readonly List<FileRefresher> _activeRefreshers = []; /// <summary> /// A dynamic list of paths that should be ignored. Added to during our own file system modifications. /// </summary> - private readonly ConcurrentDictionary<string, string> _tempIgnoredPaths = new ConcurrentDictionary<string, string>(StringComparer.OrdinalIgnoreCase); + private readonly ConcurrentDictionary<string, string> _tempIgnoredPaths = new(StringComparer.OrdinalIgnoreCase); - private bool _disposed = false; + private bool _disposed; /// <summary> /// Initializes a new instance of the <see cref="LibraryMonitor" /> class. @@ -46,34 +46,31 @@ namespace Emby.Server.Implementations.IO /// <param name="libraryManager">The library manager.</param> /// <param name="configurationManager">The configuration manager.</param> /// <param name="fileSystem">The filesystem.</param> + /// <param name="appLifetime">The <see cref="IHostApplicationLifetime"/>.</param> public LibraryMonitor( ILogger<LibraryMonitor> logger, ILibraryManager libraryManager, IServerConfigurationManager configurationManager, - IFileSystem fileSystem) + IFileSystem fileSystem, + IHostApplicationLifetime appLifetime) { _libraryManager = libraryManager; _logger = logger; _configurationManager = configurationManager; _fileSystem = fileSystem; - } - /// <summary> - /// Add the path to our temporary ignore list. Use when writing to a path within our listening scope. - /// </summary> - /// <param name="path">The path.</param> - private void TemporarilyIgnore(string path) - { - _tempIgnoredPaths[path] = path; + appLifetime.ApplicationStarted.Register(Start); } + /// <inheritdoc /> public void ReportFileSystemChangeBeginning(string path) { ArgumentException.ThrowIfNullOrEmpty(path); - TemporarilyIgnore(path); + _tempIgnoredPaths[path] = path; } + /// <inheritdoc /> public async void ReportFileSystemChangeComplete(string path, bool refreshPath) { ArgumentException.ThrowIfNullOrEmpty(path); @@ -107,14 +104,10 @@ namespace Emby.Server.Implementations.IO var options = _libraryManager.GetLibraryOptions(item); - if (options is not null) - { - return options.EnableRealtimeMonitor; - } - - return false; + return options is not null && options.EnableRealtimeMonitor; } + /// <inheritdoc /> public void Start() { _libraryManager.ItemAdded += OnLibraryManagerItemAdded; @@ -306,21 +299,12 @@ namespace Emby.Server.Implementations.IO { if (removeFromList) { - RemoveWatcherFromList(watcher); + _fileSystemWatchers.TryRemove(watcher.Path, out _); } } } /// <summary> - /// Removes the watcher from list. - /// </summary> - /// <param name="watcher">The watcher.</param> - private void RemoveWatcherFromList(FileSystemWatcher watcher) - { - _fileSystemWatchers.TryRemove(watcher.Path, out _); - } - - /// <summary> /// Handles the Error event of the watcher control. /// </summary> /// <param name="sender">The source of the event.</param> @@ -352,6 +336,7 @@ namespace Emby.Server.Implementations.IO } } + /// <inheritdoc /> public void ReportFileSystemChanged(string path) { ArgumentException.ThrowIfNullOrEmpty(path); @@ -479,31 +464,15 @@ namespace Emby.Server.Implementations.IO } } - /// <summary> - /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. - /// </summary> + /// <inheritdoc /> public void Dispose() { - Dispose(true); - GC.SuppressFinalize(this); - } - - /// <summary> - /// Releases unmanaged and - optionally - managed resources. - /// </summary> - /// <param name="disposing"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param> - protected virtual void Dispose(bool disposing) - { if (_disposed) { return; } - if (disposing) - { - Stop(); - } - + Stop(); _disposed = true; } } diff --git a/Emby.Server.Implementations/IO/LibraryMonitorStartup.cs b/Emby.Server.Implementations/IO/LibraryMonitorStartup.cs deleted file mode 100644 index c51cf0545..000000000 --- a/Emby.Server.Implementations/IO/LibraryMonitorStartup.cs +++ /dev/null @@ -1,35 +0,0 @@ -using System.Threading.Tasks; -using MediaBrowser.Controller.Library; -using MediaBrowser.Controller.Plugins; - -namespace Emby.Server.Implementations.IO -{ - /// <summary> - /// <see cref="IServerEntryPoint" /> which is responsible for starting the library monitor. - /// </summary> - public sealed class LibraryMonitorStartup : IServerEntryPoint - { - private readonly ILibraryMonitor _monitor; - - /// <summary> - /// Initializes a new instance of the <see cref="LibraryMonitorStartup"/> class. - /// </summary> - /// <param name="monitor">The library monitor.</param> - public LibraryMonitorStartup(ILibraryMonitor monitor) - { - _monitor = monitor; - } - - /// <inheritdoc /> - public Task RunAsync() - { - _monitor.Start(); - return Task.CompletedTask; - } - - /// <inheritdoc /> - public void Dispose() - { - } - } -} diff --git a/Emby.Server.Implementations/Library/LibraryManager.cs b/Emby.Server.Implementations/Library/LibraryManager.cs index 8ae913dad..13a381060 100644 --- a/Emby.Server.Implementations/Library/LibraryManager.cs +++ b/Emby.Server.Implementations/Library/LibraryManager.cs @@ -22,7 +22,6 @@ using Jellyfin.Data.Entities; using Jellyfin.Data.Enums; using Jellyfin.Extensions; using MediaBrowser.Common.Extensions; -using MediaBrowser.Common.Progress; using MediaBrowser.Controller; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Drawing; @@ -1022,7 +1021,7 @@ namespace Emby.Server.Implementations.Library // Start by just validating the children of the root, but go no further await RootFolder.ValidateChildren( - new SimpleProgress<double>(), + new Progress<double>(), new MetadataRefreshOptions(new DirectoryService(_fileSystem)), recursive: false, cancellationToken).ConfigureAwait(false); @@ -1030,7 +1029,7 @@ namespace Emby.Server.Implementations.Library await GetUserRootFolder().RefreshMetadata(cancellationToken).ConfigureAwait(false); await GetUserRootFolder().ValidateChildren( - new SimpleProgress<double>(), + new Progress<double>(), new MetadataRefreshOptions(new DirectoryService(_fileSystem)), recursive: false, cancellationToken).ConfigureAwait(false); @@ -1048,18 +1047,14 @@ namespace Emby.Server.Implementations.Library await ValidateTopLibraryFolders(cancellationToken).ConfigureAwait(false); - var innerProgress = new ActionableProgress<double>(); - - innerProgress.RegisterAction(pct => progress.Report(pct * 0.96)); + var innerProgress = new Progress<double>(pct => progress.Report(pct * 0.96)); // Validate the entire media library await RootFolder.ValidateChildren(innerProgress, new MetadataRefreshOptions(new DirectoryService(_fileSystem)), recursive: true, cancellationToken).ConfigureAwait(false); progress.Report(96); - innerProgress = new ActionableProgress<double>(); - - innerProgress.RegisterAction(pct => progress.Report(96 + (pct * .04))); + innerProgress = new Progress<double>(pct => progress.Report(96 + (pct * .04))); await RunPostScanTasks(innerProgress, cancellationToken).ConfigureAwait(false); @@ -1081,12 +1076,10 @@ namespace Emby.Server.Implementations.Library foreach (var task in tasks) { - var innerProgress = new ActionableProgress<double>(); - // Prevent access to modified closure var currentNumComplete = numComplete; - innerProgress.RegisterAction(pct => + var innerProgress = new Progress<double>(pct => { double innerPercent = pct; innerPercent /= 100; @@ -1239,6 +1232,19 @@ namespace Emby.Server.Implementations.Library return item; } + /// <inheritdoc /> + public T GetItemById<T>(Guid id) + where T : BaseItem + { + var item = GetItemById(id); + if (item is T typedItem) + { + return typedItem; + } + + return null; + } + public List<BaseItem> GetItemList(InternalItemsQuery query, bool allowExternalContent) { if (query.Recursive && !query.ParentId.IsEmpty()) @@ -2954,7 +2960,7 @@ namespace Emby.Server.Implementations.Library Task.Run(() => { // No need to start if scanning the library because it will handle it - ValidateMediaLibrary(new SimpleProgress<double>(), CancellationToken.None); + ValidateMediaLibrary(new Progress<double>(), CancellationToken.None); }); } diff --git a/Emby.Server.Implementations/Library/LiveStreamHelper.cs b/Emby.Server.Implementations/Library/LiveStreamHelper.cs index 59d705ace..d4aeae41a 100644 --- a/Emby.Server.Implementations/Library/LiveStreamHelper.cs +++ b/Emby.Server.Implementations/Library/LiveStreamHelper.cs @@ -48,20 +48,23 @@ namespace Emby.Server.Implementations.Library if (!string.IsNullOrEmpty(cacheKey)) { - FileStream jsonStream = AsyncFile.OpenRead(cacheFilePath); try { - mediaInfo = await JsonSerializer.DeserializeAsync<MediaInfo>(jsonStream, _jsonOptions, cancellationToken).ConfigureAwait(false); + FileStream jsonStream = AsyncFile.OpenRead(cacheFilePath); - // _logger.LogDebug("Found cached media info"); + await using (jsonStream.ConfigureAwait(false)) + { + mediaInfo = await JsonSerializer.DeserializeAsync<MediaInfo>(jsonStream, _jsonOptions, cancellationToken).ConfigureAwait(false); + // _logger.LogDebug("Found cached media info"); + } } - catch (Exception ex) + catch (IOException ex) { - _logger.LogError(ex, "Error deserializing mediainfo cache"); + _logger.LogDebug(ex, "Could not open cached media info"); } - finally + catch (Exception ex) { - await jsonStream.DisposeAsync().ConfigureAwait(false); + _logger.LogError(ex, "Error opening cached media info"); } } diff --git a/Emby.Server.Implementations/Library/MediaSourceManager.cs b/Emby.Server.Implementations/Library/MediaSourceManager.cs index c38f1af91..18ada6aeb 100644 --- a/Emby.Server.Implementations/Library/MediaSourceManager.cs +++ b/Emby.Server.Implementations/Library/MediaSourceManager.cs @@ -11,6 +11,7 @@ using System.Linq; using System.Text.Json; using System.Threading; using System.Threading.Tasks; +using AsyncKeyedLock; using Jellyfin.Data.Entities; using Jellyfin.Data.Enums; using Jellyfin.Extensions; @@ -52,7 +53,7 @@ namespace Emby.Server.Implementations.Library private readonly IDirectoryService _directoryService; private readonly ConcurrentDictionary<string, ILiveStream> _openStreams = new ConcurrentDictionary<string, ILiveStream>(StringComparer.OrdinalIgnoreCase); - private readonly SemaphoreSlim _liveStreamSemaphore = new SemaphoreSlim(1, 1); + private readonly AsyncNonKeyedLocker _liveStreamLocker = new(1); private readonly JsonSerializerOptions _jsonOptions = JsonDefaults.Options; private IMediaSourceProvider[] _providers; @@ -468,12 +469,10 @@ namespace Emby.Server.Implementations.Library public async Task<Tuple<LiveStreamResponse, IDirectStreamProvider>> OpenLiveStreamInternal(LiveStreamRequest request, CancellationToken cancellationToken) { - await _liveStreamSemaphore.WaitAsync(cancellationToken).ConfigureAwait(false); - MediaSourceInfo mediaSource; ILiveStream liveStream; - try + using (await _liveStreamLocker.LockAsync(cancellationToken).ConfigureAwait(false)) { var (provider, keyId) = GetProvider(request.OpenToken); @@ -493,10 +492,6 @@ namespace Emby.Server.Implementations.Library _openStreams[mediaSource.LiveStreamId] = liveStream; } - finally - { - _liveStreamSemaphore.Release(); - } try { @@ -837,9 +832,7 @@ namespace Emby.Server.Implementations.Library { ArgumentException.ThrowIfNullOrEmpty(id); - await _liveStreamSemaphore.WaitAsync().ConfigureAwait(false); - - try + using (await _liveStreamLocker.LockAsync().ConfigureAwait(false)) { if (_openStreams.TryGetValue(id, out ILiveStream liveStream)) { @@ -858,10 +851,6 @@ namespace Emby.Server.Implementations.Library } } } - finally - { - _liveStreamSemaphore.Release(); - } } private (IMediaSourceProvider MediaSourceProvider, string KeyId) GetProvider(string key) @@ -898,7 +887,7 @@ namespace Emby.Server.Implementations.Library CloseLiveStream(key).GetAwaiter().GetResult(); } - _liveStreamSemaphore.Dispose(); + _liveStreamLocker.Dispose(); } } } diff --git a/Emby.Server.Implementations/Localization/Core/et.json b/Emby.Server.Implementations/Localization/Core/et.json index 081462407..977307b06 100644 --- a/Emby.Server.Implementations/Localization/Core/et.json +++ b/Emby.Server.Implementations/Localization/Core/et.json @@ -31,7 +31,7 @@ "VersionNumber": "Versioon {0}", "ValueSpecialEpisodeName": "Eriepisood - {0}", "ValueHasBeenAddedToLibrary": "{0} lisati meediakogusse", - "UserStartedPlayingItemWithValues": "{0} taasesitab {1} serveris {2}", + "UserStartedPlayingItemWithValues": "{0} taasesitab {1} seadmes {2}", "UserPasswordChangedWithName": "Kasutaja {0} parool muudeti", "UserLockedOutWithName": "Kasutaja {0} lukustati", "UserDeletedWithName": "Kasutaja {0} kustutati", @@ -52,7 +52,7 @@ "PluginUninstalledWithName": "{0} eemaldati", "PluginInstalledWithName": "{0} paigaldati", "Plugin": "Plugin", - "Playlists": "Pleilistid", + "Playlists": "Esitusloendid", "Photos": "Fotod", "NotificationOptionVideoPlaybackStopped": "Video taasesitus lõppes", "NotificationOptionVideoPlayback": "Video taasesitus algas", @@ -123,5 +123,7 @@ "External": "Väline", "HearingImpaired": "Kuulmispuudega", "TaskKeyframeExtractorDescription": "Eraldab videofailidest võtmekaadreid, et luua täpsemaid HLS-i esitusloendeid. See ülesanne võib kesta pikka aega.", - "TaskKeyframeExtractor": "Võtmekaadri ekstraktor" + "TaskKeyframeExtractor": "Võtmekaadri ekstraktor", + "TaskRefreshTrickplayImages": "Loo eelvaate pildid", + "TaskRefreshTrickplayImagesDescription": "Loob eelvaated videotele, kus lubatud." } diff --git a/Emby.Server.Implementations/Localization/Core/ga.json b/Emby.Server.Implementations/Localization/Core/ga.json new file mode 100644 index 000000000..28e54bff5 --- /dev/null +++ b/Emby.Server.Implementations/Localization/Core/ga.json @@ -0,0 +1,3 @@ +{ + "Albums": "Albaim" +} diff --git a/Emby.Server.Implementations/Localization/Core/ky.json b/Emby.Server.Implementations/Localization/Core/ky.json new file mode 100644 index 000000000..0967ef424 --- /dev/null +++ b/Emby.Server.Implementations/Localization/Core/ky.json @@ -0,0 +1 @@ +{} diff --git a/Emby.Server.Implementations/Localization/Core/mk.json b/Emby.Server.Implementations/Localization/Core/mk.json index cbccad87f..7ef907918 100644 --- a/Emby.Server.Implementations/Localization/Core/mk.json +++ b/Emby.Server.Implementations/Localization/Core/mk.json @@ -122,5 +122,6 @@ "TaskRefreshChapterImagesDescription": "Создава тамбнеил за видеата шти имаат поглавја.", "TaskCleanActivityLogDescription": "Избришува логови на активности постари од определеното време.", "TaskCleanActivityLog": "Избриши Лог на Активности", - "External": "Надворешен" + "External": "Надворешен", + "HearingImpaired": "Оштетен слух" } diff --git a/Emby.Server.Implementations/Localization/Core/ms.json b/Emby.Server.Implementations/Localization/Core/ms.json index a07222975..ebd3f7560 100644 --- a/Emby.Server.Implementations/Localization/Core/ms.json +++ b/Emby.Server.Implementations/Localization/Core/ms.json @@ -124,5 +124,7 @@ "External": "Luaran", "TaskOptimizeDatabase": "Optimumkan pangkalan data", "TaskKeyframeExtractor": "Ekstrak bingkai kunci", - "TaskKeyframeExtractorDescription": "Ekstrak bingkai kunci dari fail video untuk membina HLS playlist yang lebih tepat. Tugas ini mungkin perlukan masa yang panjang." + "TaskKeyframeExtractorDescription": "Ekstrak bingkai kunci dari fail video untuk membina HLS playlist yang lebih tepat. Tugas ini mungkin perlukan masa yang panjang.", + "TaskRefreshTrickplayImagesDescription": "Jana gambar prebiu Trickplay untuk video dalam perpustakaan.", + "TaskRefreshTrickplayImages": "Jana gambar Trickplay" } diff --git a/Emby.Server.Implementations/Localization/Core/or.json b/Emby.Server.Implementations/Localization/Core/or.json index 0e9d81ee8..8251c1290 100644 --- a/Emby.Server.Implementations/Localization/Core/or.json +++ b/Emby.Server.Implementations/Localization/Core/or.json @@ -1,4 +1,12 @@ { "External": "ବହିଃସ୍ଥ", - "Genres": "ଧରଣ" + "Genres": "ଧରଣ", + "Albums": "ଆଲବମଗୁଡ଼ିକ", + "Artists": "କଳାକାରଗୁଡ଼ିକ", + "Application": "ଆପ୍ଲିକେସନ", + "Books": "ବହିଗୁଡ଼ିକ", + "Channels": "ଚ୍ୟାନେଲଗୁଡ଼ିକ", + "ChapterNameValue": "ବିଭାଗ {0}", + "Collections": "ସଂଗ୍ରହଗୁଡ଼ିକ", + "Folders": "ଫୋଲ୍ଡରଗୁଡ଼ିକ" } diff --git a/Emby.Server.Implementations/Localization/Core/sl-SI.json b/Emby.Server.Implementations/Localization/Core/sl-SI.json index 1944e072c..110af11b7 100644 --- a/Emby.Server.Implementations/Localization/Core/sl-SI.json +++ b/Emby.Server.Implementations/Localization/Core/sl-SI.json @@ -124,5 +124,7 @@ "TaskKeyframeExtractor": "Ekstraktor ključnih sličic", "External": "Zunanji", "TaskKeyframeExtractorDescription": "Iz video datoteke Izvleče ključne sličice, da ustvari bolj natančne sezname predvajanja HLS. Proces lahko traja dolgo časa.", - "HearingImpaired": "Oslabljen sluh" + "HearingImpaired": "Oslabljen sluh", + "TaskRefreshTrickplayImages": "Ustvari Trickplay slike", + "TaskRefreshTrickplayImagesDescription": "Ustvari trickplay predoglede za posnetke v omogočenih knjižnicah." } diff --git a/Emby.Server.Implementations/Localization/Core/vi.json b/Emby.Server.Implementations/Localization/Core/vi.json index 44ce4ac5b..e92752c5f 100644 --- a/Emby.Server.Implementations/Localization/Core/vi.json +++ b/Emby.Server.Implementations/Localization/Core/vi.json @@ -123,5 +123,7 @@ "TaskKeyframeExtractor": "Trích Xuất Khung Hình", "TaskKeyframeExtractorDescription": "Trích xuất khung hình chính từ các tệp video để tạo danh sách phát HLS chính xác hơn. Tác vụ này có thể chạy trong một thời gian dài.", "External": "Bên ngoài", - "HearingImpaired": "Khiếm Thính" + "HearingImpaired": "Khiếm Thính", + "TaskRefreshTrickplayImages": "Tạo Ảnh Xem Trước Trickplay", + "TaskRefreshTrickplayImagesDescription": "Tạo bản xem trước trịckplay cho video trong thư viện đã bật." } diff --git a/Emby.Server.Implementations/ScheduledTasks/ScheduledTaskWorker.cs b/Emby.Server.Implementations/ScheduledTasks/ScheduledTaskWorker.cs index 1af2c96d2..efb6436ae 100644 --- a/Emby.Server.Implementations/ScheduledTasks/ScheduledTaskWorker.cs +++ b/Emby.Server.Implementations/ScheduledTasks/ScheduledTaskWorker.cs @@ -14,7 +14,6 @@ using Jellyfin.Data.Events; using Jellyfin.Extensions.Json; using MediaBrowser.Common.Configuration; using MediaBrowser.Common.Extensions; -using MediaBrowser.Common.Progress; using MediaBrowser.Model.Tasks; using Microsoft.Extensions.Logging; @@ -371,7 +370,7 @@ namespace Emby.Server.Implementations.ScheduledTasks throw new InvalidOperationException("Cannot execute a Task that is already running"); } - var progress = new SimpleProgress<double>(); + var progress = new Progress<double>(); CurrentCancellationTokenSource = new CancellationTokenSource(); diff --git a/Emby.Server.Implementations/Session/SessionManager.cs b/Emby.Server.Implementations/Session/SessionManager.cs index bbb3938dc..40b3b0339 100644 --- a/Emby.Server.Implementations/Session/SessionManager.cs +++ b/Emby.Server.Implementations/Session/SessionManager.cs @@ -394,6 +394,7 @@ namespace Emby.Server.Implementations.Session session.PlayState.SubtitleStreamIndex = info.SubtitleStreamIndex; session.PlayState.PlayMethod = info.PlayMethod; session.PlayState.RepeatMode = info.RepeatMode; + session.PlayState.PlaybackOrder = info.PlaybackOrder; session.PlaylistItemId = info.PlaylistItemId; var nowPlayingQueue = info.NowPlayingQueue; |
