diff --git a/api/funkwhale_api/subsonic/serializers.py b/api/funkwhale_api/subsonic/serializers.py index fc21a99f2aa540024754810d96865638d2a1eb28..5308146e14f6ffa1cc416242285e5fcf9261f14d 100644 --- a/api/funkwhale_api/subsonic/serializers.py +++ b/api/funkwhale_api/subsonic/serializers.py @@ -24,7 +24,8 @@ class GetArtistsSerializer(serializers.Serializer): first_letter_mapping = collections.defaultdict(list) for artist in values: - first_letter_mapping[artist["name"][0].upper()].append(artist) + if artist["name"]: + first_letter_mapping[artist["name"][0].upper()].append(artist) for letter, artists in sorted(first_letter_mapping.items()): letter_data = { @@ -129,6 +130,14 @@ class GetAlbumSerializer(serializers.Serializer): return payload +class GetSongSerializer(serializers.Serializer): + def to_representation(self, track): + tf = track.files.all() + if not len(tf): + return {} + return get_track_data(track.album, track, tf[0]) + + def get_starred_tracks_data(favorites): by_track_id = {f.track_id: f for f in favorites} tracks = ( diff --git a/api/funkwhale_api/subsonic/views.py b/api/funkwhale_api/subsonic/views.py index bb5f44166875f34ee7b625c60d93dd004d9bc6a0..b2469a8cade7aad9c27d3aa6e5aed649704477ee 100644 --- a/api/funkwhale_api/subsonic/views.py +++ b/api/funkwhale_api/subsonic/views.py @@ -38,7 +38,7 @@ def find_object(queryset, model_field="pk", field="id", cast=int): ) try: value = cast(raw_value) - except (TypeError, ValidationError): + except (ValueError, TypeError, ValidationError): return response.Response( { "error": { @@ -147,6 +147,15 @@ class SubsonicViewSet(viewsets.GenericViewSet): return response.Response(payload, status=200) + @list_route(methods=["get", "post"], url_name="get_song", url_path="getSong") + @find_object(music_models.Track.objects.all()) + def get_song(self, request, *args, **kwargs): + track = kwargs.pop("obj") + data = serializers.GetSongSerializer(track).data + payload = {"song": data} + + return response.Response(payload, status=200) + @list_route( methods=["get", "post"], url_name="get_artist_info2", url_path="getArtistInfo2" ) diff --git a/api/tests/subsonic/test_serializers.py b/api/tests/subsonic/test_serializers.py index 6fdf02e2d11bd56b1d523935a1ecd4863d77b806..bd07008dfa70eda3a67867bc6dd39c27553eed91 100644 --- a/api/tests/subsonic/test_serializers.py +++ b/api/tests/subsonic/test_serializers.py @@ -6,6 +6,7 @@ def test_get_artists_serializer(factories): artist1 = factories["music.Artist"](name="eliot") artist2 = factories["music.Artist"](name="Ellena") artist3 = factories["music.Artist"](name="Rilay") + artist4 = factories["music.Artist"](name="") # Shouldn't be serialised factories["music.Album"].create_batch(size=3, artist=artist1) factories["music.Album"].create_batch(size=2, artist=artist2) @@ -28,7 +29,7 @@ def test_get_artists_serializer(factories): } queryset = artist1.__class__.objects.filter( - pk__in=[artist1.pk, artist2.pk, artist3.pk] + pk__in=[artist1.pk, artist2.pk, artist3.pk, artist4.pk] ) assert serializers.GetArtistsSerializer(queryset).data == expected diff --git a/api/tests/subsonic/test_views.py b/api/tests/subsonic/test_views.py index b7431efab4a05962fa6ed5d34ba0c963336ad9a3..1c7c528ccb8f00afbbe15719b1c06dc9bfde22ac 100644 --- a/api/tests/subsonic/test_views.py +++ b/api/tests/subsonic/test_views.py @@ -102,6 +102,17 @@ def test_get_artist(f, db, logged_in_api_client, factories): assert response.data == expected +@pytest.mark.parametrize("f", ["xml", "json"]) +def test_get_invalid_artist(f, db, logged_in_api_client, factories): + url = reverse("api:subsonic-get-artist") + assert url.endswith("getArtist") is True + expected = {"error": {"code": 0, "message": 'For input string "asdf"'}} + response = logged_in_api_client.get(url, {"id": "asdf"}) + + assert response.status_code == 200 + assert response.data == expected + + @pytest.mark.parametrize("f", ["xml", "json"]) def test_get_artist_info2(f, db, logged_in_api_client, factories): url = reverse("api:subsonic-get-artist-info2") @@ -129,6 +140,20 @@ def test_get_album(f, db, logged_in_api_client, factories): assert response.data == expected +@pytest.mark.parametrize("f", ["xml", "json"]) +def test_get_song(f, db, logged_in_api_client, factories): + url = reverse("api:subsonic-get-song") + assert url.endswith("getSong") is True + artist = factories["music.Artist"]() + album = factories["music.Album"](artist=artist) + track = factories["music.Track"](album=album) + tf = factories["music.TrackFile"](track=track) + response = logged_in_api_client.get(url, {"f": f, "id": track.pk}) + + assert response.status_code == 200 + assert response.data == {"song": serializers.get_track_data(track.album, track, tf)} + + @pytest.mark.parametrize("f", ["xml", "json"]) def test_stream(f, db, logged_in_api_client, factories, mocker): url = reverse("api:subsonic-stream")