Now display CLI instructions to download a set of tracks
This commit is contained in:
parent
c34ea44687
commit
e8eaf6db94
|
@ -362,6 +362,12 @@ class TrackFile(models.Model):
|
||||||
'api:v1:trackfiles-serve', kwargs={'pk': self.pk})
|
'api:v1:trackfiles-serve', kwargs={'pk': self.pk})
|
||||||
return self.audio_file.url
|
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):
|
class ImportBatch(models.Model):
|
||||||
creation_date = models.DateTimeField(default=timezone.now)
|
creation_date = models.DateTimeField(default=timezone.now)
|
||||||
|
|
|
@ -34,7 +34,7 @@ class ImportBatchSerializer(serializers.ModelSerializer):
|
||||||
class TrackFileSerializer(serializers.ModelSerializer):
|
class TrackFileSerializer(serializers.ModelSerializer):
|
||||||
class Meta:
|
class Meta:
|
||||||
model = models.TrackFile
|
model = models.TrackFile
|
||||||
fields = ('id', 'path', 'duration', 'source')
|
fields = ('id', 'path', 'duration', 'source', 'filename')
|
||||||
|
|
||||||
|
|
||||||
class SimpleAlbumSerializer(serializers.ModelSerializer):
|
class SimpleAlbumSerializer(serializers.ModelSerializer):
|
||||||
|
|
|
@ -139,9 +139,8 @@ class TrackFileViewSet(viewsets.ReadOnlyModelViewSet):
|
||||||
return Response(status=404)
|
return Response(status=404)
|
||||||
|
|
||||||
response = Response()
|
response = Response()
|
||||||
filename = "filename*=UTF-8''{}{}".format(
|
filename = "filename*=UTF-8''{}".format(
|
||||||
urllib.parse.quote(f.track.full_name),
|
urllib.parse.quote(f.filename))
|
||||||
os.path.splitext(f.audio_file.name)[-1])
|
|
||||||
response["Content-Disposition"] = "attachment; {}".format(filename)
|
response["Content-Disposition"] = "attachment; {}".format(filename)
|
||||||
response['X-Accel-Redirect'] = "{}{}".format(
|
response['X-Accel-Redirect'] = "{}{}".format(
|
||||||
settings.PROTECT_FILES_PATH,
|
settings.PROTECT_FILES_PATH,
|
||||||
|
|
|
@ -40,26 +40,70 @@
|
||||||
<td><track-favorite-icon class="favorite-icon" :track="track"></track-favorite-icon></td>
|
<td><track-favorite-icon class="favorite-icon" :track="track"></track-favorite-icon></td>
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</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>
|
</table>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import backend from '@/audio/backend'
|
import backend from '@/audio/backend'
|
||||||
|
import auth from '@/auth'
|
||||||
import TrackFavoriteIcon from '@/components/favorites/TrackFavoriteIcon'
|
import TrackFavoriteIcon from '@/components/favorites/TrackFavoriteIcon'
|
||||||
import PlayButton from '@/components/audio/PlayButton'
|
import PlayButton from '@/components/audio/PlayButton'
|
||||||
|
|
||||||
|
import Modal from '@/components/semantic/Modal'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
props: {
|
props: {
|
||||||
tracks: {type: Array, required: true},
|
tracks: {type: Array, required: true},
|
||||||
displayPosition: {type: Boolean, default: false}
|
displayPosition: {type: Boolean, default: false}
|
||||||
},
|
},
|
||||||
components: {
|
components: {
|
||||||
|
Modal,
|
||||||
TrackFavoriteIcon,
|
TrackFavoriteIcon,
|
||||||
PlayButton
|
PlayButton
|
||||||
},
|
},
|
||||||
data () {
|
data () {
|
||||||
return {
|
return {
|
||||||
backend: backend
|
backend: backend,
|
||||||
|
auth: auth,
|
||||||
|
showDownloadModal: false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,7 +34,7 @@
|
||||||
</div>
|
</div>
|
||||||
<div class="ui vertical stripe segment">
|
<div class="ui vertical stripe segment">
|
||||||
<h2>Tracks</h2>
|
<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>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -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>
|
Loading…
Reference in New Issue