using MediaBrowser.Common.Extensions; using System.Collections.Concurrent; using System.Collections.Generic; using System.Linq; using System.Runtime.Serialization; using System.Threading; using System.Threading.Tasks; namespace MediaBrowser.Controller.Entities { /// /// Class IndexFolder /// public class IndexFolder : Folder { /// /// Initializes a new instance of the class. /// /// The parent. /// The shadow. /// The children. /// Name of the index. /// if set to true [group contents]. public IndexFolder(Folder parent, BaseItem shadow, IEnumerable children, string indexName, bool groupContents = true) { ChildSource = children; ShadowItem = shadow; GroupContents = groupContents; if (shadow == null) { Name = ForcedSortName = ""; } else { SetShadowValues(); } Id = (parent.Id.ToString() + Name).GetMBId(typeof(IndexFolder)); IndexName = indexName; Parent = parent; } /// /// Resets the parent. /// /// The parent. public void ResetParent(Folder parent) { Parent = parent; Id = (parent.Id.ToString() + Name).GetMBId(typeof(IndexFolder)); } /// /// Override this to true if class should be grouped under a container in indicies /// The container class should be defined via IndexContainer /// /// true if [group in index]; otherwise, false. [IgnoreDataMember] public override bool GroupInIndex { get { return ShadowItem != null && ShadowItem.GroupInIndex; } } /// /// Override this to return the folder that should be used to construct a container /// for this item in an index. GroupInIndex should be true as well. /// /// The index container. [IgnoreDataMember] public override Folder IndexContainer { get { return ShadowItem != null ? ShadowItem.IndexContainer : new IndexFolder(this, null, null, "", false); } } /// /// Gets or sets a value indicating whether [group contents]. /// /// true if [group contents]; otherwise, false. protected bool GroupContents { get; set; } /// /// Gets or sets the child source. /// /// The child source. protected IEnumerable ChildSource { get; set; } /// /// Gets or sets our children. /// /// Our children. protected ConcurrentBag OurChildren { get; set; } /// /// Gets the name of the index. /// /// The name of the index. public string IndexName { get; private set; } /// /// Override to return the children defined to us when we were created /// /// The actual children. protected override ConcurrentBag LoadChildren() { var originalChildSource = ChildSource.ToList(); var kids = originalChildSource; if (GroupContents) { // Recursively group up the chain var group = true; var isSubsequentLoop = false; while (group) { kids = isSubsequentLoop || kids.Any(i => i.GroupInIndex) ? GroupedSource(kids).ToList() : originalChildSource; group = kids.Any(i => i.GroupInIndex); isSubsequentLoop = true; } } // 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); } /// /// Sets the parents. /// /// The parent. /// The kids. private void SetParents(Folder parent, IEnumerable kids) { foreach (var child in kids) { child.ResetParent(parent); child.SetParents(child, child.Children.OfType()); } } /// /// Groupeds the source. /// /// The source. /// IEnumerable{BaseItem}. protected IEnumerable GroupedSource(IEnumerable source) { return source.GroupBy(i => i.IndexContainer).Select(container => new IndexFolder(this, container.Key, container, null, false)); } /// /// The item we are shadowing as a folder (Genre, Actor, etc.) /// We inherit the images and other meta from this item /// /// The shadow item. protected BaseItem ShadowItem { get; set; } /// /// Sets the shadow values. /// protected void SetShadowValues() { if (ShadowItem != null) { Name = ShadowItem.Name; ForcedSortName = ShadowItem.SortName; Genres = ShadowItem.Genres; Studios = ShadowItem.Studios; OfficialRating = ShadowItem.OfficialRating; BackdropImagePaths = ShadowItem.BackdropImagePaths; Images = ShadowItem.Images; Overview = ShadowItem.Overview; DisplayMediaType = ShadowItem.GetType().Name; } } /// /// Overrides the base implementation to refresh metadata for local trailers /// /// The cancellation token. /// if set to true [is new item]. /// if set to true [force]. /// if set to true [allow slow providers]. /// if set to true [reset resolve args]. /// Task{System.Boolean}. public override async Task RefreshMetadata(CancellationToken cancellationToken, bool forceSave = false, bool forceRefresh = false, bool allowSlowProviders = true, bool resetResolveArgs = true) { if (ShadowItem != null) { var changed = await ShadowItem.RefreshMetadata(cancellationToken, forceSave, forceRefresh, allowSlowProviders, resetResolveArgs).ConfigureAwait(false); cancellationToken.ThrowIfCancellationRequested(); SetShadowValues(); return changed; } return false; } } }