Safely close database connections and delete backends
Also fix NewClosure leak caused by disconnected object signals
This commit is contained in:
parent
bd78e8c275
commit
b5eb13449b
@ -22,6 +22,7 @@
|
|||||||
|
|
||||||
#include "closure.h"
|
#include "closure.h"
|
||||||
|
|
||||||
|
#include "core/logging.h"
|
||||||
#include "core/timeconstants.h"
|
#include "core/timeconstants.h"
|
||||||
|
|
||||||
namespace _detail {
|
namespace _detail {
|
||||||
@ -30,8 +31,7 @@ ClosureBase::ClosureBase(ObjectHelper *helper)
|
|||||||
: helper_(helper) {
|
: helper_(helper) {
|
||||||
}
|
}
|
||||||
|
|
||||||
ClosureBase::~ClosureBase() {
|
ClosureBase::~ClosureBase() {}
|
||||||
}
|
|
||||||
|
|
||||||
CallbackClosure::CallbackClosure(QObject *sender, const char *signal, std::function<void()> callback)
|
CallbackClosure::CallbackClosure(QObject *sender, const char *signal, std::function<void()> callback)
|
||||||
: ClosureBase(new ObjectHelper(sender, signal, this)),
|
: ClosureBase(new ObjectHelper(sender, signal, this)),
|
||||||
@ -53,6 +53,8 @@ ObjectHelper::ObjectHelper(QObject *sender, const char *signal, ClosureBase *clo
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ObjectHelper::~ObjectHelper() {}
|
||||||
|
|
||||||
void ObjectHelper::Invoked() {
|
void ObjectHelper::Invoked() {
|
||||||
closure_->Invoke();
|
closure_->Invoke();
|
||||||
deleteLater();
|
deleteLater();
|
||||||
|
@ -34,6 +34,8 @@
|
|||||||
#include <QList>
|
#include <QList>
|
||||||
#include <QTimer>
|
#include <QTimer>
|
||||||
|
|
||||||
|
#include "core/logging.h"
|
||||||
|
|
||||||
namespace _detail {
|
namespace _detail {
|
||||||
|
|
||||||
class ObjectHelper;
|
class ObjectHelper;
|
||||||
@ -62,6 +64,7 @@ class ObjectHelper : public QObject {
|
|||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
ObjectHelper(QObject *parent, const char *signal, ClosureBase *closure);
|
ObjectHelper(QObject *parent, const char *signal, ClosureBase *closure);
|
||||||
|
~ObjectHelper();
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void Invoked();
|
void Invoked();
|
||||||
|
@ -21,6 +21,7 @@
|
|||||||
#include "config.h"
|
#include "config.h"
|
||||||
|
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
#include <QThread>
|
#include <QThread>
|
||||||
@ -50,7 +51,10 @@ SCollection::SCollection(Application *app, QObject *parent)
|
|||||||
backend_(nullptr),
|
backend_(nullptr),
|
||||||
model_(nullptr),
|
model_(nullptr),
|
||||||
watcher_(nullptr),
|
watcher_(nullptr),
|
||||||
watcher_thread_(nullptr) {
|
watcher_thread_(nullptr),
|
||||||
|
original_thread_(nullptr) {
|
||||||
|
|
||||||
|
original_thread_ = thread();
|
||||||
|
|
||||||
backend_ = new CollectionBackend();
|
backend_ = new CollectionBackend();
|
||||||
backend()->moveToThread(app->database()->thread());
|
backend()->moveToThread(app->database()->thread());
|
||||||
@ -64,11 +68,17 @@ SCollection::SCollection(Application *app, QObject *parent)
|
|||||||
}
|
}
|
||||||
|
|
||||||
SCollection::~SCollection() {
|
SCollection::~SCollection() {
|
||||||
|
|
||||||
|
if (watcher_) {
|
||||||
watcher_->Stop();
|
watcher_->Stop();
|
||||||
watcher_->deleteLater();
|
watcher_->deleteLater();
|
||||||
|
}
|
||||||
|
if (watcher_thread_) {
|
||||||
watcher_thread_->exit();
|
watcher_thread_->exit();
|
||||||
watcher_thread_->wait(5000 /* five seconds */);
|
watcher_thread_->wait(5000 /* five seconds */);
|
||||||
|
}
|
||||||
backend_->deleteLater();
|
backend_->deleteLater();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void SCollection::Init() {
|
void SCollection::Init() {
|
||||||
@ -98,6 +108,29 @@ void SCollection::Init() {
|
|||||||
|
|
||||||
// This will start the watcher checking for updates
|
// This will start the watcher checking for updates
|
||||||
backend_->LoadDirectoriesAsync();
|
backend_->LoadDirectoriesAsync();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void SCollection::Exit() {
|
||||||
|
|
||||||
|
wait_for_exit_ << backend_ << watcher_;
|
||||||
|
|
||||||
|
disconnect(backend_, 0, watcher_, 0);
|
||||||
|
disconnect(watcher_, 0, backend_, 0);
|
||||||
|
|
||||||
|
connect(backend_, SIGNAL(ExitFinished()), this, SLOT(ExitReceived()));
|
||||||
|
connect(watcher_, SIGNAL(ExitFinished()), this, SLOT(ExitReceived()));
|
||||||
|
backend_->ExitAsync();
|
||||||
|
watcher_->ExitAsync();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void SCollection::ExitReceived() {
|
||||||
|
|
||||||
|
disconnect(sender(), 0, this, 0);
|
||||||
|
wait_for_exit_.removeAll(sender());
|
||||||
|
if (wait_for_exit_.isEmpty()) emit ExitFinished();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void SCollection::IncrementalScan() { watcher_->IncrementalScanAsync(); }
|
void SCollection::IncrementalScan() { watcher_->IncrementalScanAsync(); }
|
||||||
|
@ -49,6 +49,7 @@ class SCollection : public QObject {
|
|||||||
static const char *kFtsTable;
|
static const char *kFtsTable;
|
||||||
|
|
||||||
void Init();
|
void Init();
|
||||||
|
void Exit();
|
||||||
|
|
||||||
CollectionBackend *backend() const { return backend_; }
|
CollectionBackend *backend() const { return backend_; }
|
||||||
CollectionModel *model() const { return model_; }
|
CollectionModel *model() const { return model_; }
|
||||||
@ -70,12 +71,17 @@ class SCollection : public QObject {
|
|||||||
void Rescan(const SongList &songs);
|
void Rescan(const SongList &songs);
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
|
void ExitReceived();
|
||||||
|
|
||||||
void IncrementalScan();
|
void IncrementalScan();
|
||||||
|
|
||||||
void CurrentSongChanged(const Song &song);
|
void CurrentSongChanged(const Song &song);
|
||||||
void SongsStatisticsChanged(const SongList& songs);
|
void SongsStatisticsChanged(const SongList& songs);
|
||||||
void Stopped();
|
void Stopped();
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void ExitFinished();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Application *app_;
|
Application *app_;
|
||||||
CollectionBackend *backend_;
|
CollectionBackend *backend_;
|
||||||
@ -83,9 +89,12 @@ class SCollection : public QObject {
|
|||||||
|
|
||||||
CollectionWatcher *watcher_;
|
CollectionWatcher *watcher_;
|
||||||
Thread *watcher_thread_;
|
Thread *watcher_thread_;
|
||||||
|
QThread *original_thread_;
|
||||||
|
|
||||||
// DB schema versions which should trigger a full collection rescan (each of those with a short reason why).
|
// DB schema versions which should trigger a full collection rescan (each of those with a short reason why).
|
||||||
QHash<int, QString> full_rescan_revisions_;
|
QHash<int, QString> full_rescan_revisions_;
|
||||||
|
|
||||||
|
QList<QObject*> wait_for_exit_;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -20,8 +20,12 @@
|
|||||||
|
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
|
|
||||||
#include <QtGlobal>
|
#include <QtGlobal>
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
|
#include <QApplication>
|
||||||
|
#include <QThread>
|
||||||
#include <QMutex>
|
#include <QMutex>
|
||||||
#include <QSet>
|
#include <QSet>
|
||||||
#include <QMap>
|
#include <QMap>
|
||||||
@ -52,7 +56,14 @@ const char *CollectionBackend::kSettingsGroup = "Collection";
|
|||||||
|
|
||||||
CollectionBackend::CollectionBackend(QObject *parent) :
|
CollectionBackend::CollectionBackend(QObject *parent) :
|
||||||
CollectionBackendInterface(parent),
|
CollectionBackendInterface(parent),
|
||||||
db_(nullptr) {}
|
db_(nullptr),
|
||||||
|
original_thread_(nullptr) {
|
||||||
|
|
||||||
|
original_thread_ = thread();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
CollectionBackend::~CollectionBackend() {}
|
||||||
|
|
||||||
void CollectionBackend::Init(Database *db, const Song::Source source, const QString &songs_table, const QString &dirs_table, const QString &subdirs_table, const QString &fts_table) {
|
void CollectionBackend::Init(Database *db, const Song::Source source, const QString &songs_table, const QString &dirs_table, const QString &subdirs_table, const QString &fts_table) {
|
||||||
db_ = db;
|
db_ = db;
|
||||||
@ -63,6 +74,29 @@ void CollectionBackend::Init(Database *db, const Song::Source source, const QStr
|
|||||||
fts_table_ = fts_table;
|
fts_table_ = fts_table;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CollectionBackend::Close() {
|
||||||
|
|
||||||
|
if (db_) {
|
||||||
|
QMutexLocker l(db_->Mutex());
|
||||||
|
db_->Close();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void CollectionBackend::ExitAsync() {
|
||||||
|
metaObject()->invokeMethod(this, "Exit", Qt::QueuedConnection);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CollectionBackend::Exit() {
|
||||||
|
|
||||||
|
assert(QThread::currentThread() == thread());
|
||||||
|
|
||||||
|
Close();
|
||||||
|
moveToThread(original_thread_);
|
||||||
|
emit ExitFinished();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
void CollectionBackend::LoadDirectoriesAsync() {
|
void CollectionBackend::LoadDirectoriesAsync() {
|
||||||
metaObject()->invokeMethod(this, "LoadDirectories", Qt::QueuedConnection);
|
metaObject()->invokeMethod(this, "LoadDirectories", Qt::QueuedConnection);
|
||||||
}
|
}
|
||||||
|
@ -41,6 +41,7 @@
|
|||||||
#include "collectionquery.h"
|
#include "collectionquery.h"
|
||||||
#include "directory.h"
|
#include "directory.h"
|
||||||
|
|
||||||
|
class QThread;
|
||||||
class Database;
|
class Database;
|
||||||
|
|
||||||
class CollectionBackendInterface : public QObject {
|
class CollectionBackendInterface : public QObject {
|
||||||
@ -123,7 +124,12 @@ class CollectionBackend : public CollectionBackendInterface {
|
|||||||
static const char *kSettingsGroup;
|
static const char *kSettingsGroup;
|
||||||
|
|
||||||
Q_INVOKABLE CollectionBackend(QObject *parent = nullptr);
|
Q_INVOKABLE CollectionBackend(QObject *parent = nullptr);
|
||||||
|
~CollectionBackend();
|
||||||
|
|
||||||
void Init(Database *db, const Song::Source source, const QString &songs_table, const QString &dirs_table, const QString &subdirs_table, const QString &fts_table);
|
void Init(Database *db, const Song::Source source, const QString &songs_table, const QString &dirs_table, const QString &subdirs_table, const QString &fts_table);
|
||||||
|
void Close();
|
||||||
|
|
||||||
|
void ExitAsync();
|
||||||
|
|
||||||
Database *db() const { return db_; }
|
Database *db() const { return db_; }
|
||||||
|
|
||||||
@ -183,6 +189,7 @@ class CollectionBackend : public CollectionBackendInterface {
|
|||||||
SongList GetSongsBySongId(const QStringList &song_ids);
|
SongList GetSongsBySongId(const QStringList &song_ids);
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
|
void Exit();
|
||||||
void LoadDirectories();
|
void LoadDirectories();
|
||||||
void UpdateTotalSongCount();
|
void UpdateTotalSongCount();
|
||||||
void UpdateTotalArtistCount();
|
void UpdateTotalArtistCount();
|
||||||
@ -200,7 +207,7 @@ class CollectionBackend : public CollectionBackendInterface {
|
|||||||
void ResetStatistics(int id);
|
void ResetStatistics(int id);
|
||||||
void SongPathChanged(const Song &song, const QFileInfo &new_file);
|
void SongPathChanged(const Song &song, const QFileInfo &new_file);
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void DirectoryDiscovered(const Directory &dir, const SubdirectoryList &subdirs);
|
void DirectoryDiscovered(const Directory &dir, const SubdirectoryList &subdirs);
|
||||||
void DirectoryDeleted(const Directory &dir);
|
void DirectoryDeleted(const Directory &dir);
|
||||||
|
|
||||||
@ -214,6 +221,8 @@ signals:
|
|||||||
void TotalArtistCountUpdated(int total);
|
void TotalArtistCountUpdated(int total);
|
||||||
void TotalAlbumCountUpdated(int total);
|
void TotalAlbumCountUpdated(int total);
|
||||||
|
|
||||||
|
void ExitFinished();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
struct CompilationInfo {
|
struct CompilationInfo {
|
||||||
CompilationInfo() : has_compilation_detected(false), has_not_compilation_detected(false) {}
|
CompilationInfo() : has_compilation_detected(false), has_not_compilation_detected(false) {}
|
||||||
@ -243,6 +252,7 @@ signals:
|
|||||||
QString dirs_table_;
|
QString dirs_table_;
|
||||||
QString subdirs_table_;
|
QString subdirs_table_;
|
||||||
QString fts_table_;
|
QString fts_table_;
|
||||||
|
QThread *original_thread_;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -129,7 +129,10 @@ CollectionModel::CollectionModel(CollectionBackend *backend, Application *app, Q
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
CollectionModel::~CollectionModel() { delete root_; }
|
CollectionModel::~CollectionModel() {
|
||||||
|
backend_->Close();
|
||||||
|
delete root_;
|
||||||
|
}
|
||||||
|
|
||||||
void CollectionModel::set_pretty_covers(bool use_pretty_covers) {
|
void CollectionModel::set_pretty_covers(bool use_pretty_covers) {
|
||||||
|
|
||||||
@ -757,6 +760,11 @@ CollectionModel::QueryResult CollectionModel::RunQuery(CollectionItem *parent) {
|
|||||||
while (q.Next()) {
|
while (q.Next()) {
|
||||||
result.rows << SqlRow(q);
|
result.rows << SqlRow(q);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (QThread::currentThread() != thread() && QThread::currentThread() != backend_->thread()) {
|
||||||
|
backend_->Close();
|
||||||
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -20,7 +20,11 @@
|
|||||||
|
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
|
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
|
#include <QApplication>
|
||||||
|
#include <QThread>
|
||||||
#include <QIODevice>
|
#include <QIODevice>
|
||||||
#include <QDir>
|
#include <QDir>
|
||||||
#include <QDirIterator>
|
#include <QDirIterator>
|
||||||
@ -79,7 +83,10 @@ CollectionWatcher::CollectionWatcher(Song::Source source, QObject *parent)
|
|||||||
rescan_timer_(new QTimer(this)),
|
rescan_timer_(new QTimer(this)),
|
||||||
rescan_paused_(false),
|
rescan_paused_(false),
|
||||||
total_watches_(0),
|
total_watches_(0),
|
||||||
cue_parser_(new CueParser(backend_, this)) {
|
cue_parser_(new CueParser(backend_, this)),
|
||||||
|
original_thread_(nullptr) {
|
||||||
|
|
||||||
|
original_thread_ = thread();
|
||||||
|
|
||||||
rescan_timer_->setInterval(1000);
|
rescan_timer_->setInterval(1000);
|
||||||
rescan_timer_->setSingleShot(true);
|
rescan_timer_->setSingleShot(true);
|
||||||
@ -93,6 +100,21 @@ CollectionWatcher::CollectionWatcher(Song::Source source, QObject *parent)
|
|||||||
connect(rescan_timer_, SIGNAL(timeout()), SLOT(RescanPathsNow()));
|
connect(rescan_timer_, SIGNAL(timeout()), SLOT(RescanPathsNow()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CollectionWatcher::ExitAsync() {
|
||||||
|
metaObject()->invokeMethod(this, "Exit", Qt::QueuedConnection);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CollectionWatcher::Exit() {
|
||||||
|
|
||||||
|
assert(QThread::currentThread() == thread());
|
||||||
|
|
||||||
|
Stop();
|
||||||
|
if (backend_) backend_->Close();
|
||||||
|
moveToThread(original_thread_);
|
||||||
|
emit ExitFinished();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
CollectionWatcher::ScanTransaction::ScanTransaction(CollectionWatcher *watcher, const int dir, const bool incremental, const bool ignores_mtime, const bool prevent_delete)
|
CollectionWatcher::ScanTransaction::ScanTransaction(CollectionWatcher *watcher, const int dir, const bool incremental, const bool ignores_mtime, const bool prevent_delete)
|
||||||
: progress_(0),
|
: progress_(0),
|
||||||
progress_max_(0),
|
progress_max_(0),
|
||||||
|
@ -60,6 +60,8 @@ class CollectionWatcher : public QObject {
|
|||||||
|
|
||||||
void Stop() { stop_requested_ = true; }
|
void Stop() { stop_requested_ = true; }
|
||||||
|
|
||||||
|
void ExitAsync();
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void NewOrUpdatedSongs(const SongList &songs);
|
void NewOrUpdatedSongs(const SongList &songs);
|
||||||
void SongsMTimeUpdated(const SongList &songs);
|
void SongsMTimeUpdated(const SongList &songs);
|
||||||
@ -68,6 +70,7 @@ signals:
|
|||||||
void SubdirsDiscovered(const SubdirectoryList &subdirs);
|
void SubdirsDiscovered(const SubdirectoryList &subdirs);
|
||||||
void SubdirsMTimeUpdated(const SubdirectoryList &subdirs);
|
void SubdirsMTimeUpdated(const SubdirectoryList &subdirs);
|
||||||
void CompilationsNeedUpdating();
|
void CompilationsNeedUpdating();
|
||||||
|
void ExitFinished();
|
||||||
|
|
||||||
void ScanStarted(int task_id);
|
void ScanStarted(int task_id);
|
||||||
|
|
||||||
@ -142,6 +145,7 @@ signals:
|
|||||||
};
|
};
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
|
void Exit();
|
||||||
void DirectoryChanged(const QString &path);
|
void DirectoryChanged(const QString &path);
|
||||||
void IncrementalScanNow();
|
void IncrementalScanNow();
|
||||||
void FullScanNow();
|
void FullScanNow();
|
||||||
@ -204,6 +208,8 @@ signals:
|
|||||||
|
|
||||||
SongList song_rescan_queue_; // Set by ui thread
|
SongList song_rescan_queue_; // Set by ui thread
|
||||||
|
|
||||||
|
QThread *original_thread_;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
inline QString CollectionWatcher::NoExtensionPart(const QString& fileName) {
|
inline QString CollectionWatcher::NoExtensionPart(const QString& fileName) {
|
||||||
|
@ -223,10 +223,12 @@ Application::~Application() {
|
|||||||
|
|
||||||
for (QThread *thread : threads_) {
|
for (QThread *thread : threads_) {
|
||||||
thread->wait();
|
thread->wait();
|
||||||
|
thread->deleteLater();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Application::MoveToNewThread(QObject *object) {
|
QThread *Application::MoveToNewThread(QObject *object) {
|
||||||
|
|
||||||
QThread *thread = new QThread(this);
|
QThread *thread = new QThread(this);
|
||||||
|
|
||||||
@ -234,6 +236,9 @@ void Application::MoveToNewThread(QObject *object) {
|
|||||||
|
|
||||||
thread->start();
|
thread->start();
|
||||||
threads_ << thread;
|
threads_ << thread;
|
||||||
|
|
||||||
|
return thread;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Application::MoveToThread(QObject *object, QThread *thread) {
|
void Application::MoveToThread(QObject *object, QThread *thread) {
|
||||||
@ -241,6 +246,38 @@ void Application::MoveToThread(QObject *object, QThread *thread) {
|
|||||||
object->moveToThread(thread);
|
object->moveToThread(thread);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Application::Exit() {
|
||||||
|
|
||||||
|
wait_for_exit_ << collection()
|
||||||
|
<< playlist_backend()
|
||||||
|
<< device_manager()
|
||||||
|
<< internet_services();
|
||||||
|
|
||||||
|
connect(collection(), SIGNAL(ExitFinished()), this, SLOT(ExitReceived()));
|
||||||
|
collection()->Exit();
|
||||||
|
|
||||||
|
connect(playlist_backend(), SIGNAL(ExitFinished()), this, SLOT(ExitReceived()));
|
||||||
|
playlist_backend()->ExitAsync();
|
||||||
|
|
||||||
|
connect(device_manager(), SIGNAL(ExitFinished()), this, SLOT(ExitReceived()));
|
||||||
|
device_manager()->Exit();
|
||||||
|
|
||||||
|
connect(internet_services(), SIGNAL(ExitFinished()), this, SLOT(ExitReceived()));
|
||||||
|
internet_services()->Exit();
|
||||||
|
|
||||||
|
database()->Close();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void Application::ExitReceived() {
|
||||||
|
|
||||||
|
disconnect(sender(), 0, this, 0);
|
||||||
|
|
||||||
|
wait_for_exit_.removeAll(sender());
|
||||||
|
if (wait_for_exit_.isEmpty()) emit ExitFinished();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
void Application::AddError(const QString& message) { emit ErrorAdded(message); }
|
void Application::AddError(const QString& message) { emit ErrorAdded(message); }
|
||||||
void Application::ReloadSettings() { emit SettingsChanged(); }
|
void Application::ReloadSettings() { emit SettingsChanged(); }
|
||||||
void Application::OpenSettingsDialogAtPage(SettingsDialog::Page page) { emit SettingsDialogRequested(page); }
|
void Application::OpenSettingsDialogAtPage(SettingsDialog::Page page) { emit SettingsDialogRequested(page); }
|
||||||
|
@ -111,9 +111,14 @@ class Application : public QObject {
|
|||||||
MoodbarLoader *moodbar_loader() const;
|
MoodbarLoader *moodbar_loader() const;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
void MoveToNewThread(QObject *object);
|
void Exit();
|
||||||
|
|
||||||
|
QThread *MoveToNewThread(QObject *object);
|
||||||
void MoveToThread(QObject *object, QThread *thread);
|
void MoveToThread(QObject *object, QThread *thread);
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void ExitReceived();
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void AddError(const QString &message);
|
void AddError(const QString &message);
|
||||||
void ReloadSettings();
|
void ReloadSettings();
|
||||||
@ -123,10 +128,12 @@ signals:
|
|||||||
void ErrorAdded(const QString &message);
|
void ErrorAdded(const QString &message);
|
||||||
void SettingsChanged();
|
void SettingsChanged();
|
||||||
void SettingsDialogRequested(SettingsDialog::Page page);
|
void SettingsDialogRequested(SettingsDialog::Page page);
|
||||||
|
void ExitFinished();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::unique_ptr<ApplicationImpl> p_;
|
std::unique_ptr<ApplicationImpl> p_;
|
||||||
QList<QThread*> threads_;
|
QList<QThread*> threads_;
|
||||||
|
QList<QObject*> wait_for_exit_;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -211,6 +211,7 @@ int Database::FTSNext(sqlite3_tokenizer_cursor *cursor, const char* *token, int
|
|||||||
void Database::StaticInit() {
|
void Database::StaticInit() {
|
||||||
|
|
||||||
if (sFTSTokenizer) return;
|
if (sFTSTokenizer) return;
|
||||||
|
|
||||||
sFTSTokenizer = new sqlite3_tokenizer_module;
|
sFTSTokenizer = new sqlite3_tokenizer_module;
|
||||||
sFTSTokenizer->iVersion = 0;
|
sFTSTokenizer->iVersion = 0;
|
||||||
sFTSTokenizer->xCreate = &Database::FTSCreate;
|
sFTSTokenizer->xCreate = &Database::FTSCreate;
|
||||||
@ -242,7 +243,21 @@ Database::Database(Application *app, QObject *parent, const QString &database_na
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Database::~Database() {}
|
Database::~Database() {
|
||||||
|
|
||||||
|
QMutexLocker l(&connect_mutex_);
|
||||||
|
|
||||||
|
for (QString connection : connections_) {
|
||||||
|
qLog(Error) << connection << "still open!";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!connections_.isEmpty())
|
||||||
|
qLog(Error) << connections_.count() << "connections still open!";
|
||||||
|
|
||||||
|
if (sFTSTokenizer)
|
||||||
|
delete sFTSTokenizer;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
QSqlDatabase Database::Connect() {
|
QSqlDatabase Database::Connect() {
|
||||||
|
|
||||||
@ -257,6 +272,11 @@ QSqlDatabase Database::Connect() {
|
|||||||
|
|
||||||
const QString connection_id = QString("%1_thread_%2").arg(connection_id_).arg(reinterpret_cast<quint64>(QThread::currentThread()));
|
const QString connection_id = QString("%1_thread_%2").arg(connection_id_).arg(reinterpret_cast<quint64>(QThread::currentThread()));
|
||||||
|
|
||||||
|
if (!connections_.contains(connection_id)) {
|
||||||
|
//qLog(Debug) << "Opened database with connection id" << connection_id;
|
||||||
|
connections_ << connection_id;
|
||||||
|
}
|
||||||
|
|
||||||
// Try to find an existing connection for this thread
|
// Try to find an existing connection for this thread
|
||||||
QSqlDatabase db = QSqlDatabase::database(connection_id);
|
QSqlDatabase db = QSqlDatabase::database(connection_id);
|
||||||
if (db.isOpen()) {
|
if (db.isOpen()) {
|
||||||
@ -346,6 +366,25 @@ QSqlDatabase Database::Connect() {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Database::Close() {
|
||||||
|
|
||||||
|
QMutexLocker l(&connect_mutex_);
|
||||||
|
|
||||||
|
const QString connection_id = QString("%1_thread_%2").arg(connection_id_).arg(reinterpret_cast<quint64>(QThread::currentThread()));
|
||||||
|
|
||||||
|
// Try to find an existing connection for this thread
|
||||||
|
QSqlDatabase db = QSqlDatabase::database(connection_id);
|
||||||
|
if (db.isOpen()) {
|
||||||
|
db.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (connections_.contains(connection_id)) {
|
||||||
|
//qLog(Debug) << "Closed database with connection id" << connection_id;
|
||||||
|
connections_.removeAll(connection_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
void Database::UpdateMainSchema(QSqlDatabase *db) {
|
void Database::UpdateMainSchema(QSqlDatabase *db) {
|
||||||
|
|
||||||
// Get the database's schema version
|
// Get the database's schema version
|
||||||
|
@ -68,6 +68,7 @@ class Database : public QObject {
|
|||||||
static const char *kMagicAllSongsTables;
|
static const char *kMagicAllSongsTables;
|
||||||
|
|
||||||
QSqlDatabase Connect();
|
QSqlDatabase Connect();
|
||||||
|
void Close();
|
||||||
bool CheckErrors(const QSqlQuery &query);
|
bool CheckErrors(const QSqlQuery &query);
|
||||||
QMutex *Mutex() { return &mutex_; }
|
QMutex *Mutex() { return &mutex_; }
|
||||||
|
|
||||||
@ -111,6 +112,7 @@ signals:
|
|||||||
|
|
||||||
// This ID makes the QSqlDatabase name unique to the object as well as the thread
|
// This ID makes the QSqlDatabase name unique to the object as well as the thread
|
||||||
int connection_id_;
|
int connection_id_;
|
||||||
|
QStringList connections_;
|
||||||
|
|
||||||
static QMutex sNextConnectionIdMutex;
|
static QMutex sNextConnectionIdMutex;
|
||||||
static int sNextConnectionId;
|
static int sNextConnectionId;
|
||||||
|
@ -55,7 +55,7 @@ void DeleteFiles::Start(const SongList &songs) {
|
|||||||
task_id_ = task_manager_->StartTask(tr("Deleting files"));
|
task_id_ = task_manager_->StartTask(tr("Deleting files"));
|
||||||
task_manager_->SetTaskBlocksCollectionScans(true);
|
task_manager_->SetTaskBlocksCollectionScans(true);
|
||||||
|
|
||||||
thread_ = new QThread;
|
thread_ = new QThread(this);
|
||||||
connect(thread_, SIGNAL(started()), SLOT(ProcessSomeFiles()));
|
connect(thread_, SIGNAL(started()), SLOT(ProcessSomeFiles()));
|
||||||
|
|
||||||
moveToThread(thread_);
|
moveToThread(thread_);
|
||||||
|
@ -989,7 +989,7 @@ void MainWindow::Exit() {
|
|||||||
|
|
||||||
if (app_->player()->engine()->is_fadeout_enabled()) {
|
if (app_->player()->engine()->is_fadeout_enabled()) {
|
||||||
// To shut down the application when fadeout will be finished
|
// To shut down the application when fadeout will be finished
|
||||||
connect(app_->player()->engine(), SIGNAL(FadeoutFinishedSignal()), qApp, SLOT(quit()));
|
connect(app_->player()->engine(), SIGNAL(FadeoutFinishedSignal()), this, SLOT(DoExit()));
|
||||||
if (app_->player()->GetState() == Engine::Playing) {
|
if (app_->player()->GetState() == Engine::Playing) {
|
||||||
app_->player()->Stop();
|
app_->player()->Stop();
|
||||||
hide();
|
hide();
|
||||||
@ -998,6 +998,19 @@ void MainWindow::Exit() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DoExit();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainWindow::DoExit() {
|
||||||
|
|
||||||
|
connect(app_, SIGNAL(ExitFinished()), this, SLOT(ExitFinished()));
|
||||||
|
app_->Exit();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainWindow::ExitFinished() {
|
||||||
|
|
||||||
qApp->quit();
|
qApp->quit();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -243,6 +243,7 @@ signals:
|
|||||||
void Raise();
|
void Raise();
|
||||||
|
|
||||||
void Exit();
|
void Exit();
|
||||||
|
void DoExit();
|
||||||
|
|
||||||
void HandleNotificationPreview(OSD::Behaviour type, QString line1, QString line2);
|
void HandleNotificationPreview(OSD::Behaviour type, QString line1, QString line2);
|
||||||
|
|
||||||
@ -262,6 +263,8 @@ signals:
|
|||||||
void LoveButtonVisibilityChanged(bool value);
|
void LoveButtonVisibilityChanged(bool value);
|
||||||
void Love();
|
void Love();
|
||||||
|
|
||||||
|
void ExitFinished();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
void SaveSettings();
|
void SaveSettings();
|
||||||
|
@ -52,7 +52,10 @@ CurrentAlbumCoverLoader::CurrentAlbumCoverLoader(Application *app, QObject *pare
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
CurrentAlbumCoverLoader::~CurrentAlbumCoverLoader() {}
|
CurrentAlbumCoverLoader::~CurrentAlbumCoverLoader() {
|
||||||
|
if (temp_cover_) temp_cover_->remove();
|
||||||
|
if (temp_cover_thumbnail_) temp_cover_thumbnail_->remove();
|
||||||
|
}
|
||||||
|
|
||||||
void CurrentAlbumCoverLoader::LoadAlbumCover(const Song &song) {
|
void CurrentAlbumCoverLoader::LoadAlbumCover(const Song &song) {
|
||||||
last_song_ = song;
|
last_song_ = song;
|
||||||
|
@ -18,9 +18,14 @@
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
|
#include <assert.h>
|
||||||
|
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
|
#include <QApplication>
|
||||||
|
#include <QThread>
|
||||||
#include <QMutex>
|
#include <QMutex>
|
||||||
#include <QIODevice>
|
#include <QIODevice>
|
||||||
#include <QFile>
|
#include <QFile>
|
||||||
@ -32,17 +37,47 @@
|
|||||||
|
|
||||||
#include "core/database.h"
|
#include "core/database.h"
|
||||||
#include "core/scopedtransaction.h"
|
#include "core/scopedtransaction.h"
|
||||||
|
#include "core/logging.h"
|
||||||
#include "devicedatabasebackend.h"
|
#include "devicedatabasebackend.h"
|
||||||
|
|
||||||
const int DeviceDatabaseBackend::kDeviceSchemaVersion = 0;
|
const int DeviceDatabaseBackend::kDeviceSchemaVersion = 0;
|
||||||
|
|
||||||
DeviceDatabaseBackend::DeviceDatabaseBackend(QObject *parent) :
|
DeviceDatabaseBackend::DeviceDatabaseBackend(QObject *parent) :
|
||||||
QObject(parent),
|
QObject(parent),
|
||||||
db_(nullptr)
|
db_(nullptr),
|
||||||
{}
|
original_thread_(nullptr)
|
||||||
|
{
|
||||||
|
|
||||||
|
original_thread_ = thread();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
DeviceDatabaseBackend::~DeviceDatabaseBackend() {}
|
||||||
|
|
||||||
void DeviceDatabaseBackend::Init(Database* db) { db_ = db; }
|
void DeviceDatabaseBackend::Init(Database* db) { db_ = db; }
|
||||||
|
|
||||||
|
void DeviceDatabaseBackend::Close() {
|
||||||
|
|
||||||
|
if (db_) {
|
||||||
|
QMutexLocker l(db_->Mutex());
|
||||||
|
db_->Close();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void DeviceDatabaseBackend::ExitAsync() {
|
||||||
|
metaObject()->invokeMethod(this, "Exit", Qt::QueuedConnection);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DeviceDatabaseBackend::Exit() {
|
||||||
|
|
||||||
|
assert(QThread::currentThread() == thread());
|
||||||
|
Close();
|
||||||
|
moveToThread(original_thread_);
|
||||||
|
emit ExitFinished();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
DeviceDatabaseBackend::DeviceList DeviceDatabaseBackend::GetAllDevices() {
|
DeviceDatabaseBackend::DeviceList DeviceDatabaseBackend::GetAllDevices() {
|
||||||
|
|
||||||
QMutexLocker l(db_->Mutex());
|
QMutexLocker l(db_->Mutex());
|
||||||
@ -101,6 +136,7 @@ int DeviceDatabaseBackend::AddDevice(const Device &device) {
|
|||||||
db_->ExecSchemaCommands(db, schema, 0, true);
|
db_->ExecSchemaCommands(db, schema, 0, true);
|
||||||
|
|
||||||
t.Commit();
|
t.Commit();
|
||||||
|
|
||||||
return id;
|
return id;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -40,6 +40,7 @@ class DeviceDatabaseBackend : public QObject {
|
|||||||
|
|
||||||
public:
|
public:
|
||||||
Q_INVOKABLE DeviceDatabaseBackend(QObject *parent = nullptr);
|
Q_INVOKABLE DeviceDatabaseBackend(QObject *parent = nullptr);
|
||||||
|
~DeviceDatabaseBackend();
|
||||||
|
|
||||||
struct Device {
|
struct Device {
|
||||||
Device() : id_(-1) {}
|
Device() : id_(-1) {}
|
||||||
@ -58,6 +59,9 @@ class DeviceDatabaseBackend : public QObject {
|
|||||||
static const int kDeviceSchemaVersion;
|
static const int kDeviceSchemaVersion;
|
||||||
|
|
||||||
void Init(Database *db);
|
void Init(Database *db);
|
||||||
|
void Close();
|
||||||
|
void ExitAsync();
|
||||||
|
|
||||||
Database *db() const { return db_; }
|
Database *db() const { return db_; }
|
||||||
|
|
||||||
DeviceList GetAllDevices();
|
DeviceList GetAllDevices();
|
||||||
@ -66,8 +70,16 @@ class DeviceDatabaseBackend : public QObject {
|
|||||||
|
|
||||||
void SetDeviceOptions(int id, const QString &friendly_name, const QString &icon_name, MusicStorage::TranscodeMode mode, Song::FileType format);
|
void SetDeviceOptions(int id, const QString &friendly_name, const QString &icon_name, MusicStorage::TranscodeMode mode, Song::FileType format);
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void Exit();
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void ExitFinished();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Database *db_;
|
Database *db_;
|
||||||
|
QThread *original_thread_;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // DEVICEDATABASEBACKEND_H
|
#endif // DEVICEDATABASEBACKEND_H
|
||||||
|
@ -158,9 +158,17 @@ DeviceManager::~DeviceManager() {
|
|||||||
delete lister;
|
delete lister;
|
||||||
}
|
}
|
||||||
|
|
||||||
backend_->deleteLater();
|
backend_->Close();
|
||||||
|
|
||||||
delete root_;
|
delete root_;
|
||||||
|
delete backend_;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void DeviceManager::Exit() {
|
||||||
|
|
||||||
|
connect(backend_, SIGNAL(ExitFinished()), this, SIGNAL(ExitFinished()));
|
||||||
|
backend_->ExitAsync();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -174,6 +182,7 @@ void DeviceManager::LoadAllDevices() {
|
|||||||
info->InitFromDb(device);
|
info->InitFromDb(device);
|
||||||
emit DeviceCreatedFromDB(info);
|
emit DeviceCreatedFromDB(info);
|
||||||
}
|
}
|
||||||
|
backend_->Close();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -85,6 +85,8 @@ class DeviceManager : public SimpleTreeModel<DeviceInfo> {
|
|||||||
static const int kDeviceIconSize;
|
static const int kDeviceIconSize;
|
||||||
static const int kDeviceIconOverlaySize;
|
static const int kDeviceIconOverlaySize;
|
||||||
|
|
||||||
|
void Exit();
|
||||||
|
|
||||||
DeviceStateFilterModel *connected_devices_model() const { return connected_devices_model_; }
|
DeviceStateFilterModel *connected_devices_model() const { return connected_devices_model_; }
|
||||||
|
|
||||||
// Get info about devices
|
// Get info about devices
|
||||||
@ -115,6 +117,7 @@ class DeviceManager : public SimpleTreeModel<DeviceInfo> {
|
|||||||
void Unmount(QModelIndex idx);
|
void Unmount(QModelIndex idx);
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
|
void ExitFinished();
|
||||||
void DeviceConnected(QModelIndex idx);
|
void DeviceConnected(QModelIndex idx);
|
||||||
void DeviceDisconnected(QModelIndex idx);
|
void DeviceDisconnected(QModelIndex idx);
|
||||||
void DeviceCreatedFromDB(DeviceInfo* info);
|
void DeviceCreatedFromDB(DeviceInfo* info);
|
||||||
|
@ -102,6 +102,8 @@ Itdb_iTunesDB *GPodLoader::TryLoad() {
|
|||||||
// Add the songs we've just loaded
|
// Add the songs we've just loaded
|
||||||
backend_->AddOrUpdateSongs(songs);
|
backend_->AddOrUpdateSongs(songs);
|
||||||
|
|
||||||
|
backend_->Close();
|
||||||
|
|
||||||
return db;
|
return db;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -96,6 +96,8 @@ bool MtpLoader::TryLoad() {
|
|||||||
// Add the songs we've just loaded
|
// Add the songs we've just loaded
|
||||||
backend_->AddOrUpdateSongs(songs);
|
backend_->AddOrUpdateSongs(songs);
|
||||||
|
|
||||||
|
backend_->Close();
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -40,6 +40,7 @@ class InternetService : public QObject {
|
|||||||
InternetService(Song::Source source, const QString &name, const QString &url_scheme, Application *app, QObject *parent = nullptr);
|
InternetService(Song::Source source, const QString &name, const QString &url_scheme, Application *app, QObject *parent = nullptr);
|
||||||
|
|
||||||
virtual ~InternetService() {}
|
virtual ~InternetService() {}
|
||||||
|
virtual void Exit() {}
|
||||||
|
|
||||||
virtual Song::Source source() const { return source_; }
|
virtual Song::Source source() const { return source_; }
|
||||||
virtual QString name() const { return name_; }
|
virtual QString name() const { return name_; }
|
||||||
@ -75,6 +76,7 @@ class InternetService : public QObject {
|
|||||||
virtual void ResetSongsRequest() {}
|
virtual void ResetSongsRequest() {}
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
|
void ExitFinished();
|
||||||
void Login();
|
void Login();
|
||||||
void Logout();
|
void Logout();
|
||||||
void Login(const QString &api_token, const QString &username, const QString &password);
|
void Login(const QString &api_token, const QString &username, const QString &password);
|
||||||
|
@ -72,3 +72,22 @@ void InternetServices::ReloadSettings() {
|
|||||||
service->ReloadSettings();
|
service->ReloadSettings();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void InternetServices::Exit() {
|
||||||
|
|
||||||
|
for (InternetService *service : services_.values()) {
|
||||||
|
wait_for_exit_ << service;
|
||||||
|
connect(service, SIGNAL(ExitFinished()), this, SLOT(ExitReceived()));
|
||||||
|
service->Exit();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void InternetServices::ExitReceived() {
|
||||||
|
|
||||||
|
InternetService *service = qobject_cast<InternetService*>(sender());
|
||||||
|
|
||||||
|
wait_for_exit_.removeAll(service);
|
||||||
|
if (wait_for_exit_.isEmpty()) emit ExitFinished();
|
||||||
|
|
||||||
|
}
|
||||||
|
@ -47,9 +47,17 @@ class InternetServices : public QObject {
|
|||||||
void AddService(InternetService *service);
|
void AddService(InternetService *service);
|
||||||
void RemoveService(InternetService *service);
|
void RemoveService(InternetService *service);
|
||||||
void ReloadSettings();
|
void ReloadSettings();
|
||||||
|
void Exit();
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void ExitFinished();
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void ExitReceived();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QMap<Song::Source, InternetService*> services_;
|
QMap<Song::Source, InternetService*> services_;
|
||||||
|
QList<InternetService*> wait_for_exit_;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -115,7 +115,7 @@ void MusicBrainzClient::Cancel(int id) {
|
|||||||
|
|
||||||
while (!requests_.isEmpty() && requests_.contains(id)) {
|
while (!requests_.isEmpty() && requests_.contains(id)) {
|
||||||
QNetworkReply *reply = requests_.take(id);
|
QNetworkReply *reply = requests_.take(id);
|
||||||
disconnect(reply, 0, nullptr, 0);
|
disconnect(reply, 0, this, 0);
|
||||||
if (reply->isRunning()) reply->abort();
|
if (reply->isRunning()) reply->abort();
|
||||||
reply->deleteLater();
|
reply->deleteLater();
|
||||||
}
|
}
|
||||||
|
@ -20,8 +20,11 @@
|
|||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
|
#include <assert.h>
|
||||||
|
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
|
#include <QApplication>
|
||||||
|
#include <QThread>
|
||||||
#include <QMutex>
|
#include <QMutex>
|
||||||
#include <QIODevice>
|
#include <QIODevice>
|
||||||
#include <QDir>
|
#include <QDir>
|
||||||
@ -56,7 +59,36 @@ using std::shared_ptr;
|
|||||||
const int PlaylistBackend::kSongTableJoins = 2;
|
const int PlaylistBackend::kSongTableJoins = 2;
|
||||||
|
|
||||||
PlaylistBackend::PlaylistBackend(Application *app, QObject *parent)
|
PlaylistBackend::PlaylistBackend(Application *app, QObject *parent)
|
||||||
: QObject(parent), app_(app), db_(app_->database()) {}
|
: QObject(parent), app_(app), db_(app_->database()), original_thread_(nullptr) {
|
||||||
|
|
||||||
|
original_thread_ = thread();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
PlaylistBackend::~PlaylistBackend() {}
|
||||||
|
|
||||||
|
void PlaylistBackend::Close() {
|
||||||
|
|
||||||
|
if (db_) {
|
||||||
|
QMutexLocker l(db_->Mutex());
|
||||||
|
db_->Close();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void PlaylistBackend::ExitAsync() {
|
||||||
|
metaObject()->invokeMethod(this, "Exit", Qt::QueuedConnection);
|
||||||
|
}
|
||||||
|
|
||||||
|
void PlaylistBackend::Exit() {
|
||||||
|
|
||||||
|
assert(QThread::currentThread() == thread());
|
||||||
|
|
||||||
|
Close();
|
||||||
|
moveToThread(original_thread_);
|
||||||
|
emit ExitFinished();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
PlaylistBackend::PlaylistList PlaylistBackend::GetAllPlaylists() {
|
PlaylistBackend::PlaylistList PlaylistBackend::GetAllPlaylists() {
|
||||||
return GetPlaylists(GetPlaylists_All);
|
return GetPlaylists(GetPlaylists_All);
|
||||||
@ -157,6 +189,10 @@ QSqlQuery PlaylistBackend::GetPlaylistRows(int playlist) {
|
|||||||
q.bindValue(":playlist", playlist);
|
q.bindValue(":playlist", playlist);
|
||||||
q.exec();
|
q.exec();
|
||||||
|
|
||||||
|
if (QThread::currentThread() != thread() && QThread::currentThread() != qApp->thread()) {
|
||||||
|
db_->Close();
|
||||||
|
}
|
||||||
|
|
||||||
return q;
|
return q;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -39,6 +39,7 @@
|
|||||||
#include "collection/sqlrow.h"
|
#include "collection/sqlrow.h"
|
||||||
#include "playlistitem.h"
|
#include "playlistitem.h"
|
||||||
|
|
||||||
|
class QThread;
|
||||||
class Application;
|
class Application;
|
||||||
class Database;
|
class Database;
|
||||||
|
|
||||||
@ -47,6 +48,7 @@ class PlaylistBackend : public QObject {
|
|||||||
|
|
||||||
public:
|
public:
|
||||||
Q_INVOKABLE PlaylistBackend(Application *app, QObject *parent = nullptr);
|
Q_INVOKABLE PlaylistBackend(Application *app, QObject *parent = nullptr);
|
||||||
|
~PlaylistBackend();
|
||||||
|
|
||||||
struct Playlist {
|
struct Playlist {
|
||||||
Playlist() : id(-1), favorite(false), last_played(0) {}
|
Playlist() : id(-1), favorite(false), last_played(0) {}
|
||||||
@ -62,6 +64,9 @@ class PlaylistBackend : public QObject {
|
|||||||
|
|
||||||
static const int kSongTableJoins;
|
static const int kSongTableJoins;
|
||||||
|
|
||||||
|
void Close();
|
||||||
|
void ExitAsync();
|
||||||
|
|
||||||
PlaylistList GetAllPlaylists();
|
PlaylistList GetAllPlaylists();
|
||||||
PlaylistList GetAllOpenPlaylists();
|
PlaylistList GetAllOpenPlaylists();
|
||||||
PlaylistList GetAllFavoritePlaylists();
|
PlaylistList GetAllFavoritePlaylists();
|
||||||
@ -82,8 +87,12 @@ class PlaylistBackend : public QObject {
|
|||||||
Application *app() const { return app_; }
|
Application *app() const { return app_; }
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
|
void Exit();
|
||||||
void SavePlaylist(int playlist, const PlaylistItemList &items, int last_played);
|
void SavePlaylist(int playlist, const PlaylistItemList &items, int last_played);
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void ExitFinished();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
struct NewSongFromQueryState {
|
struct NewSongFromQueryState {
|
||||||
QHash<QString, SongList> cached_cues_;
|
QHash<QString, SongList> cached_cues_;
|
||||||
@ -105,6 +114,7 @@ class PlaylistBackend : public QObject {
|
|||||||
|
|
||||||
Application *app_;
|
Application *app_;
|
||||||
Database *db_;
|
Database *db_;
|
||||||
|
QThread *original_thread_;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // PLAYLISTBACKEND_H
|
#endif // PLAYLISTBACKEND_H
|
||||||
|
@ -22,6 +22,7 @@
|
|||||||
#include "config.h"
|
#include "config.h"
|
||||||
|
|
||||||
#include <QtGlobal>
|
#include <QtGlobal>
|
||||||
|
#include <QApplication>
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
#include <QWidget>
|
#include <QWidget>
|
||||||
#include <QtConcurrentRun>
|
#include <QtConcurrentRun>
|
||||||
@ -363,6 +364,10 @@ TagCompletionModel::TagCompletionModel(CollectionBackend *backend, Playlist::Col
|
|||||||
setStringList(backend->GetAll(col));
|
setStringList(backend->GetAll(col));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (QThread::currentThread() != backend->thread() && QThread::currentThread() != qApp->thread()) {
|
||||||
|
backend->Close();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
QString TagCompletionModel::database_column(Playlist::Column column) {
|
QString TagCompletionModel::database_column(Playlist::Column column) {
|
||||||
|
@ -80,6 +80,7 @@ class PlaylistHeader;
|
|||||||
class PlaylistProxyStyle : public QProxyStyle {
|
class PlaylistProxyStyle : public QProxyStyle {
|
||||||
public:
|
public:
|
||||||
PlaylistProxyStyle(QStyle *base);
|
PlaylistProxyStyle(QStyle *base);
|
||||||
|
|
||||||
void drawControl(ControlElement element, const QStyleOption *option, QPainter *painter, const QWidget *widget) const;
|
void drawControl(ControlElement element, const QStyleOption *option, QPainter *painter, const QWidget *widget) const;
|
||||||
void drawPrimitive(PrimitiveElement element, const QStyleOption *option, QPainter *painter, const QWidget *widget) const;
|
void drawPrimitive(PrimitiveElement element, const QStyleOption *option, QPainter *painter, const QWidget *widget) const;
|
||||||
|
|
||||||
|
@ -51,7 +51,7 @@ QobuzBaseRequest::~QobuzBaseRequest() {
|
|||||||
|
|
||||||
while (!replies_.isEmpty()) {
|
while (!replies_.isEmpty()) {
|
||||||
QNetworkReply *reply = replies_.takeFirst();
|
QNetworkReply *reply = replies_.takeFirst();
|
||||||
disconnect(reply, 0, nullptr, 0);
|
disconnect(reply, 0, this, 0);
|
||||||
if (reply->isRunning()) reply->abort();
|
if (reply->isRunning()) reply->abort();
|
||||||
reply->deleteLater();
|
reply->deleteLater();
|
||||||
}
|
}
|
||||||
|
@ -49,7 +49,7 @@ QobuzFavoriteRequest::~QobuzFavoriteRequest() {
|
|||||||
|
|
||||||
while (!replies_.isEmpty()) {
|
while (!replies_.isEmpty()) {
|
||||||
QNetworkReply *reply = replies_.takeFirst();
|
QNetworkReply *reply = replies_.takeFirst();
|
||||||
disconnect(reply, 0, nullptr, 0);
|
disconnect(reply, 0, this, 0);
|
||||||
reply->abort();
|
reply->abort();
|
||||||
reply->deleteLater();
|
reply->deleteLater();
|
||||||
}
|
}
|
||||||
|
@ -77,7 +77,7 @@ QobuzRequest::~QobuzRequest() {
|
|||||||
|
|
||||||
while (!album_cover_replies_.isEmpty()) {
|
while (!album_cover_replies_.isEmpty()) {
|
||||||
QNetworkReply *reply = album_cover_replies_.takeFirst();
|
QNetworkReply *reply = album_cover_replies_.takeFirst();
|
||||||
disconnect(reply, 0, nullptr, 0);
|
disconnect(reply, 0, this, 0);
|
||||||
if (reply->isRunning()) reply->abort();
|
if (reply->isRunning()) reply->abort();
|
||||||
reply->deleteLater();
|
reply->deleteLater();
|
||||||
}
|
}
|
||||||
|
@ -176,13 +176,35 @@ QobuzService::~QobuzService() {
|
|||||||
|
|
||||||
while (!stream_url_requests_.isEmpty()) {
|
while (!stream_url_requests_.isEmpty()) {
|
||||||
QobuzStreamURLRequest *stream_url_req = stream_url_requests_.takeFirst();
|
QobuzStreamURLRequest *stream_url_req = stream_url_requests_.takeFirst();
|
||||||
disconnect(stream_url_req, 0, nullptr, 0);
|
disconnect(stream_url_req, 0, this, 0);
|
||||||
stream_url_req->deleteLater();
|
stream_url_req->deleteLater();
|
||||||
}
|
}
|
||||||
|
|
||||||
artists_collection_backend_->deleteLater();
|
delete artists_collection_backend_;
|
||||||
albums_collection_backend_->deleteLater();
|
delete albums_collection_backend_;
|
||||||
songs_collection_backend_->deleteLater();
|
delete songs_collection_backend_;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void QobuzService::Exit() {
|
||||||
|
|
||||||
|
wait_for_exit_ << artists_collection_backend_ << albums_collection_backend_ << songs_collection_backend_;
|
||||||
|
|
||||||
|
connect(artists_collection_backend_, SIGNAL(ExitFinished()), this, SLOT(ExitReceived()));
|
||||||
|
connect(albums_collection_backend_, SIGNAL(ExitFinished()), this, SLOT(ExitReceived()));
|
||||||
|
connect(songs_collection_backend_, SIGNAL(ExitFinished()), this, SLOT(ExitReceived()));
|
||||||
|
|
||||||
|
artists_collection_backend_->ExitAsync();
|
||||||
|
albums_collection_backend_->ExitAsync();
|
||||||
|
songs_collection_backend_->ExitAsync();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void QobuzService::ExitReceived() {
|
||||||
|
|
||||||
|
disconnect(sender(), 0, this, 0);
|
||||||
|
wait_for_exit_.removeAll(sender());
|
||||||
|
if (wait_for_exit_.isEmpty()) emit ExitFinished();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -395,7 +417,7 @@ void QobuzService::TryLogin() {
|
|||||||
void QobuzService::ResetArtistsRequest() {
|
void QobuzService::ResetArtistsRequest() {
|
||||||
|
|
||||||
if (artists_request_.get()) {
|
if (artists_request_.get()) {
|
||||||
disconnect(artists_request_.get(), 0, nullptr, 0);
|
disconnect(artists_request_.get(), 0, this, 0);
|
||||||
disconnect(this, 0, artists_request_.get(), 0);
|
disconnect(this, 0, artists_request_.get(), 0);
|
||||||
artists_request_.reset();
|
artists_request_.reset();
|
||||||
}
|
}
|
||||||
@ -446,7 +468,7 @@ void QobuzService::ArtistsUpdateProgressReceived(const int id, const int progres
|
|||||||
void QobuzService::ResetAlbumsRequest() {
|
void QobuzService::ResetAlbumsRequest() {
|
||||||
|
|
||||||
if (albums_request_.get()) {
|
if (albums_request_.get()) {
|
||||||
disconnect(albums_request_.get(), 0, nullptr, 0);
|
disconnect(albums_request_.get(), 0, this, 0);
|
||||||
disconnect(this, 0, albums_request_.get(), 0);
|
disconnect(this, 0, albums_request_.get(), 0);
|
||||||
albums_request_.reset();
|
albums_request_.reset();
|
||||||
}
|
}
|
||||||
@ -495,7 +517,7 @@ void QobuzService::AlbumsUpdateProgressReceived(const int id, const int progress
|
|||||||
void QobuzService::ResetSongsRequest() {
|
void QobuzService::ResetSongsRequest() {
|
||||||
|
|
||||||
if (songs_request_.get()) {
|
if (songs_request_.get()) {
|
||||||
disconnect(songs_request_.get(), 0, nullptr, 0);
|
disconnect(songs_request_.get(), 0, this, 0);
|
||||||
disconnect(this, 0, songs_request_.get(), 0);
|
disconnect(this, 0, songs_request_.get(), 0);
|
||||||
songs_request_.reset();
|
songs_request_.reset();
|
||||||
}
|
}
|
||||||
|
@ -40,6 +40,7 @@
|
|||||||
#include "internet/internetsearch.h"
|
#include "internet/internetsearch.h"
|
||||||
#include "settings/qobuzsettingspage.h"
|
#include "settings/qobuzsettingspage.h"
|
||||||
|
|
||||||
|
class QThread;
|
||||||
class Application;
|
class Application;
|
||||||
class NetworkAccessManager;
|
class NetworkAccessManager;
|
||||||
class QobuzUrlHandler;
|
class QobuzUrlHandler;
|
||||||
@ -60,6 +61,7 @@ class QobuzService : public InternetService {
|
|||||||
|
|
||||||
static const Song::Source kSource;
|
static const Song::Source kSource;
|
||||||
|
|
||||||
|
void Exit();
|
||||||
void ReloadSettings();
|
void ReloadSettings();
|
||||||
|
|
||||||
void Logout();
|
void Logout();
|
||||||
@ -123,6 +125,7 @@ class QobuzService : public InternetService {
|
|||||||
void ResetSongsRequest();
|
void ResetSongsRequest();
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
|
void ExitReceived();
|
||||||
void SendLogin();
|
void SendLogin();
|
||||||
void HandleLoginSSLErrors(QList<QSslError> ssl_errors);
|
void HandleLoginSSLErrors(QList<QSslError> ssl_errors);
|
||||||
void HandleAuthReply(QNetworkReply *reply);
|
void HandleAuthReply(QNetworkReply *reply);
|
||||||
@ -217,6 +220,8 @@ class QobuzService : public InternetService {
|
|||||||
|
|
||||||
QStringList login_errors_;
|
QStringList login_errors_;
|
||||||
|
|
||||||
|
QList<QObject*> wait_for_exit_;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // QOBUZSERVICE_H
|
#endif // QOBUZSERVICE_H
|
||||||
|
@ -54,7 +54,7 @@ QobuzStreamURLRequest::QobuzStreamURLRequest(QobuzService *service, NetworkAcces
|
|||||||
QobuzStreamURLRequest::~QobuzStreamURLRequest() {
|
QobuzStreamURLRequest::~QobuzStreamURLRequest() {
|
||||||
|
|
||||||
if (reply_) {
|
if (reply_) {
|
||||||
disconnect(reply_, 0, nullptr, 0);
|
disconnect(reply_, 0, this, 0);
|
||||||
if (reply_->isRunning()) reply_->abort();
|
if (reply_->isRunning()) reply_->abort();
|
||||||
reply_->deleteLater();
|
reply_->deleteLater();
|
||||||
}
|
}
|
||||||
@ -107,7 +107,7 @@ void QobuzStreamURLRequest::GetStreamURL() {
|
|||||||
++tries_;
|
++tries_;
|
||||||
|
|
||||||
if (reply_) {
|
if (reply_) {
|
||||||
disconnect(reply_, 0, nullptr, 0);
|
disconnect(reply_, 0, this, 0);
|
||||||
if (reply_->isRunning()) reply_->abort();
|
if (reply_->isRunning()) reply_->abort();
|
||||||
reply_->deleteLater();
|
reply_->deleteLater();
|
||||||
}
|
}
|
||||||
@ -147,7 +147,7 @@ void QobuzStreamURLRequest::GetStreamURL() {
|
|||||||
void QobuzStreamURLRequest::StreamURLReceived() {
|
void QobuzStreamURLRequest::StreamURLReceived() {
|
||||||
|
|
||||||
if (!reply_) return;
|
if (!reply_) return;
|
||||||
disconnect(reply_, 0, nullptr, 0);
|
disconnect(reply_, 0, this, 0);
|
||||||
reply_->deleteLater();
|
reply_->deleteLater();
|
||||||
|
|
||||||
QByteArray data = GetReplyData(reply_);
|
QByteArray data = GetReplyData(reply_);
|
||||||
|
@ -50,7 +50,7 @@ SubsonicBaseRequest::~SubsonicBaseRequest() {
|
|||||||
|
|
||||||
while (!replies_.isEmpty()) {
|
while (!replies_.isEmpty()) {
|
||||||
QNetworkReply *reply = replies_.takeFirst();
|
QNetworkReply *reply = replies_.takeFirst();
|
||||||
disconnect(reply, 0, nullptr, 0);
|
disconnect(reply, 0, this, 0);
|
||||||
if (reply->isRunning()) reply->abort();
|
if (reply->isRunning()) reply->abort();
|
||||||
reply->deleteLater();
|
reply->deleteLater();
|
||||||
}
|
}
|
||||||
|
@ -71,7 +71,7 @@ SubsonicRequest::~SubsonicRequest() {
|
|||||||
|
|
||||||
while (!album_cover_replies_.isEmpty()) {
|
while (!album_cover_replies_.isEmpty()) {
|
||||||
QNetworkReply *reply = album_cover_replies_.takeFirst();
|
QNetworkReply *reply = album_cover_replies_.takeFirst();
|
||||||
disconnect(reply, 0, nullptr, 0);
|
disconnect(reply, 0, this, 0);
|
||||||
if (reply->isRunning()) reply->abort();
|
if (reply->isRunning()) reply->abort();
|
||||||
reply->deleteLater();
|
reply->deleteLater();
|
||||||
}
|
}
|
||||||
|
@ -95,7 +95,14 @@ SubsonicService::SubsonicService(Application *app, QObject *parent)
|
|||||||
}
|
}
|
||||||
|
|
||||||
SubsonicService::~SubsonicService() {
|
SubsonicService::~SubsonicService() {
|
||||||
collection_backend_->deleteLater();
|
delete collection_backend_;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SubsonicService::Exit() {
|
||||||
|
|
||||||
|
connect(collection_backend_, SIGNAL(ExitFinished()), this, SIGNAL(ExitFinished()));
|
||||||
|
collection_backend_->ExitAsync();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void SubsonicService::ShowConfig() {
|
void SubsonicService::ShowConfig() {
|
||||||
@ -317,8 +324,8 @@ void SubsonicService::CheckConfiguration() {
|
|||||||
|
|
||||||
void SubsonicService::ResetSongsRequest() {
|
void SubsonicService::ResetSongsRequest() {
|
||||||
|
|
||||||
if (songs_request_.get()) {
|
if (songs_request_.get()) { // WARNING: Don't disconnect everything. NewClosure() relies on destroyed()!!!
|
||||||
disconnect(songs_request_.get(), 0, nullptr, 0);
|
disconnect(songs_request_.get(), 0, this, 0);
|
||||||
disconnect(this, 0, songs_request_.get(), 0);
|
disconnect(this, 0, songs_request_.get(), 0);
|
||||||
songs_request_.reset();
|
songs_request_.reset();
|
||||||
}
|
}
|
||||||
|
@ -60,6 +60,7 @@ class SubsonicService : public InternetService {
|
|||||||
static const Song::Source kSource;
|
static const Song::Source kSource;
|
||||||
|
|
||||||
void ReloadSettings();
|
void ReloadSettings();
|
||||||
|
void Exit();
|
||||||
|
|
||||||
Application *app() { return app_; }
|
Application *app() { return app_; }
|
||||||
|
|
||||||
|
@ -52,7 +52,7 @@ TidalBaseRequest::~TidalBaseRequest() {
|
|||||||
|
|
||||||
while (!replies_.isEmpty()) {
|
while (!replies_.isEmpty()) {
|
||||||
QNetworkReply *reply = replies_.takeFirst();
|
QNetworkReply *reply = replies_.takeFirst();
|
||||||
disconnect(reply, 0, nullptr, 0);
|
disconnect(reply, 0, this, 0);
|
||||||
if (reply->isRunning()) reply->abort();
|
if (reply->isRunning()) reply->abort();
|
||||||
reply->deleteLater();
|
reply->deleteLater();
|
||||||
}
|
}
|
||||||
|
@ -50,7 +50,7 @@ TidalFavoriteRequest::~TidalFavoriteRequest() {
|
|||||||
|
|
||||||
while (!replies_.isEmpty()) {
|
while (!replies_.isEmpty()) {
|
||||||
QNetworkReply *reply = replies_.takeFirst();
|
QNetworkReply *reply = replies_.takeFirst();
|
||||||
disconnect(reply, 0, nullptr, 0);
|
disconnect(reply, 0, this, 0);
|
||||||
reply->abort();
|
reply->abort();
|
||||||
reply->deleteLater();
|
reply->deleteLater();
|
||||||
}
|
}
|
||||||
|
@ -80,7 +80,7 @@ TidalRequest::~TidalRequest() {
|
|||||||
|
|
||||||
while (!album_cover_replies_.isEmpty()) {
|
while (!album_cover_replies_.isEmpty()) {
|
||||||
QNetworkReply *reply = album_cover_replies_.takeFirst();
|
QNetworkReply *reply = album_cover_replies_.takeFirst();
|
||||||
disconnect(reply, 0, nullptr, 0);
|
disconnect(reply, 0, this, 0);
|
||||||
if (reply->isRunning()) reply->abort();
|
if (reply->isRunning()) reply->abort();
|
||||||
reply->deleteLater();
|
reply->deleteLater();
|
||||||
}
|
}
|
||||||
|
@ -182,13 +182,35 @@ TidalService::~TidalService() {
|
|||||||
|
|
||||||
while (!stream_url_requests_.isEmpty()) {
|
while (!stream_url_requests_.isEmpty()) {
|
||||||
TidalStreamURLRequest *stream_url_req = stream_url_requests_.takeFirst();
|
TidalStreamURLRequest *stream_url_req = stream_url_requests_.takeFirst();
|
||||||
disconnect(stream_url_req, 0, nullptr, 0);
|
disconnect(stream_url_req, 0, this, 0);
|
||||||
stream_url_req->deleteLater();
|
stream_url_req->deleteLater();
|
||||||
}
|
}
|
||||||
|
|
||||||
artists_collection_backend_->deleteLater();
|
delete artists_collection_backend_;
|
||||||
albums_collection_backend_->deleteLater();
|
delete albums_collection_backend_;
|
||||||
songs_collection_backend_->deleteLater();
|
delete songs_collection_backend_;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void TidalService::Exit() {
|
||||||
|
|
||||||
|
wait_for_exit_ << artists_collection_backend_ << albums_collection_backend_ << songs_collection_backend_;
|
||||||
|
|
||||||
|
connect(artists_collection_backend_, SIGNAL(ExitFinished()), this, SLOT(ExitReceived()));
|
||||||
|
connect(albums_collection_backend_, SIGNAL(ExitFinished()), this, SLOT(ExitReceived()));
|
||||||
|
connect(songs_collection_backend_, SIGNAL(ExitFinished()), this, SLOT(ExitReceived()));
|
||||||
|
|
||||||
|
artists_collection_backend_->ExitAsync();
|
||||||
|
albums_collection_backend_->ExitAsync();
|
||||||
|
songs_collection_backend_->ExitAsync();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void TidalService::ExitReceived() {
|
||||||
|
|
||||||
|
disconnect(sender(), 0, this, 0);
|
||||||
|
wait_for_exit_.removeAll(sender());
|
||||||
|
if (wait_for_exit_.isEmpty()) emit ExitFinished();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -639,7 +661,7 @@ void TidalService::TryLogin() {
|
|||||||
void TidalService::ResetArtistsRequest() {
|
void TidalService::ResetArtistsRequest() {
|
||||||
|
|
||||||
if (artists_request_.get()) {
|
if (artists_request_.get()) {
|
||||||
disconnect(artists_request_.get(), 0, nullptr, 0);
|
disconnect(artists_request_.get(), 0, this, 0);
|
||||||
disconnect(this, 0, artists_request_.get(), 0);
|
disconnect(this, 0, artists_request_.get(), 0);
|
||||||
artists_request_.reset();
|
artists_request_.reset();
|
||||||
}
|
}
|
||||||
@ -694,7 +716,7 @@ void TidalService::ArtistsUpdateProgressReceived(const int id, const int progres
|
|||||||
void TidalService::ResetAlbumsRequest() {
|
void TidalService::ResetAlbumsRequest() {
|
||||||
|
|
||||||
if (albums_request_.get()) {
|
if (albums_request_.get()) {
|
||||||
disconnect(albums_request_.get(), 0, nullptr, 0);
|
disconnect(albums_request_.get(), 0, this, 0);
|
||||||
disconnect(this, 0, albums_request_.get(), 0);
|
disconnect(this, 0, albums_request_.get(), 0);
|
||||||
albums_request_.reset();
|
albums_request_.reset();
|
||||||
}
|
}
|
||||||
@ -747,7 +769,7 @@ void TidalService::AlbumsUpdateProgressReceived(const int id, const int progress
|
|||||||
void TidalService::ResetSongsRequest() {
|
void TidalService::ResetSongsRequest() {
|
||||||
|
|
||||||
if (songs_request_.get()) {
|
if (songs_request_.get()) {
|
||||||
disconnect(songs_request_.get(), 0, nullptr, 0);
|
disconnect(songs_request_.get(), 0, this, 0);
|
||||||
disconnect(this, 0, songs_request_.get(), 0);
|
disconnect(this, 0, songs_request_.get(), 0);
|
||||||
songs_request_.reset();
|
songs_request_.reset();
|
||||||
}
|
}
|
||||||
|
@ -61,6 +61,7 @@ class TidalService : public InternetService {
|
|||||||
|
|
||||||
static const Song::Source kSource;
|
static const Song::Source kSource;
|
||||||
|
|
||||||
|
void Exit();
|
||||||
void ReloadSettings();
|
void ReloadSettings();
|
||||||
|
|
||||||
void Logout();
|
void Logout();
|
||||||
@ -132,6 +133,7 @@ class TidalService : public InternetService {
|
|||||||
void ResetSongsRequest();
|
void ResetSongsRequest();
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
|
void ExitReceived();
|
||||||
void StartAuthorisation();
|
void StartAuthorisation();
|
||||||
void AuthorisationUrlReceived(const QUrl &url);
|
void AuthorisationUrlReceived(const QUrl &url);
|
||||||
void HandleLoginSSLErrors(QList<QSslError> ssl_errors);
|
void HandleLoginSSLErrors(QList<QSslError> ssl_errors);
|
||||||
@ -245,6 +247,8 @@ class TidalService : public InternetService {
|
|||||||
|
|
||||||
QStringList login_errors_;
|
QStringList login_errors_;
|
||||||
|
|
||||||
|
QList<QObject*> wait_for_exit_;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // TIDALSERVICE_H
|
#endif // TIDALSERVICE_H
|
||||||
|
@ -52,7 +52,7 @@ TidalStreamURLRequest::TidalStreamURLRequest(TidalService *service, NetworkAcces
|
|||||||
TidalStreamURLRequest::~TidalStreamURLRequest() {
|
TidalStreamURLRequest::~TidalStreamURLRequest() {
|
||||||
|
|
||||||
if (reply_) {
|
if (reply_) {
|
||||||
disconnect(reply_, 0, nullptr, 0);
|
disconnect(reply_, 0, this, 0);
|
||||||
if (reply_->isRunning()) reply_->abort();
|
if (reply_->isRunning()) reply_->abort();
|
||||||
reply_->deleteLater();
|
reply_->deleteLater();
|
||||||
}
|
}
|
||||||
@ -109,7 +109,7 @@ void TidalStreamURLRequest::GetStreamURL() {
|
|||||||
++tries_;
|
++tries_;
|
||||||
|
|
||||||
if (reply_) {
|
if (reply_) {
|
||||||
disconnect(reply_, 0, nullptr, 0);
|
disconnect(reply_, 0, this, 0);
|
||||||
if (reply_->isRunning()) reply_->abort();
|
if (reply_->isRunning()) reply_->abort();
|
||||||
reply_->deleteLater();
|
reply_->deleteLater();
|
||||||
}
|
}
|
||||||
@ -144,7 +144,7 @@ void TidalStreamURLRequest::GetStreamURL() {
|
|||||||
void TidalStreamURLRequest::StreamURLReceived() {
|
void TidalStreamURLRequest::StreamURLReceived() {
|
||||||
|
|
||||||
if (!reply_) return;
|
if (!reply_) return;
|
||||||
disconnect(reply_, 0, nullptr, 0);
|
disconnect(reply_, 0, this, 0);
|
||||||
reply_->deleteLater();
|
reply_->deleteLater();
|
||||||
|
|
||||||
QByteArray data = GetReplyData(reply_, true);
|
QByteArray data = GetReplyData(reply_, true);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user