Commit 955f88e9 authored by Eliot Berriot's avatar Eliot Berriot
Browse files

Merge branch 'subsonic-getsongsbygenre' into 'develop'

Implemented missing getSongsByGenre subsonic endpoint

See merge request funkwhale/funkwhale!897
parents dc731532 921317a2
......@@ -108,7 +108,6 @@ class TrackFactory(
title = factory.Faker("sentence", nb_words=3)
mbid = factory.Faker("uuid4")
album = factory.SubFactory(AlbumFactory)
artist = factory.SelfAttribute("album.artist")
position = 1
playable = playable_factory("track")
......@@ -124,6 +123,26 @@ class TrackFactory(
fid=factory.Faker("federation_url", local=True), album__local=True
)
@factory.post_generation
def artist(self, created, extracted, **kwargs):
"""
A bit intricated, because we want to be able to specify a different
track artist with a fallback on album artist if nothing is specified.
And handle cases where build or build_batch are used (so no db calls)
"""
if extracted:
self.artist = extracted
elif kwargs:
if created:
self.artist = ArtistFactory(**kwargs)
else:
self.artist = ArtistFactory.build(**kwargs)
elif self.album:
self.artist = self.album.artist
if created:
self.save()
@factory.post_generation
def license(self, created, extracted, **kwargs):
if not created:
......
......@@ -333,6 +333,48 @@ class SubsonicViewSet(viewsets.GenericViewSet):
}
return response.Response(data)
@action(
detail=False,
methods=["get", "post"],
url_name="get_songs_by_genre",
url_path="getSongsByGenre",
)
def get_songs_by_genre(self, request, *args, **kwargs):
data = request.GET or request.POST
actor = utils.get_actor_from_request(request)
queryset = music_models.Track.objects.all().exclude(
moderation_filters.get_filtered_content_query(
moderation_filters.USER_FILTER_CONFIG["TRACK"], request.user
)
)
queryset = queryset.playable_by(actor)
try:
size = int(
data["count"]
) # yep. Some endpoints have size, other have count…
except (TypeError, KeyError, ValueError):
size = 50
genre = data.get("genre")
queryset = (
queryset.playable_by(actor)
.filter(
Q(tagged_items__tag__name=genre)
| Q(artist__tagged_items__tag__name=genre)
| Q(album__artist__tagged_items__tag__name=genre)
| Q(album__tagged_items__tag__name=genre)
)
.prefetch_related("uploads")
.distinct()
.order_by("-creation_date")[:size]
)
data = {
"songsByGenre": {
"song": serializers.GetSongSerializer(queryset, many=True).data
}
}
return response.Response(data)
@action(
detail=False,
methods=["get", "post"],
......
......@@ -469,6 +469,28 @@ def test_get_album_list2_by_genre(f, db, logged_in_api_client, factories):
}
@pytest.mark.parametrize("f", ["json"])
@pytest.mark.parametrize(
"tags_field",
["set_tags", "artist__set_tags", "album__set_tags", "album__artist__set_tags"],
)
def test_get_songs_by_genre(f, tags_field, db, logged_in_api_client, factories):
url = reverse("api:subsonic-get_songs_by_genre")
assert url.endswith("getSongsByGenre") is True
track1 = factories["music.Track"](playable=True, **{tags_field: ["Rock"]})
track2 = factories["music.Track"](playable=True, **{tags_field: ["Rock"]})
factories["music.Track"](playable=True, **{tags_field: ["Pop"]})
expected = {
"songsByGenre": {"song": serializers.get_song_list_data([track2, track1])}
}
response = logged_in_api_client.get(
url, {"f": f, "count": 5, "offset": 0, "genre": "rock"}
)
assert response.status_code == 200
assert response.data == expected
@pytest.mark.parametrize("f", ["json"])
def test_search3(f, db, logged_in_api_client, factories):
url = reverse("api:subsonic-search3")
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment