1
0
mirror of https://github.com/clementine-player/Clementine synced 2024-12-16 19:31:02 +01:00

Use FSEvent-based filesystem watcher on Mac.

(cherry picked from commit 9fdfb52225)
This commit is contained in:
John Maguire 2012-01-05 14:51:23 +00:00 committed by David Sansome
parent ea9a8ea2ed
commit ec2f57148b
11 changed files with 126 additions and 33 deletions

View File

@ -66,6 +66,7 @@ set(SOURCES
core/deletefiles.cpp
core/encoding.cpp
core/filesystemmusicstorage.cpp
core/filesystemwatcherinterface.cpp
core/fht.cpp
core/fmpsparser.cpp
core/globalshortcutbackend.cpp
@ -80,6 +81,7 @@ set(SOURCES
core/organise.cpp
core/organiseformat.cpp
core/player.cpp
core/qtfslistener.cpp
core/qxtglobalshortcutbackend.cpp
core/scopedtransaction.cpp
core/settingsprovider.cpp
@ -332,6 +334,7 @@ set(HEADERS
core/crashreporting.h
core/database.h
core/deletefiles.h
core/filesystemwatcherinterface.h
core/globalshortcuts.h
core/globalshortcutbackend.h
core/gnomeglobalshortcutbackend.h
@ -340,6 +343,7 @@ set(HEADERS
core/network.h
core/organise.h
core/player.h
core/qtfslistener.h
core/songloader.h
core/taskmanager.h
core/urlhandler.h

View File

@ -0,0 +1,23 @@
#include "filesystemwatcherinterface.h"
#include "qtfslistener.h"
#ifdef Q_OS_DARWIN
#include "macfslistener.h"
#endif
FileSystemWatcherInterface::FileSystemWatcherInterface(QObject* parent)
: QObject(parent) {
}
FileSystemWatcherInterface* FileSystemWatcherInterface::Create(QObject* parent) {
FileSystemWatcherInterface* ret;
#ifdef Q_OS_DARWIN
ret = new MacFSListener(parent);
#else
ret = new QtFSListener(parent);
#endif
ret->Init();
return ret;
}

View File

@ -0,0 +1,19 @@
#ifndef FILESYSTEMWATCHERINTERFACE_H
#define FILESYSTEMWATCHERINTERFACE_H
#include <QObject>
class FileSystemWatcherInterface : public QObject {
Q_OBJECT
public:
FileSystemWatcherInterface(QObject* parent = 0);
virtual void Init() {}
virtual void AddPath(const QString& path) = 0;
static FileSystemWatcherInterface* Create(QObject* parent = 0);
signals:
void PathChanged(const QString& path);
};
#endif

View File

@ -6,7 +6,9 @@
#include <QObject>
#include <QSet>
class MacFSListener : public QObject {
#include "filesystemwatcherinterface.h"
class MacFSListener : public FileSystemWatcherInterface {
Q_OBJECT
public:

View File

@ -8,7 +8,7 @@
#include "core/scoped_nsobject.h"
MacFSListener::MacFSListener(QObject* parent)
: QObject(parent),
: FileSystemWatcherInterface(parent),
run_loop_(NULL),
stream_(NULL) {
}
@ -34,12 +34,14 @@ void MacFSListener::EventStreamCallback(
}
void MacFSListener::AddPath(const QString& path) {
Q_ASSERT(run_loop_);
paths_.insert(path);
UpdateStream();
}
void MacFSListener::UpdateStream() {
if (stream_) {
FSEventStreamStop(stream_);
FSEventStreamInvalidate(stream_);
FSEventStreamRelease(stream_);
stream_ = NULL;

12
src/core/qtfslistener.cpp Normal file
View File

@ -0,0 +1,12 @@
#include "qtfslistener.h"
QtFSListener::QtFSListener(QObject* parent)
: FileSystemWatcherInterface(parent),
watcher_(this) {
connect(&watcher_, SIGNAL(directoryChanged(const QString&)),
SIGNAL(PathChanged(const QString&)));
}
void QtFSListener::AddPath(const QString& path) {
watcher_.addPath(path);
}

19
src/core/qtfslistener.h Normal file
View File

@ -0,0 +1,19 @@
#ifndef QTFSLISTENER_H
#define QTFSLISTENER_H
#include "filesystemwatcherinterface.h"
#include <QFileSystemWatcher>
class QtFSListener : public FileSystemWatcherInterface {
Q_OBJECT
public:
QtFSListener(QObject* parent);
virtual void AddPath(const QString& path);
private:
QFileSystemWatcher watcher_;
};
#endif

View File

@ -16,9 +16,11 @@
*/
#include "library.h"
#include "librarymodel.h"
#include "librarybackend.h"
#include "core/database.h"
#include "core/filesystemwatcherinterface.h"
#include "smartplaylists/generator.h"
#include "smartplaylists/querygenerator.h"
#include "smartplaylists/search.h"
@ -119,6 +121,7 @@ void Library::WatcherInitialised() {
watcher->set_backend(backend_);
watcher->set_task_manager(task_manager_);
watcher->set_filesystem_watcher(FileSystemWatcherInterface::Create());
connect(backend_, SIGNAL(DirectoryDiscovered(Directory,SubdirectoryList)),
watcher, SLOT(AddDirectory(Directory,SubdirectoryList)));

View File

@ -16,12 +16,13 @@
*/
#include "librarywatcher.h"
#include "librarybackend.h"
#include "core/filesystemwatcherinterface.h"
#include "core/logging.h"
#include "core/taskmanager.h"
#include "playlistparsers/cueparser.h"
#include <QFileSystemWatcher>
#include <QDateTime>
#include <QDirIterator>
#include <QtDebug>
@ -48,6 +49,7 @@ LibraryWatcher::LibraryWatcher(QObject* parent)
: QObject(parent),
backend_(NULL),
task_manager_(NULL),
fs_watcher_(NULL),
stop_requested_(false),
scan_on_startup_(true),
monitor_(true),
@ -113,7 +115,7 @@ LibraryWatcher::ScanTransaction::~ScanTransaction() {
if (watcher_->monitor_) {
// Watch the new subdirectories
foreach (const Subdirectory& subdir, new_subdirs) {
watcher_->AddWatch(watcher_->watched_dirs_[dir_].watcher, subdir.path);
watcher_->AddWatch(subdir.path);
}
}
}
@ -183,8 +185,6 @@ SubdirectoryList LibraryWatcher::ScanTransaction::GetAllSubdirs() {
void LibraryWatcher::AddDirectory(const Directory& dir, const SubdirectoryList& subdirs) {
DirData data;
data.dir = dir;
data.watcher = new QFileSystemWatcher(this);
connect(data.watcher, SIGNAL(directoryChanged(QString)), SLOT(DirectoryChanged(QString)));
watched_dirs_[dir.id] = data;
if (subdirs.isEmpty()) {
@ -207,7 +207,7 @@ void LibraryWatcher::AddDirectory(const Directory& dir, const SubdirectoryList&
ScanSubdirectory(subdir.path, subdir, &transaction);
if (monitor_)
AddWatch(data.watcher, subdir.path);
AddWatch(subdir.path);
}
}
@ -546,18 +546,16 @@ uint LibraryWatcher::GetMtimeForCue(const QString& cue_path) {
: 0;
}
void LibraryWatcher::AddWatch(QFileSystemWatcher* w, const QString& path) {
void LibraryWatcher::AddWatch(const QString& path) {
if (!QFile::exists(path))
return;
w->addPath(path);
connect(fs_watcher_, SIGNAL(PathChanged(const QString&)), this,
SLOT(DirectoryChanged(const QString&)), Qt::UniqueConnection);
fs_watcher_->AddPath(path);
}
void LibraryWatcher::RemoveDirectory(const Directory& dir) {
if (watched_dirs_.contains(dir.id)) {
delete watched_dirs_[dir.id].watcher;
}
rescan_queue_.remove(dir.id);
watched_dirs_.remove(dir.id);
}
@ -575,13 +573,9 @@ bool LibraryWatcher::FindSongByPath(const SongList& list, const QString& path, S
void LibraryWatcher::DirectoryChanged(const QString &subdir) {
// Find what dir it was in
QFileSystemWatcher* watcher = qobject_cast<QFileSystemWatcher*>(sender());
if (!watcher)
return;
Directory dir;
foreach (const DirData& info, watched_dirs_) {
if (info.watcher == watcher)
if (subdir.startsWith(info.dir.path))
dir = info.dir;
}
@ -689,7 +683,7 @@ void LibraryWatcher::ReloadSettings() {
s.beginGroup(kSettingsGroup);
scan_on_startup_ = s.value("startup_scan", true).toBool();
monitor_ = s.value("monitor", true).toBool();
best_image_filters_.clear();
QStringList filters = s.value("cover_art_patterns",
QStringList() << "front" << "cover").toStringList();
@ -698,18 +692,15 @@ void LibraryWatcher::ReloadSettings() {
if (!s.isEmpty())
best_image_filters_ << s;
}
if (!monitor_ && was_monitoring_before) {
// Remove all directories from all QFileSystemWatchers
foreach (const DirData& data, watched_dirs_.values()) {
data.watcher->removePaths(data.watcher->directories());
}
// TODO: Remove all directories from watcher.
} else if (monitor_ && !was_monitoring_before) {
// Add all directories to all QFileSystemWatchers again
foreach (const DirData& data, watched_dirs_.values()) {
SubdirectoryList subdirs = backend_->SubdirsInDirectory(data.dir.id);
foreach (const Subdirectory& subdir, subdirs) {
AddWatch(data.watcher, subdir.path);
AddWatch(subdir.path);
}
}
}

View File

@ -29,6 +29,7 @@ class QFileSystemWatcher;
class QTimer;
class CueParser;
class FileSystemWatcherInterface;
class LibraryBackend;
class TaskManager;
@ -42,8 +43,9 @@ class LibraryWatcher : public QObject {
void set_backend(LibraryBackend* backend) { backend_ = backend; }
void set_task_manager(TaskManager* task_manager) { task_manager_ = task_manager; }
void set_filesystem_watcher(FileSystemWatcherInterface* watcher) { fs_watcher_ = watcher; }
void set_device_name(const QString& device_name) { device_name_ = device_name; }
void IncrementalScanAsync();
void FullScanAsync();
void SetRescanPausedAsync(bool pause);
@ -144,7 +146,7 @@ class LibraryWatcher : public QObject {
inline static QString DirectoryPart( const QString &fileName );
QString PickBestImage(const QStringList& images);
QString ImageForSong(const QString& path, QMap<QString, QStringList>& album_art);
void AddWatch(QFileSystemWatcher* w, const QString& path);
void AddWatch(const QString& path);
uint GetMtimeForCue(const QString& cue_path);
void PerformScan(bool incremental, bool ignore_mtimes);
@ -172,20 +174,21 @@ class LibraryWatcher : public QObject {
// One of these gets stored for each Directory we're watching
struct DirData {
Directory dir;
QFileSystemWatcher* watcher;
};
LibraryBackend* backend_;
TaskManager* task_manager_;
QString device_name_;
FileSystemWatcherInterface* fs_watcher_;
/* A list of words use to try to identify the (likely) best image
* found in an directory to use as cover artwork.
* e.g. using ["front", "cover"] would identify front.jpg and
* exclude back.jpg.
*/
QStringList best_image_filters_;
bool stop_requested_;
bool scan_on_startup_;
bool monitor_;

View File

@ -16,14 +16,18 @@
*/
#include "musicdnsclient.h"
#include "core/network.h"
#include <QBuffer>
#include <QCoreApplication>
#include <QNetworkReply>
#include <QXmlStreamReader>
#include <QtDebug>
const char* MusicDnsClient::kClientId = "c44f70e49000dd7c0d1388bff2bf4152";
#include "core/logging.h"
#include "core/network.h"
//const char* MusicDnsClient::kClientId = "c44f70e49000dd7c0d1388bff2bf4152";
const char* MusicDnsClient::kClientId = "0736ac2cd889ef77f26f6b5e3fb8a09c";
const char* MusicDnsClient::kUrl = "http://ofa.musicdns.org/ofa/1/track";
const int MusicDnsClient::kDefaultTimeout = 5000; // msec
@ -49,10 +53,12 @@ void MusicDnsClient::Start(int id, const QString& fingerprint, int duration_msec
<< Param("cvr", QString("%1 %2").arg(QCoreApplication::applicationName(),
QCoreApplication::applicationVersion()))
<< Param("dur", QString::number(duration_msec))
<< Param("lkt", "1")
<< Param("fmt", "unknown")
<< Param("fpt", fingerprint)
<< Param("gnr", "unknown")
<< Param("rmd", "1")
<< Param("rmt", "0")
<< Param("tnm", "0")
<< Param("ttl", "unknown")
<< Param("yrr", "0");
@ -61,6 +67,8 @@ void MusicDnsClient::Start(int id, const QString& fingerprint, int duration_msec
url.setQueryItems(parameters);
QNetworkRequest req(url);
qLog(Debug) << url;
QNetworkReply* reply = network_->get(req);
connect(reply, SIGNAL(finished()), SLOT(RequestFinished()));
requests_[reply] = id;
@ -90,12 +98,19 @@ void MusicDnsClient::RequestFinished() {
int id = requests_.take(reply);
qLog(Debug) << reply->attribute(QNetworkRequest::HttpStatusCodeAttribute);
if (reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt() != 200) {
emit Finished(id, QString());
return;
}
QXmlStreamReader reader(reply);
QByteArray data = reply->readAll();
qLog(Debug) << data;
QBuffer buffer(&data);
buffer.open(QIODevice::ReadOnly);
QXmlStreamReader reader(&buffer);
while (!reader.atEnd()) {
if (reader.readNext() == QXmlStreamReader::StartElement && reader.name() == "puid") {
QString puid = reader.attributes().value("id").toString();