From ced851891d1276143e18eac038948a58f91f3772 Mon Sep 17 00:00:00 2001
From: Eliot Berriot <contact@eliotberriot.com>
Date: Thu, 19 Apr 2018 21:19:40 +0200
Subject: [PATCH] Fix #163: Avoid downloading audio files multiple times from
 remote libraries

---
 api/funkwhale_api/music/views.py    | 27 ++++++++++++++++++---------
 changes/changelog.d/163.enhancement |  1 +
 2 files changed, 19 insertions(+), 9 deletions(-)
 create mode 100644 changes/changelog.d/163.enhancement

diff --git a/api/funkwhale_api/music/views.py b/api/funkwhale_api/music/views.py
index e8ace1b3..f961e3fd 100644
--- a/api/funkwhale_api/music/views.py
+++ b/api/funkwhale_api/music/views.py
@@ -23,13 +23,14 @@ from rest_framework import permissions
 from musicbrainzngs import ResponseError
 
 from funkwhale_api.common import utils as funkwhale_utils
-from funkwhale_api.federation import actors
-from funkwhale_api.requests.models import ImportRequest
-from funkwhale_api.musicbrainz import api
 from funkwhale_api.common.permissions import (
     ConditionalAuthentication, HasModelPermission)
 from taggit.models import Tag
+from funkwhale_api.federation import actors
 from funkwhale_api.federation.authentication import SignatureAuthentication
+from funkwhale_api.federation.models import LibraryTrack
+from funkwhale_api.musicbrainz import api
+from funkwhale_api.requests.models import ImportRequest
 
 from . import filters
 from . import forms
@@ -195,12 +196,13 @@ class TrackFileViewSet(viewsets.ReadOnlyModelViewSet):
 
     @detail_route(methods=['get'])
     def serve(self, request, *args, **kwargs):
+        queryset = models.TrackFile.objects.select_related(
+            'library_track',
+            'track__album__artist',
+            'track__artist',
+        )
         try:
-            f = models.TrackFile.objects.select_related(
-                'library_track',
-                'track__album__artist',
-                'track__artist',
-            ).get(pk=kwargs['pk'])
+            f = queryset.get(pk=kwargs['pk'])
         except models.TrackFile.DoesNotExist:
             return Response(status=404)
 
@@ -213,7 +215,14 @@ class TrackFileViewSet(viewsets.ReadOnlyModelViewSet):
         if library_track and not audio_file:
             if not library_track.audio_file:
                 # we need to populate from cache
-                library_track.download_audio()
+                with transaction.atomic():
+                    # why the transaction/select_for_update?
+                    # this is because browsers may send multiple requests
+                    # in a short time range, for partial content,
+                    # thus resulting in multiple downloads from the remote
+                    qs = LibraryTrack.objects.select_for_update()
+                    library_track = qs.get(pk=library_track.pk)
+                    library_track.download_audio()
             audio_file = library_track.audio_file
             mt = library_track.audio_mimetype
         response = Response()
diff --git a/changes/changelog.d/163.enhancement b/changes/changelog.d/163.enhancement
new file mode 100644
index 00000000..89225eb9
--- /dev/null
+++ b/changes/changelog.d/163.enhancement
@@ -0,0 +1 @@
+Avoid downloading audio files multiple times from remote libraries (#163)
-- 
GitLab