Bond_009 4 роки тому
батько
коміт
119f64f5e7

+ 15 - 6
Emby.Server.Implementations/Channels/ChannelManager.cs

@@ -746,12 +746,21 @@ namespace Emby.Server.Implementations.Channels
             // null if came from cache
             // null if came from cache
             if (itemsResult != null)
             if (itemsResult != null)
             {
             {
-                var internalItems = itemsResult.Items
-                    .Select(i => GetChannelItemEntity(i, channelProvider, channel.Id, parentItem, cancellationToken))
-                    .ToArray();
+                var items = itemsResult.Items;
+                var itemsLen = items.Count;
+                var internalItems = new Guid[itemsLen];
+                for (int i = 0; i < itemsLen; i++)
+                {
+                    internalItems[i] = (await GetChannelItemEntityAsync(
+                        items[i],
+                        channelProvider,
+                        channel.Id,
+                        parentItem,
+                        cancellationToken).ConfigureAwait(false)).Id;
+                }
 
 
                 var existingIds = _libraryManager.GetItemIds(query);
                 var existingIds = _libraryManager.GetItemIds(query);
-                var deadIds = existingIds.Except(internalItems.Select(i => i.Id))
+                var deadIds = existingIds.Except(internalItems)
                     .ToArray();
                     .ToArray();
 
 
                 foreach (var deadId in deadIds)
                 foreach (var deadId in deadIds)
@@ -963,7 +972,7 @@ namespace Emby.Server.Implementations.Channels
             return item;
             return item;
         }
         }
 
 
-        private BaseItem GetChannelItemEntity(ChannelItemInfo info, IChannel channelProvider, Guid internalChannelId, BaseItem parentFolder, CancellationToken cancellationToken)
+        private async Task<BaseItem> GetChannelItemEntityAsync(ChannelItemInfo info, IChannel channelProvider, Guid internalChannelId, BaseItem parentFolder, CancellationToken cancellationToken)
         {
         {
             var parentFolderId = parentFolder.Id;
             var parentFolderId = parentFolder.Id;
 
 
@@ -1165,7 +1174,7 @@ namespace Emby.Server.Implementations.Channels
             }
             }
             else if (forceUpdate)
             else if (forceUpdate)
             {
             {
-                item.UpdateToRepository(ItemUpdateType.None, cancellationToken);
+                await item.UpdateToRepositoryAsync(ItemUpdateType.None, cancellationToken).ConfigureAwait(false);
             }
             }
 
 
             if ((isNew || forceUpdate) && info.Type == ChannelItemType.Media)
             if ((isNew || forceUpdate) && info.Type == ChannelItemType.Media)

+ 20 - 31
Emby.Server.Implementations/Collections/CollectionManager.cs

@@ -132,7 +132,7 @@ namespace Emby.Server.Implementations.Collections
         }
         }
 
 
         /// <inheritdoc />
         /// <inheritdoc />
-        public BoxSet CreateCollection(CollectionCreationOptions options)
+        public async Task<BoxSet> CreateCollectionAsync(CollectionCreationOptions options)
         {
         {
             var name = options.Name;
             var name = options.Name;
 
 
@@ -141,7 +141,7 @@ namespace Emby.Server.Implementations.Collections
             // This could cause it to get re-resolved as a plain folder
             // This could cause it to get re-resolved as a plain folder
             var folderName = _fileSystem.GetValidFilename(name) + " [boxset]";
             var folderName = _fileSystem.GetValidFilename(name) + " [boxset]";
 
 
-            var parentFolder = GetCollectionsFolder(true).GetAwaiter().GetResult();
+            var parentFolder = await GetCollectionsFolder(true).ConfigureAwait(false);
 
 
             if (parentFolder == null)
             if (parentFolder == null)
             {
             {
@@ -169,12 +169,16 @@ namespace Emby.Server.Implementations.Collections
 
 
                 if (options.ItemIdList.Length > 0)
                 if (options.ItemIdList.Length > 0)
                 {
                 {
-                    AddToCollection(collection.Id, options.ItemIdList, false, new MetadataRefreshOptions(new DirectoryService(_fileSystem))
-                    {
-                        // The initial adding of items is going to create a local metadata file
-                        // This will cause internet metadata to be skipped as a result
-                        MetadataRefreshMode = MetadataRefreshMode.FullRefresh
-                    });
+                    await AddToCollectionAsync(
+                        collection.Id,
+                        options.ItemIdList.Select(x => new Guid(x)),
+                        false,
+                        new MetadataRefreshOptions(new DirectoryService(_fileSystem))
+                        {
+                            // The initial adding of items is going to create a local metadata file
+                            // This will cause internet metadata to be skipped as a result
+                            MetadataRefreshMode = MetadataRefreshMode.FullRefresh
+                        }).ConfigureAwait(false);
                 }
                 }
                 else
                 else
                 {
                 {
@@ -197,18 +201,10 @@ namespace Emby.Server.Implementations.Collections
         }
         }
 
 
         /// <inheritdoc />
         /// <inheritdoc />
-        public void AddToCollection(Guid collectionId, IEnumerable<string> ids)
-        {
-            AddToCollection(collectionId, ids, true, new MetadataRefreshOptions(new DirectoryService(_fileSystem)));
-        }
+        public Task AddToCollectionAsync(Guid collectionId, IEnumerable<Guid> ids)
+            => AddToCollectionAsync(collectionId, ids, true, new MetadataRefreshOptions(new DirectoryService(_fileSystem)));
 
 
-        /// <inheritdoc />
-        public void AddToCollection(Guid collectionId, IEnumerable<Guid> ids)
-        {
-            AddToCollection(collectionId, ids.Select(i => i.ToString("N", CultureInfo.InvariantCulture)), true, new MetadataRefreshOptions(new DirectoryService(_fileSystem)));
-        }
-
-        private void AddToCollection(Guid collectionId, IEnumerable<string> ids, bool fireEvent, MetadataRefreshOptions refreshOptions)
+        private async Task AddToCollectionAsync(Guid collectionId, IEnumerable<Guid> ids, bool fireEvent, MetadataRefreshOptions refreshOptions)
         {
         {
             var collection = _libraryManager.GetItemById(collectionId) as BoxSet;
             var collection = _libraryManager.GetItemById(collectionId) as BoxSet;
             if (collection == null)
             if (collection == null)
@@ -224,15 +220,14 @@ namespace Emby.Server.Implementations.Collections
 
 
             foreach (var id in ids)
             foreach (var id in ids)
             {
             {
-                var guidId = new Guid(id);
-                var item = _libraryManager.GetItemById(guidId);
+                var item = _libraryManager.GetItemById(id);
 
 
                 if (item == null)
                 if (item == null)
                 {
                 {
                     throw new ArgumentException("No item exists with the supplied Id");
                     throw new ArgumentException("No item exists with the supplied Id");
                 }
                 }
 
 
-                if (!currentLinkedChildrenIds.Contains(guidId))
+                if (!currentLinkedChildrenIds.Contains(id))
                 {
                 {
                     itemList.Add(item);
                     itemList.Add(item);
 
 
@@ -249,7 +244,7 @@ namespace Emby.Server.Implementations.Collections
 
 
                 collection.UpdateRatingToItems(linkedChildrenList);
                 collection.UpdateRatingToItems(linkedChildrenList);
 
 
-                collection.UpdateToRepository(ItemUpdateType.MetadataEdit, CancellationToken.None);
+                await collection.UpdateToRepositoryAsync(ItemUpdateType.MetadataEdit, CancellationToken.None).ConfigureAwait(false);
 
 
                 refreshOptions.ForceSave = true;
                 refreshOptions.ForceSave = true;
                 _providerManager.QueueRefresh(collection.Id, refreshOptions, RefreshPriority.High);
                 _providerManager.QueueRefresh(collection.Id, refreshOptions, RefreshPriority.High);
@@ -266,13 +261,7 @@ namespace Emby.Server.Implementations.Collections
         }
         }
 
 
         /// <inheritdoc />
         /// <inheritdoc />
-        public void RemoveFromCollection(Guid collectionId, IEnumerable<string> itemIds)
-        {
-            RemoveFromCollection(collectionId, itemIds.Select(i => new Guid(i)));
-        }
-
-        /// <inheritdoc />
-        public void RemoveFromCollection(Guid collectionId, IEnumerable<Guid> itemIds)
+        public async Task RemoveFromCollectionAsync(Guid collectionId, IEnumerable<Guid> itemIds)
         {
         {
             var collection = _libraryManager.GetItemById(collectionId) as BoxSet;
             var collection = _libraryManager.GetItemById(collectionId) as BoxSet;
 
 
@@ -309,7 +298,7 @@ namespace Emby.Server.Implementations.Collections
                 collection.LinkedChildren = collection.LinkedChildren.Except(list).ToArray();
                 collection.LinkedChildren = collection.LinkedChildren.Except(list).ToArray();
             }
             }
 
 
-            collection.UpdateToRepository(ItemUpdateType.MetadataEdit, CancellationToken.None);
+            await collection.UpdateToRepositoryAsync(ItemUpdateType.MetadataEdit, CancellationToken.None).ConfigureAwait(false);
             _providerManager.QueueRefresh(
             _providerManager.QueueRefresh(
                 collection.Id,
                 collection.Id,
                 new MetadataRefreshOptions(new DirectoryService(_fileSystem))
                 new MetadataRefreshOptions(new DirectoryService(_fileSystem))

+ 15 - 24
Emby.Server.Implementations/Library/LibraryManager.cs

@@ -771,7 +771,7 @@ namespace Emby.Server.Implementations.Library
             if (folder.ParentId != rootFolder.Id)
             if (folder.ParentId != rootFolder.Id)
             {
             {
                 folder.ParentId = rootFolder.Id;
                 folder.ParentId = rootFolder.Id;
-                folder.UpdateToRepository(ItemUpdateType.MetadataImport, CancellationToken.None);
+                folder.UpdateToRepositoryAsync(ItemUpdateType.MetadataImport, CancellationToken.None).GetAwaiter().GetResult();
             }
             }
 
 
             rootFolder.AddVirtualChild(folder);
             rootFolder.AddVirtualChild(folder);
@@ -1868,7 +1868,8 @@ namespace Emby.Server.Implementations.Library
             return image.Path != null && !image.IsLocalFile;
             return image.Path != null && !image.IsLocalFile;
         }
         }
 
 
-        public void UpdateImages(BaseItem item, bool forceUpdate = false)
+        /// <inheritdoc />
+        public async Task UpdateImagesAsync(BaseItem item, bool forceUpdate = false)
         {
         {
             if (item == null)
             if (item == null)
             {
             {
@@ -1891,7 +1892,7 @@ namespace Emby.Server.Implementations.Library
                     try
                     try
                     {
                     {
                         var index = item.GetImageIndex(img);
                         var index = item.GetImageIndex(img);
-                        image = ConvertImageToLocal(item, img, index).ConfigureAwait(false).GetAwaiter().GetResult();
+                        image = await ConvertImageToLocal(item, img, index).ConfigureAwait(false);
                     }
                     }
                     catch (ArgumentException)
                     catch (ArgumentException)
                     {
                     {
@@ -1913,7 +1914,7 @@ namespace Emby.Server.Implementations.Library
                 }
                 }
                 catch (Exception ex)
                 catch (Exception ex)
                 {
                 {
-                    _logger.LogError(ex, "Cannnot get image dimensions for {0}", image.Path);
+                    _logger.LogError(ex, "Cannot get image dimensions for {0}", image.Path);
                     image.Width = 0;
                     image.Width = 0;
                     image.Height = 0;
                     image.Height = 0;
                     continue;
                     continue;
@@ -1943,10 +1944,8 @@ namespace Emby.Server.Implementations.Library
             RegisterItem(item);
             RegisterItem(item);
         }
         }
 
 
-        /// <summary>
-        /// Updates the item.
-        /// </summary>
-        public void UpdateItems(IReadOnlyList<BaseItem> items, BaseItem parent, ItemUpdateType updateReason, CancellationToken cancellationToken)
+        /// <inheritdoc />
+        public async Task UpdateItemsAsync(IReadOnlyList<BaseItem> items, BaseItem parent, ItemUpdateType updateReason, CancellationToken cancellationToken)
         {
         {
             foreach (var item in items)
             foreach (var item in items)
             {
             {
@@ -1957,7 +1956,7 @@ namespace Emby.Server.Implementations.Library
 
 
                 item.DateLastSaved = DateTime.UtcNow;
                 item.DateLastSaved = DateTime.UtcNow;
 
 
-                UpdateImages(item, updateReason >= ItemUpdateType.ImageUpdate);
+                await UpdateImagesAsync(item, updateReason >= ItemUpdateType.ImageUpdate).ConfigureAwait(false);
             }
             }
 
 
             _itemRepository.SaveItems(items, cancellationToken);
             _itemRepository.SaveItems(items, cancellationToken);
@@ -1991,17 +1990,9 @@ namespace Emby.Server.Implementations.Library
             }
             }
         }
         }
 
 
-        /// <summary>
-        /// Updates the item.
-        /// </summary>
-        /// <param name="item">The item.</param>
-        /// <param name="parent">The parent item.</param>
-        /// <param name="updateReason">The update reason.</param>
-        /// <param name="cancellationToken">The cancellation token.</param>
-        public void UpdateItem(BaseItem item, BaseItem parent, ItemUpdateType updateReason, CancellationToken cancellationToken)
-        {
-            UpdateItems(new[] { item }, parent, updateReason, cancellationToken);
-        }
+        /// <inheritdoc />
+        public Task UpdateItemAsync(BaseItem item, BaseItem parent, ItemUpdateType updateReason, CancellationToken cancellationToken)
+            => UpdateItemsAsync(new[] { item }, parent, updateReason, cancellationToken);
 
 
         /// <summary>
         /// <summary>
         /// Reports the item removed.
         /// Reports the item removed.
@@ -2233,7 +2224,7 @@ namespace Emby.Server.Implementations.Library
 
 
             if (refresh)
             if (refresh)
             {
             {
-                item.UpdateToRepository(ItemUpdateType.MetadataImport, CancellationToken.None);
+                item.UpdateToRepositoryAsync(ItemUpdateType.MetadataImport, CancellationToken.None).GetAwaiter().GetResult();
                 ProviderManager.QueueRefresh(item.Id, new MetadataRefreshOptions(new DirectoryService(_fileSystem)), RefreshPriority.Normal);
                 ProviderManager.QueueRefresh(item.Id, new MetadataRefreshOptions(new DirectoryService(_fileSystem)), RefreshPriority.Normal);
             }
             }
 
 
@@ -2420,7 +2411,7 @@ namespace Emby.Server.Implementations.Library
             if (!string.Equals(viewType, item.ViewType, StringComparison.OrdinalIgnoreCase))
             if (!string.Equals(viewType, item.ViewType, StringComparison.OrdinalIgnoreCase))
             {
             {
                 item.ViewType = viewType;
                 item.ViewType = viewType;
-                item.UpdateToRepository(ItemUpdateType.MetadataEdit, CancellationToken.None);
+                item.UpdateToRepositoryAsync(ItemUpdateType.MetadataEdit, CancellationToken.None).GetAwaiter().GetResult();
             }
             }
 
 
             var refresh = isNew || DateTime.UtcNow - item.DateLastRefreshed >= _viewRefreshInterval;
             var refresh = isNew || DateTime.UtcNow - item.DateLastRefreshed >= _viewRefreshInterval;
@@ -2902,7 +2893,7 @@ namespace Emby.Server.Implementations.Library
 
 
                     await ProviderManager.SaveImage(item, url, image.Type, imageIndex, CancellationToken.None).ConfigureAwait(false);
                     await ProviderManager.SaveImage(item, url, image.Type, imageIndex, CancellationToken.None).ConfigureAwait(false);
 
 
-                    item.UpdateToRepository(ItemUpdateType.ImageUpdate, CancellationToken.None);
+                    await item.UpdateToRepositoryAsync(ItemUpdateType.ImageUpdate, CancellationToken.None).ConfigureAwait(false);
 
 
                     return item.GetImageInfo(image.Type, imageIndex);
                     return item.GetImageInfo(image.Type, imageIndex);
                 }
                 }
@@ -2920,7 +2911,7 @@ namespace Emby.Server.Implementations.Library
 
 
             // Remove this image to prevent it from retrying over and over
             // Remove this image to prevent it from retrying over and over
             item.RemoveImage(image);
             item.RemoveImage(image);
-            item.UpdateToRepository(ItemUpdateType.ImageUpdate, CancellationToken.None);
+            await item.UpdateToRepositoryAsync(ItemUpdateType.ImageUpdate, CancellationToken.None).ConfigureAwait(false);
 
 
             throw new InvalidOperationException();
             throw new InvalidOperationException();
         }
         }

+ 10 - 6
Emby.Server.Implementations/LiveTv/LiveTvManager.cs

@@ -422,7 +422,7 @@ namespace Emby.Server.Implementations.LiveTv
             }
             }
         }
         }
 
 
-        private LiveTvChannel GetChannel(ChannelInfo channelInfo, string serviceName, BaseItem parentFolder, CancellationToken cancellationToken)
+        private async Task<LiveTvChannel> GetChannelAsync(ChannelInfo channelInfo, string serviceName, BaseItem parentFolder, CancellationToken cancellationToken)
         {
         {
             var parentFolderId = parentFolder.Id;
             var parentFolderId = parentFolder.Id;
             var isNew = false;
             var isNew = false;
@@ -512,7 +512,7 @@ namespace Emby.Server.Implementations.LiveTv
             }
             }
             else if (forceUpdate)
             else if (forceUpdate)
             {
             {
-                _libraryManager.UpdateItem(item, parentFolder, ItemUpdateType.MetadataImport, cancellationToken);
+                await _libraryManager.UpdateItemAsync(item, parentFolder, ItemUpdateType.MetadataImport, cancellationToken).ConfigureAwait(false);
             }
             }
 
 
             return item;
             return item;
@@ -1129,7 +1129,7 @@ namespace Emby.Server.Implementations.LiveTv
 
 
                 try
                 try
                 {
                 {
-                    var item = GetChannel(channelInfo.Item2, channelInfo.Item1, parentFolder, cancellationToken);
+                    var item = await GetChannelAsync(channelInfo.Item2, channelInfo.Item1, parentFolder, cancellationToken).ConfigureAwait(false);
 
 
                     list.Add(item);
                     list.Add(item);
                 }
                 }
@@ -1146,7 +1146,7 @@ namespace Emby.Server.Implementations.LiveTv
                 double percent = numComplete;
                 double percent = numComplete;
                 percent /= allChannelsList.Count;
                 percent /= allChannelsList.Count;
 
 
-                progress.Report(5 * percent + 10);
+                progress.Report((5 * percent) + 10);
             }
             }
 
 
             progress.Report(15);
             progress.Report(15);
@@ -1221,7 +1221,11 @@ namespace Emby.Server.Implementations.LiveTv
 
 
                     if (updatedPrograms.Count > 0)
                     if (updatedPrograms.Count > 0)
                     {
                     {
-                        _libraryManager.UpdateItems(updatedPrograms, currentChannel, ItemUpdateType.MetadataImport, cancellationToken);
+                        await _libraryManager.UpdateItemsAsync(
+                            updatedPrograms,
+                            currentChannel,
+                            ItemUpdateType.MetadataImport,
+                            cancellationToken).ConfigureAwait(false);
                     }
                     }
 
 
                     currentChannel.IsMovie = isMovie;
                     currentChannel.IsMovie = isMovie;
@@ -1234,7 +1238,7 @@ namespace Emby.Server.Implementations.LiveTv
                         currentChannel.AddTag("Kids");
                         currentChannel.AddTag("Kids");
                     }
                     }
 
 
-                    currentChannel.UpdateToRepository(ItemUpdateType.MetadataImport, cancellationToken);
+                    await currentChannel.UpdateToRepositoryAsync(ItemUpdateType.MetadataImport, cancellationToken).ConfigureAwait(false);
                     await currentChannel.RefreshMetadata(
                     await currentChannel.RefreshMetadata(
                         new MetadataRefreshOptions(new DirectoryService(_fileSystem))
                         new MetadataRefreshOptions(new DirectoryService(_fileSystem))
                         {
                         {

+ 8 - 8
Emby.Server.Implementations/Playlists/PlaylistManager.cs

@@ -184,17 +184,17 @@ namespace Emby.Server.Implementations.Playlists
             return Playlist.GetPlaylistItems(playlistMediaType, items, user, options);
             return Playlist.GetPlaylistItems(playlistMediaType, items, user, options);
         }
         }
 
 
-        public void AddToPlaylist(string playlistId, ICollection<Guid> itemIds, Guid userId)
+        public Task AddToPlaylistAsync(string playlistId, ICollection<Guid> itemIds, Guid userId)
         {
         {
             var user = userId.Equals(Guid.Empty) ? null : _userManager.GetUserById(userId);
             var user = userId.Equals(Guid.Empty) ? null : _userManager.GetUserById(userId);
 
 
-            AddToPlaylistInternal(playlistId, itemIds, user, new DtoOptions(false)
+            return AddToPlaylistInternal(playlistId, itemIds, user, new DtoOptions(false)
             {
             {
                 EnableImages = true
                 EnableImages = true
             });
             });
         }
         }
 
 
-        private void AddToPlaylistInternal(string playlistId, ICollection<Guid> newItemIds, User user, DtoOptions options)
+        private async Task AddToPlaylistInternal(string playlistId, ICollection<Guid> newItemIds, User user, DtoOptions options)
         {
         {
             // Retrieve the existing playlist
             // Retrieve the existing playlist
             var playlist = _libraryManager.GetItemById(playlistId) as Playlist
             var playlist = _libraryManager.GetItemById(playlistId) as Playlist
@@ -238,7 +238,7 @@ namespace Emby.Server.Implementations.Playlists
 
 
             // Update the playlist in the repository
             // Update the playlist in the repository
             playlist.LinkedChildren = newLinkedChildren;
             playlist.LinkedChildren = newLinkedChildren;
-            playlist.UpdateToRepository(ItemUpdateType.MetadataEdit, CancellationToken.None);
+            await playlist.UpdateToRepositoryAsync(ItemUpdateType.MetadataEdit, CancellationToken.None).ConfigureAwait(false);
 
 
             // Update the playlist on disk
             // Update the playlist on disk
             if (playlist.IsFile)
             if (playlist.IsFile)
@@ -256,7 +256,7 @@ namespace Emby.Server.Implementations.Playlists
                 RefreshPriority.High);
                 RefreshPriority.High);
         }
         }
 
 
-        public void RemoveFromPlaylist(string playlistId, IEnumerable<string> entryIds)
+        public async Task RemoveFromPlaylistAsync(string playlistId, IEnumerable<string> entryIds)
         {
         {
             if (!(_libraryManager.GetItemById(playlistId) is Playlist playlist))
             if (!(_libraryManager.GetItemById(playlistId) is Playlist playlist))
             {
             {
@@ -273,7 +273,7 @@ namespace Emby.Server.Implementations.Playlists
                 .Select(i => i.Item1)
                 .Select(i => i.Item1)
                 .ToArray();
                 .ToArray();
 
 
-            playlist.UpdateToRepository(ItemUpdateType.MetadataEdit, CancellationToken.None);
+            await playlist.UpdateToRepositoryAsync(ItemUpdateType.MetadataEdit, CancellationToken.None).ConfigureAwait(false);
 
 
             if (playlist.IsFile)
             if (playlist.IsFile)
             {
             {
@@ -289,7 +289,7 @@ namespace Emby.Server.Implementations.Playlists
                 RefreshPriority.High);
                 RefreshPriority.High);
         }
         }
 
 
-        public void MoveItem(string playlistId, string entryId, int newIndex)
+        public async Task MoveItemAsync(string playlistId, string entryId, int newIndex)
         {
         {
             if (!(_libraryManager.GetItemById(playlistId) is Playlist playlist))
             if (!(_libraryManager.GetItemById(playlistId) is Playlist playlist))
             {
             {
@@ -322,7 +322,7 @@ namespace Emby.Server.Implementations.Playlists
 
 
             playlist.LinkedChildren = newList.ToArray();
             playlist.LinkedChildren = newList.ToArray();
 
 
-            playlist.UpdateToRepository(ItemUpdateType.MetadataEdit, CancellationToken.None);
+            await playlist.UpdateToRepositoryAsync(ItemUpdateType.MetadataEdit, CancellationToken.None).ConfigureAwait(false);
 
 
             if (playlist.IsFile)
             if (playlist.IsFile)
             {
             {

+ 8 - 7
Jellyfin.Api/Controllers/CollectionController.cs

@@ -1,5 +1,6 @@
 using System;
 using System;
 using System.ComponentModel.DataAnnotations;
 using System.ComponentModel.DataAnnotations;
+using System.Threading.Tasks;
 using Jellyfin.Api.Constants;
 using Jellyfin.Api.Constants;
 using Jellyfin.Api.Extensions;
 using Jellyfin.Api.Extensions;
 using Jellyfin.Api.Helpers;
 using Jellyfin.Api.Helpers;
@@ -51,7 +52,7 @@ namespace Jellyfin.Api.Controllers
         /// <returns>A <see cref="CollectionCreationOptions"/> with information about the new collection.</returns>
         /// <returns>A <see cref="CollectionCreationOptions"/> with information about the new collection.</returns>
         [HttpPost]
         [HttpPost]
         [ProducesResponseType(StatusCodes.Status200OK)]
         [ProducesResponseType(StatusCodes.Status200OK)]
-        public ActionResult<CollectionCreationResult> CreateCollection(
+        public async Task<ActionResult<CollectionCreationResult>> CreateCollection(
             [FromQuery] string? name,
             [FromQuery] string? name,
             [FromQuery] string? ids,
             [FromQuery] string? ids,
             [FromQuery] Guid? parentId,
             [FromQuery] Guid? parentId,
@@ -59,14 +60,14 @@ namespace Jellyfin.Api.Controllers
         {
         {
             var userId = _authContext.GetAuthorizationInfo(Request).UserId;
             var userId = _authContext.GetAuthorizationInfo(Request).UserId;
 
 
-            var item = _collectionManager.CreateCollection(new CollectionCreationOptions
+            var item = await _collectionManager.CreateCollectionAsync(new CollectionCreationOptions
             {
             {
                 IsLocked = isLocked,
                 IsLocked = isLocked,
                 Name = name,
                 Name = name,
                 ParentId = parentId,
                 ParentId = parentId,
                 ItemIdList = RequestHelpers.Split(ids, ',', true),
                 ItemIdList = RequestHelpers.Split(ids, ',', true),
                 UserIds = new[] { userId }
                 UserIds = new[] { userId }
-            });
+            }).ConfigureAwait(false);
 
 
             var dtoOptions = new DtoOptions().AddClientFields(Request);
             var dtoOptions = new DtoOptions().AddClientFields(Request);
 
 
@@ -87,9 +88,9 @@ namespace Jellyfin.Api.Controllers
         /// <returns>A <see cref="NoContentResult"/> indicating success.</returns>
         /// <returns>A <see cref="NoContentResult"/> indicating success.</returns>
         [HttpPost("{collectionId}/Items")]
         [HttpPost("{collectionId}/Items")]
         [ProducesResponseType(StatusCodes.Status204NoContent)]
         [ProducesResponseType(StatusCodes.Status204NoContent)]
-        public ActionResult AddToCollection([FromRoute] Guid collectionId, [FromQuery, Required] string? itemIds)
+        public async Task<ActionResult> AddToCollection([FromRoute] Guid collectionId, [FromQuery, Required] string? itemIds)
         {
         {
-            _collectionManager.AddToCollection(collectionId, RequestHelpers.Split(itemIds, ',', true));
+            await _collectionManager.AddToCollectionAsync(collectionId, RequestHelpers.GetGuids(itemIds)).ConfigureAwait(true);
             return NoContent();
             return NoContent();
         }
         }
 
 
@@ -102,9 +103,9 @@ namespace Jellyfin.Api.Controllers
         /// <returns>A <see cref="NoContentResult"/> indicating success.</returns>
         /// <returns>A <see cref="NoContentResult"/> indicating success.</returns>
         [HttpDelete("{collectionId}/Items")]
         [HttpDelete("{collectionId}/Items")]
         [ProducesResponseType(StatusCodes.Status204NoContent)]
         [ProducesResponseType(StatusCodes.Status204NoContent)]
-        public ActionResult RemoveFromCollection([FromRoute] Guid collectionId, [FromQuery, Required] string? itemIds)
+        public async Task<ActionResult> RemoveFromCollection([FromRoute] Guid collectionId, [FromQuery, Required] string? itemIds)
         {
         {
-            _collectionManager.RemoveFromCollection(collectionId, RequestHelpers.Split(itemIds, ',', true));
+            await _collectionManager.RemoveFromCollectionAsync(collectionId, RequestHelpers.GetGuids(itemIds)).ConfigureAwait(false);
             return NoContent();
             return NoContent();
         }
         }
     }
     }

+ 7 - 7
Jellyfin.Api/Controllers/ImageController.cs

@@ -174,7 +174,7 @@ namespace Jellyfin.Api.Controllers
         [Authorize(Policy = Policies.RequiresElevation)]
         [Authorize(Policy = Policies.RequiresElevation)]
         [ProducesResponseType(StatusCodes.Status204NoContent)]
         [ProducesResponseType(StatusCodes.Status204NoContent)]
         [ProducesResponseType(StatusCodes.Status404NotFound)]
         [ProducesResponseType(StatusCodes.Status404NotFound)]
-        public ActionResult DeleteItemImage(
+        public async Task<ActionResult> DeleteItemImage(
             [FromRoute] Guid itemId,
             [FromRoute] Guid itemId,
             [FromRoute] ImageType imageType,
             [FromRoute] ImageType imageType,
             [FromRoute] int? imageIndex = null)
             [FromRoute] int? imageIndex = null)
@@ -185,7 +185,7 @@ namespace Jellyfin.Api.Controllers
                 return NotFound();
                 return NotFound();
             }
             }
 
 
-            item.DeleteImage(imageType, imageIndex ?? 0);
+            await item.DeleteImageAsync(imageType, imageIndex ?? 0).ConfigureAwait(false);
             return NoContent();
             return NoContent();
         }
         }
 
 
@@ -218,7 +218,7 @@ namespace Jellyfin.Api.Controllers
             // Handle image/png; charset=utf-8
             // Handle image/png; charset=utf-8
             var mimeType = Request.ContentType.Split(';').FirstOrDefault();
             var mimeType = Request.ContentType.Split(';').FirstOrDefault();
             await _providerManager.SaveImage(item, Request.Body, mimeType, imageType, null, CancellationToken.None).ConfigureAwait(false);
             await _providerManager.SaveImage(item, Request.Body, mimeType, imageType, null, CancellationToken.None).ConfigureAwait(false);
-            item.UpdateToRepository(ItemUpdateType.ImageUpdate, CancellationToken.None);
+            await item.UpdateToRepositoryAsync(ItemUpdateType.ImageUpdate, CancellationToken.None).ConfigureAwait(false);
 
 
             return NoContent();
             return NoContent();
         }
         }
@@ -237,7 +237,7 @@ namespace Jellyfin.Api.Controllers
         [Authorize(Policy = Policies.RequiresElevation)]
         [Authorize(Policy = Policies.RequiresElevation)]
         [ProducesResponseType(StatusCodes.Status204NoContent)]
         [ProducesResponseType(StatusCodes.Status204NoContent)]
         [ProducesResponseType(StatusCodes.Status404NotFound)]
         [ProducesResponseType(StatusCodes.Status404NotFound)]
-        public ActionResult UpdateItemImageIndex(
+        public async Task<ActionResult> UpdateItemImageIndex(
             [FromRoute] Guid itemId,
             [FromRoute] Guid itemId,
             [FromRoute] ImageType imageType,
             [FromRoute] ImageType imageType,
             [FromRoute] int imageIndex,
             [FromRoute] int imageIndex,
@@ -249,7 +249,7 @@ namespace Jellyfin.Api.Controllers
                 return NotFound();
                 return NotFound();
             }
             }
 
 
-            item.SwapImages(imageType, imageIndex, newIndex);
+            await item.SwapImagesAsync(imageType, imageIndex, newIndex).ConfigureAwait(false);
             return NoContent();
             return NoContent();
         }
         }
 
 
@@ -264,7 +264,7 @@ namespace Jellyfin.Api.Controllers
         [Authorize(Policy = Policies.DefaultAuthorization)]
         [Authorize(Policy = Policies.DefaultAuthorization)]
         [ProducesResponseType(StatusCodes.Status200OK)]
         [ProducesResponseType(StatusCodes.Status200OK)]
         [ProducesResponseType(StatusCodes.Status404NotFound)]
         [ProducesResponseType(StatusCodes.Status404NotFound)]
-        public ActionResult<IEnumerable<ImageInfo>> GetItemImageInfos([FromRoute] Guid itemId)
+        public async Task<ActionResult<IEnumerable<ImageInfo>>> GetItemImageInfos([FromRoute] Guid itemId)
         {
         {
             var item = _libraryManager.GetItemById(itemId);
             var item = _libraryManager.GetItemById(itemId);
             if (item == null)
             if (item == null)
@@ -281,7 +281,7 @@ namespace Jellyfin.Api.Controllers
                 return list;
                 return list;
             }
             }
 
 
-            _libraryManager.UpdateImages(item); // this makes sure dimensions and hashes are correct
+            await _libraryManager.UpdateImagesAsync(item).ConfigureAwait(false); // this makes sure dimensions and hashes are correct
 
 
             foreach (var image in itemImages)
             foreach (var image in itemImages)
             {
             {

+ 4 - 3
Jellyfin.Api/Controllers/ItemUpdateController.cs

@@ -3,6 +3,7 @@ using System.Collections.Generic;
 using System.ComponentModel.DataAnnotations;
 using System.ComponentModel.DataAnnotations;
 using System.Linq;
 using System.Linq;
 using System.Threading;
 using System.Threading;
+using System.Threading.Tasks;
 using Jellyfin.Api.Constants;
 using Jellyfin.Api.Constants;
 using MediaBrowser.Controller.Configuration;
 using MediaBrowser.Controller.Configuration;
 using MediaBrowser.Controller.Entities;
 using MediaBrowser.Controller.Entities;
@@ -67,7 +68,7 @@ namespace Jellyfin.Api.Controllers
         [HttpPost("Items/{itemId}")]
         [HttpPost("Items/{itemId}")]
         [ProducesResponseType(StatusCodes.Status204NoContent)]
         [ProducesResponseType(StatusCodes.Status204NoContent)]
         [ProducesResponseType(StatusCodes.Status404NotFound)]
         [ProducesResponseType(StatusCodes.Status404NotFound)]
-        public ActionResult UpdateItem([FromRoute] Guid itemId, [FromBody, Required] BaseItemDto request)
+        public async Task<ActionResult> UpdateItem([FromRoute] Guid itemId, [FromBody, Required] BaseItemDto request)
         {
         {
             var item = _libraryManager.GetItemById(itemId);
             var item = _libraryManager.GetItemById(itemId);
             if (item == null)
             if (item == null)
@@ -101,7 +102,7 @@ namespace Jellyfin.Api.Controllers
 
 
             item.OnMetadataChanged();
             item.OnMetadataChanged();
 
 
-            item.UpdateToRepository(ItemUpdateType.MetadataEdit, CancellationToken.None);
+            await item.UpdateToRepositoryAsync(ItemUpdateType.MetadataEdit, CancellationToken.None).ConfigureAwait(false);
 
 
             if (isLockedChanged && item.IsFolder)
             if (isLockedChanged && item.IsFolder)
             {
             {
@@ -110,7 +111,7 @@ namespace Jellyfin.Api.Controllers
                 foreach (var child in folder.GetRecursiveChildren())
                 foreach (var child in folder.GetRecursiveChildren())
                 {
                 {
                     child.IsLocked = newLockData;
                     child.IsLocked = newLockData;
-                    child.UpdateToRepository(ItemUpdateType.MetadataEdit, CancellationToken.None);
+                    await child.UpdateToRepositoryAsync(ItemUpdateType.MetadataEdit, CancellationToken.None).ConfigureAwait(false);
                 }
                 }
             }
             }
 
 

+ 6 - 6
Jellyfin.Api/Controllers/PlaylistsController.cs

@@ -83,12 +83,12 @@ namespace Jellyfin.Api.Controllers
         /// <returns>An <see cref="NoContentResult"/> on success.</returns>
         /// <returns>An <see cref="NoContentResult"/> on success.</returns>
         [HttpPost("{playlistId}/Items")]
         [HttpPost("{playlistId}/Items")]
         [ProducesResponseType(StatusCodes.Status204NoContent)]
         [ProducesResponseType(StatusCodes.Status204NoContent)]
-        public ActionResult AddToPlaylist(
+        public async Task<ActionResult> AddToPlaylist(
             [FromRoute] string? playlistId,
             [FromRoute] string? playlistId,
             [FromQuery] string? ids,
             [FromQuery] string? ids,
             [FromQuery] Guid? userId)
             [FromQuery] Guid? userId)
         {
         {
-            _playlistManager.AddToPlaylist(playlistId, RequestHelpers.GetGuids(ids), userId ?? Guid.Empty);
+            await _playlistManager.AddToPlaylistAsync(playlistId, RequestHelpers.GetGuids(ids), userId ?? Guid.Empty).ConfigureAwait(false);
             return NoContent();
             return NoContent();
         }
         }
 
 
@@ -102,12 +102,12 @@ namespace Jellyfin.Api.Controllers
         /// <returns>An <see cref="NoContentResult"/> on success.</returns>
         /// <returns>An <see cref="NoContentResult"/> on success.</returns>
         [HttpPost("{playlistId}/Items/{itemId}/Move/{newIndex}")]
         [HttpPost("{playlistId}/Items/{itemId}/Move/{newIndex}")]
         [ProducesResponseType(StatusCodes.Status204NoContent)]
         [ProducesResponseType(StatusCodes.Status204NoContent)]
-        public ActionResult MoveItem(
+        public async Task<ActionResult> MoveItem(
             [FromRoute] string? playlistId,
             [FromRoute] string? playlistId,
             [FromRoute] string? itemId,
             [FromRoute] string? itemId,
             [FromRoute] int newIndex)
             [FromRoute] int newIndex)
         {
         {
-            _playlistManager.MoveItem(playlistId, itemId, newIndex);
+            await _playlistManager.MoveItemAsync(playlistId, itemId, newIndex).ConfigureAwait(false);
             return NoContent();
             return NoContent();
         }
         }
 
 
@@ -120,9 +120,9 @@ namespace Jellyfin.Api.Controllers
         /// <returns>An <see cref="NoContentResult"/> on success.</returns>
         /// <returns>An <see cref="NoContentResult"/> on success.</returns>
         [HttpDelete("{playlistId}/Items")]
         [HttpDelete("{playlistId}/Items")]
         [ProducesResponseType(StatusCodes.Status204NoContent)]
         [ProducesResponseType(StatusCodes.Status204NoContent)]
-        public ActionResult RemoveFromPlaylist([FromRoute] string? playlistId, [FromQuery] string? entryIds)
+        public async Task<ActionResult> RemoveFromPlaylist([FromRoute] string? playlistId, [FromQuery] string? entryIds)
         {
         {
-            _playlistManager.RemoveFromPlaylist(playlistId, RequestHelpers.Split(entryIds, ',', true));
+            await _playlistManager.RemoveFromPlaylistAsync(playlistId, RequestHelpers.Split(entryIds, ',', true)).ConfigureAwait(false);
             return NoContent();
             return NoContent();
         }
         }
 
 

+ 1 - 1
Jellyfin.Api/Controllers/RemoteImageController.cs

@@ -221,7 +221,7 @@ namespace Jellyfin.Api.Controllers
             await _providerManager.SaveImage(item, imageUrl, type, null, CancellationToken.None)
             await _providerManager.SaveImage(item, imageUrl, type, null, CancellationToken.None)
                 .ConfigureAwait(false);
                 .ConfigureAwait(false);
 
 
-            item.UpdateToRepository(ItemUpdateType.ImageUpdate, CancellationToken.None);
+            await item.UpdateToRepositoryAsync(ItemUpdateType.ImageUpdate, CancellationToken.None).ConfigureAwait(false);
             return NoContent();
             return NoContent();
         }
         }
 
 

+ 7 - 7
Jellyfin.Api/Controllers/VideosController.cs

@@ -161,7 +161,7 @@ namespace Jellyfin.Api.Controllers
         [Authorize(Policy = Policies.RequiresElevation)]
         [Authorize(Policy = Policies.RequiresElevation)]
         [ProducesResponseType(StatusCodes.Status200OK)]
         [ProducesResponseType(StatusCodes.Status200OK)]
         [ProducesResponseType(StatusCodes.Status404NotFound)]
         [ProducesResponseType(StatusCodes.Status404NotFound)]
-        public ActionResult DeleteAlternateSources([FromRoute] Guid itemId)
+        public async Task<ActionResult> DeleteAlternateSources([FromRoute] Guid itemId)
         {
         {
             var video = (Video)_libraryManager.GetItemById(itemId);
             var video = (Video)_libraryManager.GetItemById(itemId);
 
 
@@ -180,12 +180,12 @@ namespace Jellyfin.Api.Controllers
                 link.SetPrimaryVersionId(null);
                 link.SetPrimaryVersionId(null);
                 link.LinkedAlternateVersions = Array.Empty<LinkedChild>();
                 link.LinkedAlternateVersions = Array.Empty<LinkedChild>();
 
 
-                link.UpdateToRepository(ItemUpdateType.MetadataEdit, CancellationToken.None);
+                await link.UpdateToRepositoryAsync(ItemUpdateType.MetadataEdit, CancellationToken.None).ConfigureAwait(false);
             }
             }
 
 
             video.LinkedAlternateVersions = Array.Empty<LinkedChild>();
             video.LinkedAlternateVersions = Array.Empty<LinkedChild>();
             video.SetPrimaryVersionId(null);
             video.SetPrimaryVersionId(null);
-            video.UpdateToRepository(ItemUpdateType.MetadataEdit, CancellationToken.None);
+            await video.UpdateToRepositoryAsync(ItemUpdateType.MetadataEdit, CancellationToken.None).ConfigureAwait(false);
 
 
             return NoContent();
             return NoContent();
         }
         }
@@ -201,7 +201,7 @@ namespace Jellyfin.Api.Controllers
         [Authorize(Policy = Policies.RequiresElevation)]
         [Authorize(Policy = Policies.RequiresElevation)]
         [ProducesResponseType(StatusCodes.Status204NoContent)]
         [ProducesResponseType(StatusCodes.Status204NoContent)]
         [ProducesResponseType(StatusCodes.Status400BadRequest)]
         [ProducesResponseType(StatusCodes.Status400BadRequest)]
-        public ActionResult MergeVersions([FromQuery, Required] string? itemIds)
+        public async Task<ActionResult> MergeVersions([FromQuery, Required] string? itemIds)
         {
         {
             var items = RequestHelpers.Split(itemIds, ',', true)
             var items = RequestHelpers.Split(itemIds, ',', true)
                 .Select(i => _libraryManager.GetItemById(i))
                 .Select(i => _libraryManager.GetItemById(i))
@@ -239,7 +239,7 @@ namespace Jellyfin.Api.Controllers
             {
             {
                 item.SetPrimaryVersionId(primaryVersion.Id.ToString("N", CultureInfo.InvariantCulture));
                 item.SetPrimaryVersionId(primaryVersion.Id.ToString("N", CultureInfo.InvariantCulture));
 
 
-                item.UpdateToRepository(ItemUpdateType.MetadataEdit, CancellationToken.None);
+                await item.UpdateToRepositoryAsync(ItemUpdateType.MetadataEdit, CancellationToken.None).ConfigureAwait(false);
 
 
                 list.Add(new LinkedChild
                 list.Add(new LinkedChild
                 {
                 {
@@ -258,12 +258,12 @@ namespace Jellyfin.Api.Controllers
                 if (item.LinkedAlternateVersions.Length > 0)
                 if (item.LinkedAlternateVersions.Length > 0)
                 {
                 {
                     item.LinkedAlternateVersions = Array.Empty<LinkedChild>();
                     item.LinkedAlternateVersions = Array.Empty<LinkedChild>();
-                    item.UpdateToRepository(ItemUpdateType.MetadataEdit, CancellationToken.None);
+                    await item.UpdateToRepositoryAsync(ItemUpdateType.MetadataEdit, CancellationToken.None).ConfigureAwait(false);
                 }
                 }
             }
             }
 
 
             primaryVersion.LinkedAlternateVersions = list.ToArray();
             primaryVersion.LinkedAlternateVersions = list.ToArray();
-            primaryVersion.UpdateToRepository(ItemUpdateType.MetadataEdit, CancellationToken.None);
+            await primaryVersion.UpdateToRepositoryAsync(ItemUpdateType.MetadataEdit, CancellationToken.None).ConfigureAwait(false);
             return NoContent();
             return NoContent();
         }
         }
 
 

+ 6 - 6
MediaBrowser.Controller/Collections/ICollectionManager.cs

@@ -1,5 +1,6 @@
 using System;
 using System;
 using System.Collections.Generic;
 using System.Collections.Generic;
+using System.Threading.Tasks;
 using Jellyfin.Data.Entities;
 using Jellyfin.Data.Entities;
 using MediaBrowser.Controller.Entities;
 using MediaBrowser.Controller.Entities;
 using MediaBrowser.Controller.Entities.Movies;
 using MediaBrowser.Controller.Entities.Movies;
@@ -27,24 +28,23 @@ namespace MediaBrowser.Controller.Collections
         /// Creates the collection.
         /// Creates the collection.
         /// </summary>
         /// </summary>
         /// <param name="options">The options.</param>
         /// <param name="options">The options.</param>
-        BoxSet CreateCollection(CollectionCreationOptions options);
+        Task<BoxSet> CreateCollectionAsync(CollectionCreationOptions options);
 
 
         /// <summary>
         /// <summary>
         /// Adds to collection.
         /// Adds to collection.
         /// </summary>
         /// </summary>
         /// <param name="collectionId">The collection identifier.</param>
         /// <param name="collectionId">The collection identifier.</param>
         /// <param name="itemIds">The item ids.</param>
         /// <param name="itemIds">The item ids.</param>
-        void AddToCollection(Guid collectionId, IEnumerable<string> itemIds);
+        /// <returns><see cref="Task"/> representing the asynchronous operation.</returns>
+        Task AddToCollectionAsync(Guid collectionId, IEnumerable<Guid> itemIds);
 
 
         /// <summary>
         /// <summary>
         /// Removes from collection.
         /// Removes from collection.
         /// </summary>
         /// </summary>
         /// <param name="collectionId">The collection identifier.</param>
         /// <param name="collectionId">The collection identifier.</param>
         /// <param name="itemIds">The item ids.</param>
         /// <param name="itemIds">The item ids.</param>
-        void RemoveFromCollection(Guid collectionId, IEnumerable<string> itemIds);
-
-        void AddToCollection(Guid collectionId, IEnumerable<Guid> itemIds);
-        void RemoveFromCollection(Guid collectionId, IEnumerable<Guid> itemIds);
+        /// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
+        Task RemoveFromCollectionAsync(Guid collectionId, IEnumerable<Guid> itemIds);
 
 
         /// <summary>
         /// <summary>
         /// Collapses the items within box sets.
         /// Collapses the items within box sets.

+ 9 - 11
MediaBrowser.Controller/Entities/BaseItem.cs

@@ -1390,7 +1390,7 @@ namespace MediaBrowser.Controller.Entities
                         new List<FileSystemMetadata>();
                         new List<FileSystemMetadata>();
 
 
                     var ownedItemsChanged = await RefreshedOwnedItems(options, files, cancellationToken).ConfigureAwait(false);
                     var ownedItemsChanged = await RefreshedOwnedItems(options, files, cancellationToken).ConfigureAwait(false);
-                    LibraryManager.UpdateImages(this); // ensure all image properties in DB are fresh
+                    await LibraryManager.UpdateImagesAsync(this).ConfigureAwait(false); // ensure all image properties in DB are fresh
 
 
                     if (ownedItemsChanged)
                     if (ownedItemsChanged)
                     {
                     {
@@ -2279,7 +2279,7 @@ namespace MediaBrowser.Controller.Entities
         /// </summary>
         /// </summary>
         /// <param name="type">The type.</param>
         /// <param name="type">The type.</param>
         /// <param name="index">The index.</param>
         /// <param name="index">The index.</param>
-        public void DeleteImage(ImageType type, int index)
+        public async Task DeleteImageAsync(ImageType type, int index)
         {
         {
             var info = GetImageInfo(type, index);
             var info = GetImageInfo(type, index);
 
 
@@ -2297,7 +2297,7 @@ namespace MediaBrowser.Controller.Entities
                 FileSystem.DeleteFile(info.Path);
                 FileSystem.DeleteFile(info.Path);
             }
             }
 
 
-            UpdateToRepository(ItemUpdateType.ImageUpdate, CancellationToken.None);
+            await UpdateToRepositoryAsync(ItemUpdateType.ImageUpdate, CancellationToken.None).ConfigureAwait(false);
         }
         }
 
 
         public void RemoveImage(ItemImageInfo image)
         public void RemoveImage(ItemImageInfo image)
@@ -2310,10 +2310,8 @@ namespace MediaBrowser.Controller.Entities
             ImageInfos = ImageInfos.Except(deletedImages).ToArray();
             ImageInfos = ImageInfos.Except(deletedImages).ToArray();
         }
         }
 
 
-        public virtual void UpdateToRepository(ItemUpdateType updateReason, CancellationToken cancellationToken)
-        {
-            LibraryManager.UpdateItem(this, GetParent(), updateReason, cancellationToken);
-        }
+        public virtual Task UpdateToRepositoryAsync(ItemUpdateType updateReason, CancellationToken cancellationToken)
+         => LibraryManager.UpdateItemAsync(this, GetParent(), updateReason, cancellationToken);
 
 
         /// <summary>
         /// <summary>
         /// Validates that images within the item are still on the filesystem.
         /// Validates that images within the item are still on the filesystem.
@@ -2558,7 +2556,7 @@ namespace MediaBrowser.Controller.Entities
             return type == ImageType.Backdrop || type == ImageType.Screenshot || type == ImageType.Chapter;
             return type == ImageType.Backdrop || type == ImageType.Screenshot || type == ImageType.Chapter;
         }
         }
 
 
-        public void SwapImages(ImageType type, int index1, int index2)
+        public Task SwapImagesAsync(ImageType type, int index1, int index2)
         {
         {
             if (!AllowsMultipleImages(type))
             if (!AllowsMultipleImages(type))
             {
             {
@@ -2571,13 +2569,13 @@ namespace MediaBrowser.Controller.Entities
             if (info1 == null || info2 == null)
             if (info1 == null || info2 == null)
             {
             {
                 // Nothing to do
                 // Nothing to do
-                return;
+                return Task.CompletedTask;
             }
             }
 
 
             if (!info1.IsLocalFile || !info2.IsLocalFile)
             if (!info1.IsLocalFile || !info2.IsLocalFile)
             {
             {
                 // TODO: Not supported  yet
                 // TODO: Not supported  yet
-                return;
+                return Task.CompletedTask;
             }
             }
 
 
             var path1 = info1.Path;
             var path1 = info1.Path;
@@ -2594,7 +2592,7 @@ namespace MediaBrowser.Controller.Entities
             info2.Width = 0;
             info2.Width = 0;
             info2.Height = 0;
             info2.Height = 0;
 
 
-            UpdateToRepository(ItemUpdateType.ImageUpdate, CancellationToken.None);
+            return UpdateToRepositoryAsync(ItemUpdateType.ImageUpdate, CancellationToken.None);
         }
         }
 
 
         public virtual bool IsPlayed(User user)
         public virtual bool IsPlayed(User user)

+ 2 - 2
MediaBrowser.Controller/Entities/Folder.cs

@@ -350,12 +350,12 @@ namespace MediaBrowser.Controller.Entities
 
 
                         if (currentChild.UpdateFromResolvedItem(child) > ItemUpdateType.None)
                         if (currentChild.UpdateFromResolvedItem(child) > ItemUpdateType.None)
                         {
                         {
-                            currentChild.UpdateToRepository(ItemUpdateType.MetadataImport, cancellationToken);
+                            await currentChild.UpdateToRepositoryAsync(ItemUpdateType.MetadataImport, cancellationToken).ConfigureAwait(false);
                         }
                         }
                         else
                         else
                         {
                         {
                             // metadata is up-to-date; make sure DB has correct images dimensions and hash
                             // metadata is up-to-date; make sure DB has correct images dimensions and hash
-                            LibraryManager.UpdateImages(currentChild);
+                            await LibraryManager.UpdateImagesAsync(currentChild).ConfigureAwait(false);
                         }
                         }
 
 
                         continue;
                         continue;

+ 4 - 3
MediaBrowser.Controller/Entities/Video.cs

@@ -495,9 +495,10 @@ namespace MediaBrowser.Controller.Entities
             }
             }
         }
         }
 
 
-        public override void UpdateToRepository(ItemUpdateType updateReason, CancellationToken cancellationToken)
+        /// <inheritdoc />
+        public override async Task UpdateToRepositoryAsync(ItemUpdateType updateReason, CancellationToken cancellationToken)
         {
         {
-            base.UpdateToRepository(updateReason, cancellationToken);
+            await base.UpdateToRepositoryAsync(updateReason, cancellationToken).ConfigureAwait(false);
 
 
             var localAlternates = GetLocalAlternateVersionIds()
             var localAlternates = GetLocalAlternateVersionIds()
                 .Select(i => LibraryManager.GetItemById(i))
                 .Select(i => LibraryManager.GetItemById(i))
@@ -514,7 +515,7 @@ namespace MediaBrowser.Controller.Entities
                 item.Genres = Genres;
                 item.Genres = Genres;
                 item.ProviderIds = ProviderIds;
                 item.ProviderIds = ProviderIds;
 
 
-                item.UpdateToRepository(ItemUpdateType.MetadataDownload, cancellationToken);
+                await item.UpdateToRepositoryAsync(ItemUpdateType.MetadataDownload, cancellationToken).ConfigureAwait(false);
             }
             }
         }
         }
 
 

+ 33 - 8
MediaBrowser.Controller/Library/ILibraryManager.cs

@@ -72,6 +72,7 @@ namespace MediaBrowser.Controller.Library
         /// <param name="name">The name.</param>
         /// <param name="name">The name.</param>
         /// <returns>Task{Artist}.</returns>
         /// <returns>Task{Artist}.</returns>
         MusicArtist GetArtist(string name);
         MusicArtist GetArtist(string name);
+
         MusicArtist GetArtist(string name, DtoOptions options);
         MusicArtist GetArtist(string name, DtoOptions options);
         /// <summary>
         /// <summary>
         /// Gets a Studio.
         /// Gets a Studio.
@@ -124,7 +125,7 @@ namespace MediaBrowser.Controller.Library
         /// </summary>
         /// </summary>
         void QueueLibraryScan();
         void QueueLibraryScan();
 
 
-        void UpdateImages(BaseItem item, bool forceUpdate = false);
+        Task UpdateImagesAsync(BaseItem item, bool forceUpdate = false);
 
 
         /// <summary>
         /// <summary>
         /// Gets the default view.
         /// Gets the default view.
@@ -179,6 +180,7 @@ namespace MediaBrowser.Controller.Library
         /// <param name="sortOrder">The sort order.</param>
         /// <param name="sortOrder">The sort order.</param>
         /// <returns>IEnumerable{BaseItem}.</returns>
         /// <returns>IEnumerable{BaseItem}.</returns>
         IEnumerable<BaseItem> Sort(IEnumerable<BaseItem> items, User user, IEnumerable<string> sortBy, SortOrder sortOrder);
         IEnumerable<BaseItem> Sort(IEnumerable<BaseItem> items, User user, IEnumerable<string> sortBy, SortOrder sortOrder);
+
         IEnumerable<BaseItem> Sort(IEnumerable<BaseItem> items, User user, IEnumerable<ValueTuple<string, SortOrder>> orderBy);
         IEnumerable<BaseItem> Sort(IEnumerable<BaseItem> items, User user, IEnumerable<ValueTuple<string, SortOrder>> orderBy);
 
 
         /// <summary>
         /// <summary>
@@ -200,9 +202,16 @@ namespace MediaBrowser.Controller.Library
         /// <summary>
         /// <summary>
         /// Updates the item.
         /// Updates the item.
         /// </summary>
         /// </summary>
-        void UpdateItems(IReadOnlyList<BaseItem> items, BaseItem parent, ItemUpdateType updateReason, CancellationToken cancellationToken);
+        Task UpdateItemsAsync(IReadOnlyList<BaseItem> items, BaseItem parent, ItemUpdateType updateReason, CancellationToken cancellationToken);
 
 
-        void UpdateItem(BaseItem item, BaseItem parent, ItemUpdateType updateReason, CancellationToken cancellationToken);
+        /// <summary>
+        /// Updates the item.
+        /// </summary>
+        /// <param name="item">The item.</param>
+        /// <param name="parent">The parent item.</param>
+        /// <param name="updateReason">The update reason.</param>
+        /// <param name="cancellationToken">The cancellation token.</param>
+        Task UpdateItemAsync(BaseItem item, BaseItem parent, ItemUpdateType updateReason, CancellationToken cancellationToken);
 
 
         /// <summary>
         /// <summary>
         /// Retrieves the item.
         /// Retrieves the item.
@@ -317,7 +326,8 @@ namespace MediaBrowser.Controller.Library
         /// <param name="name">The name.</param>
         /// <param name="name">The name.</param>
         /// <param name="viewType">Type of the view.</param>
         /// <param name="viewType">Type of the view.</param>
         /// <param name="sortName">Name of the sort.</param>
         /// <param name="sortName">Name of the sort.</param>
-        UserView GetNamedView(string name,
+        UserView GetNamedView(
+            string name,
             string viewType,
             string viewType,
             string sortName);
             string sortName);
 
 
@@ -329,7 +339,8 @@ namespace MediaBrowser.Controller.Library
         /// <param name="viewType">Type of the view.</param>
         /// <param name="viewType">Type of the view.</param>
         /// <param name="sortName">Name of the sort.</param>
         /// <param name="sortName">Name of the sort.</param>
         /// <param name="uniqueId">The unique identifier.</param>
         /// <param name="uniqueId">The unique identifier.</param>
-        UserView GetNamedView(string name,
+        UserView GetNamedView(
+            string name,
             Guid parentId,
             Guid parentId,
             string viewType,
             string viewType,
             string sortName,
             string sortName,
@@ -341,7 +352,8 @@ namespace MediaBrowser.Controller.Library
         /// <param name="parent">The parent.</param>
         /// <param name="parent">The parent.</param>
         /// <param name="viewType">Type of the view.</param>
         /// <param name="viewType">Type of the view.</param>
         /// <param name="sortName">Name of the sort.</param>
         /// <param name="sortName">Name of the sort.</param>
-        UserView GetShadowView(BaseItem parent,
+        UserView GetShadowView(
+            BaseItem parent,
           string viewType,
           string viewType,
           string sortName);
           string sortName);
 
 
@@ -393,7 +405,9 @@ namespace MediaBrowser.Controller.Library
         /// <param name="fileSystemChildren">The file system children.</param>
         /// <param name="fileSystemChildren">The file system children.</param>
         /// <param name="directoryService">The directory service.</param>
         /// <param name="directoryService">The directory service.</param>
         /// <returns>IEnumerable&lt;Trailer&gt;.</returns>
         /// <returns>IEnumerable&lt;Trailer&gt;.</returns>
-        IEnumerable<Video> FindTrailers(BaseItem owner, List<FileSystemMetadata> fileSystemChildren,
+        IEnumerable<Video> FindTrailers(
+            BaseItem owner,
+            List<FileSystemMetadata> fileSystemChildren,
             IDirectoryService directoryService);
             IDirectoryService directoryService);
 
 
         /// <summary>
         /// <summary>
@@ -403,7 +417,9 @@ namespace MediaBrowser.Controller.Library
         /// <param name="fileSystemChildren">The file system children.</param>
         /// <param name="fileSystemChildren">The file system children.</param>
         /// <param name="directoryService">The directory service.</param>
         /// <param name="directoryService">The directory service.</param>
         /// <returns>IEnumerable&lt;Video&gt;.</returns>
         /// <returns>IEnumerable&lt;Video&gt;.</returns>
-        IEnumerable<Video> FindExtras(BaseItem owner, List<FileSystemMetadata> fileSystemChildren,
+        IEnumerable<Video> FindExtras(
+            BaseItem owner,
+            List<FileSystemMetadata> fileSystemChildren,
             IDirectoryService directoryService);
             IDirectoryService directoryService);
 
 
         /// <summary>
         /// <summary>
@@ -522,16 +538,25 @@ namespace MediaBrowser.Controller.Library
         Guid GetMusicGenreId(string name);
         Guid GetMusicGenreId(string name);
 
 
         Task AddVirtualFolder(string name, string collectionType, LibraryOptions options, bool refreshLibrary);
         Task AddVirtualFolder(string name, string collectionType, LibraryOptions options, bool refreshLibrary);
+
         Task RemoveVirtualFolder(string name, bool refreshLibrary);
         Task RemoveVirtualFolder(string name, bool refreshLibrary);
+
         void AddMediaPath(string virtualFolderName, MediaPathInfo path);
         void AddMediaPath(string virtualFolderName, MediaPathInfo path);
+
         void UpdateMediaPath(string virtualFolderName, MediaPathInfo path);
         void UpdateMediaPath(string virtualFolderName, MediaPathInfo path);
+
         void RemoveMediaPath(string virtualFolderName, string path);
         void RemoveMediaPath(string virtualFolderName, string path);
 
 
         QueryResult<(BaseItem, ItemCounts)> GetGenres(InternalItemsQuery query);
         QueryResult<(BaseItem, ItemCounts)> GetGenres(InternalItemsQuery query);
+
         QueryResult<(BaseItem, ItemCounts)> GetMusicGenres(InternalItemsQuery query);
         QueryResult<(BaseItem, ItemCounts)> GetMusicGenres(InternalItemsQuery query);
+
         QueryResult<(BaseItem, ItemCounts)> GetStudios(InternalItemsQuery query);
         QueryResult<(BaseItem, ItemCounts)> GetStudios(InternalItemsQuery query);
+
         QueryResult<(BaseItem, ItemCounts)> GetArtists(InternalItemsQuery query);
         QueryResult<(BaseItem, ItemCounts)> GetArtists(InternalItemsQuery query);
+
         QueryResult<(BaseItem, ItemCounts)> GetAlbumArtists(InternalItemsQuery query);
         QueryResult<(BaseItem, ItemCounts)> GetAlbumArtists(InternalItemsQuery query);
+
         QueryResult<(BaseItem, ItemCounts)> GetAllArtists(InternalItemsQuery query);
         QueryResult<(BaseItem, ItemCounts)> GetAllArtists(InternalItemsQuery query);
 
 
         int GetCount(InternalItemsQuery query);
         int GetCount(InternalItemsQuery query);

+ 3 - 3
MediaBrowser.Controller/Playlists/IPlaylistManager.cs

@@ -29,7 +29,7 @@ namespace MediaBrowser.Controller.Playlists
         /// <param name="itemIds">The item ids.</param>
         /// <param name="itemIds">The item ids.</param>
         /// <param name="userId">The user identifier.</param>
         /// <param name="userId">The user identifier.</param>
         /// <returns>Task.</returns>
         /// <returns>Task.</returns>
-        void AddToPlaylist(string playlistId, ICollection<Guid> itemIds, Guid userId);
+        Task AddToPlaylistAsync(string playlistId, ICollection<Guid> itemIds, Guid userId);
 
 
         /// <summary>
         /// <summary>
         /// Removes from playlist.
         /// Removes from playlist.
@@ -37,7 +37,7 @@ namespace MediaBrowser.Controller.Playlists
         /// <param name="playlistId">The playlist identifier.</param>
         /// <param name="playlistId">The playlist identifier.</param>
         /// <param name="entryIds">The entry ids.</param>
         /// <param name="entryIds">The entry ids.</param>
         /// <returns>Task.</returns>
         /// <returns>Task.</returns>
-        void RemoveFromPlaylist(string playlistId, IEnumerable<string> entryIds);
+        Task RemoveFromPlaylistAsync(string playlistId, IEnumerable<string> entryIds);
 
 
         /// <summary>
         /// <summary>
         /// Gets the playlists folder.
         /// Gets the playlists folder.
@@ -53,6 +53,6 @@ namespace MediaBrowser.Controller.Playlists
         /// <param name="entryId">The entry identifier.</param>
         /// <param name="entryId">The entry identifier.</param>
         /// <param name="newIndex">The new index.</param>
         /// <param name="newIndex">The new index.</param>
         /// <returns>Task.</returns>
         /// <returns>Task.</returns>
-        void MoveItem(string playlistId, string entryId, int newIndex);
+        Task MoveItemAsync(string playlistId, string entryId, int newIndex);
     }
     }
 }
 }

+ 3 - 2
MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs

@@ -3,6 +3,7 @@
 using System;
 using System;
 using System.Collections.Generic;
 using System.Collections.Generic;
 using System.Diagnostics;
 using System.Diagnostics;
+using System.Globalization;
 using System.Linq;
 using System.Linq;
 using System.Text.RegularExpressions;
 using System.Text.RegularExpressions;
 using Microsoft.Extensions.Logging;
 using Microsoft.Extensions.Logging;
@@ -258,8 +259,8 @@ namespace MediaBrowser.MediaEncoding.Encoder
                 RegexOptions.Multiline))
                 RegexOptions.Multiline))
             {
             {
                 var version = new Version(
                 var version = new Version(
-                    int.Parse(match.Groups["major"].Value),
-                    int.Parse(match.Groups["minor"].Value));
+                    int.Parse(match.Groups["major"].Value, CultureInfo.InvariantCulture),
+                    int.Parse(match.Groups["minor"].Value, CultureInfo.InvariantCulture));
 
 
                 map.Add(match.Groups["name"].Value, version);
                 map.Add(match.Groups["name"].Value, version);
             }
             }

+ 2 - 2
MediaBrowser.Providers/Manager/MetadataService.cs

@@ -212,7 +212,7 @@ namespace MediaBrowser.Providers.Manager
                 await SavePeopleMetadataAsync(result.People, libraryOptions, cancellationToken).ConfigureAwait(false);
                 await SavePeopleMetadataAsync(result.People, libraryOptions, cancellationToken).ConfigureAwait(false);
             }
             }
 
 
-            result.Item.UpdateToRepository(reason, cancellationToken);
+            await result.Item.UpdateToRepositoryAsync(reason, cancellationToken).ConfigureAwait(false);
         }
         }
 
 
         private async Task SavePeopleMetadataAsync(List<PersonInfo> people, LibraryOptions libraryOptions, CancellationToken cancellationToken)
         private async Task SavePeopleMetadataAsync(List<PersonInfo> people, LibraryOptions libraryOptions, CancellationToken cancellationToken)
@@ -246,7 +246,7 @@ namespace MediaBrowser.Providers.Manager
 
 
                     if (saveEntity)
                     if (saveEntity)
                     {
                     {
-                        personEntity.UpdateToRepository(updateType, cancellationToken);
+                        await personEntity.UpdateToRepositoryAsync(updateType, cancellationToken).ConfigureAwait(false);
                     }
                     }
                 }
                 }
             }
             }

+ 2 - 2
MediaBrowser.Providers/TV/DummySeasonProvider.cs

@@ -87,7 +87,7 @@ namespace MediaBrowser.Providers.TV
                 else if (existingSeason.IsVirtualItem)
                 else if (existingSeason.IsVirtualItem)
                 {
                 {
                     existingSeason.IsVirtualItem = false;
                     existingSeason.IsVirtualItem = false;
-                    existingSeason.UpdateToRepository(ItemUpdateType.MetadataEdit, cancellationToken);
+                    await existingSeason.UpdateToRepositoryAsync(ItemUpdateType.MetadataEdit, cancellationToken).ConfigureAwait(false);
                     seasons = null;
                     seasons = null;
                 }
                 }
             }
             }
@@ -113,7 +113,7 @@ namespace MediaBrowser.Providers.TV
                 else if (existingSeason.IsVirtualItem)
                 else if (existingSeason.IsVirtualItem)
                 {
                 {
                     existingSeason.IsVirtualItem = false;
                     existingSeason.IsVirtualItem = false;
-                    existingSeason.UpdateToRepository(ItemUpdateType.MetadataEdit, cancellationToken);
+                    await existingSeason.UpdateToRepositoryAsync(ItemUpdateType.MetadataEdit, cancellationToken).ConfigureAwait(false);
                     seasons = null;
                     seasons = null;
                 }
                 }
             }
             }