|
@@ -58,22 +58,23 @@ namespace Emby.Server.Implementations.Library
|
|
|
private ILibraryPostScanTask[] PostscanTasks { get; set; }
|
|
|
|
|
|
/// <summary>
|
|
|
- /// Gets the intro providers.
|
|
|
+ /// Gets or sets the intro providers.
|
|
|
/// </summary>
|
|
|
/// <value>The intro providers.</value>
|
|
|
private IIntroProvider[] IntroProviders { get; set; }
|
|
|
|
|
|
/// <summary>
|
|
|
- /// Gets the list of entity resolution ignore rules
|
|
|
+ /// Gets or sets the list of entity resolution ignore rules
|
|
|
/// </summary>
|
|
|
/// <value>The entity resolution ignore rules.</value>
|
|
|
private IResolverIgnoreRule[] EntityResolutionIgnoreRules { get; set; }
|
|
|
|
|
|
/// <summary>
|
|
|
- /// Gets the list of currently registered entity resolvers
|
|
|
+ /// Gets or sets the list of currently registered entity resolvers
|
|
|
/// </summary>
|
|
|
/// <value>The entity resolvers enumerable.</value>
|
|
|
private IItemResolver[] EntityResolvers { get; set; }
|
|
|
+
|
|
|
private IMultiItemResolver[] MultiItemResolvers { get; set; }
|
|
|
|
|
|
/// <summary>
|
|
@@ -83,7 +84,7 @@ namespace Emby.Server.Implementations.Library
|
|
|
private IBaseItemComparer[] Comparers { get; set; }
|
|
|
|
|
|
/// <summary>
|
|
|
- /// Gets the active item repository
|
|
|
+ /// Gets or sets the active item repository
|
|
|
/// </summary>
|
|
|
/// <value>The item repository.</value>
|
|
|
public IItemRepository ItemRepository { get; set; }
|
|
@@ -133,12 +134,14 @@ namespace Emby.Server.Implementations.Library
|
|
|
private readonly Func<IProviderManager> _providerManagerFactory;
|
|
|
private readonly Func<IUserViewManager> _userviewManager;
|
|
|
public bool IsScanRunning { get; private set; }
|
|
|
+
|
|
|
private IServerApplicationHost _appHost;
|
|
|
|
|
|
/// <summary>
|
|
|
/// The _library items cache
|
|
|
/// </summary>
|
|
|
private readonly ConcurrentDictionary<Guid, BaseItem> _libraryItemsCache;
|
|
|
+
|
|
|
/// <summary>
|
|
|
/// Gets the library items cache.
|
|
|
/// </summary>
|
|
@@ -150,7 +153,8 @@ namespace Emby.Server.Implementations.Library
|
|
|
/// <summary>
|
|
|
/// Initializes a new instance of the <see cref="LibraryManager" /> class.
|
|
|
/// </summary>
|
|
|
- /// <param name="logger">The logger.</param>
|
|
|
+ /// <param name="appHost">The application host</param>
|
|
|
+ /// <param name="loggerFactory">The logger factory.</param>
|
|
|
/// <param name="taskManager">The task manager.</param>
|
|
|
/// <param name="userManager">The user manager.</param>
|
|
|
/// <param name="configurationManager">The configuration manager.</param>
|
|
@@ -167,6 +171,7 @@ namespace Emby.Server.Implementations.Library
|
|
|
Func<IProviderManager> providerManagerFactory,
|
|
|
Func<IUserViewManager> userviewManager)
|
|
|
{
|
|
|
+ _appHost = appHost;
|
|
|
_logger = loggerFactory.CreateLogger(nameof(LibraryManager));
|
|
|
_taskManager = taskManager;
|
|
|
_userManager = userManager;
|
|
@@ -176,7 +181,7 @@ namespace Emby.Server.Implementations.Library
|
|
|
_fileSystem = fileSystem;
|
|
|
_providerManagerFactory = providerManagerFactory;
|
|
|
_userviewManager = userviewManager;
|
|
|
- _appHost = appHost;
|
|
|
+
|
|
|
_libraryItemsCache = new ConcurrentDictionary<Guid, BaseItem>();
|
|
|
|
|
|
ConfigurationManager.ConfigurationUpdated += ConfigurationUpdated;
|
|
@@ -191,8 +196,9 @@ namespace Emby.Server.Implementations.Library
|
|
|
/// <param name="resolvers">The resolvers.</param>
|
|
|
/// <param name="introProviders">The intro providers.</param>
|
|
|
/// <param name="itemComparers">The item comparers.</param>
|
|
|
- /// <param name="postscanTasks">The postscan tasks.</param>
|
|
|
- public void AddParts(IEnumerable<IResolverIgnoreRule> rules,
|
|
|
+ /// <param name="postscanTasks">The post scan tasks.</param>
|
|
|
+ public void AddParts(
|
|
|
+ IEnumerable<IResolverIgnoreRule> rules,
|
|
|
IEnumerable<IItemResolver> resolvers,
|
|
|
IEnumerable<IIntroProvider> introProviders,
|
|
|
IEnumerable<IBaseItemComparer> itemComparers,
|
|
@@ -203,24 +209,19 @@ namespace Emby.Server.Implementations.Library
|
|
|
MultiItemResolvers = EntityResolvers.OfType<IMultiItemResolver>().ToArray();
|
|
|
IntroProviders = introProviders.ToArray();
|
|
|
Comparers = itemComparers.ToArray();
|
|
|
-
|
|
|
- PostscanTasks = postscanTasks.OrderBy(i =>
|
|
|
- {
|
|
|
- var hasOrder = i as IHasOrder;
|
|
|
-
|
|
|
- return hasOrder == null ? 0 : hasOrder.Order;
|
|
|
-
|
|
|
- }).ToArray();
|
|
|
+ PostscanTasks = postscanTasks.ToArray();
|
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
|
/// The _root folder
|
|
|
/// </summary>
|
|
|
private volatile AggregateFolder _rootFolder;
|
|
|
+
|
|
|
/// <summary>
|
|
|
/// The _root folder sync lock
|
|
|
/// </summary>
|
|
|
private readonly object _rootFolderSyncLock = new object();
|
|
|
+
|
|
|
/// <summary>
|
|
|
/// Gets the root folder.
|
|
|
/// </summary>
|
|
@@ -239,11 +240,13 @@ namespace Emby.Server.Implementations.Library
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
+
|
|
|
return _rootFolder;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
private bool _wizardCompleted;
|
|
|
+
|
|
|
/// <summary>
|
|
|
/// Records the configuration values.
|
|
|
/// </summary>
|
|
@@ -258,7 +261,7 @@ namespace Emby.Server.Implementations.Library
|
|
|
/// </summary>
|
|
|
/// <param name="sender">The sender.</param>
|
|
|
/// <param name="e">The <see cref="EventArgs" /> instance containing the event data.</param>
|
|
|
- void ConfigurationUpdated(object sender, EventArgs e)
|
|
|
+ private void ConfigurationUpdated(object sender, EventArgs e)
|
|
|
{
|
|
|
var config = ConfigurationManager.Configuration;
|
|
|
|
|
@@ -278,6 +281,7 @@ namespace Emby.Server.Implementations.Library
|
|
|
{
|
|
|
throw new ArgumentNullException(nameof(item));
|
|
|
}
|
|
|
+
|
|
|
if (item is IItemByName)
|
|
|
{
|
|
|
if (!(item is MusicArtist))
|
|
@@ -285,18 +289,7 @@ namespace Emby.Server.Implementations.Library
|
|
|
return;
|
|
|
}
|
|
|
}
|
|
|
-
|
|
|
- else if (item.IsFolder)
|
|
|
- {
|
|
|
- //if (!(item is ICollectionFolder) && !(item is UserView) && !(item is Channel) && !(item is AggregateFolder))
|
|
|
- //{
|
|
|
- // if (item.SourceType != SourceType.Library)
|
|
|
- // {
|
|
|
- // return;
|
|
|
- // }
|
|
|
- //}
|
|
|
- }
|
|
|
- else
|
|
|
+ else if (!item.IsFolder)
|
|
|
{
|
|
|
if (!(item is Video) && !(item is LiveTvChannel))
|
|
|
{
|
|
@@ -345,12 +338,14 @@ namespace Emby.Server.Implementations.Library
|
|
|
// channel no longer installed
|
|
|
}
|
|
|
}
|
|
|
+
|
|
|
options.DeleteFileLocation = false;
|
|
|
}
|
|
|
|
|
|
if (item is LiveTvProgram)
|
|
|
{
|
|
|
- _logger.LogDebug("Deleting item, Type: {0}, Name: {1}, Path: {2}, Id: {3}",
|
|
|
+ _logger.LogDebug(
|
|
|
+ "Deleting item, Type: {0}, Name: {1}, Path: {2}, Id: {3}",
|
|
|
item.GetType().Name,
|
|
|
item.Name ?? "Unknown name",
|
|
|
item.Path ?? string.Empty,
|
|
@@ -358,7 +353,8 @@ namespace Emby.Server.Implementations.Library
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
- _logger.LogInformation("Deleting item, Type: {0}, Name: {1}, Path: {2}, Id: {3}",
|
|
|
+ _logger.LogInformation(
|
|
|
+ "Deleting item, Type: {0}, Name: {1}, Path: {2}, Id: {3}",
|
|
|
item.GetType().Name,
|
|
|
item.Name ?? "Unknown name",
|
|
|
item.Path ?? string.Empty,
|
|
@@ -371,19 +367,20 @@ namespace Emby.Server.Implementations.Library
|
|
|
|
|
|
foreach (var metadataPath in GetMetadataPaths(item, children))
|
|
|
{
|
|
|
- _logger.LogDebug("Deleting path {0}", metadataPath);
|
|
|
+ if (!Directory.Exists(metadataPath))
|
|
|
+ {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
+ _logger.LogDebug("Deleting path {MetadataPath}", metadataPath);
|
|
|
|
|
|
try
|
|
|
{
|
|
|
Directory.Delete(metadataPath, true);
|
|
|
- }
|
|
|
- catch (IOException)
|
|
|
- {
|
|
|
-
|
|
|
}
|
|
|
catch (Exception ex)
|
|
|
{
|
|
|
- _logger.LogError(ex, "Error deleting {metadataPath}", metadataPath);
|
|
|
+ _logger.LogError(ex, "Error deleting {MetadataPath}", metadataPath);
|
|
|
}
|
|
|
}
|
|
|
|
|
@@ -497,12 +494,13 @@ namespace Emby.Server.Implementations.Library
|
|
|
{
|
|
|
throw new ArgumentNullException(nameof(key));
|
|
|
}
|
|
|
+
|
|
|
if (type == null)
|
|
|
{
|
|
|
throw new ArgumentNullException(nameof(type));
|
|
|
}
|
|
|
|
|
|
- if (key.StartsWith(ConfigurationManager.ApplicationPaths.ProgramDataPath))
|
|
|
+ if (key.StartsWith(ConfigurationManager.ApplicationPaths.ProgramDataPath, StringComparison.Ordinal))
|
|
|
{
|
|
|
// Try to normalize paths located underneath program-data in an attempt to make them more portable
|
|
|
key = key.Substring(ConfigurationManager.ApplicationPaths.ProgramDataPath.Length)
|
|
@@ -520,13 +518,11 @@ namespace Emby.Server.Implementations.Library
|
|
|
return key.GetMD5();
|
|
|
}
|
|
|
|
|
|
- public BaseItem ResolvePath(FileSystemMetadata fileInfo,
|
|
|
- Folder parent = null)
|
|
|
- {
|
|
|
- return ResolvePath(fileInfo, new DirectoryService(_logger, _fileSystem), null, parent);
|
|
|
- }
|
|
|
+ public BaseItem ResolvePath(FileSystemMetadata fileInfo, Folder parent = null)
|
|
|
+ => ResolvePath(fileInfo, new DirectoryService(_logger, _fileSystem), null, parent);
|
|
|
|
|
|
- private BaseItem ResolvePath(FileSystemMetadata fileInfo,
|
|
|
+ private BaseItem ResolvePath(
|
|
|
+ FileSystemMetadata fileInfo,
|
|
|
IDirectoryService directoryService,
|
|
|
IItemResolver[] resolvers,
|
|
|
Folder parent = null,
|
|
@@ -581,7 +577,7 @@ namespace Emby.Server.Implementations.Library
|
|
|
{
|
|
|
_logger.LogError(ex, "Error in GetFilteredFileSystemEntries isPhysicalRoot: {0} IsVf: {1}", isPhysicalRoot, isVf);
|
|
|
|
|
|
- files = new FileSystemMetadata[] { };
|
|
|
+ files = Array.Empty<FileSystemMetadata>();
|
|
|
}
|
|
|
else
|
|
|
{
|
|
@@ -609,13 +605,7 @@ namespace Emby.Server.Implementations.Library
|
|
|
}
|
|
|
|
|
|
public bool IgnoreFile(FileSystemMetadata file, BaseItem parent)
|
|
|
- {
|
|
|
- if (EntityResolutionIgnoreRules.Any(r => r.ShouldIgnore(file, parent)))
|
|
|
- {
|
|
|
- return true;
|
|
|
- }
|
|
|
- return false;
|
|
|
- }
|
|
|
+ => EntityResolutionIgnoreRules.Any(r => r.ShouldIgnore(file, parent));
|
|
|
|
|
|
public List<FileSystemMetadata> NormalizeRootPathList(IEnumerable<FileSystemMetadata> paths)
|
|
|
{
|
|
@@ -655,7 +645,8 @@ namespace Emby.Server.Implementations.Library
|
|
|
return ResolvePaths(files, directoryService, parent, libraryOptions, collectionType, EntityResolvers);
|
|
|
}
|
|
|
|
|
|
- public IEnumerable<BaseItem> ResolvePaths(IEnumerable<FileSystemMetadata> files,
|
|
|
+ public IEnumerable<BaseItem> ResolvePaths(
|
|
|
+ IEnumerable<FileSystemMetadata> files,
|
|
|
IDirectoryService directoryService,
|
|
|
Folder parent,
|
|
|
LibraryOptions libraryOptions,
|
|
@@ -681,6 +672,7 @@ namespace Emby.Server.Implementations.Library
|
|
|
{
|
|
|
ResolverHelper.SetInitialItemValues(item, parent, _fileSystem, this, directoryService);
|
|
|
}
|
|
|
+
|
|
|
items.AddRange(ResolveFileList(result.ExtraFiles, directoryService, parent, collectionType, resolvers, libraryOptions));
|
|
|
return items;
|
|
|
}
|
|
@@ -690,7 +682,8 @@ namespace Emby.Server.Implementations.Library
|
|
|
return ResolveFileList(fileList, directoryService, parent, collectionType, resolvers, libraryOptions);
|
|
|
}
|
|
|
|
|
|
- private IEnumerable<BaseItem> ResolveFileList(IEnumerable<FileSystemMetadata> fileList,
|
|
|
+ private IEnumerable<BaseItem> ResolveFileList(
|
|
|
+ IEnumerable<FileSystemMetadata> fileList,
|
|
|
IDirectoryService directoryService,
|
|
|
Folder parent,
|
|
|
string collectionType,
|
|
@@ -775,6 +768,7 @@ namespace Emby.Server.Implementations.Library
|
|
|
|
|
|
private volatile UserRootFolder _userRootFolder;
|
|
|
private readonly object _syncLock = new object();
|
|
|
+
|
|
|
public Folder GetUserRootFolder()
|
|
|
{
|
|
|
if (_userRootFolder == null)
|
|
@@ -819,8 +813,6 @@ namespace Emby.Server.Implementations.Library
|
|
|
throw new ArgumentNullException(nameof(path));
|
|
|
}
|
|
|
|
|
|
- //_logger.LogInformation("FindByPath {0}", path);
|
|
|
-
|
|
|
var query = new InternalItemsQuery
|
|
|
{
|
|
|
Path = path,
|
|
@@ -894,7 +886,6 @@ namespace Emby.Server.Implementations.Library
|
|
|
/// </summary>
|
|
|
/// <param name="value">The value.</param>
|
|
|
/// <returns>Task{Year}.</returns>
|
|
|
- /// <exception cref="ArgumentOutOfRangeException"></exception>
|
|
|
public Year GetYear(int value)
|
|
|
{
|
|
|
if (value <= 0)
|
|
@@ -1036,20 +1027,25 @@ namespace Emby.Server.Implementations.Library
|
|
|
|
|
|
private async Task ValidateTopLibraryFolders(CancellationToken cancellationToken)
|
|
|
{
|
|
|
- var rootChildren = RootFolder.Children.ToList();
|
|
|
- rootChildren = GetUserRootFolder().Children.ToList();
|
|
|
-
|
|
|
await RootFolder.RefreshMetadata(cancellationToken).ConfigureAwait(false);
|
|
|
|
|
|
// Start by just validating the children of the root, but go no further
|
|
|
- await RootFolder.ValidateChildren(new SimpleProgress<double>(), cancellationToken, new MetadataRefreshOptions(new DirectoryService(_logger, _fileSystem)), recursive: false);
|
|
|
+ await RootFolder.ValidateChildren(
|
|
|
+ new SimpleProgress<double>(),
|
|
|
+ cancellationToken,
|
|
|
+ new MetadataRefreshOptions(new DirectoryService(_logger, _fileSystem)),
|
|
|
+ recursive: false).ConfigureAwait(false);
|
|
|
|
|
|
await GetUserRootFolder().RefreshMetadata(cancellationToken).ConfigureAwait(false);
|
|
|
|
|
|
- await GetUserRootFolder().ValidateChildren(new SimpleProgress<double>(), cancellationToken, new MetadataRefreshOptions(new DirectoryService(_logger, _fileSystem)), recursive: false).ConfigureAwait(false);
|
|
|
+ await GetUserRootFolder().ValidateChildren(
|
|
|
+ new SimpleProgress<double>(),
|
|
|
+ cancellationToken,
|
|
|
+ new MetadataRefreshOptions(new DirectoryService(_logger, _fileSystem)),
|
|
|
+ recursive: false).ConfigureAwait(false);
|
|
|
|
|
|
// Quickly scan CollectionFolders for changes
|
|
|
- foreach (var folder in GetUserRootFolder().Children.OfType<Folder>().ToList())
|
|
|
+ foreach (var folder in GetUserRootFolder().Children.OfType<Folder>())
|
|
|
{
|
|
|
await folder.RefreshMetadata(cancellationToken).ConfigureAwait(false);
|
|
|
}
|
|
@@ -1213,7 +1209,7 @@ namespace Emby.Server.Implementations.Library
|
|
|
private string GetCollectionType(string path)
|
|
|
{
|
|
|
return _fileSystem.GetFilePaths(path, new[] { ".collection" }, true, false)
|
|
|
- .Select(i => Path.GetFileNameWithoutExtension(i))
|
|
|
+ .Select(Path.GetFileNameWithoutExtension)
|
|
|
.FirstOrDefault(i => !string.IsNullOrEmpty(i));
|
|
|
}
|
|
|
|
|
@@ -1227,7 +1223,7 @@ namespace Emby.Server.Implementations.Library
|
|
|
{
|
|
|
if (id == Guid.Empty)
|
|
|
{
|
|
|
- throw new ArgumentException(nameof(id), "Guid can't be empty");
|
|
|
+ throw new ArgumentException("Guid can't be empty", nameof(id));
|
|
|
}
|
|
|
|
|
|
if (LibraryItemsCache.TryGetValue(id, out BaseItem item))
|
|
@@ -1395,17 +1391,7 @@ namespace Emby.Server.Implementations.Library
|
|
|
|
|
|
var parents = query.AncestorIds.Select(i => GetItemById(i)).ToList();
|
|
|
|
|
|
- if (parents.All(i =>
|
|
|
- {
|
|
|
- if (i is ICollectionFolder || i is UserView)
|
|
|
- {
|
|
|
- return true;
|
|
|
- }
|
|
|
-
|
|
|
- //_logger.LogDebug("Query requires ancestor query due to type: " + i.GetType().Name);
|
|
|
- return false;
|
|
|
-
|
|
|
- }))
|
|
|
+ if (parents.All(i => i is ICollectionFolder || i is UserView))
|
|
|
{
|
|
|
// Optimize by querying against top level views
|
|
|
query.TopParentIds = parents.SelectMany(i => GetTopParentIdsForQuery(i, query.User)).ToArray();
|
|
@@ -1461,17 +1447,7 @@ namespace Emby.Server.Implementations.Library
|
|
|
|
|
|
private void SetTopParentIdsOrAncestors(InternalItemsQuery query, List<BaseItem> parents)
|
|
|
{
|
|
|
- if (parents.All(i =>
|
|
|
- {
|
|
|
- if (i is ICollectionFolder || i is UserView)
|
|
|
- {
|
|
|
- return true;
|
|
|
- }
|
|
|
-
|
|
|
- //_logger.LogDebug("Query requires ancestor query due to type: " + i.GetType().Name);
|
|
|
- return false;
|
|
|
-
|
|
|
- }))
|
|
|
+ if (parents.All(i => i is ICollectionFolder || i is UserView))
|
|
|
{
|
|
|
// Optimize by querying against top level views
|
|
|
query.TopParentIds = parents.SelectMany(i => GetTopParentIdsForQuery(i, query.User)).ToArray();
|
|
@@ -1520,11 +1496,9 @@ namespace Emby.Server.Implementations.Library
|
|
|
|
|
|
private IEnumerable<Guid> GetTopParentIdsForQuery(BaseItem item, User user)
|
|
|
{
|
|
|
- var view = item as UserView;
|
|
|
-
|
|
|
- if (view != null)
|
|
|
+ if (item is UserView view)
|
|
|
{
|
|
|
- if (string.Equals(view.ViewType, CollectionType.LiveTv))
|
|
|
+ if (string.Equals(view.ViewType, CollectionType.LiveTv, StringComparison.Ordinal))
|
|
|
{
|
|
|
return new[] { view.Id };
|
|
|
}
|
|
@@ -1537,8 +1511,10 @@ namespace Emby.Server.Implementations.Library
|
|
|
{
|
|
|
return GetTopParentIdsForQuery(displayParent, user);
|
|
|
}
|
|
|
+
|
|
|
return Array.Empty<Guid>();
|
|
|
}
|
|
|
+
|
|
|
if (!view.ParentId.Equals(Guid.Empty))
|
|
|
{
|
|
|
var displayParent = GetItemById(view.ParentId);
|
|
@@ -1546,6 +1522,7 @@ namespace Emby.Server.Implementations.Library
|
|
|
{
|
|
|
return GetTopParentIdsForQuery(displayParent, user);
|
|
|
}
|
|
|
+
|
|
|
return Array.Empty<Guid>();
|
|
|
}
|
|
|
|
|
@@ -1559,11 +1536,11 @@ namespace Emby.Server.Implementations.Library
|
|
|
.Where(i => user.IsFolderGrouped(i.Id))
|
|
|
.SelectMany(i => GetTopParentIdsForQuery(i, user));
|
|
|
}
|
|
|
+
|
|
|
return Array.Empty<Guid>();
|
|
|
}
|
|
|
|
|
|
- var collectionFolder = item as CollectionFolder;
|
|
|
- if (collectionFolder != null)
|
|
|
+ if (item is CollectionFolder collectionFolder)
|
|
|
{
|
|
|
return collectionFolder.PhysicalFolderIds;
|
|
|
}
|
|
@@ -1573,6 +1550,7 @@ namespace Emby.Server.Implementations.Library
|
|
|
{
|
|
|
return new[] { topParent.Id };
|
|
|
}
|
|
|
+
|
|
|
return Array.Empty<Guid>();
|
|
|
}
|
|
|
|
|
@@ -1769,19 +1747,16 @@ namespace Emby.Server.Implementations.Library
|
|
|
{
|
|
|
var comparer = Comparers.FirstOrDefault(c => string.Equals(name, c.Name, StringComparison.OrdinalIgnoreCase));
|
|
|
|
|
|
- if (comparer != null)
|
|
|
+ // If it requires a user, create a new one, and assign the user
|
|
|
+ if (comparer is IUserBaseItemComparer)
|
|
|
{
|
|
|
- // If it requires a user, create a new one, and assign the user
|
|
|
- if (comparer is IUserBaseItemComparer)
|
|
|
- {
|
|
|
- var userComparer = (IUserBaseItemComparer)Activator.CreateInstance(comparer.GetType());
|
|
|
+ var userComparer = (IUserBaseItemComparer)Activator.CreateInstance(comparer.GetType());
|
|
|
|
|
|
- userComparer.User = user;
|
|
|
- userComparer.UserManager = _userManager;
|
|
|
- userComparer.UserDataRepository = _userDataRepository;
|
|
|
+ userComparer.User = user;
|
|
|
+ userComparer.UserManager = _userManager;
|
|
|
+ userComparer.UserDataRepository = _userDataRepository;
|
|
|
|
|
|
- return userComparer;
|
|
|
- }
|
|
|
+ return userComparer;
|
|
|
}
|
|
|
|
|
|
return comparer;
|
|
@@ -1792,7 +1767,6 @@ namespace Emby.Server.Implementations.Library
|
|
|
/// </summary>
|
|
|
/// <param name="item">The item.</param>
|
|
|
/// <param name="parent">The parent item.</param>
|
|
|
- /// <returns>Task.</returns>
|
|
|
public void CreateItem(BaseItem item, BaseItem parent)
|
|
|
{
|
|
|
CreateItems(new[] { item }, parent, CancellationToken.None);
|
|
@@ -1802,20 +1776,23 @@ namespace Emby.Server.Implementations.Library
|
|
|
/// Creates the items.
|
|
|
/// </summary>
|
|
|
/// <param name="items">The items.</param>
|
|
|
+ /// <param name="parent">The parent item</param>
|
|
|
/// <param name="cancellationToken">The cancellation token.</param>
|
|
|
- /// <returns>Task.</returns>
|
|
|
public void CreateItems(IEnumerable<BaseItem> items, BaseItem parent, CancellationToken cancellationToken)
|
|
|
{
|
|
|
- ItemRepository.SaveItems(items, cancellationToken);
|
|
|
+ // Don't iterate multiple times
|
|
|
+ var itemsList = items.ToList();
|
|
|
+
|
|
|
+ ItemRepository.SaveItems(itemsList, cancellationToken);
|
|
|
|
|
|
- foreach (var item in items)
|
|
|
+ foreach (var item in itemsList)
|
|
|
{
|
|
|
RegisterItem(item);
|
|
|
}
|
|
|
|
|
|
if (ItemAdded != null)
|
|
|
{
|
|
|
- foreach (var item in items)
|
|
|
+ foreach (var item in itemsList)
|
|
|
{
|
|
|
// With the live tv guide this just creates too much noise
|
|
|
if (item.SourceType != SourceType.Library)
|
|
@@ -1825,11 +1802,13 @@ namespace Emby.Server.Implementations.Library
|
|
|
|
|
|
try
|
|
|
{
|
|
|
- ItemAdded(this, new ItemChangeEventArgs
|
|
|
- {
|
|
|
- Item = item,
|
|
|
- Parent = parent ?? item.GetParent()
|
|
|
- });
|
|
|
+ ItemAdded(
|
|
|
+ this,
|
|
|
+ new ItemChangeEventArgs
|
|
|
+ {
|
|
|
+ Item = item,
|
|
|
+ Parent = parent ?? item.GetParent()
|
|
|
+ });
|
|
|
}
|
|
|
catch (Exception ex)
|
|
|
{
|
|
@@ -1851,7 +1830,10 @@ namespace Emby.Server.Implementations.Library
|
|
|
/// </summary>
|
|
|
public void UpdateItems(IEnumerable<BaseItem> items, BaseItem parent, ItemUpdateType updateReason, CancellationToken cancellationToken)
|
|
|
{
|
|
|
- foreach (var item in items)
|
|
|
+ // Don't iterate multiple times
|
|
|
+ var itemsList = items.ToList();
|
|
|
+
|
|
|
+ foreach (var item in itemsList)
|
|
|
{
|
|
|
if (item.IsFileProtocol)
|
|
|
{
|
|
@@ -1863,14 +1845,11 @@ namespace Emby.Server.Implementations.Library
|
|
|
RegisterItem(item);
|
|
|
}
|
|
|
|
|
|
- //var logName = item.LocationType == LocationType.Remote ? item.Name ?? item.Path : item.Path ?? item.Name;
|
|
|
- //_logger.LogDebug("Saving {0} to database.", logName);
|
|
|
-
|
|
|
- ItemRepository.SaveItems(items, cancellationToken);
|
|
|
+ ItemRepository.SaveItems(itemsList, cancellationToken);
|
|
|
|
|
|
if (ItemUpdated != null)
|
|
|
{
|
|
|
- foreach (var item in items)
|
|
|
+ foreach (var item in itemsList)
|
|
|
{
|
|
|
// With the live tv guide this just creates too much noise
|
|
|
if (item.SourceType != SourceType.Library)
|
|
@@ -1880,12 +1859,14 @@ namespace Emby.Server.Implementations.Library
|
|
|
|
|
|
try
|
|
|
{
|
|
|
- ItemUpdated(this, new ItemChangeEventArgs
|
|
|
- {
|
|
|
- Item = item,
|
|
|
- Parent = parent,
|
|
|
- UpdateReason = updateReason
|
|
|
- });
|
|
|
+ ItemUpdated(
|
|
|
+ this,
|
|
|
+ new ItemChangeEventArgs
|
|
|
+ {
|
|
|
+ Item = item,
|
|
|
+ Parent = parent,
|
|
|
+ UpdateReason = updateReason
|
|
|
+ });
|
|
|
}
|
|
|
catch (Exception ex)
|
|
|
{
|
|
@@ -1899,9 +1880,9 @@ namespace Emby.Server.Implementations.Library
|
|
|
/// Updates the item.
|
|
|
/// </summary>
|
|
|
/// <param name="item">The item.</param>
|
|
|
+ /// <param name="parent">The parent item.</param>
|
|
|
/// <param name="updateReason">The update reason.</param>
|
|
|
/// <param name="cancellationToken">The cancellation token.</param>
|
|
|
- /// <returns>Task.</returns>
|
|
|
public void UpdateItem(BaseItem item, BaseItem parent, ItemUpdateType updateReason, CancellationToken cancellationToken)
|
|
|
{
|
|
|
UpdateItems(new [] { item }, parent, updateReason, cancellationToken);
|
|
@@ -1911,17 +1892,20 @@ namespace Emby.Server.Implementations.Library
|
|
|
/// Reports the item removed.
|
|
|
/// </summary>
|
|
|
/// <param name="item">The item.</param>
|
|
|
+ /// <param name="parent">The parent item.</param>
|
|
|
public void ReportItemRemoved(BaseItem item, BaseItem parent)
|
|
|
{
|
|
|
if (ItemRemoved != null)
|
|
|
{
|
|
|
try
|
|
|
{
|
|
|
- ItemRemoved(this, new ItemChangeEventArgs
|
|
|
- {
|
|
|
- Item = item,
|
|
|
- Parent = parent
|
|
|
- });
|
|
|
+ ItemRemoved(
|
|
|
+ this,
|
|
|
+ new ItemChangeEventArgs
|
|
|
+ {
|
|
|
+ Item = item,
|
|
|
+ Parent = parent
|
|
|
+ });
|
|
|
}
|
|
|
catch (Exception ex)
|
|
|
{
|
|
@@ -2047,8 +2031,7 @@ namespace Emby.Server.Implementations.Library
|
|
|
|
|
|
public string GetConfiguredContentType(BaseItem item, bool inheritConfiguredPath)
|
|
|
{
|
|
|
- var collectionFolder = item as ICollectionFolder;
|
|
|
- if (collectionFolder != null)
|
|
|
+ if (item is ICollectionFolder collectionFolder)
|
|
|
{
|
|
|
return collectionFolder.CollectionType;
|
|
|
}
|
|
@@ -2058,13 +2041,11 @@ namespace Emby.Server.Implementations.Library
|
|
|
|
|
|
private string GetContentTypeOverride(string path, bool inherit)
|
|
|
{
|
|
|
- var nameValuePair = ConfigurationManager.Configuration.ContentTypes.FirstOrDefault(i => _fileSystem.AreEqual(i.Name, path) || (inherit && !string.IsNullOrEmpty(i.Name) && _fileSystem.ContainsSubPath(i.Name, path)));
|
|
|
- if (nameValuePair != null)
|
|
|
- {
|
|
|
- return nameValuePair.Value;
|
|
|
- }
|
|
|
-
|
|
|
- return null;
|
|
|
+ var nameValuePair = ConfigurationManager.Configuration.ContentTypes
|
|
|
+ .FirstOrDefault(i => _fileSystem.AreEqual(i.Name, path)
|
|
|
+ || (inherit && !string.IsNullOrEmpty(i.Name)
|
|
|
+ && _fileSystem.ContainsSubPath(i.Name, path)));
|
|
|
+ return nameValuePair?.Value;
|
|
|
}
|
|
|
|
|
|
private string GetTopFolderContentType(BaseItem item)
|
|
@@ -2081,6 +2062,7 @@ namespace Emby.Server.Implementations.Library
|
|
|
{
|
|
|
break;
|
|
|
}
|
|
|
+
|
|
|
item = parent;
|
|
|
}
|
|
|
|
|
@@ -2092,9 +2074,9 @@ namespace Emby.Server.Implementations.Library
|
|
|
}
|
|
|
|
|
|
private readonly TimeSpan _viewRefreshInterval = TimeSpan.FromHours(24);
|
|
|
- //private readonly TimeSpan _viewRefreshInterval = TimeSpan.FromMinutes(1);
|
|
|
|
|
|
- public UserView GetNamedView(User user,
|
|
|
+ public UserView GetNamedView(
|
|
|
+ User user,
|
|
|
string name,
|
|
|
string viewType,
|
|
|
string sortName)
|
|
@@ -2102,13 +2084,15 @@ namespace Emby.Server.Implementations.Library
|
|
|
return GetNamedView(user, name, Guid.Empty, viewType, sortName);
|
|
|
}
|
|
|
|
|
|
- public UserView GetNamedView(string name,
|
|
|
+ public UserView GetNamedView(
|
|
|
+ string name,
|
|
|
string viewType,
|
|
|
string sortName)
|
|
|
{
|
|
|
- var path = Path.Combine(ConfigurationManager.ApplicationPaths.InternalMetadataPath,
|
|
|
- "views",
|
|
|
- _fileSystem.GetValidFilename(viewType));
|
|
|
+ var path = Path.Combine(
|
|
|
+ ConfigurationManager.ApplicationPaths.InternalMetadataPath,
|
|
|
+ "views",
|
|
|
+ _fileSystem.GetValidFilename(viewType));
|
|
|
|
|
|
var id = GetNewItemId(path + "_namedview_" + name, typeof(UserView));
|
|
|
|
|
@@ -2144,7 +2128,8 @@ namespace Emby.Server.Implementations.Library
|
|
|
return item;
|
|
|
}
|
|
|
|
|
|
- public UserView GetNamedView(User user,
|
|
|
+ public UserView GetNamedView(
|
|
|
+ User user,
|
|
|
string name,
|
|
|
Guid parentId,
|
|
|
string viewType,
|
|
@@ -2173,10 +2158,10 @@ namespace Emby.Server.Implementations.Library
|
|
|
Name = name,
|
|
|
ViewType = viewType,
|
|
|
ForcedSortName = sortName,
|
|
|
- UserId = user.Id
|
|
|
+ UserId = user.Id,
|
|
|
+ DisplayParentId = parentId
|
|
|
};
|
|
|
|
|
|
- item.DisplayParentId = parentId;
|
|
|
|
|
|
CreateItem(item, null);
|
|
|
|
|
@@ -2193,20 +2178,24 @@ namespace Emby.Server.Implementations.Library
|
|
|
|
|
|
if (refresh)
|
|
|
{
|
|
|
- _providerManagerFactory().QueueRefresh(item.Id, new MetadataRefreshOptions(new DirectoryService(_logger, _fileSystem))
|
|
|
- {
|
|
|
- // Need to force save to increment DateLastSaved
|
|
|
- ForceSave = true
|
|
|
+ _providerManagerFactory().QueueRefresh(
|
|
|
+ item.Id,
|
|
|
+ new MetadataRefreshOptions(new DirectoryService(_logger, _fileSystem))
|
|
|
+ {
|
|
|
+ // Need to force save to increment DateLastSaved
|
|
|
+ ForceSave = true
|
|
|
|
|
|
- }, RefreshPriority.Normal);
|
|
|
+ },
|
|
|
+ RefreshPriority.Normal);
|
|
|
}
|
|
|
|
|
|
return item;
|
|
|
}
|
|
|
|
|
|
- public UserView GetShadowView(BaseItem parent,
|
|
|
- string viewType,
|
|
|
- string sortName)
|
|
|
+ public UserView GetShadowView(
|
|
|
+ BaseItem parent,
|
|
|
+ string viewType,
|
|
|
+ string sortName)
|
|
|
{
|
|
|
if (parent == null)
|
|
|
{
|
|
@@ -2257,18 +2246,21 @@ namespace Emby.Server.Implementations.Library
|
|
|
|
|
|
if (refresh)
|
|
|
{
|
|
|
- _providerManagerFactory().QueueRefresh(item.Id, new MetadataRefreshOptions(new DirectoryService(_logger, _fileSystem))
|
|
|
- {
|
|
|
- // Need to force save to increment DateLastSaved
|
|
|
- ForceSave = true
|
|
|
-
|
|
|
- }, RefreshPriority.Normal);
|
|
|
+ _providerManagerFactory().QueueRefresh(
|
|
|
+ item.Id,
|
|
|
+ new MetadataRefreshOptions(new DirectoryService(_logger, _fileSystem))
|
|
|
+ {
|
|
|
+ // Need to force save to increment DateLastSaved
|
|
|
+ ForceSave = true
|
|
|
+ },
|
|
|
+ RefreshPriority.Normal);
|
|
|
}
|
|
|
|
|
|
return item;
|
|
|
}
|
|
|
|
|
|
- public UserView GetNamedView(string name,
|
|
|
+ public UserView GetNamedView(
|
|
|
+ string name,
|
|
|
Guid parentId,
|
|
|
string viewType,
|
|
|
string sortName,
|
|
@@ -2331,17 +2323,21 @@ namespace Emby.Server.Implementations.Library
|
|
|
|
|
|
if (refresh)
|
|
|
{
|
|
|
- _providerManagerFactory().QueueRefresh(item.Id, new MetadataRefreshOptions(new DirectoryService(_logger, _fileSystem))
|
|
|
- {
|
|
|
- // Need to force save to increment DateLastSaved
|
|
|
- ForceSave = true
|
|
|
- }, RefreshPriority.Normal);
|
|
|
+ _providerManagerFactory().QueueRefresh(
|
|
|
+ item.Id,
|
|
|
+ new MetadataRefreshOptions(new DirectoryService(_logger, _fileSystem))
|
|
|
+ {
|
|
|
+ // Need to force save to increment DateLastSaved
|
|
|
+ ForceSave = true
|
|
|
+ },
|
|
|
+ RefreshPriority.Normal);
|
|
|
}
|
|
|
|
|
|
return item;
|
|
|
}
|
|
|
|
|
|
- public void AddExternalSubtitleStreams(List<MediaStream> streams,
|
|
|
+ public void AddExternalSubtitleStreams(
|
|
|
+ List<MediaStream> streams,
|
|
|
string videoPath,
|
|
|
string[] files)
|
|
|
{
|
|
@@ -2445,6 +2441,7 @@ namespace Emby.Server.Implementations.Library
|
|
|
{
|
|
|
changed = true;
|
|
|
}
|
|
|
+
|
|
|
episode.IndexNumber = episodeInfo.EpisodeNumber;
|
|
|
}
|
|
|
|
|
@@ -2454,6 +2451,7 @@ namespace Emby.Server.Implementations.Library
|
|
|
{
|
|
|
changed = true;
|
|
|
}
|
|
|
+
|
|
|
episode.IndexNumberEnd = episodeInfo.EndingEpsiodeNumber;
|
|
|
}
|
|
|
|
|
@@ -2463,6 +2461,7 @@ namespace Emby.Server.Implementations.Library
|
|
|
{
|
|
|
changed = true;
|
|
|
}
|
|
|
+
|
|
|
episode.ParentIndexNumber = episodeInfo.SeasonNumber;
|
|
|
}
|
|
|
}
|
|
@@ -2492,6 +2491,7 @@ namespace Emby.Server.Implementations.Library
|
|
|
|
|
|
private NamingOptions _namingOptions;
|
|
|
private string[] _videoFileExtensions;
|
|
|
+
|
|
|
private NamingOptions GetNamingOptionsInternal()
|
|
|
{
|
|
|
if (_namingOptions == null)
|
|
@@ -2688,7 +2688,7 @@ namespace Emby.Server.Implementations.Library
|
|
|
var newPath = path.Replace(from, to, StringComparison.OrdinalIgnoreCase);
|
|
|
var changed = false;
|
|
|
|
|
|
- if (!string.Equals(newPath, path))
|
|
|
+ if (!string.Equals(newPath, path, StringComparison.Ordinal))
|
|
|
{
|
|
|
if (to.IndexOf('/') != -1)
|
|
|
{
|
|
@@ -2812,6 +2812,7 @@ namespace Emby.Server.Implementations.Library
|
|
|
{
|
|
|
continue;
|
|
|
}
|
|
|
+
|
|
|
throw;
|
|
|
}
|
|
|
}
|
|
@@ -2916,6 +2917,7 @@ namespace Emby.Server.Implementations.Library
|
|
|
}
|
|
|
|
|
|
private const string ShortcutFileExtension = ".mblink";
|
|
|
+
|
|
|
public void AddMediaPath(string virtualFolderName, MediaPathInfo pathInfo)
|
|
|
{
|
|
|
AddMediaPathInternal(virtualFolderName, pathInfo, true);
|
|
@@ -2932,7 +2934,7 @@ namespace Emby.Server.Implementations.Library
|
|
|
|
|
|
if (string.IsNullOrWhiteSpace(path))
|
|
|
{
|
|
|
- throw new ArgumentNullException(nameof(path));
|
|
|
+ throw new ArgumentException(nameof(path));
|
|
|
}
|
|
|
|
|
|
if (!Directory.Exists(path))
|