diff --git a/api/funkwhale_api/music/models.py b/api/funkwhale_api/music/models.py
index d7a08b7a40f5f06d7501bb666e9b64323e5f1e6d..596f890b7e43b609d036c4a46040973544eaf2fd 100644
--- a/api/funkwhale_api/music/models.py
+++ b/api/funkwhale_api/music/models.py
@@ -362,6 +362,12 @@ class TrackFile(models.Model):
                 'api:v1:trackfiles-serve', kwargs={'pk': self.pk})
         return self.audio_file.url
 
+    @property
+    def filename(self):
+        return '{}{}'.format(
+            self.track.full_name,
+            os.path.splitext(self.audio_file.name)[-1])
+
 
 class ImportBatch(models.Model):
     creation_date = models.DateTimeField(default=timezone.now)
diff --git a/api/funkwhale_api/music/serializers.py b/api/funkwhale_api/music/serializers.py
index 6b839b9cfce30e2a650c95146a21f2fe4d6ed181..40fbb65ea4a0267ec031d1c53dfdb5e09adaa47d 100644
--- a/api/funkwhale_api/music/serializers.py
+++ b/api/funkwhale_api/music/serializers.py
@@ -34,7 +34,7 @@ class ImportBatchSerializer(serializers.ModelSerializer):
 class TrackFileSerializer(serializers.ModelSerializer):
     class Meta:
         model = models.TrackFile
-        fields = ('id', 'path', 'duration', 'source')
+        fields = ('id', 'path', 'duration', 'source', 'filename')
 
 
 class SimpleAlbumSerializer(serializers.ModelSerializer):
diff --git a/api/funkwhale_api/music/views.py b/api/funkwhale_api/music/views.py
index 4a4032c57f80695ba88fb92826941be3ede23605..98319255270bfdae84f70e3335fc4917f650885b 100644
--- a/api/funkwhale_api/music/views.py
+++ b/api/funkwhale_api/music/views.py
@@ -139,9 +139,8 @@ class TrackFileViewSet(viewsets.ReadOnlyModelViewSet):
             return Response(status=404)
 
         response = Response()
-        filename = "filename*=UTF-8''{}{}".format(
-            urllib.parse.quote(f.track.full_name),
-            os.path.splitext(f.audio_file.name)[-1])
+        filename = "filename*=UTF-8''{}".format(
+            urllib.parse.quote(f.filename))
         response["Content-Disposition"] = "attachment; {}".format(filename)
         response['X-Accel-Redirect'] = "{}{}".format(
             settings.PROTECT_FILES_PATH,
diff --git a/front/src/components/audio/track/Table.vue b/front/src/components/audio/track/Table.vue
index 8dca90902efec2027fb22e7a8650e399d8bc8d9b..e9beaa05a5ac8f0bb6285597a9316eb8afab4e4f 100644
--- a/front/src/components/audio/track/Table.vue
+++ b/front/src/components/audio/track/Table.vue
@@ -40,26 +40,70 @@
         <td><track-favorite-icon class="favorite-icon" :track="track"></track-favorite-icon></td>
       </tr>
     </tbody>
+    <tfoot class="full-width">
+      <tr>
+        <th colspan="3">
+          <button @click="showDownloadModal = !showDownloadModal" class="ui basic button">Download...</button>
+          <modal :show.sync="showDownloadModal">
+            <div class="header">
+              Download tracks
+            </div>
+            <div class="content">
+              <div class="description">
+                <p>There is currently no way to download directly multiple tracks from funkwhale as a ZIP archive.
+                  However, you can use a command line tools such as <a href="https://curl.haxx.se/" target="_blank">cURL</a> to easily download a list of tracks.
+                </p>
+                <p>Simply copy paste the snippet below into a terminal to launch the download.</p>
+                <div class="ui warning message">
+                  Keep your PRIVATE_TOKEN secret as it gives access to your account.
+                </div>
+                <pre>
+export PRIVATE_TOKEN="{{ auth.getAuthToken ()}}"
+<template v-for="track in tracks">
+curl -G -o "{{ track.files[0].filename }}" <template v-if="auth.user.authenticated">--header "Authorization: JWT $PRIVATE_TOKEN"</template> "{{ backend.absoluteUrl(track.files[0].path) }}"</template>
+</pre>
+              </div>
+            </div>
+            <div class="actions">
+              <div class="ui black deny button">
+                Cancel
+              </div>
+            </div>
+          </modal>
+        </th>
+        <th></th>
+        <th colspan="4"></th>
+        <th colspan="6"></th>
+        <th colspan="6"></th>
+        <th></th>
+      </tr>
+    </tfoot>
   </table>
 </template>
 
 <script>
 import backend from '@/audio/backend'
+import auth from '@/auth'
 import TrackFavoriteIcon from '@/components/favorites/TrackFavoriteIcon'
 import PlayButton from '@/components/audio/PlayButton'
 
+import Modal from '@/components/semantic/Modal'
+
 export default {
   props: {
     tracks: {type: Array, required: true},
     displayPosition: {type: Boolean, default: false}
   },
   components: {
+    Modal,
     TrackFavoriteIcon,
     PlayButton
   },
   data () {
     return {
-      backend: backend
+      backend: backend,
+      auth: auth,
+      showDownloadModal: false
     }
   }
 }
diff --git a/front/src/components/library/Album.vue b/front/src/components/library/Album.vue
index 494f2396b9bc976d7a6c4593656517abc2cc84d5..cf3403400b8a07afc56cd0ca0edd7874ca8bacf6 100644
--- a/front/src/components/library/Album.vue
+++ b/front/src/components/library/Album.vue
@@ -34,7 +34,7 @@
       </div>
       <div class="ui vertical stripe segment">
         <h2>Tracks</h2>
-        <track-table v-if="album" display-position="true" :tracks="album.tracks"></track-table>
+        <track-table v-if="album" :display-position="true" :tracks="album.tracks"></track-table>
       </div>
     </template>
   </div>
diff --git a/front/src/components/semantic/Modal.vue b/front/src/components/semantic/Modal.vue
new file mode 100644
index 0000000000000000000000000000000000000000..ec7a5a0884262ac2437570cd6a360e231eaccaba
--- /dev/null
+++ b/front/src/components/semantic/Modal.vue
@@ -0,0 +1,53 @@
+<template>
+  <div :class="['ui', {'active': show}, 'modal']">
+    <i class="close icon"></i>
+    <slot>
+      
+    </slot>
+  </div>
+</template>
+
+<script>
+import $ from 'jquery'
+
+export default {
+  props: {
+    show: {type: Boolean, required: true}
+  },
+  data () {
+    return {
+      control: null
+    }
+  },
+  mounted () {
+    this.control = $(this.$el).modal({
+      onApprove: function () {
+        this.$emit('approved')
+      }.bind(this),
+      onDeny: function () {
+        this.$emit('deny')
+      }.bind(this),
+      onHidden: function () {
+        this.$emit('update:show', false)
+      }.bind(this)
+    })
+  },
+  watch: {
+    show: {
+      handler (newValue) {
+        if (newValue) {
+          this.control.modal('show')
+        } else {
+          this.control.modal('hide')
+        }
+      }
+    }
+  }
+
+}
+</script>
+
+<!-- Add "scoped" attribute to limit CSS to this component only -->
+<style scoped lang="scss">
+
+</style>