Files
StarlightRadio/src/store.js
T

188 lines
6.1 KiB
JavaScript

"use strict";
const { getDb } = require("./db");
const { naturalCompare } = require("./util");
/* ==================== songs ==================== */
function getSongByFile(file) {
return getDb().prepare("SELECT * FROM songs WHERE file = ?").get(file);
}
function getSongById(id) {
return getDb().prepare("SELECT * FROM songs WHERE id = ?").get(id);
}
function getAllSongs() {
const rows = getDb().prepare("SELECT * FROM songs").all();
return rows.sort((a, b) => naturalCompare(a.file, b.file));
}
function upsertSong(s) {
const d = getDb();
const ex = getSongByFile(s.file);
if (ex) {
d.prepare(
`UPDATE songs SET name=?, artist=?, cover=?, duration=?, file_size=? WHERE file=?`
).run(
s.name != null ? s.name : ex.name,
s.artist != null ? s.artist : ex.artist,
s.cover != null ? s.cover : ex.cover,
s.duration != null ? s.duration : ex.duration,
s.file_size != null ? s.file_size : ex.file_size,
s.file
);
return getSongByFile(s.file);
}
d.prepare(
`INSERT INTO songs (file, name, artist, cover, duration, file_size, heat, cache_status, created_at)
VALUES (?, ?, ?, ?, ?, ?, 0, 'pending', ?)`
).run(
s.file,
s.name || "",
s.artist || "",
s.cover || "",
s.duration != null ? s.duration : null,
s.file_size || 0,
Date.now()
);
return getSongByFile(s.file);
}
function updateSong(file, fields) {
const keys = Object.keys(fields);
if (!keys.length) return;
const set = keys.map((k) => `${k}=?`).join(", ");
getDb()
.prepare(`UPDATE songs SET ${set} WHERE file=?`)
.run(...keys.map((k) => fields[k]), file);
}
function deleteSongByFile(file) {
return getDb().prepare("DELETE FROM songs WHERE file = ?").run(file);
}
function setCacheStatus(file, status) {
getDb().prepare("UPDATE songs SET cache_status=? WHERE file=?").run(status, file);
}
function incrementHeat(file) {
getDb().prepare("UPDATE songs SET heat = heat + 1 WHERE file=?").run(file);
return getSongByFile(file)?.heat || 0;
}
/* ==================== streams ==================== */
function createStream(id, name) {
getDb()
.prepare(
"INSERT INTO streams (id, name, enabled, current_pos, media_seq, created_at) VALUES (?, ?, 1, 0, 0, ?)"
)
.run(id, name || id, Date.now());
return getStream(id);
}
function listStreams() {
return getDb().prepare("SELECT * FROM streams ORDER BY created_at ASC").all();
}
function getStream(id) {
return getDb().prepare("SELECT * FROM streams WHERE id = ?").get(id);
}
function deleteStream(id) {
return getDb().prepare("DELETE FROM streams WHERE id = ?").run(id);
}
function setStreamState(id, fields) {
const keys = Object.keys(fields);
if (!keys.length) return;
const set = keys.map((k) => `${k}=?`).join(", ");
getDb()
.prepare(`UPDATE streams SET ${set} WHERE id=?`)
.run(...keys.map((k) => fields[k]), id);
}
function setStreamEnabled(id, enabled) {
setStreamState(id, { enabled: enabled ? 1 : 0 });
}
/* ==================== stream_playlist ==================== */
function getPlaylistRows(streamId) {
return getDb()
.prepare(
`SELECT sp.id AS entry_id, sp.position, s.*
FROM stream_playlist sp JOIN songs s ON s.id = sp.song_id
WHERE sp.stream_id = ? ORDER BY sp.position ASC`
)
.all(streamId);
}
function playlistCount(streamId) {
return getDb()
.prepare("SELECT COUNT(*) c FROM stream_playlist WHERE stream_id=?")
.get(streamId).c;
}
function addToPlaylist(streamId, songId) {
const d = getDb();
const max =
d.prepare("SELECT COALESCE(MAX(position),-1) m FROM stream_playlist WHERE stream_id=?")
.get(streamId).m + 1;
d.prepare(
"INSERT INTO stream_playlist (stream_id, song_id, position) VALUES (?, ?, ?)"
).run(streamId, songId, max);
}
function removePlaylistEntry(streamId, entryId) {
const d = getDb();
const row = d.prepare("SELECT position FROM stream_playlist WHERE id=? AND stream_id=?").get(entryId, streamId);
if (!row) return false;
d.prepare("DELETE FROM stream_playlist WHERE id=?").run(entryId);
d.prepare("UPDATE stream_playlist SET position = position - 1 WHERE stream_id=? AND position > ?")
.run(streamId, row.position);
return true;
}
function setPlaylistOrder(streamId, entryIds) {
const d = getDb();
const stmt = d.prepare("UPDATE stream_playlist SET position=? WHERE id=? AND stream_id=?");
const tx = d.transaction((ids) => {
ids.forEach((id, i) => stmt.run(i, id, streamId));
});
tx(entryIds);
}
function clearPlaylist(streamId) {
getDb().prepare("DELETE FROM stream_playlist WHERE stream_id=?").run(streamId);
}
/* ==================== sessions ==================== */
function createSession(token, expiresAt) {
getDb()
.prepare("INSERT INTO sessions (token, created_at, expires_at) VALUES (?, ?, ?)")
.run(token, Date.now(), expiresAt);
}
function getSession(token) {
const s = getDb().prepare("SELECT * FROM sessions WHERE token=?").get(token);
if (!s) return null;
if (s.expires_at < Date.now()) {
deleteSession(token);
return null;
}
return s;
}
function deleteSession(token) {
getDb().prepare("DELETE FROM sessions WHERE token=?").run(token);
}
function purgeExpiredSessions() {
return getDb().prepare("DELETE FROM sessions WHERE expires_at < ?").run(Date.now()).changes;
}
/* ==================== settings ==================== */
function getSetting(key, def = null) {
const r = getDb().prepare("SELECT value FROM settings WHERE key=?").get(key);
if (!r) return def;
try {
return JSON.parse(r.value);
} catch (_) {
return r.value;
}
}
function setSetting(key, value) {
getDb()
.prepare(
"INSERT INTO settings (key, value) VALUES (?, ?) ON CONFLICT(key) DO UPDATE SET value=excluded.value"
)
.run(key, JSON.stringify(value));
}
module.exports = {
getSongByFile, getSongById, getAllSongs, upsertSong, updateSong,
deleteSongByFile, setCacheStatus, incrementHeat,
createStream, listStreams, getStream, deleteStream, setStreamState, setStreamEnabled,
getPlaylistRows, playlistCount, addToPlaylist, removePlaylistEntry,
setPlaylistOrder, clearPlaylist,
createSession, getSession, deleteSession, purgeExpiredSessions,
getSetting, setSetting,
};