Add "Library" page with tag/label editor

This commit is contained in:
Annika Backstrom 2019-12-17 10:22:58 -05:00
parent 232929a47a
commit bc48e231ee
10 changed files with 12144 additions and 21 deletions

View File

@ -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");

View File

@ -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();
} }

View File

@ -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);
}); });
}); });

View File

@ -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%);
}

View File

@ -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
View 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

File diff suppressed because it is too large Load Diff

View File

@ -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 }}

View File

@ -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>

View File

@ -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>