@ -6,7 +6,6 @@ using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Dto ;
using MediaBrowser.Model.Entities ;
using MediaBrowser.Model.Querying ;
using MoreLinq ;
using System ;
using System.Collections ;
using System.Collections.Generic ;
@ -15,13 +14,14 @@ using System.Linq;
using System.Runtime.Serialization ;
using System.Threading ;
using System.Threading.Tasks ;
using MediaBrowser.Model.Users ;
namespace MediaBrowser.Controller.Entities
{
/// <summary>
/// Class Folder
/// </summary>
public class Folder : BaseItem , IHasThemeMedia , IHasTags
public class Folder : BaseItem , IHasThemeMedia , IHasTags , IHasPreferredMetadataLanguage
{
public static IUserManager UserManager { get ; set ; }
public static IUserViewManager UserViewManager { get ; set ; }
@ -30,6 +30,14 @@ namespace MediaBrowser.Controller.Entities
public List < Guid > ThemeVideoIds { get ; set ; }
public List < string > Tags { get ; set ; }
public string PreferredMetadataLanguage { get ; set ; }
/// <summary>
/// Gets or sets the preferred metadata country code.
/// </summary>
/// <value>The preferred metadata country code.</value>
public string PreferredMetadataCountryCode { get ; set ; }
public Folder ( )
{
LinkedChildren = new List < LinkedChild > ( ) ;
@ -72,6 +80,19 @@ namespace MediaBrowser.Controller.Entities
}
}
protected override bool IsAllowTagFilterEnforced ( )
{
if ( this is ICollectionFolder )
{
return false ;
}
if ( this is UserView )
{
return false ;
}
return true ;
}
/// <summary>
/// Gets or sets a value indicating whether this instance is physical root.
/// </summary>
@ -98,6 +119,7 @@ namespace MediaBrowser.Controller.Entities
public virtual List < LinkedChild > LinkedChildren { get ; set ; }
[IgnoreDataMember]
protected virtual bool SupportsShortcutChildren
{
get { return true ; }
@ -237,14 +259,13 @@ namespace MediaBrowser.Controller.Entities
protected virtual IEnumerable < string > GetIndexByOptions ( )
{
return new List < string > {
{ LocalizedStrings . Instance . GetString ( "None DispPref ") } ,
{ LocalizedStrings . Instance . GetString ( "Performer DispPref ") } ,
{ LocalizedStrings . Instance . GetString ( "Genre DispPref ") } ,
{ LocalizedStrings . Instance . GetString ( "Director DispPref ") } ,
{ LocalizedStrings . Instance . GetString ( "Year DispPref ") } ,
{ LocalizedStrings . Instance . GetString ( "Studio DispPref ") }
{ "None "} ,
{ "Performer "} ,
{ "Genre "} ,
{ "Director "} ,
{ "Year "} ,
{ "Studio "}
} ;
}
/// <summary>
@ -275,7 +296,17 @@ namespace MediaBrowser.Controller.Entities
{
get
{
return _children ? ? ( _children = LoadChildrenInternal ( ) ) ;
if ( _children = = null )
{
lock ( _childrenSyncLock )
{
if ( _children = = null )
{
_children = LoadChildrenInternal ( ) ;
}
}
}
return _children ;
}
}
@ -301,14 +332,24 @@ namespace MediaBrowser.Controller.Entities
public override bool IsVisible ( User user )
{
if ( this is ICollectionFolder )
if ( this is ICollectionFolder & & ! ( this is BasePluginFolder ) )
{
if ( user . Policy . BlockedMediaFolders . Contains ( Id . ToString ( "N" ) , StringComparer . OrdinalIgnoreCase ) | |
if ( user . Policy . BlockedMediaFolders ! = null )
{
if ( user . Policy . BlockedMediaFolders . Contains ( Id . ToString ( "N" ) , StringComparer . OrdinalIgnoreCase ) | |
// Backwards compatibility
user . Policy . BlockedMediaFolders . Contains ( Name , StringComparer . OrdinalIgnoreCase ) )
// Backwards compatibility
user . Policy . BlockedMediaFolders . Contains ( Name , StringComparer . OrdinalIgnoreCase ) )
{
return false ;
}
}
else
{
return false ;
if ( ! user . Policy . EnableAllFolders & & ! user . Policy . EnabledFolders . Contains ( Id . ToString ( "N" ) , StringComparer . OrdinalIgnoreCase ) )
{
return false ;
}
}
}
@ -345,12 +386,7 @@ namespace MediaBrowser.Controller.Entities
/// <returns>Task.</returns>
public Task ValidateChildren ( IProgress < double > progress , CancellationToken cancellationToken , MetadataRefreshOptions metadataRefreshOptions , bool recursive = true )
{
return ValidateChildrenWithCancellationSupport ( progress , cancellationToken , recursive , true , metadataRefreshOptions , metadataRefreshOptions . DirectoryService ) ;
}
private Task ValidateChildrenWithCancellationSupport ( IProgress < double > progress , CancellationToken cancellationToken , bool recursive , bool refreshChildMetadata , MetadataRefreshOptions refreshOptions , IDirectoryService directoryService )
{
return ValidateChildrenInternal ( progress , cancellationToken , recursive , refreshChildMetadata , refreshOptions , directoryService ) ;
return ValidateChildrenInternal ( progress , cancellationToken , recursive , true , metadataRefreshOptions , metadataRefreshOptions . DirectoryService ) ;
}
private Dictionary < Guid , BaseItem > GetActualChildrenDictionary ( )
@ -540,50 +576,49 @@ namespace MediaBrowser.Controller.Entities
var children = ActualChildren . ToList ( ) ;
var percentages = new Dictionary < Guid , double > ( children . Count ) ;
var tasks = new List < Task > ( ) ;
var numComplete = 0 ;
var count = children . Count ;
foreach ( var child in children )
{
if ( tasks . Count > = 2 )
{
await Task . WhenAll ( tasks ) . ConfigureAwait ( false ) ;
tasks . Clear ( ) ;
}
cancellationToken . ThrowIfCancellationRequested ( ) ;
var innerProgress = new ActionableProgress < double > ( ) ;
// Avoid implicitly captured closure
var currentChild = child ;
innerProgress . RegisterAction ( p = >
if ( child . IsFolder )
{
lock ( percentages )
var innerProgress = new ActionableProgress < double > ( ) ;
// Avoid implicitly captured closure
var currentChild = child ;
innerProgress . RegisterAction ( p = >
{
percentages [ currentChild . Id ] = p / 100 ;
lock ( percentages )
{
percentages [ currentChild . Id ] = p / 100 ;
var percent = percentages . Values . Sum ( ) ;
percent / = children . C ount;
p ercent * = 100 ;
progress . Report ( p ercent) ;
}
} ) ;
var innerP ercent = percentages . Values . Sum ( ) ;
innerPercent / = c ount;
innerP ercent * = 100 ;
progress . Report ( innerP ercent) ;
}
} ) ;
if ( child . IsFolder )
{
await RefreshChildMetadata ( child , refreshOptions , recursive , innerProgress , cancellationToken )
. ConfigureAwait ( false ) ;
}
else
{
// Avoid implicitly captured closure
var taskChild = child ;
tasks . Add ( Task . Run ( async ( ) = > await RefreshChildMetadata ( taskChild , refreshOptions , false , innerProgress , cancellationToken ) . ConfigureAwait ( false ) , cancellationToken ) ) ;
await RefreshChildMetadata ( child , refreshOptions , false , new Progress < double > ( ) , cancellationToken )
. ConfigureAwait ( false ) ;
}
numComplete + + ;
double percent = numComplete ;
percent / = count ;
percent * = 100 ;
progress . Report ( percent ) ;
}
await Task . WhenAll ( tasks ) . ConfigureAwait ( false ) ;
progress . Report ( 100 ) ;
}
@ -648,7 +683,7 @@ namespace MediaBrowser.Controller.Entities
}
} ) ;
await child . ValidateChildren WithCancellationSupport ( innerProgress , cancellationToken , true , false , null , directoryService )
await child . ValidateChildren Internal ( innerProgress , cancellationToken , true , false , null , directoryService )
. ConfigureAwait ( false ) ;
}
}
@ -678,12 +713,12 @@ namespace MediaBrowser.Controller.Entities
path = System . IO . Path . GetDirectoryName ( path ) ;
}
if ( ContainsPath ( LibraryManager . Get Default VirtualFolders( ) , originalPath ) )
if ( ContainsPath ( LibraryManager . Get VirtualFolders( ) , originalPath ) )
{
return true ;
}
return UserManager. Users . Any ( user = > ContainsPath( LibraryManager . GetVirtualFolders ( user ) , originalPath ) ) ;
return ContainsPath( LibraryManager . GetVirtualFolders ( ) , originalPath ) ;
}
/// <summary>
@ -731,28 +766,6 @@ namespace MediaBrowser.Controller.Entities
return childrenItems ;
}
/// <summary>
/// Retrieves the child.
/// </summary>
/// <param name="child">The child.</param>
/// <returns>BaseItem.</returns>
private BaseItem RetrieveChild ( Guid child )
{
var item = LibraryManager . GetItemById ( child ) ;
if ( item ! = null )
{
if ( item is IByReferenceItem )
{
return LibraryManager . GetOrAddByReferenceItem ( item ) ;
}
item . Parent = this ;
}
return item ;
}
private BaseItem RetrieveChild ( BaseItem child )
{
if ( child . Id = = Guid . Empty )
@ -786,18 +799,31 @@ namespace MediaBrowser.Controller.Entities
{
var user = query . User ;
var items = query . Recursive
? GetRecursiveChildren ( user )
: GetChildren ( user , true ) ;
Func < BaseItem , bool > filter = i = > UserViewBuilder . Filter ( i , user , query , UserDataManager , LibraryManager ) ;
IEnumerable < BaseItem > items ;
if ( query . User = = null )
{
items = query . Recursive
? GetRecursiveChildren ( filter )
: Children . Where ( filter ) ;
}
else
{
items = query . Recursive
? GetRecursiveChildren ( user , filter )
: GetChildren ( user , true ) . Where ( filter ) ;
}
var result = SortAndFilter ( items , query ) ;
var result = PostFilterAndSort ( items , query ) ;
return Task . FromResult ( result ) ;
}
protected QueryResult < BaseItem > SortAndFilter ( IEnumerable < BaseItem > items , InternalItemsQuery query )
protected QueryResult < BaseItem > PostFilterAndSort ( IEnumerable < BaseItem > items , InternalItemsQuery query )
{
return UserViewBuilder . SortAndFilter ( items , this , null , query , LibraryManager , UserDataManager ) ;
return UserViewBuilder . PostFilterAndSort ( items , this , null , query , Library Manager) ;
}
/// <summary>
@ -822,11 +848,11 @@ namespace MediaBrowser.Controller.Entities
//the true root should return our users root folder children
if ( IsPhysicalRoot ) return user . RootFolder . GetChildren ( user , includeLinkedChildren ) ;
var list = new List < BaseItem > ( ) ;
var result = new Dictionary < Guid , BaseItem > ( ) ;
var hasLinkedChildren = AddChildren ToList ( user , includeLinkedChildren , lis t, includeHidden , false ) ;
AddChildren ( user , includeLinkedChildren , resul t, includeHidden , false , null ) ;
return hasLinkedChildren ? list . DistinctBy ( i = > i . Id ) . ToList ( ) : list ;
return result. Values ;
}
protected virtual IEnumerable < BaseItem > GetEligibleChildrenForRecursiveChildren ( User user )
@ -839,31 +865,30 @@ namespace MediaBrowser.Controller.Entities
/// </summary>
/// <param name="user">The user.</param>
/// <param name="includeLinkedChildren">if set to <c>true</c> [include linked children].</param>
/// <param name=" list">The lis t.</param>
/// <param name=" result">The resul t.</param>
/// <param name="includeHidden">if set to <c>true</c> [include hidden].</param>
/// <param name="recursive">if set to <c>true</c> [recursive].</param>
/// <param name="filter">The filter.</param>
/// <returns><c>true</c> if XXXX, <c>false</c> otherwise</returns>
private bool AddChildrenToList ( User user , bool includeLinkedChildren , List< BaseItem > lis t, bool includeHidden , bool recursive )
private void AddChildren ( User user , bool includeLinkedChildren , Dictionary< Guid , BaseItem > resul t, bool includeHidden , bool recursive , Func < BaseItem , bool > filter )
{
var hasLinkedChildren = false ;
foreach ( var child in GetEligibleChildrenForRecursiveChildren ( user ) )
{
if ( child . IsVisible ( user ) )
{
if ( includeHidden | | ! child . IsHiddenFromUser ( user ) )
{
list . Add ( child ) ;
if ( filter = = null | | filter ( child ) )
{
result [ child . Id ] = child ;
}
}
if ( recursive & & child . IsFolder )
{
var folder = ( Folder ) child ;
if ( folder . AddChildrenToList ( user , includeLinkedChildren , list , includeHidden , true ) )
{
hasLinkedChildren = true ;
}
folder . AddChildren ( user , includeLinkedChildren , result , includeHidden , true , filter ) ;
}
}
}
@ -874,14 +899,13 @@ namespace MediaBrowser.Controller.Entities
{
if ( child . IsVisible ( user ) )
{
hasLinkedChildren = true ;
list . Add ( child ) ;
if ( filter = = null | | filter ( child ) )
{
result [ child . Id ] = child ;
}
}
}
}
return hasLinkedChildren ;
}
/// <summary>
@ -891,18 +915,23 @@ namespace MediaBrowser.Controller.Entities
/// <param name="includeLinkedChildren">if set to <c>true</c> [include linked children].</param>
/// <returns>IEnumerable{BaseItem}.</returns>
/// <exception cref="System.ArgumentNullException"></exception>
public virtual IEnumerable < BaseItem > GetRecursiveChildren ( User user , bool includeLinkedChildren = true )
public IEnumerable < BaseItem > GetRecursiveChildren ( User user , bool includeLinkedChildren = true )
{
return GetRecursiveChildren ( user , i = > true ) ;
}
public virtual IEnumerable < BaseItem > GetRecursiveChildren ( User user , Func < BaseItem , bool > filter )
{
if ( user = = null )
{
throw new ArgumentNullException ( "user" ) ;
}
var list = new List < BaseItem > ( ) ;
var result = new Dictionary < Guid , BaseItem > ( ) ;
var hasLinkedChildren = AddChildrenToList ( user , includeLinkedChildren , list , false , true ) ;
AddChildren ( user , true , result , false , true , filter ) ;
return hasLinkedChildren ? list . DistinctBy ( i = > i . Id ) . ToList ( ) : list ;
return result. Values ;
}
/// <summary>
@ -910,10 +939,15 @@ namespace MediaBrowser.Controller.Entities
/// </summary>
/// <returns>IList{BaseItem}.</returns>
public IList < BaseItem > GetRecursiveChildren ( )
{
return GetRecursiveChildren ( i = > true ) ;
}
public IList < BaseItem > GetRecursiveChildren ( Func < BaseItem , bool > filter )
{
var list = new List < BaseItem > ( ) ;
AddChildrenToList ( list , true , null ) ;
AddChildrenToList ( list , true , filter ) ;
return list ;
}
@ -1022,6 +1056,15 @@ namespace MediaBrowser.Controller.Entities
. Where ( i = > i . Item2 ! = null ) ;
}
[IgnoreDataMember]
protected override bool SupportsOwnedItems
{
get
{
return base . SupportsOwnedItems | | SupportsShortcutChildren ;
}
}
protected override async Task < bool > RefreshedOwnedItems ( MetadataRefreshOptions options , List < FileSystemInfo > fileSystemChildren , CancellationToken cancellationToken )
{
var changesFound = false ;
@ -1126,8 +1169,7 @@ namespace MediaBrowser.Controller.Entities
bool resetPosition )
{
// Sweep through recursively and update status
var tasks = GetRecursiveChildren ( user , true )
. Where ( i = > ! i . IsFolder & & i . LocationType ! = LocationType . Virtual )
var tasks = GetRecursiveChildren ( user , i = > ! i . IsFolder & & i . LocationType ! = LocationType . Virtual )
. Select ( c = > c . MarkPlayed ( user , datePlayed , resetPosition ) ) ;
await Task . WhenAll ( tasks ) . ConfigureAwait ( false ) ;
@ -1141,8 +1183,7 @@ namespace MediaBrowser.Controller.Entities
public override async Task MarkUnplayed ( User user )
{
// Sweep through recursively and update status
var tasks = GetRecursiveChildren ( user , true )
. Where ( i = > ! i . IsFolder & & i . LocationType ! = LocationType . Virtual )
var tasks = GetRecursiveChildren ( user , i = > ! i . IsFolder & & i . LocationType ! = LocationType . Virtual )
. Select ( c = > c . MarkUnplayed ( user ) ) ;
await Task . WhenAll ( tasks ) . ConfigureAwait ( false ) ;
@ -1171,15 +1212,15 @@ namespace MediaBrowser.Controller.Entities
return this ;
}
return RecursiveChildren. FirstOrDefault ( i = > string . Equals ( i . Path , path , StringComparison . OrdinalIgnoreCase ) | |
return Get RecursiveChildren( i = > string . Equals ( i . Path , path , StringComparison . OrdinalIgnoreCase ) | |
( ! i . IsFolder & & ! i . IsInMixedFolder & & string . Equals ( i . ContainingFolderPath , path , StringComparison . OrdinalIgnoreCase ) ) | |
i . PhysicalLocations . Contains ( path , StringComparer . OrdinalIgnoreCase ) ) ;
i . PhysicalLocations . Contains ( path , StringComparer . OrdinalIgnoreCase ) )
. FirstOrDefault ( ) ;
}
public override bool IsPlayed ( User user )
{
return GetRecursiveChildren ( user )
. Where ( i = > ! i . IsFolder & & i . LocationType ! = LocationType . Virtual )
return GetRecursiveChildren ( user , i = > ! i . IsFolder & & i . LocationType ! = LocationType . Virtual )
. All ( i = > i . IsPlayed ( user ) ) ;
}
@ -1206,8 +1247,7 @@ namespace MediaBrowser.Controller.Entities
}
else
{
children = folder . GetRecursiveChildren ( user )
. Where ( i = > ! i . IsFolder & & i . LocationType ! = LocationType . Virtual ) ;
children = folder . GetRecursiveChildren ( user , i = > ! i . IsFolder & & i . LocationType ! = LocationType . Virtual ) ;
}
// Loop through each recursive child