Add "Library" page with tag/label editor
This commit is contained in:
parent
232929a47a
commit
bc48e231ee
@ -1,5 +1,9 @@
|
|||||||
CREATE TABLE tags(uuid TEXT PRIMARY KEY, tag TEXT, label TEXT);
|
CREATE TABLE tags (tag TEXT, seen_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP);
|
||||||
INSERT INTO tags (uuid, tag, label) VALUES
|
INSERT INTO tags (tag) VALUES
|
||||||
|
("12121212"),
|
||||||
|
("badc0ffee");
|
||||||
|
CREATE TABLE library (uuid TEXT PRIMARY KEY, tag TEXT, label TEXT);
|
||||||
|
INSERT INTO library (uuid, tag, label) VALUES
|
||||||
("585687cb-1ff7-4106-a888-a40ff09b7fa1", "12121212", "OK Then"),
|
("585687cb-1ff7-4106-a888-a40ff09b7fa1", "12121212", "OK Then"),
|
||||||
("85a6f785-51e1-4220-be75-fd306b973221", "12121212", "Other"),
|
("85a6f785-51e1-4220-be75-fd306b973221", "12121212", "Other"),
|
||||||
("b4eabe35-9f96-4455-9294-56cc375cfbc4", "badc0ffee", " Brazil Theme");
|
("b4eabe35-9f96-4455-9294-56cc375cfbc4", "badc0ffee", " Brazil Theme");
|
||||||
|
@ -9,7 +9,8 @@ class SqliteBackend {
|
|||||||
}
|
}
|
||||||
|
|
||||||
find(tag, callback) {
|
find(tag, callback) {
|
||||||
this.db.get("SELECT uuid, tag, label FROM tags WHERE tag = ? AND _ROWID_ >= (abs(random()) % (SELECT max(_ROWID_) FROM tags)) LIMIT 1", tag, (err, row) => {
|
this.db.run("INSERT INTO tags (tag) VALUES (?)", tag);
|
||||||
|
this.db.get("SELECT uuid, tag, label FROM library WHERE tag = ? AND _ROWID_ >= (abs(random()) % (SELECT max(_ROWID_) FROM tags)) LIMIT 1", tag, (err, row) => {
|
||||||
if (typeof row === 'undefined') {
|
if (typeof row === 'undefined') {
|
||||||
return callback();
|
return callback();
|
||||||
}
|
}
|
||||||
|
38
player.js
38
player.js
@ -101,6 +101,7 @@ app.set('view engine', '.hbs');
|
|||||||
app.set('views', __dirname + '/views');
|
app.set('views', __dirname + '/views');
|
||||||
|
|
||||||
app.use(morgan('dev'))
|
app.use(morgan('dev'))
|
||||||
|
app.use(express.json())
|
||||||
app.use(express.static(__dirname + '/static'))
|
app.use(express.static(__dirname + '/static'))
|
||||||
|
|
||||||
PlayLog.on('update', tag => {
|
PlayLog.on('update', tag => {
|
||||||
@ -122,19 +123,34 @@ app.get('/', function (req, res, next) {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
app.get('/api/tags', function(req, res, next) {
|
||||||
|
db.all("SELECT DISTINCT tag FROM tags", (err, rows) => {
|
||||||
|
res.send(rows);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
app.patch('/api/tracks/:track', function(req, res, next) {
|
||||||
|
var track_uuid = req.params.track,
|
||||||
|
label = req.body.label,
|
||||||
|
tag = req.body.tag;
|
||||||
|
|
||||||
|
console.log(track_uuid, req.body, label, tag);
|
||||||
|
db.run("UPDATE library SET label = ?, tag = ? WHERE uuid = ?", label, tag, track_uuid, (err, rows) => {
|
||||||
|
db.get("SELECT * FROM library WHERE uuid = ?", track_uuid, (err, row) => {
|
||||||
|
res.send(row);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
app.get('/api/tracks', function(req, res, next) {
|
||||||
|
db.all("SELECT * FROM library ORDER BY label ASC", (err, rows) => {
|
||||||
|
res.send(rows);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
app.get('/library', function(req, res, next) {
|
app.get('/library', function(req, res, next) {
|
||||||
db.all("SELECT * FROM tags", (err, rows) => {
|
res.render('library', {
|
||||||
var content = views.library.render({
|
|
||||||
rows: rows,
|
|
||||||
config: config,
|
|
||||||
});
|
|
||||||
|
|
||||||
var html = views.base.render({
|
|
||||||
title: 'Library',
|
title: 'Library',
|
||||||
content: content,
|
|
||||||
});
|
|
||||||
|
|
||||||
res.send(html);
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -21,3 +21,9 @@ footer {
|
|||||||
margin-top: 2em;
|
margin-top: 2em;
|
||||||
font-size: 0.75rem;
|
font-size: 0.75rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.media-track {
|
||||||
|
margin: 1ex 0;
|
||||||
|
padding: 1ex;
|
||||||
|
border: 1px solid hsl(0, 0%, 88.2%);
|
||||||
|
}
|
||||||
|
@ -22,7 +22,7 @@ window.onload = function() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
setInterval(function() {
|
setInterval(function() {
|
||||||
if (ws.readyState == WebSocket.CLOSED) {
|
if (ws && ws.readyState == WebSocket.CLOSED) {
|
||||||
connectWebsocket();
|
connectWebsocket();
|
||||||
}
|
}
|
||||||
}, 1000);
|
}, 1000);
|
||||||
|
122
static/library.js
Normal file
122
static/library.js
Normal file
@ -0,0 +1,122 @@
|
|||||||
|
(function() {
|
||||||
|
|
||||||
|
var appData = {
|
||||||
|
tracks: [],
|
||||||
|
tags: [],
|
||||||
|
};
|
||||||
|
|
||||||
|
const MediaTrack = {
|
||||||
|
props: ['track', 'tags'],
|
||||||
|
data: function() {
|
||||||
|
return {
|
||||||
|
editing: false,
|
||||||
|
saving: false,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
patch_url: function() {
|
||||||
|
return '/api/tracks/' + this.track.uuid;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
edit: function() {
|
||||||
|
this.saving = false;
|
||||||
|
this.editing = true;
|
||||||
|
},
|
||||||
|
save: function() {
|
||||||
|
this.saving = true;
|
||||||
|
|
||||||
|
var request = new Request(this.patch_url, {
|
||||||
|
method: "PATCH",
|
||||||
|
headers: {
|
||||||
|
'Accept': 'application/json',
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
tag: this.track.tag,
|
||||||
|
label: this.track.label,
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
fetch(request)
|
||||||
|
.then(response => {
|
||||||
|
return response.json();
|
||||||
|
})
|
||||||
|
.then(response => {
|
||||||
|
this.editing = false;
|
||||||
|
this.track.label = response.label;
|
||||||
|
this.track.tag = response.tag;
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
console.error(error)
|
||||||
|
});
|
||||||
|
},
|
||||||
|
},
|
||||||
|
template: `
|
||||||
|
<div class="media-track" @click="edit()">
|
||||||
|
<template v-if="editing">
|
||||||
|
<form method="patch" :action="patch_url">
|
||||||
|
<select v-model="track.tag" :disabled="saving">
|
||||||
|
<option v-for="tag in tags">
|
||||||
|
{{ tag }}
|
||||||
|
</option>
|
||||||
|
</select>
|
||||||
|
<input type="text" v-model="track.label" :disabled="saving">
|
||||||
|
<input type="submit" value="Save" @click.prevent.stop.once="save()" :disabled="saving">
|
||||||
|
</form>
|
||||||
|
</template>
|
||||||
|
<template v-else>{{ track.label }}</template>
|
||||||
|
</div>
|
||||||
|
`
|
||||||
|
};
|
||||||
|
|
||||||
|
const MediaLibrary = {
|
||||||
|
props: ['tracks', 'tags'],
|
||||||
|
components: {
|
||||||
|
'media-track': MediaTrack,
|
||||||
|
},
|
||||||
|
template: `
|
||||||
|
<div class="media-library">
|
||||||
|
<media-track v-for="track in tracks"
|
||||||
|
:key="track.uuid"
|
||||||
|
:track="track"
|
||||||
|
:tags="tags"
|
||||||
|
></media-track>
|
||||||
|
</div>
|
||||||
|
`,
|
||||||
|
};
|
||||||
|
|
||||||
|
var app = new Vue({
|
||||||
|
el: '#app',
|
||||||
|
data: appData,
|
||||||
|
components: {
|
||||||
|
'media-library': MediaLibrary,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
var request = new Request('/api/tags');
|
||||||
|
fetch(request)
|
||||||
|
.then(response => {
|
||||||
|
return response.json();
|
||||||
|
})
|
||||||
|
.then(response => {
|
||||||
|
appData.tags = response.map(tag => tag.tag);
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
console.error(error);
|
||||||
|
});
|
||||||
|
|
||||||
|
var request = new Request('/api/tracks');
|
||||||
|
fetch(request)
|
||||||
|
.then(response => {
|
||||||
|
return response.json();
|
||||||
|
})
|
||||||
|
.then(response => {
|
||||||
|
appData.tracks = response;
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
console.error(error);
|
||||||
|
});
|
||||||
|
|
||||||
|
})();
|
||||||
|
// vim:ts=2 sw=2 et:
|
11965
static/vue.debug.js
Normal file
11965
static/vue.debug.js
Normal file
File diff suppressed because it is too large
Load Diff
@ -4,6 +4,9 @@
|
|||||||
{{> log}}
|
{{> log}}
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
|
<p><a href="/library">Manage library</a></p>
|
||||||
|
|
||||||
|
<script src="/jukebox.js"></script>
|
||||||
<script>
|
<script>
|
||||||
var jukebox = {
|
var jukebox = {
|
||||||
port: {{ config.port }}
|
port: {{ config.port }}
|
||||||
|
@ -17,7 +17,6 @@
|
|||||||
<footer>
|
<footer>
|
||||||
<p>made with love by annika</p>
|
<p>made with love by annika</p>
|
||||||
</footer>
|
</footer>
|
||||||
<script src="/jukebox.js"></script>
|
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
</html>
|
</html>
|
||||||
|
@ -1 +1,8 @@
|
|||||||
hello
|
<div id="app">
|
||||||
|
<media-library
|
||||||
|
v-bind:tracks="tracks"
|
||||||
|
v-bind:tags="tags"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<script src="/vue.debug.js"></script>
|
||||||
|
<script src="/library.js"></script>
|
||||||
|
Loading…
Reference in New Issue
Block a user