Add login to SoundCloud + get user tracks and activties (stream)
Still need to handle playlists and to clean up things (e.g. to check why refresh_token doesn't work and if we can do something about this)
This commit is contained in:
parent
4520a1c115
commit
1b7f99127d
@ -197,6 +197,7 @@ set(SOURCES
|
|||||||
internet/somafmservice.cpp
|
internet/somafmservice.cpp
|
||||||
internet/somafmurlhandler.cpp
|
internet/somafmurlhandler.cpp
|
||||||
internet/soundcloudservice.cpp
|
internet/soundcloudservice.cpp
|
||||||
|
internet/soundcloudsettingspage.cpp
|
||||||
internet/spotifyserver.cpp
|
internet/spotifyserver.cpp
|
||||||
internet/spotifyservice.cpp
|
internet/spotifyservice.cpp
|
||||||
internet/spotifysettingspage.cpp
|
internet/spotifysettingspage.cpp
|
||||||
@ -497,6 +498,7 @@ set(HEADERS
|
|||||||
internet/somafmservice.h
|
internet/somafmservice.h
|
||||||
internet/somafmurlhandler.h
|
internet/somafmurlhandler.h
|
||||||
internet/soundcloudservice.h
|
internet/soundcloudservice.h
|
||||||
|
internet/soundcloudsettingspage.h
|
||||||
internet/spotifyserver.h
|
internet/spotifyserver.h
|
||||||
internet/spotifyservice.h
|
internet/spotifyservice.h
|
||||||
internet/spotifysettingspage.h
|
internet/spotifysettingspage.h
|
||||||
@ -689,6 +691,7 @@ set(UI
|
|||||||
internet/magnatunedownloaddialog.ui
|
internet/magnatunedownloaddialog.ui
|
||||||
internet/magnatunesettingspage.ui
|
internet/magnatunesettingspage.ui
|
||||||
internet/searchboxwidget.ui
|
internet/searchboxwidget.ui
|
||||||
|
internet/soundcloudsettingspage.ui
|
||||||
internet/spotifysettingspage.ui
|
internet/spotifysettingspage.ui
|
||||||
internet/subsonicsettingspage.ui
|
internet/subsonicsettingspage.ui
|
||||||
|
|
||||||
|
@ -10,6 +10,8 @@
|
|||||||
#include "core/logging.h"
|
#include "core/logging.h"
|
||||||
#include "internet/localredirectserver.h"
|
#include "internet/localredirectserver.h"
|
||||||
|
|
||||||
|
const char* OAuthenticator::kRemoteURL = "https://clementine-data.appspot.com/skydrive";
|
||||||
|
|
||||||
OAuthenticator::OAuthenticator(const QString& client_id,
|
OAuthenticator::OAuthenticator(const QString& client_id,
|
||||||
const QString& client_secret,
|
const QString& client_secret,
|
||||||
RedirectStyle redirect, QObject* parent)
|
RedirectStyle redirect, QObject* parent)
|
||||||
@ -29,14 +31,19 @@ void OAuthenticator::StartAuthorisation(const QString& oauth_endpoint,
|
|||||||
url.addQueryItem("response_type", "code");
|
url.addQueryItem("response_type", "code");
|
||||||
url.addQueryItem("client_id", client_id_);
|
url.addQueryItem("client_id", client_id_);
|
||||||
QUrl redirect_url;
|
QUrl redirect_url;
|
||||||
|
|
||||||
|
const QString port = QString::number(server->url().port());
|
||||||
|
|
||||||
if (redirect_style_ == RedirectStyle::REMOTE) {
|
if (redirect_style_ == RedirectStyle::REMOTE) {
|
||||||
const int port = server->url().port();
|
redirect_url = QUrl(kRemoteURL);
|
||||||
redirect_url =
|
url.addQueryItem("port", port);
|
||||||
QUrl(QString("https://clementine-data.appspot.com/skydrive?port=%1")
|
} else if (redirect_style_ == RedirectStyle::REMOTE_WITH_STATE) {
|
||||||
.arg(port));
|
redirect_url = QUrl(kRemoteURL);
|
||||||
|
url.addQueryItem("state", port);
|
||||||
} else {
|
} else {
|
||||||
redirect_url = server->url();
|
redirect_url = server->url();
|
||||||
}
|
}
|
||||||
|
|
||||||
url.addQueryItem("redirect_uri", redirect_url.toString());
|
url.addQueryItem("redirect_uri", redirect_url.toString());
|
||||||
url.addQueryItem("scope", scope);
|
url.addQueryItem("scope", scope);
|
||||||
|
|
||||||
@ -66,7 +73,8 @@ void OAuthenticator::RequestAccessToken(const QByteArray& code,
|
|||||||
const QUrl& url) {
|
const QUrl& url) {
|
||||||
typedef QPair<QString, QString> Param;
|
typedef QPair<QString, QString> Param;
|
||||||
QList<Param> parameters;
|
QList<Param> parameters;
|
||||||
parameters << Param("code", code) << Param("client_id", client_id_)
|
parameters << Param("code", code)
|
||||||
|
<< Param("client_id", client_id_)
|
||||||
<< Param("client_secret", client_secret_)
|
<< Param("client_secret", client_secret_)
|
||||||
<< Param("grant_type", "authorization_code")
|
<< Param("grant_type", "authorization_code")
|
||||||
// Even though we don't use this URI anymore, it must match the
|
// Even though we don't use this URI anymore, it must match the
|
||||||
|
@ -18,6 +18,10 @@ class OAuthenticator : public QObject {
|
|||||||
// Redirect via data.clementine-player.org for when localhost is
|
// Redirect via data.clementine-player.org for when localhost is
|
||||||
// unsupported (eg. Skydrive).
|
// unsupported (eg. Skydrive).
|
||||||
REMOTE = 1,
|
REMOTE = 1,
|
||||||
|
// Same as REMOTE, but pass the 'port' parameter as 'state' parameter, for
|
||||||
|
// services which don't allow other parameters to be append to the redirect
|
||||||
|
// URI (e.g. SoundCloud)
|
||||||
|
REMOTE_WITH_STATE = 2
|
||||||
};
|
};
|
||||||
|
|
||||||
OAuthenticator(const QString& client_id, const QString& client_secret,
|
OAuthenticator(const QString& client_id, const QString& client_secret,
|
||||||
@ -44,6 +48,8 @@ signals:
|
|||||||
void RefreshAccessTokenFinished(QNetworkReply* reply);
|
void RefreshAccessTokenFinished(QNetworkReply* reply);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
static const char* kRemoteURL;
|
||||||
|
|
||||||
QByteArray ParseHttpRequest(const QByteArray& request) const;
|
QByteArray ParseHttpRequest(const QByteArray& request) const;
|
||||||
void RequestAccessToken(const QByteArray& code, const QUrl& url);
|
void RequestAccessToken(const QByteArray& code, const QUrl& url);
|
||||||
void SetExpiryTime(int expires_in_seconds);
|
void SetExpiryTime(int expires_in_seconds);
|
||||||
|
@ -27,6 +27,7 @@
|
|||||||
#include <qjson/serializer.h>
|
#include <qjson/serializer.h>
|
||||||
|
|
||||||
#include "internetmodel.h"
|
#include "internetmodel.h"
|
||||||
|
#include "oauthenticator.h"
|
||||||
#include "searchboxwidget.h"
|
#include "searchboxwidget.h"
|
||||||
|
|
||||||
#include "core/application.h"
|
#include "core/application.h"
|
||||||
@ -44,10 +45,15 @@
|
|||||||
|
|
||||||
const char* SoundCloudService::kApiClientId =
|
const char* SoundCloudService::kApiClientId =
|
||||||
"2add0f709fcfae1fd7a198ec7573d2d4";
|
"2add0f709fcfae1fd7a198ec7573d2d4";
|
||||||
|
const char* SoundCloudService::kApiClientSecret =
|
||||||
|
"d1cd7829da2e98e1e0621d85d57a2077";
|
||||||
|
|
||||||
const char* SoundCloudService::kServiceName = "SoundCloud";
|
const char* SoundCloudService::kServiceName = "SoundCloud";
|
||||||
const char* SoundCloudService::kSettingsGroup = "SoundCloud";
|
const char* SoundCloudService::kSettingsGroup = "SoundCloud";
|
||||||
const char* SoundCloudService::kUrl = "https://api.soundcloud.com/";
|
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 char* SoundCloudService::kHomepage = "http://soundcloud.com/";
|
||||||
|
|
||||||
const int SoundCloudService::kSearchDelayMsec = 400;
|
const int SoundCloudService::kSearchDelayMsec = 400;
|
||||||
@ -60,6 +66,8 @@ SoundCloudService::SoundCloudService(Application* app, InternetModel* parent)
|
|||||||
: InternetService(kServiceName, app, parent, parent),
|
: InternetService(kServiceName, app, parent, parent),
|
||||||
root_(nullptr),
|
root_(nullptr),
|
||||||
search_(nullptr),
|
search_(nullptr),
|
||||||
|
user_tracks_(nullptr),
|
||||||
|
user_activities_(nullptr),
|
||||||
network_(new NetworkAccessManager(this)),
|
network_(new NetworkAccessManager(this)),
|
||||||
context_menu_(nullptr),
|
context_menu_(nullptr),
|
||||||
search_box_(new SearchBoxWidget(this)),
|
search_box_(new SearchBoxWidget(this)),
|
||||||
@ -100,6 +108,7 @@ void SoundCloudService::LazyPopulate(QStandardItem* item) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void SoundCloudService::EnsureItemsCreated() {
|
void SoundCloudService::EnsureItemsCreated() {
|
||||||
|
if (!search_) {
|
||||||
search_ =
|
search_ =
|
||||||
new QStandardItem(IconLoader::Load("edit-find"), tr("Search results"));
|
new QStandardItem(IconLoader::Load("edit-find"), tr("Search results"));
|
||||||
search_->setToolTip(
|
search_->setToolTip(
|
||||||
@ -108,14 +117,141 @@ void SoundCloudService::EnsureItemsCreated() {
|
|||||||
search_->setData(InternetModel::PlayBehaviour_MultipleItems,
|
search_->setData(InternetModel::PlayBehaviour_MultipleItems,
|
||||||
InternetModel::Role_PlayBehaviour);
|
InternetModel::Role_PlayBehaviour);
|
||||||
root_->appendRow(search_);
|
root_->appendRow(search_);
|
||||||
|
}
|
||||||
|
if (!user_tracks_ && !user_activities_ && IsLoggedIn()) {
|
||||||
|
user_tracks_ =
|
||||||
|
new QStandardItem(tr("Tracks"));
|
||||||
|
user_tracks_->setData(InternetModel::PlayBehaviour_MultipleItems,
|
||||||
|
InternetModel::Role_PlayBehaviour);
|
||||||
|
root_->appendRow(user_tracks_);
|
||||||
|
|
||||||
|
user_activities_ =
|
||||||
|
new QStandardItem(tr("Activities stream"));
|
||||||
|
user_activities_->setData(InternetModel::PlayBehaviour_MultipleItems,
|
||||||
|
InternetModel::Role_PlayBehaviour);
|
||||||
|
root_->appendRow(user_activities_);
|
||||||
|
RetrieveUserData(); // at least, try to (this will do nothing if user isn't logged)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
QWidget* SoundCloudService::HeaderWidget() const { return search_box_; }
|
QWidget* SoundCloudService::HeaderWidget() const {
|
||||||
|
return search_box_;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SoundCloudService::ShowConfig() {
|
||||||
|
app_->OpenSettingsDialogAtPage(SettingsDialog::Page_SoundCloud);
|
||||||
|
}
|
||||||
|
|
||||||
void SoundCloudService::Homepage() {
|
void SoundCloudService::Homepage() {
|
||||||
QDesktopServices::openUrl(QUrl(kHomepage));
|
QDesktopServices::openUrl(QUrl(kHomepage));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SoundCloudService::Connect() {
|
||||||
|
OAuthenticator* oauth = new OAuthenticator(
|
||||||
|
kApiClientId, kApiClientSecret, OAuthenticator::RedirectStyle::REMOTE_WITH_STATE, this);
|
||||||
|
QSettings s;
|
||||||
|
s.beginGroup(kSettingsGroup);
|
||||||
|
QString refresh_token = s.value("refresh_token").toString();
|
||||||
|
if (!refresh_token.isEmpty()) {
|
||||||
|
oauth->RefreshAuthorisation(kOAuthTokenEndpoint, refresh_token);
|
||||||
|
} else {
|
||||||
|
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("refresh_token", oauth->refresh_token());
|
||||||
|
s.setValue("access_token", access_token_);
|
||||||
|
qLog(Debug) << "access_token" << oauth->access_token();
|
||||||
|
qLog(Debug) << "refresh_token" << oauth->refresh_token();
|
||||||
|
qLog(Debug) << "expiry_time()" << oauth->expiry_time();
|
||||||
|
|
||||||
|
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");
|
||||||
|
root_->removeRow(user_activities_->row());
|
||||||
|
root_->removeRow(user_tracks_->row());
|
||||||
|
user_activities_ = nullptr;
|
||||||
|
user_tracks_ = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SoundCloudService::RetrieveUserData() {
|
||||||
|
LoadAccessTokenIfEmpty();
|
||||||
|
RetrieveUserTracks();
|
||||||
|
RetrieveUserActivities();
|
||||||
|
}
|
||||||
|
|
||||||
|
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));
|
||||||
|
// 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));
|
||||||
|
// Fill results list
|
||||||
|
for (QStandardItem* activity : activities) {
|
||||||
|
user_activities_->appendRow(activity);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void SoundCloudService::Search(const QString& text, bool now) {
|
void SoundCloudService::Search(const QString& text, bool now) {
|
||||||
pending_search_ = text;
|
pending_search_ = text;
|
||||||
|
|
||||||
@ -161,7 +297,9 @@ void SoundCloudService::SearchFinished(QNetworkReply* reply, int task_id) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void SoundCloudService::ClearSearchResults() {
|
void SoundCloudService::ClearSearchResults() {
|
||||||
if (search_) search_->removeRows(0, search_->rowCount());
|
if (search_) {
|
||||||
|
search_->removeRows(0, search_->rowCount());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int SoundCloudService::SimpleSearch(const QString& text) {
|
int SoundCloudService::SimpleSearch(const QString& text) {
|
||||||
@ -228,6 +366,24 @@ QVariant SoundCloudService::ExtractResult(QNetworkReply* reply) {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QList<QStandardItem*> SoundCloudService::ExtractActivities(const QVariant& result) {
|
||||||
|
QList<QStandardItem*> activities;
|
||||||
|
QVariantList q_variant_list = result.toMap()["collection"].toList();
|
||||||
|
for (const QVariant& q : q_variant_list) {
|
||||||
|
QMap<QString, QVariant> activity = q.toMap();
|
||||||
|
const QString type = activity["type"].toString();
|
||||||
|
if (type == "track") {
|
||||||
|
Song song = ExtractSong(activity["origin"].toMap());
|
||||||
|
if (song.is_valid()) {
|
||||||
|
activities << CreateSongItem(song);
|
||||||
|
}
|
||||||
|
} else if (type == "playlist") {
|
||||||
|
// TODO
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return activities;
|
||||||
|
}
|
||||||
|
|
||||||
SongList SoundCloudService::ExtractSongs(const QVariant& result) {
|
SongList SoundCloudService::ExtractSongs(const QVariant& result) {
|
||||||
SongList songs;
|
SongList songs;
|
||||||
|
|
||||||
|
@ -22,6 +22,7 @@
|
|||||||
#include "internetservice.h"
|
#include "internetservice.h"
|
||||||
|
|
||||||
class NetworkAccessManager;
|
class NetworkAccessManager;
|
||||||
|
class OAuthenticator;
|
||||||
class SearchBoxWidget;
|
class SearchBoxWidget;
|
||||||
|
|
||||||
class QMenu;
|
class QMenu;
|
||||||
@ -42,15 +43,26 @@ class SoundCloudService : public InternetService {
|
|||||||
void ShowContextMenu(const QPoint& global_pos);
|
void ShowContextMenu(const QPoint& global_pos);
|
||||||
QWidget* HeaderWidget() const;
|
QWidget* HeaderWidget() const;
|
||||||
|
|
||||||
|
void Connect();
|
||||||
|
bool IsLoggedIn();
|
||||||
|
void Logout();
|
||||||
|
|
||||||
int SimpleSearch(const QString& query);
|
int SimpleSearch(const QString& query);
|
||||||
|
|
||||||
static const char* kServiceName;
|
static const char* kServiceName;
|
||||||
static const char* kSettingsGroup;
|
static const char* kSettingsGroup;
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void SimpleSearchResults(int id, SongList songs);
|
void SimpleSearchResults(int id, SongList songs);
|
||||||
|
void Connected();
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
void ShowConfig();
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
|
void ConnectFinished(OAuthenticator* oauth);
|
||||||
|
void UserTracksRetrieved(QNetworkReply* reply);
|
||||||
|
void UserActivitiesRetrieved(QNetworkReply* reply);
|
||||||
void Search(const QString& text, bool now = false);
|
void Search(const QString& text, bool now = false);
|
||||||
void DoSearch();
|
void DoSearch();
|
||||||
void SearchFinished(QNetworkReply* reply, int task);
|
void SearchFinished(QNetworkReply* reply, int task);
|
||||||
@ -59,6 +71,12 @@ signals:
|
|||||||
void Homepage();
|
void Homepage();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
// 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 ClearSearchResults();
|
void ClearSearchResults();
|
||||||
void EnsureItemsCreated();
|
void EnsureItemsCreated();
|
||||||
void EnsureMenuCreated();
|
void EnsureMenuCreated();
|
||||||
@ -66,11 +84,15 @@ signals:
|
|||||||
const QList<QPair<QString, QString> >& params);
|
const QList<QPair<QString, QString> >& params);
|
||||||
// Convenient function for extracting result from reply
|
// Convenient function for extracting result from reply
|
||||||
QVariant ExtractResult(QNetworkReply* reply);
|
QVariant ExtractResult(QNetworkReply* reply);
|
||||||
|
// Returns items directly, as activities can be playlists or songs
|
||||||
|
QList<QStandardItem*> ExtractActivities(const QVariant& result);
|
||||||
SongList ExtractSongs(const QVariant& result);
|
SongList ExtractSongs(const QVariant& result);
|
||||||
Song ExtractSong(const QVariantMap& result_song);
|
Song ExtractSong(const QVariantMap& result_song);
|
||||||
|
|
||||||
QStandardItem* root_;
|
QStandardItem* root_;
|
||||||
QStandardItem* search_;
|
QStandardItem* search_;
|
||||||
|
QStandardItem* user_tracks_;
|
||||||
|
QStandardItem* user_activities_;
|
||||||
|
|
||||||
NetworkAccessManager* network_;
|
NetworkAccessManager* network_;
|
||||||
|
|
||||||
@ -82,8 +104,13 @@ signals:
|
|||||||
|
|
||||||
QByteArray api_key_;
|
QByteArray api_key_;
|
||||||
|
|
||||||
|
QString access_token_;
|
||||||
|
QDateTime expiry_time_;
|
||||||
|
|
||||||
static const char* kUrl;
|
static const char* kUrl;
|
||||||
static const char* kUrlCover;
|
static const char* kOAuthEndpoint;
|
||||||
|
static const char* kOAuthTokenEndpoint;
|
||||||
|
static const char* kOAuthScope;
|
||||||
static const char* kHomepage;
|
static const char* kHomepage;
|
||||||
|
|
||||||
static const int kSongSearchLimit;
|
static const int kSongSearchLimit;
|
||||||
@ -91,6 +118,7 @@ signals:
|
|||||||
static const int kSearchDelayMsec;
|
static const int kSearchDelayMsec;
|
||||||
|
|
||||||
static const char* kApiClientId;
|
static const char* kApiClientId;
|
||||||
|
static const char* kApiClientSecret;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // SOUNDCLOUDSERVICE_H
|
#endif // SOUNDCLOUDSERVICE_H
|
||||||
|
73
src/internet/soundcloudsettingspage.cpp
Normal file
73
src/internet/soundcloudsettingspage.cpp
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
/* This file is part of Clementine.
|
||||||
|
Copyright 2014, 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 "soundcloudservice.h"
|
||||||
|
#include "soundcloudsettingspage.h"
|
||||||
|
#include "ui_soundcloudsettingspage.h"
|
||||||
|
#include "core/application.h"
|
||||||
|
#include "internet/internetmodel.h"
|
||||||
|
|
||||||
|
SoundCloudSettingsPage::SoundCloudSettingsPage(SettingsDialog* parent)
|
||||||
|
: SettingsPage(parent),
|
||||||
|
ui_(new Ui::SoundCloudSettingsPage),
|
||||||
|
service_(
|
||||||
|
dialog()->app()->internet_model()->Service<SoundCloudService>()) {
|
||||||
|
ui_->setupUi(this);
|
||||||
|
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);
|
||||||
|
}
|
50
src/internet/soundcloudsettingspage.h
Normal file
50
src/internet/soundcloudsettingspage.h
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
/* This file is part of Clementine.
|
||||||
|
Copyright 2014, 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 SOUNDCLOUDSETTINGSPAGE_H
|
||||||
|
#define SOUNDCLOUDSETTINGSPAGE_H
|
||||||
|
|
||||||
|
#include "ui/settingspage.h"
|
||||||
|
|
||||||
|
class SoundCloudService;
|
||||||
|
class Ui_SoundCloudSettingsPage;
|
||||||
|
|
||||||
|
class SoundCloudSettingsPage : public SettingsPage {
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
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 // SOUNDCLOUDSETTINGSPAGE_H
|
114
src/internet/soundcloudsettingspage.ui
Normal file
114
src/internet/soundcloudsettingspage.ui
Normal file
@ -0,0 +1,114 @@
|
|||||||
|
<?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>Sound Cloud</string>
|
||||||
|
</property>
|
||||||
|
<property name="windowIcon">
|
||||||
|
<iconset resource="../../data/data.qrc">
|
||||||
|
<normaloff>:/providers/soundcloud.png</normaloff>:/providers/soundcloud.png</iconset>
|
||||||
|
</property>
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="label">
|
||||||
|
<property name="text">
|
||||||
|
<string>You don't need to be logged in to search and to listen to music on SoundCloud.</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="label_3">
|
||||||
|
<property name="text">
|
||||||
|
<string>However, you need to login to access your playlists and your stream.</string>
|
||||||
|
</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>
|
||||||
|
<resources>
|
||||||
|
<include location="../../data/data.qrc"/>
|
||||||
|
</resources>
|
||||||
|
<connections/>
|
||||||
|
</ui>
|
@ -38,6 +38,7 @@
|
|||||||
#include "internet/digitallyimportedsettingspage.h"
|
#include "internet/digitallyimportedsettingspage.h"
|
||||||
#include "internet/groovesharksettingspage.h"
|
#include "internet/groovesharksettingspage.h"
|
||||||
#include "internet/magnatunesettingspage.h"
|
#include "internet/magnatunesettingspage.h"
|
||||||
|
#include "internet/soundcloudsettingspage.h"
|
||||||
#include "internet/spotifysettingspage.h"
|
#include "internet/spotifysettingspage.h"
|
||||||
#include "internet/subsonicsettingspage.h"
|
#include "internet/subsonicsettingspage.h"
|
||||||
#include "internet/ubuntuonesettingspage.h"
|
#include "internet/ubuntuonesettingspage.h"
|
||||||
@ -176,6 +177,7 @@ SettingsDialog::SettingsDialog(Application* app, BackgroundStreams* streams,
|
|||||||
AddPage(Page_Box, new BoxSettingsPage(this), providers);
|
AddPage(Page_Box, new BoxSettingsPage(this), providers);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
AddPage(Page_SoundCloud, new SoundCloudSettingsPage(this), providers);
|
||||||
AddPage(Page_Spotify, new SpotifySettingsPage(this), providers);
|
AddPage(Page_Spotify, new SpotifySettingsPage(this), providers);
|
||||||
|
|
||||||
#ifdef HAVE_VK
|
#ifdef HAVE_VK
|
||||||
|
@ -69,6 +69,7 @@ class SettingsDialog : public QDialog {
|
|||||||
Page_Library,
|
Page_Library,
|
||||||
Page_Lastfm,
|
Page_Lastfm,
|
||||||
Page_Grooveshark,
|
Page_Grooveshark,
|
||||||
|
Page_SoundCloud,
|
||||||
Page_Spotify,
|
Page_Spotify,
|
||||||
Page_Magnatune,
|
Page_Magnatune,
|
||||||
Page_DigitallyImported,
|
Page_DigitallyImported,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user