Fork of Tangara with customizations
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 
tangara-fw/src/tangara/audio/playlist.cpp

154 lines
3.3 KiB

/*
* Copyright 2024 ailurux <ailuruxx@gmail.com>
*
* SPDX-License-Identifier: GPL-3.0-only
*/
#include "playlist.hpp"
#include <string.h>
#include "audio/playlist.hpp"
#include "database/database.hpp"
#include "esp_log.h"
#include "ff.h"
namespace audio {
[[maybe_unused]] static constexpr char kTag[] = "playlist";
Playlist::Playlist(std::string playlistFilepath)
: filepath_(playlistFilepath), mutex_(), total_size_(0), pos_(0) {}
auto Playlist::open() -> bool {
FRESULT res =
f_open(&file_, filepath_.c_str(), FA_READ | FA_WRITE | FA_OPEN_ALWAYS);
if (res != FR_OK) {
ESP_LOGE(kTag, "failed to open file! res: %i", res);
return false;
}
// Count all entries
consumeAndCount(-1);
// Grab the first one
skipTo(0);
return true;
}
Playlist::~Playlist() {
f_close(&file_);
}
auto Playlist::currentPosition() const -> size_t {
return pos_;
}
auto Playlist::size() const -> size_t {
return total_size_;
}
auto Playlist::append(Item i) -> void {
std::unique_lock<std::mutex> lock(mutex_);
auto offset = f_tell(&file_);
// Seek to end and append
auto res = f_lseek(&file_, f_size(&file_));
if (res != FR_OK) {
ESP_LOGE(kTag, "Seek to end of file failed? Error %d", res);
return;
}
// TODO: Resolve paths for track id, etc
std::string path;
if (std::holds_alternative<std::string>(i)) {
path = std::get<std::string>(i);
f_printf(&file_, "%s\n", path.c_str());
total_size_++;
if (current_value_.empty()) {
current_value_ = path;
}
}
// Restore position
res = f_lseek(&file_, offset);
if (res != FR_OK) {
ESP_LOGE(kTag, "Failed to restore file position after append?");
return;
}
res = f_sync(&file_);
if (res != FR_OK) {
ESP_LOGE(kTag, "Failed to sync playlist file after append");
return;
}
}
auto Playlist::skipTo(size_t position) -> void {
pos_ = position;
consumeAndCount(position);
}
auto Playlist::next() -> void {
if (!atEnd()) {
pos_++;
skipTo(pos_);
}
}
auto Playlist::prev() -> void {
// Naive approach to see how that goes for now
pos_--;
skipTo(pos_);
}
auto Playlist::value() const -> std::string {
return current_value_;
}
auto Playlist::clear() -> bool {
auto res = f_close(&file_);
if (res != FR_OK) {
return false;
}
res =
f_open(&file_, filepath_.c_str(), FA_READ | FA_WRITE | FA_CREATE_ALWAYS);
if (res != FR_OK) {
return false;
}
total_size_ = 0;
current_value_.clear();
pos_ = 0;
return true;
}
auto Playlist::atEnd() -> bool {
return pos_ + 1 >= total_size_;
}
auto Playlist::filepath() -> std::string {
return filepath_;
}
auto Playlist::consumeAndCount(ssize_t upto) -> bool {
std::unique_lock<std::mutex> lock(mutex_);
TCHAR buff[512];
size_t count = 0;
f_rewind(&file_);
while (!f_eof(&file_)) {
// TODO: Correctly handle lines longer than this
// TODO: Also correctly handle the case where the last entry doesn't end in
// \n
auto res = f_gets(buff, 512, &file_);
if (res == NULL) {
ESP_LOGW(kTag, "Error consuming playlist file at line %d", count);
return false;
}
count++;
if (upto >= 0 && count > upto) {
size_t len = strlen(buff);
current_value_.assign(buff, len - 1);
break;
}
}
if (upto < 0) {
total_size_ = count;
f_rewind(&file_);
}
return true;
}
} // namespace audio