mirror of
https://code.eliotberriot.com/funkwhale/funkwhale.git
synced 2025-10-04 03:49:16 +02:00
See #272: updated API to return upload data on tracks
This commit is contained in:
parent
8489c79c89
commit
d3f8fb6cb0
16 changed files with 97 additions and 142 deletions
|
@ -51,7 +51,7 @@ class TrackFavoriteViewSet(
|
|||
queryset = queryset.filter(
|
||||
fields.privacy_level_query(self.request.user, "user__privacy_level")
|
||||
)
|
||||
tracks = Track.objects.annotate_playable_by_actor(
|
||||
tracks = Track.objects.with_playable_uploads(
|
||||
music_utils.get_actor_from_request(self.request)
|
||||
).select_related("artist", "album__artist")
|
||||
queryset = queryset.prefetch_related(Prefetch("track", queryset=tracks))
|
||||
|
|
|
@ -41,7 +41,7 @@ class ListeningViewSet(
|
|||
queryset = queryset.filter(
|
||||
fields.privacy_level_query(self.request.user, "user__privacy_level")
|
||||
)
|
||||
tracks = Track.objects.annotate_playable_by_actor(
|
||||
tracks = Track.objects.with_playable_uploads(
|
||||
music_utils.get_actor_from_request(self.request)
|
||||
).select_related("artist", "album__artist")
|
||||
return queryset.prefetch_related(Prefetch("track", queryset=tracks))
|
||||
|
|
|
@ -124,8 +124,8 @@ class ArtistQuerySet(models.QuerySet):
|
|||
|
||||
def annotate_playable_by_actor(self, actor):
|
||||
tracks = (
|
||||
Track.objects.playable_by(actor)
|
||||
.filter(artist=models.OuterRef("id"))
|
||||
Upload.objects.playable_by(actor)
|
||||
.filter(track__artist=models.OuterRef("id"))
|
||||
.order_by("id")
|
||||
.values("id")[:1]
|
||||
)
|
||||
|
@ -192,8 +192,8 @@ class AlbumQuerySet(models.QuerySet):
|
|||
|
||||
def annotate_playable_by_actor(self, actor):
|
||||
tracks = (
|
||||
Track.objects.playable_by(actor)
|
||||
.filter(album=models.OuterRef("id"))
|
||||
Upload.objects.playable_by(actor)
|
||||
.filter(track__album=models.OuterRef("id"))
|
||||
.order_by("id")
|
||||
.values("id")[:1]
|
||||
)
|
||||
|
@ -207,6 +207,15 @@ class AlbumQuerySet(models.QuerySet):
|
|||
else:
|
||||
return self.exclude(tracks__in=tracks).distinct()
|
||||
|
||||
def with_prefetched_tracks_and_playable_uploads(self, actor):
|
||||
tracks = Track.objects.with_playable_uploads(actor)
|
||||
return self.prefetch_related(
|
||||
models.Prefetch(
|
||||
'tracks',
|
||||
queryset=tracks,
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
class Album(APIModelMixin):
|
||||
title = models.CharField(max_length=255)
|
||||
|
@ -403,18 +412,14 @@ class TrackQuerySet(models.QuerySet):
|
|||
else:
|
||||
return self.exclude(uploads__in=files).distinct()
|
||||
|
||||
def annotate_duration(self):
|
||||
first_upload = Upload.objects.filter(track=models.OuterRef("pk")).order_by("pk")
|
||||
return self.annotate(
|
||||
duration=models.Subquery(first_upload.values("duration")[:1])
|
||||
)
|
||||
|
||||
def annotate_file_data(self):
|
||||
first_upload = Upload.objects.filter(track=models.OuterRef("pk")).order_by("pk")
|
||||
return self.annotate(
|
||||
bitrate=models.Subquery(first_upload.values("bitrate")[:1]),
|
||||
size=models.Subquery(first_upload.values("size")[:1]),
|
||||
mimetype=models.Subquery(first_upload.values("mimetype")[:1]),
|
||||
def with_playable_uploads(self, actor):
|
||||
uploads = Upload.objects.playable_by(actor).select_related('track')
|
||||
return self.prefetch_related(
|
||||
models.Prefetch(
|
||||
'uploads',
|
||||
queryset=uploads,
|
||||
to_attr='playable_uploads'
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
|
|
|
@ -59,7 +59,7 @@ class ArtistSimpleSerializer(serializers.ModelSerializer):
|
|||
|
||||
class AlbumTrackSerializer(serializers.ModelSerializer):
|
||||
artist = ArtistSimpleSerializer(read_only=True)
|
||||
is_playable = serializers.SerializerMethodField()
|
||||
uploads = serializers.SerializerMethodField()
|
||||
listen_url = serializers.SerializerMethodField()
|
||||
duration = serializers.SerializerMethodField()
|
||||
|
||||
|
@ -73,16 +73,14 @@ class AlbumTrackSerializer(serializers.ModelSerializer):
|
|||
"artist",
|
||||
"creation_date",
|
||||
"position",
|
||||
"is_playable",
|
||||
"uploads",
|
||||
"listen_url",
|
||||
"duration",
|
||||
)
|
||||
|
||||
def get_is_playable(self, obj):
|
||||
try:
|
||||
return bool(obj.is_playable_by_actor)
|
||||
except AttributeError:
|
||||
return None
|
||||
def get_uploads(self, obj):
|
||||
uploads = getattr(obj, "playable_uploads", [])
|
||||
return TrackUploadSerializer(uploads, many=True).data
|
||||
|
||||
def get_listen_url(self, obj):
|
||||
return obj.listen_url
|
||||
|
@ -123,7 +121,9 @@ class AlbumSerializer(serializers.ModelSerializer):
|
|||
|
||||
def get_is_playable(self, obj):
|
||||
try:
|
||||
return any([bool(t.is_playable_by_actor) for t in obj.tracks.all()])
|
||||
return any(
|
||||
[bool(getattr(t, "playable_uploads", [])) for t in obj.tracks.all()]
|
||||
)
|
||||
except AttributeError:
|
||||
return None
|
||||
|
||||
|
@ -145,16 +145,26 @@ class TrackAlbumSerializer(serializers.ModelSerializer):
|
|||
)
|
||||
|
||||
|
||||
class TrackUploadSerializer(serializers.ModelSerializer):
|
||||
class Meta:
|
||||
model = models.Upload
|
||||
fields = (
|
||||
"uuid",
|
||||
"listen_url",
|
||||
"size",
|
||||
"duration",
|
||||
"bitrate",
|
||||
"mimetype",
|
||||
"extension",
|
||||
)
|
||||
|
||||
|
||||
class TrackSerializer(serializers.ModelSerializer):
|
||||
artist = ArtistSimpleSerializer(read_only=True)
|
||||
album = TrackAlbumSerializer(read_only=True)
|
||||
lyrics = serializers.SerializerMethodField()
|
||||
is_playable = serializers.SerializerMethodField()
|
||||
uploads = serializers.SerializerMethodField()
|
||||
listen_url = serializers.SerializerMethodField()
|
||||
duration = serializers.SerializerMethodField()
|
||||
bitrate = serializers.SerializerMethodField()
|
||||
size = serializers.SerializerMethodField()
|
||||
mimetype = serializers.SerializerMethodField()
|
||||
|
||||
class Meta:
|
||||
model = models.Track
|
||||
|
@ -167,12 +177,8 @@ class TrackSerializer(serializers.ModelSerializer):
|
|||
"creation_date",
|
||||
"position",
|
||||
"lyrics",
|
||||
"is_playable",
|
||||
"uploads",
|
||||
"listen_url",
|
||||
"duration",
|
||||
"bitrate",
|
||||
"size",
|
||||
"mimetype",
|
||||
)
|
||||
|
||||
def get_lyrics(self, obj):
|
||||
|
@ -181,35 +187,9 @@ class TrackSerializer(serializers.ModelSerializer):
|
|||
def get_listen_url(self, obj):
|
||||
return obj.listen_url
|
||||
|
||||
def get_is_playable(self, obj):
|
||||
try:
|
||||
return bool(obj.is_playable_by_actor)
|
||||
except AttributeError:
|
||||
return None
|
||||
|
||||
def get_duration(self, obj):
|
||||
try:
|
||||
return obj.duration
|
||||
except AttributeError:
|
||||
return None
|
||||
|
||||
def get_bitrate(self, obj):
|
||||
try:
|
||||
return obj.bitrate
|
||||
except AttributeError:
|
||||
return None
|
||||
|
||||
def get_size(self, obj):
|
||||
try:
|
||||
return obj.size
|
||||
except AttributeError:
|
||||
return None
|
||||
|
||||
def get_mimetype(self, obj):
|
||||
try:
|
||||
return obj.mimetype
|
||||
except AttributeError:
|
||||
return None
|
||||
def get_uploads(self, obj):
|
||||
uploads = getattr(obj, "playable_uploads", [])
|
||||
return TrackUploadSerializer(uploads, many=True).data
|
||||
|
||||
|
||||
class LibraryForOwnerSerializer(serializers.ModelSerializer):
|
||||
|
|
|
@ -5,7 +5,6 @@ import mutagen
|
|||
import pydub
|
||||
|
||||
from funkwhale_api.common.search import normalize_query, get_query # noqa
|
||||
from funkwhale_api.common import utils
|
||||
|
||||
|
||||
def guess_mimetype(f):
|
||||
|
|
|
@ -93,17 +93,9 @@ class AlbumViewSet(viewsets.ReadOnlyModelViewSet):
|
|||
|
||||
def get_queryset(self):
|
||||
queryset = super().get_queryset()
|
||||
tracks = models.Track.objects.annotate_playable_by_actor(
|
||||
tracks = models.Track.objects.select_related("artist").with_playable_uploads(
|
||||
utils.get_actor_from_request(self.request)
|
||||
).select_related("artist")
|
||||
if (
|
||||
hasattr(self, "kwargs")
|
||||
and self.kwargs
|
||||
and self.request.method.lower() == "get"
|
||||
):
|
||||
# we are detailing a single album, so we can add the overhead
|
||||
# to fetch additional data
|
||||
tracks = tracks.annotate_duration()
|
||||
)
|
||||
qs = queryset.prefetch_related(Prefetch("tracks", queryset=tracks))
|
||||
return qs.distinct()
|
||||
|
||||
|
@ -194,18 +186,10 @@ class TrackViewSet(TagViewSetMixin, viewsets.ReadOnlyModelViewSet):
|
|||
if user.is_authenticated and filter_favorites == "true":
|
||||
queryset = queryset.filter(track_favorites__user=user)
|
||||
|
||||
queryset = queryset.annotate_playable_by_actor(
|
||||
queryset = queryset.with_playable_uploads(
|
||||
utils.get_actor_from_request(self.request)
|
||||
).annotate_duration()
|
||||
if (
|
||||
hasattr(self, "kwargs")
|
||||
and self.kwargs
|
||||
and self.request.method.lower() == "get"
|
||||
):
|
||||
# we are detailing a single track, so we can add the overhead
|
||||
# to fetch additional data
|
||||
queryset = queryset.annotate_file_data()
|
||||
return queryset.distinct()
|
||||
)
|
||||
return queryset
|
||||
|
||||
@detail_route(methods=["get"])
|
||||
@transaction.non_atomic_requests
|
||||
|
|
|
@ -38,15 +38,14 @@ class PlaylistQuerySet(models.QuerySet):
|
|||
)
|
||||
return self.prefetch_related(plt_prefetch)
|
||||
|
||||
def annotate_playable_by_actor(self, actor):
|
||||
plts = (
|
||||
PlaylistTrack.objects.playable_by(actor)
|
||||
.filter(playlist=models.OuterRef("id"))
|
||||
.order_by("id")
|
||||
.values("id")[:1]
|
||||
def with_playable_plts(self, actor):
|
||||
return self.prefetch_related(
|
||||
models.Prefetch(
|
||||
"playlist_tracks",
|
||||
queryset=PlaylistTrack.objects.playable_by(actor),
|
||||
to_attr="playable_plts",
|
||||
)
|
||||
)
|
||||
subquery = models.Subquery(plts)
|
||||
return self.annotate(is_playable_by_actor=subquery)
|
||||
|
||||
def playable_by(self, actor, include=True):
|
||||
plts = PlaylistTrack.objects.playable_by(actor, include)
|
||||
|
@ -148,7 +147,7 @@ class Playlist(models.Model):
|
|||
|
||||
class PlaylistTrackQuerySet(models.QuerySet):
|
||||
def for_nested_serialization(self, actor=None):
|
||||
tracks = music_models.Track.objects.annotate_playable_by_actor(actor)
|
||||
tracks = music_models.Track.objects.with_playable_uploads(actor)
|
||||
tracks = tracks.select_related("artist", "album__artist")
|
||||
return self.prefetch_related(
|
||||
models.Prefetch("track", queryset=tracks, to_attr="_prefetched_track")
|
||||
|
@ -156,8 +155,8 @@ class PlaylistTrackQuerySet(models.QuerySet):
|
|||
|
||||
def annotate_playable_by_actor(self, actor):
|
||||
tracks = (
|
||||
music_models.Track.objects.playable_by(actor)
|
||||
.filter(pk=models.OuterRef("track"))
|
||||
music_models.Upload.objects.playable_by(actor)
|
||||
.filter(track__pk=models.OuterRef("track"))
|
||||
.order_by("id")
|
||||
.values("id")[:1]
|
||||
)
|
||||
|
|
|
@ -93,7 +93,7 @@ class PlaylistSerializer(serializers.ModelSerializer):
|
|||
|
||||
def get_is_playable(self, obj):
|
||||
try:
|
||||
return bool(obj.is_playable_by_actor)
|
||||
return bool(obj.playable_plts)
|
||||
except AttributeError:
|
||||
return None
|
||||
|
||||
|
|
|
@ -78,7 +78,7 @@ class PlaylistViewSet(
|
|||
def get_queryset(self):
|
||||
return self.queryset.filter(
|
||||
fields.privacy_level_query(self.request.user)
|
||||
).annotate_playable_by_actor(music_utils.get_actor_from_request(self.request))
|
||||
).with_playable_plts(music_utils.get_actor_from_request(self.request))
|
||||
|
||||
def perform_create(self, serializer):
|
||||
return serializer.save(
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue