mirror of
https://github.com/tomahawk-player/tomahawk.git
synced 2025-08-01 03:40:16 +02:00
After discussion in #qt, I looked at our threading code and it's all wrong/unsafe. This properly threads scanning. Also makes it idle priority and splits it into a separate scan manager
This commit is contained in:
@@ -42,6 +42,7 @@ SET( tomahawkSources ${tomahawkSources}
|
||||
scrobbler.cpp
|
||||
shortcuthandler.cpp
|
||||
|
||||
scanmanager.cpp
|
||||
tomahawkapp.cpp
|
||||
main.cpp
|
||||
)
|
||||
@@ -81,6 +82,7 @@ SET( tomahawkHeaders ${tomahawkHeaders}
|
||||
musicscanner.h
|
||||
scriptresolver.h
|
||||
scrobbler.h
|
||||
scanmanager.h
|
||||
shortcuthandler.h
|
||||
)
|
||||
|
||||
|
@@ -8,14 +8,13 @@
|
||||
|
||||
using namespace Tomahawk;
|
||||
|
||||
|
||||
MusicScanner::MusicScanner( const QString& dir, quint32 bs )
|
||||
: QThread()
|
||||
: QObject()
|
||||
, m_dir( dir )
|
||||
, m_batchsize( bs )
|
||||
, m_dirLister( 0 )
|
||||
, m_dirListerThreadController( 0 )
|
||||
{
|
||||
moveToThread( this );
|
||||
|
||||
m_ext2mime.insert( "mp3", "audio/mpeg" );
|
||||
|
||||
#ifndef NO_OGG
|
||||
@@ -31,15 +30,6 @@ MusicScanner::MusicScanner( const QString& dir, quint32 bs )
|
||||
// m_ext2mime.insert( "mp4", "audio/mp4" );
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
MusicScanner::run()
|
||||
{
|
||||
QTimer::singleShot( 0, this, SLOT( startScan() ) );
|
||||
exec();
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
MusicScanner::startScan()
|
||||
{
|
||||
@@ -74,18 +64,21 @@ MusicScanner::scan()
|
||||
connect( this, SIGNAL( batchReady( QVariantList ) ),
|
||||
SLOT( commitBatch( QVariantList ) ), Qt::DirectConnection );
|
||||
|
||||
DirLister* lister = new DirLister( QDir( m_dir, 0 ), m_dirmtimes );
|
||||
m_dirListerThreadController = new QThread( this );
|
||||
m_dirLister = new DirLister( QDir( m_dir, 0 ), m_dirmtimes );
|
||||
m_dirLister->moveToThread( m_dirListerThreadController );
|
||||
|
||||
connect( lister, SIGNAL( fileToScan( QFileInfo ) ),
|
||||
connect( m_dirLister, SIGNAL( fileToScan( QFileInfo ) ),
|
||||
SLOT( scanFile( QFileInfo ) ), Qt::QueuedConnection );
|
||||
|
||||
// queued, so will only fire after all dirs have been scanned:
|
||||
connect( lister, SIGNAL( finished( const QMap<QString, unsigned int>& ) ),
|
||||
connect( m_dirLister, SIGNAL( finished( const QMap<QString, unsigned int>& ) ),
|
||||
SLOT( listerFinished( const QMap<QString, unsigned int>& ) ), Qt::QueuedConnection );
|
||||
|
||||
connect( lister, SIGNAL( finished() ), lister, SLOT( deleteLater() ) );
|
||||
|
||||
lister->start();
|
||||
connect( m_dirLister, SIGNAL( destroyed(QObject*) ), this, SLOT( listerDestroyed(QObject*) ) );
|
||||
|
||||
m_dirListerThreadController->start();
|
||||
QMetaObject::invokeMethod( m_dirLister, "go" );
|
||||
}
|
||||
|
||||
|
||||
@@ -111,9 +104,20 @@ MusicScanner::listerFinished( const QMap<QString, unsigned int>& newmtimes )
|
||||
qDebug() << "Skipped the following files (no tags / no valid audio):";
|
||||
foreach( const QString& s, m_skippedFiles )
|
||||
qDebug() << s;
|
||||
|
||||
m_dirLister->deleteLater();
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
MusicScanner::listerDestroyed( QObject* dirLister )
|
||||
{
|
||||
qDebug() << Q_FUNC_INFO;
|
||||
m_dirLister = 0;
|
||||
m_dirListerThreadController->deleteLater();
|
||||
m_dirListerThreadController = 0;
|
||||
}
|
||||
|
||||
void
|
||||
MusicScanner::commitBatch( const QVariantList& tracks )
|
||||
{
|
||||
|
@@ -5,69 +5,26 @@
|
||||
#include <taglib/tag.h>
|
||||
|
||||
#include <QVariantMap>
|
||||
#include <QThread>
|
||||
#include <QDir>
|
||||
#include <QFileInfo>
|
||||
#include <QString>
|
||||
#include <QDebug>
|
||||
#include <QDateTime>
|
||||
|
||||
class MusicScanner : public QThread
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
MusicScanner( const QString& dir, quint32 bs = 0 );
|
||||
|
||||
protected:
|
||||
void run();
|
||||
|
||||
signals:
|
||||
//void fileScanned( QVariantMap );
|
||||
void finished( int, int );
|
||||
void batchReady( const QVariantList& );
|
||||
|
||||
private:
|
||||
QVariant readFile( const QFileInfo& fi );
|
||||
|
||||
private slots:
|
||||
void listerFinished( const QMap<QString, unsigned int>& newmtimes );
|
||||
void scanFile( const QFileInfo& fi );
|
||||
void startScan();
|
||||
void scan();
|
||||
void setMtimes( const QMap<QString, unsigned int>& m );
|
||||
void commitBatch( const QVariantList& );
|
||||
|
||||
private:
|
||||
QString m_dir;
|
||||
QMap<QString,QString> m_ext2mime; // eg: mp3 -> audio/mpeg
|
||||
unsigned int m_scanned;
|
||||
unsigned int m_skipped;
|
||||
|
||||
QList<QString> m_skippedFiles;
|
||||
|
||||
QMap<QString, unsigned int> m_dirmtimes;
|
||||
QMap<QString, unsigned int> m_newdirmtimes;
|
||||
|
||||
QList<QVariant> m_scannedfiles;
|
||||
quint32 m_batchsize;
|
||||
};
|
||||
|
||||
|
||||
#include <QTimer>
|
||||
|
||||
// descend dir tree comparing dir mtimes to last known mtime
|
||||
// emit signal for any dir with new content, so we can scan it.
|
||||
// finally, emit the list of new mtimes we observed.
|
||||
class DirLister : public QThread
|
||||
class DirLister : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
DirLister( QDir d, QMap<QString, unsigned int>& mtimes )
|
||||
: QThread(), m_dir( d ), m_dirmtimes( mtimes )
|
||||
: QObject(), m_dir( d ), m_dirmtimes( mtimes )
|
||||
{
|
||||
qDebug() << Q_FUNC_INFO;
|
||||
moveToThread(this);
|
||||
}
|
||||
|
||||
~DirLister()
|
||||
@@ -75,13 +32,6 @@ public:
|
||||
qDebug() << Q_FUNC_INFO;
|
||||
}
|
||||
|
||||
protected:
|
||||
void run()
|
||||
{
|
||||
QTimer::singleShot(0,this,SLOT(go()));
|
||||
exec();
|
||||
}
|
||||
|
||||
signals:
|
||||
void fileToScan( QFileInfo );
|
||||
void finished( const QMap<QString, unsigned int>& );
|
||||
@@ -130,4 +80,46 @@ private:
|
||||
QMap<QString, unsigned int> m_newdirmtimes;
|
||||
};
|
||||
|
||||
class MusicScanner : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
MusicScanner( const QString& dir, quint32 bs = 0 );
|
||||
|
||||
signals:
|
||||
//void fileScanned( QVariantMap );
|
||||
void finished( int, int );
|
||||
void batchReady( const QVariantList& );
|
||||
|
||||
private:
|
||||
QVariant readFile( const QFileInfo& fi );
|
||||
|
||||
private slots:
|
||||
void listerFinished( const QMap<QString, unsigned int>& newmtimes );
|
||||
void listerDestroyed( QObject* dirLister );
|
||||
void scanFile( const QFileInfo& fi );
|
||||
void startScan();
|
||||
void scan();
|
||||
void setMtimes( const QMap<QString, unsigned int>& m );
|
||||
void commitBatch( const QVariantList& );
|
||||
|
||||
private:
|
||||
QString m_dir;
|
||||
QMap<QString,QString> m_ext2mime; // eg: mp3 -> audio/mpeg
|
||||
unsigned int m_scanned;
|
||||
unsigned int m_skipped;
|
||||
|
||||
QList<QString> m_skippedFiles;
|
||||
|
||||
QMap<QString, unsigned int> m_dirmtimes;
|
||||
QMap<QString, unsigned int> m_newdirmtimes;
|
||||
|
||||
QList<QVariant> m_scannedfiles;
|
||||
quint32 m_batchsize;
|
||||
|
||||
DirLister* m_dirLister;
|
||||
QThread* m_dirListerThreadController;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
58
src/scanmanager.cpp
Normal file
58
src/scanmanager.cpp
Normal file
@@ -0,0 +1,58 @@
|
||||
#include "scanmanager.h"
|
||||
#include "musicscanner.h"
|
||||
|
||||
#include <QDebug>
|
||||
#include <QThread>
|
||||
|
||||
ScanManager* ScanManager::s_instance = 0;
|
||||
|
||||
|
||||
ScanManager*
|
||||
ScanManager::instance()
|
||||
{
|
||||
return s_instance;
|
||||
}
|
||||
|
||||
|
||||
ScanManager::ScanManager( QObject* parent )
|
||||
: QObject( parent )
|
||||
, m_scanner( 0 )
|
||||
, m_musicScannerThreadController( 0 )
|
||||
{
|
||||
s_instance = this;
|
||||
}
|
||||
|
||||
|
||||
ScanManager::~ScanManager()
|
||||
{
|
||||
s_instance = 0;
|
||||
m_musicScannerThreadController->deleteLater();
|
||||
m_musicScannerThreadController = 0;
|
||||
m_scanner->deleteLater();
|
||||
m_scanner = 0;
|
||||
}
|
||||
|
||||
void
|
||||
ScanManager::runManualScan( const QString &path )
|
||||
{
|
||||
qDebug() << Q_FUNC_INFO;
|
||||
if ( !m_musicScannerThreadController && !m_scanner ) //still running if these are not zero
|
||||
{
|
||||
m_musicScannerThreadController = new QThread( this );
|
||||
MusicScanner* m_scanner = new MusicScanner( path );
|
||||
m_scanner->moveToThread( m_musicScannerThreadController );
|
||||
connect( m_scanner, SIGNAL( finished() ), m_scanner, SLOT( deleteLater() ) );
|
||||
connect( m_scanner, SIGNAL( destroyed(QObject*) ), this, SLOT( scanDestroyed(QObject*) ) );
|
||||
m_musicScannerThreadController->start( QThread::IdlePriority );
|
||||
QMetaObject::invokeMethod( m_scanner, "startScan" );
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ScanManager::scannerDestroyed( QObject* scanner )
|
||||
{
|
||||
qDebug() << Q_FUNC_INFO;
|
||||
m_scanner = 0;
|
||||
m_musicScannerThreadController->deleteLater();
|
||||
m_musicScannerThreadController = 0;
|
||||
}
|
32
src/scanmanager.h
Normal file
32
src/scanmanager.h
Normal file
@@ -0,0 +1,32 @@
|
||||
#ifndef SCANMANAGER_H
|
||||
#define SCANMANAGER_H
|
||||
|
||||
#include <QObject>
|
||||
|
||||
#include "dllmacro.h"
|
||||
|
||||
class MusicScanner;
|
||||
class QThread;
|
||||
|
||||
class DLLEXPORT ScanManager : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
static ScanManager* instance();
|
||||
|
||||
explicit ScanManager( QObject* parent = 0 );
|
||||
virtual ~ScanManager();
|
||||
|
||||
void runManualScan( const QString &path );
|
||||
|
||||
private slots:
|
||||
void scannerDestroyed( QObject *scanner );
|
||||
|
||||
private:
|
||||
static ScanManager* s_instance;
|
||||
|
||||
MusicScanner* m_scanner;
|
||||
QThread* m_musicScannerThreadController;
|
||||
};
|
||||
|
||||
#endif
|
@@ -22,6 +22,7 @@
|
||||
#include "sip/SipHandler.h"
|
||||
#include "sip/twitter/tomahawkoauthtwitter.h"
|
||||
#include <database/database.h>
|
||||
#include "scanmanager.h"
|
||||
|
||||
static QString
|
||||
md5( const QByteArray& src )
|
||||
@@ -145,11 +146,7 @@ SettingsDialog::~SettingsDialog()
|
||||
s->setScriptResolvers( resolvers );
|
||||
|
||||
if( rescan )
|
||||
{
|
||||
MusicScanner* scanner = new MusicScanner(s->scannerPath() );
|
||||
connect( scanner, SIGNAL( finished() ), scanner, SLOT( deleteLater() ) );
|
||||
scanner->start();
|
||||
}
|
||||
ScanManager::instance()->runManualScan( s->scannerPath() );
|
||||
|
||||
if( rejabber )
|
||||
{
|
||||
@@ -180,21 +177,6 @@ SettingsDialog::showPathSelector()
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
SettingsDialog::doScan()
|
||||
{
|
||||
// TODO this doesnt really belong here..
|
||||
QString path = ui->lineEditMusicPath->text();
|
||||
MusicScanner* scanner = new MusicScanner( path );
|
||||
connect( scanner, SIGNAL( finished() ), scanner, SLOT( deleteLater() ) );
|
||||
scanner->start();
|
||||
|
||||
QMessageBox::information( this, tr( "Scanning Started" ),
|
||||
tr( "Scanning now, check console output. TODO." ),
|
||||
QMessageBox::Ok );
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
SettingsDialog::onRejected()
|
||||
{
|
||||
|
@@ -48,8 +48,7 @@ protected:
|
||||
private slots:
|
||||
void onRejected();
|
||||
void showPathSelector();
|
||||
void doScan();
|
||||
|
||||
|
||||
void toggleUpnp( bool preferStaticEnabled );
|
||||
|
||||
void showProxySettings();
|
||||
|
@@ -5,6 +5,7 @@
|
||||
#include <QTimer>
|
||||
#include <QString>
|
||||
#include <QRegExp>
|
||||
#include <QThread>
|
||||
#include <utils/tomahawkutils.h>
|
||||
|
||||
using namespace gloox;
|
||||
|
@@ -10,7 +10,6 @@
|
||||
#include <QSharedPointer>
|
||||
#include <QMap>
|
||||
#include <QNetworkProxy>
|
||||
#include <QThread>
|
||||
|
||||
#include <string>
|
||||
|
||||
|
@@ -25,6 +25,7 @@
|
||||
#include "scriptresolver.h"
|
||||
#include "sourcelist.h"
|
||||
#include "shortcuthandler.h"
|
||||
#include "scanmanager.h"
|
||||
#include "tomahawksettings.h"
|
||||
|
||||
#include "audio/audioengine.h"
|
||||
@@ -149,10 +150,11 @@ TomahawkApp::TomahawkApp( int& argc, char *argv[] )
|
||||
|
||||
new TomahawkSettings( this );
|
||||
m_audioEngine = new AudioEngine;
|
||||
|
||||
new ScanManager( this );
|
||||
|
||||
new Pipeline( this );
|
||||
new SourceList( this );
|
||||
|
||||
|
||||
m_servent = new Servent( this );
|
||||
connect( m_servent, SIGNAL( ready() ), SLOT( setupSIP() ) );
|
||||
|
||||
|
@@ -32,13 +32,13 @@
|
||||
#include "widgets/welcomewidget.h"
|
||||
|
||||
#include "audiocontrols.h"
|
||||
#include "musicscanner.h"
|
||||
#include "settingsdialog.h"
|
||||
#include "tomahawksettings.h"
|
||||
#include "sourcelist.h"
|
||||
#include "transferview.h"
|
||||
#include "tomahawktrayicon.h"
|
||||
#include "playlist/dynamic/GeneratorInterface.h"
|
||||
#include "scanmanager.h"
|
||||
|
||||
using namespace Tomahawk;
|
||||
|
||||
@@ -249,23 +249,9 @@ TomahawkWindow::rescanCollectionManually()
|
||||
s->scannerPath(), &ok );
|
||||
s->setValue( "scannerpath", path );
|
||||
if ( ok && !path.isEmpty() )
|
||||
{
|
||||
MusicScanner* scanner = new MusicScanner( path );
|
||||
connect( scanner, SIGNAL( finished() ), this, SLOT( scanFinished() ) );
|
||||
scanner->start();
|
||||
}
|
||||
ScanManager::instance()->runManualScan( path );
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
TomahawkWindow::scanFinished()
|
||||
{
|
||||
qDebug() << Q_FUNC_INFO;
|
||||
MusicScanner* scanner = (MusicScanner*) sender();
|
||||
scanner->deleteLater();
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
TomahawkWindow::addPeerManually()
|
||||
{
|
||||
|
@@ -12,6 +12,7 @@
|
||||
|
||||
class QAction;
|
||||
|
||||
class MusicScanner;
|
||||
class AudioControls;
|
||||
class TomahawkTrayIcon;
|
||||
|
||||
@@ -46,11 +47,9 @@ public slots:
|
||||
void createPlaylist();
|
||||
void loadSpiff();
|
||||
void showSettingsDialog();
|
||||
void rescanCollectionManually();
|
||||
|
||||
private slots:
|
||||
void scanFinished();
|
||||
void rescanCollectionManually();
|
||||
|
||||
void onSipConnected();
|
||||
void onSipDisconnected();
|
||||
void onSipError();
|
||||
|
Reference in New Issue
Block a user