esmp3/src/playlist_manager.cpp

165 lines
4.2 KiB
C++

#include "playlist_manager.h"
#include <SD.h>
#include "spi_master.h"
#include <ArduinoJson.h>
PlaylistManager::PlaylistManager() {
scan_files();
}
void PlaylistManager::scan_files() {
SPIMaster::select_sd();
std::vector<String> 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<data.length()-1) {
String rfid_id = data.substring(0, eq);
String folder = data.substring(eq + 1);
TRACE(" Adding mapping: %s=>%s\n", rfid_id.c_str(), folder.c_str());
_map[rfid_id] = folder;
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<String, String>::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<s.length(); i++) {
char c = s.charAt(i);
if (c < 0x20 || c >= 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) {
if (!_playlists.count(folder)) {
_playlists[folder] = new Playlist(folder);
}
return _playlists[folder];
}
void PlaylistManager::dump_ids() {
for (std::map<String, String>::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<String, String>::iterator it = _map.begin(); it!=_map.end(); it++) {
folders[it->second] = it->first;
}
JsonArray started = json.createNestedArray("started");
for (std::map<String, Playlist*>::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<String>();
}
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<String, String>::iterator it = _map.begin(); it != _map.end(); it++) {
s += it->first;
s += "=";
s += it->second;
s += '\n';
}
return s;
}