diff options
| author | jacqueline <me@jacqueline.id.au> | 2023-05-08 13:38:36 +1000 |
|---|---|---|
| committer | jacqueline <me@jacqueline.id.au> | 2023-05-08 17:47:29 +1000 |
| commit | 16e6180ba7946119538d03463ea7d37fccc4dcb3 (patch) | |
| tree | 968483bfa477b43a72b8b6dfefaac3e771163e9e /src/database/song.cpp | |
| parent | fe19478e0f286191c3bf1b9946b89ed26d5c4bae (diff) | |
| download | tangara-fw-16e6180ba7946119538d03463ea7d37fccc4dcb3.tar.gz | |
Database init is now stable!
Diffstat (limited to 'src/database/song.cpp')
| -rw-r--r-- | src/database/song.cpp | 131 |
1 files changed, 131 insertions, 0 deletions
diff --git a/src/database/song.cpp b/src/database/song.cpp new file mode 100644 index 00000000..32507fa2 --- /dev/null +++ b/src/database/song.cpp @@ -0,0 +1,131 @@ +#include "song.hpp" + +#include <esp_log.h> +#include <ff.h> +#include <komihash.h> +#include <tags.h> + +namespace database { + +namespace libtags { + +struct Aux { + FIL file; + FILINFO info; + SongTags* tags; +}; + +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; +} + +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; +} + +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); + if (t == Ttitle) { + aux->tags->title = v; + } else if (t == Tartist) { + aux->tags->artist = v; + } else if (t == Talbum) { + aux->tags->album = v; + } +} + +static void toc(Tagctx* ctx, int ms, int offset) {} + +} // namespace libtags + +static const std::size_t kBufSize = 1024; +static const char* kTag = "TAGS"; + +auto ReadAndParseTags(const std::string& path, SongTags* out) -> bool { + libtags::Aux aux; + aux.tags = out; + if (f_stat(path.c_str(), &aux.info) != FR_OK || + f_open(&aux.file, path.c_str(), FA_READ) != FR_OK) { + ESP_LOGW(kTag, "failed to open file %s", path.c_str()); + return false; + } + // Fine to have this on the stack; this is only called on the leveldb task. + char buf[kBufSize]; + Tagctx ctx; + 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; + int res = tagsget(&ctx); + f_close(&aux.file); + + if (res != 0) { + // Parsing failed. + return false; + } + + switch (ctx.format) { + case Fmp3: + out->encoding = ENC_MP3; + break; + default: + out->encoding = ENC_UNSUPPORTED; + } + + 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); +} + +} // namespace database |
