Verified Commit cd361287 authored by Eliot Berriot's avatar Eliot Berriot
Browse files

Fix #684: Include shared/public playlists in Subsonic API responses

parent e4954f8b
......@@ -18,7 +18,7 @@ def authenticate(username, password):
if password.startswith("enc:"):
password = password.replace("enc:", "", 1)
password = binascii.unhexlify(password).decode("utf-8")
user = User.objects.get(
user = User.objects.select_related("actor").get(
username__iexact=username, is_active=True, subsonic_api_token=password
)
except (User.DoesNotExist, binascii.Error):
......@@ -29,7 +29,7 @@ def authenticate(username, password):
def authenticate_salt(username, salt, token):
try:
user = User.objects.get(
user = User.objects.select_related("actor").get(
username=username, is_active=True, subsonic_api_token__isnull=False
)
except User.DoesNotExist:
......
......@@ -11,7 +11,7 @@ from rest_framework.serializers import ValidationError
import funkwhale_api
from funkwhale_api.activity import record
from funkwhale_api.common import preferences, utils as common_utils
from funkwhale_api.common import fields, preferences, utils as common_utils
from funkwhale_api.favorites.models import TrackFavorite
from funkwhale_api.music import models as music_models
from funkwhale_api.music import utils
......@@ -68,9 +68,7 @@ def find_object(
{
"error": {
"code": 70,
"message": "{} not found".format(
qs.model.__class__.__name__
),
"message": "{} not found".format(qs.model.__name__),
}
}
)
......@@ -82,6 +80,14 @@ def find_object(
return decorator
def get_playlist_qs(request):
qs = playlists_models.Playlist.objects.filter(
fields.privacy_level_query(request.user)
)
qs = qs.with_tracks_count().exclude(_tracks_count=0).select_related("user")
return qs.order_by("-creation_date")
class SubsonicViewSet(viewsets.GenericViewSet):
content_negotiation_class = negotiation.SubsonicContentNegociation
authentication_classes = [authentication.SubsonicAuthentication]
......@@ -398,11 +404,9 @@ class SubsonicViewSet(viewsets.GenericViewSet):
url_path="getPlaylists",
)
def get_playlists(self, request, *args, **kwargs):
playlists = request.user.playlists.with_tracks_count().select_related("user")
qs = get_playlist_qs(request)
data = {
"playlists": {
"playlist": [serializers.get_playlist_data(p) for p in playlists]
}
"playlists": {"playlist": [serializers.get_playlist_data(p) for p in qs]}
}
return response.Response(data)
......@@ -412,7 +416,7 @@ class SubsonicViewSet(viewsets.GenericViewSet):
url_name="get_playlist",
url_path="getPlaylist",
)
@find_object(playlists_models.Playlist.objects.with_tracks_count())
@find_object(lambda request: get_playlist_qs(request))
def get_playlist(self, request, *args, **kwargs):
playlist = kwargs.pop("obj")
data = {"playlist": serializers.get_playlist_detail_data(playlist)}
......
......@@ -387,21 +387,40 @@ def test_search3(f, db, logged_in_api_client, factories):
def test_get_playlists(f, db, logged_in_api_client, factories):
url = reverse("api:subsonic-get_playlists")
assert url.endswith("getPlaylists") is True
playlist = factories["playlists.Playlist"](user=logged_in_api_client.user)
playlist1 = factories["playlists.PlaylistTrack"](
playlist__user=logged_in_api_client.user
).playlist
playlist2 = factories["playlists.PlaylistTrack"](
playlist__privacy_level="everyone"
).playlist
playlist3 = factories["playlists.PlaylistTrack"](
playlist__privacy_level="instance"
).playlist
# private
factories["playlists.PlaylistTrack"](playlist__privacy_level="me")
# no track
factories["playlists.Playlist"](privacy_level="everyone")
response = logged_in_api_client.get(url, {"f": f})
qs = playlist.__class__.objects.with_tracks_count()
assert response.status_code == 200
assert response.data == {
"playlists": {"playlist": [serializers.get_playlist_data(qs.first())]}
qs = (
playlist1.__class__.objects.with_tracks_count()
.filter(pk__in=[playlist1.pk, playlist2.pk, playlist3.pk])
.order_by("-creation_date")
)
expected = {
"playlists": {"playlist": [serializers.get_playlist_data(p) for p in qs]}
}
assert response.status_code == 200
assert response.data == expected
@pytest.mark.parametrize("f", ["json"])
def test_get_playlist(f, db, logged_in_api_client, factories):
url = reverse("api:subsonic-get_playlist")
assert url.endswith("getPlaylist") is True
playlist = factories["playlists.Playlist"](user=logged_in_api_client.user)
playlist = factories["playlists.PlaylistTrack"](
playlist__user=logged_in_api_client.user
).playlist
response = logged_in_api_client.get(url, {"f": f, "id": playlist.pk})
qs = playlist.__class__.objects.with_tracks_count()
......
Include shared/public playlists in Subsonic API responses (#684)
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