diff --git a/src/contrib/taglib_plugin/TaglibMetadataReader.cpp b/src/contrib/taglib_plugin/TaglibMetadataReader.cpp index c03901817..27d74489c 100644 --- a/src/contrib/taglib_plugin/TaglibMetadataReader.cpp +++ b/src/contrib/taglib_plugin/TaglibMetadataReader.cpp @@ -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); diff --git a/src/core/library/Indexer.cpp b/src/core/library/Indexer.cpp index 1ee6a63ee..90eccaeca 100644 --- a/src/core/library/Indexer.cpp +++ b/src/core/library/Indexer.cpp @@ -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"); diff --git a/src/core/library/LocalLibrary.cpp b/src/core/library/LocalLibrary.cpp index 8b350f433..51fabd623 100644 --- a/src/core/library/LocalLibrary.cpp +++ b/src/core/library/LocalLibrary.cpp @@ -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 ''," diff --git a/src/core/library/LocalLibraryConstants.h b/src/core/library/LocalLibraryConstants.h index 8b80e056f..7685465a0 100755 --- a/src/core/library/LocalLibraryConstants.h +++ b/src/core/library/LocalLibraryConstants.h @@ -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 { diff --git a/src/core/library/track/IndexerTrack.cpp b/src/core/library/track/IndexerTrack.cpp index 6d8cc7510..87cb0624e 100644 --- a/src/core/library/track/IndexerTrack.cpp +++ b/src/core/library/track/IndexerTrack.cpp @@ -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 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(); } diff --git a/src/core/library/track/IndexerTrack.h b/src/core/library/track/IndexerTrack.h index a986da28c..21780f8c3 100644 --- a/src/core/library/track/IndexerTrack.h +++ b/src/core/library/track/IndexerTrack.h @@ -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, diff --git a/src/musikbox/Main.cpp b/src/musikbox/Main.cpp index a7218b070..203d29a78 100644 --- a/src/musikbox/Main.cpp +++ b/src/musikbox/Main.cpp @@ -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 diff --git a/src/musikbox/app/util/GlobalHotkeys.cpp b/src/musikbox/app/util/GlobalHotkeys.cpp index 7bcae0ada..3be88d359 100755 --- a/src/musikbox/app/util/GlobalHotkeys.cpp +++ b/src/musikbox/app/util/GlobalHotkeys.cpp @@ -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; } \ No newline at end of file diff --git a/src/musikbox/app/util/GlobalHotkeys.h b/src/musikbox/app/util/GlobalHotkeys.h index c59816a30..b4c58a0ef 100755 --- a/src/musikbox/app/util/GlobalHotkeys.h +++ b/src/musikbox/app/util/GlobalHotkeys.h @@ -3,16 +3,19 @@ #include "stdafx.h" #include +#include 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; }; \ No newline at end of file