Added first-class support for album artist.

This commit is contained in:
casey 2016-05-21 00:00:58 -07:00
parent 6d025cdad1
commit af60dedb4b
9 changed files with 100 additions and 68 deletions

View File

@ -129,8 +129,7 @@ bool TaglibMetadataReader::GetGenericTag(const char* uri, musik::core::IMetadata
if (!file.isNull()) {
TagLib::Tag *tag = file.tag();
if(tag) {
if (tag) {
if (!tag->title().isEmpty()) {
this->SetTagValue("title", tag->title(), target);
}
@ -272,7 +271,7 @@ bool TaglibMetadataReader::GetID3v2Tag(const char* uri, musik::core::IMetadataWr
/* artists */
this->SetSlashSeparatedValues("artist" ,allTags["TPE1"], track);
this->SetSlashSeparatedValues("artist", allTags["TPE2"], track);
this->SetSlashSeparatedValues("album_artist", allTags["TPE2"], track);
this->SetSlashSeparatedValues("conductor", allTags["TPE3"], track);
this->SetSlashSeparatedValues("interpreted", allTags["TPE4"], track);

View File

@ -356,6 +356,8 @@ void Indexer::ThreadLoop() {
bool firstTime = true; /* through the loop */
while (!this->Exited()) {
this->restart = false;
Preferences prefs("Indexer");
if(!firstTime || (firstTime && prefs.GetBool("SyncOnStartup", true))) { /* first time through the loop skips this */
@ -444,27 +446,19 @@ void Indexer::SyncDelete() {
void Indexer::SyncCleanup() {
// Remove old artists
this->dbConnection.Execute("DELETE FROM track_artists WHERE track_id NOT IN (SELECT id FROM tracks)");
boost::thread::yield();
this->dbConnection.Execute("DELETE FROM artists WHERE id NOT IN (SELECT DISTINCT(visual_artist_id) FROM tracks) AND id NOT IN (SELECT DISTINCT(artist_id) FROM track_artists)");
boost::thread::yield();
this->dbConnection.Execute("DELETE FROM artists WHERE id NOT IN (SELECT DISTINCT(visual_artist_id) FROM tracks) AND id NOT IN (SELECT DISTINCT(album_artist_id) FROM tracks) AND id NOT IN (SELECT DISTINCT(artist_id) FROM track_artists)");
// Remove old genres
this->dbConnection.Execute("DELETE FROM track_genres WHERE track_id NOT IN (SELECT id FROM tracks)");
boost::thread::yield();
this->dbConnection.Execute("DELETE FROM genres WHERE id NOT IN (SELECT DISTINCT(visual_genre_id) FROM tracks) AND id NOT IN (SELECT DISTINCT(genre_id) FROM track_genres)");
boost::thread::yield();
// Remove old albums
this->dbConnection.Execute("DELETE FROM albums WHERE id NOT IN (SELECT DISTINCT(album_id) FROM tracks)");
boost::thread::yield();
// Remove metadata
this->dbConnection.Execute("DELETE FROM track_meta WHERE track_id NOT IN (SELECT id FROM tracks)");
boost::thread::yield();
this->dbConnection.Execute("DELETE FROM meta_values WHERE id NOT IN (SELECT DISTINCT(meta_value_id) FROM track_meta)");
boost::thread::yield();
this->dbConnection.Execute("DELETE FROM meta_keys WHERE id NOT IN (SELECT DISTINCT(meta_key_id) FROM meta_values)");
boost::thread::yield();
// ANALYZE
this->dbConnection.Execute("ANALYZE");

View File

@ -281,6 +281,7 @@ void LocalLibrary::CreateDatabase(db::Connection &db){
"year INTEGER DEFAULT 0,"
"visual_genre_id INTEGER DEFAULT 0,"
"visual_artist_id INTEGER DEFAULT 0,"
"album_artist_id INTEGER DEFAULT 0,"
"path_id INTEGER,"
"album_id INTEGER DEFAULT 0,"
"title TEXT default '',"

View File

@ -39,6 +39,7 @@
namespace musik { namespace core { namespace library { namespace constants {
namespace Track {
/* DB fields */
static const char* TABLE_NAME = "tracks";
static const char* ID = "id";
static const char* TRACK_NUM = "track";
@ -46,15 +47,22 @@ namespace musik { namespace core { namespace library { namespace constants {
static const char* DURATION = "duration";
static const char* FILESIZE = "filesize";
static const char* YEAR = "year";
static const char* GENRE_ID = "visual_genre_id";
static const char* ARTIST_ID = "visual_artist_id";
static const char* ALBUM_ID = "album_id";
static const char* PATH_ID = "path_id";
static const char* TITLE = "title";
static const char* FILENAME = "filename";
static const char* FILETIME = "filetime";
static const char* THUMBNAIL_ID = "thumbnail_id";
static const char* PATH = "path";
static const char* GENRE_ID = "visual_genre_id";
static const char* ARTIST_ID = "visual_artist_id";
static const char* ALBUM_ARTIST_ID = "album_artist_id";
static const char* ALBUM_ID = "album_id";
static const char* PATH_ID = "path_id";
/* used in Track instances where foreign key IDs have been
replaced with actual values... */
static const char* GENRE = "genre";
static const char* ARTIST = "artist";
static const char* ALBUM = "album";
static const char* ALBUM_ARTIST = "album_artist";
}
namespace Genres {

View File

@ -237,7 +237,7 @@ static void removeKnownFields(Track::MetadataMap& metadata) {
metadata.erase("album");
}
DBID IndexerTrack::ExtractThumbnail(db::Connection& connection, const std::string& libraryDirectory) {
DBID IndexerTrack::SaveThumbnail(db::Connection& connection, const std::string& libraryDirectory) {
DBID thumbnailId = 0;
if (this->internalMetadata->thumbnailData) {
@ -351,29 +351,40 @@ void IndexerTrack::ProcessNonStandardMetadata(db::Connection& connection) {
}
}
DBID IndexerTrack::ExtractAlbum(db::Connection& dbConnection) {
DBID albumId = 0;
db::CachedStatement stmt("SELECT id FROM albums WHERE name=?", dbConnection);
std::string album = this->GetValue("album");
stmt.BindText(0, album);
DBID IndexerTrack::SaveSingleValueField(
db::Connection& dbConnection,
const std::string& trackMetadataKeyName,
const std::string& fieldTableName)
{
DBID id = 0;
std::string selectQuery = boost::str(boost::format(
"SELECT id FROM %1% WHERE name=?") % fieldTableName);
db::CachedStatement stmt(selectQuery.c_str(), dbConnection);
std::string value = this->GetValue(trackMetadataKeyName.c_str());
stmt.BindText(0, value);
if (stmt.Step() == db::Row) {
albumId = stmt.ColumnInt(0);
id = stmt.ColumnInt(0);
}
else {
db::Statement insertAlbum("INSERT INTO albums (name) VALUES (?)", dbConnection);
insertAlbum.BindText(0,album);
if (insertAlbum.Step() == db::Done) {
albumId = dbConnection.LastInsertedId();
std::string insertStatement = boost::str(boost::format(
"INSERT INTO %1% (name) VALUES (?)") % fieldTableName);
db::Statement insertValue(insertStatement.c_str(), dbConnection);
insertValue.BindText(0, value);
if (insertValue.Step() == db::Done) {
id = dbConnection.LastInsertedId();
}
}
return albumId;
return id;
}
DBID IndexerTrack::ExtractAndSaveMultiValueField(
DBID IndexerTrack::SaveMultiValueField(
db::Connection& connection,
const std::string& tracksTableColumnName,
const std::string& fieldTableName,
@ -386,12 +397,12 @@ DBID IndexerTrack::ExtractAndSaveMultiValueField(
std::set<std::string> processed; /* for deduping */
MetadataIteratorRange genres = this->GetValues(tracksTableColumnName.c_str());
while (genres.first != genres.second) {
if (processed.find(genres.first->second) == processed.end()) {
processed.insert(genres.first->second);
MetadataIteratorRange values = this->GetValues(tracksTableColumnName.c_str());
while (values.first != values.second) {
if (processed.find(values.first->second) == processed.end()) {
processed.insert(values.first->second);
std::string value = genres.first->second;
std::string value = values.first->second;
fieldId = SaveNormalizedFieldValue(
connection,
@ -410,7 +421,7 @@ DBID IndexerTrack::ExtractAndSaveMultiValueField(
++count;
}
++genres.first;
++values.first;
}
if (count > 1 || fieldId == 0) {
@ -424,8 +435,8 @@ DBID IndexerTrack::ExtractAndSaveMultiValueField(
return fieldId;
}
DBID IndexerTrack::ExtractGenre(db::Connection& dbConnection) {
return this->ExtractAndSaveMultiValueField(
DBID IndexerTrack::SaveGenre(db::Connection& dbConnection) {
return this->SaveMultiValueField(
dbConnection,
GENRE_TRACK_COLUMN_NAME,
GENRES_TABLE_NAME,
@ -433,8 +444,8 @@ DBID IndexerTrack::ExtractGenre(db::Connection& dbConnection) {
GENRE_TRACK_FOREIGN_KEY);
}
DBID IndexerTrack::ExtractArtist(db::Connection& dbConnection) {
return this->ExtractAndSaveMultiValueField(
DBID IndexerTrack::SaveArtist(db::Connection& dbConnection) {
return this->SaveMultiValueField(
dbConnection,
ARTIST_TRACK_COLUMN_NAME,
ARTISTS_TABLE_NAME,
@ -445,6 +456,10 @@ DBID IndexerTrack::ExtractArtist(db::Connection& dbConnection) {
bool IndexerTrack::Save(db::Connection &dbConnection, std::string libraryDirectory) {
db::ScopedTransaction transaction(dbConnection);
if (this->GetValue("album_artist") == "") {
this->SetValue("album_artist", this->GetValue("artist").c_str());
}
/* remove existing relations -- we're going to update them with fresh data */
if (this->id != 0) {
@ -457,24 +472,26 @@ bool IndexerTrack::Save(db::Connection &dbConnection, std::string libraryDirecto
this->id = writeToTracksTable(dbConnection, *this);
DBID albumId = this->ExtractAlbum(dbConnection);
DBID genreId = this->ExtractGenre(dbConnection);
DBID artistId = this->ExtractArtist(dbConnection);
DBID thumbnailId = this->ExtractThumbnail(dbConnection, libraryDirectory);
DBID albumId = this->SaveSingleValueField(dbConnection, "album", "albums");
DBID genreId = this->SaveGenre(dbConnection);
DBID artistId = this->SaveArtist(dbConnection);
DBID albumArtistId = this->SaveSingleValueField(dbConnection, "album_artist", "artists");
DBID thumbnailId = this->SaveThumbnail(dbConnection, libraryDirectory);
/* update all of the track foreign keys */
{
db::CachedStatement stmt(
"UPDATE tracks " \
"SET album_id=?, visual_genre_id=?, visual_artist_id=?, thumbnail_id=? " \
"SET album_id=?, visual_genre_id=?, visual_artist_id=?, album_artist_id=?, thumbnail_id=? " \
"WHERE id=?", dbConnection);
stmt.BindInt(0, albumId);
stmt.BindInt(1, genreId);
stmt.BindInt(2, artistId);
stmt.BindInt(3, thumbnailId);
stmt.BindInt(4, this->id);
stmt.BindInt(3, albumArtistId);
stmt.BindInt(4, thumbnailId);
stmt.BindInt(5, this->id);
stmt.Step();
}

View File

@ -89,17 +89,24 @@ namespace musik { namespace core {
MetadataWithThumbnail *internalMetadata;
DBID ExtractThumbnail(
DBID SaveThumbnail(
db::Connection& connection,
const std::string& libraryDirectory);
DBID ExtractAlbum(db::Connection& connection);
DBID SaveAlbum(db::Connection& connection);
DBID ExtractGenre(db::Connection& connection);
DBID SaveGenre(db::Connection& connection);
DBID ExtractArtist(db::Connection& connection);
DBID SaveArtist(db::Connection& connection);
DBID ExtractAndSaveMultiValueField(
DBID SaveAlbumArtist(db::Connection& connection);
DBID SaveSingleValueField(
db::Connection& connection,
const std::string& trackMetadataKeyName,
const std::string& fieldTableName);
DBID SaveMultiValueField(
db::Connection& connection,
const std::string& tracksTableColumnName,
const std::string& fieldTableName,

View File

@ -174,7 +174,7 @@ int main(int argc, char* argv[])
using musik::core::LibraryFactory;
LibraryPtr library = LibraryFactory::Libraries().at(0);
GlobalHotkeys globalHotkeys(tp);
GlobalHotkeys globalHotkeys(tp, library);
ILayoutPtr libraryLayout(new LibraryLayout(tp, library));
ILayoutPtr consoleLayout(new MainLayout(tp, library));
@ -207,16 +207,14 @@ int main(int argc, char* argv[])
else if (kn == "^D") { /* ctrl+d quits */
quit = true;
}
else if (ch == KEY_F(1)) {
changeLayout(state, libraryLayout);
}
else if (ch == KEY_F(8)) {
changeLayout(state, consoleLayout);
}
else if (!globalHotkeys.Handle(ch)) {
if (ch >= KEY_F(0) && ch <= KEY_F(12)) {
if (ch == KEY_F(8)) {
changeLayout(state, consoleLayout);
}
else if (ch == KEY_F(1)) {
changeLayout(state, libraryLayout);
}
}
else if (state.input) {
if (state.input) {
state.input->WriteChar(ch);
}
/* otherwise, send the unhandled keypress directly to the

View File

@ -1,9 +1,11 @@
#include "stdafx.h"
#include "GlobalHotkeys.h"
GlobalHotkeys::GlobalHotkeys(Transport& transport)
: transport(transport) {
using musik::core::LibraryPtr;
GlobalHotkeys::GlobalHotkeys(Transport& transport, LibraryPtr library)
: transport(transport) {
this->library = library;
}
GlobalHotkeys::~GlobalHotkeys() {
@ -30,6 +32,9 @@ bool GlobalHotkeys::Handle(int64 ch) {
this->transport.SetVolume(this->transport.Volume() - 0.05);
return true;
}
else if (ch == KEY_F(5)) {
library->Indexer()->Synchronize(true);
}
return false;
}

View File

@ -3,16 +3,19 @@
#include "stdafx.h"
#include <core/playback/Transport.h>
#include <core/library/ILibrary.h>
using musik::core::audio::Transport;
using musik::core::LibraryPtr;
class GlobalHotkeys {
public:
GlobalHotkeys(Transport& transport);
GlobalHotkeys(Transport& transport, LibraryPtr library);
~GlobalHotkeys(); /* non-virtual; do not use as a base class */
bool Handle(int64 ch);
private:
Transport& transport;
LibraryPtr library;
};