Skip to content

Commit 912a285

Browse files
committed
fix: dont call inner on AddToCollectionAsync to prevent deadlocks
1 parent 62cd559 commit 912a285

File tree

3 files changed

+103
-100
lines changed

3 files changed

+103
-100
lines changed

Decorators/CollectionManagerDecorator.cs

Lines changed: 52 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
using MediaBrowser.Controller.Entities;
55
using MediaBrowser.Controller.Entities.Movies;
66
using MediaBrowser.Controller.Library;
7+
using MediaBrowser.Controller.Providers;
78
using Microsoft.Extensions.Logging;
89

910
namespace Gelato.Decorators;
@@ -12,6 +13,8 @@ public sealed class CollectionManagerDecorator(
1213
ICollectionManager inner,
1314
Lazy<GelatoManager> manager,
1415
ILibraryManager libraryManager,
16+
IProviderManager providerManager,
17+
IDirectoryService directoryService,
1518
ILogger<CollectionManagerDecorator> log
1619
) : ICollectionManager
1720
{
@@ -21,11 +24,7 @@ public event EventHandler<CollectionCreatedEventArgs>? CollectionCreated
2124
remove => inner.CollectionCreated -= value;
2225
}
2326

24-
public event EventHandler<CollectionModifiedEventArgs>? ItemsAddedToCollection
25-
{
26-
add => inner.ItemsAddedToCollection += value;
27-
remove => inner.ItemsAddedToCollection -= value;
28-
}
27+
public event EventHandler<CollectionModifiedEventArgs>? ItemsAddedToCollection;
2928

3029
public event EventHandler<CollectionModifiedEventArgs>? ItemsRemovedFromCollection
3130
{
@@ -38,64 +37,68 @@ public Task<BoxSet> CreateCollectionAsync(CollectionCreationOptions options) =>
3837

3938
public async Task AddToCollectionAsync(Guid collectionId, IEnumerable<Guid> itemIds)
4039
{
41-
var guids = itemIds as Guid[] ?? itemIds.ToArray();
42-
await inner.AddToCollectionAsync(collectionId, guids).ConfigureAwait(false);
43-
4440
if (libraryManager.GetItemById(collectionId) is not BoxSet collection)
45-
return;
46-
47-
var gelatoItems = guids
48-
.Select(libraryManager.GetItemById)
49-
.Where(item => item is not null && item.IsGelato())
50-
.ToList();
51-
52-
if (gelatoItems.Count == 0)
53-
return;
41+
throw new ArgumentException(
42+
"No collection exists with the supplied collectionId " + collectionId
43+
);
5444

55-
var needsFix = false;
45+
List<BaseItem>? itemList = null;
46+
var linkedChildrenList = collection.GetLinkedChildren();
47+
var currentLinkedChildrenIds = linkedChildrenList.Select(i => i.Id).ToList();
5648

57-
for (var i = 0; i < collection.LinkedChildren.Length; i++)
49+
foreach (var id in itemIds)
5850
{
59-
var linkedChild = collection.LinkedChildren[i];
51+
var item =
52+
libraryManager.GetItemById(id)
53+
?? throw new ArgumentException("No item exists with the supplied Id " + id);
6054

61-
if (
62-
string.IsNullOrEmpty(linkedChild.LibraryItemId)
63-
&& !string.IsNullOrEmpty(linkedChild.Path)
64-
)
55+
if (!currentLinkedChildrenIds.Contains(id))
6556
{
66-
var matchingItem = gelatoItems.FirstOrDefault(item =>
67-
item?.Path == linkedChild.Path
68-
);
69-
70-
if (matchingItem != null)
71-
{
72-
log.LogDebug(
73-
"Fixing Gelato LinkedChild with path {Path} for item {Id}",
74-
linkedChild.Path,
75-
matchingItem.Id
76-
);
77-
78-
collection.LinkedChildren[i] = new LinkedChild
79-
{
80-
LibraryItemId = matchingItem.Id.ToString("N", CultureInfo.InvariantCulture),
81-
Type = LinkedChildType.Manual,
82-
};
83-
needsFix = true;
84-
}
57+
(itemList ??= []).Add(item);
58+
linkedChildrenList.Add(item);
8559
}
8660
}
8761

88-
if (needsFix)
62+
if (itemList is null)
63+
return;
64+
65+
var originalLen = collection.LinkedChildren.Length;
66+
LinkedChild[] newChildren = new LinkedChild[originalLen + itemList.Count];
67+
collection.LinkedChildren.CopyTo(newChildren, 0);
68+
69+
for (var i = 0; i < itemList.Count; i++)
8970
{
71+
var item = itemList[i];
72+
newChildren[originalLen + i] = item.IsGelato()
73+
? new LinkedChild
74+
{
75+
LibraryItemId = item.Id.ToString("N", CultureInfo.InvariantCulture),
76+
Type = LinkedChildType.Manual,
77+
}
78+
: LinkedChild.Create(item);
79+
9080
log.LogDebug(
91-
"Fixing {Count} Gelato items in collection {Name}",
92-
gelatoItems.Count,
81+
"Adding item {Id} (Gelato={IsGelato}) to collection {Name}",
82+
item.Id,
83+
item.IsGelato(),
9384
collection.Name
9485
);
95-
await collection
96-
.UpdateToRepositoryAsync(ItemUpdateType.MetadataEdit, CancellationToken.None)
97-
.ConfigureAwait(false);
9886
}
87+
88+
collection.LinkedChildren = newChildren;
89+
collection.UpdateRatingToItems(linkedChildrenList);
90+
91+
await collection
92+
.UpdateToRepositoryAsync(ItemUpdateType.MetadataEdit, CancellationToken.None)
93+
.ConfigureAwait(false);
94+
95+
providerManager.QueueRefresh(
96+
collection.Id,
97+
new MetadataRefreshOptions(directoryService) { ForceSave = true },
98+
RefreshPriority.High
99+
);
100+
101+
ItemsAddedToCollection?.Invoke(this, new CollectionModifiedEventArgs(collection, itemList));
99102
}
100103

101104
public Task RemoveFromCollectionAsync(Guid collectionId, IEnumerable<Guid> itemIds) =>

Decorators/PlaylistManagerDecorator.cs

Lines changed: 49 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
using System.Globalization;
2+
using MediaBrowser.Controller.Dto;
23
using MediaBrowser.Controller.Entities;
34
using MediaBrowser.Controller.Library;
45
using MediaBrowser.Controller.Playlists;
6+
using MediaBrowser.Controller.Providers;
57
using MediaBrowser.Model.Entities;
68
using MediaBrowser.Model.Playlists;
79
using Microsoft.Extensions.Logging;
@@ -12,6 +14,9 @@ public sealed class PlaylistManagerDecorator(
1214
IPlaylistManager inner,
1315
Lazy<GelatoManager> manager,
1416
ILibraryManager libraryManager,
17+
IUserManager userManager,
18+
IProviderManager providerManager,
19+
IDirectoryService directoryService,
1520
ILogger<PlaylistManagerDecorator> log
1621
) : IPlaylistManager
1722
{
@@ -37,64 +42,58 @@ public async Task AddItemToPlaylistAsync(
3742
Guid userId
3843
)
3944
{
40-
await inner.AddItemToPlaylistAsync(playlistId, itemIds, userId).ConfigureAwait(false);
41-
4245
if (libraryManager.GetItemById(playlistId) is not Playlist playlist)
43-
return;
46+
throw new ArgumentException("No Playlist exists with Id " + playlistId);
4447

45-
var addedItems = itemIds
46-
.Select(libraryManager.GetItemById)
47-
.Where(item => item is not null)
48-
.ToList();
48+
var user = userId == Guid.Empty ? null : userManager.GetUserById(userId);
49+
var options = new DtoOptions(false) { EnableImages = true };
4950

50-
var gelatoItems = addedItems.Where(item => item.IsGelato()).ToList();
51-
if (gelatoItems.Count == 0)
52-
return;
51+
var resolved = itemIds.Select(libraryManager.GetItemById).Where(i => i is not null);
52+
var newItems = Playlist
53+
.GetPlaylistItems(resolved, user, options)
54+
.Where(i => i.SupportsAddingToPlaylist);
55+
56+
var existingIds = playlist.LinkedChildren.Select(c => c.ItemId).ToHashSet();
57+
var toAdd = newItems.Where(i => !existingIds.Contains(i.Id)).Distinct().ToList();
5358

54-
var needsFix = false;
59+
var numDuplicates = itemIds.Count - toAdd.Count;
60+
if (numDuplicates > 0)
61+
log.LogWarning(
62+
"Ignored adding {DuplicateCount} duplicate items to playlist {PlaylistName}.",
63+
numDuplicates,
64+
playlist.Name
65+
);
5566

56-
for (int i = 0; i < playlist.LinkedChildren.Length; i++)
57-
{
58-
var linkedChild = playlist.LinkedChildren[i];
67+
if (toAdd.Count == 0)
68+
return;
5969

60-
if (
61-
string.IsNullOrEmpty(linkedChild.LibraryItemId)
62-
&& !string.IsNullOrEmpty(linkedChild.Path)
63-
)
64-
{
65-
var matchingItem = gelatoItems.FirstOrDefault(item =>
66-
item != null && item.Path == linkedChild.Path
67-
);
68-
69-
if (matchingItem != null)
70-
{
71-
log.LogDebug(
72-
"Fixing Gelato LinkedChild with path {Path} for item {Id}",
73-
linkedChild.Path,
74-
matchingItem.Id
75-
);
76-
77-
playlist.LinkedChildren[i] = new LinkedChild
70+
var newChildren = toAdd
71+
.Select(item =>
72+
item.IsGelato()
73+
? new LinkedChild
7874
{
79-
LibraryItemId = matchingItem.Id.ToString("N", CultureInfo.InvariantCulture),
75+
LibraryItemId = item.Id.ToString("N", CultureInfo.InvariantCulture),
8076
Type = LinkedChildType.Manual,
81-
};
82-
needsFix = true;
83-
}
84-
}
85-
}
86-
87-
if (needsFix)
88-
{
89-
log.LogDebug(
90-
"Fixing {Count} Gelato items in playlist {Name}",
91-
gelatoItems.Count,
92-
playlist.Name
93-
);
94-
await playlist
95-
.UpdateToRepositoryAsync(ItemUpdateType.MetadataEdit, CancellationToken.None)
96-
.ConfigureAwait(false);
97-
}
77+
}
78+
: LinkedChild.Create(item)
79+
)
80+
.ToArray();
81+
82+
playlist.LinkedChildren = [.. playlist.LinkedChildren, .. newChildren];
83+
playlist.DateLastMediaAdded = DateTime.UtcNow;
84+
85+
await playlist
86+
.UpdateToRepositoryAsync(ItemUpdateType.MetadataEdit, CancellationToken.None)
87+
.ConfigureAwait(false);
88+
89+
if (playlist.IsFile)
90+
inner.SavePlaylistFile(playlist);
91+
92+
providerManager.QueueRefresh(
93+
playlist.Id,
94+
new MetadataRefreshOptions(directoryService) { ForceSave = true },
95+
RefreshPriority.High
96+
);
9897
}
9998

10099
public Task RemoveItemFromPlaylistAsync(string playlistId, IEnumerable<string> entryIds) =>

GelatoManager.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -838,7 +838,7 @@ is not Series series
838838

839839
stopwatch.Stop();
840840

841-
_log.LogInformation(
841+
_log.LogDebug(
842842
"Sync completed for {SeriesName}: {SeasonsInserted} season(s) and {EpisodesInserted} episode(s) in {Dur}",
843843
series.Name,
844844
seasonsInserted,
@@ -1016,6 +1016,7 @@ private List<BaseItem> SaveItems(IEnumerable<BaseItem> items, Folder parent)
10161016
item.Overview = meta.Description ?? meta.Overview;
10171017
item.DateModified = DateTime.UtcNow;
10181018
item.DateLastSaved = DateTime.UtcNow;
1019+
item.DateCreated = DateTime.UtcNow;
10191020

10201021
var primary = meta.Poster ?? meta.Thumbnail;
10211022
if (!string.IsNullOrWhiteSpace(primary))

0 commit comments

Comments
 (0)