Compare commits
8 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
23645f9fea | ||
|
8153388f19 | ||
|
fa9e279259 | ||
|
47a405543c | ||
|
748d88d993 | ||
|
25ec9c65f4 | ||
|
27c1a37173 | ||
|
3594af5be1 |
@ -215,6 +215,11 @@ optional_component(SEAFILE ON "Seafile support"
|
|||||||
DEPENDS "Taglib 1.8" "TAGLIB_VERSION VERSION_GREATER 1.7.999"
|
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"
|
optional_component(AUDIOCD ON "Devices: Audio CD support"
|
||||||
DEPENDS "libcdio" CDIO_FOUND
|
DEPENDS "libcdio" CDIO_FOUND
|
||||||
)
|
)
|
||||||
|
@ -310,6 +310,7 @@
|
|||||||
<file>playstore/uk_generic_rgb_wo_45.png</file>
|
<file>playstore/uk_generic_rgb_wo_45.png</file>
|
||||||
<file>playstore/vi_generic_rgb_wo_45.png</file>
|
<file>playstore/vi_generic_rgb_wo_45.png</file>
|
||||||
<file>providers/amazon.png</file>
|
<file>providers/amazon.png</file>
|
||||||
|
<file>providers/amazonclouddrive.png</file>
|
||||||
<file>providers/aol.png</file>
|
<file>providers/aol.png</file>
|
||||||
<file>providers/bbc.png</file>
|
<file>providers/bbc.png</file>
|
||||||
<file>providers/box.png</file>
|
<file>providers/box.png</file>
|
||||||
@ -385,6 +386,7 @@
|
|||||||
<file>schema/schema-45.sql</file>
|
<file>schema/schema-45.sql</file>
|
||||||
<file>schema/schema-46.sql</file>
|
<file>schema/schema-46.sql</file>
|
||||||
<file>schema/schema-47.sql</file>
|
<file>schema/schema-47.sql</file>
|
||||||
|
<file>schema/schema-48.sql</file>
|
||||||
<file>schema/schema-4.sql</file>
|
<file>schema/schema-4.sql</file>
|
||||||
<file>schema/schema-5.sql</file>
|
<file>schema/schema-5.sql</file>
|
||||||
<file>schema/schema-6.sql</file>
|
<file>schema/schema-6.sql</file>
|
||||||
|
BIN
data/providers/amazonclouddrive.png
Normal file
BIN
data/providers/amazonclouddrive.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 3.0 KiB |
50
data/schema/schema-48.sql
Normal file
50
data/schema/schema-48.sql
Normal 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;
|
@ -957,7 +957,12 @@ bool TagReader::ReadCloudFile(const QUrl& download_url, const QString& title,
|
|||||||
int size, const QString& mime_type,
|
int size, const QString& mime_type,
|
||||||
const QString& authorisation_header,
|
const QString& authorisation_header,
|
||||||
pb::tagreader::SongMetadata* song) const {
|
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(
|
std::unique_ptr<CloudStream> stream(new CloudStream(
|
||||||
download_url, title, size, authorisation_header, network_));
|
download_url, title, size, authorisation_header, network_));
|
||||||
|
@ -1178,6 +1178,20 @@ optional_source(HAVE_SEAFILE
|
|||||||
internet/seafile/seafilesettingspage.ui
|
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
|
# Pulse audio integration
|
||||||
optional_source(HAVE_LIBPULSE
|
optional_source(HAVE_LIBPULSE
|
||||||
|
@ -21,6 +21,7 @@
|
|||||||
#define CMAKE_EXECUTABLE_SUFFIX "${CMAKE_EXECUTABLE_SUFFIX}"
|
#define CMAKE_EXECUTABLE_SUFFIX "${CMAKE_EXECUTABLE_SUFFIX}"
|
||||||
|
|
||||||
#cmakedefine ENABLE_VISUALISATIONS
|
#cmakedefine ENABLE_VISUALISATIONS
|
||||||
|
#cmakedefine HAVE_AMAZON_CLOUD_DRIVE
|
||||||
#cmakedefine HAVE_AUDIOCD
|
#cmakedefine HAVE_AUDIOCD
|
||||||
#cmakedefine HAVE_BOX
|
#cmakedefine HAVE_BOX
|
||||||
#cmakedefine HAVE_BREAKPAD
|
#cmakedefine HAVE_BREAKPAD
|
||||||
|
@ -47,7 +47,7 @@
|
|||||||
#include <QVariant>
|
#include <QVariant>
|
||||||
|
|
||||||
const char* Database::kDatabaseFilename = "clementine.db";
|
const char* Database::kDatabaseFilename = "clementine.db";
|
||||||
const int Database::kSchemaVersion = 47;
|
const int Database::kSchemaVersion = 48;
|
||||||
const char* Database::kMagicAllSongsTables = "%allsongstables";
|
const char* Database::kMagicAllSongsTables = "%allsongstables";
|
||||||
|
|
||||||
int Database::sNextConnectionId = 1;
|
int Database::sNextConnectionId = 1;
|
||||||
|
@ -903,6 +903,15 @@ void GstEnginePipeline::SourceSetupCallback(GstURIDecodeBin* bin,
|
|||||||
g_object_set(element, "extra-headers", headers, nullptr);
|
g_object_set(element, "extra-headers", headers, nullptr);
|
||||||
gst_structure_free(headers);
|
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")) {
|
if (g_object_class_find_property(G_OBJECT_GET_CLASS(element), "user-agent")) {
|
||||||
QString user_agent =
|
QString user_agent =
|
||||||
|
246
src/internet/amazon/amazonclouddrive.cpp
Normal file
246
src/internet/amazon/amazonclouddrive.cpp
Normal file
@ -0,0 +1,246 @@
|
|||||||
|
/* This file is part of Clementine.
|
||||||
|
Copyright 2015, 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 "internet/amazon/amazonclouddrive.h"
|
||||||
|
|
||||||
|
#include <QIcon>
|
||||||
|
|
||||||
|
#include <qjson/parser.h>
|
||||||
|
#include <qjson/serializer.h>
|
||||||
|
|
||||||
|
#include "core/application.h"
|
||||||
|
#include "core/closure.h"
|
||||||
|
#include "core/logging.h"
|
||||||
|
#include "core/network.h"
|
||||||
|
#include "core/player.h"
|
||||||
|
#include "core/waitforsignal.h"
|
||||||
|
#include "internet/core/oauthenticator.h"
|
||||||
|
#include "internet/amazon/amazonurlhandler.h"
|
||||||
|
#include "library/librarybackend.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) {
|
||||||
|
EnsureConnected(); // Access token must be up to date.
|
||||||
|
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);
|
||||||
|
|
||||||
|
QSettings s;
|
||||||
|
s.beginGroup(kSettingsGroup);
|
||||||
|
QString refresh_token = s.value("refresh_token").toString();
|
||||||
|
if (refresh_token.isEmpty()) {
|
||||||
|
oauth->StartAuthorisation(kOAuthEndpoint, kOAuthTokenEndpoint, kOAuthScope);
|
||||||
|
} else {
|
||||||
|
oauth->RefreshAuthorisation(kOAuthTokenEndpoint, refresh_token);
|
||||||
|
}
|
||||||
|
|
||||||
|
NewClosure(oauth, SIGNAL(Finished()), this,
|
||||||
|
SLOT(ConnectFinished(OAuthenticator*)), oauth);
|
||||||
|
}
|
||||||
|
|
||||||
|
void AmazonCloudDrive::EnsureConnected() {
|
||||||
|
if (access_token_.isEmpty() ||
|
||||||
|
QDateTime::currentDateTime().secsTo(expiry_time_) < 60) {
|
||||||
|
Connect();
|
||||||
|
WaitForSignal(this, SIGNAL(Connected()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void AmazonCloudDrive::ForgetCredentials() {
|
||||||
|
QSettings s;
|
||||||
|
s.beginGroup(kSettingsGroup);
|
||||||
|
s.remove("");
|
||||||
|
access_token_ = QString();
|
||||||
|
expiry_time_ = QDateTime();
|
||||||
|
}
|
||||||
|
|
||||||
|
void AmazonCloudDrive::ConnectFinished(OAuthenticator* oauth) {
|
||||||
|
oauth->deleteLater();
|
||||||
|
|
||||||
|
QSettings s;
|
||||||
|
s.beginGroup(kSettingsGroup);
|
||||||
|
s.setValue("refresh_token", oauth->refresh_token());
|
||||||
|
|
||||||
|
access_token_ = oauth->access_token();
|
||||||
|
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();
|
||||||
|
QSettings s;
|
||||||
|
s.beginGroup(kSettingsGroup);
|
||||||
|
QString checkpoint = s.value("checkpoint", "").toString();
|
||||||
|
RequestChanges(checkpoint);
|
||||||
|
|
||||||
|
// We wait until we know the endpoint URLs before emitting Connected();
|
||||||
|
emit Connected();
|
||||||
|
}
|
||||||
|
|
||||||
|
void AmazonCloudDrive::RequestChanges(const QString& checkpoint) {
|
||||||
|
EnsureConnected();
|
||||||
|
QUrl url(QString(kChangesEndpoint).arg(metadata_url_));
|
||||||
|
|
||||||
|
QVariantMap data;
|
||||||
|
data["includePurged"] = "true";
|
||||||
|
if (!checkpoint.isEmpty()) {
|
||||||
|
data["checkpoint"] = checkpoint;
|
||||||
|
}
|
||||||
|
QJson::Serializer serializer;
|
||||||
|
QByteArray json = serializer.serialize(data);
|
||||||
|
|
||||||
|
QNetworkRequest request(url);
|
||||||
|
AddAuthorizationHeader(&request);
|
||||||
|
QNetworkReply* reply = network_->post(request, json);
|
||||||
|
NewClosure(reply, SIGNAL(finished()), this,
|
||||||
|
SLOT(RequestChangesFinished(QNetworkReply*)), reply);
|
||||||
|
}
|
||||||
|
|
||||||
|
void AmazonCloudDrive::RequestChangesFinished(QNetworkReply* reply) {
|
||||||
|
reply->deleteLater();
|
||||||
|
|
||||||
|
QByteArray data = reply->readAll();
|
||||||
|
QBuffer buffer(&data);
|
||||||
|
buffer.open(QIODevice::ReadOnly);
|
||||||
|
|
||||||
|
QJson::Parser parser;
|
||||||
|
QVariantMap response = parser.parse(&buffer).toMap();
|
||||||
|
|
||||||
|
QString checkpoint = response["checkpoint"].toString();
|
||||||
|
QSettings settings;
|
||||||
|
settings.beginGroup(kSettingsGroup);
|
||||||
|
settings.setValue("checkpoint", checkpoint);
|
||||||
|
|
||||||
|
QVariantList nodes = response["nodes"].toList();
|
||||||
|
for (const QVariant& n : nodes) {
|
||||||
|
QVariantMap node = n.toMap();
|
||||||
|
if (node["kind"].toString() == "FOLDER") {
|
||||||
|
// Skip directories.
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
QUrl url;
|
||||||
|
url.setScheme("amazonclouddrive");
|
||||||
|
url.setPath(node["id"].toString());
|
||||||
|
|
||||||
|
QString status = node["status"].toString();
|
||||||
|
if (status == "PURGED") {
|
||||||
|
// Remove no longer available files.
|
||||||
|
Song song = library_backend_->GetSongByUrl(url);
|
||||||
|
if (song.is_valid()) {
|
||||||
|
library_backend_->DeleteSongs(SongList() << song);
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (status != "AVAILABLE") {
|
||||||
|
// Ignore any other statuses.
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
QVariantMap content_properties = node["contentProperties"].toMap();
|
||||||
|
QString mime_type = content_properties["contentType"].toString();
|
||||||
|
|
||||||
|
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());
|
||||||
|
|
||||||
|
MaybeAddFileToDatabase(song, mime_type, content_url, QString("Bearer %1").arg(access_token_));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// The API potentially returns a second JSON dictionary appended with a
|
||||||
|
// newline at the end of the response with {"end": true} indicating that our
|
||||||
|
// client is up to date with the latest changes.
|
||||||
|
const int last_newline_index = data.lastIndexOf('\n');
|
||||||
|
QByteArray last_line = data.mid(last_newline_index);
|
||||||
|
QVariantMap end_json = parser.parse(last_line).toMap();
|
||||||
|
if (end_json.contains("end") && end_json["end"].toBool()) {
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
RequestChanges(checkpoint);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void AmazonCloudDrive::AddAuthorizationHeader(QNetworkRequest* request) {
|
||||||
|
request->setRawHeader("Authorization",
|
||||||
|
QString("Bearer %1").arg(access_token_).toUtf8());
|
||||||
|
}
|
71
src/internet/amazon/amazonclouddrive.h
Normal file
71
src/internet/amazon/amazonclouddrive.h
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
/* This file is part of Clementine.
|
||||||
|
Copyright 2015, 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_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(const QString& checkpoint);
|
||||||
|
void AddAuthorizationHeader(QNetworkRequest* request);
|
||||||
|
void EnsureConnected();
|
||||||
|
|
||||||
|
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_
|
80
src/internet/amazon/amazonsettingspage.cpp
Normal file
80
src/internet/amazon/amazonsettingspage.cpp
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
/* This file is part of Clementine.
|
||||||
|
Copyright 2015, 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 "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()));
|
||||||
|
connect(service_, SIGNAL(Connected()), SLOT(Connected()));
|
||||||
|
|
||||||
|
dialog()->installEventFilter(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
AmazonSettingsPage::~AmazonSettingsPage() { delete ui_; }
|
||||||
|
|
||||||
|
void AmazonSettingsPage::Load() {
|
||||||
|
QSettings s;
|
||||||
|
s.beginGroup(AmazonCloudDrive::kSettingsGroup);
|
||||||
|
|
||||||
|
const QString token = s.value("refresh_token").toString();
|
||||||
|
|
||||||
|
if (!token.isEmpty()) {
|
||||||
|
ui_->login_state->SetLoggedIn(LoginStateWidget::LoggedIn);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void AmazonSettingsPage::Save() {
|
||||||
|
QSettings s;
|
||||||
|
s.beginGroup(AmazonCloudDrive::kSettingsGroup);
|
||||||
|
}
|
||||||
|
|
||||||
|
void AmazonSettingsPage::LoginClicked() {
|
||||||
|
service_->Connect();
|
||||||
|
ui_->login_button->setEnabled(false);
|
||||||
|
ui_->login_state->SetLoggedIn(LoginStateWidget::LoginInProgress);
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
void AmazonSettingsPage::Connected() {
|
||||||
|
ui_->login_state->SetLoggedIn(LoginStateWidget::LoggedIn);
|
||||||
|
}
|
53
src/internet/amazon/amazonsettingspage.h
Normal file
53
src/internet/amazon/amazonsettingspage.h
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
/* This file is part of Clementine.
|
||||||
|
Copyright 2015, 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_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();
|
||||||
|
void Connected();
|
||||||
|
|
||||||
|
private:
|
||||||
|
Ui_AmazonSettingsPage* ui_;
|
||||||
|
|
||||||
|
AmazonCloudDrive* service_;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // INTERNET_AMAZON_AMAZONSETTINGSPAGE_H_
|
110
src/internet/amazon/amazonsettingspage.ui
Normal file
110
src/internet/amazon/amazonsettingspage.ui
Normal 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>
|
28
src/internet/amazon/amazonurlhandler.cpp
Normal file
28
src/internet/amazon/amazonurlhandler.cpp
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
/* This file is part of Clementine.
|
||||||
|
Copyright 2015, 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 "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));
|
||||||
|
}
|
39
src/internet/amazon/amazonurlhandler.h
Normal file
39
src/internet/amazon/amazonurlhandler.h
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
/* This file is part of Clementine.
|
||||||
|
Copyright 2015, 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_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_
|
@ -64,6 +64,9 @@
|
|||||||
#ifdef HAVE_SEAFILE
|
#ifdef HAVE_SEAFILE
|
||||||
#include "internet/seafile/seafileservice.h"
|
#include "internet/seafile/seafileservice.h"
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef HAVE_AMAZON_CLOUD_DRIVE
|
||||||
|
#include "internet/amazon/amazonclouddrive.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
using smart_playlists::Generator;
|
using smart_playlists::Generator;
|
||||||
using smart_playlists::GeneratorMimeData;
|
using smart_playlists::GeneratorMimeData;
|
||||||
@ -117,6 +120,9 @@ InternetModel::InternetModel(Application* app, QObject* parent)
|
|||||||
#ifdef HAVE_VK
|
#ifdef HAVE_VK
|
||||||
AddService(new VkService(app, this));
|
AddService(new VkService(app, this));
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef HAVE_AMAZON_CLOUD_DRIVE
|
||||||
|
AddService(new AmazonCloudDrive(app, this));
|
||||||
|
#endif
|
||||||
|
|
||||||
invisibleRootItem()->sortChildren(0, Qt::AscendingOrder);
|
invisibleRootItem()->sortChildren(0, Qt::AscendingOrder);
|
||||||
UpdateServices();
|
UpdateServices();
|
||||||
|
@ -821,7 +821,7 @@ MainWindow::MainWindow(Application* app, SystemTrayIcon* tray_icon, OSD* osd,
|
|||||||
connect(ui_->action_kittens, SIGNAL(toggled(bool)), app_->network_remote(),
|
connect(ui_->action_kittens, SIGNAL(toggled(bool)), app_->network_remote(),
|
||||||
SLOT(EnableKittens(bool)));
|
SLOT(EnableKittens(bool)));
|
||||||
// Hide the console
|
// 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());
|
NowPlayingWidgetPositionChanged(ui_->now_playing->show_above_status_bar());
|
||||||
|
|
||||||
// Load theme
|
// Load theme
|
||||||
|
@ -461,6 +461,7 @@
|
|||||||
<addaction name="action_hypnotoad"/>
|
<addaction name="action_hypnotoad"/>
|
||||||
<addaction name="action_enterprise"/>
|
<addaction name="action_enterprise"/>
|
||||||
<addaction name="action_kittens"/>
|
<addaction name="action_kittens"/>
|
||||||
|
<addaction name="action_console"/>
|
||||||
<addaction name="separator"/>
|
<addaction name="separator"/>
|
||||||
</widget>
|
</widget>
|
||||||
<widget class="QMenu" name="menu_tools">
|
<widget class="QMenu" name="menu_tools">
|
||||||
|
@ -84,6 +84,10 @@
|
|||||||
#include "internet/seafile/seafilesettingspage.h"
|
#include "internet/seafile/seafilesettingspage.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef HAVE_AMAZON_CLOUD_DRIVE
|
||||||
|
#include "internet/amazon/amazonsettingspage.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
#include <QAbstractButton>
|
#include <QAbstractButton>
|
||||||
#include <QDesktopWidget>
|
#include <QDesktopWidget>
|
||||||
#include <QPainter>
|
#include <QPainter>
|
||||||
@ -193,6 +197,10 @@ SettingsDialog::SettingsDialog(Application* app, BackgroundStreams* streams,
|
|||||||
AddPage(Page_Seafile, new SeafileSettingsPage(this), providers);
|
AddPage(Page_Seafile, new SeafileSettingsPage(this), providers);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef HAVE_AMAZON_CLOUD_DRIVE
|
||||||
|
AddPage(Page_AmazonCloudDrive, new AmazonSettingsPage(this), providers);
|
||||||
|
#endif
|
||||||
|
|
||||||
AddPage(Page_Magnatune, new MagnatuneSettingsPage(this), providers);
|
AddPage(Page_Magnatune, new MagnatuneSettingsPage(this), providers);
|
||||||
AddPage(Page_DigitallyImported, new DigitallyImportedSettingsPage(this),
|
AddPage(Page_DigitallyImported, new DigitallyImportedSettingsPage(this),
|
||||||
providers);
|
providers);
|
||||||
|
@ -86,7 +86,8 @@ class SettingsDialog : public QDialog {
|
|||||||
Page_Box,
|
Page_Box,
|
||||||
Page_Vk,
|
Page_Vk,
|
||||||
Page_Seafile,
|
Page_Seafile,
|
||||||
Page_InternetShow
|
Page_InternetShow,
|
||||||
|
Page_AmazonCloudDrive,
|
||||||
};
|
};
|
||||||
|
|
||||||
enum Role { Role_IsSeparator = Qt::UserRole };
|
enum Role { Role_IsSeparator = Qt::UserRole };
|
||||||
|
Loading…
x
Reference in New Issue
Block a user