diff options
| author | jacqueline <me@jacqueline.id.au> | 2023-06-15 10:42:28 +1000 |
|---|---|---|
| committer | jacqueline <me@jacqueline.id.au> | 2023-06-15 10:42:28 +1000 |
| commit | c6bb42cdd21b63accd20012373a8a0e41d8566f5 (patch) | |
| tree | 7fdbab3c5f1e285b54ea4949a31db41602b93b83 /src/database/include | |
| parent | 0024bb1dbe0df319bc7bf022f0c4614cc9c8e0ed (diff) | |
| download | tangara-fw-c6bb42cdd21b63accd20012373a8a0e41d8566f5.tar.gz | |
song -> track
Diffstat (limited to 'src/database/include')
| -rw-r--r-- | src/database/include/database.hpp | 24 | ||||
| -rw-r--r-- | src/database/include/records.hpp | 36 | ||||
| -rw-r--r-- | src/database/include/song.hpp | 166 | ||||
| -rw-r--r-- | src/database/include/tag_parser.hpp | 6 | ||||
| -rw-r--r-- | src/database/include/track.hpp | 169 |
5 files changed, 202 insertions, 199 deletions
diff --git a/src/database/include/database.hpp b/src/database/include/database.hpp index 5214b8df..1a8388e8 100644 --- a/src/database/include/database.hpp +++ b/src/database/include/database.hpp @@ -23,9 +23,9 @@ #include "leveldb/slice.h" #include "records.hpp" #include "result.hpp" -#include "song.hpp" #include "tag_parser.hpp" #include "tasks.hpp" +#include "track.hpp" namespace database { @@ -82,7 +82,7 @@ class Database { auto Update() -> std::future<void>; - auto GetSongs(std::size_t page_size) -> std::future<Result<Song>*>; + auto GetTracks(std::size_t page_size) -> std::future<Result<Track>*>; auto GetDump(std::size_t page_size) -> std::future<Result<std::string>*>; template <typename T> @@ -109,14 +109,14 @@ class Database { ITagParser* tag_parser, std::shared_ptr<tasks::Worker> worker); - auto dbMintNewSongId() -> SongId; - auto dbEntomb(SongId song, uint64_t hash) -> void; + auto dbMintNewTrackId() -> TrackId; + auto dbEntomb(TrackId track, uint64_t hash) -> void; - auto dbPutSongData(const SongData& s) -> void; - auto dbGetSongData(SongId id) -> std::optional<SongData>; - auto dbPutHash(const uint64_t& hash, SongId i) -> void; - auto dbGetHash(const uint64_t& hash) -> std::optional<SongId>; - auto dbPutSong(SongId id, const std::string& path, const uint64_t& hash) + auto dbPutTrackData(const TrackData& s) -> void; + auto dbGetTrackData(TrackId id) -> std::optional<TrackData>; + auto dbPutHash(const uint64_t& hash, TrackId i) -> void; + auto dbGetHash(const uint64_t& hash) -> std::optional<TrackId>; + auto dbPutTrack(TrackId id, const std::string& path, const uint64_t& hash) -> void; template <typename T> @@ -128,9 +128,9 @@ class Database { }; template <> -auto Database::ParseRecord<Song>(const leveldb::Slice& key, - const leveldb::Slice& val) - -> std::optional<Song>; +auto Database::ParseRecord<Track>(const leveldb::Slice& key, + const leveldb::Slice& val) + -> std::optional<Track>; template <> auto Database::ParseRecord<std::string>(const leveldb::Slice& key, const leveldb::Slice& val) diff --git a/src/database/include/records.hpp b/src/database/include/records.hpp index 1b66ad42..95a1a1e8 100644 --- a/src/database/include/records.hpp +++ b/src/database/include/records.hpp @@ -13,7 +13,7 @@ #include "leveldb/db.h" #include "leveldb/slice.h" -#include "song.hpp" +#include "track.hpp" namespace database { @@ -31,49 +31,49 @@ class OwningSlice { }; /* - * Returns the prefix added to every SongData key. This can be used to iterate + * Returns the prefix added to every TrackData key. This can be used to iterate * over every data record in the database. */ auto CreateDataPrefix() -> OwningSlice; -/* Creates a data key for a song with the specified id. */ -auto CreateDataKey(const SongId& id) -> OwningSlice; +/* Creates a data key for a track with the specified id. */ +auto CreateDataKey(const TrackId& id) -> OwningSlice; /* - * Encodes a SongData instance into bytes, in preparation for storing it within + * Encodes a TrackData instance into bytes, in preparation for storing it within * the database. This encoding is consistent, and will remain stable over time. */ -auto CreateDataValue(const SongData& song) -> OwningSlice; +auto CreateDataValue(const TrackData& track) -> OwningSlice; /* - * Parses bytes previously encoded via CreateDataValue back into a SongData. May - * return nullopt if parsing fails. + * Parses bytes previously encoded via CreateDataValue back into a TrackData. + * May return nullopt if parsing fails. */ -auto ParseDataValue(const leveldb::Slice& slice) -> std::optional<SongData>; +auto ParseDataValue(const leveldb::Slice& slice) -> std::optional<TrackData>; /* Creates a hash key for the specified hash. */ auto CreateHashKey(const uint64_t& hash) -> OwningSlice; /* - * Encodes a hash value (at this point just a song id) into bytes, in + * Encodes a hash value (at this point just a track id) into bytes, in * preparation for storing within the database. This encoding is consistent, and * will remain stable over time. */ -auto CreateHashValue(SongId id) -> OwningSlice; +auto CreateHashValue(TrackId id) -> OwningSlice; /* - * Parses bytes previously encoded via CreateHashValue back into a song id. May + * Parses bytes previously encoded via CreateHashValue back into a track id. May * return nullopt if parsing fails. */ -auto ParseHashValue(const leveldb::Slice&) -> std::optional<SongId>; +auto ParseHashValue(const leveldb::Slice&) -> std::optional<TrackId>; -/* Encodes a SongId as bytes. */ -auto SongIdToBytes(SongId id) -> OwningSlice; +/* Encodes a TrackId as bytes. */ +auto TrackIdToBytes(TrackId id) -> OwningSlice; /* - * Converts a song id encoded via SongIdToBytes back into a SongId. May return - * nullopt if parsing fails. + * Converts a track id encoded via TrackIdToBytes back into a TrackId. May + * return nullopt if parsing fails. */ -auto BytesToSongId(const std::string& bytes) -> std::optional<SongId>; +auto BytesToTrackId(const std::string& bytes) -> std::optional<TrackId>; } // namespace database diff --git a/src/database/include/song.hpp b/src/database/include/song.hpp deleted file mode 100644 index d03660dc..00000000 --- a/src/database/include/song.hpp +++ /dev/null @@ -1,166 +0,0 @@ -/* - * Copyright 2023 jacqueline <me@jacqueline.id.au> - * - * SPDX-License-Identifier: GPL-3.0-only - */ - -#pragma once - -#include <stdint.h> - -#include <optional> -#include <string> -#include <utility> - -#include "leveldb/db.h" -#include "span.hpp" - -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; - -/* - * 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, - kWav = 2, - kOgg = 3, - kFlac = 4, -}; - -/* - * 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; - - std::optional<int> channels; - std::optional<int> sample_rate; - std::optional<int> bits_per_sample; - - /* - * 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; - - 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_; - const std::string filepath_; - const uint64_t tags_hash_; - const uint32_t play_count_; - 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, - uint32_t play_count, - bool is_tombstoned) - : id_(id), - filepath_(path), - tags_hash_(hash), - play_count_(play_count), - is_tombstoned_(is_tombstoned) {} - - auto id() const -> SongId { return id_; } - auto filepath() const -> std::string { return filepath_; } - auto play_count() const -> uint32_t { return play_count_; } - auto tags_hash() const -> uint64_t { return tags_hash_; } - 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(const SongData& data, const SongTags& tags) : data_(data), tags_(tags) {} - Song(const Song& other) = default; - - auto data() const -> const SongData& { return data_; } - auto tags() const -> const SongTags& { return tags_; } - - bool operator==(const Song&) const = default; - Song operator=(const Song& other) const { return Song(other); } - - private: - const SongData data_; - const SongTags tags_; -}; - -void swap(Song& first, Song& second); - -} // namespace database diff --git a/src/database/include/tag_parser.hpp b/src/database/include/tag_parser.hpp index 7dab93a1..4be5ad16 100644 --- a/src/database/include/tag_parser.hpp +++ b/src/database/include/tag_parser.hpp @@ -8,20 +8,20 @@ #include <string> -#include "song.hpp" +#include "track.hpp" namespace database { class ITagParser { public: virtual ~ITagParser() {} - virtual auto ReadAndParseTags(const std::string& path, SongTags* out) + virtual auto ReadAndParseTags(const std::string& path, TrackTags* out) -> bool = 0; }; class TagParserImpl : public ITagParser { public: - virtual auto ReadAndParseTags(const std::string& path, SongTags* out) + virtual auto ReadAndParseTags(const std::string& path, TrackTags* out) -> bool override; }; diff --git a/src/database/include/track.hpp b/src/database/include/track.hpp new file mode 100644 index 00000000..5a0c0ca8 --- /dev/null +++ b/src/database/include/track.hpp @@ -0,0 +1,169 @@ +/* + * Copyright 2023 jacqueline <me@jacqueline.id.au> + * + * SPDX-License-Identifier: GPL-3.0-only + */ + +#pragma once + +#include <stdint.h> + +#include <optional> +#include <string> +#include <utility> + +#include "leveldb/db.h" +#include "span.hpp" + +namespace database { + +/* + * Uniquely describes a single track within the database. This value will be + * consistent across database updates, and should ideally (but is not guaranteed + * to) endure even across a track being removed and re-added. + * + * Four billion tracks should be enough for anybody. + */ +typedef uint32_t TrackId; + +/* + * 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, + kWav = 2, + kOgg = 3, + kFlac = 4, +}; + +/* + * Owning container for tag-related track metadata that was extracted from a + * file. + */ +struct TrackTags { + 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; + + std::optional<int> channels; + std::optional<int> sample_rate; + std::optional<int> bits_per_sample; + + /* + * Returns a hash of the 'identifying' tags of this track. That is, a hash + * that can be used to determine if one track is likely the same as another, + * across things like re-encoding, re-mastering, or moving the underlying + * file. + */ + auto Hash() const -> uint64_t; + + bool operator==(const TrackTags&) const = default; +}; + +/* + * Immutable owning container for all of the metadata we store for a particular + * track. 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 track has been + * played. + * + * Because a TrackData is immutable, it is thread safe but will not reflect any + * changes to the dynamic attributes that may happen after it was obtained. + * + * Tracks may be 'tombstoned'; this indicates that the track 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 + * TrackData for most purposes. We keep the entry in our database so that we can + * properly restore dynamic attributes (such as play count) if the track later + * re-appears on disk. + */ +class TrackData { + private: + const TrackId id_; + const std::string filepath_; + const uint64_t tags_hash_; + const uint32_t play_count_; + const bool is_tombstoned_; + + public: + /* Constructor used when adding new tracks to the database. */ + TrackData(TrackId id, const std::string& path, uint64_t hash) + : id_(id), + filepath_(path), + tags_hash_(hash), + play_count_(0), + is_tombstoned_(false) {} + + TrackData(TrackId id, + const std::string& path, + uint64_t hash, + uint32_t play_count, + bool is_tombstoned) + : id_(id), + filepath_(path), + tags_hash_(hash), + play_count_(play_count), + is_tombstoned_(is_tombstoned) {} + + auto id() const -> TrackId { return id_; } + auto filepath() const -> std::string { return filepath_; } + auto play_count() const -> uint32_t { return play_count_; } + auto tags_hash() const -> uint64_t { return tags_hash_; } + auto is_tombstoned() const -> bool { return is_tombstoned_; } + + auto UpdateHash(uint64_t new_hash) const -> TrackData; + + /* + * Marks this track data as a 'tombstone'. Tombstoned tracks are not playable, + * and should not generally be shown to users. + */ + auto Entomb() const -> TrackData; + + /* + * Clears the tombstone bit of this track, and updates the path to reflect its + * new location. + */ + auto Exhume(const std::string& new_path) const -> TrackData; + + bool operator==(const TrackData&) const = default; +}; + +/* + * Immutable and owning combination of a track'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 Track instances for + * a long time. + */ +class Track { + public: + Track(const TrackData& data, const TrackTags& tags) + : data_(data), tags_(tags) {} + Track(const Track& other) = default; + + auto data() const -> const TrackData& { return data_; } + auto tags() const -> const TrackTags& { return tags_; } + + bool operator==(const Track&) const = default; + Track operator=(const Track& other) const { return Track(other); } + + private: + const TrackData data_; + const TrackTags tags_; +}; + +void swap(Track& first, Track& second); + +} // namespace database |
