File indexing completed on 2024-05-05 04:48:50

0001 /****************************************************************************************
0002  * Copyright (c) 2010-2013 Ralf Engels <ralf-engels@gmx.de>                             *
0003  *                                                                                      *
0004  * This program is free software; you can redistribute it and/or modify it under        *
0005  * the terms of the GNU General Public License as published by the Free Software        *
0006  * Foundation; either version 2 of the License, or (at your option) any later           *
0007  * version.                                                                             *
0008  *                                                                                      *
0009  * This program is distributed in the hope that it will be useful, but WITHOUT ANY      *
0010  * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A      *
0011  * PARTICULAR PURPOSE. See the GNU General Public License for more details.             *
0012  *                                                                                      *
0013  * You should have received a copy of the GNU General Public License along with         *
0014  * this program.  If not, see <http://www.gnu.org/licenses/>.                           *
0015  ****************************************************************************************/
0016 
0017 #define DEBUG_PREFIX "AbstractDirectoryWatcher"
0018 
0019 #include "AbstractDirectoryWatcher.h"
0020 
0021 #include "amarokconfig.h"
0022 #include "core/support/Debug.h"
0023 
0024 #include <KDirWatch>
0025 
0026 #include <QTimer>
0027 #include <QFileInfo>
0028 #include <QMutexLocker>
0029 
0030 static const int WATCH_INTERVAL = 60 * 1000; // = 60 seconds
0031 static const int DELAYED_SCAN_INTERVAL = 2 * 1000; // = 2 seconds
0032 
0033 AbstractDirectoryWatcher::AbstractDirectoryWatcher()
0034     : QObject()
0035     , ThreadWeaver::Job()
0036     , m_delayedScanTimer( nullptr )
0037     , m_watcher( nullptr )
0038     , m_aborted( false )
0039     , m_blocked( false )
0040 {
0041     m_delayedScanTimer = new QTimer( this );
0042     m_delayedScanTimer->setSingleShot( true );
0043     connect( m_delayedScanTimer, &QTimer::timeout, this, &AbstractDirectoryWatcher::delayTimeout );
0044 
0045     // -- create a new watcher
0046     m_watcher = new KDirWatch( this );
0047 
0048     connect( m_watcher, &KDirWatch::dirty,
0049              this, &AbstractDirectoryWatcher::delayedScan );
0050     connect( m_watcher, &KDirWatch::created,
0051              this, &AbstractDirectoryWatcher::delayedScan );
0052     connect( m_watcher, &KDirWatch::deleted,
0053              this, &AbstractDirectoryWatcher::delayedScan );
0054 
0055     m_watcher->startScan( false );
0056 }
0057 
0058 void
0059 AbstractDirectoryWatcher::run(ThreadWeaver::JobPointer self, ThreadWeaver::Thread *thread)
0060 {
0061     Q_UNUSED(self);
0062     Q_UNUSED(thread);
0063 
0064     // TODO: re-create the watcher if scanRecursively has changed
0065     QSet<QString> oldWatchDirs;
0066 
0067     forever {
0068         m_mutex.lock();
0069         m_waitCondition.wait( &m_mutex, WATCH_INTERVAL );
0070 
0071         if( m_aborted )
0072             break;
0073 
0074         // -- start scan
0075         if( AmarokConfig::monitorChanges() )
0076         {
0077             if( m_watcher->isStopped() )
0078             {
0079                 // Check if directories changed while we didn't have a watcher
0080                 QList<QUrl> urls;
0081                 foreach( const QString &path, collectionFolders() )
0082                 {
0083                     urls.append( QUrl::fromLocalFile( path ) );
0084                 }
0085                 Q_EMIT requestScan( urls, GenericScanManager::PartialUpdateScan );
0086                 m_watcher->startScan( true );
0087             }
0088 
0089 
0090             // -- update the KDirWatch with the current set of directories
0091             const QStringList colFolders=collectionFolders();
0092             QSet<QString> dirs(colFolders.begin(), colFolders.end());
0093 
0094             // - add new
0095             QSet<QString> newDirs = dirs - oldWatchDirs;
0096             foreach( const QString& dir, newDirs )
0097             {
0098                 m_watcher->addDir( dir,
0099                                    AmarokConfig::scanRecursively() ?
0100                                    KDirWatch::WatchSubDirs : KDirWatch::WatchDirOnly );
0101             }
0102 
0103             // - remove old
0104             QSet<QString> removeDirs = oldWatchDirs - dirs;
0105             foreach( const QString& dir, removeDirs )
0106             {
0107                 m_watcher->removeDir( dir );
0108             }
0109 
0110             oldWatchDirs = dirs;
0111 
0112         }
0113         else
0114         {
0115             if( !m_watcher->isStopped() )
0116                 m_watcher->stopScan();
0117         }
0118 
0119         m_mutex.unlock();
0120     }
0121 
0122 }
0123 
0124 void
0125 AbstractDirectoryWatcher::defaultBegin(const ThreadWeaver::JobPointer& self, ThreadWeaver::Thread *thread)
0126 {
0127     Q_EMIT started(self);
0128     ThreadWeaver::Job::defaultBegin(self, thread);
0129 }
0130 
0131 void
0132 AbstractDirectoryWatcher::defaultEnd(const ThreadWeaver::JobPointer& self, ThreadWeaver::Thread *thread)
0133 {
0134     ThreadWeaver::Job::defaultEnd(self, thread);
0135     if (!self->success()) {
0136         Q_EMIT failed(self);
0137     }
0138     Q_EMIT done(self);
0139 }
0140 
0141 void
0142 AbstractDirectoryWatcher::requestAbort()
0143 {
0144     DEBUG_BLOCK
0145 
0146     m_aborted = true;
0147     m_waitCondition.wakeAll();
0148 }
0149 
0150 void
0151 AbstractDirectoryWatcher::setBlockScanning( bool block )
0152 {
0153     m_blocked = block;
0154 
0155     // send out the old requests
0156     if( !m_blocked )
0157         delayTimeout();
0158 }
0159 
0160 void
0161 AbstractDirectoryWatcher::delayTimeout()
0162 {
0163     QMutexLocker locker( &m_dirsMutex );
0164 
0165     if( m_blocked || m_aborted )
0166         return;
0167 
0168     if( m_scanDirsRequested.isEmpty() )
0169         return;
0170 
0171     Q_EMIT requestScan( m_scanDirsRequested.values(), GenericScanManager::PartialUpdateScan );
0172     m_scanDirsRequested.clear();
0173 }
0174 
0175 void
0176 AbstractDirectoryWatcher::delayedScan( const QString &path )
0177 {
0178     QFileInfo info( path );
0179     if( info.isDir() )
0180         addDirToList( path );
0181     else
0182         addDirToList( info.path() );
0183 
0184     m_delayedScanTimer->start( DELAYED_SCAN_INTERVAL );
0185 }
0186 
0187 void
0188 AbstractDirectoryWatcher::addDirToList( const QString &directory )
0189 {
0190     QMutexLocker locker( &m_dirsMutex );
0191 
0192     debug() << "addDirToList for"<<directory;
0193 
0194     m_scanDirsRequested.insert( QUrl::fromUserInput(directory) );
0195 }
0196 
0197 
0198