Example #1
0
/*!
 * \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);
}