[ai] refactoring, fixed crash
This commit is contained in:
parent
c32eabf464
commit
7f120ae62d
225
src/main.cpp
225
src/main.cpp
|
|
@ -693,12 +693,12 @@ String getState()
|
||||||
|
|
||||||
jsonState["playing"] = audio.isRunning();
|
jsonState["playing"] = audio.isRunning();
|
||||||
|
|
||||||
if (currentNode != nullptr)
|
if (currentNode != nullptr && currentNode->getCurrentPlaying() != nullptr)
|
||||||
jsonState["title"] = *currentNode->getCurrentPlaying();
|
jsonState["title"] = *currentNode->getCurrentPlaying();
|
||||||
else
|
else
|
||||||
jsonState["title"] = "Stopped";
|
jsonState["title"] = "Stopped";
|
||||||
|
|
||||||
if (currentNode != nullptr)
|
if (currentNode != nullptr && currentNode->getCurrentPlaying() != nullptr)
|
||||||
jsonState["filepath"] = currentNode->getCurrentPlayingFilePath();
|
jsonState["filepath"] = currentNode->getCurrentPlayingFilePath();
|
||||||
else
|
else
|
||||||
jsonState["filepath"] = "";
|
jsonState["filepath"] = "";
|
||||||
|
|
@ -1105,191 +1105,98 @@ void readRFID()
|
||||||
lastInteraction = millis();
|
lastInteraction = millis();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void serveStaticFile(AsyncWebServerRequest *request,
|
||||||
|
const String &plainPath,
|
||||||
|
const String &gzPath,
|
||||||
|
const char *contentType,
|
||||||
|
const char *cacheControl,
|
||||||
|
const __FlashStringHelper *notFoundMsg,
|
||||||
|
bool allowGzip = true)
|
||||||
|
{
|
||||||
|
webreq_enter();
|
||||||
|
// Ensure SD is active and RFID is deactivated while serving files.
|
||||||
|
deactivateRFID();
|
||||||
|
activateSD();
|
||||||
|
|
||||||
|
// Prefer gz if present
|
||||||
|
bool useGz = allowGzip && SD.exists(gzPath);
|
||||||
|
const String &sendPath = useGz ? gzPath : plainPath;
|
||||||
|
|
||||||
|
if (SD.exists(sendPath))
|
||||||
|
{
|
||||||
|
#ifdef DEBUG
|
||||||
|
Serial.printf("Serving %s heap=%u webreq_cnt=%u\n", sendPath.c_str(), (unsigned)xPortGetFreeHeapSize(), (unsigned)webreq_cnt);
|
||||||
|
#endif
|
||||||
|
// Chunked streaming with short SD lock per read
|
||||||
|
struct FileCtx { File f; };
|
||||||
|
FileCtx *ctx = new FileCtx();
|
||||||
|
ctx->f = SD.open(sendPath);
|
||||||
|
if (!ctx->f) { delete ctx; request->send(500, txt_plain, F("Open failed")); webreq_exit(); return; }
|
||||||
|
auto resp = request->beginChunkedResponse(contentType,
|
||||||
|
[ctx](uint8_t *buffer, size_t maxLen, size_t index) -> size_t {
|
||||||
|
size_t toRead = maxLen;
|
||||||
|
if (toRead > 512) toRead = 512;
|
||||||
|
sd_lock_acquire();
|
||||||
|
size_t n = ctx->f.read(buffer, toRead);
|
||||||
|
sd_lock_release();
|
||||||
|
if (n == 0) { ctx->f.close(); }
|
||||||
|
return n;
|
||||||
|
});
|
||||||
|
resp->addHeader(hdr_cache_control_key, cacheControl);
|
||||||
|
resp->addHeader(hdr_connection_key, hdr_connection_val);
|
||||||
|
if (useGz) {
|
||||||
|
resp->addHeader(F("Content-Encoding"), F("gzip"));
|
||||||
|
}
|
||||||
|
// Ensure FileCtx cleanup even on aborted connections
|
||||||
|
request->onDisconnect([ctx](){
|
||||||
|
sd_lock_acquire();
|
||||||
|
if (ctx->f) ctx->f.close();
|
||||||
|
sd_lock_release();
|
||||||
|
delete ctx;
|
||||||
|
webreq_exit();
|
||||||
|
});
|
||||||
|
request->send(resp);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Fallback: 404 for missing asset
|
||||||
|
request->send(404, txt_plain, notFoundMsg);
|
||||||
|
webreq_exit();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void init_webserver() {
|
void init_webserver() {
|
||||||
server.on("/", HTTP_GET, [](AsyncWebServerRequest *request)
|
server.on("/", HTTP_GET, [](AsyncWebServerRequest *request)
|
||||||
{
|
{
|
||||||
webreq_enter();
|
|
||||||
/* onDisconnect set later with cleanup */
|
|
||||||
deactivateRFID();
|
|
||||||
activateSD();
|
|
||||||
static String htmlPath = "";
|
static String htmlPath = "";
|
||||||
static String htmlPathGz = "";
|
static String htmlPathGz = "";
|
||||||
if (htmlPath.isEmpty()) {
|
if (htmlPath.isEmpty()) {
|
||||||
htmlPath = getSysDir(index_file);
|
htmlPath = getSysDir(index_file);
|
||||||
htmlPathGz = htmlPath + F(".gz");
|
htmlPathGz = htmlPath + F(".gz");
|
||||||
}
|
}
|
||||||
|
serveStaticFile(request, htmlPath, htmlPathGz, txt_html_charset, hdr_cache_control_val, F("ERROR: /system/index.html(.gz) not found!"), true);
|
||||||
// Prefer gz if present
|
|
||||||
bool useGz = SD.exists(htmlPathGz);
|
|
||||||
const String &sendPath = useGz ? htmlPathGz : htmlPath;
|
|
||||||
|
|
||||||
if (SD.exists(sendPath))
|
|
||||||
{
|
|
||||||
#ifdef DEBUG
|
|
||||||
Serial.printf("Serving %s heap=%u webreq_cnt=%u\n", sendPath.c_str(), (unsigned)xPortGetFreeHeapSize(), (unsigned)webreq_cnt);
|
|
||||||
#endif
|
|
||||||
// Chunked streaming with short SD lock per read
|
|
||||||
struct FileCtx { File f; };
|
|
||||||
FileCtx* ctx = new FileCtx();
|
|
||||||
ctx->f = SD.open(sendPath);
|
|
||||||
if (!ctx->f) { delete ctx; request->send(500, txt_plain, F("Open failed")); return; }
|
|
||||||
auto response = request->beginChunkedResponse(String(txt_html_charset),
|
|
||||||
[ctx](uint8_t *buffer, size_t maxLen, size_t index) -> size_t {
|
|
||||||
size_t toRead = maxLen;
|
|
||||||
if (toRead > 512) toRead = 512;
|
|
||||||
sd_lock_acquire();
|
|
||||||
size_t n = ctx->f.read(buffer, toRead);
|
|
||||||
sd_lock_release();
|
|
||||||
if (n == 0) { ctx->f.close(); }
|
|
||||||
return n;
|
|
||||||
});
|
|
||||||
response->addHeader(hdr_cache_control_key, hdr_cache_control_val);
|
|
||||||
response->addHeader(hdr_connection_key, hdr_connection_val);
|
|
||||||
if (useGz) {
|
|
||||||
response->addHeader(F("Content-Encoding"), F("gzip"));
|
|
||||||
}
|
|
||||||
// Ensure FileCtx cleanup even on aborted connections
|
|
||||||
request->onDisconnect([ctx](){
|
|
||||||
sd_lock_acquire();
|
|
||||||
if (ctx->f) ctx->f.close();
|
|
||||||
sd_lock_release();
|
|
||||||
delete ctx;
|
|
||||||
webreq_exit();
|
|
||||||
});
|
|
||||||
request->send(response);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// Fallback: serve minimal error if file not found
|
|
||||||
request->send(404, txt_plain, F("ERROR: /system/index.html(.gz) not found!"));
|
|
||||||
}
|
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
server.on("/style.css", HTTP_GET, [](AsyncWebServerRequest *request)
|
server.on("/style.css", HTTP_GET, [](AsyncWebServerRequest *request)
|
||||||
{
|
{
|
||||||
webreq_enter();
|
|
||||||
/* onDisconnect set later with cleanup */
|
|
||||||
deactivateRFID();
|
|
||||||
activateSD();
|
|
||||||
// Ensure SD is active and RFID is deactivated while serving files.
|
|
||||||
static String cssPath = "";
|
static String cssPath = "";
|
||||||
static String cssPathGz = "";
|
static String cssPathGz = "";
|
||||||
if (cssPath.isEmpty()) {
|
if (cssPath.isEmpty()) {
|
||||||
cssPath = getSysDir(style_file);
|
cssPath = getSysDir(style_file);
|
||||||
cssPathGz = cssPath + F(".gz");
|
cssPathGz = cssPath + F(".gz");
|
||||||
}
|
}
|
||||||
|
serveStaticFile(request, cssPath, cssPathGz, "text/css", "public, max-age=300", F("ERROR: /system/style.css(.gz) not found!"), true);
|
||||||
// Prefer gz if present
|
|
||||||
bool useGz = SD.exists(cssPathGz);
|
|
||||||
const String &sendPath = useGz ? cssPathGz : cssPath;
|
|
||||||
|
|
||||||
if (SD.exists(sendPath))
|
|
||||||
{
|
|
||||||
#ifdef DEBUG
|
|
||||||
Serial.printf("Serving %s heap=%u webreq_cnt=%u\n", sendPath.c_str(), (unsigned)xPortGetFreeHeapSize(), (unsigned)webreq_cnt);
|
|
||||||
#endif
|
|
||||||
{
|
|
||||||
// Chunked streaming with short SD lock per read
|
|
||||||
struct FileCtx { File f; };
|
|
||||||
FileCtx* ctx = new FileCtx();
|
|
||||||
ctx->f = SD.open(sendPath);
|
|
||||||
if (!ctx->f) { delete ctx; request->send(500, txt_plain, F("Open failed")); return; }
|
|
||||||
auto resp = request->beginChunkedResponse(F("text/css"),
|
|
||||||
[ctx](uint8_t *buffer, size_t maxLen, size_t index) -> size_t {
|
|
||||||
size_t toRead = maxLen;
|
|
||||||
if (toRead > 512) toRead = 512;
|
|
||||||
sd_lock_acquire();
|
|
||||||
size_t n = ctx->f.read(buffer, toRead);
|
|
||||||
sd_lock_release();
|
|
||||||
if (n == 0) { ctx->f.close(); }
|
|
||||||
return n;
|
|
||||||
});
|
|
||||||
resp->addHeader(hdr_cache_control_key, F("public, max-age=300"));
|
|
||||||
resp->addHeader(hdr_connection_key, hdr_connection_val);
|
|
||||||
if (useGz) {
|
|
||||||
resp->addHeader(F("Content-Encoding"), F("gzip"));
|
|
||||||
}
|
|
||||||
// Ensure FileCtx cleanup even on aborted connections
|
|
||||||
request->onDisconnect([ctx](){
|
|
||||||
sd_lock_acquire();
|
|
||||||
if (ctx->f) ctx->f.close();
|
|
||||||
sd_lock_release();
|
|
||||||
delete ctx;
|
|
||||||
webreq_exit();
|
|
||||||
});
|
|
||||||
request->send(resp);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// Fallback: serve minimal CSS if file not found
|
|
||||||
request->send(404, txt_plain, F("ERROR: /system/style.css(.gz) not found!"));
|
|
||||||
}
|
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
server.on("/script.js", HTTP_GET, [](AsyncWebServerRequest *request)
|
server.on("/script.js", HTTP_GET, [](AsyncWebServerRequest *request)
|
||||||
{
|
{
|
||||||
webreq_enter();
|
|
||||||
/* onDisconnect set later with cleanup */
|
|
||||||
deactivateRFID();
|
|
||||||
activateSD();
|
|
||||||
|
|
||||||
static String jsPath = "";
|
static String jsPath = "";
|
||||||
static String jsPathGz = "";
|
static String jsPathGz = "";
|
||||||
if (jsPath.isEmpty()) {
|
if (jsPath.isEmpty()) {
|
||||||
jsPath = getSysDir(script_file);
|
jsPath = getSysDir(script_file);
|
||||||
jsPathGz = jsPath + F(".gz");
|
jsPathGz = jsPath + F(".gz");
|
||||||
}
|
}
|
||||||
|
serveStaticFile(request, jsPath, jsPathGz, "application/javascript", "public, max-age=300", F("ERROR: /system/script.js(.gz) not found!"), true);
|
||||||
// Prefer gz if present
|
|
||||||
bool useGz = SD.exists(jsPathGz);
|
|
||||||
const String &sendPath = useGz ? jsPathGz : jsPath;
|
|
||||||
|
|
||||||
if (SD.exists(sendPath))
|
|
||||||
{
|
|
||||||
#ifdef DEBUG
|
|
||||||
Serial.printf("Serving %s heap=%u webreq_cnt=%u\n", sendPath.c_str(), (unsigned)xPortGetFreeHeapSize(), (unsigned)webreq_cnt);
|
|
||||||
#endif
|
|
||||||
{
|
|
||||||
// Chunked streaming with short SD lock per read
|
|
||||||
struct FileCtx { File f; };
|
|
||||||
FileCtx* ctx = new FileCtx();
|
|
||||||
ctx->f = SD.open(sendPath);
|
|
||||||
if (!ctx->f) { delete ctx; request->send(500, txt_plain, F("Open failed")); return; }
|
|
||||||
auto resp = request->beginChunkedResponse(F("application/javascript"),
|
|
||||||
[ctx](uint8_t *buffer, size_t maxLen, size_t index) -> size_t {
|
|
||||||
size_t toRead = maxLen;
|
|
||||||
if (toRead > 512) toRead = 512;
|
|
||||||
sd_lock_acquire();
|
|
||||||
size_t n = ctx->f.read(buffer, toRead);
|
|
||||||
sd_lock_release();
|
|
||||||
if (n == 0) { ctx->f.close(); }
|
|
||||||
return n;
|
|
||||||
});
|
|
||||||
resp->addHeader(hdr_cache_control_key, F("public, max-age=300"));
|
|
||||||
resp->addHeader(hdr_connection_key, hdr_connection_val);
|
|
||||||
if (useGz) {
|
|
||||||
resp->addHeader(F("Content-Encoding"), F("gzip"));
|
|
||||||
}
|
|
||||||
// Ensure FileCtx cleanup even on aborted connections
|
|
||||||
request->onDisconnect([ctx](){
|
|
||||||
sd_lock_acquire();
|
|
||||||
if (ctx->f) ctx->f.close();
|
|
||||||
sd_lock_release();
|
|
||||||
delete ctx;
|
|
||||||
webreq_exit();
|
|
||||||
});
|
|
||||||
request->send(resp);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// Fallback: serve minimal JS if file not found
|
|
||||||
request->send(404, txt_plain, F("ERROR: /system/script.js(.gz) not found!"));
|
|
||||||
}
|
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// Dynamic endpoints to avoid template processing heap spikes
|
// Dynamic endpoints to avoid template processing heap spikes
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue