diff --git a/api/config/settings/common.py b/api/config/settings/common.py index a20081dd2f3905c90baade172c51ee1fbe506f13..f33c338a9c38e400744061dc982c7759225a985e 100644 --- a/api/config/settings/common.py +++ b/api/config/settings/common.py @@ -816,6 +816,11 @@ SESSION_ENGINE = "django.contrib.sessions.backends.cache" # XXX: deprecated, see #186 PLAYLISTS_MAX_TRACKS = env.int("PLAYLISTS_MAX_TRACKS", default=250) +# not hard limits, but only applied in API responses +ARTIST_MAX_ALBUMS = env.int("_ARTIST_MAX_ALBUMS", default=12) +ALBUM_MAX_TRACKS = env.int("_ALBUM_MAX_TRACKS", default=50) +TRACK_MAX_UPLOADS = env.int("_TRACK_MAX_UPLOADS", default=5) + ACCOUNT_USERNAME_BLACKLIST = [ "funkwhale", "library", diff --git a/api/funkwhale_api/music/serializers.py b/api/funkwhale_api/music/serializers.py index 77854d437647a3e661ea462c33217db0b3425447..6c97dd0f61b13c890669644f08f83e4adf7c441d 100644 --- a/api/funkwhale_api/music/serializers.py +++ b/api/funkwhale_api/music/serializers.py @@ -77,9 +77,10 @@ class ArtistAlbumSerializer(serializers.ModelSerializer): class ArtistWithAlbumsSerializer(serializers.ModelSerializer): - albums = ArtistAlbumSerializer(many=True, read_only=True) tags = serializers.SerializerMethodField() attributed_to = serializers.SerializerMethodField() + albums = serializers.SerializerMethodField() + albums_count = serializers.SerializerMethodField() tracks_count = serializers.SerializerMethodField() class Meta: @@ -94,6 +95,7 @@ class ArtistWithAlbumsSerializer(serializers.ModelSerializer): "is_local", "tags", "attributed_to", + "albums_count", "tracks_count", ) @@ -107,6 +109,16 @@ class ArtistWithAlbumsSerializer(serializers.ModelSerializer): tracks = getattr(o, "_prefetched_tracks", None) return len(tracks) if tracks else None + def get_albums(self, o): + ordered_albums = list(o.albums.all())[: settings.ARTIST_MAX_ALBUMS] + return [ + ArtistAlbumSerializer(album).data + for album in ordered_albums[: settings.ARTIST_MAX_ALBUMS] + ] + + def get_albums_count(self, o): + return len(list(o.albums.all())) + def serialize_artist_simple(artist): return { @@ -150,6 +162,7 @@ class AlbumSerializer(serializers.ModelSerializer): artist = serializers.SerializerMethodField() cover = cover_field is_playable = serializers.SerializerMethodField() + tracks_count = serializers.SerializerMethodField() tags = serializers.SerializerMethodField() attributed_to = serializers.SerializerMethodField() @@ -169,6 +182,7 @@ class AlbumSerializer(serializers.ModelSerializer): "is_local", "tags", "attributed_to", + "tracks_count", ) get_attributed_to = serialize_attributed_to @@ -177,8 +191,11 @@ class AlbumSerializer(serializers.ModelSerializer): return serialize_artist_simple(o.artist) def get_tracks(self, o): - ordered_tracks = o.tracks.all() - return [serialize_album_track(track) for track in ordered_tracks] + ordered_tracks = list(o.tracks.all())[: settings.ALBUM_MAX_TRACKS] + return [ + serialize_album_track(track) + for track in ordered_tracks[: settings.ALBUM_MAX_TRACKS] + ] def get_is_playable(self, obj): try: @@ -192,6 +209,9 @@ class AlbumSerializer(serializers.ModelSerializer): tagged_items = getattr(obj, "_prefetched_tagged_items", []) return [ti.tag.name for ti in tagged_items] + def get_tracks_count(self, o): + return len(list(o.tracks.all())) + class TrackAlbumSerializer(serializers.ModelSerializer): artist = serializers.SerializerMethodField() @@ -231,6 +251,7 @@ class TrackSerializer(serializers.ModelSerializer): artist = serializers.SerializerMethodField() album = TrackAlbumSerializer(read_only=True) uploads = serializers.SerializerMethodField() + uploads_count = serializers.SerializerMethodField() listen_url = serializers.SerializerMethodField() tags = serializers.SerializerMethodField() attributed_to = serializers.SerializerMethodField() @@ -254,6 +275,7 @@ class TrackSerializer(serializers.ModelSerializer): "is_local", "tags", "attributed_to", + "uploads_count", ) get_attributed_to = serialize_attributed_to @@ -265,7 +287,15 @@ class TrackSerializer(serializers.ModelSerializer): return obj.listen_url def get_uploads(self, obj): - return [serialize_upload(u) for u in getattr(obj, "playable_uploads", [])] + return [ + serialize_upload(u) + for u in list(getattr(obj, "playable_uploads", []))[ + : settings.TRACK_MAX_UPLOADS + ] + ] + + def get_uploads_count(self, obj): + return len(list(getattr(obj, "playable_uploads", []))) def get_tags(self, obj): tagged_items = getattr(obj, "_prefetched_tagged_items", []) diff --git a/api/tests/music/test_serializers.py b/api/tests/music/test_serializers.py index 4eaf54ad54aa0559a3c05662c8f78b2960276b1b..e1b420665fff8656be97f295bd0fbe9a1eb1fd52 100644 --- a/api/tests/music/test_serializers.py +++ b/api/tests/music/test_serializers.py @@ -56,9 +56,11 @@ def test_artist_album_serializer(factories, to_api_date): assert serializer.data == expected -def test_artist_with_albums_serializer(factories, to_api_date): +def test_artist_with_albums_serializer(factories, to_api_date, settings): + settings.ARTIST_MAX_ALBUMS = 1 actor = factories["federation.Actor"]() track = factories["music.Track"](album__artist__attributed_to=actor) + factories["music.Album"](artist=track.album.artist) artist = track.artist artist = artist.__class__.objects.with_albums().get(pk=artist.pk) album = list(artist.albums.all())[0] @@ -74,6 +76,7 @@ def test_artist_with_albums_serializer(factories, to_api_date): "tags": [], "attributed_to": federation_serializers.APIActorSerializer(actor).data, "tracks_count": 42, + "albums_count": 2, } serializer = serializers.ArtistWithAlbumsSerializer(artist) assert serializer.data == expected @@ -159,10 +162,12 @@ def test_upload_owner_serializer(factories, to_api_date): assert serializer.data == expected -def test_album_serializer(factories, to_api_date): +def test_album_serializer(factories, to_api_date, settings): + settings.ALBUM_MAX_TRACKS = 2 actor = factories["federation.Actor"]() track1 = factories["music.Track"](position=2, album__attributed_to=actor) track2 = factories["music.Track"](position=1, album=track1.album) + factories["music.Track"](position=3, album=track1.album) album = track1.album expected = { "id": album.id, @@ -183,13 +188,15 @@ def test_album_serializer(factories, to_api_date): "is_local": album.is_local, "tags": [], "attributed_to": federation_serializers.APIActorSerializer(actor).data, + "tracks_count": 3, } serializer = serializers.AlbumSerializer(album) assert serializer.data == expected -def test_track_serializer(factories, to_api_date): +def test_track_serializer(factories, to_api_date, settings): + settings.TRACK_MAX_UPLOADS = 1 actor = factories["federation.Actor"]() upload = factories["music.Upload"]( track__license="cc-by-4.0", @@ -198,7 +205,8 @@ def test_track_serializer(factories, to_api_date): track__attributed_to=actor, ) track = upload.track - setattr(track, "playable_uploads", [upload]) + hidden_upload = factories["music.Upload"]() + setattr(track, "playable_uploads", [upload, hidden_upload]) expected = { "id": track.id, "fid": track.fid, @@ -209,6 +217,7 @@ def test_track_serializer(factories, to_api_date): "position": track.position, "disc_number": track.disc_number, "uploads": [serializers.serialize_upload(upload)], + "uploads_count": 2, "creation_date": to_api_date(track.creation_date), "listen_url": track.listen_url, "license": upload.track.license.code, diff --git a/docs/swagger.yml b/docs/swagger.yml index a6952098b1243f8b2dc76e5db7285331c4712803..b9d4ed246ab0d643ad8ecc91a7a9d5517135c20c 100644 --- a/docs/swagger.yml +++ b/docs/swagger.yml @@ -1282,8 +1282,14 @@ definitions: example: 42 albums: type: "array" + description: "List of albums associated with this artist. The list is truncated to avoid returning 100s of objects." items: $ref: "#/definitions/ArtistAlbum" + albums_count: + type: "number" + minimum: 0 + example: 12 + description: "Total number of albums associated with the artist" BaseAlbum: type: "object" @@ -1330,8 +1336,14 @@ definitions: properties: tracks: type: "array" + description: "List of tracks associated with this album. The list is truncated to avoid returning 100s of objects." items: $ref: "#/definitions/AlbumTrack" + tracks_count: + type: "number" + minimum: 0 + example: 12 + description: "Total number of tracks associated with the album" ArtistAlbum: type: "object" @@ -1499,9 +1511,14 @@ definitions: $ref: "#/definitions/BaseArtist" uploads: type: "array" - description: "List of uploads associated with this track" + description: "List of uploads associated with this track. The list is truncated to avoid returning 100s of objects." items: $ref: "#/definitions/Upload" + uploads_count: + type: "number" + minimum: 0 + example: 12 + description: "Total number of uploads associated with the track" Upload: type: "object" properties: diff --git a/front/src/components/audio/PlayButton.vue b/front/src/components/audio/PlayButton.vue index 96e210e7df5362486b6e49163e6ff5b8392e54bc..ed9397c903245a190511c390d7d0e29204858509 100644 --- a/front/src/components/audio/PlayButton.vue +++ b/front/src/components/audio/PlayButton.vue @@ -199,7 +199,7 @@ export default { let params = {'artist': self.artist.id, 'ordering': 'album__release_date,position'} self.getTracksPage(1, params, resolve) } else if (self.album) { - let params = {'album': self.album.id, 'ordering': 'position'} + let params = {'album': self.album.id, 'ordering': 'position,creation_date'} self.getTracksPage(1, params, resolve) } }) diff --git a/front/src/components/audio/album/Card.vue b/front/src/components/audio/album/Card.vue index 67d1eb706cc68e3145583c26f464f0017bd3a1ec..3c145a33eefc527f40281f3ab65b366c04356db7 100644 --- a/front/src/components/audio/album/Card.vue +++ b/front/src/components/audio/album/Card.vue @@ -45,7 +45,7 @@ </div> </div> <div class="extra content"> - <play-button class="mini basic orange right floated" :tracks="tracksWithAlbum" :album="album"> + <play-button class="mini basic orange right floated" :is-playable="album.is_playable" :album="album"> <translate translate-context="Content/Queue/Button.Label/Short, Verb">Play all</translate> </play-button> <span> diff --git a/front/src/components/library/AlbumBase.vue b/front/src/components/library/AlbumBase.vue index e42f3e826be6cec0707577b85203af0d2e6c71c6..f11e66ad8c0a1a1bc7d5bcf4aa072924c1995468 100644 --- a/front/src/components/library/AlbumBase.vue +++ b/front/src/components/library/AlbumBase.vue @@ -18,7 +18,7 @@ <div class="header-buttons"> <div class="ui buttons"> - <play-button class="orange" :tracks="object.tracks"> + <play-button class="orange" :album="object" :is-playable="object.is_playable"> <translate translate-context="Content/Queue/Button.Label/Short, Verb">Play all</translate> </play-button> </div> @@ -161,6 +161,7 @@ export default { self.object = backend.Album.clean(response.data) self.discs = self.object.tracks.reduce(groupByDisc, []) self.isLoading = false + // todoooo }) } },