mirror of
https://github.com/clangen/musikcube.git
synced 2025-02-11 09:40:26 +00:00
- 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:
parent
1c13ac278d
commit
fc86bacaaf
@ -215,7 +215,7 @@ void Indexer::SynchronizeInternal() {
|
||||
|
||||
for(std::size_t i = 0; i < paths.size(); ++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) */
|
||||
@ -228,7 +228,7 @@ void Indexer::SynchronizeInternal() {
|
||||
}
|
||||
|
||||
if (!this->Restarted() && !this->Exited()) {
|
||||
this->SyncDelete(pathIds);
|
||||
this->SyncDelete();
|
||||
}
|
||||
|
||||
/* cleanup -- remove stale artists, albums, genres, etc */
|
||||
@ -270,26 +270,9 @@ void Indexer::SynchronizeInternal() {
|
||||
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(
|
||||
const std::string &syncRoot,
|
||||
const std::string ¤tPath,
|
||||
DBID parentDirId,
|
||||
DBID pathId)
|
||||
{
|
||||
if (this->Exited() || this->Restarted()) {
|
||||
@ -300,39 +283,6 @@ void Indexer::SyncDirectory(
|
||||
std::string normalizedCurrentPath = normalizeDir(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 */
|
||||
|
||||
try { /* boost::filesystem may throw */
|
||||
@ -343,11 +293,13 @@ void Indexer::SyncDirectory(
|
||||
boost::filesystem::directory_iterator end;
|
||||
boost::filesystem::directory_iterator file(path);
|
||||
|
||||
std::string pathIdStr = boost::lexical_cast<std::string>(pathId);
|
||||
|
||||
for( ; file != end && !this->Exited() && !this->Restarted(); file++) {
|
||||
if (is_directory(file->status())) {
|
||||
/* recursion here */
|
||||
musik::debug::info(TAG, "scanning " + file->path().string());
|
||||
this->SyncDirectory(syncRoot, file->path().string(), dirId, pathId);
|
||||
this->SyncDirectory(syncRoot, file->path().string(), pathId);
|
||||
}
|
||||
else {
|
||||
++this->filesIndexed;
|
||||
@ -355,7 +307,7 @@ void Indexer::SyncDirectory(
|
||||
musik::core::IndexerTrack track(0);
|
||||
|
||||
/* get cached filesize, parts, size, etc */
|
||||
if (track.NeedsToBeIndexed(file->path(), this->dbConnection, dirId)) {
|
||||
if (track.NeedsToBeIndexed(file->path(), this->dbConnection)) {
|
||||
bool saveToDb = false;
|
||||
|
||||
/* read the tag from the plugin */
|
||||
@ -373,7 +325,8 @@ void Indexer::SyncDirectory(
|
||||
|
||||
/* write it to the db, if read successfully */
|
||||
if (saveToDb) {
|
||||
track.Save(this->dbConnection, this->libraryPath, dirId);
|
||||
track.SetValue("path_id", pathIdStr.c_str());
|
||||
track.Save(this->dbConnection, this->libraryPath);
|
||||
|
||||
this->filesSaved++;
|
||||
if (this->filesSaved % 100 == 0) {
|
||||
@ -443,133 +396,38 @@ void Indexer::ThreadLoop() {
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////
|
||||
///\brief
|
||||
///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)");
|
||||
void Indexer::SyncDelete() {
|
||||
/* remove all tracks that no longer reference a valid path entry */
|
||||
|
||||
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)");
|
||||
|
||||
{
|
||||
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);
|
||||
/* remove files that are no longer on the filesystem. */
|
||||
|
||||
db::Statement stmtRemove("DELETE FROM tracks WHERE id=?", this->dbConnection);
|
||||
|
||||
for (std::size_t i = 0; i < paths.size(); ++i) {
|
||||
stmt.BindInt(0, paths[i]);
|
||||
db::Statement allTracks(
|
||||
"SELECT t.id, t.filename "
|
||||
"FROM tracks t "
|
||||
"WHERE p.id=?", this->dbConnection);
|
||||
|
||||
stmtSyncPath.BindInt(0,paths[i]);
|
||||
stmtSyncPath.Step();
|
||||
|
||||
std::string syncPathString = stmtSyncPath.ColumnText(0);
|
||||
while(allTracks.Step() == db::Row && !this->Exited() && !this->Restarted()) {
|
||||
bool remove = false;
|
||||
std::string file = allTracks.ColumnText(1);
|
||||
|
||||
stmtSyncPath.Reset();
|
||||
|
||||
while(stmt.Step() == db::Row && !this->Exited() && !this->Restarted()) {
|
||||
bool 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;
|
||||
}
|
||||
}
|
||||
try {
|
||||
boost::filesystem::path file(file);
|
||||
if (!boost::filesystem::exists(file)) {
|
||||
remove = true;
|
||||
}
|
||||
catch(...) {
|
||||
remove = false;
|
||||
}
|
||||
|
||||
if (remove) {
|
||||
stmtRemove.BindInt(0, stmt.ColumnInt(0));
|
||||
stmtRemove.Step();
|
||||
stmtRemove.Reset();
|
||||
}
|
||||
|
||||
++count;
|
||||
}
|
||||
|
||||
stmt.Reset();
|
||||
catch(...) {
|
||||
}
|
||||
|
||||
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 " \
|
||||
"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 folders f ON f.id=t.folder_id " \
|
||||
"ORDER BY ar.sort_order, al.sort_order, t.track, f.relative_path, t.filename",
|
||||
"ORDER BY ar.sort_order, al.sort_order, t.track, t.filename",
|
||||
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->dbConnection.Execute("DELETE FROM folders WHERE path_id NOT IN (SELECT id FROM paths)");
|
||||
|
||||
this->PathsUpdated();
|
||||
}
|
||||
|
||||
@ -775,17 +630,15 @@ void Indexer::RunAnalyzers() {
|
||||
/* for each track... */
|
||||
|
||||
DBID trackId = 0;
|
||||
DBID folderId = 0;
|
||||
|
||||
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);
|
||||
|
||||
getNextTrack.BindInt(0, trackId);
|
||||
|
||||
while(getNextTrack.Step() == db::Row ) {
|
||||
trackId = getNextTrack.ColumnInt(0);
|
||||
folderId = getNextTrack.ColumnInt(1);
|
||||
|
||||
getNextTrack.Reset();
|
||||
getNextTrack.UnBindAll();
|
||||
@ -838,7 +691,7 @@ void Indexer::RunAnalyzers() {
|
||||
completed successfully, then save the track. */
|
||||
|
||||
if(successPlugins>0) {
|
||||
track.Save(this->dbConnection, this->libraryPath,folderId);
|
||||
track.Save(this->dbConnection, this->libraryPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -76,7 +76,7 @@ namespace musik { namespace core {
|
||||
|
||||
bool Restarted();
|
||||
|
||||
void SyncDelete(const std::vector<DBID>& paths);
|
||||
void SyncDelete();
|
||||
void SyncCleanup();
|
||||
void ProcessAddRemoveQueue();
|
||||
void SyncOptimize();
|
||||
@ -87,7 +87,6 @@ namespace musik { namespace core {
|
||||
void SyncDirectory(
|
||||
const std::string& syncRoot,
|
||||
const std::string& currentPath,
|
||||
DBID parentDirId,
|
||||
DBID pathId);
|
||||
|
||||
db::Connection dbConnection;
|
||||
|
@ -234,7 +234,6 @@ bool LocalLibrary::IsSpecialMTOMetaKey(std::string &metakey){
|
||||
specialMTOMetaKeys.insert("album");
|
||||
specialMTOMetaKeys.insert("visual_genre");
|
||||
specialMTOMetaKeys.insert("visual_artist");
|
||||
specialMTOMetaKeys.insert("folder");
|
||||
}
|
||||
|
||||
return specialMTOMetaKeys.find(metakey)!=specialMTOMetaKeys.end();
|
||||
@ -272,8 +271,8 @@ void LocalLibrary::CreateDatabase(db::Connection &db){
|
||||
"year INTEGER DEFAULT 0,"
|
||||
"visual_genre_id INTEGER DEFAULT 0,"
|
||||
"visual_artist_id INTEGER DEFAULT 0,"
|
||||
"path_id INTEGER,"
|
||||
"album_id INTEGER DEFAULT 0,"
|
||||
"folder_id INTEGER DEFAULT 0,"
|
||||
"title TEXT default '',"
|
||||
"filename TEXT default '',"
|
||||
"filetime INTEGER DEFAULT 0,"
|
||||
@ -333,16 +332,7 @@ void LocalLibrary::CreateDatabase(db::Connection &db){
|
||||
"path TEXT default ''"
|
||||
")");
|
||||
|
||||
// Create the folders-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
|
||||
// Create the thumbnails table
|
||||
db.Execute("CREATE TABLE IF NOT EXISTS thumbnails ("
|
||||
"id INTEGER PRIMARY KEY AUTOINCREMENT,"
|
||||
"filename TEXT default '',"
|
||||
@ -350,13 +340,14 @@ void LocalLibrary::CreateDatabase(db::Connection &db){
|
||||
"checksum INTEGER DEFAULT 0"
|
||||
")");
|
||||
|
||||
// Create the playlists-table
|
||||
// Create the playlists
|
||||
db.Execute("CREATE TABLE IF NOT EXISTS playlists ("
|
||||
"id INTEGER PRIMARY KEY AUTOINCREMENT,"
|
||||
"name TEXT default '',"
|
||||
"user_id INTEGER default 0"
|
||||
")");
|
||||
// Create the playlists-table
|
||||
|
||||
// Create the playlist_tracks table
|
||||
db.Execute("CREATE TABLE IF NOT EXISTS playlist_tracks ("
|
||||
"track_id INTEGER DEFAULT 0,"
|
||||
"playlist_id INTEGER DEFAULT 0,"
|
||||
@ -365,13 +356,11 @@ void LocalLibrary::CreateDatabase(db::Connection &db){
|
||||
|
||||
// INDEXES
|
||||
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 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 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_index7 ON tracks (folder_id)");
|
||||
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)");
|
||||
|
@ -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_ARTIST_ID = "visual_artist_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* FILENAME = "filename";
|
||||
static const char* FILETIME = "filetime";
|
||||
static const char* THUMBNAIL_ID = "thumbnail_id";
|
||||
static const char* SORT_ORDER = "sort_order1";
|
||||
static const char* PATH = "path"; /* not in DB; synthesized */
|
||||
static const char* PATH = "path";
|
||||
}
|
||||
|
||||
namespace Genres {
|
||||
@ -123,15 +123,6 @@ namespace musik { namespace core { namespace library { namespace constants {
|
||||
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 {
|
||||
static const char* TABLE_NAME = "thumbnails";
|
||||
static const char* ID = "id";
|
||||
|
@ -137,12 +137,11 @@ DBID IndexerTrack::Id() {
|
||||
|
||||
bool IndexerTrack::NeedsToBeIndexed(
|
||||
const boost::filesystem::path &file,
|
||||
db::Connection &dbConnection,
|
||||
DBID currentFolderId)
|
||||
db::Connection &dbConnection)
|
||||
{
|
||||
try {
|
||||
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(".");
|
||||
if (lastDot != std::string::npos){
|
||||
@ -158,10 +157,9 @@ bool IndexerTrack::NeedsToBeIndexed(
|
||||
db::CachedStatement stmt(
|
||||
"SELECT id, filename, filesize, filetime " \
|
||||
"FROM tracks t " \
|
||||
"WHERE folder_id=? AND filename=?", dbConnection);
|
||||
"WHERE filename=?", dbConnection);
|
||||
|
||||
stmt.BindInt(0, currentFolderId);
|
||||
stmt.BindText(1, this->GetValue("filename"));
|
||||
stmt.BindText(0, this->GetValue("filename"));
|
||||
|
||||
bool fileDifferent = true;
|
||||
|
||||
@ -184,11 +182,10 @@ bool IndexerTrack::NeedsToBeIndexed(
|
||||
static DBID writeToTracksTable(
|
||||
db::Connection &dbConnection,
|
||||
IndexerTrack& track,
|
||||
DBID folderId,
|
||||
DBID tempSortOrder)
|
||||
{
|
||||
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);
|
||||
|
||||
stmt.BindText(1, track.GetValue("track"));
|
||||
@ -196,10 +193,10 @@ static DBID writeToTracksTable(
|
||||
stmt.BindText(3, track.GetValue("duration"));
|
||||
stmt.BindText(4, track.GetValue("filesize"));
|
||||
stmt.BindText(5, track.GetValue("year"));
|
||||
stmt.BindInt(6, folderId);
|
||||
stmt.BindText(7, track.GetValue("title"));
|
||||
stmt.BindText(8, track.GetValue("filename"));
|
||||
stmt.BindText(9, track.GetValue("filetime"));
|
||||
stmt.BindText(6, track.GetValue("title"));
|
||||
stmt.BindText(7, track.GetValue("filename"));
|
||||
stmt.BindText(8, track.GetValue("filetime"));
|
||||
stmt.BindText(9, track.GetValue("path_id"));
|
||||
stmt.BindInt(10, tempSortOrder);
|
||||
|
||||
if (track.Id() != 0) {
|
||||
@ -448,7 +445,7 @@ DBID IndexerTrack::ExtractArtist(db::Connection& dbConnection) {
|
||||
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);
|
||||
|
||||
/* 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 */
|
||||
|
||||
this->id = writeToTracksTable(dbConnection, *this, folderId, tempSortOrder);
|
||||
this->id = writeToTracksTable(dbConnection, *this, tempSortOrder);
|
||||
|
||||
DBID albumId = this->ExtractAlbum(dbConnection);
|
||||
DBID genreId = this->ExtractGenre(dbConnection);
|
||||
@ -567,9 +564,9 @@ bool IndexerTrack::Reload(db::Connection &db) {
|
||||
"ORDER BY tm.id", db);
|
||||
|
||||
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 " \
|
||||
"FROM tracks t, folders f, 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);
|
||||
"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, paths p, albums al " \
|
||||
"WHERE t.id=? AND t.album_id=al.id", db);
|
||||
|
||||
track.BindInt(0, this->id);
|
||||
if(track.Step() == db::Row) {
|
||||
@ -581,10 +578,9 @@ bool IndexerTrack::Reload(db::Connection &db) {
|
||||
this->SetValue("title", track.ColumnText(5));
|
||||
this->SetValue("filename", track.ColumnText(6));
|
||||
this->SetValue("thumbnail_id", track.ColumnText(7));
|
||||
this->SetValue("path", track.ColumnText(8));
|
||||
this->SetValue("album", track.ColumnText(9));
|
||||
this->SetValue("filetime", track.ColumnText(10));
|
||||
this->tempSortOrder = track.ColumnInt(11);
|
||||
this->SetValue("album", track.ColumnText(8));
|
||||
this->SetValue("filetime", track.ColumnText(9));
|
||||
this->tempSortOrder = track.ColumnInt(10);
|
||||
|
||||
genres.BindInt(0, this->id);
|
||||
while (genres.Step() == db::Row) {
|
||||
@ -592,7 +588,7 @@ bool IndexerTrack::Reload(db::Connection &db) {
|
||||
}
|
||||
|
||||
artists.BindInt(0, this->id);
|
||||
while (artists.Step()==db::Row) {
|
||||
while (artists.Step() == db::Row) {
|
||||
this->SetValue("artist", artists.ColumnText(0));
|
||||
}
|
||||
|
||||
|
@ -66,13 +66,11 @@ namespace musik { namespace core {
|
||||
|
||||
bool NeedsToBeIndexed(
|
||||
const boost::filesystem::path &file,
|
||||
db::Connection &dbConnection,
|
||||
DBID currentFolderId);
|
||||
db::Connection &dbConnection);
|
||||
|
||||
bool Save(
|
||||
db::Connection &dbConnection,
|
||||
std::string libraryDirectory,
|
||||
DBID folderId);
|
||||
std::string libraryDirectory);
|
||||
|
||||
bool Reload(db::Connection &db);
|
||||
|
||||
|
@ -197,5 +197,4 @@ LibraryTrack::MetaData::MetaData()
|
||||
|
||||
LibraryTrack::MetaData::~MetaData() {
|
||||
delete this->thumbnailData;
|
||||
}
|
||||
|
||||
}
|
@ -39,6 +39,7 @@
|
||||
#include <core/config.h>
|
||||
#include <core/library/track/Track.h>
|
||||
#include <core/library/LocalLibrary.h>
|
||||
#include <core/db/Connection.h>
|
||||
|
||||
namespace musik { namespace core { namespace http {
|
||||
class Responder;
|
||||
@ -63,9 +64,9 @@ namespace musik { namespace core {
|
||||
virtual DBID Id();
|
||||
|
||||
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 SetThumbnail(const char *data,long size);
|
||||
virtual void SetThumbnail(const char *data, long size);
|
||||
|
||||
virtual std::string URI();
|
||||
virtual std::string URL();
|
||||
|
8
src/musikbox/IKeyHandler.h
Executable file
8
src/musikbox/IKeyHandler.h
Executable file
@ -0,0 +1,8 @@
|
||||
#pragma once
|
||||
|
||||
#include "stdafx.h"
|
||||
|
||||
class IKeyHandler {
|
||||
public:
|
||||
virtual void KeyPress(int64 ch) = 0;
|
||||
};
|
@ -1,7 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include "stdafx.h"
|
||||
#include <boost/shared_ptr.hpp>
|
||||
|
||||
class IScrollAdapter {
|
||||
public:
|
||||
|
@ -3,8 +3,11 @@
|
||||
#include "Screen.h"
|
||||
#include "LibraryLayout.h"
|
||||
|
||||
LibraryLayout::LibraryLayout(LibraryPtr library)
|
||||
#define CATEGORY_WIDTH 25
|
||||
|
||||
LibraryLayout::LibraryLayout(Transport& transport, LibraryPtr library)
|
||||
: LayoutBase() {
|
||||
this->transport = &transport;
|
||||
this->library = library;
|
||||
this->InitializeWindows();
|
||||
}
|
||||
@ -18,17 +21,17 @@ void LibraryLayout::Layout() {
|
||||
this->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->trackList->SetPosition(20, 0);
|
||||
this->trackList->SetSize(this->GetWidth() - 20, this->GetHeight());
|
||||
this->trackList->SetPosition(CATEGORY_WIDTH, 0);
|
||||
this->trackList->SetSize(this->GetWidth() - CATEGORY_WIDTH, this->GetHeight());
|
||||
this->trackList->SetFocusOrder(1);
|
||||
}
|
||||
|
||||
void LibraryLayout::InitializeWindows() {
|
||||
this->albumList.reset(new CategoryListView(library));
|
||||
this->trackList.reset(new TrackListView(library));
|
||||
this->albumList.reset(new CategoryListView(this->library));
|
||||
this->trackList.reset(new TrackListView(*this->transport, this->library));
|
||||
|
||||
this->AddWindow(this->albumList);
|
||||
this->AddWindow(this->trackList);
|
||||
|
@ -4,15 +4,17 @@
|
||||
#include "CategoryListView.h"
|
||||
#include "TrackListView.h"
|
||||
|
||||
#include <core/playback/Transport.h>
|
||||
#include <core/library/ILibrary.h>
|
||||
|
||||
#include <sigslot/sigslot.h>
|
||||
|
||||
using musik::core::LibraryPtr;
|
||||
using musik::core::audio::Transport;
|
||||
|
||||
class LibraryLayout : public LayoutBase, public sigslot::has_slots<> {
|
||||
public:
|
||||
LibraryLayout(LibraryPtr library);
|
||||
LibraryLayout(Transport& transport, LibraryPtr library);
|
||||
virtual ~LibraryLayout();
|
||||
|
||||
virtual void Layout();
|
||||
@ -24,6 +26,7 @@ class LibraryLayout : public LayoutBase, public sigslot::has_slots<> {
|
||||
void OnCategoryViewSelectionChanged(
|
||||
ListWindow *view, size_t newIndex, size_t oldIndex);
|
||||
|
||||
Transport* transport;
|
||||
LibraryPtr library;
|
||||
std::shared_ptr<CategoryListView> albumList;
|
||||
std::shared_ptr<TrackListView> trackList;
|
||||
|
@ -42,6 +42,7 @@
|
||||
#include "MainLayout.h"
|
||||
#include "LibraryLayout.h"
|
||||
#include "IInput.h"
|
||||
#include "IKeyHandler.h"
|
||||
#include "WindowMessageQueue.h"
|
||||
|
||||
#include <boost/locale.hpp>
|
||||
@ -60,6 +61,7 @@ struct WindowState {
|
||||
ILayout* layout;
|
||||
IWindow* focused;
|
||||
IInput* input;
|
||||
IKeyHandler* keyHandler;
|
||||
IScrollable* scrollable;
|
||||
};
|
||||
|
||||
@ -85,6 +87,7 @@ void changeLayout(WindowState& current, ILayout* newLayout) {
|
||||
current.focused = current.layout->GetFocus();
|
||||
current.input = dynamic_cast<IInput*>(current.focused);
|
||||
current.scrollable = dynamic_cast<IScrollable*>(current.focused);
|
||||
current.keyHandler = dynamic_cast<IKeyHandler*>(current.focused);
|
||||
}
|
||||
|
||||
if (current.input) {
|
||||
@ -102,8 +105,9 @@ void focusNextInLayout(WindowState& current) {
|
||||
}
|
||||
|
||||
current.focused = current.layout->FocusNext();
|
||||
current.scrollable = dynamic_cast<IScrollable*>(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) {
|
||||
curs_set(1);
|
||||
@ -164,7 +168,7 @@ int main(int argc, char* argv[])
|
||||
LibraryPtr library = LibraryFactory::Libraries().at(0);
|
||||
|
||||
MainLayout mainLayout(tp, library);
|
||||
LibraryLayout libraryLayout(library);
|
||||
LibraryLayout libraryLayout(tp, library);
|
||||
|
||||
mainLayout.Hide();
|
||||
libraryLayout.Hide();
|
||||
@ -234,6 +238,9 @@ int main(int argc, char* argv[])
|
||||
else if (state.input) {
|
||||
state.input->WriteChar(ch);
|
||||
}
|
||||
else if (state.keyHandler) {
|
||||
state.keyHandler->KeyPress(ch);
|
||||
}
|
||||
|
||||
Window::WriteToScreen();
|
||||
WindowMessageQueue::Instance().Dispatch();
|
||||
|
@ -6,17 +6,23 @@
|
||||
#include "TrackListView.h"
|
||||
#include "IWindowMessage.h"
|
||||
|
||||
#include <core/library/LocalLibraryConstants.h>
|
||||
|
||||
#include <boost/format.hpp>
|
||||
#include <boost/format/group.hpp>
|
||||
|
||||
#include <iomanip>
|
||||
|
||||
#define WINDOW_MESSAGE_QUERY_COMPLETED 1002
|
||||
|
||||
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) {
|
||||
this->SetContentColor(BOX_COLOR_WHITE_ON_BLACK);
|
||||
this->transport = &transport;
|
||||
this->library = library;
|
||||
this->library->QueryCompleted.connect(this, &TrackListView::OnQueryCompleted);
|
||||
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) {
|
||||
if (message.MessageType() == WINDOW_MESSAGE_QUERY_COMPLETED) {
|
||||
if (this->query && this->query->GetStatus() == IQuery::Finished) {
|
||||
|
@ -5,18 +5,23 @@
|
||||
#include "ListWindow.h"
|
||||
#include "TrackListViewQuery.h"
|
||||
#include "ScrollAdapterBase.h"
|
||||
#include "IKeyHandler.h"
|
||||
|
||||
#include <core/playback/Transport.h>
|
||||
#include <core/library/ILibrary.h>
|
||||
|
||||
using musik::core::QueryPtr;
|
||||
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:
|
||||
TrackListView(LibraryPtr library, IWindow *parent = NULL);
|
||||
TrackListView(Transport& transport, LibraryPtr library, IWindow *parent = NULL);
|
||||
~TrackListView();
|
||||
|
||||
virtual void ProcessMessage(IWindowMessage &message);
|
||||
virtual void KeyPress(int64 ch);
|
||||
|
||||
void Requery(const std::string& column, DBID id);
|
||||
|
||||
protected:
|
||||
@ -39,5 +44,6 @@ class TrackListView : public ListWindow, public sigslot::has_slots<> {
|
||||
std::shared_ptr<TrackListViewQuery> query;
|
||||
std::shared_ptr<std::vector<TrackPtr>> metadata;
|
||||
Adapter* adapter;
|
||||
Transport* transport;
|
||||
LibraryPtr library;
|
||||
};
|
@ -4,12 +4,14 @@
|
||||
#include "TrackListViewQuery.h"
|
||||
|
||||
#include <core/library/track/LibraryTrack.h>
|
||||
#include <core/library/LocalLibraryConstants.h>
|
||||
#include <core/db/Statement.h>
|
||||
|
||||
using musik::core::db::Statement;
|
||||
using musik::core::db::Row;
|
||||
using musik::core::TrackPtr;
|
||||
using musik::core::LibraryTrack;
|
||||
using namespace musik::core::library::constants;
|
||||
|
||||
TrackListViewQuery::TrackListViewQuery(LibraryPtr library, const std::string& column, DBID id) {
|
||||
this->library = library;
|
||||
@ -31,21 +33,30 @@ bool TrackListViewQuery::OnRun(Connection& db) {
|
||||
result.reset(new std::vector<TrackPtr>());
|
||||
}
|
||||
|
||||
std::string query = boost::str(
|
||||
boost::format(
|
||||
"SELECT DISTINCT tracks.id, tracks.track, tracks.title "
|
||||
"FROM tracks "
|
||||
"WHERE %1%=? "
|
||||
"ORDER BY tracks.track;") % this->column);
|
||||
std::string query = boost::str(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 " \
|
||||
"FROM tracks t, paths p, albums al, artists ar, genres gn " \
|
||||
"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);
|
||||
|
||||
Statement trackQuery(query.c_str(), db);
|
||||
|
||||
Statement stmt(query.c_str(), db);
|
||||
stmt.BindInt(0, this->id);
|
||||
trackQuery.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);
|
||||
}
|
||||
|
||||
|
@ -16,7 +16,6 @@ class TrackListViewQuery : public QueryBase {
|
||||
|
||||
TrackListViewQuery(LibraryPtr library, const std::string& column, DBID id);
|
||||
~TrackListViewQuery();
|
||||
|
||||
std::string Name() {
|
||||
return "TrackListViewQuery";
|
||||
}
|
||||
|
@ -151,6 +151,7 @@
|
||||
<ClInclude Include="CategoryListViewQuery.h" />
|
||||
<ClInclude Include="CategoryListView.h" />
|
||||
<ClInclude Include="IDisplayable.h" />
|
||||
<ClInclude Include="IKeyHandler.h" />
|
||||
<ClInclude Include="IWindowGroup.h" />
|
||||
<ClInclude Include="IWindowMessage.h" />
|
||||
<ClInclude Include="LayoutBase.h" />
|
||||
|
@ -177,6 +177,9 @@
|
||||
<ClInclude Include="Window.h">
|
||||
<Filter>curses\window</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="IKeyHandler.h">
|
||||
<Filter>curses</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Filter Include="curses">
|
||||
|
Loading…
x
Reference in New Issue
Block a user