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