/*! * \brief Scan a directory recursively for music and albumart. * Inserts, updates and removes any files any files found in the * database. * * \param directory Directory to scan * * \returns Nothing. */ void FileScanner::SearchDir(QString &directory) { m_startdir = directory; MusicLoadedMap music_files; MusicLoadedMap::Iterator iter; MythScreenStack *popupStack = GetMythMainWindow()->GetStack("popup stack"); QString message = QObject::tr("Searching for music files"); MythUIBusyDialog *busy = new MythUIBusyDialog(message, popupStack, "musicscanbusydialog"); if (busy->Create()) popupStack->AddScreen(busy, false); else busy = NULL; BuildFileList(m_startdir, music_files, 0); if (busy) busy->Close(); ScanMusic(music_files); ScanArtwork(music_files); message = QObject::tr("Updating music database"); MythUIProgressDialog *file_checking = new MythUIProgressDialog(message, popupStack, "scalingprogressdialog"); if (file_checking->Create()) { popupStack->AddScreen(file_checking, false); file_checking->SetTotal(music_files.size()); } else { delete file_checking; file_checking = NULL; } /* This can be optimised quite a bit by consolidating all commands via a lot of refactoring. 1) group all files of the same decoder type, and don't create/delete a Decoder pr. AddFileToDB. Or make Decoders be singletons, it should be a fairly simple change. 2) RemoveFileFromDB should group the remove into one big SQL. 3) UpdateFileInDB, same as 1. */ uint counter = 0; for (iter = music_files.begin(); iter != music_files.end(); iter++) { if (*iter == kFileSystem) AddFileToDB(iter.key()); else if (*iter == kDatabase) RemoveFileFromDB(iter.key ()); else if (*iter == kNeedUpdate) UpdateFileInDB(iter.key()); if (file_checking) { file_checking->SetProgress(++counter); qApp->processEvents(); } } if (file_checking) file_checking->Close(); // Cleanup orphaned entries from the database cleanDB(); }
/*! * \brief Scan a list of directories recursively for music and albumart. * Inserts, updates and removes any files any files found in the * database. * * \param dirList List of directories to scan * * \returns Nothing. */ void MusicFileScanner::SearchDirs(const QStringList &dirList) { QString host = gCoreContext->GetHostName(); if (IsRunning()) { // check how long the scanner has been running // if it's more than 60 minutes assume something went wrong QString lastRun = gCoreContext->GetSetting("MusicScannerLastRunStart", ""); if (!lastRun.isEmpty()) { QDateTime dtLastRun = QDateTime::fromString(lastRun, Qt::ISODate); if (dtLastRun.isValid()) { if (MythDate::current() > dtLastRun.addSecs(60*60)) { LOG(VB_GENERAL, LOG_INFO, "Music file scanner has been running for more than 60 minutes. Lets reset and try again"); gCoreContext->SendMessage(QString("MUSIC_SCANNER_ERROR %1 %2").arg(host).arg("Stalled")); // give the user time to read the notification before restarting the scan sleep(5); } else { LOG(VB_GENERAL, LOG_INFO, "Music file scanner is already running"); gCoreContext->SendMessage(QString("MUSIC_SCANNER_ERROR %1 %2").arg(host).arg("Already_Running")); return; } } } } //TODO: could sanity check the directory exists and is readable here? LOG(VB_GENERAL, LOG_INFO, "Music file scanner started"); gCoreContext->SendMessage(QString("MUSIC_SCANNER_STARTED %1").arg(host)); updateLastRunStart(); QString status = QString("running"); updateLastRunStatus(status); m_tracksTotal = m_tracksAdded = m_tracksUnchanged = m_tracksRemoved = m_tracksUpdated = 0; m_coverartTotal = m_coverartAdded = m_coverartUnchanged = m_coverartRemoved = m_coverartUpdated = 0; MusicLoadedMap music_files; MusicLoadedMap art_files; MusicLoadedMap::Iterator iter; for (int x = 0; x < dirList.count(); x++) { QString startDir = dirList[x]; m_startDirs.append(startDir + '/'); LOG(VB_GENERAL, LOG_INFO, QString("Searching '%1' for music files").arg(startDir)); BuildFileList(startDir, music_files, art_files, 0); } m_tracksTotal = music_files.count(); m_coverartTotal = art_files.count(); ScanMusic(music_files); ScanArtwork(art_files); LOG(VB_GENERAL, LOG_INFO, "Updating database"); /* This can be optimised quite a bit by consolidating all commands via a lot of refactoring. 1) group all files of the same decoder type, and don't create/delete a Decoder pr. AddFileToDB. Or make Decoders be singletons, it should be a fairly simple change. 2) RemoveFileFromDB should group the remove into one big SQL. 3) UpdateFileInDB, same as 1. */ for (iter = music_files.begin(); iter != music_files.end(); iter++) { if ((*iter).location == MusicFileScanner::kFileSystem) AddFileToDB(iter.key(), (*iter).startDir); else if ((*iter).location == MusicFileScanner::kDatabase) RemoveFileFromDB(iter.key(), (*iter).startDir); else if ((*iter).location == MusicFileScanner::kNeedUpdate) { UpdateFileInDB(iter.key(), (*iter).startDir); ++m_tracksUpdated; } } for (iter = art_files.begin(); iter != art_files.end(); iter++) { if ((*iter).location == MusicFileScanner::kFileSystem) AddFileToDB(iter.key(), (*iter).startDir); else if ((*iter).location == MusicFileScanner::kDatabase) RemoveFileFromDB(iter.key(), (*iter).startDir); else if ((*iter).location == MusicFileScanner::kNeedUpdate) { UpdateFileInDB(iter.key(), (*iter).startDir); ++m_coverartUpdated; } } // Cleanup orphaned entries from the database cleanDB(); QString trackStatus = QString("total tracks found: %1 (unchanged: %2, added: %3, removed: %4, updated %5)") .arg(m_tracksTotal).arg(m_tracksUnchanged).arg(m_tracksAdded) .arg(m_tracksRemoved).arg(m_tracksUpdated); QString coverartStatus = QString("total coverart found: %1 (unchanged: %2, added: %3, removed: %4, updated %5)") .arg(m_coverartTotal).arg(m_coverartUnchanged).arg(m_coverartAdded) .arg(m_coverartRemoved).arg(m_coverartUpdated); LOG(VB_GENERAL, LOG_INFO, "Music file scanner finished "); LOG(VB_GENERAL, LOG_INFO, trackStatus); LOG(VB_GENERAL, LOG_INFO, coverartStatus); gCoreContext->SendMessage(QString("MUSIC_SCANNER_FINISHED %1 %2 %3 %4 %5") .arg(host).arg(m_tracksTotal).arg(m_tracksAdded) .arg(m_coverartTotal).arg(m_coverartAdded)); updateLastRunEnd(); status = QString("success - %1 - %2").arg(trackStatus).arg(coverartStatus); updateLastRunStatus(status); }