aboutsummaryrefslogtreecommitdiff
path: root/Emby.Server.Implementations/Library/LibraryManager.cs
diff options
context:
space:
mode:
Diffstat (limited to 'Emby.Server.Implementations/Library/LibraryManager.cs')
-rw-r--r--Emby.Server.Implementations/Library/LibraryManager.cs349
1 files changed, 198 insertions, 151 deletions
diff --git a/Emby.Server.Implementations/Library/LibraryManager.cs b/Emby.Server.Implementations/Library/LibraryManager.cs
index 013781258..8054beae3 100644
--- a/Emby.Server.Implementations/Library/LibraryManager.cs
+++ b/Emby.Server.Implementations/Library/LibraryManager.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
#pragma warning disable CS1591
using System;
@@ -19,6 +21,7 @@ using Emby.Server.Implementations.Playlists;
using Emby.Server.Implementations.ScheduledTasks;
using Jellyfin.Data.Entities;
using Jellyfin.Data.Enums;
+using Jellyfin.Extensions;
using MediaBrowser.Common.Extensions;
using MediaBrowser.Common.Progress;
using MediaBrowser.Controller;
@@ -42,13 +45,13 @@ using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.IO;
using MediaBrowser.Model.Library;
-using MediaBrowser.Model.Net;
using MediaBrowser.Model.Querying;
using MediaBrowser.Model.Tasks;
using MediaBrowser.Providers.MediaInfo;
using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.Logging;
using Episode = MediaBrowser.Controller.Entities.TV.Episode;
+using EpisodeInfo = Emby.Naming.TV.EpisodeInfo;
using Genre = MediaBrowser.Controller.Entities.Genre;
using Person = MediaBrowser.Controller.Entities.Person;
using VideoResolver = Emby.Naming.Video.VideoResolver;
@@ -176,10 +179,7 @@ namespace Emby.Server.Implementations.Library
{
lock (_rootFolderSyncLock)
{
- if (_rootFolder == null)
- {
- _rootFolder = CreateRootFolder();
- }
+ _rootFolder ??= CreateRootFolder();
}
}
@@ -197,33 +197,33 @@ namespace Emby.Server.Implementations.Library
/// Gets or sets the postscan tasks.
/// </summary>
/// <value>The postscan tasks.</value>
- private ILibraryPostScanTask[] PostscanTasks { get; set; }
+ private ILibraryPostScanTask[] PostscanTasks { get; set; } = Array.Empty<ILibraryPostScanTask>();
/// <summary>
/// Gets or sets the intro providers.
/// </summary>
/// <value>The intro providers.</value>
- private IIntroProvider[] IntroProviders { get; set; }
+ private IIntroProvider[] IntroProviders { get; set; } = Array.Empty<IIntroProvider>();
/// <summary>
/// Gets or sets the list of entity resolution ignore rules.
/// </summary>
/// <value>The entity resolution ignore rules.</value>
- private IResolverIgnoreRule[] EntityResolutionIgnoreRules { get; set; }
+ private IResolverIgnoreRule[] EntityResolutionIgnoreRules { get; set; } = Array.Empty<IResolverIgnoreRule>();
/// <summary>
/// Gets or sets the list of currently registered entity resolvers.
/// </summary>
/// <value>The entity resolvers enumerable.</value>
- private IItemResolver[] EntityResolvers { get; set; }
+ private IItemResolver[] EntityResolvers { get; set; } = Array.Empty<IItemResolver>();
- private IMultiItemResolver[] MultiItemResolvers { get; set; }
+ private IMultiItemResolver[] MultiItemResolvers { get; set; } = Array.Empty<IMultiItemResolver>();
/// <summary>
/// Gets or sets the comparers.
/// </summary>
/// <value>The comparers.</value>
- private IBaseItemComparer[] Comparers { get; set; }
+ private IBaseItemComparer[] Comparers { get; set; } = Array.Empty<IBaseItemComparer>();
public bool IsScanRunning { get; private set; }
@@ -287,14 +287,14 @@ namespace Emby.Server.Implementations.Library
if (item is IItemByName)
{
- if (!(item is MusicArtist))
+ if (item is not MusicArtist)
{
return;
}
}
else if (!item.IsFolder)
{
- if (!(item is Video) && !(item is LiveTvChannel))
+ if (item is not Video && item is not LiveTvChannel)
{
return;
}
@@ -559,7 +559,6 @@ namespace Emby.Server.Implementations.Library
var args = new ItemResolveArgs(_configurationManager.ApplicationPaths, directoryService)
{
Parent = parent,
- Path = fullPath,
FileInfo = fileInfo,
CollectionType = collectionType,
LibraryOptions = libraryOptions
@@ -685,7 +684,7 @@ namespace Emby.Server.Implementations.Library
foreach (var item in items)
{
- ResolverHelper.SetInitialItemValues(item, parent, _fileSystem, this, directoryService);
+ ResolverHelper.SetInitialItemValues(item, parent, this, directoryService);
}
items.AddRange(ResolveFileList(result.ExtraFiles, directoryService, parent, collectionType, resolvers, libraryOptions));
@@ -698,25 +697,32 @@ namespace Emby.Server.Implementations.Library
}
private IEnumerable<BaseItem> ResolveFileList(
- IEnumerable<FileSystemMetadata> fileList,
+ IReadOnlyList<FileSystemMetadata> fileList,
IDirectoryService directoryService,
Folder parent,
string collectionType,
IItemResolver[] resolvers,
LibraryOptions libraryOptions)
{
- return fileList.Select(f =>
+ // Given that fileList is a list we can save enumerator allocations by indexing
+ for (var i = 0; i < fileList.Count; i++)
{
+ var file = fileList[i];
+ BaseItem result = null;
try
{
- return ResolvePath(f, directoryService, resolvers, parent, collectionType, libraryOptions);
+ result = ResolvePath(file, directoryService, resolvers, parent, collectionType, libraryOptions);
}
catch (Exception ex)
{
- _logger.LogError(ex, "Error resolving path {path}", f.FullName);
- return null;
+ _logger.LogError(ex, "Error resolving path {Path}", file.FullName);
}
- }).Where(i => i != null);
+
+ if (result != null)
+ {
+ yield return result;
+ }
+ }
}
/// <summary>
@@ -860,7 +866,7 @@ namespace Emby.Server.Implementations.Library
{
var path = Person.GetPath(name);
var id = GetItemByNameId<Person>(path);
- if (!(GetItemById(id) is Person item))
+ if (GetItemById(id) is not Person item)
{
item = new Person
{
@@ -1067,17 +1073,17 @@ namespace Emby.Server.Implementations.Library
// Start by just validating the children of the root, but go no further
await RootFolder.ValidateChildren(
new SimpleProgress<double>(),
- cancellationToken,
new MetadataRefreshOptions(new DirectoryService(_fileSystem)),
- recursive: false).ConfigureAwait(false);
+ recursive: false,
+ cancellationToken).ConfigureAwait(false);
await GetUserRootFolder().RefreshMetadata(cancellationToken).ConfigureAwait(false);
await GetUserRootFolder().ValidateChildren(
new SimpleProgress<double>(),
- cancellationToken,
new MetadataRefreshOptions(new DirectoryService(_fileSystem)),
- recursive: false).ConfigureAwait(false);
+ recursive: false,
+ cancellationToken).ConfigureAwait(false);
// Quickly scan CollectionFolders for changes
foreach (var folder in GetUserRootFolder().Children.OfType<Folder>())
@@ -1097,7 +1103,7 @@ namespace Emby.Server.Implementations.Library
innerProgress.RegisterAction(pct => progress.Report(pct * 0.96));
// Validate the entire media library
- await RootFolder.ValidateChildren(innerProgress, cancellationToken, new MetadataRefreshOptions(new DirectoryService(_fileSystem)), recursive: true).ConfigureAwait(false);
+ await RootFolder.ValidateChildren(innerProgress, new MetadataRefreshOptions(new DirectoryService(_fileSystem)), recursive: true, cancellationToken).ConfigureAwait(false);
progress.Report(96);
@@ -1164,7 +1170,7 @@ namespace Emby.Server.Implementations.Library
progress.Report(percent * 100);
}
- _itemRepository.UpdateInheritedValues(cancellationToken);
+ _itemRepository.UpdateInheritedValues();
progress.Report(100);
}
@@ -1241,11 +1247,20 @@ namespace Emby.Server.Implementations.Library
return info;
}
- private string GetCollectionType(string path)
+ private CollectionTypeOptions? GetCollectionType(string path)
{
- return _fileSystem.GetFilePaths(path, new[] { ".collection" }, true, false)
- .Select(Path.GetFileNameWithoutExtension)
- .FirstOrDefault(i => !string.IsNullOrEmpty(i));
+ var files = _fileSystem.GetFilePaths(path, new[] { ".collection" }, true, false);
+ foreach (var file in files)
+ {
+ // TODO: @bond use a ReadOnlySpan<char> here when Enum.TryParse supports it
+ // https://github.com/dotnet/runtime/issues/20008
+ if (Enum.TryParse<CollectionTypeOptions>(Path.GetFileNameWithoutExtension(file), true, out var res))
+ {
+ return res;
+ }
+ }
+
+ return null;
}
/// <summary>
@@ -1746,22 +1761,20 @@ namespace Emby.Server.Implementations.Library
return orderedItems ?? items;
}
- public IEnumerable<BaseItem> Sort(IEnumerable<BaseItem> items, User user, IEnumerable<ValueTuple<string, SortOrder>> orderByList)
+ public IEnumerable<BaseItem> Sort(IEnumerable<BaseItem> items, User user, IEnumerable<ValueTuple<string, SortOrder>> orderBy)
{
var isFirst = true;
IOrderedEnumerable<BaseItem> orderedItems = null;
- foreach (var orderBy in orderByList)
+ foreach (var (name, sortOrder) in orderBy)
{
- var comparer = GetComparer(orderBy.Item1, user);
+ var comparer = GetComparer(name, user);
if (comparer == null)
{
continue;
}
- var sortOrder = orderBy.Item2;
-
if (isFirst)
{
orderedItems = sortOrder == SortOrder.Descending ? items.OrderByDescending(i => i, comparer) : items.OrderBy(i => i, comparer);
@@ -1906,12 +1919,17 @@ namespace Emby.Server.Implementations.Library
}
catch (ArgumentException)
{
- _logger.LogWarning("Cannot get image index for {0}", img.Path);
+ _logger.LogWarning("Cannot get image index for {ImagePath}", img.Path);
continue;
}
- catch (InvalidOperationException)
+ catch (Exception ex) when (ex is InvalidOperationException || ex is IOException)
{
- _logger.LogWarning("Cannot fetch image from {0}", img.Path);
+ _logger.LogWarning(ex, "Cannot fetch image from {ImagePath}", img.Path);
+ continue;
+ }
+ catch (HttpRequestException ex)
+ {
+ _logger.LogWarning(ex, "Cannot fetch image from {ImagePath}. Http status code: {HttpStatus}", img.Path, ex.StatusCode);
continue;
}
}
@@ -1924,7 +1942,7 @@ namespace Emby.Server.Implementations.Library
}
catch (Exception ex)
{
- _logger.LogError(ex, "Cannot get image dimensions for {0}", image.Path);
+ _logger.LogError(ex, "Cannot get image dimensions for {ImagePath}", image.Path);
image.Width = 0;
image.Height = 0;
continue;
@@ -1936,7 +1954,7 @@ namespace Emby.Server.Implementations.Library
}
catch (Exception ex)
{
- _logger.LogError(ex, "Cannot compute blurhash for {0}", image.Path);
+ _logger.LogError(ex, "Cannot compute blurhash for {ImagePath}", image.Path);
image.BlurHash = string.Empty;
}
@@ -1946,7 +1964,7 @@ namespace Emby.Server.Implementations.Library
}
catch (Exception ex)
{
- _logger.LogError(ex, "Cannot update DateModified for {0}", image.Path);
+ _logger.LogError(ex, "Cannot update DateModified for {ImagePath}", image.Path);
}
}
@@ -1955,9 +1973,12 @@ namespace Emby.Server.Implementations.Library
}
/// <inheritdoc />
- public Task UpdateItemsAsync(IReadOnlyList<BaseItem> items, BaseItem parent, ItemUpdateType updateReason, CancellationToken cancellationToken)
+ public async Task UpdateItemsAsync(IReadOnlyList<BaseItem> items, BaseItem parent, ItemUpdateType updateReason, CancellationToken cancellationToken)
{
- RunMetadataSavers(items, updateReason);
+ foreach (var item in items)
+ {
+ await RunMetadataSavers(item, updateReason).ConfigureAwait(false);
+ }
_itemRepository.SaveItems(items, cancellationToken);
@@ -1988,25 +2009,22 @@ namespace Emby.Server.Implementations.Library
}
}
}
-
- return Task.CompletedTask;
}
/// <inheritdoc />
public Task UpdateItemAsync(BaseItem item, BaseItem parent, ItemUpdateType updateReason, CancellationToken cancellationToken)
=> UpdateItemsAsync(new[] { item }, parent, updateReason, cancellationToken);
- public void RunMetadataSavers(IReadOnlyList<BaseItem> items, ItemUpdateType updateReason)
+ public Task RunMetadataSavers(BaseItem item, ItemUpdateType updateReason)
{
- foreach (var item in items)
+ if (item.IsFileProtocol)
{
- if (item.IsFileProtocol)
- {
- ProviderManager.SaveMetadata(item, updateReason);
- }
-
- item.DateLastSaved = DateTime.UtcNow;
+ ProviderManager.SaveMetadata(item, updateReason);
}
+
+ item.DateLastSaved = DateTime.UtcNow;
+
+ return UpdateImagesAsync(item, updateReason >= ItemUpdateType.ImageUpdate);
}
/// <summary>
@@ -2064,7 +2082,7 @@ namespace Emby.Server.Implementations.Library
return new List<Folder>();
}
- return GetCollectionFoldersInternal(item, GetUserRootFolder().Children.OfType<Folder>().ToList());
+ return GetCollectionFoldersInternal(item, GetUserRootFolder().Children.OfType<Folder>());
}
public List<Folder> GetCollectionFolders(BaseItem item, List<Folder> allUserRootChildren)
@@ -2089,20 +2107,20 @@ namespace Emby.Server.Implementations.Library
return GetCollectionFoldersInternal(item, allUserRootChildren);
}
- private static List<Folder> GetCollectionFoldersInternal(BaseItem item, List<Folder> allUserRootChildren)
+ private static List<Folder> GetCollectionFoldersInternal(BaseItem item, IEnumerable<Folder> allUserRootChildren)
{
return allUserRootChildren
- .Where(i => string.Equals(i.Path, item.Path, StringComparison.OrdinalIgnoreCase) || i.PhysicalLocations.Contains(item.Path, StringComparer.OrdinalIgnoreCase))
+ .Where(i => string.Equals(i.Path, item.Path, StringComparison.OrdinalIgnoreCase) || i.PhysicalLocations.Contains(item.Path.AsSpan(), StringComparison.OrdinalIgnoreCase))
.ToList();
}
public LibraryOptions GetLibraryOptions(BaseItem item)
{
- if (!(item is CollectionFolder collectionFolder))
+ if (item is not CollectionFolder collectionFolder)
{
+ // List.Find is more performant than FirstOrDefault due to enumerator allocation
collectionFolder = GetCollectionFolders(item)
- .OfType<CollectionFolder>()
- .FirstOrDefault();
+ .Find(folder => folder is CollectionFolder) as CollectionFolder;
}
return collectionFolder == null ? new LibraryOptions() : collectionFolder.GetLibraryOptions();
@@ -2462,9 +2480,19 @@ namespace Emby.Server.Implementations.Library
public BaseItem GetParentItem(string parentId, Guid? userId)
{
- if (!string.IsNullOrEmpty(parentId))
+ if (string.IsNullOrEmpty(parentId))
{
- return GetItemById(new Guid(parentId));
+ return GetParentItem((Guid?)null, userId);
+ }
+
+ return GetParentItem(new Guid(parentId), userId);
+ }
+
+ public BaseItem GetParentItem(Guid? parentId, Guid? userId)
+ {
+ if (parentId.HasValue)
+ {
+ return GetItemById(parentId.Value);
}
if (userId.HasValue && userId != Guid.Empty)
@@ -2478,8 +2506,7 @@ namespace Emby.Server.Implementations.Library
/// <inheritdoc />
public bool IsVideoFile(string path)
{
- var resolver = new VideoResolver(GetNamingOptions());
- return resolver.IsVideoFile(path);
+ return VideoResolver.IsVideoFile(path, GetNamingOptions());
}
/// <inheritdoc />
@@ -2494,7 +2521,7 @@ namespace Emby.Server.Implementations.Library
public bool FillMissingEpisodeNumbersFromPath(Episode episode, bool forceRefresh)
{
var series = episode.Series;
- bool? isAbsoluteNaming = series == null ? false : string.Equals(series.DisplayOrder, "absolute", StringComparison.OrdinalIgnoreCase);
+ bool? isAbsoluteNaming = series != null && string.Equals(series.DisplayOrder, "absolute", StringComparison.OrdinalIgnoreCase);
if (!isAbsoluteNaming.Value)
{
// In other words, no filter applied
@@ -2506,9 +2533,24 @@ namespace Emby.Server.Implementations.Library
var isFolder = episode.VideoType == VideoType.BluRay || episode.VideoType == VideoType.Dvd;
// TODO nullable - what are we trying to do there with empty episodeInfo?
- var episodeInfo = episode.IsFileProtocol
- ? resolver.Resolve(episode.Path, isFolder, null, null, isAbsoluteNaming) ?? new Naming.TV.EpisodeInfo(episode.Path)
- : new Naming.TV.EpisodeInfo(episode.Path);
+ EpisodeInfo episodeInfo = null;
+ if (episode.IsFileProtocol)
+ {
+ 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))
+ {
+ episodeInfo = resolver.Resolve(parent.Path, true, null, null, isAbsoluteNaming);
+ if (episodeInfo != null)
+ {
+ // add the container
+ episodeInfo.Container = Path.GetExtension(episode.Path)?.TrimStart('.');
+ }
+ }
+ }
+
+ episodeInfo ??= new EpisodeInfo(episode.Path);
try
{
@@ -2643,6 +2685,7 @@ namespace Emby.Server.Implementations.Library
return changed;
}
+ /// <inheritdoc />
public NamingOptions GetNamingOptions()
{
if (_namingOptions == null)
@@ -2656,13 +2699,12 @@ namespace Emby.Server.Implementations.Library
public ItemLookupInfo ParseName(string name)
{
- var resolver = new VideoResolver(GetNamingOptions());
-
- var result = resolver.CleanDateTime(name);
+ var namingOptions = GetNamingOptions();
+ var result = VideoResolver.CleanDateTime(name, namingOptions);
return new ItemLookupInfo
{
- Name = resolver.TryCleanString(result.Name, out var newName) ? newName.ToString() : result.Name,
+ Name = VideoResolver.TryCleanString(result.Name, namingOptions, out var newName) ? newName.ToString() : result.Name,
Year = result.Year
};
}
@@ -2676,9 +2718,7 @@ namespace Emby.Server.Implementations.Library
.SelectMany(i => _fileSystem.GetFiles(i.FullName, _videoFileExtensions, false, false))
.ToList();
- var videoListResolver = new VideoListResolver(namingOptions);
-
- var videos = videoListResolver.Resolve(fileSystemChildren);
+ var videos = VideoListResolver.Resolve(fileSystemChildren, namingOptions);
var currentVideo = videos.FirstOrDefault(i => string.Equals(owner.Path, i.Files[0].Path, StringComparison.OrdinalIgnoreCase));
@@ -2722,9 +2762,7 @@ namespace Emby.Server.Implementations.Library
.SelectMany(i => _fileSystem.GetFiles(i.FullName, _videoFileExtensions, false, false))
.ToList();
- var videoListResolver = new VideoListResolver(namingOptions);
-
- var videos = videoListResolver.Resolve(fileSystemChildren);
+ var videos = VideoListResolver.Resolve(fileSystemChildren, namingOptions);
var currentVideo = videos.FirstOrDefault(i => string.Equals(owner.Path, i.Files[0].Path, StringComparison.OrdinalIgnoreCase));
@@ -2758,6 +2796,7 @@ namespace Emby.Server.Implementations.Library
public string GetPathAfterNetworkSubstitution(string path, BaseItem ownerItem)
{
+ string newPath;
if (ownerItem != null)
{
var libraryOptions = GetLibraryOptions(ownerItem);
@@ -2765,15 +2804,9 @@ namespace Emby.Server.Implementations.Library
{
foreach (var pathInfo in libraryOptions.PathInfos)
{
- if (string.IsNullOrWhiteSpace(pathInfo.Path) || string.IsNullOrWhiteSpace(pathInfo.NetworkPath))
- {
- continue;
- }
-
- var substitutionResult = SubstitutePathInternal(path, pathInfo.Path, pathInfo.NetworkPath);
- if (substitutionResult.Item2)
+ if (path.TryReplaceSubPath(pathInfo.Path, pathInfo.NetworkPath, out newPath))
{
- return substitutionResult.Item1;
+ return newPath;
}
}
}
@@ -2782,24 +2815,16 @@ namespace Emby.Server.Implementations.Library
var metadataPath = _configurationManager.Configuration.MetadataPath;
var metadataNetworkPath = _configurationManager.Configuration.MetadataNetworkPath;
- if (!string.IsNullOrWhiteSpace(metadataPath) && !string.IsNullOrWhiteSpace(metadataNetworkPath))
+ if (path.TryReplaceSubPath(metadataPath, metadataNetworkPath, out newPath))
{
- var metadataSubstitutionResult = SubstitutePathInternal(path, metadataPath, metadataNetworkPath);
- if (metadataSubstitutionResult.Item2)
- {
- return metadataSubstitutionResult.Item1;
- }
+ return newPath;
}
foreach (var map in _configurationManager.Configuration.PathSubstitutions)
{
- if (!string.IsNullOrWhiteSpace(map.From))
+ if (path.TryReplaceSubPath(map.From, map.To, out newPath))
{
- var substitutionResult = SubstitutePathInternal(path, map.From, map.To);
- if (substitutionResult.Item2)
- {
- return substitutionResult.Item1;
- }
+ return newPath;
}
}
@@ -2808,47 +2833,12 @@ namespace Emby.Server.Implementations.Library
public string SubstitutePath(string path, string from, string to)
{
- return SubstitutePathInternal(path, from, to).Item1;
- }
-
- private Tuple<string, bool> SubstitutePathInternal(string path, string from, string to)
- {
- if (string.IsNullOrWhiteSpace(path))
- {
- throw new ArgumentNullException(nameof(path));
- }
-
- if (string.IsNullOrWhiteSpace(from))
- {
- throw new ArgumentNullException(nameof(from));
- }
-
- if (string.IsNullOrWhiteSpace(to))
- {
- throw new ArgumentNullException(nameof(to));
- }
-
- from = from.Trim();
- to = to.Trim();
-
- var newPath = path.Replace(from, to, StringComparison.OrdinalIgnoreCase);
- var changed = false;
-
- if (!string.Equals(newPath, path, StringComparison.Ordinal))
+ if (path.TryReplaceSubPath(from, to, out var newPath))
{
- if (to.IndexOf('/', StringComparison.Ordinal) != -1)
- {
- newPath = newPath.Replace('\\', '/');
- }
- else
- {
- newPath = newPath.Replace('/', '\\');
- }
-
- changed = true;
+ return newPath;
}
- return new Tuple<string, bool>(newPath, changed);
+ return path;
}
private void SetExtraTypeFromFilename(Video item)
@@ -2906,12 +2896,20 @@ namespace Emby.Server.Implementations.Library
public void UpdatePeople(BaseItem item, List<PersonInfo> people)
{
+ UpdatePeopleAsync(item, people, CancellationToken.None).GetAwaiter().GetResult();
+ }
+
+ /// <inheritdoc />
+ public async Task UpdatePeopleAsync(BaseItem item, List<PersonInfo> people, CancellationToken cancellationToken)
+ {
if (!item.SupportsPeople)
{
return;
}
_itemRepository.UpdatePeople(item.Id, people);
+
+ await SavePeopleMetadataAsync(people, cancellationToken).ConfigureAwait(false);
}
public async Task<ItemImageInfo> ConvertImageToLocal(BaseItem item, ItemImageInfo image, int imageIndex)
@@ -2947,7 +2945,7 @@ namespace Emby.Server.Implementations.Library
throw new InvalidOperationException();
}
- public async Task AddVirtualFolder(string name, string collectionType, LibraryOptions options, bool refreshLibrary)
+ public async Task AddVirtualFolder(string name, CollectionTypeOptions? collectionType, LibraryOptions options, bool refreshLibrary)
{
if (string.IsNullOrWhiteSpace(name))
{
@@ -2981,9 +2979,9 @@ namespace Emby.Server.Implementations.Library
{
Directory.CreateDirectory(virtualFolderPath);
- if (!string.IsNullOrEmpty(collectionType))
+ if (collectionType != null)
{
- var path = Path.Combine(virtualFolderPath, collectionType + ".collection");
+ var path = Path.Combine(virtualFolderPath, collectionType.ToString().ToLowerInvariant() + ".collection");
File.WriteAllBytes(path, Array.Empty<byte>());
}
@@ -3015,6 +3013,58 @@ namespace Emby.Server.Implementations.Library
}
}
+ private async Task SavePeopleMetadataAsync(IEnumerable<PersonInfo> people, CancellationToken cancellationToken)
+ {
+ var personsToSave = new List<BaseItem>();
+
+ foreach (var person in people)
+ {
+ cancellationToken.ThrowIfCancellationRequested();
+
+ var itemUpdateType = ItemUpdateType.MetadataDownload;
+ var saveEntity = false;
+ var personEntity = GetPerson(person.Name);
+
+ // if PresentationUniqueKey is empty it's likely a new item.
+ if (string.IsNullOrEmpty(personEntity.PresentationUniqueKey))
+ {
+ personEntity.PresentationUniqueKey = personEntity.CreatePresentationUniqueKey();
+ saveEntity = true;
+ }
+
+ foreach (var id in person.ProviderIds)
+ {
+ if (!string.Equals(personEntity.GetProviderId(id.Key), id.Value, StringComparison.OrdinalIgnoreCase))
+ {
+ personEntity.SetProviderId(id.Key, id.Value);
+ saveEntity = true;
+ }
+ }
+
+ if (!string.IsNullOrWhiteSpace(person.ImageUrl) && !personEntity.HasImage(ImageType.Primary))
+ {
+ personEntity.SetImage(
+ new ItemImageInfo
+ {
+ Path = person.ImageUrl,
+ Type = ImageType.Primary
+ },
+ 0);
+
+ saveEntity = true;
+ itemUpdateType = ItemUpdateType.ImageUpdate;
+ }
+
+ if (saveEntity)
+ {
+ personsToSave.Add(personEntity);
+ await RunMetadataSavers(personEntity, itemUpdateType).ConfigureAwait(false);
+ }
+ }
+
+ CreateItems(personsToSave, null, CancellationToken.None);
+ }
+
private void StartScanInBackground()
{
Task.Run(() =>
@@ -3024,9 +3074,9 @@ namespace Emby.Server.Implementations.Library
});
}
- public void AddMediaPath(string virtualFolderName, MediaPathInfo pathInfo)
+ public void AddMediaPath(string virtualFolderName, MediaPathInfo mediaPath)
{
- AddMediaPathInternal(virtualFolderName, pathInfo, true);
+ AddMediaPathInternal(virtualFolderName, mediaPath, true);
}
private void AddMediaPathInternal(string virtualFolderName, MediaPathInfo pathInfo, bool saveLibraryOptions)
@@ -3079,11 +3129,11 @@ namespace Emby.Server.Implementations.Library
}
}
- public void UpdateMediaPath(string virtualFolderName, MediaPathInfo pathInfo)
+ public void UpdateMediaPath(string virtualFolderName, MediaPathInfo mediaPath)
{
- if (pathInfo == null)
+ if (mediaPath == null)
{
- throw new ArgumentNullException(nameof(pathInfo));
+ throw new ArgumentNullException(nameof(mediaPath));
}
var rootFolderPath = _configurationManager.ApplicationPaths.DefaultUserViewsPath;
@@ -3096,9 +3146,9 @@ namespace Emby.Server.Implementations.Library
var list = libraryOptions.PathInfos.ToList();
foreach (var originalPathInfo in list)
{
- if (string.Equals(pathInfo.Path, originalPathInfo.Path, StringComparison.Ordinal))
+ if (string.Equals(mediaPath.Path, originalPathInfo.Path, StringComparison.Ordinal))
{
- originalPathInfo.NetworkPath = pathInfo.NetworkPath;
+ originalPathInfo.NetworkPath = mediaPath.NetworkPath;
break;
}
}
@@ -3121,10 +3171,7 @@ namespace Emby.Server.Implementations.Library
{
if (!list.Any(i => string.Equals(i.Path, location, StringComparison.Ordinal)))
{
- list.Add(new MediaPathInfo
- {
- Path = location
- });
+ list.Add(new MediaPathInfo(location));
}
}