aboutsummaryrefslogtreecommitdiff
path: root/Emby.Server.Implementations/Library/LibraryManager.cs
diff options
context:
space:
mode:
Diffstat (limited to 'Emby.Server.Implementations/Library/LibraryManager.cs')
-rw-r--r--Emby.Server.Implementations/Library/LibraryManager.cs753
1 files changed, 308 insertions, 445 deletions
diff --git a/Emby.Server.Implementations/Library/LibraryManager.cs b/Emby.Server.Implementations/Library/LibraryManager.cs
index 028673529..808cedd67 100644
--- a/Emby.Server.Implementations/Library/LibraryManager.cs
+++ b/Emby.Server.Implementations/Library/LibraryManager.cs
@@ -3,6 +3,7 @@
#pragma warning disable CS1591
using System;
+using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
@@ -11,16 +12,15 @@ using System.Net;
using System.Net.Http;
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;
+using Emby.Server.Implementations.ScheduledTasks.Tasks;
using Jellyfin.Data.Entities;
using Jellyfin.Data.Enums;
+using Jellyfin.Extensions;
using MediaBrowser.Common.Extensions;
using MediaBrowser.Common.Progress;
using MediaBrowser.Controller;
@@ -46,7 +46,6 @@ using MediaBrowser.Model.IO;
using MediaBrowser.Model.Library;
using MediaBrowser.Model.Querying;
using MediaBrowser.Model.Tasks;
-using MediaBrowser.Providers.MediaInfo;
using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.Logging;
using Episode = MediaBrowser.Controller.Entities.TV.Episode;
@@ -65,7 +64,7 @@ namespace Emby.Server.Implementations.Library
private const string ShortcutFileExtension = ".mblink";
private readonly ILogger<LibraryManager> _logger;
- private readonly IMemoryCache _memoryCache;
+ private readonly ConcurrentDictionary<Guid, BaseItem> _cache;
private readonly ITaskManager _taskManager;
private readonly IUserManager _userManager;
private readonly IUserDataManager _userDataRepository;
@@ -78,6 +77,8 @@ namespace Emby.Server.Implementations.Library
private readonly IFileSystem _fileSystem;
private readonly IItemRepository _itemRepository;
private readonly IImageProcessor _imageProcessor;
+ private readonly NamingOptions _namingOptions;
+ private readonly ExtraResolver _extraResolver;
/// <summary>
/// The _root folder sync lock.
@@ -87,9 +88,6 @@ namespace Emby.Server.Implementations.Library
private readonly TimeSpan _viewRefreshInterval = TimeSpan.FromHours(24);
- private NamingOptions _namingOptions;
- private string[] _videoFileExtensions;
-
/// <summary>
/// The _root folder.
/// </summary>
@@ -102,7 +100,7 @@ namespace Emby.Server.Implementations.Library
/// Initializes a new instance of the <see cref="LibraryManager" /> class.
/// </summary>
/// <param name="appHost">The application host.</param>
- /// <param name="logger">The logger.</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>
@@ -114,10 +112,11 @@ namespace Emby.Server.Implementations.Library
/// <param name="mediaEncoder">The media encoder.</param>
/// <param name="itemRepository">The item repository.</param>
/// <param name="imageProcessor">The image processor.</param>
- /// <param name="memoryCache">The memory cache.</param>
+ /// <param name="namingOptions">The naming options.</param>
+ /// <param name="directoryService">The directory service.</param>
public LibraryManager(
IServerApplicationHost appHost,
- ILogger<LibraryManager> logger,
+ ILoggerFactory loggerFactory,
ITaskManager taskManager,
IUserManager userManager,
IServerConfigurationManager configurationManager,
@@ -129,10 +128,11 @@ namespace Emby.Server.Implementations.Library
IMediaEncoder mediaEncoder,
IItemRepository itemRepository,
IImageProcessor imageProcessor,
- IMemoryCache memoryCache)
+ NamingOptions namingOptions,
+ IDirectoryService directoryService)
{
_appHost = appHost;
- _logger = logger;
+ _logger = loggerFactory.CreateLogger<LibraryManager>();
_taskManager = taskManager;
_userManager = userManager;
_configurationManager = configurationManager;
@@ -144,7 +144,10 @@ namespace Emby.Server.Implementations.Library
_mediaEncoder = mediaEncoder;
_itemRepository = itemRepository;
_imageProcessor = imageProcessor;
- _memoryCache = memoryCache;
+ _cache = new ConcurrentDictionary<Guid, BaseItem>();
+ _namingOptions = namingOptions;
+
+ _extraResolver = new ExtraResolver(loggerFactory.CreateLogger<ExtraResolver>(), namingOptions, directoryService);
_configurationManager.ConfigurationUpdated += ConfigurationUpdated;
@@ -174,7 +177,7 @@ namespace Emby.Server.Implementations.Library
{
get
{
- if (_rootFolder == null)
+ if (_rootFolder is null)
{
lock (_rootFolderSyncLock)
{
@@ -279,27 +282,24 @@ namespace Emby.Server.Implementations.Library
public void RegisterItem(BaseItem item)
{
- if (item == null)
- {
- throw new ArgumentNullException(nameof(item));
- }
+ ArgumentNullException.ThrowIfNull(item);
if (item is IItemByName)
{
- if (!(item is MusicArtist))
+ if (item is not MusicArtist)
{
return;
}
}
else if (!item.IsFolder)
{
- if (!(item is Video) && !(item is LiveTvChannel))
+ if (item is not Video && item is not LiveTvChannel)
{
return;
}
}
- _memoryCache.Set(item.Id, item);
+ _cache[item.Id] = item;
}
public void DeleteItem(BaseItem item, DeleteOptions options)
@@ -309,10 +309,7 @@ namespace Emby.Server.Implementations.Library
public void DeleteItem(BaseItem item, DeleteOptions options, bool notifyParentItem)
{
- if (item == null)
- {
- throw new ArgumentNullException(nameof(item));
- }
+ ArgumentNullException.ThrowIfNull(item);
var parent = item.GetOwner() ?? item.GetParent();
@@ -321,10 +318,7 @@ namespace Emby.Server.Implementations.Library
public void DeleteItem(BaseItem item, DeleteOptions options, BaseItem parent, bool notifyParentItem)
{
- if (item == null)
- {
- throw new ArgumentNullException(nameof(item));
- }
+ ArgumentNullException.ThrowIfNull(item);
if (item.SourceType == SourceType.Channel)
{
@@ -332,8 +326,7 @@ namespace Emby.Server.Implementations.Library
{
try
{
- var task = BaseItem.ChannelManager.DeleteItem(item);
- Task.WaitAll(task);
+ BaseItem.ChannelManager.DeleteItem(item).GetAwaiter().GetResult();
}
catch (ArgumentException)
{
@@ -364,8 +357,8 @@ namespace Emby.Server.Implementations.Library
}
var children = item.IsFolder
- ? ((Folder)item).GetRecursiveChildren(false).ToList()
- : new List<BaseItem>();
+ ? ((Folder)item).GetRecursiveChildren(false)
+ : Array.Empty<BaseItem>();
foreach (var metadataPath in GetMetadataPaths(item, children))
{
@@ -447,7 +440,7 @@ namespace Emby.Server.Implementations.Library
_itemRepository.DeleteItem(child.Id);
}
- _memoryCache.Remove(item.Id);
+ _cache.TryRemove(item.Id, out _);
ReportItemRemoved(item, parent);
}
@@ -473,9 +466,9 @@ namespace Emby.Server.Implementations.Library
private BaseItem ResolveItem(ItemResolveArgs args, IItemResolver[] resolvers)
{
var item = (resolvers ?? EntityResolvers).Select(r => Resolve(args, r))
- .FirstOrDefault(i => i != null);
+ .FirstOrDefault(i => i is not null);
- if (item != null)
+ if (item is not null)
{
ResolverHelper.SetInitialItemValues(item, args, _fileSystem, this);
}
@@ -491,7 +484,7 @@ namespace Emby.Server.Implementations.Library
}
catch (Exception ex)
{
- _logger.LogError(ex, "Error in {resolver} resolving {path}", resolver.GetType().Name, args.Path);
+ _logger.LogError(ex, "Error in {Resolver} resolving {Path}", resolver.GetType().Name, args.Path);
return null;
}
}
@@ -503,15 +496,8 @@ namespace Emby.Server.Implementations.Library
private Guid GetNewItemIdInternal(string key, Type type, bool forceCaseInsensitive)
{
- if (string.IsNullOrEmpty(key))
- {
- throw new ArgumentNullException(nameof(key));
- }
-
- if (type == null)
- {
- throw new ArgumentNullException(nameof(type));
- }
+ ArgumentException.ThrowIfNullOrEmpty(key);
+ ArgumentNullException.ThrowIfNull(type);
string programDataPath = _configurationManager.ApplicationPaths.ProgramDataPath;
if (key.StartsWith(programDataPath, StringComparison.Ordinal))
@@ -532,8 +518,8 @@ namespace Emby.Server.Implementations.Library
return key.GetMD5();
}
- public BaseItem ResolvePath(FileSystemMetadata fileInfo, Folder parent = null)
- => ResolvePath(fileInfo, new DirectoryService(_fileSystem), null, parent);
+ public BaseItem ResolvePath(FileSystemMetadata fileInfo, Folder parent = null, IDirectoryService directoryService = null)
+ => ResolvePath(fileInfo, directoryService ?? new DirectoryService(_fileSystem), null, parent);
private BaseItem ResolvePath(
FileSystemMetadata fileInfo,
@@ -543,19 +529,16 @@ namespace Emby.Server.Implementations.Library
string collectionType = null,
LibraryOptions libraryOptions = null)
{
- if (fileInfo == null)
- {
- throw new ArgumentNullException(nameof(fileInfo));
- }
+ ArgumentNullException.ThrowIfNull(fileInfo);
var fullPath = fileInfo.FullName;
- if (string.IsNullOrEmpty(collectionType) && parent != null)
+ if (string.IsNullOrEmpty(collectionType) && parent is not null)
{
collectionType = GetContentTypeOverride(fullPath, true);
}
- var args = new ItemResolveArgs(_configurationManager.ApplicationPaths, directoryService)
+ var args = new ItemResolveArgs(_configurationManager.ApplicationPaths, this)
{
Parent = parent,
FileInfo = fileInfo,
@@ -586,7 +569,7 @@ namespace Emby.Server.Implementations.Library
}
catch (Exception ex)
{
- if (parent != null && parent.IsPhysicalRoot)
+ if (parent is not null && parent.IsPhysicalRoot)
{
_logger.LogError(ex, "Error in GetFilteredFileSystemEntries isPhysicalRoot: {0} IsVf: {1}", isPhysicalRoot, isVf);
@@ -646,14 +629,14 @@ namespace Emby.Server.Implementations.Library
/// 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>
+ /// <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");
}
- public IEnumerable<BaseItem> ResolvePaths(IEnumerable<FileSystemMetadata> files, IDirectoryService directoryService, Folder parent, LibraryOptions libraryOptions, string collectionType)
+ public IEnumerable<BaseItem> ResolvePaths(IEnumerable<FileSystemMetadata> files, IDirectoryService directoryService, Folder parent, LibraryOptions libraryOptions, string collectionType = null)
{
return ResolvePaths(files, directoryService, parent, libraryOptions, collectionType, EntityResolvers);
}
@@ -668,24 +651,18 @@ namespace Emby.Server.Implementations.Library
{
var fileList = files.Where(i => !IgnoreFile(i, parent)).ToList();
- if (parent != null)
+ if (parent is not null)
{
- var multiItemResolvers = resolvers == null ? MultiItemResolvers : resolvers.OfType<IMultiItemResolver>().ToArray();
+ var multiItemResolvers = resolvers is null ? MultiItemResolvers : resolvers.OfType<IMultiItemResolver>().ToArray();
foreach (var resolver in multiItemResolvers)
{
var result = resolver.ResolveMultiple(parent, fileList, collectionType, directoryService);
- if (result != null && result.Items.Count > 0)
+ if (result?.Items.Count > 0)
{
- var items = new List<BaseItem>();
- items.AddRange(result.Items);
-
- foreach (var item in items)
- {
- ResolverHelper.SetInitialItemValues(item, parent, this, directoryService);
- }
-
+ var items = result.Items;
+ items.RemoveAll(item => !ResolverHelper.SetInitialItemValues(item, parent, this, directoryService));
items.AddRange(ResolveFileList(result.ExtraFiles, directoryService, parent, collectionType, resolvers, libraryOptions));
return items;
}
@@ -717,7 +694,7 @@ namespace Emby.Server.Implementations.Library
_logger.LogError(ex, "Error resolving path {Path}", file.FullName);
}
- if (result != null)
+ if (result is not null)
{
yield return result;
}
@@ -756,7 +733,7 @@ namespace Emby.Server.Implementations.Library
Path = path
};
- if (folder.Id.Equals(Guid.Empty))
+ if (folder.Id.Equals(default))
{
if (string.IsNullOrEmpty(folder.Path))
{
@@ -770,12 +747,12 @@ namespace Emby.Server.Implementations.Library
var dbItem = GetItemById(folder.Id) as BasePluginFolder;
- if (dbItem != null && string.Equals(dbItem.Path, folder.Path, StringComparison.OrdinalIgnoreCase))
+ if (dbItem is not null && string.Equals(dbItem.Path, folder.Path, StringComparison.OrdinalIgnoreCase))
{
folder = dbItem;
}
- if (folder.ParentId != rootFolder.Id)
+ if (!folder.ParentId.Equals(rootFolder.Id))
{
folder.ParentId = rootFolder.Id;
folder.UpdateToRepositoryAsync(ItemUpdateType.MetadataImport, CancellationToken.None).GetAwaiter().GetResult();
@@ -790,15 +767,15 @@ namespace Emby.Server.Implementations.Library
public Folder GetUserRootFolder()
{
- if (_userRootFolder == null)
+ if (_userRootFolder is null)
{
lock (_userRootFolderSyncLock)
{
- if (_userRootFolder == null)
+ if (_userRootFolder is null)
{
var userRootPath = _configurationManager.ApplicationPaths.DefaultUserViewsPath;
- _logger.LogDebug("Creating userRootPath at {path}", userRootPath);
+ _logger.LogDebug("Creating userRootPath at {Path}", userRootPath);
Directory.CreateDirectory(userRootPath);
var newItemId = GetNewItemId(userRootPath, typeof(UserRootFolder));
@@ -809,10 +786,10 @@ namespace Emby.Server.Implementations.Library
}
catch (Exception ex)
{
- _logger.LogError(ex, "Error creating UserRootFolder {path}", newItemId);
+ _logger.LogError(ex, "Error creating UserRootFolder {Path}", newItemId);
}
- if (tmpItem == null)
+ if (tmpItem is null)
{
_logger.LogDebug("Creating new userRootFolder with DeepCopy");
tmpItem = ((Folder)ResolvePath(_fileSystem.GetDirectoryInfo(userRootPath))).DeepCopy<Folder, UserRootFolder>();
@@ -826,7 +803,7 @@ namespace Emby.Server.Implementations.Library
}
_userRootFolder = tmpItem;
- _logger.LogDebug("Setting userRootFolder: {folder}", _userRootFolder);
+ _logger.LogDebug("Setting userRootFolder: {Folder}", _userRootFolder);
}
}
}
@@ -838,10 +815,7 @@ namespace Emby.Server.Implementations.Library
{
// If this returns multiple items it could be tricky figuring out which one is correct.
// In most cases, the newest one will be and the others obsolete but not yet cleaned up
- if (string.IsNullOrEmpty(path))
- {
- throw new ArgumentNullException(nameof(path));
- }
+ ArgumentException.ThrowIfNullOrEmpty(path);
var query = new InternalItemsQuery
{
@@ -865,7 +839,7 @@ namespace Emby.Server.Implementations.Library
{
var path = Person.GetPath(name);
var id = GetItemByNameId<Person>(path);
- if (!(GetItemById(id) is Person item))
+ if (GetItemById(id) is not Person item)
{
item = new Person
{
@@ -964,7 +938,7 @@ namespace Emby.Server.Implementations.Library
{
var existing = GetItemList(new InternalItemsQuery
{
- IncludeItemTypes = new[] { nameof(MusicArtist) },
+ IncludeItemTypes = new[] { BaseItemKind.MusicArtist },
Name = name,
DtoOptions = options
}).Cast<MusicArtist>()
@@ -972,7 +946,7 @@ namespace Emby.Server.Implementations.Library
.Cast<T>()
.FirstOrDefault();
- if (existing != null)
+ if (existing is not null)
{
return existing;
}
@@ -981,7 +955,7 @@ namespace Emby.Server.Implementations.Library
var path = getPathFn(name);
var id = GetItemByNameId<T>(path);
var item = GetItemById(id) as T;
- if (item == null)
+ if (item is null)
{
item = new T
{
@@ -1005,14 +979,8 @@ namespace Emby.Server.Implementations.Library
return GetNewItemIdInternal(path, typeof(T), forceCaseInsensitiveId);
}
- /// <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>
- public Task ValidatePeople(CancellationToken cancellationToken, IProgress<double> progress)
+ /// <inheritdoc />
+ public Task ValidatePeopleAsync(IProgress<double> progress, CancellationToken cancellationToken)
{
// Ensure the location is available.
Directory.CreateDirectory(_configurationManager.ApplicationPaths.PeoplePath);
@@ -1035,15 +1003,6 @@ namespace Emby.Server.Implementations.Library
}
/// <summary>
- /// Queues the library scan.
- /// </summary>
- public void QueueLibraryScan()
- {
- // Just run the scheduled task so that the user can see it
- _taskManager.QueueScheduledTask<RefreshMediaLibraryTask>();
- }
-
- /// <summary>
/// Validates the media library internal.
/// </summary>
/// <param name="progress">The progress.</param>
@@ -1196,7 +1155,7 @@ namespace Emby.Server.Implementations.Library
.ToList();
}
- private VirtualFolderInfo GetVirtualFolderInfo(string dir, List<BaseItem> allCollectionFolders, Dictionary<Guid, Guid> refreshQueue)
+ private VirtualFolderInfo GetVirtualFolderInfo(string dir, List<BaseItem> allCollectionFolders, HashSet<Guid> refreshQueue)
{
var info = new VirtualFolderInfo
{
@@ -1212,34 +1171,34 @@ namespace Emby.Server.Implementations.Library
}
catch (Exception ex)
{
- _logger.LogError(ex, "Error resolving shortcut file {file}", i);
+ _logger.LogError(ex, "Error resolving shortcut file {File}", i);
return null;
}
})
- .Where(i => i != null)
- .OrderBy(i => i)
+ .Where(i => i is not null)
+ .Order()
.ToArray(),
CollectionType = GetCollectionType(dir)
};
var libraryFolder = allCollectionFolders.FirstOrDefault(i => string.Equals(i.Path, dir, StringComparison.OrdinalIgnoreCase));
-
- if (libraryFolder != null && libraryFolder.HasImage(ImageType.Primary))
+ if (libraryFolder is not null)
{
- info.PrimaryImageItemId = libraryFolder.Id.ToString("N", CultureInfo.InvariantCulture);
- }
+ var libraryFolderId = libraryFolder.Id.ToString("N", CultureInfo.InvariantCulture);
+ info.ItemId = libraryFolderId;
+ if (libraryFolder.HasImage(ImageType.Primary))
+ {
+ info.PrimaryImageItemId = libraryFolderId;
+ }
- if (libraryFolder != null)
- {
- info.ItemId = libraryFolder.Id.ToString("N", CultureInfo.InvariantCulture);
info.LibraryOptions = GetLibraryOptions(libraryFolder);
- if (refreshQueue != null)
+ if (refreshQueue is not null)
{
info.RefreshProgress = libraryFolder.GetRefreshProgress();
- info.RefreshStatus = info.RefreshProgress.HasValue ? "Active" : refreshQueue.ContainsKey(libraryFolder.Id) ? "Queued" : "Idle";
+ info.RefreshStatus = info.RefreshProgress.HasValue ? "Active" : refreshQueue.Contains(libraryFolder.Id) ? "Queued" : "Idle";
}
}
@@ -1249,10 +1208,8 @@ namespace Emby.Server.Implementations.Library
private CollectionTypeOptions? GetCollectionType(string path)
{
var files = _fileSystem.GetFilePaths(path, new[] { ".collection" }, true, false);
- foreach (var file in files)
+ foreach (ReadOnlySpan<char> file in files)
{
- // TODO: @bond use a ReadOnlySpan<char> here when Enum.TryParse supports it
- // https://github.com/dotnet/runtime/issues/20008
if (Enum.TryParse<CollectionTypeOptions>(Path.GetFileNameWithoutExtension(file), true, out var res))
{
return res;
@@ -1267,22 +1224,22 @@ namespace Emby.Server.Implementations.Library
/// </summary>
/// <param name="id">The id.</param>
/// <returns>BaseItem.</returns>
- /// <exception cref="ArgumentNullException">id</exception>
+ /// <exception cref="ArgumentNullException"><paramref name="id"/> is <c>null</c>.</exception>
public BaseItem GetItemById(Guid id)
{
- if (id == Guid.Empty)
+ if (id.Equals(default))
{
throw new ArgumentException("Guid can't be empty", nameof(id));
}
- if (_memoryCache.TryGetValue(id, out BaseItem item))
+ if (_cache.TryGetValue(id, out BaseItem item))
{
return item;
}
item = RetrieveItem(id);
- if (item != null)
+ if (item is not null)
{
RegisterItem(item);
}
@@ -1292,21 +1249,28 @@ namespace Emby.Server.Implementations.Library
public List<BaseItem> GetItemList(InternalItemsQuery query, bool allowExternalContent)
{
- if (query.Recursive && query.ParentId != Guid.Empty)
+ if (query.Recursive && !query.ParentId.Equals(default))
{
var parent = GetItemById(query.ParentId);
- if (parent != null)
+ if (parent is not null)
{
- SetTopParentIdsOrAncestors(query, new List<BaseItem> { parent });
+ SetTopParentIdsOrAncestors(query, new[] { parent });
}
}
- if (query.User != null)
+ if (query.User is not null)
{
AddUserToQuery(query, query.User, allowExternalContent);
}
- return _itemRepository.GetItemList(query);
+ var itemList = _itemRepository.GetItemList(query);
+ var user = query.User;
+ if (user is not null)
+ {
+ return itemList.Where(i => i.IsVisible(user)).ToList();
+ }
+
+ return itemList;
}
public List<BaseItem> GetItemList(InternalItemsQuery query)
@@ -1316,16 +1280,16 @@ namespace Emby.Server.Implementations.Library
public int GetCount(InternalItemsQuery query)
{
- if (query.Recursive && !query.ParentId.Equals(Guid.Empty))
+ if (query.Recursive && !query.ParentId.Equals(default))
{
var parent = GetItemById(query.ParentId);
- if (parent != null)
+ if (parent is not null)
{
- SetTopParentIdsOrAncestors(query, new List<BaseItem> { parent });
+ SetTopParentIdsOrAncestors(query, new[] { parent });
}
}
- if (query.User != null)
+ if (query.User is not null)
{
AddUserToQuery(query, query.User);
}
@@ -1339,7 +1303,7 @@ namespace Emby.Server.Implementations.Library
if (query.AncestorIds.Length == 0 && query.TopParentIds.Length == 0)
{
- if (query.User != null)
+ if (query.User is not null)
{
AddUserToQuery(query, query.User);
}
@@ -1350,7 +1314,7 @@ namespace Emby.Server.Implementations.Library
public QueryResult<BaseItem> QueryItems(InternalItemsQuery query)
{
- if (query.User != null)
+ if (query.User is not null)
{
AddUserToQuery(query, query.User);
}
@@ -1360,15 +1324,15 @@ namespace Emby.Server.Implementations.Library
return _itemRepository.GetItems(query);
}
- return new QueryResult<BaseItem>
- {
- Items = _itemRepository.GetItemList(query)
- };
+ return new QueryResult<BaseItem>(
+ query.StartIndex,
+ null,
+ _itemRepository.GetItemList(query));
}
public List<Guid> GetItemIds(InternalItemsQuery query)
{
- if (query.User != null)
+ if (query.User is not null)
{
AddUserToQuery(query, query.User);
}
@@ -1376,9 +1340,9 @@ namespace Emby.Server.Implementations.Library
return _itemRepository.GetItemIdsList(query);
}
- public QueryResult<(BaseItem, ItemCounts)> GetStudios(InternalItemsQuery query)
+ public QueryResult<(BaseItem Item, ItemCounts ItemCounts)> GetStudios(InternalItemsQuery query)
{
- if (query.User != null)
+ if (query.User is not null)
{
AddUserToQuery(query, query.User);
}
@@ -1387,9 +1351,9 @@ namespace Emby.Server.Implementations.Library
return _itemRepository.GetStudios(query);
}
- public QueryResult<(BaseItem, ItemCounts)> GetGenres(InternalItemsQuery query)
+ public QueryResult<(BaseItem Item, ItemCounts ItemCounts)> GetGenres(InternalItemsQuery query)
{
- if (query.User != null)
+ if (query.User is not null)
{
AddUserToQuery(query, query.User);
}
@@ -1398,9 +1362,9 @@ namespace Emby.Server.Implementations.Library
return _itemRepository.GetGenres(query);
}
- public QueryResult<(BaseItem, ItemCounts)> GetMusicGenres(InternalItemsQuery query)
+ public QueryResult<(BaseItem Item, ItemCounts ItemCounts)> GetMusicGenres(InternalItemsQuery query)
{
- if (query.User != null)
+ if (query.User is not null)
{
AddUserToQuery(query, query.User);
}
@@ -1409,9 +1373,9 @@ namespace Emby.Server.Implementations.Library
return _itemRepository.GetMusicGenres(query);
}
- public QueryResult<(BaseItem, ItemCounts)> GetAllArtists(InternalItemsQuery query)
+ public QueryResult<(BaseItem Item, ItemCounts ItemCounts)> GetAllArtists(InternalItemsQuery query)
{
- if (query.User != null)
+ if (query.User is not null)
{
AddUserToQuery(query, query.User);
}
@@ -1420,9 +1384,9 @@ namespace Emby.Server.Implementations.Library
return _itemRepository.GetAllArtists(query);
}
- public QueryResult<(BaseItem, ItemCounts)> GetArtists(InternalItemsQuery query)
+ public QueryResult<(BaseItem Item, ItemCounts ItemCounts)> GetArtists(InternalItemsQuery query)
{
- if (query.User != null)
+ if (query.User is not null)
{
AddUserToQuery(query, query.User);
}
@@ -1444,7 +1408,7 @@ namespace Emby.Server.Implementations.Library
for (int i = 0; i < len; i++)
{
parents[i] = GetItemById(ancestorIds[i]);
- if (!(parents[i] is ICollectionFolder || parents[i] is UserView))
+ if (parents[i] is not (ICollectionFolder or UserView))
{
return;
}
@@ -1461,9 +1425,9 @@ namespace Emby.Server.Implementations.Library
}
}
- public QueryResult<(BaseItem, ItemCounts)> GetAlbumArtists(InternalItemsQuery query)
+ public QueryResult<(BaseItem Item, ItemCounts ItemCounts)> GetAlbumArtists(InternalItemsQuery query)
{
- if (query.User != null)
+ if (query.User is not null)
{
AddUserToQuery(query, query.User);
}
@@ -1474,16 +1438,16 @@ namespace Emby.Server.Implementations.Library
public QueryResult<BaseItem> GetItemsResult(InternalItemsQuery query)
{
- if (query.Recursive && !query.ParentId.Equals(Guid.Empty))
+ if (query.Recursive && !query.ParentId.Equals(default))
{
var parent = GetItemById(query.ParentId);
- if (parent != null)
+ if (parent is not null)
{
- SetTopParentIdsOrAncestors(query, new List<BaseItem> { parent });
+ SetTopParentIdsOrAncestors(query, new[] { parent });
}
}
- if (query.User != null)
+ if (query.User is not null)
{
AddUserToQuery(query, query.User);
}
@@ -1493,13 +1457,13 @@ namespace Emby.Server.Implementations.Library
return _itemRepository.GetItems(query);
}
- return new QueryResult<BaseItem>
- {
- Items = _itemRepository.GetItemList(query)
- };
+ return new QueryResult<BaseItem>(
+ query.StartIndex,
+ null,
+ _itemRepository.GetItemList(query));
}
- private void SetTopParentIdsOrAncestors(InternalItemsQuery query, List<BaseItem> parents)
+ private void SetTopParentIdsOrAncestors(InternalItemsQuery query, IReadOnlyCollection<BaseItem> parents)
{
if (parents.All(i => i is ICollectionFolder || i is UserView))
{
@@ -1530,7 +1494,7 @@ namespace Emby.Server.Implementations.Library
private void AddUserToQuery(InternalItemsQuery query, User user, bool allowExternalContent = true)
{
if (query.AncestorIds.Length == 0 &&
- query.ParentId.Equals(Guid.Empty) &&
+ query.ParentId.Equals(default) &&
query.ChannelIds.Count == 0 &&
query.TopParentIds.Length == 0 &&
string.IsNullOrEmpty(query.AncestorWithPresentationUniqueKey) &&
@@ -1545,6 +1509,12 @@ namespace Emby.Server.Implementations.Library
});
query.TopParentIds = userViews.SelectMany(i => GetTopParentIdsForQuery(i, user)).ToArray();
+
+ // Prevent searching in all libraries due to empty filter
+ if (query.TopParentIds.Length == 0)
+ {
+ query.TopParentIds = new[] { Guid.NewGuid() };
+ }
}
}
@@ -1558,10 +1528,10 @@ namespace Emby.Server.Implementations.Library
}
// Translate view into folders
- if (!view.DisplayParentId.Equals(Guid.Empty))
+ if (!view.DisplayParentId.Equals(default))
{
var displayParent = GetItemById(view.DisplayParentId);
- if (displayParent != null)
+ if (displayParent is not null)
{
return GetTopParentIdsForQuery(displayParent, user);
}
@@ -1569,10 +1539,10 @@ namespace Emby.Server.Implementations.Library
return Array.Empty<Guid>();
}
- if (!view.ParentId.Equals(Guid.Empty))
+ if (!view.ParentId.Equals(default))
{
var displayParent = GetItemById(view.ParentId);
- if (displayParent != null)
+ if (displayParent is not null)
{
return GetTopParentIdsForQuery(displayParent, user);
}
@@ -1581,7 +1551,7 @@ namespace Emby.Server.Implementations.Library
}
// Handle grouping
- if (user != null && !string.IsNullOrEmpty(view.ViewType) && UserView.IsEligibleForGrouping(view.ViewType)
+ if (user is not null && !string.IsNullOrEmpty(view.ViewType) && UserView.IsEligibleForGrouping(view.ViewType)
&& user.GetPreference(PreferenceKind.GroupedFolders).Length > 0)
{
return GetUserRootFolder()
@@ -1601,7 +1571,7 @@ namespace Emby.Server.Implementations.Library
}
var topParent = item.GetTopParent();
- if (topParent != null)
+ if (topParent is not null)
{
return new[] { topParent.Id };
}
@@ -1626,7 +1596,7 @@ namespace Emby.Server.Implementations.Library
return items
.SelectMany(i => i.ToArray())
.Select(ResolveIntro)
- .Where(i => i != null);
+ .Where(i => i is not null);
}
/// <summary>
@@ -1646,32 +1616,11 @@ namespace Emby.Server.Implementations.Library
{
_logger.LogError(ex, "Error getting intros");
- return new List<IntroInfo>();
+ return Enumerable.Empty<IntroInfo>();
}
}
/// <summary>
- /// Gets all intro files.
- /// </summary>
- /// <returns>IEnumerable{System.String}.</returns>
- public IEnumerable<string> GetAllIntroFiles()
- {
- return IntroProviders.SelectMany(i =>
- {
- try
- {
- return i.GetAllIntroFiles().ToList();
- }
- catch (Exception ex)
- {
- _logger.LogError(ex, "Error getting intro files");
-
- return new List<string>();
- }
- });
- }
-
- /// <summary>
/// Resolves the intro.
/// </summary>
/// <param name="info">The info.</param>
@@ -1685,7 +1634,7 @@ namespace Emby.Server.Implementations.Library
// Get an existing item by Id
video = GetItemById(info.ItemId.Value) as Video;
- if (video == null)
+ if (video is null)
{
_logger.LogError("Unable to locate item with Id {ID}.", info.ItemId.Value);
}
@@ -1697,16 +1646,16 @@ namespace Emby.Server.Implementations.Library
// Try to resolve the path into a video
video = ResolvePath(_fileSystem.GetFileSystemInfo(info.Path)) as Video;
- if (video == null)
+ if (video is null)
{
- _logger.LogError("Intro resolver returned null for {path}.", info.Path);
+ _logger.LogError("Intro resolver returned null for {Path}.", info.Path);
}
else
{
// Pull the saved db item that will include metadata
var dbItem = GetItemById(video.Id) as Video;
- if (dbItem != null)
+ if (dbItem is not null)
{
video = dbItem;
}
@@ -1718,7 +1667,7 @@ namespace Emby.Server.Implementations.Library
}
catch (Exception ex)
{
- _logger.LogError(ex, "Error resolving path {path}.", info.Path);
+ _logger.LogError(ex, "Error resolving path {Path}.", info.Path);
}
}
else
@@ -1743,7 +1692,7 @@ namespace Emby.Server.Implementations.Library
IOrderedEnumerable<BaseItem> orderedItems = null;
- foreach (var orderBy in sortBy.Select(o => GetComparer(o, user)).Where(c => c != null))
+ foreach (var orderBy in sortBy.Select(o => GetComparer(o, user)).Where(c => c is not null))
{
if (isFirst)
{
@@ -1760,22 +1709,20 @@ namespace Emby.Server.Implementations.Library
return orderedItems ?? items;
}
- public IEnumerable<BaseItem> Sort(IEnumerable<BaseItem> items, User user, IEnumerable<ValueTuple<string, SortOrder>> orderByList)
+ public IEnumerable<BaseItem> Sort(IEnumerable<BaseItem> items, User user, IEnumerable<(string OrderBy, SortOrder SortOrder)> orderBy)
{
var isFirst = true;
IOrderedEnumerable<BaseItem> orderedItems = null;
- foreach (var orderBy in orderByList)
+ foreach (var (name, sortOrder) in orderBy)
{
- var comparer = GetComparer(orderBy.Item1, user);
- if (comparer == null)
+ var comparer = GetComparer(name, user);
+ if (comparer is null)
{
continue;
}
- var sortOrder = orderBy.Item2;
-
if (isFirst)
{
orderedItems = sortOrder == SortOrder.Descending ? items.OrderByDescending(i => i, comparer) : items.OrderBy(i => i, comparer);
@@ -1841,7 +1788,7 @@ namespace Emby.Server.Implementations.Library
RegisterItem(item);
}
- if (ItemAdded != null)
+ if (ItemAdded is not null)
{
foreach (var item in items)
{
@@ -1871,7 +1818,7 @@ namespace Emby.Server.Implementations.Library
private bool ImageNeedsRefresh(ItemImageInfo image)
{
- if (image.Path != null && image.IsLocalFile)
+ if (image.Path is not null && image.IsLocalFile)
{
if (image.Width == 0 || image.Height == 0 || string.IsNullOrEmpty(image.BlurHash))
{
@@ -1889,18 +1836,17 @@ namespace Emby.Server.Implementations.Library
}
}
- return image.Path != null && !image.IsLocalFile;
+ return image.Path is not null && !image.IsLocalFile;
}
/// <inheritdoc />
public async Task UpdateImagesAsync(BaseItem item, bool forceUpdate = false)
{
- if (item == null)
- {
- throw new ArgumentNullException(nameof(item));
- }
+ ArgumentNullException.ThrowIfNull(item);
- 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 is not null).ToArray()
+ : item.ImageInfos.Where(ImageNeedsRefresh).ToArray();
// Skip image processing if current or live tv source
if (outdated.Length == 0 || item.SourceType != SourceType.Library)
{
@@ -1923,7 +1869,7 @@ namespace Emby.Server.Implementations.Library
_logger.LogWarning("Cannot get image index for {ImagePath}", img.Path);
continue;
}
- catch (Exception ex) when (ex is InvalidOperationException || ex is IOException)
+ catch (Exception ex) when (ex is InvalidOperationException or IOException)
{
_logger.LogWarning(ex, "Cannot fetch image from {ImagePath}", img.Path);
continue;
@@ -1935,23 +1881,24 @@ namespace Emby.Server.Implementations.Library
}
}
+ ImageDimensions size;
try
{
- ImageDimensions size = _imageProcessor.GetImageDimensions(item, image);
+ size = _imageProcessor.GetImageDimensions(item, image);
image.Width = size.Width;
image.Height = size.Height;
}
catch (Exception ex)
{
_logger.LogError(ex, "Cannot get image dimensions for {ImagePath}", image.Path);
+ size = default;
image.Width = 0;
image.Height = 0;
- continue;
}
try
{
- image.BlurHash = _imageProcessor.GetImageBlurHash(image.Path);
+ image.BlurHash = _imageProcessor.GetImageBlurHash(image.Path, size);
}
catch (Exception ex)
{
@@ -1983,7 +1930,7 @@ namespace Emby.Server.Implementations.Library
_itemRepository.SaveItems(items, cancellationToken);
- if (ItemUpdated != null)
+ if (ItemUpdated is not null)
{
foreach (var item in items)
{
@@ -2016,16 +1963,16 @@ namespace Emby.Server.Implementations.Library
public Task UpdateItemAsync(BaseItem item, BaseItem parent, ItemUpdateType updateReason, CancellationToken cancellationToken)
=> UpdateItemsAsync(new[] { item }, parent, updateReason, cancellationToken);
- public Task RunMetadataSavers(BaseItem item, ItemUpdateType updateReason)
+ public async Task RunMetadataSavers(BaseItem item, ItemUpdateType updateReason)
{
if (item.IsFileProtocol)
{
- ProviderManager.SaveMetadata(item, updateReason);
+ await ProviderManager.SaveMetadataAsync(item, updateReason).ConfigureAwait(false);
}
item.DateLastSaved = DateTime.UtcNow;
- return UpdateImagesAsync(item, updateReason >= ItemUpdateType.ImageUpdate);
+ await UpdateImagesAsync(item, updateReason >= ItemUpdateType.ImageUpdate).ConfigureAwait(false);
}
/// <summary>
@@ -2035,7 +1982,7 @@ namespace Emby.Server.Implementations.Library
/// <param name="parent">The parent item.</param>
public void ReportItemRemoved(BaseItem item, BaseItem parent)
{
- if (ItemRemoved != null)
+ if (ItemRemoved is not null)
{
try
{
@@ -2066,41 +2013,38 @@ namespace Emby.Server.Implementations.Library
public List<Folder> GetCollectionFolders(BaseItem item)
{
- while (item != null)
+ return GetCollectionFolders(item, GetUserRootFolder().Children.OfType<Folder>());
+ }
+
+ public List<Folder> GetCollectionFolders(BaseItem item, IEnumerable<Folder> allUserRootChildren)
+ {
+ while (item is not null)
{
var parent = item.GetParent();
- if (parent == null || parent is AggregateFolder)
+ if (parent is AggregateFolder)
{
break;
}
- item = parent;
- }
-
- if (item == null)
- {
- return new List<Folder>();
- }
-
- return GetCollectionFoldersInternal(item, GetUserRootFolder().Children.OfType<Folder>());
- }
+ if (parent is null)
+ {
+ var owner = item.GetOwner();
- public List<Folder> GetCollectionFolders(BaseItem item, List<Folder> allUserRootChildren)
- {
- while (item != null)
- {
- var parent = item.GetParent();
+ if (owner is null)
+ {
+ break;
+ }
- if (parent == null || parent is AggregateFolder)
+ item = owner;
+ }
+ else
{
- break;
+ item = parent;
}
-
- item = parent;
}
- if (item == null)
+ if (item is null)
{
return new List<Folder>();
}
@@ -2117,14 +2061,16 @@ namespace Emby.Server.Implementations.Library
public LibraryOptions GetLibraryOptions(BaseItem item)
{
- if (!(item is CollectionFolder collectionFolder))
+ if (item is not CollectionFolder collectionFolder)
{
// List.Find is more performant than FirstOrDefault due to enumerator allocation
collectionFolder = GetCollectionFolders(item)
.Find(folder => folder is CollectionFolder) as CollectionFolder;
}
- return collectionFolder == null ? new LibraryOptions() : collectionFolder.GetLibraryOptions();
+ return collectionFolder is null
+ ? new LibraryOptions()
+ : collectionFolder.GetLibraryOptions();
}
public string GetContentType(BaseItem item)
@@ -2189,15 +2135,15 @@ namespace Emby.Server.Implementations.Library
private string GetTopFolderContentType(BaseItem item)
{
- if (item == null)
+ if (item is null)
{
return null;
}
- while (!item.ParentId.Equals(Guid.Empty))
+ while (!item.ParentId.Equals(default))
{
var parent = item.GetParent();
- if (parent == null || parent is AggregateFolder)
+ if (parent is null || parent is AggregateFolder)
{
break;
}
@@ -2237,7 +2183,7 @@ namespace Emby.Server.Implementations.Library
var refresh = false;
- if (item == null || !string.Equals(item.Path, path, StringComparison.OrdinalIgnoreCase))
+ if (item is null || !string.Equals(item.Path, path, StringComparison.OrdinalIgnoreCase))
{
Directory.CreateDirectory(path);
@@ -2272,7 +2218,9 @@ namespace Emby.Server.Implementations.Library
string viewType,
string sortName)
{
- var parentIdString = parentId.Equals(Guid.Empty) ? null : parentId.ToString("N", CultureInfo.InvariantCulture);
+ var parentIdString = parentId.Equals(default)
+ ? null
+ : parentId.ToString("N", CultureInfo.InvariantCulture);
var idValues = "38_namedview_" + name + user.Id.ToString("N", CultureInfo.InvariantCulture) + (parentIdString ?? string.Empty) + (viewType ?? string.Empty);
var id = GetNewItemId(idValues, typeof(UserView));
@@ -2283,7 +2231,7 @@ namespace Emby.Server.Implementations.Library
var isNew = false;
- if (item == null)
+ if (item is null)
{
Directory.CreateDirectory(path);
@@ -2306,10 +2254,10 @@ namespace Emby.Server.Implementations.Library
var refresh = isNew || DateTime.UtcNow - item.DateLastRefreshed >= _viewRefreshInterval;
- if (!refresh && !item.DisplayParentId.Equals(Guid.Empty))
+ if (!refresh && !item.DisplayParentId.Equals(default))
{
var displayParent = GetItemById(item.DisplayParentId);
- refresh = displayParent != null && displayParent.DateLastSaved > item.DateLastRefreshed;
+ refresh = displayParent is not null && displayParent.DateLastSaved > item.DateLastRefreshed;
}
if (refresh)
@@ -2332,10 +2280,7 @@ namespace Emby.Server.Implementations.Library
string viewType,
string sortName)
{
- if (parent == null)
- {
- throw new ArgumentNullException(nameof(parent));
- }
+ ArgumentNullException.ThrowIfNull(parent);
var name = parent.Name;
var parentId = parent.Id;
@@ -2350,7 +2295,7 @@ namespace Emby.Server.Implementations.Library
var isNew = false;
- if (item == null)
+ if (item is null)
{
Directory.CreateDirectory(path);
@@ -2373,10 +2318,10 @@ namespace Emby.Server.Implementations.Library
var refresh = isNew || DateTime.UtcNow - item.DateLastRefreshed >= _viewRefreshInterval;
- if (!refresh && !item.DisplayParentId.Equals(Guid.Empty))
+ if (!refresh && !item.DisplayParentId.Equals(default))
{
var displayParent = GetItemById(item.DisplayParentId);
- refresh = displayParent != null && displayParent.DateLastSaved > item.DateLastRefreshed;
+ refresh = displayParent is not null && displayParent.DateLastSaved > item.DateLastRefreshed;
}
if (refresh)
@@ -2401,12 +2346,11 @@ namespace Emby.Server.Implementations.Library
string sortName,
string uniqueId)
{
- if (string.IsNullOrEmpty(name))
- {
- throw new ArgumentNullException(nameof(name));
- }
+ ArgumentException.ThrowIfNullOrEmpty(name);
- var parentIdString = parentId.Equals(Guid.Empty) ? null : parentId.ToString("N", CultureInfo.InvariantCulture);
+ var parentIdString = parentId.Equals(default)
+ ? null
+ : parentId.ToString("N", CultureInfo.InvariantCulture);
var idValues = "37_namedview_" + name + (parentIdString ?? string.Empty) + (viewType ?? string.Empty);
if (!string.IsNullOrEmpty(uniqueId))
{
@@ -2421,7 +2365,7 @@ namespace Emby.Server.Implementations.Library
var isNew = false;
- if (item == null)
+ if (item is null)
{
Directory.CreateDirectory(path);
@@ -2450,10 +2394,10 @@ namespace Emby.Server.Implementations.Library
var refresh = isNew || DateTime.UtcNow - item.DateLastRefreshed >= _viewRefreshInterval;
- if (!refresh && !item.DisplayParentId.Equals(Guid.Empty))
+ if (!refresh && !item.DisplayParentId.Equals(default))
{
var displayParent = GetItemById(item.DisplayParentId);
- refresh = displayParent != null && displayParent.DateLastSaved > item.DateLastRefreshed;
+ refresh = displayParent is not null && displayParent.DateLastSaved > item.DateLastRefreshed;
}
if (refresh)
@@ -2471,24 +2415,6 @@ namespace Emby.Server.Implementations.Library
return item;
}
- public void AddExternalSubtitleStreams(
- List<MediaStream> streams,
- string videoPath,
- string[] files)
- {
- new SubtitleResolver(BaseItem.LocalizationManager).AddExternalSubtitleStreams(streams, videoPath, streams.Count, files);
- }
-
- public BaseItem GetParentItem(string parentId, Guid? userId)
- {
- if (string.IsNullOrEmpty(parentId))
- {
- return GetParentItem((Guid?)null, userId);
- }
-
- return GetParentItem(new Guid(parentId), userId);
- }
-
public BaseItem GetParentItem(Guid? parentId, Guid? userId)
{
if (parentId.HasValue)
@@ -2496,7 +2422,7 @@ namespace Emby.Server.Implementations.Library
return GetItemById(parentId.Value);
}
- if (userId.HasValue && userId != Guid.Empty)
+ if (userId.HasValue && !userId.Equals(default))
{
return GetUserRootFolder();
}
@@ -2505,16 +2431,12 @@ namespace Emby.Server.Implementations.Library
}
/// <inheritdoc />
- public bool IsVideoFile(string path)
+ public void QueueLibraryScan()
{
- return VideoResolver.IsVideoFile(path, GetNamingOptions());
+ _taskManager.QueueScheduledTask<RefreshMediaLibraryTask>();
}
/// <inheritdoc />
- public bool IsAudioFile(string path)
- => AudioFileParser.IsAudioFile(path, GetNamingOptions());
-
- /// <inheritdoc />
public int? GetSeasonNumberFromPath(string path)
=> SeasonPathParser.Parse(path, true, true).SeasonNumber;
@@ -2522,14 +2444,14 @@ namespace Emby.Server.Implementations.Library
public bool FillMissingEpisodeNumbersFromPath(Episode episode, bool forceRefresh)
{
var series = episode.Series;
- bool? isAbsoluteNaming = series != null && string.Equals(series.DisplayOrder, "absolute", StringComparison.OrdinalIgnoreCase);
+ bool? isAbsoluteNaming = series is not null && string.Equals(series.DisplayOrder, "absolute", StringComparison.OrdinalIgnoreCase);
if (!isAbsoluteNaming.Value)
{
// In other words, no filter applied
isAbsoluteNaming = null;
}
- var resolver = new EpisodeResolver(GetNamingOptions());
+ var resolver = new EpisodeResolver(_namingOptions);
var isFolder = episode.VideoType == VideoType.BluRay || episode.VideoType == VideoType.Dvd;
@@ -2539,10 +2461,11 @@ namespace Emby.Server.Implementations.Library
{
episodeInfo = resolver.Resolve(episode.Path, isFolder, null, null, isAbsoluteNaming);
// Resolve from parent folder if it's not the Season folder
- if (episodeInfo == null && episode.Parent.GetType() == typeof(Folder))
+ var parent = episode.GetParent();
+ if (episodeInfo is null && parent.GetType() == typeof(Folder))
{
- episodeInfo = resolver.Resolve(episode.Parent.Path, true, null, null, isAbsoluteNaming);
- if (episodeInfo != null)
+ episodeInfo = resolver.Resolve(parent.Path, true, null, null, isAbsoluteNaming);
+ if (episodeInfo is not null)
{
// add the container
episodeInfo.Container = Path.GetExtension(episode.Path)?.TrimStart('.');
@@ -2583,7 +2506,7 @@ namespace Emby.Server.Implementations.Library
}
catch (Exception ex)
{
- _logger.LogError(ex, "Error reading the episode informations with ffprobe. Episode: {EpisodeInfo}", episodeInfo.Path);
+ _logger.LogError(ex, "Error reading the episode information with ffprobe. Episode: {EpisodeInfo}", episodeInfo.Path);
}
var changed = false;
@@ -2662,7 +2585,7 @@ namespace Emby.Server.Implementations.Library
{
var season = episode.Season;
- if (season != null)
+ if (season is not null)
{
episode.ParentIndexNumber = season.IndexNumber;
}
@@ -2670,9 +2593,9 @@ namespace Emby.Server.Implementations.Library
{
/*
Anime series don't generally have a season in their file name, however,
- tvdb needs a season to correctly get the metadata.
+ 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
+ // FIXME perhaps this would be better for TVDb parser to ask for season 1 if no season is specified
episode.ParentIndexNumber = 1;
}
@@ -2685,122 +2608,86 @@ namespace Emby.Server.Implementations.Library
return changed;
}
- /// <inheritdoc />
- public NamingOptions GetNamingOptions()
- {
- if (_namingOptions == null)
- {
- _namingOptions = new NamingOptions();
- _videoFileExtensions = _namingOptions.VideoFileExtensions;
- }
-
- return _namingOptions;
- }
-
public ItemLookupInfo ParseName(string name)
{
- var namingOptions = GetNamingOptions();
+ var namingOptions = _namingOptions;
var result = VideoResolver.CleanDateTime(name, namingOptions);
return new ItemLookupInfo
{
- Name = VideoResolver.TryCleanString(result.Name, namingOptions, out var newName) ? newName.ToString() : result.Name,
+ Name = VideoResolver.TryCleanString(result.Name, namingOptions, out var newName) ? newName : result.Name,
Year = result.Year
};
}
- public IEnumerable<Video> FindTrailers(BaseItem owner, List<FileSystemMetadata> fileSystemChildren, IDirectoryService directoryService)
+ public IEnumerable<BaseItem> FindExtras(BaseItem owner, IReadOnlyList<FileSystemMetadata> fileSystemChildren, IDirectoryService directoryService)
{
- var namingOptions = GetNamingOptions();
-
- var files = owner.IsInMixedFolder ? new List<FileSystemMetadata>() : fileSystemChildren.Where(i => i.IsDirectory)
- .Where(i => string.Equals(i.Name, BaseItem.TrailerFolderName, StringComparison.OrdinalIgnoreCase))
- .SelectMany(i => _fileSystem.GetFiles(i.FullName, _videoFileExtensions, false, false))
- .ToList();
-
- var videos = VideoListResolver.Resolve(fileSystemChildren, namingOptions);
-
- var currentVideo = videos.FirstOrDefault(i => string.Equals(owner.Path, i.Files[0].Path, StringComparison.OrdinalIgnoreCase));
-
- if (currentVideo != null)
+ var ownerVideoInfo = VideoResolver.Resolve(owner.Path, owner.IsFolder, _namingOptions);
+ if (ownerVideoInfo is null)
{
- files.AddRange(currentVideo.Extras.Where(i => i.ExtraType == ExtraType.Trailer).Select(i => _fileSystem.GetFileInfo(i.Path)));
+ yield break;
}
- var resolvers = new IItemResolver[]
+ var count = fileSystemChildren.Count;
+ for (var i = 0; i < count; i++)
{
- new GenericVideoResolver<Trailer>(this)
- };
-
- return ResolvePaths(files, directoryService, null, new LibraryOptions(), null, resolvers)
- .OfType<Trailer>()
- .Select(video =>
+ var current = fileSystemChildren[i];
+ if (current.IsDirectory && _namingOptions.AllExtrasTypesFolderNames.ContainsKey(current.Name))
{
- // Try to retrieve it from the db. If we don't find it, use the resolved version
- if (GetItemById(video.Id) is Trailer dbItem)
+ var filesInSubFolder = _fileSystem.GetFiles(current.FullName, null, false, false);
+ foreach (var file in filesInSubFolder)
{
- video = dbItem;
- }
-
- video.ParentId = Guid.Empty;
- video.OwnerId = owner.Id;
- video.ExtraType = ExtraType.Trailer;
- video.TrailerTypes = new[] { TrailerType.LocalTrailer };
-
- return video;
-
- // Sort them so that the list can be easily compared for changes
- }).OrderBy(i => i.Path);
- }
-
- public IEnumerable<Video> FindExtras(BaseItem owner, List<FileSystemMetadata> fileSystemChildren, IDirectoryService directoryService)
- {
- var namingOptions = GetNamingOptions();
-
- var files = owner.IsInMixedFolder ? new List<FileSystemMetadata>() : fileSystemChildren.Where(i => i.IsDirectory)
- .Where(i => BaseItem.AllExtrasTypesFolderNames.Contains(i.Name ?? string.Empty, StringComparer.OrdinalIgnoreCase))
- .SelectMany(i => _fileSystem.GetFiles(i.FullName, _videoFileExtensions, false, false))
- .ToList();
-
- var videos = VideoListResolver.Resolve(fileSystemChildren, namingOptions);
-
- var currentVideo = videos.FirstOrDefault(i => string.Equals(owner.Path, i.Files[0].Path, StringComparison.OrdinalIgnoreCase));
-
- if (currentVideo != null)
- {
- files.AddRange(currentVideo.Extras.Where(i => i.ExtraType != ExtraType.Trailer).Select(i => _fileSystem.GetFileInfo(i.Path)));
- }
+ if (!_extraResolver.TryGetExtraTypeForOwner(file.FullName, ownerVideoInfo, out var extraType))
+ {
+ continue;
+ }
- return ResolvePaths(files, directoryService, null, new LibraryOptions(), null)
- .OfType<Video>()
- .Select(video =>
+ var extra = GetExtra(file, extraType.Value);
+ if (extra is not null)
+ {
+ yield return extra;
+ }
+ }
+ }
+ else if (!current.IsDirectory && _extraResolver.TryGetExtraTypeForOwner(current.FullName, ownerVideoInfo, out var extraType))
{
- // Try to retrieve it from the db. If we don't find it, use the resolved version
- var dbItem = GetItemById(video.Id) as Video;
-
- if (dbItem != null)
+ var extra = GetExtra(current, extraType.Value);
+ if (extra is not null)
{
- video = dbItem;
+ yield return extra;
}
+ }
+ }
- video.ParentId = Guid.Empty;
- video.OwnerId = owner.Id;
-
- SetExtraTypeFromFilename(video);
+ BaseItem GetExtra(FileSystemMetadata file, ExtraType extraType)
+ {
+ var extra = ResolvePath(_fileSystem.GetFileInfo(file.FullName), directoryService, _extraResolver.GetResolversForExtraType(extraType));
+ if (extra is not Video && extra is not Audio)
+ {
+ return null;
+ }
- return video;
+ // Try to retrieve it from the db. If we don't find it, use the resolved version
+ var itemById = GetItemById(extra.Id);
+ if (itemById is not null)
+ {
+ extra = itemById;
+ }
- // Sort them so that the list can be easily compared for changes
- }).OrderBy(i => i.Path);
+ extra.ExtraType = extraType;
+ extra.ParentId = Guid.Empty;
+ extra.OwnerId = owner.Id;
+ return extra;
+ }
}
public string GetPathAfterNetworkSubstitution(string path, BaseItem ownerItem)
{
string newPath;
- if (ownerItem != null)
+ if (ownerItem is not null)
{
var libraryOptions = GetLibraryOptions(ownerItem);
- if (libraryOptions != null)
+ if (libraryOptions is not null)
{
foreach (var pathInfo in libraryOptions.PathInfos)
{
@@ -2831,25 +2718,6 @@ namespace Emby.Server.Implementations.Library
return path;
}
- public string SubstitutePath(string path, string from, string to)
- {
- if (path.TryReplaceSubPath(from, to, out var newPath))
- {
- return newPath;
- }
-
- return path;
- }
-
- private void SetExtraTypeFromFilename(Video item)
- {
- var resolver = new ExtraResolver(GetNamingOptions());
-
- var result = resolver.GetExtraInfo(item.Path);
-
- item.ExtraType = result.ExtraType;
- }
-
public List<PersonInfo> GetPeople(InternalPeopleQuery query)
{
return _itemRepository.GetPeople(query);
@@ -2875,7 +2743,8 @@ namespace Emby.Server.Implementations.Library
public List<Person> GetPeopleItems(InternalPeopleQuery query)
{
- return _itemRepository.GetPeopleNames(query).Select(i =>
+ return _itemRepository.GetPeopleNames(query)
+ .Select(i =>
{
try
{
@@ -2886,7 +2755,10 @@ namespace Emby.Server.Implementations.Library
_logger.LogError(ex, "Error getting person");
return null;
}
- }).Where(i => i != null).ToList();
+ })
+ .Where(i => i is not null)
+ .Where(i => query.User is null || i.IsVisible(query.User))
+ .ToList();
}
public List<string> GetPeopleNames(InternalPeopleQuery query)
@@ -2956,18 +2828,21 @@ namespace Emby.Server.Implementations.Library
var rootFolderPath = _configurationManager.ApplicationPaths.DefaultUserViewsPath;
+ var existingNameCount = 1; // first numbered name will be 2
var virtualFolderPath = Path.Combine(rootFolderPath, name);
+ var originalName = name;
while (Directory.Exists(virtualFolderPath))
{
- name += "1";
+ existingNameCount++;
+ name = originalName + existingNameCount;
virtualFolderPath = Path.Combine(rootFolderPath, name);
}
var mediaPathInfos = options.PathInfos;
- if (mediaPathInfos != null)
+ if (mediaPathInfos is not null)
{
var invalidpath = mediaPathInfos.FirstOrDefault(i => !Directory.Exists(i.Path));
- if (invalidpath != null)
+ if (invalidpath is not null)
{
throw new ArgumentException("The specified path does not exist: " + invalidpath.Path + ".");
}
@@ -2979,7 +2854,7 @@ namespace Emby.Server.Implementations.Library
{
Directory.CreateDirectory(virtualFolderPath);
- if (collectionType != null)
+ if (collectionType is not null)
{
var path = Path.Combine(virtualFolderPath, collectionType.ToString().ToLowerInvariant() + ".collection");
@@ -2988,7 +2863,7 @@ namespace Emby.Server.Implementations.Library
CollectionFolder.SaveLibraryOptions(virtualFolderPath, options);
- if (mediaPathInfos != null)
+ if (mediaPathInfos is not null)
{
foreach (var path in mediaPathInfos)
{
@@ -3015,7 +2890,7 @@ namespace Emby.Server.Implementations.Library
private async Task SavePeopleMetadataAsync(IEnumerable<PersonInfo> people, CancellationToken cancellationToken)
{
- var personsToSave = new List<BaseItem>();
+ List<BaseItem> personsToSave = null;
foreach (var person in people)
{
@@ -3057,12 +2932,15 @@ namespace Emby.Server.Implementations.Library
if (saveEntity)
{
- personsToSave.Add(personEntity);
+ (personsToSave ??= new()).Add(personEntity);
await RunMetadataSavers(personEntity, itemUpdateType).ConfigureAwait(false);
}
}
- CreateItems(personsToSave, null, CancellationToken.None);
+ if (personsToSave is not null)
+ {
+ CreateItems(personsToSave, null, CancellationToken.None);
+ }
}
private void StartScanInBackground()
@@ -3074,17 +2952,14 @@ namespace Emby.Server.Implementations.Library
});
}
- public void AddMediaPath(string virtualFolderName, MediaPathInfo pathInfo)
+ public void AddMediaPath(string virtualFolderName, MediaPathInfo mediaPath)
{
- AddMediaPathInternal(virtualFolderName, pathInfo, true);
+ AddMediaPathInternal(virtualFolderName, mediaPath, true);
}
private void AddMediaPathInternal(string virtualFolderName, MediaPathInfo pathInfo, bool saveLibraryOptions)
{
- if (pathInfo == null)
- {
- throw new ArgumentNullException(nameof(pathInfo));
- }
+ ArgumentNullException.ThrowIfNull(pathInfo);
var path = pathInfo.Path;
@@ -3129,12 +3004,9 @@ namespace Emby.Server.Implementations.Library
}
}
- public void UpdateMediaPath(string virtualFolderName, MediaPathInfo pathInfo)
+ public void UpdateMediaPath(string virtualFolderName, MediaPathInfo mediaPath)
{
- if (pathInfo == null)
- {
- throw new ArgumentNullException(nameof(pathInfo));
- }
+ ArgumentNullException.ThrowIfNull(mediaPath);
var rootFolderPath = _configurationManager.ApplicationPaths.DefaultUserViewsPath;
var virtualFolderPath = Path.Combine(rootFolderPath, virtualFolderName);
@@ -3146,9 +3018,9 @@ namespace Emby.Server.Implementations.Library
var list = libraryOptions.PathInfos.ToList();
foreach (var originalPathInfo in list)
{
- if (string.Equals(pathInfo.Path, originalPathInfo.Path, StringComparison.Ordinal))
+ if (string.Equals(mediaPath.Path, originalPathInfo.Path, StringComparison.Ordinal))
{
- originalPathInfo.NetworkPath = pathInfo.NetworkPath;
+ originalPathInfo.NetworkPath = mediaPath.NetworkPath;
break;
}
}
@@ -3171,10 +3043,7 @@ namespace Emby.Server.Implementations.Library
{
if (!list.Any(i => string.Equals(i.Path, location, StringComparison.Ordinal)))
{
- list.Add(new MediaPathInfo
- {
- Path = location
- });
+ list.Add(new MediaPathInfo(location));
}
}
@@ -3230,22 +3099,19 @@ namespace Emby.Server.Implementations.Library
throw new ArgumentNullException(nameof(path));
}
- var removeList = new List<NameValuePair>();
+ List<NameValuePair> removeList = null;
foreach (var contentType in _configurationManager.Configuration.ContentTypes)
{
- if (string.IsNullOrWhiteSpace(contentType.Name))
- {
- removeList.Add(contentType);
- }
- else if (_fileSystem.AreEqual(path, contentType.Name)
+ if (string.IsNullOrWhiteSpace(contentType.Name)
+ || _fileSystem.AreEqual(path, contentType.Name)
|| _fileSystem.ContainsSubPath(path, contentType.Name))
{
- removeList.Add(contentType);
+ (removeList ??= new()).Add(contentType);
}
}
- if (removeList.Count > 0)
+ if (removeList is not null)
{
_configurationManager.Configuration.ContentTypes = _configurationManager.Configuration.ContentTypes
.Except(removeList)
@@ -3257,10 +3123,7 @@ namespace Emby.Server.Implementations.Library
public void RemoveMediaPath(string virtualFolderName, string mediaPath)
{
- if (string.IsNullOrEmpty(mediaPath))
- {
- throw new ArgumentNullException(nameof(mediaPath));
- }
+ ArgumentException.ThrowIfNullOrEmpty(mediaPath);
var rootFolderPath = _configurationManager.ApplicationPaths.DefaultUserViewsPath;
var virtualFolderPath = Path.Combine(rootFolderPath, virtualFolderName);