diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs
index 8ed51a1949..814c101961 100644
--- a/Emby.Server.Implementations/ApplicationHost.cs
+++ b/Emby.Server.Implementations/ApplicationHost.cs
@@ -973,7 +973,7 @@ namespace Emby.Server.Implementations
yield return typeof(IServerApplicationHost).Assembly;
// Include composable parts in the Providers assembly
- yield return typeof(ProviderUtils).Assembly;
+ yield return typeof(ProviderManager).Assembly;
// Include composable parts in the Photos assembly
yield return typeof(PhotoProvider).Assembly;
diff --git a/Jellyfin.Api/Controllers/ItemLookupController.cs b/Jellyfin.Api/Controllers/ItemLookupController.cs
index 4161e43f6a..c49f856169 100644
--- a/Jellyfin.Api/Controllers/ItemLookupController.cs
+++ b/Jellyfin.Api/Controllers/ItemLookupController.cs
@@ -263,7 +263,8 @@ namespace Jellyfin.Api.Controllers
ImageRefreshMode = MetadataRefreshMode.FullRefresh,
ReplaceAllMetadata = true,
ReplaceAllImages = replaceAllImages,
- SearchResult = searchResult
+ SearchResult = searchResult,
+ RemoveOldMetadata = true
},
CancellationToken.None).ConfigureAwait(false);
diff --git a/MediaBrowser.Controller/Providers/ImageRefreshOptions.cs b/MediaBrowser.Controller/Providers/ImageRefreshOptions.cs
index 08d129a82e..a9d16a49e4 100644
--- a/MediaBrowser.Controller/Providers/ImageRefreshOptions.cs
+++ b/MediaBrowser.Controller/Providers/ImageRefreshOptions.cs
@@ -27,6 +27,11 @@ namespace MediaBrowser.Controller.Providers
public bool IsAutomated { get; set; }
+ ///
+ /// Gets or sets a value indicating whether old metadata should be removed if it isn't replaced.
+ ///
+ public bool RemoveOldMetadata { get; set; }
+
public bool IsReplacingImage(ImageType type)
{
return ImageRefreshMode == MetadataRefreshMode.FullRefresh &&
diff --git a/MediaBrowser.Controller/Providers/MetadataRefreshOptions.cs b/MediaBrowser.Controller/Providers/MetadataRefreshOptions.cs
index 90fd6e269d..a38bbaf695 100644
--- a/MediaBrowser.Controller/Providers/MetadataRefreshOptions.cs
+++ b/MediaBrowser.Controller/Providers/MetadataRefreshOptions.cs
@@ -30,6 +30,7 @@ namespace MediaBrowser.Controller.Providers
ReplaceAllImages = copy.ReplaceAllImages;
ReplaceImages = copy.ReplaceImages;
SearchResult = copy.SearchResult;
+ RemoveOldMetadata = copy.RemoveOldMetadata;
if (copy.RefreshPaths != null && copy.RefreshPaths.Length > 0)
{
diff --git a/MediaBrowser.Providers/Books/AudioBookMetadataService.cs b/MediaBrowser.Providers/Books/AudioBookMetadataService.cs
index eabc66c6b8..96e1165b69 100644
--- a/MediaBrowser.Providers/Books/AudioBookMetadataService.cs
+++ b/MediaBrowser.Providers/Books/AudioBookMetadataService.cs
@@ -31,7 +31,7 @@ namespace MediaBrowser.Providers.Books
bool replaceData,
bool mergeMetadataSettings)
{
- ProviderUtils.MergeBaseItemData(source, target, lockedFields, replaceData, mergeMetadataSettings);
+ base.MergeData(source, target, lockedFields, replaceData, mergeMetadataSettings);
var sourceItem = source.Item;
var targetItem = target.Item;
diff --git a/MediaBrowser.Providers/Books/BookMetadataService.cs b/MediaBrowser.Providers/Books/BookMetadataService.cs
index 3f3782dfb9..50b9922c69 100644
--- a/MediaBrowser.Providers/Books/BookMetadataService.cs
+++ b/MediaBrowser.Providers/Books/BookMetadataService.cs
@@ -26,7 +26,7 @@ namespace MediaBrowser.Providers.Books
///
protected override void MergeData(MetadataResult source, MetadataResult target, MetadataField[] lockedFields, bool replaceData, bool mergeMetadataSettings)
{
- ProviderUtils.MergeBaseItemData(source, target, lockedFields, replaceData, mergeMetadataSettings);
+ base.MergeData(source, target, lockedFields, replaceData, mergeMetadataSettings);
if (replaceData || string.IsNullOrEmpty(target.Item.SeriesName))
{
diff --git a/MediaBrowser.Providers/BoxSets/BoxSetMetadataService.cs b/MediaBrowser.Providers/BoxSets/BoxSetMetadataService.cs
index 88ce8d087d..cbbb343e5e 100644
--- a/MediaBrowser.Providers/BoxSets/BoxSetMetadataService.cs
+++ b/MediaBrowser.Providers/BoxSets/BoxSetMetadataService.cs
@@ -47,7 +47,7 @@ namespace MediaBrowser.Providers.BoxSets
///
protected override void MergeData(MetadataResult source, MetadataResult target, MetadataField[] lockedFields, bool replaceData, bool mergeMetadataSettings)
{
- ProviderUtils.MergeBaseItemData(source, target, lockedFields, replaceData, mergeMetadataSettings);
+ base.MergeData(source, target, lockedFields, replaceData, mergeMetadataSettings);
var sourceItem = source.Item;
var targetItem = target.Item;
diff --git a/MediaBrowser.Providers/Channels/ChannelMetadataService.cs b/MediaBrowser.Providers/Channels/ChannelMetadataService.cs
index db2213bad4..0267fa13f7 100644
--- a/MediaBrowser.Providers/Channels/ChannelMetadataService.cs
+++ b/MediaBrowser.Providers/Channels/ChannelMetadataService.cs
@@ -4,7 +4,6 @@ using MediaBrowser.Controller.Channels;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Providers;
-using MediaBrowser.Model.Entities;
using MediaBrowser.Model.IO;
using MediaBrowser.Providers.Manager;
using Microsoft.Extensions.Logging;
@@ -22,11 +21,5 @@ namespace MediaBrowser.Providers.Channels
: base(serverConfigurationManager, logger, providerManager, fileSystem, libraryManager)
{
}
-
- ///
- protected override void MergeData(MetadataResult source, MetadataResult target, MetadataField[] lockedFields, bool replaceData, bool mergeMetadataSettings)
- {
- ProviderUtils.MergeBaseItemData(source, target, lockedFields, replaceData, mergeMetadataSettings);
- }
}
}
diff --git a/MediaBrowser.Providers/Folders/CollectionFolderMetadataService.cs b/MediaBrowser.Providers/Folders/CollectionFolderMetadataService.cs
index e0f3131fdb..0629824d30 100644
--- a/MediaBrowser.Providers/Folders/CollectionFolderMetadataService.cs
+++ b/MediaBrowser.Providers/Folders/CollectionFolderMetadataService.cs
@@ -4,7 +4,6 @@ using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Providers;
-using MediaBrowser.Model.Entities;
using MediaBrowser.Model.IO;
using MediaBrowser.Providers.Manager;
using Microsoft.Extensions.Logging;
@@ -22,11 +21,5 @@ namespace MediaBrowser.Providers.Folders
: base(serverConfigurationManager, logger, providerManager, fileSystem, libraryManager)
{
}
-
- ///
- protected override void MergeData(MetadataResult source, MetadataResult target, MetadataField[] lockedFields, bool replaceData, bool mergeMetadataSettings)
- {
- ProviderUtils.MergeBaseItemData(source, target, lockedFields, replaceData, mergeMetadataSettings);
- }
}
}
diff --git a/MediaBrowser.Providers/Folders/FolderMetadataService.cs b/MediaBrowser.Providers/Folders/FolderMetadataService.cs
index 998bf4c6a3..79d52991a1 100644
--- a/MediaBrowser.Providers/Folders/FolderMetadataService.cs
+++ b/MediaBrowser.Providers/Folders/FolderMetadataService.cs
@@ -4,7 +4,6 @@ using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Providers;
-using MediaBrowser.Model.Entities;
using MediaBrowser.Model.IO;
using MediaBrowser.Providers.Manager;
using Microsoft.Extensions.Logging;
@@ -26,11 +25,5 @@ namespace MediaBrowser.Providers.Folders
///
// Make sure the type-specific services get picked first
public override int Order => 10;
-
- ///
- protected override void MergeData(MetadataResult source, MetadataResult target, MetadataField[] lockedFields, bool replaceData, bool mergeMetadataSettings)
- {
- ProviderUtils.MergeBaseItemData(source, target, lockedFields, replaceData, mergeMetadataSettings);
- }
}
}
diff --git a/MediaBrowser.Providers/Folders/UserViewMetadataService.cs b/MediaBrowser.Providers/Folders/UserViewMetadataService.cs
index 2d536f12ec..79c5597e54 100644
--- a/MediaBrowser.Providers/Folders/UserViewMetadataService.cs
+++ b/MediaBrowser.Providers/Folders/UserViewMetadataService.cs
@@ -4,7 +4,6 @@ using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Providers;
-using MediaBrowser.Model.Entities;
using MediaBrowser.Model.IO;
using MediaBrowser.Providers.Manager;
using Microsoft.Extensions.Logging;
@@ -22,11 +21,5 @@ namespace MediaBrowser.Providers.Folders
: base(serverConfigurationManager, logger, providerManager, fileSystem, libraryManager)
{
}
-
- ///
- protected override void MergeData(MetadataResult source, MetadataResult target, MetadataField[] lockedFields, bool replaceData, bool mergeMetadataSettings)
- {
- ProviderUtils.MergeBaseItemData(source, target, lockedFields, replaceData, mergeMetadataSettings);
- }
}
}
diff --git a/MediaBrowser.Providers/Genres/GenreMetadataService.cs b/MediaBrowser.Providers/Genres/GenreMetadataService.cs
index f7ea767e7c..4d10d89874 100644
--- a/MediaBrowser.Providers/Genres/GenreMetadataService.cs
+++ b/MediaBrowser.Providers/Genres/GenreMetadataService.cs
@@ -4,7 +4,6 @@ using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Providers;
-using MediaBrowser.Model.Entities;
using MediaBrowser.Model.IO;
using MediaBrowser.Providers.Manager;
using Microsoft.Extensions.Logging;
@@ -22,11 +21,5 @@ namespace MediaBrowser.Providers.Genres
: base(serverConfigurationManager, logger, providerManager, fileSystem, libraryManager)
{
}
-
- ///
- protected override void MergeData(MetadataResult source, MetadataResult target, MetadataField[] lockedFields, bool replaceData, bool mergeMetadataSettings)
- {
- ProviderUtils.MergeBaseItemData(source, target, lockedFields, replaceData, mergeMetadataSettings);
- }
}
}
diff --git a/MediaBrowser.Providers/LiveTv/LiveTvMetadataService.cs b/MediaBrowser.Providers/LiveTv/LiveTvMetadataService.cs
index 2e6cf45302..c94d365301 100644
--- a/MediaBrowser.Providers/LiveTv/LiveTvMetadataService.cs
+++ b/MediaBrowser.Providers/LiveTv/LiveTvMetadataService.cs
@@ -4,7 +4,6 @@ using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.LiveTv;
using MediaBrowser.Controller.Providers;
-using MediaBrowser.Model.Entities;
using MediaBrowser.Model.IO;
using MediaBrowser.Providers.Manager;
using Microsoft.Extensions.Logging;
@@ -22,11 +21,5 @@ namespace MediaBrowser.Providers.LiveTv
: base(serverConfigurationManager, logger, providerManager, fileSystem, libraryManager)
{
}
-
- ///
- protected override void MergeData(MetadataResult source, MetadataResult target, MetadataField[] lockedFields, bool replaceData, bool mergeMetadataSettings)
- {
- ProviderUtils.MergeBaseItemData(source, target, lockedFields, replaceData, mergeMetadataSettings);
- }
}
}
diff --git a/MediaBrowser.Providers/Manager/ItemImageProvider.cs b/MediaBrowser.Providers/Manager/ItemImageProvider.cs
index 0d1bdec585..0f21ec7b22 100644
--- a/MediaBrowser.Providers/Manager/ItemImageProvider.cs
+++ b/MediaBrowser.Providers/Manager/ItemImageProvider.cs
@@ -62,6 +62,29 @@ namespace MediaBrowser.Providers.Manager
_fileSystem = fileSystem;
}
+ ///
+ /// Removes all existing images from the provided item.
+ ///
+ /// The to remove images from.
+ /// true if changes were made to the item; otherwise false.
+ public bool RemoveImages(BaseItem item)
+ {
+ var singular = new List();
+ for (var i = 0; i < _singularImages.Length; i++)
+ {
+ var currentImage = item.GetImageInfo(_singularImages[i], 0);
+ if (currentImage != null)
+ {
+ singular.Add(currentImage);
+ }
+ }
+
+ singular.AddRange(item.GetImages(ImageType.Backdrop));
+ PruneImages(item, singular);
+
+ return singular.Count > 0;
+ }
+
///
/// Verifies existing images have valid paths and adds any new local images provided.
///
@@ -100,7 +123,7 @@ namespace MediaBrowser.Providers.Manager
public async Task RefreshImages(
BaseItem item,
LibraryOptions libraryOptions,
- List providers,
+ IEnumerable providers,
ImageRefreshOptions refreshOptions,
CancellationToken cancellationToken)
{
@@ -160,14 +183,14 @@ namespace MediaBrowser.Providers.Manager
foreach (var imageType in images)
{
- if (!IsEnabled(savedOptions, imageType))
+ if (!savedOptions.IsEnabled(imageType))
{
continue;
}
- if (!HasImage(item, imageType) || (refreshOptions.IsReplacingImage(imageType) && !downloadedImages.Contains(imageType)))
+ if (!item.HasImage(imageType) || (refreshOptions.IsReplacingImage(imageType) && !downloadedImages.Contains(imageType)))
{
- _logger.LogDebug("Running {0} for {1}", provider.GetType().Name, item.Path ?? item.Name);
+ _logger.LogDebug("Running {Provider} for {Item}", provider.GetType().Name, item.Path ?? item.Name);
var response = await provider.GetImage(item, imageType, cancellationToken).ConfigureAwait(false);
@@ -183,7 +206,7 @@ namespace MediaBrowser.Providers.Manager
{
if (response.Protocol == MediaProtocol.Http)
{
- _logger.LogDebug("Setting image url into item {0}", item.Id);
+ _logger.LogDebug("Setting image url into item {Item}", item.Id);
var index = item.AllowsMultipleImages(imageType) ? item.GetImages(imageType).Count() : 0;
item.SetImage(
new ItemImageInfo
@@ -220,39 +243,6 @@ namespace MediaBrowser.Providers.Manager
}
}
- private bool HasImage(BaseItem item, ImageType type)
- {
- return item.HasImage(type);
- }
-
- ///
- /// Determines if an item already contains the given images.
- ///
- /// The item.
- /// The images.
- /// The saved options.
- /// The backdrop limit.
- /// true if the specified item contains images; otherwise, false.
- private bool ContainsImages(BaseItem item, List images, TypeOptions savedOptions, int backdropLimit)
- {
- // Using .Any causes the creation of a DisplayClass aka. variable capture
- for (var i = 0; i < _singularImages.Length; i++)
- {
- var type = _singularImages[i];
- if (images.Contains(type) && !HasImage(item, type) && savedOptions.GetLimit(type) > 0)
- {
- return false;
- }
- }
-
- if (images.Contains(ImageType.Backdrop) && item.GetImages(ImageType.Backdrop).Count() < backdropLimit)
- {
- return false;
- }
-
- return true;
- }
-
///
/// Refreshes from a remote provider.
///
@@ -289,7 +279,7 @@ namespace MediaBrowser.Providers.Manager
return;
}
- _logger.LogDebug("Running {0} for {1}", provider.GetType().Name, item.Path ?? item.Name);
+ _logger.LogDebug("Running {Provider} for {Item}", provider.GetType().Name, item.Path ?? item.Name);
var images = await _providerManager.GetAvailableRemoteImages(
item,
@@ -305,12 +295,12 @@ namespace MediaBrowser.Providers.Manager
foreach (var imageType in _singularImages)
{
- if (!IsEnabled(savedOptions, imageType))
+ if (!savedOptions.IsEnabled(imageType))
{
continue;
}
- if (!HasImage(item, imageType) || (refreshOptions.IsReplacingImage(imageType) && !downloadedImages.Contains(imageType)))
+ if (!item.HasImage(imageType) || (refreshOptions.IsReplacingImage(imageType) && !downloadedImages.Contains(imageType)))
{
minWidth = savedOptions.GetMinWidth(imageType);
var downloaded = await DownloadImage(item, provider, result, list, minWidth, imageType, cancellationToken).ConfigureAwait(false);
@@ -336,14 +326,37 @@ namespace MediaBrowser.Providers.Manager
}
}
- private bool IsEnabled(TypeOptions options, ImageType type)
+ ///
+ /// Determines if an item already contains the given images.
+ ///
+ /// The item.
+ /// The images.
+ /// The saved options.
+ /// The backdrop limit.
+ /// true if the specified item contains images; otherwise, false.
+ private bool ContainsImages(BaseItem item, List images, TypeOptions savedOptions, int backdropLimit)
{
- return options.IsEnabled(type);
+ // Using .Any causes the creation of a DisplayClass aka. variable capture
+ for (var i = 0; i < _singularImages.Length; i++)
+ {
+ var type = _singularImages[i];
+ if (images.Contains(type) && !item.HasImage(type) && savedOptions.GetLimit(type) > 0)
+ {
+ return false;
+ }
+ }
+
+ if (images.Contains(ImageType.Backdrop) && item.GetImages(ImageType.Backdrop).Count() < backdropLimit)
+ {
+ return false;
+ }
+
+ return true;
}
- private void PruneImages(BaseItem item, ItemImageInfo[] images)
+ private void PruneImages(BaseItem item, IReadOnlyList images)
{
- for (var i = 0; i < images.Length; i++)
+ for (var i = 0; i < images.Count; i++)
{
var image = images[i];
@@ -355,6 +368,11 @@ namespace MediaBrowser.Providers.Manager
}
catch (FileNotFoundException)
{
+ // nothing to do, already gone
+ }
+ catch (UnauthorizedAccessException ex)
+ {
+ _logger.LogWarning(ex, "Unable to delete {Image}", image.Path);
}
}
}
@@ -381,12 +399,7 @@ namespace MediaBrowser.Providers.Manager
{
var currentImage = item.GetImageInfo(type, 0);
- if (currentImage == null)
- {
- item.SetImagePath(type, image.FileInfo);
- changed = true;
- }
- else if (!string.Equals(currentImage.Path, image.FileInfo.FullName, StringComparison.OrdinalIgnoreCase))
+ if (currentImage == null || !string.Equals(currentImage.Path, image.FileInfo.FullName, StringComparison.OrdinalIgnoreCase))
{
item.SetImagePath(type, image.FileInfo);
changed = true;
@@ -494,7 +507,7 @@ namespace MediaBrowser.Providers.Manager
await _providerManager.SaveImage(
item,
stream,
- response.Content.Headers.ContentType.MediaType,
+ response.Content.Headers.ContentType?.MediaType,
type,
null,
cancellationToken).ConfigureAwait(false);
@@ -617,11 +630,11 @@ namespace MediaBrowser.Providers.Manager
await _providerManager.SaveImage(
item,
stream,
- response.Content.Headers.ContentType.MediaType,
+ response.Content.Headers.ContentType?.MediaType,
imageType,
null,
cancellationToken).ConfigureAwait(false);
- result.UpdateType = result.UpdateType | ItemUpdateType.ImageUpdate;
+ result.UpdateType |= ItemUpdateType.ImageUpdate;
}
catch (HttpRequestException)
{
diff --git a/MediaBrowser.Providers/Manager/MetadataService.cs b/MediaBrowser.Providers/Manager/MetadataService.cs
index 94045b38b9..0c52d26736 100644
--- a/MediaBrowser.Providers/Manager/MetadataService.cs
+++ b/MediaBrowser.Providers/Manager/MetadataService.cs
@@ -8,8 +8,10 @@ using System.Linq;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
+using Diacritics.Extensions;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Entities;
+using MediaBrowser.Controller.Entities.Audio;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Configuration;
@@ -74,14 +76,10 @@ namespace MediaBrowser.Providers.Manager
var itemOfType = (TItemType)item;
var updateType = ItemUpdateType.None;
- var requiresRefresh = false;
var libraryOptions = LibraryManager.GetLibraryOptions(item);
- if (!requiresRefresh && libraryOptions.AutomaticRefreshIntervalDays > 0 && (DateTime.UtcNow - item.DateLastRefreshed).TotalDays >= libraryOptions.AutomaticRefreshIntervalDays)
- {
- requiresRefresh = true;
- }
+ var requiresRefresh = libraryOptions.AutomaticRefreshIntervalDays > 0 && (DateTime.UtcNow - item.DateLastRefreshed).TotalDays >= libraryOptions.AutomaticRefreshIntervalDays;
if (!requiresRefresh && refreshOptions.MetadataRefreshMode != MetadataRefreshMode.None)
{
@@ -90,7 +88,7 @@ namespace MediaBrowser.Providers.Manager
if (requiresRefresh)
{
- Logger.LogDebug("Refreshing {0} {1} because item.RequiresRefresh() returned true", typeof(TItemType).Name, item.Path ?? item.Name);
+ Logger.LogDebug("Refreshing {Type} {Item} because item.RequiresRefresh() returned true", typeof(TItemType).Name, item.Path ?? item.Name);
}
}
@@ -98,6 +96,14 @@ namespace MediaBrowser.Providers.Manager
var allImageProviders = ((ProviderManager)ProviderManager).GetImageProviders(item, refreshOptions).ToList();
+ if (refreshOptions.RemoveOldMetadata && refreshOptions.ReplaceAllImages)
+ {
+ if (ImageProvider.RemoveImages(item))
+ {
+ updateType |= ItemUpdateType.ImageUpdate;
+ }
+ }
+
// Start by validating images
try
{
@@ -110,7 +116,7 @@ namespace MediaBrowser.Providers.Manager
catch (Exception ex)
{
localImagesFailed = true;
- Logger.LogError(ex, "Error validating images for {0}", item.Path ?? item.Name ?? "Unknown name");
+ Logger.LogError(ex, "Error validating images for {Item}", item.Path ?? item.Name ?? "Unknown name");
}
var metadataResult = new MetadataResult
@@ -380,8 +386,7 @@ namespace MediaBrowser.Providers.Manager
{
var updateType = ItemUpdateType.None;
- var folder = item as Folder;
- if (folder != null && folder.SupportsDateLastMediaAdded)
+ if (item is Folder folder && folder.SupportsDateLastMediaAdded)
{
var dateLastMediaAdded = DateTime.MinValue;
var any = false;
@@ -668,7 +673,7 @@ namespace MediaBrowser.Providers.Manager
foreach (var provider in providers.OfType>().ToList())
{
var providerName = provider.GetType().Name;
- Logger.LogDebug("Running {0} for {1}", providerName, logName);
+ Logger.LogDebug("Running {Provider} for {Item}", providerName, logName);
var itemInfo = new ItemInfo(item);
@@ -713,7 +718,7 @@ namespace MediaBrowser.Providers.Manager
break;
}
- Logger.LogDebug("{0} returned no metadata for {1}", providerName, logName);
+ Logger.LogDebug("{Provider} returned no metadata for {Item}", providerName, logName);
}
catch (OperationCanceledException)
{
@@ -749,8 +754,11 @@ namespace MediaBrowser.Providers.Manager
}
else
{
- // TODO: If the new metadata from above has some blank data, this can cause old data to get filled into those empty fields
- MergeData(metadata, temp, Array.Empty(), false, false);
+ if (!options.RemoveOldMetadata)
+ {
+ MergeData(metadata, temp, Array.Empty(), false, false);
+ }
+
MergeData(temp, metadata, item.LockedFields, true, false);
}
}
@@ -780,7 +788,7 @@ namespace MediaBrowser.Providers.Manager
private async Task RunCustomProvider(ICustomMetadataProvider provider, TItemType item, string logName, MetadataRefreshOptions options, RefreshResult refreshResult, CancellationToken cancellationToken)
{
- Logger.LogDebug("Running {0} for {1}", provider.GetType().Name, logName);
+ Logger.LogDebug("Running {Provider} for {Item}", provider.GetType().Name, logName);
try
{
@@ -811,7 +819,7 @@ namespace MediaBrowser.Providers.Manager
foreach (var provider in providers)
{
var providerName = provider.GetType().Name;
- Logger.LogDebug("Running {0} for {1}", providerName, logName);
+ Logger.LogDebug("Running {Provider} for {Item}", providerName, logName);
if (id != null && !tmpDataMerged)
{
@@ -834,7 +842,7 @@ namespace MediaBrowser.Providers.Manager
}
else
{
- Logger.LogDebug("{0} returned no metadata for {1}", providerName, logName);
+ Logger.LogDebug("{Provider} returned no metadata for {Item}", providerName, logName);
}
}
catch (OperationCanceledException)
@@ -867,13 +875,6 @@ namespace MediaBrowser.Providers.Manager
}
}
- protected abstract void MergeData(
- MetadataResult source,
- MetadataResult target,
- MetadataField[] lockedFields,
- bool replaceData,
- bool mergeMetadataSettings);
-
private bool HasChanged(BaseItem item, IHasItemChangeMonitor changeMonitor, IDirectoryService directoryService)
{
try
@@ -882,16 +883,312 @@ namespace MediaBrowser.Providers.Manager
if (hasChanged)
{
- Logger.LogDebug("{0} reports change to {1}", changeMonitor.GetType().Name, item.Path ?? item.Name);
+ Logger.LogDebug("{Monitor} reports change to {Item}", changeMonitor.GetType().Name, item.Path ?? item.Name);
}
return hasChanged;
}
catch (Exception ex)
{
- Logger.LogError(ex, "Error in {0}.HasChanged", changeMonitor.GetType().Name);
+ Logger.LogError(ex, "Error in {Monitor}.HasChanged", changeMonitor.GetType().Name);
return false;
}
}
+
+ ///
+ /// Merges metadata from source into target.
+ ///
+ /// The source for new metadata.
+ /// The target to insert new metadata into.
+ /// The fields that are locked and should not be updated.
+ /// true if existing data should be replaced.
+ /// true if the metadata settings in target should be updated to match source.
+ /// Thrown if source or target are null.
+ protected virtual void MergeData(
+ MetadataResult source,
+ MetadataResult target,
+ MetadataField[] lockedFields,
+ bool replaceData,
+ bool mergeMetadataSettings)
+ {
+ MergeBaseItemData(source, target, lockedFields, replaceData, mergeMetadataSettings);
+ }
+
+ internal static void MergeBaseItemData(
+ MetadataResult sourceResult,
+ MetadataResult targetResult,
+ MetadataField[] lockedFields,
+ bool replaceData,
+ bool mergeMetadataSettings)
+ {
+ var source = sourceResult.Item;
+ var target = targetResult.Item;
+
+ if (source == null)
+ {
+ throw new ArgumentException("Item cannot be null.", nameof(sourceResult));
+ }
+
+ if (target == null)
+ {
+ throw new ArgumentException("Item cannot be null.", nameof(targetResult));
+ }
+
+ if (!lockedFields.Contains(MetadataField.Name))
+ {
+ if (replaceData || string.IsNullOrEmpty(target.Name))
+ {
+ // Safeguard against incoming data having an empty name
+ if (!string.IsNullOrWhiteSpace(source.Name))
+ {
+ target.Name = source.Name;
+ }
+ }
+ }
+
+ if (replaceData || string.IsNullOrEmpty(target.OriginalTitle))
+ {
+ // Safeguard against incoming data having an empty name
+ if (!string.IsNullOrWhiteSpace(source.OriginalTitle))
+ {
+ target.OriginalTitle = source.OriginalTitle;
+ }
+ }
+
+ if (replaceData || !target.CommunityRating.HasValue)
+ {
+ target.CommunityRating = source.CommunityRating;
+ }
+
+ if (replaceData || !target.EndDate.HasValue)
+ {
+ target.EndDate = source.EndDate;
+ }
+
+ if (!lockedFields.Contains(MetadataField.Genres))
+ {
+ if (replaceData || target.Genres.Length == 0)
+ {
+ target.Genres = source.Genres;
+ }
+ }
+
+ if (replaceData || !target.IndexNumber.HasValue)
+ {
+ target.IndexNumber = source.IndexNumber;
+ }
+
+ if (!lockedFields.Contains(MetadataField.OfficialRating))
+ {
+ if (replaceData || string.IsNullOrEmpty(target.OfficialRating))
+ {
+ target.OfficialRating = source.OfficialRating;
+ }
+ }
+
+ if (replaceData || string.IsNullOrEmpty(target.CustomRating))
+ {
+ target.CustomRating = source.CustomRating;
+ }
+
+ if (replaceData || string.IsNullOrEmpty(target.Tagline))
+ {
+ target.Tagline = source.Tagline;
+ }
+
+ if (!lockedFields.Contains(MetadataField.Overview))
+ {
+ if (replaceData || string.IsNullOrEmpty(target.Overview))
+ {
+ target.Overview = source.Overview;
+ }
+ }
+
+ if (replaceData || !target.ParentIndexNumber.HasValue)
+ {
+ target.ParentIndexNumber = source.ParentIndexNumber;
+ }
+
+ if (!lockedFields.Contains(MetadataField.Cast))
+ {
+ if (replaceData || targetResult.People == null || targetResult.People.Count == 0)
+ {
+ targetResult.People = sourceResult.People;
+ }
+ else if (targetResult.People != null && sourceResult.People != null)
+ {
+ MergePeople(sourceResult.People, targetResult.People);
+ }
+ }
+
+ if (replaceData || !target.PremiereDate.HasValue)
+ {
+ target.PremiereDate = source.PremiereDate;
+ }
+
+ if (replaceData || !target.ProductionYear.HasValue)
+ {
+ target.ProductionYear = source.ProductionYear;
+ }
+
+ if (!lockedFields.Contains(MetadataField.Runtime))
+ {
+ if (replaceData || !target.RunTimeTicks.HasValue)
+ {
+ if (target is not Audio && target is not Video)
+ {
+ target.RunTimeTicks = source.RunTimeTicks;
+ }
+ }
+ }
+
+ if (!lockedFields.Contains(MetadataField.Studios))
+ {
+ if (replaceData || target.Studios.Length == 0)
+ {
+ target.Studios = source.Studios;
+ }
+ }
+
+ if (!lockedFields.Contains(MetadataField.Tags))
+ {
+ if (replaceData || target.Tags.Length == 0)
+ {
+ target.Tags = source.Tags;
+ }
+ }
+
+ if (!lockedFields.Contains(MetadataField.ProductionLocations))
+ {
+ if (replaceData || target.ProductionLocations.Length == 0)
+ {
+ target.ProductionLocations = source.ProductionLocations;
+ }
+ }
+
+ foreach (var id in source.ProviderIds)
+ {
+ var key = id.Key;
+
+ // Don't replace existing Id's.
+ if (replaceData)
+ {
+ target.ProviderIds[key] = id.Value;
+ }
+ else
+ {
+ target.ProviderIds.TryAdd(key, id.Value);
+ }
+ }
+
+ MergeAlbumArtist(source, target, replaceData);
+ MergeCriticRating(source, target, replaceData);
+ MergeTrailers(source, target, replaceData);
+ MergeVideoInfo(source, target, replaceData);
+ MergeDisplayOrder(source, target, replaceData);
+
+ if (replaceData || string.IsNullOrEmpty(target.ForcedSortName))
+ {
+ var forcedSortName = source.ForcedSortName;
+
+ if (!string.IsNullOrWhiteSpace(forcedSortName))
+ {
+ target.ForcedSortName = forcedSortName;
+ }
+ }
+
+ if (mergeMetadataSettings)
+ {
+ target.LockedFields = source.LockedFields;
+ target.IsLocked = source.IsLocked;
+
+ // Grab the value if it's there, but if not then don't overwrite with the default
+ if (source.DateCreated != default)
+ {
+ target.DateCreated = source.DateCreated;
+ }
+
+ target.PreferredMetadataCountryCode = source.PreferredMetadataCountryCode;
+ target.PreferredMetadataLanguage = source.PreferredMetadataLanguage;
+ }
+ }
+
+ private static void MergePeople(List source, List target)
+ {
+ foreach (var person in target)
+ {
+ var normalizedName = person.Name.RemoveDiacritics();
+ var personInSource = source.FirstOrDefault(i => string.Equals(i.Name.RemoveDiacritics(), normalizedName, StringComparison.OrdinalIgnoreCase));
+
+ if (personInSource != null)
+ {
+ foreach (var providerId in personInSource.ProviderIds)
+ {
+ person.ProviderIds.TryAdd(providerId.Key, providerId.Value);
+ }
+
+ if (string.IsNullOrWhiteSpace(person.ImageUrl))
+ {
+ person.ImageUrl = personInSource.ImageUrl;
+ }
+ }
+ }
+ }
+
+ private static void MergeDisplayOrder(BaseItem source, BaseItem target, bool replaceData)
+ {
+ if (source is IHasDisplayOrder sourceHasDisplayOrder
+ && target is IHasDisplayOrder targetHasDisplayOrder)
+ {
+ if (replaceData || string.IsNullOrEmpty(targetHasDisplayOrder.DisplayOrder))
+ {
+ var displayOrder = sourceHasDisplayOrder.DisplayOrder;
+
+ if (!string.IsNullOrWhiteSpace(displayOrder))
+ {
+ targetHasDisplayOrder.DisplayOrder = displayOrder;
+ }
+ }
+ }
+ }
+
+ private static void MergeAlbumArtist(BaseItem source, BaseItem target, bool replaceData)
+ {
+ if (source is IHasAlbumArtist sourceHasAlbumArtist
+ && target is IHasAlbumArtist targetHasAlbumArtist)
+ {
+ if (replaceData || targetHasAlbumArtist.AlbumArtists.Count == 0)
+ {
+ targetHasAlbumArtist.AlbumArtists = sourceHasAlbumArtist.AlbumArtists;
+ }
+ }
+ }
+
+ private static void MergeCriticRating(BaseItem source, BaseItem target, bool replaceData)
+ {
+ if (replaceData || !target.CriticRating.HasValue)
+ {
+ target.CriticRating = source.CriticRating;
+ }
+ }
+
+ private static void MergeTrailers(BaseItem source, BaseItem target, bool replaceData)
+ {
+ if (replaceData || target.RemoteTrailers.Count == 0)
+ {
+ target.RemoteTrailers = source.RemoteTrailers;
+ }
+ }
+
+ private static void MergeVideoInfo(BaseItem source, BaseItem target, bool replaceData)
+ {
+ if (source is Video sourceCast && target is Video targetCast)
+ {
+ if (replaceData || targetCast.Video3DFormat == null)
+ {
+ targetCast.Video3DFormat = sourceCast.Video3DFormat;
+ }
+ }
+ }
}
}
diff --git a/MediaBrowser.Providers/Manager/ProviderUtils.cs b/MediaBrowser.Providers/Manager/ProviderUtils.cs
deleted file mode 100644
index b90136d509..0000000000
--- a/MediaBrowser.Providers/Manager/ProviderUtils.cs
+++ /dev/null
@@ -1,295 +0,0 @@
-#nullable disable
-
-#pragma warning disable CS1591
-
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using Diacritics.Extensions;
-using MediaBrowser.Controller.Entities;
-using MediaBrowser.Controller.Entities.Audio;
-using MediaBrowser.Controller.Providers;
-using MediaBrowser.Model.Entities;
-
-namespace MediaBrowser.Providers.Manager
-{
- public static class ProviderUtils
- {
- public static void MergeBaseItemData(
- MetadataResult sourceResult,
- MetadataResult targetResult,
- MetadataField[] lockedFields,
- bool replaceData,
- bool mergeMetadataSettings)
- where T : BaseItem
- {
- var source = sourceResult.Item;
- var target = targetResult.Item;
-
- if (source == null)
- {
- throw new ArgumentException("Item cannot be null.", nameof(sourceResult));
- }
-
- if (target == null)
- {
- throw new ArgumentException("Item cannot be null.", nameof(targetResult));
- }
-
- if (!lockedFields.Contains(MetadataField.Name))
- {
- if (replaceData || string.IsNullOrEmpty(target.Name))
- {
- // Safeguard against incoming data having an empty name
- if (!string.IsNullOrWhiteSpace(source.Name))
- {
- target.Name = source.Name;
- }
- }
- }
-
- if (replaceData || string.IsNullOrEmpty(target.OriginalTitle))
- {
- // Safeguard against incoming data having an empty name
- if (!string.IsNullOrWhiteSpace(source.OriginalTitle))
- {
- target.OriginalTitle = source.OriginalTitle;
- }
- }
-
- if (replaceData || !target.CommunityRating.HasValue)
- {
- target.CommunityRating = source.CommunityRating;
- }
-
- if (replaceData || !target.EndDate.HasValue)
- {
- target.EndDate = source.EndDate;
- }
-
- if (!lockedFields.Contains(MetadataField.Genres))
- {
- if (replaceData || target.Genres.Length == 0)
- {
- target.Genres = source.Genres;
- }
- }
-
- if (replaceData || !target.IndexNumber.HasValue)
- {
- target.IndexNumber = source.IndexNumber;
- }
-
- if (!lockedFields.Contains(MetadataField.OfficialRating))
- {
- if (replaceData || string.IsNullOrEmpty(target.OfficialRating))
- {
- target.OfficialRating = source.OfficialRating;
- }
- }
-
- if (replaceData || string.IsNullOrEmpty(target.CustomRating))
- {
- target.CustomRating = source.CustomRating;
- }
-
- if (replaceData || string.IsNullOrEmpty(target.Tagline))
- {
- target.Tagline = source.Tagline;
- }
-
- if (!lockedFields.Contains(MetadataField.Overview))
- {
- if (replaceData || string.IsNullOrEmpty(target.Overview))
- {
- target.Overview = source.Overview;
- }
- }
-
- if (replaceData || !target.ParentIndexNumber.HasValue)
- {
- target.ParentIndexNumber = source.ParentIndexNumber;
- }
-
- if (!lockedFields.Contains(MetadataField.Cast))
- {
- if (replaceData || targetResult.People == null || targetResult.People.Count == 0)
- {
- targetResult.People = sourceResult.People;
- }
- else if (targetResult.People != null && sourceResult.People != null)
- {
- MergePeople(sourceResult.People, targetResult.People);
- }
- }
-
- if (replaceData || !target.PremiereDate.HasValue)
- {
- target.PremiereDate = source.PremiereDate;
- }
-
- if (replaceData || !target.ProductionYear.HasValue)
- {
- target.ProductionYear = source.ProductionYear;
- }
-
- if (!lockedFields.Contains(MetadataField.Runtime))
- {
- if (replaceData || !target.RunTimeTicks.HasValue)
- {
- if (target is not Audio && target is not Video)
- {
- target.RunTimeTicks = source.RunTimeTicks;
- }
- }
- }
-
- if (!lockedFields.Contains(MetadataField.Studios))
- {
- if (replaceData || target.Studios.Length == 0)
- {
- target.Studios = source.Studios;
- }
- }
-
- if (!lockedFields.Contains(MetadataField.Tags))
- {
- if (replaceData || target.Tags.Length == 0)
- {
- target.Tags = source.Tags;
- }
- }
-
- if (!lockedFields.Contains(MetadataField.ProductionLocations))
- {
- if (replaceData || target.ProductionLocations.Length == 0)
- {
- target.ProductionLocations = source.ProductionLocations;
- }
- }
-
- foreach (var id in source.ProviderIds)
- {
- var key = id.Key;
-
- // Don't replace existing Id's.
- if (replaceData || !target.ProviderIds.ContainsKey(key))
- {
- target.ProviderIds[key] = id.Value;
- }
- }
-
- MergeAlbumArtist(source, target, replaceData);
- MergeCriticRating(source, target, replaceData);
- MergeTrailers(source, target, replaceData);
- MergeVideoInfo(source, target, replaceData);
- MergeDisplayOrder(source, target, replaceData);
-
- if (replaceData || string.IsNullOrEmpty(target.ForcedSortName))
- {
- var forcedSortName = source.ForcedSortName;
-
- if (!string.IsNullOrWhiteSpace(forcedSortName))
- {
- target.ForcedSortName = forcedSortName;
- }
- }
-
- if (mergeMetadataSettings)
- {
- target.LockedFields = source.LockedFields;
- target.IsLocked = source.IsLocked;
-
- // Grab the value if it's there, but if not then don't overwrite the default
- if (source.DateCreated != default)
- {
- target.DateCreated = source.DateCreated;
- }
-
- target.PreferredMetadataCountryCode = source.PreferredMetadataCountryCode;
- target.PreferredMetadataLanguage = source.PreferredMetadataLanguage;
- }
- }
-
- private static void MergePeople(List source, List target)
- {
- foreach (var person in target)
- {
- var normalizedName = person.Name.RemoveDiacritics();
- var personInSource = source.FirstOrDefault(i => string.Equals(i.Name.RemoveDiacritics(), normalizedName, StringComparison.OrdinalIgnoreCase));
-
- if (personInSource != null)
- {
- foreach (var providerId in personInSource.ProviderIds)
- {
- if (!person.ProviderIds.ContainsKey(providerId.Key))
- {
- person.ProviderIds[providerId.Key] = providerId.Value;
- }
- }
-
- if (string.IsNullOrWhiteSpace(person.ImageUrl))
- {
- person.ImageUrl = personInSource.ImageUrl;
- }
- }
- }
- }
-
- private static void MergeDisplayOrder(BaseItem source, BaseItem target, bool replaceData)
- {
- if (source is IHasDisplayOrder sourceHasDisplayOrder
- && target is IHasDisplayOrder targetHasDisplayOrder)
- {
- if (replaceData || string.IsNullOrEmpty(targetHasDisplayOrder.DisplayOrder))
- {
- var displayOrder = sourceHasDisplayOrder.DisplayOrder;
-
- if (!string.IsNullOrWhiteSpace(displayOrder))
- {
- targetHasDisplayOrder.DisplayOrder = displayOrder;
- }
- }
- }
- }
-
- private static void MergeAlbumArtist(BaseItem source, BaseItem target, bool replaceData)
- {
- if (source is IHasAlbumArtist sourceHasAlbumArtist
- && target is IHasAlbumArtist targetHasAlbumArtist)
- {
- if (replaceData || targetHasAlbumArtist.AlbumArtists.Count == 0)
- {
- targetHasAlbumArtist.AlbumArtists = sourceHasAlbumArtist.AlbumArtists;
- }
- }
- }
-
- private static void MergeCriticRating(BaseItem source, BaseItem target, bool replaceData)
- {
- if (replaceData || !target.CriticRating.HasValue)
- {
- target.CriticRating = source.CriticRating;
- }
- }
-
- private static void MergeTrailers(BaseItem source, BaseItem target, bool replaceData)
- {
- if (replaceData || target.RemoteTrailers.Count == 0)
- {
- target.RemoteTrailers = source.RemoteTrailers;
- }
- }
-
- private static void MergeVideoInfo(BaseItem source, BaseItem target, bool replaceData)
- {
- if (source is Video sourceCast && target is Video targetCast)
- {
- if (replaceData || targetCast.Video3DFormat == null)
- {
- targetCast.Video3DFormat = sourceCast.Video3DFormat;
- }
- }
- }
- }
-}
diff --git a/MediaBrowser.Providers/Movies/MovieMetadataService.cs b/MediaBrowser.Providers/Movies/MovieMetadataService.cs
index c477fb70f6..984a3c122a 100644
--- a/MediaBrowser.Providers/Movies/MovieMetadataService.cs
+++ b/MediaBrowser.Providers/Movies/MovieMetadataService.cs
@@ -42,7 +42,7 @@ namespace MediaBrowser.Providers.Movies
///
protected override void MergeData(MetadataResult source, MetadataResult target, MetadataField[] lockedFields, bool replaceData, bool mergeMetadataSettings)
{
- ProviderUtils.MergeBaseItemData(source, target, lockedFields, replaceData, mergeMetadataSettings);
+ base.MergeData(source, target, lockedFields, replaceData, mergeMetadataSettings);
var sourceItem = source.Item;
var targetItem = target.Item;
diff --git a/MediaBrowser.Providers/Movies/TrailerMetadataService.cs b/MediaBrowser.Providers/Movies/TrailerMetadataService.cs
index f32d9ec0a3..ad0c5aaa7b 100644
--- a/MediaBrowser.Providers/Movies/TrailerMetadataService.cs
+++ b/MediaBrowser.Providers/Movies/TrailerMetadataService.cs
@@ -42,7 +42,7 @@ namespace MediaBrowser.Providers.Movies
///
protected override void MergeData(MetadataResult source, MetadataResult target, MetadataField[] lockedFields, bool replaceData, bool mergeMetadataSettings)
{
- ProviderUtils.MergeBaseItemData(source, target, lockedFields, replaceData, mergeMetadataSettings);
+ base.MergeData(source, target, lockedFields, replaceData, mergeMetadataSettings);
if (replaceData || target.Item.TrailerTypes.Length == 0)
{
diff --git a/MediaBrowser.Providers/Music/AlbumMetadataService.cs b/MediaBrowser.Providers/Music/AlbumMetadataService.cs
index 7c5b80e1e4..7743d3b27b 100644
--- a/MediaBrowser.Providers/Music/AlbumMetadataService.cs
+++ b/MediaBrowser.Providers/Music/AlbumMetadataService.cs
@@ -114,7 +114,7 @@ namespace MediaBrowser.Providers.Music
bool replaceData,
bool mergeMetadataSettings)
{
- ProviderUtils.MergeBaseItemData(source, target, lockedFields, replaceData, mergeMetadataSettings);
+ base.MergeData(source, target, lockedFields, replaceData, mergeMetadataSettings);
var sourceItem = source.Item;
var targetItem = target.Item;
diff --git a/MediaBrowser.Providers/Music/ArtistMetadataService.cs b/MediaBrowser.Providers/Music/ArtistMetadataService.cs
index e29475dd73..1f342c0db1 100644
--- a/MediaBrowser.Providers/Music/ArtistMetadataService.cs
+++ b/MediaBrowser.Providers/Music/ArtistMetadataService.cs
@@ -6,7 +6,6 @@ using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.Audio;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Providers;
-using MediaBrowser.Model.Entities;
using MediaBrowser.Model.IO;
using MediaBrowser.Providers.Manager;
using Microsoft.Extensions.Logging;
@@ -39,11 +38,5 @@ namespace MediaBrowser.Providers.Music
})
: item.GetRecursiveChildren(i => i is IHasArtist && !i.IsFolder);
}
-
- ///
- protected override void MergeData(MetadataResult source, MetadataResult target, MetadataField[] lockedFields, bool replaceData, bool mergeMetadataSettings)
- {
- ProviderUtils.MergeBaseItemData(source, target, lockedFields, replaceData, mergeMetadataSettings);
- }
}
}
diff --git a/MediaBrowser.Providers/Music/AudioMetadataService.cs b/MediaBrowser.Providers/Music/AudioMetadataService.cs
index 8b9fc8a08d..4577f77456 100644
--- a/MediaBrowser.Providers/Music/AudioMetadataService.cs
+++ b/MediaBrowser.Providers/Music/AudioMetadataService.cs
@@ -26,7 +26,7 @@ namespace MediaBrowser.Providers.Music
///
protected override void MergeData(MetadataResult