From 707a099e6379f79b9d4af31d4abaf7c1e77e543f Mon Sep 17 00:00:00 2001 From: Gabriel Augendre Date: Wed, 4 Jan 2023 15:55:59 +0100 Subject: [PATCH] Handle unlimited files --- data/www/script.js | 61 ++++++++++++++++++++++++++++++++++++---------- src/main.cpp | 41 ++++++++++++++++++++++--------- 2 files changed, 78 insertions(+), 24 deletions(-) diff --git a/data/www/script.js b/data/www/script.js index 8d7f5c0..0bca97c 100644 --- a/data/www/script.js +++ b/data/www/script.js @@ -1,6 +1,7 @@ const GLOBAL_TIMEOUT = 10000; let connectionOk = true; let statusTimeout = null; +let selectedFile = ""; function play() { console.log("Play..."); @@ -23,9 +24,9 @@ function volume(modifier) { .catch(handleError); } -function loadStatus() { +async function loadStatus() { console.log("Status..."); - fetch("/status", { signal: AbortSignal.timeout(GLOBAL_TIMEOUT) }) + return fetch("/status", { signal: AbortSignal.timeout(GLOBAL_TIMEOUT) }) .then(response => response.json()) .then(handleStatus) .catch(handleError); @@ -49,18 +50,18 @@ function handleStatus(data) { location.reload(); } - let dom = ""; - data.files.available.forEach((element, index) => { - let className = "w3-blue"; - if (index === data.files.selectedIndex) { - className = "w3-green"; + if (data.files.selected != selectedFile) { + selectedFile = data.files.selected; + document.querySelectorAll(".w3-green").forEach(element => { + element.classList.remove("w3-green"); + element.classList.add("w3-blue"); + }); + const previouslySelected = document.querySelector(`[data-name='${selectedFile}']`); + if (previouslySelected) { + previouslySelected.classList.remove("w3-blue"); + previouslySelected.classList.add("w3-green"); } - dom += ``; - }); - if (data.files.moreNotShown) { - dom += ``; } - document.getElementById("available-files").innerHTML = dom; document.getElementById("volume-current").innerText = data.volume.current; document.getElementById("volume-increase").disabled = !data.volume.canIncrease; @@ -68,6 +69,40 @@ function handleStatus(data) { statusTimeout = setTimeout(loadStatus, GLOBAL_TIMEOUT); } +async function listFiles(cursor=0) { + console.log("List files..."); + return fetch(`/list-files?cursor=${cursor}`, { signal: AbortSignal.timeout(GLOBAL_TIMEOUT) }) + .then(response => response.json()) + .then(data => { + let dom = ""; + data.files.forEach(element => { + if (!element) { + // Filter out null + return; + } + let className = "w3-blue"; + if (element === selectedFile) { + className = "w3-green"; + } + dom += ``; + }); + if (data.files.moreNotShown) { + dom += ``; + } + const availableFilesNode = document.getElementById("available-files"); + if (cursor == 0) { + availableFilesNode.innerHTML = dom; + } + else { + availableFilesNode.innerHTML += dom; + } + if (data.next) { + return listFiles(data.next); + } + }) + .catch(handleError); +} + function handleError(error) { console.error(error); clearTimeout(statusTimeout); @@ -78,6 +113,6 @@ function handleError(error) { } (() => { - loadStatus(); + loadStatus().then(() => listFiles()); statusTimeout = setTimeout(loadStatus, GLOBAL_TIMEOUT); })(); diff --git a/src/main.cpp b/src/main.cpp index 5f0a95f..1d39f86 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -83,6 +83,7 @@ void play() void onStop(AsyncWebServerRequest *request) { + Serial.println("Stop playing"); audio.stopSong(); request->send(200); } @@ -98,40 +99,57 @@ void onStatus(AsyncWebServerRequest *request) Serial.println("Status"); AsyncResponseStream *response = request->beginResponseStream("application/json"); - DynamicJsonDocument root(4096); - JsonObject files = root.createNestedObject("files"); - files["selectedIndex"] = -1; - files["moreNotShown"] = false; + StaticJsonDocument<96> root; + root["files"]["selected"] = selectedFile.c_str(); JsonObject volume = root.createNestedObject("volume"); volume["current"] = currentVolume; volume["canDecrease"] = currentVolume > 0; volume["canIncrease"] = currentVolume < 21; - JsonArray availableFiles = files.createNestedArray("available"); + serializeJson(root, *response); + request->send(response); +} + +void onListFiles(AsyncWebServerRequest *request) +{ + Serial.print("List files cursor="); + int cursor = 0; + if (request->hasParam("cursor")) { + String s_cursor = request->getParam("cursor")->value(); + cursor = s_cursor.toInt(); + } + Serial.println(cursor); + + AsyncResponseStream *response = request->beginResponseStream("application/json"); + + StaticJsonDocument<512> root; + root["next"] = -1; + JsonArray files = root.createNestedArray("files"); File music = SD.open("/"); File file = music.openNextFile(); - unsigned int index = 0; + int index = 0; while (file) { String fileName = file.name(); if (fileIsValid(fileName)) { - availableFiles.add(fileName); - if (fileName == selectedFile) { - files["selectedIndex"] = index; - } index++; + if (index >= cursor) + files.add(fileName); } file.close(); if (root.overflowed()) { - files["moreNotShown"] = true; + root["next"] = index; break; } file = music.openNextFile(); } + if (root["next"] == -1) { + root.remove("next"); + } serializeJson(root, *response); request->send(response); @@ -316,6 +334,7 @@ void setup() server.on("/play", HTTP_GET, onPlay); server.on("/stop", HTTP_GET, onStop); server.on("/status", HTTP_GET, onStatus); + server.on("/list-files", HTTP_GET, onListFiles); server.on("/select-file", HTTP_POST, onSelectFile); server.on("/change-volume", HTTP_POST, onChangeVolume); server.on("/upload", HTTP_POST, onUpload, onUploadFile);