- Killed a bunch of really unnecessary, complicated path management

logic in the Indexer.
- Created a new IKeyHandler for windows that want to receive keyboard
  events but aren't necessarily input fields.
- Added some hacked up code to get playback working in the
  TrackListView. This functionality will be extracted to LibraryLayout
  in the near future.
This commit is contained in:
casey 2016-05-16 22:00:45 -07:00
parent 1c13ac278d
commit fc86bacaaf
19 changed files with 151 additions and 267 deletions

View File

@ -215,7 +215,7 @@ void Indexer::SynchronizeInternal() {
for(std::size_t i = 0; i < paths.size(); ++i) { for(std::size_t i = 0; i < paths.size(); ++i) {
std::string path = paths[i]; std::string path = paths[i];
this->SyncDirectory(path, path ,0, pathIds[i]); this->SyncDirectory(path, path, pathIds[i]);
} }
/* remove undesired entries from db (files themselves will remain) */ /* remove undesired entries from db (files themselves will remain) */
@ -228,7 +228,7 @@ void Indexer::SynchronizeInternal() {
} }
if (!this->Restarted() && !this->Exited()) { if (!this->Restarted() && !this->Exited()) {
this->SyncDelete(pathIds); this->SyncDelete();
} }
/* cleanup -- remove stale artists, albums, genres, etc */ /* cleanup -- remove stale artists, albums, genres, etc */
@ -270,26 +270,9 @@ void Indexer::SynchronizeInternal() {
musik::debug::info(TAG, "done!"); musik::debug::info(TAG, "done!");
} }
//////////////////////////////////////////
///\brief
///Reads all tracks in a folder
///
///\param dir
///Folder to check files in
///
///\param parentDirId
///Database ID of the parent folder (folders table)
///
///\param pathId
///Database ID of the current path (paths table)
///
///Read all tracks in a folder. All folders in that folder is recursively called.
//////////////////////////////////////////
void Indexer::SyncDirectory( void Indexer::SyncDirectory(
const std::string &syncRoot, const std::string &syncRoot,
const std::string &currentPath, const std::string &currentPath,
DBID parentDirId,
DBID pathId) DBID pathId)
{ {
if (this->Exited() || this->Restarted()) { if (this->Exited() || this->Restarted()) {
@ -300,39 +283,6 @@ void Indexer::SyncDirectory(
std::string normalizedCurrentPath = normalizeDir(currentPath); std::string normalizedCurrentPath = normalizeDir(currentPath);
std::string leaf = boost::filesystem::path(currentPath).leaf().string(); /* trailing subdir in currentPath */ std::string leaf = boost::filesystem::path(currentPath).leaf().string(); /* trailing subdir in currentPath */
DBID dirId = 0;
/* get relative folder id */
{
db::CachedStatement stmt("SELECT id FROM folders WHERE name=? AND path_id=? AND parent_id=?", this->dbConnection);
stmt.BindText(0, leaf);
stmt.BindInt(1, pathId);
stmt.BindInt(2, parentDirId);
if (stmt.Step() == db::Row) {
dirId = stmt.ColumnInt(0);
}
}
/* no ID yet? needs to be inserted... */
if (dirId == 0) {
std::string relativePath(normalizedCurrentPath.substr(normalizedSyncRoot.size()));
db::CachedStatement stmt("INSERT INTO folders (name, path_id, parent_id, relative_path) VALUES(?,?,?,?)", this->dbConnection);
stmt.BindText(0, leaf);
stmt.BindInt(1, pathId);
stmt.BindInt(2, parentDirId);
stmt.BindText(3, relativePath);
if (stmt.Step() != db::Done) {
return; /* ugh, failed? */
}
dirId = this->dbConnection.LastInsertedId();
}
/* start recursive filesystem scan */ /* start recursive filesystem scan */
try { /* boost::filesystem may throw */ try { /* boost::filesystem may throw */
@ -343,11 +293,13 @@ void Indexer::SyncDirectory(
boost::filesystem::directory_iterator end; boost::filesystem::directory_iterator end;
boost::filesystem::directory_iterator file(path); boost::filesystem::directory_iterator file(path);
std::string pathIdStr = boost::lexical_cast<std::string>(pathId);
for( ; file != end && !this->Exited() && !this->Restarted(); file++) { for( ; file != end && !this->Exited() && !this->Restarted(); file++) {
if (is_directory(file->status())) { if (is_directory(file->status())) {
/* recursion here */ /* recursion here */
musik::debug::info(TAG, "scanning " + file->path().string()); musik::debug::info(TAG, "scanning " + file->path().string());
this->SyncDirectory(syncRoot, file->path().string(), dirId, pathId); this->SyncDirectory(syncRoot, file->path().string(), pathId);
} }
else { else {
++this->filesIndexed; ++this->filesIndexed;
@ -355,7 +307,7 @@ void Indexer::SyncDirectory(
musik::core::IndexerTrack track(0); musik::core::IndexerTrack track(0);
/* get cached filesize, parts, size, etc */ /* get cached filesize, parts, size, etc */
if (track.NeedsToBeIndexed(file->path(), this->dbConnection, dirId)) { if (track.NeedsToBeIndexed(file->path(), this->dbConnection)) {
bool saveToDb = false; bool saveToDb = false;
/* read the tag from the plugin */ /* read the tag from the plugin */
@ -373,7 +325,8 @@ void Indexer::SyncDirectory(
/* write it to the db, if read successfully */ /* write it to the db, if read successfully */
if (saveToDb) { if (saveToDb) {
track.Save(this->dbConnection, this->libraryPath, dirId); track.SetValue("path_id", pathIdStr.c_str());
track.Save(this->dbConnection, this->libraryPath);
this->filesSaved++; this->filesSaved++;
if (this->filesSaved % 100 == 0) { if (this->filesSaved % 100 == 0) {
@ -443,133 +396,38 @@ void Indexer::ThreadLoop() {
} }
} }
////////////////////////////////////////// void Indexer::SyncDelete() {
///\brief /* remove all tracks that no longer reference a valid path entry */
///Part of the synchronizer to delete removed files.
///
///\param paths
///Vector of path-id to check for deletion
///
///This method will first check for removed folders and automaticaly
///delete all tracks in the removed folders.
///Secondly it will check all files it they are removed.
///
///\remarks
///This method will not delete related information (meta-data, albums, etc)
//////////////////////////////////////////
void Indexer::SyncDelete(const std::vector<DBID>& paths) {
/* delete pruned paths from path table */
this->dbConnection.Execute("DELETE FROM folders WHERE path_id NOT IN (SELECT id FROM paths)");
db::Statement stmtSyncPath("SELECT p.path FROM paths p WHERE p.id=?", this->dbConnection); this->dbConnection.Execute("DELETE FROM tracks WHERE path_id NOT IN (SELECT id FROM paths)");
{ /* remove files that are no longer on the filesystem. */
db::Statement stmt(
"SELECT f.id, p.path || f.relative_path "
"FROM folders f, paths p "
"WHERE f.path_id=p.id AND p.id=?", this->dbConnection);
db::Statement stmtRemove("DELETE FROM folders WHERE id=?",this->dbConnection);
for (std::size_t i = 0; i < paths.size(); ++i) {
stmt.BindInt(0, paths[i]);
stmtSyncPath.BindInt(0, paths[i]);
stmtSyncPath.Step();
std::string syncPathString(stmtSyncPath.ColumnText(0));
stmtSyncPath.Reset();
while (stmt.Step() == db::Row && !this->Exited() && !this->Restarted()) {
bool remove = true;
std::string dir = stmt.ColumnText(1);
try {
boost::filesystem::path path(dir);
if (boost::filesystem::exists(path)) {
remove = false;
}
else {
boost::filesystem::path syncPath(syncPathString);
if (!boost::filesystem::exists(syncPath)) {
remove = false;
}
}
}
catch (...){
remove = false;
}
if (remove) {
stmtRemove.BindInt(0, stmt.ColumnInt(0));
stmtRemove.Step();
stmtRemove.Reset();
}
}
stmt.Reset();
}
}
/* we deleted folders above. remove all tracks that were in those dirs*/
this->dbConnection.Execute("DELETE FROM tracks WHERE folder_id NOT IN (SELECT id FROM folders)");
// Remove tracks
db::Statement stmtCount("SELECT count(*) FROM tracks", this->dbConnection);
DBID songs = 0, count = 0;
if (stmtCount.Step() == db::Row) {
songs = stmtCount.ColumnInt(0);
}
db::Statement stmt(
"SELECT t.id, p.path || f.relative_path || '/' || t.filename "
"FROM tracks t, folders f, paths p "
"WHERE t.folder_id=f.id AND f.path_id=p.id AND p.id=?", this->dbConnection);
db::Statement stmtRemove("DELETE FROM tracks WHERE id=?", this->dbConnection); db::Statement stmtRemove("DELETE FROM tracks WHERE id=?", this->dbConnection);
for (std::size_t i = 0; i < paths.size(); ++i) { db::Statement allTracks(
stmt.BindInt(0, paths[i]); "SELECT t.id, t.filename "
"FROM tracks t "
"WHERE p.id=?", this->dbConnection);
stmtSyncPath.BindInt(0,paths[i]); while(allTracks.Step() == db::Row && !this->Exited() && !this->Restarted()) {
stmtSyncPath.Step(); bool remove = false;
std::string file = allTracks.ColumnText(1);
std::string syncPathString = stmtSyncPath.ColumnText(0);
stmtSyncPath.Reset(); try {
boost::filesystem::path file(file);
while(stmt.Step() == db::Row && !this->Exited() && !this->Restarted()) { if (!boost::filesystem::exists(file)) {
bool remove = true; remove = true;
std::string file = stmt.ColumnText(1);
try{
boost::filesystem::path file(file);
if (boost::filesystem::exists(file)) {
remove = false;
}
else {
boost::filesystem::path syncPath(syncPathString);
if (!boost::filesystem::exists(syncPath)) {
remove = false;
}
}
} }
catch(...) {
remove = false;
}
if (remove) {
stmtRemove.BindInt(0, stmt.ColumnInt(0));
stmtRemove.Step();
stmtRemove.Reset();
}
++count;
} }
catch(...) {
stmt.Reset(); }
if (remove) {
stmtRemove.BindInt(0, allTracks.ColumnInt(0));
stmtRemove.Step();
stmtRemove.Reset();
}
} }
} }
@ -694,8 +552,7 @@ void Indexer::SyncOptimize() {
db::Statement outer("SELECT t.id FROM tracks t " \ db::Statement outer("SELECT t.id FROM tracks t " \
"LEFT OUTER JOIN artists ar ON ar.id=t.visual_artist_id " \ "LEFT OUTER JOIN artists ar ON ar.id=t.visual_artist_id " \
"LEFT OUTER JOIN albums al ON al.id=t.album_id " \ "LEFT OUTER JOIN albums al ON al.id=t.album_id " \
"LEFT OUTER JOIN folders f ON f.id=t.folder_id " \ "ORDER BY ar.sort_order, al.sort_order, t.track, t.filename",
"ORDER BY ar.sort_order, al.sort_order, t.track, f.relative_path, t.filename",
this->dbConnection); this->dbConnection);
db::Statement inner("UPDATE tracks SET sort_order1=? WHERE id=?",this->dbConnection); db::Statement inner("UPDATE tracks SET sort_order1=? WHERE id=?",this->dbConnection);
@ -745,8 +602,6 @@ void Indexer::ProcessAddRemoveQueue() {
this->addRemoveQueue.pop_front(); this->addRemoveQueue.pop_front();
} }
this->dbConnection.Execute("DELETE FROM folders WHERE path_id NOT IN (SELECT id FROM paths)");
this->PathsUpdated(); this->PathsUpdated();
} }
@ -775,17 +630,15 @@ void Indexer::RunAnalyzers() {
/* for each track... */ /* for each track... */
DBID trackId = 0; DBID trackId = 0;
DBID folderId = 0;
db::Statement getNextTrack( db::Statement getNextTrack(
"SELECT id, folder_id FROM tracks WHERE id>? ORDER BY id LIMIT 1", "SELECT id FROM tracks WHERE id>? ORDER BY id LIMIT 1",
this->dbConnection); this->dbConnection);
getNextTrack.BindInt(0, trackId); getNextTrack.BindInt(0, trackId);
while(getNextTrack.Step() == db::Row ) { while(getNextTrack.Step() == db::Row ) {
trackId = getNextTrack.ColumnInt(0); trackId = getNextTrack.ColumnInt(0);
folderId = getNextTrack.ColumnInt(1);
getNextTrack.Reset(); getNextTrack.Reset();
getNextTrack.UnBindAll(); getNextTrack.UnBindAll();
@ -838,7 +691,7 @@ void Indexer::RunAnalyzers() {
completed successfully, then save the track. */ completed successfully, then save the track. */
if(successPlugins>0) { if(successPlugins>0) {
track.Save(this->dbConnection, this->libraryPath,folderId); track.Save(this->dbConnection, this->libraryPath);
} }
} }
} }

View File

@ -76,7 +76,7 @@ namespace musik { namespace core {
bool Restarted(); bool Restarted();
void SyncDelete(const std::vector<DBID>& paths); void SyncDelete();
void SyncCleanup(); void SyncCleanup();
void ProcessAddRemoveQueue(); void ProcessAddRemoveQueue();
void SyncOptimize(); void SyncOptimize();
@ -87,7 +87,6 @@ namespace musik { namespace core {
void SyncDirectory( void SyncDirectory(
const std::string& syncRoot, const std::string& syncRoot,
const std::string& currentPath, const std::string& currentPath,
DBID parentDirId,
DBID pathId); DBID pathId);
db::Connection dbConnection; db::Connection dbConnection;

View File

@ -234,7 +234,6 @@ bool LocalLibrary::IsSpecialMTOMetaKey(std::string &metakey){
specialMTOMetaKeys.insert("album"); specialMTOMetaKeys.insert("album");
specialMTOMetaKeys.insert("visual_genre"); specialMTOMetaKeys.insert("visual_genre");
specialMTOMetaKeys.insert("visual_artist"); specialMTOMetaKeys.insert("visual_artist");
specialMTOMetaKeys.insert("folder");
} }
return specialMTOMetaKeys.find(metakey)!=specialMTOMetaKeys.end(); return specialMTOMetaKeys.find(metakey)!=specialMTOMetaKeys.end();
@ -272,8 +271,8 @@ void LocalLibrary::CreateDatabase(db::Connection &db){
"year INTEGER DEFAULT 0," "year INTEGER DEFAULT 0,"
"visual_genre_id INTEGER DEFAULT 0," "visual_genre_id INTEGER DEFAULT 0,"
"visual_artist_id INTEGER DEFAULT 0," "visual_artist_id INTEGER DEFAULT 0,"
"path_id INTEGER,"
"album_id INTEGER DEFAULT 0," "album_id INTEGER DEFAULT 0,"
"folder_id INTEGER DEFAULT 0,"
"title TEXT default ''," "title TEXT default '',"
"filename TEXT default ''," "filename TEXT default '',"
"filetime INTEGER DEFAULT 0," "filetime INTEGER DEFAULT 0,"
@ -333,16 +332,7 @@ void LocalLibrary::CreateDatabase(db::Connection &db){
"path TEXT default ''" "path TEXT default ''"
")"); ")");
// Create the folders-table // Create the thumbnails table
db.Execute("CREATE TABLE IF NOT EXISTS folders ("
"id INTEGER PRIMARY KEY AUTOINCREMENT,"
"name TEXT default '',"
"relative_path TEXT default '',"
"parent_id INTEGER DEFAULT 0,"
"path_id INTEGER DEFAULT 0"
")");
// Create the folders-table
db.Execute("CREATE TABLE IF NOT EXISTS thumbnails (" db.Execute("CREATE TABLE IF NOT EXISTS thumbnails ("
"id INTEGER PRIMARY KEY AUTOINCREMENT," "id INTEGER PRIMARY KEY AUTOINCREMENT,"
"filename TEXT default ''," "filename TEXT default '',"
@ -350,13 +340,14 @@ void LocalLibrary::CreateDatabase(db::Connection &db){
"checksum INTEGER DEFAULT 0" "checksum INTEGER DEFAULT 0"
")"); ")");
// Create the playlists-table // Create the playlists
db.Execute("CREATE TABLE IF NOT EXISTS playlists (" db.Execute("CREATE TABLE IF NOT EXISTS playlists ("
"id INTEGER PRIMARY KEY AUTOINCREMENT," "id INTEGER PRIMARY KEY AUTOINCREMENT,"
"name TEXT default ''," "name TEXT default '',"
"user_id INTEGER default 0" "user_id INTEGER default 0"
")"); ")");
// Create the playlists-table
// Create the playlist_tracks table
db.Execute("CREATE TABLE IF NOT EXISTS playlist_tracks (" db.Execute("CREATE TABLE IF NOT EXISTS playlist_tracks ("
"track_id INTEGER DEFAULT 0," "track_id INTEGER DEFAULT 0,"
"playlist_id INTEGER DEFAULT 0," "playlist_id INTEGER DEFAULT 0,"
@ -365,13 +356,11 @@ void LocalLibrary::CreateDatabase(db::Connection &db){
// INDEXES // INDEXES
db.Execute("CREATE UNIQUE INDEX IF NOT EXISTS users_index ON users (login)"); db.Execute("CREATE UNIQUE INDEX IF NOT EXISTS users_index ON users (login)");
db.Execute("CREATE UNIQUE INDEX IF NOT EXISTS folders_index ON folders (name,parent_id,path_id)");
db.Execute("CREATE UNIQUE INDEX IF NOT EXISTS paths_index ON paths (path)"); db.Execute("CREATE UNIQUE INDEX IF NOT EXISTS paths_index ON paths (path)");
db.Execute("CREATE INDEX IF NOT EXISTS genre_index ON genres (sort_order)"); db.Execute("CREATE INDEX IF NOT EXISTS genre_index ON genres (sort_order)");
db.Execute("CREATE INDEX IF NOT EXISTS artist_index ON artists (sort_order)"); db.Execute("CREATE INDEX IF NOT EXISTS artist_index ON artists (sort_order)");
db.Execute("CREATE INDEX IF NOT EXISTS album_index ON albums (sort_order)"); db.Execute("CREATE INDEX IF NOT EXISTS album_index ON albums (sort_order)");
db.Execute("CREATE INDEX IF NOT EXISTS track_index1 ON tracks (album_id,sort_order1)"); db.Execute("CREATE INDEX IF NOT EXISTS track_index1 ON tracks (album_id,sort_order1)");
db.Execute("CREATE INDEX IF NOT EXISTS track_index7 ON tracks (folder_id)");
db.Execute("CREATE INDEX IF NOT EXISTS thumbnail_index ON thumbnails (filesize)"); db.Execute("CREATE INDEX IF NOT EXISTS thumbnail_index ON thumbnails (filesize)");
db.Execute("CREATE INDEX IF NOT EXISTS trackgenre_index1 ON track_genres (track_id,genre_id)"); db.Execute("CREATE INDEX IF NOT EXISTS trackgenre_index1 ON track_genres (track_id,genre_id)");

View File

@ -49,13 +49,13 @@ namespace musik { namespace core { namespace library { namespace constants {
static const char* DISPLAY_GENRE_ID = "visual_genre_id"; static const char* DISPLAY_GENRE_ID = "visual_genre_id";
static const char* DISPLAY_ARTIST_ID = "visual_artist_id"; static const char* DISPLAY_ARTIST_ID = "visual_artist_id";
static const char* ALBUM_ID = "album_id"; static const char* ALBUM_ID = "album_id";
static const char* RELATIVE_PATH_ID = "folder_id"; static const char* PATH_ID = "path_id";
static const char* TITLE = "title"; static const char* TITLE = "title";
static const char* FILENAME = "filename"; static const char* FILENAME = "filename";
static const char* FILETIME = "filetime"; static const char* FILETIME = "filetime";
static const char* THUMBNAIL_ID = "thumbnail_id"; static const char* THUMBNAIL_ID = "thumbnail_id";
static const char* SORT_ORDER = "sort_order1"; static const char* SORT_ORDER = "sort_order1";
static const char* PATH = "path"; /* not in DB; synthesized */ static const char* PATH = "path";
} }
namespace Genres { namespace Genres {
@ -123,15 +123,6 @@ namespace musik { namespace core { namespace library { namespace constants {
static const char* PATH = "path"; static const char* PATH = "path";
} }
namespace RelativePaths {
static const char* TABLE_NAME = "folders";
static const char* ID = "id";
static const char* NAME = "name";
static const char* RELATIVE_PATH = "relative_path";
static const char* PARENT_ID = "parent_id";
static const char* PATH_ID = "path_id";
}
namespace Thumbnails { namespace Thumbnails {
static const char* TABLE_NAME = "thumbnails"; static const char* TABLE_NAME = "thumbnails";
static const char* ID = "id"; static const char* ID = "id";

View File

@ -137,12 +137,11 @@ DBID IndexerTrack::Id() {
bool IndexerTrack::NeedsToBeIndexed( bool IndexerTrack::NeedsToBeIndexed(
const boost::filesystem::path &file, const boost::filesystem::path &file,
db::Connection &dbConnection, db::Connection &dbConnection)
DBID currentFolderId)
{ {
try { try {
this->SetValue("path", file.string().c_str()); this->SetValue("path", file.string().c_str());
this->SetValue("filename", file.leaf().string().c_str()); this->SetValue("filename", file.string().c_str());
size_t lastDot = file.leaf().string().find_last_of("."); size_t lastDot = file.leaf().string().find_last_of(".");
if (lastDot != std::string::npos){ if (lastDot != std::string::npos){
@ -158,10 +157,9 @@ bool IndexerTrack::NeedsToBeIndexed(
db::CachedStatement stmt( db::CachedStatement stmt(
"SELECT id, filename, filesize, filetime " \ "SELECT id, filename, filesize, filetime " \
"FROM tracks t " \ "FROM tracks t " \
"WHERE folder_id=? AND filename=?", dbConnection); "WHERE filename=?", dbConnection);
stmt.BindInt(0, currentFolderId); stmt.BindText(0, this->GetValue("filename"));
stmt.BindText(1, this->GetValue("filename"));
bool fileDifferent = true; bool fileDifferent = true;
@ -184,11 +182,10 @@ bool IndexerTrack::NeedsToBeIndexed(
static DBID writeToTracksTable( static DBID writeToTracksTable(
db::Connection &dbConnection, db::Connection &dbConnection,
IndexerTrack& track, IndexerTrack& track,
DBID folderId,
DBID tempSortOrder) DBID tempSortOrder)
{ {
db::CachedStatement stmt("INSERT OR REPLACE INTO tracks " \ db::CachedStatement stmt("INSERT OR REPLACE INTO tracks " \
"(id, track, bpm, duration, filesize, year, folder_id, title, filename, filetime, sort_order1) " \ "(id, track, bpm, duration, filesize, year, title, filename, filetime, path_id, sort_order1) " \
"VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", dbConnection); "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", dbConnection);
stmt.BindText(1, track.GetValue("track")); stmt.BindText(1, track.GetValue("track"));
@ -196,10 +193,10 @@ static DBID writeToTracksTable(
stmt.BindText(3, track.GetValue("duration")); stmt.BindText(3, track.GetValue("duration"));
stmt.BindText(4, track.GetValue("filesize")); stmt.BindText(4, track.GetValue("filesize"));
stmt.BindText(5, track.GetValue("year")); stmt.BindText(5, track.GetValue("year"));
stmt.BindInt(6, folderId); stmt.BindText(6, track.GetValue("title"));
stmt.BindText(7, track.GetValue("title")); stmt.BindText(7, track.GetValue("filename"));
stmt.BindText(8, track.GetValue("filename")); stmt.BindText(8, track.GetValue("filetime"));
stmt.BindText(9, track.GetValue("filetime")); stmt.BindText(9, track.GetValue("path_id"));
stmt.BindInt(10, tempSortOrder); stmt.BindInt(10, tempSortOrder);
if (track.Id() != 0) { if (track.Id() != 0) {
@ -448,7 +445,7 @@ DBID IndexerTrack::ExtractArtist(db::Connection& dbConnection) {
ARTIST_TRACK_FOREIGN_KEY); ARTIST_TRACK_FOREIGN_KEY);
} }
bool IndexerTrack::Save(db::Connection &dbConnection, std::string libraryDirectory, DBID folderId) { bool IndexerTrack::Save(db::Connection &dbConnection, std::string libraryDirectory) {
db::ScopedTransaction transaction(dbConnection); db::ScopedTransaction transaction(dbConnection);
/* remove existing relations -- we're going to update them with fresh data */ /* remove existing relations -- we're going to update them with fresh data */
@ -461,7 +458,7 @@ bool IndexerTrack::Save(db::Connection &dbConnection, std::string libraryDirecto
/* write generic info to the tracks table */ /* write generic info to the tracks table */
this->id = writeToTracksTable(dbConnection, *this, folderId, tempSortOrder); this->id = writeToTracksTable(dbConnection, *this, tempSortOrder);
DBID albumId = this->ExtractAlbum(dbConnection); DBID albumId = this->ExtractAlbum(dbConnection);
DBID genreId = this->ExtractGenre(dbConnection); DBID genreId = this->ExtractGenre(dbConnection);
@ -567,9 +564,9 @@ bool IndexerTrack::Reload(db::Connection &db) {
"ORDER BY tm.id", db); "ORDER BY tm.id", db);
db::Statement track( db::Statement track(
"SELECT t.track, t.bpm, t.duration, t.filesize, t.year, t.title, t.filename, t.thumbnail_id, p.path|| f.relative_path || '/' || t.filename, al.name, t.filetime, t.sort_order1 " \ "SELECT t.track, t.bpm, t.duration, t.filesize, t.year, t.title, t.filename, t.thumbnail_id, al.name, t.filetime, t.sort_order1 " \
"FROM tracks t, folders f, paths p, albums al " \ "FROM tracks t, paths p, albums al " \
"WHERE t.id=? AND t.folder_id=f.id AND f.path_id=p.id AND t.album_id=al.id", db); "WHERE t.id=? AND t.album_id=al.id", db);
track.BindInt(0, this->id); track.BindInt(0, this->id);
if(track.Step() == db::Row) { if(track.Step() == db::Row) {
@ -581,10 +578,9 @@ bool IndexerTrack::Reload(db::Connection &db) {
this->SetValue("title", track.ColumnText(5)); this->SetValue("title", track.ColumnText(5));
this->SetValue("filename", track.ColumnText(6)); this->SetValue("filename", track.ColumnText(6));
this->SetValue("thumbnail_id", track.ColumnText(7)); this->SetValue("thumbnail_id", track.ColumnText(7));
this->SetValue("path", track.ColumnText(8)); this->SetValue("album", track.ColumnText(8));
this->SetValue("album", track.ColumnText(9)); this->SetValue("filetime", track.ColumnText(9));
this->SetValue("filetime", track.ColumnText(10)); this->tempSortOrder = track.ColumnInt(10);
this->tempSortOrder = track.ColumnInt(11);
genres.BindInt(0, this->id); genres.BindInt(0, this->id);
while (genres.Step() == db::Row) { while (genres.Step() == db::Row) {
@ -592,7 +588,7 @@ bool IndexerTrack::Reload(db::Connection &db) {
} }
artists.BindInt(0, this->id); artists.BindInt(0, this->id);
while (artists.Step()==db::Row) { while (artists.Step() == db::Row) {
this->SetValue("artist", artists.ColumnText(0)); this->SetValue("artist", artists.ColumnText(0));
} }

View File

@ -66,13 +66,11 @@ namespace musik { namespace core {
bool NeedsToBeIndexed( bool NeedsToBeIndexed(
const boost::filesystem::path &file, const boost::filesystem::path &file,
db::Connection &dbConnection, db::Connection &dbConnection);
DBID currentFolderId);
bool Save( bool Save(
db::Connection &dbConnection, db::Connection &dbConnection,
std::string libraryDirectory, std::string libraryDirectory);
DBID folderId);
bool Reload(db::Connection &db); bool Reload(db::Connection &db);

View File

@ -197,5 +197,4 @@ LibraryTrack::MetaData::MetaData()
LibraryTrack::MetaData::~MetaData() { LibraryTrack::MetaData::~MetaData() {
delete this->thumbnailData; delete this->thumbnailData;
} }

View File

@ -39,6 +39,7 @@
#include <core/config.h> #include <core/config.h>
#include <core/library/track/Track.h> #include <core/library/track/Track.h>
#include <core/library/LocalLibrary.h> #include <core/library/LocalLibrary.h>
#include <core/db/Connection.h>
namespace musik { namespace core { namespace http { namespace musik { namespace core { namespace http {
class Responder; class Responder;
@ -63,9 +64,9 @@ namespace musik { namespace core {
virtual DBID Id(); virtual DBID Id();
virtual std::string GetValue(const char* metakey); virtual std::string GetValue(const char* metakey);
virtual void SetValue(const char* metakey,const char* value); virtual void SetValue(const char* metakey, const char* value);
virtual void ClearValue(const char* metakey); virtual void ClearValue(const char* metakey);
virtual void SetThumbnail(const char *data,long size); virtual void SetThumbnail(const char *data, long size);
virtual std::string URI(); virtual std::string URI();
virtual std::string URL(); virtual std::string URL();

8
src/musikbox/IKeyHandler.h Executable file
View File

@ -0,0 +1,8 @@
#pragma once
#include "stdafx.h"
class IKeyHandler {
public:
virtual void KeyPress(int64 ch) = 0;
};

View File

@ -1,7 +1,6 @@
#pragma once #pragma once
#include "stdafx.h" #include "stdafx.h"
#include <boost/shared_ptr.hpp>
class IScrollAdapter { class IScrollAdapter {
public: public:

View File

@ -3,8 +3,11 @@
#include "Screen.h" #include "Screen.h"
#include "LibraryLayout.h" #include "LibraryLayout.h"
LibraryLayout::LibraryLayout(LibraryPtr library) #define CATEGORY_WIDTH 25
LibraryLayout::LibraryLayout(Transport& transport, LibraryPtr library)
: LayoutBase() { : LayoutBase() {
this->transport = &transport;
this->library = library; this->library = library;
this->InitializeWindows(); this->InitializeWindows();
} }
@ -18,17 +21,17 @@ void LibraryLayout::Layout() {
this->SetPosition(0, 0); this->SetPosition(0, 0);
this->albumList->SetPosition(0, 0); this->albumList->SetPosition(0, 0);
this->albumList->SetSize(20, this->GetHeight()); this->albumList->SetSize(CATEGORY_WIDTH, this->GetHeight());
this->albumList->SetFocusOrder(0); this->albumList->SetFocusOrder(0);
this->trackList->SetPosition(20, 0); this->trackList->SetPosition(CATEGORY_WIDTH, 0);
this->trackList->SetSize(this->GetWidth() - 20, this->GetHeight()); this->trackList->SetSize(this->GetWidth() - CATEGORY_WIDTH, this->GetHeight());
this->trackList->SetFocusOrder(1); this->trackList->SetFocusOrder(1);
} }
void LibraryLayout::InitializeWindows() { void LibraryLayout::InitializeWindows() {
this->albumList.reset(new CategoryListView(library)); this->albumList.reset(new CategoryListView(this->library));
this->trackList.reset(new TrackListView(library)); this->trackList.reset(new TrackListView(*this->transport, this->library));
this->AddWindow(this->albumList); this->AddWindow(this->albumList);
this->AddWindow(this->trackList); this->AddWindow(this->trackList);

View File

@ -4,15 +4,17 @@
#include "CategoryListView.h" #include "CategoryListView.h"
#include "TrackListView.h" #include "TrackListView.h"
#include <core/playback/Transport.h>
#include <core/library/ILibrary.h> #include <core/library/ILibrary.h>
#include <sigslot/sigslot.h> #include <sigslot/sigslot.h>
using musik::core::LibraryPtr; using musik::core::LibraryPtr;
using musik::core::audio::Transport;
class LibraryLayout : public LayoutBase, public sigslot::has_slots<> { class LibraryLayout : public LayoutBase, public sigslot::has_slots<> {
public: public:
LibraryLayout(LibraryPtr library); LibraryLayout(Transport& transport, LibraryPtr library);
virtual ~LibraryLayout(); virtual ~LibraryLayout();
virtual void Layout(); virtual void Layout();
@ -24,6 +26,7 @@ class LibraryLayout : public LayoutBase, public sigslot::has_slots<> {
void OnCategoryViewSelectionChanged( void OnCategoryViewSelectionChanged(
ListWindow *view, size_t newIndex, size_t oldIndex); ListWindow *view, size_t newIndex, size_t oldIndex);
Transport* transport;
LibraryPtr library; LibraryPtr library;
std::shared_ptr<CategoryListView> albumList; std::shared_ptr<CategoryListView> albumList;
std::shared_ptr<TrackListView> trackList; std::shared_ptr<TrackListView> trackList;

View File

@ -42,6 +42,7 @@
#include "MainLayout.h" #include "MainLayout.h"
#include "LibraryLayout.h" #include "LibraryLayout.h"
#include "IInput.h" #include "IInput.h"
#include "IKeyHandler.h"
#include "WindowMessageQueue.h" #include "WindowMessageQueue.h"
#include <boost/locale.hpp> #include <boost/locale.hpp>
@ -60,6 +61,7 @@ struct WindowState {
ILayout* layout; ILayout* layout;
IWindow* focused; IWindow* focused;
IInput* input; IInput* input;
IKeyHandler* keyHandler;
IScrollable* scrollable; IScrollable* scrollable;
}; };
@ -85,6 +87,7 @@ void changeLayout(WindowState& current, ILayout* newLayout) {
current.focused = current.layout->GetFocus(); current.focused = current.layout->GetFocus();
current.input = dynamic_cast<IInput*>(current.focused); current.input = dynamic_cast<IInput*>(current.focused);
current.scrollable = dynamic_cast<IScrollable*>(current.focused); current.scrollable = dynamic_cast<IScrollable*>(current.focused);
current.keyHandler = dynamic_cast<IKeyHandler*>(current.focused);
} }
if (current.input) { if (current.input) {
@ -102,8 +105,9 @@ void focusNextInLayout(WindowState& current) {
} }
current.focused = current.layout->FocusNext(); current.focused = current.layout->FocusNext();
current.scrollable = dynamic_cast<IScrollable*>(current.focused);
current.input = dynamic_cast<IInput*>(current.focused); current.input = dynamic_cast<IInput*>(current.focused);
current.scrollable = dynamic_cast<IScrollable*>(current.focused);
current.keyHandler = dynamic_cast<IKeyHandler*>(current.focused);
if (current.input != NULL) { if (current.input != NULL) {
curs_set(1); curs_set(1);
@ -164,7 +168,7 @@ int main(int argc, char* argv[])
LibraryPtr library = LibraryFactory::Libraries().at(0); LibraryPtr library = LibraryFactory::Libraries().at(0);
MainLayout mainLayout(tp, library); MainLayout mainLayout(tp, library);
LibraryLayout libraryLayout(library); LibraryLayout libraryLayout(tp, library);
mainLayout.Hide(); mainLayout.Hide();
libraryLayout.Hide(); libraryLayout.Hide();
@ -234,6 +238,9 @@ int main(int argc, char* argv[])
else if (state.input) { else if (state.input) {
state.input->WriteChar(ch); state.input->WriteChar(ch);
} }
else if (state.keyHandler) {
state.keyHandler->KeyPress(ch);
}
Window::WriteToScreen(); Window::WriteToScreen();
WindowMessageQueue::Instance().Dispatch(); WindowMessageQueue::Instance().Dispatch();

View File

@ -6,17 +6,23 @@
#include "TrackListView.h" #include "TrackListView.h"
#include "IWindowMessage.h" #include "IWindowMessage.h"
#include <core/library/LocalLibraryConstants.h>
#include <boost/format.hpp> #include <boost/format.hpp>
#include <boost/format/group.hpp> #include <boost/format/group.hpp>
#include <iomanip> #include <iomanip>
#define WINDOW_MESSAGE_QUERY_COMPLETED 1002 #define WINDOW_MESSAGE_QUERY_COMPLETED 1002
using musik::core::IQuery; using musik::core::IQuery;
using musik::core::audio::Transport;
using namespace musik::core::library::constants;
TrackListView::TrackListView(LibraryPtr library, IWindow *parent) TrackListView::TrackListView(Transport& transport, LibraryPtr library, IWindow *parent)
: ListWindow(parent) { : ListWindow(parent) {
this->SetContentColor(BOX_COLOR_WHITE_ON_BLACK); this->SetContentColor(BOX_COLOR_WHITE_ON_BLACK);
this->transport = &transport;
this->library = library; this->library = library;
this->library->QueryCompleted.connect(this, &TrackListView::OnQueryCompleted); this->library->QueryCompleted.connect(this, &TrackListView::OnQueryCompleted);
this->adapter = new Adapter(*this); this->adapter = new Adapter(*this);
@ -37,6 +43,18 @@ void TrackListView::OnQueryCompleted(QueryPtr query) {
} }
} }
void TrackListView::KeyPress(int64 ch) {
if (ch == 10) { /* return */
size_t selected = this->GetSelectedIndex();
if (this->metadata->size() > selected) {
TrackPtr track = this->metadata->at(selected);
std::string fn = track->GetValue(Track::FILENAME);
this->transport->Stop();
this->transport->Start(fn);
}
}
}
void TrackListView::ProcessMessage(IWindowMessage &message) { void TrackListView::ProcessMessage(IWindowMessage &message) {
if (message.MessageType() == WINDOW_MESSAGE_QUERY_COMPLETED) { if (message.MessageType() == WINDOW_MESSAGE_QUERY_COMPLETED) {
if (this->query && this->query->GetStatus() == IQuery::Finished) { if (this->query && this->query->GetStatus() == IQuery::Finished) {

View File

@ -5,18 +5,23 @@
#include "ListWindow.h" #include "ListWindow.h"
#include "TrackListViewQuery.h" #include "TrackListViewQuery.h"
#include "ScrollAdapterBase.h" #include "ScrollAdapterBase.h"
#include "IKeyHandler.h"
#include <core/playback/Transport.h>
#include <core/library/ILibrary.h> #include <core/library/ILibrary.h>
using musik::core::QueryPtr; using musik::core::QueryPtr;
using musik::core::LibraryPtr; using musik::core::LibraryPtr;
using musik::core::audio::Transport;
class TrackListView : public ListWindow, public sigslot::has_slots<> { class TrackListView : public ListWindow, public IKeyHandler, public sigslot::has_slots<> {
public: public:
TrackListView(LibraryPtr library, IWindow *parent = NULL); TrackListView(Transport& transport, LibraryPtr library, IWindow *parent = NULL);
~TrackListView(); ~TrackListView();
virtual void ProcessMessage(IWindowMessage &message); virtual void ProcessMessage(IWindowMessage &message);
virtual void KeyPress(int64 ch);
void Requery(const std::string& column, DBID id); void Requery(const std::string& column, DBID id);
protected: protected:
@ -39,5 +44,6 @@ class TrackListView : public ListWindow, public sigslot::has_slots<> {
std::shared_ptr<TrackListViewQuery> query; std::shared_ptr<TrackListViewQuery> query;
std::shared_ptr<std::vector<TrackPtr>> metadata; std::shared_ptr<std::vector<TrackPtr>> metadata;
Adapter* adapter; Adapter* adapter;
Transport* transport;
LibraryPtr library; LibraryPtr library;
}; };

View File

@ -4,12 +4,14 @@
#include "TrackListViewQuery.h" #include "TrackListViewQuery.h"
#include <core/library/track/LibraryTrack.h> #include <core/library/track/LibraryTrack.h>
#include <core/library/LocalLibraryConstants.h>
#include <core/db/Statement.h> #include <core/db/Statement.h>
using musik::core::db::Statement; using musik::core::db::Statement;
using musik::core::db::Row; using musik::core::db::Row;
using musik::core::TrackPtr; using musik::core::TrackPtr;
using musik::core::LibraryTrack; using musik::core::LibraryTrack;
using namespace musik::core::library::constants;
TrackListViewQuery::TrackListViewQuery(LibraryPtr library, const std::string& column, DBID id) { TrackListViewQuery::TrackListViewQuery(LibraryPtr library, const std::string& column, DBID id) {
this->library = library; this->library = library;
@ -31,21 +33,30 @@ bool TrackListViewQuery::OnRun(Connection& db) {
result.reset(new std::vector<TrackPtr>()); result.reset(new std::vector<TrackPtr>());
} }
std::string query = boost::str( std::string query = boost::str(boost::format(
boost::format( "SELECT DISTINCT t.track, t.bpm, t.duration, t.filesize, t.year, t.title, t.filename, t.thumbnail_id, al.name AS album, gn.name AS genre, ar.name AS artist, t.filetime, t.sort_order1 " \
"SELECT DISTINCT tracks.id, tracks.track, tracks.title " "FROM tracks t, paths p, albums al, artists ar, genres gn " \
"FROM tracks " "WHERE t.%s=? AND t.album_id=al.id AND t.visual_genre_id=gn.id AND t.visual_artist_id=ar.id") % this->column);
"WHERE %1%=? "
"ORDER BY tracks.track;") % this->column); Statement trackQuery(query.c_str(), db);
Statement stmt(query.c_str(), db); trackQuery.BindInt(0, this->id);
stmt.BindInt(0, this->id); while (trackQuery.Step() == Row) {
TrackPtr track = TrackPtr(new LibraryTrack(this->id, this->library));
track->SetValue(Track::TRACK_NUM, trackQuery.ColumnText(0));
track->SetValue(Track::BPM, trackQuery.ColumnText(1));
track->SetValue(Track::DURATION, trackQuery.ColumnText(2));
track->SetValue(Track::FILESIZE, trackQuery.ColumnText(3));
track->SetValue(Track::YEAR, trackQuery.ColumnText(4));
track->SetValue(Track::TITLE, trackQuery.ColumnText(5));
track->SetValue(Track::FILENAME, trackQuery.ColumnText(6));
track->SetValue(Track::THUMBNAIL_ID, trackQuery.ColumnText(7));
track->SetValue(Track::ALBUM_ID, trackQuery.ColumnText(8));
track->SetValue(Track::DISPLAY_GENRE_ID, trackQuery.ColumnText(9));
track->SetValue(Track::DISPLAY_ARTIST_ID, trackQuery.ColumnText(10));
track->SetValue(Track::FILETIME, trackQuery.ColumnText(11));
while (stmt.Step() == Row) {
DBID id = stmt.ColumnInt64(0);
TrackPtr track = TrackPtr(new LibraryTrack(id, this->library));
track->SetValue("track", boost::lexical_cast<std::string>(stmt.ColumnInt(1)).c_str());
track->SetValue("title", stmt.ColumnText(2));
result->push_back(track); result->push_back(track);
} }

View File

@ -16,7 +16,6 @@ class TrackListViewQuery : public QueryBase {
TrackListViewQuery(LibraryPtr library, const std::string& column, DBID id); TrackListViewQuery(LibraryPtr library, const std::string& column, DBID id);
~TrackListViewQuery(); ~TrackListViewQuery();
std::string Name() { std::string Name() {
return "TrackListViewQuery"; return "TrackListViewQuery";
} }

View File

@ -151,6 +151,7 @@
<ClInclude Include="CategoryListViewQuery.h" /> <ClInclude Include="CategoryListViewQuery.h" />
<ClInclude Include="CategoryListView.h" /> <ClInclude Include="CategoryListView.h" />
<ClInclude Include="IDisplayable.h" /> <ClInclude Include="IDisplayable.h" />
<ClInclude Include="IKeyHandler.h" />
<ClInclude Include="IWindowGroup.h" /> <ClInclude Include="IWindowGroup.h" />
<ClInclude Include="IWindowMessage.h" /> <ClInclude Include="IWindowMessage.h" />
<ClInclude Include="LayoutBase.h" /> <ClInclude Include="LayoutBase.h" />

View File

@ -177,6 +177,9 @@
<ClInclude Include="Window.h"> <ClInclude Include="Window.h">
<Filter>curses\window</Filter> <Filter>curses\window</Filter>
</ClInclude> </ClInclude>
<ClInclude Include="IKeyHandler.h">
<Filter>curses</Filter>
</ClInclude>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Filter Include="curses"> <Filter Include="curses">