folder play / next song fixes

This commit is contained in:
Stefan Ostermann 2025-12-13 23:18:48 +01:00
parent 69bc259a6c
commit 6ecb54e5ee
4 changed files with 83 additions and 58 deletions

View File

@ -572,6 +572,10 @@ DirectoryNode *DirectoryNode::findPreviousMP3Globally(const String &currentGloba
void DirectoryNode::buildFlatMP3List(std::vector<std::pair<DirectoryNode *, int>> &allMP3s)
{
#ifdef DEBUG
Serial.println("Building flat mp3 list for folder");
#endif
// Pre-reserve to reduce reallocations
allMP3s.reserve(allMP3s.size() + mp3Files.size());
// Add all MP3 files from this directory
@ -594,64 +598,56 @@ size_t DirectoryNode::getNumOfFiles() const
DirectoryNode *DirectoryNode::advanceToNextMP3(const String &currentGlobal)
{
bool useFirst = false;
Serial.println(currentGlobal.c_str());
// Build a flat list of all MP3 files in order to correctly find the next one across directories
std::vector<std::pair<DirectoryNode *, int>> allMP3s;
buildFlatMP3List(allMP3s);
if (allMP3s.empty())
{
Serial.println(F("advanceToNextMP3: No MP3s found in tree"));
currentPlaying = "";
return this;
}
int currentIndex = -1;
if (!currentGlobal.isEmpty())
{
for (size_t i = 0; i < mp3Files.size(); i++)
for (size_t i = 0; i < allMP3s.size(); i++)
{
buildFullPath(mp3Files[i], buffer, buffer_size);
if (currentGlobal == String(buffer))
DirectoryNode *node = allMP3s[i].first;
int fileIndex = allMP3s[i].second;
node->buildFullPath(node->mp3Files[fileIndex], buffer, buffer_size);
if (comparePathWithString(buffer, currentGlobal))
{
// Found the current playing MP3 file
if (i < mp3Files.size() - 1)
{
// Advance to the next MP3 file in the same directory
setCurrentPlaying(mp3Files[i + 1]);
return this;
}
useFirst = true;
// Reached the end of the MP3 files in the directory
currentIndex = (int)i;
break;
}
}
}
// We are either not playing, or we've exhausted all the MP3 files in this directory.
// Therefore, we need to recursively look in our subdirectories.
for (auto subdir : subdirectories)
// If current song found and not the last one, move to next
if (currentIndex >= 0 && currentIndex < (int)allMP3s.size() - 1)
{
if (useFirst && subdir->mp3Files.size() > 0)
{
subdir->setCurrentPlaying(subdir->mp3Files[0]);
return subdir;
}
// Have each subdirectory advance its song
for (size_t i = 0; i < subdir->mp3Files.size(); i++)
{
subdir->buildFullPath(subdir->mp3Files[i], buffer, buffer_size);
if (currentGlobal == String(buffer))
{
// Found the current playing MP3 file
if (i < subdir->mp3Files.size() - 1)
{
// Advance to the next MP3 file in the same directory
subdir->setCurrentPlaying(subdir->mp3Files[i + 1]);
return subdir;
}
else
{
useFirst = true;
}
// Reached the end of the MP3 files in the directory
break;
}
}
DirectoryNode *nextNode = allMP3s[currentIndex + 1].first;
int nextFileIndex = allMP3s[currentIndex + 1].second;
nextNode->setCurrentPlaying(nextNode->mp3Files[nextFileIndex]);
return nextNode;
}
// If not playing anything (start), play first
if (currentIndex == -1 && currentGlobal.isEmpty())
{
DirectoryNode *nextNode = allMP3s[0].first;
int nextFileIndex = allMP3s[0].second;
nextNode->setCurrentPlaying(nextNode->mp3Files[nextFileIndex]);
return nextNode;
}
// If we get here, there were no MP3 files or subdirectories left to check
// If we get here, either we are at the last song, or the current song was not found
currentPlaying = "";
Serial.println(F("no more nodes found"));
return this;

View File

@ -492,16 +492,18 @@ void playSongByRFID(const String &id)
else
{
// Find index of current playing file within the folder list
uint16_t targetId = currentNode->getCurrentPlayingId();
for (size_t i = 0; i < folderFlatList.size(); i++)
{
DirectoryNode *node = folderFlatList[i].first;
int fileIdx = folderFlatList[i].second;
if (node->getCurrentPlaying() == mp3File)
if (node == currentNode && node->getFileIdAt(fileIdx) == targetId)
{
folderFlatIndex = (int)i;
break;
}
}
Serial.print(F("RFID Folder Index: ")); Serial.println(folderFlatIndex);
}
// Compute root path for safety checks (path up to last '/')
@ -1124,10 +1126,26 @@ void previous()
void audio_eof_mp3(const char *info)
{
Serial.println(F("audio file ended."));
if (prepareSleepMode)
#ifdef DEBUG
if (folderModeActive)
Serial.println("folder mode active");
#endif
if (prepareSleepMode)
return;
// If folder-mode is active, advance only inside that folder.
if (folderModeActive)
{
if (folderRootNode == nullptr) {
#ifdef DEBUG
Serial.println(F("DEBUG: folderRootNode was null, fixing..."));
#endif
folderRootNode = currentNode;
}
}
if (folderModeActive && folderRootNode != nullptr)
{
// Ensure flat list is built
@ -1135,16 +1153,24 @@ void audio_eof_mp3(const char *info)
folderRootNode->buildFlatMP3List(folderFlatList);
// Try to find current index if not set
if (folderFlatIndex < 0)
if (folderFlatIndex < 0 && currentNode != nullptr)
{
String cur = currentNode ? currentNode->getCurrentPlaying() : String();
uint16_t currentId = currentNode->getCurrentPlayingId();
Serial.print(F("EOF: Searching for ID ")); Serial.println(currentId);
for (size_t i = 0; i < folderFlatList.size(); i++)
{
if (folderFlatList[i].first->getCurrentPlaying() == cur)
if (folderFlatList[i].first == currentNode &&
folderFlatList[i].first->getFileIdAt(folderFlatList[i].second) == currentId)
{
folderFlatIndex = (int)i;
#ifdef DEBUG
Serial.print(F("EOF: Found at ")); Serial.println(folderFlatIndex);
#endif
break;
}
}
if (folderFlatIndex < 0) {
Serial.println(F("EOF: ID not found in flat list"));
}
}
@ -1270,9 +1296,10 @@ static void serveStaticFile(AsyncWebServerRequest *request,
if (ctx->f) ctx->f.close();
sd_lock_release();
delete ctx;
webreq_exit();
});
request->send(resp);
webreq_exit();
}
else
{
@ -1535,8 +1562,7 @@ void setup()
AsyncWiFiManager wifiManager(&server, &dns);
// Memory optimizations for WiFiManager
wifiManager.setDebugOutput(false); // Disable debug strings
wifiManager.setDebugOutput(true);
// Reduce timeouts to free memory faster
wifiManager.setTimeout(180); // Reduced from 180
@ -1548,8 +1574,6 @@ void setup()
#endif
WRITE_PERI_REG(RTC_CNTL_BROWN_OUT_REG, 0); // disable brownout detector
// wifiManager.resetSettings();
if (wifiManager.autoConnect("HannaBox"))
{
Serial.printf("Heap before init_webserver: %u\n", (unsigned)xPortGetFreeHeapSize());
@ -1585,6 +1609,7 @@ void id_song_action(AsyncWebServerRequest *request)
webreq_enter();
request->onDisconnect([](){ webreq_exit(); });
int params = request->params();
folderModeActive = true;
for (int i = 0; i < params; i++)
{
const AsyncWebParameter *p = request->getParam(i);
@ -1593,6 +1618,10 @@ void id_song_action(AsyncWebServerRequest *request)
playSongById(atoi(p->value().c_str()));
}
}
if (currentNode != nullptr)
{
folderRootNode = currentNode;
}
lastInteraction = millis();
request->send_P(200, txt_plain, PSTR("ok"));
}

View File

@ -139,7 +139,7 @@ static inline void sd_lock_acquire()
}
static inline void sd_lock_release()
{
{
__sync_lock_release(&sd_lock_flag);
}
@ -212,7 +212,7 @@ std::map<String, MappingEntry> rfid_map;
// Folder-play helper: when a mapping requests "folder only" playback we keep
// track of the folder root node so EOF handling can advance only inside that folder.
bool folderModeActive = false;
bool folderModeActive = true;
bool pendingSeek = false;
uint32_t pendingSeekSeconds = 0;

View File

@ -121,8 +121,8 @@
<div>
<label for="mode">Mode:</label>
<select id="mode" name="mode">
<option value="s">Single (play selected song / file)</option>
<option value="f">Folder (play selected folder, then stop)</option>
<option value="s">Single (play selected song / file)</option>
<option value="r">Random (randomize order in folder, then stop)</option>
<option value="c">Continuous (continuous playback / loop folder)</option>
</select>