using MediaBrowser.Common.Extensions; using MediaBrowser.Model.Entities; using MoreLinq; 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 { /// <summary> /// Class IndexFolder /// </summary> public class IndexFolder : Folder { /// <summary> /// Initializes a new instance of the <see cref="IndexFolder" /> class. /// </summary> /// <param name="parent">The parent.</param> /// <param name="shadow">The shadow.</param> /// <param name="children">The children.</param> /// <param name="indexName">Name of the index.</param> /// <param name="groupContents">if set to <c>true</c> [group contents].</param> public IndexFolder(Folder parent, BaseItem shadow, IEnumerable<BaseItem> children, string indexName, bool groupContents = true) { ChildSource = children; ShadowItem = shadow; GroupContents = groupContents; if (shadow == null) { Name = ForcedSortName = "<Unknown>"; } else { SetShadowValues(); } Id = (parent.Id.ToString() + Name).GetMBId(typeof(IndexFolder)); IndexName = indexName; Parent = parent; LoadSavedChildren(); } /// <summary> /// Resets the parent. /// </summary> /// <param name="parent">The parent.</param> public void ResetParent(Folder parent) { Parent = parent; Id = (parent.Id.ToString() + Name).GetMBId(typeof(IndexFolder)); } /// <summary> /// Override this to true if class should be grouped under a container in indicies /// The container class should be defined via IndexContainer /// </summary> /// <value><c>true</c> if [group in index]; otherwise, <c>false</c>.</value> [IgnoreDataMember] public override bool GroupInIndex { get { return ShadowItem != null && ShadowItem.GroupInIndex; } } public override LocationType LocationType { get { return LocationType.Virtual; } } /// <summary> /// 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. /// </summary> /// <value>The index container.</value> [IgnoreDataMember] public override Folder IndexContainer { get { return ShadowItem != null ? ShadowItem.IndexContainer : new IndexFolder(this, null, null, "<Unknown>", false); } } /// <summary> /// Gets or sets a value indicating whether [group contents]. /// </summary> /// <value><c>true</c> if [group contents]; otherwise, <c>false</c>.</value> protected bool GroupContents { get; set; } /// <summary> /// Gets or sets the child source. /// </summary> /// <value>The child source.</value> protected IEnumerable<BaseItem> ChildSource { get; set; } /// <summary> /// Gets or sets our children. /// </summary> /// <value>Our children.</value> protected ConcurrentBag<BaseItem> OurChildren { get; set; } /// <summary> /// Gets the name of the index. /// </summary> /// <value>The name of the index.</value> public string IndexName { get; private set; } /// <summary> /// Override to return the children defined to us when we were created /// </summary> /// <value>The actual children.</value> protected override IEnumerable<BaseItem> 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<IndexFolder>()); return kids.DistinctBy(i => i.Id); } /// <summary> /// Sets the parents. /// </summary> /// <param name="parent">The parent.</param> /// <param name="kids">The kids.</param> private void SetParents(Folder parent, IEnumerable<IndexFolder> kids) { foreach (var child in kids) { child.ResetParent(parent); child.SetParents(child, child.Children.OfType<IndexFolder>()); } } /// <summary> /// Groupeds the source. /// </summary> /// <param name="source">The source.</param> /// <returns>IEnumerable{BaseItem}.</returns> protected IEnumerable<BaseItem> GroupedSource(IEnumerable<BaseItem> source) { return source.GroupBy(i => i.IndexContainer).Select(container => new IndexFolder(this, container.Key, container, null, false)); } /// <summary> /// The item we are shadowing as a folder (Genre, Actor, etc.) /// We inherit the images and other meta from this item /// </summary> /// <value>The shadow item.</value> protected BaseItem ShadowItem { get; set; } /// <summary> /// Sets the shadow values. /// </summary> protected void SetShadowValues() { if (ShadowItem != null) { Name = ShadowItem.Name; ForcedSortName = ShadowItem.SortName; Genres = ShadowItem.Genres; Studios = ShadowItem.Studios; OfficialRating = ShadowItem.OfficialRatingForComparison; BackdropImagePaths = ShadowItem.BackdropImagePaths; Images = ShadowItem.Images; Overview = ShadowItem.Overview; DisplayMediaType = ShadowItem.GetType().Name; } } /// <summary> /// Overrides the base implementation to refresh metadata for local trailers /// </summary> /// <param name="cancellationToken">The cancellation token.</param> /// <param name="forceSave">if set to <c>true</c> [is new item].</param> /// <param name="forceRefresh">if set to <c>true</c> [force].</param> /// <param name="allowSlowProviders">if set to <c>true</c> [allow slow providers].</param> /// <param name="resetResolveArgs">if set to <c>true</c> [reset resolve args].</param> /// <returns>Task{System.Boolean}.</returns> public override Task<bool> RefreshMetadata(CancellationToken cancellationToken, bool forceSave = false, bool forceRefresh = false, bool allowSlowProviders = true, bool resetResolveArgs = true) { // We should never get in here since these are not part of the library return Task.FromResult(false); } } }