summaryrefslogtreecommitdiff
path: root/src
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
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')
-rw-r--r--src/tangara/database/database.cpp2
-rw-r--r--src/tangara/database/database.hpp2
-rw-r--r--src/tangara/database/index.cpp9
-rw-r--r--src/tangara/database/index.hpp1
-rw-r--r--src/tangara/database/tag_parser.cpp1
-rw-r--r--src/tangara/database/track.cpp51
-rw-r--r--src/tangara/database/track.hpp5
7 files changed, 69 insertions, 2 deletions
diff --git a/src/tangara/database/database.cpp b/src/tangara/database/database.cpp
index 9851b041..67893b6e 100644
--- a/src/tangara/database/database.cpp
+++ b/src/tangara/database/database.cpp
@@ -292,7 +292,7 @@ auto Database::setTrackData(TrackId id, const TrackData& data) -> void {
auto Database::getIndexes() -> std::vector<IndexInfo> {
// TODO(jacqueline): This probably needs to be async? When we have runtime
// configurable indexes, they will need to come from somewhere.
- return {kAllTracks, kAllAlbums, kAlbumsByArtist,
+ return {kAllTracks, kAllAlbums, kAllArtists, kAlbumsByArtist,
kTracksByGenre, kPodcasts, kAudiobooks};
}
diff --git a/src/tangara/database/database.hpp b/src/tangara/database/database.hpp
index e46a123e..9a7e1d4e 100644
--- a/src/tangara/database/database.hpp
+++ b/src/tangara/database/database.hpp
@@ -38,7 +38,7 @@
namespace database {
-const uint8_t kCurrentDbVersion = 8;
+const uint8_t kCurrentDbVersion = 9;
struct SearchKey;
class Record;
diff --git a/src/tangara/database/index.cpp b/src/tangara/database/index.cpp
index 1cdc0d07..58a7ead4 100644
--- a/src/tangara/database/index.cpp
+++ b/src/tangara/database/index.cpp
@@ -56,6 +56,13 @@ const IndexInfo kAllAlbums{
.components = {Tag::kAlbum, Tag::kAlbumOrder},
};
+const IndexInfo kAllArtists{
+ .id = 7,
+ .type = MediaType::kMusic,
+ .name = "All Artists",
+ .components = {Tag::kAllArtists, Tag::kTitle},
+};
+
const IndexInfo kPodcasts{
.id = 5,
.type = MediaType::kPodcast,
@@ -114,6 +121,8 @@ class Indexer {
return "Unknown Album";
case Tag::kAlbumArtist:
return track_tags_.artist().value_or("Unknown Artist");
+ case Tag::kAllArtists:
+ return track_tags_.artist().value_or("Unknown Artist");
case Tag::kGenres:
return std::pmr::vector<std::pmr::string>{};
case Tag::kDisc:
diff --git a/src/tangara/database/index.hpp b/src/tangara/database/index.hpp
index e1c6283a..98e57d54 100644
--- a/src/tangara/database/index.hpp
+++ b/src/tangara/database/index.hpp
@@ -78,6 +78,7 @@ extern const IndexInfo kAlbumsByArtist;
extern const IndexInfo kTracksByGenre;
extern const IndexInfo kAllTracks;
extern const IndexInfo kAllAlbums;
+extern const IndexInfo kAllArtists;
extern const IndexInfo kPodcasts;
extern const IndexInfo kAudiobooks;
diff --git a/src/tangara/database/tag_parser.cpp b/src/tangara/database/tag_parser.cpp
index 15323a7c..cfe4b8a5 100644
--- a/src/tangara/database/tag_parser.cpp
+++ b/src/tangara/database/tag_parser.cpp
@@ -170,6 +170,7 @@ 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;
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_;
}
diff --git a/src/tangara/database/track.hpp b/src/tangara/database/track.hpp
index 65c5cfec..21d6349d 100644
--- a/src/tangara/database/track.hpp
+++ b/src/tangara/database/track.hpp
@@ -73,6 +73,7 @@ enum class Tag {
kTrack = 5,
kAlbumOrder = 6,
kGenres = 7,
+ kAllArtists = 8,
};
using TagValue = std::variant<std::monostate,
@@ -114,6 +115,9 @@ class TrackTags {
auto artist() const -> const std::optional<std::pmr::string>&;
auto artist(std::string_view) -> void;
+ auto allArtists() const -> std::span<const std::pmr::string>;
+ auto allArtists(const std::string_view) -> void;
+
auto album() const -> const std::optional<std::pmr::string>&;
auto album(std::string_view) -> void;
@@ -144,6 +148,7 @@ class TrackTags {
std::optional<std::pmr::string> title_;
std::optional<std::pmr::string> artist_;
+ std::pmr::vector<std::pmr::string> allArtists_;
std::optional<std::pmr::string> album_;
std::optional<std::pmr::string> album_artist_;
std::optional<uint8_t> disc_;