- Read/write cache files in non-gui thread
- Show progress when reading/writing cache
This commit is contained in:
committed by
craig.p.drummond
parent
bc47b560f1
commit
6d1a6bddd9
@@ -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
|
||||
-----
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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!
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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...
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user