mirror of
https://github.com/SoPat712/allstarr.git
synced 2026-10-07 22:03:14 -04:00
fix(protocols): hide non-playable provider tracks
This commit is contained in:
2 files changed
+99
-3
No files matched your search
@@ -160,6 +160,10 @@ public sealed class ProtocolProviderStreamingGatewayTests
|
||||
request.ProviderPriority.SequenceEqual(new[] { "deezer" }))))
|
||||
.ReturnsAsync((ProviderRouteRequest request) =>
|
||||
EmptyPlan<IProviderMetadataCapability>(request));
|
||||
router.Setup(item => item.PlanAsync<IProviderStreamingCapability>(
|
||||
It.IsAny<ProviderRouteRequest>()))
|
||||
.ReturnsAsync((ProviderRouteRequest request) =>
|
||||
EmptyPlan<IProviderStreamingCapability>(request));
|
||||
var gateway = new ProtocolProviderGateway(
|
||||
router.Object,
|
||||
registry,
|
||||
@@ -174,6 +178,68 @@ public sealed class ProtocolProviderStreamingGatewayTests
|
||||
router.VerifyAll();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task MetadataSearch_DoesNotPublishSongsWithoutAPlayableRoute()
|
||||
{
|
||||
var metadata = new Mock<IProviderMetadataCapability>(MockBehavior.Strict);
|
||||
metadata.SetupGet(item => item.ProviderId).Returns("metadata-only");
|
||||
metadata.SetupGet(item => item.Capability).Returns(ProviderCapabilityKind.Metadata);
|
||||
metadata.Setup(item => item.SearchTracksAsync(
|
||||
It.IsAny<ProviderExecutionContext>(),
|
||||
It.IsAny<ProviderMetadataSearchRequest>()))
|
||||
.ReturnsAsync(ProviderOutcome<ProviderPage<ProviderTrackMetadata>>.Success(new(
|
||||
"metadata-only",
|
||||
[new ProviderTrackMetadata(
|
||||
new("metadata-only", ProviderResourceKind.Track, "track-1"),
|
||||
"Track",
|
||||
[new("Artist")])])));
|
||||
metadata.Setup(item => item.SearchAlbumsAsync(
|
||||
It.IsAny<ProviderExecutionContext>(),
|
||||
It.IsAny<ProviderMetadataSearchRequest>()))
|
||||
.ReturnsAsync(ProviderOutcome<ProviderPage<ProviderAlbumMetadata>>.Success(
|
||||
new("metadata-only", [])));
|
||||
metadata.Setup(item => item.SearchArtistsAsync(
|
||||
It.IsAny<ProviderExecutionContext>(),
|
||||
It.IsAny<ProviderMetadataSearchRequest>()))
|
||||
.ReturnsAsync(ProviderOutcome<ProviderPage<ProviderArtistMetadata>>.Success(
|
||||
new("metadata-only", [])));
|
||||
var registry = new ProviderRegistry([
|
||||
new ProviderRegistration(
|
||||
new ProviderDescriptor(
|
||||
"metadata-only",
|
||||
"Metadata only",
|
||||
"Fixture provider",
|
||||
ProviderOrigin.BuiltIn,
|
||||
"1",
|
||||
"1",
|
||||
[new ProviderCapabilityDescriptor(
|
||||
ProviderCapabilityKind.Metadata,
|
||||
ProviderCapabilitySupportState.Supported,
|
||||
ProviderAccountRequirement.None,
|
||||
"1",
|
||||
["searchTracks", "getTrack"])],
|
||||
new ProviderPermissionDescriptor()),
|
||||
[metadata.Object])
|
||||
]);
|
||||
var router = new Mock<IProviderRouter>(MockBehavior.Strict);
|
||||
router.Setup(item => item.PlanAsync<IProviderMetadataCapability>(
|
||||
It.IsAny<ProviderRouteRequest>()))
|
||||
.ReturnsAsync((ProviderRouteRequest request) =>
|
||||
MetadataPlan(request, registry, metadata.Object));
|
||||
var gateway = new ProtocolProviderGateway(
|
||||
router.Object,
|
||||
registry,
|
||||
Mock.Of<IProviderRouteAccountResolver>(),
|
||||
Mock.Of<IMusicMetadataService>(),
|
||||
new HttpClientFactory());
|
||||
|
||||
var result = await gateway.SearchAsync(Context(), "Track", 10, 0, 0);
|
||||
|
||||
Assert.Empty(result.Songs);
|
||||
metadata.VerifyAll();
|
||||
router.VerifyAll();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task MetadataRelationships_UseOnlyUniqueExactIdsFromTheSameProvider()
|
||||
{
|
||||
@@ -231,7 +297,14 @@ public sealed class ProtocolProviderStreamingGatewayTests
|
||||
It.Is<ProviderTrackLookupRequest>(request => request.Id.Value == "track-1")))
|
||||
.ReturnsAsync(ProviderOutcome<ProviderTrackMetadata>.Success(track));
|
||||
var registry = MetadataRegistry(capability.Object);
|
||||
var streaming = new Mock<IProviderStreamingCapability>(MockBehavior.Strict);
|
||||
streaming.SetupGet(item => item.ProviderId).Returns(providerId);
|
||||
streaming.SetupGet(item => item.Capability).Returns(ProviderCapabilityKind.Streaming);
|
||||
var router = new Mock<IProviderRouter>(MockBehavior.Strict);
|
||||
router.Setup(item => item.PlanAsync<IProviderStreamingCapability>(
|
||||
It.IsAny<ProviderRouteRequest>()))
|
||||
.ReturnsAsync((ProviderRouteRequest request) =>
|
||||
Plan(request, registry, streaming.Object));
|
||||
router.Setup(item => item.PlanAsync<IProviderMetadataCapability>(
|
||||
It.IsAny<ProviderRouteRequest>()))
|
||||
.ReturnsAsync((ProviderRouteRequest request) =>
|
||||
@@ -680,7 +753,8 @@ public sealed class ProtocolProviderStreamingGatewayTests
|
||||
return new ProviderRouteCandidate<IProviderStreamingCapability>(
|
||||
index,
|
||||
provider,
|
||||
provider.Capabilities.Single(),
|
||||
provider.Capabilities.Single(item =>
|
||||
item.Capability == ProviderCapabilityKind.Streaming),
|
||||
capability,
|
||||
new ProviderExecutionContext(
|
||||
request.Actor,
|
||||
|
||||
@@ -125,7 +125,11 @@ public sealed class ProtocolProviderGateway(
|
||||
query, songLimit, albumLimit, artistLimit, protocol.CancellationToken);
|
||||
return new SearchResult
|
||||
{
|
||||
Songs = publicLegacy.Songs.Where(item => IsRequestedPublicProvider(item.ExternalProvider)).Take(songLimit).ToList(),
|
||||
Songs = publicLegacy.Songs
|
||||
.Where(item => IsRequestedPublicProvider(item.ExternalProvider) &&
|
||||
IsPublicStreamingProvider(item.ExternalProvider))
|
||||
.Take(songLimit)
|
||||
.ToList(),
|
||||
Albums = publicLegacy.Albums.Where(item => IsRequestedPublicProvider(item.ExternalProvider)).Take(albumLimit).ToList(),
|
||||
Artists = publicLegacy.Artists.Where(item => IsRequestedPublicProvider(item.ExternalProvider)).Take(artistLimit).ToList()
|
||||
};
|
||||
@@ -136,6 +140,13 @@ public sealed class ProtocolProviderGateway(
|
||||
NormalizeProvider(itemProvider) == requestedProviderId);
|
||||
}
|
||||
var actor = protocol.RequireActor();
|
||||
var playableProviders = songLimit > 0
|
||||
? (await ResolvePlayableProviderOrderAsync(
|
||||
protocol,
|
||||
actor,
|
||||
ResolveProviderOrder(ProviderCapabilityKind.Streaming)))
|
||||
.ToHashSet(StringComparer.Ordinal)
|
||||
: [];
|
||||
var fetchLimit = Math.Clamp(Math.Max(songLimit, Math.Max(albumLimit, artistLimit)), 1, 200);
|
||||
var providerOrder = ResolveProviderOrder(ProviderCapabilityKind.Metadata)
|
||||
.Where(item => requestedProviderId == null || item == requestedProviderId)
|
||||
@@ -163,6 +174,7 @@ public sealed class ProtocolProviderGateway(
|
||||
await Task.WhenAll(songsTask, albumsTask, artistsTask);
|
||||
return new
|
||||
{
|
||||
ProviderId = NormalizeProvider(candidate.Provider.Id),
|
||||
SongsResult = await songsTask,
|
||||
AlbumsResult = await albumsTask,
|
||||
ArtistsResult = await artistsTask
|
||||
@@ -184,7 +196,7 @@ public sealed class ProtocolProviderGateway(
|
||||
var artists = outcome.ArtistsResult.IsSuccess
|
||||
? outcome.ArtistsResult.RequireValue().Items
|
||||
: [];
|
||||
if (outcome.SongsResult.IsSuccess)
|
||||
if (playableProviders.Contains(outcome.ProviderId) && outcome.SongsResult.IsSuccess)
|
||||
{
|
||||
routed.Songs.AddRange(outcome.SongsResult.RequireValue().Items
|
||||
.Select(item => EnrichRelationships(Map(item), albums, artists)));
|
||||
@@ -286,6 +298,7 @@ public sealed class ProtocolProviderGateway(
|
||||
ProviderActorContext actor,
|
||||
IReadOnlyList<string> configuredProviderOrder)
|
||||
{
|
||||
if (configuredProviderOrder.Count == 0) return [];
|
||||
var streaming = await router.PlanAsync<IProviderStreamingCapability>(Request(
|
||||
protocol,
|
||||
actor,
|
||||
@@ -877,6 +890,15 @@ public sealed class ProtocolProviderGateway(
|
||||
.AccountRequirement == ProviderAccountRequirement.None);
|
||||
}
|
||||
|
||||
private bool IsPublicStreamingProvider(string? providerId)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(providerId)) return false;
|
||||
return registry.FindByCapability(ProviderCapabilityKind.Streaming, includeNonOperational: true)
|
||||
.Any(descriptor => descriptor.Id.Equals(providerId, StringComparison.Ordinal) &&
|
||||
descriptor.Capabilities.Single(item => item.Capability == ProviderCapabilityKind.Streaming)
|
||||
.AccountRequirement == ProviderAccountRequirement.None);
|
||||
}
|
||||
|
||||
private IReadOnlyList<string> ResolveProviderOrder(ProviderCapabilityKind capability)
|
||||
{
|
||||
var (settingKey, environmentKey, fallback) = capability switch
|
||||
|
||||
Reference in new issue
Block a user