diff --git a/MediaBrowser.Controller/Entities/CollectionFolder.cs b/MediaBrowser.Controller/Entities/CollectionFolder.cs
index 42c112752e..67692273d7 100644
--- a/MediaBrowser.Controller/Entities/CollectionFolder.cs
+++ b/MediaBrowser.Controller/Entities/CollectionFolder.cs
@@ -69,7 +69,7 @@ namespace MediaBrowser.Controller.Entities
/// Our children are actually just references to the ones in the physical root...
///
/// The actual children.
- protected override ConcurrentBag ActualChildren
+ protected override ConcurrentDictionary ActualChildren
{
get
{
@@ -92,7 +92,7 @@ namespace MediaBrowser.Controller.Entities
.Where(i => folderIds.Contains(i.Id))
.SelectMany(c => c.Children);
- return new ConcurrentBag(ourChildren);
+ return new ConcurrentDictionary(ourChildren.ToDictionary(i => i.Id));
}
}
}
diff --git a/MediaBrowser.Controller/Entities/Folder.cs b/MediaBrowser.Controller/Entities/Folder.cs
index bd2fa742eb..463e164256 100644
--- a/MediaBrowser.Controller/Entities/Folder.cs
+++ b/MediaBrowser.Controller/Entities/Folder.cs
@@ -1,6 +1,5 @@
using MediaBrowser.Common.Extensions;
using MediaBrowser.Common.Progress;
-using MediaBrowser.Controller.IO;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Localization;
using MediaBrowser.Controller.Persistence;
@@ -84,6 +83,50 @@ namespace MediaBrowser.Controller.Entities
return (userId + DisplayPreferencesId.ToString()).GetMD5();
}
+ ///
+ /// Adds the child.
+ ///
+ /// The item.
+ /// The cancellation token.
+ /// Task.
+ /// Unable to add + item.Name
+ public async Task AddChild(BaseItem item, CancellationToken cancellationToken)
+ {
+ if (!_children.TryAdd(item.Id, item))
+ {
+ throw new InvalidOperationException("Unable to add " + item.Name);
+ }
+
+ var newChildren = Children.ToList();
+
+ await LibraryManager.CreateItem(item, cancellationToken).ConfigureAwait(false);
+
+ await LibraryManager.SaveChildren(Id, newChildren, cancellationToken).ConfigureAwait(false);
+ }
+
+ ///
+ /// Removes the child.
+ ///
+ /// The item.
+ /// The cancellation token.
+ /// Task.
+ /// Unable to remove + item.Name
+ public Task RemoveChild(BaseItem item, CancellationToken cancellationToken)
+ {
+ BaseItem removed;
+
+ if (!_children.TryRemove(item.Id, out removed))
+ {
+ throw new InvalidOperationException("Unable to remove " + item.Name);
+ }
+
+ var newChildren = Children.ToList();
+
+ LibraryManager.ReportItemRemoved(item);
+
+ return LibraryManager.SaveChildren(Id, newChildren, cancellationToken);
+ }
+
#region Indexing
///
@@ -400,7 +443,7 @@ namespace MediaBrowser.Controller.Entities
///
/// The children
///
- private ConcurrentBag _children;
+ private ConcurrentDictionary _children;
///
/// The _children initialized
///
@@ -413,7 +456,7 @@ namespace MediaBrowser.Controller.Entities
/// Gets or sets the actual children.
///
/// The actual children.
- protected virtual ConcurrentBag ActualChildren
+ protected virtual ConcurrentDictionary ActualChildren
{
get
{
@@ -436,11 +479,11 @@ namespace MediaBrowser.Controller.Entities
///
/// The children.
[IgnoreDataMember]
- public ConcurrentBag Children
+ public IEnumerable Children
{
get
{
- return ActualChildren;
+ return ActualChildren.Values;
}
}
@@ -476,10 +519,10 @@ namespace MediaBrowser.Controller.Entities
/// We want this sychronous.
///
/// ConcurrentBag{BaseItem}.
- protected virtual ConcurrentBag LoadChildren()
+ protected virtual ConcurrentDictionary LoadChildren()
{
//just load our children from the repo - the library will be validated and maintained in other processes
- return new ConcurrentBag(GetCachedChildren());
+ return new ConcurrentDictionary(GetCachedChildren().ToDictionary(i => i.Id));
}
///
@@ -565,7 +608,7 @@ namespace MediaBrowser.Controller.Entities
progress.Report(5);
//build a dictionary of the current children we have now by Id so we can compare quickly and easily
- var currentChildren = ActualChildren.ToDictionary(i => i.Id);
+ var currentChildren = ActualChildren;
//create a list for our validated children
var validChildren = new ConcurrentBag>();
@@ -615,14 +658,15 @@ namespace MediaBrowser.Controller.Entities
//that's all the new and changed ones - now see if there are any that are missing
var itemsRemoved = currentChildren.Values.Except(newChildren).ToList();
- var childrenReplaced = false;
-
- if (itemsRemoved.Count > 0)
+ foreach (var item in itemsRemoved)
{
- ActualChildren = new ConcurrentBag(newChildren);
- childrenReplaced = true;
+ BaseItem removed;
- foreach (var item in itemsRemoved)
+ if (!_children.TryRemove(item.Id, out removed))
+ {
+ Logger.Error("Failed to remove {0}", item.Name);
+ }
+ else
{
LibraryManager.ReportItemRemoved(item);
}
@@ -632,19 +676,21 @@ namespace MediaBrowser.Controller.Entities
foreach (var item in newItems)
{
- Logger.Debug("** " + item.Name + " Added to library.");
-
- if (!childrenReplaced)
- {
- _children.Add(item);
- }
-
if (saveTasks.Count > 50)
{
await Task.WhenAll(saveTasks).ConfigureAwait(false);
saveTasks.Clear();
}
+ if (!_children.TryAdd(item.Id, item))
+ {
+ Logger.Error("Failed to add {0}", item.Name);
+ }
+ else
+ {
+ Logger.Debug("** " + item.Name + " Added to library.");
+ }
+
saveTasks.Add(LibraryManager.CreateItem(item, CancellationToken.None));
}
@@ -807,7 +853,7 @@ namespace MediaBrowser.Controller.Entities
}
// If indexed is false or the indexing function is null
- return result ?? (ActualChildren.Where(c => c.IsVisible(user)));
+ return result ?? (Children.Where(c => c.IsVisible(user)));
}
///
diff --git a/MediaBrowser.Controller/Entities/IndexFolder.cs b/MediaBrowser.Controller/Entities/IndexFolder.cs
index bbfb01fc65..165bab6328 100644
--- a/MediaBrowser.Controller/Entities/IndexFolder.cs
+++ b/MediaBrowser.Controller/Entities/IndexFolder.cs
@@ -1,4 +1,5 @@
-using MediaBrowser.Common.Extensions;
+using System;
+using MediaBrowser.Common.Extensions;
using MediaBrowser.Model.Entities;
using System.Collections.Concurrent;
using System.Collections.Generic;
@@ -109,7 +110,7 @@ namespace MediaBrowser.Controller.Entities
/// Override to return the children defined to us when we were created
///
/// The actual children.
- protected override ConcurrentBag LoadChildren()
+ protected override ConcurrentDictionary LoadChildren()
{
var originalChildSource = ChildSource.ToList();
@@ -134,7 +135,7 @@ namespace MediaBrowser.Controller.Entities
// Now - since we built the index grouping from the bottom up - we now need to properly set Parents from the top down
SetParents(this, kids.OfType());
- return new ConcurrentBag(kids);
+ return new ConcurrentDictionary(kids.ToDictionary(i => i.Id));
}
///
diff --git a/MediaBrowser.sln b/MediaBrowser.sln
index f9f5e94365..eb3251f741 100644
--- a/MediaBrowser.sln
+++ b/MediaBrowser.sln
@@ -173,4 +173,7 @@ Global
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
+ GlobalSection(Performance) = preSolution
+ HasPerformanceSessions = true
+ EndGlobalSection
EndGlobal