Skip to content
Snippets Groups Projects
views.py 6.39 KiB
Newer Older
  • Learn to ignore specific revisions
  • from rest_framework import decorators
    from rest_framework import exceptions
    from rest_framework import mixins
    from rest_framework import permissions as rest_permissions
    from rest_framework import response
    from rest_framework import viewsets
    
    from django.db.models import Count, Prefetch
    
    from django.db.utils import IntegrityError
    
    
    from funkwhale_api.common import permissions
    from funkwhale_api.common import preferences
    
    from funkwhale_api.federation import models as federation_models
    
    from funkwhale_api.music import models as music_models
    from funkwhale_api.music import views as music_views
    
    from funkwhale_api.users.oauth import permissions as oauth_permissions
    
    
    from . import filters, models, renderers, serializers
    
    ARTIST_PREFETCH_QS = (
        music_models.Artist.objects.select_related("description", "attachment_cover",)
        .prefetch_related(music_views.TAG_PREFETCH)
        .annotate(_tracks_count=Count("tracks"))
    )
    
    
    
    class ChannelsMixin(object):
        def dispatch(self, request, *args, **kwargs):
            if not preferences.get("audio__channels_enabled"):
                return http.HttpResponse(status=405)
            return super().dispatch(request, *args, **kwargs)
    
    
    class ChannelViewSet(
        ChannelsMixin,
        mixins.CreateModelMixin,
        mixins.RetrieveModelMixin,
        mixins.UpdateModelMixin,
        mixins.ListModelMixin,
        mixins.DestroyModelMixin,
        viewsets.GenericViewSet,
    ):
        lookup_field = "uuid"
        filterset_class = filters.ChannelFilter
        serializer_class = serializers.ChannelSerializer
        queryset = (
            models.Channel.objects.all()
    
            .prefetch_related(
                "library",
                "attributed_to",
                "actor",
    
                Prefetch("artist", queryset=ARTIST_PREFETCH_QS),
    
            .order_by("-creation_date")
        )
        permission_classes = [
            oauth_permissions.ScopePermission,
            permissions.OwnerPermission,
        ]
        required_scope = "libraries"
        anonymous_policy = "setting"
        owner_checks = ["write"]
        owner_field = "attributed_to.user"
        owner_exception = exceptions.PermissionDenied
    
        def get_serializer_class(self):
            if self.request.method.lower() in ["head", "get", "options"]:
                return serializers.ChannelSerializer
            elif self.action in ["update", "partial_update"]:
                return serializers.ChannelUpdateSerializer
            return serializers.ChannelCreateSerializer
    
        def perform_create(self, serializer):
            return serializer.save(attributed_to=self.request.user.actor)
    
    
        @decorators.action(
            detail=True,
            methods=["post"],
            permission_classes=[rest_permissions.IsAuthenticated],
        )
        def subscribe(self, request, *args, **kwargs):
            object = self.get_object()
    
            subscription = federation_models.Follow(
                target=object.actor, approved=True, actor=request.user.actor,
            )
            subscription.fid = subscription.get_federation_id()
    
            except IntegrityError:
                # there's already a subscription for this actor/channel
                subscription = object.actor.received_follows.filter(
                    actor=request.user.actor
                ).get()
    
            data = serializers.SubscriptionSerializer(subscription).data
            return response.Response(data, status=201)
    
        @decorators.action(
            detail=True,
            methods=["post", "delete"],
            permission_classes=[rest_permissions.IsAuthenticated],
        )
        def unsubscribe(self, request, *args, **kwargs):
            object = self.get_object()
            request.user.actor.emitted_follows.filter(target=object.actor).delete()
            return response.Response(status=204)
    
        @decorators.action(
            detail=True,
            methods=["get"],
            permission_classes=[],
            content_negotiation_class=renderers.PodcastRSSContentNegociation,
        )
        def rss(self, request, *args, **kwargs):
            object = self.get_object()
            uploads = (
                object.library.uploads.playable_by(None)
                .prefetch_related(
                    Prefetch(
                        "track",
                        queryset=music_models.Track.objects.select_related(
                            "attachment_cover", "description"
                        ).prefetch_related(music_views.TAG_PREFETCH,),
                    ),
                )
                .select_related("track__attachment_cover", "track__description")
                .order_by("-creation_date")
            )[:50]
            data = serializers.rss_serialize_channel_full(channel=object, uploads=uploads)
            return response.Response(data, status=200)
    
    
        def get_serializer_context(self):
            context = super().get_serializer_context()
    
            context["subscriptions_count"] = self.action in [
                "retrieve",
                "create",
                "update",
                "partial_update",
            ]
    
            if self.request.user.is_authenticated:
                context["actor"] = self.request.user.actor
    
    
    class SubscriptionsViewSet(
        ChannelsMixin,
        mixins.RetrieveModelMixin,
        mixins.ListModelMixin,
        viewsets.GenericViewSet,
    ):
        lookup_field = "uuid"
        serializer_class = serializers.SubscriptionSerializer
        queryset = (
            federation_models.Follow.objects.exclude(target__channel__isnull=True)
            .prefetch_related(
                "target__channel__library",
                "target__channel__attributed_to",
                "actor",
    
                Prefetch("target__channel__artist", queryset=ARTIST_PREFETCH_QS),
    
            )
            .order_by("-creation_date")
        )
        permission_classes = [
            oauth_permissions.ScopePermission,
            rest_permissions.IsAuthenticated,
        ]
        required_scope = "libraries"
        anonymous_policy = False
    
        def get_queryset(self):
            qs = super().get_queryset()
            return qs.filter(actor=self.request.user.actor)
    
        @decorators.action(methods=["get"], detail=False)
        def all(self, request, *args, **kwargs):
            """
            Return all the subscriptions of the current user, with only limited data
            to have a performant endpoint and avoid lots of queries just to display
            subscription status in the UI
            """
    
            subscriptions = list(
                self.get_queryset().values_list("uuid", "target__channel__uuid")
            )
    
                "results": [{"uuid": str(u[0]), "channel": u[1]} for u in subscriptions],
    
                "count": len(subscriptions),
            }
            return response.Response(payload, status=200)