mirror of
https://gitlab.science.ru.nl/technicie/MarietjeDjango.git
synced 2025-12-09 21:42:20 +01:00
Compare commits
19 Commits
fix/poetry
...
feature/ma
| Author | SHA1 | Date | |
|---|---|---|---|
| 82583d21d8 | |||
| eb1581e41e | |||
| ef250c72a6 | |||
| 6110cd6665 | |||
| 52b24e6b36 | |||
| 2dd4dd3381 | |||
| 219af8fa1d | |||
| 98e43aa688 | |||
| 831f479eec | |||
| 6a9c22b7f8 | |||
| 2d36ace60f | |||
| fbafcf1b06 | |||
| e738dc8ab5 | |||
| 0e6eaa6076 | |||
| d79e3425f4 | |||
| e280fd567d | |||
| 1f831a6dab | |||
| 8a926f3924 | |||
| b2429f941b |
@ -31,19 +31,6 @@ a {
|
|||||||
color: var(--text-color);
|
color: var(--text-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
.table {
|
|
||||||
color: var(--text-color);
|
|
||||||
}
|
|
||||||
|
|
||||||
.table-striped tbody tr:nth-of-type(odd) {
|
|
||||||
background-color: var(--background-shade);
|
|
||||||
color: var(--text-color);
|
|
||||||
}
|
|
||||||
|
|
||||||
.table-striped > tbody > tr:nth-of-type(odd) > * {
|
|
||||||
color: var(--text-color);
|
|
||||||
}
|
|
||||||
|
|
||||||
input[type="text"], input[type="password"] {
|
input[type="text"], input[type="password"] {
|
||||||
background-color: var(--background-shade-light);
|
background-color: var(--background-shade-light);
|
||||||
border: 1px solid var(--background-shade);
|
border: 1px solid var(--background-shade);
|
||||||
@ -82,6 +69,19 @@ footer {
|
|||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.table {
|
||||||
|
color: var(--text-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.table-striped tbody tr:nth-of-type(odd) {
|
||||||
|
background-color: var(--background-shade);
|
||||||
|
color: var(--text-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.table-striped > tbody > tr:nth-of-type(odd) > * {
|
||||||
|
color: var(--text-color);
|
||||||
|
}
|
||||||
|
|
||||||
.marietjequeue {
|
.marietjequeue {
|
||||||
color: #777777;
|
color: #777777;
|
||||||
}
|
}
|
||||||
@ -91,7 +91,24 @@ footer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.marietjequeue-pre-start td {
|
.marietjequeue-pre-start td {
|
||||||
border-bottom: 3px double #777777;
|
border-bottom: 3px double var(--text-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.marietjequeue-post-start td {
|
||||||
|
border-top: 3px double var(--text-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
tr.requested_song{
|
||||||
|
border-left: 1px solid var(--text-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.currentsong {
|
||||||
|
border-bottom: 1px solid var(--text-color);
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.underline_cell {
|
||||||
|
border-bottom: 1px solid var(--text-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
.block-button {
|
.block-button {
|
||||||
@ -100,13 +117,10 @@ footer {
|
|||||||
transition: 1s transform ease-in-out;
|
transition: 1s transform ease-in-out;
|
||||||
}
|
}
|
||||||
|
|
||||||
.currentsong {
|
|
||||||
border-bottom: 1px solid #DDDDDD;
|
|
||||||
}
|
|
||||||
|
|
||||||
.navbar-text {
|
.navbar-text {
|
||||||
color: var(--text-color);
|
color: var(--text-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
.danger {
|
.danger {
|
||||||
color: red !important;
|
color: red !important;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -20,17 +20,19 @@
|
|||||||
|
|
||||||
@media (prefers-color-scheme: dark) {
|
@media (prefers-color-scheme: dark) {
|
||||||
:root {
|
:root {
|
||||||
--background-color: #202020;
|
--background-color: #181818;
|
||||||
--background-shade: #404040;
|
--background-shade: #282828;
|
||||||
--background-shade-light: #696969;
|
--background-shade-light: #404040;
|
||||||
|
|
||||||
--card-background: #696969;
|
--card-background: #404040;
|
||||||
--card-background-shade: #404040;
|
--card-background-shade: #282828;
|
||||||
--card-background-contrast: #ffffff;
|
--card-background-contrast: #dddddd;
|
||||||
|
|
||||||
--title-color: #000000;
|
--title-color: #000000;
|
||||||
--sub-title-color: #dddddd;
|
--sub-title-color: #dddddd;
|
||||||
--link-color: #007bff;
|
--link-color: #007bff;
|
||||||
--text-color: #ffffff;
|
--text-color: #dddddd;
|
||||||
|
|
||||||
|
--bs-border-color: #282828;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -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>
|
||||||
@ -37,6 +40,7 @@
|
|||||||
</section>
|
</section>
|
||||||
<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>
|
||||||
|
|||||||
@ -5,7 +5,7 @@
|
|||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<nav class="navbar navbar-expand navbar-default navbar-light border-bottom">
|
<nav class="navbar navbar-expand navbar-default navbar-light border-bottom">
|
||||||
<div class="container">
|
<div class="container-lg">
|
||||||
<ul class="nav nav-pills" role="tablist">
|
<ul class="nav nav-pills" role="tablist">
|
||||||
<li class="nav-item" role="presentation">
|
<li class="nav-item" role="presentation">
|
||||||
<button class="nav-link active" id="queue-tab" data-bs-toggle="tab" data-bs-target="#queue"
|
<button class="nav-link active" id="queue-tab" data-bs-toggle="tab" data-bs-target="#queue"
|
||||||
@ -39,30 +39,32 @@
|
|||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
<ul v-if="'start_personal_queue' in infobar && infobar.start_personal_queue !== null" id="personal-queue-container" class="navbar-nav navbar-right hidden-xs">
|
<ul id="personal-queue-container" class="navbar-nav navbar-right hidden-xs">
|
||||||
<li v-if="infobar.start_personal_queue != 0" class="nav-item me-3">
|
<template v-if="infobar !== null && 'start_personal_queue' in infobar && infobar.start_personal_queue !== null">
|
||||||
<p v-if="infobar.plays_in" class="navbar-text mb-0 start-queue hidden-sm hidden-xs">
|
<li v-if="infobar.start_personal_queue !== 0" class="nav-item me-3">
|
||||||
First song starts in <% infobar.start_personal_queue.secondsToMMSS() %>
|
<p v-if="infobar.plays_in" class="navbar-text mb-0 start-queue hidden-sm hidden-xs">
|
||||||
</p>
|
First song starts in ${ infobar.start_personal_queue.secondsToMMSS() }$
|
||||||
<p v-else class="navbar-text mb-0 start-queue hidden-sm hidden-xs">
|
</p>
|
||||||
First song starts at <% (infobar.now_in_seconds + infobar.start_personal_queue).timestampToHHMMSS() %>
|
<p v-else class="navbar-text mb-0 start-queue hidden-sm hidden-xs">
|
||||||
</p>
|
First song starts at ${ (infobar.now_in_seconds + infobar.start_personal_queue).timestampToHHMMSS() }$
|
||||||
</li>
|
</p>
|
||||||
<li class="nav-item me-3">
|
</li>
|
||||||
<p v-if="infobar.plays_in" class="navbar-text mb-0 start-queue hidden-sm hidden-xs">
|
<li class="nav-item me-3">
|
||||||
Last song ends in <% infobar.end_personal_queue.secondsToMMSS() %>
|
<p v-if="infobar.plays_in" class="navbar-text mb-0 start-queue hidden-sm hidden-xs">
|
||||||
</p>
|
Last song ends in ${ infobar.end_personal_queue.secondsToMMSS() }$
|
||||||
<p v-else class="navbar-text mb-0 start-queue hidden-sm hidden-xs">
|
</p>
|
||||||
Last song ends at <% (infobar.now_in_seconds + infobar.end_personal_queue).timestampToHHMMSS() %>
|
<p v-else class="navbar-text mb-0 start-queue hidden-sm hidden-xs">
|
||||||
</p>
|
Last song ends at ${ (infobar.now_in_seconds + infobar.end_personal_queue).timestampToHHMMSS() }$
|
||||||
</li>
|
</p>
|
||||||
<li class="nav-item">
|
</li>
|
||||||
<p class="navbar-text mb-0 duration-queue" v-bind:class="{danger: infobar.length_personal_queue > infobar.max_length * 60}">(<% infobar.length_personal_queue.secondsToMMSS() %>)</p>
|
<li class="nav-item">
|
||||||
</li>
|
<p class="navbar-text mb-0 duration-queue" v-bind:class="{danger: infobar.length_personal_queue > infobar.max_length * 60}">(${ infobar.length_personal_queue.secondsToMMSS() }$)</p>
|
||||||
|
</li>
|
||||||
|
</template>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
</nav>
|
</nav>
|
||||||
<div class="container">
|
<div class="container-lg">
|
||||||
<br><br>
|
<br><br>
|
||||||
<div class="alert-location">
|
<div class="alert-location">
|
||||||
</div>
|
</div>
|
||||||
@ -71,7 +73,7 @@
|
|||||||
<div id="queue-container">
|
<div id="queue-container">
|
||||||
<table class="table table-striped">
|
<table class="table table-striped">
|
||||||
<thead>
|
<thead>
|
||||||
<tr class="table-header-style">
|
<tr class="table-header-style underline_cell">
|
||||||
<td class="col-md-4">Artist</td>
|
<td class="col-md-4">Artist</td>
|
||||||
<td class="col-md-4">Title</td>
|
<td class="col-md-4">Title</td>
|
||||||
<td class="col-md-2 d-sm-table-cell d-none">Requested By</td>
|
<td class="col-md-2 d-sm-table-cell d-none">Requested By</td>
|
||||||
@ -84,40 +86,41 @@
|
|||||||
</thead>
|
</thead>
|
||||||
<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),
|
||||||
<td class="artist"><% song.song.artist %></td>
|
underline_cell: (index === queue[-1]),
|
||||||
<td class="title"><% song.song.title %></td>
|
currentsong: (index === 0),}">
|
||||||
|
<td class="artist">${ song.song.artist }$</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>
|
||||||
<div class="d-flex flex-column">
|
<div class="d-flex flex-column">
|
||||||
<div class="d-flex flex-row">
|
<div class="d-flex flex-row">
|
||||||
<button v-if="song.can_move_up" v-on:click="move_down(queue[index-1].id)" class="btn btn-link"><i
|
<button v-if="song.can_move_up" v-on:click="move_down(queue[index-1].id)"
|
||||||
class="fa-solid fa-arrow-up"></i></button>
|
class="btn btn-link p-1 p-md-2"><i class="fa-solid fa-arrow-up"></i></button>
|
||||||
<button v-else class="btn btn-link invisible"><i class="fa-solid fa-arrow-up"></i></button>
|
<button v-else class="btn btn-link invisible p-1 p-md-2"><i class="fa-solid fa-arrow-up"></i></button>
|
||||||
|
|
||||||
<button v-if="song.can_move_down" v-on:click="move_down(song.id)" class="btn btn-link"><i
|
<button v-if="song.can_move_down" v-on:click="move_down(song.id)"
|
||||||
class="fa-solid fa-arrow-down"></i></button>
|
class="btn btn-link p-1 p-md-2"><i class="fa-solid fa-arrow-down"></i></button>
|
||||||
<button v-else class="btn btn-link invisible"><i class="fa-solid fa-arrow-down"></i></button>
|
<button v-else class="btn btn-link invisible p-1 p-md-2"><i class="fa-solid fa-arrow-down"></i></button>
|
||||||
</div>
|
|
||||||
<div class="d-flex flex-row">
|
<button v-if="song.can_delete" v-on:click="cancel_song(song.id)"
|
||||||
<button v-if="song.can_delete" v-on:click="cancel_song(song.id)" class="btn btn-link"><i
|
class="btn btn-link p-1 p-md-2"><i class="fa-solid fa-trash-can"></i></button>
|
||||||
class="fa-solid fa-trash-can"></i></button>
|
<button v-else class="btn btn-link invisible p-1 p-md-2"><i class="fa-solid fa-trash-can"></i></button>
|
||||||
<button v-else class="btn btn-link invisible"><i class="fa-solid fa-trash-can"></i></button>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
@ -128,7 +131,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="tab-pane fade" id="request" role="tabpanel" aria-labelledby="request-tab">
|
<div class="tab-pane fade" id="request" role="tabpanel" aria-labelledby="request-tab">
|
||||||
<div id="request-container">
|
<div id="request-container" class="table-responsive">
|
||||||
<table id="request-table" class="table table-striped">
|
<table id="request-table" class="table table-striped">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
@ -176,7 +179,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>
|
||||||
@ -187,21 +190,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>
|
||||||
<button v-on:click="request_song(song.id);" class="btn btn-link p-0 text-decoration-none"><% song.title %></button>
|
<button v-on:click="request_song(song.id);" class="btn btn-link p-0 text-decoration-none">${ song.title }$</button>
|
||||||
</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>
|
||||||
<button v-on:click="report_song(song.id);" class="btn btn-link p-0 text-decoration-none">
|
<button v-on:click="report_song(song.id);" class="btn btn-link p-0 text-decoration-none">
|
||||||
@ -225,18 +228,34 @@
|
|||||||
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 personal_queue_vue = createApp({
|
||||||
el: '#queue-container',
|
delimiters: ['${', '}$'],
|
||||||
delimiters: ['<%', '%>'],
|
data() {
|
||||||
data: {
|
return {
|
||||||
current_song: null,
|
infobar: null,
|
||||||
queue: [],
|
}
|
||||||
user_data: null,
|
},
|
||||||
refreshing: true,
|
}).mount('#personal-queue-container');
|
||||||
refreshTimer: null,
|
const queue_vue = createApp({
|
||||||
clockInterval: null,
|
delimiters: ['${', '}$'],
|
||||||
started_at: null,
|
data() {
|
||||||
playsIn: true,
|
return {
|
||||||
|
current_song: null,
|
||||||
|
queue: [],
|
||||||
|
user_data: null,
|
||||||
|
refreshing: true,
|
||||||
|
refreshTimer: null,
|
||||||
|
clockInterval: null,
|
||||||
|
started_at: null,
|
||||||
|
playsIn: true,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
playsIn: {
|
||||||
|
handler(val, oldVal) {
|
||||||
|
this.update_infobar();
|
||||||
|
}
|
||||||
|
},
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
this.clockInterval = setInterval(this.update_song_times, 1000);
|
this.clockInterval = setInterval(this.update_song_times, 1000);
|
||||||
@ -302,16 +321,14 @@
|
|||||||
plays_in: this.playsIn,
|
plays_in: this.playsIn,
|
||||||
now_in_seconds: 0,
|
now_in_seconds: 0,
|
||||||
}
|
}
|
||||||
const now_in_seconds = Math.round((new Date()).getTime() / 1000);
|
infoBar.now_in_seconds = Math.round((new Date()).getTime() / 1000);
|
||||||
infoBar.now_in_seconds = now_in_seconds;
|
|
||||||
let current_song_played = now_in_seconds - this.queue[0].started_at;
|
|
||||||
// If the current song is the current user's, their queue has started.
|
// If the current song is the current user's, their queue has started.
|
||||||
if (this.queue[0].user.id == this.user_data.id) {
|
if (this.queue[0].user.id === this.user_data.id) {
|
||||||
infoBar.start_personal_queue = 0;
|
infoBar.start_personal_queue = 0;
|
||||||
}
|
}
|
||||||
for (let i = 0; i < this.queue.length; i++) {
|
for (let i = 0; i < this.queue.length; i++) {
|
||||||
const current_song = this.queue[i];
|
const current_song = this.queue[i];
|
||||||
if (i == 0) {
|
if (i === 0) {
|
||||||
const current_song_remaining_seconds = current_song.song.duration - this.queue[1].time_until_song_seconds;
|
const current_song_remaining_seconds = current_song.song.duration - this.queue[1].time_until_song_seconds;
|
||||||
infoBar['length_personal_queue'] -= current_song_remaining_seconds;
|
infoBar['length_personal_queue'] -= current_song_remaining_seconds;
|
||||||
infoBar['length_total_queue'] -= current_song_remaining_seconds;
|
infoBar['length_total_queue'] -= current_song_remaining_seconds;
|
||||||
@ -321,11 +338,11 @@
|
|||||||
infoBar['length_personal_queue'] += current_song.song.duration;
|
infoBar['length_personal_queue'] += current_song.song.duration;
|
||||||
infoBar['end_personal_queue'] = infoBar['length_total_queue'];
|
infoBar['end_personal_queue'] = infoBar['length_total_queue'];
|
||||||
if (infoBar['start_personal_queue'] === null) {
|
if (infoBar['start_personal_queue'] === null) {
|
||||||
infoBar['start_personal_queue'] = infoBar['length_total_queue'] - current_song.song.duration - this.queue[1].time_until_song_seconds
|
infoBar['start_personal_queue'] = infoBar['length_total_queue'] - current_song.song.duration - this.queue[1].time_until_song_seconds;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
this.$emit("infobar", infoBar);
|
personal_queue_vue.infobar = infoBar;
|
||||||
},
|
},
|
||||||
refresh() {
|
refresh() {
|
||||||
if (!this.refreshing) {
|
if (!this.refreshing) {
|
||||||
@ -411,29 +428,20 @@
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
});
|
}).mount("#queue-container");
|
||||||
const personal_queue_vue = new Vue({
|
|
||||||
el: '#personal-queue-container',
|
|
||||||
delimiters: ['<%', '%>'],
|
|
||||||
data: {
|
|
||||||
infobar: [],
|
|
||||||
},
|
|
||||||
mounted() {
|
|
||||||
queue_vue.$on("infobar", infoBar => this.infobar = infoBar);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
</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: {
|
||||||
@ -537,7 +545,8 @@
|
|||||||
"Content-Type": 'application/json',
|
"Content-Type": 'application/json',
|
||||||
},
|
},
|
||||||
}).then(response => {
|
}).then(response => {
|
||||||
if (response.status === 200) {
|
// TODO: Communicate failure through HTTP error codes (403) instead of checking response.success.
|
||||||
|
if (response.status === 200 && response.success) {
|
||||||
return response.json();
|
return response.json();
|
||||||
} else {
|
} else {
|
||||||
throw response;
|
throw response;
|
||||||
@ -596,7 +605,7 @@
|
|||||||
this.page_number = page_number;
|
this.page_number = page_number;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
}).mount('#request-container');
|
||||||
</script>
|
</script>
|
||||||
<script>
|
<script>
|
||||||
function volume_down() {
|
function volume_down() {
|
||||||
|
|||||||
@ -86,7 +86,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={
|
||||||
|
|||||||
@ -4,7 +4,7 @@
|
|||||||
{% block title %}Manage{% endblock %}
|
{% block title %}Manage{% endblock %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<div class="container">
|
<div class="container-lg">
|
||||||
<div class="table-responsive mt-5">
|
<div class="table-responsive mt-5">
|
||||||
<table id="request-table" class="table table-striped">
|
<table id="request-table" class="table table-striped">
|
||||||
<thead>
|
<thead>
|
||||||
@ -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,17 +64,18 @@
|
|||||||
</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,
|
||||||
user_data: null,
|
user_data: null,
|
||||||
|
}
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
search_input: {
|
search_input: {
|
||||||
@ -167,6 +168,6 @@
|
|||||||
this.page_number = page_number;
|
this.page_number = page_number;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
}).mount('#request-table');
|
||||||
</script>
|
</script>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|||||||
@ -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,26 +32,35 @@
|
|||||||
<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">
|
||||||
<div v-if="fileObject.artist === '' || fileObject.artist === null" class="alert alert-danger">Please enter an artist for this song.</div>
|
<div v-if="fileObject.artist === '' || fileObject.artist === null"
|
||||||
<input v-if="upload_in_progress || uploaded" type="text" name="artist[]" class="form-control input-sm artist" disabled
|
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"/>
|
placeholder="Artist" v-model="fileObject.artist"/>
|
||||||
<input v-else type="text" name="artist[]" class="form-control input-sm artist"
|
<input v-else type="text" name="artist[]"
|
||||||
|
class="form-control input-sm artist"
|
||||||
placeholder="Artist" v-model="fileObject.artist"/>
|
placeholder="Artist" v-model="fileObject.artist"/>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group mb-3">
|
<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>
|
<div v-if="fileObject.title === '' || fileObject.title === null"
|
||||||
<input v-if="upload_in_progress || uploaded" type="text" name="title[]" class="form-control input-sm title" disabled
|
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"/>
|
placeholder="Title" v-model="fileObject.title"/>
|
||||||
<input v-else type="text" name="title[]" class="form-control input-sm title"
|
<input v-else type="text" name="title[]" class="form-control input-sm title"
|
||||||
placeholder="Title" v-model="fileObject.title"/>
|
placeholder="Title" v-model="fileObject.title"/>
|
||||||
</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
|
||||||
<div v-else class="alert alert-danger"><% fileObject.error_message %></div>
|
finished successfully.
|
||||||
|
</div>
|
||||||
|
<div v-else class="alert alert-danger">${ fileObject.error_message }$</div>
|
||||||
</template>
|
</template>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -56,14 +68,20 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="card-footer">
|
<div class="card-footer">
|
||||||
<div class="progress mt-2 mb-3">
|
<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 :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>
|
</div>
|
||||||
<template v-if="upload_in_progress || uploaded">
|
<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-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>
|
<button v-else class="btn btn-primary btn-block w-100 disabled">Clear</button>
|
||||||
</template>
|
</template>
|
||||||
<template v-else>
|
<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"/>
|
<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>
|
<button v-else class="btn btn-primary btn-block w-100 disabled">Upload</button>
|
||||||
</template>
|
</template>
|
||||||
</div>
|
</div>
|
||||||
@ -73,20 +91,21 @@
|
|||||||
</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://cdnjs.cloudflare.com/ajax/libs/jsmediatags/3.9.5/jsmediatags.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 () {
|
||||||
if (this.uploaded !== false || this.upload_in_progress !== false || this.fileObjects.length === 0) {
|
if (this.uploaded !== false || this.upload_in_progress !== false || this.fileObjects.length === 0) {
|
||||||
return false;
|
return false;
|
||||||
} else {
|
} else {
|
||||||
@ -98,14 +117,14 @@
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
everything_successfully_uploaded: function() {
|
everything_successfully_uploaded: function () {
|
||||||
return this.fileObjects.map((fileObject) => {
|
return this.fileObjects.map((fileObject) => {
|
||||||
return fileObject.upload_finished === true && fileObject.success === true;
|
return fileObject.upload_finished === true && fileObject.success === true;
|
||||||
}).reduce((previousValue, currentValue) => {
|
}).reduce((previousValue, currentValue) => {
|
||||||
return previousValue && currentValue;
|
return previousValue && currentValue;
|
||||||
}, true);
|
}, true);
|
||||||
},
|
},
|
||||||
progress_bar_width: function() {
|
progress_bar_width: function () {
|
||||||
if (this.fileObjects.length === 0) {
|
if (this.fileObjects.length === 0) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -158,14 +177,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 +202,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 +232,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);
|
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>
|
</script>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|||||||
@ -28,6 +28,7 @@
|
|||||||
<th>#</th>
|
<th>#</th>
|
||||||
<th>User</th>
|
<th>User</th>
|
||||||
<th style="text-align: right;"># Songs</th>
|
<th style="text-align: right;"># Songs</th>
|
||||||
|
<th></th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
@ -54,6 +55,7 @@
|
|||||||
<th>#</th>
|
<th>#</th>
|
||||||
<th>User</th>
|
<th>User</th>
|
||||||
<th style="text-align: right;"># Requests</th>
|
<th style="text-align: right;"># Requests</th>
|
||||||
|
<th></th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
@ -109,6 +111,7 @@
|
|||||||
<th>#</th>
|
<th>#</th>
|
||||||
<th>User</th>
|
<th>User</th>
|
||||||
<th style="text-align: right;"># Unique</th>
|
<th style="text-align: right;"># Unique</th>
|
||||||
|
<th></th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
@ -176,7 +179,9 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="col-md-6">
|
<div class="col-md-6">
|
||||||
<h2>Most played uploaders</h2>
|
<h2>Most played uploaders</h2>
|
||||||
<p>These are the {{ stats.stats_top_count }} people whose songs are requested most often by other people, as shown in the left column. The right column shows how many times that person has queued his own songs.</p>
|
<p>These are the {{ stats.stats_top_count }} people whose songs are requested most often by other
|
||||||
|
people, as shown in the left column. The right column shows how many times that person has queued
|
||||||
|
their own songs.</p>
|
||||||
<div class="table-responsive">
|
<div class="table-responsive">
|
||||||
<table class="table table-striped">
|
<table class="table table-striped">
|
||||||
<thead>
|
<thead>
|
||||||
@ -202,7 +207,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="col-md-6">
|
<div class="col-md-6">
|
||||||
<h2>Most played songs last 14 days</h2>
|
<h2>Most played songs last 14 days</h2>
|
||||||
<p>These songs are played the {{ stats.stats_top_count }} most in the last two weeks.</p>
|
<p>These {{ stats.stats_top_count }} songs have been requested the most in the last two weeks.</p>
|
||||||
<div class="table-responsive">
|
<div class="table-responsive">
|
||||||
<table class="table table-striped">
|
<table class="table table-striped">
|
||||||
<thead>
|
<thead>
|
||||||
|
|||||||
@ -21,9 +21,10 @@
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
<div class="col-md-6">
|
<div class="col-md-6">
|
||||||
<h2>Most played songs</h2>
|
<h2>Most played songs</h2>
|
||||||
<p>You have requested <strong> {{ stats.unique_requests }} </strong> different
|
<p>You have requested <strong> {{ stats.unique_requests }} </strong> different songs a total of
|
||||||
songs a total of <strong> {{ stats.total_requests }} </strong> times. This
|
<strong> {{ stats.total_requests }} </strong> times. This means
|
||||||
means <strong> {% widthratio stats.unique_requests stats.total_requests 100 %}% </strong> of your requests have been unique. </p>
|
<strong> {% widthratio stats.unique_requests stats.total_requests 100 %}% </strong> of your requests
|
||||||
|
have been unique. These are the song you have requested the most.</p>
|
||||||
<h4>Top {{ stats.stats_top_count }}:</h4>
|
<h4>Top {{ stats.stats_top_count }}:</h4>
|
||||||
<div class="table-responsive">
|
<div class="table-responsive">
|
||||||
<table class="table table-striped">
|
<table class="table table-striped">
|
||||||
@ -50,6 +51,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="col-md-6">
|
<div class="col-md-6">
|
||||||
<h2>Most played artists</h2>
|
<h2>Most played artists</h2>
|
||||||
|
<p>These are the artists you have requested the most.</p>
|
||||||
<h4>Top {{ stats.stats_top_count }}:</h4>
|
<h4>Top {{ stats.stats_top_count }}:</h4>
|
||||||
<div class="table-responsive">
|
<div class="table-responsive">
|
||||||
<table class="table table-striped">
|
<table class="table table-striped">
|
||||||
@ -57,7 +59,7 @@
|
|||||||
<tr>
|
<tr>
|
||||||
<th>#</th>
|
<th>#</th>
|
||||||
<th>Artist</th>
|
<th>Artist</th>
|
||||||
<th style="text-align: right;"># Requests</th>
|
<th style="white-space:nowrap; text-align: right;"># Requests</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
@ -74,11 +76,11 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="col-md-6">
|
<div class="col-md-6">
|
||||||
<h2>Uploads requested</h2>
|
<h2>Uploads requested</h2>
|
||||||
<p> You have uploaded a total of <strong> {{stats.total_uploads }} </strong> songs. The left column
|
<p>You have uploaded a total of <strong> {{stats.total_uploads }} </strong> songs. The left column
|
||||||
shows how many times these have been requested by other people. The right column shows
|
shows how many times these have been requested by other people. The right column shows how many times
|
||||||
how many times you requested your own songs. In total your songs
|
you requested your own songs. In total your songs have been queued
|
||||||
have been queued <strong> {{stats.total_played_uploads }} </strong> times by others and
|
<strong> {{stats.total_played_uploads }} </strong> times by others and
|
||||||
<strong> {{stats.total_played_user_uploads }} </strong> by yourself.
|
<strong> {{stats.total_played_user_uploads }} </strong> times by yourself.</p>
|
||||||
<h4>Top {{ stats.stats_top_count }}:</h4>
|
<h4>Top {{ stats.stats_top_count }}:</h4>
|
||||||
<div class="table-responsive">
|
<div class="table-responsive">
|
||||||
<table class="table table-striped">
|
<table class="table table-striped">
|
||||||
@ -87,8 +89,8 @@
|
|||||||
<th>#</th>
|
<th>#</th>
|
||||||
<th>Artist</th>
|
<th>Artist</th>
|
||||||
<th>Title</th>
|
<th>Title</th>
|
||||||
<th style="text-align: right;">Others</th>
|
<th style="white-space:nowrap; text-align: right;"># Others</th>
|
||||||
<th>You</th>
|
<th style="white-space:nowrap;"># You</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
@ -107,8 +109,8 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="col-md-6">
|
<div class="col-md-6">
|
||||||
<h2>Upload artists requested</h2>
|
<h2>Upload artists requested</h2>
|
||||||
<p> The left column shows how many times songs from artists uploaded by you have been requested by
|
<p>The left column shows how many times songs from artists uploaded by you have been requested by
|
||||||
other people. The right column shows how many times you requested those songs.
|
other people. The right column shows how many times you requested those songs.</p>
|
||||||
<h4>Top {{ stats.stats_top_count }}:</h4>
|
<h4>Top {{ stats.stats_top_count }}:</h4>
|
||||||
<div class="table-responsive">
|
<div class="table-responsive">
|
||||||
<table class="table table-striped">
|
<table class="table table-striped">
|
||||||
@ -116,8 +118,8 @@
|
|||||||
<tr>
|
<tr>
|
||||||
<th>#</th>
|
<th>#</th>
|
||||||
<th>Artist</th>
|
<th>Artist</th>
|
||||||
<th style="text-align: right;">Others</th>
|
<th style="white-space:nowrap; text-align: right;"># Others</th>
|
||||||
<th>You</th>
|
<th style="white-space:nowrap;"># You</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
@ -135,14 +137,15 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="col-md-6">
|
<div class="col-md-6">
|
||||||
<h2>Most played uploaders</h2>
|
<h2>Most played uploaders</h2>
|
||||||
<p> The people whose songs you have queued the most are:</p>
|
<p>These are the people whose songs you have requested the most.</p>
|
||||||
<div class="table-responsive">
|
<div class="table-responsive">
|
||||||
<table class="table table-striped">
|
<table class="table table-striped">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th>#</th>
|
<th>#</th>
|
||||||
<th>Uploader</th>
|
<th>Uploader</th>
|
||||||
<th style="text-align: right;"># Requests</th>
|
<th style="white-space:nowrap; text-align: right;"># Requests</th>
|
||||||
|
<th></th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
@ -160,14 +163,14 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="col-md-6">
|
<div class="col-md-6">
|
||||||
<h2>Biggest fans</h2>
|
<h2>Biggest fans</h2>
|
||||||
<p> The people that queued your songs the most are:</p>
|
<p>These are the people that have requested your songs the most.</p>
|
||||||
<div class="table-responsive">
|
<div class="table-responsive">
|
||||||
<table class="table table-striped">
|
<table class="table table-striped">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th>#</th>
|
<th>#</th>
|
||||||
<th>User</th>
|
<th>User</th>
|
||||||
<th style="text-align: right;"># Requests</th>
|
<th style="white-space:nowrap; text-align: right;"># Requests</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
[tool.poetry]
|
[tool.poetry]
|
||||||
name = "MarietjeDjango"
|
name = "marietje"
|
||||||
version = "4.1.0"
|
version = "4.1.0"
|
||||||
description = "A music player for the south canteen of the Huygens building"
|
description = "A music player for the south canteen of the Huygens building"
|
||||||
authors = [
|
authors = [
|
||||||
@ -11,7 +11,7 @@ authors = [
|
|||||||
"Lars van Rhijn <l.vanrhijn@student.science.ru.nl>",
|
"Lars van Rhijn <l.vanrhijn@student.science.ru.nl>",
|
||||||
]
|
]
|
||||||
maintainers = [
|
maintainers = [
|
||||||
"Kees van Kempen <ru@keesvankempen.nl",
|
"Kees van Kempen <ru@keesvankempen.nl>",
|
||||||
"Lars van Rhijn <l.vanrhijn@student.science.ru.nl>",
|
"Lars van Rhijn <l.vanrhijn@student.science.ru.nl>",
|
||||||
]
|
]
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
|
|||||||
Reference in New Issue
Block a user