Merge branch 'subsonic-all-playlists' into 'master'
Fix #684: Include shared/public playlists in Subsonic API responses See merge request funkwhale/funkwhale!561
This commit is contained in:
commit
293e526f2f
|
@ -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()
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
Include shared/public playlists in Subsonic API responses (#684)
|
Loading…
Reference in New Issue