From 882e245a09ce6b5a417267fd31c2ba191ef6eae4 Mon Sep 17 00:00:00 2001 From: Eliot Berriot Date: Wed, 11 Mar 2020 13:59:31 +0100 Subject: [PATCH] Fix #1036: Favor local uploads when playing a track with multiple uploads --- api/funkwhale_api/music/serializers.py | 19 ++++++++++++++++++- api/funkwhale_api/subsonic/views.py | 8 ++++++-- api/tests/music/test_serializers.py | 21 +++++++++++++++++++++ changes/changelog.d/1036.enhancement | 1 + 4 files changed, 46 insertions(+), 3 deletions(-) create mode 100644 changes/changelog.d/1036.enhancement diff --git a/api/funkwhale_api/music/serializers.py b/api/funkwhale_api/music/serializers.py index 6ccddb056..3357f14a6 100644 --- a/api/funkwhale_api/music/serializers.py +++ b/api/funkwhale_api/music/serializers.py @@ -281,6 +281,22 @@ def serialize_upload(upload): } +def sort_uploads_for_listen(uploads): + """ + Given a list of uploads, return a sorted list of uploads, with local or locally + cached ones first, and older first + """ + score = {upload: 0 for upload in uploads} + for upload in uploads: + if upload.is_local: + score[upload] = 3 + elif upload.audio_file: + score[upload] = 2 + + sorted_tuples = sorted(score.items(), key=lambda t: (t[1], -t[0].pk), reverse=True) + return [t[0] for t in sorted_tuples] + + class TrackSerializer(OptionalDescriptionMixin, serializers.Serializer): artist = serializers.SerializerMethodField() album = TrackAlbumSerializer(read_only=True) @@ -310,7 +326,8 @@ class TrackSerializer(OptionalDescriptionMixin, serializers.Serializer): return obj.listen_url def get_uploads(self, obj): - return [serialize_upload(u) for u in getattr(obj, "playable_uploads", [])] + uploads = getattr(obj, "playable_uploads", []) + return [serialize_upload(u) for u in sort_uploads_for_listen(uploads)] def get_tags(self, obj): tagged_items = getattr(obj, "_prefetched_tagged_items", []) diff --git a/api/funkwhale_api/subsonic/views.py b/api/funkwhale_api/subsonic/views.py index e656b5bdc..e1d4d10b7 100644 --- a/api/funkwhale_api/subsonic/views.py +++ b/api/funkwhale_api/subsonic/views.py @@ -25,6 +25,7 @@ from funkwhale_api.common import ( from funkwhale_api.favorites.models import TrackFavorite from funkwhale_api.moderation import filters as moderation_filters from funkwhale_api.music import models as music_models +from funkwhale_api.music import serializers as music_serializers from funkwhale_api.music import utils from funkwhale_api.music import views as music_views from funkwhale_api.playlists import models as playlists_models @@ -255,10 +256,13 @@ class SubsonicViewSet(viewsets.GenericViewSet): data = request.GET or request.POST track = kwargs.pop("obj") queryset = track.uploads.select_related("track__album__artist", "track__artist") - upload = queryset.first() - if not upload: + sorted_uploads = music_serializers.sort_uploads_for_listen(queryset) + + if not sorted_uploads: return response.Response(status=404) + upload = sorted_uploads[0] + max_bitrate = data.get("maxBitRate") try: max_bitrate = min(max(int(max_bitrate), 0), 320) or None diff --git a/api/tests/music/test_serializers.py b/api/tests/music/test_serializers.py index 3cffcb814..60ecff579 100644 --- a/api/tests/music/test_serializers.py +++ b/api/tests/music/test_serializers.py @@ -584,3 +584,24 @@ def test_detail_serializers_with_description_description( expected = common_serializers.ContentSerializer(content).data serializer = serializer_class(obj, context={"description": True}) assert serializer.data["description"] == expected + + +def test_sort_uploads_for_listen(factories): + local_upload = factories["music.Upload"](library__local=True) + new_local_upload = factories["music.Upload"](library__local=True) + remote_upload = factories["music.Upload"](audio_file__from_path=None) + remote_upload_with_local_version = factories["music.Upload"]() + + unsorted = [ + remote_upload_with_local_version, + new_local_upload, + remote_upload, + local_upload, + ] + expected = [ + local_upload, + new_local_upload, + remote_upload, + remote_upload_with_local_version, + ] + assert serializers.sort_uploads_for_listen(unsorted) == expected diff --git a/changes/changelog.d/1036.enhancement b/changes/changelog.d/1036.enhancement new file mode 100644 index 000000000..d876cfd60 --- /dev/null +++ b/changes/changelog.d/1036.enhancement @@ -0,0 +1 @@ +Favor local uploads when playing a track with multiple uploads (#1036)