diff --git a/src/DirectoryNode.cpp b/src/DirectoryNode.cpp index d72d512..28fc1f5 100644 --- a/src/DirectoryNode.cpp +++ b/src/DirectoryNode.cpp @@ -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> allMP3s; + std::vector> 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>& allMP3s) +void DirectoryNode::buildFlatMP3List(std::vector> &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> } // 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 += "
    \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("
      \n")); } - if (name != "/") - { - html += "
    • " + name + "
    • \n"; + if (name != "/") { + append(F("
    • ")); + html += name; // Still uses String, but unavoidable for dynamic content + append(F("
    • \n")); } - for (int i = 0; i < mp3Files.size(); i++) - { - html += "
    • " + mp3Files[i] + "
    • \n"; + for (size_t i = 0; i < mp3Files.size(); i++) { + append(F("
    • ")); + html += mp3Files[i]; // Dynamic file name + append(F("
    • \n")); } - for (DirectoryNode *childNode : subdirectories) - { - html += childNode->getDirectoryStructureHTML(); + for (DirectoryNode* child : subdirectories) { + html += child->getDirectoryStructureHTML(); } - if (name == "/") - { - html += "
    \n"; + + if (name == "/") { + append(F("
\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; //
    \n + + // Current directory entry + if (name != "/") { + size += 22 + name.length() + 10; // \n + ID digits (est) + } + + // MP3 files + for (size_t i = 0; i < mp3Files.size(); i++) { + size += 16 + mp3Files[i].length() + 10; // \n + ID digits + } + + // Subdirectories + for (DirectoryNode* child : subdirectories) { + size += child->calculateHTMLSize(); + } + + // Closing tag + if (name == "/") size += 7; //
\n + + return size; +} + void DirectoryNode::appendIndentation(String &html, int level) const { for (int i = 0; i < level; i++) diff --git a/src/DirectoryNode.h b/src/DirectoryNode.h index 683a2a5..6ef825e 100644 --- a/src/DirectoryNode.h +++ b/src/DirectoryNode.h @@ -55,6 +55,7 @@ public: DirectoryNode* advanceToMP3(const uint16_t id); void advanceToFirstMP3InThisNode(); String getDirectoryStructureHTML() const; + size_t calculateHTMLSize() const; void appendIndentation(String& html, int level) const; DirectoryNode* findFirstDirectoryWithMP3s(); String getCurrentPlayingFilePath() const; diff --git a/web/hanna.png b/web/hanna.png new file mode 100644 index 0000000..c6ab97c Binary files /dev/null and b/web/hanna.png differ diff --git a/web/index.html b/web/index.html index 6a583cb..b6b12bc 100644 --- a/web/index.html +++ b/web/index.html @@ -1,93 +1,178 @@ - + + HannaBox - + -

🎵 HannaBox 🎵

-

- - -
- - -

-
-
- - - -
-
- - -
- - -

-

🎶 Playlist 🎶

- %DIRECTORY% -

- - - - - -