This commit is contained in:
Jonas Kvinge
2019-07-24 23:29:09 +02:00
parent da0d61f36a
commit 41484f8673
14 changed files with 135 additions and 41 deletions

View File

@@ -127,8 +127,10 @@ void SCollection::Exit() {
void SCollection::ExitReceived() { void SCollection::ExitReceived() {
disconnect(sender(), 0, this, 0); QObject *obj = static_cast<QObject*>(sender());
wait_for_exit_.removeAll(sender()); disconnect(obj, 0, this, 0);
qLog(Debug) << obj << "successfully exited.";
wait_for_exit_.removeAll(obj);
if (wait_for_exit_.isEmpty()) emit ExitFinished(); if (wait_for_exit_.isEmpty()) emit ExitFinished();
} }

View File

@@ -248,19 +248,27 @@ void Application::MoveToThread(QObject *object, QThread *thread) {
void Application::Exit() { void Application::Exit() {
wait_for_exit_ << collection() wait_for_exit_ << tag_reader_client()
<< collection()
<< playlist_backend() << playlist_backend()
<< album_cover_loader()
#ifndef Q_OS_WIN #ifndef Q_OS_WIN
<< device_manager() << device_manager()
#endif #endif
<< internet_services(); << internet_services();
connect(tag_reader_client(), SIGNAL(ExitFinished()), this, SLOT(ExitReceived()));
tag_reader_client()->ExitAsync();
connect(collection(), SIGNAL(ExitFinished()), this, SLOT(ExitReceived())); connect(collection(), SIGNAL(ExitFinished()), this, SLOT(ExitReceived()));
collection()->Exit(); collection()->Exit();
connect(playlist_backend(), SIGNAL(ExitFinished()), this, SLOT(ExitReceived())); connect(playlist_backend(), SIGNAL(ExitFinished()), this, SLOT(ExitReceived()));
playlist_backend()->ExitAsync(); playlist_backend()->ExitAsync();
connect(album_cover_loader(), SIGNAL(ExitFinished()), this, SLOT(ExitReceived()));
album_cover_loader()->ExitAsync();
#ifndef Q_OS_WIN #ifndef Q_OS_WIN
connect(device_manager(), SIGNAL(ExitFinished()), this, SLOT(ExitReceived())); connect(device_manager(), SIGNAL(ExitFinished()), this, SLOT(ExitReceived()));
device_manager()->Exit(); device_manager()->Exit();
@@ -269,16 +277,21 @@ void Application::Exit() {
connect(internet_services(), SIGNAL(ExitFinished()), this, SLOT(ExitReceived())); connect(internet_services(), SIGNAL(ExitFinished()), this, SLOT(ExitReceived()));
internet_services()->Exit(); internet_services()->Exit();
database()->Close();
} }
void Application::ExitReceived() { void Application::ExitReceived() {
disconnect(sender(), 0, this, 0); QObject *obj = static_cast<QObject*>(sender());
disconnect(obj, 0, this, 0);
wait_for_exit_.removeAll(sender()); qLog(Debug) << obj << "successfully exited.";
if (wait_for_exit_.isEmpty()) emit ExitFinished();
wait_for_exit_.removeAll(obj);
if (wait_for_exit_.isEmpty()) {
database()->Close();
connect(database(), SIGNAL(ExitFinished()), this, SIGNAL(ExitFinished()));
database()->ExitAsync();
}
} }

View File

@@ -229,7 +229,10 @@ Database::Database(Application *app, QObject *parent, const QString &database_na
mutex_(QMutex::Recursive), mutex_(QMutex::Recursive),
injected_database_name_(database_name), injected_database_name_(database_name),
query_hash_(0), query_hash_(0),
startup_schema_version_(-1) { startup_schema_version_(-1),
original_thread_(nullptr) {
original_thread_ = thread();
{ {
QMutexLocker l(&sNextConnectionIdMutex); QMutexLocker l(&sNextConnectionIdMutex);
@@ -259,6 +262,19 @@ Database::~Database() {
} }
void Database::ExitAsync() {
metaObject()->invokeMethod(this, "Exit", Qt::QueuedConnection);
}
void Database::Exit() {
assert(QThread::currentThread() == thread());
Close();
moveToThread(original_thread_);
emit ExitFinished();
}
QSqlDatabase Database::Connect() { QSqlDatabase Database::Connect() {
QMutexLocker l(&connect_mutex_); QMutexLocker l(&connect_mutex_);

View File

@@ -44,6 +44,7 @@ struct sqlite3_tokenizer_cursor;
struct sqlite3_tokenizer_module; struct sqlite3_tokenizer_module;
} }
class QThread;
class Application; class Application;
class Database : public QObject { class Database : public QObject {
@@ -67,6 +68,7 @@ class Database : public QObject {
static const char *kDatabaseFilename; static const char *kDatabaseFilename;
static const char *kMagicAllSongsTables; static const char *kMagicAllSongsTables;
void ExitAsync();
QSqlDatabase Connect(); QSqlDatabase Connect();
void Close(); void Close();
bool CheckErrors(const QSqlQuery &query); bool CheckErrors(const QSqlQuery &query);
@@ -82,9 +84,13 @@ class Database : public QObject {
void AttachDatabaseOnDbConnection(const QString &database_name, const AttachedDatabase &database, QSqlDatabase &db); void AttachDatabaseOnDbConnection(const QString &database_name, const AttachedDatabase &database, QSqlDatabase &db);
void DetachDatabase(const QString &database_name); void DetachDatabase(const QString &database_name);
signals: signals:
void ExitFinished();
void Error(const QString &message); void Error(const QString &message);
private slots:
void Exit();
public slots: public slots:
void DoBackup(); void DoBackup();
@@ -126,6 +132,8 @@ signals:
// This is the schema version of Strawberry's DB from the app's last run. // This is the schema version of Strawberry's DB from the app's last run.
int startup_schema_version_; int startup_schema_version_;
QThread *original_thread_;
// Do static initialisation like loading sqlite functions. // Do static initialisation like loading sqlite functions.
static void StaticInit(); static void StaticInit();

View File

@@ -237,7 +237,8 @@ MainWindow::MainWindow(Application *app, SystemTrayIcon *tray_icon, OSD *osd, co
playing_widget_(true), playing_widget_(true),
doubleclick_addmode_(BehaviourSettingsPage::AddBehaviour_Append), doubleclick_addmode_(BehaviourSettingsPage::AddBehaviour_Append),
doubleclick_playmode_(BehaviourSettingsPage::PlayBehaviour_Never), doubleclick_playmode_(BehaviourSettingsPage::PlayBehaviour_Never),
menu_playmode_(BehaviourSettingsPage::PlayBehaviour_Never) menu_playmode_(BehaviourSettingsPage::PlayBehaviour_Never),
exit_count_(0)
{ {
qLog(Debug) << "Starting"; qLog(Debug) << "Starting";
@@ -985,20 +986,26 @@ void MainWindow::SaveSettings() {
void MainWindow::Exit() { void MainWindow::Exit() {
++exit_count_;
SaveSettings(); SaveSettings();
if (app_->player()->engine()->is_fadeout_enabled()) { if (exit_count_ > 1) {
// To shut down the application when fadeout will be finished qApp->quit();
connect(app_->player()->engine(), SIGNAL(FadeoutFinishedSignal()), this, SLOT(DoExit())); }
if (app_->player()->GetState() == Engine::Playing) { else {
app_->player()->Stop(); if (app_->player()->engine()->is_fadeout_enabled()) {
hide(); // To shut down the application when fadeout will be finished
if (tray_icon_) tray_icon_->SetVisible(false); connect(app_->player()->engine(), SIGNAL(FadeoutFinishedSignal()), this, SLOT(DoExit()));
return; // Don't quit the application now: wait for the fadeout finished signal if (app_->player()->GetState() == Engine::Playing) {
} app_->player()->Stop();
hide();
if (tray_icon_) tray_icon_->SetVisible(false);
return; // Don't quit the application now: wait for the fadeout finished signal
}
}
DoExit();
} }
DoExit();
} }
@@ -1323,8 +1330,8 @@ void MainWindow::closeEvent(QCloseEvent *event) {
} }
else { else {
Exit(); Exit();
QApplication::quit();
} }
} }
void MainWindow::SetHiddenInTray(bool hidden) { void MainWindow::SetHiddenInTray(bool hidden) {

View File

@@ -369,6 +369,7 @@ signals:
Song song_; Song song_;
Song song_playing_; Song song_playing_;
QImage image_original_; QImage image_original_;
int exit_count_;
}; };

View File

@@ -42,6 +42,7 @@ TagReaderClient *TagReaderClient::sInstance = nullptr;
TagReaderClient::TagReaderClient(QObject *parent) : QObject(parent), worker_pool_(new WorkerPool<HandlerType>(this)) { TagReaderClient::TagReaderClient(QObject *parent) : QObject(parent), worker_pool_(new WorkerPool<HandlerType>(this)) {
sInstance = this; sInstance = this;
original_thread_ = thread();
worker_pool_->SetExecutableName(kWorkerExecutableName); worker_pool_->SetExecutableName(kWorkerExecutableName);
worker_pool_->SetWorkerCount(QThread::idealThreadCount()); worker_pool_->SetWorkerCount(QThread::idealThreadCount());
@@ -50,6 +51,18 @@ TagReaderClient::TagReaderClient(QObject *parent) : QObject(parent), worker_pool
void TagReaderClient::Start() { worker_pool_->Start(); } void TagReaderClient::Start() { worker_pool_->Start(); }
void TagReaderClient::ExitAsync() {
metaObject()->invokeMethod(this, "Exit", Qt::QueuedConnection);
}
void TagReaderClient::Exit() {
assert(QThread::currentThread() == thread());
moveToThread(original_thread_);
emit ExitFinished();
}
void TagReaderClient::WorkerFailedToStart() { void TagReaderClient::WorkerFailedToStart() {
qLog(Error) << "The" << kWorkerExecutableName << "executable was not found in the current directory or on the PATH. Strawberry will not be able to read music file tags without it."; qLog(Error) << "The" << kWorkerExecutableName << "executable was not found in the current directory or on the PATH. Strawberry will not be able to read music file tags without it.";
} }

View File

@@ -36,6 +36,7 @@
#include "song.h" #include "song.h"
#include "tagreadermessages.pb.h" #include "tagreadermessages.pb.h"
class QThread;
class Song; class Song;
template <typename HandlerType> class WorkerPool; template <typename HandlerType> class WorkerPool;
@@ -51,6 +52,7 @@ class TagReaderClient : public QObject {
static const char *kWorkerExecutableName; static const char *kWorkerExecutableName;
void Start(); void Start();
void ExitAsync();
ReplyType *ReadFile(const QString &filename); ReplyType *ReadFile(const QString &filename);
ReplyType *SaveFile(const QString &filename, const Song &metadata); ReplyType *SaveFile(const QString &filename, const Song &metadata);
@@ -67,7 +69,11 @@ class TagReaderClient : public QObject {
// TODO: Make this not a singleton // TODO: Make this not a singleton
static TagReaderClient *Instance() { return sInstance; } static TagReaderClient *Instance() { return sInstance; }
signals:
void ExitFinished();
private slots: private slots:
void Exit();
void WorkerFailedToStart(); void WorkerFailedToStart();
private: private:
@@ -75,6 +81,7 @@ class TagReaderClient : public QObject {
WorkerPool<HandlerType> *worker_pool_; WorkerPool<HandlerType> *worker_pool_;
QList<pb::tagreader::Message> message_queue_; QList<pb::tagreader::Message> message_queue_;
QThread *original_thread_;
}; };
typedef TagReaderClient::ReplyType TagReaderReply; typedef TagReaderClient::ReplyType TagReaderReply;

View File

@@ -23,12 +23,12 @@
#include <QtGlobal> #include <QtGlobal>
#include <QObject> #include <QObject>
#include <QDir>
#include <QQueue>
#include <QMutex>
#include <QStandardPaths> #include <QStandardPaths>
#include <QSize> #include <QDir>
#include <QThread>
#include <QMutex>
#include <QList> #include <QList>
#include <QQueue>
#include <QSet> #include <QSet>
#include <QVariant> #include <QVariant>
#include <QString> #include <QString>
@@ -37,6 +37,7 @@
#include <QImage> #include <QImage>
#include <QPixmap> #include <QPixmap>
#include <QPainter> #include <QPainter>
#include <QSize>
#include <QNetworkAccessManager> #include <QNetworkAccessManager>
#include <QNetworkReply> #include <QNetworkReply>
#include <QNetworkRequest> #include <QNetworkRequest>
@@ -61,13 +62,30 @@ AlbumCoverLoader::AlbumCoverLoader(QObject *parent)
cover_filename_(CollectionSettingsPage::SaveCover_Hash), cover_filename_(CollectionSettingsPage::SaveCover_Hash),
cover_overwrite_(false), cover_overwrite_(false),
cover_lowercase_(true), cover_lowercase_(true),
cover_replace_spaces_(true) cover_replace_spaces_(true),
original_thread_(nullptr)
{ {
original_thread_ = thread();
ReloadSettings(); ReloadSettings();
} }
void AlbumCoverLoader::ExitAsync() {
stop_requested_ = true;
metaObject()->invokeMethod(this, "Exit", Qt::QueuedConnection);
}
void AlbumCoverLoader::Exit() {
assert(QThread::currentThread() == thread());
moveToThread(original_thread_);
emit ExitFinished();
}
void AlbumCoverLoader::ReloadSettings() { void AlbumCoverLoader::ReloadSettings() {
QSettings s; QSettings s;

View File

@@ -41,6 +41,7 @@
#include "settings/collectionsettingspage.h" #include "settings/collectionsettingspage.h"
#include "albumcoverloaderoptions.h" #include "albumcoverloaderoptions.h"
class QThread;
class Song; class Song;
class NetworkAccessManager; class NetworkAccessManager;
@@ -52,6 +53,7 @@ class AlbumCoverLoader : public QObject {
void ReloadSettings(); void ReloadSettings();
void ExitAsync();
void Stop() { stop_requested_ = true; } void Stop() { stop_requested_ = true; }
static QString ImageCacheDir(const Song::Source source); static QString ImageCacheDir(const Song::Source source);
@@ -68,11 +70,13 @@ class AlbumCoverLoader : public QObject {
static QPixmap TryLoadPixmap(const QUrl &automatic, const QUrl &manual, const QUrl &url = QUrl()); static QPixmap TryLoadPixmap(const QUrl &automatic, const QUrl &manual, const QUrl &url = QUrl());
static QImage ScaleAndPad(const AlbumCoverLoaderOptions &options, const QImage &image); static QImage ScaleAndPad(const AlbumCoverLoaderOptions &options, const QImage &image);
signals: signals:
void ExitFinished();
void ImageLoaded(const quint64 id, const QUrl &cover_url, const QImage &image); void ImageLoaded(const quint64 id, const QUrl &cover_url, const QImage &image);
void ImageLoaded(const quint64 id, const QUrl &cover_url, const QImage &scaled, const QImage &original); void ImageLoaded(const quint64 id, const QUrl &cover_url, const QImage &scaled, const QImage &original);
protected slots: protected slots:
void Exit();
void ProcessTasks(); void ProcessTasks();
void RemoteFetchFinished(QNetworkReply *reply, const QUrl &cover_url); void RemoteFetchFinished(QNetworkReply *reply, const QUrl &cover_url);
@@ -128,6 +132,8 @@ signals:
bool cover_lowercase_; bool cover_lowercase_;
bool cover_replace_spaces_; bool cover_replace_spaces_;
QThread *original_thread_;
}; };
#endif // ALBUMCOVERLOADER_H #endif // ALBUMCOVERLOADER_H

View File

@@ -86,7 +86,6 @@ void InternetServices::Exit() {
void InternetServices::ExitReceived() { void InternetServices::ExitReceived() {
InternetService *service = qobject_cast<InternetService*>(sender()); InternetService *service = qobject_cast<InternetService*>(sender());
wait_for_exit_.removeAll(service); wait_for_exit_.removeAll(service);
if (wait_for_exit_.isEmpty()) emit ExitFinished(); if (wait_for_exit_.isEmpty()) emit ExitFinished();

View File

@@ -180,9 +180,9 @@ QobuzService::~QobuzService() {
stream_url_req->deleteLater(); stream_url_req->deleteLater();
} }
delete artists_collection_backend_; artists_collection_backend_->deleteLater();
delete albums_collection_backend_; albums_collection_backend_->deleteLater();
delete songs_collection_backend_; songs_collection_backend_->deleteLater();
} }
@@ -202,8 +202,10 @@ void QobuzService::Exit() {
void QobuzService::ExitReceived() { void QobuzService::ExitReceived() {
disconnect(sender(), 0, this, 0); QObject *obj = static_cast<QObject*>(sender());
wait_for_exit_.removeAll(sender()); disconnect(obj, 0, this, 0);
qLog(Debug) << obj << "successfully exited.";
wait_for_exit_.removeAll(obj);
if (wait_for_exit_.isEmpty()) emit ExitFinished(); if (wait_for_exit_.isEmpty()) emit ExitFinished();
} }

View File

@@ -95,7 +95,7 @@ SubsonicService::SubsonicService(Application *app, QObject *parent)
} }
SubsonicService::~SubsonicService() { SubsonicService::~SubsonicService() {
delete collection_backend_; collection_backend_->deleteLater();
} }
void SubsonicService::Exit() { void SubsonicService::Exit() {

View File

@@ -186,9 +186,9 @@ TidalService::~TidalService() {
stream_url_req->deleteLater(); stream_url_req->deleteLater();
} }
delete artists_collection_backend_; artists_collection_backend_->deleteLater();
delete albums_collection_backend_; albums_collection_backend_->deleteLater();
delete songs_collection_backend_; songs_collection_backend_->deleteLater();
} }
@@ -208,8 +208,10 @@ void TidalService::Exit() {
void TidalService::ExitReceived() { void TidalService::ExitReceived() {
disconnect(sender(), 0, this, 0); QObject *obj = static_cast<QObject*>(sender());
wait_for_exit_.removeAll(sender()); disconnect(obj, 0, this, 0);
qLog(Debug) << obj << "successfully exited.";
wait_for_exit_.removeAll(obj);
if (wait_for_exit_.isEmpty()) emit ExitFinished(); if (wait_for_exit_.isEmpty()) emit ExitFinished();
} }