mirror of
https://gitlab.science.ru.nl/technicie/MarietjeDjango.git
synced 2025-12-12 23:02:20 +01:00
260 lines
14 KiB
HTML
260 lines
14 KiB
HTML
{% extends 'marietje/base.html' %}
|
|
{% load static %}
|
|
|
|
{% block title %}Upload{% endblock %}
|
|
|
|
{% block content %}
|
|
<div class="container">
|
|
<div class="row mt-5">
|
|
<div class="col-lg-6 mx-auto">
|
|
<div class="card uploadform" id="uploadform">
|
|
<form action="{% url "songs:upload" %}" method="POST" enctype="multipart/form-data">
|
|
<div class="card-header">
|
|
<h3>Upload</h3>
|
|
</div>
|
|
<div class="card-body">
|
|
{% csrf_token %}
|
|
<div class="fileupload fileupload-new" data-provides="fileupload">
|
|
<span class="btn btn-primary btn-file">
|
|
<span v-if="fileObjects.length === 0 && !files_loading">
|
|
Select files
|
|
</span>
|
|
<span v-else-if="files_loading">
|
|
Loading new files...
|
|
</span>
|
|
<span v-else>
|
|
Change
|
|
</span>
|
|
<input class="filefield" id="filefield" type="file" name="file[]" accept="audio/*"
|
|
multiple @change="set_new_files"/>
|
|
</span>
|
|
</div>
|
|
<div class="songs">
|
|
<div v-for="fileObject in fileObjects" class="song-container card mb-3">
|
|
<div class="card-header">
|
|
<h3>${ fileObject.name }$</h3>
|
|
</div>
|
|
<div class="card-body">
|
|
<div class="form-group mb-3">
|
|
<div v-if="fileObject.artist === '' || fileObject.artist === null"
|
|
class="alert alert-danger">Please enter an artist for this song.
|
|
</div>
|
|
<input v-if="upload_in_progress || uploaded" type="text" name="artist[]"
|
|
class="form-control input-sm artist" disabled
|
|
placeholder="Artist" v-model="fileObject.artist"/>
|
|
<input v-else type="text" name="artist[]"
|
|
class="form-control input-sm artist"
|
|
placeholder="Artist" v-model="fileObject.artist"/>
|
|
</div>
|
|
<div class="form-group mb-3">
|
|
<div v-if="fileObject.title === '' || fileObject.title === null"
|
|
class="alert alert-danger">Please enter a title for this song.
|
|
</div>
|
|
<input v-if="upload_in_progress || uploaded" type="text" name="title[]"
|
|
class="form-control input-sm title" disabled
|
|
placeholder="Title" v-model="fileObject.title"/>
|
|
<input v-else type="text" name="title[]" class="form-control input-sm title"
|
|
placeholder="Title" v-model="fileObject.title"/>
|
|
</div>
|
|
<template v-if="fileObject.upload_finished === true">
|
|
<div v-if="fileObject.success === true" class="alert alert-success">Upload
|
|
finished successfully.
|
|
</div>
|
|
<div v-else class="alert alert-danger">${ fileObject.error_message }$</div>
|
|
</template>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="card-footer">
|
|
<div class="progress mt-2 mb-3">
|
|
<div :class="{ 'progress-bar-animated': (upload_in_progress), 'bg-success': (uploaded && everything_successfully_uploaded), 'bg-danger': (uploaded && !everything_successfully_uploaded) }"
|
|
class="progress-bar progress-bar-striped" role="progressbar"
|
|
:style="{ width: (progress_bar_width + '%') }" aria-valuenow="50" aria-valuemin="0"
|
|
aria-valuemax="100"></div>
|
|
</div>
|
|
<template v-if="upload_in_progress || uploaded">
|
|
<button v-if="uploaded" class="btn btn-primary btn-block w-100" v-on:click="clear">
|
|
Clear
|
|
</button>
|
|
<button v-else class="btn btn-primary btn-block w-100 disabled">Clear</button>
|
|
</template>
|
|
<template v-else>
|
|
<input v-if="ready_for_upload" id="upload" class="btn btn-primary btn-block w-100"
|
|
type="submit" value="Upload" v-on:click="upload"/>
|
|
<button v-else class="btn btn-primary btn-block w-100 disabled">Upload</button>
|
|
</template>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<link rel="stylesheet" href="{% static 'songs/css/upload.css' %}"/>
|
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/jsmediatags/3.9.5/jsmediatags.min.js"></script>
|
|
<script>
|
|
let upload_vue = createApp({
|
|
delimiters: ['${', '}$'],
|
|
data() {
|
|
return {
|
|
files: [],
|
|
fileObjects: [],
|
|
uploaded: false,
|
|
upload_in_progress: false,
|
|
files_loading: false,
|
|
}
|
|
},
|
|
computed: {
|
|
ready_for_upload: function () {
|
|
if (this.uploaded !== false || this.upload_in_progress !== false || this.fileObjects.length === 0) {
|
|
return false;
|
|
} else {
|
|
for (let i = 0; i < this.fileObjects.length; i++) {
|
|
if (this.fileObjects[i].artist === null || this.fileObjects[i].artist === '' || this.fileObjects[i].title === null || this.fileObjects[i].title === '') {
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
},
|
|
everything_successfully_uploaded: function () {
|
|
return this.fileObjects.map((fileObject) => {
|
|
return fileObject.upload_finished === true && fileObject.success === true;
|
|
}).reduce((previousValue, currentValue) => {
|
|
return previousValue && currentValue;
|
|
}, true);
|
|
},
|
|
progress_bar_width: function () {
|
|
if (this.fileObjects.length === 0) {
|
|
return 0;
|
|
}
|
|
|
|
const files_uploaded_successfully = this.fileObjects.map((fileObject) => {
|
|
if (fileObject.upload_finished === true && fileObject.success === true) {
|
|
return 1;
|
|
} else {
|
|
return 0;
|
|
}
|
|
}).reduce((previousValue, currentValue) => {
|
|
return previousValue + currentValue;
|
|
}, 0);
|
|
|
|
return Math.round((files_uploaded_successfully / this.fileObjects.length) * 100);
|
|
}
|
|
},
|
|
methods: {
|
|
clear(event) {
|
|
event.preventDefault();
|
|
this.files = [];
|
|
this.fileObjects = [];
|
|
this.uploaded = false;
|
|
this.upload_in_progress = false;
|
|
},
|
|
upload(event) {
|
|
this.upload_in_progress = true;
|
|
event.preventDefault();
|
|
let allPromises = [];
|
|
for (let i = 0; i < this.fileObjects.length; i++) {
|
|
const current_file = this.fileObjects[i].file;
|
|
const current_artist = this.fileObjects[i].artist;
|
|
const current_title = this.fileObjects[i].title;
|
|
let data = new FormData();
|
|
data.append('file', current_file);
|
|
data.append('artist', current_artist);
|
|
data.append('title', current_title);
|
|
allPromises.push(fetch('/api/v1/songs/upload/', {
|
|
method: 'POST',
|
|
headers: {
|
|
"X-CSRFToken": CSRF_TOKEN,
|
|
},
|
|
body: data,
|
|
}).then(result => {
|
|
if (result.status === 200) {
|
|
return result.json();
|
|
} else {
|
|
throw result;
|
|
}
|
|
}).then(() => {
|
|
this.fileObjects[i].success = true;
|
|
}).catch(e => {
|
|
console.log(e);
|
|
if (e instanceof Response) {
|
|
try {
|
|
e.json().then(data => {
|
|
this.fileObjects[i].error_message = data.errorMessage;
|
|
this.fileObjects[i].success = false;
|
|
});
|
|
} catch {
|
|
this.fileObjects[i].error_message = "An exception occurred while uploading this file, please try again.";
|
|
this.fileObjects[i].success = false;
|
|
}
|
|
} else {
|
|
this.fileObjects[i].error_message = "An exception occurred while uploading this file, please try again.";
|
|
this.fileObjects[i].success = false;
|
|
}
|
|
}).finally(() => {
|
|
this.fileObjects[i].upload_finished = true;
|
|
}));
|
|
}
|
|
Promise.all(allPromises).finally(() => {
|
|
this.upload_in_progress = false;
|
|
this.uploaded = true;
|
|
});
|
|
},
|
|
async set_new_files(event) {
|
|
this.files_loading = true;
|
|
this.uploaded = false;
|
|
this.upload_in_progress = false;
|
|
this.files = event.target.files;
|
|
let newFileObjects = [];
|
|
for (let i = 0; i < this.files.length; i++) {
|
|
await this.parseSong(this.files[i]).then((song) => {
|
|
newFileObjects.push(
|
|
{
|
|
"file": this.files[i],
|
|
"name": this.files[i].name,
|
|
"artist": song.artist,
|
|
"title": song.title,
|
|
"success": null,
|
|
"error_message": null,
|
|
"upload_finished": false,
|
|
}
|
|
);
|
|
}).catch(() => {
|
|
newFileObjects.push(
|
|
{
|
|
"file": this.files[i],
|
|
"name": this.files[i].name,
|
|
"artist": "",
|
|
"title": "",
|
|
"success": null,
|
|
"error_message": null,
|
|
"upload_finished": false,
|
|
}
|
|
)
|
|
});
|
|
}
|
|
this.fileObjects = newFileObjects;
|
|
this.files_loading = false;
|
|
},
|
|
async parseSong(file) {
|
|
let jsMediaTags = window.jsmediatags;
|
|
|
|
const tags = await new Promise((resolve, reject) => {
|
|
jsMediaTags.read(file, {
|
|
onSuccess: function (tag) {
|
|
resolve(tag);
|
|
},
|
|
onError: function (error) {
|
|
reject(error);
|
|
}
|
|
});
|
|
});
|
|
|
|
return tags.tags;
|
|
}
|
|
}
|
|
}).mount('#uploadform');
|
|
</script>
|
|
{% endblock %}
|