2012-11-29 18:19:41 +01:00
|
|
|
#include "dropboxservice.h"
|
|
|
|
|
2012-11-29 20:18:08 +01:00
|
|
|
#include <QFileInfo>
|
|
|
|
|
2012-11-29 18:48:49 +01:00
|
|
|
#include <qjson/parser.h>
|
|
|
|
|
2012-11-29 20:18:08 +01:00
|
|
|
#include "core/application.h"
|
2012-11-29 18:43:56 +01:00
|
|
|
#include "core/logging.h"
|
|
|
|
#include "core/network.h"
|
2012-11-29 20:18:08 +01:00
|
|
|
#include "core/player.h"
|
2012-11-29 20:36:13 +01:00
|
|
|
#include "core/utilities.h"
|
2012-11-29 20:18:08 +01:00
|
|
|
#include "core/waitforsignal.h"
|
2012-11-29 18:19:41 +01:00
|
|
|
#include "internet/dropboxauthenticator.h"
|
2012-11-29 20:18:08 +01:00
|
|
|
#include "internet/dropboxurlhandler.h"
|
2012-11-29 20:36:13 +01:00
|
|
|
#include "library/librarybackend.h"
|
|
|
|
|
|
|
|
using Utilities::ParseRFC822DateTime;
|
2012-11-29 18:19:41 +01:00
|
|
|
|
|
|
|
const char* DropboxService::kServiceName = "Dropbox";
|
|
|
|
const char* DropboxService::kSettingsGroup = "Dropbox";
|
|
|
|
|
|
|
|
namespace {
|
|
|
|
|
|
|
|
static const char* kServiceId = "dropbox";
|
|
|
|
|
2012-11-29 18:43:56 +01:00
|
|
|
static const char* kMetadataEndpoint =
|
|
|
|
"https://api.dropbox.com/1/metadata/dropbox/";
|
2012-11-29 20:18:08 +01:00
|
|
|
static const char* kMediaEndpoint =
|
|
|
|
"https://api.dropbox.com/1/media/dropbox/";
|
2012-11-29 18:43:56 +01:00
|
|
|
|
2012-11-29 18:19:41 +01:00
|
|
|
} // namespace
|
|
|
|
|
|
|
|
DropboxService::DropboxService(Application* app, InternetModel* parent)
|
|
|
|
: CloudFileService(
|
|
|
|
app, parent,
|
|
|
|
kServiceName, kServiceId,
|
|
|
|
QIcon(":/providers/dropbox.png"),
|
2012-11-29 18:43:56 +01:00
|
|
|
SettingsDialog::Page_Dropbox),
|
|
|
|
network_(new NetworkAccessManager(this)) {
|
|
|
|
QSettings settings;
|
|
|
|
settings.beginGroup(kSettingsGroup);
|
|
|
|
access_token_ = settings.value("access_token").toString();
|
|
|
|
access_token_secret_ = settings.value("access_token_secret").toString();
|
2012-11-29 20:18:08 +01:00
|
|
|
app->player()->RegisterUrlHandler(new DropboxUrlHandler(this, this));
|
2012-11-29 18:19:41 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
bool DropboxService::has_credentials() const {
|
|
|
|
return !access_token_.isEmpty();
|
|
|
|
}
|
|
|
|
|
|
|
|
void DropboxService::Connect() {
|
2012-11-29 18:43:56 +01:00
|
|
|
if (has_credentials()) {
|
|
|
|
RequestFileList("");
|
2012-11-29 18:19:41 +01:00
|
|
|
} else {
|
|
|
|
ShowSettingsDialog();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void DropboxService::AuthenticationFinished(DropboxAuthenticator* authenticator) {
|
|
|
|
authenticator->deleteLater();
|
|
|
|
|
2012-11-29 18:43:56 +01:00
|
|
|
access_token_ = authenticator->access_token();
|
|
|
|
access_token_secret_ = authenticator->access_token_secret();
|
|
|
|
|
2012-11-29 18:19:41 +01:00
|
|
|
QSettings settings;
|
|
|
|
settings.beginGroup(kSettingsGroup);
|
|
|
|
|
2012-11-29 18:43:56 +01:00
|
|
|
settings.setValue("access_token", access_token_);
|
|
|
|
settings.setValue("access_token_secret", access_token_secret_);
|
2012-11-29 18:19:41 +01:00
|
|
|
settings.setValue("name", authenticator->name());
|
|
|
|
|
|
|
|
emit Connected();
|
2012-11-29 18:43:56 +01:00
|
|
|
|
|
|
|
RequestFileList("");
|
|
|
|
}
|
|
|
|
|
|
|
|
QByteArray DropboxService::GenerateAuthorisationHeader() {
|
|
|
|
return DropboxAuthenticator::GenerateAuthorisationHeader(
|
|
|
|
access_token_,
|
|
|
|
access_token_secret_);
|
|
|
|
}
|
|
|
|
|
|
|
|
void DropboxService::RequestFileList(const QString& path) {
|
|
|
|
QUrl url(QString(kMetadataEndpoint) + path);
|
|
|
|
QNetworkRequest request(url);
|
|
|
|
request.setRawHeader("Authorization", GenerateAuthorisationHeader());
|
|
|
|
|
|
|
|
QNetworkReply* reply = network_->get(request);
|
|
|
|
NewClosure(reply, SIGNAL(finished()),
|
|
|
|
this, SLOT(RequestFileListFinished(QNetworkReply*)), reply);
|
|
|
|
}
|
|
|
|
|
2012-11-29 18:48:49 +01:00
|
|
|
namespace {
|
|
|
|
|
|
|
|
bool IsSupportedMimeType(const QString& mime_type) {
|
|
|
|
return mime_type == "audio/ogg" ||
|
|
|
|
mime_type == "audio/mpeg";
|
|
|
|
}
|
|
|
|
|
|
|
|
} // namespace
|
|
|
|
|
2012-11-29 18:43:56 +01:00
|
|
|
void DropboxService::RequestFileListFinished(QNetworkReply* reply) {
|
|
|
|
reply->deleteLater();
|
2012-11-29 18:48:49 +01:00
|
|
|
|
|
|
|
QJson::Parser parser;
|
|
|
|
QVariantMap response = parser.parse(reply).toMap();
|
|
|
|
QVariantList contents = response["contents"].toList();
|
|
|
|
foreach (const QVariant& c, contents) {
|
|
|
|
QVariantMap item = c.toMap();
|
|
|
|
const bool directory = item["is_dir"].toBool();
|
|
|
|
if (directory) {
|
|
|
|
RequestFileList(item["path"].toString());
|
2012-11-29 20:36:13 +01:00
|
|
|
} else {
|
2012-11-29 20:18:08 +01:00
|
|
|
QUrl url;
|
|
|
|
url.setScheme("dropbox");
|
|
|
|
url.setPath(item["path"].toString());
|
2012-11-29 20:36:13 +01:00
|
|
|
MaybeAddFileToDatabase(url, item);
|
2012-11-29 18:48:49 +01:00
|
|
|
}
|
|
|
|
}
|
2012-11-29 18:19:41 +01:00
|
|
|
}
|
2012-11-29 20:18:08 +01:00
|
|
|
|
2012-11-29 20:36:13 +01:00
|
|
|
void DropboxService::MaybeAddFileToDatabase(
|
|
|
|
const QUrl& url, const QVariantMap& file) {
|
|
|
|
if (!IsSupportedMimeType(file["mime_type"].toString())) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
Song song = library_backend_->GetSongByUrl(url);
|
|
|
|
if (song.is_valid()) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
QNetworkReply* reply = FetchContentUrl(url);
|
|
|
|
NewClosure(reply, SIGNAL(finished()),
|
|
|
|
this, SLOT(FetchContentUrlFinished(QNetworkReply*, QVariantMap)),
|
|
|
|
reply, file);
|
|
|
|
}
|
|
|
|
|
2012-11-29 20:18:08 +01:00
|
|
|
QNetworkReply* DropboxService::FetchContentUrl(const QUrl& url) {
|
|
|
|
QUrl request_url(QString(kMediaEndpoint) + url.path());
|
|
|
|
QNetworkRequest request(request_url);
|
|
|
|
request.setRawHeader("Authorization", GenerateAuthorisationHeader());
|
|
|
|
return network_->post(request, QByteArray());
|
|
|
|
}
|
|
|
|
|
|
|
|
void DropboxService::FetchContentUrlFinished(
|
|
|
|
QNetworkReply* reply, const QVariantMap& data) {
|
|
|
|
reply->deleteLater();
|
|
|
|
QJson::Parser parser;
|
|
|
|
QVariantMap response = parser.parse(reply).toMap();
|
|
|
|
QFileInfo info(data["path"].toString());
|
|
|
|
TagReaderClient::ReplyType* tag_reply = app_->tag_reader_client()->ReadCloudFile(
|
|
|
|
response["url"].toUrl(),
|
|
|
|
info.fileName(),
|
|
|
|
data["bytes"].toInt(),
|
|
|
|
data["mime_type"].toString(),
|
|
|
|
QString::null);
|
|
|
|
NewClosure(tag_reply, SIGNAL(Finished(bool)),
|
|
|
|
this, SLOT(ReadTagsFinished(TagReaderClient::ReplyType*,QVariantMap)),
|
2012-11-29 20:36:13 +01:00
|
|
|
tag_reply, data);
|
2012-11-29 20:18:08 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void DropboxService::ReadTagsFinished(
|
|
|
|
TagReaderClient::ReplyType* reply, const QVariantMap& file) {
|
|
|
|
qLog(Debug) << reply->message().DebugString().c_str();
|
2012-11-29 20:36:13 +01:00
|
|
|
|
|
|
|
const auto& message = reply->message().read_cloud_file_response();
|
|
|
|
if (!message.has_metadata() ||
|
|
|
|
!message.metadata().filesize()) {
|
|
|
|
qLog(Debug) << "Failed to tag:" << file["path"].toString();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
Song song;
|
|
|
|
song.InitFromProtobuf(message.metadata());
|
|
|
|
song.set_directory_id(0);
|
|
|
|
song.set_etag(file["rev"].toString());
|
|
|
|
song.set_mtime(ParseRFC822DateTime(file["modified"].toString()).toTime_t());
|
|
|
|
QUrl url;
|
|
|
|
url.setScheme("dropbox");
|
|
|
|
url.setPath(file["path"].toString());
|
|
|
|
song.set_url(url);
|
|
|
|
if (song.title().isEmpty()) {
|
|
|
|
QFileInfo info(file["path"].toString());
|
|
|
|
song.set_title(info.fileName());
|
|
|
|
}
|
|
|
|
|
|
|
|
qLog(Debug) << "Adding song to db:" << song.title();
|
|
|
|
library_backend_->AddOrUpdateSongs(SongList() << song);
|
2012-11-29 20:18:08 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
QUrl DropboxService::GetStreamingUrlFromSongId(const QUrl& url) {
|
|
|
|
QNetworkReply* reply = FetchContentUrl(url);
|
|
|
|
WaitForSignal(reply, SIGNAL(finished()));
|
|
|
|
|
|
|
|
QJson::Parser parser;
|
|
|
|
QVariantMap response = parser.parse(reply).toMap();
|
|
|
|
return response["url"].toUrl();
|
|
|
|
}
|