2019-11-01 17:38:54 +00:00
#pragma warning disable CS1591
2019-01-13 19:21:32 +00:00
using System ;
using System.Collections.Concurrent ;
using System.Collections.Generic ;
using System.Globalization ;
using System.IO ;
using System.Linq ;
using System.Net ;
using System.Threading ;
using System.Threading.Tasks ;
using Emby.Naming.Audio ;
using Emby.Naming.Common ;
using Emby.Naming.TV ;
using Emby.Naming.Video ;
using Emby.Server.Implementations.Library.Resolvers ;
using Emby.Server.Implementations.Library.Validators ;
using Emby.Server.Implementations.Playlists ;
using Emby.Server.Implementations.ScheduledTasks ;
2019-01-06 20:50:43 +00:00
using MediaBrowser.Common.Extensions ;
2013-04-22 04:38:03 +00:00
using MediaBrowser.Common.Progress ;
2019-01-13 19:21:32 +00:00
using MediaBrowser.Controller ;
2013-03-04 05:43:06 +00:00
using MediaBrowser.Controller.Configuration ;
2019-01-13 19:21:32 +00:00
using MediaBrowser.Controller.Dto ;
2013-02-21 01:33:05 +00:00
using MediaBrowser.Controller.Entities ;
2013-04-21 01:02:16 +00:00
using MediaBrowser.Controller.Entities.Audio ;
2013-05-24 15:01:53 +00:00
using MediaBrowser.Controller.Entities.TV ;
2013-02-21 01:33:05 +00:00
using MediaBrowser.Controller.IO ;
2013-02-28 19:32:41 +00:00
using MediaBrowser.Controller.Library ;
2019-01-13 19:21:32 +00:00
using MediaBrowser.Controller.LiveTv ;
2020-02-17 13:56:31 +00:00
using MediaBrowser.Controller.MediaEncoding ;
2013-04-08 15:55:53 +00:00
using MediaBrowser.Controller.Persistence ;
2014-02-02 13:36:31 +00:00
using MediaBrowser.Controller.Providers ;
2013-03-03 16:53:58 +00:00
using MediaBrowser.Controller.Resolvers ;
2013-03-10 04:22:36 +00:00
using MediaBrowser.Controller.Sorting ;
2013-03-12 05:40:24 +00:00
using MediaBrowser.Model.Configuration ;
2020-02-17 13:56:31 +00:00
using MediaBrowser.Model.Dlna ;
2016-06-17 13:06:13 +00:00
using MediaBrowser.Model.Dto ;
2019-01-13 19:21:32 +00:00
using MediaBrowser.Model.Entities ;
using MediaBrowser.Model.IO ;
2015-11-13 20:53:29 +00:00
using MediaBrowser.Model.Library ;
2016-01-24 04:21:31 +00:00
using MediaBrowser.Model.Net ;
2019-01-13 19:21:32 +00:00
using MediaBrowser.Model.Querying ;
2016-10-23 19:47:34 +00:00
using MediaBrowser.Model.Tasks ;
2018-09-12 17:26:21 +00:00
using MediaBrowser.Providers.MediaInfo ;
2019-01-13 19:21:32 +00:00
using Microsoft.Extensions.Logging ;
using SortOrder = MediaBrowser . Model . Entities . SortOrder ;
using VideoResolver = Emby . Naming . Video . VideoResolver ;
2013-02-21 01:33:05 +00:00
2016-11-03 06:37:52 +00:00
namespace Emby.Server.Implementations.Library
2013-02-21 01:33:05 +00:00
{
/// <summary>
/// Class LibraryManager
/// </summary>
2013-02-28 19:32:41 +00:00
public class LibraryManager : ILibraryManager
2013-02-21 01:33:05 +00:00
{
2020-04-04 22:28:46 +00:00
private readonly ILogger _logger ;
private readonly ITaskManager _taskManager ;
private readonly IUserManager _userManager ;
private readonly IUserDataManager _userDataRepository ;
private readonly IServerConfigurationManager _configurationManager ;
private readonly Lazy < ILibraryMonitor > _libraryMonitorFactory ;
private readonly Lazy < IProviderManager > _providerManagerFactory ;
private readonly Lazy < IUserViewManager > _userviewManagerFactory ;
private readonly IServerApplicationHost _appHost ;
private readonly IMediaEncoder _mediaEncoder ;
private readonly IFileSystem _fileSystem ;
private readonly IItemRepository _itemRepository ;
private readonly ConcurrentDictionary < Guid , BaseItem > _libraryItemsCache ;
2020-01-11 20:31:35 +00:00
private NamingOptions _namingOptions ;
private string [ ] _videoFileExtensions ;
2020-04-04 22:28:46 +00:00
private ILibraryMonitor LibraryMonitor = > _libraryMonitorFactory . Value ;
private IProviderManager ProviderManager = > _providerManagerFactory . Value ;
private IUserViewManager UserViewManager = > _userviewManagerFactory . Value ;
2013-05-27 16:53:10 +00:00
/// <summary>
/// Gets or sets the postscan tasks.
/// </summary>
/// <value>The postscan tasks.</value>
2014-02-09 23:08:01 +00:00
private ILibraryPostScanTask [ ] PostscanTasks { get ; set ; }
2013-10-13 17:52:57 +00:00
2013-03-02 02:44:46 +00:00
/// <summary>
2019-03-13 21:32:52 +00:00
/// Gets or sets the intro providers.
2013-03-02 02:44:46 +00:00
/// </summary>
/// <value>The intro providers.</value>
2014-02-09 23:08:01 +00:00
private IIntroProvider [ ] IntroProviders { get ; set ; }
2013-03-02 02:44:46 +00:00
2013-03-01 21:22:34 +00:00
/// <summary>
2019-03-13 21:32:52 +00:00
/// Gets or sets the list of entity resolution ignore rules
2013-03-01 21:22:34 +00:00
/// </summary>
/// <value>The entity resolution ignore rules.</value>
2013-11-04 19:04:23 +00:00
private IResolverIgnoreRule [ ] EntityResolutionIgnoreRules { get ; set ; }
2013-03-01 21:22:34 +00:00
/// <summary>
2019-03-13 21:32:52 +00:00
/// Gets or sets the list of currently registered entity resolvers
2013-03-01 21:22:34 +00:00
/// </summary>
/// <value>The entity resolvers enumerable.</value>
2013-11-04 19:04:23 +00:00
private IItemResolver [ ] EntityResolvers { get ; set ; }
2019-03-13 21:32:52 +00:00
2014-12-28 06:21:39 +00:00
private IMultiItemResolver [ ] MultiItemResolvers { get ; set ; }
2013-03-01 21:22:34 +00:00
2013-03-10 04:22:36 +00:00
/// <summary>
/// Gets or sets the comparers.
/// </summary>
/// <value>The comparers.</value>
2013-11-04 19:04:23 +00:00
private IBaseItemComparer [ ] Comparers { get ; set ; }
2013-03-10 04:22:36 +00:00
2013-02-21 01:33:05 +00:00
/// <summary>
2013-05-03 04:10:11 +00:00
/// Occurs when [item added].
2013-02-21 01:33:05 +00:00
/// </summary>
2013-05-03 04:10:11 +00:00
public event EventHandler < ItemChangeEventArgs > ItemAdded ;
2013-02-21 01:33:05 +00:00
/// <summary>
2013-05-03 04:10:11 +00:00
/// Occurs when [item updated].
2013-02-21 01:33:05 +00:00
/// </summary>
2013-05-03 04:10:11 +00:00
public event EventHandler < ItemChangeEventArgs > ItemUpdated ;
2013-03-31 17:39:28 +00:00
2013-05-03 04:10:11 +00:00
/// <summary>
/// Occurs when [item removed].
/// </summary>
public event EventHandler < ItemChangeEventArgs > ItemRemoved ;
2013-02-21 01:33:05 +00:00
2016-04-26 03:39:21 +00:00
public bool IsScanRunning { get ; private set ; }
2019-03-13 21:32:52 +00:00
2013-02-21 01:33:05 +00:00
/// <summary>
/// Initializes a new instance of the <see cref="LibraryManager" /> class.
/// </summary>
2019-03-13 21:32:52 +00:00
/// <param name="appHost">The application host</param>
2020-04-04 22:28:46 +00:00
/// <param name="logger">The logger.</param>
2013-02-26 16:10:55 +00:00
/// <param name="taskManager">The task manager.</param>
2013-02-28 19:32:41 +00:00
/// <param name="userManager">The user manager.</param>
2013-03-04 05:43:06 +00:00
/// <param name="configurationManager">The configuration manager.</param>
2013-04-13 18:02:30 +00:00
/// <param name="userDataRepository">The user data repository.</param>
2019-01-17 22:55:05 +00:00
public LibraryManager (
IServerApplicationHost appHost ,
2020-04-04 22:28:46 +00:00
ILogger < LibraryManager > logger ,
2019-01-17 22:55:05 +00:00
ITaskManager taskManager ,
IUserManager userManager ,
IServerConfigurationManager configurationManager ,
IUserDataManager userDataRepository ,
2020-04-04 22:28:46 +00:00
Lazy < ILibraryMonitor > libraryMonitorFactory ,
2019-01-17 22:55:05 +00:00
IFileSystem fileSystem ,
2020-04-04 22:28:46 +00:00
Lazy < IProviderManager > providerManagerFactory ,
Lazy < IUserViewManager > userviewManagerFactory ,
IMediaEncoder mediaEncoder ,
IItemRepository itemRepository )
2013-02-21 01:33:05 +00:00
{
2019-03-13 21:32:52 +00:00
_appHost = appHost ;
2020-04-04 22:28:46 +00:00
_logger = logger ;
2013-02-26 16:10:55 +00:00
_taskManager = taskManager ;
2013-02-27 20:25:45 +00:00
_userManager = userManager ;
2020-04-04 22:28:46 +00:00
_configurationManager = configurationManager ;
2013-04-13 18:02:30 +00:00
_userDataRepository = userDataRepository ;
2014-01-28 21:25:10 +00:00
_libraryMonitorFactory = libraryMonitorFactory ;
2013-10-30 14:40:14 +00:00
_fileSystem = fileSystem ;
2014-02-02 13:36:31 +00:00
_providerManagerFactory = providerManagerFactory ;
2020-04-04 22:28:46 +00:00
_userviewManagerFactory = userviewManagerFactory ;
2020-02-19 15:06:30 +00:00
_mediaEncoder = mediaEncoder ;
2020-04-04 22:28:46 +00:00
_itemRepository = itemRepository ;
2019-03-13 21:32:52 +00:00
2014-03-20 15:55:22 +00:00
_libraryItemsCache = new ConcurrentDictionary < Guid , BaseItem > ( ) ;
2013-02-26 16:10:55 +00:00
2020-04-04 22:28:46 +00:00
_configurationManager . ConfigurationUpdated + = ConfigurationUpdated ;
2013-03-12 05:40:24 +00:00
RecordConfigurationValues ( configurationManager . Configuration ) ;
2013-02-26 16:10:55 +00:00
}
2013-03-01 21:22:34 +00:00
/// <summary>
/// Adds the parts.
/// </summary>
/// <param name="rules">The rules.</param>
/// <param name="resolvers">The resolvers.</param>
2013-03-02 02:44:46 +00:00
/// <param name="introProviders">The intro providers.</param>
2013-03-10 04:22:36 +00:00
/// <param name="itemComparers">The item comparers.</param>
2019-03-13 21:32:52 +00:00
/// <param name="postscanTasks">The post scan tasks.</param>
public void AddParts (
IEnumerable < IResolverIgnoreRule > rules ,
2013-04-08 15:55:53 +00:00
IEnumerable < IItemResolver > resolvers ,
IEnumerable < IIntroProvider > introProviders ,
2013-05-21 03:16:43 +00:00
IEnumerable < IBaseItemComparer > itemComparers ,
2014-02-09 23:08:01 +00:00
IEnumerable < ILibraryPostScanTask > postscanTasks )
2013-03-01 21:22:34 +00:00
{
2013-11-04 19:04:23 +00:00
EntityResolutionIgnoreRules = rules . ToArray ( ) ;
2013-03-02 01:09:05 +00:00
EntityResolvers = resolvers . OrderBy ( i = > i . Priority ) . ToArray ( ) ;
2014-12-28 06:21:39 +00:00
MultiItemResolvers = EntityResolvers . OfType < IMultiItemResolver > ( ) . ToArray ( ) ;
2014-02-09 23:08:01 +00:00
IntroProviders = introProviders . ToArray ( ) ;
2013-11-04 19:04:23 +00:00
Comparers = itemComparers . ToArray ( ) ;
2019-03-13 21:32:52 +00:00
PostscanTasks = postscanTasks . ToArray ( ) ;
2013-03-01 21:22:34 +00:00
}
2013-02-28 19:32:41 +00:00
/// <summary>
/// The _root folder
/// </summary>
2016-02-01 00:57:40 +00:00
private volatile AggregateFolder _rootFolder ;
2019-03-13 21:32:52 +00:00
2013-02-28 19:32:41 +00:00
/// <summary>
/// The _root folder sync lock
/// </summary>
2015-01-26 16:47:15 +00:00
private readonly object _rootFolderSyncLock = new object ( ) ;
2019-03-13 21:32:52 +00:00
2013-02-28 19:32:41 +00:00
/// <summary>
/// Gets the root folder.
/// </summary>
/// <value>The root folder.</value>
public AggregateFolder RootFolder
{
get
{
2015-01-26 16:47:15 +00:00
if ( _rootFolder = = null )
2013-02-28 19:32:41 +00:00
{
2015-01-26 16:47:15 +00:00
lock ( _rootFolderSyncLock )
{
if ( _rootFolder = = null )
{
_rootFolder = CreateRootFolder ( ) ;
}
}
2013-02-28 19:32:41 +00:00
}
2019-03-13 21:32:52 +00:00
2015-01-26 16:47:15 +00:00
return _rootFolder ;
2013-02-28 19:32:41 +00:00
}
}
2014-02-04 04:04:19 +00:00
private bool _wizardCompleted ;
2019-03-13 21:32:52 +00:00
2013-05-27 16:53:10 +00:00
/// <summary>
/// Records the configuration values.
/// </summary>
/// <param name="configuration">The configuration.</param>
2013-03-12 05:40:24 +00:00
private void RecordConfigurationValues ( ServerConfiguration configuration )
{
2014-02-04 04:04:19 +00:00
_wizardCompleted = configuration . IsStartupWizardCompleted ;
2013-03-12 05:40:24 +00:00
}
2013-02-26 16:10:55 +00:00
/// <summary>
2013-04-08 15:55:53 +00:00
/// Configurations the updated.
2013-02-26 16:10:55 +00:00
/// </summary>
2013-04-08 15:55:53 +00:00
/// <param name="sender">The sender.</param>
2013-05-27 16:53:10 +00:00
/// <param name="e">The <see cref="EventArgs" /> instance containing the event data.</param>
2019-03-13 21:32:52 +00:00
private void ConfigurationUpdated ( object sender , EventArgs e )
2013-02-26 16:10:55 +00:00
{
2020-04-04 22:28:46 +00:00
var config = _configurationManager . Configuration ;
2013-03-12 05:40:24 +00:00
2014-02-04 04:04:19 +00:00
var wizardChanged = config . IsStartupWizardCompleted ! = _wizardCompleted ;
2013-05-25 15:16:50 +00:00
2013-03-12 05:40:24 +00:00
RecordConfigurationValues ( config ) ;
2013-02-26 16:10:55 +00:00
2017-09-24 20:23:56 +00:00
if ( wizardChanged )
2013-02-26 16:10:55 +00:00
{
2015-08-19 23:57:27 +00:00
_taskManager . CancelIfRunningAndQueue < RefreshMediaLibraryTask > ( ) ;
}
2013-05-24 15:01:53 +00:00
}
2014-01-11 05:49:18 +00:00
public void RegisterItem ( BaseItem item )
{
2014-03-20 15:55:22 +00:00
if ( item = = null )
{
2019-01-06 20:50:43 +00:00
throw new ArgumentNullException ( nameof ( item ) ) ;
2014-03-20 15:55:22 +00:00
}
2019-02-24 14:47:59 +00:00
2015-08-19 23:57:27 +00:00
if ( item is IItemByName )
{
2017-05-21 07:25:49 +00:00
if ( ! ( item is MusicArtist ) )
2015-08-19 23:57:27 +00:00
{
return ;
}
}
2019-02-24 14:47:59 +00:00
else if ( ! item . IsFolder )
2016-07-05 05:40:18 +00:00
{
2018-09-12 17:26:21 +00:00
if ( ! ( item is Video ) & & ! ( item is LiveTvChannel ) )
2016-07-09 17:39:04 +00:00
{
return ;
}
2016-07-05 05:40:18 +00:00
}
2016-07-09 17:39:04 +00:00
2020-04-04 22:28:46 +00:00
_libraryItemsCache . AddOrUpdate ( item . Id , item , delegate { return item ; } ) ;
2013-03-31 17:39:28 +00:00
}
2018-09-12 17:26:21 +00:00
public void DeleteItem ( BaseItem item , DeleteOptions options )
{
DeleteItem ( item , options , false ) ;
}
public void DeleteItem ( BaseItem item , DeleteOptions options , bool notifyParentItem )
{
if ( item = = null )
{
2019-01-06 20:50:43 +00:00
throw new ArgumentNullException ( nameof ( item ) ) ;
2018-09-12 17:26:21 +00:00
}
var parent = item . GetOwner ( ) ? ? item . GetParent ( ) ;
DeleteItem ( item , options , parent , notifyParentItem ) ;
}
public void DeleteItem ( BaseItem item , DeleteOptions options , BaseItem parent , bool notifyParentItem )
2014-02-19 18:50:37 +00:00
{
2016-03-20 21:32:26 +00:00
if ( item = = null )
{
2019-01-06 20:50:43 +00:00
throw new ArgumentNullException ( nameof ( item ) ) ;
2016-03-20 21:32:26 +00:00
}
2018-09-12 17:26:21 +00:00
if ( item . SourceType = = SourceType . Channel )
{
if ( options . DeleteFromExternalProvider )
{
try
{
var task = BaseItem . ChannelManager . DeleteItem ( item ) ;
Task . WaitAll ( task ) ;
}
catch ( ArgumentException )
{
// channel no longer installed
}
}
2019-03-13 21:32:52 +00:00
2018-09-12 17:26:21 +00:00
options . DeleteFileLocation = false ;
}
2017-04-13 18:57:24 +00:00
if ( item is LiveTvProgram )
{
2019-03-13 21:32:52 +00:00
_logger . LogDebug (
"Deleting item, Type: {0}, Name: {1}, Path: {2}, Id: {3}" ,
2017-04-13 18:57:24 +00:00
item . GetType ( ) . Name ,
item . Name ? ? "Unknown name" ,
item . Path ? ? string . Empty ,
item . Id ) ;
}
else
{
2019-03-13 21:32:52 +00:00
_logger . LogInformation (
"Deleting item, Type: {0}, Name: {1}, Path: {2}, Id: {3}" ,
2017-04-13 18:57:24 +00:00
item . GetType ( ) . Name ,
item . Name ? ? "Unknown name" ,
item . Path ? ? string . Empty ,
item . Id ) ;
}
2014-02-20 04:53:15 +00:00
2014-02-19 18:50:37 +00:00
var children = item . IsFolder
2016-09-07 17:17:26 +00:00
? ( ( Folder ) item ) . GetRecursiveChildren ( false ) . ToList ( )
2014-02-19 18:50:37 +00:00
: new List < BaseItem > ( ) ;
foreach ( var metadataPath in GetMetadataPaths ( item , children ) )
{
2019-02-24 14:47:59 +00:00
if ( ! Directory . Exists ( metadataPath ) )
{
continue ;
}
_logger . LogDebug ( "Deleting path {MetadataPath}" , metadataPath ) ;
2014-02-19 18:50:37 +00:00
try
{
2019-01-26 21:08:04 +00:00
Directory . Delete ( metadataPath , true ) ;
2014-02-19 18:50:37 +00:00
}
catch ( Exception ex )
{
2019-02-24 14:47:59 +00:00
_logger . LogError ( ex , "Error deleting {MetadataPath}" , metadataPath ) ;
2014-02-19 18:50:37 +00:00
}
}
2018-09-12 17:26:21 +00:00
if ( options . DeleteFileLocation & & item . IsFileProtocol )
2014-02-19 18:50:37 +00:00
{
2017-03-07 18:27:56 +00:00
// Assume only the first is required
// Add this flag to GetDeletePaths if required in the future
var isRequiredForDelete = true ;
2019-12-15 04:38:59 +00:00
foreach ( var fileSystemInfo in item . GetDeletePaths ( ) )
2014-02-19 18:50:37 +00:00
{
2020-01-01 05:45:09 +00:00
if ( Directory . Exists ( fileSystemInfo . FullName ) | | File . Exists ( fileSystemInfo . FullName ) )
2017-03-07 18:27:56 +00:00
{
2019-01-27 16:00:17 +00:00
try
2017-03-07 18:27:56 +00:00
{
2019-01-27 16:00:17 +00:00
_logger . LogDebug ( "Deleting path {path}" , fileSystemInfo . FullName ) ;
if ( fileSystemInfo . IsDirectory )
{
2019-01-26 21:08:04 +00:00
Directory . Delete ( fileSystemInfo . FullName , true ) ;
2019-01-27 16:00:17 +00:00
}
else
{
2019-01-26 21:08:04 +00:00
File . Delete ( fileSystemInfo . FullName ) ;
2019-01-27 16:00:17 +00:00
}
2017-03-07 18:27:56 +00:00
}
2019-01-27 16:00:17 +00:00
catch ( IOException )
2017-03-07 18:27:56 +00:00
{
2019-01-27 16:00:17 +00:00
if ( isRequiredForDelete )
{
throw ;
}
2017-03-07 18:27:56 +00:00
}
2019-01-27 16:00:17 +00:00
catch ( UnauthorizedAccessException )
2017-03-07 18:27:56 +00:00
{
2019-01-27 16:00:17 +00:00
if ( isRequiredForDelete )
{
throw ;
}
2017-03-07 18:27:56 +00:00
}
2014-02-19 18:50:37 +00:00
}
2017-03-07 18:27:56 +00:00
isRequiredForDelete = false ;
2014-02-19 18:50:37 +00:00
}
}
2018-09-12 17:26:21 +00:00
item . SetParent ( null ) ;
2020-04-04 22:28:46 +00:00
_itemRepository . DeleteItem ( item . Id , CancellationToken . None ) ;
2014-02-19 18:50:37 +00:00
foreach ( var child in children )
{
2020-04-04 22:28:46 +00:00
_itemRepository . DeleteItem ( child . Id , CancellationToken . None ) ;
2014-02-19 18:50:37 +00:00
}
2014-02-20 04:53:15 +00:00
2019-01-17 17:47:41 +00:00
_libraryItemsCache . TryRemove ( item . Id , out BaseItem removed ) ;
2014-03-09 22:14:44 +00:00
2017-11-26 04:48:12 +00:00
ReportItemRemoved ( item , parent ) ;
2014-02-19 18:50:37 +00:00
}
2019-01-06 20:50:43 +00:00
private static IEnumerable < string > GetMetadataPaths ( BaseItem item , IEnumerable < BaseItem > children )
2014-02-19 18:50:37 +00:00
{
var list = new List < string >
{
2014-09-28 15:27:26 +00:00
item . GetInternalMetadataPath ( )
2014-02-19 18:50:37 +00:00
} ;
2014-09-28 15:27:26 +00:00
list . AddRange ( children . Select ( i = > i . GetInternalMetadataPath ( ) ) ) ;
2014-02-19 18:50:37 +00:00
return list ;
}
2013-02-21 01:33:05 +00:00
/// <summary>
/// Resolves the item.
/// </summary>
/// <param name="args">The args.</param>
2016-03-19 19:32:37 +00:00
/// <param name="resolvers">The resolvers.</param>
2013-02-21 01:33:05 +00:00
/// <returns>BaseItem.</returns>
2016-03-19 19:32:37 +00:00
private BaseItem ResolveItem ( ItemResolveArgs args , IItemResolver [ ] resolvers )
2013-02-21 01:33:05 +00:00
{
2016-03-19 19:32:37 +00:00
var item = ( resolvers ? ? EntityResolvers ) . Select ( r = > Resolve ( args , r ) )
2014-12-04 05:24:41 +00:00
. FirstOrDefault ( i = > i ! = null ) ;
2013-03-03 06:58:04 +00:00
if ( item ! = null )
{
2014-11-30 19:01:33 +00:00
ResolverHelper . SetInitialItemValues ( item , args , _fileSystem , this ) ;
2013-03-03 06:58:04 +00:00
}
return item ;
2013-02-21 01:33:05 +00:00
}
2014-12-04 05:24:41 +00:00
private BaseItem Resolve ( ItemResolveArgs args , IItemResolver resolver )
{
try
{
return resolver . ResolvePath ( args ) ;
}
catch ( Exception ex )
{
2018-12-20 12:11:26 +00:00
_logger . LogError ( ex , "Error in {resolver} resolving {path}" , resolver . GetType ( ) . Name , args . Path ) ;
2014-12-04 05:24:41 +00:00
return null ;
}
}
2014-11-30 19:01:33 +00:00
public Guid GetNewItemId ( string key , Type type )
2017-03-13 04:08:23 +00:00
{
return GetNewItemIdInternal ( key , type , false ) ;
}
private Guid GetNewItemIdInternal ( string key , Type type , bool forceCaseInsensitive )
2014-11-30 19:01:33 +00:00
{
2018-09-12 17:26:21 +00:00
if ( string . IsNullOrEmpty ( key ) )
2014-11-30 19:01:33 +00:00
{
2019-01-06 20:50:43 +00:00
throw new ArgumentNullException ( nameof ( key ) ) ;
2014-11-30 19:01:33 +00:00
}
2019-03-13 21:32:52 +00:00
2014-11-30 19:01:33 +00:00
if ( type = = null )
{
2019-01-06 20:50:43 +00:00
throw new ArgumentNullException ( nameof ( type ) ) ;
2014-11-30 19:01:33 +00:00
}
2020-04-04 22:28:46 +00:00
if ( key . StartsWith ( _configurationManager . ApplicationPaths . ProgramDataPath , StringComparison . Ordinal ) )
2014-11-30 19:01:33 +00:00
{
// Try to normalize paths located underneath program-data in an attempt to make them more portable
2020-04-04 22:28:46 +00:00
key = key . Substring ( _configurationManager . ApplicationPaths . ProgramDataPath . Length )
2014-11-30 19:01:33 +00:00
. TrimStart ( new [ ] { '/' , '\\' } )
. Replace ( "/" , "\\" ) ;
}
2020-04-04 22:28:46 +00:00
if ( forceCaseInsensitive | | ! _configurationManager . Configuration . EnableCaseSensitiveItemIds )
2016-03-24 06:04:58 +00:00
{
2019-01-27 11:03:43 +00:00
key = key . ToLowerInvariant ( ) ;
2016-03-24 06:04:58 +00:00
}
key = type . FullName + key ;
2014-11-30 19:01:33 +00:00
return key . GetMD5 ( ) ;
}
2019-03-13 21:32:52 +00:00
public BaseItem ResolvePath ( FileSystemMetadata fileInfo , Folder parent = null )
2019-09-10 20:37:53 +00:00
= > ResolvePath ( fileInfo , new DirectoryService ( _fileSystem ) , null , parent ) ;
2014-02-13 05:11:54 +00:00
2019-03-13 21:32:52 +00:00
private BaseItem ResolvePath (
FileSystemMetadata fileInfo ,
2016-08-13 05:49:00 +00:00
IDirectoryService directoryService ,
IItemResolver [ ] resolvers ,
Folder parent = null ,
string collectionType = null ,
LibraryOptions libraryOptions = null )
2013-02-21 01:33:05 +00:00
{
2013-06-11 20:35:54 +00:00
if ( fileInfo = = null )
2013-02-21 01:33:05 +00:00
{
2019-01-06 20:50:43 +00:00
throw new ArgumentNullException ( nameof ( fileInfo ) ) ;
2013-02-21 01:33:05 +00:00
}
2014-12-22 06:50:29 +00:00
var fullPath = fileInfo . FullName ;
2018-09-12 17:26:21 +00:00
if ( string . IsNullOrEmpty ( collectionType ) & & parent ! = null )
2014-12-22 06:50:29 +00:00
{
2015-01-10 01:38:01 +00:00
collectionType = GetContentTypeOverride ( fullPath , true ) ;
2014-12-22 06:50:29 +00:00
}
2020-04-04 22:28:46 +00:00
var args = new ItemResolveArgs ( _configurationManager . ApplicationPaths , directoryService )
2013-02-21 01:33:05 +00:00
{
Parent = parent ,
2014-12-22 06:50:29 +00:00
Path = fullPath ,
2014-10-23 04:26:01 +00:00
FileInfo = fileInfo ,
2016-08-13 05:49:00 +00:00
CollectionType = collectionType ,
LibraryOptions = libraryOptions
2013-02-21 01:33:05 +00:00
} ;
// Return null if ignore rules deem that we should do so
2015-11-13 20:53:29 +00:00
if ( IgnoreFile ( args . FileInfo , args . Parent ) )
2013-02-21 01:33:05 +00:00
{
return null ;
}
// Gather child folder and files
if ( args . IsDirectory )
{
2013-04-19 18:03:21 +00:00
var isPhysicalRoot = args . IsPhysicalRoot ;
2013-02-21 01:33:05 +00:00
// When resolving the root, we need it's grandchildren (children of user views)
2013-04-19 18:03:21 +00:00
var flattenFolderDepth = isPhysicalRoot ? 2 : 0 ;
2013-02-21 01:33:05 +00:00
2018-09-12 17:26:21 +00:00
FileSystemMetadata [ ] files ;
var isVf = args . IsVf ;
try
{
files = FileData . GetFilteredFileSystemEntries ( directoryService , args . Path , _fileSystem , _appHost , _logger , args , flattenFolderDepth : flattenFolderDepth , resolveShortcuts : isPhysicalRoot | | isVf ) ;
}
catch ( Exception ex )
{
if ( parent ! = null & & parent . IsPhysicalRoot )
{
2018-12-20 12:11:26 +00:00
_logger . LogError ( ex , "Error in GetFilteredFileSystemEntries isPhysicalRoot: {0} IsVf: {1}" , isPhysicalRoot , isVf ) ;
2018-09-12 17:26:21 +00:00
2019-03-13 21:32:52 +00:00
files = Array . Empty < FileSystemMetadata > ( ) ;
2018-09-12 17:26:21 +00:00
}
else
{
throw ;
}
}
2014-02-22 20:20:22 +00:00
2013-05-24 17:48:48 +00:00
// Need to remove subpaths that may have been resolved from shortcuts
// Example: if \\server\movies exists, then strip out \\server\movies\action
if ( isPhysicalRoot )
{
2017-08-20 19:10:00 +00:00
files = NormalizeRootPathList ( files ) . ToArray ( ) ;
2013-05-24 17:48:48 +00:00
}
2014-01-01 18:26:31 +00:00
2017-08-20 19:10:00 +00:00
args . FileSystemChildren = files ;
2013-02-21 01:33:05 +00:00
}
// Check to see if we should resolve based on our contents
2013-03-03 06:58:04 +00:00
if ( args . IsDirectory & & ! ShouldResolvePathContents ( args ) )
2013-02-21 01:33:05 +00:00
{
return null ;
}
2016-03-19 19:32:37 +00:00
return ResolveItem ( args , resolvers ) ;
2013-02-21 01:33:05 +00:00
}
2015-11-13 20:53:29 +00:00
public bool IgnoreFile ( FileSystemMetadata file , BaseItem parent )
2019-03-13 21:32:52 +00:00
= > EntityResolutionIgnoreRules . Any ( r = > r . ShouldIgnore ( file , parent ) ) ;
2015-11-04 23:49:06 +00:00
2017-08-20 19:10:00 +00:00
public List < FileSystemMetadata > NormalizeRootPathList ( IEnumerable < FileSystemMetadata > paths )
2015-11-13 20:53:29 +00:00
{
2015-11-13 20:56:26 +00:00
var originalList = paths . ToList ( ) ;
var list = originalList . Where ( i = > i . IsDirectory )
. Select ( i = > _fileSystem . NormalizePath ( i . FullName ) )
2014-01-01 18:26:31 +00:00
. Distinct ( StringComparer . OrdinalIgnoreCase )
. ToList ( ) ;
var dupes = list . Where ( subPath = > ! subPath . EndsWith ( ":\\" , StringComparison . OrdinalIgnoreCase ) & & list . Any ( i = > _fileSystem . ContainsSubPath ( i , subPath ) ) )
. ToList ( ) ;
foreach ( var dupe in dupes )
{
2018-12-13 13:18:25 +00:00
_logger . LogInformation ( "Found duplicate path: {0}" , dupe ) ;
2014-01-01 18:26:31 +00:00
}
2015-11-13 20:56:26 +00:00
var newList = list . Except ( dupes , StringComparer . OrdinalIgnoreCase ) . Select ( _fileSystem . GetDirectoryInfo ) . ToList ( ) ;
newList . AddRange ( originalList . Where ( i = > ! i . IsDirectory ) ) ;
return newList ;
2014-01-01 18:26:31 +00:00
}
2013-03-03 06:58:04 +00:00
/// <summary>
/// Determines whether a path should be ignored based on its contents - called after the contents have been read
/// </summary>
/// <param name="args">The args.</param>
/// <returns><c>true</c> if XXXX, <c>false</c> otherwise</returns>
private static bool ShouldResolvePathContents ( ItemResolveArgs args )
{
// Ignore any folders containing a file called .ignore
return ! args . ContainsFileSystemEntryByName ( ".ignore" ) ;
}
2016-08-13 05:49:00 +00:00
public IEnumerable < BaseItem > ResolvePaths ( IEnumerable < FileSystemMetadata > files , IDirectoryService directoryService , Folder parent , LibraryOptions libraryOptions , string collectionType )
2016-03-19 19:32:37 +00:00
{
2016-08-13 05:49:00 +00:00
return ResolvePaths ( files , directoryService , parent , libraryOptions , collectionType , EntityResolvers ) ;
2016-03-19 19:32:37 +00:00
}
2019-03-13 21:32:52 +00:00
public IEnumerable < BaseItem > ResolvePaths (
IEnumerable < FileSystemMetadata > files ,
2016-08-13 05:49:00 +00:00
IDirectoryService directoryService ,
2016-09-14 21:34:19 +00:00
Folder parent ,
2016-08-13 05:49:00 +00:00
LibraryOptions libraryOptions ,
string collectionType ,
IItemResolver [ ] resolvers )
2014-12-04 05:24:41 +00:00
{
2015-11-13 20:53:29 +00:00
var fileList = files . Where ( i = > ! IgnoreFile ( i , parent ) ) . ToList ( ) ;
2014-12-04 05:24:41 +00:00
if ( parent ! = null )
{
2016-03-19 19:32:37 +00:00
var multiItemResolvers = resolvers = = null ? MultiItemResolvers : resolvers . OfType < IMultiItemResolver > ( ) . ToArray ( ) ;
foreach ( var resolver in multiItemResolvers )
2014-12-04 05:24:41 +00:00
{
var result = resolver . ResolveMultiple ( parent , fileList , collectionType , directoryService ) ;
if ( result ! = null & & result . Items . Count > 0 )
{
var items = new List < BaseItem > ( ) ;
items . AddRange ( result . Items ) ;
foreach ( var item in items )
{
ResolverHelper . SetInitialItemValues ( item , parent , _fileSystem , this , directoryService ) ;
}
2019-03-13 21:32:52 +00:00
2016-08-13 05:49:00 +00:00
items . AddRange ( ResolveFileList ( result . ExtraFiles , directoryService , parent , collectionType , resolvers , libraryOptions ) ) ;
2014-12-04 05:24:41 +00:00
return items ;
}
}
}
2016-08-13 05:49:00 +00:00
return ResolveFileList ( fileList , directoryService , parent , collectionType , resolvers , libraryOptions ) ;
2014-12-04 05:24:41 +00:00
}
2019-03-13 21:32:52 +00:00
private IEnumerable < BaseItem > ResolveFileList (
IEnumerable < FileSystemMetadata > fileList ,
2016-08-13 05:49:00 +00:00
IDirectoryService directoryService ,
Folder parent ,
string collectionType ,
IItemResolver [ ] resolvers ,
LibraryOptions libraryOptions )
2013-02-21 01:33:05 +00:00
{
2014-12-04 05:24:41 +00:00
return fileList . Select ( f = >
2013-02-21 01:33:05 +00:00
{
try
{
2016-08-13 05:49:00 +00:00
return ResolvePath ( f , directoryService , resolvers , parent , collectionType , libraryOptions ) ;
2013-02-21 01:33:05 +00:00
}
catch ( Exception ex )
{
2018-12-20 12:11:26 +00:00
_logger . LogError ( ex , "Error resolving path {path}" , f . FullName ) ;
2014-11-26 04:12:29 +00:00
return null ;
2013-02-21 01:33:05 +00:00
}
2014-12-04 05:24:41 +00:00
} ) . Where ( i = > i ! = null ) ;
2013-02-21 01:33:05 +00:00
}
/// <summary>
2020-02-01 13:44:27 +00:00
/// Creates the root media folder.
2013-02-21 01:33:05 +00:00
/// </summary>
/// <returns>AggregateFolder.</returns>
2020-02-01 13:44:27 +00:00
/// <exception cref="InvalidOperationException">Cannot create the root folder until plugins have loaded.</exception>
2013-02-28 19:32:41 +00:00
public AggregateFolder CreateRootFolder ( )
2013-02-21 01:33:05 +00:00
{
2020-04-04 22:28:46 +00:00
var rootFolderPath = _configurationManager . ApplicationPaths . RootFolderPath ;
2013-06-04 16:48:23 +00:00
2019-01-26 21:08:04 +00:00
Directory . CreateDirectory ( rootFolderPath ) ;
2013-06-04 16:48:23 +00:00
2019-01-13 19:21:32 +00:00
var rootFolder = GetItemById ( GetNewItemId ( rootFolderPath , typeof ( AggregateFolder ) ) ) as AggregateFolder ? ? ( ( Folder ) ResolvePath ( _fileSystem . GetDirectoryInfo ( rootFolderPath ) ) ) . DeepCopy < Folder , AggregateFolder > ( ) ;
2018-12-30 00:24:30 +00:00
2017-11-10 21:22:38 +00:00
// In case program data folder was moved
2017-11-12 21:05:40 +00:00
if ( ! string . Equals ( rootFolder . Path , rootFolderPath , StringComparison . Ordinal ) )
{
2018-12-13 13:18:25 +00:00
_logger . LogInformation ( "Resetting root folder path to {0}" , rootFolderPath ) ;
2017-11-12 21:05:40 +00:00
rootFolder . Path = rootFolderPath ;
}
2017-11-10 21:22:38 +00:00
2013-02-21 01:33:05 +00:00
// Add in the plug-in folders
2020-04-04 22:28:46 +00:00
var path = Path . Combine ( _configurationManager . ApplicationPaths . DataPath , "playlists" ) ;
2018-09-12 17:26:21 +00:00
2019-01-26 21:08:04 +00:00
Directory . CreateDirectory ( path ) ;
2018-09-12 17:26:21 +00:00
Folder folder = new PlaylistsFolder
2013-02-21 01:33:05 +00:00
{
2018-09-12 17:26:21 +00:00
Path = path
} ;
2013-06-13 18:17:42 +00:00
2018-09-12 17:26:21 +00:00
if ( folder . Id . Equals ( Guid . Empty ) )
{
if ( string . IsNullOrEmpty ( folder . Path ) )
2013-06-13 18:17:42 +00:00
{
2018-09-12 17:26:21 +00:00
folder . Id = GetNewItemId ( folder . GetType ( ) . Name , folder . GetType ( ) ) ;
}
else
{
folder . Id = GetNewItemId ( folder . Path , folder . GetType ( ) ) ;
}
}
2013-06-13 18:17:42 +00:00
2018-09-12 17:26:21 +00:00
var dbItem = GetItemById ( folder . Id ) as BasePluginFolder ;
2015-04-06 20:43:40 +00:00
2018-09-12 17:26:21 +00:00
if ( dbItem ! = null & & string . Equals ( dbItem . Path , folder . Path , StringComparison . OrdinalIgnoreCase ) )
{
folder = dbItem ;
}
2014-05-31 14:30:59 +00:00
2018-09-12 17:26:21 +00:00
if ( folder . ParentId ! = rootFolder . Id )
{
folder . ParentId = rootFolder . Id ;
folder . UpdateToRepository ( ItemUpdateType . MetadataImport , CancellationToken . None ) ;
}
2015-11-13 20:53:29 +00:00
2018-09-12 17:26:21 +00:00
rootFolder . AddVirtualChild ( folder ) ;
2014-06-04 03:34:36 +00:00
2018-09-12 17:26:21 +00:00
RegisterItem ( folder ) ;
2013-02-21 01:33:05 +00:00
return rootFolder ;
}
2016-02-01 00:57:40 +00:00
private volatile UserRootFolder _userRootFolder ;
2014-12-13 03:56:30 +00:00
private readonly object _syncLock = new object ( ) ;
2019-03-13 21:32:52 +00:00
2014-02-21 05:04:11 +00:00
public Folder GetUserRootFolder ( )
2013-04-05 04:13:41 +00:00
{
2014-02-21 05:04:11 +00:00
if ( _userRootFolder = = null )
{
2014-12-13 03:56:30 +00:00
lock ( _syncLock )
{
if ( _userRootFolder = = null )
{
2020-04-04 22:28:46 +00:00
var userRootPath = _configurationManager . ApplicationPaths . DefaultUserViewsPath ;
2013-04-22 04:38:03 +00:00
2019-09-03 03:32:03 +00:00
_logger . LogDebug ( "Creating userRootPath at {path}" , userRootPath ) ;
2019-01-26 21:08:04 +00:00
Directory . CreateDirectory ( userRootPath ) ;
2014-02-22 22:41:29 +00:00
2019-09-03 03:32:03 +00:00
var newItemId = GetNewItemId ( userRootPath , typeof ( UserRootFolder ) ) ;
UserRootFolder tmpItem = null ;
try
{
tmpItem = GetItemById ( newItemId ) as UserRootFolder ;
}
catch ( Exception ex )
{
_logger . LogError ( ex , "Error creating UserRootFolder {path}" , newItemId ) ;
}
2014-12-13 03:56:30 +00:00
2015-01-27 22:45:59 +00:00
if ( tmpItem = = null )
2014-12-13 03:56:30 +00:00
{
2019-09-03 03:32:03 +00:00
_logger . LogDebug ( "Creating new userRootFolder with DeepCopy" ) ;
2019-01-13 19:21:32 +00:00
tmpItem = ( ( Folder ) ResolvePath ( _fileSystem . GetDirectoryInfo ( userRootPath ) ) ) . DeepCopy < Folder , UserRootFolder > ( ) ;
2014-12-13 03:56:30 +00:00
}
2015-01-27 22:45:59 +00:00
2017-11-10 21:22:38 +00:00
// In case program data folder was moved
2017-11-12 21:05:40 +00:00
if ( ! string . Equals ( tmpItem . Path , userRootPath , StringComparison . Ordinal ) )
{
2018-12-13 13:18:25 +00:00
_logger . LogInformation ( "Resetting user root folder path to {0}" , userRootPath ) ;
2017-11-12 21:05:40 +00:00
tmpItem . Path = userRootPath ;
}
2017-11-10 21:22:38 +00:00
2015-01-27 22:45:59 +00:00
_userRootFolder = tmpItem ;
2019-09-03 03:32:03 +00:00
_logger . LogDebug ( "Setting userRootFolder: {folder}" , _userRootFolder ) ;
2014-12-13 03:56:30 +00:00
}
}
2014-02-21 05:04:11 +00:00
}
return _userRootFolder ;
2013-09-11 17:54:59 +00:00
}
2016-12-13 07:36:30 +00:00
2016-04-27 17:53:23 +00:00
public BaseItem FindByPath ( string path , bool? isFolder )
2016-03-01 19:39:46 +00:00
{
2019-01-07 23:27:46 +00:00
// If this returns multiple items it could be tricky figuring out which one is correct.
2016-07-09 17:39:04 +00:00
// In most cases, the newest one will be and the others obsolete but not yet cleaned up
2018-09-12 17:26:21 +00:00
if ( string . IsNullOrEmpty ( path ) )
2017-05-30 18:24:50 +00:00
{
2019-01-06 20:50:43 +00:00
throw new ArgumentNullException ( nameof ( path ) ) ;
2017-05-30 18:24:50 +00:00
}
2016-03-01 19:39:46 +00:00
var query = new InternalItemsQuery
{
2016-04-27 17:53:23 +00:00
Path = path ,
2016-07-09 17:39:04 +00:00
IsFolder = isFolder ,
2019-10-20 14:08:40 +00:00
OrderBy = new [ ] { ( ItemSortBy . DateCreated , SortOrder . Descending ) } ,
2017-05-21 07:25:49 +00:00
Limit = 1 ,
DtoOptions = new DtoOptions ( true )
2016-03-01 19:39:46 +00:00
} ;
2016-12-15 06:41:10 +00:00
2016-07-09 17:39:04 +00:00
return GetItemList ( query )
2016-05-10 18:43:17 +00:00
. FirstOrDefault ( ) ;
2016-03-01 19:39:46 +00:00
}
2013-02-21 01:33:05 +00:00
/// <summary>
2020-02-01 13:44:27 +00:00
/// Gets the person.
2013-02-21 01:33:05 +00:00
/// </summary>
/// <param name="name">The name.</param>
/// <returns>Task{Person}.</returns>
2013-09-17 02:08:18 +00:00
public Person GetPerson ( string name )
2013-02-21 01:33:05 +00:00
{
2017-05-22 04:54:02 +00:00
return CreateItemByName < Person > ( Person . GetPath , name , new DtoOptions ( true ) ) ;
2013-02-21 01:33:05 +00:00
}
/// <summary>
2020-02-01 13:44:27 +00:00
/// Gets the studio.
2013-02-21 01:33:05 +00:00
/// </summary>
/// <param name="name">The name.</param>
2013-09-10 18:56:00 +00:00
/// <returns>Task{Studio}.</returns>
2013-09-17 02:08:18 +00:00
public Studio GetStudio ( string name )
2013-09-10 18:56:00 +00:00
{
2017-05-22 04:54:02 +00:00
return CreateItemByName < Studio > ( Studio . GetPath , name , new DtoOptions ( true ) ) ;
2013-02-21 01:33:05 +00:00
}
2017-05-18 21:05:47 +00:00
public Guid GetStudioId ( string name )
{
return GetItemByNameId < Studio > ( Studio . GetPath , name ) ;
}
public Guid GetGenreId ( string name )
{
return GetItemByNameId < Genre > ( Genre . GetPath , name ) ;
}
public Guid GetMusicGenreId ( string name )
{
return GetItemByNameId < MusicGenre > ( MusicGenre . GetPath , name ) ;
}
2013-02-21 01:33:05 +00:00
/// <summary>
2020-02-01 13:44:27 +00:00
/// Gets the genre.
2013-02-21 01:33:05 +00:00
/// </summary>
/// <param name="name">The name.</param>
2013-09-10 18:56:00 +00:00
/// <returns>Task{Genre}.</returns>
2013-09-17 02:08:18 +00:00
public Genre GetGenre ( string name )
2013-09-10 18:56:00 +00:00
{
2017-05-22 04:54:02 +00:00
return CreateItemByName < Genre > ( Genre . GetPath , name , new DtoOptions ( true ) ) ;
2013-02-21 01:33:05 +00:00
}
2013-06-11 03:31:00 +00:00
/// <summary>
2020-02-01 13:44:27 +00:00
/// Gets the music genre.
2013-06-11 03:31:00 +00:00
/// </summary>
/// <param name="name">The name.</param>
/// <returns>Task{MusicGenre}.</returns>
2013-09-17 02:08:18 +00:00
public MusicGenre GetMusicGenre ( string name )
2013-07-01 17:17:33 +00:00
{
2017-05-22 04:54:02 +00:00
return CreateItemByName < MusicGenre > ( MusicGenre . GetPath , name , new DtoOptions ( true ) ) ;
2013-09-10 18:56:00 +00:00
}
2013-02-21 01:33:05 +00:00
/// <summary>
2020-02-01 13:44:27 +00:00
/// Gets the year.
2013-02-21 01:33:05 +00:00
/// </summary>
/// <param name="value">The value.</param>
/// <returns>Task{Year}.</returns>
2013-09-17 02:08:18 +00:00
public Year GetYear ( int value )
2013-02-21 01:33:05 +00:00
{
if ( value < = 0 )
{
2019-01-13 19:21:32 +00:00
throw new ArgumentOutOfRangeException ( nameof ( value ) , "Years less than or equal to 0 are invalid." ) ;
2013-02-21 01:33:05 +00:00
}
2016-08-18 05:56:10 +00:00
var name = value . ToString ( CultureInfo . InvariantCulture ) ;
2013-02-21 01:33:05 +00:00
2017-05-22 04:54:02 +00:00
return CreateItemByName < Year > ( Year . GetPath , name , new DtoOptions ( true ) ) ;
2015-01-13 03:46:44 +00:00
}
2013-11-21 20:48:26 +00:00
/// <summary>
/// Gets a Genre
/// </summary>
/// <param name="name">The name.</param>
/// <returns>Task{Genre}.</returns>
public MusicArtist GetArtist ( string name )
{
2017-05-22 04:54:02 +00:00
return GetArtist ( name , new DtoOptions ( true ) ) ;
2013-09-11 17:54:59 +00:00
}
2017-05-22 04:54:02 +00:00
public MusicArtist GetArtist ( string name , DtoOptions options )
{
return CreateItemByName < MusicArtist > ( MusicArtist . GetPath , name , options ) ;
}
private T CreateItemByName < T > ( Func < string , string > getPathFn , string name , DtoOptions options )
2013-02-21 01:33:05 +00:00
where T : BaseItem , new ( )
{
2016-06-20 03:34:47 +00:00
if ( typeof ( T ) = = typeof ( MusicArtist ) )
2013-11-21 20:48:26 +00:00
{
2016-05-04 16:33:22 +00:00
var existing = GetItemList ( new InternalItemsQuery
{
IncludeItemTypes = new [ ] { typeof ( T ) . Name } ,
2017-05-21 07:25:49 +00:00
Name = name ,
2017-05-22 04:54:02 +00:00
DtoOptions = options
2016-05-04 16:33:22 +00:00
} ) . Cast < MusicArtist > ( )
2016-05-07 18:58:16 +00:00
. OrderBy ( i = > i . IsAccessedByName ? 1 : 0 )
2016-05-04 16:33:22 +00:00
. Cast < T > ( )
. FirstOrDefault ( ) ;
2013-11-21 20:48:26 +00:00
if ( existing ! = null )
{
2014-02-22 20:20:22 +00:00
return existing ;
2013-11-21 20:48:26 +00:00
}
}
2017-05-18 21:05:47 +00:00
var id = GetItemByNameId < T > ( getPathFn , name ) ;
2016-05-13 02:36:01 +00:00
2015-08-06 01:21:18 +00:00
var item = GetItemById ( id ) as T ;
2013-09-10 18:56:00 +00:00
2013-02-21 01:33:05 +00:00
if ( item = = null )
{
2017-05-18 21:05:47 +00:00
var path = getPathFn ( name ) ;
2013-02-21 01:33:05 +00:00
item = new T
{
Name = name ,
Id = id ,
2015-08-06 01:21:18 +00:00
DateCreated = DateTime . UtcNow ,
DateModified = DateTime . UtcNow ,
2013-02-21 01:33:05 +00:00
Path = path
} ;
2018-09-12 17:26:21 +00:00
CreateItem ( item , null ) ;
2013-11-21 20:48:26 +00:00
}
2014-02-22 20:20:22 +00:00
return item ;
2013-02-21 01:33:05 +00:00
}
2017-05-18 21:05:47 +00:00
private Guid GetItemByNameId < T > ( Func < string , string > getPathFn , string name )
where T : BaseItem , new ( )
{
var path = getPathFn ( name ) ;
2020-04-04 22:28:46 +00:00
var forceCaseInsensitiveId = _configurationManager . Configuration . EnableNormalizedItemByNameIds ;
2017-05-18 21:05:47 +00:00
return GetNewItemIdInternal ( path , typeof ( T ) , forceCaseInsensitiveId ) ;
}
2013-02-21 01:33:05 +00:00
/// <summary>
/// Validate and refresh the People sub-set of the IBN.
/// The items are stored in the db but not loaded into memory until actually requested by an operation.
/// </summary>
/// <param name="cancellationToken">The cancellation token.</param>
/// <param name="progress">The progress.</param>
/// <returns>Task.</returns>
2013-10-13 17:52:57 +00:00
public Task ValidatePeople ( CancellationToken cancellationToken , IProgress < double > progress )
2013-02-21 01:33:05 +00:00
{
2014-03-09 22:14:44 +00:00
// Ensure the location is available.
2020-04-04 22:28:46 +00:00
Directory . CreateDirectory ( _configurationManager . ApplicationPaths . PeoplePath ) ;
2014-03-02 18:01:46 +00:00
2019-02-06 19:38:42 +00:00
return new PeopleValidator ( this , _logger , _fileSystem ) . ValidatePeople ( cancellationToken , progress ) ;
2013-02-21 01:33:05 +00:00
}
/// <summary>
/// Reloads the root media folder
/// </summary>
/// <param name="progress">The progress.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Task.</returns>
2013-04-14 19:39:25 +00:00
public Task ValidateMediaLibrary ( IProgress < double > progress , CancellationToken cancellationToken )
{
// Just run the scheduled task so that the user can see it
2013-09-24 15:08:51 +00:00
_taskManager . CancelIfRunningAndQueue < RefreshMediaLibraryTask > ( ) ;
2018-09-12 17:26:21 +00:00
return Task . CompletedTask ;
2013-04-14 19:39:25 +00:00
}
2013-12-15 16:53:32 +00:00
/// <summary>
/// Queues the library scan.
/// </summary>
public void QueueLibraryScan ( )
{
// Just run the scheduled task so that the user can see it
_taskManager . QueueScheduledTask < RefreshMediaLibraryTask > ( ) ;
}
2014-02-21 05:04:11 +00:00
2013-04-14 19:39:25 +00:00
/// <summary>
/// Validates the media library internal.
/// </summary>
/// <param name="progress">The progress.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Task.</returns>
public async Task ValidateMediaLibraryInternal ( IProgress < double > progress , CancellationToken cancellationToken )
2013-11-27 02:38:11 +00:00
{
2016-04-26 03:39:21 +00:00
IsScanRunning = true ;
2020-04-04 22:28:46 +00:00
LibraryMonitor . Stop ( ) ;
2013-11-27 02:38:11 +00:00
try
{
await PerformLibraryValidation ( progress , cancellationToken ) . ConfigureAwait ( false ) ;
}
finally
{
2020-04-04 22:28:46 +00:00
LibraryMonitor . Start ( ) ;
2016-04-26 03:39:21 +00:00
IsScanRunning = false ;
2013-11-27 02:38:11 +00:00
}
}
2018-09-12 17:26:21 +00:00
private async Task ValidateTopLibraryFolders ( CancellationToken cancellationToken )
2013-02-21 01:33:05 +00:00
{
2013-02-28 19:32:41 +00:00
await RootFolder . RefreshMetadata ( cancellationToken ) . ConfigureAwait ( false ) ;
2013-02-21 01:33:05 +00:00
// Start by just validating the children of the root, but go no further
2019-03-13 21:32:52 +00:00
await RootFolder . ValidateChildren (
new SimpleProgress < double > ( ) ,
cancellationToken ,
2019-09-10 20:37:53 +00:00
new MetadataRefreshOptions ( new DirectoryService ( _fileSystem ) ) ,
2019-03-13 21:32:52 +00:00
recursive : false ) . ConfigureAwait ( false ) ;
2013-02-21 01:33:05 +00:00
2016-12-15 06:41:10 +00:00
await GetUserRootFolder ( ) . RefreshMetadata ( cancellationToken ) . ConfigureAwait ( false ) ;
2014-02-21 05:04:11 +00:00
2019-03-13 21:32:52 +00:00
await GetUserRootFolder ( ) . ValidateChildren (
new SimpleProgress < double > ( ) ,
cancellationToken ,
2019-09-10 20:37:53 +00:00
new MetadataRefreshOptions ( new DirectoryService ( _fileSystem ) ) ,
2019-03-13 21:32:52 +00:00
recursive : false ) . ConfigureAwait ( false ) ;
2013-05-25 15:16:50 +00:00
2016-12-15 06:41:10 +00:00
// Quickly scan CollectionFolders for changes
2019-03-13 21:32:52 +00:00
foreach ( var folder in GetUserRootFolder ( ) . Children . OfType < Folder > ( ) )
2016-12-15 06:41:10 +00:00
{
await folder . RefreshMetadata ( cancellationToken ) . ConfigureAwait ( false ) ;
}
2018-09-12 17:26:21 +00:00
}
private async Task PerformLibraryValidation ( IProgress < double > progress , CancellationToken cancellationToken )
{
2018-12-13 13:18:25 +00:00
_logger . LogInformation ( "Validating media library" ) ;
2018-09-12 17:26:21 +00:00
await ValidateTopLibraryFolders ( cancellationToken ) . ConfigureAwait ( false ) ;
2016-12-15 06:41:10 +00:00
2013-09-25 22:41:25 +00:00
var innerProgress = new ActionableProgress < double > ( ) ;
2020-02-22 06:04:52 +00:00
innerProgress . RegisterAction ( pct = > progress . Report ( pct * 0.96 ) ) ;
2013-04-22 04:38:03 +00:00
2020-02-01 14:36:40 +00:00
// Validate the entire media library
2019-09-10 20:37:53 +00:00
await RootFolder . ValidateChildren ( innerProgress , cancellationToken , new MetadataRefreshOptions ( new DirectoryService ( _fileSystem ) ) , recursive : true ) . ConfigureAwait ( false ) ;
2013-04-22 04:38:03 +00:00
2018-09-12 17:26:21 +00:00
progress . Report ( 96 ) ;
2013-06-23 17:48:30 +00:00
2013-09-25 22:41:25 +00:00
innerProgress = new ActionableProgress < double > ( ) ;
2018-09-12 17:26:21 +00:00
innerProgress . RegisterAction ( pct = > progress . Report ( 96 + ( pct * . 04 ) ) ) ;
2013-09-25 22:41:25 +00:00
await RunPostScanTasks ( innerProgress , cancellationToken ) . ConfigureAwait ( false ) ;
2013-04-22 04:38:03 +00:00
progress . Report ( 100 ) ;
2013-02-21 01:33:05 +00:00
}
2013-05-27 16:53:10 +00:00
/// <summary>
/// Runs the post scan tasks.
/// </summary>
/// <param name="progress">The progress.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Task.</returns>
private async Task RunPostScanTasks ( IProgress < double > progress , CancellationToken cancellationToken )
{
2013-09-25 22:41:25 +00:00
var tasks = PostscanTasks . ToList ( ) ;
2013-05-27 16:53:10 +00:00
2013-09-25 22:41:25 +00:00
var numComplete = 0 ;
var numTasks = tasks . Count ;
foreach ( var task in tasks )
2013-05-27 16:53:10 +00:00
{
var innerProgress = new ActionableProgress < double > ( ) ;
2013-09-25 22:41:25 +00:00
// Prevent access to modified closure
var currentNumComplete = numComplete ;
2013-05-27 16:53:10 +00:00
innerProgress . RegisterAction ( pct = >
{
2018-09-12 17:26:21 +00:00
double innerPercent = pct ;
innerPercent / = 100 ;
innerPercent + = currentNumComplete ;
2013-09-25 22:41:25 +00:00
innerPercent / = numTasks ;
2018-09-12 17:26:21 +00:00
innerPercent * = 100 ;
2013-09-25 22:41:25 +00:00
progress . Report ( innerPercent ) ;
2013-05-27 16:53:10 +00:00
} ) ;
2018-12-13 13:18:25 +00:00
_logger . LogDebug ( "Running post-scan task {0}" , task . GetType ( ) . Name ) ;
2015-07-10 04:44:21 +00:00
2013-05-27 16:53:10 +00:00
try
{
2015-02-08 18:21:24 +00:00
await task . Run ( innerProgress , cancellationToken ) . ConfigureAwait ( false ) ;
2013-05-27 16:53:10 +00:00
}
2013-09-18 02:43:34 +00:00
catch ( OperationCanceledException )
{
2018-12-13 13:18:25 +00:00
_logger . LogInformation ( "Post-scan task cancelled: {0}" , task . GetType ( ) . Name ) ;
2017-05-10 19:12:03 +00:00
throw ;
2013-09-18 02:43:34 +00:00
}
2013-05-27 16:53:10 +00:00
catch ( Exception ex )
{
2020-02-01 13:44:27 +00:00
_logger . LogError ( ex , "Error running post-scan task" ) ;
2013-05-27 16:53:10 +00:00
}
2013-09-25 22:41:25 +00:00
numComplete + + ;
double percent = numComplete ;
percent / = numTasks ;
progress . Report ( percent * 100 ) ;
}
2020-04-04 22:28:46 +00:00
_itemRepository . UpdateInheritedValues ( cancellationToken ) ;
2017-08-11 06:29:49 +00:00
2013-09-25 22:41:25 +00:00
progress . Report ( 100 ) ;
2013-05-25 15:16:50 +00:00
}
2013-02-21 01:33:05 +00:00
/// <summary>
/// Gets the default view.
/// </summary>
/// <returns>IEnumerable{VirtualFolderInfo}.</returns>
2017-06-23 16:04:45 +00:00
public List < VirtualFolderInfo > GetVirtualFolders ( )
2013-02-21 01:33:05 +00:00
{
2017-06-23 16:04:45 +00:00
return GetVirtualFolders ( false ) ;
2013-02-21 01:33:05 +00:00
}
2017-06-23 16:04:45 +00:00
public List < VirtualFolderInfo > GetVirtualFolders ( bool includeRefreshState )
2013-02-21 01:33:05 +00:00
{
2019-09-03 03:32:03 +00:00
_logger . LogDebug ( "Getting topLibraryFolders" ) ;
2015-10-15 17:37:27 +00:00
var topLibraryFolders = GetUserRootFolder ( ) . Children . ToList ( ) ;
2019-09-03 03:32:03 +00:00
_logger . LogDebug ( "Getting refreshQueue" ) ;
2020-04-04 22:28:46 +00:00
var refreshQueue = includeRefreshState ? ProviderManager . GetRefreshQueue ( ) : null ;
2017-06-23 16:04:45 +00:00
2020-04-04 22:28:46 +00:00
return _fileSystem . GetDirectoryPaths ( _configurationManager . ApplicationPaths . DefaultUserViewsPath )
2017-06-23 16:04:45 +00:00
. Select ( dir = > GetVirtualFolderInfo ( dir , topLibraryFolders , refreshQueue ) )
. ToList ( ) ;
2015-10-15 17:37:27 +00:00
}
2013-07-12 19:56:40 +00:00
2017-06-23 16:04:45 +00:00
private VirtualFolderInfo GetVirtualFolderInfo ( string dir , List < BaseItem > allCollectionFolders , Dictionary < Guid , Guid > refreshQueue )
2015-10-15 17:37:27 +00:00
{
var info = new VirtualFolderInfo
{
Name = Path . GetFileName ( dir ) ,
2013-07-12 19:56:40 +00:00
2016-11-03 06:37:52 +00:00
Locations = _fileSystem . GetFilePaths ( dir , false )
. Where ( i = > string . Equals ( ShortcutFileExtension , Path . GetExtension ( i ) , StringComparison . OrdinalIgnoreCase ) )
2018-09-12 17:26:21 +00:00
. Select ( i = >
{
try
{
return _appHost . ExpandVirtualPath ( _fileSystem . ResolveShortcut ( i ) ) ;
}
catch ( Exception ex )
{
2018-12-20 12:11:26 +00:00
_logger . LogError ( ex , "Error resolving shortcut file {file}" , i ) ;
2018-09-12 17:26:21 +00:00
return null ;
}
} )
. Where ( i = > i ! = null )
2015-10-15 17:37:27 +00:00
. OrderBy ( i = > i )
2017-08-19 19:43:35 +00:00
. ToArray ( ) ,
2015-10-15 17:37:27 +00:00
CollectionType = GetCollectionType ( dir )
} ;
2016-08-13 20:54:29 +00:00
var libraryFolder = allCollectionFolders . FirstOrDefault ( i = > string . Equals ( i . Path , dir , StringComparison . OrdinalIgnoreCase ) ) ;
2015-10-15 17:37:27 +00:00
if ( libraryFolder ! = null & & libraryFolder . HasImage ( ImageType . Primary ) )
{
2019-02-28 22:22:57 +00:00
info . PrimaryImageItemId = libraryFolder . Id . ToString ( "N" , CultureInfo . InvariantCulture ) ;
2015-10-15 17:37:27 +00:00
}
2015-10-16 05:36:16 +00:00
if ( libraryFolder ! = null )
{
2019-02-28 22:22:57 +00:00
info . ItemId = libraryFolder . Id . ToString ( "N" , CultureInfo . InvariantCulture ) ;
2016-10-02 04:31:47 +00:00
info . LibraryOptions = GetLibraryOptions ( libraryFolder ) ;
2017-06-23 16:04:45 +00:00
if ( refreshQueue ! = null )
{
info . RefreshProgress = libraryFolder . GetRefreshProgress ( ) ;
info . RefreshStatus = info . RefreshProgress . HasValue ? "Active" : refreshQueue . ContainsKey ( libraryFolder . Id ) ? "Queued" : "Idle" ;
}
2016-08-13 20:54:29 +00:00
}
2015-10-15 17:37:27 +00:00
return info ;
2013-02-21 01:33:05 +00:00
}
2013-02-28 19:32:41 +00:00
2013-07-12 19:56:40 +00:00
private string GetCollectionType ( string path )
{
2017-03-30 17:56:32 +00:00
return _fileSystem . GetFilePaths ( path , new [ ] { ".collection" } , true , false )
2019-03-13 21:32:52 +00:00
. Select ( Path . GetFileNameWithoutExtension )
2018-09-12 17:26:21 +00:00
. FirstOrDefault ( i = > ! string . IsNullOrEmpty ( i ) ) ;
2013-07-12 19:56:40 +00:00
}
2013-02-28 19:32:41 +00:00
/// <summary>
/// Gets the item by id.
/// </summary>
/// <param name="id">The id.</param>
/// <returns>BaseItem.</returns>
2019-01-13 20:37:13 +00:00
/// <exception cref="ArgumentNullException">id</exception>
2013-02-28 19:32:41 +00:00
public BaseItem GetItemById ( Guid id )
{
2019-02-08 21:59:28 +00:00
if ( id = = Guid . Empty )
2013-02-28 19:32:41 +00:00
{
2019-03-13 21:32:52 +00:00
throw new ArgumentException ( "Guid can't be empty" , nameof ( id ) ) ;
2013-02-28 19:32:41 +00:00
}
2013-03-02 02:44:46 +00:00
2020-04-04 22:28:46 +00:00
if ( _libraryItemsCache . TryGetValue ( id , out BaseItem item ) )
2013-05-08 20:58:52 +00:00
{
return item ;
}
2013-03-31 17:39:28 +00:00
2014-03-20 15:55:22 +00:00
item = RetrieveItem ( id ) ;
if ( item ! = null )
{
RegisterItem ( item ) ;
}
return item ;
2013-03-02 02:44:46 +00:00
}
2017-08-09 19:56:38 +00:00
public List < BaseItem > GetItemList ( InternalItemsQuery query , bool allowExternalContent )
2015-06-01 14:49:23 +00:00
{
2019-10-20 14:08:40 +00:00
if ( query . Recursive & & query . ParentId ! = Guid . Empty )
2016-06-30 14:50:08 +00:00
{
2018-09-12 17:26:21 +00:00
var parent = GetItemById ( query . ParentId ) ;
2016-06-30 14:50:08 +00:00
if ( parent ! = null )
{
SetTopParentIdsOrAncestors ( query , new List < BaseItem > { parent } ) ;
}
}
2015-11-13 20:53:29 +00:00
if ( query . User ! = null )
{
2017-06-03 07:36:32 +00:00
AddUserToQuery ( query , query . User , allowExternalContent ) ;
2015-11-13 20:53:29 +00:00
}
2020-04-04 22:28:46 +00:00
return _itemRepository . GetItemList ( query ) ;
2015-06-01 14:49:23 +00:00
}
2017-08-09 19:56:38 +00:00
public List < BaseItem > GetItemList ( InternalItemsQuery query )
2017-06-03 07:36:32 +00:00
{
return GetItemList ( query , true ) ;
}
2016-12-12 19:40:27 +00:00
public int GetCount ( InternalItemsQuery query )
{
2018-09-12 17:26:21 +00:00
if ( query . Recursive & & ! query . ParentId . Equals ( Guid . Empty ) )
2016-12-12 19:40:27 +00:00
{
2018-09-12 17:26:21 +00:00
var parent = GetItemById ( query . ParentId ) ;
2016-12-12 19:40:27 +00:00
if ( parent ! = null )
{
SetTopParentIdsOrAncestors ( query , new List < BaseItem > { parent } ) ;
}
}
if ( query . User ! = null )
{
AddUserToQuery ( query , query . User ) ;
}
2020-04-04 22:28:46 +00:00
return _itemRepository . GetCount ( query ) ;
2016-12-12 19:40:27 +00:00
}
2017-08-09 19:56:38 +00:00
public List < BaseItem > GetItemList ( InternalItemsQuery query , List < BaseItem > parents )
2016-06-30 19:01:48 +00:00
{
SetTopParentIdsOrAncestors ( query , parents ) ;
if ( query . AncestorIds . Length = = 0 & & query . TopParentIds . Length = = 0 )
{
if ( query . User ! = null )
{
AddUserToQuery ( query , query . User ) ;
}
}
2020-04-04 22:28:46 +00:00
return _itemRepository . GetItemList ( query ) ;
2016-06-30 19:01:48 +00:00
}
2015-08-19 23:57:27 +00:00
public QueryResult < BaseItem > QueryItems ( InternalItemsQuery query )
{
2015-11-13 20:53:29 +00:00
if ( query . User ! = null )
{
AddUserToQuery ( query , query . User ) ;
}
2016-06-11 20:12:01 +00:00
if ( query . EnableTotalRecordCount )
{
2020-04-04 22:28:46 +00:00
return _itemRepository . GetItems ( query ) ;
2016-06-11 20:12:01 +00:00
}
return new QueryResult < BaseItem >
{
2020-04-04 22:28:46 +00:00
Items = _itemRepository . GetItemList ( query ) . ToArray ( )
2016-06-11 20:12:01 +00:00
} ;
2015-08-19 23:57:27 +00:00
}
2015-07-07 02:25:23 +00:00
public List < Guid > GetItemIds ( InternalItemsQuery query )
{
2015-11-13 20:53:29 +00:00
if ( query . User ! = null )
{
AddUserToQuery ( query , query . User ) ;
}
2020-04-04 22:28:46 +00:00
return _itemRepository . GetItemIdsList ( query ) ;
2015-07-07 02:25:23 +00:00
}
2019-02-08 23:48:09 +00:00
public QueryResult < ( BaseItem , ItemCounts ) > GetStudios ( InternalItemsQuery query )
2016-06-17 13:06:13 +00:00
{
if ( query . User ! = null )
{
AddUserToQuery ( query , query . User ) ;
}
SetTopParentOrAncestorIds ( query ) ;
2020-04-04 22:28:46 +00:00
return _itemRepository . GetStudios ( query ) ;
2016-06-17 13:06:13 +00:00
}
2019-02-08 23:48:09 +00:00
public QueryResult < ( BaseItem , ItemCounts ) > GetGenres ( InternalItemsQuery query )
2016-06-17 13:06:13 +00:00
{
if ( query . User ! = null )
{
AddUserToQuery ( query , query . User ) ;
}
SetTopParentOrAncestorIds ( query ) ;
2020-04-04 22:28:46 +00:00
return _itemRepository . GetGenres ( query ) ;
2016-06-17 13:06:13 +00:00
}
2019-02-08 23:48:09 +00:00
public QueryResult < ( BaseItem , ItemCounts ) > GetMusicGenres ( InternalItemsQuery query )
2016-06-17 13:06:13 +00:00
{
if ( query . User ! = null )
{
AddUserToQuery ( query , query . User ) ;
}
SetTopParentOrAncestorIds ( query ) ;
2020-04-04 22:28:46 +00:00
return _itemRepository . GetMusicGenres ( query ) ;
2016-06-17 13:06:13 +00:00
}
2019-02-08 23:48:09 +00:00
public QueryResult < ( BaseItem , ItemCounts ) > GetAllArtists ( InternalItemsQuery query )
2016-08-06 04:38:01 +00:00
{
if ( query . User ! = null )
{
AddUserToQuery ( query , query . User ) ;
}
SetTopParentOrAncestorIds ( query ) ;
2020-04-04 22:28:46 +00:00
return _itemRepository . GetAllArtists ( query ) ;
2016-08-06 04:38:01 +00:00
}
2019-02-08 23:48:09 +00:00
public QueryResult < ( BaseItem , ItemCounts ) > GetArtists ( InternalItemsQuery query )
2016-06-17 13:06:13 +00:00
{
if ( query . User ! = null )
{
AddUserToQuery ( query , query . User ) ;
}
SetTopParentOrAncestorIds ( query ) ;
2020-04-04 22:28:46 +00:00
return _itemRepository . GetArtists ( query ) ;
2016-06-17 13:06:13 +00:00
}
private void SetTopParentOrAncestorIds ( InternalItemsQuery query )
{
2020-02-19 20:56:35 +00:00
var ancestorIds = query . AncestorIds ;
int len = ancestorIds . Length ;
if ( len = = 0 )
2016-06-17 13:06:13 +00:00
{
return ;
}
2020-02-19 20:56:35 +00:00
var parents = new BaseItem [ len ] ;
for ( int i = 0 ; i < len ; i + + )
2016-06-17 13:06:13 +00:00
{
2020-02-19 20:56:35 +00:00
parents [ i ] = GetItemById ( ancestorIds [ i ] ) ;
if ( ! ( parents [ i ] is ICollectionFolder | | parents [ i ] is UserView ) )
2016-12-15 06:41:10 +00:00
{
2020-02-19 20:56:35 +00:00
return ;
2016-12-15 06:41:10 +00:00
}
2016-06-17 13:06:13 +00:00
}
2020-02-19 20:56:35 +00:00
// Optimize by querying against top level views
query . TopParentIds = parents . SelectMany ( i = > GetTopParentIdsForQuery ( i , query . User ) ) . ToArray ( ) ;
query . AncestorIds = Array . Empty < Guid > ( ) ;
// Prevent searching in all libraries due to empty filter
if ( query . TopParentIds . Length = = 0 )
{
query . TopParentIds = new [ ] { Guid . NewGuid ( ) } ;
}
2016-06-17 13:06:13 +00:00
}
2019-02-08 23:48:09 +00:00
public QueryResult < ( BaseItem , ItemCounts ) > GetAlbumArtists ( InternalItemsQuery query )
2016-06-17 13:06:13 +00:00
{
if ( query . User ! = null )
{
AddUserToQuery ( query , query . User ) ;
}
SetTopParentOrAncestorIds ( query ) ;
2020-04-04 22:28:46 +00:00
return _itemRepository . GetAlbumArtists ( query ) ;
2016-06-17 13:06:13 +00:00
}
2016-03-20 06:46:51 +00:00
public QueryResult < BaseItem > GetItemsResult ( InternalItemsQuery query )
{
2018-09-12 17:26:21 +00:00
if ( query . Recursive & & ! query . ParentId . Equals ( Guid . Empty ) )
2016-03-20 06:46:51 +00:00
{
2018-09-12 17:26:21 +00:00
var parent = GetItemById ( query . ParentId ) ;
2016-03-20 06:46:51 +00:00
if ( parent ! = null )
{
SetTopParentIdsOrAncestors ( query , new List < BaseItem > { parent } ) ;
}
}
if ( query . User ! = null )
{
AddUserToQuery ( query , query . User ) ;
}
2016-05-09 03:13:38 +00:00
if ( query . EnableTotalRecordCount )
{
2020-04-04 22:28:46 +00:00
return _itemRepository . GetItems ( query ) ;
2016-06-16 13:24:12 +00:00
}
2020-04-04 22:28:46 +00:00
var list = _itemRepository . GetItemList ( query ) ;
2017-08-09 19:56:38 +00:00
2016-03-20 06:46:51 +00:00
return new QueryResult < BaseItem >
{
2019-09-02 06:19:29 +00:00
Items = list
2016-03-20 06:46:51 +00:00
} ;
}
2015-11-18 05:49:20 +00:00
private void SetTopParentIdsOrAncestors ( InternalItemsQuery query , List < BaseItem > parents )
{
2019-03-13 21:32:52 +00:00
if ( parents . All ( i = > i is ICollectionFolder | | i is UserView ) )
2015-11-18 05:49:20 +00:00
{
// Optimize by querying against top level views
2018-09-12 17:26:21 +00:00
query . TopParentIds = parents . SelectMany ( i = > GetTopParentIdsForQuery ( i , query . User ) ) . ToArray ( ) ;
2016-12-15 06:41:10 +00:00
// Prevent searching in all libraries due to empty filter
if ( query . TopParentIds . Length = = 0 )
{
2018-09-12 17:26:21 +00:00
query . TopParentIds = new [ ] { Guid . NewGuid ( ) } ;
2016-12-15 06:41:10 +00:00
}
2015-11-18 05:49:20 +00:00
}
else
{
// We need to be able to query from any arbitrary ancestor up the tree
2018-09-12 17:26:21 +00:00
query . AncestorIds = parents . SelectMany ( i = > i . GetIdsForAncestorQuery ( ) ) . ToArray ( ) ;
2016-12-15 06:41:10 +00:00
// Prevent searching in all libraries due to empty filter
if ( query . AncestorIds . Length = = 0 )
{
2018-09-12 17:26:21 +00:00
query . AncestorIds = new [ ] { Guid . NewGuid ( ) } ;
2016-12-15 06:41:10 +00:00
}
2015-11-18 05:49:20 +00:00
}
2016-12-15 06:41:10 +00:00
2017-05-23 16:43:24 +00:00
query . Parent = null ;
2015-11-18 05:49:20 +00:00
}
2017-06-03 07:36:32 +00:00
private void AddUserToQuery ( InternalItemsQuery query , User user , bool allowExternalContent = true )
2015-11-13 20:53:29 +00:00
{
2016-09-14 21:34:19 +00:00
if ( query . AncestorIds . Length = = 0 & &
2018-09-12 17:26:21 +00:00
query . ParentId . Equals ( Guid . Empty ) & &
2016-09-14 21:34:19 +00:00
query . ChannelIds . Length = = 0 & &
query . TopParentIds . Length = = 0 & &
2018-09-12 17:26:21 +00:00
string . IsNullOrEmpty ( query . AncestorWithPresentationUniqueKey ) & &
string . IsNullOrEmpty ( query . SeriesPresentationUniqueKey ) & &
2016-12-06 08:24:29 +00:00
query . ItemIds . Length = = 0 )
2015-11-13 20:53:29 +00:00
{
2020-04-04 22:28:46 +00:00
var userViews = UserViewManager . GetUserViews ( new UserViewQuery
2015-11-14 18:57:26 +00:00
{
2018-09-12 17:26:21 +00:00
UserId = user . Id ,
2017-06-03 07:36:32 +00:00
IncludeHidden = true ,
IncludeExternalContent = allowExternalContent
2018-09-12 17:26:21 +00:00
} ) ;
2015-11-13 20:53:29 +00:00
2018-09-12 17:26:21 +00:00
query . TopParentIds = userViews . SelectMany ( i = > GetTopParentIdsForQuery ( i , user ) ) . ToArray ( ) ;
2015-11-13 20:53:29 +00:00
}
}
2016-12-13 07:36:30 +00:00
private IEnumerable < Guid > GetTopParentIdsForQuery ( BaseItem item , User user )
2015-11-14 18:57:26 +00:00
{
2019-03-13 21:32:52 +00:00
if ( item is UserView view )
2015-11-14 18:57:26 +00:00
{
2019-03-13 21:32:52 +00:00
if ( string . Equals ( view . ViewType , CollectionType . LiveTv , StringComparison . Ordinal ) )
2015-11-14 18:57:26 +00:00
{
2016-12-13 07:36:30 +00:00
return new [ ] { view . Id } ;
2015-11-14 18:57:26 +00:00
}
// Translate view into folders
2018-09-12 17:26:21 +00:00
if ( ! view . DisplayParentId . Equals ( Guid . Empty ) )
2015-11-14 18:57:26 +00:00
{
var displayParent = GetItemById ( view . DisplayParentId ) ;
if ( displayParent ! = null )
{
2016-12-13 07:36:30 +00:00
return GetTopParentIdsForQuery ( displayParent , user ) ;
2015-11-14 18:57:26 +00:00
}
2019-03-13 21:32:52 +00:00
2018-09-12 17:26:21 +00:00
return Array . Empty < Guid > ( ) ;
2015-11-14 18:57:26 +00:00
}
2019-03-13 21:32:52 +00:00
2018-09-12 17:26:21 +00:00
if ( ! view . ParentId . Equals ( Guid . Empty ) )
2015-11-14 18:57:26 +00:00
{
var displayParent = GetItemById ( view . ParentId ) ;
if ( displayParent ! = null )
{
2016-12-13 07:36:30 +00:00
return GetTopParentIdsForQuery ( displayParent , user ) ;
2015-11-14 18:57:26 +00:00
}
2019-03-13 21:32:52 +00:00
2018-09-12 17:26:21 +00:00
return Array . Empty < Guid > ( ) ;
2015-11-14 18:57:26 +00:00
}
// Handle grouping
2018-09-12 17:26:21 +00:00
if ( user ! = null & & ! string . IsNullOrEmpty ( view . ViewType ) & & UserView . IsEligibleForGrouping ( view . ViewType ) & & user . Configuration . GroupedFolders . Length > 0 )
2015-11-18 05:49:20 +00:00
{
2018-09-12 17:26:21 +00:00
return GetUserRootFolder ( )
2016-05-18 05:34:10 +00:00
. GetChildren ( user , true )
. OfType < CollectionFolder > ( )
2018-09-12 17:26:21 +00:00
. Where ( i = > string . IsNullOrEmpty ( i . CollectionType ) | | string . Equals ( i . CollectionType , view . ViewType , StringComparison . OrdinalIgnoreCase ) )
2016-05-18 17:02:56 +00:00
. Where ( i = > user . IsFolderGrouped ( i . Id ) )
2016-12-13 07:36:30 +00:00
. SelectMany ( i = > GetTopParentIdsForQuery ( i , user ) ) ;
2015-11-18 05:49:20 +00:00
}
2019-03-13 21:32:52 +00:00
2018-09-12 17:26:21 +00:00
return Array . Empty < Guid > ( ) ;
2015-11-14 18:57:26 +00:00
}
2019-03-13 21:32:52 +00:00
if ( item is CollectionFolder collectionFolder )
2015-11-14 18:57:26 +00:00
{
2016-12-13 07:36:30 +00:00
return collectionFolder . PhysicalFolderIds ;
2015-11-14 18:57:26 +00:00
}
2016-12-15 06:41:10 +00:00
2015-11-14 18:57:26 +00:00
var topParent = item . GetTopParent ( ) ;
if ( topParent ! = null )
{
2016-12-13 07:36:30 +00:00
return new [ ] { topParent . Id } ;
2015-11-14 18:57:26 +00:00
}
2019-03-13 21:32:52 +00:00
2018-09-12 17:26:21 +00:00
return Array . Empty < Guid > ( ) ;
2015-11-14 18:57:26 +00:00
}
2013-03-02 02:44:46 +00:00
/// <summary>
/// Gets the intros.
/// </summary>
/// <param name="item">The item.</param>
/// <param name="user">The user.</param>
/// <returns>IEnumerable{System.String}.</returns>
2014-09-22 21:56:54 +00:00
public async Task < IEnumerable < Video > > GetIntros ( BaseItem item , User user )
2013-03-02 02:44:46 +00:00
{
2014-09-22 21:56:54 +00:00
var tasks = IntroProviders
2020-02-19 20:56:35 +00:00
. OrderBy ( i = > i . GetType ( ) . Name . Contains ( "Default" , StringComparison . OrdinalIgnoreCase ) ? 1 : 0 )
2014-09-22 21:56:54 +00:00
. Take ( 1 )
. Select ( i = > GetIntros ( i , item , user ) ) ;
var items = await Task . WhenAll ( tasks ) . ConfigureAwait ( false ) ;
return items
. SelectMany ( i = > i . ToArray ( ) )
2013-10-04 17:04:18 +00:00
. Select ( ResolveIntro )
. Where ( i = > i ! = null ) ;
}
2014-09-22 21:56:54 +00:00
/// <summary>
/// Gets the intros.
/// </summary>
/// <param name="provider">The provider.</param>
/// <param name="item">The item.</param>
/// <param name="user">The user.</param>
/// <returns>Task<IEnumerable<IntroInfo>>.</returns>
private async Task < IEnumerable < IntroInfo > > GetIntros ( IIntroProvider provider , BaseItem item , User user )
{
try
{
return await provider . GetIntros ( item , user ) . ConfigureAwait ( false ) ;
}
catch ( Exception ex )
{
2018-12-20 12:11:26 +00:00
_logger . LogError ( ex , "Error getting intros" ) ;
2014-09-22 21:56:54 +00:00
return new List < IntroInfo > ( ) ;
}
}
2013-10-04 17:04:18 +00:00
/// <summary>
/// Gets all intro files.
/// </summary>
/// <returns>IEnumerable{System.String}.</returns>
public IEnumerable < string > GetAllIntroFiles ( )
{
2014-09-23 00:04:50 +00:00
return IntroProviders . SelectMany ( i = >
{
try
{
return i . GetAllIntroFiles ( ) . ToList ( ) ;
}
catch ( Exception ex )
{
2018-12-20 12:11:26 +00:00
_logger . LogError ( ex , "Error getting intro files" ) ;
2014-09-23 00:04:50 +00:00
return new List < string > ( ) ;
}
} ) ;
2013-10-04 17:04:18 +00:00
}
/// <summary>
/// Resolves the intro.
/// </summary>
/// <param name="info">The info.</param>
/// <returns>Video.</returns>
private Video ResolveIntro ( IntroInfo info )
{
Video video = null ;
2013-10-13 17:52:57 +00:00
2013-10-04 17:04:18 +00:00
if ( info . ItemId . HasValue )
{
// Get an existing item by Id
video = GetItemById ( info . ItemId . Value ) as Video ;
if ( video = = null )
{
2018-12-20 12:11:26 +00:00
_logger . LogError ( "Unable to locate item with Id {ID}." , info . ItemId . Value ) ;
2013-10-04 17:04:18 +00:00
}
}
else if ( ! string . IsNullOrEmpty ( info . Path ) )
{
try
{
2019-01-07 23:27:46 +00:00
// Try to resolve the path into a video
2013-10-30 14:40:14 +00:00
video = ResolvePath ( _fileSystem . GetFileSystemInfo ( info . Path ) ) as Video ;
2013-10-04 17:04:18 +00:00
if ( video = = null )
{
2018-12-20 12:11:26 +00:00
_logger . LogError ( "Intro resolver returned null for {path}." , info . Path ) ;
2013-10-04 17:04:18 +00:00
}
else
{
// Pull the saved db item that will include metadata
var dbItem = GetItemById ( video . Id ) as Video ;
if ( dbItem ! = null )
{
video = dbItem ;
}
2016-02-05 05:59:22 +00:00
else
{
return null ;
}
2013-10-04 17:04:18 +00:00
}
}
catch ( Exception ex )
{
2018-12-20 12:11:26 +00:00
_logger . LogError ( ex , "Error resolving path {path}." , info . Path ) ;
2013-10-04 17:04:18 +00:00
}
}
else
{
2018-12-13 13:18:25 +00:00
_logger . LogError ( "IntroProvider returned an IntroInfo with null Path and ItemId." ) ;
2013-10-04 17:04:18 +00:00
}
return video ;
2013-02-28 19:32:41 +00:00
}
2013-03-10 04:22:36 +00:00
/// <summary>
/// Sorts the specified sort by.
/// </summary>
/// <param name="items">The items.</param>
/// <param name="user">The user.</param>
/// <param name="sortBy">The sort by.</param>
/// <param name="sortOrder">The sort order.</param>
/// <returns>IEnumerable{BaseItem}.</returns>
public IEnumerable < BaseItem > Sort ( IEnumerable < BaseItem > items , User user , IEnumerable < string > sortBy , SortOrder sortOrder )
{
var isFirst = true ;
IOrderedEnumerable < BaseItem > orderedItems = null ;
foreach ( var orderBy in sortBy . Select ( o = > GetComparer ( o , user ) ) . Where ( c = > c ! = null ) )
{
if ( isFirst )
{
orderedItems = sortOrder = = SortOrder . Descending ? items . OrderByDescending ( i = > i , orderBy ) : items . OrderBy ( i = > i , orderBy ) ;
}
else
{
orderedItems = sortOrder = = SortOrder . Descending ? orderedItems . ThenByDescending ( i = > i , orderBy ) : orderedItems . ThenBy ( i = > i , orderBy ) ;
}
isFirst = false ;
}
return orderedItems ? ? items ;
}
2018-09-12 17:26:21 +00:00
public IEnumerable < BaseItem > Sort ( IEnumerable < BaseItem > items , User user , IEnumerable < ValueTuple < string , SortOrder > > orderByList )
2017-09-04 19:28:22 +00:00
{
var isFirst = true ;
IOrderedEnumerable < BaseItem > orderedItems = null ;
foreach ( var orderBy in orderByList )
{
var comparer = GetComparer ( orderBy . Item1 , user ) ;
if ( comparer = = null )
{
continue ;
}
var sortOrder = orderBy . Item2 ;
if ( isFirst )
{
orderedItems = sortOrder = = SortOrder . Descending ? items . OrderByDescending ( i = > i , comparer ) : items . OrderBy ( i = > i , comparer ) ;
}
else
{
orderedItems = sortOrder = = SortOrder . Descending ? orderedItems . ThenByDescending ( i = > i , comparer ) : orderedItems . ThenBy ( i = > i , comparer ) ;
}
isFirst = false ;
}
return orderedItems ? ? items ;
}
2013-03-10 04:22:36 +00:00
/// <summary>
/// Gets the comparer.
/// </summary>
/// <param name="name">The name.</param>
/// <param name="user">The user.</param>
/// <returns>IBaseItemComparer.</returns>
private IBaseItemComparer GetComparer ( string name , User user )
{
var comparer = Comparers . FirstOrDefault ( c = > string . Equals ( name , c . Name , StringComparison . OrdinalIgnoreCase ) ) ;
2019-03-13 21:32:52 +00:00
// If it requires a user, create a new one, and assign the user
if ( comparer is IUserBaseItemComparer )
2013-03-10 04:22:36 +00:00
{
2019-03-13 21:32:52 +00:00
var userComparer = ( IUserBaseItemComparer ) Activator . CreateInstance ( comparer . GetType ( ) ) ;
2013-03-10 04:22:36 +00:00
2019-03-13 21:32:52 +00:00
userComparer . User = user ;
userComparer . UserManager = _userManager ;
userComparer . UserDataRepository = _userDataRepository ;
2013-03-10 04:22:36 +00:00
2019-03-13 21:32:52 +00:00
return userComparer ;
2013-03-10 04:22:36 +00:00
}
return comparer ;
}
2013-04-08 15:55:53 +00:00
2013-05-03 04:10:11 +00:00
/// <summary>
/// Creates the item.
/// </summary>
/// <param name="item">The item.</param>
2019-01-06 20:50:43 +00:00
/// <param name="parent">The parent item.</param>
2018-09-12 17:26:21 +00:00
public void CreateItem ( BaseItem item , BaseItem parent )
2013-05-03 04:10:11 +00:00
{
2018-09-12 17:26:21 +00:00
CreateItems ( new [ ] { item } , parent , CancellationToken . None ) ;
2013-05-23 15:39:48 +00:00
}
2013-05-03 04:10:11 +00:00
2013-05-23 15:39:48 +00:00
/// <summary>
/// Creates the items.
/// </summary>
/// <param name="items">The items.</param>
2019-03-13 21:32:52 +00:00
/// <param name="parent">The parent item</param>
2013-05-23 15:39:48 +00:00
/// <param name="cancellationToken">The cancellation token.</param>
2017-11-26 04:48:12 +00:00
public void CreateItems ( IEnumerable < BaseItem > items , BaseItem parent , CancellationToken cancellationToken )
2013-05-23 15:39:48 +00:00
{
2019-03-13 21:32:52 +00:00
// Don't iterate multiple times
var itemsList = items . ToList ( ) ;
2020-04-04 22:28:46 +00:00
_itemRepository . SaveItems ( itemsList , cancellationToken ) ;
2013-05-23 15:39:48 +00:00
2019-03-13 21:32:52 +00:00
foreach ( var item in itemsList )
2013-05-23 15:39:48 +00:00
{
2016-10-09 07:18:43 +00:00
RegisterItem ( item ) ;
2013-05-23 15:39:48 +00:00
}
2013-05-22 03:42:25 +00:00
2013-05-03 04:10:11 +00:00
if ( ItemAdded ! = null )
{
2019-03-13 21:32:52 +00:00
foreach ( var item in itemsList )
2013-05-03 17:26:44 +00:00
{
2018-09-12 17:26:21 +00:00
// With the live tv guide this just creates too much noise
if ( item . SourceType ! = SourceType . Library )
{
continue ;
}
2013-05-23 15:39:48 +00:00
try
{
2019-03-13 21:32:52 +00:00
ItemAdded (
this ,
new ItemChangeEventArgs
{
Item = item ,
Parent = parent ? ? item . GetParent ( )
} ) ;
2013-05-23 15:39:48 +00:00
}
catch ( Exception ex )
{
2018-12-20 12:11:26 +00:00
_logger . LogError ( ex , "Error in ItemAdded event handler" ) ;
2013-05-23 15:39:48 +00:00
}
2013-05-03 17:26:44 +00:00
}
2013-05-03 04:10:11 +00:00
}
}
2017-10-22 06:22:43 +00:00
public void UpdateImages ( BaseItem item )
{
2020-04-04 22:28:46 +00:00
_itemRepository . SaveImages ( item ) ;
2017-10-22 06:22:43 +00:00
RegisterItem ( item ) ;
}
2013-05-03 04:10:11 +00:00
/// <summary>
2013-06-25 01:22:21 +00:00
/// Updates the item.
2013-05-03 04:10:11 +00:00
/// </summary>
2019-02-08 23:57:58 +00:00
public void UpdateItems ( IEnumerable < BaseItem > items , BaseItem parent , ItemUpdateType updateReason , CancellationToken cancellationToken )
2013-05-03 04:10:11 +00:00
{
2019-03-13 21:32:52 +00:00
// Don't iterate multiple times
var itemsList = items . ToList ( ) ;
foreach ( var item in itemsList )
2013-05-24 15:01:53 +00:00
{
2018-09-12 17:26:21 +00:00
if ( item . IsFileProtocol )
{
2020-04-04 22:28:46 +00:00
ProviderManager . SaveMetadata ( item , updateReason ) ;
2018-09-12 17:26:21 +00:00
}
2014-02-21 05:04:11 +00:00
2018-09-12 17:26:21 +00:00
item . DateLastSaved = DateTime . UtcNow ;
2013-12-06 15:59:40 +00:00
2018-09-12 17:26:21 +00:00
RegisterItem ( item ) ;
}
2014-02-06 04:39:16 +00:00
2020-04-04 22:28:46 +00:00
_itemRepository . SaveItems ( itemsList , cancellationToken ) ;
2013-09-25 19:59:02 +00:00
2013-06-25 01:22:21 +00:00
if ( ItemUpdated ! = null )
{
2019-03-13 21:32:52 +00:00
foreach ( var item in itemsList )
2013-06-25 01:22:21 +00:00
{
2018-09-12 17:26:21 +00:00
// With the live tv guide this just creates too much noise
if ( item . SourceType ! = SourceType . Library )
2013-11-13 16:45:41 +00:00
{
2018-09-12 17:26:21 +00:00
continue ;
}
try
{
2019-03-13 21:32:52 +00:00
ItemUpdated (
this ,
new ItemChangeEventArgs
{
Item = item ,
Parent = parent ,
UpdateReason = updateReason
} ) ;
2018-09-12 17:26:21 +00:00
}
catch ( Exception ex )
{
2018-12-20 12:11:26 +00:00
_logger . LogError ( ex , "Error in ItemUpdated event handler" ) ;
2018-09-12 17:26:21 +00:00
}
2013-06-25 01:22:21 +00:00
}
}
2013-05-24 15:01:53 +00:00
}
2018-09-12 17:26:21 +00:00
/// <summary>
/// Updates the item.
/// </summary>
/// <param name="item">The item.</param>
2019-03-13 21:32:52 +00:00
/// <param name="parent">The parent item.</param>
2018-09-12 17:26:21 +00:00
/// <param name="updateReason">The update reason.</param>
/// <param name="cancellationToken">The cancellation token.</param>
public void UpdateItem ( BaseItem item , BaseItem parent , ItemUpdateType updateReason , CancellationToken cancellationToken )
{
2019-10-11 10:24:55 +00:00
UpdateItems ( new [ ] { item } , parent , updateReason , cancellationToken ) ;
2018-09-12 17:26:21 +00:00
}
2013-05-03 04:10:11 +00:00
/// <summary>
/// Reports the item removed.
/// </summary>
/// <param name="item">The item.</param>
2019-03-13 21:32:52 +00:00
/// <param name="parent">The parent item.</param>
2017-11-26 04:48:12 +00:00
public void ReportItemRemoved ( BaseItem item , BaseItem parent )
2013-05-03 04:10:11 +00:00
{
if ( ItemRemoved ! = null )
{
2013-05-03 17:26:44 +00:00
try
{
2019-03-13 21:32:52 +00:00
ItemRemoved (
this ,
new ItemChangeEventArgs
{
Item = item ,
Parent = parent
} ) ;
2013-05-03 17:26:44 +00:00
}
catch ( Exception ex )
{
2018-12-20 12:11:26 +00:00
_logger . LogError ( ex , "Error in ItemRemoved event handler" ) ;
2013-05-03 17:26:44 +00:00
}
2013-05-03 04:10:11 +00:00
}
}
2013-04-08 15:55:53 +00:00
/// <summary>
/// Retrieves the item.
/// </summary>
/// <param name="id">The id.</param>
2013-06-17 20:35:43 +00:00
/// <returns>BaseItem.</returns>
2013-06-26 16:08:16 +00:00
public BaseItem RetrieveItem ( Guid id )
2013-04-08 15:55:53 +00:00
{
2020-04-04 22:28:46 +00:00
return _itemRepository . RetrieveItem ( id ) ;
2013-11-21 20:48:26 +00:00
}
2017-01-29 20:00:29 +00:00
public List < Folder > GetCollectionFolders ( BaseItem item )
2015-02-15 03:36:07 +00:00
{
2017-01-29 20:00:29 +00:00
while ( item ! = null )
2015-02-15 03:36:07 +00:00
{
2017-01-29 20:00:29 +00:00
var parent = item . GetParent ( ) ;
if ( parent = = null | | parent is AggregateFolder )
{
break ;
}
item = parent ;
2015-02-15 03:36:07 +00:00
}
if ( item = = null )
{
return new List < Folder > ( ) ;
}
2017-05-04 18:14:45 +00:00
return GetCollectionFoldersInternal ( item , GetUserRootFolder ( ) . Children . OfType < Folder > ( ) . ToList ( ) ) ;
}
public List < Folder > GetCollectionFolders ( BaseItem item , List < Folder > allUserRootChildren )
{
while ( item ! = null )
{
var parent = item . GetParent ( ) ;
if ( parent = = null | | parent is AggregateFolder )
{
break ;
}
item = parent ;
}
if ( item = = null )
{
return new List < Folder > ( ) ;
}
return GetCollectionFoldersInternal ( item , allUserRootChildren ) ;
}
2019-01-06 20:50:43 +00:00
private static List < Folder > GetCollectionFoldersInternal ( BaseItem item , List < Folder > allUserRootChildren )
2017-05-04 18:14:45 +00:00
{
return allUserRootChildren
2017-01-29 20:00:29 +00:00
. Where ( i = > string . Equals ( i . Path , item . Path , StringComparison . OrdinalIgnoreCase ) | | i . PhysicalLocations . Contains ( item . Path , StringComparer . OrdinalIgnoreCase ) )
. ToList ( ) ;
2015-02-15 03:36:07 +00:00
}
2016-08-13 05:49:00 +00:00
public LibraryOptions GetLibraryOptions ( BaseItem item )
{
2019-09-02 06:19:29 +00:00
if ( ! ( item is CollectionFolder collectionFolder ) )
2016-10-02 04:31:47 +00:00
{
collectionFolder = GetCollectionFolders ( item )
. OfType < CollectionFolder > ( )
. FirstOrDefault ( ) ;
}
2019-02-08 21:59:28 +00:00
return collectionFolder = = null ? new LibraryOptions ( ) : collectionFolder . GetLibraryOptions ( ) ;
2016-08-13 05:49:00 +00:00
}
2014-12-21 18:58:17 +00:00
public string GetContentType ( BaseItem item )
{
2015-01-10 01:38:01 +00:00
string configuredContentType = GetConfiguredContentType ( item , false ) ;
2018-09-12 17:26:21 +00:00
if ( ! string . IsNullOrEmpty ( configuredContentType ) )
2014-12-22 06:50:29 +00:00
{
2015-01-10 01:38:01 +00:00
return configuredContentType ;
2014-12-22 06:50:29 +00:00
}
2019-02-08 21:59:28 +00:00
2015-01-10 01:38:01 +00:00
configuredContentType = GetConfiguredContentType ( item , true ) ;
2018-09-12 17:26:21 +00:00
if ( ! string . IsNullOrEmpty ( configuredContentType ) )
2015-01-10 01:38:01 +00:00
{
return configuredContentType ;
}
2019-02-08 21:59:28 +00:00
2015-01-10 01:38:01 +00:00
return GetInheritedContentType ( item ) ;
2015-01-02 05:36:27 +00:00
}
public string GetInheritedContentType ( BaseItem item )
{
var type = GetTopFolderContentType ( item ) ;
2014-12-22 06:50:29 +00:00
2018-09-12 17:26:21 +00:00
if ( ! string . IsNullOrEmpty ( type ) )
2014-12-22 06:50:29 +00:00
{
return type ;
}
2015-11-13 20:53:29 +00:00
return item . GetParents ( )
2014-12-22 06:50:29 +00:00
. Select ( GetConfiguredContentType )
2018-09-12 17:26:21 +00:00
. LastOrDefault ( i = > ! string . IsNullOrEmpty ( i ) ) ;
2014-12-22 06:50:29 +00:00
}
2015-01-10 01:38:01 +00:00
public string GetConfiguredContentType ( BaseItem item )
2014-12-22 06:50:29 +00:00
{
2015-01-10 01:38:01 +00:00
return GetConfiguredContentType ( item , false ) ;
2014-12-22 06:50:29 +00:00
}
2015-01-10 01:38:01 +00:00
public string GetConfiguredContentType ( string path )
2014-12-22 06:50:29 +00:00
{
2015-01-10 01:38:01 +00:00
return GetContentTypeOverride ( path , false ) ;
}
2014-12-22 06:50:29 +00:00
2015-01-10 01:38:01 +00:00
public string GetConfiguredContentType ( BaseItem item , bool inheritConfiguredPath )
{
2019-03-13 21:32:52 +00:00
if ( item is ICollectionFolder collectionFolder )
2015-01-10 01:38:01 +00:00
{
return collectionFolder . CollectionType ;
}
2019-02-08 21:59:28 +00:00
2015-01-10 01:38:01 +00:00
return GetContentTypeOverride ( item . ContainingFolderPath , inheritConfiguredPath ) ;
2014-12-21 18:58:17 +00:00
}
2015-01-10 01:38:01 +00:00
private string GetContentTypeOverride ( string path , bool inherit )
{
2020-04-04 22:28:46 +00:00
var nameValuePair = _configurationManager . Configuration . ContentTypes
2019-03-13 21:32:52 +00:00
. FirstOrDefault ( i = > _fileSystem . AreEqual ( i . Name , path )
| | ( inherit & & ! string . IsNullOrEmpty ( i . Name )
& & _fileSystem . ContainsSubPath ( i . Name , path ) ) ) ;
return nameValuePair ? . Value ;
2015-01-10 01:38:01 +00:00
}
2015-02-15 03:36:07 +00:00
2014-12-21 18:58:17 +00:00
private string GetTopFolderContentType ( BaseItem item )
2013-07-12 19:56:40 +00:00
{
2015-11-14 18:57:26 +00:00
if ( item = = null )
2013-07-12 19:56:40 +00:00
{
2015-11-14 18:57:26 +00:00
return null ;
2013-07-12 19:56:40 +00:00
}
2018-09-12 17:26:21 +00:00
while ( ! item . ParentId . Equals ( Guid . Empty ) )
2013-07-12 19:56:40 +00:00
{
2018-09-12 17:26:21 +00:00
var parent = item . GetParent ( ) ;
if ( parent = = null | | parent is AggregateFolder )
{
break ;
}
2019-03-13 21:32:52 +00:00
2018-09-12 17:26:21 +00:00
item = parent ;
2013-07-12 19:56:40 +00:00
}
2014-12-21 01:23:56 +00:00
return GetUserRootFolder ( ) . Children
2014-10-12 01:46:02 +00:00
. OfType < ICollectionFolder > ( )
2014-12-21 01:23:56 +00:00
. Where ( i = > string . Equals ( i . Path , item . Path , StringComparison . OrdinalIgnoreCase ) | | i . PhysicalLocations . Contains ( item . Path ) )
2013-07-12 19:56:40 +00:00
. Select ( i = > i . CollectionType )
2018-09-12 17:26:21 +00:00
. FirstOrDefault ( i = > ! string . IsNullOrEmpty ( i ) ) ;
2013-07-12 19:56:40 +00:00
}
2013-11-21 20:48:26 +00:00
2015-09-17 01:33:46 +00:00
private readonly TimeSpan _viewRefreshInterval = TimeSpan . FromHours ( 24 ) ;
2015-05-08 19:10:53 +00:00
2019-03-13 21:32:52 +00:00
public UserView GetNamedView (
User user ,
2015-03-14 04:50:23 +00:00
string name ,
string viewType ,
2018-09-12 17:26:21 +00:00
string sortName )
2014-06-07 19:46:24 +00:00
{
2018-09-12 17:26:21 +00:00
return GetNamedView ( user , name , Guid . Empty , viewType , sortName ) ;
2015-08-14 18:00:26 +00:00
}
2019-03-13 21:32:52 +00:00
public UserView GetNamedView (
string name ,
2015-08-14 18:00:26 +00:00
string viewType ,
2018-09-12 17:26:21 +00:00
string sortName )
2015-08-14 18:00:26 +00:00
{
2019-03-13 21:32:52 +00:00
var path = Path . Combine (
2020-04-04 22:28:46 +00:00
_configurationManager . ApplicationPaths . InternalMetadataPath ,
2019-03-13 21:32:52 +00:00
"views" ,
_fileSystem . GetValidFilename ( viewType ) ) ;
2014-06-07 19:46:24 +00:00
2014-11-30 19:01:33 +00:00
var id = GetNewItemId ( path + "_namedview_" + name , typeof ( UserView ) ) ;
2014-07-26 17:30:15 +00:00
var item = GetItemById ( id ) as UserView ;
2014-09-05 03:48:53 +00:00
2014-10-29 22:01:02 +00:00
var refresh = false ;
2015-11-18 05:49:20 +00:00
if ( item = = null | | ! string . Equals ( item . Path , path , StringComparison . OrdinalIgnoreCase ) )
2014-06-07 19:46:24 +00:00
{
2019-01-26 21:08:04 +00:00
Directory . CreateDirectory ( path ) ;
2014-06-07 19:46:24 +00:00
item = new UserView
{
Path = path ,
2014-07-26 17:30:15 +00:00
Id = id ,
2014-06-07 19:46:24 +00:00
DateCreated = DateTime . UtcNow ,
Name = name ,
2015-03-14 04:50:23 +00:00
ViewType = viewType ,
2014-06-07 19:46:24 +00:00
ForcedSortName = sortName
} ;
2018-09-12 17:26:21 +00:00
CreateItem ( item , null ) ;
2014-06-07 19:46:24 +00:00
2014-10-29 22:01:02 +00:00
refresh = true ;
}
if ( refresh )
{
2017-10-03 18:39:37 +00:00
item . UpdateToRepository ( ItemUpdateType . MetadataImport , CancellationToken . None ) ;
2020-04-04 22:28:46 +00:00
ProviderManager . QueueRefresh ( item . Id , new MetadataRefreshOptions ( new DirectoryService ( _fileSystem ) ) , RefreshPriority . Normal ) ;
2014-10-29 22:01:02 +00:00
}
return item ;
}
2019-03-13 21:32:52 +00:00
public UserView GetNamedView (
User user ,
2015-03-14 04:50:23 +00:00
string name ,
2018-09-12 17:26:21 +00:00
Guid parentId ,
2015-03-14 04:50:23 +00:00
string viewType ,
2018-09-12 17:26:21 +00:00
string sortName )
2015-03-14 04:50:23 +00:00
{
2019-02-28 22:22:57 +00:00
var parentIdString = parentId . Equals ( Guid . Empty ) ? null : parentId . ToString ( "N" , CultureInfo . InvariantCulture ) ;
var idValues = "38_namedview_" + name + user . Id . ToString ( "N" , CultureInfo . InvariantCulture ) + ( parentIdString ? ? string . Empty ) + ( viewType ? ? string . Empty ) ;
2015-04-20 18:04:02 +00:00
var id = GetNewItemId ( idValues , typeof ( UserView ) ) ;
2014-10-29 22:01:02 +00:00
2020-04-04 22:28:46 +00:00
var path = Path . Combine ( _configurationManager . ApplicationPaths . InternalMetadataPath , "views" , id . ToString ( "N" , CultureInfo . InvariantCulture ) ) ;
2014-10-29 22:01:02 +00:00
var item = GetItemById ( id ) as UserView ;
2015-03-14 17:02:51 +00:00
var isNew = false ;
2014-10-29 22:01:02 +00:00
if ( item = = null )
{
2019-01-26 21:08:04 +00:00
Directory . CreateDirectory ( path ) ;
2014-10-29 22:01:02 +00:00
item = new UserView
{
Path = path ,
Id = id ,
DateCreated = DateTime . UtcNow ,
Name = name ,
ViewType = viewType ,
2015-09-15 18:09:44 +00:00
ForcedSortName = sortName ,
2019-03-13 21:32:52 +00:00
UserId = user . Id ,
DisplayParentId = parentId
2014-10-29 22:01:02 +00:00
} ;
2018-09-12 17:26:21 +00:00
CreateItem ( item , null ) ;
2015-08-14 18:00:26 +00:00
isNew = true ;
}
2016-03-27 21:11:27 +00:00
var refresh = isNew | | DateTime . UtcNow - item . DateLastRefreshed > = _viewRefreshInterval ;
2015-08-14 18:00:26 +00:00
2018-09-12 17:26:21 +00:00
if ( ! refresh & & ! item . DisplayParentId . Equals ( Guid . Empty ) )
2015-10-18 01:18:29 +00:00
{
var displayParent = GetItemById ( item . DisplayParentId ) ;
refresh = displayParent ! = null & & displayParent . DateLastSaved > item . DateLastRefreshed ;
}
2015-08-14 18:00:26 +00:00
if ( refresh )
{
2020-04-04 22:28:46 +00:00
ProviderManager . QueueRefresh (
2019-03-13 21:32:52 +00:00
item . Id ,
2019-09-10 20:37:53 +00:00
new MetadataRefreshOptions ( new DirectoryService ( _fileSystem ) )
2019-03-13 21:32:52 +00:00
{
// Need to force save to increment DateLastSaved
ForceSave = true
} ,
RefreshPriority . Normal ) ;
2015-08-14 18:00:26 +00:00
}
return item ;
}
2019-03-13 21:32:52 +00:00
public UserView GetShadowView (
BaseItem parent ,
string viewType ,
string sortName )
2015-10-16 04:46:41 +00:00
{
if ( parent = = null )
{
2019-01-06 20:50:43 +00:00
throw new ArgumentNullException ( nameof ( parent ) ) ;
2015-10-16 04:46:41 +00:00
}
var name = parent . Name ;
var parentId = parent . Id ;
2015-11-18 05:49:20 +00:00
var idValues = "38_namedview_" + name + parentId + ( viewType ? ? string . Empty ) ;
2015-10-16 04:46:41 +00:00
var id = GetNewItemId ( idValues , typeof ( UserView ) ) ;
var path = parent . Path ;
var item = GetItemById ( id ) as UserView ;
var isNew = false ;
if ( item = = null )
{
2019-01-26 21:08:04 +00:00
Directory . CreateDirectory ( path ) ;
2015-10-16 04:46:41 +00:00
item = new UserView
{
Path = path ,
Id = id ,
DateCreated = DateTime . UtcNow ,
Name = name ,
ViewType = viewType ,
ForcedSortName = sortName
} ;
item . DisplayParentId = parentId ;
2018-09-12 17:26:21 +00:00
CreateItem ( item , null ) ;
2015-10-16 04:46:41 +00:00
isNew = true ;
}
2016-03-27 21:11:27 +00:00
var refresh = isNew | | DateTime . UtcNow - item . DateLastRefreshed > = _viewRefreshInterval ;
2015-10-16 04:46:41 +00:00
2018-09-12 17:26:21 +00:00
if ( ! refresh & & ! item . DisplayParentId . Equals ( Guid . Empty ) )
2015-10-18 01:18:29 +00:00
{
var displayParent = GetItemById ( item . DisplayParentId ) ;
refresh = displayParent ! = null & & displayParent . DateLastSaved > item . DateLastRefreshed ;
}
2015-10-16 04:46:41 +00:00
if ( refresh )
{
2020-04-04 22:28:46 +00:00
ProviderManager . QueueRefresh (
2019-03-13 21:32:52 +00:00
item . Id ,
2019-09-10 20:37:53 +00:00
new MetadataRefreshOptions ( new DirectoryService ( _fileSystem ) )
2019-03-13 21:32:52 +00:00
{
// Need to force save to increment DateLastSaved
ForceSave = true
} ,
RefreshPriority . Normal ) ;
2015-10-16 04:46:41 +00:00
}
return item ;
}
2015-11-13 20:53:29 +00:00
2019-03-13 21:32:52 +00:00
public UserView GetNamedView (
string name ,
2018-09-12 17:26:21 +00:00
Guid parentId ,
2015-08-14 18:00:26 +00:00
string viewType ,
string sortName ,
2018-09-12 17:26:21 +00:00
string uniqueId )
2015-08-14 18:00:26 +00:00
{
2018-09-12 17:26:21 +00:00
if ( string . IsNullOrEmpty ( name ) )
2015-08-14 18:00:26 +00:00
{
2019-01-06 20:50:43 +00:00
throw new ArgumentNullException ( nameof ( name ) ) ;
2015-08-14 18:00:26 +00:00
}
2019-02-28 22:22:57 +00:00
var parentIdString = parentId . Equals ( Guid . Empty ) ? null : parentId . ToString ( "N" , CultureInfo . InvariantCulture ) ;
2018-09-12 17:26:21 +00:00
var idValues = "37_namedview_" + name + ( parentIdString ? ? string . Empty ) + ( viewType ? ? string . Empty ) ;
if ( ! string . IsNullOrEmpty ( uniqueId ) )
2015-08-14 18:00:26 +00:00
{
idValues + = uniqueId ;
}
var id = GetNewItemId ( idValues , typeof ( UserView ) ) ;
2020-04-04 22:28:46 +00:00
var path = Path . Combine ( _configurationManager . ApplicationPaths . InternalMetadataPath , "views" , id . ToString ( "N" , CultureInfo . InvariantCulture ) ) ;
2015-08-14 18:00:26 +00:00
var item = GetItemById ( id ) as UserView ;
var isNew = false ;
if ( item = = null )
{
2019-01-26 21:08:04 +00:00
Directory . CreateDirectory ( path ) ;
2015-08-14 18:00:26 +00:00
item = new UserView
{
Path = path ,
Id = id ,
DateCreated = DateTime . UtcNow ,
Name = name ,
ViewType = viewType ,
ForcedSortName = sortName
} ;
2018-09-12 17:26:21 +00:00
item . DisplayParentId = parentId ;
2015-03-14 04:50:23 +00:00
2018-09-12 17:26:21 +00:00
CreateItem ( item , null ) ;
2014-10-29 22:01:02 +00:00
2015-03-14 17:02:51 +00:00
isNew = true ;
2014-10-29 22:01:02 +00:00
}
2015-04-15 21:59:20 +00:00
if ( ! string . Equals ( viewType , item . ViewType , StringComparison . OrdinalIgnoreCase ) )
{
item . ViewType = viewType ;
2018-09-12 17:26:21 +00:00
item . UpdateToRepository ( ItemUpdateType . MetadataEdit , CancellationToken . None ) ;
2015-04-15 21:59:20 +00:00
}
2016-03-27 21:11:27 +00:00
var refresh = isNew | | DateTime . UtcNow - item . DateLastRefreshed > = _viewRefreshInterval ;
2014-10-29 22:01:02 +00:00
2018-09-12 17:26:21 +00:00
if ( ! refresh & & ! item . DisplayParentId . Equals ( Guid . Empty ) )
2015-10-18 01:18:29 +00:00
{
var displayParent = GetItemById ( item . DisplayParentId ) ;
refresh = displayParent ! = null & & displayParent . DateLastSaved > item . DateLastRefreshed ;
}
2014-10-29 22:01:02 +00:00
if ( refresh )
{
2020-04-04 22:28:46 +00:00
ProviderManager . QueueRefresh (
2019-03-13 21:32:52 +00:00
item . Id ,
2019-09-10 20:37:53 +00:00
new MetadataRefreshOptions ( new DirectoryService ( _fileSystem ) )
2019-03-13 21:32:52 +00:00
{
// Need to force save to increment DateLastSaved
ForceSave = true
} ,
RefreshPriority . Normal ) ;
2014-06-07 19:46:24 +00:00
}
return item ;
}
2014-11-16 20:44:08 +00:00
2019-03-13 21:32:52 +00:00
public void AddExternalSubtitleStreams (
List < MediaStream > streams ,
2018-09-12 17:26:21 +00:00
string videoPath ,
string [ ] files )
{
2019-01-13 19:21:32 +00:00
new SubtitleResolver ( BaseItem . LocalizationManager , _fileSystem ) . AddExternalSubtitleStreams ( streams , videoPath , streams . Count , files ) ;
2018-09-12 17:26:21 +00:00
}
2020-02-19 20:56:35 +00:00
/// <inheritdoc />
public bool IsVideoFile ( string path )
2014-11-16 20:44:08 +00:00
{
2017-09-10 20:40:31 +00:00
var resolver = new VideoResolver ( GetNamingOptions ( ) ) ;
2014-11-16 22:46:01 +00:00
return resolver . IsVideoFile ( path ) ;
2014-11-16 20:44:08 +00:00
}
2020-02-19 20:56:35 +00:00
/// <inheritdoc />
2016-08-13 05:49:00 +00:00
public bool IsAudioFile ( string path )
2020-02-19 20:56:35 +00:00
= > AudioFileParser . IsAudioFile ( path , GetNamingOptions ( ) ) ;
2016-08-13 05:49:00 +00:00
2020-02-19 20:56:35 +00:00
/// <inheritdoc />
2014-11-16 20:44:08 +00:00
public int? GetSeasonNumberFromPath ( string path )
2020-02-19 20:56:35 +00:00
= > SeasonPathParser . Parse ( path , true , true ) . SeasonNumber ;
2014-11-16 20:44:08 +00:00
2020-02-19 20:56:35 +00:00
/// <inheritdoc />
2018-09-12 17:26:21 +00:00
public bool FillMissingEpisodeNumbersFromPath ( Episode episode , bool forceRefresh )
2014-11-16 20:44:08 +00:00
{
2018-09-12 17:26:21 +00:00
var series = episode . Series ;
bool? isAbsoluteNaming = series = = null ? false : string . Equals ( series . DisplayOrder , "absolute" , StringComparison . OrdinalIgnoreCase ) ;
if ( ! isAbsoluteNaming . Value )
{
// In other words, no filter applied
isAbsoluteNaming = null ;
}
2017-09-10 20:40:31 +00:00
var resolver = new EpisodeResolver ( GetNamingOptions ( ) ) ;
2014-11-16 20:44:08 +00:00
2017-08-07 20:36:41 +00:00
var isFolder = episode . VideoType = = VideoType . BluRay | | episode . VideoType = = VideoType . Dvd ;
2014-12-23 03:58:14 +00:00
2018-09-12 17:26:21 +00:00
var episodeInfo = episode . IsFileProtocol ?
resolver . Resolve ( episode . Path , isFolder , null , null , isAbsoluteNaming ) :
2019-01-13 20:37:13 +00:00
new Naming . TV . EpisodeInfo ( ) ;
2014-11-16 20:44:08 +00:00
2014-12-19 04:20:07 +00:00
if ( episodeInfo = = null )
{
2019-01-13 20:37:13 +00:00
episodeInfo = new Naming . TV . EpisodeInfo ( ) ;
2014-12-19 04:20:07 +00:00
}
2020-02-19 07:39:01 +00:00
try
{
var libraryOptions = GetLibraryOptions ( episode ) ;
2020-02-19 12:37:36 +00:00
if ( libraryOptions . EnableEmbeddedEpisodeInfos & & string . Equals ( episodeInfo . Container , "mp4" , StringComparison . OrdinalIgnoreCase ) )
{
2020-02-19 07:39:01 +00:00
// Read from metadata
2020-02-19 15:06:30 +00:00
var mediaInfo = _mediaEncoder . GetMediaInfo ( new MediaInfoRequest
2020-02-19 07:39:01 +00:00
{
2020-02-19 16:46:50 +00:00
MediaSource = episode . GetMediaSources ( false ) [ 0 ] ,
2020-02-19 18:03:42 +00:00
MediaType = DlnaProfileType . Video
2020-02-19 15:06:30 +00:00
} , CancellationToken . None ) . GetAwaiter ( ) . GetResult ( ) ;
2020-02-19 18:41:10 +00:00
if ( mediaInfo . ParentIndexNumber > 0 )
{
2020-02-19 15:06:30 +00:00
episodeInfo . SeasonNumber = mediaInfo . ParentIndexNumber ;
}
2020-02-19 12:39:51 +00:00
2020-02-19 18:41:10 +00:00
if ( mediaInfo . IndexNumber > 0 )
{
2020-02-19 15:06:30 +00:00
episodeInfo . EpisodeNumber = mediaInfo . IndexNumber ;
}
2020-02-19 12:40:06 +00:00
2020-02-19 18:41:10 +00:00
if ( ! string . IsNullOrEmpty ( mediaInfo . ShowName ) )
{
2020-02-19 15:06:30 +00:00
episodeInfo . SeriesName = mediaInfo . ShowName ;
2020-02-19 07:39:01 +00:00
}
2020-02-17 13:56:31 +00:00
}
}
2020-02-19 07:39:01 +00:00
catch ( Exception ex )
{
2020-02-19 17:58:28 +00:00
_logger . LogError ( ex , "Error reading the episode informations with ffprobe. Episode: {EpisodeInfo}" , episodeInfo . Path ) ;
2020-02-19 07:39:01 +00:00
}
2020-02-17 13:56:31 +00:00
2014-12-19 04:20:07 +00:00
var changed = false ;
2014-12-23 03:58:14 +00:00
if ( episodeInfo . IsByDate )
2014-12-19 04:20:07 +00:00
{
if ( episode . IndexNumber . HasValue )
{
2014-12-23 03:58:14 +00:00
episode . IndexNumber = null ;
2014-12-19 04:20:07 +00:00
changed = true ;
}
if ( episode . IndexNumberEnd . HasValue )
{
2014-12-23 03:58:14 +00:00
episode . IndexNumberEnd = null ;
2014-12-19 04:20:07 +00:00
changed = true ;
}
2014-12-23 03:58:14 +00:00
if ( ! episode . PremiereDate . HasValue )
{
if ( episodeInfo . Year . HasValue & & episodeInfo . Month . HasValue & & episodeInfo . Day . HasValue )
{
episode . PremiereDate = new DateTime ( episodeInfo . Year . Value , episodeInfo . Month . Value , episodeInfo . Day . Value ) . ToUniversalTime ( ) ;
}
if ( episode . PremiereDate . HasValue )
{
changed = true ;
}
}
if ( ! episode . ProductionYear . HasValue )
{
episode . ProductionYear = episodeInfo . Year ;
if ( episode . ProductionYear . HasValue )
{
changed = true ;
}
}
}
else
{
2018-09-12 17:26:21 +00:00
if ( ! episode . IndexNumber . HasValue | | forceRefresh )
2014-12-23 03:58:14 +00:00
{
2018-09-12 17:26:21 +00:00
if ( episode . IndexNumber ! = episodeInfo . EpisodeNumber )
2014-12-23 03:58:14 +00:00
{
changed = true ;
}
2019-03-13 21:32:52 +00:00
2018-09-12 17:26:21 +00:00
episode . IndexNumber = episodeInfo . EpisodeNumber ;
2014-12-19 04:20:07 +00:00
}
2018-09-12 17:26:21 +00:00
if ( ! episode . IndexNumberEnd . HasValue | | forceRefresh )
2014-12-19 04:20:07 +00:00
{
2018-09-12 17:26:21 +00:00
if ( episode . IndexNumberEnd ! = episodeInfo . EndingEpsiodeNumber )
2014-12-23 03:58:14 +00:00
{
changed = true ;
}
2019-03-13 21:32:52 +00:00
2018-09-12 17:26:21 +00:00
episode . IndexNumberEnd = episodeInfo . EndingEpsiodeNumber ;
2014-12-23 03:58:14 +00:00
}
2018-09-12 17:26:21 +00:00
if ( ! episode . ParentIndexNumber . HasValue | | forceRefresh )
2014-12-23 03:58:14 +00:00
{
2018-09-12 17:26:21 +00:00
if ( episode . ParentIndexNumber ! = episodeInfo . SeasonNumber )
2014-12-23 03:58:14 +00:00
{
changed = true ;
}
2019-03-13 21:32:52 +00:00
2018-09-12 17:26:21 +00:00
episode . ParentIndexNumber = episodeInfo . SeasonNumber ;
2014-12-19 04:20:07 +00:00
}
}
2018-09-12 17:26:21 +00:00
if ( ! episode . ParentIndexNumber . HasValue )
2017-06-15 17:22:05 +00:00
{
2018-09-12 17:26:21 +00:00
var season = episode . Season ;
2017-06-15 17:22:05 +00:00
2018-09-12 17:26:21 +00:00
if ( season ! = null )
{
episode . ParentIndexNumber = season . IndexNumber ;
2017-06-15 17:22:05 +00:00
}
2019-10-11 10:24:55 +00:00
else
{
2019-11-07 01:50:02 +00:00
/ *
Anime series don ' t generally have a season in their file name , however ,
tvdb needs a season to correctly get the metadata .
Hence , a null season needs to be filled with something . * /
//FIXME perhaps this would be better for tvdb parser to ask for season 1 if no season is specified
2019-10-11 10:24:55 +00:00
episode . ParentIndexNumber = 1 ;
}
2017-06-15 17:22:05 +00:00
2018-09-12 17:26:21 +00:00
if ( episode . ParentIndexNumber . HasValue )
{
changed = true ;
}
2017-06-15 17:22:05 +00:00
}
2018-09-12 17:26:21 +00:00
return changed ;
}
public NamingOptions GetNamingOptions ( )
2015-01-10 05:53:35 +00:00
{
2017-03-29 06:26:48 +00:00
if ( _namingOptions = = null )
{
2020-01-11 20:31:35 +00:00
_namingOptions = new NamingOptions ( ) ;
_videoFileExtensions = _namingOptions . VideoFileExtensions ;
2017-03-29 06:26:48 +00:00
}
2017-01-09 06:25:09 +00:00
2017-03-29 06:26:48 +00:00
return _namingOptions ;
2015-01-10 05:53:35 +00:00
}
2014-11-16 22:46:01 +00:00
public ItemLookupInfo ParseName ( string name )
{
2017-09-10 20:40:31 +00:00
var resolver = new VideoResolver ( GetNamingOptions ( ) ) ;
2014-11-16 22:46:01 +00:00
var result = resolver . CleanDateTime ( name ) ;
return new ItemLookupInfo
{
2020-01-11 20:31:35 +00:00
Name = resolver . TryCleanString ( result . Name , out var newName ) ? newName . ToString ( ) : result . Name ,
2014-11-16 22:46:01 +00:00
Year = result . Year
} ;
}
2014-11-18 02:48:22 +00:00
2015-10-04 03:38:46 +00:00
public IEnumerable < Video > FindTrailers ( BaseItem owner , List < FileSystemMetadata > fileSystemChildren , IDirectoryService directoryService )
2014-12-01 18:42:07 +00:00
{
2017-03-29 06:26:48 +00:00
var namingOptions = GetNamingOptions ( ) ;
2017-07-31 05:16:22 +00:00
var files = owner . IsInMixedFolder ? new List < FileSystemMetadata > ( ) : fileSystemChildren . Where ( i = > i . IsDirectory )
2014-12-01 18:42:07 +00:00
. Where ( i = > string . Equals ( i . Name , BaseItem . TrailerFolderName , StringComparison . OrdinalIgnoreCase ) )
2017-03-29 06:26:48 +00:00
. SelectMany ( i = > _fileSystem . GetFiles ( i . FullName , _videoFileExtensions , false , false ) )
2014-12-01 18:42:07 +00:00
. ToList ( ) ;
2017-09-10 20:40:31 +00:00
var videoListResolver = new VideoListResolver ( namingOptions ) ;
2014-12-01 18:42:07 +00:00
2016-10-29 20:02:21 +00:00
var videos = videoListResolver . Resolve ( fileSystemChildren ) ;
2014-12-01 18:42:07 +00:00
2014-12-03 03:13:03 +00:00
var currentVideo = videos . FirstOrDefault ( i = > string . Equals ( owner . Path , i . Files . First ( ) . Path , StringComparison . OrdinalIgnoreCase ) ) ;
2014-12-01 18:42:07 +00:00
2014-12-03 03:13:03 +00:00
if ( currentVideo ! = null )
{
2019-12-06 19:40:06 +00:00
files . AddRange ( currentVideo . Extras . Where ( i = > i . ExtraType = = ExtraType . Trailer ) . Select ( i = > _fileSystem . GetFileInfo ( i . Path ) ) ) ;
2014-12-03 03:13:03 +00:00
}
2016-03-19 19:32:37 +00:00
var resolvers = new IItemResolver [ ]
{
2019-02-06 19:38:42 +00:00
new GenericVideoResolver < Trailer > ( this )
2016-03-19 19:32:37 +00:00
} ;
2016-08-13 05:49:00 +00:00
return ResolvePaths ( files , directoryService , null , new LibraryOptions ( ) , null , resolvers )
2016-03-19 19:32:37 +00:00
. OfType < Trailer > ( )
2014-12-04 05:24:41 +00:00
. Select ( video = >
2014-12-01 18:42:07 +00:00
{
2015-11-13 20:53:29 +00:00
// Try to retrieve it from the db. If we don't find it, use the resolved version
2016-03-19 19:32:37 +00:00
var dbItem = GetItemById ( video . Id ) as Trailer ;
2014-12-01 18:42:07 +00:00
2015-11-13 20:53:29 +00:00
if ( dbItem ! = null )
{
video = dbItem ;
}
2018-09-12 17:26:21 +00:00
video . ParentId = Guid . Empty ;
video . OwnerId = owner . Id ;
video . ExtraType = ExtraType . Trailer ;
2019-01-13 19:21:32 +00:00
video . TrailerTypes = new [ ] { TrailerType . LocalTrailer } ;
2014-12-01 18:42:07 +00:00
2015-11-13 20:53:29 +00:00
return video ;
// Sort them so that the list can be easily compared for changes
2017-08-11 21:55:48 +00:00
} ) . OrderBy ( i = > i . Path ) ;
2014-12-01 18:42:07 +00:00
}
2017-04-20 20:17:52 +00:00
private static readonly string [ ] ExtrasSubfolderNames = new [ ] { "extras" , "specials" , "shorts" , "scenes" , "featurettes" , "behind the scenes" , "deleted scenes" , "interviews" } ;
2016-11-08 19:50:39 +00:00
2015-10-04 03:38:46 +00:00
public IEnumerable < Video > FindExtras ( BaseItem owner , List < FileSystemMetadata > fileSystemChildren , IDirectoryService directoryService )
2014-12-01 18:42:07 +00:00
{
2017-03-29 06:26:48 +00:00
var namingOptions = GetNamingOptions ( ) ;
2018-09-12 17:26:21 +00:00
var files = owner . IsInMixedFolder ? new List < FileSystemMetadata > ( ) : fileSystemChildren . Where ( i = > i . IsDirectory )
2016-11-08 19:50:39 +00:00
. Where ( i = > ExtrasSubfolderNames . Contains ( i . Name ? ? string . Empty , StringComparer . OrdinalIgnoreCase ) )
2017-03-29 06:26:48 +00:00
. SelectMany ( i = > _fileSystem . GetFiles ( i . FullName , _videoFileExtensions , false , false ) )
2014-12-01 18:42:07 +00:00
. ToList ( ) ;
2017-09-10 20:40:31 +00:00
var videoListResolver = new VideoListResolver ( namingOptions ) ;
2014-12-01 18:42:07 +00:00
2016-10-29 20:02:21 +00:00
var videos = videoListResolver . Resolve ( fileSystemChildren ) ;
2014-12-01 18:42:07 +00:00
2014-12-03 03:13:03 +00:00
var currentVideo = videos . FirstOrDefault ( i = > string . Equals ( owner . Path , i . Files . First ( ) . Path , StringComparison . OrdinalIgnoreCase ) ) ;
if ( currentVideo ! = null )
{
2019-12-06 19:40:06 +00:00
files . AddRange ( currentVideo . Extras . Where ( i = > i . ExtraType ! = ExtraType . Trailer ) . Select ( i = > _fileSystem . GetFileInfo ( i . Path ) ) ) ;
2014-12-03 03:13:03 +00:00
}
2014-12-01 18:42:07 +00:00
2016-08-13 05:49:00 +00:00
return ResolvePaths ( files , directoryService , null , new LibraryOptions ( ) , null )
2014-12-04 05:24:41 +00:00
. OfType < Video > ( )
. Select ( video = >
2014-12-01 18:42:07 +00:00
{
2014-12-04 05:24:41 +00:00
// Try to retrieve it from the db. If we don't find it, use the resolved version
var dbItem = GetItemById ( video . Id ) as Video ;
2014-12-01 18:42:07 +00:00
2014-12-04 05:24:41 +00:00
if ( dbItem ! = null )
{
video = dbItem ;
}
2014-12-01 18:42:07 +00:00
2018-09-12 17:26:21 +00:00
video . ParentId = Guid . Empty ;
video . OwnerId = owner . Id ;
2014-12-04 05:24:41 +00:00
SetExtraTypeFromFilename ( video ) ;
2014-12-01 18:42:07 +00:00
2014-12-04 05:24:41 +00:00
return video ;
// Sort them so that the list can be easily compared for changes
2017-08-11 21:55:48 +00:00
} ) . OrderBy ( i = > i . Path ) ;
2014-12-01 18:42:07 +00:00
}
2016-09-23 06:21:54 +00:00
public string GetPathAfterNetworkSubstitution ( string path , BaseItem ownerItem )
2016-09-12 18:10:09 +00:00
{
2016-09-23 06:21:54 +00:00
if ( ownerItem ! = null )
{
var libraryOptions = GetLibraryOptions ( ownerItem ) ;
if ( libraryOptions ! = null )
{
foreach ( var pathInfo in libraryOptions . PathInfos )
{
2017-01-26 18:41:12 +00:00
if ( string . IsNullOrWhiteSpace ( pathInfo . Path ) | | string . IsNullOrWhiteSpace ( pathInfo . NetworkPath ) )
2016-09-23 06:21:54 +00:00
{
continue ;
}
var substitutionResult = SubstitutePathInternal ( path , pathInfo . Path , pathInfo . NetworkPath ) ;
if ( substitutionResult . Item2 )
{
return substitutionResult . Item1 ;
}
}
}
}
2020-04-04 22:28:46 +00:00
var metadataPath = _configurationManager . Configuration . MetadataPath ;
var metadataNetworkPath = _configurationManager . Configuration . MetadataNetworkPath ;
2016-09-27 17:51:01 +00:00
if ( ! string . IsNullOrWhiteSpace ( metadataPath ) & & ! string . IsNullOrWhiteSpace ( metadataNetworkPath ) )
{
var metadataSubstitutionResult = SubstitutePathInternal ( path , metadataPath , metadataNetworkPath ) ;
if ( metadataSubstitutionResult . Item2 )
{
return metadataSubstitutionResult . Item1 ;
}
}
2016-09-12 18:10:09 +00:00
2020-04-04 22:28:46 +00:00
foreach ( var map in _configurationManager . Configuration . PathSubstitutions )
2017-02-08 18:50:33 +00:00
{
if ( ! string . IsNullOrWhiteSpace ( map . From ) )
{
var substitutionResult = SubstitutePathInternal ( path , map . From , map . To ) ;
if ( substitutionResult . Item2 )
{
return substitutionResult . Item1 ;
}
}
}
2016-09-12 18:10:09 +00:00
return path ;
}
2015-10-04 03:38:46 +00:00
public string SubstitutePath ( string path , string from , string to )
2016-09-23 06:21:54 +00:00
{
return SubstitutePathInternal ( path , from , to ) . Item1 ;
}
private Tuple < string , bool > SubstitutePathInternal ( string path , string from , string to )
2015-10-04 03:38:46 +00:00
{
if ( string . IsNullOrWhiteSpace ( path ) )
{
2019-01-06 20:50:43 +00:00
throw new ArgumentNullException ( nameof ( path ) ) ;
2015-10-04 03:38:46 +00:00
}
if ( string . IsNullOrWhiteSpace ( from ) )
{
2019-01-06 20:50:43 +00:00
throw new ArgumentNullException ( nameof ( from ) ) ;
2015-10-04 03:38:46 +00:00
}
if ( string . IsNullOrWhiteSpace ( to ) )
{
2019-01-06 20:50:43 +00:00
throw new ArgumentNullException ( nameof ( to ) ) ;
2015-10-04 03:38:46 +00:00
}
2016-09-23 06:21:54 +00:00
from = from . Trim ( ) ;
to = to . Trim ( ) ;
var newPath = path . Replace ( from , to , StringComparison . OrdinalIgnoreCase ) ;
var changed = false ;
2015-10-04 03:38:46 +00:00
2019-03-13 21:32:52 +00:00
if ( ! string . Equals ( newPath , path , StringComparison . Ordinal ) )
2015-10-04 03:38:46 +00:00
{
2019-12-06 19:40:06 +00:00
if ( to . IndexOf ( '/' , StringComparison . Ordinal ) ! = - 1 )
2015-10-04 03:38:46 +00:00
{
newPath = newPath . Replace ( '\\' , '/' ) ;
}
else
{
newPath = newPath . Replace ( '/' , '\\' ) ;
}
2016-09-23 06:21:54 +00:00
changed = true ;
2015-10-04 03:38:46 +00:00
}
2016-09-23 06:21:54 +00:00
return new Tuple < string , bool > ( newPath , changed ) ;
2015-10-04 03:38:46 +00:00
}
2014-12-01 18:42:07 +00:00
private void SetExtraTypeFromFilename ( Video item )
{
2018-09-12 17:26:21 +00:00
var resolver = new ExtraResolver ( GetNamingOptions ( ) ) ;
2014-12-01 18:42:07 +00:00
2014-12-03 03:13:03 +00:00
var result = resolver . GetExtraInfo ( item . Path ) ;
2019-12-06 19:40:06 +00:00
item . ExtraType = result . ExtraType ;
2014-12-01 18:42:07 +00:00
}
2015-06-21 03:35:22 +00:00
2015-07-08 16:10:34 +00:00
public List < PersonInfo > GetPeople ( InternalPeopleQuery query )
{
2020-04-04 22:28:46 +00:00
return _itemRepository . GetPeople ( query ) ;
2015-07-08 16:10:34 +00:00
}
2015-06-21 03:35:22 +00:00
public List < PersonInfo > GetPeople ( BaseItem item )
{
2015-07-28 19:42:24 +00:00
if ( item . SupportsPeople )
2015-07-08 16:10:34 +00:00
{
2015-07-28 19:42:24 +00:00
var people = GetPeople ( new InternalPeopleQuery
{
ItemId = item . Id
} ) ;
2015-07-13 21:26:11 +00:00
2015-07-28 19:42:24 +00:00
if ( people . Count > 0 )
{
return people ;
}
2015-07-13 21:26:11 +00:00
}
2015-09-10 03:22:52 +00:00
return new List < PersonInfo > ( ) ;
2015-06-21 03:35:22 +00:00
}
2015-07-08 16:10:34 +00:00
public List < Person > GetPeopleItems ( InternalPeopleQuery query )
2015-07-07 02:25:23 +00:00
{
2020-04-04 22:28:46 +00:00
return _itemRepository . GetPeopleNames ( query ) . Select ( i = >
2015-07-07 02:25:23 +00:00
{
try
{
return GetPerson ( i ) ;
}
catch ( Exception ex )
{
2018-12-20 12:11:26 +00:00
_logger . LogError ( ex , "Error getting person" ) ;
2015-07-07 02:25:23 +00:00
return null ;
}
} ) . Where ( i = > i ! = null ) . ToList ( ) ;
}
2015-07-08 16:10:34 +00:00
public List < string > GetPeopleNames ( InternalPeopleQuery query )
{
2020-04-04 22:28:46 +00:00
return _itemRepository . GetPeopleNames ( query ) ;
2015-07-08 16:10:34 +00:00
}
2017-08-27 00:32:33 +00:00
public void UpdatePeople ( BaseItem item , List < PersonInfo > people )
2015-06-21 03:35:22 +00:00
{
2015-08-06 01:21:18 +00:00
if ( ! item . SupportsPeople )
{
2017-08-27 00:32:33 +00:00
return ;
2015-08-06 01:21:18 +00:00
}
2020-04-04 22:28:46 +00:00
_itemRepository . UpdatePeople ( item . Id , people ) ;
2015-06-21 03:35:22 +00:00
}
2015-10-16 17:06:31 +00:00
2018-09-12 17:26:21 +00:00
public async Task < ItemImageInfo > ConvertImageToLocal ( BaseItem item , ItemImageInfo image , int imageIndex )
2015-10-16 17:06:31 +00:00
{
2016-01-24 04:21:31 +00:00
foreach ( var url in image . Path . Split ( '|' ) )
{
try
{
2018-12-13 13:18:25 +00:00
_logger . LogDebug ( "ConvertImageToLocal item {0} - image url: {1}" , item . Id , url ) ;
2015-10-16 17:06:31 +00:00
2020-04-04 22:28:46 +00:00
await ProviderManager . SaveImage ( item , url , image . Type , imageIndex , CancellationToken . None ) . ConfigureAwait ( false ) ;
2015-10-16 17:06:31 +00:00
2017-10-03 18:39:37 +00:00
item . UpdateToRepository ( ItemUpdateType . ImageUpdate , CancellationToken . None ) ;
2016-01-24 04:21:31 +00:00
return item . GetImageInfo ( image . Type , imageIndex ) ;
}
catch ( HttpException ex )
{
if ( ex . StatusCode . HasValue & & ex . StatusCode . Value = = HttpStatusCode . NotFound )
{
continue ;
}
2019-03-13 21:32:52 +00:00
2016-01-24 04:21:31 +00:00
throw ;
}
2015-11-21 05:51:47 +00:00
}
2016-01-24 04:21:31 +00:00
// Remove this image to prevent it from retrying over and over
item . RemoveImage ( image ) ;
2017-10-03 18:39:37 +00:00
item . UpdateToRepository ( ItemUpdateType . ImageUpdate , CancellationToken . None ) ;
2016-03-20 06:46:51 +00:00
2016-01-24 04:21:31 +00:00
throw new InvalidOperationException ( ) ;
2015-10-16 17:06:31 +00:00
}
2016-05-04 16:33:22 +00:00
2018-09-12 17:26:21 +00:00
public async Task AddVirtualFolder ( string name , string collectionType , LibraryOptions options , bool refreshLibrary )
2016-05-04 16:33:22 +00:00
{
if ( string . IsNullOrWhiteSpace ( name ) )
{
2019-01-06 20:50:43 +00:00
throw new ArgumentNullException ( nameof ( name ) ) ;
2016-05-04 16:33:22 +00:00
}
2016-05-07 18:58:16 +00:00
name = _fileSystem . GetValidFilename ( name ) ;
2020-04-04 22:28:46 +00:00
var rootFolderPath = _configurationManager . ApplicationPaths . DefaultUserViewsPath ;
2016-05-04 16:33:22 +00:00
var virtualFolderPath = Path . Combine ( rootFolderPath , name ) ;
2019-01-26 21:59:53 +00:00
while ( Directory . Exists ( virtualFolderPath ) )
2016-05-04 16:33:22 +00:00
{
name + = "1" ;
virtualFolderPath = Path . Combine ( rootFolderPath , name ) ;
}
2016-09-23 06:21:54 +00:00
var mediaPathInfos = options . PathInfos ;
if ( mediaPathInfos ! = null )
2016-05-04 16:33:22 +00:00
{
2019-01-26 21:59:53 +00:00
var invalidpath = mediaPathInfos . FirstOrDefault ( i = > ! Directory . Exists ( i . Path ) ) ;
2016-05-04 16:33:22 +00:00
if ( invalidpath ! = null )
{
2016-09-23 06:21:54 +00:00
throw new ArgumentException ( "The specified path does not exist: " + invalidpath . Path + "." ) ;
2016-05-04 16:33:22 +00:00
}
}
2020-04-04 22:28:46 +00:00
LibraryMonitor . Stop ( ) ;
2016-05-04 16:33:22 +00:00
try
{
2019-01-26 21:08:04 +00:00
Directory . CreateDirectory ( virtualFolderPath ) ;
2016-05-04 16:33:22 +00:00
if ( ! string . IsNullOrEmpty ( collectionType ) )
{
var path = Path . Combine ( virtualFolderPath , collectionType + ".collection" ) ;
2019-01-26 22:09:07 +00:00
File . WriteAllBytes ( path , Array . Empty < byte > ( ) ) ;
2016-05-04 16:33:22 +00:00
}
2016-08-13 05:49:00 +00:00
CollectionFolder . SaveLibraryOptions ( virtualFolderPath , options ) ;
2016-09-23 06:21:54 +00:00
if ( mediaPathInfos ! = null )
2016-05-04 16:33:22 +00:00
{
2016-09-23 06:21:54 +00:00
foreach ( var path in mediaPathInfos )
2016-05-04 16:33:22 +00:00
{
2016-09-23 06:21:54 +00:00
AddMediaPathInternal ( name , path , false ) ;
2016-05-04 16:33:22 +00:00
}
}
}
finally
{
2018-09-12 17:26:21 +00:00
if ( refreshLibrary )
2016-05-04 16:33:22 +00:00
{
2018-09-12 17:26:21 +00:00
await ValidateTopLibraryFolders ( CancellationToken . None ) . ConfigureAwait ( false ) ;
2016-05-04 16:33:22 +00:00
2018-09-12 17:26:21 +00:00
StartScanInBackground ( ) ;
}
else
{
// Need to add a delay here or directory watchers may still pick up the changes
await Task . Delay ( 1000 ) . ConfigureAwait ( false ) ;
2020-04-04 22:28:46 +00:00
LibraryMonitor . Start ( ) ;
2018-09-12 17:26:21 +00:00
}
2016-05-04 16:33:22 +00:00
}
}
2018-09-12 17:26:21 +00:00
private void StartScanInBackground ( )
{
Task . Run ( ( ) = >
{
// No need to start if scanning the library because it will handle it
ValidateMediaLibrary ( new SimpleProgress < double > ( ) , CancellationToken . None ) ;
} ) ;
}
2019-01-06 20:50:43 +00:00
private static bool ValidateNetworkPath ( string path )
2016-09-25 18:39:13 +00:00
{
2016-11-03 06:37:52 +00:00
//if (Environment.OSVersion.Platform == PlatformID.Win32NT)
//{
// // We can't validate protocol-based paths, so just allow them
// if (path.IndexOf("://", StringComparison.OrdinalIgnoreCase) == -1)
// {
2019-01-26 21:59:53 +00:00
// return Directory.Exists(path);
2016-11-03 06:37:52 +00:00
// }
//}
2016-09-25 18:39:13 +00:00
// Without native support for unc, we cannot validate this when running under mono
return true ;
}
2016-09-23 06:21:54 +00:00
private const string ShortcutFileExtension = ".mblink" ;
2019-03-13 21:32:52 +00:00
2016-09-23 06:21:54 +00:00
public void AddMediaPath ( string virtualFolderName , MediaPathInfo pathInfo )
{
AddMediaPathInternal ( virtualFolderName , pathInfo , true ) ;
}
private void AddMediaPathInternal ( string virtualFolderName , MediaPathInfo pathInfo , bool saveLibraryOptions )
{
if ( pathInfo = = null )
{
2019-01-06 20:50:43 +00:00
throw new ArgumentNullException ( nameof ( pathInfo ) ) ;
2016-09-23 06:21:54 +00:00
}
var path = pathInfo . Path ;
if ( string . IsNullOrWhiteSpace ( path ) )
{
2019-03-13 21:32:52 +00:00
throw new ArgumentException ( nameof ( path ) ) ;
2016-09-23 06:21:54 +00:00
}
2019-01-26 21:59:53 +00:00
if ( ! Directory . Exists ( path ) )
2016-09-23 06:21:54 +00:00
{
2016-11-03 06:37:52 +00:00
throw new FileNotFoundException ( "The path does not exist." ) ;
2016-09-23 06:21:54 +00:00
}
2016-09-25 18:39:13 +00:00
if ( ! string . IsNullOrWhiteSpace ( pathInfo . NetworkPath ) & & ! ValidateNetworkPath ( pathInfo . NetworkPath ) )
2016-09-24 17:58:17 +00:00
{
2016-11-03 06:37:52 +00:00
throw new FileNotFoundException ( "The network path does not exist." ) ;
2016-09-24 17:58:17 +00:00
}
2020-04-04 22:28:46 +00:00
var rootFolderPath = _configurationManager . ApplicationPaths . DefaultUserViewsPath ;
2016-09-23 06:21:54 +00:00
var virtualFolderPath = Path . Combine ( rootFolderPath , virtualFolderName ) ;
2019-01-26 20:47:11 +00:00
var shortcutFilename = Path . GetFileNameWithoutExtension ( path ) ;
2016-09-23 06:21:54 +00:00
var lnk = Path . Combine ( virtualFolderPath , shortcutFilename + ShortcutFileExtension ) ;
2019-01-26 21:59:53 +00:00
while ( File . Exists ( lnk ) )
2016-09-23 06:21:54 +00:00
{
shortcutFilename + = "1" ;
lnk = Path . Combine ( virtualFolderPath , shortcutFilename + ShortcutFileExtension ) ;
}
2018-09-12 17:26:21 +00:00
_fileSystem . CreateShortcut ( lnk , _appHost . ReverseVirtualPath ( path ) ) ;
2016-09-23 06:21:54 +00:00
RemoveContentTypeOverrides ( path ) ;
if ( saveLibraryOptions )
{
var libraryOptions = CollectionFolder . GetLibraryOptions ( virtualFolderPath ) ;
var list = libraryOptions . PathInfos . ToList ( ) ;
list . Add ( pathInfo ) ;
libraryOptions . PathInfos = list . ToArray ( ) ;
2016-09-28 05:11:41 +00:00
SyncLibraryOptionsToLocations ( virtualFolderPath , libraryOptions ) ;
2016-09-23 06:21:54 +00:00
CollectionFolder . SaveLibraryOptions ( virtualFolderPath , libraryOptions ) ;
}
}
2016-09-24 06:22:03 +00:00
public void UpdateMediaPath ( string virtualFolderName , MediaPathInfo pathInfo )
{
if ( pathInfo = = null )
{
2019-01-06 20:50:43 +00:00
throw new ArgumentNullException ( nameof ( pathInfo ) ) ;
2016-09-24 06:22:03 +00:00
}
2016-09-25 18:39:13 +00:00
if ( ! string . IsNullOrWhiteSpace ( pathInfo . NetworkPath ) & & ! ValidateNetworkPath ( pathInfo . NetworkPath ) )
2016-09-24 17:58:17 +00:00
{
2016-11-03 06:37:52 +00:00
throw new FileNotFoundException ( "The network path does not exist." ) ;
2016-09-24 17:58:17 +00:00
}
2020-04-04 22:28:46 +00:00
var rootFolderPath = _configurationManager . ApplicationPaths . DefaultUserViewsPath ;
2016-09-24 06:22:03 +00:00
var virtualFolderPath = Path . Combine ( rootFolderPath , virtualFolderName ) ;
var libraryOptions = CollectionFolder . GetLibraryOptions ( virtualFolderPath ) ;
2016-09-24 17:58:17 +00:00
SyncLibraryOptionsToLocations ( virtualFolderPath , libraryOptions ) ;
2016-09-24 06:22:03 +00:00
var list = libraryOptions . PathInfos . ToList ( ) ;
foreach ( var originalPathInfo in list )
{
if ( string . Equals ( pathInfo . Path , originalPathInfo . Path , StringComparison . Ordinal ) )
{
originalPathInfo . NetworkPath = pathInfo . NetworkPath ;
break ;
}
}
2016-09-24 17:58:17 +00:00
2016-09-24 06:22:03 +00:00
libraryOptions . PathInfos = list . ToArray ( ) ;
CollectionFolder . SaveLibraryOptions ( virtualFolderPath , libraryOptions ) ;
}
2016-09-24 17:58:17 +00:00
private void SyncLibraryOptionsToLocations ( string virtualFolderPath , LibraryOptions options )
{
var topLibraryFolders = GetUserRootFolder ( ) . Children . ToList ( ) ;
2017-06-23 16:04:45 +00:00
var info = GetVirtualFolderInfo ( virtualFolderPath , topLibraryFolders , null ) ;
2016-09-24 17:58:17 +00:00
2017-08-19 19:43:35 +00:00
if ( info . Locations . Length > 0 & & info . Locations . Length ! = options . PathInfos . Length )
2016-09-24 17:58:17 +00:00
{
var list = options . PathInfos . ToList ( ) ;
foreach ( var location in info . Locations )
{
if ( ! list . Any ( i = > string . Equals ( i . Path , location , StringComparison . Ordinal ) ) )
{
list . Add ( new MediaPathInfo
{
Path = location
} ) ;
}
}
options . PathInfos = list . ToArray ( ) ;
}
}
2018-09-12 17:26:21 +00:00
public async Task RemoveVirtualFolder ( string name , bool refreshLibrary )
2016-05-04 20:50:47 +00:00
{
if ( string . IsNullOrWhiteSpace ( name ) )
{
2019-01-06 20:50:43 +00:00
throw new ArgumentNullException ( nameof ( name ) ) ;
2016-05-04 20:50:47 +00:00
}
2020-04-04 22:28:46 +00:00
var rootFolderPath = _configurationManager . ApplicationPaths . DefaultUserViewsPath ;
2016-05-04 20:50:47 +00:00
var path = Path . Combine ( rootFolderPath , name ) ;
2019-01-26 21:59:53 +00:00
if ( ! Directory . Exists ( path ) )
2016-05-04 20:50:47 +00:00
{
2016-11-03 06:37:52 +00:00
throw new FileNotFoundException ( "The media folder does not exist" ) ;
2016-05-04 20:50:47 +00:00
}
2020-04-04 22:28:46 +00:00
LibraryMonitor . Stop ( ) ;
2016-05-04 20:50:47 +00:00
try
{
2019-01-26 21:08:04 +00:00
Directory . Delete ( path , true ) ;
2016-05-04 20:50:47 +00:00
}
finally
{
2018-09-12 17:26:21 +00:00
CollectionFolder . OnCollectionFolderChange ( ) ;
if ( refreshLibrary )
2016-05-04 20:50:47 +00:00
{
2018-09-12 17:26:21 +00:00
await ValidateTopLibraryFolders ( CancellationToken . None ) . ConfigureAwait ( false ) ;
2016-05-04 20:50:47 +00:00
2018-09-12 17:26:21 +00:00
StartScanInBackground ( ) ;
}
else
{
// Need to add a delay here or directory watchers may still pick up the changes
await Task . Delay ( 1000 ) . ConfigureAwait ( false ) ;
2020-04-04 22:28:46 +00:00
LibraryMonitor . Start ( ) ;
2018-09-12 17:26:21 +00:00
}
2016-05-04 20:50:47 +00:00
}
}
2016-06-27 20:33:35 +00:00
private void RemoveContentTypeOverrides ( string path )
{
2016-07-10 15:44:53 +00:00
if ( string . IsNullOrWhiteSpace ( path ) )
{
2019-01-06 20:50:43 +00:00
throw new ArgumentNullException ( nameof ( path ) ) ;
2016-07-10 15:44:53 +00:00
}
2016-06-27 20:33:35 +00:00
var removeList = new List < NameValuePair > ( ) ;
2020-04-04 22:28:46 +00:00
foreach ( var contentType in _configurationManager . Configuration . ContentTypes )
2016-06-27 20:33:35 +00:00
{
2016-12-24 07:41:53 +00:00
if ( string . IsNullOrWhiteSpace ( contentType . Name ) )
{
removeList . Add ( contentType ) ;
}
2017-01-11 17:56:26 +00:00
else if ( _fileSystem . AreEqual ( path , contentType . Name )
2016-06-27 20:33:35 +00:00
| | _fileSystem . ContainsSubPath ( path , contentType . Name ) )
{
removeList . Add ( contentType ) ;
}
}
if ( removeList . Count > 0 )
{
2020-04-04 22:28:46 +00:00
_configurationManager . Configuration . ContentTypes = _configurationManager . Configuration . ContentTypes
2016-06-27 20:33:35 +00:00
. Except ( removeList )
. ToArray ( ) ;
2020-04-04 22:28:46 +00:00
_configurationManager . SaveConfiguration ( ) ;
2016-06-27 20:33:35 +00:00
}
2016-05-04 16:33:22 +00:00
}
2016-05-04 20:50:47 +00:00
public void RemoveMediaPath ( string virtualFolderName , string mediaPath )
{
2018-09-12 17:26:21 +00:00
if ( string . IsNullOrEmpty ( mediaPath ) )
2016-05-04 20:50:47 +00:00
{
2019-01-06 20:50:43 +00:00
throw new ArgumentNullException ( nameof ( mediaPath ) ) ;
2016-05-04 20:50:47 +00:00
}
2020-04-04 22:28:46 +00:00
var rootFolderPath = _configurationManager . ApplicationPaths . DefaultUserViewsPath ;
2016-09-24 06:22:03 +00:00
var virtualFolderPath = Path . Combine ( rootFolderPath , virtualFolderName ) ;
2016-05-04 20:50:47 +00:00
2019-01-26 21:59:53 +00:00
if ( ! Directory . Exists ( virtualFolderPath ) )
2016-05-04 20:50:47 +00:00
{
2016-11-03 06:37:52 +00:00
throw new FileNotFoundException ( string . Format ( "The media collection {0} does not exist" , virtualFolderName ) ) ;
2016-05-04 20:50:47 +00:00
}
2016-11-03 06:37:52 +00:00
var shortcut = _fileSystem . GetFilePaths ( virtualFolderPath , true )
. Where ( i = > string . Equals ( ShortcutFileExtension , Path . GetExtension ( i ) , StringComparison . OrdinalIgnoreCase ) )
2018-09-12 17:26:21 +00:00
. FirstOrDefault ( f = > _appHost . ExpandVirtualPath ( _fileSystem . ResolveShortcut ( f ) ) . Equals ( mediaPath , StringComparison . OrdinalIgnoreCase ) ) ;
2016-05-04 20:50:47 +00:00
if ( ! string . IsNullOrEmpty ( shortcut ) )
{
_fileSystem . DeleteFile ( shortcut ) ;
}
2016-09-24 06:22:03 +00:00
var libraryOptions = CollectionFolder . GetLibraryOptions ( virtualFolderPath ) ;
libraryOptions . PathInfos = libraryOptions
. PathInfos
. Where ( i = > ! string . Equals ( i . Path , mediaPath , StringComparison . Ordinal ) )
. ToArray ( ) ;
CollectionFolder . SaveLibraryOptions ( virtualFolderPath , libraryOptions ) ;
2016-05-04 20:50:47 +00:00
}
2013-02-21 01:33:05 +00:00
}
2018-12-15 18:53:09 +00:00
}