[ai] refactoring, fixed crash
This commit is contained in:
parent
c32eabf464
commit
7f120ae62d
223
src/main.cpp
223
src/main.cpp
|
|
@ -693,12 +693,12 @@ String getState()
|
|||
|
||||
jsonState["playing"] = audio.isRunning();
|
||||
|
||||
if (currentNode != nullptr)
|
||||
if (currentNode != nullptr && currentNode->getCurrentPlaying() != nullptr)
|
||||
jsonState["title"] = *currentNode->getCurrentPlaying();
|
||||
else
|
||||
jsonState["title"] = "Stopped";
|
||||
|
||||
if (currentNode != nullptr)
|
||||
if (currentNode != nullptr && currentNode->getCurrentPlaying() != nullptr)
|
||||
jsonState["filepath"] = currentNode->getCurrentPlayingFilePath();
|
||||
else
|
||||
jsonState["filepath"] = "";
|
||||
|
|
@ -1105,191 +1105,98 @@ void readRFID()
|
|||
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() {
|
||||
server.on("/", HTTP_GET, [](AsyncWebServerRequest *request)
|
||||
{
|
||||
webreq_enter();
|
||||
/* onDisconnect set later with cleanup */
|
||||
deactivateRFID();
|
||||
activateSD();
|
||||
static String htmlPath = "";
|
||||
static String htmlPathGz = "";
|
||||
if (htmlPath.isEmpty()) {
|
||||
htmlPath = getSysDir(index_file);
|
||||
htmlPathGz = htmlPath + F(".gz");
|
||||
}
|
||||
|
||||
// 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!"));
|
||||
}
|
||||
|
||||
serveStaticFile(request, htmlPath, htmlPathGz, txt_html_charset, hdr_cache_control_val, F("ERROR: /system/index.html(.gz) not found!"), true);
|
||||
});
|
||||
|
||||
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 cssPathGz = "";
|
||||
if (cssPath.isEmpty()) {
|
||||
cssPath = getSysDir(style_file);
|
||||
cssPathGz = cssPath + F(".gz");
|
||||
}
|
||||
|
||||
// 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!"));
|
||||
}
|
||||
|
||||
serveStaticFile(request, cssPath, cssPathGz, "text/css", "public, max-age=300", F("ERROR: /system/style.css(.gz) not found!"), true);
|
||||
});
|
||||
|
||||
server.on("/script.js", HTTP_GET, [](AsyncWebServerRequest *request)
|
||||
{
|
||||
webreq_enter();
|
||||
/* onDisconnect set later with cleanup */
|
||||
deactivateRFID();
|
||||
activateSD();
|
||||
|
||||
static String jsPath = "";
|
||||
static String jsPathGz = "";
|
||||
if (jsPath.isEmpty()) {
|
||||
jsPath = getSysDir(script_file);
|
||||
jsPathGz = jsPath + F(".gz");
|
||||
}
|
||||
|
||||
// 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!"));
|
||||
}
|
||||
|
||||
serveStaticFile(request, jsPath, jsPathGz, "application/javascript", "public, max-age=300", F("ERROR: /system/script.js(.gz) not found!"), true);
|
||||
});
|
||||
|
||||
// Dynamic endpoints to avoid template processing heap spikes
|
||||
|
|
|
|||
Loading…
Reference in New Issue