Lazy initialise everything in Application.

This commit is contained in:
John Maguire 2016-02-11 15:11:31 +00:00
parent 6844dcc140
commit f2daa772c1
9 changed files with 238 additions and 220 deletions

View File

@ -41,6 +41,13 @@ class Lazy {
T* operator->() const { return get(); }
// Returns true if the object is not yet initialised.
explicit operator bool() const { return ptr_; }
// Deletes the underlying object and will re-run the initialisation function
// if the object is requested again.
void reset() { ptr_.reset(nullptr); }
private:
void CheckInitialised() const {
if (!ptr_) {

View File

@ -25,6 +25,8 @@
#include <execinfo.h>
#endif
#include <iostream>
#include <QCoreApplication>
#include <QDateTime>
#include <QStringList>
@ -202,10 +204,11 @@ QDebug CreateLogger(Level level, const QString& class_name, int line) {
}
QDebug ret(type);
ret.nospace() << kMessageHandlerMagic << QDateTime::currentDateTime()
.toString("hh:mm:ss.zzz")
.toAscii()
.constData() << level_name
ret.nospace() << kMessageHandlerMagic
<< QDateTime::currentDateTime()
.toString("hh:mm:ss.zzz")
.toAscii()
.constData() << level_name
<< function_line.leftJustified(32).toAscii().constData();
return ret.space();
@ -257,7 +260,8 @@ void DumpStackTrace() {
backtrace_symbols(reinterpret_cast<void**>(&callstack), callstack_size);
// Start from 1 to skip ourself.
for (int i = 1; i < callstack_size; ++i) {
qLog(Debug) << DemangleSymbol(QString::fromAscii(symbols[i]));
std::cerr << DemangleSymbol(QString::fromAscii(symbols[i])).toStdString()
<< std::endl;
}
free(symbols);
#else
@ -269,7 +273,7 @@ void DumpStackTrace() {
namespace {
template<typename T>
template <typename T>
QString print_duration(T duration, const std::string& unit) {
return QString("%1%2").arg(duration.count()).arg(unit.c_str());
}

View File

@ -21,29 +21,6 @@
*/
#include "application.h"
#include "appearance.h"
#include "config.h"
#include "database.h"
#include "player.h"
#include "tagreaderclient.h"
#include "taskmanager.h"
#include "covers/albumcoverloader.h"
#include "covers/coverproviders.h"
#include "covers/currentartloader.h"
#include "devices/devicemanager.h"
#include "internet/core/internetmodel.h"
#include "globalsearch/globalsearch.h"
#include "library/library.h"
#include "library/librarybackend.h"
#include "networkremote/networkremote.h"
#include "networkremote/networkremotehelper.h"
#include "playlist/playlistbackend.h"
#include "playlist/playlistmanager.h"
#include "internet/podcasts/gpoddersync.h"
#include "internet/podcasts/podcastbackend.h"
#include "internet/podcasts/podcastdeleter.h"
#include "internet/podcasts/podcastdownloader.h"
#include "internet/podcasts/podcastupdater.h"
#ifdef HAVE_LIBLASTFM
#include "internet/lastfm/lastfmservice.h"
@ -58,95 +35,96 @@ bool Application::kIsPortable = false;
Application::Application(QObject* parent)
: QObject(parent),
tag_reader_client_(nullptr),
database_(nullptr),
album_cover_loader_(nullptr),
playlist_backend_(nullptr),
podcast_backend_(nullptr),
appearance_(nullptr),
cover_providers_(nullptr),
task_manager_(nullptr),
player_(nullptr),
playlist_manager_(nullptr),
current_art_loader_(nullptr),
global_search_(nullptr),
internet_model_(nullptr),
library_(nullptr),
device_manager_(nullptr),
podcast_updater_(nullptr),
podcast_deleter_(nullptr),
podcast_downloader_(nullptr),
gpodder_sync_(nullptr),
moodbar_loader_(nullptr),
moodbar_controller_(nullptr),
network_remote_(nullptr),
network_remote_helper_(nullptr),
scrobbler_(nullptr) {
tag_reader_client_ = new TagReaderClient(this);
MoveToNewThread(tag_reader_client_);
tag_reader_client_->Start();
database_ = new Database(this, this);
MoveToNewThread(database_);
album_cover_loader_ = new AlbumCoverLoader(this);
MoveToNewThread(album_cover_loader_);
playlist_backend_ = new PlaylistBackend(this, this);
MoveToThread(playlist_backend_, database_->thread());
podcast_backend_ = new PodcastBackend(this, this);
MoveToThread(podcast_backend_, database_->thread());
appearance_ = new Appearance(this);
cover_providers_ = new CoverProviders(this);
task_manager_ = new TaskManager(this);
player_ = new Player(this, this);
playlist_manager_ = new PlaylistManager(this, this);
current_art_loader_ = new CurrentArtLoader(this, this);
global_search_ = new GlobalSearch(this, this);
internet_model_ = new InternetModel(this, this);
library_ = new Library(this, this);
device_manager_ = new DeviceManager(this, this);
podcast_updater_ = new PodcastUpdater(this, this);
podcast_deleter_ = new PodcastDeleter(this, this);
MoveToNewThread(podcast_deleter_);
podcast_downloader_ = new PodcastDownloader(this, this);
gpodder_sync_ = new GPodderSync(this, this);
tag_reader_client_([&](){
TagReaderClient* client = new TagReaderClient(this);
MoveToNewThread(client);
client->Start();
return client;
}),
database_([&]() {
Database* db = new Database(this, this);
MoveToNewThread(db);
DoInAMinuteOrSo(db, SLOT(DoBackup()));
return db;
}),
album_cover_loader_([&]() {
AlbumCoverLoader* loader = new AlbumCoverLoader(this);
MoveToNewThread(loader);
return loader;
}),
playlist_backend_([&]() {
PlaylistBackend* backend = new PlaylistBackend(this, this);
MoveToThread(backend, database_->thread());
return backend;
}),
podcast_backend_([&]() {
PodcastBackend* backend = new PodcastBackend(this, this);
MoveToThread(backend, database_->thread());
return backend;
}),
appearance_([=]() { return new Appearance(this); }),
cover_providers_([=]() { return new CoverProviders(this); }),
task_manager_([=]() { return new TaskManager(this); }),
player_([=]() { return new Player(this, this); }),
playlist_manager_([=]() { return new PlaylistManager(this); }),
current_art_loader_([=]() { return new CurrentArtLoader(this, this); }),
global_search_([=]() { return new GlobalSearch(this, this); }),
internet_model_([=]() { return new InternetModel(this, this); }),
library_([=]() { return new Library(this, this); }),
device_manager_([=]() { return new DeviceManager(this, this); }),
podcast_updater_([=]() { return new PodcastUpdater(this, this); }),
podcast_deleter_([=]() {
PodcastDeleter* deleter = new PodcastDeleter(this, this);
MoveToNewThread(deleter);
return deleter;
}),
podcast_downloader_([=]() {
return new PodcastDownloader(this, this);
}),
gpodder_sync_([=]() { return new GPodderSync(this, this); }),
moodbar_loader_([=]() {
#ifdef HAVE_MOODBAR
moodbar_loader_ = new MoodbarLoader(this, this);
moodbar_controller_ = new MoodbarController(this, this);
return new MoodbarLoader(this, this);
#else
return nullptr;
#endif
// Network Remote
network_remote_ = new NetworkRemote(this);
MoveToNewThread(network_remote_);
// This must be before libraray_->Init();
}),
moodbar_controller_([=]() {
#ifdef HAVE_MOODBAR
return new MoodbarController(this, this);
#else
return nullptr;
#endif
}),
network_remote_([=]() {
NetworkRemote* remote = new NetworkRemote(this);
MoveToNewThread(remote);
return remote;
}),
network_remote_helper_([=]() {
return new NetworkRemoteHelper(this);
}),
scrobbler_([=]() {
#ifdef HAVE_LIBLASTFM
return new LastFMService(this, this);
#else
return nullptr;
#endif
}) {
// This must be before library_->Init();
// In the constructor the helper waits for the signal
// PlaylistManagerInitialized
// to start the remote. Without the playlist manager clementine can
// crash when a client connects before the manager is initialized!
network_remote_helper_ = new NetworkRemoteHelper(this);
#ifdef HAVE_LIBLASTFM
scrobbler_ = new LastFMService(this, this);
#endif // HAVE_LIBLASTFM
network_remote_helper_.get();
library_->Init();
DoInAMinuteOrSo(database_, SLOT(DoBackup()));
}
Application::~Application() {
// It's important that the device manager is deleted before the database.
// Deleting the database deletes all objects that have been created in its
// thread, including some device library backends.
delete device_manager_;
device_manager_ = nullptr;
device_manager_.reset();
for (QObject* object : objects_in_threads_) {
object->deleteLater();

View File

@ -22,37 +22,37 @@
#ifndef CORE_APPLICATION_H_
#define CORE_APPLICATION_H_
#include "config.h"
#include "core/appearance.h"
#include "core/database.h"
#include "core/lazy.h"
#include "core/player.h"
#include "covers/albumcoverloader.h"
#include "covers/coverproviders.h"
#include "covers/currentartloader.h"
#include "devices/devicemanager.h"
#include "globalsearch/globalsearch.h"
#include "internet/core/internetmodel.h"
#include "internet/core/scrobbler.h"
#include "internet/podcasts/gpoddersync.h"
#include "internet/podcasts/podcastbackend.h"
#include "internet/podcasts/podcastdeleter.h"
#include "internet/podcasts/podcastdownloader.h"
#include "internet/podcasts/podcastupdater.h"
#include "library/librarybackend.h"
#include "library/library.h"
#include "moodbar/moodbarcontroller.h"
#include "moodbar/moodbarloader.h"
#include "networkremote/networkremote.h"
#include "networkremote/networkremotehelper.h"
#include "playlist/playlistbackend.h"
#include "playlist/playlistmanager.h"
#include "tagreaderclient.h"
#include "taskmanager.h"
#include "ui/settingsdialog.h"
#include <QObject>
class AlbumCoverLoader;
class Appearance;
class CoverProviders;
class CurrentArtLoader;
class Database;
class DeviceManager;
class GlobalSearch;
class GPodderSync;
class InternetModel;
class Library;
class LibraryBackend;
class LibraryModel;
class MoodbarController;
class MoodbarLoader;
class NetworkRemote;
class NetworkRemoteHelper;
class Player;
class PlaylistBackend;
class PodcastDeleter;
class PodcastDownloader;
class PlaylistManager;
class PodcastBackend;
class PodcastUpdater;
class Scrobbler;
class TagReaderClient;
class TaskManager;
class Application : public QObject {
Q_OBJECT
@ -68,32 +68,42 @@ class Application : public QObject {
QString language_without_region() const;
void set_language_name(const QString& name) { language_name_ = name; }
TagReaderClient* tag_reader_client() const { return tag_reader_client_; }
Database* database() const { return database_; }
AlbumCoverLoader* album_cover_loader() const { return album_cover_loader_; }
PlaylistBackend* playlist_backend() const { return playlist_backend_; }
PodcastBackend* podcast_backend() const { return podcast_backend_; }
Appearance* appearance() const { return appearance_; }
CoverProviders* cover_providers() const { return cover_providers_; }
TaskManager* task_manager() const { return task_manager_; }
Player* player() const { return player_; }
PlaylistManager* playlist_manager() const { return playlist_manager_; }
CurrentArtLoader* current_art_loader() const { return current_art_loader_; }
GlobalSearch* global_search() const { return global_search_; }
InternetModel* internet_model() const { return internet_model_; }
Library* library() const { return library_; }
DeviceManager* device_manager() const { return device_manager_; }
PodcastUpdater* podcast_updater() const { return podcast_updater_; }
PodcastDeleter* podcast_deleter() const { return podcast_deleter_; }
PodcastDownloader* podcast_downloader() const { return podcast_downloader_; }
GPodderSync* gpodder_sync() const { return gpodder_sync_; }
MoodbarLoader* moodbar_loader() const { return moodbar_loader_; }
MoodbarController* moodbar_controller() const { return moodbar_controller_; }
NetworkRemote* network_remote() const { return network_remote_; }
NetworkRemoteHelper* network_remote_helper() const {
return network_remote_helper_;
TagReaderClient* tag_reader_client() const {
return tag_reader_client_.get();
}
Scrobbler* scrobbler() const { return scrobbler_; }
Database* database() const { return database_.get(); }
AlbumCoverLoader* album_cover_loader() const {
return album_cover_loader_.get();
}
PlaylistBackend* playlist_backend() const { return playlist_backend_.get(); }
PodcastBackend* podcast_backend() const { return podcast_backend_.get(); }
Appearance* appearance() const { return appearance_.get(); }
CoverProviders* cover_providers() const { return cover_providers_.get(); }
TaskManager* task_manager() const { return task_manager_.get(); }
Player* player() const { return player_.get(); }
PlaylistManager* playlist_manager() const { return playlist_manager_.get(); }
CurrentArtLoader* current_art_loader() const {
return current_art_loader_.get();
}
GlobalSearch* global_search() const { return global_search_.get(); }
InternetModel* internet_model() const { return internet_model_.get(); }
Library* library() const { return library_.get(); }
DeviceManager* device_manager() const { return device_manager_.get(); }
PodcastUpdater* podcast_updater() const { return podcast_updater_.get(); }
PodcastDeleter* podcast_deleter() const { return podcast_deleter_.get(); }
PodcastDownloader* podcast_downloader() const {
return podcast_downloader_.get();
}
GPodderSync* gpodder_sync() const { return gpodder_sync_.get(); }
MoodbarLoader* moodbar_loader() const { return moodbar_loader_.get(); }
MoodbarController* moodbar_controller() const {
return moodbar_controller_.get();
}
NetworkRemote* network_remote() const { return network_remote_.get(); }
NetworkRemoteHelper* network_remote_helper() const {
return network_remote_helper_.get();
}
Scrobbler* scrobbler() const { return scrobbler_.get(); }
LibraryBackend* library_backend() const;
LibraryModel* library_model() const;
@ -106,7 +116,7 @@ class Application : public QObject {
void ReloadSettings();
void OpenSettingsDialogAtPage(SettingsDialog::Page page);
signals:
signals:
void ErrorAdded(const QString& message);
void SettingsChanged();
void SettingsDialogRequested(SettingsDialog::Page page);
@ -114,30 +124,30 @@ class Application : public QObject {
private:
QString language_name_;
TagReaderClient* tag_reader_client_;
Database* database_;
AlbumCoverLoader* album_cover_loader_;
PlaylistBackend* playlist_backend_;
PodcastBackend* podcast_backend_;
Appearance* appearance_;
CoverProviders* cover_providers_;
TaskManager* task_manager_;
Player* player_;
PlaylistManager* playlist_manager_;
CurrentArtLoader* current_art_loader_;
GlobalSearch* global_search_;
InternetModel* internet_model_;
Library* library_;
DeviceManager* device_manager_;
PodcastUpdater* podcast_updater_;
PodcastDeleter* podcast_deleter_;
PodcastDownloader* podcast_downloader_;
GPodderSync* gpodder_sync_;
MoodbarLoader* moodbar_loader_;
MoodbarController* moodbar_controller_;
NetworkRemote* network_remote_;
NetworkRemoteHelper* network_remote_helper_;
Scrobbler* scrobbler_;
Lazy<TagReaderClient> tag_reader_client_;
Lazy<Database> database_;
Lazy<AlbumCoverLoader> album_cover_loader_;
Lazy<PlaylistBackend> playlist_backend_;
Lazy<PodcastBackend> podcast_backend_;
Lazy<Appearance> appearance_;
Lazy<CoverProviders> cover_providers_;
Lazy<TaskManager> task_manager_;
Lazy<Player> player_;
Lazy<PlaylistManager> playlist_manager_;
Lazy<CurrentArtLoader> current_art_loader_;
Lazy<GlobalSearch> global_search_;
Lazy<InternetModel> internet_model_;
Lazy<Library> library_;
Lazy<DeviceManager> device_manager_;
Lazy<PodcastUpdater> podcast_updater_;
Lazy<PodcastDeleter> podcast_deleter_;
Lazy<PodcastDownloader> podcast_downloader_;
Lazy<GPodderSync> gpodder_sync_;
Lazy<MoodbarLoader> moodbar_loader_;
Lazy<MoodbarController> moodbar_controller_;
Lazy<NetworkRemote> network_remote_;
Lazy<NetworkRemoteHelper> network_remote_helper_;
Lazy<Scrobbler> scrobbler_;
QList<QObject*> objects_in_threads_;
QList<QThread*> threads_;

View File

@ -17,15 +17,18 @@
#include "networkremote.h"
#include "core/logging.h"
#include "covers/currentartloader.h"
#include "networkremote/zeroconf.h"
#include "playlist/playlistmanager.h"
#include <QDataStream>
#include <QSettings>
#include <QHostInfo>
#include <QNetworkProxy>
#include <QTcpServer>
#include "core/logging.h"
#include "covers/currentartloader.h"
#include "networkremote/incomingdataparser.h"
#include "networkremote/outgoingdatacreator.h"
#include "networkremote/zeroconf.h"
#include "playlist/playlistmanager.h"
const char* NetworkRemote::kSettingsGroup = "NetworkRemote";
const quint16 NetworkRemote::kDefaultServerPort = 5500;

View File

@ -3,14 +3,17 @@
#include <memory>
#include <QTcpServer>
#include <QTcpSocket>
#include <QList>
#include <QObject>
#include "core/player.h"
#include "core/application.h"
#include "incomingdataparser.h"
#include "outgoingdatacreator.h"
#include "remoteclient.h"
class Application;
class IncomingDataParser;
class OutgoingDataCreator;
class QHostAddress;
class QImage;
class QTcpServer;
class QTcpSocket;
class RemoteClient;
class NetworkRemote : public QObject {
Q_OBJECT

View File

@ -15,10 +15,11 @@
along with Clementine. If not, see <http://www.gnu.org/licenses/>.
*/
#include "core/logging.h"
#include "networkremote/networkremotehelper.h"
#include "networkremote.h"
#include "networkremotehelper.h"
#include "core/application.h"
#include "core/logging.h"
#include "networkremote/networkremote.h"
NetworkRemoteHelper* NetworkRemoteHelper::sInstance = nullptr;

View File

@ -17,14 +17,15 @@
#include "songsender.h"
#include "networkremote.h"
#include <QFileInfo>
#include "core/application.h"
#include "core/logging.h"
#include "core/utilities.h"
#include "library/librarybackend.h"
#include "networkremote/networkremote.h"
#include "networkremote/outgoingdatacreator.h"
#include "networkremote/remoteclient.h"
#include "playlist/playlistitem.h"
const quint32 SongSender::kFileChunkSize = 100000; // in Bytes
@ -32,16 +33,18 @@ const quint32 SongSender::kFileChunkSize = 100000; // in Bytes
SongSender::SongSender(Application* app, RemoteClient* client)
: app_(app),
client_(client),
transcoder_(new Transcoder(this, NetworkRemote::kTranscoderSettingPostfix)) {
transcoder_(
new Transcoder(this, NetworkRemote::kTranscoderSettingPostfix)) {
QSettings s;
s.beginGroup(NetworkRemote::kSettingsGroup);
transcode_lossless_files_ = s.value("convert_lossless", false).toBool();
// Load preset
QString last_output_format = s.value("last_output_format", "audio/x-vorbis").toString();
QString last_output_format =
s.value("last_output_format", "audio/x-vorbis").toString();
QList<TranscoderPreset> presets = transcoder_->GetAllPresets();
for (int i = 0; i<presets.count(); ++i) {
for (int i = 0; i < presets.count(); ++i) {
if (last_output_format == presets.at(i).codec_mimetype_) {
transcoder_preset_ = presets.at(i);
break;
@ -58,8 +61,9 @@ SongSender::SongSender(Application* app, RemoteClient* client)
SongSender::~SongSender() {
disconnect(transcoder_, SIGNAL(JobComplete(QString, QString, bool)), this,
SLOT(TranscodeJobComplete(QString, QString, bool)));
disconnect(transcoder_, SIGNAL(AllJobsComplete()), this, SLOT(StartTransfer()));
SLOT(TranscodeJobComplete(QString, QString, bool)));
disconnect(transcoder_, SIGNAL(AllJobsComplete()), this,
SLOT(StartTransfer()));
transcoder_->Cancel();
}
@ -102,8 +106,7 @@ void SongSender::SendSongs(const pb::remote::RequestDownloadSongs& request) {
void SongSender::TranscodeLosslessFiles() {
for (DownloadItem item : download_queue_) {
// Check only lossless files
if (!item.song_.IsFileLossless())
continue;
if (!item.song_.IsFileLossless()) continue;
// Add the file to the transcoder
QString local_file = item.song_.url().toLocalFile();
@ -122,7 +125,8 @@ void SongSender::TranscodeLosslessFiles() {
}
}
void SongSender::TranscodeJobComplete(const QString& input, const QString& output, bool success) {
void SongSender::TranscodeJobComplete(const QString& input,
const QString& output, bool success) {
qLog(Debug) << input << "transcoded to" << output << success;
// If it wasn't successful send original file
@ -204,7 +208,8 @@ void SongSender::OfferNextSong() {
chunk->set_file_number(item.song_no_);
chunk->set_size(file.size());
OutgoingDataCreator::CreateSong(item.song_, QImage(), -1, chunk->mutable_song_metadata());
OutgoingDataCreator::CreateSong(item.song_, QImage(), -1,
chunk->mutable_song_metadata());
}
client_->SendData(&msg);
@ -215,8 +220,7 @@ void SongSender::ResponseSongOffer(bool accepted) {
// Get the item and send the single song
DownloadItem item = download_queue_.dequeue();
if (accepted)
SendSingleSong(item);
if (accepted) SendSingleSong(item);
// And offer the next song
OfferNextSong();
@ -273,7 +277,8 @@ void SongSender::SendSingleSong(DownloadItem download_item) {
int i = app_->playlist_manager()->active()->current_row();
pb::remote::SongMetadata* song_metadata =
msg.mutable_response_song_file_chunk()->mutable_song_metadata();
OutgoingDataCreator::CreateSong(download_item.song_, null_image, i,song_metadata);
OutgoingDataCreator::CreateSong(download_item.song_, null_image, i,
song_metadata);
// if the file was transcoded, we have to change the filename and filesize
if (is_transcoded) {
@ -341,7 +346,7 @@ void SongSender::SendPlaylist(int playlist_id) {
}
}
void SongSender::SendUrls(const pb::remote::RequestDownloadSongs &request) {
void SongSender::SendUrls(const pb::remote::RequestDownloadSongs& request) {
SongList song_list;
// First gather all valid songs

View File

@ -15,18 +15,23 @@
along with Clementine. If not, see <http://www.gnu.org/licenses/>.
*/
#include "iconloader.h"
#include "networkremotesettingspage.h"
#include "ui_networkremotesettingspage.h"
#include <QDesktopServices>
#include <QFile>
#include <QHostInfo>
#include <QNetworkInterface>
#include <QSettings>
#include <QUrl>
#include "core/application.h"
#include "networkremote/networkremote.h"
#include "networkremote/networkremotehelper.h"
#include "transcoder/transcoder.h"
#include "transcoder/transcoderoptionsdialog.h"
#include <QDesktopServices>
#include <QSettings>
#include <QHostInfo>
#include <QNetworkInterface>
#include "ui/iconloader.h"
#include "ui/settingsdialog.h"
const char* NetworkRemoteSettingsPage::kPlayStoreUrl =
"https://play.google.com/store/apps/details?id=de.qspool.clementineremote";
@ -83,10 +88,12 @@ void NetworkRemoteSettingsPage::Load() {
ui_->auth_code->setValue(s.value("auth_code", qrand() % 100000).toInt());
ui_->allow_downloads->setChecked(s.value("allow_downloads", false).toBool());
ui_->convert_lossless->setChecked(s.value("convert_lossless", false).toBool());
ui_->convert_lossless->setChecked(
s.value("convert_lossless", false).toBool());
// Load settings
QString last_output_format = s.value("last_output_format", "audio/x-vorbis").toString();
QString last_output_format =
s.value("last_output_format", "audio/x-vorbis").toString();
for (int i = 0; i < ui_->format->count(); ++i) {
if (last_output_format ==
ui_->format->itemData(i).value<TranscoderPreset>().codec_mimetype_) {