feat: Allow deleting files and directories
All checks were successful
Build RPi Pico firmware image / Build-Firmware (push) Successful in 4m40s
Check code formatting / Check-C-Format (push) Successful in 7s
Check code formatting / Check-Python-Flake8 (push) Successful in 10s
Check code formatting / Check-Bash-Shellcheck (push) Successful in 4s
Run unit tests on host / Run-Unit-Tests (push) Successful in 8s
Run pytests / Check-Pytest (push) Successful in 10s
All checks were successful
Build RPi Pico firmware image / Build-Firmware (push) Successful in 4m40s
Check code formatting / Check-C-Format (push) Successful in 7s
Check code formatting / Check-Python-Flake8 (push) Successful in 10s
Check code formatting / Check-Bash-Shellcheck (push) Successful in 4s
Run unit tests on host / Run-Unit-Tests (push) Successful in 8s
Run pytests / Check-Pytest (push) Successful in 10s
Signed-off-by: Matthias Blankertz <matthias@blankertz.org>
This commit is contained in:
@@ -235,51 +235,22 @@
|
||||
<div class="scroll-container">
|
||||
<div class="tree" id="playlist-filebrowser-tree">
|
||||
<ul><li>Loading...</li></ul>
|
||||
<!--<ul>
|
||||
<li>
|
||||
<span class="caret"></span>
|
||||
<span class="node">Fruits</span>
|
||||
<ul>
|
||||
<li>
|
||||
<span class="caret"></span>
|
||||
<span class="node">Apple</span>
|
||||
</li>
|
||||
<li>
|
||||
<span class="caret"></span>
|
||||
<span class="node">Citrus</span>
|
||||
<ul>
|
||||
<li>
|
||||
<span class="caret"></span>
|
||||
<span class="node">Orange</span>
|
||||
</li>
|
||||
<li>
|
||||
<span class="caret"></span>
|
||||
<span class="node">Lemon</span>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
<span class="caret"></span>
|
||||
<span class="node">Strawberry</span>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul> -->
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex-horizontal">
|
||||
<button id="playlist-filebrowser-cancel">Cancel</button>
|
||||
<button id="playlist-filebrowser-delete" style="background-color: orangered">Delete selected</button>
|
||||
<button id="playlist-filebrowser-addtrack">Add track(s)</button>
|
||||
</div>
|
||||
<label>Upload files</label>
|
||||
<hr>
|
||||
<div class="flex-horizontal">
|
||||
<input type="file" id="playlist-filebrowser-upload-files" multiple accept="audio/mpeg" />
|
||||
<progress id="playlist-filebrowser-upload-progress" max="100" value="0" style:"flex-grow: 1">0%</progress>
|
||||
<button id="playlist-filebrowser-upload">Upload</button>
|
||||
<progress id="playlist-filebrowser-upload-progress" max="100" value="0">0%</progress>
|
||||
</div>
|
||||
<div class="flex-horizontal">
|
||||
<input type="text" id="playlist-filebrowser-mkdir-name" />
|
||||
<button id="playlist-filebrowser-mkdir">Create directory</button>
|
||||
<input type="text" id="playlist-filebrowser-mkdir-name" placeholder="Directory Name" />
|
||||
<button id="playlist-filebrowser-mkdir" style="width: 20%">Create directory</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -752,6 +723,9 @@
|
||||
document.getElementById('playlist-filebrowser-mkdir').addEventListener("click", (e) => {
|
||||
createDirectory();
|
||||
});
|
||||
document.getElementById('playlist-filebrowser-delete').addEventListener("click", (e) => {
|
||||
deleteItems();
|
||||
});
|
||||
tree.init();
|
||||
}
|
||||
|
||||
@@ -907,6 +881,27 @@
|
||||
// Reload file list from device
|
||||
onShow();
|
||||
}
|
||||
|
||||
async function deleteItems() {
|
||||
const tree = document.getElementById("playlist-filebrowser-tree");
|
||||
const selectedNodes = [...tree.querySelectorAll(".selected")];
|
||||
if (selectedNodes.length === 0) {
|
||||
alert("Please select something to delete");
|
||||
return;
|
||||
}
|
||||
const items = selectedNodes.map(n => n.getAttribute('data-path'));
|
||||
items.sort();
|
||||
items.reverse();
|
||||
for (const item of items) {
|
||||
const saveRes = await fetch(`/api/v1/audiofiles?location=${item}`,
|
||||
{method: 'DELETE'});
|
||||
if (!saveRes.ok) {
|
||||
alert(`Failed to delete item ${item}: ${await saveRes.text()}`);
|
||||
}
|
||||
}
|
||||
// Reload file list from device
|
||||
onShow();
|
||||
}
|
||||
|
||||
let tree = (() => {
|
||||
let tree = null;
|
||||
|
||||
@@ -161,7 +161,11 @@ class PlayerApp:
|
||||
self._onIdle()
|
||||
if filename is not None:
|
||||
print(f'Playing {filename!r}')
|
||||
self.mp3file = open(filename, 'rb')
|
||||
try:
|
||||
self.mp3file = open(filename, 'rb')
|
||||
except OSError as ex:
|
||||
print(f"Could not play file {filename}: {ex}")
|
||||
return
|
||||
self.player.play(self.mp3file, offset)
|
||||
self.paused = False
|
||||
self._onActive()
|
||||
|
||||
@@ -223,3 +223,26 @@ async def audiofile_upload(request):
|
||||
return '', 204
|
||||
else:
|
||||
return 'size mismatch', 500
|
||||
|
||||
|
||||
def recursive_delete(path):
|
||||
stat = os.stat(path)
|
||||
if stat[0] == 0x8000:
|
||||
os.remove(path)
|
||||
elif stat[0] == 0x4000:
|
||||
for entry in os.ilistdir(path):
|
||||
entry_path = path + '/' + entry[0]
|
||||
recursive_delete(entry_path)
|
||||
os.rmdir(path)
|
||||
|
||||
|
||||
@webapp.route('/api/v1/audiofiles', methods=['DELETE'])
|
||||
async def audiofile_delete(request):
|
||||
if 'location' not in request.args:
|
||||
return 'missing location', 400
|
||||
location = request.args['location']
|
||||
if '..' in location or len(location) == 0:
|
||||
return 'bad location', 400
|
||||
path = fsroot + '/' + request.args['location']
|
||||
recursive_delete(path)
|
||||
return '', 204
|
||||
|
||||
Reference in New Issue
Block a user