[ai] improved look&feel
This commit is contained in:
@@ -96,12 +96,12 @@ void DirectoryNode::buildDirectoryTree(const char *currentPath)
|
||||
subdirectories.clear();
|
||||
mp3Files.clear();
|
||||
ids.clear();
|
||||
|
||||
|
||||
// Reserve memory to reduce heap fragmentation (optimization 3)
|
||||
subdirectories.reserve(8); // Reserve space for 8 subdirectories
|
||||
mp3Files.reserve(16); // Reserve space for 16 MP3 files
|
||||
ids.reserve(16); // Reserve space for 16 IDs
|
||||
|
||||
subdirectories.reserve(8); // Reserve space for 8 subdirectories
|
||||
mp3Files.reserve(16); // Reserve space for 16 MP3 files
|
||||
ids.reserve(16); // Reserve space for 16 IDs
|
||||
|
||||
File rootDir = SD.open(currentPath);
|
||||
while (true)
|
||||
{
|
||||
@@ -117,10 +117,11 @@ void DirectoryNode::buildDirectoryTree(const char *currentPath)
|
||||
subdirectories.push_back(newNode);
|
||||
newNode->buildDirectoryTree((String(currentPath) + entry.name()).c_str());
|
||||
}
|
||||
else if (String(entry.name()).endsWith(".mp3")||String(entry.name()).endsWith(".MP3"))
|
||||
else if (String(entry.name()).endsWith(".mp3") || String(entry.name()).endsWith(".MP3"))
|
||||
{
|
||||
String fullPath = String(currentPath);
|
||||
if (!fullPath.endsWith("/")) fullPath += "/";
|
||||
if (!fullPath.endsWith("/"))
|
||||
fullPath += "/";
|
||||
fullPath += entry.name();
|
||||
mp3Files.push_back(fullPath);
|
||||
ids.push_back(getNextId());
|
||||
@@ -130,8 +131,6 @@ void DirectoryNode::buildDirectoryTree(const char *currentPath)
|
||||
rootDir.close();
|
||||
}
|
||||
|
||||
|
||||
|
||||
void DirectoryNode::printDirectoryTree(int level) const
|
||||
{
|
||||
for (int i = 0; i < level; i++)
|
||||
@@ -210,7 +209,7 @@ DirectoryNode *DirectoryNode::advanceToMP3(const uint16_t id)
|
||||
}
|
||||
|
||||
// Recursively search in subdirectory
|
||||
DirectoryNode* result = subdir->advanceToMP3(id);
|
||||
DirectoryNode *result = subdir->advanceToMP3(id);
|
||||
if (result != nullptr && result->getCurrentPlaying() != nullptr)
|
||||
{
|
||||
return result;
|
||||
@@ -222,10 +221,10 @@ DirectoryNode *DirectoryNode::advanceToMP3(const uint16_t id)
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
||||
DirectoryNode *DirectoryNode::advanceToMP3(const String *songName)
|
||||
{
|
||||
if (songName == nullptr) {
|
||||
if (songName == nullptr)
|
||||
{
|
||||
Serial.println("advanceToMP3: songName is null");
|
||||
return nullptr;
|
||||
}
|
||||
@@ -236,12 +235,15 @@ DirectoryNode *DirectoryNode::advanceToMP3(const String *songName)
|
||||
// First, search in the current directory's MP3 files
|
||||
for (size_t i = 0; i < mp3Files.size(); i++)
|
||||
{
|
||||
if (isAbsolutePath) {
|
||||
if (*songName == mp3Files[i]) {
|
||||
if (isAbsolutePath)
|
||||
{
|
||||
if (*songName == mp3Files[i])
|
||||
{
|
||||
setCurrentPlaying(&mp3Files[i]);
|
||||
return this;
|
||||
}
|
||||
} else if (mp3Files[i].endsWith(*songName))
|
||||
}
|
||||
else if (mp3Files[i].endsWith(*songName))
|
||||
{
|
||||
setCurrentPlaying(&mp3Files[i]);
|
||||
return this;
|
||||
@@ -259,14 +261,17 @@ DirectoryNode *DirectoryNode::advanceToMP3(const String *songName)
|
||||
|
||||
// Search all files within subdir:
|
||||
for (size_t i = 0; i < subdir->mp3Files.size(); i++)
|
||||
{
|
||||
|
||||
if (isAbsolutePath) {
|
||||
if (*songName == subdir->mp3Files[i]) {
|
||||
subdir->setCurrentPlaying(&subdir->mp3Files[i]);
|
||||
return subdir;
|
||||
{
|
||||
|
||||
if (isAbsolutePath)
|
||||
{
|
||||
if (*songName == subdir->mp3Files[i])
|
||||
{
|
||||
subdir->setCurrentPlaying(&subdir->mp3Files[i]);
|
||||
return subdir;
|
||||
}
|
||||
}
|
||||
} else if (subdir->mp3Files[i].endsWith(*songName))
|
||||
else if (subdir->mp3Files[i].endsWith(*songName))
|
||||
{
|
||||
subdir->setCurrentPlaying(&subdir->mp3Files[i]);
|
||||
return subdir;
|
||||
@@ -322,7 +327,7 @@ DirectoryNode *DirectoryNode::goToPreviousMP3(uint32_t thresholdSeconds)
|
||||
setCurrentPlaying(&mp3Files[currentIndex - 1]);
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
// If we're at the first song or song not found in current directory,
|
||||
// we need to find the previous song globally
|
||||
Serial.println("goToPreviousMP3: At first song or song not found, looking for previous globally");
|
||||
@@ -338,14 +343,14 @@ DirectoryNode *DirectoryNode::findPreviousMP3Globally(const String *currentGloba
|
||||
}
|
||||
|
||||
// Build a flat list of all MP3 files in order
|
||||
std::vector<std::pair<DirectoryNode*, int>> allMP3s;
|
||||
std::vector<std::pair<DirectoryNode *, int>> allMP3s;
|
||||
buildFlatMP3List(allMP3s);
|
||||
|
||||
// Find current song in the flat list
|
||||
int currentGlobalIndex = -1;
|
||||
for (size_t i = 0; i < allMP3s.size(); i++)
|
||||
{
|
||||
DirectoryNode* node = allMP3s[i].first;
|
||||
DirectoryNode *node = allMP3s[i].first;
|
||||
int fileIndex = allMP3s[i].second;
|
||||
if (node->mp3Files[fileIndex] == *currentGlobal)
|
||||
{
|
||||
@@ -357,12 +362,12 @@ DirectoryNode *DirectoryNode::findPreviousMP3Globally(const String *currentGloba
|
||||
// If current song found and not the first globally, move to previous
|
||||
if (currentGlobalIndex > 0)
|
||||
{
|
||||
DirectoryNode* prevNode = allMP3s[currentGlobalIndex - 1].first;
|
||||
DirectoryNode *prevNode = allMP3s[currentGlobalIndex - 1].first;
|
||||
int prevFileIndex = allMP3s[currentGlobalIndex - 1].second;
|
||||
|
||||
|
||||
Serial.print("findPreviousMP3Globally: Moving to previous song globally: ");
|
||||
Serial.println(prevNode->mp3Files[prevFileIndex]);
|
||||
|
||||
|
||||
prevNode->setCurrentPlaying(&prevNode->mp3Files[prevFileIndex]);
|
||||
return prevNode;
|
||||
}
|
||||
@@ -371,7 +376,7 @@ DirectoryNode *DirectoryNode::findPreviousMP3Globally(const String *currentGloba
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void DirectoryNode::buildFlatMP3List(std::vector<std::pair<DirectoryNode*, int>>& allMP3s)
|
||||
void DirectoryNode::buildFlatMP3List(std::vector<std::pair<DirectoryNode *, int>> &allMP3s)
|
||||
{
|
||||
// Add all MP3 files from this directory
|
||||
for (size_t i = 0; i < mp3Files.size(); i++)
|
||||
@@ -380,7 +385,7 @@ void DirectoryNode::buildFlatMP3List(std::vector<std::pair<DirectoryNode*, int>>
|
||||
}
|
||||
|
||||
// Recursively add MP3 files from subdirectories
|
||||
for (DirectoryNode* subdir : subdirectories)
|
||||
for (DirectoryNode *subdir : subdirectories)
|
||||
{
|
||||
subdir->buildFlatMP3List(allMP3s);
|
||||
}
|
||||
@@ -449,41 +454,79 @@ DirectoryNode *DirectoryNode::advanceToNextMP3(const String *currentGlobal)
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
String DirectoryNode::getDirectoryStructureHTML() const
|
||||
{
|
||||
String DirectoryNode::getDirectoryStructureHTML() const {
|
||||
// Calculate required size first (prevents reallocations)
|
||||
size_t htmlSize = calculateHTMLSize();
|
||||
String html;
|
||||
html.reserve(512);
|
||||
|
||||
if (name == "/")
|
||||
{
|
||||
html += "<ul>\n";
|
||||
html.reserve(htmlSize); // Precise allocation - no wasted RAM
|
||||
|
||||
// Helper lambda to append without temporary Strings
|
||||
auto append = [&html](const __FlashStringHelper* fstr) {
|
||||
html += fstr;
|
||||
};
|
||||
auto appendId = [&html](uint32_t id) {
|
||||
html += id; // Direct numeric append (NO temporary String)
|
||||
};
|
||||
|
||||
if (name == "/") {
|
||||
append(F("<ul>\n"));
|
||||
}
|
||||
|
||||
if (name != "/")
|
||||
{
|
||||
html += "<li data-id=\"" + String(id) + "\"><b>" + name + "</b></li>\n";
|
||||
if (name != "/") {
|
||||
append(F("<li data-id=\""));
|
||||
appendId(id);
|
||||
append(F("\"><b>"));
|
||||
html += name; // Still uses String, but unavoidable for dynamic content
|
||||
append(F("</b></li>\n"));
|
||||
}
|
||||
|
||||
for (int i = 0; i < mp3Files.size(); i++)
|
||||
{
|
||||
html += "<li data-id=\"" + String(ids[i]) + "\">" + mp3Files[i] + "</li>\n";
|
||||
for (size_t i = 0; i < mp3Files.size(); i++) {
|
||||
append(F("<li data-id=\""));
|
||||
appendId(ids[i]);
|
||||
append(F("\">"));
|
||||
html += mp3Files[i]; // Dynamic file name
|
||||
append(F("</li>\n"));
|
||||
}
|
||||
|
||||
for (DirectoryNode *childNode : subdirectories)
|
||||
{
|
||||
html += childNode->getDirectoryStructureHTML();
|
||||
for (DirectoryNode* child : subdirectories) {
|
||||
html += child->getDirectoryStructureHTML();
|
||||
}
|
||||
if (name == "/")
|
||||
{
|
||||
html += "</ul>\n";
|
||||
|
||||
if (name == "/") {
|
||||
append(F("</ul>\n"));
|
||||
}
|
||||
|
||||
return html;
|
||||
}
|
||||
|
||||
// NEW: Calculate exact required size first
|
||||
size_t DirectoryNode::calculateHTMLSize() const {
|
||||
size_t size = 0;
|
||||
|
||||
// Opening/closing tags
|
||||
if (name == "/") size += 6; // <ul>\n
|
||||
|
||||
// Current directory entry
|
||||
if (name != "/") {
|
||||
size += 22 + name.length() + 10; // <li...><b></b></li>\n + ID digits (est)
|
||||
}
|
||||
|
||||
// MP3 files
|
||||
for (size_t i = 0; i < mp3Files.size(); i++) {
|
||||
size += 16 + mp3Files[i].length() + 10; // <li...></li>\n + ID digits
|
||||
}
|
||||
|
||||
// Subdirectories
|
||||
for (DirectoryNode* child : subdirectories) {
|
||||
size += child->calculateHTMLSize();
|
||||
}
|
||||
|
||||
// Closing tag
|
||||
if (name == "/") size += 7; // </ul>\n
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
void DirectoryNode::appendIndentation(String &html, int level) const
|
||||
{
|
||||
for (int i = 0; i < level; i++)
|
||||
|
||||
Reference in New Issue
Block a user