Initial support for Amazon Cloud Drive.
This commit is contained in:
parent
f3426c05c7
commit
3594af5be1
|
@ -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
|
||||
)
|
||||
|
|
|
@ -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 |
|
@ -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;
|
|
@ -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_));
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 =
|
||||
|
|
|
@ -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());
|
||||
}
|
|
@ -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_
|
|
@ -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);
|
||||
}
|
|
@ -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_
|
|
@ -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>
|
|
@ -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));
|
||||
}
|
|
@ -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_
|
|
@ -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>
|
|
@ -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();
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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">
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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 };
|
||||
|
|
Loading…
Reference in New Issue