#include "playlist_manager.h" #include #include "spi_master.h" #include PlaylistManager::PlaylistManager() { scan_files(); } void PlaylistManager::scan_files() { SPIMaster::select_sd(); std::vector folders; File root = SD.open("/"); File entry; while (entry = root.openNextFile()) { String foldername = entry.name(); if (foldername.startsWith("/.")) continue; foldername.remove(foldername.length()); folders.push_back(foldername); _check_for_special_chars(foldername); entry.close(); } _map.clear(); if (!SD.exists("/_mapping.txt\n")) { ERROR("WARNING: File /_mapping.txt not found.\n"); } else { File f = SD.open("/_mapping.txt"); DEBUG("Reading /_mapping.txt...\n"); while (f.available()) { char buffer[512]; size_t pos = f.readBytesUntil('\n', buffer, 511); buffer[pos] = '\0'; String data = buffer; uint8_t eq = data.indexOf('='); if (eq>0 && eq%s\n", rfid_id.c_str(), folder.c_str()); _map[rfid_id] = folder; if (folder.charAt(0)=='/') { bool found=false; for (String f: folders) { if (f.equals(folder)) { found = true; break; } } if (!found) { INFO("WARNING: Found mapping for RFID id %s which maps to non-existing folder %s!\n", rfid_id.c_str(), folder.c_str()); } } } } f.close(); } root.close(); _unmapped_folders.clear(); for (String folder: folders) { bool found = false; for(std::map::iterator it = _map.begin(); it != _map.end(); it++) { if (it->second.equals(folder)) { found = true; break; } } if (!found) { // Checking folder for media files File dir = SD.open(folder); while (entry = dir.openNextFile()) { String filename = entry.name(); filename = filename.substring(folder.length() + 1); if (!filename.startsWith(".") && filename.endsWith(".mp3")) { found = true; } entry.close(); if (found) break; } if (found) { _unmapped_folders.push_back(folder); } } } SPIMaster::select_sd(false); } void PlaylistManager::_check_for_special_chars(String s) { for(int i=0; i= 0x7F) { ERROR("WARNING: Folder / file '%s' contains non-ascii chars!\n", s.c_str()); return; } } } Playlist* PlaylistManager::get_playlist_for_id(String id) { if (!_map.count(id)) return NULL; String folder = _map[id]; return get_playlist_for_folder(folder); } Playlist* PlaylistManager::get_playlist_for_folder(String folder) { Playlist* p; if (!_playlists.count(folder)) { p = new Playlist(folder); _playlists[folder] = p; if (p->persistence == PERSIST_PERMANENTLY) { // TODO Load persistence from file String search = folder; search += "="; SPIMaster::select_sd(); if (SD.exists("/_positions.txt")) { File f = SD.open("/_positions.txt", "r"); while (true) { String s = f.readStringUntil('\n'); if (s.length()==0) break; if (s.startsWith(search)) { s = s.substring(search.length()); int idx = s.indexOf(','); String title_index = s.substring(0, idx); uint32_t position = s.substring(idx+1).toInt(); p->set_track_by_id(title_index); p->set_position(position); break; } } f.close(); } SPIMaster::select_sd(false); } } else { p = _playlists[folder]; if (p->persistence == PERSIST_NONE) { p->reset(); } } return p; } void PlaylistManager::dump_ids() { for (std::map::iterator it = _map.begin(); it!=_map.end(); it++) { INFO(" %s -> %s\n", it->first.c_str(), it->second.c_str()); } } String PlaylistManager::json() { DynamicJsonDocument json(10240); json["_type"] = "playlist_manager"; JsonObject folders = json.createNestedObject("folders"); for (std::map::iterator it = _map.begin(); it!=_map.end(); it++) { folders[it->second] = it->first; } JsonArray started = json.createNestedArray("started"); for (std::map::iterator it = _playlists.begin(); it!=_playlists.end(); it++) { if (it->second->is_fresh()) continue; started.add(it->first); } JsonArray unmapped = json.createNestedArray("unmapped"); for(String folder : _unmapped_folders) { unmapped.add(folder); } return json.as(); } bool PlaylistManager::add_mapping(String id, String folder) { DEBUG("Adding mapping: %s=>%s\n", id.c_str(), folder.c_str()); _map[id] = folder; _save_mapping(); return true; } void PlaylistManager::_save_mapping() { SPIMaster::select_sd(); File f = SD.open("/_mapping.txt", "w"); String s = create_mapping_txt(); unsigned char* buf = new unsigned char[s.length()]; s.getBytes(buf, s.length()); f.write(buf, s.length()-1); f.close(); SPIMaster::select_sd(false); delete buf; } String PlaylistManager::create_mapping_txt() { String s; for(std::map::iterator it = _map.begin(); it != _map.end(); it++) { s += it->first; s += "="; s += it->second; s += '\n'; } return s; } void PlaylistManager::persist(Playlist* p) { if (p->persistence != PERSIST_PERMANENTLY) return; String search = p->path(); search += '='; bool old_file_existed = false; SPIMaster::select_sd(); if (SD.exists("_positions.txt")) { SD.rename("/_positions.txt", "/_positions.temp.txt"); old_file_existed = true; } File dst = SD.open("/_positions.txt", "w"); if (old_file_existed) { File src = SD.open("/_positions.temp.txt", "r"); while (true) { String line = src.readStringUntil('\n'); line.trim(); if (line.startsWith(search)) continue; dst.println(line); } src.close(); SD.remove("/_positions.temp.txt"); } dst.print(search); dst.print(p->get_current_track_id()); dst.print(','); dst.println(p->get_position()); dst.close(); SPIMaster::select_sd(false); }