Updated ITagStore interface, and also (1) removed "year" from the

"tracks" table, it's now stored as extended metadata, and (2) fixed bug
where extended metadata was not always getting properly inserted into
the "track_meta" table due to an "optimization".
This commit is contained in:
casey langen 2017-12-31 22:49:16 -08:00
parent 28dfeeb3be
commit 14c823fb63
11 changed files with 104 additions and 36 deletions

View File

@ -50,7 +50,7 @@ using namespace musik::core;
using namespace musik::core::library;
using namespace musik::core::runtime;
#define DATABASE_VERSION 6
#define DATABASE_VERSION 7
#define VERBOSE_LOGGING 0
#define MESSAGE_QUERY_COMPLETED 5000
@ -336,6 +336,14 @@ static void upgradeV5ToV6(db::Connection& db) {
scheduleSyncDueToDbUpgrade = true;
}
static void upgradeV6ToV7(db::Connection& db) {
db.Execute("UPDATE tracks SET filetime=0");
db.Execute("DELETE FROM track_meta;");
db.Execute("DELETE FROM meta_keys;");
db.Execute("DELETE FROM meta_values;");
scheduleSyncDueToDbUpgrade = true;
}
static void setVersion(db::Connection& db, int version) {
db.Execute("DELETE FROM version");
@ -354,7 +362,6 @@ void LocalLibrary::CreateDatabase(db::Connection &db){
"bpm REAL DEFAULT 0,"
"duration INTEGER DEFAULT 0,"
"filesize INTEGER DEFAULT 0,"
"year INTEGER DEFAULT 0,"
"visual_genre_id INTEGER DEFAULT 0,"
"visual_artist_id INTEGER DEFAULT 0,"
"album_artist_id INTEGER DEFAULT 0,"
@ -506,7 +513,7 @@ void LocalLibrary::CreateDatabase(db::Connection &db){
db.Execute(
"CREATE VIEW tracks_view AS "
"SELECT DISTINCT "
" t.id, t.track, t.disc, t.bpm, t.duration, t.filesize, t.year, t.title, t.filename, "
" t.id, t.track, t.disc, t.bpm, t.duration, t.filesize, t.title, t.filename, "
" t.thumbnail_id, t.external_id, al.name AS album, alar.name AS album_artist, gn.name AS genre, "
" ar.name AS artist, t.filetime, t.visual_genre_id, t.visual_artist_id, t.album_artist_id, t.album_id "
"FROM "
@ -553,6 +560,10 @@ void LocalLibrary::CreateDatabase(db::Connection &db){
upgradeV5ToV6(db);
}
if (lastVersion >= 1 && lastVersion < 7) {
upgradeV6ToV7(db);
}
/* ensure our version is set correctly */
setVersion(db, DATABASE_VERSION);

View File

@ -98,6 +98,37 @@ static const std::string FILTERED_GENRE_QUERY =
"WHERE genres.id = tracks.visual_genre_id AND LOWER(genres.name) LIKE ? AND tracks.visible = 1 %s"
"ORDER BY genres.sort_order;";
static const std::string REGULAR_EXTENDED_PROPERTY_QUERY =
"SELECT DISTINCT meta_values.* "
"FROM meta_values, track_meta, tracks "
"WHERE "
" meta_values.id = track_meta.meta_value_id AND "
" track_meta.track_id = tracks.id AND "
" tracks.visible = 1 AND "
" meta_values.meta_key_id IN( "
" SELECT DISTINCT meta_keys.id "
" FROM meta_keys "
" WHERE LOWER(meta_keys.name) = LOWER(?) "
" ) "
"%s "
"ORDER BY meta_values.content ASC";
static const std::string FILTERED_EXTENDED_PROPERTY_QUERY =
"SELECT DISTINCT meta_values.* "
"FROM meta_values, track_meta, tracks "
"WHERE "
" meta_values.id = track_meta.meta_value_id AND "
" track_meta.track_id = tracks.id AND "
" tracks.visible = 1 AND "
" meta_values.meta_key_id IN( "
" SELECT DISTINCT meta_keys.id "
" FROM meta_keys "
" WHERE LOWER(meta_keys.name) = LOWER(?) "
" ) "
"AND LOWER(meta_values.content) LIKE LOWER(?) "
"%s "
"ORDER BY meta_values.content ASC";
static const std::string REGULAR_PLAYLISTS_QUERY =
"SELECT DISTINCT id, name "
"FROM playlists %s "

View File

@ -41,7 +41,7 @@ using namespace musik::core::db::local;
using namespace musik::core;
using namespace musik::core::library;
static const std::string COLUMNS = "t.track, t.disc, t.bpm, t.duration, t.filesize, t.year, t.title, t.filename, t.thumbnail_id, al.name AS album, alar.name AS album_artist, gn.name AS genre, ar.name AS artist, t.filetime, t.visual_genre_id, t.visual_artist_id, t.album_artist_id, t.album_id, t.source_id, t.external_id";
static const std::string COLUMNS = "t.track, t.disc, t.bpm, t.duration, t.filesize, t.title, t.filename, t.thumbnail_id, al.name AS album, alar.name AS album_artist, gn.name AS genre, ar.name AS artist, t.filetime, t.visual_genre_id, t.visual_artist_id, t.album_artist_id, t.album_id, t.source_id, t.external_id";
static const std::string TABLES = "tracks t, albums al, artists alar, artists ar, genres gn";
static const std::string PREDICATE = "t.album_id=al.id AND t.album_artist_id=alar.id AND t.visual_genre_id=gn.id AND t.visual_artist_id=ar.id";

View File

@ -134,6 +134,11 @@ void IndexerTrack::ClearValue(const char* metakey) {
}
}
bool IndexerTrack::Contains(const char* metakey) {
auto md = this->internalMetadata;
return md && md->metadata.find(metakey) != md->metadata.end();
}
void IndexerTrack::SetThumbnail(const char *data, long size) {
if (this->internalMetadata->thumbnailData) {
delete[] this->internalMetadata->thumbnailData;
@ -273,14 +278,14 @@ static int64_t writeToTracksTable(
if (id != 0) {
query =
"UPDATE tracks "
"SET track=?, disc=?, bpm=?, duration=?, filesize=?, year=?, "
"SET track=?, disc=?, bpm=?, duration=?, filesize=?, "
" title=?, filename=?, filetime=?, path_id=?, external_id=? "
"WHERE id=?";
}
else {
query =
"INSERT INTO tracks "
"(track, disc, bpm, duration, filesize, year, title, filename, filetime, path_id, external_id) "
"(track, disc, bpm, duration, filesize, title, filename, filetime, path_id, external_id) "
"VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)";
}
@ -291,12 +296,11 @@ static int64_t writeToTracksTable(
stmt.BindText(2, track.GetString("bpm"));
stmt.BindInt32(3, track.GetInt32("duration"));
stmt.BindInt32(4, track.GetInt32("filesize"));
stmt.BindText(5, track.GetString("year"));
stmt.BindText(6, track.GetString("title"));
stmt.BindText(7, track.GetString("filename"));
stmt.BindInt32(8, track.GetInt32("filetime"));
stmt.BindInt64(9, track.GetInt64("path_id"));
stmt.BindText(10, track.GetString("external_id"));
stmt.BindText(5, track.GetString("title"));
stmt.BindText(6, track.GetString("filename"));
stmt.BindInt32(7, track.GetInt32("filetime"));
stmt.BindInt64(8, track.GetInt64("path_id"));
stmt.BindText(9, track.GetString("external_id"));
if (id != 0) {
stmt.BindInt64(11, id);
@ -327,7 +331,6 @@ static void removeKnownFields(Track::MetadataMap& metadata) {
metadata.erase("disc");
metadata.erase("bpm");
metadata.erase("duration");
metadata.erase("year");
metadata.erase("title");
metadata.erase("filename");
metadata.erase("filetime");
@ -497,13 +500,9 @@ void IndexerTrack::ProcessNonStandardMetadata(db::Connection& connection) {
}
}
if (keyCached && valueCached) {
continue; /* duplicate info. we don't need to save it again. */
}
/* now that we have a keyId and a valueId, create the relationship */
if (valueId != 0) {
if (valueId != 0 && keyId != 0) {
insertTrackMeta.Reset();
insertTrackMeta.BindInt64(0, this->id);
insertTrackMeta.BindInt64(1, valueId);

View File

@ -48,6 +48,7 @@ namespace musik { namespace core {
/* ITagStore */
virtual void SetValue(const char* metakey, const char* value);
virtual void ClearValue(const char* metakey);
virtual bool Contains(const char* metakey);
virtual void SetThumbnail(const char *data, long size);
virtual void SetReplayGain(const musik::core::sdk::ReplayGain& replayGain);

View File

@ -117,6 +117,11 @@ void LibraryTrack::ClearValue(const char* metakey) {
this->metadata.erase(metakey);
}
bool LibraryTrack::Contains(const char* metakey) {
std::unique_lock<std::mutex> lock(this->mutex);
return this->metadata.find(metakey) != this->metadata.end();
}
void LibraryTrack::SetThumbnail(const char *data, long size) {
/* do nothing. we implement a fat interface; this is just used by
IndexerTrack. */
@ -209,7 +214,7 @@ bool LibraryTrack::Load(Track *target, db::Connection &db) {
"ORDER BY tm.id", db);
db::Statement trackQuery(
"SELECT t.track, t.disc, t.bpm, t.duration, t.filesize, t.year, t.title, t.filename, t.thumbnail_id, al.name, t.filetime, t.visual_genre_id, t.visual_artist_id, t.album_artist_id, t.album_id " \
"SELECT t.track, t.disc, t.bpm, t.duration, t.filesize, t.title, t.filename, t.thumbnail_id, al.name, t.filetime, t.visual_genre_id, t.visual_artist_id, t.album_artist_id, t.album_id " \
"FROM tracks t, paths p, albums al " \
"WHERE t.id=? AND t.album_id=al.id", db);
@ -220,16 +225,15 @@ bool LibraryTrack::Load(Track *target, db::Connection &db) {
target->SetValue("bpm", trackQuery.ColumnText(2));
target->SetValue("duration", trackQuery.ColumnText(3));
target->SetValue("filesize", trackQuery.ColumnText(4));
target->SetValue("year", trackQuery.ColumnText(5));
target->SetValue("title", trackQuery.ColumnText(6));
target->SetValue("filename", trackQuery.ColumnText(7));
target->SetValue("thumbnail_id", trackQuery.ColumnText(8));
target->SetValue("album", trackQuery.ColumnText(9));
target->SetValue("filetime", trackQuery.ColumnText(10));
target->SetValue("visual_genre_id", trackQuery.ColumnText(11));
target->SetValue("visual_artist_id", trackQuery.ColumnText(12));
target->SetValue("album_artist_id", trackQuery.ColumnText(13));
target->SetValue("album_id", trackQuery.ColumnText(14));
target->SetValue("title", trackQuery.ColumnText(5));
target->SetValue("filename", trackQuery.ColumnText(6));
target->SetValue("thumbnail_id", trackQuery.ColumnText(7));
target->SetValue("album", trackQuery.ColumnText(8));
target->SetValue("filetime", trackQuery.ColumnText(9));
target->SetValue("visual_genre_id", trackQuery.ColumnText(10));
target->SetValue("visual_artist_id", trackQuery.ColumnText(11));
target->SetValue("album_artist_id", trackQuery.ColumnText(12));
target->SetValue("album_id", trackQuery.ColumnText(13));
genresQuery.BindInt64(0, (int64_t) target->GetId());
while (genresQuery.Step() == db::Row) {

View File

@ -60,6 +60,7 @@ namespace musik { namespace core {
/* ITagStore */
virtual void SetValue(const char* metakey, const char* value);
virtual void ClearValue(const char* metakey);
virtual bool Contains(const char* metakey);
virtual void SetThumbnail(const char *data, long size);
virtual void SetReplayGain(const musik::core::sdk::ReplayGain& replayGain);

View File

@ -104,6 +104,7 @@ class SdkWrapper : public Track {
virtual void SetValue(const char* key, const char* value) override { NO_IMPL }
virtual void ClearValue(const char* key) override { NO_IMPL }
virtual void SetThumbnail(const char *data, long size) override { NO_IMPL }
virtual bool Contains(const char* key) override { NO_IMPL }
virtual void SetReplayGain(const ReplayGain& replayGain) override { NO_IMPL }
virtual void SetId(int64_t id) override { NO_IMPL }
virtual std::string GetString(const char* metakey) override { NO_IMPL }
@ -186,6 +187,10 @@ void TagStore::ClearValue(const char* key) {
this->track->ClearValue(key);
}
bool TagStore::Contains(const char* key) {
return this->track->Contains(key);
}
void TagStore::SetThumbnail(const char *data, long size) {
this->track->SetThumbnail(data, size);
}

View File

@ -66,6 +66,7 @@ namespace musik { namespace core {
virtual void SetValue(const char* key, const char* value) = 0;
virtual void ClearValue(const char* key) = 0;
virtual void SetThumbnail(const char *data, long size) = 0;
virtual bool Contains(const char* key) = 0;
virtual void SetReplayGain(const musik::core::sdk::ReplayGain& replayGain) = 0;
/* IResource */
@ -112,6 +113,7 @@ namespace musik { namespace core {
virtual void Release() override;
virtual void SetValue(const char* key, const char* value) override;
virtual void ClearValue(const char* key) override;
virtual bool Contains(const char* key) override;
virtual void SetThumbnail(const char *data, long size) override;
virtual void SetReplayGain(const musik::core::sdk::ReplayGain& replayGain) override;

View File

@ -48,7 +48,8 @@ namespace musik { namespace core { namespace sdk {
virtual void Retain() = 0;
virtual void Release() = 0;
virtual void SetValue(const char* key, const char* value) = 0;
virtual void ClearValue(const char* value) = 0;
virtual void ClearValue(const char* key) = 0;
virtual bool Contains(const char* key) = 0;
virtual void SetThumbnail(const char *data, long size) = 0;
virtual void SetReplayGain(const ReplayGain& replayGain) = 0;
};

View File

@ -97,6 +97,10 @@ static inline std::wstring utf8to16(const char* utf8) {
}
#endif
static bool isValidYear(const std::string& year) {
return std::stoi(year) > 0;
}
static float toReplayGainFloat(const std::string& input) {
/* trim any trailing " db" or "db" noise... */
std::string lower = boost::algorithm::to_lower_copy(input);
@ -377,16 +381,25 @@ bool TaglibMetadataReader::ReadID3V2(const char* uri, ITagStore *track) {
/* year */
if (!allTags["TYER"].isEmpty()) { /* ID3v2.3*/
this->SetTagValue("year", allTags["TYER"].front()->toString().substr(0, 4), track);
if (!track->Contains("year") && !allTags["TYER"].isEmpty()) { /* ID3v2.3*/
auto year = allTags["TYER"].front()->toString().substr(0, 4);
if (isValidYear(year.to8Bit())) {
this->SetTagValue("year", year, track);
}
}
if (!allTags["TDRC"].isEmpty()) { /* ID3v2.4*/
this->SetTagValue("year", allTags["TDRC"].front()->toString().substr(0, 4), track);
if (!track->Contains("year") && !allTags["TDRC"].isEmpty()) { /* ID3v2.4*/
auto year = allTags["TDRC"].front()->toString().substr(0, 4);
if (isValidYear(year.to8Bit())) {
this->SetTagValue("year", year, track);
}
}
if (!allTags["TCOP"].isEmpty()) { /* ID3v2.3*/
this->SetTagValue("year", allTags["TCOP"].front()->toString().substr(0, 4), track);
if (!track->Contains("year") && !allTags["TCOP"].isEmpty()) { /* ID3v2.3*/
auto year = allTags["TCOP"].front()->toString().substr(0, 4);
if (isValidYear(year.to8Bit())) {
this->SetTagValue("year", year, track);
}
}
/* replay gain */