/* This file is part of Clementine. Copyright 2012-2013, Andreas Copyright 2012, David Sansome Copyright 2012, Marti Raudsepp Copyright 2012, 2014, John Maguire Copyright 2013, Arnaud Bienner Copyright 2014, Krzysztof Sobiecki Clementine is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Clementine is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Clementine. If not, see . */ #include "application.h" #include #include #include "config.h" #include "core/appearance.h" #include "core/database.h" #include "core/lazy.h" #include "core/player.h" #include "core/tagreaderclient.h" #include "core/taskmanager.h" #include "covers/albumcoverloader.h" #include "covers/coverproviders.h" #include "covers/currentartloader.h" #include "covers/discogscoverprovider.h" #include "covers/musicbrainzcoverprovider.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/library.h" #include "library/librarybackend.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" #ifdef HAVE_LIBLASTFM #include "covers/lastfmcoverprovider.h" #include "internet/lastfm/lastfmservice.h" #endif // HAVE_LIBLASTFM #ifdef HAVE_MOODBAR #include "moodbar/moodbarcontroller.h" #include "moodbar/moodbarloader.h" #endif bool Application::kIsPortable = false; const char* Application::kLegacyPortableDataDir = "data"; const char* Application::kDefaultPortableDataDir = "clementine-data"; const char* Application::kPortableDataDir = nullptr; const char* Application::kDebugFeaturesKey = "CLEMENTINE_DEBUG"; const QStringList Application::kDefaultMusicExtensionsAllowedRemotely = { "aac", "alac", "flac", "m3u", "m4a", "mp3", "ogg", "wav", "wmv"}; // Use CLEMENTINE_DEBUG=1 to enable debug features. bool Application::DebugFeaturesEnabled() { QString showConsole = QProcessEnvironment::systemEnvironment().value(kDebugFeaturesKey, "0"); return (showConsole == "1"); } class ApplicationImpl { public: ApplicationImpl(Application* app) : settings_timer_(app), tag_reader_client_([=]() { TagReaderClient* client = new TagReaderClient(app); app->MoveToNewThread(client); client->Start(); return client; }), database_([=]() { Database* db = new Database(app, app); app->MoveToNewThread(db); DoInAMinuteOrSo(db, SLOT(DoBackup())); return db; }), album_cover_loader_([=]() { AlbumCoverLoader* loader = new AlbumCoverLoader(app); app->MoveToNewThread(loader); return loader; }), playlist_backend_([=]() { PlaylistBackend* backend = new PlaylistBackend(app, app); app->MoveToThread(backend, database_->thread()); return backend; }), podcast_backend_([=]() { PodcastBackend* backend = new PodcastBackend(app, app); app->MoveToThread(backend, database_->thread()); return backend; }), appearance_([=]() { return new Appearance(app); }), cover_providers_([=]() { CoverProviders* cover_providers = new CoverProviders(app); // Initialize the repository of cover providers. cover_providers->AddProvider(new MusicbrainzCoverProvider); cover_providers->AddProvider(new DiscogsCoverProvider); #ifdef HAVE_LIBLASTFM cover_providers->AddProvider(new LastFmCoverProvider(app)); #endif return cover_providers; }), task_manager_([=]() { return new TaskManager(app); }), player_([=]() { return new Player(app, app); }), playlist_manager_([=]() { return new PlaylistManager(app); }), current_art_loader_([=]() { return new CurrentArtLoader(app, app); }), global_search_([=]() { return new GlobalSearch(app, app); }), internet_model_([=]() { return new InternetModel(app, app); }), library_([=]() { return new Library(app, app); }), device_manager_([=]() { return new DeviceManager(app, app); }), podcast_updater_([=]() { return new PodcastUpdater(app, app); }), podcast_deleter_([=]() { PodcastDeleter* deleter = new PodcastDeleter(app, app); app->MoveToNewThread(deleter); return deleter; }), podcast_downloader_([=]() { return new PodcastDownloader(app, app); }), gpodder_sync_([=]() { return new GPodderSync(app, app); }), moodbar_loader_([=]() { #ifdef HAVE_MOODBAR return new MoodbarLoader(app, app); #else return nullptr; #endif }), moodbar_controller_([=]() { #ifdef HAVE_MOODBAR return new MoodbarController(app, app); #else return nullptr; #endif }), // Since NetworkRemote is moved to a different thread and creates // timers there, it should also be deleted on that thread. network_remote_( [=]() { NetworkRemote* remote = new NetworkRemote(app); app->MoveToNewThread(remote); return remote; }, [=](NetworkRemote* remote) { remote->deleteLater(); }), network_remote_helper_([=]() { return new NetworkRemoteHelper(app); }), scrobbler_([=]() { #ifdef HAVE_LIBLASTFM return new LastFMService(app, app); #else return nullptr; #endif }) { } QTimer settings_timer_; QSettings settings_; Lazy tag_reader_client_; Lazy database_; Lazy album_cover_loader_; Lazy playlist_backend_; Lazy podcast_backend_; Lazy appearance_; Lazy cover_providers_; Lazy task_manager_; Lazy player_; Lazy playlist_manager_; Lazy current_art_loader_; Lazy global_search_; Lazy internet_model_; Lazy library_; Lazy device_manager_; Lazy podcast_updater_; Lazy podcast_deleter_; Lazy podcast_downloader_; Lazy gpodder_sync_; Lazy moodbar_loader_; Lazy moodbar_controller_; Lazy network_remote_; Lazy network_remote_helper_; Lazy scrobbler_; }; Application::Application(QObject* parent) : QObject(parent), p_(new ApplicationImpl(this)) { setObjectName("Clementine Application"); // 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(); library()->Init(); // TODO(John Maguire): Make this not a weird singleton. tag_reader_client(); p_->settings_timer_.setInterval(1000); p_->settings_timer_.setSingleShot(true); connect(&(p_->settings_timer_), SIGNAL(timeout()), SLOT(SaveSettings_())); } 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. p_->device_manager_.reset(); for (QThread* thread : threads_) { thread->quit(); } for (QThread* thread : threads_) { thread->wait(); } } void Application::MoveToNewThread(QObject* object) { QThread* thread = new QThread(this); if (!object->objectName().isEmpty()) thread->setObjectName(object->objectName() + " thread"); MoveToThread(object, thread); thread->start(); threads_ << thread; } void Application::MoveToThread(QObject* object, QThread* thread) { object->setParent(nullptr); object->moveToThread(thread); } void Application::AddError(const QString& message) { emit ErrorAdded(message); } QString Application::language_without_region() const { const int underscore = language_name_.indexOf('_'); if (underscore != -1) { return language_name_.left(underscore); } return language_name_; } void Application::SaveSettings_() { emit SaveSettings(&(p_->settings_)); } void Application::ReloadSettings() { emit SettingsChanged(); } void Application::OpenSettingsDialogAtPage(SettingsDialog::Page page) { emit SettingsDialogRequested(page); } AlbumCoverLoader* Application::album_cover_loader() const { return p_->album_cover_loader_.get(); } Appearance* Application::appearance() const { return p_->appearance_.get(); } CoverProviders* Application::cover_providers() const { return p_->cover_providers_.get(); } CurrentArtLoader* Application::current_art_loader() const { return p_->current_art_loader_.get(); } Database* Application::database() const { return p_->database_.get(); } DeviceManager* Application::device_manager() const { return p_->device_manager_.get(); } GlobalSearch* Application::global_search() const { return p_->global_search_.get(); } GPodderSync* Application::gpodder_sync() const { return p_->gpodder_sync_.get(); } InternetModel* Application::internet_model() const { return p_->internet_model_.get(); } Library* Application::library() const { return p_->library_.get(); } LibraryBackend* Application::library_backend() const { return library()->backend(); } LibraryDirectoryModel* Application::directory_model() const { return library()->directory_model(); } LibraryModel* Application::library_model() const { return library()->model(); } MoodbarController* Application::moodbar_controller() const { return p_->moodbar_controller_.get(); } MoodbarLoader* Application::moodbar_loader() const { return p_->moodbar_loader_.get(); } NetworkRemoteHelper* Application::network_remote_helper() const { return p_->network_remote_helper_.get(); } NetworkRemote* Application::network_remote() const { return p_->network_remote_.get(); } Player* Application::player() const { return p_->player_.get(); } PlaylistBackend* Application::playlist_backend() const { return p_->playlist_backend_.get(); } PlaylistManager* Application::playlist_manager() const { return p_->playlist_manager_.get(); } PodcastBackend* Application::podcast_backend() const { return p_->podcast_backend_.get(); } PodcastDeleter* Application::podcast_deleter() const { return p_->podcast_deleter_.get(); } PodcastDownloader* Application::podcast_downloader() const { return p_->podcast_downloader_.get(); } PodcastUpdater* Application::podcast_updater() const { return p_->podcast_updater_.get(); } Scrobbler* Application::scrobbler() const { return p_->scrobbler_.get(); } TagReaderClient* Application::tag_reader_client() const { return p_->tag_reader_client_.get(); } TaskManager* Application::task_manager() const { return p_->task_manager_.get(); } void Application::DirtySettings() { p_->settings_timer_.start(); }