diff --git a/CMakeLists.txt b/CMakeLists.txt index 97d2b9b3a..94f2bf979 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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 ) diff --git a/data/data.qrc b/data/data.qrc index 162c2f7ff..afd87697b 100644 --- a/data/data.qrc +++ b/data/data.qrc @@ -310,6 +310,7 @@ playstore/uk_generic_rgb_wo_45.png playstore/vi_generic_rgb_wo_45.png providers/amazon.png + providers/amazonclouddrive.png providers/aol.png providers/bbc.png providers/box.png @@ -385,6 +386,7 @@ schema/schema-45.sql schema/schema-46.sql schema/schema-47.sql + schema/schema-48.sql schema/schema-4.sql schema/schema-5.sql schema/schema-6.sql diff --git a/data/providers/amazonclouddrive.png b/data/providers/amazonclouddrive.png new file mode 100644 index 000000000..d3707287a Binary files /dev/null and b/data/providers/amazonclouddrive.png differ diff --git a/data/schema/schema-48.sql b/data/schema/schema-48.sql new file mode 100644 index 000000000..bdff64858 --- /dev/null +++ b/data/schema/schema-48.sql @@ -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; diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 4a80c5994..355b605db 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -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 diff --git a/src/config.h.in b/src/config.h.in index f9279072f..342919fbc 100644 --- a/src/config.h.in +++ b/src/config.h.in @@ -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 diff --git a/src/core/database.cpp b/src/core/database.cpp index e351aa32e..e3bbee56a 100644 --- a/src/core/database.cpp +++ b/src/core/database.cpp @@ -47,7 +47,7 @@ #include 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; diff --git a/src/engines/gstenginepipeline.cpp b/src/engines/gstenginepipeline.cpp index 7f48f1fb2..5887a6dbd 100644 --- a/src/engines/gstenginepipeline.cpp +++ b/src/engines/gstenginepipeline.cpp @@ -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 = diff --git a/src/internet/amazon/amazonclouddrive.cpp b/src/internet/amazon/amazonclouddrive.cpp new file mode 100644 index 000000000..7b2dd1be0 --- /dev/null +++ b/src/internet/amazon/amazonclouddrive.cpp @@ -0,0 +1,246 @@ +/* This file is part of Clementine. + Copyright 2015, John Maguire + + 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 . +*/ + +#include "internet/amazon/amazonclouddrive.h" + +#include + +#include +#include + +#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()); +} diff --git a/src/internet/amazon/amazonclouddrive.h b/src/internet/amazon/amazonclouddrive.h new file mode 100644 index 000000000..49350dee1 --- /dev/null +++ b/src/internet/amazon/amazonclouddrive.h @@ -0,0 +1,71 @@ +/* This file is part of Clementine. + Copyright 2015, John Maguire + + 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 . +*/ + +#ifndef INTERNET_AMAZON_AMAZON_CLOUD_DRIVE_H_ +#define INTERNET_AMAZON_AMAZON_CLOUD_DRIVE_H_ + +#include "internet/core/cloudfileservice.h" + +#include +#include +#include + +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_ diff --git a/src/internet/amazon/amazonsettingspage.cpp b/src/internet/amazon/amazonsettingspage.cpp new file mode 100644 index 000000000..0fd41fcce --- /dev/null +++ b/src/internet/amazon/amazonsettingspage.cpp @@ -0,0 +1,80 @@ +/* This file is part of Clementine. + Copyright 2015, John Maguire + + 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 . +*/ + +#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()) { + 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); +} diff --git a/src/internet/amazon/amazonsettingspage.h b/src/internet/amazon/amazonsettingspage.h new file mode 100644 index 000000000..8ff6b303e --- /dev/null +++ b/src/internet/amazon/amazonsettingspage.h @@ -0,0 +1,53 @@ +/* This file is part of Clementine. + Copyright 2015, John Maguire + + 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 . +*/ + +#ifndef INTERNET_AMAZON_AMAZONSETTINGSPAGE_H_ +#define INTERNET_AMAZON_AMAZONSETTINGSPAGE_H_ + +#include "ui/settingspage.h" + +#include +#include + +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_ diff --git a/src/internet/amazon/amazonsettingspage.ui b/src/internet/amazon/amazonsettingspage.ui new file mode 100644 index 000000000..d496f3424 --- /dev/null +++ b/src/internet/amazon/amazonsettingspage.ui @@ -0,0 +1,110 @@ + + + AmazonSettingsPage + + + + 0 + 0 + 569 + 491 + + + + Amazon + + + + :/providers/amazonclouddrive.png:/providers/amazonclouddrive.png + + + + + + Clementine can play music that you have uploaded to Amazon Cloud Drive + + + true + + + + + + + + + + + 28 + + + 0 + + + 0 + + + + + + + Login + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + Clicking the Login button will open a web browser. You should return to Clementine after you have logged in. + + + true + + + + + + + + + + Qt::Vertical + + + + 20 + 357 + + + + + + + + + LoginStateWidget + QWidget +
widgets/loginstatewidget.h
+ 1 +
+
+ + + + +
diff --git a/src/internet/amazon/amazonurlhandler.cpp b/src/internet/amazon/amazonurlhandler.cpp new file mode 100644 index 000000000..42433a3f1 --- /dev/null +++ b/src/internet/amazon/amazonurlhandler.cpp @@ -0,0 +1,28 @@ +/* This file is part of Clementine. + Copyright 2015, John Maguire + + 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 . +*/ + +#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)); +} diff --git a/src/internet/amazon/amazonurlhandler.h b/src/internet/amazon/amazonurlhandler.h new file mode 100644 index 000000000..807f24732 --- /dev/null +++ b/src/internet/amazon/amazonurlhandler.h @@ -0,0 +1,39 @@ +/* This file is part of Clementine. + Copyright 2015, John Maguire + + 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 . +*/ + +#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_ diff --git a/src/internet/core/internetmodel.cpp b/src/internet/core/internetmodel.cpp index 4783466ee..09754cd5e 100644 --- a/src/internet/core/internetmodel.cpp +++ b/src/internet/core/internetmodel.cpp @@ -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(); diff --git a/src/ui/settingsdialog.cpp b/src/ui/settingsdialog.cpp index b82e00a27..5cfb1bda8 100644 --- a/src/ui/settingsdialog.cpp +++ b/src/ui/settingsdialog.cpp @@ -84,6 +84,10 @@ #include "internet/seafile/seafilesettingspage.h" #endif +#ifdef HAVE_AMAZON_CLOUD_DRIVE +#include "internet/amazon/amazonsettingspage.h" +#endif + #include #include #include @@ -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); diff --git a/src/ui/settingsdialog.h b/src/ui/settingsdialog.h index 2c4a29db6..607ecc0e8 100644 --- a/src/ui/settingsdialog.h +++ b/src/ui/settingsdialog.h @@ -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 };