- Read/write cache files in non-gui thread

- Show progress when reading/writing cache
This commit is contained in:
craig.p.drummond
2013-02-01 18:29:57 +00:00
committed by craig.p.drummond
parent bc47b560f1
commit 6d1a6bddd9
5 changed files with 198 additions and 99 deletions

View File

@@ -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
-----

View File

@@ -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<Song> &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<Song>::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<Song> >("QSet<Song>");
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<Song> &)), scanner, SLOT(scan(const QString &, const QString &, bool, const QSet<Song> &)));
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<Song> 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));
}
}

View File

@@ -28,38 +28,41 @@
#include "song.h"
#include "utils.h"
#include "freespaceinfo.h"
#include "musiclibraryitemroot.h"
#include <QThread>
#include <QStringList>
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<Song> &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<Song> &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<Song> 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<Song> &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;

View File

@@ -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!
}
}

View File

@@ -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...
}
}