diff --git a/MediaBrowser.Controller/Providers/IMetadataService.cs b/MediaBrowser.Controller/Providers/IMetadataService.cs
index 05fbb18ee4..f0f1d18624 100644
--- a/MediaBrowser.Controller/Providers/IMetadataService.cs
+++ b/MediaBrowser.Controller/Providers/IMetadataService.cs
@@ -23,6 +23,11 @@ namespace MediaBrowser.Controller.Providers
/// true if this instance can refresh the specified item.
bool CanRefresh(BaseItem item);
+ ///
+ /// Determines whether this instance primarily targets the specified type.
+ ///
+ /// The type.
+ /// true if this instance primarily targets the specified type.
bool CanRefreshPrimary(Type type);
///
diff --git a/tests/Jellyfin.Providers.Tests/Manager/ProviderManagerTests.cs b/tests/Jellyfin.Providers.Tests/Manager/ProviderManagerTests.cs
index 8100dcfa6f..5845b31be8 100644
--- a/tests/Jellyfin.Providers.Tests/Manager/ProviderManagerTests.cs
+++ b/tests/Jellyfin.Providers.Tests/Manager/ProviderManagerTests.cs
@@ -1,6 +1,8 @@
using System;
using System.Collections.Generic;
using System.Linq;
+using System.Threading;
+using System.Threading.Tasks;
using MediaBrowser.Controller.BaseItemManager;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Entities;
@@ -9,6 +11,7 @@ using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Configuration;
using MediaBrowser.Providers.Manager;
+using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
using Moq;
using Xunit;
@@ -17,6 +20,95 @@ namespace Jellyfin.Providers.Tests.Manager
{
public class ProviderManagerTests
{
+ private static readonly ILogger _logger = new NullLogger();
+
+ private static TheoryData[], int> RefreshSingleItemOrderData()
+ => new ()
+ {
+ // no order set, uses provided order
+ {
+ new[]
+ {
+ MockIMetadataService(true, true),
+ MockIMetadataService(true, true)
+ },
+ 0
+ },
+ // sort order sets priority when all match
+ {
+ new[]
+ {
+ MockIMetadataService(true, true, 1),
+ MockIMetadataService(true, true, 0),
+ MockIMetadataService(true, true, 2)
+ },
+ 1
+ },
+ // CanRefreshPrimary prioritized
+ {
+ new[]
+ {
+ MockIMetadataService(false, true),
+ MockIMetadataService(true, true),
+ },
+ 1
+ },
+ // falls back to CanRefresh
+ {
+ new[]
+ {
+ MockIMetadataService(false, false),
+ MockIMetadataService(false, true)
+ },
+ 1
+ },
+ };
+
+ [Theory]
+ [MemberData(nameof(RefreshSingleItemOrderData))]
+ public void RefreshSingleItem_ServiceOrdering_FollowsPriority(Mock[] servicesList, int expectedIndex)
+ {
+ var item = new Movie();
+
+ using var providerManager = GetProviderManager();
+ AddParts(providerManager, metadataServices: servicesList.Select(s => s.Object).ToArray());
+
+ var refreshOptions = new MetadataRefreshOptions(Mock.Of(MockBehavior.Strict));
+ var actual = providerManager.RefreshSingleItem(item, refreshOptions, CancellationToken.None);
+
+ Assert.Equal(ItemUpdateType.MetadataDownload, actual.Result);
+ for (var i = 0; i < servicesList.Length; i++)
+ {
+ if (i == expectedIndex)
+ {
+ servicesList[i].Verify(mock => mock.RefreshMetadata(It.IsAny(), It.IsAny(), It.IsAny()), Times.Once());
+ }
+ else
+ {
+ servicesList[i].Verify(mock => mock.RefreshMetadata(It.IsAny(), It.IsAny(), It.IsAny()), Times.Never());
+ }
+ }
+ }
+
+ [Theory]
+ [InlineData(true)]
+ [InlineData(false)]
+ public void RefreshSingleItem_RefreshMetadata_WhenServiceFound(bool serviceFound)
+ {
+ var item = new Movie();
+
+ var servicesList = new[] { MockIMetadataService(false, serviceFound) };
+
+ using var providerManager = GetProviderManager();
+ AddParts(providerManager, metadataServices: servicesList.Select(s => s.Object).ToArray());
+
+ var refreshOptions = new MetadataRefreshOptions(Mock.Of(MockBehavior.Strict));
+ var actual = providerManager.RefreshSingleItem(item, refreshOptions, CancellationToken.None);
+
+ var expectedResult = serviceFound ? ItemUpdateType.MetadataDownload : ItemUpdateType.None;
+ Assert.Equal(expectedResult, actual.Result);
+ }
+
private static TheoryData GetImageProvidersOrderData()
=> new ()
{
@@ -313,6 +405,20 @@ namespace Jellyfin.Providers.Tests.Manager
Assert.Equal(expected ? 1 : 0, actualProviders.Length);
}
+ private static Mock MockIMetadataService(bool refreshPrimary, bool canRefresh, int order = 0)
+ {
+ var service = new Mock(MockBehavior.Strict);
+ service.Setup(s => s.Order)
+ .Returns(order);
+ service.Setup(s => s.CanRefreshPrimary(It.IsAny()))
+ .Returns(refreshPrimary);
+ service.Setup(s => s.CanRefresh(It.IsAny()))
+ .Returns(canRefresh);
+ service.Setup(s => s.RefreshMetadata(It.IsAny(), It.IsAny(), It.IsAny()))
+ .Returns(Task.FromResult(ItemUpdateType.MetadataDownload));
+ return service;
+ }
+
private static IImageProvider MockIImageProvider(string name, BaseItem expectedType, bool supports = true, int? order = null, bool errorOnSupported = false)
where TProviderType : class, IImageProvider
{
@@ -460,11 +566,11 @@ namespace Jellyfin.Providers.Tests.Manager
null,
serverConfigurationManager.Object,
null,
- new NullLogger(),
+ _logger,
null,
null,
libraryManager.Object,
- baseItemManager);
+ baseItemManager!);
return providerManager;
}