|
@@ -1,7 +1,6 @@
|
|
#pragma warning disable CS1591
|
|
#pragma warning disable CS1591
|
|
|
|
|
|
using System;
|
|
using System;
|
|
-using System.Collections.Concurrent;
|
|
|
|
using System.Collections.Generic;
|
|
using System.Collections.Generic;
|
|
using System.Globalization;
|
|
using System.Globalization;
|
|
using System.IO;
|
|
using System.IO;
|
|
@@ -46,11 +45,11 @@ using MediaBrowser.Model.Net;
|
|
using MediaBrowser.Model.Querying;
|
|
using MediaBrowser.Model.Querying;
|
|
using MediaBrowser.Model.Tasks;
|
|
using MediaBrowser.Model.Tasks;
|
|
using MediaBrowser.Providers.MediaInfo;
|
|
using MediaBrowser.Providers.MediaInfo;
|
|
|
|
+using Microsoft.Extensions.Caching.Memory;
|
|
using Microsoft.Extensions.Logging;
|
|
using Microsoft.Extensions.Logging;
|
|
using Episode = MediaBrowser.Controller.Entities.TV.Episode;
|
|
using Episode = MediaBrowser.Controller.Entities.TV.Episode;
|
|
using Genre = MediaBrowser.Controller.Entities.Genre;
|
|
using Genre = MediaBrowser.Controller.Entities.Genre;
|
|
using Person = MediaBrowser.Controller.Entities.Person;
|
|
using Person = MediaBrowser.Controller.Entities.Person;
|
|
-using SortOrder = MediaBrowser.Model.Entities.SortOrder;
|
|
|
|
using VideoResolver = Emby.Naming.Video.VideoResolver;
|
|
using VideoResolver = Emby.Naming.Video.VideoResolver;
|
|
|
|
|
|
namespace Emby.Server.Implementations.Library
|
|
namespace Emby.Server.Implementations.Library
|
|
@@ -60,7 +59,10 @@ namespace Emby.Server.Implementations.Library
|
|
/// </summary>
|
|
/// </summary>
|
|
public class LibraryManager : ILibraryManager
|
|
public class LibraryManager : ILibraryManager
|
|
{
|
|
{
|
|
|
|
+ private const string ShortcutFileExtension = ".mblink";
|
|
|
|
+
|
|
private readonly ILogger<LibraryManager> _logger;
|
|
private readonly ILogger<LibraryManager> _logger;
|
|
|
|
+ private readonly IMemoryCache _memoryCache;
|
|
private readonly ITaskManager _taskManager;
|
|
private readonly ITaskManager _taskManager;
|
|
private readonly IUserManager _userManager;
|
|
private readonly IUserManager _userManager;
|
|
private readonly IUserDataManager _userDataRepository;
|
|
private readonly IUserDataManager _userDataRepository;
|
|
@@ -72,66 +74,26 @@ namespace Emby.Server.Implementations.Library
|
|
private readonly IMediaEncoder _mediaEncoder;
|
|
private readonly IMediaEncoder _mediaEncoder;
|
|
private readonly IFileSystem _fileSystem;
|
|
private readonly IFileSystem _fileSystem;
|
|
private readonly IItemRepository _itemRepository;
|
|
private readonly IItemRepository _itemRepository;
|
|
- private readonly ConcurrentDictionary<Guid, BaseItem> _libraryItemsCache;
|
|
|
|
private readonly IImageProcessor _imageProcessor;
|
|
private readonly IImageProcessor _imageProcessor;
|
|
|
|
|
|
- private NamingOptions _namingOptions;
|
|
|
|
- private string[] _videoFileExtensions;
|
|
|
|
-
|
|
|
|
- private ILibraryMonitor LibraryMonitor => _libraryMonitorFactory.Value;
|
|
|
|
-
|
|
|
|
- private IProviderManager ProviderManager => _providerManagerFactory.Value;
|
|
|
|
-
|
|
|
|
- private IUserViewManager UserViewManager => _userviewManagerFactory.Value;
|
|
|
|
-
|
|
|
|
- /// <summary>
|
|
|
|
- /// Gets or sets the postscan tasks.
|
|
|
|
- /// </summary>
|
|
|
|
- /// <value>The postscan tasks.</value>
|
|
|
|
- private ILibraryPostScanTask[] PostscanTasks { get; set; }
|
|
|
|
-
|
|
|
|
- /// <summary>
|
|
|
|
- /// Gets or sets the intro providers.
|
|
|
|
- /// </summary>
|
|
|
|
- /// <value>The intro providers.</value>
|
|
|
|
- private IIntroProvider[] IntroProviders { get; set; }
|
|
|
|
-
|
|
|
|
- /// <summary>
|
|
|
|
- /// 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 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>
|
|
/// <summary>
|
|
- /// Gets or sets the comparers.
|
|
|
|
|
|
+ /// The _root folder sync lock.
|
|
/// </summary>
|
|
/// </summary>
|
|
- /// <value>The comparers.</value>
|
|
|
|
- private IBaseItemComparer[] Comparers { get; set; }
|
|
|
|
|
|
+ private readonly object _rootFolderSyncLock = new object();
|
|
|
|
+ private readonly object _userRootFolderSyncLock = new object();
|
|
|
|
|
|
- /// <summary>
|
|
|
|
- /// Occurs when [item added].
|
|
|
|
- /// </summary>
|
|
|
|
- public event EventHandler<ItemChangeEventArgs> ItemAdded;
|
|
|
|
|
|
+ private readonly TimeSpan _viewRefreshInterval = TimeSpan.FromHours(24);
|
|
|
|
|
|
- /// <summary>
|
|
|
|
- /// Occurs when [item updated].
|
|
|
|
- /// </summary>
|
|
|
|
- public event EventHandler<ItemChangeEventArgs> ItemUpdated;
|
|
|
|
|
|
+ private NamingOptions _namingOptions;
|
|
|
|
+ private string[] _videoFileExtensions;
|
|
|
|
|
|
/// <summary>
|
|
/// <summary>
|
|
- /// Occurs when [item removed].
|
|
|
|
|
|
+ /// The _root folder.
|
|
/// </summary>
|
|
/// </summary>
|
|
- public event EventHandler<ItemChangeEventArgs> ItemRemoved;
|
|
|
|
|
|
+ private volatile AggregateFolder _rootFolder;
|
|
|
|
+ private volatile UserRootFolder _userRootFolder;
|
|
|
|
|
|
- public bool IsScanRunning { get; private set; }
|
|
|
|
|
|
+ private bool _wizardCompleted;
|
|
|
|
|
|
/// <summary>
|
|
/// <summary>
|
|
/// Initializes a new instance of the <see cref="LibraryManager" /> class.
|
|
/// Initializes a new instance of the <see cref="LibraryManager" /> class.
|
|
@@ -149,6 +111,7 @@ namespace Emby.Server.Implementations.Library
|
|
/// <param name="mediaEncoder">The media encoder.</param>
|
|
/// <param name="mediaEncoder">The media encoder.</param>
|
|
/// <param name="itemRepository">The item repository.</param>
|
|
/// <param name="itemRepository">The item repository.</param>
|
|
/// <param name="imageProcessor">The image processor.</param>
|
|
/// <param name="imageProcessor">The image processor.</param>
|
|
|
|
+ /// <param name="memoryCache">The memory cache.</param>
|
|
public LibraryManager(
|
|
public LibraryManager(
|
|
IServerApplicationHost appHost,
|
|
IServerApplicationHost appHost,
|
|
ILogger<LibraryManager> logger,
|
|
ILogger<LibraryManager> logger,
|
|
@@ -162,7 +125,8 @@ namespace Emby.Server.Implementations.Library
|
|
Lazy<IUserViewManager> userviewManagerFactory,
|
|
Lazy<IUserViewManager> userviewManagerFactory,
|
|
IMediaEncoder mediaEncoder,
|
|
IMediaEncoder mediaEncoder,
|
|
IItemRepository itemRepository,
|
|
IItemRepository itemRepository,
|
|
- IImageProcessor imageProcessor)
|
|
|
|
|
|
+ IImageProcessor imageProcessor,
|
|
|
|
+ IMemoryCache memoryCache)
|
|
{
|
|
{
|
|
_appHost = appHost;
|
|
_appHost = appHost;
|
|
_logger = logger;
|
|
_logger = logger;
|
|
@@ -177,8 +141,7 @@ namespace Emby.Server.Implementations.Library
|
|
_mediaEncoder = mediaEncoder;
|
|
_mediaEncoder = mediaEncoder;
|
|
_itemRepository = itemRepository;
|
|
_itemRepository = itemRepository;
|
|
_imageProcessor = imageProcessor;
|
|
_imageProcessor = imageProcessor;
|
|
-
|
|
|
|
- _libraryItemsCache = new ConcurrentDictionary<Guid, BaseItem>();
|
|
|
|
|
|
+ _memoryCache = memoryCache;
|
|
|
|
|
|
_configurationManager.ConfigurationUpdated += ConfigurationUpdated;
|
|
_configurationManager.ConfigurationUpdated += ConfigurationUpdated;
|
|
|
|
|
|
@@ -186,37 +149,19 @@ namespace Emby.Server.Implementations.Library
|
|
}
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
/// <summary>
|
|
- /// Adds the parts.
|
|
|
|
|
|
+ /// Occurs when [item added].
|
|
/// </summary>
|
|
/// </summary>
|
|
- /// <param name="rules">The rules.</param>
|
|
|
|
- /// <param name="resolvers">The resolvers.</param>
|
|
|
|
- /// <param name="introProviders">The intro providers.</param>
|
|
|
|
- /// <param name="itemComparers">The item comparers.</param>
|
|
|
|
- /// <param name="postscanTasks">The post scan tasks.</param>
|
|
|
|
- public void AddParts(
|
|
|
|
- IEnumerable<IResolverIgnoreRule> rules,
|
|
|
|
- IEnumerable<IItemResolver> resolvers,
|
|
|
|
- IEnumerable<IIntroProvider> introProviders,
|
|
|
|
- IEnumerable<IBaseItemComparer> itemComparers,
|
|
|
|
- IEnumerable<ILibraryPostScanTask> postscanTasks)
|
|
|
|
- {
|
|
|
|
- EntityResolutionIgnoreRules = rules.ToArray();
|
|
|
|
- EntityResolvers = resolvers.OrderBy(i => i.Priority).ToArray();
|
|
|
|
- MultiItemResolvers = EntityResolvers.OfType<IMultiItemResolver>().ToArray();
|
|
|
|
- IntroProviders = introProviders.ToArray();
|
|
|
|
- Comparers = itemComparers.ToArray();
|
|
|
|
- PostscanTasks = postscanTasks.ToArray();
|
|
|
|
- }
|
|
|
|
|
|
+ public event EventHandler<ItemChangeEventArgs> ItemAdded;
|
|
|
|
|
|
/// <summary>
|
|
/// <summary>
|
|
- /// The _root folder.
|
|
|
|
|
|
+ /// Occurs when [item updated].
|
|
/// </summary>
|
|
/// </summary>
|
|
- private volatile AggregateFolder _rootFolder;
|
|
|
|
|
|
+ public event EventHandler<ItemChangeEventArgs> ItemUpdated;
|
|
|
|
|
|
/// <summary>
|
|
/// <summary>
|
|
- /// The _root folder sync lock.
|
|
|
|
|
|
+ /// Occurs when [item removed].
|
|
/// </summary>
|
|
/// </summary>
|
|
- private readonly object _rootFolderSyncLock = new object();
|
|
|
|
|
|
+ public event EventHandler<ItemChangeEventArgs> ItemRemoved;
|
|
|
|
|
|
/// <summary>
|
|
/// <summary>
|
|
/// Gets the root folder.
|
|
/// Gets the root folder.
|
|
@@ -241,7 +186,68 @@ namespace Emby.Server.Implementations.Library
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
- private bool _wizardCompleted;
|
|
|
|
|
|
+ private ILibraryMonitor LibraryMonitor => _libraryMonitorFactory.Value;
|
|
|
|
+
|
|
|
|
+ private IProviderManager ProviderManager => _providerManagerFactory.Value;
|
|
|
|
+
|
|
|
|
+ private IUserViewManager UserViewManager => _userviewManagerFactory.Value;
|
|
|
|
+
|
|
|
|
+ /// <summary>
|
|
|
|
+ /// Gets or sets the postscan tasks.
|
|
|
|
+ /// </summary>
|
|
|
|
+ /// <value>The postscan tasks.</value>
|
|
|
|
+ private ILibraryPostScanTask[] PostscanTasks { get; set; }
|
|
|
|
+
|
|
|
|
+ /// <summary>
|
|
|
|
+ /// Gets or sets the intro providers.
|
|
|
|
+ /// </summary>
|
|
|
|
+ /// <value>The intro providers.</value>
|
|
|
|
+ private IIntroProvider[] IntroProviders { get; set; }
|
|
|
|
+
|
|
|
|
+ /// <summary>
|
|
|
|
+ /// 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 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>
|
|
|
|
+ /// Gets or sets the comparers.
|
|
|
|
+ /// </summary>
|
|
|
|
+ /// <value>The comparers.</value>
|
|
|
|
+ private IBaseItemComparer[] Comparers { get; set; }
|
|
|
|
+
|
|
|
|
+ public bool IsScanRunning { get; private set; }
|
|
|
|
+
|
|
|
|
+ /// <summary>
|
|
|
|
+ /// Adds the parts.
|
|
|
|
+ /// </summary>
|
|
|
|
+ /// <param name="rules">The rules.</param>
|
|
|
|
+ /// <param name="resolvers">The resolvers.</param>
|
|
|
|
+ /// <param name="introProviders">The intro providers.</param>
|
|
|
|
+ /// <param name="itemComparers">The item comparers.</param>
|
|
|
|
+ /// <param name="postscanTasks">The post scan tasks.</param>
|
|
|
|
+ public void AddParts(
|
|
|
|
+ IEnumerable<IResolverIgnoreRule> rules,
|
|
|
|
+ IEnumerable<IItemResolver> resolvers,
|
|
|
|
+ IEnumerable<IIntroProvider> introProviders,
|
|
|
|
+ IEnumerable<IBaseItemComparer> itemComparers,
|
|
|
|
+ IEnumerable<ILibraryPostScanTask> postscanTasks)
|
|
|
|
+ {
|
|
|
|
+ EntityResolutionIgnoreRules = rules.ToArray();
|
|
|
|
+ EntityResolvers = resolvers.OrderBy(i => i.Priority).ToArray();
|
|
|
|
+ MultiItemResolvers = EntityResolvers.OfType<IMultiItemResolver>().ToArray();
|
|
|
|
+ IntroProviders = introProviders.ToArray();
|
|
|
|
+ Comparers = itemComparers.ToArray();
|
|
|
|
+ PostscanTasks = postscanTasks.ToArray();
|
|
|
|
+ }
|
|
|
|
|
|
/// <summary>
|
|
/// <summary>
|
|
/// Records the configuration values.
|
|
/// Records the configuration values.
|
|
@@ -293,7 +299,7 @@ namespace Emby.Server.Implementations.Library
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
- _libraryItemsCache.AddOrUpdate(item.Id, item, delegate { return item; });
|
|
|
|
|
|
+ _memoryCache.Set(item.Id, item);
|
|
}
|
|
}
|
|
|
|
|
|
public void DeleteItem(BaseItem item, DeleteOptions options)
|
|
public void DeleteItem(BaseItem item, DeleteOptions options)
|
|
@@ -341,7 +347,7 @@ namespace Emby.Server.Implementations.Library
|
|
if (item is LiveTvProgram)
|
|
if (item is LiveTvProgram)
|
|
{
|
|
{
|
|
_logger.LogDebug(
|
|
_logger.LogDebug(
|
|
- "Deleting item, Type: {0}, Name: {1}, Path: {2}, Id: {3}",
|
|
|
|
|
|
+ "Removing item, Type: {0}, Name: {1}, Path: {2}, Id: {3}",
|
|
item.GetType().Name,
|
|
item.GetType().Name,
|
|
item.Name ?? "Unknown name",
|
|
item.Name ?? "Unknown name",
|
|
item.Path ?? string.Empty,
|
|
item.Path ?? string.Empty,
|
|
@@ -350,7 +356,7 @@ namespace Emby.Server.Implementations.Library
|
|
else
|
|
else
|
|
{
|
|
{
|
|
_logger.LogInformation(
|
|
_logger.LogInformation(
|
|
- "Deleting item, Type: {0}, Name: {1}, Path: {2}, Id: {3}",
|
|
|
|
|
|
+ "Removing item, Type: {0}, Name: {1}, Path: {2}, Id: {3}",
|
|
item.GetType().Name,
|
|
item.GetType().Name,
|
|
item.Name ?? "Unknown name",
|
|
item.Name ?? "Unknown name",
|
|
item.Path ?? string.Empty,
|
|
item.Path ?? string.Empty,
|
|
@@ -368,7 +374,12 @@ namespace Emby.Server.Implementations.Library
|
|
continue;
|
|
continue;
|
|
}
|
|
}
|
|
|
|
|
|
- _logger.LogDebug("Deleting path {MetadataPath}", metadataPath);
|
|
|
|
|
|
+ _logger.LogDebug(
|
|
|
|
+ "Deleting metadata path, Type: {0}, Name: {1}, Path: {2}, Id: {3}",
|
|
|
|
+ item.GetType().Name,
|
|
|
|
+ item.Name ?? "Unknown name",
|
|
|
|
+ metadataPath,
|
|
|
|
+ item.Id);
|
|
|
|
|
|
try
|
|
try
|
|
{
|
|
{
|
|
@@ -392,7 +403,13 @@ namespace Emby.Server.Implementations.Library
|
|
{
|
|
{
|
|
try
|
|
try
|
|
{
|
|
{
|
|
- _logger.LogDebug("Deleting path {path}", fileSystemInfo.FullName);
|
|
|
|
|
|
+ _logger.LogInformation(
|
|
|
|
+ "Deleting item path, Type: {0}, Name: {1}, Path: {2}, Id: {3}",
|
|
|
|
+ item.GetType().Name,
|
|
|
|
+ item.Name ?? "Unknown name",
|
|
|
|
+ fileSystemInfo.FullName,
|
|
|
|
+ item.Id);
|
|
|
|
+
|
|
if (fileSystemInfo.IsDirectory)
|
|
if (fileSystemInfo.IsDirectory)
|
|
{
|
|
{
|
|
Directory.Delete(fileSystemInfo.FullName, true);
|
|
Directory.Delete(fileSystemInfo.FullName, true);
|
|
@@ -430,7 +447,7 @@ namespace Emby.Server.Implementations.Library
|
|
_itemRepository.DeleteItem(child.Id);
|
|
_itemRepository.DeleteItem(child.Id);
|
|
}
|
|
}
|
|
|
|
|
|
- _libraryItemsCache.TryRemove(item.Id, out BaseItem removed);
|
|
|
|
|
|
+ _memoryCache.Remove(item.Id);
|
|
|
|
|
|
ReportItemRemoved(item, parent);
|
|
ReportItemRemoved(item, parent);
|
|
}
|
|
}
|
|
@@ -500,8 +517,8 @@ namespace Emby.Server.Implementations.Library
|
|
{
|
|
{
|
|
// Try to normalize paths located underneath program-data in an attempt to make them more portable
|
|
// Try to normalize paths located underneath program-data in an attempt to make them more portable
|
|
key = key.Substring(_configurationManager.ApplicationPaths.ProgramDataPath.Length)
|
|
key = key.Substring(_configurationManager.ApplicationPaths.ProgramDataPath.Length)
|
|
- .TrimStart(new[] { '/', '\\' })
|
|
|
|
- .Replace("/", "\\");
|
|
|
|
|
|
+ .TrimStart('/', '\\')
|
|
|
|
+ .Replace('/', '\\');
|
|
}
|
|
}
|
|
|
|
|
|
if (forceCaseInsensitive || !_configurationManager.Configuration.EnableCaseSensitiveItemIds)
|
|
if (forceCaseInsensitive || !_configurationManager.Configuration.EnableCaseSensitiveItemIds)
|
|
@@ -764,14 +781,11 @@ namespace Emby.Server.Implementations.Library
|
|
return rootFolder;
|
|
return rootFolder;
|
|
}
|
|
}
|
|
|
|
|
|
- private volatile UserRootFolder _userRootFolder;
|
|
|
|
- private readonly object _syncLock = new object();
|
|
|
|
-
|
|
|
|
public Folder GetUserRootFolder()
|
|
public Folder GetUserRootFolder()
|
|
{
|
|
{
|
|
if (_userRootFolder == null)
|
|
if (_userRootFolder == null)
|
|
{
|
|
{
|
|
- lock (_syncLock)
|
|
|
|
|
|
+ lock (_userRootFolderSyncLock)
|
|
{
|
|
{
|
|
if (_userRootFolder == null)
|
|
if (_userRootFolder == null)
|
|
{
|
|
{
|
|
@@ -1234,7 +1248,7 @@ namespace Emby.Server.Implementations.Library
|
|
throw new ArgumentException("Guid can't be empty", nameof(id));
|
|
throw new ArgumentException("Guid can't be empty", nameof(id));
|
|
}
|
|
}
|
|
|
|
|
|
- if (_libraryItemsCache.TryGetValue(id, out BaseItem item))
|
|
|
|
|
|
+ if (_memoryCache.TryGetValue(id, out BaseItem item))
|
|
{
|
|
{
|
|
return item;
|
|
return item;
|
|
}
|
|
}
|
|
@@ -1321,7 +1335,7 @@ namespace Emby.Server.Implementations.Library
|
|
|
|
|
|
return new QueryResult<BaseItem>
|
|
return new QueryResult<BaseItem>
|
|
{
|
|
{
|
|
- Items = _itemRepository.GetItemList(query).ToArray()
|
|
|
|
|
|
+ Items = _itemRepository.GetItemList(query)
|
|
};
|
|
};
|
|
}
|
|
}
|
|
|
|
|
|
@@ -1452,11 +1466,9 @@ namespace Emby.Server.Implementations.Library
|
|
return _itemRepository.GetItems(query);
|
|
return _itemRepository.GetItems(query);
|
|
}
|
|
}
|
|
|
|
|
|
- var list = _itemRepository.GetItemList(query);
|
|
|
|
-
|
|
|
|
return new QueryResult<BaseItem>
|
|
return new QueryResult<BaseItem>
|
|
{
|
|
{
|
|
- Items = list
|
|
|
|
|
|
+ Items = _itemRepository.GetItemList(query)
|
|
};
|
|
};
|
|
}
|
|
}
|
|
|
|
|
|
@@ -1579,7 +1591,6 @@ namespace Emby.Server.Implementations.Library
|
|
public async Task<IEnumerable<Video>> GetIntros(BaseItem item, User user)
|
|
public async Task<IEnumerable<Video>> GetIntros(BaseItem item, User user)
|
|
{
|
|
{
|
|
var tasks = IntroProviders
|
|
var tasks = IntroProviders
|
|
- .OrderBy(i => i.GetType().Name.Contains("Default", StringComparison.OrdinalIgnoreCase) ? 1 : 0)
|
|
|
|
.Take(1)
|
|
.Take(1)
|
|
.Select(i => GetIntros(i, item, user));
|
|
.Select(i => GetIntros(i, item, user));
|
|
|
|
|
|
@@ -1865,7 +1876,8 @@ namespace Emby.Server.Implementations.Library
|
|
}
|
|
}
|
|
|
|
|
|
var outdated = forceUpdate ? item.ImageInfos.Where(i => i.Path != null).ToArray() : item.ImageInfos.Where(ImageNeedsRefresh).ToArray();
|
|
var outdated = forceUpdate ? item.ImageInfos.Where(i => i.Path != null).ToArray() : item.ImageInfos.Where(ImageNeedsRefresh).ToArray();
|
|
- if (outdated.Length == 0)
|
|
|
|
|
|
+ // Skip image processing if current or live tv source
|
|
|
|
+ if (outdated.Length == 0 || item.SourceType != SourceType.Library)
|
|
{
|
|
{
|
|
RegisterItem(item);
|
|
RegisterItem(item);
|
|
return;
|
|
return;
|
|
@@ -1934,12 +1946,9 @@ namespace Emby.Server.Implementations.Library
|
|
/// <summary>
|
|
/// <summary>
|
|
/// Updates the item.
|
|
/// Updates the item.
|
|
/// </summary>
|
|
/// </summary>
|
|
- public void UpdateItems(IEnumerable<BaseItem> items, BaseItem parent, ItemUpdateType updateReason, CancellationToken cancellationToken)
|
|
|
|
|
|
+ public void UpdateItems(IReadOnlyList<BaseItem> items, BaseItem parent, ItemUpdateType updateReason, CancellationToken cancellationToken)
|
|
{
|
|
{
|
|
- // Don't iterate multiple times
|
|
|
|
- var itemsList = items.ToList();
|
|
|
|
-
|
|
|
|
- foreach (var item in itemsList)
|
|
|
|
|
|
+ foreach (var item in items)
|
|
{
|
|
{
|
|
if (item.IsFileProtocol)
|
|
if (item.IsFileProtocol)
|
|
{
|
|
{
|
|
@@ -1951,11 +1960,11 @@ namespace Emby.Server.Implementations.Library
|
|
UpdateImages(item, updateReason >= ItemUpdateType.ImageUpdate);
|
|
UpdateImages(item, updateReason >= ItemUpdateType.ImageUpdate);
|
|
}
|
|
}
|
|
|
|
|
|
- _itemRepository.SaveItems(itemsList, cancellationToken);
|
|
|
|
|
|
+ _itemRepository.SaveItems(items, cancellationToken);
|
|
|
|
|
|
if (ItemUpdated != null)
|
|
if (ItemUpdated != null)
|
|
{
|
|
{
|
|
- foreach (var item in itemsList)
|
|
|
|
|
|
+ foreach (var item in items)
|
|
{
|
|
{
|
|
// With the live tv guide this just creates too much noise
|
|
// With the live tv guide this just creates too much noise
|
|
if (item.SourceType != SourceType.Library)
|
|
if (item.SourceType != SourceType.Library)
|
|
@@ -2178,8 +2187,6 @@ namespace Emby.Server.Implementations.Library
|
|
.FirstOrDefault(i => !string.IsNullOrEmpty(i));
|
|
.FirstOrDefault(i => !string.IsNullOrEmpty(i));
|
|
}
|
|
}
|
|
|
|
|
|
- private readonly TimeSpan _viewRefreshInterval = TimeSpan.FromHours(24);
|
|
|
|
-
|
|
|
|
public UserView GetNamedView(
|
|
public UserView GetNamedView(
|
|
User user,
|
|
User user,
|
|
string name,
|
|
string name,
|
|
@@ -2477,14 +2484,9 @@ namespace Emby.Server.Implementations.Library
|
|
|
|
|
|
var isFolder = episode.VideoType == VideoType.BluRay || episode.VideoType == VideoType.Dvd;
|
|
var isFolder = episode.VideoType == VideoType.BluRay || episode.VideoType == VideoType.Dvd;
|
|
|
|
|
|
- var episodeInfo = episode.IsFileProtocol ?
|
|
|
|
- resolver.Resolve(episode.Path, isFolder, null, null, isAbsoluteNaming) :
|
|
|
|
- new Naming.TV.EpisodeInfo();
|
|
|
|
-
|
|
|
|
- if (episodeInfo == null)
|
|
|
|
- {
|
|
|
|
- episodeInfo = new Naming.TV.EpisodeInfo();
|
|
|
|
- }
|
|
|
|
|
|
+ var episodeInfo = episode.IsFileProtocol
|
|
|
|
+ ? resolver.Resolve(episode.Path, isFolder, null, null, isAbsoluteNaming) ?? new Naming.TV.EpisodeInfo()
|
|
|
|
+ : new Naming.TV.EpisodeInfo();
|
|
|
|
|
|
try
|
|
try
|
|
{
|
|
{
|
|
@@ -2492,11 +2494,13 @@ namespace Emby.Server.Implementations.Library
|
|
if (libraryOptions.EnableEmbeddedEpisodeInfos && string.Equals(episodeInfo.Container, "mp4", StringComparison.OrdinalIgnoreCase))
|
|
if (libraryOptions.EnableEmbeddedEpisodeInfos && string.Equals(episodeInfo.Container, "mp4", StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
{
|
|
// Read from metadata
|
|
// Read from metadata
|
|
- var mediaInfo = _mediaEncoder.GetMediaInfo(new MediaInfoRequest
|
|
|
|
- {
|
|
|
|
- MediaSource = episode.GetMediaSources(false)[0],
|
|
|
|
- MediaType = DlnaProfileType.Video
|
|
|
|
- }, CancellationToken.None).GetAwaiter().GetResult();
|
|
|
|
|
|
+ var mediaInfo = _mediaEncoder.GetMediaInfo(
|
|
|
|
+ new MediaInfoRequest
|
|
|
|
+ {
|
|
|
|
+ MediaSource = episode.GetMediaSources(false)[0],
|
|
|
|
+ MediaType = DlnaProfileType.Video
|
|
|
|
+ },
|
|
|
|
+ CancellationToken.None).GetAwaiter().GetResult();
|
|
if (mediaInfo.ParentIndexNumber > 0)
|
|
if (mediaInfo.ParentIndexNumber > 0)
|
|
{
|
|
{
|
|
episodeInfo.SeasonNumber = mediaInfo.ParentIndexNumber;
|
|
episodeInfo.SeasonNumber = mediaInfo.ParentIndexNumber;
|
|
@@ -2654,7 +2658,7 @@ namespace Emby.Server.Implementations.Library
|
|
|
|
|
|
var videos = videoListResolver.Resolve(fileSystemChildren);
|
|
var videos = videoListResolver.Resolve(fileSystemChildren);
|
|
|
|
|
|
- var currentVideo = videos.FirstOrDefault(i => string.Equals(owner.Path, i.Files.First().Path, StringComparison.OrdinalIgnoreCase));
|
|
|
|
|
|
+ var currentVideo = videos.FirstOrDefault(i => string.Equals(owner.Path, i.Files[0].Path, StringComparison.OrdinalIgnoreCase));
|
|
|
|
|
|
if (currentVideo != null)
|
|
if (currentVideo != null)
|
|
{
|
|
{
|
|
@@ -2671,9 +2675,7 @@ namespace Emby.Server.Implementations.Library
|
|
.Select(video =>
|
|
.Select(video =>
|
|
{
|
|
{
|
|
// Try to retrieve it from the db. If we don't find it, use the resolved version
|
|
// Try to retrieve it from the db. If we don't find it, use the resolved version
|
|
- var dbItem = GetItemById(video.Id) as Trailer;
|
|
|
|
-
|
|
|
|
- if (dbItem != null)
|
|
|
|
|
|
+ if (GetItemById(video.Id) is Trailer dbItem)
|
|
{
|
|
{
|
|
video = dbItem;
|
|
video = dbItem;
|
|
}
|
|
}
|
|
@@ -3000,23 +3002,6 @@ namespace Emby.Server.Implementations.Library
|
|
});
|
|
});
|
|
}
|
|
}
|
|
|
|
|
|
- private static bool ValidateNetworkPath(string path)
|
|
|
|
- {
|
|
|
|
- // if (Environment.OSVersion.Platform == PlatformID.Win32NT)
|
|
|
|
- //{
|
|
|
|
- // // We can't validate protocol-based paths, so just allow them
|
|
|
|
- // if (path.IndexOf("://", StringComparison.OrdinalIgnoreCase) == -1)
|
|
|
|
- // {
|
|
|
|
- // return Directory.Exists(path);
|
|
|
|
- // }
|
|
|
|
- //}
|
|
|
|
-
|
|
|
|
- // Without native support for unc, we cannot validate this when running under mono
|
|
|
|
- return true;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- private const string ShortcutFileExtension = ".mblink";
|
|
|
|
-
|
|
|
|
public void AddMediaPath(string virtualFolderName, MediaPathInfo pathInfo)
|
|
public void AddMediaPath(string virtualFolderName, MediaPathInfo pathInfo)
|
|
{
|
|
{
|
|
AddMediaPathInternal(virtualFolderName, pathInfo, true);
|
|
AddMediaPathInternal(virtualFolderName, pathInfo, true);
|
|
@@ -3041,11 +3026,6 @@ namespace Emby.Server.Implementations.Library
|
|
throw new FileNotFoundException("The path does not exist.");
|
|
throw new FileNotFoundException("The path does not exist.");
|
|
}
|
|
}
|
|
|
|
|
|
- if (!string.IsNullOrWhiteSpace(pathInfo.NetworkPath) && !ValidateNetworkPath(pathInfo.NetworkPath))
|
|
|
|
- {
|
|
|
|
- throw new FileNotFoundException("The network path does not exist.");
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
var rootFolderPath = _configurationManager.ApplicationPaths.DefaultUserViewsPath;
|
|
var rootFolderPath = _configurationManager.ApplicationPaths.DefaultUserViewsPath;
|
|
var virtualFolderPath = Path.Combine(rootFolderPath, virtualFolderName);
|
|
var virtualFolderPath = Path.Combine(rootFolderPath, virtualFolderName);
|
|
|
|
|
|
@@ -3084,11 +3064,6 @@ namespace Emby.Server.Implementations.Library
|
|
throw new ArgumentNullException(nameof(pathInfo));
|
|
throw new ArgumentNullException(nameof(pathInfo));
|
|
}
|
|
}
|
|
|
|
|
|
- if (!string.IsNullOrWhiteSpace(pathInfo.NetworkPath) && !ValidateNetworkPath(pathInfo.NetworkPath))
|
|
|
|
- {
|
|
|
|
- throw new FileNotFoundException("The network path does not exist.");
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
var rootFolderPath = _configurationManager.ApplicationPaths.DefaultUserViewsPath;
|
|
var rootFolderPath = _configurationManager.ApplicationPaths.DefaultUserViewsPath;
|
|
var virtualFolderPath = Path.Combine(rootFolderPath, virtualFolderName);
|
|
var virtualFolderPath = Path.Combine(rootFolderPath, virtualFolderName);
|
|
|
|
|
|
@@ -3220,7 +3195,8 @@ namespace Emby.Server.Implementations.Library
|
|
|
|
|
|
if (!Directory.Exists(virtualFolderPath))
|
|
if (!Directory.Exists(virtualFolderPath))
|
|
{
|
|
{
|
|
- throw new FileNotFoundException(string.Format("The media collection {0} does not exist", virtualFolderName));
|
|
|
|
|
|
+ throw new FileNotFoundException(
|
|
|
|
+ string.Format(CultureInfo.InvariantCulture, "The media collection {0} does not exist", virtualFolderName));
|
|
}
|
|
}
|
|
|
|
|
|
var shortcut = _fileSystem.GetFilePaths(virtualFolderPath, true)
|
|
var shortcut = _fileSystem.GetFilePaths(virtualFolderPath, true)
|