summaryrefslogtreecommitdiff
path: root/src/tangara/database/track.cpp
diff options
context:
space:
mode:
authorjacqueline <me@jacqueline.id.au>2024-12-23 16:08:47 +1100
committerjacqueline <me@jacqueline.id.au>2024-12-30 14:59:54 +1100
commite9e608cfa09792a64fbda0946c92ec396622a088 (patch)
treece1ad5109ed338c71438e7491481d8d81843093f /src/tangara/database/track.cpp
parente09ab5f6fbcc6395d5f0fbe7e5407483358919c3 (diff)
downloadtangara-fw-e9e608cfa09792a64fbda0946c92ec396622a088.tar.gz
Add a new track tag + index for multiple artists
We still mostly use the singular 'Artist' tag for e.g. displaying a nice name in the now playing screen, but where a track has an 'ARTISTS=' tag, we'll split by semicolon and then use the resulting list to populate an index of tracks by artist
Diffstat (limited to 'src/tangara/database/track.cpp')
-rw-r--r--src/tangara/database/track.cpp51
1 files changed, 51 insertions, 0 deletions
diff --git a/src/tangara/database/track.cpp b/src/tangara/database/track.cpp
index 49babb6a..f0959c98 100644
--- a/src/tangara/database/track.cpp
+++ b/src/tangara/database/track.cpp
@@ -20,6 +20,7 @@
namespace database {
+static constexpr char kAllArtistDelimiters[] = ";";
static constexpr char kGenreDelimiters[] = ",;";
auto tagName(Tag t) -> std::string {
@@ -28,6 +29,8 @@ auto tagName(Tag t) -> std::string {
return "title";
case Tag::kArtist:
return "artist";
+ case Tag::kAllArtists:
+ return "all_artists";
case Tag::kAlbum:
return "album";
case Tag::kAlbumArtist:
@@ -111,6 +114,8 @@ auto TrackTags::get(Tag t) const -> TagValue {
return valueOrMonostate(title_);
case Tag::kArtist:
return valueOrMonostate(artist_);
+ case Tag::kAllArtists:
+ return allArtists_;
case Tag::kAlbum:
return valueOrMonostate(album_);
case Tag::kAlbumArtist:
@@ -135,6 +140,9 @@ auto TrackTags::set(Tag t, std::string_view v) -> void {
case Tag::kArtist:
artist(v);
break;
+ case Tag::kAllArtists:
+ allArtists(v);
+ break;
case Tag::kAlbum:
album(v);
break;
@@ -165,6 +173,7 @@ auto TrackTags::allPresent() const -> std::vector<Tag> {
};
add_if_present(Tag::kTitle, title_);
add_if_present(Tag::kArtist, artist_);
+ add_if_present(Tag::kAllArtists, !allArtists_.empty());
add_if_present(Tag::kAlbum, album_);
add_if_present(Tag::kAlbumArtist, album_artist_);
add_if_present(Tag::kDisc, disc_);
@@ -189,6 +198,48 @@ auto TrackTags::artist(std::string_view s) -> void {
artist_ = s;
}
+auto TrackTags::allArtists() const -> std::span<const std::pmr::string> {
+ return allArtists_;
+}
+
+auto TrackTags::allArtists(const std::string_view s) -> void {
+ allArtists_.clear();
+ std::string src = {s.data(), s.size()};
+ char* token = std::strtok(src.data(), kAllArtistDelimiters);
+
+ auto trim_and_add = [this](std::string_view s) {
+ std::string copy = {s.data(), s.size()};
+
+ // Trim the left
+ copy.erase(copy.begin(),
+ std::find_if(copy.begin(), copy.end(), [](unsigned char ch) {
+ return !std::isspace(ch);
+ }));
+
+ // Trim the right
+ copy.erase(std::find_if(copy.rbegin(), copy.rend(),
+ [](unsigned char ch) { return !std::isspace(ch); })
+ .base(),
+ copy.end());
+
+ // Ignore empty strings.
+ if (!copy.empty()) {
+ allArtists_.push_back({copy.data(), copy.size()});
+ }
+ };
+
+ if (token == NULL) {
+ // No delimiters found in the input. Treat this as a single artist.
+ trim_and_add(s);
+ } else {
+ while (token != NULL) {
+ // Add tokens until no more delimiters found.
+ trim_and_add(token);
+ token = std::strtok(NULL, kAllArtistDelimiters);
+ }
+ }
+}
+
auto TrackTags::album() const -> const std::optional<std::pmr::string>& {
return album_;
}