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.
132 lines
3.1 KiB
132 lines
3.1 KiB
2 years ago
|
#include "song.hpp"
|
||
2 years ago
|
|
||
2 years ago
|
#include <esp_log.h>
|
||
2 years ago
|
#include <ff.h>
|
||
2 years ago
|
#include <komihash.h>
|
||
2 years ago
|
#include <tags.h>
|
||
|
|
||
2 years ago
|
namespace database {
|
||
|
|
||
2 years ago
|
namespace libtags {
|
||
|
|
||
|
struct Aux {
|
||
|
FIL file;
|
||
|
FILINFO info;
|
||
2 years ago
|
SongTags* tags;
|
||
2 years ago
|
};
|
||
|
|
||
2 years ago
|
static int read(Tagctx* ctx, void* buf, int cnt) {
|
||
|
Aux* aux = reinterpret_cast<Aux*>(ctx->aux);
|
||
|
UINT bytes_read;
|
||
|
if (f_read(&aux->file, buf, cnt, &bytes_read) != FR_OK) {
|
||
|
return -1;
|
||
|
}
|
||
|
return bytes_read;
|
||
2 years ago
|
}
|
||
|
|
||
2 years ago
|
static int seek(Tagctx* ctx, int offset, int whence) {
|
||
|
Aux* aux = reinterpret_cast<Aux*>(ctx->aux);
|
||
|
FRESULT res;
|
||
|
if (whence == 0) {
|
||
|
// Seek from the start of the file. This is f_lseek's behaviour.
|
||
|
res = f_lseek(&aux->file, offset);
|
||
|
} else if (whence == 1) {
|
||
|
// Seek from current offset.
|
||
|
res = f_lseek(&aux->file, aux->file.fptr + offset);
|
||
|
} else if (whence == 2) {
|
||
|
// Seek from the end of the file
|
||
|
res = f_lseek(&aux->file, aux->info.fsize + offset);
|
||
|
} else {
|
||
|
return -1;
|
||
|
}
|
||
|
return res;
|
||
2 years ago
|
}
|
||
|
|
||
2 years ago
|
static void tag(Tagctx* ctx,
|
||
|
int t,
|
||
|
const char* k,
|
||
|
const char* v,
|
||
|
int offset,
|
||
|
int size,
|
||
|
Tagread f) {
|
||
|
Aux* aux = reinterpret_cast<Aux*>(ctx->aux);
|
||
2 years ago
|
if (t == Ttitle) {
|
||
2 years ago
|
aux->tags->title = v;
|
||
2 years ago
|
} else if (t == Tartist) {
|
||
2 years ago
|
aux->tags->artist = v;
|
||
2 years ago
|
} else if (t == Talbum) {
|
||
2 years ago
|
aux->tags->album = v;
|
||
2 years ago
|
}
|
||
|
}
|
||
|
|
||
2 years ago
|
static void toc(Tagctx* ctx, int ms, int offset) {}
|
||
2 years ago
|
|
||
2 years ago
|
} // namespace libtags
|
||
2 years ago
|
|
||
|
static const std::size_t kBufSize = 1024;
|
||
|
static const char* kTag = "TAGS";
|
||
|
|
||
2 years ago
|
auto ReadAndParseTags(const std::string& path, SongTags* out) -> bool {
|
||
2 years ago
|
libtags::Aux aux;
|
||
2 years ago
|
aux.tags = out;
|
||
2 years ago
|
if (f_stat(path.c_str(), &aux.info) != FR_OK ||
|
||
|
f_open(&aux.file, path.c_str(), FA_READ) != FR_OK) {
|
||
2 years ago
|
ESP_LOGW(kTag, "failed to open file %s", path.c_str());
|
||
2 years ago
|
return false;
|
||
|
}
|
||
|
// Fine to have this on the stack; this is only called on the leveldb task.
|
||
|
char buf[kBufSize];
|
||
|
Tagctx ctx;
|
||
2 years ago
|
ctx.read = libtags::read;
|
||
|
ctx.seek = libtags::seek;
|
||
|
ctx.tag = libtags::tag;
|
||
|
ctx.toc = libtags::toc;
|
||
|
ctx.aux = &aux;
|
||
|
ctx.buf = buf;
|
||
|
ctx.bufsz = kBufSize;
|
||
2 years ago
|
int res = tagsget(&ctx);
|
||
|
f_close(&aux.file);
|
||
|
|
||
|
if (res != 0) {
|
||
2 years ago
|
// Parsing failed.
|
||
2 years ago
|
return false;
|
||
|
}
|
||
|
|
||
2 years ago
|
switch (ctx.format) {
|
||
|
case Fmp3:
|
||
|
out->encoding = ENC_MP3;
|
||
|
break;
|
||
|
default:
|
||
|
out->encoding = ENC_UNSUPPORTED;
|
||
2 years ago
|
}
|
||
2 years ago
|
|
||
2 years ago
|
return true;
|
||
|
}
|
||
|
|
||
|
auto HashString(komihash_stream_t* stream, std::string str) -> void {
|
||
|
komihash_stream_update(stream, str.c_str(), str.length());
|
||
|
}
|
||
|
|
||
|
auto SongTags::Hash() const -> uint64_t {
|
||
|
komihash_stream_t stream;
|
||
|
komihash_stream_init(&stream, 0);
|
||
|
HashString(&stream, title.value_or(""));
|
||
|
HashString(&stream, artist.value_or(""));
|
||
|
HashString(&stream, album.value_or(""));
|
||
|
return komihash_stream_final(&stream);
|
||
|
}
|
||
|
|
||
|
auto SongData::UpdateHash(uint64_t new_hash) const -> SongData {
|
||
|
return SongData(id_, filepath_, new_hash, play_count_, is_tombstoned_);
|
||
|
}
|
||
|
|
||
|
auto SongData::Entomb() const -> SongData {
|
||
|
return SongData(id_, filepath_, tags_hash_, play_count_, true);
|
||
|
}
|
||
|
|
||
|
auto SongData::Exhume(const std::string& new_path) const -> SongData {
|
||
|
return SongData(id_, new_path, tags_hash_, play_count_, false);
|
||
2 years ago
|
}
|
||
|
|
||
2 years ago
|
} // namespace database
|