aboutsummaryrefslogtreecommitdiff
path: root/Emby.Server.Implementations/Library
diff options
context:
space:
mode:
Diffstat (limited to 'Emby.Server.Implementations/Library')
-rw-r--r--Emby.Server.Implementations/Library/CoreResolutionIgnoreRule.cs4
-rw-r--r--Emby.Server.Implementations/Library/LibraryManager.cs294
-rw-r--r--Emby.Server.Implementations/Library/LiveStreamHelper.cs8
-rw-r--r--Emby.Server.Implementations/Library/MediaSourceManager.cs54
-rw-r--r--Emby.Server.Implementations/Library/MediaStreamSelector.cs56
-rw-r--r--Emby.Server.Implementations/Library/PathExtensions.cs98
-rw-r--r--Emby.Server.Implementations/Library/ResolverHelper.cs15
-rw-r--r--Emby.Server.Implementations/Library/Resolvers/Audio/AudioResolver.cs16
-rw-r--r--Emby.Server.Implementations/Library/Resolvers/Audio/MusicAlbumResolver.cs7
-rw-r--r--Emby.Server.Implementations/Library/Resolvers/Audio/MusicArtistResolver.cs15
-rw-r--r--Emby.Server.Implementations/Library/Resolvers/BaseVideoResolver.cs48
-rw-r--r--Emby.Server.Implementations/Library/Resolvers/Books/BookResolver.cs2
-rw-r--r--Emby.Server.Implementations/Library/Resolvers/ExtraResolver.cs19
-rw-r--r--Emby.Server.Implementations/Library/Resolvers/GenericFolderResolver.cs2
-rw-r--r--Emby.Server.Implementations/Library/Resolvers/GenericVideoResolver.cs6
-rw-r--r--Emby.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs48
-rw-r--r--Emby.Server.Implementations/Library/Resolvers/PhotoResolver.cs18
-rw-r--r--Emby.Server.Implementations/Library/Resolvers/PlaylistResolver.cs27
-rw-r--r--Emby.Server.Implementations/Library/Resolvers/TV/EpisodeResolver.cs18
-rw-r--r--Emby.Server.Implementations/Library/Resolvers/TV/SeasonResolver.cs28
-rw-r--r--Emby.Server.Implementations/Library/Resolvers/TV/SeriesResolver.cs12
-rw-r--r--Emby.Server.Implementations/Library/SearchEngine.cs5
-rw-r--r--Emby.Server.Implementations/Library/UserDataManager.cs2
-rw-r--r--Emby.Server.Implementations/Library/UserViewManager.cs36
-rw-r--r--Emby.Server.Implementations/Library/Validators/CollectionPostScanTask.cs2
25 files changed, 459 insertions, 381 deletions
diff --git a/Emby.Server.Implementations/Library/CoreResolutionIgnoreRule.cs b/Emby.Server.Implementations/Library/CoreResolutionIgnoreRule.cs
index e558fbe27..665d70a41 100644
--- a/Emby.Server.Implementations/Library/CoreResolutionIgnoreRule.cs
+++ b/Emby.Server.Implementations/Library/CoreResolutionIgnoreRule.cs
@@ -52,7 +52,7 @@ namespace Emby.Server.Implementations.Library
if (fileInfo.IsDirectory)
{
- if (parent != null)
+ if (parent is not null)
{
// Ignore extras folders but allow it at the collection level
if (_namingOptions.AllExtrasTypesFolderNames.ContainsKey(filename)
@@ -65,7 +65,7 @@ namespace Emby.Server.Implementations.Library
}
else
{
- if (parent != null)
+ if (parent is not null)
{
// Don't resolve these into audio files
if (Path.GetFileNameWithoutExtension(filename.AsSpan()).Equals(BaseItem.ThemeSongFileName, StringComparison.Ordinal)
diff --git a/Emby.Server.Implementations/Library/LibraryManager.cs b/Emby.Server.Implementations/Library/LibraryManager.cs
index cef82ebbc..ea45bf0ba 100644
--- a/Emby.Server.Implementations/Library/LibraryManager.cs
+++ b/Emby.Server.Implementations/Library/LibraryManager.cs
@@ -113,6 +113,7 @@ namespace Emby.Server.Implementations.Library
/// <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,
ILoggerFactory loggerFactory,
@@ -128,7 +129,8 @@ namespace Emby.Server.Implementations.Library
IItemRepository itemRepository,
IImageProcessor imageProcessor,
IMemoryCache memoryCache,
- NamingOptions namingOptions)
+ NamingOptions namingOptions,
+ IDirectoryService directoryService)
{
_appHost = appHost;
_logger = loggerFactory.CreateLogger<LibraryManager>();
@@ -146,7 +148,7 @@ namespace Emby.Server.Implementations.Library
_memoryCache = memoryCache;
_namingOptions = namingOptions;
- _extraResolver = new ExtraResolver(loggerFactory.CreateLogger<ExtraResolver>(), namingOptions);
+ _extraResolver = new ExtraResolver(loggerFactory.CreateLogger<ExtraResolver>(), namingOptions, directoryService);
_configurationManager.ConfigurationUpdated += ConfigurationUpdated;
@@ -176,7 +178,7 @@ namespace Emby.Server.Implementations.Library
{
get
{
- if (_rootFolder == null)
+ if (_rootFolder is null)
{
lock (_rootFolderSyncLock)
{
@@ -356,8 +358,8 @@ namespace Emby.Server.Implementations.Library
}
var children = item.IsFolder
- ? ((Folder)item).GetRecursiveChildren(false).ToList()
- : new List<BaseItem>();
+ ? ((Folder)item).GetRecursiveChildren(false)
+ : Enumerable.Empty<BaseItem>();
foreach (var metadataPath in GetMetadataPaths(item, children))
{
@@ -465,9 +467,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);
}
@@ -495,11 +497,7 @@ namespace Emby.Server.Implementations.Library
private Guid GetNewItemIdInternal(string key, Type type, bool forceCaseInsensitive)
{
- if (string.IsNullOrEmpty(key))
- {
- throw new ArgumentNullException(nameof(key));
- }
-
+ ArgumentException.ThrowIfNullOrEmpty(key);
ArgumentNullException.ThrowIfNull(type);
string programDataPath = _configurationManager.ApplicationPaths.ProgramDataPath;
@@ -536,12 +534,12 @@ namespace Emby.Server.Implementations.Library
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,
@@ -572,7 +570,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);
@@ -654,9 +652,9 @@ 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)
{
@@ -697,7 +695,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;
}
@@ -750,7 +748,7 @@ 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;
}
@@ -770,11 +768,11 @@ 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;
@@ -792,7 +790,7 @@ namespace Emby.Server.Implementations.Library
_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>();
@@ -818,10 +816,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
{
@@ -952,7 +947,7 @@ namespace Emby.Server.Implementations.Library
.Cast<T>()
.FirstOrDefault();
- if (existing != null)
+ if (existing is not null)
{
return existing;
}
@@ -961,7 +956,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
{
@@ -1161,7 +1156,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
{
@@ -1181,30 +1176,30 @@ namespace Emby.Server.Implementations.Library
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";
}
}
@@ -1245,7 +1240,7 @@ namespace Emby.Server.Implementations.Library
item = RetrieveItem(id);
- if (item != null)
+ if (item is not null)
{
RegisterItem(item);
}
@@ -1258,18 +1253,25 @@ namespace Emby.Server.Implementations.Library
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)
@@ -1282,13 +1284,13 @@ namespace Emby.Server.Implementations.Library
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);
}
@@ -1302,7 +1304,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);
}
@@ -1313,7 +1315,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);
}
@@ -1331,7 +1333,7 @@ namespace Emby.Server.Implementations.Library
public List<Guid> GetItemIds(InternalItemsQuery query)
{
- if (query.User != null)
+ if (query.User is not null)
{
AddUserToQuery(query, query.User);
}
@@ -1341,7 +1343,7 @@ namespace Emby.Server.Implementations.Library
public QueryResult<(BaseItem Item, ItemCounts ItemCounts)> GetStudios(InternalItemsQuery query)
{
- if (query.User != null)
+ if (query.User is not null)
{
AddUserToQuery(query, query.User);
}
@@ -1352,7 +1354,7 @@ namespace Emby.Server.Implementations.Library
public QueryResult<(BaseItem Item, ItemCounts ItemCounts)> GetGenres(InternalItemsQuery query)
{
- if (query.User != null)
+ if (query.User is not null)
{
AddUserToQuery(query, query.User);
}
@@ -1363,7 +1365,7 @@ namespace Emby.Server.Implementations.Library
public QueryResult<(BaseItem Item, ItemCounts ItemCounts)> GetMusicGenres(InternalItemsQuery query)
{
- if (query.User != null)
+ if (query.User is not null)
{
AddUserToQuery(query, query.User);
}
@@ -1374,7 +1376,7 @@ namespace Emby.Server.Implementations.Library
public QueryResult<(BaseItem Item, ItemCounts ItemCounts)> GetAllArtists(InternalItemsQuery query)
{
- if (query.User != null)
+ if (query.User is not null)
{
AddUserToQuery(query, query.User);
}
@@ -1385,7 +1387,7 @@ namespace Emby.Server.Implementations.Library
public QueryResult<(BaseItem Item, ItemCounts ItemCounts)> GetArtists(InternalItemsQuery query)
{
- if (query.User != null)
+ if (query.User is not null)
{
AddUserToQuery(query, query.User);
}
@@ -1426,7 +1428,7 @@ namespace Emby.Server.Implementations.Library
public QueryResult<(BaseItem Item, ItemCounts ItemCounts)> GetAlbumArtists(InternalItemsQuery query)
{
- if (query.User != null)
+ if (query.User is not null)
{
AddUserToQuery(query, query.User);
}
@@ -1440,13 +1442,13 @@ namespace Emby.Server.Implementations.Library
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);
}
@@ -1462,7 +1464,7 @@ namespace Emby.Server.Implementations.Library
_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))
{
@@ -1508,6 +1510,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() };
+ }
}
}
@@ -1524,7 +1532,7 @@ namespace Emby.Server.Implementations.Library
if (!view.DisplayParentId.Equals(default))
{
var displayParent = GetItemById(view.DisplayParentId);
- if (displayParent != null)
+ if (displayParent is not null)
{
return GetTopParentIdsForQuery(displayParent, user);
}
@@ -1535,7 +1543,7 @@ namespace Emby.Server.Implementations.Library
if (!view.ParentId.Equals(default))
{
var displayParent = GetItemById(view.ParentId);
- if (displayParent != null)
+ if (displayParent is not null)
{
return GetTopParentIdsForQuery(displayParent, user);
}
@@ -1544,7 +1552,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()
@@ -1564,7 +1572,7 @@ namespace Emby.Server.Implementations.Library
}
var topParent = item.GetTopParent();
- if (topParent != null)
+ if (topParent is not null)
{
return new[] { topParent.Id };
}
@@ -1589,7 +1597,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>
@@ -1609,7 +1617,7 @@ namespace Emby.Server.Implementations.Library
{
_logger.LogError(ex, "Error getting intros");
- return new List<IntroInfo>();
+ return Enumerable.Empty<IntroInfo>();
}
}
@@ -1627,7 +1635,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);
}
@@ -1639,7 +1647,7 @@ 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);
}
@@ -1648,7 +1656,7 @@ namespace Emby.Server.Implementations.Library
// 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;
}
@@ -1685,7 +1693,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)
{
@@ -1711,7 +1719,7 @@ namespace Emby.Server.Implementations.Library
foreach (var (name, sortOrder) in orderBy)
{
var comparer = GetComparer(name, user);
- if (comparer == null)
+ if (comparer is null)
{
continue;
}
@@ -1781,7 +1789,7 @@ namespace Emby.Server.Implementations.Library
RegisterItem(item);
}
- if (ItemAdded != null)
+ if (ItemAdded is not null)
{
foreach (var item in items)
{
@@ -1811,7 +1819,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))
{
@@ -1829,7 +1837,7 @@ namespace Emby.Server.Implementations.Library
}
}
- return image.Path != null && !image.IsLocalFile;
+ return image.Path is not null && !image.IsLocalFile;
}
/// <inheritdoc />
@@ -1838,7 +1846,7 @@ namespace Emby.Server.Implementations.Library
ArgumentNullException.ThrowIfNull(item);
var outdated = forceUpdate
- ? item.ImageInfos.Where(i => i.Path != null).ToArray()
+ ? 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)
@@ -1884,7 +1892,7 @@ namespace Emby.Server.Implementations.Library
catch (Exception ex)
{
_logger.LogError(ex, "Cannot get image dimensions for {ImagePath}", image.Path);
- size = new ImageDimensions(0, 0);
+ size = default;
image.Width = 0;
image.Height = 0;
}
@@ -1923,7 +1931,7 @@ namespace Emby.Server.Implementations.Library
_itemRepository.SaveItems(items, cancellationToken);
- if (ItemUpdated != null)
+ if (ItemUpdated is not null)
{
foreach (var item in items)
{
@@ -1975,7 +1983,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
{
@@ -2006,41 +2014,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>();
}
@@ -2064,7 +2069,7 @@ namespace Emby.Server.Implementations.Library
.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)
@@ -2129,7 +2134,7 @@ namespace Emby.Server.Implementations.Library
private string GetTopFolderContentType(BaseItem item)
{
- if (item == null)
+ if (item is null)
{
return null;
}
@@ -2137,7 +2142,7 @@ namespace Emby.Server.Implementations.Library
while (!item.ParentId.Equals(default))
{
var parent = item.GetParent();
- if (parent == null || parent is AggregateFolder)
+ if (parent is null || parent is AggregateFolder)
{
break;
}
@@ -2177,7 +2182,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);
@@ -2225,7 +2230,7 @@ namespace Emby.Server.Implementations.Library
var isNew = false;
- if (item == null)
+ if (item is null)
{
Directory.CreateDirectory(path);
@@ -2251,7 +2256,7 @@ namespace Emby.Server.Implementations.Library
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)
@@ -2289,7 +2294,7 @@ namespace Emby.Server.Implementations.Library
var isNew = false;
- if (item == null)
+ if (item is null)
{
Directory.CreateDirectory(path);
@@ -2315,7 +2320,7 @@ namespace Emby.Server.Implementations.Library
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)
@@ -2340,10 +2345,7 @@ 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(default)
? null
@@ -2362,7 +2364,7 @@ namespace Emby.Server.Implementations.Library
var isNew = false;
- if (item == null)
+ if (item is null)
{
Directory.CreateDirectory(path);
@@ -2394,7 +2396,7 @@ namespace Emby.Server.Implementations.Library
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)
@@ -2441,7 +2443,7 @@ 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
@@ -2459,10 +2461,10 @@ 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
var parent = episode.GetParent();
- if (episodeInfo == null && parent.GetType() == typeof(Folder))
+ if (episodeInfo is null && parent.GetType() == typeof(Folder))
{
episodeInfo = resolver.Resolve(parent.Path, true, null, null, isAbsoluteNaming);
- if (episodeInfo != null)
+ if (episodeInfo is not null)
{
// add the container
episodeInfo.Container = Path.GetExtension(episode.Path)?.TrimStart('.');
@@ -2582,7 +2584,7 @@ namespace Emby.Server.Implementations.Library
{
var season = episode.Season;
- if (season != null)
+ if (season is not null)
{
episode.ParentIndexNumber = season.IndexNumber;
}
@@ -2590,9 +2592,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;
}
@@ -2620,7 +2622,7 @@ namespace Emby.Server.Implementations.Library
public IEnumerable<BaseItem> FindExtras(BaseItem owner, IReadOnlyList<FileSystemMetadata> fileSystemChildren, IDirectoryService directoryService)
{
var ownerVideoInfo = VideoResolver.Resolve(owner.Path, owner.IsFolder, _namingOptions);
- if (ownerVideoInfo == null)
+ if (ownerVideoInfo is null)
{
yield break;
}
@@ -2640,7 +2642,7 @@ namespace Emby.Server.Implementations.Library
}
var extra = GetExtra(file, extraType.Value);
- if (extra != null)
+ if (extra is not null)
{
yield return extra;
}
@@ -2649,7 +2651,7 @@ namespace Emby.Server.Implementations.Library
else if (!current.IsDirectory && _extraResolver.TryGetExtraTypeForOwner(current.FullName, ownerVideoInfo, out var extraType))
{
var extra = GetExtra(current, extraType.Value);
- if (extra != null)
+ if (extra is not null)
{
yield return extra;
}
@@ -2666,7 +2668,7 @@ namespace Emby.Server.Implementations.Library
// Try to retrieve it from the db. If we don't find it, use the resolved version
var itemById = GetItemById(extra.Id);
- if (itemById != null)
+ if (itemById is not null)
{
extra = itemById;
}
@@ -2681,10 +2683,10 @@ namespace Emby.Server.Implementations.Library
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)
{
@@ -2753,10 +2755,8 @@ namespace Emby.Server.Implementations.Library
return null;
}
})
- .Where(i => i != null)
- .Where(i => query.User == null ?
- true :
- i.IsVisible(query.User))
+ .Where(i => i is not null)
+ .Where(i => query.User is null || i.IsVisible(query.User))
.ToList();
}
@@ -2838,10 +2838,10 @@ namespace Emby.Server.Implementations.Library
}
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 + ".");
}
@@ -2853,7 +2853,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");
@@ -2862,7 +2862,7 @@ namespace Emby.Server.Implementations.Library
CollectionFolder.SaveLibraryOptions(virtualFolderPath, options);
- if (mediaPathInfos != null)
+ if (mediaPathInfos is not null)
{
foreach (var path in mediaPathInfos)
{
@@ -2889,7 +2889,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)
{
@@ -2931,12 +2931,12 @@ namespace Emby.Server.Implementations.Library
if (saveEntity)
{
- personsToSave.Add(personEntity);
+ (personsToSave ??= new()).Add(personEntity);
await RunMetadataSavers(personEntity, itemUpdateType).ConfigureAwait(false);
}
}
- if (personsToSave.Count > 0)
+ if (personsToSave is not null)
{
CreateItems(personsToSave, null, CancellationToken.None);
}
@@ -3098,22 +3098,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)
@@ -3125,10 +3122,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);
diff --git a/Emby.Server.Implementations/Library/LiveStreamHelper.cs b/Emby.Server.Implementations/Library/LiveStreamHelper.cs
index 20624cc7a..936a08da8 100644
--- a/Emby.Server.Implementations/Library/LiveStreamHelper.cs
+++ b/Emby.Server.Implementations/Library/LiveStreamHelper.cs
@@ -60,7 +60,7 @@ namespace Emby.Server.Implementations.Library
}
}
- if (mediaInfo == null)
+ if (mediaInfo is null)
{
if (addProbeDelay)
{
@@ -81,7 +81,7 @@ namespace Emby.Server.Implementations.Library
},
cancellationToken).ConfigureAwait(false);
- if (cacheFilePath != null)
+ if (cacheFilePath is not null)
{
Directory.CreateDirectory(Path.GetDirectoryName(cacheFilePath));
await using FileStream createStream = AsyncFile.OpenWrite(cacheFilePath);
@@ -130,7 +130,7 @@ namespace Emby.Server.Implementations.Library
var audioStream = mediaStreams.FirstOrDefault(i => i.Type == MediaStreamType.Audio);
- if (audioStream == null || audioStream.Index == -1)
+ if (audioStream is null || audioStream.Index == -1)
{
mediaSource.DefaultAudioStreamIndex = null;
}
@@ -140,7 +140,7 @@ namespace Emby.Server.Implementations.Library
}
var videoStream = mediaStreams.FirstOrDefault(i => i.Type == MediaStreamType.Video);
- if (videoStream != null)
+ if (videoStream is not null)
{
if (!videoStream.BitRate.HasValue)
{
diff --git a/Emby.Server.Implementations/Library/MediaSourceManager.cs b/Emby.Server.Implementations/Library/MediaSourceManager.cs
index bfccc7db7..c9a26a30f 100644
--- a/Emby.Server.Implementations/Library/MediaSourceManager.cs
+++ b/Emby.Server.Implementations/Library/MediaSourceManager.cs
@@ -154,8 +154,8 @@ namespace Emby.Server.Implementations.Library
// If file is strm or main media stream is missing, force a metadata refresh with remote probing
if (allowMediaProbe && mediaSources[0].Type != MediaSourceType.Placeholder
&& (item.Path.EndsWith(".strm", StringComparison.OrdinalIgnoreCase)
- || (item.MediaType == MediaType.Video && !mediaSources[0].MediaStreams.Any(i => i.Type == MediaStreamType.Video))
- || (item.MediaType == MediaType.Audio && !mediaSources[0].MediaStreams.Any(i => i.Type == MediaStreamType.Audio))))
+ || (item.MediaType == MediaType.Video && mediaSources[0].MediaStreams.All(i => i.Type != MediaStreamType.Video))
+ || (item.MediaType == MediaType.Audio && mediaSources[0].MediaStreams.All(i => i.Type != MediaStreamType.Audio))))
{
await item.RefreshMetadata(
new MetadataRefreshOptions(_directoryService)
@@ -182,7 +182,7 @@ namespace Emby.Server.Implementations.Library
source.SupportsDirectStream = SupportsDirectStream(source.Path, source.Protocol);
}
- if (user != null)
+ if (user is not null)
{
SetDefaultAudioAndSubtitleStreamIndexes(item, source, user);
@@ -248,7 +248,7 @@ namespace Emby.Server.Implementations.Library
if (protocol == MediaProtocol.Http)
{
- if (path != null)
+ if (path is not null)
{
if (path.Contains(".m3u", StringComparison.OrdinalIgnoreCase))
{
@@ -328,7 +328,7 @@ namespace Emby.Server.Implementations.Library
var sources = hasMediaSources.GetMediaSources(enablePathSubstitution);
- if (user != null)
+ if (user is not null)
{
foreach (var source in sources)
{
@@ -357,7 +357,7 @@ namespace Emby.Server.Implementations.Library
}
var culture = _localizationManager.FindLanguageInfo(language);
- if (culture != null)
+ if (culture is not null)
{
return culture.ThreeLetterISOLanguageNames;
}
@@ -383,7 +383,7 @@ namespace Emby.Server.Implementations.Library
var preferredSubs = NormalizeLanguage(user.SubtitleLanguagePreference);
var defaultAudioIndex = source.DefaultAudioStreamIndex;
- var audioLangage = defaultAudioIndex == null
+ var audioLangage = defaultAudioIndex is null
? null
: source.MediaStreams.Where(i => i.Type == MediaStreamType.Audio && i.Index == defaultAudioIndex).Select(i => i.Language).FirstOrDefault();
@@ -417,13 +417,13 @@ namespace Emby.Server.Implementations.Library
public void SetDefaultAudioAndSubtitleStreamIndexes(BaseItem item, MediaSourceInfo source, User user)
{
// Item would only be null if the app didn't supply ItemId as part of the live stream open request
- var mediaType = item == null ? MediaType.Video : item.MediaType;
+ var mediaType = item is null ? MediaType.Video : item.MediaType;
if (string.Equals(mediaType, MediaType.Video, StringComparison.OrdinalIgnoreCase))
{
- var userData = item == null ? new UserItemData() : _userDataManager.GetUserData(user, item);
+ var userData = item is null ? new UserItemData() : _userDataManager.GetUserData(user, item);
- var allowRememberingSelection = item == null || item.EnableRememberingTrackSelections;
+ var allowRememberingSelection = item is null || item.EnableRememberingTrackSelections;
SetDefaultAudioStreamIndex(source, userData, user, allowRememberingSelection);
SetDefaultSubtitleStreamIndex(source, userData, user, allowRememberingSelection);
@@ -432,7 +432,7 @@ namespace Emby.Server.Implementations.Library
{
var audio = source.MediaStreams.FirstOrDefault(i => i.Type == MediaStreamType.Audio);
- if (audio != null)
+ if (audio is not null)
{
source.DefaultAudioStreamIndex = audio.Index;
}
@@ -543,7 +543,7 @@ namespace Emby.Server.Implementations.Library
var audioStream = mediaSource.MediaStreams.FirstOrDefault(i => i.Type == MediaStreamType.Audio);
- if (audioStream == null || audioStream.Index == -1)
+ if (audioStream is null || audioStream.Index == -1)
{
mediaSource.DefaultAudioStreamIndex = null;
}
@@ -553,7 +553,7 @@ namespace Emby.Server.Implementations.Library
}
var videoStream = mediaSource.MediaStreams.FirstOrDefault(i => i.Type == MediaStreamType.Video);
- if (videoStream != null)
+ if (videoStream is not null)
{
if (!videoStream.BitRate.HasValue)
{
@@ -638,7 +638,7 @@ namespace Emby.Server.Implementations.Library
}
}
- if (mediaInfo == null)
+ if (mediaInfo is null)
{
if (addProbeDelay)
{
@@ -661,7 +661,7 @@ namespace Emby.Server.Implementations.Library
},
cancellationToken).ConfigureAwait(false);
- if (cacheFilePath != null)
+ if (cacheFilePath is not null)
{
Directory.CreateDirectory(Path.GetDirectoryName(cacheFilePath));
await using FileStream createStream = File.Create(cacheFilePath);
@@ -713,7 +713,7 @@ namespace Emby.Server.Implementations.Library
var audioStream = mediaStreams.FirstOrDefault(i => i.Type == MediaStreamType.Audio);
- if (audioStream == null || audioStream.Index == -1)
+ if (audioStream is null || audioStream.Index == -1)
{
mediaSource.DefaultAudioStreamIndex = null;
}
@@ -723,7 +723,7 @@ namespace Emby.Server.Implementations.Library
}
var videoStream = mediaStreams.FirstOrDefault(i => i.Type == MediaStreamType.Video);
- if (videoStream != null)
+ if (videoStream is not null)
{
if (!videoStream.BitRate.HasValue)
{
@@ -762,10 +762,7 @@ namespace Emby.Server.Implementations.Library
public Task<Tuple<MediaSourceInfo, IDirectStreamProvider>> GetLiveStreamWithDirectStreamProvider(string id, CancellationToken cancellationToken)
{
- if (string.IsNullOrEmpty(id))
- {
- throw new ArgumentNullException(nameof(id));
- }
+ ArgumentException.ThrowIfNullOrEmpty(id);
// TODO probably shouldn't throw here but it is kept for "backwards compatibility"
var info = GetLiveStreamInfo(id) ?? throw new ResourceNotFoundException();
@@ -774,10 +771,7 @@ namespace Emby.Server.Implementations.Library
public ILiveStream GetLiveStreamInfo(string id)
{
- if (string.IsNullOrEmpty(id))
- {
- throw new ArgumentNullException(nameof(id));
- }
+ ArgumentException.ThrowIfNullOrEmpty(id);
if (_openStreams.TryGetValue(id, out ILiveStream info))
{
@@ -801,10 +795,7 @@ namespace Emby.Server.Implementations.Library
public async Task CloseLiveStream(string id)
{
- if (string.IsNullOrEmpty(id))
- {
- throw new ArgumentNullException(nameof(id));
- }
+ ArgumentException.ThrowIfNullOrEmpty(id);
await _liveStreamSemaphore.WaitAsync().ConfigureAwait(false);
@@ -835,10 +826,7 @@ namespace Emby.Server.Implementations.Library
private (IMediaSourceProvider MediaSourceProvider, string KeyId) GetProvider(string key)
{
- if (string.IsNullOrEmpty(key))
- {
- throw new ArgumentException("Key can't be empty.", nameof(key));
- }
+ ArgumentException.ThrowIfNullOrEmpty(key);
var keys = key.Split(LiveStreamIdDelimeter, 2);
diff --git a/Emby.Server.Implementations/Library/MediaStreamSelector.cs b/Emby.Server.Implementations/Library/MediaStreamSelector.cs
index 609b95772..6aef87c52 100644
--- a/Emby.Server.Implementations/Library/MediaStreamSelector.cs
+++ b/Emby.Server.Implementations/Library/MediaStreamSelector.cs
@@ -19,7 +19,7 @@ namespace Emby.Server.Implementations.Library
{
var defaultStream = sortedStreams.FirstOrDefault(i => i.IsDefault);
- if (defaultStream != null)
+ if (defaultStream is not null)
{
return defaultStream.Index;
}
@@ -89,17 +89,7 @@ namespace Emby.Server.Implementations.Library
// Give some preference to external text subs for better performance
return streams
.Where(i => i.Type == type)
- .OrderBy(i =>
- {
- var index = languagePreferences.FindIndex(x => string.Equals(x, i.Language, StringComparison.OrdinalIgnoreCase));
-
- return index == -1 ? 100 : index;
- })
- .ThenBy(i => GetBooleanOrderBy(i.IsDefault))
- .ThenBy(i => GetBooleanOrderBy(i.SupportsExternalStream))
- .ThenBy(i => GetBooleanOrderBy(i.IsTextSubtitleStream))
- .ThenBy(i => GetBooleanOrderBy(i.IsExternal))
- .ThenBy(i => i.Index);
+ .OrderByDescending(i => GetStreamScore(i, languagePreferences));
}
public static void SetSubtitleStreamScores(
@@ -113,9 +103,9 @@ namespace Emby.Server.Implementations.Library
return;
}
- var sortedStreams = GetSortedStreams(streams, MediaStreamType.Subtitle, preferredLanguages);
+ var sortedStreams = GetSortedStreams(streams, MediaStreamType.Subtitle, preferredLanguages).ToList();
- var filteredStreams = new List<MediaStream>();
+ List<MediaStream>? filteredStreams = null;
if (mode == SubtitlePlaybackMode.Default)
{
@@ -144,46 +134,26 @@ namespace Emby.Server.Implementations.Library
}
// load forced subs if we have found no suitable full subtitles
- var iterStreams = filteredStreams.Count == 0
+ var iterStreams = filteredStreams is null || filteredStreams.Count == 0
? sortedStreams.Where(s => s.IsForced && string.Equals(s.Language, audioTrackLanguage, StringComparison.OrdinalIgnoreCase))
: filteredStreams;
foreach (var stream in iterStreams)
{
- stream.Score = GetSubtitleScore(stream, preferredLanguages);
+ stream.Score = GetStreamScore(stream, preferredLanguages);
}
}
- private static int GetSubtitleScore(MediaStream stream, IReadOnlyList<string> languagePreferences)
+ internal static int GetStreamScore(MediaStream stream, IReadOnlyList<string> languagePreferences)
{
- var values = new List<int>();
-
var index = languagePreferences.FindIndex(x => string.Equals(x, stream.Language, StringComparison.OrdinalIgnoreCase));
-
- values.Add(index == -1 ? 0 : 100 - index);
-
- values.Add(stream.IsForced ? 1 : 0);
- values.Add(stream.IsDefault ? 1 : 0);
- values.Add(stream.SupportsExternalStream ? 1 : 0);
- values.Add(stream.IsTextSubtitleStream ? 1 : 0);
- values.Add(stream.IsExternal ? 1 : 0);
-
- values.Reverse();
- var scale = 1;
- var score = 0;
-
- foreach (var value in values)
- {
- score += scale * (value + 1);
- scale *= 10;
- }
-
+ var score = index == -1 ? 1 : 101 - index;
+ score = (score * 10) + (stream.IsForced ? 2 : 1);
+ score = (score * 10) + (stream.IsDefault ? 2 : 1);
+ score = (score * 10) + (stream.SupportsExternalStream ? 2 : 1);
+ score = (score * 10) + (stream.IsTextSubtitleStream ? 2 : 1);
+ score = (score * 10) + (stream.IsExternal ? 2 : 1);
return score;
}
-
- private static int GetBooleanOrderBy(bool value)
- {
- return value ? 0 : 1;
- }
}
}
diff --git a/Emby.Server.Implementations/Library/PathExtensions.cs b/Emby.Server.Implementations/Library/PathExtensions.cs
index 64e7d5446..c4b6b3756 100644
--- a/Emby.Server.Implementations/Library/PathExtensions.cs
+++ b/Emby.Server.Implementations/Library/PathExtensions.cs
@@ -1,5 +1,6 @@
using System;
using System.Diagnostics.CodeAnalysis;
+using System.IO;
using MediaBrowser.Common.Providers;
namespace Emby.Server.Implementations.Library
@@ -86,24 +87,8 @@ namespace Emby.Server.Implementations.Library
return false;
}
- char oldDirectorySeparatorChar;
- char newDirectorySeparatorChar;
- // True normalization is still not possible https://github.com/dotnet/runtime/issues/2162
- // The reasoning behind this is that a forward slash likely means it's a Linux path and
- // so the whole path should be normalized to use / and vice versa for Windows (although Windows doesn't care much).
- if (newSubPath.Contains('/', StringComparison.Ordinal))
- {
- oldDirectorySeparatorChar = '\\';
- newDirectorySeparatorChar = '/';
- }
- else
- {
- oldDirectorySeparatorChar = '/';
- newDirectorySeparatorChar = '\\';
- }
-
- path = path.Replace(oldDirectorySeparatorChar, newDirectorySeparatorChar);
- subPath = subPath.Replace(oldDirectorySeparatorChar, newDirectorySeparatorChar);
+ subPath = subPath.NormalizePath(out var newDirectorySeparatorChar);
+ path = path.NormalizePath(newDirectorySeparatorChar);
// We have to ensure that the sub path ends with a directory separator otherwise we'll get weird results
// when the sub path matches a similar but in-complete subpath
@@ -127,5 +112,82 @@ namespace Emby.Server.Implementations.Library
return true;
}
+
+ /// <summary>
+ /// Retrieves the full resolved path and normalizes path separators to the <see cref="Path.DirectorySeparatorChar"/>.
+ /// </summary>
+ /// <param name="path">The path to canonicalize.</param>
+ /// <returns>The fully expanded, normalized path.</returns>
+ public static string Canonicalize(this string path)
+ {
+ return Path.GetFullPath(path).NormalizePath();
+ }
+
+ /// <summary>
+ /// Normalizes the path's directory separator character to the currently defined <see cref="Path.DirectorySeparatorChar"/>.
+ /// </summary>
+ /// <param name="path">The path to normalize.</param>
+ /// <returns>The normalized path string or <see langword="null"/> if the input path is null or empty.</returns>
+ [return: NotNullIfNotNull(nameof(path))]
+ public static string? NormalizePath(this string? path)
+ {
+ return path.NormalizePath(Path.DirectorySeparatorChar);
+ }
+
+ /// <summary>
+ /// Normalizes the path's directory separator character.
+ /// </summary>
+ /// <param name="path">The path to normalize.</param>
+ /// <param name="separator">The separator character the path now uses or <see langword="null"/>.</param>
+ /// <returns>The normalized path string or <see langword="null"/> if the input path is null or empty.</returns>
+ [return: NotNullIfNotNull(nameof(path))]
+ public static string? NormalizePath(this string? path, out char separator)
+ {
+ if (string.IsNullOrEmpty(path))
+ {
+ separator = default;
+ return path;
+ }
+
+ var newSeparator = '\\';
+
+ // True normalization is still not possible https://github.com/dotnet/runtime/issues/2162
+ // The reasoning behind this is that a forward slash likely means it's a Linux path and
+ // so the whole path should be normalized to use / and vice versa for Windows (although Windows doesn't care much).
+ if (path.Contains('/', StringComparison.Ordinal))
+ {
+ newSeparator = '/';
+ }
+
+ separator = newSeparator;
+
+ return path.NormalizePath(newSeparator);
+ }
+
+ /// <summary>
+ /// Normalizes the path's directory separator character to the specified character.
+ /// </summary>
+ /// <param name="path">The path to normalize.</param>
+ /// <param name="newSeparator">The replacement directory separator character. Must be a valid directory separator.</param>
+ /// <returns>The normalized path.</returns>
+ /// <exception cref="ArgumentException">Thrown if the new separator character is not a directory separator.</exception>
+ [return: NotNullIfNotNull(nameof(path))]
+ public static string? NormalizePath(this string? path, char newSeparator)
+ {
+ const char Bs = '\\';
+ const char Fs = '/';
+
+ if (!(newSeparator == Bs || newSeparator == Fs))
+ {
+ throw new ArgumentException("The character must be a directory separator.");
+ }
+
+ if (string.IsNullOrEmpty(path))
+ {
+ return path;
+ }
+
+ return newSeparator == Bs ? path.Replace(Fs, newSeparator) : path.Replace(Bs, newSeparator);
+ }
}
}
diff --git a/Emby.Server.Implementations/Library/ResolverHelper.cs b/Emby.Server.Implementations/Library/ResolverHelper.cs
index 4100a74a5..7a61e2607 100644
--- a/Emby.Server.Implementations/Library/ResolverHelper.cs
+++ b/Emby.Server.Implementations/Library/ResolverHelper.cs
@@ -25,13 +25,10 @@ namespace Emby.Server.Implementations.Library
public static bool SetInitialItemValues(BaseItem item, Folder? parent, ILibraryManager libraryManager, IDirectoryService directoryService)
{
// This version of the below method has no ItemResolveArgs, so we have to require the path already being set
- if (string.IsNullOrEmpty(item.Path))
- {
- throw new ArgumentException("Item must have a Path");
- }
+ ArgumentException.ThrowIfNullOrEmpty(item.Path);
// If the resolver didn't specify this
- if (parent != null)
+ if (parent is not null)
{
item.SetParent(parent);
}
@@ -43,7 +40,7 @@ namespace Emby.Server.Implementations.Library
// Make sure DateCreated and DateModified have values
var fileInfo = directoryService.GetFile(item.Path);
- if (fileInfo == null)
+ if (fileInfo is null)
{
return false;
}
@@ -71,7 +68,7 @@ namespace Emby.Server.Implementations.Library
}
// If the resolver didn't specify this
- if (args.Parent != null)
+ if (args.Parent is not null)
{
item.SetParent(args.Parent);
}
@@ -113,7 +110,7 @@ namespace Emby.Server.Implementations.Library
{
var childData = args.IsDirectory ? args.GetFileSystemEntryByPath(item.Path) : null;
- if (childData != null)
+ if (childData is not null)
{
SetDateCreated(item, childData);
}
@@ -140,7 +137,7 @@ namespace Emby.Server.Implementations.Library
if (config.UseFileCreationTimeForDateAdded)
{
// directoryService.getFile may return null
- if (info != null)
+ if (info is not null)
{
var dateCreated = info.CreationTimeUtc;
diff --git a/Emby.Server.Implementations/Library/Resolvers/Audio/AudioResolver.cs b/Emby.Server.Implementations/Library/Resolvers/Audio/AudioResolver.cs
index 7a6aea9c1..a74f82475 100644
--- a/Emby.Server.Implementations/Library/Resolvers/Audio/AudioResolver.cs
+++ b/Emby.Server.Implementations/Library/Resolvers/Audio/AudioResolver.cs
@@ -45,7 +45,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.Audio
{
var result = ResolveMultipleInternal(parent, files, collectionType);
- if (result != null)
+ if (result is not null)
{
foreach (var item in result.Items)
{
@@ -116,7 +116,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.Audio
// Use regular audio type for mixed libraries, owned items and music
if (isMixedCollectionType ||
- args.Parent == null ||
+ args.Parent is null ||
isMusicCollectionType)
{
item = new MediaBrowser.Controller.Entities.Audio.Audio();
@@ -126,7 +126,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.Audio
item = new AudioBook();
}
- if (item != null)
+ if (item is not null)
{
item.IsShortcut = string.Equals(extension, ".strm", StringComparison.OrdinalIgnoreCase);
@@ -144,7 +144,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.Audio
// TODO: Allow GetMultiDiscMovie in here
var result = ResolveMultipleAudio(args.Parent, args.GetActualFileSystemChildren(), parseName);
- if (result == null || result.Items.Count != 1 || result.Items[0] is not AudioBook item)
+ if (result is null || result.Items.Count != 1 || result.Items[0] is not AudioBook item)
{
return null;
}
@@ -158,7 +158,6 @@ namespace Emby.Server.Implementations.Library.Resolvers.Audio
private MultiItemResolverResult ResolveMultipleAudio(Folder parent, IEnumerable<FileSystemMetadata> fileSystemEntries, bool parseName)
{
var files = new List<FileSystemMetadata>();
- var items = new List<BaseItem>();
var leftOver = new List<FileSystemMetadata>();
// Loop through each child file/folder and see if we find a video
@@ -180,10 +179,10 @@ namespace Emby.Server.Implementations.Library.Resolvers.Audio
var result = new MultiItemResolverResult
{
ExtraFiles = leftOver,
- Items = items
+ Items = new List<BaseItem>()
};
- var isInMixedFolder = resolverResult.Count > 1 || (parent != null && parent.IsTopParent);
+ var isInMixedFolder = resolverResult.Count > 1 || (parent is not null && parent.IsTopParent);
foreach (var resolvedItem in resolverResult)
{
@@ -193,7 +192,8 @@ namespace Emby.Server.Implementations.Library.Resolvers.Audio
continue;
}
- if (resolvedItem.Files.Count == 0)
+ // Until multi-part books are handled letting files stack hides them from browsing in the client
+ if (resolvedItem.Files.Count == 0 || resolvedItem.Extras.Count > 0 || resolvedItem.AlternateVersions.Count > 0)
{
continue;
}
diff --git a/Emby.Server.Implementations/Library/Resolvers/Audio/MusicAlbumResolver.cs b/Emby.Server.Implementations/Library/Resolvers/Audio/MusicAlbumResolver.cs
index a922e3685..bbc70701c 100644
--- a/Emby.Server.Implementations/Library/Resolvers/Audio/MusicAlbumResolver.cs
+++ b/Emby.Server.Implementations/Library/Resolvers/Audio/MusicAlbumResolver.cs
@@ -25,16 +25,19 @@ namespace Emby.Server.Implementations.Library.Resolvers.Audio
{
private readonly ILogger<MusicAlbumResolver> _logger;
private readonly NamingOptions _namingOptions;
+ private readonly IDirectoryService _directoryService;
/// <summary>
/// Initializes a new instance of the <see cref="MusicAlbumResolver"/> class.
/// </summary>
/// <param name="logger">The logger.</param>
/// <param name="namingOptions">The naming options.</param>
- public MusicAlbumResolver(ILogger<MusicAlbumResolver> logger, NamingOptions namingOptions)
+ /// <param name="directoryService">The directory service.</param>
+ public MusicAlbumResolver(ILogger<MusicAlbumResolver> logger, NamingOptions namingOptions, IDirectoryService directoryService)
{
_logger = logger;
_namingOptions = namingOptions;
+ _directoryService = directoryService;
}
/// <summary>
@@ -109,7 +112,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.Audio
}
// If args contains music it's a music album
- if (ContainsMusic(args.FileSystemChildren, true, args.DirectoryService))
+ if (ContainsMusic(args.FileSystemChildren, true, _directoryService))
{
return true;
}
diff --git a/Emby.Server.Implementations/Library/Resolvers/Audio/MusicArtistResolver.cs b/Emby.Server.Implementations/Library/Resolvers/Audio/MusicArtistResolver.cs
index 2538c2b5b..c858dc53d 100644
--- a/Emby.Server.Implementations/Library/Resolvers/Audio/MusicArtistResolver.cs
+++ b/Emby.Server.Implementations/Library/Resolvers/Audio/MusicArtistResolver.cs
@@ -6,6 +6,7 @@ using System.Threading.Tasks;
using Emby.Naming.Common;
using MediaBrowser.Controller.Entities.Audio;
using MediaBrowser.Controller.Library;
+using MediaBrowser.Controller.Providers;
using MediaBrowser.Controller.Resolvers;
using MediaBrowser.Model.Entities;
using Microsoft.Extensions.Logging;
@@ -18,19 +19,23 @@ namespace Emby.Server.Implementations.Library.Resolvers.Audio
public class MusicArtistResolver : ItemResolver<MusicArtist>
{
private readonly ILogger<MusicAlbumResolver> _logger;
- private NamingOptions _namingOptions;
+ private readonly NamingOptions _namingOptions;
+ private readonly IDirectoryService _directoryService;
/// <summary>
/// Initializes a new instance of the <see cref="MusicArtistResolver"/> class.
/// </summary>
/// <param name="logger">Instance of the <see cref="MusicAlbumResolver"/> interface.</param>
/// <param name="namingOptions">The <see cref="NamingOptions"/>.</param>
+ /// <param name="directoryService">The directory service.</param>
public MusicArtistResolver(
ILogger<MusicAlbumResolver> logger,
- NamingOptions namingOptions)
+ NamingOptions namingOptions,
+ IDirectoryService directoryService)
{
_logger = logger;
_namingOptions = namingOptions;
+ _directoryService = directoryService;
}
/// <summary>
@@ -78,9 +83,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.Audio
return null;
}
- var directoryService = args.DirectoryService;
-
- var albumResolver = new MusicAlbumResolver(_logger, _namingOptions);
+ var albumResolver = new MusicAlbumResolver(_logger, _namingOptions, _directoryService);
var directories = args.FileSystemChildren.Where(i => i.IsDirectory);
@@ -97,7 +100,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.Audio
}
// If we contain a music album assume we are an artist folder
- if (albumResolver.IsMusicAlbum(fileSystemInfo.FullName, directoryService))
+ if (albumResolver.IsMusicAlbum(fileSystemInfo.FullName, _directoryService))
{
// Stop once we see a music album
state.Stop();
diff --git a/Emby.Server.Implementations/Library/Resolvers/BaseVideoResolver.cs b/Emby.Server.Implementations/Library/Resolvers/BaseVideoResolver.cs
index b2a7abb1b..381796d0e 100644
--- a/Emby.Server.Implementations/Library/Resolvers/BaseVideoResolver.cs
+++ b/Emby.Server.Implementations/Library/Resolvers/BaseVideoResolver.cs
@@ -25,14 +25,17 @@ namespace Emby.Server.Implementations.Library.Resolvers
{
private readonly ILogger _logger;
- protected BaseVideoResolver(ILogger logger, NamingOptions namingOptions)
+ protected BaseVideoResolver(ILogger logger, NamingOptions namingOptions, IDirectoryService directoryService)
{
_logger = logger;
NamingOptions = namingOptions;
+ DirectoryService = directoryService;
}
protected NamingOptions NamingOptions { get; }
+ protected IDirectoryService DirectoryService { get; }
+
/// <summary>
/// Resolves the specified args.
/// </summary>
@@ -65,13 +68,26 @@ namespace Emby.Server.Implementations.Library.Resolvers
var filename = child.Name;
if (child.IsDirectory)
{
- if (IsDvdDirectory(child.FullName, filename, args.DirectoryService))
+ if (IsDvdDirectory(child.FullName, filename, DirectoryService))
{
- videoType = VideoType.Dvd;
+ var videoTmp = new TVideoType
+ {
+ Path = args.Path,
+ VideoType = VideoType.Dvd
+ };
+ Set3DFormat(videoTmp);
+ return videoTmp;
}
- else if (IsBluRayDirectory(filename))
+
+ if (IsBluRayDirectory(filename))
{
- videoType = VideoType.BluRay;
+ var videoTmp = new TVideoType
+ {
+ Path = args.Path,
+ VideoType = VideoType.BluRay
+ };
+ Set3DFormat(videoTmp);
+ return videoTmp;
}
}
else if (IsDvdFile(filename))
@@ -79,7 +95,7 @@ namespace Emby.Server.Implementations.Library.Resolvers
videoType = VideoType.Dvd;
}
- if (videoType == null)
+ if (videoType is null)
{
continue;
}
@@ -93,7 +109,7 @@ namespace Emby.Server.Implementations.Library.Resolvers
videoInfo = VideoResolver.Resolve(args.Path, false, NamingOptions, parseName);
}
- if (videoInfo == null || (!videoInfo.IsStub && !VideoResolver.IsVideoFile(args.Path, NamingOptions)))
+ if (videoInfo is null || (!videoInfo.IsStub && !VideoResolver.IsVideoFile(args.Path, NamingOptions)))
{
return null;
}
@@ -163,17 +179,15 @@ namespace Emby.Server.Implementations.Library.Resolvers
try
{
// use disc-utils, both DVDs and BDs use UDF filesystem
- using (var videoFileStream = File.Open(video.Path, FileMode.Open, FileAccess.Read))
- using (UdfReader udfReader = new UdfReader(videoFileStream))
+ using var videoFileStream = File.Open(video.Path, FileMode.Open, FileAccess.Read, FileShare.Read);
+ using UdfReader udfReader = new UdfReader(videoFileStream);
+ if (udfReader.DirectoryExists("VIDEO_TS"))
{
- if (udfReader.DirectoryExists("VIDEO_TS"))
- {
- video.IsoType = IsoType.Dvd;
- }
- else if (udfReader.DirectoryExists("BDMV"))
- {
- video.IsoType = IsoType.BluRay;
- }
+ video.IsoType = IsoType.Dvd;
+ }
+ else if (udfReader.DirectoryExists("BDMV"))
+ {
+ video.IsoType = IsoType.BluRay;
}
}
catch (Exception ex)
diff --git a/Emby.Server.Implementations/Library/Resolvers/Books/BookResolver.cs b/Emby.Server.Implementations/Library/Resolvers/Books/BookResolver.cs
index 6fc200e3b..042422c6f 100644
--- a/Emby.Server.Implementations/Library/Resolvers/Books/BookResolver.cs
+++ b/Emby.Server.Implementations/Library/Resolvers/Books/BookResolver.cs
@@ -34,7 +34,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.Books
var extension = Path.GetExtension(args.Path);
- if (extension != null && _validExtensions.Contains(extension, StringComparison.OrdinalIgnoreCase))
+ if (extension is not null && _validExtensions.Contains(extension, StringComparison.OrdinalIgnoreCase))
{
// It's a book
return new Book
diff --git a/Emby.Server.Implementations/Library/Resolvers/ExtraResolver.cs b/Emby.Server.Implementations/Library/Resolvers/ExtraResolver.cs
index 408e640f9..b4791b945 100644
--- a/Emby.Server.Implementations/Library/Resolvers/ExtraResolver.cs
+++ b/Emby.Server.Implementations/Library/Resolvers/ExtraResolver.cs
@@ -4,6 +4,8 @@ using System.IO;
using Emby.Naming.Common;
using Emby.Naming.Video;
using MediaBrowser.Controller.Entities;
+using MediaBrowser.Controller.Library;
+using MediaBrowser.Controller.Providers;
using MediaBrowser.Controller.Resolvers;
using MediaBrowser.Model.Entities;
using Microsoft.Extensions.Logging;
@@ -14,7 +16,7 @@ namespace Emby.Server.Implementations.Library.Resolvers
/// <summary>
/// Resolves a Path into a Video or Video subclass.
/// </summary>
- internal class ExtraResolver
+ internal class ExtraResolver : BaseVideoResolver<Video>
{
private readonly NamingOptions _namingOptions;
private readonly IItemResolver[] _trailerResolvers;
@@ -25,11 +27,18 @@ namespace Emby.Server.Implementations.Library.Resolvers
/// </summary>
/// <param name="logger">The logger.</param>
/// <param name="namingOptions">An instance of <see cref="NamingOptions"/>.</param>
- public ExtraResolver(ILogger<ExtraResolver> logger, NamingOptions namingOptions)
+ /// <param name="directoryService">The directory service.</param>
+ public ExtraResolver(ILogger<ExtraResolver> logger, NamingOptions namingOptions, IDirectoryService directoryService)
+ : base(logger, namingOptions, directoryService)
{
_namingOptions = namingOptions;
- _trailerResolvers = new IItemResolver[] { new GenericVideoResolver<Trailer>(logger, namingOptions) };
- _videoResolvers = new IItemResolver[] { new GenericVideoResolver<Video>(logger, namingOptions) };
+ _trailerResolvers = new IItemResolver[] { new GenericVideoResolver<Trailer>(logger, namingOptions, directoryService) };
+ _videoResolvers = new IItemResolver[] { this };
+ }
+
+ protected override Video Resolve(ItemResolveArgs args)
+ {
+ return ResolveVideo<Video>(args, true);
}
/// <summary>
@@ -48,7 +57,7 @@ namespace Emby.Server.Implementations.Library.Resolvers
public bool TryGetExtraTypeForOwner(string path, VideoFileInfo ownerVideoFileInfo, [NotNullWhen(true)] out ExtraType? extraType)
{
var extraResult = GetExtraInfo(path, _namingOptions);
- if (extraResult.ExtraType == null)
+ if (extraResult.ExtraType is null)
{
extraType = null;
return false;
diff --git a/Emby.Server.Implementations/Library/Resolvers/GenericFolderResolver.cs b/Emby.Server.Implementations/Library/Resolvers/GenericFolderResolver.cs
index 079962282..1c2de912a 100644
--- a/Emby.Server.Implementations/Library/Resolvers/GenericFolderResolver.cs
+++ b/Emby.Server.Implementations/Library/Resolvers/GenericFolderResolver.cs
@@ -22,7 +22,7 @@ namespace Emby.Server.Implementations.Library.Resolvers
{
base.SetInitialItemValues(item, args);
- item.IsRoot = args.Parent == null;
+ item.IsRoot = args.Parent is null;
}
}
}
diff --git a/Emby.Server.Implementations/Library/Resolvers/GenericVideoResolver.cs b/Emby.Server.Implementations/Library/Resolvers/GenericVideoResolver.cs
index 5e33b402d..ba320266a 100644
--- a/Emby.Server.Implementations/Library/Resolvers/GenericVideoResolver.cs
+++ b/Emby.Server.Implementations/Library/Resolvers/GenericVideoResolver.cs
@@ -2,6 +2,7 @@
using Emby.Naming.Common;
using MediaBrowser.Controller.Entities;
+using MediaBrowser.Controller.Providers;
using Microsoft.Extensions.Logging;
namespace Emby.Server.Implementations.Library.Resolvers
@@ -18,8 +19,9 @@ namespace Emby.Server.Implementations.Library.Resolvers
/// </summary>
/// <param name="logger">The logger.</param>
/// <param name="namingOptions">The naming options.</param>
- public GenericVideoResolver(ILogger logger, NamingOptions namingOptions)
- : base(logger, namingOptions)
+ /// <param name="directoryService">The directory service.</param>
+ public GenericVideoResolver(ILogger logger, NamingOptions namingOptions, IDirectoryService directoryService)
+ : base(logger, namingOptions, directoryService)
{
}
}
diff --git a/Emby.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs b/Emby.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs
index 8f9e5f01b..ea980b992 100644
--- a/Emby.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs
+++ b/Emby.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs
@@ -43,8 +43,9 @@ namespace Emby.Server.Implementations.Library.Resolvers.Movies
/// <param name="imageProcessor">The image processor.</param>
/// <param name="logger">The logger.</param>
/// <param name="namingOptions">The naming options.</param>
- public MovieResolver(IImageProcessor imageProcessor, ILogger<MovieResolver> logger, NamingOptions namingOptions)
- : base(logger, namingOptions)
+ /// <param name="directoryService">The directory service.</param>
+ public MovieResolver(IImageProcessor imageProcessor, ILogger<MovieResolver> logger, NamingOptions namingOptions, IDirectoryService directoryService)
+ : base(logger, namingOptions, directoryService)
{
_imageProcessor = imageProcessor;
}
@@ -64,7 +65,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.Movies
{
var result = ResolveMultipleInternal(parent, files, collectionType);
- if (result != null)
+ if (result is not null)
{
foreach (var item in result.Items)
{
@@ -97,18 +98,18 @@ namespace Emby.Server.Implementations.Library.Resolvers.Movies
if (string.Equals(collectionType, CollectionType.MusicVideos, StringComparison.OrdinalIgnoreCase))
{
- movie = FindMovie<MusicVideo>(args, args.Path, args.Parent, files, args.DirectoryService, collectionType, false);
+ movie = FindMovie<MusicVideo>(args, args.Path, args.Parent, files, DirectoryService, collectionType, false);
}
if (string.Equals(collectionType, CollectionType.HomeVideos, StringComparison.OrdinalIgnoreCase))
{
- movie = FindMovie<Video>(args, args.Path, args.Parent, files, args.DirectoryService, collectionType, false);
+ movie = FindMovie<Video>(args, args.Path, args.Parent, files, DirectoryService, collectionType, false);
}
if (string.IsNullOrEmpty(collectionType))
{
// Owned items will be caught by the video extra resolver
- if (args.Parent == null)
+ if (args.Parent is null)
{
return null;
}
@@ -118,19 +119,19 @@ namespace Emby.Server.Implementations.Library.Resolvers.Movies
return null;
}
- movie = FindMovie<Movie>(args, args.Path, args.Parent, files, args.DirectoryService, collectionType, true);
+ movie = FindMovie<Movie>(args, args.Path, args.Parent, files, DirectoryService, collectionType, true);
}
if (string.Equals(collectionType, CollectionType.Movies, StringComparison.OrdinalIgnoreCase))
{
- movie = FindMovie<Movie>(args, args.Path, args.Parent, files, args.DirectoryService, collectionType, true);
+ movie = FindMovie<Movie>(args, args.Path, args.Parent, files, DirectoryService, collectionType, true);
}
// ignore extras
- return movie?.ExtraType == null ? movie : null;
+ return movie?.ExtraType is null ? movie : null;
}
- if (args.Parent == null)
+ if (args.Parent is null)
{
return base.Resolve(args);
}
@@ -168,12 +169,12 @@ namespace Emby.Server.Implementations.Library.Resolvers.Movies
}
// Ignore extras
- if (item?.ExtraType != null)
+ if (item?.ExtraType is not null)
{
return null;
}
- if (item != null)
+ if (item is not null)
{
item.IsInMixedFolder = true;
}
@@ -205,7 +206,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.Movies
if (string.IsNullOrEmpty(collectionType))
{
// Owned items should just use the plain video type
- if (parent == null)
+ if (parent is null)
{
return ResolveVideos<Video>(parent, files, false, collectionType, false);
}
@@ -268,7 +269,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.Movies
var videoInfos = files
.Select(i => VideoResolver.Resolve(i.FullName, i.IsDirectory, NamingOptions, parseName))
- .Where(f => f != null)
+ .Where(f => f is not null)
.ToList();
var resolverResult = VideoListResolver.Resolve(videoInfos, NamingOptions, supportMultiEditions, parseName);
@@ -284,7 +285,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.Movies
{
var firstVideo = video.Files[0];
var path = firstVideo.Path;
- if (video.ExtraType != null)
+ if (video.ExtraType is not null)
{
result.ExtraFiles.Add(files.Find(f => string.Equals(f.FullName, path, StringComparison.OrdinalIgnoreCase)));
continue;
@@ -313,13 +314,8 @@ namespace Emby.Server.Implementations.Library.Resolvers.Movies
return result;
}
- private static bool IsIgnored(string filename)
- {
- // Ignore samples
- Match m = Regex.Match(filename, @"\bsample\b", RegexOptions.IgnoreCase | RegexOptions.Compiled);
-
- return m.Success;
- }
+ private static bool IsIgnored(ReadOnlySpan<char> filename)
+ => Regex.IsMatch(filename, @"\bsample\b", RegexOptions.IgnoreCase | RegexOptions.Compiled);
private static bool ContainsFile(IReadOnlyList<VideoInfo> result, FileSystemMetadata file)
{
@@ -376,7 +372,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.Movies
if (!justName.IsEmpty)
{
- // check for tmdb id
+ // Check for TMDb id
var tmdbid = justName.GetAttributeValue("tmdbid");
if (!string.IsNullOrWhiteSpace(tmdbid))
@@ -387,7 +383,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.Movies
if (!string.IsNullOrEmpty(item.Path))
{
- // check for imdb id - we use full media path, as we can assume, that this will match in any use case (either id in parent dir or in file name)
+ // Check for IMDb id - we use full media path, as we can assume that this will match in any use case (whether id in parent dir or in file name)
var imdbid = item.Path.AsSpan().GetAttributeValue("imdbid");
if (!string.IsNullOrWhiteSpace(imdbid))
@@ -529,7 +525,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.Movies
}
return false;
- }).OrderBy(i => i).ToList();
+ }).Order().ToList();
// If different video types were found, don't allow this
if (videoTypes.Distinct().Count() > 1)
@@ -568,7 +564,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.Movies
private bool IsInvalid(Folder parent, ReadOnlySpan<char> collectionType)
{
- if (parent != null)
+ if (parent is not null)
{
if (parent.IsRoot)
{
diff --git a/Emby.Server.Implementations/Library/Resolvers/PhotoResolver.cs b/Emby.Server.Implementations/Library/Resolvers/PhotoResolver.cs
index e11fb262e..9026160ff 100644
--- a/Emby.Server.Implementations/Library/Resolvers/PhotoResolver.cs
+++ b/Emby.Server.Implementations/Library/Resolvers/PhotoResolver.cs
@@ -1,7 +1,5 @@
#nullable disable
-#pragma warning disable CS1591
-
using System;
using System.Collections.Generic;
using System.IO;
@@ -12,15 +10,20 @@ using Jellyfin.Extensions;
using MediaBrowser.Controller.Drawing;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Library;
+using MediaBrowser.Controller.Providers;
using MediaBrowser.Controller.Resolvers;
using MediaBrowser.Model.Entities;
namespace Emby.Server.Implementations.Library.Resolvers
{
+ /// <summary>
+ /// Class PhotoResolver.
+ /// </summary>
public class PhotoResolver : ItemResolver<Photo>
{
private readonly IImageProcessor _imageProcessor;
private readonly NamingOptions _namingOptions;
+ private readonly IDirectoryService _directoryService;
private static readonly HashSet<string> _ignoreFiles = new HashSet<string>(StringComparer.OrdinalIgnoreCase)
{
@@ -35,10 +38,17 @@ namespace Emby.Server.Implementations.Library.Resolvers
"default"
};
- public PhotoResolver(IImageProcessor imageProcessor, NamingOptions namingOptions)
+ /// <summary>
+ /// Initializes a new instance of the <see cref="PhotoResolver"/> class.
+ /// </summary>
+ /// <param name="imageProcessor">The image processor.</param>
+ /// <param name="namingOptions">The naming options.</param>
+ /// <param name="directoryService">The directory service.</param>
+ public PhotoResolver(IImageProcessor imageProcessor, NamingOptions namingOptions, IDirectoryService directoryService)
{
_imageProcessor = imageProcessor;
_namingOptions = namingOptions;
+ _directoryService = directoryService;
}
/// <summary>
@@ -61,7 +71,7 @@ namespace Emby.Server.Implementations.Library.Resolvers
var filename = Path.GetFileNameWithoutExtension(args.Path);
// Make sure the image doesn't belong to a video file
- var files = args.DirectoryService.GetFiles(Path.GetDirectoryName(args.Path));
+ var files = _directoryService.GetFiles(Path.GetDirectoryName(args.Path));
foreach (var file in files)
{
diff --git a/Emby.Server.Implementations/Library/Resolvers/PlaylistResolver.cs b/Emby.Server.Implementations/Library/Resolvers/PlaylistResolver.cs
index 6b0dfe986..5d569009d 100644
--- a/Emby.Server.Implementations/Library/Resolvers/PlaylistResolver.cs
+++ b/Emby.Server.Implementations/Library/Resolvers/PlaylistResolver.cs
@@ -30,17 +30,20 @@ namespace Emby.Server.Implementations.Library.Resolvers
{
if (args.IsDirectory)
{
- // It's a boxset if the path is a directory with [playlist] in it's the name
- // TODO: Should this use Path.GetDirectoryName() instead?
- bool isBoxSet = Path.GetFileName(args.Path)
- ?.Contains("[playlist]", StringComparison.OrdinalIgnoreCase)
- ?? false;
- if (isBoxSet)
+ // It's a boxset if the path is a directory with [playlist] in its name
+ var filename = Path.GetFileName(Path.TrimEndingDirectorySeparator(args.Path));
+ if (string.IsNullOrEmpty(filename))
+ {
+ return null;
+ }
+
+ if (filename.Contains("[playlist]", StringComparison.OrdinalIgnoreCase))
{
return new Playlist
{
Path = args.Path,
- Name = Path.GetFileName(args.Path).Replace("[playlist]", string.Empty, StringComparison.OrdinalIgnoreCase).Trim()
+ Name = filename.Replace("[playlist]", string.Empty, StringComparison.OrdinalIgnoreCase).Trim(),
+ OpenAccess = true
};
}
@@ -51,7 +54,8 @@ namespace Emby.Server.Implementations.Library.Resolvers
return new Playlist
{
Path = args.Path,
- Name = Path.GetFileName(args.Path)
+ Name = filename,
+ OpenAccess = true
};
}
}
@@ -60,15 +64,16 @@ namespace Emby.Server.Implementations.Library.Resolvers
// It should have the correct collection type and a supported file extension
else if (_musicPlaylistCollectionTypes.Contains(args.CollectionType ?? string.Empty, StringComparison.OrdinalIgnoreCase))
{
- var extension = Path.GetExtension(args.Path);
- if (Playlist.SupportedExtensions.Contains(extension ?? string.Empty, StringComparison.OrdinalIgnoreCase))
+ var extension = Path.GetExtension(args.Path.AsSpan());
+ if (Playlist.SupportedExtensions.Contains(extension, StringComparison.OrdinalIgnoreCase))
{
return new Playlist
{
Path = args.Path,
Name = Path.GetFileNameWithoutExtension(args.Path),
IsInMixedFolder = true,
- PlaylistMediaType = MediaType.Audio
+ PlaylistMediaType = MediaType.Audio,
+ OpenAccess = true
};
}
}
diff --git a/Emby.Server.Implementations/Library/Resolvers/TV/EpisodeResolver.cs b/Emby.Server.Implementations/Library/Resolvers/TV/EpisodeResolver.cs
index 9ba079edf..392ee4c77 100644
--- a/Emby.Server.Implementations/Library/Resolvers/TV/EpisodeResolver.cs
+++ b/Emby.Server.Implementations/Library/Resolvers/TV/EpisodeResolver.cs
@@ -5,6 +5,7 @@ using System.Linq;
using Emby.Naming.Common;
using MediaBrowser.Controller.Entities.TV;
using MediaBrowser.Controller.Library;
+using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Entities;
using Microsoft.Extensions.Logging;
@@ -20,8 +21,9 @@ namespace Emby.Server.Implementations.Library.Resolvers.TV
/// </summary>
/// <param name="logger">The logger.</param>
/// <param name="namingOptions">The naming options.</param>
- public EpisodeResolver(ILogger<EpisodeResolver> logger, NamingOptions namingOptions)
- : base(logger, namingOptions)
+ /// <param name="directoryService">The directory service.</param>
+ public EpisodeResolver(ILogger<EpisodeResolver> logger, NamingOptions namingOptions, IDirectoryService directoryService)
+ : base(logger, namingOptions, directoryService)
{
}
@@ -34,7 +36,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.TV
{
var parent = args.Parent;
- if (parent == null)
+ if (parent is null)
{
return null;
}
@@ -46,34 +48,34 @@ namespace Emby.Server.Implementations.Library.Resolvers.TV
// If the parent is a Season or Series and the parent is not an extras folder, then this is an Episode if the VideoResolver returns something
// Also handle flat tv folders
- if (season != null ||
+ if (season is not null ||
string.Equals(args.GetCollectionType(), CollectionType.TvShows, StringComparison.OrdinalIgnoreCase) ||
args.HasParent<Series>())
{
var episode = ResolveVideo<Episode>(args, false);
// Ignore extras
- if (episode == null || episode.ExtraType != null)
+ if (episode is null || episode.ExtraType is not null)
{
return null;
}
var series = parent as Series ?? parent.GetParents().OfType<Series>().FirstOrDefault();
- if (series != null)
+ if (series is not null)
{
episode.SeriesId = series.Id;
episode.SeriesName = series.Name;
}
- if (season != null)
+ if (season is not null)
{
episode.SeasonId = season.Id;
episode.SeasonName = season.Name;
}
// Assume season 1 if there's no season folder and a season number could not be determined
- if (season == null && !episode.ParentIndexNumber.HasValue && (episode.IndexNumber.HasValue || episode.PremiereDate.HasValue))
+ if (season is null && !episode.ParentIndexNumber.HasValue && (episode.IndexNumber.HasValue || episode.PremiereDate.HasValue))
{
episode.ParentIndexNumber = 1;
}
diff --git a/Emby.Server.Implementations/Library/Resolvers/TV/SeasonResolver.cs b/Emby.Server.Implementations/Library/Resolvers/TV/SeasonResolver.cs
index ea4851458..e9538a5c9 100644
--- a/Emby.Server.Implementations/Library/Resolvers/TV/SeasonResolver.cs
+++ b/Emby.Server.Implementations/Library/Resolvers/TV/SeasonResolver.cs
@@ -66,7 +66,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.TV
var episodeInfo = resolver.Resolve(testPath, true);
- if (episodeInfo?.EpisodeNumber != null && episodeInfo.SeasonNumber.HasValue)
+ if (episodeInfo?.EpisodeNumber is not null && episodeInfo.SeasonNumber.HasValue)
{
_logger.LogDebug(
"Found folder underneath series with episode number: {0}. Season {1}. Episode {2}",
@@ -81,14 +81,24 @@ namespace Emby.Server.Implementations.Library.Resolvers.TV
if (season.IndexNumber.HasValue)
{
var seasonNumber = season.IndexNumber.Value;
-
- season.Name = seasonNumber == 0 ?
- args.LibraryOptions.SeasonZeroDisplayName :
- string.Format(
- CultureInfo.InvariantCulture,
- _localization.GetLocalizedString("NameSeasonNumber"),
- seasonNumber,
- args.LibraryOptions.PreferredMetadataLanguage);
+ if (string.IsNullOrEmpty(season.Name))
+ {
+ var seasonNames = series.SeasonNames;
+ if (seasonNames.TryGetValue(seasonNumber, out var seasonName))
+ {
+ season.Name = seasonName;
+ }
+ else
+ {
+ season.Name = seasonNumber == 0 ?
+ args.LibraryOptions.SeasonZeroDisplayName :
+ string.Format(
+ CultureInfo.InvariantCulture,
+ _localization.GetLocalizedString("NameSeasonNumber"),
+ seasonNumber,
+ args.LibraryOptions.PreferredMetadataLanguage);
+ }
+ }
}
return season;
diff --git a/Emby.Server.Implementations/Library/Resolvers/TV/SeriesResolver.cs b/Emby.Server.Implementations/Library/Resolvers/TV/SeriesResolver.cs
index f5ac3c665..d4f275bed 100644
--- a/Emby.Server.Implementations/Library/Resolvers/TV/SeriesResolver.cs
+++ b/Emby.Server.Implementations/Library/Resolvers/TV/SeriesResolver.cs
@@ -76,7 +76,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.TV
{
if (args.ContainsFileSystemEntryByName("tvshow.nfo"))
{
- if (args.Parent != null && args.Parent.IsRoot)
+ if (args.Parent is not null && args.Parent.IsRoot)
{
// For now, return null, but if we want to allow this in the future then add some additional checks to guard against a misplaced tvshow.nfo
return null;
@@ -89,7 +89,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.TV
};
}
- if (args.Parent != null && args.Parent.IsRoot)
+ if (args.Parent is not null && args.Parent.IsRoot)
{
return null;
}
@@ -138,7 +138,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.TV
var episodeResolver = new Naming.TV.EpisodeResolver(namingOptions);
var episodeInfo = episodeResolver.Resolve(fullName, false, true, false, fillExtendedInfo: false);
- if (episodeInfo != null && episodeInfo.EpisodeNumber.HasValue)
+ if (episodeInfo is not null && episodeInfo.EpisodeNumber.HasValue)
{
return true;
}
@@ -184,6 +184,12 @@ namespace Emby.Server.Implementations.Library.Resolvers.TV
{
var justName = Path.GetFileName(path.AsSpan());
+ var imdbId = justName.GetAttributeValue("imdbid");
+ if (!string.IsNullOrEmpty(imdbId))
+ {
+ item.SetProviderId(MetadataProvider.Imdb, imdbId);
+ }
+
var tvdbId = justName.GetAttributeValue("tvdbid");
if (!string.IsNullOrEmpty(tvdbId))
{
diff --git a/Emby.Server.Implementations/Library/SearchEngine.cs b/Emby.Server.Implementations/Library/SearchEngine.cs
index 60778a443..b916b9170 100644
--- a/Emby.Server.Implementations/Library/SearchEngine.cs
+++ b/Emby.Server.Implementations/Library/SearchEngine.cs
@@ -73,10 +73,7 @@ namespace Emby.Server.Implementations.Library
{
var searchTerm = query.SearchTerm;
- if (string.IsNullOrEmpty(searchTerm))
- {
- throw new ArgumentException("SearchTerm can't be empty.", nameof(query));
- }
+ ArgumentException.ThrowIfNullOrEmpty(searchTerm);
searchTerm = searchTerm.Trim().RemoveDiacritics();
diff --git a/Emby.Server.Implementations/Library/UserDataManager.cs b/Emby.Server.Implementations/Library/UserDataManager.cs
index aecab7d9c..a0a90b129 100644
--- a/Emby.Server.Implementations/Library/UserDataManager.cs
+++ b/Emby.Server.Implementations/Library/UserDataManager.cs
@@ -126,7 +126,7 @@ namespace Emby.Server.Implementations.Library
{
var userData = _repository.GetUserData(internalUserId, keys);
- if (userData != null)
+ if (userData is not null)
{
return userData;
}
diff --git a/Emby.Server.Implementations/Library/UserViewManager.cs b/Emby.Server.Implementations/Library/UserViewManager.cs
index ec411aa3b..2c3dc1857 100644
--- a/Emby.Server.Implementations/Library/UserViewManager.cs
+++ b/Emby.Server.Implementations/Library/UserViewManager.cs
@@ -46,10 +46,9 @@ namespace Emby.Server.Implementations.Library
public Folder[] GetUserViews(UserViewQuery query)
{
var user = _userManager.GetUserById(query.UserId);
-
- if (user == null)
+ if (user is null)
{
- throw new ArgumentException("User Id specified in the query does not exist.", nameof(query));
+ throw new ArgumentException("User id specified in the query does not exist.", nameof(query));
}
var folders = _libraryManager.GetUserRootFolder()
@@ -58,7 +57,6 @@ namespace Emby.Server.Implementations.Library
.ToList();
var groupedFolders = new List<ICollectionFolder>();
-
var list = new List<Folder>();
foreach (var folder in folders)
@@ -66,13 +64,27 @@ namespace Emby.Server.Implementations.Library
var collectionFolder = folder as ICollectionFolder;
var folderViewType = collectionFolder?.CollectionType;
+ // Playlist library requires special handling because the folder only refrences user playlists
+ if (string.Equals(folderViewType, CollectionType.Playlists, StringComparison.OrdinalIgnoreCase))
+ {
+ var items = folder.GetItemList(new InternalItemsQuery(user)
+ {
+ ParentId = folder.ParentId
+ });
+
+ if (!items.Any(item => item.IsVisible(user)))
+ {
+ continue;
+ }
+ }
+
if (UserView.IsUserSpecific(folder))
{
list.Add(_libraryManager.GetNamedView(user, folder.Name, folder.Id, folderViewType, null));
continue;
}
- if (collectionFolder != null && UserView.IsEligibleForGrouping(folder) && user.IsFolderGrouped(folder.Id))
+ if (collectionFolder is not null && UserView.IsEligibleForGrouping(folder) && user.IsFolderGrouped(folder.Id))
{
groupedFolders.Add(collectionFolder);
continue;
@@ -111,10 +123,10 @@ namespace Emby.Server.Implementations.Library
if (query.IncludeExternalContent)
{
- var channelResult = _channelManager.GetChannelsInternal(new ChannelQuery
+ var channelResult = _channelManager.GetChannelsInternalAsync(new ChannelQuery
{
UserId = query.UserId
- });
+ }).GetAwaiter().GetResult();
var channels = channelResult.Items;
@@ -132,14 +144,12 @@ namespace Emby.Server.Implementations.Library
}
var sorted = _libraryManager.Sort(list, user, new[] { ItemSortBy.SortName }, SortOrder.Ascending).ToList();
-
var orders = user.GetPreferenceValues<Guid>(PreferenceKind.OrderedViews);
return list
.OrderBy(i =>
{
var index = Array.IndexOf(orders, i.Id);
-
if (index == -1
&& i is UserView view
&& !view.DisplayParentId.Equals(default))
@@ -208,15 +218,15 @@ namespace Emby.Server.Implementations.Library
// Only grab the index container for media
var container = item.IsFolder || !request.GroupItems ? null : item.LatestItemsIndexContainer;
- if (container == null)
+ if (container is null)
{
list.Add(new Tuple<BaseItem, List<BaseItem>>(null, new List<BaseItem> { item }));
}
else
{
- var current = list.FirstOrDefault(i => i.Item1 != null && i.Item1.Id.Equals(container.Id));
+ var current = list.FirstOrDefault(i => i.Item1 is not null && i.Item1.Id.Equals(container.Id));
- if (current != null)
+ if (current is not null)
{
current.Item2.Add(item);
}
@@ -286,7 +296,7 @@ namespace Emby.Server.Implementations.Library
if (parents.Count == 0)
{
- return new List<BaseItem>();
+ return Array.Empty<BaseItem>();
}
if (includeItemTypes.Length == 0)
diff --git a/Emby.Server.Implementations/Library/Validators/CollectionPostScanTask.cs b/Emby.Server.Implementations/Library/Validators/CollectionPostScanTask.cs
index 88b93a211..df45793c3 100644
--- a/Emby.Server.Implementations/Library/Validators/CollectionPostScanTask.cs
+++ b/Emby.Server.Implementations/Library/Validators/CollectionPostScanTask.cs
@@ -118,7 +118,7 @@ namespace Emby.Server.Implementations.Library.Validators
try
{
var boxSet = boxSets.FirstOrDefault(b => b?.Name == collectionName) as BoxSet;
- if (boxSet == null)
+ if (boxSet is null)
{
// won't automatically create collection if only one movie in it
if (movieIds.Count >= 2)