feat: Add names to playlists
All checks were successful
Build RPi Pico firmware image / Build-Firmware (push) Successful in 4m38s
Check code formatting / Check-C-Format (push) Successful in 7s
Check code formatting / Check-Python-Flake8 (push) Successful in 9s
Check code formatting / Check-Bash-Shellcheck (push) Successful in 4s
Run unit tests on host / Run-Unit-Tests (push) Successful in 7s
Run pytests / Check-Pytest (push) Successful in 10s
All checks were successful
Build RPi Pico firmware image / Build-Firmware (push) Successful in 4m38s
Check code formatting / Check-C-Format (push) Successful in 7s
Check code formatting / Check-Python-Flake8 (push) Successful in 9s
Check code formatting / Check-Bash-Shellcheck (push) Successful in 4s
Run unit tests on host / Run-Unit-Tests (push) Successful in 7s
Run pytests / Check-Pytest (push) Successful in 10s
Fixes #63. Signed-off-by: Matthias Blankertz <matthias@blankertz.org>
This commit is contained in:
@@ -213,6 +213,10 @@
|
||||
<option value="audioplay">Audioplay (no shuffle, start at previous track)</option>
|
||||
</select>
|
||||
</div>
|
||||
<div>
|
||||
<label>Playlist name</label>
|
||||
<input type="text" placeholder="Playlist name" id="playlist-edit-name" />
|
||||
</div>
|
||||
<div class="flex-horizontal">
|
||||
<div style="flex-grow: 1">
|
||||
<label>Tracks</label>
|
||||
@@ -493,7 +497,7 @@
|
||||
document.getElementById('playlist-exist-button-delete')
|
||||
.addEventListener('click', (e) => {
|
||||
if (lastSelected === null) return;
|
||||
const tagid = lastSelected.innerText;
|
||||
const tagid = lastSelected.getAttribute('data-tag');
|
||||
if(confirm(`Really delete playlist ${tagid}?`)) {
|
||||
fetch(`/api/v1/playlist/${tagid}`, {
|
||||
method: 'DELETE'})
|
||||
@@ -504,7 +508,7 @@
|
||||
.addEventListener('click', selectLastTag);
|
||||
document.getElementById('playlist-exist-button-edit').addEventListener("click", (e) => {
|
||||
if (lastSelected !== null)
|
||||
showScreen("playlist_edit", {load: lastSelected.innerText});
|
||||
showScreen("playlist_edit", {load: lastSelected.getAttribute('data-tag')});
|
||||
});
|
||||
document.getElementById('playlist-exist-list').addEventListener("click", (e) => {
|
||||
const node = e.target.closest("li");
|
||||
@@ -559,8 +563,9 @@
|
||||
container.innerHTML = "";
|
||||
for (const playlist of playlists) {
|
||||
const li = document.createElement("li");
|
||||
li.innerHTML = playlist;
|
||||
li.innerHTML = `${playlist.name} (${playlist.tag})`;
|
||||
li.className = "node"
|
||||
li.setAttribute('data-tag', playlist.tag);
|
||||
container.appendChild(li)
|
||||
}
|
||||
}
|
||||
@@ -585,7 +590,7 @@
|
||||
document.getElementById('playlist-exist-list')
|
||||
.querySelectorAll("li")
|
||||
.forEach(n => {
|
||||
if (n.innerText == tagtext) {
|
||||
if (n.getAttribute('data-tag') == tagtext) {
|
||||
n.classList.add("selected");
|
||||
lastSelected = n;
|
||||
} else {
|
||||
@@ -687,6 +692,8 @@
|
||||
} else if (playlist.persist === "track" && playlist.shuffle === "no") {
|
||||
playlisttype.value = "audioplay";
|
||||
}
|
||||
const playlistname = document.getElementById('playlist-edit-name');
|
||||
playlistname.value = playlist.name;
|
||||
}
|
||||
|
||||
async function save() {
|
||||
@@ -709,6 +716,8 @@
|
||||
playlistData.shuffle = "no";
|
||||
break;
|
||||
}
|
||||
const playlistname = document.getElementById('playlist-edit-name');
|
||||
playlistData.name = playlistname.value;
|
||||
const container = document.getElementById('playlist-edit-list');
|
||||
playlistData.paths = [...container.querySelectorAll("li")].map((node) => node.innerText);
|
||||
const saveRes = await fetch(`/api/v1/playlist/${playlistId}`,
|
||||
|
||||
@@ -33,12 +33,13 @@ class BTreeDB(IPlaylistDB):
|
||||
PERSIST_OFFSET = b'offset'
|
||||
|
||||
class Playlist(IPlaylist):
|
||||
def __init__(self, parent: "BTreeDB", tag: bytes, pos: int, persist, shuffle):
|
||||
def __init__(self, parent: "BTreeDB", tag: bytes, pos: int, persist, shuffle, name):
|
||||
self.parent = parent
|
||||
self.tag = tag
|
||||
self.pos = pos
|
||||
self.persist = persist
|
||||
self.shuffle = shuffle
|
||||
self.name = name
|
||||
self.length = self.parent._getPlaylistLength(self.tag)
|
||||
self._shuffle()
|
||||
|
||||
@@ -168,6 +169,10 @@ class BTreeDB(IPlaylistDB):
|
||||
return (b''.join([tag, b'/playlist/']),
|
||||
b''.join([tag, b'/playlist0']))
|
||||
|
||||
@staticmethod
|
||||
def _keyPlaylistName(tag):
|
||||
return b''.join([tag, b'/playlistname'])
|
||||
|
||||
def _flush(self):
|
||||
"""
|
||||
Flush the database and call the flush_func if it was provided.
|
||||
@@ -222,12 +227,13 @@ class BTreeDB(IPlaylistDB):
|
||||
raise RuntimeError("Malformed playlist key")
|
||||
return int(elements[2])+1
|
||||
|
||||
def _savePlaylist(self, tag, entries, persist, shuffle, flush=True):
|
||||
def _savePlaylist(self, tag, entries, persist, shuffle, name, flush=True):
|
||||
self._deletePlaylist(tag, False)
|
||||
for idx, entry in enumerate(entries):
|
||||
self.db[self._keyPlaylistEntry(tag, idx)] = entry
|
||||
self.db[self._keyPlaylistPersist(tag)] = persist
|
||||
self.db[self._keyPlaylistShuffle(tag)] = shuffle
|
||||
self.db[self._keyPlaylistName(tag)] = name.encode()
|
||||
if flush:
|
||||
self._flush()
|
||||
|
||||
@@ -240,7 +246,7 @@ class BTreeDB(IPlaylistDB):
|
||||
pass
|
||||
for k in (self._keyPlaylistPos(tag), self._keyPlaylistPosOffset(tag),
|
||||
self._keyPlaylistPersist(tag), self._keyPlaylistShuffle(tag),
|
||||
self._keyPlaylistShuffleSeed(tag)):
|
||||
self._keyPlaylistShuffleSeed(tag), self._keyPlaylistName(tag)):
|
||||
try:
|
||||
del self.db[k]
|
||||
except KeyError:
|
||||
@@ -248,15 +254,18 @@ class BTreeDB(IPlaylistDB):
|
||||
if flush:
|
||||
self._flush()
|
||||
|
||||
def getPlaylistTags(self):
|
||||
def getPlaylists(self):
|
||||
"""
|
||||
Get a keys-only dict of all defined playlists. Playlists currently do not have names, but are identified by
|
||||
their tag.
|
||||
Get a list of all defined playlists with their tag and names.
|
||||
"""
|
||||
playlist_tags = set()
|
||||
for item in self.db:
|
||||
playlist_tags.add(item.split(b'/')[0])
|
||||
return playlist_tags
|
||||
playlists = []
|
||||
for tag in playlist_tags:
|
||||
name = self.db.get(self._keyPlaylistName(tag), b'').decode()
|
||||
playlists.append({'tag': tag, 'name': name})
|
||||
return playlists
|
||||
|
||||
def getPlaylistForTag(self, tag: bytes):
|
||||
"""
|
||||
@@ -275,18 +284,19 @@ class BTreeDB(IPlaylistDB):
|
||||
return None
|
||||
if self._keyPlaylistEntry(tag, pos) not in self.db:
|
||||
pos = 0
|
||||
name = self.db.get(self._keyPlaylistName(tag), b'').decode()
|
||||
shuffle = self.db.get(self._keyPlaylistShuffle(tag), self.SHUFFLE_NO)
|
||||
return self.Playlist(self, tag, pos, persist, shuffle)
|
||||
return self.Playlist(self, tag, pos, persist, shuffle, name)
|
||||
|
||||
def createPlaylistForTag(self, tag: bytes, entries: typing.Iterable[bytes], persist=PERSIST_TRACK,
|
||||
shuffle=SHUFFLE_NO):
|
||||
shuffle=SHUFFLE_NO, name: str = ''):
|
||||
"""
|
||||
Create and save a playlist for 'tag' and return the Playlist object. If a playlist already existed for 'tag' it
|
||||
is overwritten.
|
||||
"""
|
||||
assert persist in (self.PERSIST_NO, self.PERSIST_TRACK, self.PERSIST_OFFSET)
|
||||
assert shuffle in (self.SHUFFLE_NO, self.SHUFFLE_YES)
|
||||
self._savePlaylist(tag, entries, persist, shuffle)
|
||||
self._savePlaylist(tag, entries, persist, shuffle, name)
|
||||
return self.getPlaylistForTag(tag)
|
||||
|
||||
def deletePlaylistForTag(self, tag: bytes):
|
||||
@@ -370,6 +380,14 @@ class BTreeDB(IPlaylistDB):
|
||||
_ = int(val)
|
||||
except ValueError:
|
||||
fail(f' Bad playlistposoffset value for {last_tag}: {val!r}')
|
||||
elif fields[1] == b'playlistname':
|
||||
val = self.db[k]
|
||||
try:
|
||||
name = val.decode()
|
||||
if dump:
|
||||
print(f'\tName: {name}')
|
||||
except UnicodeError:
|
||||
fail(f' Bad playlistname for {last_tag}: Not valid unicode')
|
||||
else:
|
||||
fail(f'Unknown key {k!r}')
|
||||
return result
|
||||
|
||||
@@ -112,7 +112,7 @@ async def static(request, path):
|
||||
|
||||
@webapp.route('/api/v1/playlists', methods=['GET'])
|
||||
async def playlists_get(request):
|
||||
return sorted(playlist_db.getPlaylistTags())
|
||||
return playlist_db.getPlaylists()
|
||||
|
||||
|
||||
def is_hex(s):
|
||||
@@ -133,10 +133,11 @@ async def playlist_get(request, tag):
|
||||
return None, 404
|
||||
|
||||
return {
|
||||
'shuffle': playlist.__dict__.get('shuffle'),
|
||||
'persist': playlist.__dict__.get('persist'),
|
||||
'shuffle': playlist.shuffle,
|
||||
'persist': playlist.persist,
|
||||
'paths': [(p[len(fsroot):] if p.startswith(fsroot) else p).decode()
|
||||
for p in playlist.getPaths()],
|
||||
'name': playlist.name
|
||||
}
|
||||
|
||||
|
||||
@@ -156,7 +157,8 @@ async def playlist_put(request, tag):
|
||||
playlist_db.createPlaylistForTag(tag.encode(),
|
||||
(fsroot + path.encode() for path in playlist.get('paths', [])),
|
||||
playlist.get('persist', 'track').encode(),
|
||||
playlist.get('shuffle', 'no').encode())
|
||||
playlist.get('shuffle', 'no').encode(),
|
||||
playlist.get('name', ''))
|
||||
return '', 204
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user