Updated rest framework to 3.9

This commit is contained in:
Eliot Berriot 2019-01-11 13:33:35 +01:00
parent 4a6df06360
commit 14392ebb0c
No known key found for this signature in database
GPG Key ID: DD6965E2476E5C27
14 changed files with 187 additions and 116 deletions

View File

@ -10,7 +10,7 @@ from funkwhale_api.playlists import views as playlists_views
from funkwhale_api.subsonic.views import SubsonicViewSet from funkwhale_api.subsonic.views import SubsonicViewSet
router = routers.SimpleRouter() router = routers.SimpleRouter()
router.register(r"settings", GlobalPreferencesViewSet, base_name="settings") router.register(r"settings", GlobalPreferencesViewSet, basename="settings")
router.register(r"activity", activity_views.ActivityViewSet, "activity") router.register(r"activity", activity_views.ActivityViewSet, "activity")
router.register(r"tags", views.TagViewSet, "tags") router.register(r"tags", views.TagViewSet, "tags")
router.register(r"tracks", views.TrackViewSet, "tracks") router.register(r"tracks", views.TrackViewSet, "tracks")
@ -27,7 +27,7 @@ router.register(
v1_patterns = router.urls v1_patterns = router.urls
subsonic_router = routers.SimpleRouter(trailing_slash=False) subsonic_router = routers.SimpleRouter(trailing_slash=False)
subsonic_router.register(r"subsonic/rest", SubsonicViewSet, base_name="subsonic") subsonic_router.register(r"subsonic/rest", SubsonicViewSet, basename="subsonic")
v1_patterns += [ v1_patterns += [

View File

@ -1,9 +1,9 @@
from rest_framework import response from rest_framework import response
from rest_framework.decorators import list_route from rest_framework import decorators
def action_route(serializer_class): def action_route(serializer_class):
@list_route(methods=["post"]) @decorators.action(methods=["post"], detail=False)
def action(self, request, *args, **kwargs): def action(self, request, *args, **kwargs):
queryset = self.get_queryset() queryset = self.get_queryset()
serializer = serializer_class(request.data, queryset=queryset) serializer = serializer_class(request.data, queryset=queryset)

View File

@ -1,5 +1,5 @@
from rest_framework import mixins, status, viewsets from rest_framework import mixins, status, viewsets
from rest_framework.decorators import list_route from rest_framework.decorators import action
from rest_framework.permissions import IsAuthenticatedOrReadOnly from rest_framework.permissions import IsAuthenticatedOrReadOnly
from rest_framework.response import Response from rest_framework.response import Response
@ -62,7 +62,7 @@ class TrackFavoriteViewSet(
favorite = models.TrackFavorite.add(track=track, user=self.request.user) favorite = models.TrackFavorite.add(track=track, user=self.request.user)
return favorite return favorite
@list_route(methods=["delete", "post"]) @action(methods=["delete", "post"], detail=False)
def remove(self, request, *args, **kwargs): def remove(self, request, *args, **kwargs):
try: try:
pk = int(request.data["track"]) pk = int(request.data["track"])
@ -72,7 +72,7 @@ class TrackFavoriteViewSet(
favorite.delete() favorite.delete()
return Response([], status=status.HTTP_204_NO_CONTENT) return Response([], status=status.HTTP_204_NO_CONTENT)
@list_route(methods=["get"]) @action(methods=["get"], detail=False)
def all(self, request, *args, **kwargs): def all(self, request, *args, **kwargs):
""" """
Return all the favorites of the current user, with only limited data Return all the favorites of the current user, with only limited data

View File

@ -66,7 +66,7 @@ class LibraryFollowViewSet(
context["actor"] = self.request.user.actor context["actor"] = self.request.user.actor
return context return context
@decorators.detail_route(methods=["post"]) @decorators.action(methods=["post"], detail=True)
def accept(self, request, *args, **kwargs): def accept(self, request, *args, **kwargs):
try: try:
follow = self.queryset.get( follow = self.queryset.get(
@ -77,7 +77,7 @@ class LibraryFollowViewSet(
update_follow(follow, approved=True) update_follow(follow, approved=True)
return response.Response(status=204) return response.Response(status=204)
@decorators.detail_route(methods=["post"]) @decorators.action(methods=["post"], detail=True)
def reject(self, request, *args, **kwargs): def reject(self, request, *args, **kwargs):
try: try:
follow = self.queryset.get( follow = self.queryset.get(
@ -105,7 +105,7 @@ class LibraryViewSet(mixins.RetrieveModelMixin, viewsets.GenericViewSet):
qs = super().get_queryset() qs = super().get_queryset()
return qs.viewable_by(actor=self.request.user.actor) return qs.viewable_by(actor=self.request.user.actor)
@decorators.detail_route(methods=["post"]) @decorators.action(methods=["post"], detail=True)
def scan(self, request, *args, **kwargs): def scan(self, request, *args, **kwargs):
library = self.get_object() library = self.get_object()
if library.actor.get_user(): if library.actor.get_user():
@ -122,7 +122,7 @@ class LibraryViewSet(mixins.RetrieveModelMixin, viewsets.GenericViewSet):
) )
return response.Response({"status": "skipped"}, 200) return response.Response({"status": "skipped"}, 200)
@decorators.list_route(methods=["post"]) @decorators.action(methods=["post"], detail=False)
def fetch(self, request, *args, **kwargs): def fetch(self, request, *args, **kwargs):
try: try:
fid = request.data["fid"] fid = request.data["fid"]
@ -175,7 +175,7 @@ class InboxItemViewSet(
qs = super().get_queryset() qs = super().get_queryset()
return qs.filter(actor=self.request.user.actor) return qs.filter(actor=self.request.user.actor)
@decorators.list_route(methods=["post"]) @decorators.action(methods=["post"], detail=False)
def action(self, request, *args, **kwargs): def action(self, request, *args, **kwargs):
queryset = self.get_queryset() queryset = self.get_queryset()
serializer = api_serializers.InboxItemActionSerializer( serializer = api_serializers.InboxItemActionSerializer(

View File

@ -3,7 +3,7 @@ from django.core import paginator
from django.http import HttpResponse from django.http import HttpResponse
from django.urls import reverse from django.urls import reverse
from rest_framework import exceptions, mixins, response, viewsets from rest_framework import exceptions, mixins, response, viewsets
from rest_framework.decorators import detail_route, list_route from rest_framework.decorators import action
from funkwhale_api.common import preferences from funkwhale_api.common import preferences
from funkwhale_api.music import models as music_models from funkwhale_api.music import models as music_models
@ -23,7 +23,7 @@ class SharedViewSet(FederationMixin, viewsets.GenericViewSet):
authentication_classes = [authentication.SignatureAuthentication] authentication_classes = [authentication.SignatureAuthentication]
renderer_classes = [renderers.ActivityPubRenderer] renderer_classes = [renderers.ActivityPubRenderer]
@list_route(methods=["post"]) @action(methods=["post"], detail=False)
def inbox(self, request, *args, **kwargs): def inbox(self, request, *args, **kwargs):
if request.method.lower() == "post" and request.actor is None: if request.method.lower() == "post" and request.actor is None:
raise exceptions.AuthenticationFailed( raise exceptions.AuthenticationFailed(
@ -42,7 +42,7 @@ class ActorViewSet(FederationMixin, mixins.RetrieveModelMixin, viewsets.GenericV
queryset = models.Actor.objects.local().select_related("user") queryset = models.Actor.objects.local().select_related("user")
serializer_class = serializers.ActorSerializer serializer_class = serializers.ActorSerializer
@detail_route(methods=["get", "post"]) @action(methods=["get", "post"], detail=True)
def inbox(self, request, *args, **kwargs): def inbox(self, request, *args, **kwargs):
if request.method.lower() == "post" and request.actor is None: if request.method.lower() == "post" and request.actor is None:
raise exceptions.AuthenticationFailed( raise exceptions.AuthenticationFailed(
@ -52,17 +52,17 @@ class ActorViewSet(FederationMixin, mixins.RetrieveModelMixin, viewsets.GenericV
activity.receive(activity=request.data, on_behalf_of=request.actor) activity.receive(activity=request.data, on_behalf_of=request.actor)
return response.Response({}, status=200) return response.Response({}, status=200)
@detail_route(methods=["get", "post"]) @action(methods=["get", "post"], detail=True)
def outbox(self, request, *args, **kwargs): def outbox(self, request, *args, **kwargs):
return response.Response({}, status=200) return response.Response({}, status=200)
@detail_route(methods=["get"]) @action(methods=["get"], detail=True)
def followers(self, request, *args, **kwargs): def followers(self, request, *args, **kwargs):
self.get_object() self.get_object()
# XXX to implement # XXX to implement
return response.Response({}) return response.Response({})
@detail_route(methods=["get"]) @action(methods=["get"], detail=True)
def following(self, request, *args, **kwargs): def following(self, request, *args, **kwargs):
self.get_object() self.get_object()
# XXX to implement # XXX to implement
@ -74,7 +74,7 @@ class WellKnownViewSet(viewsets.GenericViewSet):
permission_classes = [] permission_classes = []
renderer_classes = [renderers.JSONRenderer, renderers.WebfingerRenderer] renderer_classes = [renderers.JSONRenderer, renderers.WebfingerRenderer]
@list_route(methods=["get"]) @action(methods=["get"], detail=False)
def nodeinfo(self, request, *args, **kwargs): def nodeinfo(self, request, *args, **kwargs):
if not preferences.get("instance__nodeinfo_enabled"): if not preferences.get("instance__nodeinfo_enabled"):
return HttpResponse(status=404) return HttpResponse(status=404)
@ -88,7 +88,7 @@ class WellKnownViewSet(viewsets.GenericViewSet):
} }
return response.Response(data) return response.Response(data)
@list_route(methods=["get"]) @action(methods=["get"], detail=False)
def webfinger(self, request, *args, **kwargs): def webfinger(self, request, *args, **kwargs):
if not preferences.get("federation__enabled"): if not preferences.get("federation__enabled"):
return HttpResponse(status=405) return HttpResponse(status=405)
@ -180,7 +180,7 @@ class MusicLibraryViewSet(
return response.Response(data) return response.Response(data)
@detail_route(methods=["get"]) @action(methods=["get"], detail=True)
def followers(self, request, *args, **kwargs): def followers(self, request, *args, **kwargs):
self.get_object() self.get_object()
# XXX Implement this # XXX Implement this

View File

@ -1,5 +1,5 @@
from rest_framework import mixins, response, viewsets from rest_framework import mixins, response, viewsets
from rest_framework.decorators import detail_route, list_route from rest_framework import decorators as rest_decorators
from django.shortcuts import get_object_or_404 from django.shortcuts import get_object_or_404
from funkwhale_api.common import preferences, decorators from funkwhale_api.common import preferences, decorators
@ -35,7 +35,7 @@ class ManageUploadViewSet(
"duration", "duration",
] ]
@list_route(methods=["post"]) @rest_decorators.action(methods=["post"], detail=False)
def action(self, request, *args, **kwargs): def action(self, request, *args, **kwargs):
queryset = self.get_queryset() queryset = self.get_queryset()
serializer = serializers.ManageUploadActionSerializer( serializer = serializers.ManageUploadActionSerializer(
@ -87,7 +87,7 @@ class ManageInvitationViewSet(
def perform_create(self, serializer): def perform_create(self, serializer):
serializer.save(owner=self.request.user) serializer.save(owner=self.request.user)
@list_route(methods=["post"]) @rest_decorators.action(methods=["post"], detail=False)
def action(self, request, *args, **kwargs): def action(self, request, *args, **kwargs):
queryset = self.get_queryset() queryset = self.get_queryset()
serializer = serializers.ManageInvitationActionSerializer( serializer = serializers.ManageInvitationActionSerializer(
@ -125,14 +125,14 @@ class ManageDomainViewSet(
"instance_policy", "instance_policy",
] ]
@detail_route(methods=["get"]) @rest_decorators.action(methods=["get"], detail=True)
def nodeinfo(self, request, *args, **kwargs): def nodeinfo(self, request, *args, **kwargs):
domain = self.get_object() domain = self.get_object()
federation_tasks.update_domain_nodeinfo(domain_name=domain.name) federation_tasks.update_domain_nodeinfo(domain_name=domain.name)
domain.refresh_from_db() domain.refresh_from_db()
return response.Response(domain.nodeinfo, status=200) return response.Response(domain.nodeinfo, status=200)
@detail_route(methods=["get"]) @rest_decorators.action(methods=["get"], detail=True)
def stats(self, request, *args, **kwargs): def stats(self, request, *args, **kwargs):
domain = self.get_object() domain = self.get_object()
return response.Response(domain.get_stats(), status=200) return response.Response(domain.get_stats(), status=200)
@ -176,7 +176,7 @@ class ManageActorViewSet(
return obj return obj
@detail_route(methods=["get"]) @rest_decorators.action(methods=["get"], detail=True)
def stats(self, request, *args, **kwargs): def stats(self, request, *args, **kwargs):
domain = self.get_object() domain = self.get_object()
return response.Response(domain.get_stats(), status=200) return response.Response(domain.get_stats(), status=200)

View File

@ -11,7 +11,7 @@ from rest_framework import mixins
from rest_framework import permissions from rest_framework import permissions
from rest_framework import settings as rest_settings from rest_framework import settings as rest_settings
from rest_framework import views, viewsets from rest_framework import views, viewsets
from rest_framework.decorators import detail_route, list_route from rest_framework.decorators import action
from rest_framework.response import Response from rest_framework.response import Response
from taggit.models import Tag from taggit.models import Tag
@ -28,25 +28,25 @@ logger = logging.getLogger(__name__)
def get_libraries(filter_uploads): def get_libraries(filter_uploads):
def view(self, request, *args, **kwargs): def libraries(self, request, *args, **kwargs):
obj = self.get_object() obj = self.get_object()
actor = utils.get_actor_from_request(request) actor = utils.get_actor_from_request(request)
uploads = models.Upload.objects.all() uploads = models.Upload.objects.all()
uploads = filter_uploads(obj, uploads) uploads = filter_uploads(obj, uploads)
uploads = uploads.playable_by(actor) uploads = uploads.playable_by(actor)
libraries = models.Library.objects.filter( qs = models.Library.objects.filter(
pk__in=uploads.values_list("library", flat=True) pk__in=uploads.values_list("library", flat=True)
).annotate(_uploads_count=Count("uploads")) ).annotate(_uploads_count=Count("uploads"))
libraries = libraries.select_related("actor") qs = qs.select_related("actor")
page = self.paginate_queryset(libraries) page = self.paginate_queryset(qs)
if page is not None: if page is not None:
serializer = federation_api_serializers.LibrarySerializer(page, many=True) serializer = federation_api_serializers.LibrarySerializer(page, many=True)
return self.get_paginated_response(serializer.data) return self.get_paginated_response(serializer.data)
serializer = federation_api_serializers.LibrarySerializer(libraries, many=True) serializer = federation_api_serializers.LibrarySerializer(qs, many=True)
return Response(serializer.data) return Response(serializer.data)
return view return libraries
class TagViewSetMixin(object): class TagViewSetMixin(object):
@ -73,7 +73,7 @@ class ArtistViewSet(viewsets.ReadOnlyModelViewSet):
) )
return queryset.prefetch_related(Prefetch("albums", queryset=albums)) return queryset.prefetch_related(Prefetch("albums", queryset=albums))
libraries = detail_route(methods=["get"])( libraries = action(methods=["get"], detail=True)(
get_libraries( get_libraries(
filter_uploads=lambda o, uploads: uploads.filter( filter_uploads=lambda o, uploads: uploads.filter(
Q(track__artist=o) | Q(track__album__artist=o) Q(track__artist=o) | Q(track__album__artist=o)
@ -101,7 +101,7 @@ class AlbumViewSet(viewsets.ReadOnlyModelViewSet):
qs = queryset.prefetch_related(Prefetch("tracks", queryset=tracks)) qs = queryset.prefetch_related(Prefetch("tracks", queryset=tracks))
return qs return qs
libraries = detail_route(methods=["get"])( libraries = action(methods=["get"], detail=True)(
get_libraries(filter_uploads=lambda o, uploads: uploads.filter(track__album=o)) get_libraries(filter_uploads=lambda o, uploads: uploads.filter(track__album=o))
) )
@ -144,7 +144,9 @@ class LibraryViewSet(
) )
instance.delete() instance.delete()
@detail_route(methods=["get"]) follows = action
@action(methods=["get"], detail=True)
@transaction.non_atomic_requests @transaction.non_atomic_requests
def follows(self, request, *args, **kwargs): def follows(self, request, *args, **kwargs):
library = self.get_object() library = self.get_object()
@ -193,7 +195,7 @@ class TrackViewSet(TagViewSetMixin, viewsets.ReadOnlyModelViewSet):
) )
return queryset return queryset
@detail_route(methods=["get"]) @action(methods=["get"], detail=True)
@transaction.non_atomic_requests @transaction.non_atomic_requests
def lyrics(self, request, *args, **kwargs): def lyrics(self, request, *args, **kwargs):
try: try:
@ -218,7 +220,7 @@ class TrackViewSet(TagViewSetMixin, viewsets.ReadOnlyModelViewSet):
serializer = serializers.LyricsSerializer(lyrics) serializer = serializers.LyricsSerializer(lyrics)
return Response(serializer.data) return Response(serializer.data)
libraries = detail_route(methods=["get"])( libraries = action(methods=["get"], detail=True)(
get_libraries(filter_uploads=lambda o, uploads: uploads.filter(track=o)) get_libraries(filter_uploads=lambda o, uploads: uploads.filter(track=o))
) )
@ -388,7 +390,7 @@ class UploadViewSet(
qs = super().get_queryset() qs = super().get_queryset()
return qs.filter(library__actor=self.request.user.actor) return qs.filter(library__actor=self.request.user.actor)
@list_route(methods=["post"]) @action(methods=["post"], detail=False)
def action(self, request, *args, **kwargs): def action(self, request, *args, **kwargs):
queryset = self.get_queryset() queryset = self.get_queryset()
serializer = serializers.UploadActionSerializer(request.data, queryset=queryset) serializer = serializers.UploadActionSerializer(request.data, queryset=queryset)

View File

@ -1,5 +1,5 @@
from rest_framework import viewsets from rest_framework import viewsets
from rest_framework.decorators import list_route from rest_framework.decorators import action
from rest_framework.response import Response from rest_framework.response import Response
from rest_framework.views import APIView from rest_framework.views import APIView
@ -47,19 +47,19 @@ class ReleaseBrowse(APIView):
class SearchViewSet(viewsets.ViewSet): class SearchViewSet(viewsets.ViewSet):
permission_classes = [ConditionalAuthentication] permission_classes = [ConditionalAuthentication]
@list_route(methods=["get"]) @action(methods=["get"], detail=False)
def recordings(self, request, *args, **kwargs): def recordings(self, request, *args, **kwargs):
query = request.GET["query"] query = request.GET["query"]
results = api.recordings.search(query) results = api.recordings.search(query)
return Response(results) return Response(results)
@list_route(methods=["get"]) @action(methods=["get"], detail=False)
def releases(self, request, *args, **kwargs): def releases(self, request, *args, **kwargs):
query = request.GET["query"] query = request.GET["query"]
results = api.releases.search(query) results = api.releases.search(query)
return Response(results) return Response(results)
@list_route(methods=["get"]) @action(methods=["get"], detail=False)
def artists(self, request, *args, **kwargs): def artists(self, request, *args, **kwargs):
query = request.GET["query"] query = request.GET["query"]
results = api.artists.search(query) results = api.artists.search(query)

View File

@ -1,7 +1,7 @@
from django.db import transaction from django.db import transaction
from django.db.models import Count from django.db.models import Count
from rest_framework import exceptions, mixins, viewsets from rest_framework import exceptions, mixins, viewsets
from rest_framework.decorators import detail_route from rest_framework.decorators import action
from rest_framework.permissions import IsAuthenticatedOrReadOnly from rest_framework.permissions import IsAuthenticatedOrReadOnly
from rest_framework.response import Response from rest_framework.response import Response
@ -36,7 +36,7 @@ class PlaylistViewSet(
filterset_class = filters.PlaylistFilter filterset_class = filters.PlaylistFilter
ordering_fields = ("id", "name", "creation_date", "modification_date") ordering_fields = ("id", "name", "creation_date", "modification_date")
@detail_route(methods=["get"]) @action(methods=["get"], detail=True)
def tracks(self, request, *args, **kwargs): def tracks(self, request, *args, **kwargs):
playlist = self.get_object() playlist = self.get_object()
plts = playlist.playlist_tracks.all().for_nested_serialization( plts = playlist.playlist_tracks.all().for_nested_serialization(
@ -46,7 +46,7 @@ class PlaylistViewSet(
data = {"count": len(plts), "results": serializer.data} data = {"count": len(plts), "results": serializer.data}
return Response(data, status=200) return Response(data, status=200)
@detail_route(methods=["post"]) @action(methods=["post"], detail=True)
@transaction.atomic @transaction.atomic
def add(self, request, *args, **kwargs): def add(self, request, *args, **kwargs):
playlist = self.get_object() playlist = self.get_object()
@ -67,7 +67,7 @@ class PlaylistViewSet(
data = {"count": len(plts), "results": serializer.data} data = {"count": len(plts), "results": serializer.data}
return Response(data, status=201) return Response(data, status=201)
@detail_route(methods=["delete"]) @action(methods=["delete"], detail=True)
@transaction.atomic @transaction.atomic
def clear(self, request, *args, **kwargs): def clear(self, request, *args, **kwargs):
playlist = self.get_object() playlist = self.get_object()

View File

@ -1,6 +1,6 @@
from django.db.models import Q from django.db.models import Q
from rest_framework import mixins, permissions, status, viewsets from rest_framework import mixins, permissions, status, viewsets
from rest_framework.decorators import detail_route, list_route from rest_framework.decorators import action
from rest_framework.response import Response from rest_framework.response import Response
from funkwhale_api.common import permissions as common_permissions from funkwhale_api.common import permissions as common_permissions
@ -40,7 +40,7 @@ class RadioViewSet(
def perform_update(self, serializer): def perform_update(self, serializer):
return serializer.save(user=self.request.user) return serializer.save(user=self.request.user)
@detail_route(methods=["get"]) @action(methods=["get"], detail=True)
def tracks(self, request, *args, **kwargs): def tracks(self, request, *args, **kwargs):
radio = self.get_object() radio = self.get_object()
tracks = radio.get_candidates().for_nested_serialization() tracks = radio.get_candidates().for_nested_serialization()
@ -50,14 +50,14 @@ class RadioViewSet(
serializer = TrackSerializer(page, many=True) serializer = TrackSerializer(page, many=True)
return self.get_paginated_response(serializer.data) return self.get_paginated_response(serializer.data)
@list_route(methods=["get"]) @action(methods=["get"], detail=False)
def filters(self, request, *args, **kwargs): def filters(self, request, *args, **kwargs):
serializer = serializers.FilterSerializer( serializer = serializers.FilterSerializer(
filters.registry.exposed_filters, many=True filters.registry.exposed_filters, many=True
) )
return Response(serializer.data) return Response(serializer.data)
@list_route(methods=["post"]) @action(methods=["post"], detail=False)
def validate(self, request, *args, **kwargs): def validate(self, request, *args, **kwargs):
try: try:
f_list = request.data["filters"] f_list = request.data["filters"]

View File

@ -1,11 +1,12 @@
import datetime import datetime
import functools
from django.conf import settings from django.conf import settings
from django.utils import timezone from django.utils import timezone
from rest_framework import exceptions from rest_framework import exceptions
from rest_framework import permissions as rest_permissions from rest_framework import permissions as rest_permissions
from rest_framework import renderers, response, viewsets from rest_framework import renderers, response, viewsets
from rest_framework.decorators import list_route from rest_framework.decorators import action
from rest_framework.serializers import ValidationError from rest_framework.serializers import ValidationError
import funkwhale_api import funkwhale_api
@ -25,6 +26,7 @@ def find_object(
queryset, model_field="pk", field="id", cast=int, filter_playable=False queryset, model_field="pk", field="id", cast=int, filter_playable=False
): ):
def decorator(func): def decorator(func):
@functools.wraps(func)
def inner(self, request, *args, **kwargs): def inner(self, request, *args, **kwargs):
data = request.GET or request.POST data = request.GET or request.POST
try: try:
@ -110,12 +112,13 @@ class SubsonicViewSet(viewsets.GenericViewSet):
return response.Response(payload, status=200) return response.Response(payload, status=200)
@list_route(methods=["get", "post"], permission_classes=[]) @action(detail=False, methods=["get", "post"], permission_classes=[])
def ping(self, request, *args, **kwargs): def ping(self, request, *args, **kwargs):
data = {"status": "ok", "version": "1.16.0"} data = {"status": "ok", "version": "1.16.0"}
return response.Response(data, status=200) return response.Response(data, status=200)
@list_route( @action(
detail=False,
methods=["get", "post"], methods=["get", "post"],
url_name="get_license", url_name="get_license",
permissions_classes=[], permissions_classes=[],
@ -136,7 +139,12 @@ class SubsonicViewSet(viewsets.GenericViewSet):
} }
return response.Response(data, status=200) return response.Response(data, status=200)
@list_route(methods=["get", "post"], url_name="get_artists", url_path="getArtists") @action(
detail=False,
methods=["get", "post"],
url_name="get_artists",
url_path="getArtists",
)
def get_artists(self, request, *args, **kwargs): def get_artists(self, request, *args, **kwargs):
artists = music_models.Artist.objects.all().playable_by( artists = music_models.Artist.objects.all().playable_by(
utils.get_actor_from_request(request) utils.get_actor_from_request(request)
@ -146,7 +154,12 @@ class SubsonicViewSet(viewsets.GenericViewSet):
return response.Response(payload, status=200) return response.Response(payload, status=200)
@list_route(methods=["get", "post"], url_name="get_indexes", url_path="getIndexes") @action(
detail=False,
methods=["get", "post"],
url_name="get_indexes",
url_path="getIndexes",
)
def get_indexes(self, request, *args, **kwargs): def get_indexes(self, request, *args, **kwargs):
artists = music_models.Artist.objects.all().playable_by( artists = music_models.Artist.objects.all().playable_by(
utils.get_actor_from_request(request) utils.get_actor_from_request(request)
@ -156,7 +169,12 @@ class SubsonicViewSet(viewsets.GenericViewSet):
return response.Response(payload, status=200) return response.Response(payload, status=200)
@list_route(methods=["get", "post"], url_name="get_artist", url_path="getArtist") @action(
detail=False,
methods=["get", "post"],
url_name="get_artist",
url_path="getArtist",
)
@find_object(music_models.Artist.objects.all(), filter_playable=True) @find_object(music_models.Artist.objects.all(), filter_playable=True)
def get_artist(self, request, *args, **kwargs): def get_artist(self, request, *args, **kwargs):
artist = kwargs.pop("obj") artist = kwargs.pop("obj")
@ -165,7 +183,9 @@ class SubsonicViewSet(viewsets.GenericViewSet):
return response.Response(payload, status=200) return response.Response(payload, status=200)
@list_route(methods=["get", "post"], url_name="get_song", url_path="getSong") @action(
detail=False, methods=["get", "post"], url_name="get_song", url_path="getSong"
)
@find_object(music_models.Track.objects.all(), filter_playable=True) @find_object(music_models.Track.objects.all(), filter_playable=True)
def get_song(self, request, *args, **kwargs): def get_song(self, request, *args, **kwargs):
track = kwargs.pop("obj") track = kwargs.pop("obj")
@ -174,8 +194,11 @@ class SubsonicViewSet(viewsets.GenericViewSet):
return response.Response(payload, status=200) return response.Response(payload, status=200)
@list_route( @action(
methods=["get", "post"], url_name="get_artist_info2", url_path="getArtistInfo2" detail=False,
methods=["get", "post"],
url_name="get_artist_info2",
url_path="getArtistInfo2",
) )
@find_object(music_models.Artist.objects.all(), filter_playable=True) @find_object(music_models.Artist.objects.all(), filter_playable=True)
def get_artist_info2(self, request, *args, **kwargs): def get_artist_info2(self, request, *args, **kwargs):
@ -183,7 +206,9 @@ class SubsonicViewSet(viewsets.GenericViewSet):
return response.Response(payload, status=200) return response.Response(payload, status=200)
@list_route(methods=["get", "post"], url_name="get_album", url_path="getAlbum") @action(
detail=False, methods=["get", "post"], url_name="get_album", url_path="getAlbum"
)
@find_object( @find_object(
music_models.Album.objects.select_related("artist"), filter_playable=True music_models.Album.objects.select_related("artist"), filter_playable=True
) )
@ -193,7 +218,7 @@ class SubsonicViewSet(viewsets.GenericViewSet):
payload = {"album": data} payload = {"album": data}
return response.Response(payload, status=200) return response.Response(payload, status=200)
@list_route(methods=["get", "post"], url_name="stream", url_path="stream") @action(detail=False, methods=["get", "post"], url_name="stream", url_path="stream")
@find_object(music_models.Track.objects.all(), filter_playable=True) @find_object(music_models.Track.objects.all(), filter_playable=True)
def stream(self, request, *args, **kwargs): def stream(self, request, *args, **kwargs):
data = request.GET or request.POST data = request.GET or request.POST
@ -208,30 +233,36 @@ class SubsonicViewSet(viewsets.GenericViewSet):
format = None format = None
return music_views.handle_serve(upload=upload, user=request.user, format=format) return music_views.handle_serve(upload=upload, user=request.user, format=format)
@list_route(methods=["get", "post"], url_name="star", url_path="star") @action(detail=False, methods=["get", "post"], url_name="star", url_path="star")
@find_object(music_models.Track.objects.all()) @find_object(music_models.Track.objects.all())
def star(self, request, *args, **kwargs): def star(self, request, *args, **kwargs):
track = kwargs.pop("obj") track = kwargs.pop("obj")
TrackFavorite.add(user=request.user, track=track) TrackFavorite.add(user=request.user, track=track)
return response.Response({"status": "ok"}) return response.Response({"status": "ok"})
@list_route(methods=["get", "post"], url_name="unstar", url_path="unstar") @action(detail=False, methods=["get", "post"], url_name="unstar", url_path="unstar")
@find_object(music_models.Track.objects.all()) @find_object(music_models.Track.objects.all())
def unstar(self, request, *args, **kwargs): def unstar(self, request, *args, **kwargs):
track = kwargs.pop("obj") track = kwargs.pop("obj")
request.user.track_favorites.filter(track=track).delete() request.user.track_favorites.filter(track=track).delete()
return response.Response({"status": "ok"}) return response.Response({"status": "ok"})
@list_route( @action(
methods=["get", "post"], url_name="get_starred2", url_path="getStarred2" detail=False,
methods=["get", "post"],
url_name="get_starred2",
url_path="getStarred2",
) )
def get_starred2(self, request, *args, **kwargs): def get_starred2(self, request, *args, **kwargs):
favorites = request.user.track_favorites.all() favorites = request.user.track_favorites.all()
data = {"starred2": {"song": serializers.get_starred_tracks_data(favorites)}} data = {"starred2": {"song": serializers.get_starred_tracks_data(favorites)}}
return response.Response(data) return response.Response(data)
@list_route( @action(
methods=["get", "post"], url_name="get_random_songs", url_path="getRandomSongs" detail=False,
methods=["get", "post"],
url_name="get_random_songs",
url_path="getRandomSongs",
) )
def get_random_songs(self, request, *args, **kwargs): def get_random_songs(self, request, *args, **kwargs):
data = request.GET or request.POST data = request.GET or request.POST
@ -253,14 +284,22 @@ class SubsonicViewSet(viewsets.GenericViewSet):
} }
return response.Response(data) return response.Response(data)
@list_route(methods=["get", "post"], url_name="get_starred", url_path="getStarred") @action(
detail=False,
methods=["get", "post"],
url_name="get_starred",
url_path="getStarred",
)
def get_starred(self, request, *args, **kwargs): def get_starred(self, request, *args, **kwargs):
favorites = request.user.track_favorites.all() favorites = request.user.track_favorites.all()
data = {"starred": {"song": serializers.get_starred_tracks_data(favorites)}} data = {"starred": {"song": serializers.get_starred_tracks_data(favorites)}}
return response.Response(data) return response.Response(data)
@list_route( @action(
methods=["get", "post"], url_name="get_album_list2", url_path="getAlbumList2" detail=False,
methods=["get", "post"],
url_name="get_album_list2",
url_path="getAlbumList2",
) )
def get_album_list2(self, request, *args, **kwargs): def get_album_list2(self, request, *args, **kwargs):
queryset = music_models.Album.objects.with_tracks_count().order_by( queryset = music_models.Album.objects.with_tracks_count().order_by(
@ -287,7 +326,9 @@ class SubsonicViewSet(viewsets.GenericViewSet):
data = {"albumList2": {"album": serializers.get_album_list2_data(queryset)}} data = {"albumList2": {"album": serializers.get_album_list2_data(queryset)}}
return response.Response(data) return response.Response(data)
@list_route(methods=["get", "post"], url_name="search3", url_path="search3") @action(
detail=False, methods=["get", "post"], url_name="search3", url_path="search3"
)
def search3(self, request, *args, **kwargs): def search3(self, request, *args, **kwargs):
data = request.GET or request.POST data = request.GET or request.POST
query = str(data.get("query", "")).replace("*", "") query = str(data.get("query", "")).replace("*", "")
@ -350,8 +391,11 @@ class SubsonicViewSet(viewsets.GenericViewSet):
payload["searchResult3"][c["subsonic"]] = c["serializer"](queryset) payload["searchResult3"][c["subsonic"]] = c["serializer"](queryset)
return response.Response(payload) return response.Response(payload)
@list_route( @action(
methods=["get", "post"], url_name="get_playlists", url_path="getPlaylists" detail=False,
methods=["get", "post"],
url_name="get_playlists",
url_path="getPlaylists",
) )
def get_playlists(self, request, *args, **kwargs): def get_playlists(self, request, *args, **kwargs):
playlists = request.user.playlists.with_tracks_count().select_related("user") playlists = request.user.playlists.with_tracks_count().select_related("user")
@ -362,8 +406,11 @@ class SubsonicViewSet(viewsets.GenericViewSet):
} }
return response.Response(data) return response.Response(data)
@list_route( @action(
methods=["get", "post"], url_name="get_playlist", url_path="getPlaylist" detail=False,
methods=["get", "post"],
url_name="get_playlist",
url_path="getPlaylist",
) )
@find_object(playlists_models.Playlist.objects.with_tracks_count()) @find_object(playlists_models.Playlist.objects.with_tracks_count())
def get_playlist(self, request, *args, **kwargs): def get_playlist(self, request, *args, **kwargs):
@ -371,8 +418,11 @@ class SubsonicViewSet(viewsets.GenericViewSet):
data = {"playlist": serializers.get_playlist_detail_data(playlist)} data = {"playlist": serializers.get_playlist_detail_data(playlist)}
return response.Response(data) return response.Response(data)
@list_route( @action(
methods=["get", "post"], url_name="update_playlist", url_path="updatePlaylist" detail=False,
methods=["get", "post"],
url_name="update_playlist",
url_path="updatePlaylist",
) )
@find_object(lambda request: request.user.playlists.all(), field="playlistId") @find_object(lambda request: request.user.playlists.all(), field="playlistId")
def update_playlist(self, request, *args, **kwargs): def update_playlist(self, request, *args, **kwargs):
@ -413,8 +463,11 @@ class SubsonicViewSet(viewsets.GenericViewSet):
data = {"status": "ok"} data = {"status": "ok"}
return response.Response(data) return response.Response(data)
@list_route( @action(
methods=["get", "post"], url_name="delete_playlist", url_path="deletePlaylist" detail=False,
methods=["get", "post"],
url_name="delete_playlist",
url_path="deletePlaylist",
) )
@find_object(lambda request: request.user.playlists.all()) @find_object(lambda request: request.user.playlists.all())
def delete_playlist(self, request, *args, **kwargs): def delete_playlist(self, request, *args, **kwargs):
@ -423,8 +476,11 @@ class SubsonicViewSet(viewsets.GenericViewSet):
data = {"status": "ok"} data = {"status": "ok"}
return response.Response(data) return response.Response(data)
@list_route( @action(
methods=["get", "post"], url_name="create_playlist", url_path="createPlaylist" detail=False,
methods=["get", "post"],
url_name="create_playlist",
url_path="createPlaylist",
) )
def create_playlist(self, request, *args, **kwargs): def create_playlist(self, request, *args, **kwargs):
data = request.GET or request.POST data = request.GET or request.POST
@ -462,7 +518,12 @@ class SubsonicViewSet(viewsets.GenericViewSet):
data = {"playlist": serializers.get_playlist_detail_data(playlist)} data = {"playlist": serializers.get_playlist_detail_data(playlist)}
return response.Response(data) return response.Response(data)
@list_route(methods=["get", "post"], url_name="get_avatar", url_path="getAvatar") @action(
detail=False,
methods=["get", "post"],
url_name="get_avatar",
url_path="getAvatar",
)
@find_object( @find_object(
queryset=users_models.User.objects.exclude(avatar=None).exclude(avatar=""), queryset=users_models.User.objects.exclude(avatar=None).exclude(avatar=""),
model_field="username__iexact", model_field="username__iexact",
@ -479,7 +540,9 @@ class SubsonicViewSet(viewsets.GenericViewSet):
r[file_header] = path r[file_header] = path
return r return r
@list_route(methods=["get", "post"], url_name="get_user", url_path="getUser") @action(
detail=False, methods=["get", "post"], url_name="get_user", url_path="getUser"
)
@find_object( @find_object(
queryset=lambda request: users_models.User.objects.filter(pk=request.user.pk), queryset=lambda request: users_models.User.objects.filter(pk=request.user.pk),
model_field="username__iexact", model_field="username__iexact",
@ -490,7 +553,8 @@ class SubsonicViewSet(viewsets.GenericViewSet):
data = {"user": serializers.get_user_detail_data(request.user)} data = {"user": serializers.get_user_detail_data(request.user)}
return response.Response(data) return response.Response(data)
@list_route( @action(
detail=False,
methods=["get", "post"], methods=["get", "post"],
url_name="get_music_folders", url_name="get_music_folders",
url_path="getMusicFolders", url_path="getMusicFolders",
@ -499,8 +563,11 @@ class SubsonicViewSet(viewsets.GenericViewSet):
data = {"musicFolders": {"musicFolder": [{"id": 1, "name": "Music"}]}} data = {"musicFolders": {"musicFolder": [{"id": 1, "name": "Music"}]}}
return response.Response(data) return response.Response(data)
@list_route( @action(
methods=["get", "post"], url_name="get_cover_art", url_path="getCoverArt" detail=False,
methods=["get", "post"],
url_name="get_cover_art",
url_path="getCoverArt",
) )
def get_cover_art(self, request, *args, **kwargs): def get_cover_art(self, request, *args, **kwargs):
data = request.GET or request.POST data = request.GET or request.POST
@ -536,7 +603,9 @@ class SubsonicViewSet(viewsets.GenericViewSet):
r[file_header] = path r[file_header] = path
return r return r
@list_route(methods=["get", "post"], url_name="scrobble", url_path="scrobble") @action(
detail=False, methods=["get", "post"], url_name="scrobble", url_path="scrobble"
)
def scrobble(self, request, *args, **kwargs): def scrobble(self, request, *args, **kwargs):
data = request.GET or request.POST data = request.GET or request.POST
serializer = serializers.ScrobbleSerializer( serializer = serializers.ScrobbleSerializer(

View File

@ -1,7 +1,7 @@
from allauth.account.adapter import get_adapter from allauth.account.adapter import get_adapter
from rest_auth.registration.views import RegisterView as BaseRegisterView from rest_auth.registration.views import RegisterView as BaseRegisterView
from rest_framework import mixins, viewsets from rest_framework import mixins, viewsets
from rest_framework.decorators import detail_route, list_route from rest_framework.decorators import action
from rest_framework.response import Response from rest_framework.response import Response
from funkwhale_api.common import preferences from funkwhale_api.common import preferences
@ -28,13 +28,13 @@ class UserViewSet(mixins.UpdateModelMixin, viewsets.GenericViewSet):
serializer_class = serializers.UserWriteSerializer serializer_class = serializers.UserWriteSerializer
lookup_field = "username" lookup_field = "username"
@list_route(methods=["get"]) @action(methods=["get"], detail=False)
def me(self, request, *args, **kwargs): def me(self, request, *args, **kwargs):
"""Return information about the current user""" """Return information about the current user"""
serializer = serializers.MeSerializer(request.user) serializer = serializers.MeSerializer(request.user)
return Response(serializer.data) return Response(serializer.data)
@detail_route(methods=["get", "post", "delete"], url_path="subsonic-token") @action(methods=["get", "post", "delete"], url_path="subsonic-token", detail=True)
def subsonic_token(self, request, *args, **kwargs): def subsonic_token(self, request, *args, **kwargs):
if not self.request.user.username == kwargs.get("username"): if not self.request.user.username == kwargs.get("username"):
return Response(status=403) return Response(status=403)

View File

@ -30,7 +30,7 @@ celery>=4.1,<4.2
# Your custom requirements go here # Your custom requirements go here
django-cors-headers>=2.1,<2.2 django-cors-headers>=2.1,<2.2
musicbrainzngs==0.6 musicbrainzngs==0.6
djangorestframework>=3.7,<3.8 djangorestframework>=3.9,<3.10
djangorestframework-jwt>=1.11,<1.12 djangorestframework-jwt>=1.11,<1.12
oauth2client<4 oauth2client<4
pendulum>=2,<3 pendulum>=2,<3

View File

@ -45,7 +45,7 @@ def test_exception_wrong_credentials(f, db, api_client):
@pytest.mark.parametrize("f", ["json"]) @pytest.mark.parametrize("f", ["json"])
def test_exception_missing_credentials(f, db, api_client): def test_exception_missing_credentials(f, db, api_client):
url = reverse("api:subsonic-get-artists") url = reverse("api:subsonic-get_artists")
response = api_client.get(url) response = api_client.get(url)
expected = { expected = {
@ -66,7 +66,7 @@ def test_disabled_subsonic(preferences, api_client):
@pytest.mark.parametrize("f", ["xml", "json"]) @pytest.mark.parametrize("f", ["xml", "json"])
def test_get_license(f, db, logged_in_api_client, mocker): def test_get_license(f, db, logged_in_api_client, mocker):
url = reverse("api:subsonic-get-license") url = reverse("api:subsonic-get_license")
assert url.endswith("getLicense") is True assert url.endswith("getLicense") is True
now = timezone.now() now = timezone.now()
mocker.patch("django.utils.timezone.now", return_value=now) mocker.patch("django.utils.timezone.now", return_value=now)
@ -100,7 +100,7 @@ def test_ping(f, db, api_client):
def test_get_artists( def test_get_artists(
f, db, logged_in_api_client, factories, mocker, queryset_equal_queries f, db, logged_in_api_client, factories, mocker, queryset_equal_queries
): ):
url = reverse("api:subsonic-get-artists") url = reverse("api:subsonic-get_artists")
assert url.endswith("getArtists") is True assert url.endswith("getArtists") is True
factories["music.Artist"].create_batch(size=3, playable=True) factories["music.Artist"].create_batch(size=3, playable=True)
playable_by = mocker.spy(music_models.ArtistQuerySet, "playable_by") playable_by = mocker.spy(music_models.ArtistQuerySet, "playable_by")
@ -120,7 +120,7 @@ def test_get_artists(
def test_get_artist( def test_get_artist(
f, db, logged_in_api_client, factories, mocker, queryset_equal_queries f, db, logged_in_api_client, factories, mocker, queryset_equal_queries
): ):
url = reverse("api:subsonic-get-artist") url = reverse("api:subsonic-get_artist")
assert url.endswith("getArtist") is True assert url.endswith("getArtist") is True
artist = factories["music.Artist"](playable=True) artist = factories["music.Artist"](playable=True)
factories["music.Album"].create_batch(size=3, artist=artist, playable=True) factories["music.Album"].create_batch(size=3, artist=artist, playable=True)
@ -136,7 +136,7 @@ def test_get_artist(
@pytest.mark.parametrize("f", ["json"]) @pytest.mark.parametrize("f", ["json"])
def test_get_invalid_artist(f, db, logged_in_api_client, factories): def test_get_invalid_artist(f, db, logged_in_api_client, factories):
url = reverse("api:subsonic-get-artist") url = reverse("api:subsonic-get_artist")
assert url.endswith("getArtist") is True assert url.endswith("getArtist") is True
expected = {"error": {"code": 0, "message": 'For input string "asdf"'}} expected = {"error": {"code": 0, "message": 'For input string "asdf"'}}
response = logged_in_api_client.get(url, {"id": "asdf"}) response = logged_in_api_client.get(url, {"id": "asdf"})
@ -149,7 +149,7 @@ def test_get_invalid_artist(f, db, logged_in_api_client, factories):
def test_get_artist_info2( def test_get_artist_info2(
f, db, logged_in_api_client, factories, mocker, queryset_equal_queries f, db, logged_in_api_client, factories, mocker, queryset_equal_queries
): ):
url = reverse("api:subsonic-get-artist-info2") url = reverse("api:subsonic-get_artist_info2")
assert url.endswith("getArtistInfo2") is True assert url.endswith("getArtistInfo2") is True
artist = factories["music.Artist"](playable=True) artist = factories["music.Artist"](playable=True)
playable_by = mocker.spy(music_models.ArtistQuerySet, "playable_by") playable_by = mocker.spy(music_models.ArtistQuerySet, "playable_by")
@ -167,7 +167,7 @@ def test_get_artist_info2(
def test_get_album( def test_get_album(
f, db, logged_in_api_client, factories, mocker, queryset_equal_queries f, db, logged_in_api_client, factories, mocker, queryset_equal_queries
): ):
url = reverse("api:subsonic-get-album") url = reverse("api:subsonic-get_album")
assert url.endswith("getAlbum") is True assert url.endswith("getAlbum") is True
artist = factories["music.Artist"]() artist = factories["music.Artist"]()
album = factories["music.Album"](artist=artist) album = factories["music.Album"](artist=artist)
@ -188,7 +188,7 @@ def test_get_album(
def test_get_song( def test_get_song(
f, db, logged_in_api_client, factories, mocker, queryset_equal_queries f, db, logged_in_api_client, factories, mocker, queryset_equal_queries
): ):
url = reverse("api:subsonic-get-song") url = reverse("api:subsonic-get_song")
assert url.endswith("getSong") is True assert url.endswith("getSong") is True
artist = factories["music.Artist"]() artist = factories["music.Artist"]()
album = factories["music.Album"](artist=artist) album = factories["music.Album"](artist=artist)
@ -264,7 +264,7 @@ def test_unstar(f, db, logged_in_api_client, factories):
@pytest.mark.parametrize("f", ["json"]) @pytest.mark.parametrize("f", ["json"])
def test_get_starred2(f, db, logged_in_api_client, factories): def test_get_starred2(f, db, logged_in_api_client, factories):
url = reverse("api:subsonic-get-starred2") url = reverse("api:subsonic-get_starred2")
assert url.endswith("getStarred2") is True assert url.endswith("getStarred2") is True
track = factories["music.Track"]() track = factories["music.Track"]()
favorite = factories["favorites.TrackFavorite"]( favorite = factories["favorites.TrackFavorite"](
@ -280,7 +280,7 @@ def test_get_starred2(f, db, logged_in_api_client, factories):
@pytest.mark.parametrize("f", ["json"]) @pytest.mark.parametrize("f", ["json"])
def test_get_random_songs(f, db, logged_in_api_client, factories, mocker): def test_get_random_songs(f, db, logged_in_api_client, factories, mocker):
url = reverse("api:subsonic-get-random-songs") url = reverse("api:subsonic-get_random_songs")
assert url.endswith("getRandomSongs") is True assert url.endswith("getRandomSongs") is True
track1 = factories["music.Track"]() track1 = factories["music.Track"]()
track2 = factories["music.Track"]() track2 = factories["music.Track"]()
@ -303,7 +303,7 @@ def test_get_random_songs(f, db, logged_in_api_client, factories, mocker):
@pytest.mark.parametrize("f", ["json"]) @pytest.mark.parametrize("f", ["json"])
def test_get_starred(f, db, logged_in_api_client, factories): def test_get_starred(f, db, logged_in_api_client, factories):
url = reverse("api:subsonic-get-starred") url = reverse("api:subsonic-get_starred")
assert url.endswith("getStarred") is True assert url.endswith("getStarred") is True
track = factories["music.Track"]() track = factories["music.Track"]()
favorite = factories["favorites.TrackFavorite"]( favorite = factories["favorites.TrackFavorite"](
@ -321,7 +321,7 @@ def test_get_starred(f, db, logged_in_api_client, factories):
def test_get_album_list2( def test_get_album_list2(
f, db, logged_in_api_client, factories, mocker, queryset_equal_queries f, db, logged_in_api_client, factories, mocker, queryset_equal_queries
): ):
url = reverse("api:subsonic-get-album-list2") url = reverse("api:subsonic-get_album_list2")
assert url.endswith("getAlbumList2") is True assert url.endswith("getAlbumList2") is True
album1 = factories["music.Album"](playable=True) album1 = factories["music.Album"](playable=True)
album2 = factories["music.Album"](playable=True) album2 = factories["music.Album"](playable=True)
@ -338,7 +338,7 @@ def test_get_album_list2(
@pytest.mark.parametrize("f", ["json"]) @pytest.mark.parametrize("f", ["json"])
def test_get_album_list2_pagination(f, db, logged_in_api_client, factories): def test_get_album_list2_pagination(f, db, logged_in_api_client, factories):
url = reverse("api:subsonic-get-album-list2") url = reverse("api:subsonic-get_album_list2")
assert url.endswith("getAlbumList2") is True assert url.endswith("getAlbumList2") is True
album1 = factories["music.Album"](playable=True) album1 = factories["music.Album"](playable=True)
factories["music.Album"](playable=True) factories["music.Album"](playable=True)
@ -385,7 +385,7 @@ def test_search3(f, db, logged_in_api_client, factories):
@pytest.mark.parametrize("f", ["json"]) @pytest.mark.parametrize("f", ["json"])
def test_get_playlists(f, db, logged_in_api_client, factories): def test_get_playlists(f, db, logged_in_api_client, factories):
url = reverse("api:subsonic-get-playlists") url = reverse("api:subsonic-get_playlists")
assert url.endswith("getPlaylists") is True assert url.endswith("getPlaylists") is True
playlist = factories["playlists.Playlist"](user=logged_in_api_client.user) playlist = factories["playlists.Playlist"](user=logged_in_api_client.user)
response = logged_in_api_client.get(url, {"f": f}) response = logged_in_api_client.get(url, {"f": f})
@ -399,7 +399,7 @@ def test_get_playlists(f, db, logged_in_api_client, factories):
@pytest.mark.parametrize("f", ["json"]) @pytest.mark.parametrize("f", ["json"])
def test_get_playlist(f, db, logged_in_api_client, factories): def test_get_playlist(f, db, logged_in_api_client, factories):
url = reverse("api:subsonic-get-playlist") url = reverse("api:subsonic-get_playlist")
assert url.endswith("getPlaylist") is True assert url.endswith("getPlaylist") is True
playlist = factories["playlists.Playlist"](user=logged_in_api_client.user) playlist = factories["playlists.Playlist"](user=logged_in_api_client.user)
response = logged_in_api_client.get(url, {"f": f, "id": playlist.pk}) response = logged_in_api_client.get(url, {"f": f, "id": playlist.pk})
@ -413,7 +413,7 @@ def test_get_playlist(f, db, logged_in_api_client, factories):
@pytest.mark.parametrize("f", ["json"]) @pytest.mark.parametrize("f", ["json"])
def test_update_playlist(f, db, logged_in_api_client, factories): def test_update_playlist(f, db, logged_in_api_client, factories):
url = reverse("api:subsonic-update-playlist") url = reverse("api:subsonic-update_playlist")
assert url.endswith("updatePlaylist") is True assert url.endswith("updatePlaylist") is True
playlist = factories["playlists.Playlist"](user=logged_in_api_client.user) playlist = factories["playlists.Playlist"](user=logged_in_api_client.user)
factories["playlists.PlaylistTrack"](index=0, playlist=playlist) factories["playlists.PlaylistTrack"](index=0, playlist=playlist)
@ -437,7 +437,7 @@ def test_update_playlist(f, db, logged_in_api_client, factories):
@pytest.mark.parametrize("f", ["json"]) @pytest.mark.parametrize("f", ["json"])
def test_delete_playlist(f, db, logged_in_api_client, factories): def test_delete_playlist(f, db, logged_in_api_client, factories):
url = reverse("api:subsonic-delete-playlist") url = reverse("api:subsonic-delete_playlist")
assert url.endswith("deletePlaylist") is True assert url.endswith("deletePlaylist") is True
playlist = factories["playlists.Playlist"](user=logged_in_api_client.user) playlist = factories["playlists.Playlist"](user=logged_in_api_client.user)
response = logged_in_api_client.get(url, {"f": f, "id": playlist.pk}) response = logged_in_api_client.get(url, {"f": f, "id": playlist.pk})
@ -448,7 +448,7 @@ def test_delete_playlist(f, db, logged_in_api_client, factories):
@pytest.mark.parametrize("f", ["json"]) @pytest.mark.parametrize("f", ["json"])
def test_create_playlist(f, db, logged_in_api_client, factories): def test_create_playlist(f, db, logged_in_api_client, factories):
url = reverse("api:subsonic-create-playlist") url = reverse("api:subsonic-create_playlist")
assert url.endswith("createPlaylist") is True assert url.endswith("createPlaylist") is True
track1 = factories["music.Track"]() track1 = factories["music.Track"]()
track2 = factories["music.Track"]() track2 = factories["music.Track"]()
@ -470,7 +470,7 @@ def test_create_playlist(f, db, logged_in_api_client, factories):
@pytest.mark.parametrize("f", ["json"]) @pytest.mark.parametrize("f", ["json"])
def test_get_music_folders(f, db, logged_in_api_client, factories): def test_get_music_folders(f, db, logged_in_api_client, factories):
url = reverse("api:subsonic-get-music-folders") url = reverse("api:subsonic-get_music_folders")
assert url.endswith("getMusicFolders") is True assert url.endswith("getMusicFolders") is True
response = logged_in_api_client.get(url, {"f": f}) response = logged_in_api_client.get(url, {"f": f})
assert response.status_code == 200 assert response.status_code == 200
@ -483,7 +483,7 @@ def test_get_music_folders(f, db, logged_in_api_client, factories):
def test_get_indexes( def test_get_indexes(
f, db, logged_in_api_client, factories, mocker, queryset_equal_queries f, db, logged_in_api_client, factories, mocker, queryset_equal_queries
): ):
url = reverse("api:subsonic-get-indexes") url = reverse("api:subsonic-get_indexes")
assert url.endswith("getIndexes") is True assert url.endswith("getIndexes") is True
factories["music.Artist"].create_batch(size=3, playable=True) factories["music.Artist"].create_batch(size=3, playable=True)
expected = { expected = {
@ -501,7 +501,7 @@ def test_get_indexes(
def test_get_cover_art_album(factories, logged_in_api_client): def test_get_cover_art_album(factories, logged_in_api_client):
url = reverse("api:subsonic-get-cover-art") url = reverse("api:subsonic-get_cover_art")
assert url.endswith("getCoverArt") is True assert url.endswith("getCoverArt") is True
album = factories["music.Album"]() album = factories["music.Album"]()
response = logged_in_api_client.get(url, {"id": "al-{}".format(album.pk)}) response = logged_in_api_client.get(url, {"id": "al-{}".format(album.pk)})
@ -515,7 +515,7 @@ def test_get_cover_art_album(factories, logged_in_api_client):
def test_get_avatar(factories, logged_in_api_client): def test_get_avatar(factories, logged_in_api_client):
user = factories["users.User"]() user = factories["users.User"]()
url = reverse("api:subsonic-get-avatar") url = reverse("api:subsonic-get_avatar")
assert url.endswith("getAvatar") is True assert url.endswith("getAvatar") is True
response = logged_in_api_client.get(url, {"username": user.username}) response = logged_in_api_client.get(url, {"username": user.username})
@ -541,7 +541,7 @@ def test_scrobble(factories, logged_in_api_client):
@pytest.mark.parametrize("f", ["json"]) @pytest.mark.parametrize("f", ["json"])
def test_get_user(f, db, logged_in_api_client, factories): def test_get_user(f, db, logged_in_api_client, factories):
url = reverse("api:subsonic-get-user") url = reverse("api:subsonic-get_user")
assert url.endswith("getUser") is True assert url.endswith("getUser") is True
response = logged_in_api_client.get( response = logged_in_api_client.get(
url, {"f": f, "username": logged_in_api_client.user.username} url, {"f": f, "username": logged_in_api_client.user.username}