diff --git a/src/library/baseplaylistfeature.cpp b/src/library/baseplaylistfeature.cpp index 318c96bfc883..17428450d24b 100644 --- a/src/library/baseplaylistfeature.cpp +++ b/src/library/baseplaylistfeature.cpp @@ -95,21 +95,22 @@ void BasePlaylistFeature::activate() { } void BasePlaylistFeature::activateChild(const QModelIndex& index) { - //qDebug() << "BasePlaylistFeature::activateChild()" << index; + qDebug() << "BasePlaylistFeature::activateChild()" << objectName() << index; // Switch the playlist table model's playlist. + QString playlistName = index.data().toString(); + int playlistId = -1; // tro's lambda idea. This code calls synchronously! m_pTrackCollection->callSync( - [this, index] (void) { - QString playlistName = index.data().toString(); - int playlistId = m_playlistDao.getPlaylistIdFromName(playlistName); - if (m_pPlaylistTableModel) { - m_pPlaylistTableModel->setTableModel(playlistId); - } - if (m_pPlaylistTableModel) { - emit(showTrackModel(m_pPlaylistTableModel)); - } + [this, &playlistName, &playlistId] (void) { + playlistId = m_playlistDao.getPlaylistIdFromName(playlistName); }, __PRETTY_FUNCTION__); + if (m_pPlaylistTableModel) { + m_pPlaylistTableModel->setTableModel(playlistId); + } + if (m_pPlaylistTableModel) { + emit(showTrackModel(m_pPlaylistTableModel)); + } } void BasePlaylistFeature::slotRenamePlaylist() { diff --git a/src/library/basetrackcache.cpp b/src/library/basetrackcache.cpp index e77a67b44f26..04754f3d1ecd 100644 --- a/src/library/basetrackcache.cpp +++ b/src/library/basetrackcache.cpp @@ -187,9 +187,9 @@ bool BaseTrackCache::updateIndexWithQuery(const QString& queryString) { QTime timer; timer.start(); - if (sDebug) { +// if (sDebug) { qDebug() << "updateIndexWithQuery issuing query:" << queryString; - } +// } QSqlQuery query(m_database); // This causes a memory savings since QSqlCachedResult (what QtSQLite uses) @@ -246,7 +246,7 @@ void BaseTrackCache::buildIndex() { // tro's lambda idea. This code calls Synchronously! m_pTrackCollection->callSync( - [this, queryString] (void) { + [this, &queryString] (void) { if (!updateIndexWithQuery(queryString)) { qDebug() << "buildIndex failed!"; } diff --git a/src/library/dao/trackdao.cpp b/src/library/dao/trackdao.cpp index bbd9fc2e1907..e6a7afe751d2 100644 --- a/src/library/dao/trackdao.cpp +++ b/src/library/dao/trackdao.cpp @@ -902,20 +902,20 @@ TrackPointer TrackDAO::getTrackFromDB(const int id) const { // Listen to dirty and changed signals connect(pTrack.data(), SIGNAL(dirty(TrackInfoObject*)), this, SLOT(slotTrackDirty(TrackInfoObject*)), -// Qt::DirectConnection); - Qt::QueuedConnection); // tro + Qt::DirectConnection); +// Qt::QueuedConnection); // tro connect(pTrack.data(), SIGNAL(clean(TrackInfoObject*)), this, SLOT(slotTrackClean(TrackInfoObject*)), -// Qt::DirectConnection); - Qt::QueuedConnection); // tro + Qt::DirectConnection); +// Qt::QueuedConnection); // tro connect(pTrack.data(), SIGNAL(changed(TrackInfoObject*)), this, SLOT(slotTrackChanged(TrackInfoObject*)), -// Qt::DirectConnection); - Qt::QueuedConnection); // tro + Qt::DirectConnection); +// Qt::QueuedConnection); // tro connect(pTrack.data(), SIGNAL(save(TrackInfoObject*)), this, SLOT(slotTrackSave(TrackInfoObject*)), -// Qt::DirectConnection); - Qt::QueuedConnection); // tro + Qt::DirectConnection); +// Qt::QueuedConnection); // tro m_sTracksMutex.lock(); // Automatic conversion to a weak pointer @@ -1106,8 +1106,7 @@ void TrackDAO::invalidateTrackLocationsInLibrary(QString libraryPath) { } } -void TrackDAO::markTrackLocationAsVerified(const QString& location) -{ +void TrackDAO::markTrackLocationAsVerified(const QString& location) { //qDebug() << "TrackDAO::markTrackLocationAsVerified" << QThread::currentThread() << m_database.connectionName(); //qDebug() << "markTrackLocationAsVerified()" << location; @@ -1299,7 +1298,7 @@ bool TrackDAO::isTrackFormatSupported(TrackInfoObject* pTrack) const { return false; } -void TrackDAO::verifyTracksOutside(const QString& libraryPath, volatile bool* pCancel, volatile bool* pPause) { +void TrackDAO::verifyTracksOutside(const QString& libraryPath, volatile bool* pCancel) { // This function is called from the LibraryScanner Thread ScopedTransaction transaction(m_database); QSqlQuery query(m_database); diff --git a/src/library/dao/trackdao.h b/src/library/dao/trackdao.h index 4062da1bb815..beae5c910816 100644 --- a/src/library/dao/trackdao.h +++ b/src/library/dao/trackdao.h @@ -101,7 +101,7 @@ class TrackDAO : public QObject, public virtual DAO { void detectMovedFiles(QSet* pTracksMovedSetNew, QSet* pTracksMovedSetOld); void databaseTrackAdded(TrackPointer pTrack); void databaseTracksMoved(QSet tracksMovedSetOld, QSet tracksMovedSetNew); - void verifyTracksOutside(const QString& libraryPath, volatile bool* pCancel, volatile bool* pPause); + void verifyTracksOutside(const QString& libraryPath, volatile bool* pCancel); signals: void trackDirty(int trackId); diff --git a/src/library/libraryscanner.cpp b/src/library/libraryscanner.cpp index a1f48afa29e0..9592cc0ae96b 100644 --- a/src/library/libraryscanner.cpp +++ b/src/library/libraryscanner.cpp @@ -233,7 +233,7 @@ void LibraryScanner::run() { // m_trackDao.addTracksFinish(); ////////////////////////////////////// //Verify all Tracks inside Library but outside the library path - m_trackDao.verifyTracksOutside(m_qLibraryPath, &m_bCancelLibraryScan, &m_bPauseLibraryScan); + m_trackDao.verifyTracksOutside(m_qLibraryPath, &m_bCancelLibraryScan/*, &m_bPauseLibraryScan*/); // tro's lambda idea. This code calls Synchronously! diff --git a/src/library/libraryscanner.h b/src/library/libraryscanner.h index 34e4ad60cd59..8c85a86e6bd5 100644 --- a/src/library/libraryscanner.h +++ b/src/library/libraryscanner.h @@ -56,6 +56,7 @@ class LibraryScanner : public QThread { void scanFinished(); void progressHashing(QString); private: + TrackCollection* m_pTrackCollection; // The library trackcollection QSqlDatabase m_database; // Hang on to a different DB connection // since we run in a different thread */ diff --git a/src/library/playlistfeature.cpp b/src/library/playlistfeature.cpp index c0aaa7266f16..54a53ed564f5 100644 --- a/src/library/playlistfeature.cpp +++ b/src/library/playlistfeature.cpp @@ -60,8 +60,8 @@ void PlaylistFeature::onRightClickChild(const QPoint& globalPos, QModelIndex ind int playlistId = -1; bool locked = false; - // tro's lambda idea. This code calls asynchronously! - m_pTrackCollection->callAsync( + // tro's lambda idea. This code calls synchronously! + m_pTrackCollection->callSync( [this, &playlistName, &playlistId, &locked] (void) { playlistId = m_playlistDao.getPlaylistIdFromName(playlistName); locked = m_playlistDao.isPlaylistLocked(playlistId); @@ -181,6 +181,7 @@ void PlaylistFeature::decorateChild(TreeItem* item, int playlist_id) { } void PlaylistFeature::slotPlaylistTableChanged(int playlistId) { + // here callSync uses if (!m_pPlaylistTableModel) { return; } @@ -188,7 +189,7 @@ void PlaylistFeature::slotPlaylistTableChanged(int playlistId) { PlaylistDAO::HiddenType type; m_pTrackCollection->callSync( - [this, playlistId, &type] (void) { + [this, &playlistId, &type] (void) { type = m_playlistDao.getHiddenType(playlistId); }, __PRETTY_FUNCTION__ + objectName()); diff --git a/src/library/trackcollection.cpp b/src/library/trackcollection.cpp index 44a433d8c9ad..b22014e76ad2 100644 --- a/src/library/trackcollection.cpp +++ b/src/library/trackcollection.cpp @@ -19,6 +19,7 @@ #include "configobject.h" #define MAX_LAMBDA_COUNT 8 +#define MAX_CHUNK_SIZE 10 TrackCollection::TrackCollection(ConfigObject* pConfig) : m_pConfig(pConfig), @@ -111,7 +112,10 @@ void TrackCollection::run() { DBG() << " ### Thread ended ###"; } -// callAsync calls from any thread +// callAsync can be called from any thread. Be careful: callAsync runs asynchonously. +// @param: lambda function, string (for debug purposes). +// Catched values in lambda must be guarantly alive until end of execution lambda +// (if catched by value) or you can catch by value. void TrackCollection::callAsync(func lambda, QString where) { qDebug() << "callAsync from" << where; if (lambda == NULL) return; @@ -121,10 +125,13 @@ void TrackCollection::callAsync(func lambda, QString where) { addLambdaToQueue(lambda); } +// callAsync can be called from any thread +// @param: lambda function, string (for debug purposes). void TrackCollection::callSync(func lambda, QString where) { - // if (m_inCallSync) { - // Q_ASSERT(!m_inCallSync); - // } + qDebug() << "callSync BEGIN from"<set(0.0); } m_inCallSync = false; + qDebug() << "callSync END from"<open()) { - QMessageBox::critical(0, tr("Cannot open database"), - tr("Unable to establish a database connection.\n" - "Mixxx requires Qt with SQLite support. Please read " - "the Qt SQL driver documentation for information on how " - "to build it.\n\n" - "Click OK to exit."), QMessageBox::Ok); + MainExecuter::callSync([this](void) { + QMessageBox::critical(0, tr("Cannot open database"), + tr("Unable to establish a database connection.\n" + "Mixxx requires Qt with SQLite support. Please read " + "the Qt SQL driver documentation for information on how " + "to build it.\n\n" + "Click OK to exit."), QMessageBox::Ok); + }); return false; } - int requiredSchemaVersion = 20; // TODO(xxx) avoid constant 20 - QString schemaFilename = m_pConfig->getResourcePath(); - schemaFilename.append("schema.xml"); - QString okToExit = tr("Click OK to exit."); - QString upgradeFailed = tr("Cannot upgrade database schema"); - QString upgradeToVersionFailed = tr("Unable to upgrade your database schema to version %1") - .arg(QString::number(requiredSchemaVersion)); - int result = SchemaManager::upgradeToSchemaVersion(schemaFilename, *m_database, requiredSchemaVersion); - if (result < 0) { - if (result == -1) { - QMessageBox::warning(0, upgradeFailed, - upgradeToVersionFailed + "\n" + - tr("Your %1 file may be outdated.").arg(schemaFilename) + - "\n\n" + okToExit, - QMessageBox::Ok); - } else if (result == -2) { - QMessageBox::warning(0, upgradeFailed, - upgradeToVersionFailed + "\n" + - tr("Your mixxxdb.sqlite file may be corrupt.") + "\n" + - tr("Try renaming it and restarting Mixxx.") + - "\n\n" + okToExit, - QMessageBox::Ok); - } else { // -3 - QMessageBox::warning(0, upgradeFailed, - upgradeToVersionFailed + "\n" + - tr("Your %1 file may be missing or invalid.").arg(schemaFilename) + - "\n\n" + okToExit, - QMessageBox::Ok); + MainExecuter::callSync([this](void) { + int requiredSchemaVersion = 20; // TODO(xxx) avoid constant 20 + QString schemaFilename = m_pConfig->getResourcePath(); + schemaFilename.append("schema.xml"); + QString okToExit = tr("Click OK to exit."); + QString upgradeFailed = tr("Cannot upgrade database schema"); + QString upgradeToVersionFailed = tr("Unable to upgrade your database schema to version %1") + .arg(QString::number(requiredSchemaVersion)); + int result = SchemaManager::upgradeToSchemaVersion(schemaFilename, *m_database, requiredSchemaVersion); + if (result < 0) { + if (result == -1) { + QMessageBox::warning(0, upgradeFailed, + upgradeToVersionFailed + "\n" + + tr("Your %1 file may be outdated.").arg(schemaFilename) + + "\n\n" + okToExit, + QMessageBox::Ok); + } else if (result == -2) { + QMessageBox::warning(0, upgradeFailed, + upgradeToVersionFailed + "\n" + + tr("Your mixxxdb.sqlite file may be corrupt.") + "\n" + + tr("Try renaming it and restarting Mixxx.") + + "\n\n" + okToExit, + QMessageBox::Ok); + } else { // -3 + QMessageBox::warning(0, upgradeFailed, + upgradeToVersionFailed + "\n" + + tr("Your %1 file may be missing or invalid.").arg(schemaFilename) + + "\n\n" + okToExit, + QMessageBox::Ok); + } + return false; } - return false; - } + }); return true; } @@ -274,43 +264,43 @@ bool TrackCollection::importDirectory(const QString& directory, TrackDAO& trackD return false; } - this->callSync( - [this, &it, &directory, &trackDao, &nameFilters, &pause ] (void) { +// this->callSync( +// [this, &it, &directory, &trackDao, &nameFilters, &pause ] (void) { - trackDao.addTracksPrepare(); /////// +// trackDao.addTracksPrepare(); /////// - QString absoluteFilePath = it.next(); +// QString absoluteFilePath = it.next(); + addTrackToChunk(it.next(), trackDao); // If the track is in the database, mark it as existing. This code gets exectuted // when other files in the same directory have changed (the directory hash has changed). - trackDao.markTrackLocationAsVerified(absoluteFilePath); - - // If the file already exists in the database, continue and go on to - // the next file. - - // If the file doesn't already exist in the database, then add - // it. If it does exist in the database, then it is either in the - // user's library OR the user has "removed" the track via - // "Right-Click -> Remove". These tracks stay in the library, but - // their mixxx_deleted column is 1. - if (!trackDao.trackExistsInDatabase(absoluteFilePath)) { - //qDebug() << "Loading" << it.fileName(); - emit(progressLoading(it.fileName())); - - TrackPointer pTrack = TrackPointer(new TrackInfoObject( - absoluteFilePath), &QObject::deleteLater); - if (trackDao.addTracksAdd(pTrack.data(), false)) { - // Successful added - // signal the main instance of TrackDao, that there is a - // new Track in the database - m_trackDao->databaseTrackAdded(pTrack); - } else { - qDebug() << "Track ("+absoluteFilePath+") could not be added"; - } - } - trackDao.addTracksPrepare(); /////// - - }, __PRETTY_FUNCTION__); +// trackDao.markTrackLocationAsVerified(absoluteFilePath); + +// // If the file already exists in the database, continue and go on to +// // the next file. + +// // If the file doesn't already exist in the database, then add +// // it. If it does exist in the database, then it is either in the +// // user's library OR the user has "removed" the track via +// // "Right-Click -> Remove". These tracks stay in the library, but +// // their mixxx_deleted column is 1. +// if (!trackDao.trackExistsInDatabase(absoluteFilePath)) { +// //qDebug() << "Loading" << it.fileName(); +// emit(progressLoading(it.fileName())); + +// TrackPointer pTrack = TrackPointer(new TrackInfoObject( +// absoluteFilePath), &QObject::deleteLater); +// if (trackDao.addTracksAdd(pTrack.data(), false)) { +// // Successful added +// // signal the main instance of TrackDao, that there is a +// // new Track in the database +// m_trackDao->databaseTrackAdded(pTrack); +// } else { +// qDebug() << "Track ("+absoluteFilePath+") could not be added"; +// } +// } +// m_trackDao->addTracksFinish(); +// }, __PRETTY_FUNCTION__); } emit(finishedLoading()); return true; @@ -373,3 +363,48 @@ void TrackCollection::createAndPopulateDbConnection() { m_analysisDao = new AnalysisDao(*m_database, m_pConfig); m_trackDao = new TrackDAO(*m_database, *m_cueDao, *m_playlistDao, *m_crateDao, *m_analysisDao, m_pConfig); } + +void TrackCollection::addTrackToChunk(const QString filePath, TrackDAO& trackDao) { + if (m_tracksListInCnunk.count() < MAX_CHUNK_SIZE) { + m_tracksListInCnunk.append(filePath); + } else { + callSync( [this, &trackDao] (void) { + addChunkToDatabase(trackDao); + }, "addTrackToChunk"); + } +} + +void TrackCollection::addChunkToDatabase(TrackDAO& trackDao) { + DBG() << "Adding chunk to DB: " << m_tracksListInCnunk; + trackDao.addTracksPrepare(); + foreach (QString trackPath, m_tracksListInCnunk) { + // If the track is in the database, mark it as existing. This code gets exectuted + // when other files in the same directory have changed (the directory hash has changed). + trackDao.markTrackLocationAsVerified(trackPath); + + // If the file already exists in the database, continue and go on to + // the next file. + + // If the file doesn't already exist in the database, then add + // it. If it does exist in the database, then it is either in the + // user's library OR the user has "removed" the track via + // "Right-Click -> Remove". These tracks stay in the library, but + // their mixxx_deleted column is 1. + if (!trackDao.trackExistsInDatabase(trackPath)) { + //qDebug() << "Loading" << it.fileName(); + emit(progressLoading(trackPath)); + + TrackPointer pTrack = TrackPointer(new TrackInfoObject(trackPath), &QObject::deleteLater); + if (trackDao.addTracksAdd(pTrack.data(), false)) { + // Successful added + // signal the main instance of TrackDao, that there is a + // new Track in the database + m_trackDao->databaseTrackAdded(pTrack); + } else { + qDebug() << "Track ("+trackPath+") could not be added"; + } + } + } + trackDao.addTracksFinish(); + m_tracksListInCnunk.clear(); +} diff --git a/src/library/trackcollection.h b/src/library/trackcollection.h index fb351d154d44..02dcea0ade94 100644 --- a/src/library/trackcollection.h +++ b/src/library/trackcollection.h @@ -52,11 +52,13 @@ class BpmDetector; @author Albert Santoni */ +// Helper class for calling some code in Main thread class MainExecuter : public QObject { Q_OBJECT public: ~MainExecuter() {} + // singletone static MainExecuter& getInstance() { static MainExecuter instance; return instance; @@ -115,6 +117,9 @@ public slots: QAtomicInt m_lamdaCount; }; + +// Separate thread providing database access. Holds all DAO. To make access to DB +// see callAsync/callSync methods. class TrackCollection : public QThread { Q_OBJECT public: @@ -174,6 +179,11 @@ class TrackCollection : public QThread { const QRegExp m_supportedFileExtensionsRegex; bool m_inCallSync; + QStringList m_tracksListInCnunk; + + void addTrackToChunk(const QString filePath, TrackDAO &trackDao); + void addChunkToDatabase(TrackDAO &trackDao); + }; #endif