/** Do a non-recursive import of all the songs in a directory. Does NOT decend into subdirectories. @param trackDao The track data access object which provides a connection to the database. We use this parameter in order to make this function callable from separate threads. You need to use a different DB connection for each thread. @return true if the scan completed without being cancelled. False if the scan was cancelled part-way through. */ bool TrackCollection::importDirectory(const QString& directory, TrackDAO& trackDao, const QStringList& nameFilters, volatile bool* cancel) { //qDebug() << "TrackCollection::importDirectory(" << directory<< ")"; emit(startedLoading()); // QFileInfoList files; //get a list of the contents of the directory and go through it. QDirIterator it(directory, nameFilters, QDir::Files | QDir::NoDotAndDotDot); while (it.hasNext()) { //If a flag was raised telling us to cancel the library scan then stop. if (*cancel) { return false; } QString absoluteFilePath = it.next(); // 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"; } } } emit(finishedLoading()); return true; }
LibraryScanner::LibraryScanner(QWidget* pParentWidget, TrackCollection* collection) : m_pCollection(collection), m_libraryHashDao(m_database), m_cueDao(m_database), m_playlistDao(m_database), m_crateDao(m_database), m_directoryDao(m_database), m_analysisDao(m_database, collection->getConfig()), m_trackDao(m_database, m_cueDao, m_playlistDao, m_crateDao, m_analysisDao, m_libraryHashDao, collection->getConfig()) { // Don't initialize m_database here, we need to do it in run() so the DB // conn is in the right thread. qDebug() << "Starting LibraryScanner thread."; // Move LibraryScanner to its own thread so that our signals/slots will // queue to our event loop. moveToThread(this); m_pool.moveToThread(this); unsigned static id = 0; // the id of this LibraryScanner, for debugging purposes setObjectName(QString("LibraryScanner %1").arg(++id)); m_pool.setMaxThreadCount(kScannerThreadPoolSize); // Listen to signals from our public methods (invoked by other threads) and // connect them to our slots to run the command on the scanner thread. connect(this, SIGNAL(startScan()), this, SLOT(slotStartScan())); // Force the GUI thread's TrackInfoObject cache to be cleared when a library // scan is finished, because we might have modified the database directly // when we detected moved files, and the TIOs corresponding to the moved // files would then have the wrong track location. connect(this, SIGNAL(scanFinished()), &(collection->getTrackDAO()), SLOT(clearCache())); connect(this, SIGNAL(trackAdded(TrackPointer)), &(collection->getTrackDAO()), SLOT(databaseTrackAdded(TrackPointer))); connect(this, SIGNAL(tracksMoved(QSet<int>, QSet<int>)), &(collection->getTrackDAO()), SLOT(databaseTracksMoved(QSet<int>, QSet<int>))); connect(this, SIGNAL(tracksChanged(QSet<int>)), &(collection->getTrackDAO()), SLOT(databaseTracksChanged(QSet<int>))); // Parented to pParentWidget so we don't need to delete it. LibraryScannerDlg* pProgress = new LibraryScannerDlg(pParentWidget); connect(this, SIGNAL(progressLoading(QString)), pProgress, SLOT(slotUpdate(QString))); connect(this, SIGNAL(progressHashing(QString)), pProgress, SLOT(slotUpdate(QString))); connect(this, SIGNAL(scanStarted()), pProgress, SLOT(slotScanStarted())); connect(this, SIGNAL(scanFinished()), pProgress, SLOT(slotScanFinished())); connect(pProgress, SIGNAL(scanCancelled()), this, SLOT(cancel())); connect(&m_trackDao, SIGNAL(progressVerifyTracksOutside(QString)), pProgress, SLOT(slotUpdate(QString))); connect(&m_trackDao, SIGNAL(progressCoverArt(QString)), pProgress, SLOT(slotUpdateCover(QString))); start(); }
void LibraryScanner::slotStartScan() { qDebug() << "LibraryScanner::slotStartScan"; QSet<QString> trackLocations = m_trackDao.getTrackLocations(); QHash<QString, int> directoryHashes = m_libraryHashDao.getDirectoryHashes(); QRegExp extensionFilter = QRegExp(SoundSourceProxy::supportedFileExtensionsRegex(), Qt::CaseInsensitive); QRegExp coverExtensionFilter = QRegExp(CoverArtUtils::supportedCoverArtExtensionsRegex(), Qt::CaseInsensitive); QStringList directoryBlacklist = ScannerUtil::getDirectoryBlacklist(); m_scannerGlobal = ScannerGlobalPointer( new ScannerGlobal(trackLocations, directoryHashes, extensionFilter, coverExtensionFilter, directoryBlacklist)); m_scannerGlobal->startTimer(); emit(scanStarted()); // Try to upgrade the library from 1.7 (XML) to 1.8+ (DB) if needed. If the // upgrade_filename already exists, then do not try to upgrade since we have // already done it. // TODO(XXX) SETTINGS_PATH may change in new Mixxx Versions. Here we need // the SETTINGS_PATH from Mixxx V <= 1.7 QString upgrade_filename = QDir::homePath().append("/").append(SETTINGS_PATH).append("DBUPGRADED"); qDebug() << "upgrade filename is " << upgrade_filename; QFile upgradefile(upgrade_filename); if (!upgradefile.exists()) { QTime t2; t2.start(); LegacyLibraryImporter libImport(m_trackDao, m_playlistDao); connect(&libImport, SIGNAL(progress(QString)), this, SIGNAL(progressLoading(QString))); ScopedTransaction transaction(m_database); libImport.import(); transaction.commit(); qDebug("Legacy importer took %d ms", t2.elapsed()); } // First, we're going to mark all the directories that we've previously // hashed as needing verification. As we search through the directory tree // when we rescan, we'll mark any directory that does still exist as // verified. m_libraryHashDao.invalidateAllDirectories(); // Mark all the tracks in the library as needing verification of their // existence. (ie. we want to check they're still on your hard drive where // we think they are) m_trackDao.invalidateTrackLocationsInLibrary(); qDebug() << "Recursively scanning library."; // Start scanning the library. This prepares insertion queries in TrackDAO // (must be called before calling addTracksAdd) and begins a transaction. m_trackDao.addTracksPrepare(); // Recursivly scan each directory in the directories table. QStringList dirs = m_directoryDao.getDirs(); // If there are no directories then we have nothing to do. Cleanup and // finish the scan immediately. if (dirs.isEmpty()) { slotFinishScan(); return; } // Queue up recursive scan tasks for every directory. When all tasks are // done, TaskWatcher will signal slotFinishScan. TaskWatcher* pWatcher = &m_scannerGlobal->getTaskWatcher(); connect(pWatcher, SIGNAL(allTasksDone()), this, SLOT(slotFinishScan())); foreach (const QString& dirPath, dirs) { // Acquire a security bookmark for this directory if we are in a // sandbox. For speed we avoid opening security bookmarks when recursive // scanning so that relies on having an open bookmark for the containing // directory. MDir dir(dirPath); queueTask(new RecursiveScanDirectoryTask(this, m_scannerGlobal, dir.dir(), dir.token())); }