summaryrefslogtreecommitdiff
path: root/src/database/include/song.hpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/database/include/song.hpp')
-rw-r--r--src/database/include/song.hpp78
1 files changed, 74 insertions, 4 deletions
diff --git a/src/database/include/song.hpp b/src/database/include/song.hpp
index 12a7ef0c..e51e5587 100644
--- a/src/database/include/song.hpp
+++ b/src/database/include/song.hpp
@@ -1,7 +1,7 @@
#pragma once
#include <stdint.h>
-#include <cstdint>
+
#include <optional>
#include <string>
@@ -10,20 +10,68 @@
namespace database {
+/*
+ * Uniquely describes a single song within the database. This value will be
+ * consistent across database updates, and should ideally (but is not guaranteed
+ * to) endure even across a song being removed and re-added.
+ *
+ * Four billion songs should be enough for anybody.
+ */
typedef uint32_t SongId;
-enum Encoding { ENC_UNSUPPORTED, ENC_MP3 };
+/*
+ * Audio file encodings that we are aware of. Used to select an appropriate
+ * decoder at play time.
+ *
+ * Values of this enum are persisted in this database, so it is probably never a
+ * good idea to change the int representation of an existing value.
+ */
+enum class Encoding {
+ kUnsupported = 0,
+ kMp3 = 1,
+};
+/*
+ * Owning container for tag-related song metadata that was extracted from a
+ * file.
+ */
struct SongTags {
Encoding encoding;
std::optional<std::string> title;
+
+ // TODO(jacqueline): It would be nice to use shared_ptr's for the artist and
+ // album, since there's likely a fair number of duplicates for each
+ // (especially the former).
+
std::optional<std::string> artist;
std::optional<std::string> album;
+
+ /*
+ * Returns a hash of the 'identifying' tags of this song. That is, a hash that
+ * can be used to determine if one song is likely the same as another, across
+ * things like re-encoding, re-mastering, or moving the underlying file.
+ */
auto Hash() const -> uint64_t;
-};
-auto ReadAndParseTags(const std::string& path, SongTags* out) -> bool;
+ bool operator==(const SongTags&) const = default;
+};
+/*
+ * Immutable owning container for all of the metadata we store for a particular
+ * song. This includes two main kinds of metadata:
+ * 1. static(ish) attributes, such as the id, path on disk, hash of the tags
+ * 2. dynamic attributes, such as the number of times this song has been
+ * played.
+ *
+ * Because a SongData is immutable, it is thread safe but will not reflect any
+ * changes to the dynamic attributes that may happen after it was obtained.
+ *
+ * Songs may be 'tombstoned'; this indicates that the song is no longer present
+ * at its previous location on disk, and we do not have any existing files with
+ * a matching tags_hash. When this is the case, we ignore this SongData for most
+ * purposes. We keep the entry in our database so that we can properly restore
+ * dynamic attributes (such as play count) if the song later re-appears on disk.
+ */
class SongData {
private:
const SongId id_;
@@ -33,12 +81,14 @@ class SongData {
const bool is_tombstoned_;
public:
+ /* Constructor used when adding new songs to the database. */
SongData(SongId id, const std::string& path, uint64_t hash)
: id_(id),
filepath_(path),
tags_hash_(hash),
play_count_(0),
is_tombstoned_(false) {}
+
SongData(SongId id,
const std::string& path,
uint64_t hash,
@@ -57,12 +107,30 @@ class SongData {
auto is_tombstoned() const -> bool { return is_tombstoned_; }
auto UpdateHash(uint64_t new_hash) const -> SongData;
+
+ /*
+ * Marks this song data as a 'tombstone'. Tombstoned songs are not playable,
+ * and should not generally be shown to users.
+ */
auto Entomb() const -> SongData;
+
+ /*
+ * Clears the tombstone bit of this song, and updates the path to reflect its
+ * new location.
+ */
auto Exhume(const std::string& new_path) const -> SongData;
bool operator==(const SongData&) const = default;
};
+/*
+ * Immutable and owning combination of a song's tags and metadata.
+ *
+ * Note that instances of this class may have a fairly large memory impact, due
+ * to the large number of strings they own. Prefer to query the database again
+ * (which has its own caching layer), rather than retaining Song instances for a
+ * long time.
+ */
class Song {
public:
Song(SongData data, SongTags tags) : data_(data), tags_(tags) {}
@@ -70,6 +138,8 @@ class Song {
auto data() -> const SongData& { return data_; }
auto tags() -> const SongTags& { return tags_; }
+ bool operator==(const Song&) const = default;
+
private:
const SongData data_;
const SongTags tags_;