Pass through TXXX kv pairs from libtags, treated as vorbis comments

mp3 is no good i do not like them at all
This commit is contained in:
jacqueline 2024-12-31 11:32:51 +11:00
parent b6b62cb8ea
commit 33e89a0672
3 changed files with 47 additions and 22 deletions
lib/libtags
src/tangara/database

View file

@ -68,6 +68,10 @@ v2cb(Tagctx *ctx, char *k, char *v)
return 0; return 0;
}else if(strcmp(k-1, "COM") == 0 || strcmp(k-1, "COMM") == 0){ }else if(strcmp(k-1, "COM") == 0 || strcmp(k-1, "COMM") == 0){
txtcb(ctx, Tcomment, k-1, v); txtcb(ctx, Tcomment, k-1, v);
}else if(strcmp(k, "XXX") == 0){
k = v;
v += strlen(v) + 1;
txtcb(ctx, Tunknown, k, v);
}else{ }else{
txtcb(ctx, Tunknown, k-1, v); txtcb(ctx, Tunknown, k-1, v);
} }

View file

@ -13,6 +13,7 @@
#include <iomanip> #include <iomanip>
#include <memory> #include <memory>
#include <mutex> #include <mutex>
#include <optional>
#include "database/track.hpp" #include "database/track.hpp"
#include "debug.hpp" #include "debug.hpp"
@ -47,6 +48,29 @@ static auto convert_tag(int tag) -> std::optional<Tag> {
} }
} }
static std::unordered_map<std::string, Tag> sVorbisNameToTag{
{"TITLE", Tag::kTitle},
{"ALBUM", Tag::kAlbum},
{"ARTIST", Tag::kArtist},
{"ARTISTS", Tag::kAllArtists},
{"ALBUMARTIST", Tag::kAlbumArtist},
{"TRACK", Tag::kTrack},
{"TRACKNUMBER", Tag::kTrack},
{"GENRE", Tag::kGenres},
{"DISC", Tag::kDisc},
{"DISCNUMBER", Tag::kDisc},
};
static auto convert_vorbis_tag(const std::string_view name)
-> std::optional<Tag> {
std::string name_upper{name};
std::transform(name.begin(), name.end(), name_upper.begin(), ::toupper);
if (sVorbisNameToTag.contains(name_upper)) {
return sVorbisNameToTag[name_upper];
}
return {};
}
namespace libtags { namespace libtags {
struct Aux { struct Aux {
@ -96,7 +120,14 @@ static void tag(Tagctx* ctx,
int size, int size,
Tagread f) { Tagread f) {
Aux* aux = reinterpret_cast<Aux*>(ctx->aux); Aux* aux = reinterpret_cast<Aux*>(ctx->aux);
auto tag = convert_tag(t); std::optional<Tag> tag;
if (t == Tunknown && k && v) {
// Sometimes 'unknown' tags are vorbis comments shoved into a generic tag
// name in other containers.
tag = convert_vorbis_tag(k);
} else {
tag = convert_tag(t);
}
if (!tag) { if (!tag) {
return; return;
} }
@ -168,18 +199,7 @@ auto TagParserImpl::ReadAndParseTags(std::string_view path)
return tags; return tags;
} }
OggTagParser::OggTagParser() { OggTagParser::OggTagParser() {}
nameToTag_["TITLE"] = Tag::kTitle;
nameToTag_["ALBUM"] = Tag::kAlbum;
nameToTag_["ARTIST"] = Tag::kArtist;
nameToTag_["ARTISTS"] = Tag::kAllArtists;
nameToTag_["ALBUMARTIST"] = Tag::kAlbumArtist;
nameToTag_["TRACK"] = Tag::kTrack;
nameToTag_["TRACKNUMBER"] = Tag::kTrack;
nameToTag_["GENRE"] = Tag::kGenres;
nameToTag_["DISC"] = Tag::kDisc;
nameToTag_["DISCNUMBER"] = Tag::kDisc;
}
auto OggTagParser::ReadAndParseTags(std::string_view p) auto OggTagParser::ReadAndParseTags(std::string_view p)
-> std::shared_ptr<TrackTags> { -> std::shared_ptr<TrackTags> {
@ -295,8 +315,9 @@ auto OggTagParser::parseComments(TrackTags& res, std::span<unsigned char> data)
std::string key_upper{key}; std::string key_upper{key};
std::transform(key.begin(), key.end(), key_upper.begin(), ::toupper); std::transform(key.begin(), key.end(), key_upper.begin(), ::toupper);
if (nameToTag_.contains(key_upper) && !val.empty()) { auto tag = convert_vorbis_tag(key);
res.set(nameToTag_[key_upper], val); if (tag && !val.empty()) {
res.set(*tag, val);
} }
} }
@ -316,14 +337,16 @@ auto GenericTagParser::ReadAndParseTags(std::string_view p)
std::string path{p}; std::string path{p};
libtags::Aux aux; libtags::Aux aux;
// Fail fast if trying to parse a file that doesn't appear to be a supported audio format // Fail fast if trying to parse a file that doesn't appear to be a supported
// For context, see: https://codeberg.org/cool-tech-zone/tangara-fw/issues/149 // audio format For context, see:
// https://codeberg.org/cool-tech-zone/tangara-fw/issues/149
bool found = false; bool found = false;
for (const auto& ext : supported_exts) { for (const auto& ext : supported_exts) {
// Case-insensitive file extension check // Case-insensitive file extension check
if (std::equal(ext.rbegin(), ext.rend(), path.rbegin(), if (std::equal(ext.rbegin(), ext.rend(), path.rbegin(), [](char a, char b) {
[](char a, char b) { return std::tolower(a) == std::tolower(b); })) { return std::tolower(a) == std::tolower(b);
found=true; })) {
found = true;
break; break;
} }
} }

View file

@ -47,8 +47,6 @@ class OggTagParser : public ITagParser {
private: private:
auto parseComments(TrackTags&, std::span<unsigned char> data) -> void; auto parseComments(TrackTags&, std::span<unsigned char> data) -> void;
auto parseLength(std::span<unsigned char> data) -> uint64_t; auto parseLength(std::span<unsigned char> data) -> uint64_t;
std::unordered_map<std::string, Tag> nameToTag_;
}; };
class GenericTagParser : public ITagParser { class GenericTagParser : public ITagParser {