fixed some crashes, html..

This commit is contained in:
Stefan Ostermann 2025-07-28 14:15:11 +02:00
parent 571a1c5c39
commit a1d486dd2d
3 changed files with 181 additions and 78 deletions

View File

@ -119,7 +119,10 @@ void DirectoryNode::buildDirectoryTree(const char *currentPath)
} }
else if (String(entry.name()).endsWith(".mp3")||String(entry.name()).endsWith(".MP3")) else if (String(entry.name()).endsWith(".mp3")||String(entry.name()).endsWith(".MP3"))
{ {
mp3Files.push_back(entry.name()); String fullPath = String(currentPath);
if (!fullPath.endsWith("/")) fullPath += "/";
fullPath += entry.name();
mp3Files.push_back(fullPath);
ids.push_back(getNextId()); ids.push_back(getNextId());
} }
entry.close(); entry.close();
@ -182,75 +185,97 @@ void DirectoryNode::advanceToFirstMP3InThisNode()
DirectoryNode *DirectoryNode::advanceToMP3(const uint16_t id) DirectoryNode *DirectoryNode::advanceToMP3(const uint16_t id)
{ {
// First check MP3 files in this directory
for (size_t i = 0; i < ids.size(); i++) for (size_t i = 0; i < ids.size(); i++)
{
if (id == ids[i])
{ {
if (id == ids[i]) // Found the current MP3 file
{ currentPlaying = &mp3Files[i];
// Found the current MP3 file currentPlayingId = id;
currentPlaying = &mp3Files[i]; return this;
currentPlayingId = id;
return this;
}
} }
}
// Recursively search subdirectories
for (auto subdir : subdirectories) for (auto subdir : subdirectories)
{ {
// Check if the ID matches a subdirectory ID
if (subdir->getId() == id) if (subdir->getId() == id)
{ {
subdir->advanceToFirstMP3InThisNode(); subdir->advanceToFirstMP3InThisNode();
return subdir; return subdir;
} }
// Have each subdirectory advance its song // Recursively search in subdirectory
for (size_t i = 0; i < subdir->ids.size(); i++) DirectoryNode* result = subdir->advanceToMP3(id);
if (result != nullptr && result->getCurrentPlaying() != nullptr)
{ {
if (id == subdir->ids[i]) return result;
{
// Found the current MP3 file
subdir->currentPlaying = &subdir->mp3Files[i];
subdir->currentPlayingId = id;
return subdir;
}
} }
} }
// If we get here, there were no MP3 files or subdirectories left to check // If we get here, no song with this ID was found
currentPlaying = nullptr; Serial.println("advanceToMP3: No song found for ID: " + String(id));
Serial.println("no more nodes found"); return nullptr;
return this;
} }
DirectoryNode *DirectoryNode::advanceToMP3(const String *currentGlobal) DirectoryNode *DirectoryNode::advanceToMP3(const String *currentGlobal)
{ {
if (currentGlobal == nullptr) {
Serial.println("advanceToMP3: currentGlobal is null");
return nullptr;
}
// Check if the input is an absolute path (starts with '/') or just a filename
bool isAbsolutePath = currentGlobal->startsWith("/");
// First, check MP3 files in this directory
for (size_t i = 0; i < mp3Files.size(); i++)
{
if (isAbsolutePath) {
// For absolute paths, do exact match
if (*currentGlobal == mp3Files[i])
{
setCurrentPlaying(&mp3Files[i]);
return this;
}
} else {
// For filenames, extract filename from full path and compare
String filename = mp3Files[i];
int lastSlash = filename.lastIndexOf('/');
if (lastSlash != -1) {
filename = filename.substring(lastSlash + 1);
}
if (*currentGlobal == filename)
{
setCurrentPlaying(&mp3Files[i]);
return this;
}
}
}
// Recursively search subdirectories
for (auto subdir : subdirectories) for (auto subdir : subdirectories)
{ {
if (subdir->getName() == *currentGlobal) // Check if the string matches a directory name (only for non-absolute paths)
if (!isAbsolutePath && subdir->getName() == *currentGlobal)
{ {
subdir->advanceToFirstMP3InThisNode(); subdir->advanceToFirstMP3InThisNode();
return subdir; return subdir;
} }
// Have each subdirectory advance its song // Recursively search in subdirectory
for (size_t i = 0; i < subdir->mp3Files.size(); i++) DirectoryNode* result = subdir->advanceToMP3(currentGlobal);
if (result != nullptr && result->getCurrentPlaying() != nullptr)
{ {
if (*currentGlobal == subdir->mp3Files[i]) return result;
{
// Found the current MP3 file
if (i < subdir->mp3Files.size() - 1)
{
subdir->setCurrentPlaying(&subdir->mp3Files[i]);
return subdir;
}
}
} }
} }
// If we get here, there were no MP3 files or subdirectories left to check // If we get here, no matching song was found
currentPlaying = nullptr; Serial.println("advanceToMP3: No song found for: " + *currentGlobal);
Serial.println("no more nodes found"); return nullptr;
return this;
} }
/** /**
@ -466,16 +491,7 @@ String DirectoryNode::getCurrentPlayingFilePath() const
{ {
if (currentPlaying != nullptr) if (currentPlaying != nullptr)
{ {
String filePath = name; return *currentPlaying;
if (!filePath.startsWith("/")) {
filePath = "/"+filePath;
}
if (!filePath.endsWith("/"))
{
filePath += "/";
}
filePath += *currentPlaying;
return filePath;
} }
return ""; return "";
} }

View File

@ -302,26 +302,43 @@ void playSongById(uint16_t id, uint32_t continueSeconds = 0)
{ {
currentNode = rootNode.advanceToMP3(id); currentNode = rootNode.advanceToMP3(id);
if (currentNode==nullptr) { if (currentNode == nullptr) {
Serial.println("No node found for ID: " + String(id));
return;
}
// Check if the current playing song is valid
if (currentNode->getCurrentPlaying() == nullptr) {
currentNode = nullptr;
Serial.println("No song found for ID: " + String(id));
return; return;
} }
String mp3File = currentNode->getCurrentPlayingFilePath(); String mp3File = currentNode->getCurrentPlayingFilePath();
Serial.print("playing by id: "); if (mp3File.length() == 0) {
Serial.print(id);Serial.print(" ");Serial.println(continueSeconds);
Serial.println(mp3File.c_str());
deactivateRFID();
activateSD();
if (currentNode != nullptr && currentNode->getCurrentPlaying()==nullptr) {
currentNode = nullptr; currentNode = nullptr;
Serial.println("no node by id found, exiting playSongById"); Serial.println("Empty file path for ID: " + String(id));
return; return;
} }
playFile(mp3File.c_str()); Serial.print("Playing by ID: ");
if (continueSeconds!=0) { Serial.print(id);
Serial.print(" ");
Serial.println(continueSeconds);
Serial.println(mp3File.c_str());
deactivateRFID();
activateSD();
if (!playFile(mp3File.c_str())) {
Serial.println("Failed to play file: " + mp3File);
currentNode = nullptr;
activateRFID();
deactivateSD();
return;
}
if (continueSeconds != 0) {
audio.setAudioPlayPosition(continueSeconds); audio.setAudioPlayPosition(continueSeconds);
} }
activateRFID(); activateRFID();
@ -330,12 +347,45 @@ void playSongById(uint16_t id, uint32_t continueSeconds = 0)
void playSongByName(String song) void playSongByName(String song)
{ {
if (song.length() == 0) {
Serial.println("Empty song name provided");
return;
}
currentNode = rootNode.advanceToMP3(&song); currentNode = rootNode.advanceToMP3(&song);
if (currentNode == nullptr) {
Serial.println("No node found for song: " + song);
return;
}
// Check if the current playing song is valid
if (currentNode->getCurrentPlaying() == nullptr) {
currentNode = nullptr;
Serial.println("No song found for name: " + song);
return;
}
String mp3File = currentNode->getCurrentPlayingFilePath(); String mp3File = currentNode->getCurrentPlayingFilePath();
Serial.println(mp3File.c_str()); if (mp3File.length() == 0) {
currentNode = nullptr;
Serial.println("Empty file path for song: " + song);
return;
}
Serial.println("Playing song: " + mp3File);
deactivateRFID(); deactivateRFID();
activateSD(); activateSD();
playFile(mp3File.c_str());
if (!playFile(mp3File.c_str())) {
Serial.println("Failed to play file: " + mp3File);
currentNode = nullptr;
activateRFID();
deactivateSD();
return;
}
activateRFID();
deactivateSD();
} }
void playSongByPath(String path) void playSongByPath(String path)
@ -345,13 +395,23 @@ void playSongByPath(String path)
void playSongByRFID(String id) void playSongByRFID(String id)
{ {
auto songit = rfid_map.find(id); if (id.length() == 0) {
if (songit==rfid_map.end()) { Serial.println("Empty RFID ID provided");
Serial.println("song for uid not found.");
return; return;
} }
Serial.println("searching for ");
Serial.println(songit->second); auto songit = rfid_map.find(id);
if (songit == rfid_map.end()) {
Serial.println("Song for UID not found: " + id);
return;
}
if (songit->second.length() == 0) {
Serial.println("Empty song name mapped to RFID: " + id);
return;
}
Serial.println("Searching for song: " + songit->second);
playSongByName(songit->second); playSongByName(songit->second);
} }
@ -517,7 +577,7 @@ void editMapping(AsyncWebServerRequest *request) {
} }
} }
std::map<String, String> readDataFromFile(const char *filename) { std::map<String, String> readDataFromFile(String filename) {
File file = SD.open(filename); File file = SD.open(filename);
@ -530,6 +590,7 @@ std::map<String, String> readDataFromFile(const char *filename) {
// Extract key and value // Extract key and value
String key = line.substring(0, separatorIndex).c_str(); String key = line.substring(0, separatorIndex).c_str();
String value = line.substring(separatorIndex + 1).c_str(); String value = line.substring(separatorIndex + 1).c_str();
Serial.println("found rfid mapping for "+value);
// Add key-value pair to the map // Add key-value pair to the map
rfid_map[key] = value; rfid_map[key] = value;
} }
@ -549,6 +610,29 @@ String processor(const String &var)
{ {
return rootNode.getDirectoryStructureHTML(); return rootNode.getDirectoryStructureHTML();
} }
if (var == "MAPPING") {
auto htmlEscape = [](const String& s) -> String {
String out;
for (size_t i = 0; i < s.length(); ++i) {
char c = s[i];
if (c == '&') out += "&amp;";
else if (c == '<') out += "";
else if (c == '>') out += "";
else if (c == '"') out += "";
else if (c == '\'') out += "";
else out += c;
}
return out;
};
String html = "<table style='width:100%;border-collapse:collapse;'><tr><th style='border:1px solid #ccc;padding:4px;'>RFID</th><th style='border:1px solid #ccc;padding:4px;'>Song</th></tr>";
for (const auto& pair : rfid_map) {
html += "<tr><td style='border:1px solid #ccc;padding:4px;'>" + htmlEscape(pair.first) + "</td><td style='border:1px solid #ccc;padding:4px;'>" + htmlEscape(pair.second) + "</td></tr>";
}
html += "</table>";
return html;
}
return String(); // Return empty string instead of creating new String return String(); // Return empty string instead of creating new String
} }
@ -747,7 +831,7 @@ void setup()
rootNode.buildDirectoryTree("/"); rootNode.buildDirectoryTree("/");
rootNode.printDirectoryTree(); rootNode.printDirectoryTree();
readDataFromFile(mapping_file.c_str()); readDataFromFile(getSysDir(mapping_file));

View File

@ -8,9 +8,7 @@
<body> <body>
<h1>🎵 HannaBox 🎵</h1> <h1>🎵 HannaBox 🎵</h1>
<span id="state"></span><br/><br/> <span id="state"></span><br/><br/>
<span id="voltage"></span><br/>
<span id="uid"></span><br/>
<span id="heap"></span><br/>
<div> <div>
<button class="prev-button" onclick="simpleGetCall('previous');"></button> <button class="prev-button" onclick="simpleGetCall('previous');"></button>
@ -42,11 +40,13 @@
<button id="toggleFileManagerButton" class="action-btn" onclick="toggleFileManager()">Toggle File Manager</button> <button id="toggleFileManagerButton" class="action-btn" onclick="toggleFileManager()">Toggle Manager</button>
<div id="fileManager" style="display:none; margin-top:20px;"> <div id="fileManager" style="display:none; margin-top:20px;">
<h2>File Manager</h2> <h2>Manager</h2>
<span id="voltage"></span><br/>
<span id="uid"></span><br/>
<span id="heap"></span><br/>
<h3>Upload File</h3> <h3>Upload File</h3>
<form id="uploadForm" class="form" method="POST" action="/upload" enctype="multipart/form-data"> <form id="uploadForm" class="form" method="POST" action="/upload" enctype="multipart/form-data">
<input type="file" name="data" id="uploadFile" accept=".mp3,.wav,.flac,.m4a,.ogg"/> <input type="file" name="data" id="uploadFile" accept=".mp3,.wav,.flac,.m4a,.ogg"/>
@ -56,11 +56,13 @@
<div class="progress-bar"> <div class="progress-bar">
<div class="progress-fill" id="progressFill"></div> <div class="progress-fill" id="progressFill"></div>
</div> </div>
<span id="progressText">0%</span> <span id="progressText">0</span>
</div> </div>
</form> </form>
<h3>Edit RFID Mapping</h3> <h3>Edit RFID Mapping</h3>
Hint: Use a folder or filename, not the absolute file path!
<div class="form">%MAPPING%</div>
<form id="editMappingForm" class="form"> <form id="editMappingForm" class="form">
<label for="rfid">RFID:</label> <label for="rfid">RFID:</label>
<input type="text" id="rfid" name="rfid" required><br> <input type="text" id="rfid" name="rfid" required><br>
@ -77,8 +79,9 @@
<input type="text" id="moveTo" placeholder="/newname.mp3"/> <input type="text" id="moveTo" placeholder="/newname.mp3"/>
<button type="button" class="action-btn" onclick="moveFile()">Move/Rename</button> <button type="button" class="action-btn" onclick="moveFile()">Move/Rename</button>
</form> </form>
<form class="form">
<h3>Delete File</h3> <h3>Delete File</h3>
<form class="form">
<label for="deleteFileName">Filename:</label> <label for="deleteFileName">Filename:</label>
<input type="text" id="deleteFileName" placeholder="/song.mp3"/> <input type="text" id="deleteFileName" placeholder="/song.mp3"/>
<button type="button" class="action-btn" onclick="deleteFileOnServer()">Delete</button> <button type="button" class="action-btn" onclick="deleteFileOnServer()">Delete</button>