1
0
mirror of https://github.com/tomahawk-player/tomahawk.git synced 2025-04-14 13:01:53 +02:00

Implement watched folders and scan-on-startup. Folders are scanned after 10 seconds without a change. Also handles deferring scans of directories if attempted during an ongoing scan, both for recursive and non-recursive scans.

Fixes TWK-30 / THK-30.
This commit is contained in:
Jeff Mitchell 2011-04-02 00:18:29 -04:00
parent d818a7f697
commit 3bc496eaaf
14 changed files with 185 additions and 50 deletions

View File

@ -124,7 +124,7 @@ void
ACLSystem::authorizePath( const QString& dbid, const QString& path, ACLSystem::ACL type )
{
TomahawkSettings *s = TomahawkSettings::instance();
if( !s->scannerPath().contains( path ) )
if( !s->scannerPaths().contains( path ) )
{
qDebug() << "path selected is not in our scanner path!";
return;

View File

@ -32,6 +32,7 @@ Database::instance()
Database::Database( const QString& dbname, QObject* parent )
: QObject( parent )
, m_ready( false )
, m_impl( new DatabaseImpl( dbname, this ) )
, m_workerRW( new DatabaseWorker( m_impl, this, true ) )
{
@ -39,6 +40,7 @@ Database::Database( const QString& dbname, QObject* parent )
connect( m_impl, SIGNAL( indexReady() ), SIGNAL( indexReady() ) );
connect( m_impl, SIGNAL( indexReady() ), SIGNAL( ready() ) );
connect( m_impl, SIGNAL( indexReady() ), SLOT( setIsReadyTrue() ) );
m_workerRW->start();
}

View File

@ -52,6 +52,8 @@ public:
const bool indexReady() const { return m_indexReady; }
void loadIndex();
bool isReady() const { return m_ready; }
signals:
void indexReady(); // search index
@ -63,7 +65,11 @@ signals:
public slots:
void enqueue( QSharedPointer<DatabaseCommand> lc );
private slots:
void setIsReadyTrue() { m_ready = true; }
private:
bool m_ready;
DatabaseImpl* m_impl;
DatabaseWorker* m_workerRW;
QHash< QString, DatabaseWorker* > m_workers;

View File

@ -84,6 +84,8 @@ signals:
public slots:
private:
bool m_ready;
bool updateSchema( int currentver );
QSqlDatabase db;

View File

@ -66,29 +66,50 @@ TomahawkSettings::~TomahawkSettings()
QStringList
TomahawkSettings::scannerPath() const
TomahawkSettings::scannerPaths()
{
//FIXME: After enough time, remove this hack (and make const)
#ifndef TOMAHAWK_HEADLESS
return value( "scannerpath", QDesktopServices::storageLocation( QDesktopServices::MusicLocation ) ).toStringList();
if( value( "scannerpaths" ).isNull() )
setValue( "scannerpaths", value( "scannerpath" ) );
return value( "scannerpaths", QDesktopServices::storageLocation( QDesktopServices::MusicLocation ) ).toStringList();
#else
return value( "scannerpath", "" ).toStringList();
if( value( "scannerpaths" ).isNull() )
setValue( "scannerpaths", value( "scannerpath" ) );
return value( "scannerpaths", "" ).toStringList();
#endif
}
void
TomahawkSettings::setScannerPath( const QStringList& path )
TomahawkSettings::setScannerPaths( const QStringList& paths )
{
setValue( "scannerpath", path );
setValue( "scannerpaths", paths );
}
bool
TomahawkSettings::hasScannerPath() const
TomahawkSettings::hasScannerPaths() const
{
return contains( "scannerpath" );
//FIXME: After enough time, remove this hack
return contains( "scannerpaths" ) || contains( "scannerpath" );
}
bool
TomahawkSettings::watchForChanges() const
{
return value( "watchForChanges", true ).toBool();
}
void
TomahawkSettings::setWatchForChanges( bool watch )
{
setValue( "watchForChanges", watch );
}
void
TomahawkSettings::setAcceptedLegalWarning( bool accept )
{

View File

@ -41,9 +41,12 @@ public:
void applyChanges() { emit changed(); }
/// General settings
QStringList scannerPath() const; /// QDesktopServices::MusicLocation by default
void setScannerPath( const QStringList& path );
bool hasScannerPath() const;
QStringList scannerPaths(); /// QDesktopServices::MusicLocation by default
void setScannerPaths( const QStringList& paths );
bool hasScannerPaths() const;
bool watchForChanges() const;
void setWatchForChanges( bool watch );
bool acceptedLegalWarning() const;
void setAcceptedLegalWarning( bool accept );

View File

@ -31,14 +31,16 @@ using namespace Tomahawk;
void
DirLister::go()
{
scanDir( m_dir, 0 );
foreach( QString dir, m_dirs )
scanDir( QDir( dir, 0 ), 0, ( m_recursive ? DirLister::Recursive : DirLister::NonRecursive ) );
emit finished( m_newdirmtimes );
}
void
DirLister::scanDir( QDir dir, int depth )
DirLister::scanDir( QDir dir, int depth, DirLister::Mode mode )
{
qDebug() << "DirLister::scanDir scanning: " << dir.absolutePath();
QFileInfoList dirs;
const uint mtime = QFileInfo( dir.absolutePath() ).lastModified().toUTC().toTime_t();
m_newdirmtimes.insert( dir.absolutePath(), mtime );
@ -65,14 +67,18 @@ DirLister::scanDir( QDir dir, int depth )
foreach( const QFileInfo& di, dirs )
{
scanDir( di.absoluteFilePath(), depth + 1 );
if( mode == DirLister::Recursive || !m_dirmtimes.contains( di.absolutePath() ) )
scanDir( di.absoluteFilePath(), depth + 1, DirLister::Recursive );
else //should be the non-recursive case since the second test above should only happen with a new dir
scanDir( di.absoluteFilePath(), depth + 1, DirLister::MTimeOnly );
}
}
MusicScanner::MusicScanner( const QStringList& dirs, quint32 bs )
MusicScanner::MusicScanner( const QStringList& dirs, bool recursive, quint32 bs )
: QObject()
, m_dirs( dirs )
, m_recursive( recursive )
, m_batchsize( bs )
, m_dirLister( 0 )
, m_dirListerThreadController( 0 )
@ -150,8 +156,7 @@ MusicScanner::scan()
m_dirListerThreadController = new QThread( this );
//FIXME: MULTIPLECOLLECTIONDIRS
m_dirLister = new DirLister( QDir( m_dirs.first(), 0 ), m_dirmtimes );
m_dirLister = new DirLister( m_dirs, m_dirmtimes, m_recursive );
m_dirLister->moveToThread( m_dirListerThreadController );
connect( m_dirLister, SIGNAL( fileToScan( QFileInfo ) ),

View File

@ -39,8 +39,15 @@ class DirLister : public QObject
Q_OBJECT
public:
DirLister( QDir d, QMap<QString, unsigned int>& mtimes )
: QObject(), m_dir( d ), m_dirmtimes( mtimes )
enum Mode {
NonRecursive,
Recursive,
MTimeOnly
};
DirLister( QStringList dirs, QMap<QString, unsigned int>& mtimes, bool recursive )
: QObject(), m_dirs( dirs ), m_dirmtimes( mtimes ), m_recursive( recursive )
{
qDebug() << Q_FUNC_INFO;
}
@ -56,11 +63,13 @@ signals:
private slots:
void go();
void scanDir( QDir dir, int depth );
void scanDir( QDir dir, int depth, DirLister::Mode mode );
private:
QDir m_dir;
QStringList m_dirs;
QMap<QString, unsigned int> m_dirmtimes;
bool m_recursive;
QMap<QString, unsigned int> m_newdirmtimes;
};
@ -69,7 +78,7 @@ class MusicScanner : public QObject
Q_OBJECT
public:
MusicScanner( const QStringList& dirs, quint32 bs = 0 );
MusicScanner( const QStringList& dirs, bool recursive = true, quint32 bs = 0 );
~MusicScanner();
signals:
@ -105,6 +114,7 @@ private:
QMap<QString, unsigned int> m_newdirmtimes;
QList<QVariant> m_scannedfiles;
bool m_recursive;
quint32 m_batchsize;
DirLister* m_dirLister;

View File

@ -45,20 +45,33 @@ ScanManager::ScanManager( QObject* parent )
: QObject( parent )
, m_scanner( 0 )
, m_musicScannerThreadController( 0 )
, m_currScannerPaths()
, m_dirWatcher( 0 )
, m_queuedScanTimer( 0 )
, m_deferredScanTimer( 0 )
, m_queuedChangedDirs()
, m_deferredDirs()
{
s_instance = this;
m_dirWatcher = new QFileSystemWatcher( parent );
m_queuedScanTimer = new QTimer( this );
m_queuedScanTimer->setSingleShot( true );
m_deferredScanTimer = new QTimer( this );
m_deferredScanTimer->setSingleShot( false );
m_deferredScanTimer->setInterval( 1000 );
m_dirWatcher = new QFileSystemWatcher( this );
connect( TomahawkSettings::instance(), SIGNAL( changed() ), SLOT( onSettingsChanged() ) );
connect( m_queuedScanTimer, SIGNAL( timeout() ), SLOT( queuedScanTimeout() ) );
connect( m_deferredScanTimer, SIGNAL( timeout() ), SLOT( deferredScanTimeout() ) );
connect( m_dirWatcher, SIGNAL( directoryChanged( const QString & ) ), SLOT( handleChangedDir( const QString & ) ) );
if ( TomahawkSettings::instance()->hasScannerPath() )
m_currScannerPath = TomahawkSettings::instance()->scannerPath();
if ( TomahawkSettings::instance()->hasScannerPaths() )
m_currScannerPaths = TomahawkSettings::instance()->scannerPaths();
qDebug() << "loading initial directories to watch";
QTimer::singleShot( 1000, this, SLOT( startupWatchPaths() ) );
m_deferredScanTimer->start();
}
@ -91,14 +104,18 @@ ScanManager::~ScanManager()
void
ScanManager::onSettingsChanged()
{
if ( TomahawkSettings::instance()->hasScannerPath() &&
m_currScannerPath != TomahawkSettings::instance()->scannerPath() )
if ( TomahawkSettings::instance()->hasScannerPaths() &&
m_currScannerPaths != TomahawkSettings::instance()->scannerPaths() )
{
m_currScannerPath = TomahawkSettings::instance()->scannerPath();
m_currScannerPaths = TomahawkSettings::instance()->scannerPaths();
m_dirWatcher->removePaths( m_dirWatcher->directories() );
m_dirWatcher->addPaths( m_currScannerPath );
runManualScan( m_currScannerPath );
m_dirWatcher->addPaths( m_currScannerPaths );
runManualScan( m_currScannerPaths );
}
if( TomahawkSettings::instance()->watchForChanges() &&
!m_queuedChangedDirs.isEmpty() )
runManualScan( m_queuedChangedDirs, false );
}
@ -107,21 +124,21 @@ ScanManager::startupWatchPaths()
{
qDebug() << Q_FUNC_INFO;
if( !Database::instance() )
if( !Database::instance() || ( Database::instance() && !Database::instance()->isReady() ) )
{
QTimer::singleShot( 1000, this, SLOT( startupWatchPaths() ) );
return;
}
DatabaseCommand_DirMtimes* cmd = new DatabaseCommand_DirMtimes( m_currScannerPath );
connect( cmd, SIGNAL( done( QMap<QString, unsigned int> ) ),
SLOT( setInitialPaths( QMap<QString, unsigned int> ) ) );
Database::instance()->enqueue( QSharedPointer<DatabaseCommand>(cmd) );
DatabaseCommand_DirMtimes* cmd = new DatabaseCommand_DirMtimes( m_currScannerPaths );
connect( cmd, SIGNAL( done( QMap< QString, unsigned int > ) ),
SLOT( setInitialPaths( QMap< QString, unsigned int > ) ) );
Database::instance()->enqueue( QSharedPointer< DatabaseCommand >( cmd ) );
}
void
ScanManager::setInitialPaths( QMap<QString, unsigned int> pathMap )
ScanManager::setInitialPaths( QMap< QString, unsigned int > pathMap )
{
qDebug() << Q_FUNC_INFO;
foreach( QString path, pathMap.keys() )
@ -129,27 +146,45 @@ ScanManager::setInitialPaths( QMap<QString, unsigned int> pathMap )
qDebug() << "Adding " << path << " to watcher";
m_dirWatcher->addPath( path );
}
runManualScan( TomahawkSettings::instance()->scannerPaths() );
}
void
ScanManager::runManualScan( const QStringList& path )
ScanManager::runManualScan( const QStringList& paths, bool recursive )
{
qDebug() << Q_FUNC_INFO;
if ( !m_musicScannerThreadController && !m_scanner ) //still running if these are not zero
{
m_musicScannerThreadController = new QThread( this );
m_scanner = new MusicScanner( path );
QStringList allPaths = paths;
foreach( QString path, m_deferredDirs[recursive] )
{
if( !allPaths.contains( path ) )
allPaths << path;
}
m_scanner = new MusicScanner( paths, recursive );
m_scanner->moveToThread( m_musicScannerThreadController );
connect( m_scanner, SIGNAL( finished() ), SLOT( scannerFinished() ) );
connect( m_scanner, SIGNAL( addWatchedDirs( const QStringList & ) ), SLOT( addWatchedDirs( const QStringList & ) ) );
connect( m_scanner, SIGNAL( removeWatchedDir( const QString & ) ), SLOT( removeWatchedDir( const QString & ) ) );
m_musicScannerThreadController->start( QThread::IdlePriority );
QMetaObject::invokeMethod( m_scanner, "startScan" );
m_deferredDirs[recursive].clear();
}
else
qDebug() << "Could not run manual scan, old scan still running";
{
qDebug() << "Could not run manual scan, old scan still running; deferring paths";
foreach( QString path, paths )
{
if( !m_deferredDirs[recursive].contains( path ) )
{
qDebug() << "Deferring path " << path;
m_deferredDirs[recursive] << path;
}
}
}
}
void
@ -180,6 +215,34 @@ ScanManager::handleChangedDir( const QString& path )
{
qDebug() << Q_FUNC_INFO;
qDebug() << "Dir changed: " << path;
m_queuedChangedDirs << path;
if( TomahawkSettings::instance()->watchForChanges() )
m_queuedScanTimer->start( 10000 );
}
void
ScanManager::queuedScanTimeout()
{
qDebug() << Q_FUNC_INFO;
runManualScan( m_queuedChangedDirs, false );
m_queuedChangedDirs.clear();
}
void
ScanManager::deferredScanTimeout()
{
if( !m_deferredDirs[true].isEmpty() )
{
qDebug() << "Running scan for deferred recursive paths";
runManualScan( m_deferredDirs[true], true );
}
else if( !m_deferredDirs[false].isEmpty() )
{
qDebug() << "Running scan for deferred non-recursive paths";
runManualScan( m_deferredDirs[false], false );
}
}

View File

@ -19,15 +19,17 @@
#ifndef SCANMANAGER_H
#define SCANMANAGER_H
#include <QHash>
#include <QMap>
#include <QObject>
#include <QStringList>
#include <QMap>
#include "dllmacro.h"
class MusicScanner;
class QThread;
class QFileSystemWatcher;
class QTimer;
class ScanManager : public QObject
{
@ -38,17 +40,16 @@ public:
explicit ScanManager( QObject* parent = 0 );
virtual ~ScanManager();
void runManualScan( const QStringList& path );
signals:
void finished();
public slots:
void runManualScan( const QStringList& paths, bool recursive = true );
void handleChangedDir( const QString& path );
void addWatchedDirs( const QStringList& paths );
void removeWatchedDir( const QString& path );
void setInitialPaths( QMap<QString, unsigned int> pathMap );
void setInitialPaths( QMap< QString, unsigned int > pathMap );
private slots:
void scannerQuit();
@ -56,6 +57,8 @@ private slots:
void scannerDestroyed( QObject* scanner );
void startupWatchPaths();
void queuedScanTimeout();
void deferredScanTimeout();
void onSettingsChanged();
@ -64,8 +67,13 @@ private:
MusicScanner* m_scanner;
QThread* m_musicScannerThreadController;
QStringList m_currScannerPath;
QStringList m_currScannerPaths;
QFileSystemWatcher* m_dirWatcher;
QTimer* m_queuedScanTimer;
QTimer* m_deferredScanTimer;
QStringList m_queuedChangedDirs;
QHash< bool, QStringList > m_deferredDirs;
};
#endif

View File

@ -87,7 +87,8 @@ SettingsDialog::SettingsDialog( QWidget *parent )
// MUSIC SCANNER
//FIXME: MULTIPLECOLLECTIONDIRS
ui->lineEditMusicPath->setText( s->scannerPath().first() );
ui->lineEditMusicPath->setText( s->scannerPaths().first() );
ui->checkBoxWatchForChanges->setChecked( s->watchForChanges() );
// LAST FM
ui->checkBoxEnableLastfm->setChecked( s->scrobblingEnabled() );
@ -134,7 +135,8 @@ SettingsDialog::~SettingsDialog()
s->setExternalHostname( ui->staticHostName->text() );
s->setExternalPort( ui->staticPort->value() );
s->setScannerPath( QStringList( ui->lineEditMusicPath->text() ) );
s->setScannerPaths( QStringList( ui->lineEditMusicPath->text() ) );
s->setWatchForChanges( ui->checkBoxWatchForChanges->isChecked() );
s->setScrobblingEnabled( ui->checkBoxEnableLastfm->isChecked() );
s->setLastFmUsername( ui->lineEditLastfmUsername->text() );

View File

@ -66,7 +66,7 @@
</sizepolicy>
</property>
<property name="placeholderText">
<string>e.g. user@example.com</string>
<string>e.g. user@example.com</string>
</property>
</widget>
</item>
@ -454,6 +454,19 @@
</item>
</layout>
</item>
<item>
<widget class="QCheckBox" name="checkBoxWatchForChanges">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Watch for changes</string>
</property>
</widget>
</item>
</layout>
</item>
<item>

View File

@ -285,7 +285,7 @@ TomahawkApp::TomahawkApp( int& argc, char *argv[] )
}
#ifndef TOMAHAWK_HEADLESS
if ( !TomahawkSettings::instance()->hasScannerPath() )
if ( !TomahawkSettings::instance()->hasScannerPaths() )
{
m_mainwindow->showSettingsDialog();
}

View File

@ -319,8 +319,8 @@ TomahawkWindow::showSettingsDialog()
void
TomahawkWindow::updateCollectionManually()
{
if ( TomahawkSettings::instance()->hasScannerPath() )
ScanManager::instance()->runManualScan( TomahawkSettings::instance()->scannerPath() );
if ( TomahawkSettings::instance()->hasScannerPaths() )
ScanManager::instance()->runManualScan( TomahawkSettings::instance()->scannerPaths() );
}