[ai] dir streaming, still problems

This commit is contained in:
Stefan Ostermann 2025-10-05 23:29:22 +02:00
parent dc735c044f
commit 083dfd6e2a
4 changed files with 45 additions and 10 deletions

View File

@ -578,6 +578,44 @@ String DirectoryNode::getDirectoryStructureHTML() const {
return html; return html;
} }
void DirectoryNode::streamDirectoryHTML(Print &out) const {
if (name == "/") {
out.println(F("<ul>"));
delay(0); // yield to WiFi/other tasks
}
if (name != "/") {
out.print(F("<li data-id=\""));
out.print(id);
out.print(F("\"><b>"));
out.print(name);
out.println(F("</b></li>"));
delay(0); // yield periodically while streaming
}
for (size_t i = 0; i < mp3Files.size(); i++) {
out.print(F("<li data-id=\""));
out.print(ids[i]);
out.print(F("\">"));
out.print(mp3Files[i]);
out.println(F("</li>"));
if ((i & 0x0F) == 0) { // yield every ~16 items
delay(0);
}
}
for (DirectoryNode* child : subdirectories) {
delay(0); // yield before descending
child->streamDirectoryHTML(out);
delay(0); // and after returning
}
if (name == "/") {
out.println(F("</ul>"));
delay(0);
}
}
// NEW: Calculate exact required size first // NEW: Calculate exact required size first
size_t DirectoryNode::calculateHTMLSize() const { size_t DirectoryNode::calculateHTMLSize() const {
size_t size = 0; size_t size = 0;

View File

@ -1,5 +1,6 @@
#ifndef DIRECTORYNODE_H_ #ifndef DIRECTORYNODE_H_
#define DIRECTORYNODE_H_ #define DIRECTORYNODE_H_
class Print;
#include <SD.h> #include <SD.h>
#include <vector> #include <vector>
@ -55,6 +56,7 @@ public:
DirectoryNode* advanceToMP3(const uint16_t id); DirectoryNode* advanceToMP3(const uint16_t id);
void advanceToFirstMP3InThisNode(); void advanceToFirstMP3InThisNode();
String getDirectoryStructureHTML() const; String getDirectoryStructureHTML() const;
void streamDirectoryHTML(Print &out) const;
size_t calculateHTMLSize() const; size_t calculateHTMLSize() const;
void appendIndentation(String& html, int level) const; void appendIndentation(String& html, int level) const;
DirectoryNode* findFirstDirectoryWithMP3s(); DirectoryNode* findFirstDirectoryWithMP3s();

View File

@ -1217,15 +1217,14 @@ server.on("/", HTTP_GET, [](AsyncWebServerRequest *request)
{ {
webreq_enter(); webreq_enter();
request->onDisconnect([](){ webreq_exit(); }); request->onDisconnect([](){ webreq_exit(); });
// Stream the response to avoid lifetime issues with temporary Strings and to prevent wrong Content-Length // Stream the response directly from the directory tree to avoid large temporary Strings
AsyncResponseStream* stream = request->beginResponseStream("text/html; charset=UTF-8"); AsyncResponseStream* stream = request->beginResponseStream("text/html; charset=UTF-8");
stream->addHeader("Cache-Control", "no-store"); stream->addHeader("Cache-Control", "no-store");
stream->addHeader("Connection", "close"); stream->addHeader("Connection", "close");
// Build HTML under lock, then print into stream // Generate HTML directly into the stream under lock
dir_lock_acquire(); dir_lock_acquire();
String html = rootNode.getDirectoryStructureHTML(); rootNode.streamDirectoryHTML(*stream);
dir_lock_release(); dir_lock_release();
stream->print(html);
request->send(stream); request->send(stream);
}); });
@ -1508,12 +1507,8 @@ void loop()
if (webreq_cnt > 0 && webrequest_blockings > MAX_WEBREQUEST_BLOCKINGS) { if (webreq_cnt > 0 && webrequest_blockings > MAX_WEBREQUEST_BLOCKINGS) {
Serial.println("excessive webrequest blocking - suppress reset"); Serial.println("excessive webrequest blocking - suppress reset");
// Avoid resetting server mid-response to prevent mixing headers/body or truncation // Avoid resetting server mid-response to prevent mixing headers/body or truncation
server.reset();
init_webserver();
webreq_cnt = 0; webreq_cnt = 0;
webrequest_blockings = 0; webrequest_blockings = 0;
server.begin();
} }
if (audio.isRunning()) if (audio.isRunning())

View File

@ -58,9 +58,9 @@ setInterval(updateProgress, 500); // Update progress every second
if ((this.__url || '').indexOf('/upload') !== -1) { if ((this.__url || '').indexOf('/upload') !== -1) {
timeoutMs = 600000; // 10 minutes for uploads timeoutMs = 600000; // 10 minutes for uploads
} else if (this.__method === 'GET') { } else if (this.__method === 'GET') {
timeoutMs = 3500;
} else {
timeoutMs = 6000; timeoutMs = 6000;
} else {
timeoutMs = 8000;
} }
if (isIdempotentGET && inflightKeys.has(key)) { if (isIdempotentGET && inflightKeys.has(key)) {