diff --git a/ChangeLog b/ChangeLog index 4382caeff..01d96e216 100644 --- a/ChangeLog +++ b/ChangeLog @@ -63,6 +63,7 @@ 36. Add action icons to grouped-albums style playlists at the top level. 37. Scale image in lyrics view if it is less than 300x300px 38. Allow to move streams to different categories via drag-n-drop. +39. Read/write device cache files in non-gui thread. 0.9.2 ----- diff --git a/devices/fsdevice.cpp b/devices/fsdevice.cpp index 45ac650e0..7a6502dfe 100644 --- a/devices/fsdevice.cpp +++ b/devices/fsdevice.cpp @@ -60,34 +60,72 @@ const QLatin1String FsDevice::constUseCacheKey("use_cache"); // Cantata extensio const QLatin1String FsDevice::constDefCoverFileName("cover.jpg"); const QLatin1String FsDevice::constAutoScanKey("auto_scan"); // Cantata extension! -MusicScanner::MusicScanner(const QString &f) +MusicScanner::MusicScanner() : QThread(0) - , library(0) , stopRequested(false) , count(0) , lastUpdate(0) { - folder=Utils::fixPath(QDir(f).absolutePath()); moveToThread(this); + start(); } MusicScanner::~MusicScanner() { - delete library; } -void MusicScanner::run() +void MusicScanner::scan(const QString &folder, const QString &cacheFile, bool readCache, const QSet &existingSongs) { + if (!cacheFile.isEmpty() && readCache) { + MusicLibraryItemRoot *lib=new MusicLibraryItemRoot; + MusicLibraryModel::convertCache(cacheFile); + readProgress(0.0); + if (lib->fromXML(cacheFile, QDateTime(), folder)) { + fixLibrary(lib); + if (!stopRequested) { + emit libraryUpdated(lib); + } + return; + } + } + + if (stopRequested) { + return; + } + existing=existingSongs; count=0; lastUpdate=0; - library = new MusicLibraryItemRoot; - scanFolder(folder, 0); - if (MPDParseUtils::groupSingle()) { - library->groupSingleTracks(); + MusicLibraryItemRoot *library = new MusicLibraryItemRoot; + QString topLevel=Utils::fixPath(QDir(folder).absolutePath()); + scanFolder(library, topLevel, topLevel, 0); + + if (!stopRequested) { + fixLibrary(library); + if (!cacheFile.isEmpty()) { + writeProgress(0.0); + library->toXML(cacheFile, QDateTime(), this); + } + emit libraryUpdated(library); + } else { + delete library; } - if (MPDParseUtils::groupMultiple()) { - library->groupMultipleArtists(); +} + +void MusicScanner::fixLibrary(MusicLibraryItemRoot *lib) +{ + if (!stopRequested && MPDParseUtils::groupSingle()) { + lib->groupSingleTracks(); } + if (!stopRequested && MPDParseUtils::groupMultiple()) { + lib->groupMultipleArtists(); + } +} + +void MusicScanner::saveCache(const QString &cache, MusicLibraryItemRoot *lib) +{ + writeProgress(0.0); + lib->toXML(cache, QDateTime(), this); + emit cacheSaved(); } void MusicScanner::stop() @@ -96,14 +134,7 @@ void MusicScanner::stop() Utils::stopThread(this); } -MusicLibraryItemRoot * MusicScanner::takeLibrary() -{ - MusicLibraryItemRoot *lib=library; - library=0; - return lib; -} - -void MusicScanner::scanFolder(const QString &f, int level) +void MusicScanner::scanFolder(MusicLibraryItemRoot *library, const QString &topLevel, const QString &f, int level) { if (stopRequested) { return; @@ -118,10 +149,10 @@ void MusicScanner::scanFolder(const QString &f, int level) return; } if (info.isDir()) { - scanFolder(info.absoluteFilePath(), level+1); + scanFolder(library, topLevel, info.absoluteFilePath(), level+1); } else if(info.isReadable()) { Song song; - QString fname=info.absoluteFilePath().mid(folder.length()); + QString fname=info.absoluteFilePath().mid(topLevel.length()); QSet::iterator it=existing.find(song); if (existing.end()==it) { song=Tags::read(info.absoluteFilePath()); @@ -159,6 +190,16 @@ void MusicScanner::scanFolder(const QString &f, int level) } } +void MusicScanner::readProgress(double pc) +{ + emit readingCache((int)pc); +} + +void MusicScanner::writeProgress(double pc) +{ + emit savingCache((int)pc); +} + bool FsDevice::readOpts(const QString &fileName, DeviceOptions &opts, bool readAll) { QFile file(fileName); @@ -275,57 +316,46 @@ void FsDevice::writeOpts(const QString &fileName, const DeviceOptions &opts, boo FsDevice::FsDevice(DevicesModel *m, Solid::Device &dev) : Device(m, dev) + , state(Idle) , scanned(false) + , cacheProgress(-1) , scanner(0) { + initScaner(); } FsDevice::FsDevice(DevicesModel *m, const QString &name, const QString &id) : Device(m, name, id) + , state(Idle) , scanned(false) + , cacheProgress(-1) , scanner(0) { + initScaner(); } FsDevice::~FsDevice() { - stopScanner(false); + stopScanner(); } void FsDevice::rescan(bool full) { spaceInfo.setDirty(); // If this is the first scan (scanned=false) and we are set to use cache, attempt to load that before scanning - if (isIdle() && (scanned || !opts.useCache || !readCache())) { - scanned=true; + if (isIdle()) { if (full) { + removeCache(); clear(); } - removeCache(); startScanner(full); + scanned=true; } } -bool FsDevice::readCache() -{ - if (opts.useCache) { - MusicLibraryItemRoot *root=new MusicLibraryItemRoot; - MusicLibraryModel::convertCache(cacheFileName()); - if (root->fromXML(cacheFileName(), QDateTime(), audioFolder)) { - update=root; - scanned=true; - QTimer::singleShot(0, this, SLOT(cacheRead())); - return true; - } - delete root; - } - - return false; -} - void FsDevice::stop() { if (0!=scanner) { - scanner->stop(); + stopScanner(); } } @@ -589,40 +619,52 @@ void FsDevice::cleanDirsResult(int status) emit actionStatus(status); } -void FsDevice::cacheRead() +void FsDevice::initScaner() { - setStatusMessage(QString()); - emit updating(udi(), false); + if (!scanner) { + static bool registeredTypes=false; + + if (!registeredTypes) { + qRegisterMetaType >("QSet"); + registeredTypes=true; + } + scanner=new MusicScanner(); + connect(scanner, SIGNAL(libraryUpdated(MusicLibraryItemRoot *)), this, SLOT(libraryUpdated(MusicLibraryItemRoot *))); + connect(scanner, SIGNAL(songCount(int)), this, SLOT(songCount(int))); + connect(scanner, SIGNAL(cacheSaved()), this, SLOT(cacheSaved())); + connect(scanner, SIGNAL(savingCache(int)), this, SLOT(savingCache(int))); + connect(scanner, SIGNAL(readingCache(int)), this, SLOT(readingCache(int))); + connect(this, SIGNAL(scan(const QString &, const QString &, bool, const QSet &)), scanner, SLOT(scan(const QString &, const QString &, bool, const QSet &))); + connect(this, SIGNAL(saveCache(const QString &, MusicLibraryItemRoot *)), scanner, SLOT(saveCache(const QString &, MusicLibraryItemRoot *))); + } } void FsDevice::startScanner(bool fullScan) { - stopScanner(); - scanner=new MusicScanner(audioFolder); - connect(scanner, SIGNAL(finished()), this, SLOT(libraryUpdated())); - connect(scanner, SIGNAL(songCount(int)), this, SLOT(songCount(int))); + initScaner(); QSet existingSongs; if (!fullScan) { existingSongs=allSongs(); } - scanner->start(existingSongs); + state=Updating; + emit scan(audioFolder, opts.useCache ? cacheFileName() : QString(), !scanned, existingSongs); setStatusMessage(i18n("Updating...")); emit updating(udi(), true); } -void FsDevice::stopScanner(bool showStatus) +void FsDevice::stopScanner() { - // Scan for music in a separate thread... + scanner->stop(); + state=Idle; + if (scanner) { - disconnect(scanner, SIGNAL(finished()), this, SLOT(libraryUpdated())); + disconnect(scanner, SIGNAL(libraryUpdated(MusicLibraryItemRoot *)), this, SLOT(libraryUpdated(MusicLibraryItemRoot *))); disconnect(scanner, SIGNAL(songCount(int)), this, SLOT(songCount(int))); - scanner->stop(); + disconnect(scanner, SIGNAL(cacheSaved()), this, SLOT(cacheSaved())); + disconnect(scanner, SIGNAL(savingCache(int)), this, SLOT(savingCache(int))); + disconnect(scanner, SIGNAL(readingCache(int)), this, SLOT(readingCache(int))); scanner->deleteLater(); scanner=0; - if (showStatus) { - setStatusMessage(QString()); - emit updating(udi(), false); - } } } @@ -636,18 +678,19 @@ void FsDevice::clear() const } } -void FsDevice::libraryUpdated() +void FsDevice::libraryUpdated(MusicLibraryItemRoot *lib) { - if (scanner) { - if (update) { - delete update; - } - update=scanner->takeLibrary(); - if (opts.useCache && update) { - update->toXML(cacheFileName()); - } - stopScanner(); + cacheProgress=-1; + if (update) { + delete update; } + update=lib; + if (opts.useCache && update) { + update->toXML(cacheFileName()); + } + setStatusMessage(QString()); + state=Idle; + emit updating(udi(), false); } QString FsDevice::cacheFileName() const @@ -661,10 +704,17 @@ QString FsDevice::cacheFileName() const void FsDevice::saveCache() { if (opts.useCache) { - toXML(cacheFileName()); + state=SavingCache; + emit saveCache(cacheFileName(), this); } } +void FsDevice::cacheSaved() +{ + state=Idle; + cacheProgress=-1; +} + void FsDevice::removeCache() { QString cacheFile(cacheFileName()); @@ -679,3 +729,21 @@ void FsDevice::removeCache() QFile::remove(oldCache); } } + +void FsDevice::readingCache(int pc) +{ + cacheStatus(i18n("Reading cache"), pc); +} + +void FsDevice::savingCache(int pc) +{ + cacheStatus(i18n("Saving cache"), pc); +} + +void FsDevice::cacheStatus(const QString &msg, int prog) +{ + if (prog!=cacheProgress) { + cacheProgress=prog; + setStatusMessage(i18nc("Message percent", "%1 %2%").arg(msg).arg(cacheProgress)); + } +} diff --git a/devices/fsdevice.h b/devices/fsdevice.h index 240389549..8ddf4b4ed 100644 --- a/devices/fsdevice.h +++ b/devices/fsdevice.h @@ -28,38 +28,41 @@ #include "song.h" #include "utils.h" #include "freespaceinfo.h" +#include "musiclibraryitemroot.h" #include #include -class MusicLibraryItemRoot; -class MusicScanner : public QThread +class MusicScanner : public QThread, public MusicLibraryProgressMonitor { Q_OBJECT public: - MusicScanner(const QString &f); + MusicScanner(); virtual ~MusicScanner(); - void run(); - void start(const QSet &existingSongs) { - existing=existingSongs; - QThread::start(); - } void stop(); bool wasStopped() const { return stopRequested; } - MusicLibraryItemRoot * takeLibrary(); + void readProgress(double pc); + void writeProgress(double pc); + +public Q_SLOTS: + void scan(const QString &folder, const QString &cacheFile, bool readCache, const QSet &existingSongs); + void saveCache(const QString &cache, MusicLibraryItemRoot *lib); Q_SIGNALS: void songCount(int c); + void libraryUpdated(MusicLibraryItemRoot *); + void cacheSaved(); + void readingCache(int pc); + void savingCache(int pc); private: - void scanFolder(const QString &f, int level); + void fixLibrary(MusicLibraryItemRoot *lib); + void scanFolder(MusicLibraryItemRoot *library, const QString &topLevel, const QString &f, int level); private: QSet existing; - QString folder; - MusicLibraryItemRoot *library; bool stopRequested; int count; int lastUpdate; @@ -70,6 +73,12 @@ class FsDevice : public Device Q_OBJECT public: + enum State { + Idle, + Updating, + SavingCache + }; + static const QLatin1String constCantataCacheFile; static const QLatin1String constCantataSettingsFile; static const QLatin1String constMusicFilenameSchemeKey; @@ -94,7 +103,7 @@ public: void rescan(bool full=true); void stop(); - bool isRefreshing() const { return 0!=scanner; } + bool isRefreshing() const { return Idle!=state; } QString path() const { return audioFolder; } QString coverFile() const { return opts.coverName; } void addSong(const Song &s, bool overwrite, bool copyCover); @@ -108,23 +117,35 @@ public: void removeCache(); bool isStdFs() const { return true; } +Q_SIGNALS: + // For talking to scanner... + void scan(const QString &folder, const QString &cacheFile, bool readCache, const QSet &existingSongs); + void saveCache(const QString &cacheFile, MusicLibraryItemRoot *lib); + protected: - bool readCache(); + void initScaner(); void startScanner(bool fullScan=true); - void stopScanner(bool showStatus=true); + void stopScanner(); void clear() const; protected Q_SLOTS: - void cacheRead(); - void libraryUpdated(); + void cacheSaved(); + void libraryUpdated(MusicLibraryItemRoot *lib); void percent(int pc); void addSongResult(int status); void copySongToResult(int status); void removeSongResult(int status); void cleanDirsResult(int status); + void readingCache(int pc); + void savingCache(int pc); + +private: + void cacheStatus(const QString &msg, int prog); protected: + State state; bool scanned; + int cacheProgress; MusicScanner *scanner; mutable QString audioFolder; FreeSpaceInfo spaceInfo; diff --git a/devices/remotefsdevice.cpp b/devices/remotefsdevice.cpp index dfe3838f8..0f81667d2 100644 --- a/devices/remotefsdevice.cpp +++ b/devices/remotefsdevice.cpp @@ -188,7 +188,7 @@ void RemoteFsDevice::remove(Device *dev) CFG_SYNC; } if (rfs) { - rfs->stopScanner(false); + rfs->stopScanner(); if (rfs->isConnected()) { rfs->unmount(); } @@ -261,7 +261,6 @@ RemoteFsDevice::RemoteFsDevice(DevicesModel *m, const Details &d) } RemoteFsDevice::~RemoteFsDevice() { - stopScanner(false); } void RemoteFsDevice::toggle() @@ -525,6 +524,9 @@ bool RemoteFsDevice::isOldSshfs() double RemoteFsDevice::usedCapacity() { + if (cacheProgress>-1) { + return (cacheProgress*1.0)/100.0; + } if (!isConnected()) { return -1.0; } @@ -538,6 +540,9 @@ double RemoteFsDevice::usedCapacity() QString RemoteFsDevice::capacityString() { + if (cacheProgress>-1) { + return statusMessage(); + } if (!isConnected()) { return i18n("Not Connected"); } @@ -567,9 +572,7 @@ void RemoteFsDevice::load() if (isConnected()) { setAudioFolder(); readOpts(settingsFileName(), opts, true); - if (!opts.useCache || !readCache()) { - rescan(); - } + rescan(false); // Read from cache if we have it! } } diff --git a/devices/umsdevice.cpp b/devices/umsdevice.cpp index 0f049dedd..002373fc2 100644 --- a/devices/umsdevice.cpp +++ b/devices/umsdevice.cpp @@ -52,9 +52,9 @@ void UmsDevice::connectionStateChanged() if (isConnected()) { spaceInfo.setPath(access->filePath()); setup(); - if ((opts.autoScan || scanned) && (!opts.useCache || !readCache())){ // Only scan if we are set to auto scan, or we have already scanned before... - rescan(); - } else if (!scanned && (!opts.useCache || !readCache())) { // Attempt to read cache, even if autoScan set to false + if (opts.autoScan || scanned){ // Only scan if we are set to auto scan, or we have already scanned before... + rescan(false); // Read from cache if we have it! + } else { setStatusMessage(i18n("Not Scanned")); } } else { @@ -83,6 +83,9 @@ bool UmsDevice::isConnected() const double UmsDevice::usedCapacity() { + if (cacheProgress>-1) { + return (cacheProgress*1.0)/100.0; + } if (!isConnected()) { return -1.0; } @@ -92,6 +95,9 @@ double UmsDevice::usedCapacity() QString UmsDevice::capacityString() { + if (cacheProgress>-1) { + return statusMessage(); + } if (!isConnected()) { return i18n("Not Connected"); } @@ -178,9 +184,9 @@ void UmsDevice::setup() audioFolder+='/'; } - if ((opts.autoScan || scanned) && (!opts.useCache || !readCache())){ // Only scan if we are set to auto scan, or we have already scanned before... - rescan(); - } else if (!scanned && (!opts.useCache || !readCache())) { // Attempt to read cache, even if autoScan set to false + if (opts.autoScan || scanned){ // Only scan if we are set to auto scan, or we have already scanned before... + rescan(false); // Read from cache if we have it! + } else { setStatusMessage(i18n("Not Scanned")); } } @@ -281,6 +287,6 @@ void UmsDevice::saveProperties(const QString &newPath, const DeviceOptions &newO FsDevice::writeOpts(spaceInfo.path()+constCantataSettingsFile, opts, false); if (oldPath!=audioFolder) { - rescan(); + rescan(); // Path changed, so we can ignore cache... } }