Marietje in header and fix load times for upload page

This commit is contained in:
Lars van Rhijn
2023-10-04 20:00:18 +02:00
parent 12e47ce6c2
commit b2429f941b
5 changed files with 112 additions and 79 deletions

View File

@ -15,7 +15,10 @@
<link href="{% static 'fontawesomefree/css/all.min.css' %}" rel="stylesheet" type="text/css"> <link href="{% static 'fontawesomefree/css/all.min.css' %}" rel="stylesheet" type="text/css">
<!-- Vue JS --> <!-- Vue JS -->
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.min.js"></script> <script src="https://unpkg.com/vue@3/dist/vue.global.prod.js"></script>
<script>
const { createApp } = Vue;
</script>
<!-- TaTa.js notifications --> <!-- TaTa.js notifications -->
<script src="{% static 'marietje/js/tata.js' %}"></script> <script src="{% static 'marietje/js/tata.js' %}"></script>
@ -26,6 +29,7 @@
<body> <body>
<nav class="navbar navbar-expand-lg sticky-top navbar-dark bg-primary"> <nav class="navbar navbar-expand-lg sticky-top navbar-dark bg-primary">
<div class="container"> <div class="container">
<a class="navbar-brand d-block d-lg-none" href="{% url "index" %}">Marietje 4.1</a>
<button class="navbar-toggler ms-auto" type="button" data-bs-toggle="offcanvas" <button class="navbar-toggler ms-auto" type="button" data-bs-toggle="offcanvas"
data-bs-target="#offcanvasNavbar" aria-controls="offcanvasNavbar"> data-bs-target="#offcanvasNavbar" aria-controls="offcanvasNavbar">
<span class="navbar-toggler-icon"></span> <span class="navbar-toggler-icon"></span>

View File

@ -74,22 +74,22 @@
<tbody class="queuebody"> <tbody class="queuebody">
<template v-for="(song, index) in queue"> <template v-for="(song, index) in queue">
<tr :class="{ marietjequeue: (song.user === null), currentsong: (index === 0), 'fw-bold': (index === 0) }"> <tr :class="{ marietjequeue: (song.user === null), currentsong: (index === 0), 'fw-bold': (index === 0) }">
<td class="artist"><% song.song.artist %></td> <td class="artist">${song.song.artist }$</td>
<td class="title"><% song.song.title %></td> <td class="title">${song.song.title }$</td>
<td class="d-sm-table-cell d-none requested-by"> <td class="d-sm-table-cell d-none requested-by">
<template v-if="song.user === null"> <template v-if="song.user === null">
Marietje Marietje
</template> </template>
<template v-else> <template v-else>
<% song.user.name %> ${song.user.name }$
</template> </template>
</td> </td>
<td class="d-sm-table-cell d-none plays-at" style="text-align: right"> <td class="d-sm-table-cell d-none plays-at" style="text-align: right">
<template v-if="song.time_until_song_seconds !== null && song.time_until_song_seconds > 0 && playsIn === true"> <template v-if="song.time_until_song_seconds !== null && song.time_until_song_seconds > 0 && playsIn === true">
<% song.time_until_song_seconds.secondsToMMSS() %> ${song.time_until_song_seconds.secondsToMMSS() }$
</template> </template>
<template v-else-if="playsIn === false && song.plays_at !== null && song.played === false"> <template v-else-if="playsIn === false && song.plays_at !== null && song.played === false">
<% song.plays_at.timestampToHHMMSS() %> ${song.plays_at.timestampToHHMMSS() }$
</template> </template>
</td> </td>
<td> <td>
@ -160,7 +160,7 @@
</select> </select>
<select class="pagenum input-mini" title="Select page number" v-model="page_number"> <select class="pagenum input-mini" title="Select page number" v-model="page_number">
<template v-for="(i, index) in number_of_pages"> <template v-for="(i, index) in number_of_pages">
<option :value="i"><% i %></option> <option :value="i">${i }$</option>
</template> </template>
</select> </select>
</th> </th>
@ -171,21 +171,21 @@
<template v-for="(song, index) in songs"> <template v-for="(song, index) in songs">
<tr> <tr>
<td> <td>
<% song.artist %> ${song.artist }$
</td> </td>
<td> <td>
<a href="#" v-on:click="request_song(song.id);"><% song.title %></a> <a href="#" v-on:click="request_song(song.id);">${song.title }$</a>
</td> </td>
<td> <td>
<template v-if="song.user === null"> <template v-if="song.user === null">
Marietje Marietje
</template> </template>
<template v-else> <template v-else>
<% song.user.name %> ${song.user.name }$
</template> </template>
</td> </td>
<td> <td>
<% song.duration.secondsToMMSS() %> ${song.duration.secondsToMMSS() }$
</td> </td>
<td> <td>
<a href="#" v-on:click="report_song(song.id);"> <a href="#" v-on:click="report_song(song.id);">
@ -209,18 +209,19 @@
const CAN_MOVE = {{ perms.queues.can_move|yesno:"1,0" }}; const CAN_MOVE = {{ perms.queues.can_move|yesno:"1,0" }};
</script> </script>
<script> <script>
const queue_vue = new Vue({ const queue_vue = createApp({
el: '#queue-container', delimiters: ['${', '}$'],
delimiters: ['<%', '%>'], data() {
data: { return {
current_song: null, current_song: null,
queue: [], queue: [],
user_data: null, user_data: null,
refreshing: true, refreshing: true,
refreshTimer: null, refreshTimer: null,
clockInterval: null, clockInterval: null,
started_at: null, started_at: null,
playsIn: true, playsIn: true,
}
}, },
mounted() { mounted() {
this.clockInterval = setInterval(this.update_song_times, 1000); this.clockInterval = setInterval(this.update_song_times, 1000);
@ -372,19 +373,20 @@
}); });
} }
} }
}); }).mount('#queue-container');
</script> </script>
<script> <script>
const request_vue = new Vue({ const request_vue = createApp({
el: '#request-container', delimiters: ['${', '}$'],
delimiters: ['<%', '%>'], data() {
data: { return {
songs: [], songs: [],
total_songs: 0, total_songs: 0,
search_input: "", search_input: "",
typing_timer: null, typing_timer: null,
page_size: 10, page_size: 10,
page_number: 1, page_number: 1,
}
}, },
watch: { watch: {
search_input: { search_input: {
@ -542,7 +544,7 @@
this.page_number = page_number; this.page_number = page_number;
} }
} }
}); }).mount('#request-container');
</script> </script>
<script> <script>
function volume_down() { function volume_down() {

View File

@ -84,7 +84,7 @@ class SongUploadAPIView(APIView):
song = upload_file(file, artist, title, request.user) song = upload_file(file, artist, title, request.user)
upload_counter.inc() upload_counter.inc()
return Response(status=200, data=self.serializer_class(song).data) return Response(status=200, data=self.serializer_class(song).data)
except UploadException: except (UploadException, ConnectionRefusedError):
return Response( return Response(
status=500, status=500,
data={ data={

View File

@ -41,7 +41,7 @@
</select> </select>
<select class="pagenum input-mini" title="Select page number" v-model="page_number"> <select class="pagenum input-mini" title="Select page number" v-model="page_number">
<template v-for="(i, index) in number_of_pages"> <template v-for="(i, index) in number_of_pages">
<option :value="i"><% i %></option> <option :value="i">${ i }$</option>
</template> </template>
</select> </select>
</th> </th>
@ -52,10 +52,10 @@
<template v-for="(song, index) in songs"> <template v-for="(song, index) in songs">
<tr> <tr>
<td> <td>
<% song.artist %> ${ song.artist }$
</td> </td>
<td> <td>
<a :href="'/songs/edit/' + song.id + '/'" v-on:click="request_song(song.id);"><% song.title %></a> <a :href="'/songs/edit/' + song.id + '/'" v-on:click="request_song(song.id);">${ song.title }$</a>
</td> </td>
</tr> </tr>
</template> </template>
@ -64,16 +64,17 @@
</div> </div>
</div> </div>
<script> <script>
let manage_vue = new Vue({ let manage_vue = createApp({
el: '#request-table', delimiters: ['${', '}$'],
delimiters: ['<%', '%>'], data() {
data: { return {
songs: [], songs: [],
total_songs: 0, total_songs: 0,
search_input: "", search_input: "",
typing_timer: null, typing_timer: null,
page_size: 10, page_size: 10,
page_number: 1, page_number: 1,
}
}, },
watch: { watch: {
search_input: { search_input: {
@ -168,6 +169,6 @@
this.page_number = page_number; this.page_number = page_number;
} }
} }
}); }).mount('#request-table');
</script> </script>
{% endblock %} {% endblock %}

View File

@ -16,9 +16,12 @@
{% csrf_token %} {% csrf_token %}
<div class="fileupload fileupload-new" data-provides="fileupload"> <div class="fileupload fileupload-new" data-provides="fileupload">
<span class="btn btn-primary btn-file"> <span class="btn btn-primary btn-file">
<span v-if="fileObjects.length === 0"> <span v-if="fileObjects.length === 0 && !files_loading">
Select files Select files
</span> </span>
<span v-else-if="files_loading">
Loading new files...
</span>
<span v-else> <span v-else>
Change Change
</span> </span>
@ -29,7 +32,7 @@
<div class="songs"> <div class="songs">
<div v-for="fileObject in fileObjects" class="song-container card mb-3"> <div v-for="fileObject in fileObjects" class="song-container card mb-3">
<div class="card-header"> <div class="card-header">
<h3><% fileObject.name %></h3> <h3>${ fileObject.name }$</h3>
</div> </div>
<div class="card-body"> <div class="card-body">
<div class="form-group mb-3"> <div class="form-group mb-3">
@ -48,7 +51,7 @@
</div> </div>
<template v-if="fileObject.upload_finished === true"> <template v-if="fileObject.upload_finished === true">
<div v-if="fileObject.success === true" class="alert alert-success">Upload finished successfully.</div> <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> <div v-else class="alert alert-danger">${ fileObject.error_message }$</div>
</template> </template>
</div> </div>
</div> </div>
@ -73,17 +76,18 @@
</div> </div>
</div> </div>
<link rel="stylesheet" href="{% static 'songs/css/upload.css' %}"/> <link rel="stylesheet" href="{% static 'songs/css/upload.css' %}"/>
<script type="module"> <script src="https://cdn.jsdelivr.net/npm/mp3tag.js@latest/dist/mp3tag.min.js"></script>
import * as id3 from '//unpkg.com/id3js@^2/lib/id3.js'; <script>
let upload_vue = createApp({
let upload_vue = new Vue({ delimiters: ['${', '}$'],
el: '#uploadform', data() {
delimiters: ['<%', '%>'], return {
data: { files: [],
files: [], fileObjects: [],
fileObjects: [], uploaded: false,
uploaded: false, upload_in_progress: false,
upload_in_progress: false, files_loading: false,
}
}, },
computed: { computed: {
ready_for_upload: function() { ready_for_upload: function() {
@ -158,14 +162,20 @@
}).then(() => { }).then(() => {
this.fileObjects[i].success = true; this.fileObjects[i].success = true;
}).catch(e => { }).catch(e => {
console.log(e);
if (e instanceof Response) { if (e instanceof Response) {
e.json().then(data => { try {
this.fileObjects.error_message = data.errorMessage; e.json().then(data => {
this.fileObjects.success = false; 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 { } else {
this.fileObjects.error_message = "An exception occurred while uploading this file, please try again."; this.fileObjects[i].error_message = "An exception occurred while uploading this file, please try again.";
this.fileObjects.success = false; this.fileObjects[i].success = false;
} }
}).finally(() => { }).finally(() => {
this.fileObjects[i].upload_finished = true; this.fileObjects[i].upload_finished = true;
@ -177,23 +187,25 @@
}); });
}, },
async set_new_files(event) { async set_new_files(event) {
this.files_loading = true;
this.uploaded = false;
this.upload_in_progress = false;
this.files = event.target.files; this.files = event.target.files;
let newFileObjects = []; let newFileObjects = [];
for (let i = 0; i < this.files.length; i++) { for (let i = 0; i < this.files.length; i++) {
try { await this.parseSong(this.files[i]).then((song) => {
const tags = await this.parseSong(this.files[i]);
newFileObjects.push( newFileObjects.push(
{ {
"file": this.files[i], "file": this.files[i],
"name": this.files[i].name, "name": this.files[i].name,
"artist": tags.artist, "artist": song.artist,
"title": tags.title, "title": song.title,
"success": null, "success": null,
"error_message": null, "error_message": null,
"upload_finished": false, "upload_finished": false,
} }
); );
} catch { }).catch(() => {
newFileObjects.push( newFileObjects.push(
{ {
"file": this.files[i], "file": this.files[i],
@ -205,14 +217,28 @@
"upload_finished": false, "upload_finished": false,
} }
) )
} });
} }
this.fileObjects = newFileObjects; this.fileObjects = newFileObjects;
this.files_loading = false;
}, },
parseSong(file) { async parseSong(file) {
return id3.fromFile(file); const buffer = await new Promise((resolve) => {
let fileReader = new FileReader();
fileReader.onload = (e) => resolve(fileReader.result);
fileReader.readAsArrayBuffer(file);
});
const mp3tag = new MP3Tag(buffer);
mp3tag.read()
if (mp3tag.error !== '') {
throw new Error(mp3tag.error);
} else {
return mp3tag.tags;
}
} }
} }
}); }).mount('#uploadform');
</script> </script>
{% endblock %} {% endblock %}