commit
5156422a6b
@ -70,7 +70,7 @@ CREATE INDEX idx_device_%deviceid_songs_album ON device_%deviceid_songs (album);
|
||||
CREATE INDEX idx_device_%deviceid_songs_comp_artist ON device_%deviceid_songs (effective_compilation, artist);
|
||||
|
||||
CREATE VIRTUAL TABLE device_%deviceid_fts USING fts3(
|
||||
ftstitle, ftsalbum, ftsartist, ftsalbumartist, ftscomposer, ftsperformer, ftsgrouping, ftsgenre, ftscomment,
|
||||
ftstitle, ftsalbum, ftsartist, ftsalbumartist, ftscomposer, ftsperformer, ftsgrouping, ftsgenre, ftscomment, ftsyear
|
||||
tokenize=unicode
|
||||
);
|
||||
|
||||
|
@ -42,6 +42,7 @@
|
||||
#ifdef TAGLIB_HAS_OPUS
|
||||
#include <opusfile.h>
|
||||
#endif
|
||||
#include <apetag.h>
|
||||
#include <oggflacfile.h>
|
||||
#include <popularimeterframe.h>
|
||||
#include <speexfile.h>
|
||||
@ -722,6 +723,17 @@ bool TagReader::SaveFile(const QString& filename,
|
||||
tag->itemListMap()["aART"] = TagLib::StringList(song.albumartist().c_str());
|
||||
tag->itemListMap()["cpil"] =
|
||||
TagLib::StringList(song.compilation() ? "1" : "0");
|
||||
} else if (TagLib::WavPack::File* file =
|
||||
dynamic_cast<TagLib::WavPack::File*>(fileref->file())) {
|
||||
TagLib::APE::Tag* tag = file->APETag(true);
|
||||
if (!tag) return false;
|
||||
tag->setArtist(StdStringToTaglibString(song.artist()));
|
||||
tag->setAlbum(StdStringToTaglibString(song.album()));
|
||||
tag->setTitle(StdStringToTaglibString(song.title()));
|
||||
tag->setGenre(StdStringToTaglibString(song.genre()));
|
||||
tag->setComment(StdStringToTaglibString(song.comment()));
|
||||
tag->setYear(song.year());
|
||||
tag->setTrack(song.track());
|
||||
}
|
||||
|
||||
// Handle all the files which have VorbisComments (Ogg, OPUS, ...) in the same
|
||||
|
@ -148,7 +148,6 @@ set(SOURCES
|
||||
globalsearch/simplesearchprovider.cpp
|
||||
globalsearch/somafmsearchprovider.cpp
|
||||
globalsearch/intergalacticfmsearchprovider.cpp
|
||||
globalsearch/soundcloudsearchprovider.cpp
|
||||
globalsearch/spotifysearchprovider.cpp
|
||||
globalsearch/suggestionwidget.cpp
|
||||
globalsearch/urlsearchprovider.cpp
|
||||
@ -186,8 +185,6 @@ set(SOURCES
|
||||
internet/somafm/somafmurlhandler.cpp
|
||||
internet/intergalacticfm/intergalacticfmservice.cpp
|
||||
internet/intergalacticfm/intergalacticfmurlhandler.cpp
|
||||
internet/soundcloud/soundcloudservice.cpp
|
||||
internet/soundcloud/soundcloudsettingspage.cpp
|
||||
internet/spotify/spotifyserver.cpp
|
||||
internet/spotify/spotifyservice.cpp
|
||||
internet/spotify/spotifysettingspage.cpp
|
||||
@ -461,7 +458,6 @@ set(HEADERS
|
||||
globalsearch/globalsearchview.h
|
||||
globalsearch/searchprovider.h
|
||||
globalsearch/simplesearchprovider.h
|
||||
globalsearch/soundcloudsearchprovider.h
|
||||
globalsearch/spotifysearchprovider.h
|
||||
globalsearch/suggestionwidget.h
|
||||
|
||||
@ -495,8 +491,6 @@ set(HEADERS
|
||||
internet/somafm/somafmurlhandler.h
|
||||
internet/intergalacticfm/intergalacticfmservice.h
|
||||
internet/intergalacticfm/intergalacticfmurlhandler.h
|
||||
internet/soundcloud/soundcloudservice.h
|
||||
internet/soundcloud/soundcloudsettingspage.h
|
||||
internet/spotify/spotifyserver.h
|
||||
internet/spotify/spotifyservice.h
|
||||
internet/spotify/spotifysettingspage.h
|
||||
@ -697,7 +691,6 @@ set(UI
|
||||
internet/magnatune/magnatunedownloaddialog.ui
|
||||
internet/magnatune/magnatunesettingspage.ui
|
||||
internet/core/searchboxwidget.ui
|
||||
internet/soundcloud/soundcloudsettingspage.ui
|
||||
internet/spotify/spotifysettingspage.ui
|
||||
internet/subsonic/subsonicsettingspage.ui
|
||||
|
||||
|
@ -1,91 +0,0 @@
|
||||
/* This file is part of Clementine.
|
||||
Copyright 2011, David Sansome <me@davidsansome.com>
|
||||
|
||||
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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "soundcloudsearchprovider.h"
|
||||
|
||||
#include <QIcon>
|
||||
|
||||
#include "core/application.h"
|
||||
#include "core/logging.h"
|
||||
#include "covers/albumcoverloader.h"
|
||||
#include "internet/soundcloud/soundcloudservice.h"
|
||||
#include "ui/iconloader.h"
|
||||
|
||||
SoundCloudSearchProvider::SoundCloudSearchProvider(Application* app,
|
||||
QObject* parent)
|
||||
: SearchProvider(app, parent), service_(nullptr) {}
|
||||
|
||||
void SoundCloudSearchProvider::Init(SoundCloudService* service) {
|
||||
service_ = service;
|
||||
SearchProvider::Init(
|
||||
"SoundCloud", "soundcloud", IconLoader::Load("soundcloud",
|
||||
IconLoader::Provider), WantsDelayedQueries | ArtIsProbablyRemote |
|
||||
CanShowConfig);
|
||||
|
||||
connect(service_, SIGNAL(SimpleSearchResults(int, SongList)),
|
||||
SLOT(SearchDone(int, SongList)));
|
||||
|
||||
cover_loader_options_.desired_height_ = kArtHeight;
|
||||
cover_loader_options_.pad_output_image_ = true;
|
||||
cover_loader_options_.scale_output_image_ = true;
|
||||
|
||||
connect(app_->album_cover_loader(), SIGNAL(ImageLoaded(quint64, QImage)),
|
||||
SLOT(AlbumArtLoaded(quint64, QImage)));
|
||||
}
|
||||
|
||||
void SoundCloudSearchProvider::SearchAsync(int id, const QString& query) {
|
||||
const int service_id = service_->SimpleSearch(query);
|
||||
pending_searches_[service_id] = PendingState(id, TokenizeQuery(query));
|
||||
;
|
||||
}
|
||||
|
||||
void SoundCloudSearchProvider::SearchDone(int id, const SongList& songs) {
|
||||
// Map back to the original id.
|
||||
const PendingState state = pending_searches_.take(id);
|
||||
const int global_search_id = state.orig_id_;
|
||||
|
||||
ResultList ret;
|
||||
for (const Song& song : songs) {
|
||||
Result result(this);
|
||||
result.metadata_ = song;
|
||||
|
||||
ret << result;
|
||||
}
|
||||
|
||||
emit ResultsAvailable(global_search_id, ret);
|
||||
MaybeSearchFinished(global_search_id);
|
||||
}
|
||||
|
||||
void SoundCloudSearchProvider::MaybeSearchFinished(int id) {
|
||||
if (pending_searches_.keys(PendingState(id, QStringList())).isEmpty()) {
|
||||
emit SearchFinished(id);
|
||||
}
|
||||
}
|
||||
|
||||
void SoundCloudSearchProvider::LoadArtAsync(int id, const Result& result) {
|
||||
quint64 loader_id = app_->album_cover_loader()->LoadImageAsync(
|
||||
cover_loader_options_, result.metadata_);
|
||||
cover_loader_tasks_[loader_id] = id;
|
||||
}
|
||||
|
||||
void SoundCloudSearchProvider::AlbumArtLoaded(quint64 id, const QImage& image) {
|
||||
if (!cover_loader_tasks_.contains(id)) {
|
||||
return;
|
||||
}
|
||||
int original_id = cover_loader_tasks_.take(id);
|
||||
emit ArtLoaded(original_id, image);
|
||||
}
|
@ -1,53 +0,0 @@
|
||||
/* This file is part of Clementine.
|
||||
Copyright 2012, David Sansome <me@davidsansome.com>
|
||||
|
||||
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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef SOUNDCLOUDSEARCHPROVIDER_H
|
||||
#define SOUNDCLOUDSEARCHPROVIDER_H
|
||||
|
||||
#include "searchprovider.h"
|
||||
#include "covers/albumcoverloaderoptions.h"
|
||||
#include "internet/soundcloud/soundcloudservice.h"
|
||||
|
||||
class AlbumCoverLoader;
|
||||
|
||||
class SoundCloudSearchProvider : public SearchProvider {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit SoundCloudSearchProvider(Application* app, QObject* parent = nullptr);
|
||||
void Init(SoundCloudService* service);
|
||||
|
||||
// SearchProvider
|
||||
void SearchAsync(int id, const QString& query);
|
||||
void LoadArtAsync(int id, const Result& result);
|
||||
InternetService* internet_service() { return service_; }
|
||||
|
||||
private slots:
|
||||
void AlbumArtLoaded(quint64 id, const QImage& image);
|
||||
void SearchDone(int id, const SongList& songs);
|
||||
|
||||
private:
|
||||
void MaybeSearchFinished(int id);
|
||||
|
||||
SoundCloudService* service_;
|
||||
QMap<int, PendingState> pending_searches_;
|
||||
|
||||
AlbumCoverLoaderOptions cover_loader_options_;
|
||||
QMap<quint64, int> cover_loader_tasks_;
|
||||
};
|
||||
|
||||
#endif
|
@ -41,7 +41,6 @@
|
||||
#include "internet/magnatune/magnatuneservice.h"
|
||||
#include "internet/podcasts/podcastservice.h"
|
||||
#include "internet/somafm/somafmservice.h"
|
||||
#include "internet/soundcloud/soundcloudservice.h"
|
||||
#include "internet/spotify/spotifyservice.h"
|
||||
#include "internet/subsonic/subsonicservice.h"
|
||||
#include "smartplaylists/generatormimedata.h"
|
||||
@ -93,7 +92,6 @@ InternetModel::InternetModel(Application* app, QObject* parent)
|
||||
AddService(new RadioTunesService(app, this));
|
||||
AddService(new SomaFMService(app, this));
|
||||
AddService(new IntergalacticFMService(app, this));
|
||||
AddService(new SoundCloudService(app, this));
|
||||
AddService(new SpotifyService(app, this));
|
||||
AddService(new SubsonicService(app, this));
|
||||
#ifdef HAVE_BOX
|
||||
|
@ -1,554 +0,0 @@
|
||||
/* This file is part of Clementine.
|
||||
Copyright 2012, 2014, Arnaud Bienner <arnaud.bienner@gmail.com>
|
||||
Copyright 2014, maximko <me@maximko.org>
|
||||
Copyright 2014, Krzysztof Sobiecki <sobkas@gmail.com>
|
||||
Copyright 2014, John Maguire <john.maguire@gmail.com>
|
||||
|
||||
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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "soundcloudservice.h"
|
||||
|
||||
#include <QDesktopServices>
|
||||
#include <QMenu>
|
||||
#include <QNetworkReply>
|
||||
#include <QNetworkRequest>
|
||||
#include <QTimer>
|
||||
#include <QUrlQuery>
|
||||
#include <QJsonDocument>
|
||||
#include <QJsonObject>
|
||||
#include <QJsonParseError>
|
||||
#include <QJsonArray>
|
||||
|
||||
#include "internet/core/internetmodel.h"
|
||||
#include "internet/core/oauthenticator.h"
|
||||
#include "internet/core/searchboxwidget.h"
|
||||
|
||||
#include "core/application.h"
|
||||
#include "core/closure.h"
|
||||
#include "core/logging.h"
|
||||
#include "core/mergedproxymodel.h"
|
||||
#include "core/network.h"
|
||||
#include "core/song.h"
|
||||
#include "core/taskmanager.h"
|
||||
#include "core/timeconstants.h"
|
||||
#include "core/utilities.h"
|
||||
#include "globalsearch/globalsearch.h"
|
||||
#include "globalsearch/soundcloudsearchprovider.h"
|
||||
#include "ui/iconloader.h"
|
||||
|
||||
const char* SoundCloudService::kApiClientId =
|
||||
"2add0f709fcfae1fd7a198ec7573d2d4";
|
||||
const char* SoundCloudService::kApiClientSecret =
|
||||
"d1cd7829da2e98e1e0621d85d57a2077";
|
||||
|
||||
const char* SoundCloudService::kServiceName = "SoundCloud";
|
||||
const char* SoundCloudService::kSettingsGroup = "SoundCloud";
|
||||
const char* SoundCloudService::kUrl = "https://api.soundcloud.com/";
|
||||
const char* SoundCloudService::kOAuthEndpoint = "https://soundcloud.com/connect";
|
||||
const char* SoundCloudService::kOAuthTokenEndpoint = "https://api.soundcloud.com/oauth2/token";
|
||||
const char* SoundCloudService::kOAuthScope = "non-expiring";
|
||||
const char* SoundCloudService::kHomepage = "http://soundcloud.com/";
|
||||
|
||||
const int SoundCloudService::kSearchDelayMsec = 400;
|
||||
const int SoundCloudService::kSongSearchLimit = 100;
|
||||
const int SoundCloudService::kSongSimpleSearchLimit = 100;
|
||||
|
||||
typedef QPair<QString, QString> Param;
|
||||
|
||||
SoundCloudService::SoundCloudService(Application* app, InternetModel* parent)
|
||||
: InternetService(kServiceName, app, parent, parent),
|
||||
root_(nullptr),
|
||||
search_(nullptr),
|
||||
user_tracks_(nullptr),
|
||||
user_playlists_(nullptr),
|
||||
user_activities_(nullptr),
|
||||
user_favorites_(nullptr),
|
||||
network_(new NetworkAccessManager(this)),
|
||||
context_menu_(nullptr),
|
||||
search_box_(new SearchBoxWidget(this)),
|
||||
search_delay_(new QTimer(this)),
|
||||
next_pending_search_id_(0) {
|
||||
search_delay_->setInterval(kSearchDelayMsec);
|
||||
search_delay_->setSingleShot(true);
|
||||
connect(search_delay_, SIGNAL(timeout()), SLOT(DoSearch()));
|
||||
|
||||
SoundCloudSearchProvider* search_provider =
|
||||
new SoundCloudSearchProvider(app_, this);
|
||||
search_provider->Init(this);
|
||||
app_->global_search()->AddProvider(search_provider);
|
||||
|
||||
connect(search_box_, SIGNAL(TextChanged(QString)), SLOT(Search(QString)));
|
||||
}
|
||||
|
||||
SoundCloudService::~SoundCloudService() {}
|
||||
|
||||
QStandardItem* SoundCloudService::CreateRootItem() {
|
||||
root_ = new QStandardItem(IconLoader::Load("soundcloud",
|
||||
IconLoader::Provider), kServiceName);
|
||||
root_->setData(true, InternetModel::Role_CanLazyLoad);
|
||||
root_->setData(InternetModel::PlayBehaviour_DoubleClickAction,
|
||||
InternetModel::Role_PlayBehaviour);
|
||||
return root_;
|
||||
}
|
||||
|
||||
void SoundCloudService::LazyPopulate(QStandardItem* item) {
|
||||
switch (item->data(InternetModel::Role_Type).toInt()) {
|
||||
case InternetModel::Type_Service: {
|
||||
EnsureItemsCreated();
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void SoundCloudService::EnsureItemsCreated() {
|
||||
if (!search_) {
|
||||
search_ =
|
||||
new QStandardItem(IconLoader::Load("edit-find", IconLoader::Base),
|
||||
tr("Search results"));
|
||||
search_->setToolTip(
|
||||
tr("Start typing something on the search box above to "
|
||||
"fill this search results list"));
|
||||
search_->setData(InternetModel::PlayBehaviour_MultipleItems,
|
||||
InternetModel::Role_PlayBehaviour);
|
||||
root_->appendRow(search_);
|
||||
}
|
||||
if (!user_tracks_ && !user_activities_ && !user_playlists_ && IsLoggedIn()) {
|
||||
user_activities_ = new QStandardItem(tr("Activities stream"));
|
||||
user_activities_->setData(InternetModel::PlayBehaviour_MultipleItems,
|
||||
InternetModel::Role_PlayBehaviour);
|
||||
root_->appendRow(user_activities_);
|
||||
|
||||
user_playlists_ = new QStandardItem(tr("Playlists"));
|
||||
root_->appendRow(user_playlists_);
|
||||
|
||||
user_tracks_ = new QStandardItem(tr("Tracks"));
|
||||
user_tracks_->setData(InternetModel::PlayBehaviour_MultipleItems,
|
||||
InternetModel::Role_PlayBehaviour);
|
||||
root_->appendRow(user_tracks_);
|
||||
|
||||
user_favorites_ = new QStandardItem(tr("Favorites"));
|
||||
root_->appendRow(user_favorites_);
|
||||
|
||||
RetrieveUserData(); // at least, try to (this will do nothing if user isn't
|
||||
// logged)
|
||||
}
|
||||
}
|
||||
|
||||
QWidget* SoundCloudService::HeaderWidget() const { return search_box_; }
|
||||
|
||||
void SoundCloudService::ShowConfig() {
|
||||
app_->OpenSettingsDialogAtPage(SettingsDialog::Page_SoundCloud);
|
||||
}
|
||||
|
||||
void SoundCloudService::Homepage() {
|
||||
QDesktopServices::openUrl(QUrl(kHomepage));
|
||||
}
|
||||
|
||||
void SoundCloudService::Connect() {
|
||||
OAuthenticator* oauth = new OAuthenticator(
|
||||
kApiClientId, kApiClientSecret,
|
||||
OAuthenticator::RedirectStyle::REMOTE_WITH_STATE, this);
|
||||
|
||||
oauth->StartAuthorisation(kOAuthEndpoint, kOAuthTokenEndpoint, kOAuthScope);
|
||||
|
||||
NewClosure(oauth, SIGNAL(Finished()), this,
|
||||
SLOT(ConnectFinished(OAuthenticator*)), oauth);
|
||||
}
|
||||
|
||||
void SoundCloudService::ConnectFinished(OAuthenticator* oauth) {
|
||||
oauth->deleteLater();
|
||||
|
||||
access_token_ = oauth->access_token();
|
||||
if (!access_token_.isEmpty()) {
|
||||
emit Connected();
|
||||
}
|
||||
expiry_time_ = oauth->expiry_time();
|
||||
QSettings s;
|
||||
s.beginGroup(kSettingsGroup);
|
||||
s.setValue("access_token", access_token_);
|
||||
|
||||
EnsureItemsCreated();
|
||||
}
|
||||
|
||||
void SoundCloudService::LoadAccessTokenIfEmpty() {
|
||||
if (access_token_.isEmpty()) {
|
||||
QSettings s;
|
||||
s.beginGroup(kSettingsGroup);
|
||||
if (!s.contains("access_token")) {
|
||||
return;
|
||||
}
|
||||
access_token_ = s.value("access_token").toString();
|
||||
}
|
||||
}
|
||||
|
||||
bool SoundCloudService::IsLoggedIn() {
|
||||
LoadAccessTokenIfEmpty();
|
||||
return !access_token_.isEmpty();
|
||||
}
|
||||
|
||||
void SoundCloudService::Logout() {
|
||||
QSettings s;
|
||||
s.beginGroup(kSettingsGroup);
|
||||
|
||||
access_token_.clear();
|
||||
s.remove("access_token");
|
||||
pending_playlists_requests_.clear();
|
||||
if (user_activities_) root_->removeRow(user_activities_->row());
|
||||
if (user_tracks_) root_->removeRow(user_tracks_->row());
|
||||
if (user_playlists_) root_->removeRow(user_playlists_->row());
|
||||
user_activities_ = nullptr;
|
||||
user_tracks_ = nullptr;
|
||||
user_playlists_ = nullptr;
|
||||
}
|
||||
|
||||
void SoundCloudService::RetrieveUserData() {
|
||||
LoadAccessTokenIfEmpty();
|
||||
RetrieveUserActivities();
|
||||
RetrieveUserTracks();
|
||||
RetrieveUserPlaylists();
|
||||
RetrieveUserFavorites();
|
||||
}
|
||||
|
||||
void SoundCloudService::RetrieveUserTracks() {
|
||||
QList<Param> parameters;
|
||||
parameters << Param("oauth_token", access_token_);
|
||||
QNetworkReply* reply = CreateRequest("/me/tracks", parameters);
|
||||
NewClosure(reply, SIGNAL(finished()), this,
|
||||
SLOT(UserTracksRetrieved(QNetworkReply*)), reply);
|
||||
}
|
||||
|
||||
void SoundCloudService::UserTracksRetrieved(QNetworkReply* reply) {
|
||||
reply->deleteLater();
|
||||
|
||||
SongList songs = ExtractSongs(ExtractResult(reply).array());
|
||||
// Fill results list
|
||||
for (const Song& song : songs) {
|
||||
QStandardItem* child = CreateSongItem(song);
|
||||
user_tracks_->appendRow(child);
|
||||
}
|
||||
}
|
||||
|
||||
void SoundCloudService::RetrieveUserActivities() {
|
||||
QList<Param> parameters;
|
||||
parameters << Param("oauth_token", access_token_);
|
||||
QNetworkReply* reply = CreateRequest("/me/activities", parameters);
|
||||
NewClosure(reply, SIGNAL(finished()), this,
|
||||
SLOT(UserActivitiesRetrieved(QNetworkReply*)), reply);
|
||||
}
|
||||
|
||||
void SoundCloudService::UserActivitiesRetrieved(QNetworkReply* reply) {
|
||||
reply->deleteLater();
|
||||
|
||||
QList<QStandardItem*> activities = ExtractActivities(ExtractResult(reply).object());
|
||||
// Fill results list
|
||||
for (QStandardItem* activity : activities) {
|
||||
user_activities_->appendRow(activity);
|
||||
}
|
||||
}
|
||||
|
||||
void SoundCloudService::RetrieveUserPlaylists() {
|
||||
QList<Param> parameters;
|
||||
parameters << Param("oauth_token", access_token_);
|
||||
QNetworkReply* reply = CreateRequest("/me/playlists", parameters);
|
||||
NewClosure(reply, SIGNAL(finished()), this,
|
||||
SLOT(UserPlaylistsRetrieved(QNetworkReply*)), reply);
|
||||
}
|
||||
|
||||
void SoundCloudService::RetrieveUserFavorites() {
|
||||
QList<Param> parameters;
|
||||
parameters << Param("oauth_token", access_token_);
|
||||
QNetworkReply* reply = CreateRequest("me/favorites", parameters);
|
||||
NewClosure(reply, SIGNAL(finished()), this,
|
||||
SLOT(UserFavoritesRetrieved(QNetworkReply*)), reply);
|
||||
}
|
||||
|
||||
void SoundCloudService::UserPlaylistsRetrieved(QNetworkReply* reply) {
|
||||
reply->deleteLater();
|
||||
|
||||
QJsonArray json_playlists = ExtractResult(reply).array();
|
||||
for (const QJsonValue& playlist : json_playlists) {
|
||||
QJsonObject json_playlist = playlist.toObject();
|
||||
|
||||
QStandardItem* playlist_item = CreatePlaylistItem(json_playlist["title"].toString());
|
||||
SongList songs = ExtractSongs(json_playlist["tracks"].toArray());
|
||||
for (const Song& song : songs) {
|
||||
playlist_item->appendRow(CreateSongItem(song));
|
||||
}
|
||||
user_playlists_->appendRow(playlist_item);
|
||||
}
|
||||
}
|
||||
|
||||
void SoundCloudService::UserFavoritesRetrieved(QNetworkReply* reply) {
|
||||
reply->deleteLater();
|
||||
|
||||
SongList songs = ExtractSongs(ExtractResult(reply).array());
|
||||
// Fill results list
|
||||
for (const Song& song : songs) {
|
||||
QStandardItem* child = CreateSongItem(song);
|
||||
user_favorites_->appendRow(child);
|
||||
}
|
||||
}
|
||||
|
||||
void SoundCloudService::Search(const QString& text, bool now) {
|
||||
pending_search_ = text;
|
||||
|
||||
// If there is no text (e.g. user cleared search box), we don't need to do a
|
||||
// real query that will return nothing: we can clear the playlist now
|
||||
if (text.isEmpty()) {
|
||||
search_delay_->stop();
|
||||
ClearSearchResults();
|
||||
return;
|
||||
}
|
||||
|
||||
if (now) {
|
||||
search_delay_->stop();
|
||||
DoSearch();
|
||||
} else {
|
||||
search_delay_->start();
|
||||
}
|
||||
}
|
||||
|
||||
void SoundCloudService::DoSearch() {
|
||||
ClearSearchResults();
|
||||
|
||||
QList<Param> parameters; parameters << Param("q", pending_search_) << Param("limit", QString::number(kSongSearchLimit));
|
||||
QNetworkReply* reply = CreateRequest("tracks", parameters);
|
||||
const int id = next_pending_search_id_++;
|
||||
NewClosure(reply, SIGNAL(finished()), this,
|
||||
SLOT(SearchFinished(QNetworkReply*, int)), reply, id);
|
||||
}
|
||||
|
||||
void SoundCloudService::SearchFinished(QNetworkReply* reply, int task_id) {
|
||||
reply->deleteLater();
|
||||
|
||||
SongList songs = ExtractSongs(ExtractResult(reply).array());
|
||||
// Fill results list
|
||||
for (const Song& song : songs) {
|
||||
QStandardItem* child = CreateSongItem(song);
|
||||
search_->appendRow(child);
|
||||
}
|
||||
|
||||
QModelIndex index = model()->merged_model()->mapFromSource(search_->index());
|
||||
ScrollToIndex(index);
|
||||
}
|
||||
|
||||
void SoundCloudService::ClearSearchResults() {
|
||||
if (search_) {
|
||||
search_->removeRows(0, search_->rowCount());
|
||||
}
|
||||
}
|
||||
|
||||
int SoundCloudService::SimpleSearch(const QString& text) {
|
||||
QList<Param> parameters;
|
||||
parameters << Param("q", text) << Param("limit", QString::number(kSongSimpleSearchLimit));
|
||||
QNetworkReply* reply = CreateRequest("tracks", parameters);
|
||||
const int id = next_pending_search_id_++;
|
||||
NewClosure(reply, SIGNAL(finished()), this,
|
||||
SLOT(SimpleSearchFinished(QNetworkReply*, int)), reply, id);
|
||||
return id;
|
||||
}
|
||||
|
||||
void SoundCloudService::SimpleSearchFinished(QNetworkReply* reply, int id) {
|
||||
reply->deleteLater();
|
||||
|
||||
SongList songs = ExtractSongs(ExtractResult(reply).array());
|
||||
emit SimpleSearchResults(id, songs);
|
||||
}
|
||||
|
||||
void SoundCloudService::EnsureMenuCreated() {
|
||||
if (!context_menu_) {
|
||||
context_menu_ = new QMenu;
|
||||
context_menu_->addActions(GetPlaylistActions());
|
||||
context_menu_->addSeparator();
|
||||
context_menu_->addAction(IconLoader::Load("download", IconLoader::Base),
|
||||
tr("Open %1 in browser").arg("soundcloud.com"),
|
||||
this, SLOT(Homepage()));
|
||||
context_menu_->addSeparator();
|
||||
context_menu_->addAction(IconLoader::Load("configure", IconLoader::Base),
|
||||
tr("Configure SoundCloud..."),
|
||||
this, SLOT(ShowConfig()));
|
||||
}
|
||||
}
|
||||
|
||||
void SoundCloudService::ShowContextMenu(const QPoint& global_pos) {
|
||||
EnsureMenuCreated();
|
||||
|
||||
context_menu_->popup(global_pos);
|
||||
}
|
||||
|
||||
QStandardItem* SoundCloudService::CreatePlaylistItem(const QString& playlist_name) {
|
||||
QStandardItem* item = new QStandardItem(playlist_name);
|
||||
item->setData(true, InternetModel::Role_CanLazyLoad);
|
||||
item->setData(InternetModel::PlayBehaviour_MultipleItems,
|
||||
InternetModel::Role_PlayBehaviour);
|
||||
return item;
|
||||
}
|
||||
|
||||
QNetworkReply* SoundCloudService::CreateRequest(const QString& ressource_name,
|
||||
const QList<Param>& params) {
|
||||
QUrl url(kUrl);
|
||||
|
||||
url.setPath(ressource_name);
|
||||
QUrlQuery url_query;
|
||||
|
||||
url_query.addQueryItem("client_id", kApiClientId);
|
||||
for (const Param& param : params) {
|
||||
url_query.addQueryItem(param.first, param.second);
|
||||
}
|
||||
|
||||
url.setQuery(url_query);
|
||||
|
||||
qLog(Debug) << "Request Url: " << url.toEncoded();
|
||||
|
||||
QNetworkRequest req(url);
|
||||
req.setRawHeader("Accept", "application/json");
|
||||
QNetworkReply* reply = network_->get(req);
|
||||
return reply;
|
||||
}
|
||||
|
||||
QJsonDocument SoundCloudService::ExtractResult(QNetworkReply* reply) {
|
||||
if (reply->error() != QNetworkReply::NoError) {
|
||||
qLog(Error) << "Error when retrieving SoundCloud results:"
|
||||
<< reply->errorString() << QString(" (%1)").arg(reply->error());
|
||||
if (reply->error() == QNetworkReply::ContentAccessDenied ||
|
||||
reply->error() == QNetworkReply::ContentOperationNotPermittedError ||
|
||||
reply->error() == QNetworkReply::ContentNotFoundError ||
|
||||
reply->error() == QNetworkReply::AuthenticationRequiredError) {
|
||||
// In case of access denied errors (invalid token?) logout
|
||||
Logout();
|
||||
return QJsonDocument();
|
||||
}
|
||||
}
|
||||
QJsonParseError error;
|
||||
QJsonDocument document = QJsonDocument::fromJson(reply->readAll(), &error);
|
||||
if (error.error != QJsonParseError::NoError) {
|
||||
qLog(Error) << "Error while parsing SoundCloud result";
|
||||
}
|
||||
|
||||
return document;
|
||||
}
|
||||
|
||||
void SoundCloudService::RetrievePlaylist(int playlist_id,
|
||||
QStandardItem* playlist_item) {
|
||||
const int request_id = next_retrieve_playlist_id_++;
|
||||
pending_playlists_requests_.insert(request_id,
|
||||
PlaylistInfo(playlist_id, playlist_item));
|
||||
QList<Param> parameters;
|
||||
parameters << Param("oauth_token", access_token_);
|
||||
QNetworkReply* reply =
|
||||
CreateRequest("/playlists/" + QString::number(playlist_id), parameters);
|
||||
NewClosure(reply, SIGNAL(finished()), this,
|
||||
SLOT(PlaylistRetrieved(QNetworkReply*, int)), reply, request_id);
|
||||
}
|
||||
|
||||
void SoundCloudService::PlaylistRetrieved(QNetworkReply* reply,
|
||||
int request_id) {
|
||||
if (!pending_playlists_requests_.contains(request_id)) return;
|
||||
PlaylistInfo playlist_info = pending_playlists_requests_.take(request_id);
|
||||
QJsonDocument res = ExtractResult(reply);
|
||||
SongList songs = ExtractSongs(res.object()["tracks"].toArray());
|
||||
for (const Song& song : songs) {
|
||||
QStandardItem* child = CreateSongItem(song);
|
||||
playlist_info.item_->appendRow(child);
|
||||
}
|
||||
}
|
||||
|
||||
QList<QStandardItem*> SoundCloudService::ExtractActivities(const QJsonObject& result) {
|
||||
QList<QStandardItem*> activities;
|
||||
QJsonArray q_list = result["collection"].toArray();
|
||||
for (const QJsonValue& q : q_list) {
|
||||
QJsonObject json_activity = q.toObject();
|
||||
const QString type = json_activity["type"].toString();
|
||||
if (type == "track") {
|
||||
Song song = ExtractSong(json_activity["origin"].toObject());
|
||||
if (song.is_valid()) {
|
||||
activities << CreateSongItem(song);
|
||||
}
|
||||
} else if (type == "playlist") {
|
||||
QJsonObject json_origin = json_activity["origin"].toObject();
|
||||
QStandardItem* playlist_item =
|
||||
CreatePlaylistItem(json_origin["title"].toString());
|
||||
activities << playlist_item;
|
||||
RetrievePlaylist(json_origin["id"].toInt(), playlist_item);
|
||||
}
|
||||
}
|
||||
return activities;
|
||||
}
|
||||
|
||||
SongList SoundCloudService::ExtractSongs(const QJsonArray & result) {
|
||||
SongList songs;
|
||||
|
||||
for (const QJsonValue& q : result) {
|
||||
Song song = ExtractSong(q.toObject());
|
||||
if (song.is_valid()) {
|
||||
songs << song;
|
||||
}
|
||||
}
|
||||
return songs;
|
||||
}
|
||||
|
||||
Song SoundCloudService::ExtractSong(const QJsonObject& result_song) {
|
||||
Song song;
|
||||
if (!result_song.isEmpty() && result_song["streamable"].toBool()) {
|
||||
QUrl stream_url(result_song["stream_url"].toString());
|
||||
QUrlQuery stream_url_query;
|
||||
stream_url_query.addQueryItem("client_id", kApiClientId);
|
||||
stream_url.setQuery(stream_url_query);
|
||||
song.set_url(stream_url);
|
||||
|
||||
QString username = result_song["user"].toObject()["username"].toString();
|
||||
// We don't have a real artist name, but username is the most similar thing
|
||||
// we have
|
||||
song.set_artist(username);
|
||||
|
||||
QString title = result_song["title"].toString();
|
||||
song.set_title(title);
|
||||
|
||||
QString genre = result_song["genre"].toString();
|
||||
song.set_genre(genre);
|
||||
|
||||
float bpm = result_song["bpm"].toDouble();
|
||||
song.set_bpm(bpm);
|
||||
|
||||
QVariant cover = result_song["artwork_url"];
|
||||
if (cover.isValid()) {
|
||||
// Increase cover size.
|
||||
// See https://developers.soundcloud.com/docs/api/reference#artwork_url
|
||||
QString big_cover = cover.toString().replace("large", "t500x500");
|
||||
QUrl cover_url(big_cover, QUrl::StrictMode);
|
||||
|
||||
// SoundCloud covers URL are https, but our cover loader doesn't seem to
|
||||
// deal well with https URL. Anyway, we don't need a secure connection to
|
||||
// get a cover image.
|
||||
cover_url.setScheme("http");
|
||||
song.set_art_automatic(cover_url.toEncoded());
|
||||
}
|
||||
|
||||
int playcount = result_song["playback_count"].toInt();
|
||||
song.set_playcount(playcount);
|
||||
|
||||
int year = result_song["release_year"].toInt();
|
||||
song.set_year(year);
|
||||
|
||||
QVariant q_duration = result_song["duration"];
|
||||
quint64 duration = q_duration.toULongLong() * kNsecPerMsec;
|
||||
song.set_length_nanosec(duration);
|
||||
|
||||
song.set_valid(true);
|
||||
}
|
||||
return song;
|
||||
}
|
@ -1,151 +0,0 @@
|
||||
/* This file is part of Clementine.
|
||||
Copyright 2012, 2014, Arnaud Bienner <arnaud.bienner@gmail.com>
|
||||
Copyright 2014, Krzysztof Sobiecki <sobkas@gmail.com>
|
||||
Copyright 2014, John Maguire <john.maguire@gmail.com>
|
||||
|
||||
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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef INTERNET_SOUNDCLOUD_SOUNDCLOUDSERVICE_H_
|
||||
#define INTERNET_SOUNDCLOUD_SOUNDCLOUDSERVICE_H_
|
||||
|
||||
#include "internet/core/internetmodel.h"
|
||||
#include "internet/core/internetservice.h"
|
||||
|
||||
class NetworkAccessManager;
|
||||
class OAuthenticator;
|
||||
class SearchBoxWidget;
|
||||
class QJsonDocument;
|
||||
|
||||
class QMenu;
|
||||
class QNetworkReply;
|
||||
|
||||
class SoundCloudService : public InternetService {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
SoundCloudService(Application* app, InternetModel* parent);
|
||||
~SoundCloudService();
|
||||
|
||||
// Internet Service methods
|
||||
QStandardItem* CreateRootItem();
|
||||
void LazyPopulate(QStandardItem* parent);
|
||||
|
||||
// TODO(Arnaud Bienner)
|
||||
// QList<QAction*> playlistitem_actions(const Song& song);
|
||||
void ShowContextMenu(const QPoint& global_pos);
|
||||
QWidget* HeaderWidget() const;
|
||||
|
||||
void Connect();
|
||||
bool IsLoggedIn();
|
||||
void Logout();
|
||||
|
||||
int SimpleSearch(const QString& query);
|
||||
|
||||
static const char* kServiceName;
|
||||
static const char* kSettingsGroup;
|
||||
|
||||
signals:
|
||||
void SimpleSearchResults(int id, SongList songs);
|
||||
void Connected();
|
||||
|
||||
public slots:
|
||||
void ShowConfig();
|
||||
|
||||
private slots:
|
||||
void ConnectFinished(OAuthenticator* oauth);
|
||||
void UserTracksRetrieved(QNetworkReply* reply);
|
||||
void UserActivitiesRetrieved(QNetworkReply* reply);
|
||||
void UserPlaylistsRetrieved(QNetworkReply* reply);
|
||||
void UserFavoritesRetrieved(QNetworkReply* reply);
|
||||
void PlaylistRetrieved(QNetworkReply* reply, int request_id);
|
||||
void Search(const QString& text, bool now = false);
|
||||
void DoSearch();
|
||||
void SearchFinished(QNetworkReply* reply, int task);
|
||||
void SimpleSearchFinished(QNetworkReply* reply, int id);
|
||||
|
||||
void Homepage();
|
||||
|
||||
private:
|
||||
struct PlaylistInfo {
|
||||
PlaylistInfo() {}
|
||||
PlaylistInfo(int id, QStandardItem* item) : id_(id), item_(item) {}
|
||||
|
||||
int id_;
|
||||
QStandardItem* item_;
|
||||
};
|
||||
|
||||
// Try to load "access_token" from preferences if the current access_token's
|
||||
// value is empty
|
||||
void LoadAccessTokenIfEmpty();
|
||||
void RetrieveUserData();
|
||||
void RetrieveUserTracks();
|
||||
void RetrieveUserActivities();
|
||||
void RetrieveUserPlaylists();
|
||||
void RetrieveUserFavorites();
|
||||
void RetrievePlaylist(int playlist_id, QStandardItem* playlist_item);
|
||||
void ClearSearchResults();
|
||||
void EnsureItemsCreated();
|
||||
void EnsureMenuCreated();
|
||||
|
||||
QStandardItem* CreatePlaylistItem(const QString& playlist_name);
|
||||
|
||||
QNetworkReply* CreateRequest(const QString& ressource_name,
|
||||
const QList<QPair<QString, QString>>& params);
|
||||
// Convenient function for extracting result from reply
|
||||
QJsonDocument ExtractResult(QNetworkReply* reply);
|
||||
// Returns items directly, as activities can be playlists or songs
|
||||
QList<QStandardItem*> ExtractActivities(const QJsonObject &result);
|
||||
SongList ExtractSongs(const QJsonArray &result);
|
||||
Song ExtractSong(const QJsonObject& result_song);
|
||||
|
||||
QStandardItem* root_;
|
||||
QStandardItem* search_;
|
||||
QStandardItem* user_tracks_;
|
||||
QStandardItem* user_playlists_;
|
||||
QStandardItem* user_activities_;
|
||||
QStandardItem* user_favorites_;
|
||||
|
||||
NetworkAccessManager* network_;
|
||||
|
||||
QMenu* context_menu_;
|
||||
SearchBoxWidget* search_box_;
|
||||
QTimer* search_delay_;
|
||||
QString pending_search_;
|
||||
// Request IDs
|
||||
int next_pending_search_id_;
|
||||
int next_retrieve_playlist_id_;
|
||||
|
||||
QMap<int, PlaylistInfo> pending_playlists_requests_;
|
||||
|
||||
QByteArray api_key_;
|
||||
|
||||
QString access_token_;
|
||||
QDateTime expiry_time_;
|
||||
|
||||
static const char* kUrl;
|
||||
static const char* kOAuthEndpoint;
|
||||
static const char* kOAuthTokenEndpoint;
|
||||
static const char* kOAuthScope;
|
||||
static const char* kHomepage;
|
||||
|
||||
static const int kSongSearchLimit;
|
||||
static const int kSongSimpleSearchLimit;
|
||||
static const int kSearchDelayMsec;
|
||||
|
||||
static const char* kApiClientId;
|
||||
static const char* kApiClientSecret;
|
||||
};
|
||||
|
||||
#endif // INTERNET_SOUNDCLOUD_SOUNDCLOUDSERVICE_H_
|
@ -1,77 +0,0 @@
|
||||
/* This file is part of Clementine.
|
||||
Copyright 2014, Arnaud Bienner <arnaud.bienner@gmail.com>
|
||||
Copyright 2014, Krzysztof Sobiecki <sobkas@gmail.com>
|
||||
|
||||
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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "soundcloudservice.h"
|
||||
#include "soundcloudsettingspage.h"
|
||||
#include "ui_soundcloudsettingspage.h"
|
||||
#include "core/application.h"
|
||||
#include "internet/core/internetmodel.h"
|
||||
#include "ui/iconloader.h"
|
||||
|
||||
SoundCloudSettingsPage::SoundCloudSettingsPage(SettingsDialog* parent)
|
||||
: SettingsPage(parent),
|
||||
ui_(new Ui::SoundCloudSettingsPage),
|
||||
service_(
|
||||
dialog()->app()->internet_model()->Service<SoundCloudService>()) {
|
||||
ui_->setupUi(this);
|
||||
setWindowIcon(IconLoader::Load("soundcloud", IconLoader::Provider));
|
||||
|
||||
ui_->login_state->AddCredentialGroup(ui_->login_container);
|
||||
|
||||
connect(ui_->login_button, SIGNAL(clicked()), SLOT(LoginClicked()));
|
||||
connect(ui_->login_state, SIGNAL(LogoutClicked()), SLOT(LogoutClicked()));
|
||||
connect(service_, SIGNAL(Connected()), SLOT(Connected()));
|
||||
|
||||
dialog()->installEventFilter(this);
|
||||
}
|
||||
|
||||
SoundCloudSettingsPage::~SoundCloudSettingsPage() { delete ui_; }
|
||||
|
||||
void SoundCloudSettingsPage::Load() {
|
||||
if (service_->IsLoggedIn()) {
|
||||
ui_->login_state->SetLoggedIn(LoginStateWidget::LoggedIn);
|
||||
}
|
||||
}
|
||||
|
||||
void SoundCloudSettingsPage::Save() {
|
||||
// Everything is done in the service: nothing to do here
|
||||
}
|
||||
|
||||
void SoundCloudSettingsPage::LoginClicked() {
|
||||
service_->Connect();
|
||||
ui_->login_button->setEnabled(false);
|
||||
}
|
||||
|
||||
bool SoundCloudSettingsPage::eventFilter(QObject* object, QEvent* event) {
|
||||
if (object == dialog() && event->type() == QEvent::Enter) {
|
||||
ui_->login_button->setEnabled(true);
|
||||
return false;
|
||||
}
|
||||
|
||||
return SettingsPage::eventFilter(object, event);
|
||||
}
|
||||
|
||||
void SoundCloudSettingsPage::LogoutClicked() {
|
||||
service_->Logout();
|
||||
ui_->login_button->setEnabled(true);
|
||||
ui_->login_state->SetLoggedIn(LoginStateWidget::LoggedOut);
|
||||
}
|
||||
|
||||
void SoundCloudSettingsPage::Connected() {
|
||||
ui_->login_state->SetLoggedIn(LoginStateWidget::LoggedIn);
|
||||
}
|
@ -1,51 +0,0 @@
|
||||
/* This file is part of Clementine.
|
||||
Copyright 2014, Arnaud Bienner <arnaud.bienner@gmail.com>
|
||||
Copyright 2014, Krzysztof Sobiecki <sobkas@gmail.com>
|
||||
|
||||
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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef INTERNET_SOUNDCLOUD_SOUNDCLOUDSETTINGSPAGE_H_
|
||||
#define INTERNET_SOUNDCLOUD_SOUNDCLOUDSETTINGSPAGE_H_
|
||||
|
||||
#include "ui/settingspage.h"
|
||||
|
||||
class SoundCloudService;
|
||||
class Ui_SoundCloudSettingsPage;
|
||||
|
||||
class SoundCloudSettingsPage : public SettingsPage {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit SoundCloudSettingsPage(SettingsDialog* parent = nullptr);
|
||||
~SoundCloudSettingsPage();
|
||||
|
||||
void Load();
|
||||
void Save();
|
||||
|
||||
// QObject
|
||||
bool eventFilter(QObject* object, QEvent* event);
|
||||
|
||||
private slots:
|
||||
void LoginClicked();
|
||||
void LogoutClicked();
|
||||
void Connected();
|
||||
|
||||
private:
|
||||
Ui_SoundCloudSettingsPage* ui_;
|
||||
|
||||
SoundCloudService* service_;
|
||||
};
|
||||
|
||||
#endif // INTERNET_SOUNDCLOUD_SOUNDCLOUDSETTINGSPAGE_H_
|
@ -1,106 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>SoundCloudSettingsPage</class>
|
||||
<widget class="QWidget" name="SoundCloudSettingsPage">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>569</width>
|
||||
<height>491</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>SoundCloud</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||
<item>
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="enabled">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>You don't need to be logged in to search and to listen to music on SoundCloud. However, you need to login to access your playlists and your stream.</string>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="LoginStateWidget" name="login_state" native="true"/>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QWidget" name="login_container" native="true">
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<property name="leftMargin">
|
||||
<number>28</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<item>
|
||||
<widget class="QPushButton" name="login_button">
|
||||
<property name="text">
|
||||
<string>Login</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="label_2">
|
||||
<property name="text">
|
||||
<string>Clicking the Login button will open a web browser. You should return to Clementine after you have logged in.</string>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="verticalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>357</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<customwidgets>
|
||||
<customwidget>
|
||||
<class>LoginStateWidget</class>
|
||||
<extends>QWidget</extends>
|
||||
<header>widgets/loginstatewidget.h</header>
|
||||
<container>1</container>
|
||||
</customwidget>
|
||||
</customwidgets>
|
||||
<connections/>
|
||||
</ui>
|
@ -23,6 +23,7 @@
|
||||
#include "playlist/playlisttabbar.h"
|
||||
|
||||
#include <QDir>
|
||||
#include <QSystemTrayIcon>
|
||||
|
||||
namespace {
|
||||
bool LocaleAwareCompare(const QString& a, const QString& b) {
|
||||
@ -94,6 +95,14 @@ BehaviourSettingsPage::BehaviourSettingsPage(SettingsDialog* dialog)
|
||||
#ifdef Q_OS_DARWIN
|
||||
ui_->b_show_tray_icon_->setEnabled(false);
|
||||
ui_->startup_group_->setEnabled(false);
|
||||
#else
|
||||
if (QSystemTrayIcon::isSystemTrayAvailable()) {
|
||||
ui_->b_show_tray_icon_->setEnabled(true);
|
||||
ui_->startup_group_->setEnabled(true);
|
||||
} else {
|
||||
ui_->b_show_tray_icon_->setEnabled(false);
|
||||
ui_->startup_group_->setEnabled(false);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
@ -103,11 +112,25 @@ void BehaviourSettingsPage::Load() {
|
||||
QSettings s;
|
||||
|
||||
s.beginGroup(MainWindow::kSettingsGroup);
|
||||
ui_->b_show_tray_icon_->setChecked(s.value("showtray", true).toBool());
|
||||
ui_->b_scroll_tray_icon_->setChecked(
|
||||
s.value("scrolltrayicon", ui_->b_show_tray_icon_->isChecked()).toBool());
|
||||
ui_->b_keep_running_->setChecked(
|
||||
s.value("keeprunning", ui_->b_show_tray_icon_->isChecked()).toBool());
|
||||
#ifdef Q_OS_DARWIN
|
||||
ui_->b_show_tray_icon_->setChecked(false);
|
||||
ui_->b_scroll_tray_icon_->setChecked(false);
|
||||
ui_->b_keep_running_->setChecked(false);
|
||||
#else
|
||||
if (QSystemTrayIcon::isSystemTrayAvailable()) {
|
||||
ui_->b_show_tray_icon_->setChecked(s.value("showtray", true).toBool());
|
||||
ui_->b_scroll_tray_icon_->setChecked(
|
||||
s.value("scrolltrayicon", ui_->b_show_tray_icon_->isChecked())
|
||||
.toBool());
|
||||
ui_->b_keep_running_->setChecked(
|
||||
s.value("keeprunning", ui_->b_show_tray_icon_->isChecked()).toBool());
|
||||
} else {
|
||||
ui_->b_show_tray_icon_->setChecked(false);
|
||||
ui_->b_scroll_tray_icon_->setChecked(false);
|
||||
ui_->b_keep_running_->setChecked(false);
|
||||
}
|
||||
#endif
|
||||
|
||||
ui_->doubleclick_addmode->setCurrentIndex(ui_->doubleclick_addmode->findData(
|
||||
s.value("doubleclick_addmode", MainWindow::AddBehaviour_Append).toInt()));
|
||||
ui_->doubleclick_playmode->setCurrentIndex(
|
||||
|
@ -7,7 +7,7 @@
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>516</width>
|
||||
<height>792</height>
|
||||
<height>1081</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
@ -89,83 +89,67 @@
|
||||
<item>
|
||||
<widget class="QRadioButton" name="b_always_show_">
|
||||
<property name="text">
|
||||
<string>Always show the main window</string>
|
||||
<string>Always show &the main window</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QRadioButton" name="b_always_hide_">
|
||||
<property name="text">
|
||||
<string>Always hide the main window</string>
|
||||
<string>Alwa&ys hide the main window</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QRadioButton" name="b_remember_">
|
||||
<property name="text">
|
||||
<string>Remember from last time</string>
|
||||
<string>Remember from &last time</string>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="verticalSpacer_6">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeType">
|
||||
<enum>QSizePolicy::Fixed</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>10</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="resume_after_start_">
|
||||
<property name="text">
|
||||
<string>Resume playback on start</string>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="stop_play_if_fail_">
|
||||
<property name="text">
|
||||
<string>Stop playback if song fails to play</string>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="b_grey_out_deleted_">
|
||||
<property name="text">
|
||||
<string>Grey out non existent songs in my playlists</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="b_click_edit_inline_">
|
||||
<property name="toolTip">
|
||||
<string>If activated, clicking a selected song in the playlist view will let you edit the tag value directly</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Enable song metadata inline edition with click</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="b_click_edit_inline_">
|
||||
<property name="toolTip">
|
||||
<string>If activated, clicking a selected song in the playlist view will let you edit the tag value directly</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Enable song metadata inline edition with click</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="resume_after_start_">
|
||||
<property name="text">
|
||||
<string>Resume playback on start</string>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="stop_play_if_fail_">
|
||||
<property name="text">
|
||||
<string>Stop playback if song fails to play</string>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="b_grey_out_deleted_">
|
||||
<property name="text">
|
||||
<string>Grey out non existent songs in my playlists</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="groupBox_8">
|
||||
<property name="title">
|
||||
@ -364,7 +348,7 @@
|
||||
<item>
|
||||
<widget class="QRadioButton" name="b_automatic_path">
|
||||
<property name="text">
|
||||
<string>Automatic</string>
|
||||
<string>A&utomatic</string>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>true</bool>
|
||||
@ -388,7 +372,7 @@
|
||||
<item>
|
||||
<widget class="QRadioButton" name="b_ask_path">
|
||||
<property name="text">
|
||||
<string>Ask when saving</string>
|
||||
<string>As&k when saving</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
|
@ -33,6 +33,7 @@
|
||||
#include <QSignalMapper>
|
||||
#include <QSortFilterProxyModel>
|
||||
#include <QStatusBar>
|
||||
#include <QSystemTrayIcon>
|
||||
#include <QTimer>
|
||||
#include <QUndoStack>
|
||||
#include <QtDebug>
|
||||
@ -792,20 +793,22 @@ MainWindow::MainWindow(Application* app, SystemTrayIcon* tray_icon, OSD* osd,
|
||||
mac::SetApplicationHandler(this);
|
||||
#endif
|
||||
// Tray icon
|
||||
tray_icon_->SetupMenu(ui_->action_previous_track, ui_->action_play_pause,
|
||||
ui_->action_stop, ui_->action_stop_after_this_track,
|
||||
ui_->action_next_track, ui_->action_mute,
|
||||
ui_->action_love, ui_->action_quit);
|
||||
connect(tray_icon_, SIGNAL(PlayPause()), app_->player(), SLOT(PlayPause()));
|
||||
connect(tray_icon_, SIGNAL(SeekForward()), app_->player(),
|
||||
SLOT(SeekForward()));
|
||||
connect(tray_icon_, SIGNAL(SeekBackward()), app_->player(),
|
||||
SLOT(SeekBackward()));
|
||||
connect(tray_icon_, SIGNAL(NextTrack()), app_->player(), SLOT(Next()));
|
||||
connect(tray_icon_, SIGNAL(PreviousTrack()), app_->player(),
|
||||
SLOT(Previous()));
|
||||
connect(tray_icon_, SIGNAL(ShowHide()), SLOT(ToggleShowHide()));
|
||||
connect(tray_icon_, SIGNAL(ChangeVolume(int)), SLOT(VolumeWheelEvent(int)));
|
||||
if (tray_icon_) {
|
||||
tray_icon_->SetupMenu(ui_->action_previous_track, ui_->action_play_pause,
|
||||
ui_->action_stop, ui_->action_stop_after_this_track,
|
||||
ui_->action_next_track, ui_->action_mute,
|
||||
ui_->action_love, ui_->action_quit);
|
||||
connect(tray_icon_, SIGNAL(PlayPause()), app_->player(), SLOT(PlayPause()));
|
||||
connect(tray_icon_, SIGNAL(SeekForward()), app_->player(),
|
||||
SLOT(SeekForward()));
|
||||
connect(tray_icon_, SIGNAL(SeekBackward()), app_->player(),
|
||||
SLOT(SeekBackward()));
|
||||
connect(tray_icon_, SIGNAL(NextTrack()), app_->player(), SLOT(Next()));
|
||||
connect(tray_icon_, SIGNAL(PreviousTrack()), app_->player(),
|
||||
SLOT(Previous()));
|
||||
connect(tray_icon_, SIGNAL(ShowHide()), SLOT(ToggleShowHide()));
|
||||
connect(tray_icon_, SIGNAL(ChangeVolume(int)), SLOT(VolumeWheelEvent(int)));
|
||||
}
|
||||
|
||||
// Windows 7 thumbbar buttons
|
||||
thumbbar_->SetActions(QList<QAction*>()
|
||||
@ -1010,7 +1013,10 @@ MainWindow::MainWindow(Application* app, SystemTrayIcon* tray_icon, OSD* osd,
|
||||
// Reload playlist settings, for BG and glowing
|
||||
ui_->playlist->view()->ReloadSettings();
|
||||
|
||||
#ifndef Q_OS_DARWIN
|
||||
#ifdef Q_OS_DARWIN
|
||||
// Always show mainwindow on startup on OS X.
|
||||
show();
|
||||
#else
|
||||
StartupBehaviour behaviour = StartupBehaviour(
|
||||
settings_.value("startupbehaviour", Startup_Remember).toInt());
|
||||
bool hidden = settings_.value("hidden", false).toBool();
|
||||
@ -1029,13 +1035,11 @@ MainWindow::MainWindow(Application* app, SystemTrayIcon* tray_icon, OSD* osd,
|
||||
|
||||
// Force the window to show in case somehow the config has tray and window set
|
||||
// to hide
|
||||
if (hidden && !tray_icon_->IsVisible()) {
|
||||
if (hidden && (!QSystemTrayIcon::isSystemTrayAvailable() || !tray_icon_ ||
|
||||
!tray_icon_->IsVisible())) {
|
||||
settings_.setValue("hidden", false);
|
||||
show();
|
||||
}
|
||||
#else // Q_OS_DARWIN
|
||||
// Always show mainwindow on startup on OS X.
|
||||
show();
|
||||
#endif
|
||||
|
||||
QShortcut* close_window_shortcut = new QShortcut(this);
|
||||
@ -1066,9 +1070,13 @@ MainWindow::~MainWindow() {
|
||||
|
||||
void MainWindow::ReloadSettings() {
|
||||
#ifndef Q_OS_DARWIN
|
||||
bool show_tray = settings_.value("showtray", true).toBool();
|
||||
tray_icon_->SetVisible(show_tray);
|
||||
if (!show_tray && !isVisible()) show();
|
||||
bool show_tray =
|
||||
settings_.value("showtray", QSystemTrayIcon::isSystemTrayAvailable())
|
||||
.toBool();
|
||||
|
||||
if (tray_icon_) tray_icon_->SetVisible(show_tray);
|
||||
if ((!show_tray || !QSystemTrayIcon::isSystemTrayAvailable()) && !isVisible())
|
||||
show();
|
||||
#endif
|
||||
|
||||
QSettings s;
|
||||
@ -1122,13 +1130,15 @@ void MainWindow::MediaStopped() {
|
||||
ui_->action_play_pause->setEnabled(true);
|
||||
|
||||
ui_->action_love->setEnabled(false);
|
||||
tray_icon_->LastFMButtonLoveStateChanged(false);
|
||||
if (tray_icon_) tray_icon_->LastFMButtonLoveStateChanged(false);
|
||||
|
||||
track_position_timer_->stop();
|
||||
track_slider_timer_->stop();
|
||||
ui_->track_slider->SetStopped();
|
||||
tray_icon_->SetProgress(0);
|
||||
tray_icon_->SetStopped();
|
||||
if (tray_icon_) {
|
||||
tray_icon_->SetProgress(0);
|
||||
tray_icon_->SetStopped();
|
||||
}
|
||||
}
|
||||
|
||||
void MainWindow::MediaPaused() {
|
||||
@ -1143,7 +1153,7 @@ void MainWindow::MediaPaused() {
|
||||
track_position_timer_->stop();
|
||||
track_slider_timer_->stop();
|
||||
|
||||
tray_icon_->SetPaused();
|
||||
if (tray_icon_) tray_icon_->SetPaused();
|
||||
}
|
||||
|
||||
void MainWindow::MediaPlaying() {
|
||||
@ -1164,10 +1174,12 @@ void MainWindow::MediaPlaying() {
|
||||
#ifdef HAVE_LIBLASTFM
|
||||
bool enable_love = app_->scrobbler()->IsScrobblingEnabled();
|
||||
ui_->action_love->setEnabled(enable_love);
|
||||
tray_icon_->LastFMButtonLoveStateChanged(enable_love);
|
||||
tray_icon_->SetPlaying(enable_play_pause, enable_love);
|
||||
if (tray_icon_) {
|
||||
tray_icon_->LastFMButtonLoveStateChanged(enable_love);
|
||||
tray_icon_->SetPlaying(enable_play_pause, enable_love);
|
||||
}
|
||||
#else
|
||||
tray_icon_->SetPlaying(enable_play_pause);
|
||||
if (tray_icon_) tray_icon_->SetPlaying(enable_play_pause);
|
||||
#endif
|
||||
|
||||
track_position_timer_->start();
|
||||
@ -1177,12 +1189,12 @@ void MainWindow::MediaPlaying() {
|
||||
|
||||
void MainWindow::VolumeChanged(int volume) {
|
||||
ui_->action_mute->setChecked(!volume);
|
||||
tray_icon_->MuteButtonStateChanged(!volume);
|
||||
if (tray_icon_) tray_icon_->MuteButtonStateChanged(!volume);
|
||||
}
|
||||
|
||||
void MainWindow::SongChanged(const Song& song) {
|
||||
setWindowTitle(song.PrettyTitleWithArtist());
|
||||
tray_icon_->SetProgress(0);
|
||||
if (tray_icon_) tray_icon_->SetProgress(0);
|
||||
|
||||
#ifdef HAVE_LIBLASTFM
|
||||
if (ui_->action_toggle_scrobbling->isVisible())
|
||||
@ -1231,14 +1243,14 @@ void MainWindow::ScrobblingEnabledChanged(bool value) {
|
||||
}
|
||||
|
||||
ui_->action_love->setEnabled(value);
|
||||
tray_icon_->LastFMButtonLoveStateChanged(value);
|
||||
if (tray_icon_) tray_icon_->LastFMButtonLoveStateChanged(value);
|
||||
}
|
||||
#endif
|
||||
|
||||
void MainWindow::LastFMButtonVisibilityChanged(bool value) {
|
||||
ui_->action_love->setVisible(value);
|
||||
ui_->last_fm_controls->setVisible(value);
|
||||
tray_icon_->LastFMButtonVisibilityChanged(value);
|
||||
if (tray_icon_) tray_icon_->LastFMButtonVisibilityChanged(value);
|
||||
}
|
||||
|
||||
void MainWindow::ScrobbleButtonVisibilityChanged(bool value) {
|
||||
@ -1406,7 +1418,9 @@ void MainWindow::closeEvent(QCloseEvent* event) {
|
||||
QSettings s;
|
||||
s.beginGroup(kSettingsGroup);
|
||||
|
||||
bool keep_running = s.value("keeprunning", tray_icon_->IsVisible()).toBool();
|
||||
bool keep_running(false);
|
||||
if (tray_icon_)
|
||||
keep_running = s.value("keeprunning", tray_icon_->IsVisible()).toBool();
|
||||
|
||||
if (keep_running && event->spontaneous()) {
|
||||
event->accept();
|
||||
@ -1440,7 +1454,7 @@ void MainWindow::Seeked(qlonglong microseconds) {
|
||||
const int length =
|
||||
app_->player()->GetCurrentItem()->Metadata().length_nanosec() /
|
||||
kNsecPerSec;
|
||||
tray_icon_->SetProgress(double(position) / length * 100);
|
||||
if (tray_icon_) tray_icon_->SetProgress(double(position) / length * 100);
|
||||
|
||||
// if we seeked, scrobbling is canceled, update the icon
|
||||
if (ui_->action_toggle_scrobbling->isVisible()) SetToggleScrobblingIcon(true);
|
||||
@ -1499,7 +1513,7 @@ void MainWindow::UpdateTrackPosition() {
|
||||
if (position % 10 == 0) {
|
||||
qLog(Debug) << "position" << position << "scrobble point" << scrobble_point
|
||||
<< "status" << playlist->get_lastfm_status();
|
||||
tray_icon_->SetProgress(double(position) / length * 100);
|
||||
if (tray_icon_) tray_icon_->SetProgress(double(position) / length * 100);
|
||||
|
||||
// if we're waiting for the scrobble point, update the icon
|
||||
#ifdef HAVE_LIBLASTFM
|
||||
@ -1528,13 +1542,13 @@ void MainWindow::UpdateTrackSliderPosition() {
|
||||
#ifdef HAVE_LIBLASTFM
|
||||
void MainWindow::ScrobbledRadioStream() {
|
||||
ui_->action_love->setEnabled(true);
|
||||
tray_icon_->LastFMButtonLoveStateChanged(true);
|
||||
if (tray_icon_) tray_icon_->LastFMButtonLoveStateChanged(true);
|
||||
}
|
||||
|
||||
void MainWindow::Love() {
|
||||
app_->scrobbler()->Love();
|
||||
ui_->action_love->setEnabled(false);
|
||||
tray_icon_->LastFMButtonLoveStateChanged(false);
|
||||
if (tray_icon_) tray_icon_->LastFMButtonLoveStateChanged(false);
|
||||
}
|
||||
#endif
|
||||
|
||||
@ -2761,7 +2775,7 @@ void MainWindow::Exit() {
|
||||
if (app_->player()->GetState() == Engine::Playing) {
|
||||
app_->player()->Stop();
|
||||
hide();
|
||||
tray_icon_->SetVisible(false);
|
||||
if (tray_icon_) tray_icon_->SetVisible(false);
|
||||
return; // Don't quit the application now: wait for the fadeout finished
|
||||
// signal
|
||||
}
|
||||
|
@ -34,7 +34,6 @@
|
||||
#include "internet/digitally/digitallyimportedsettingspage.h"
|
||||
#include "internet/magnatune/magnatunesettingspage.h"
|
||||
#include "internet/podcasts/podcastsettingspage.h"
|
||||
#include "internet/soundcloud/soundcloudsettingspage.h"
|
||||
#include "internet/spotify/spotifysettingspage.h"
|
||||
#include "internet/subsonic/subsonicsettingspage.h"
|
||||
#include "library/librarysettingspage.h"
|
||||
@ -175,7 +174,6 @@ SettingsDialog::SettingsDialog(Application* app, BackgroundStreams* streams,
|
||||
AddPage(Page_Skydrive, new SkydriveSettingsPage(this), providers);
|
||||
#endif
|
||||
|
||||
AddPage(Page_SoundCloud, new SoundCloudSettingsPage(this), providers);
|
||||
AddPage(Page_Spotify, new SpotifySettingsPage(this), providers);
|
||||
|
||||
#ifdef HAVE_SEAFILE
|
||||
|
@ -21,8 +21,9 @@
|
||||
|
||||
#include <QApplication>
|
||||
#include <QEvent>
|
||||
#include <QWheelEvent>
|
||||
#include <QPainter>
|
||||
#include <QSystemTrayIcon>
|
||||
#include <QWheelEvent>
|
||||
#include <QWidget>
|
||||
#include <QtDebug>
|
||||
|
||||
@ -105,6 +106,9 @@ SystemTrayIcon* SystemTrayIcon::CreateSystemTrayIcon(QObject* parent) {
|
||||
#ifdef Q_OS_DARWIN
|
||||
return new MacSystemTrayIcon(parent);
|
||||
#else
|
||||
return new QtSystemTrayIcon(parent);
|
||||
if (QSystemTrayIcon::isSystemTrayAvailable())
|
||||
return new QtSystemTrayIcon(parent);
|
||||
else
|
||||
return nullptr;
|
||||
#endif
|
||||
}
|
||||
|
@ -95,7 +95,7 @@ void OSD::ReshowCurrentSong() {
|
||||
void OSD::AlbumArtLoaded(const Song& song, const QString& uri,
|
||||
const QImage& image) {
|
||||
// Don't change tray icon details if it's a preview
|
||||
if (!preview_mode_) {
|
||||
if (!preview_mode_ && tray_icon_) {
|
||||
tray_icon_->SetNowPlaying(song, uri);
|
||||
}
|
||||
|
||||
@ -159,7 +159,7 @@ void OSD::Paused() {
|
||||
}
|
||||
|
||||
void OSD::Stopped() {
|
||||
tray_icon_->ClearNowPlaying();
|
||||
if (tray_icon_) tray_icon_->ClearNowPlaying();
|
||||
if (ignore_next_stopped_) {
|
||||
ignore_next_stopped_ = false;
|
||||
return;
|
||||
@ -215,7 +215,7 @@ void OSD::ShowMessage(const QString& summary, const QString& message,
|
||||
|
||||
#ifndef Q_OS_DARWIN
|
||||
case TrayPopup:
|
||||
tray_icon_->ShowPopup(summary, message, timeout_msec_);
|
||||
if (tray_icon_) tray_icon_->ShowPopup(summary, message, timeout_msec_);
|
||||
break;
|
||||
#endif
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user