Enable nullable for LibraryManager (#11191)

This commit is contained in:
Bond-009 2024-04-17 18:44:50 +02:00 committed by GitHub
parent 356e05e3af
commit bb018c4adc
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
11 changed files with 121 additions and 126 deletions

View File

@ -29,7 +29,7 @@ namespace Emby.Server.Implementations.Library
} }
/// <inheritdoc /> /// <inheritdoc />
public bool ShouldIgnore(FileSystemMetadata fileInfo, BaseItem parent) public bool ShouldIgnore(FileSystemMetadata fileInfo, BaseItem? parent)
{ {
// Don't ignore application folders // Don't ignore application folders
if (fileInfo.FullName.Contains(_serverApplicationPaths.RootFolderPath, StringComparison.InvariantCulture)) if (fileInfo.FullName.Contains(_serverApplicationPaths.RootFolderPath, StringComparison.InvariantCulture))

View File

@ -1,5 +1,3 @@
#nullable disable
#pragma warning disable CS1591 #pragma warning disable CS1591
using System; using System;
@ -90,8 +88,8 @@ namespace Emby.Server.Implementations.Library
/// <summary> /// <summary>
/// The _root folder. /// The _root folder.
/// </summary> /// </summary>
private volatile AggregateFolder _rootFolder; private volatile AggregateFolder? _rootFolder;
private volatile UserRootFolder _userRootFolder; private volatile UserRootFolder? _userRootFolder;
private bool _wizardCompleted; private bool _wizardCompleted;
@ -156,17 +154,17 @@ namespace Emby.Server.Implementations.Library
/// <summary> /// <summary>
/// Occurs when [item added]. /// Occurs when [item added].
/// </summary> /// </summary>
public event EventHandler<ItemChangeEventArgs> ItemAdded; public event EventHandler<ItemChangeEventArgs>? ItemAdded;
/// <summary> /// <summary>
/// Occurs when [item updated]. /// Occurs when [item updated].
/// </summary> /// </summary>
public event EventHandler<ItemChangeEventArgs> ItemUpdated; public event EventHandler<ItemChangeEventArgs>? ItemUpdated;
/// <summary> /// <summary>
/// Occurs when [item removed]. /// Occurs when [item removed].
/// </summary> /// </summary>
public event EventHandler<ItemChangeEventArgs> ItemRemoved; public event EventHandler<ItemChangeEventArgs>? ItemRemoved;
/// <summary> /// <summary>
/// Gets the root folder. /// Gets the root folder.
@ -265,7 +263,7 @@ namespace Emby.Server.Implementations.Library
/// </summary> /// </summary>
/// <param name="sender">The sender.</param> /// <param name="sender">The sender.</param>
/// <param name="e">The <see cref="EventArgs" /> instance containing the event data.</param> /// <param name="e">The <see cref="EventArgs" /> instance containing the event data.</param>
private void ConfigurationUpdated(object sender, EventArgs e) private void ConfigurationUpdated(object? sender, EventArgs e)
{ {
var config = _configurationManager.Configuration; var config = _configurationManager.Configuration;
@ -480,7 +478,7 @@ namespace Emby.Server.Implementations.Library
/// <param name="args">The args.</param> /// <param name="args">The args.</param>
/// <param name="resolvers">The resolvers.</param> /// <param name="resolvers">The resolvers.</param>
/// <returns>BaseItem.</returns> /// <returns>BaseItem.</returns>
private BaseItem ResolveItem(ItemResolveArgs args, IItemResolver[] resolvers) private BaseItem? ResolveItem(ItemResolveArgs args, IItemResolver[]? resolvers)
{ {
var item = (resolvers ?? EntityResolvers).Select(r => Resolve(args, r)) var item = (resolvers ?? EntityResolvers).Select(r => Resolve(args, r))
.FirstOrDefault(i => i is not null); .FirstOrDefault(i => i is not null);
@ -493,7 +491,7 @@ namespace Emby.Server.Implementations.Library
return item; return item;
} }
private BaseItem Resolve(ItemResolveArgs args, IItemResolver resolver) private BaseItem? Resolve(ItemResolveArgs args, IItemResolver resolver)
{ {
try try
{ {
@ -535,16 +533,16 @@ namespace Emby.Server.Implementations.Library
return key.GetMD5(); return key.GetMD5();
} }
public BaseItem ResolvePath(FileSystemMetadata fileInfo, Folder parent = null, IDirectoryService directoryService = null) public BaseItem? ResolvePath(FileSystemMetadata fileInfo, Folder? parent = null, IDirectoryService? directoryService = null)
=> ResolvePath(fileInfo, directoryService ?? new DirectoryService(_fileSystem), null, parent); => ResolvePath(fileInfo, directoryService ?? new DirectoryService(_fileSystem), null, parent);
private BaseItem ResolvePath( private BaseItem? ResolvePath(
FileSystemMetadata fileInfo, FileSystemMetadata fileInfo,
IDirectoryService directoryService, IDirectoryService directoryService,
IItemResolver[] resolvers, IItemResolver[]? resolvers,
Folder parent = null, Folder? parent = null,
CollectionType? collectionType = null, CollectionType? collectionType = null,
LibraryOptions libraryOptions = null) LibraryOptions? libraryOptions = null)
{ {
ArgumentNullException.ThrowIfNull(fileInfo); ArgumentNullException.ThrowIfNull(fileInfo);
@ -617,7 +615,7 @@ namespace Emby.Server.Implementations.Library
return ResolveItem(args, resolvers); return ResolveItem(args, resolvers);
} }
public bool IgnoreFile(FileSystemMetadata file, BaseItem parent) public bool IgnoreFile(FileSystemMetadata file, BaseItem? parent)
=> EntityResolutionIgnoreRules.Any(r => r.ShouldIgnore(file, parent)); => EntityResolutionIgnoreRules.Any(r => r.ShouldIgnore(file, parent));
public List<FileSystemMetadata> NormalizeRootPathList(IEnumerable<FileSystemMetadata> paths) public List<FileSystemMetadata> NormalizeRootPathList(IEnumerable<FileSystemMetadata> paths)
@ -692,16 +690,16 @@ namespace Emby.Server.Implementations.Library
private IEnumerable<BaseItem> ResolveFileList( private IEnumerable<BaseItem> ResolveFileList(
IReadOnlyList<FileSystemMetadata> fileList, IReadOnlyList<FileSystemMetadata> fileList,
IDirectoryService directoryService, IDirectoryService directoryService,
Folder parent, Folder? parent,
CollectionType? collectionType, CollectionType? collectionType,
IItemResolver[] resolvers, IItemResolver[]? resolvers,
LibraryOptions libraryOptions) LibraryOptions libraryOptions)
{ {
// Given that fileList is a list we can save enumerator allocations by indexing // Given that fileList is a list we can save enumerator allocations by indexing
for (var i = 0; i < fileList.Count; i++) for (var i = 0; i < fileList.Count; i++)
{ {
var file = fileList[i]; var file = fileList[i];
BaseItem result = null; BaseItem? result = null;
try try
{ {
result = ResolvePath(file, directoryService, resolvers, parent, collectionType, libraryOptions); result = ResolvePath(file, directoryService, resolvers, parent, collectionType, libraryOptions);
@ -730,7 +728,7 @@ namespace Emby.Server.Implementations.Library
Directory.CreateDirectory(rootFolderPath); Directory.CreateDirectory(rootFolderPath);
var rootFolder = GetItemById(GetNewItemId(rootFolderPath, typeof(AggregateFolder))) as AggregateFolder ?? var rootFolder = GetItemById(GetNewItemId(rootFolderPath, typeof(AggregateFolder))) as AggregateFolder ??
((Folder)ResolvePath(_fileSystem.GetDirectoryInfo(rootFolderPath))) (ResolvePath(_fileSystem.GetDirectoryInfo(rootFolderPath)) as Folder ?? throw new InvalidOperationException("Something went very wong"))
.DeepCopy<Folder, AggregateFolder>(); .DeepCopy<Folder, AggregateFolder>();
// In case program data folder was moved // In case program data folder was moved
@ -796,7 +794,7 @@ namespace Emby.Server.Implementations.Library
Directory.CreateDirectory(userRootPath); Directory.CreateDirectory(userRootPath);
var newItemId = GetNewItemId(userRootPath, typeof(UserRootFolder)); var newItemId = GetNewItemId(userRootPath, typeof(UserRootFolder));
UserRootFolder tmpItem = null; UserRootFolder? tmpItem = null;
try try
{ {
tmpItem = GetItemById(newItemId) as UserRootFolder; tmpItem = GetItemById(newItemId) as UserRootFolder;
@ -809,7 +807,8 @@ namespace Emby.Server.Implementations.Library
if (tmpItem is null) if (tmpItem is null)
{ {
_logger.LogDebug("Creating new userRootFolder with DeepCopy"); _logger.LogDebug("Creating new userRootFolder with DeepCopy");
tmpItem = ((Folder)ResolvePath(_fileSystem.GetDirectoryInfo(userRootPath))).DeepCopy<Folder, UserRootFolder>(); tmpItem = (ResolvePath(_fileSystem.GetDirectoryInfo(userRootPath)) as Folder ?? throw new InvalidOperationException("Failed to get user root path"))
.DeepCopy<Folder, UserRootFolder>();
} }
// In case program data folder was moved // In case program data folder was moved
@ -828,7 +827,8 @@ namespace Emby.Server.Implementations.Library
return _userRootFolder; return _userRootFolder;
} }
public BaseItem FindByPath(string path, bool? isFolder) /// <inheritdoc />
public BaseItem? FindByPath(string path, bool? isFolder)
{ {
// If this returns multiple items it could be tricky figuring out which one is correct. // 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 // In most cases, the newest one will be and the others obsolete but not yet cleaned up
@ -847,12 +847,8 @@ namespace Emby.Server.Implementations.Library
.FirstOrDefault(); .FirstOrDefault();
} }
/// <summary> /// <inheritdoc />
/// Gets the person. public Person? GetPerson(string name)
/// </summary>
/// <param name="name">The name.</param>
/// <returns>Task{Person}.</returns>
public Person GetPerson(string name)
{ {
var path = Person.GetPath(name); var path = Person.GetPath(name);
var id = GetItemByNameId<Person>(path); var id = GetItemByNameId<Person>(path);
@ -1159,7 +1155,7 @@ namespace Emby.Server.Implementations.Library
.ToList(); .ToList();
} }
private VirtualFolderInfo GetVirtualFolderInfo(string dir, List<BaseItem> allCollectionFolders, HashSet<Guid> refreshQueue) private VirtualFolderInfo GetVirtualFolderInfo(string dir, List<BaseItem> allCollectionFolders, HashSet<Guid>? refreshQueue)
{ {
var info = new VirtualFolderInfo var info = new VirtualFolderInfo
{ {
@ -1224,14 +1220,14 @@ namespace Emby.Server.Implementations.Library
} }
/// <inheritdoc /> /// <inheritdoc />
public BaseItem GetItemById(Guid id) public BaseItem? GetItemById(Guid id)
{ {
if (id.IsEmpty()) if (id.IsEmpty())
{ {
throw new ArgumentException("Guid can't be empty", nameof(id)); throw new ArgumentException("Guid can't be empty", nameof(id));
} }
if (_cache.TryGetValue(id, out BaseItem item)) if (_cache.TryGetValue(id, out BaseItem? item))
{ {
return item; return item;
} }
@ -1247,7 +1243,7 @@ namespace Emby.Server.Implementations.Library
} }
/// <inheritdoc /> /// <inheritdoc />
public T GetItemById<T>(Guid id) public T? GetItemById<T>(Guid id)
where T : BaseItem where T : BaseItem
{ {
var item = GetItemById(id); var item = GetItemById(id);
@ -1260,7 +1256,7 @@ namespace Emby.Server.Implementations.Library
} }
/// <inheritdoc /> /// <inheritdoc />
public T GetItemById<T>(Guid id, Guid userId) public T? GetItemById<T>(Guid id, Guid userId)
where T : BaseItem where T : BaseItem
{ {
var user = userId.IsEmpty() ? null : _userManager.GetUserById(userId); var user = userId.IsEmpty() ? null : _userManager.GetUserById(userId);
@ -1268,7 +1264,7 @@ namespace Emby.Server.Implementations.Library
} }
/// <inheritdoc /> /// <inheritdoc />
public T GetItemById<T>(Guid id, User user) public T? GetItemById<T>(Guid id, User? user)
where T : BaseItem where T : BaseItem
{ {
var item = GetItemById<T>(id); var item = GetItemById<T>(id);
@ -1435,7 +1431,7 @@ namespace Emby.Server.Implementations.Library
var parents = new BaseItem[len]; var parents = new BaseItem[len];
for (int i = 0; i < len; i++) for (int i = 0; i < len; i++)
{ {
parents[i] = GetItemById(ancestorIds[i]); parents[i] = GetItemById(ancestorIds[i]) ?? throw new ArgumentException($"Failed to find parent with id: {ancestorIds[i]}");
if (parents[i] is not (ICollectionFolder or UserView)) if (parents[i] is not (ICollectionFolder or UserView))
{ {
return; return;
@ -1449,7 +1445,7 @@ namespace Emby.Server.Implementations.Library
// Prevent searching in all libraries due to empty filter // Prevent searching in all libraries due to empty filter
if (query.TopParentIds.Length == 0) if (query.TopParentIds.Length == 0)
{ {
query.TopParentIds = new[] { Guid.NewGuid() }; query.TopParentIds = [Guid.NewGuid()];
} }
} }
@ -1546,7 +1542,7 @@ namespace Emby.Server.Implementations.Library
} }
} }
private IEnumerable<Guid> GetTopParentIdsForQuery(BaseItem item, User user) private IEnumerable<Guid> GetTopParentIdsForQuery(BaseItem item, User? user)
{ {
if (item is UserView view) if (item is UserView view)
{ {
@ -1624,7 +1620,7 @@ namespace Emby.Server.Implementations.Library
return items return items
.SelectMany(i => i.ToArray()) .SelectMany(i => i.ToArray())
.Select(ResolveIntro) .Select(ResolveIntro)
.Where(i => i is not null); .Where(i => i is not null)!; // null values got filtered out
} }
/// <summary> /// <summary>
@ -1653,9 +1649,9 @@ namespace Emby.Server.Implementations.Library
/// </summary> /// </summary>
/// <param name="info">The info.</param> /// <param name="info">The info.</param>
/// <returns>Video.</returns> /// <returns>Video.</returns>
private Video ResolveIntro(IntroInfo info) private Video? ResolveIntro(IntroInfo info)
{ {
Video video = null; Video? video = null;
if (info.ItemId.HasValue) if (info.ItemId.HasValue)
{ {
@ -1706,29 +1702,26 @@ namespace Emby.Server.Implementations.Library
return video; return video;
} }
/// <summary> /// <inheritdoc />
/// Sorts the specified sort by. public IEnumerable<BaseItem> Sort(IEnumerable<BaseItem> items, User? user, IEnumerable<ItemSortBy> sortBy, SortOrder sortOrder)
/// </summary>
/// <param name="items">The items.</param>
/// <param name="user">The user.</param>
/// <param name="sortBy">The sort by.</param>
/// <param name="sortOrder">The sort order.</param>
/// <returns>IEnumerable{BaseItem}.</returns>
public IEnumerable<BaseItem> Sort(IEnumerable<BaseItem> items, User user, IEnumerable<ItemSortBy> sortBy, SortOrder sortOrder)
{ {
var isFirst = true; var isFirst = true;
IOrderedEnumerable<BaseItem> orderedItems = null; IOrderedEnumerable<BaseItem>? orderedItems = null;
foreach (var orderBy in sortBy.Select(o => GetComparer(o, user)).Where(c => c is not null)) foreach (var orderBy in sortBy.Select(o => GetComparer(o, user)).Where(c => c is not null))
{ {
if (isFirst) if (isFirst)
{ {
orderedItems = sortOrder == SortOrder.Descending ? items.OrderByDescending(i => i, orderBy) : items.OrderBy(i => i, orderBy); orderedItems = sortOrder == SortOrder.Descending
? items.OrderByDescending(i => i, orderBy)
: items.OrderBy(i => i, orderBy);
} }
else else
{ {
orderedItems = sortOrder == SortOrder.Descending ? orderedItems.ThenByDescending(i => i, orderBy) : orderedItems.ThenBy(i => i, orderBy); orderedItems = sortOrder == SortOrder.Descending
? orderedItems!.ThenByDescending(i => i, orderBy)
: orderedItems!.ThenBy(i => i, orderBy); // orderedItems is set during the first iteration
} }
isFirst = false; isFirst = false;
@ -1737,11 +1730,12 @@ namespace Emby.Server.Implementations.Library
return orderedItems ?? items; return orderedItems ?? items;
} }
public IEnumerable<BaseItem> Sort(IEnumerable<BaseItem> items, User user, IEnumerable<(ItemSortBy OrderBy, SortOrder SortOrder)> orderBy) /// <inheritdoc />
public IEnumerable<BaseItem> Sort(IEnumerable<BaseItem> items, User? user, IEnumerable<(ItemSortBy OrderBy, SortOrder SortOrder)> orderBy)
{ {
var isFirst = true; var isFirst = true;
IOrderedEnumerable<BaseItem> orderedItems = null; IOrderedEnumerable<BaseItem>? orderedItems = null;
foreach (var (name, sortOrder) in orderBy) foreach (var (name, sortOrder) in orderBy)
{ {
@ -1753,11 +1747,15 @@ namespace Emby.Server.Implementations.Library
if (isFirst) if (isFirst)
{ {
orderedItems = sortOrder == SortOrder.Descending ? items.OrderByDescending(i => i, comparer) : items.OrderBy(i => i, comparer); orderedItems = sortOrder == SortOrder.Descending
? items.OrderByDescending(i => i, comparer)
: items.OrderBy(i => i, comparer);
} }
else else
{ {
orderedItems = sortOrder == SortOrder.Descending ? orderedItems.ThenByDescending(i => i, comparer) : orderedItems.ThenBy(i => i, comparer); orderedItems = sortOrder == SortOrder.Descending
? orderedItems!.ThenByDescending(i => i, comparer)
: orderedItems!.ThenBy(i => i, comparer); // orderedItems is set during the first iteration
} }
isFirst = false; isFirst = false;
@ -1772,14 +1770,14 @@ namespace Emby.Server.Implementations.Library
/// <param name="name">The name.</param> /// <param name="name">The name.</param>
/// <param name="user">The user.</param> /// <param name="user">The user.</param>
/// <returns>IBaseItemComparer.</returns> /// <returns>IBaseItemComparer.</returns>
private IBaseItemComparer GetComparer(ItemSortBy name, User user) private IBaseItemComparer? GetComparer(ItemSortBy name, User? user)
{ {
var comparer = Comparers.FirstOrDefault(c => name == c.Type); var comparer = Comparers.FirstOrDefault(c => name == c.Type);
// If it requires a user, create a new one, and assign the user // If it requires a user, create a new one, and assign the user
if (comparer is IUserBaseItemComparer) if (comparer is IUserBaseItemComparer)
{ {
var userComparer = (IUserBaseItemComparer)Activator.CreateInstance(comparer.GetType()); var userComparer = (IUserBaseItemComparer)Activator.CreateInstance(comparer.GetType())!; // only null for Nullable<T> instances
userComparer.User = user; userComparer.User = user;
userComparer.UserManager = _userManager; userComparer.UserManager = _userManager;
@ -1791,23 +1789,14 @@ namespace Emby.Server.Implementations.Library
return comparer; return comparer;
} }
/// <summary> /// <inheritdoc />
/// Creates the item. public void CreateItem(BaseItem item, BaseItem? parent)
/// </summary>
/// <param name="item">The item.</param>
/// <param name="parent">The parent item.</param>
public void CreateItem(BaseItem item, BaseItem parent)
{ {
CreateItems(new[] { item }, parent, CancellationToken.None); CreateItems(new[] { item }, parent, CancellationToken.None);
} }
/// <summary> /// <inheritdoc />
/// Creates the items. public void CreateItems(IReadOnlyList<BaseItem> items, BaseItem? parent, CancellationToken cancellationToken)
/// </summary>
/// <param name="items">The items.</param>
/// <param name="parent">The parent item.</param>
/// <param name="cancellationToken">The cancellation token.</param>
public void CreateItems(IReadOnlyList<BaseItem> items, BaseItem parent, CancellationToken cancellationToken)
{ {
_itemRepository.SaveItems(items, cancellationToken); _itemRepository.SaveItems(items, cancellationToken);
@ -2089,16 +2078,16 @@ namespace Emby.Server.Implementations.Library
public LibraryOptions GetLibraryOptions(BaseItem item) public LibraryOptions GetLibraryOptions(BaseItem item)
{ {
if (item is not CollectionFolder collectionFolder) if (item is CollectionFolder collectionFolder)
{ {
// List.Find is more performant than FirstOrDefault due to enumerator allocation return collectionFolder.GetLibraryOptions();
collectionFolder = GetCollectionFolders(item)
.Find(folder => folder is CollectionFolder) as CollectionFolder;
} }
return collectionFolder is null // List.Find is more performant than FirstOrDefault due to enumerator allocation
? new LibraryOptions() return GetCollectionFolders(item)
: collectionFolder.GetLibraryOptions(); .Find(folder => folder is CollectionFolder) is CollectionFolder collectionFolder2
? collectionFolder2.GetLibraryOptions()
: new LibraryOptions();
} }
public CollectionType? GetContentType(BaseItem item) public CollectionType? GetContentType(BaseItem item)
@ -2452,7 +2441,7 @@ namespace Emby.Server.Implementations.Library
{ {
if (parentId.HasValue) if (parentId.HasValue)
{ {
return GetItemById(parentId.Value); return GetItemById(parentId.Value) ?? throw new ArgumentException($"Invalid parent id: {parentId.Value}");
} }
if (!userId.IsNullOrEmpty()) if (!userId.IsNullOrEmpty())
@ -2489,7 +2478,7 @@ 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;
// TODO nullable - what are we trying to do there with empty episodeInfo? // TODO nullable - what are we trying to do there with empty episodeInfo?
EpisodeInfo episodeInfo = null; EpisodeInfo? episodeInfo = null;
if (episode.IsFileProtocol) if (episode.IsFileProtocol)
{ {
episodeInfo = resolver.Resolve(episode.Path, isFolder, null, null, isAbsoluteNaming); episodeInfo = resolver.Resolve(episode.Path, isFolder, null, null, isAbsoluteNaming);
@ -2692,7 +2681,7 @@ namespace Emby.Server.Implementations.Library
} }
} }
BaseItem GetExtra(FileSystemMetadata file, ExtraType extraType) BaseItem? GetExtra(FileSystemMetadata file, ExtraType extraType)
{ {
var extra = ResolvePath(_fileSystem.GetFileInfo(file.FullName), directoryService, _extraResolver.GetResolversForExtraType(extraType)); var extra = ResolvePath(_fileSystem.GetFileInfo(file.FullName), directoryService, _extraResolver.GetResolversForExtraType(extraType));
if (extra is not Video && extra is not Audio) if (extra is not Video && extra is not Audio)
@ -2719,9 +2708,9 @@ namespace Emby.Server.Implementations.Library
} }
} }
public string GetPathAfterNetworkSubstitution(string path, BaseItem ownerItem) public string GetPathAfterNetworkSubstitution(string path, BaseItem? ownerItem)
{ {
string newPath; string? newPath;
if (ownerItem is not null) if (ownerItem is not null)
{ {
var libraryOptions = GetLibraryOptions(ownerItem); var libraryOptions = GetLibraryOptions(ownerItem);
@ -2795,8 +2784,8 @@ namespace Emby.Server.Implementations.Library
} }
}) })
.Where(i => i is not null) .Where(i => i is not null)
.Where(i => query.User is null || i.IsVisible(query.User)) .Where(i => query.User is null || i!.IsVisible(query.User))
.ToList(); .ToList()!; // null values are filtered out
} }
public List<string> GetPeopleNames(InternalPeopleQuery query) public List<string> GetPeopleNames(InternalPeopleQuery query)
@ -2898,7 +2887,7 @@ namespace Emby.Server.Implementations.Library
if (collectionType is not null) if (collectionType is not null)
{ {
var path = Path.Combine(virtualFolderPath, collectionType.ToString().ToLowerInvariant() + ".collection"); var path = Path.Combine(virtualFolderPath, collectionType.ToString()!.ToLowerInvariant() + ".collection"); // Can't be null with legal values?
await File.WriteAllBytesAsync(path, Array.Empty<byte>()).ConfigureAwait(false); await File.WriteAllBytesAsync(path, Array.Empty<byte>()).ConfigureAwait(false);
} }
@ -2932,7 +2921,7 @@ namespace Emby.Server.Implementations.Library
private async Task SavePeopleMetadataAsync(IEnumerable<PersonInfo> people, CancellationToken cancellationToken) private async Task SavePeopleMetadataAsync(IEnumerable<PersonInfo> people, CancellationToken cancellationToken)
{ {
List<BaseItem> personsToSave = null; List<BaseItem>? personsToSave = null;
foreach (var person in people) foreach (var person in people)
{ {
@ -3150,7 +3139,7 @@ namespace Emby.Server.Implementations.Library
throw new ArgumentNullException(nameof(path)); throw new ArgumentNullException(nameof(path));
} }
List<NameValuePair> removeList = null; List<NameValuePair>? removeList = null;
foreach (var contentType in _configurationManager.Configuration.ContentTypes) foreach (var contentType in _configurationManager.Configuration.ContentTypes)
{ {
@ -3204,7 +3193,7 @@ namespace Emby.Server.Implementations.Library
CollectionFolder.SaveLibraryOptions(virtualFolderPath, libraryOptions); CollectionFolder.SaveLibraryOptions(virtualFolderPath, libraryOptions);
} }
private static bool ItemIsVisible(BaseItem item, User user) private static bool ItemIsVisible(BaseItem? item, User? user)
{ {
if (item is null) if (item is null)
{ {

View File

@ -64,6 +64,11 @@ namespace Emby.Server.Implementations.Library.Validators
try try
{ {
var item = _libraryManager.GetPerson(person); var item = _libraryManager.GetPerson(person);
if (item is null)
{
_logger.LogWarning("Failed to get person: {Name}", person);
continue;
}
var options = new MetadataRefreshOptions(new DirectoryService(_fileSystem)) var options = new MetadataRefreshOptions(new DirectoryService(_fileSystem))
{ {
@ -92,7 +97,7 @@ namespace Emby.Server.Implementations.Library.Validators
var deadEntities = _libraryManager.GetItemList(new InternalItemsQuery var deadEntities = _libraryManager.GetItemList(new InternalItemsQuery
{ {
IncludeItemTypes = new[] { BaseItemKind.Person }, IncludeItemTypes = [BaseItemKind.Person],
IsDeadPerson = true, IsDeadPerson = true,
IsLocked = false IsLocked = false
}); });

View File

@ -75,7 +75,7 @@ public class LibraryStructureController : BaseJellyfinApiController
[HttpPost] [HttpPost]
[ProducesResponseType(StatusCodes.Status204NoContent)] [ProducesResponseType(StatusCodes.Status204NoContent)]
public async Task<ActionResult> AddVirtualFolder( public async Task<ActionResult> AddVirtualFolder(
[FromQuery] string? name, [FromQuery] string name,
[FromQuery] CollectionTypeOptions? collectionType, [FromQuery] CollectionTypeOptions? collectionType,
[FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] string[] paths, [FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] string[] paths,
[FromBody] AddVirtualFolderDto? libraryOptionsDto, [FromBody] AddVirtualFolderDto? libraryOptionsDto,
@ -103,7 +103,7 @@ public class LibraryStructureController : BaseJellyfinApiController
[HttpDelete] [HttpDelete]
[ProducesResponseType(StatusCodes.Status204NoContent)] [ProducesResponseType(StatusCodes.Status204NoContent)]
public async Task<ActionResult> RemoveVirtualFolder( public async Task<ActionResult> RemoveVirtualFolder(
[FromQuery] string? name, [FromQuery] string name,
[FromQuery] bool refreshLibrary = false) [FromQuery] bool refreshLibrary = false)
{ {
await _libraryManager.RemoveVirtualFolder(name, refreshLibrary).ConfigureAwait(false); await _libraryManager.RemoveVirtualFolder(name, refreshLibrary).ConfigureAwait(false);
@ -267,18 +267,16 @@ public class LibraryStructureController : BaseJellyfinApiController
/// <param name="refreshLibrary">Whether to refresh the library.</param> /// <param name="refreshLibrary">Whether to refresh the library.</param>
/// <returns>A <see cref="NoContentResult"/>.</returns> /// <returns>A <see cref="NoContentResult"/>.</returns>
/// <response code="204">Media path removed.</response> /// <response code="204">Media path removed.</response>
/// <exception cref="ArgumentNullException">The name of the library may not be empty.</exception> /// <exception cref="ArgumentException">The name of the library and path may not be empty.</exception>
[HttpDelete("Paths")] [HttpDelete("Paths")]
[ProducesResponseType(StatusCodes.Status204NoContent)] [ProducesResponseType(StatusCodes.Status204NoContent)]
public ActionResult RemoveMediaPath( public ActionResult RemoveMediaPath(
[FromQuery] string? name, [FromQuery] string name,
[FromQuery] string? path, [FromQuery] string path,
[FromQuery] bool refreshLibrary = false) [FromQuery] bool refreshLibrary = false)
{ {
if (string.IsNullOrWhiteSpace(name)) ArgumentException.ThrowIfNullOrWhiteSpace(name);
{ ArgumentException.ThrowIfNullOrWhiteSpace(path);
throw new ArgumentNullException(nameof(name));
}
_libraryMonitor.Stop(); _libraryMonitor.Stop();

View File

@ -24,6 +24,7 @@ using MediaBrowser.Model.Entities;
using MediaBrowser.Model.MediaInfo; using MediaBrowser.Model.MediaInfo;
using MediaBrowser.Model.Session; using MediaBrowser.Model.Session;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.HttpResults;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
namespace Jellyfin.Api.Helpers; namespace Jellyfin.Api.Helpers;
@ -398,7 +399,8 @@ public class MediaInfoHelper
if (profile is not null) if (profile is not null)
{ {
var item = _libraryManager.GetItemById<BaseItem>(request.ItemId); var item = _libraryManager.GetItemById<BaseItem>(request.ItemId)
?? throw new ResourceNotFoundException();
SetDeviceSpecificData( SetDeviceSpecificData(
item, item,

View File

@ -19,6 +19,7 @@ using MediaBrowser.Model.Dlna;
using MediaBrowser.Model.Dto; using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Entities; using MediaBrowser.Model.Entities;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.HttpResults;
using Microsoft.Net.Http.Headers; using Microsoft.Net.Http.Headers;
namespace Jellyfin.Api.Helpers; namespace Jellyfin.Api.Helpers;
@ -108,7 +109,8 @@ public static class StreamingHelpers
?? state.SupportedSubtitleCodecs.FirstOrDefault(); ?? state.SupportedSubtitleCodecs.FirstOrDefault();
} }
var item = libraryManager.GetItemById<BaseItem>(streamingRequest.Id); var item = libraryManager.GetItemById<BaseItem>(streamingRequest.Id)
?? throw new ResourceNotFoundException();
state.IsInputVideo = item.MediaType == MediaType.Video; state.IsInputVideo = item.MediaType == MediaType.Video;

View File

@ -12,7 +12,7 @@ public class MediaPathDto
/// Gets or sets the name of the library. /// Gets or sets the name of the library.
/// </summary> /// </summary>
[Required] [Required]
public string? Name { get; set; } public required string Name { get; set; }
/// <summary> /// <summary>
/// Gets or sets the path to add. /// Gets or sets the path to add.

View File

@ -42,7 +42,7 @@ namespace Jellyfin.Server.Migrations.Routines
} }
var libraryOptions = virtualFolder.LibraryOptions; var libraryOptions = virtualFolder.LibraryOptions;
var collectionFolder = (CollectionFolder)_libraryManager.GetItemById(folderId); var collectionFolder = _libraryManager.GetItemById<CollectionFolder>(folderId) ?? throw new InvalidOperationException("Failed to find CollectionFolder");
// The property no longer exists in LibraryOptions, so we just re-save the options to get old data removed. // The property no longer exists in LibraryOptions, so we just re-save the options to get old data removed.
collectionFolder.UpdateLibraryOptions(libraryOptions); collectionFolder.UpdateLibraryOptions(libraryOptions);
_logger.LogInformation("Removed from '{VirtualFolder}'", virtualFolder.Name); _logger.LogInformation("Removed from '{VirtualFolder}'", virtualFolder.Name);

View File

@ -1,5 +1,3 @@
#nullable disable
#pragma warning disable CA1002, CS1591 #pragma warning disable CA1002, CS1591
using System; using System;
@ -33,17 +31,17 @@ namespace MediaBrowser.Controller.Library
/// <summary> /// <summary>
/// Occurs when [item added]. /// Occurs when [item added].
/// </summary> /// </summary>
event EventHandler<ItemChangeEventArgs> ItemAdded; event EventHandler<ItemChangeEventArgs>? ItemAdded;
/// <summary> /// <summary>
/// Occurs when [item updated]. /// Occurs when [item updated].
/// </summary> /// </summary>
event EventHandler<ItemChangeEventArgs> ItemUpdated; event EventHandler<ItemChangeEventArgs>? ItemUpdated;
/// <summary> /// <summary>
/// Occurs when [item removed]. /// Occurs when [item removed].
/// </summary> /// </summary>
event EventHandler<ItemChangeEventArgs> ItemRemoved; event EventHandler<ItemChangeEventArgs>? ItemRemoved;
/// <summary> /// <summary>
/// Gets the root folder. /// Gets the root folder.
@ -60,10 +58,10 @@ namespace MediaBrowser.Controller.Library
/// <param name="parent">The parent.</param> /// <param name="parent">The parent.</param>
/// <param name="directoryService">An instance of <see cref="IDirectoryService"/>.</param> /// <param name="directoryService">An instance of <see cref="IDirectoryService"/>.</param>
/// <returns>BaseItem.</returns> /// <returns>BaseItem.</returns>
BaseItem ResolvePath( BaseItem? ResolvePath(
FileSystemMetadata fileInfo, FileSystemMetadata fileInfo,
Folder parent = null, Folder? parent = null,
IDirectoryService directoryService = null); IDirectoryService? directoryService = null);
/// <summary> /// <summary>
/// Resolves a set of files into a list of BaseItem. /// Resolves a set of files into a list of BaseItem.
@ -86,7 +84,7 @@ namespace MediaBrowser.Controller.Library
/// </summary> /// </summary>
/// <param name="name">The name of the person.</param> /// <param name="name">The name of the person.</param>
/// <returns>Task{Person}.</returns> /// <returns>Task{Person}.</returns>
Person GetPerson(string name); Person? GetPerson(string name);
/// <summary> /// <summary>
/// Finds the by path. /// Finds the by path.
@ -94,7 +92,7 @@ namespace MediaBrowser.Controller.Library
/// <param name="path">The path.</param> /// <param name="path">The path.</param>
/// <param name="isFolder"><c>true</c> is the path is a directory; otherwise <c>false</c>.</param> /// <param name="isFolder"><c>true</c> is the path is a directory; otherwise <c>false</c>.</param>
/// <returns>BaseItem.</returns> /// <returns>BaseItem.</returns>
BaseItem FindByPath(string path, bool? isFolder); BaseItem? FindByPath(string path, bool? isFolder);
/// <summary> /// <summary>
/// Gets the artist. /// Gets the artist.
@ -166,7 +164,8 @@ namespace MediaBrowser.Controller.Library
/// </summary> /// </summary>
/// <param name="id">The id.</param> /// <param name="id">The id.</param>
/// <returns>BaseItem.</returns> /// <returns>BaseItem.</returns>
BaseItem GetItemById(Guid id); /// <exception cref="ArgumentNullException"><paramref name="id"/> is <c>null</c>.</exception>
BaseItem? GetItemById(Guid id);
/// <summary> /// <summary>
/// Gets the item by id, as T. /// Gets the item by id, as T.
@ -174,7 +173,7 @@ namespace MediaBrowser.Controller.Library
/// <param name="id">The item id.</param> /// <param name="id">The item id.</param>
/// <typeparam name="T">The type of item.</typeparam> /// <typeparam name="T">The type of item.</typeparam>
/// <returns>The item.</returns> /// <returns>The item.</returns>
T GetItemById<T>(Guid id) T? GetItemById<T>(Guid id)
where T : BaseItem; where T : BaseItem;
/// <summary> /// <summary>
@ -184,7 +183,7 @@ namespace MediaBrowser.Controller.Library
/// <param name="userId">The user id to validate against.</param> /// <param name="userId">The user id to validate against.</param>
/// <typeparam name="T">The type of item.</typeparam> /// <typeparam name="T">The type of item.</typeparam>
/// <returns>The item if found.</returns> /// <returns>The item if found.</returns>
public T GetItemById<T>(Guid id, Guid userId) public T? GetItemById<T>(Guid id, Guid userId)
where T : BaseItem; where T : BaseItem;
/// <summary> /// <summary>
@ -194,7 +193,7 @@ namespace MediaBrowser.Controller.Library
/// <param name="user">The user to validate against.</param> /// <param name="user">The user to validate against.</param>
/// <typeparam name="T">The type of item.</typeparam> /// <typeparam name="T">The type of item.</typeparam>
/// <returns>The item if found.</returns> /// <returns>The item if found.</returns>
public T GetItemById<T>(Guid id, User user) public T? GetItemById<T>(Guid id, User? user)
where T : BaseItem; where T : BaseItem;
/// <summary> /// <summary>
@ -228,9 +227,9 @@ namespace MediaBrowser.Controller.Library
/// <param name="sortBy">The sort by.</param> /// <param name="sortBy">The sort by.</param>
/// <param name="sortOrder">The sort order.</param> /// <param name="sortOrder">The sort order.</param>
/// <returns>IEnumerable{BaseItem}.</returns> /// <returns>IEnumerable{BaseItem}.</returns>
IEnumerable<BaseItem> Sort(IEnumerable<BaseItem> items, User user, IEnumerable<ItemSortBy> sortBy, SortOrder sortOrder); IEnumerable<BaseItem> Sort(IEnumerable<BaseItem> items, User? user, IEnumerable<ItemSortBy> sortBy, SortOrder sortOrder);
IEnumerable<BaseItem> Sort(IEnumerable<BaseItem> items, User user, IEnumerable<(ItemSortBy OrderBy, SortOrder SortOrder)> orderBy); IEnumerable<BaseItem> Sort(IEnumerable<BaseItem> items, User? user, IEnumerable<(ItemSortBy OrderBy, SortOrder SortOrder)> orderBy);
/// <summary> /// <summary>
/// Gets the user root folder. /// Gets the user root folder.
@ -243,7 +242,7 @@ namespace MediaBrowser.Controller.Library
/// </summary> /// </summary>
/// <param name="item">Item to create.</param> /// <param name="item">Item to create.</param>
/// <param name="parent">Parent of new item.</param> /// <param name="parent">Parent of new item.</param>
void CreateItem(BaseItem item, BaseItem parent); void CreateItem(BaseItem item, BaseItem? parent);
/// <summary> /// <summary>
/// Creates the items. /// Creates the items.
@ -251,7 +250,7 @@ namespace MediaBrowser.Controller.Library
/// <param name="items">Items to create.</param> /// <param name="items">Items to create.</param>
/// <param name="parent">Parent of new items.</param> /// <param name="parent">Parent of new items.</param>
/// <param name="cancellationToken">CancellationToken to use for operation.</param> /// <param name="cancellationToken">CancellationToken to use for operation.</param>
void CreateItems(IReadOnlyList<BaseItem> items, BaseItem parent, CancellationToken cancellationToken); void CreateItems(IReadOnlyList<BaseItem> items, BaseItem? parent, CancellationToken cancellationToken);
/// <summary> /// <summary>
/// Updates the item. /// Updates the item.
@ -529,7 +528,7 @@ namespace MediaBrowser.Controller.Library
/// <returns>QueryResult&lt;BaseItem&gt;.</returns> /// <returns>QueryResult&lt;BaseItem&gt;.</returns>
QueryResult<BaseItem> QueryItems(InternalItemsQuery query); QueryResult<BaseItem> QueryItems(InternalItemsQuery query);
string GetPathAfterNetworkSubstitution(string path, BaseItem ownerItem = null); string GetPathAfterNetworkSubstitution(string path, BaseItem? ownerItem = null);
/// <summary> /// <summary>
/// Converts the image to local. /// Converts the image to local.

View File

@ -14,6 +14,6 @@ namespace MediaBrowser.Controller.Resolvers
/// <param name="fileInfo">The file information.</param> /// <param name="fileInfo">The file information.</param>
/// <param name="parent">The parent BaseItem.</param> /// <param name="parent">The parent BaseItem.</param>
/// <returns>True if the file should be ignored.</returns> /// <returns>True if the file should be ignored.</returns>
bool ShouldIgnore(FileSystemMetadata fileInfo, BaseItem parent); bool ShouldIgnore(FileSystemMetadata fileInfo, BaseItem? parent);
} }
} }

View File

@ -947,7 +947,7 @@ namespace MediaBrowser.XbmcMetadata.Savers
if (saveImagePath) if (saveImagePath)
{ {
var personEntity = libraryManager.GetPerson(person.Name); var personEntity = libraryManager.GetPerson(person.Name);
var image = personEntity.GetImageInfo(ImageType.Primary, 0); var image = personEntity?.GetImageInfo(ImageType.Primary, 0);
if (image is not null) if (image is not null)
{ {