Initial support for Amazon Cloud Drive.

This commit is contained in:
John Maguire 2015-03-26 16:52:19 +01:00
parent f3426c05c7
commit 3594af5be1
24 changed files with 728 additions and 5 deletions

View File

@ -215,6 +215,11 @@ optional_component(SEAFILE ON "Seafile support"
DEPENDS "Taglib 1.8" "TAGLIB_VERSION VERSION_GREATER 1.7.999"
)
optional_component(AMAZON_CLOUD_DRIVE ON "Amazon Cloud Drive support"
DEPENDS "Google sparsehash" SPARSEHASH_INCLUDE_DIRS
DEPENDS "Taglib 1.8" "TAGLIB_VERSION VERSION_GREATER 1.7.999"
)
optional_component(AUDIOCD ON "Devices: Audio CD support"
DEPENDS "libcdio" CDIO_FOUND
)

View File

@ -310,6 +310,7 @@
<file>playstore/uk_generic_rgb_wo_45.png</file>
<file>playstore/vi_generic_rgb_wo_45.png</file>
<file>providers/amazon.png</file>
<file>providers/amazonclouddrive.png</file>
<file>providers/aol.png</file>
<file>providers/bbc.png</file>
<file>providers/box.png</file>
@ -385,6 +386,7 @@
<file>schema/schema-45.sql</file>
<file>schema/schema-46.sql</file>
<file>schema/schema-47.sql</file>
<file>schema/schema-48.sql</file>
<file>schema/schema-4.sql</file>
<file>schema/schema-5.sql</file>
<file>schema/schema-6.sql</file>

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

50
data/schema/schema-48.sql Normal file
View File

@ -0,0 +1,50 @@
CREATE TABLE amazon_cloud_drive_songs(
title TEXT,
album TEXT,
artist TEXT,
albumartist TEXT,
composer TEXT,
track INTEGER,
disc INTEGER,
bpm REAL,
year INTEGER,
genre TEXT,
comment TEXT,
compilation INTEGER,
length INTEGER,
bitrate INTEGER,
samplerate INTEGER,
directory INTEGER NOT NULL,
filename TEXT NOT NULL,
mtime INTEGER NOT NULL,
ctime INTEGER NOT NULL,
filesize INTEGER NOT NULL,
sampler INTEGER NOT NULL DEFAULT 0,
art_automatic TEXT,
art_manual TEXT,
filetype INTEGER NOT NULL DEFAULT 0,
playcount INTEGER NOT NULL DEFAULT 0,
lastplayed INTEGER,
rating INTEGER,
forced_compilation_on INTEGER NOT NULL DEFAULT 0,
forced_compilation_off INTEGER NOT NULL DEFAULT 0,
effective_compilation NOT NULL DEFAULT 0,
skipcount INTEGER NOT NULL DEFAULT 0,
score INTEGER NOT NULL DEFAULT 0,
beginning INTEGER NOT NULL DEFAULT 0,
cue_path TEXT,
unavailable INTEGER DEFAULT 0,
effective_albumartist TEXT,
etag TEXT,
performer TEXT,
grouping TEXT
);
CREATE VIRTUAL TABLE amazon_cloud_drive_songs_fts USING fts3 (
ftstitle, ftsalbum, ftsartist, ftsalbumartist, ftscomposer, ftsperformer, ftsgrouping, ftsgenre, ftscomment,
tokenize=unicode
);
UPDATE schema_version SET version=48;

View File

@ -957,7 +957,12 @@ bool TagReader::ReadCloudFile(const QUrl& download_url, const QString& title,
int size, const QString& mime_type,
const QString& authorisation_header,
pb::tagreader::SongMetadata* song) const {
qLog(Debug) << "Loading tags from" << title;
qLog(Debug) << "Loading tags from"
<< title
<< download_url
<< size
<< mime_type
<< authorisation_header;
std::unique_ptr<CloudStream> stream(new CloudStream(
download_url, title, size, authorisation_header, network_));

View File

@ -1178,6 +1178,20 @@ optional_source(HAVE_SEAFILE
internet/seafile/seafilesettingspage.ui
)
# Amazon Cloud Drive support
optional_source(HAVE_AMAZON_CLOUD_DRIVE
SOURCES
internet/amazon/amazonclouddrive.cpp
internet/amazon/amazonsettingspage.cpp
internet/amazon/amazonurlhandler.cpp
HEADERS
internet/amazon/amazonclouddrive.h
internet/amazon/amazonsettingspage.h
internet/amazon/amazonurlhandler.h
UI
internet/amazon/amazonsettingspage.ui
)
# Pulse audio integration
optional_source(HAVE_LIBPULSE

View File

@ -21,6 +21,7 @@
#define CMAKE_EXECUTABLE_SUFFIX "${CMAKE_EXECUTABLE_SUFFIX}"
#cmakedefine ENABLE_VISUALISATIONS
#cmakedefine HAVE_AMAZON_CLOUD_DRIVE
#cmakedefine HAVE_AUDIOCD
#cmakedefine HAVE_BOX
#cmakedefine HAVE_BREAKPAD

View File

@ -47,7 +47,7 @@
#include <QVariant>
const char* Database::kDatabaseFilename = "clementine.db";
const int Database::kSchemaVersion = 47;
const int Database::kSchemaVersion = 48;
const char* Database::kMagicAllSongsTables = "%allsongstables";
int Database::sNextConnectionId = 1;

View File

@ -903,6 +903,15 @@ void GstEnginePipeline::SourceSetupCallback(GstURIDecodeBin* bin,
g_object_set(element, "extra-headers", headers, nullptr);
gst_structure_free(headers);
}
if (g_object_class_find_property(G_OBJECT_GET_CLASS(element),
"extra-headers") &&
instance->url().host().contains("amazonaws.com")) {
GstStructure* headers = gst_structure_new(
"extra-headers", "Authorization", G_TYPE_STRING,
instance->url().fragment().toAscii().data(), nullptr);
g_object_set(element, "extra-headers", headers, nullptr);
gst_structure_free(headers);
}
if (g_object_class_find_property(G_OBJECT_GET_CLASS(element), "user-agent")) {
QString user_agent =

View File

@ -0,0 +1,179 @@
#include "internet/amazon/amazonclouddrive.h"
#include <QIcon>
#include <qjson/parser.h>
#include "core/application.h"
#include "core/closure.h"
#include "core/logging.h"
#include "core/network.h"
#include "core/player.h"
#include "internet/core/oauthenticator.h"
#include "internet/amazon/amazonurlhandler.h"
#include "ui/settingsdialog.h"
const char* AmazonCloudDrive::kServiceName = "Cloud Drive";
const char* AmazonCloudDrive::kSettingsGroup = "AmazonCloudDrive";
namespace {
static const char* kServiceId = "amazon_cloud_drive";
static const char* kClientId =
"amzn1.application-oa2-client.2b1157a7dadc45c3888567882b3a9f05";
static const char* kClientSecret =
"acfbf95340cc4c381dd43fb75b5e111882d7fd1b02a02f3013ab124baf8d1655";
static const char* kOAuthScope = "clouddrive:read";
static const char* kOAuthEndpoint = "https://www.amazon.com/ap/oa";
static const char* kOAuthTokenEndpoint = "https://api.amazon.com/auth/o2/token";
static const char* kEndpointEndpoint =
"https://drive.amazonaws.com/drive/v1/account/endpoint";
static const char* kChangesEndpoint = "%1/changes";
static const char* kDownloadEndpoint = "%1/nodes/%2/content";
} // namespace
AmazonCloudDrive::AmazonCloudDrive(Application* app, InternetModel* parent)
: CloudFileService(app, parent, kServiceName, kServiceId,
QIcon(":/providers/amazonclouddrive.png"),
SettingsDialog::Page_AmazonCloudDrive),
network_(new NetworkAccessManager) {
app->player()->RegisterUrlHandler(new AmazonUrlHandler(this, this));
}
bool AmazonCloudDrive::has_credentials() const {
QSettings s;
s.beginGroup(kSettingsGroup);
return !s.value("refresh_token").toString().isEmpty();
}
QUrl AmazonCloudDrive::GetStreamingUrlFromSongId(const QUrl& url) {
QUrl download_url(
QString(kDownloadEndpoint).arg(content_url_).arg(url.path()));
download_url.setFragment(QString("Bearer %1").arg(access_token_));
return download_url;
}
void AmazonCloudDrive::Connect() {
OAuthenticator* oauth = new OAuthenticator(
kClientId, kClientSecret,
// Amazon forbids arbitrary query parameters so REMOTE_WITH_STATE is
// required.
OAuthenticator::RedirectStyle::REMOTE_WITH_STATE, this);
oauth->StartAuthorisation(kOAuthEndpoint, kOAuthTokenEndpoint, kOAuthScope);
NewClosure(oauth, SIGNAL(Finished()), this,
SLOT(ConnectFinished(OAuthenticator*)), oauth);
}
void AmazonCloudDrive::ForgetCredentials() {
}
void AmazonCloudDrive::ConnectFinished(OAuthenticator* oauth) {
oauth->deleteLater();
qLog(Debug) << oauth->access_token()
<< oauth->expiry_time()
<< oauth->refresh_token();
QSettings s;
s.beginGroup(kSettingsGroup);
s.setValue("refresh_token", oauth->refresh_token());
access_token_ = oauth->access_token();
// TODO: Amazon expiry time is only an hour so refresh this regularly.
expiry_time_ = oauth->expiry_time();
FetchEndpoint();
}
void AmazonCloudDrive::FetchEndpoint() {
QUrl url(kEndpointEndpoint);
QNetworkRequest request(url);
AddAuthorizationHeader(&request);
QNetworkReply* reply = network_->get(request);
NewClosure(reply, SIGNAL(finished()), this,
SLOT(FetchEndpointFinished(QNetworkReply*)), reply);
}
void AmazonCloudDrive::FetchEndpointFinished(QNetworkReply* reply) {
reply->deleteLater();
QJson::Parser parser;
QVariantMap response = parser.parse(reply).toMap();
content_url_ = response["contentUrl"].toString();
metadata_url_ = response["metadataUrl"].toString();
qLog(Debug) << "content_url:" << content_url_;
qLog(Debug) << "metadata_url:" << metadata_url_;
RequestChanges();
}
void AmazonCloudDrive::RequestChanges() {
QUrl url(QString(kChangesEndpoint).arg(metadata_url_));
QNetworkRequest request(url);
AddAuthorizationHeader(&request);
QNetworkReply* reply = network_->post(request, QByteArray());
NewClosure(reply, SIGNAL(finished()), this,
SLOT(RequestChangesFinished(QNetworkReply*)), reply);
}
void AmazonCloudDrive::RequestChangesFinished(QNetworkReply* reply) {
reply->deleteLater();
QJson::Parser parser;
QVariantMap response = parser.parse(reply).toMap();
QString checkpoint = response["checkpoint"].toString();
QSettings settings;
settings.beginGroup(kSettingsGroup);
settings.setValue("checkpoint", checkpoint);
QVariantList nodes = response["nodes"].toList();
qLog(Debug) << "nodes:" << nodes.length();
for (const QVariant& n : nodes) {
QVariantMap node = n.toMap();
qLog(Debug) << node["kind"] << node["status"];
if (node["kind"].toString() == "FOLDER") {
continue;
}
QString status = node["status"].toString();
if (node["status"].toString() != "AVAILABLE") {
continue;
}
QVariantMap content_properties = node["contentProperties"].toMap();
QString mime_type = content_properties["contentType"].toString();
QUrl url;
url.setScheme("amazonclouddrive");
url.setPath(node["id"].toString());
qLog(Debug) << url << mime_type;
if (ShouldIndexFile(url, mime_type)) {
QString node_id = node["id"].toString();
QUrl content_url(
QString(kDownloadEndpoint).arg(content_url_).arg(node_id));
QString md5 = content_properties["md5"].toString();
Song song;
song.set_url(url);
song.set_etag(md5);
song.set_mtime(node["modifiedDate"].toDateTime().toTime_t());
song.set_ctime(node["createdDate"].toDateTime().toTime_t());
song.set_title(node["name"].toString());
song.set_filesize(content_properties["size"].toInt());
qLog(Debug) << "Adding:"
<< song.title()
<< mime_type
<< url
<< content_url;
MaybeAddFileToDatabase(song, mime_type, content_url, QString("Bearer %1").arg(access_token_));
}
}
}
void AmazonCloudDrive::AddAuthorizationHeader(QNetworkRequest* request) {
request->setRawHeader("Authorization",
QString("Bearer %1").arg(access_token_).toUtf8());
}

View File

@ -0,0 +1,53 @@
#ifndef INTERNET_AMAZON_AMAZON_CLOUD_DRIVE_H_
#define INTERNET_AMAZON_AMAZON_CLOUD_DRIVE_H_
#include "internet/core/cloudfileservice.h"
#include <QDateTime>
#include <QString>
#include <QUrl>
class NetworkAccessManager;
class OAuthenticator;
class QNetworkReply;
class QNetworkRequest;
class AmazonCloudDrive : public CloudFileService {
Q_OBJECT
public:
AmazonCloudDrive(Application* app, InternetModel* parent);
static const char* kServiceName;
static const char* kSettingsGroup;
virtual bool has_credentials() const;
QUrl GetStreamingUrlFromSongId(const QUrl& url);
void ForgetCredentials();
signals:
void Connected();
public slots:
void Connect();
private:
void FetchEndpoint();
void RequestChanges();
void AddAuthorizationHeader(QNetworkRequest* request);
private slots:
void ConnectFinished(OAuthenticator*);
void FetchEndpointFinished(QNetworkReply*);
void RequestChangesFinished(QNetworkReply*);
private:
NetworkAccessManager* network_;
QString access_token_;
QDateTime expiry_time_;
QString content_url_;
QString metadata_url_;
};
#endif // INTERNET_AMAZON_AMAZON_CLOUD_DRIVE_H_

View File

@ -0,0 +1,75 @@
/* This file is part of Clementine.
Copyright 2012, 2014, John Maguire <john.maguire@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 "amazonsettingspage.h"
#include "ui_amazonsettingspage.h"
#include "core/application.h"
#include "internet/amazon/amazonclouddrive.h"
#include "internet/core/internetmodel.h"
#include "ui/settingsdialog.h"
AmazonSettingsPage::AmazonSettingsPage(SettingsDialog* parent)
: SettingsPage(parent),
ui_(new Ui::AmazonSettingsPage),
service_(dialog()->app()->internet_model()->Service<AmazonCloudDrive>()) {
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()));
dialog()->installEventFilter(this);
}
AmazonSettingsPage::~AmazonSettingsPage() { delete ui_; }
void AmazonSettingsPage::Load() {
QSettings s;
s.beginGroup(AmazonCloudDrive::kSettingsGroup);
const QString name = s.value("name").toString();
if (!name.isEmpty()) {
ui_->login_state->SetLoggedIn(LoginStateWidget::LoggedIn, name);
}
}
void AmazonSettingsPage::Save() {
QSettings s;
s.beginGroup(AmazonCloudDrive::kSettingsGroup);
}
void AmazonSettingsPage::LoginClicked() {
service_->Connect();
ui_->login_button->setEnabled(false);
}
bool AmazonSettingsPage::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 AmazonSettingsPage::LogoutClicked() {
service_->ForgetCredentials();
ui_->login_state->SetLoggedIn(LoginStateWidget::LoggedOut);
}

View File

@ -0,0 +1,53 @@
/* This file is part of Clementine.
Copyright 2012, 2014, John Maguire <john.maguire@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_AMAZON_AMAZONSETTINGSPAGE_H_
#define INTERNET_AMAZON_AMAZONSETTINGSPAGE_H_
#include "ui/settingspage.h"
#include <QModelIndex>
#include <QWidget>
class AmazonCloudDrive;
class Ui_AmazonSettingsPage;
class AmazonSettingsPage : public SettingsPage {
Q_OBJECT
public:
explicit AmazonSettingsPage(SettingsDialog* parent = nullptr);
~AmazonSettingsPage();
void Load();
void Save();
// QObject
bool eventFilter(QObject* object, QEvent* event);
private slots:
void LoginClicked();
void LogoutClicked();
private:
Ui_AmazonSettingsPage* ui_;
AmazonCloudDrive* service_;
};
#endif // INTERNET_AMAZON_AMAZONSETTINGSPAGE_H_

View File

@ -0,0 +1,110 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>AmazonSettingsPage</class>
<widget class="QWidget" name="AmazonSettingsPage">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>569</width>
<height>491</height>
</rect>
</property>
<property name="windowTitle">
<string>Amazon</string>
</property>
<property name="windowIcon">
<iconset resource="../../data/data.qrc">
<normaloff>:/providers/amazonclouddrive.png</normaloff>:/providers/amazonclouddrive.png</iconset>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<widget class="QLabel" name="label">
<property name="text">
<string>Clementine can play music that you have uploaded to Amazon Cloud Drive</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>
<resources>
<include location="../../data/data.qrc"/>
</resources>
<connections/>
</ui>

View File

@ -0,0 +1,11 @@
#include "internet/amazon/amazonurlhandler.h"
#include "internet/amazon/amazonclouddrive.h"
AmazonUrlHandler::AmazonUrlHandler(AmazonCloudDrive* service, QObject* parent)
: UrlHandler(parent), service_(service) {}
UrlHandler::LoadResult AmazonUrlHandler::StartLoading(const QUrl& url) {
return LoadResult(url, LoadResult::TrackAvailable,
service_->GetStreamingUrlFromSongId(url));
}

View File

@ -0,0 +1,22 @@
#ifndef INTERNET_AMAZON_AMAZONURLHANDLER_H_
#define INTERNET_AMAZON_AMAZONURLHANDLER_H_
#include "core/urlhandler.h"
class AmazonCloudDrive;
class AmazonUrlHandler : public UrlHandler {
Q_OBJECT
public:
explicit AmazonUrlHandler(
AmazonCloudDrive* service, QObject* parent = nullptr);
QString scheme() const { return "amazonclouddrive"; }
QIcon icon() const { return QIcon(":providers/amazonclouddrive.png"); }
LoadResult StartLoading(const QUrl& url);
private:
AmazonCloudDrive* service_;
};
#endif // INTERNET_AMAZON_AMAZONURLHANDLER_H_

View File

@ -0,0 +1,110 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>DropboxSettingsPage</class>
<widget class="QWidget" name="DropboxSettingsPage">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>569</width>
<height>491</height>
</rect>
</property>
<property name="windowTitle">
<string>Dropbox</string>
</property>
<property name="windowIcon">
<iconset resource="../../data/data.qrc">
<normaloff>:/providers/dropbox.png</normaloff>:/providers/dropbox.png</iconset>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<widget class="QLabel" name="label">
<property name="text">
<string>Clementine can play music that you have uploaded to Dropbox</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>
<resources>
<include location="../../data/data.qrc"/>
</resources>
<connections/>
</ui>

View File

@ -64,6 +64,9 @@
#ifdef HAVE_SEAFILE
#include "internet/seafile/seafileservice.h"
#endif
#ifdef HAVE_AMAZON_CLOUD_DRIVE
#include "internet/amazon/amazonclouddrive.h"
#endif
using smart_playlists::Generator;
using smart_playlists::GeneratorMimeData;
@ -117,6 +120,9 @@ InternetModel::InternetModel(Application* app, QObject* parent)
#ifdef HAVE_VK
AddService(new VkService(app, this));
#endif
#ifdef HAVE_AMAZON_CLOUD_DRIVE
AddService(new AmazonCloudDrive(app, this));
#endif
invisibleRootItem()->sortChildren(0, Qt::AscendingOrder);
UpdateServices();

View File

@ -61,10 +61,16 @@ void OAuthenticator::StartAuthorisation(const QString& oauth_endpoint,
} else if (redirect_style_ == RedirectStyle::REMOTE_WITH_STATE) {
redirect_url = QUrl(kRemoteURL);
url.addQueryItem("state", port);
} else if (redirect_style_ == RedirectStyle::REMOTE_WITH_FRAGMENT) {
redirect_url = QUrl(kRemoteURL);
redirect_url.setUserName(port);
} else {
redirect_url = server->url();
}
qLog(Debug) << url
<< redirect_url;
url.addQueryItem("redirect_uri", redirect_url.toString());
url.addQueryItem("scope", scope);

View File

@ -43,7 +43,9 @@ class OAuthenticator : public QObject {
// 'state' parameter of the URL, for services which allow only redirect URL
// without parameters (e.g. SoundCloud). "state" parameter will be added to
// the redirect URL by the service itself.
REMOTE_WITH_STATE = 2
REMOTE_WITH_STATE = 2,
REMOTE_WITH_FRAGMENT = 3,
};
OAuthenticator(const QString& client_id, const QString& client_secret,

View File

@ -821,7 +821,7 @@ MainWindow::MainWindow(Application* app, SystemTrayIcon* tray_icon, OSD* osd,
connect(ui_->action_kittens, SIGNAL(toggled(bool)), app_->network_remote(),
SLOT(EnableKittens(bool)));
// Hide the console
// connect(ui_->action_console, SIGNAL(triggered()), SLOT(ShowConsole()));
connect(ui_->action_console, SIGNAL(triggered()), SLOT(ShowConsole()));
NowPlayingWidgetPositionChanged(ui_->now_playing->show_above_status_bar());
// Load theme

View File

@ -461,6 +461,7 @@
<addaction name="action_hypnotoad"/>
<addaction name="action_enterprise"/>
<addaction name="action_kittens"/>
<addaction name="action_console"/>
<addaction name="separator"/>
</widget>
<widget class="QMenu" name="menu_tools">

View File

@ -84,6 +84,10 @@
#include "internet/seafile/seafilesettingspage.h"
#endif
#ifdef HAVE_AMAZON_CLOUD_DRIVE
#include "internet/amazon/amazonsettingspage.h"
#endif
#include <QAbstractButton>
#include <QDesktopWidget>
#include <QPainter>
@ -193,6 +197,10 @@ SettingsDialog::SettingsDialog(Application* app, BackgroundStreams* streams,
AddPage(Page_Seafile, new SeafileSettingsPage(this), providers);
#endif
#ifdef HAVE_AMAZON_CLOUD_DRIVE
AddPage(Page_AmazonCloudDrive, new AmazonSettingsPage(this), providers);
#endif
AddPage(Page_Magnatune, new MagnatuneSettingsPage(this), providers);
AddPage(Page_DigitallyImported, new DigitallyImportedSettingsPage(this),
providers);

View File

@ -86,7 +86,8 @@ class SettingsDialog : public QDialog {
Page_Box,
Page_Vk,
Page_Seafile,
Page_InternetShow
Page_InternetShow,
Page_AmazonCloudDrive,
};
enum Role { Role_IsSeparator = Qt::UserRole };