2012-11-27 18:35:06 +01:00
|
|
|
#include "ubuntuoneservice.h"
|
|
|
|
|
|
|
|
#include <QDateTime>
|
2012-11-27 19:56:47 +01:00
|
|
|
#include <QSettings>
|
2012-11-28 14:43:03 +01:00
|
|
|
#include <QSortFilterProxyModel>
|
2012-11-27 18:35:06 +01:00
|
|
|
|
|
|
|
#include <qjson/parser.h>
|
|
|
|
|
|
|
|
#include "core/application.h"
|
|
|
|
#include "core/closure.h"
|
2012-11-28 14:43:03 +01:00
|
|
|
#include "core/database.h"
|
2012-11-27 18:35:06 +01:00
|
|
|
#include "core/logging.h"
|
2012-11-28 14:43:03 +01:00
|
|
|
#include "core/mergedproxymodel.h"
|
2012-11-27 18:35:06 +01:00
|
|
|
#include "core/network.h"
|
|
|
|
#include "core/player.h"
|
|
|
|
#include "core/timeconstants.h"
|
|
|
|
#include "core/utilities.h"
|
2012-11-28 14:43:03 +01:00
|
|
|
#include "globalsearch/globalsearch.h"
|
|
|
|
#include "globalsearch/librarysearchprovider.h"
|
2012-11-27 18:35:06 +01:00
|
|
|
#include "internet/internetmodel.h"
|
|
|
|
#include "internet/ubuntuoneauthenticator.h"
|
|
|
|
#include "internet/ubuntuoneurlhandler.h"
|
2012-11-28 14:43:03 +01:00
|
|
|
#include "library/librarybackend.h"
|
2012-11-28 15:26:09 +01:00
|
|
|
#include "playlist/playlist.h"
|
|
|
|
#include "ui/iconloader.h"
|
2012-11-27 18:35:06 +01:00
|
|
|
|
|
|
|
const char* UbuntuOneService::kServiceName = "Ubuntu One";
|
|
|
|
const char* UbuntuOneService::kSettingsGroup = "Ubuntu One";
|
|
|
|
|
|
|
|
namespace {
|
|
|
|
static const char* kFileStorageEndpoint =
|
2012-11-28 15:05:06 +01:00
|
|
|
"https://one.ubuntu.com/api/file_storage/v1";
|
2012-11-29 16:07:05 +01:00
|
|
|
static const char* kVolumesEndpoint =
|
|
|
|
"https://one.ubuntu.com/api/file_storage/v1/volumes";
|
2012-11-27 18:35:06 +01:00
|
|
|
static const char* kContentRoot = "https://files.one.ubuntu.com";
|
2012-11-28 16:13:10 +01:00
|
|
|
static const char* kServiceId = "ubuntu_one";
|
2012-11-27 18:35:06 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
UbuntuOneService::UbuntuOneService(Application* app, InternetModel* parent)
|
2012-11-28 16:13:10 +01:00
|
|
|
: CloudFileService(
|
|
|
|
app, parent,
|
|
|
|
kServiceName, kServiceId,
|
|
|
|
QIcon(":/providers/ubuntuone.png"),
|
2012-12-04 17:22:09 +01:00
|
|
|
SettingsDialog::Page::UbuntuOne) {
|
2012-11-28 16:13:10 +01:00
|
|
|
app_->player()->RegisterUrlHandler(new UbuntuOneUrlHandler(this, this));
|
2012-11-27 18:35:06 +01:00
|
|
|
|
2012-11-27 19:56:47 +01:00
|
|
|
QSettings s;
|
|
|
|
s.beginGroup(kSettingsGroup);
|
|
|
|
if (s.contains("consumer_key")) {
|
|
|
|
consumer_key_ = s.value("consumer_key").toString();
|
|
|
|
consumer_secret_ = s.value("consumer_secret").toString();
|
|
|
|
token_ = s.value("token").toString();
|
|
|
|
token_secret_ = s.value("token_secret").toString();
|
2012-11-28 16:13:10 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bool UbuntuOneService::has_credentials() const {
|
|
|
|
return !consumer_key_.isEmpty();
|
|
|
|
}
|
2012-11-27 19:56:47 +01:00
|
|
|
|
2012-11-28 16:13:10 +01:00
|
|
|
void UbuntuOneService::Connect() {
|
|
|
|
if (has_credentials()) {
|
2012-11-29 16:07:05 +01:00
|
|
|
RequestVolumeList();
|
2012-11-28 15:09:59 +01:00
|
|
|
} else {
|
|
|
|
ShowSettingsDialog();
|
2012-11-27 19:56:47 +01:00
|
|
|
}
|
2012-11-27 18:35:06 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
QByteArray UbuntuOneService::GenerateAuthorisationHeader() {
|
2012-11-27 19:56:47 +01:00
|
|
|
return UbuntuOneAuthenticator::GenerateAuthorisationHeader(
|
|
|
|
consumer_key_,
|
|
|
|
consumer_secret_,
|
|
|
|
token_,
|
|
|
|
token_secret_);
|
2012-11-27 18:35:06 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void UbuntuOneService::AuthenticationFinished(
|
|
|
|
UbuntuOneAuthenticator* authenticator) {
|
|
|
|
authenticator->deleteLater();
|
|
|
|
|
|
|
|
consumer_key_ = authenticator->consumer_key();
|
|
|
|
consumer_secret_ = authenticator->consumer_secret();
|
|
|
|
token_ = authenticator->token();
|
|
|
|
token_secret_ = authenticator->token_secret();
|
|
|
|
|
2012-11-27 19:56:47 +01:00
|
|
|
QSettings s;
|
|
|
|
s.beginGroup(kSettingsGroup);
|
|
|
|
s.setValue("consumer_key", consumer_key_);
|
|
|
|
s.setValue("consumer_secret", consumer_secret_);
|
|
|
|
s.setValue("token", token_);
|
|
|
|
s.setValue("token_secret", token_secret_);
|
2012-11-27 18:35:06 +01:00
|
|
|
|
2012-11-29 16:07:05 +01:00
|
|
|
RequestVolumeList();
|
|
|
|
}
|
|
|
|
|
|
|
|
QNetworkReply* UbuntuOneService::SendRequest(const QUrl& url) {
|
|
|
|
QNetworkRequest request(url);
|
|
|
|
request.setRawHeader("Authorization", GenerateAuthorisationHeader());
|
|
|
|
request.setRawHeader("Accept", "application/json");
|
|
|
|
|
|
|
|
return network_->get(request);
|
|
|
|
}
|
|
|
|
|
|
|
|
void UbuntuOneService::RequestVolumeList() {
|
|
|
|
QUrl volumes_url(kVolumesEndpoint);
|
|
|
|
QNetworkReply* reply = SendRequest(volumes_url);
|
|
|
|
NewClosure(reply, SIGNAL(finished()),
|
|
|
|
this, SLOT(VolumeListRequestFinished(QNetworkReply*)), reply);
|
|
|
|
}
|
|
|
|
|
|
|
|
void UbuntuOneService::VolumeListRequestFinished(QNetworkReply* reply) {
|
|
|
|
reply->deleteLater();
|
|
|
|
|
|
|
|
QJson::Parser parser;
|
|
|
|
QVariantList result = parser.parse(reply).toList();
|
|
|
|
foreach (const QVariant& v, result) {
|
|
|
|
RequestFileList(v.toMap()["node_path"].toString());
|
|
|
|
}
|
2012-11-27 18:35:06 +01:00
|
|
|
}
|
|
|
|
|
2012-11-28 15:05:06 +01:00
|
|
|
void UbuntuOneService::RequestFileList(const QString& path) {
|
|
|
|
QUrl files_url(QString(kFileStorageEndpoint) + path);
|
2012-11-27 18:35:06 +01:00
|
|
|
files_url.addQueryItem("include_children", "true");
|
|
|
|
|
2012-11-28 15:05:06 +01:00
|
|
|
qLog(Debug) << "Sending files request" << files_url;
|
2012-11-29 16:07:05 +01:00
|
|
|
QNetworkReply* files_reply = SendRequest(files_url);
|
2012-11-27 18:35:06 +01:00
|
|
|
NewClosure(files_reply, SIGNAL(finished()),
|
|
|
|
this, SLOT(FileListRequestFinished(QNetworkReply*)), files_reply);
|
|
|
|
}
|
|
|
|
|
|
|
|
void UbuntuOneService::FileListRequestFinished(QNetworkReply* reply) {
|
2012-11-29 16:07:05 +01:00
|
|
|
reply->deleteLater();
|
2012-11-27 18:35:06 +01:00
|
|
|
QJson::Parser parser;
|
2012-11-29 16:07:05 +01:00
|
|
|
QVariantMap result = parser.parse(reply).toMap();
|
2012-11-27 18:35:06 +01:00
|
|
|
|
|
|
|
QVariantList children = result["children"].toList();
|
2012-11-28 17:34:54 +01:00
|
|
|
foreach (const QVariant& c, children) {
|
2012-11-27 18:35:06 +01:00
|
|
|
QVariantMap child = c.toMap();
|
2012-11-28 15:05:06 +01:00
|
|
|
if (child["kind"].toString() == "file") {
|
|
|
|
MaybeAddFileToDatabase(child);
|
|
|
|
} else {
|
|
|
|
RequestFileList(child["resource_path"].toString());
|
|
|
|
}
|
2012-11-28 14:43:03 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-11-28 17:32:21 +01:00
|
|
|
namespace {
|
|
|
|
|
|
|
|
QString GuessMimeTypeForFile(const QString& filename) {
|
|
|
|
if (filename.endsWith(".mp3")) {
|
|
|
|
return "audio/mpeg";
|
|
|
|
} else if (filename.endsWith(".m4a")) {
|
|
|
|
return "audio/mpeg";
|
|
|
|
} else if (filename.endsWith(".ogg")) {
|
|
|
|
return "application/ogg";
|
|
|
|
} else if (filename.endsWith(".flac")) {
|
|
|
|
return "application/x-flac";
|
|
|
|
}
|
|
|
|
return QString::null;
|
|
|
|
}
|
|
|
|
|
|
|
|
} // namespace
|
|
|
|
|
2012-11-28 14:43:03 +01:00
|
|
|
void UbuntuOneService::MaybeAddFileToDatabase(const QVariantMap& file) {
|
2012-11-28 17:32:21 +01:00
|
|
|
const QString content_path = file["content_path"].toString();
|
|
|
|
const QString filename = file["path"].toString().mid(1);
|
|
|
|
const QString mime_type = GuessMimeTypeForFile(filename);
|
|
|
|
if (mime_type.isNull()) {
|
|
|
|
// Unknown file type.
|
|
|
|
// Potentially, we could do a HEAD request to see what Ubuntu One thinks
|
|
|
|
// the Content-Type is, probably not worth it though.
|
|
|
|
return;
|
|
|
|
}
|
2012-11-27 18:35:06 +01:00
|
|
|
|
2012-11-28 14:43:03 +01:00
|
|
|
QUrl service_url;
|
|
|
|
service_url.setScheme("ubuntuonefile");
|
|
|
|
service_url.setPath(content_path);
|
|
|
|
Song song = library_backend_->GetSongByUrl(service_url);
|
|
|
|
if (song.is_valid()) {
|
|
|
|
return;
|
|
|
|
}
|
2012-11-27 18:35:06 +01:00
|
|
|
|
2012-11-28 14:43:03 +01:00
|
|
|
QUrl content_url(kContentRoot);
|
|
|
|
content_url.setPath(content_path);
|
|
|
|
|
2012-11-28 16:34:10 +01:00
|
|
|
TagReaderClient::ReplyType* reply = app_->tag_reader_client()->ReadCloudFile(
|
2012-11-28 14:43:03 +01:00
|
|
|
content_url,
|
2012-11-28 17:32:21 +01:00
|
|
|
filename,
|
2012-11-28 14:43:03 +01:00
|
|
|
file["size"].toInt(),
|
2012-11-28 17:32:21 +01:00
|
|
|
mime_type,
|
2012-11-28 14:43:03 +01:00
|
|
|
GenerateAuthorisationHeader());
|
|
|
|
NewClosure(
|
|
|
|
reply, SIGNAL(Finished(bool)),
|
|
|
|
this, SLOT(ReadTagsFinished(TagReaderClient::ReplyType*,QVariantMap, QUrl)),
|
|
|
|
reply, file, service_url);
|
|
|
|
}
|
2012-11-27 18:35:06 +01:00
|
|
|
|
2012-11-28 14:43:03 +01:00
|
|
|
void UbuntuOneService::ReadTagsFinished(
|
|
|
|
TagReaderClient::ReplyType* reply, const QVariantMap& file, const QUrl& url) {
|
|
|
|
qLog(Debug) << reply->message().DebugString().c_str();
|
2012-11-29 14:18:30 +01:00
|
|
|
|
|
|
|
const auto& message = reply->message().read_cloud_file_response();
|
|
|
|
|
|
|
|
if (!message.has_metadata() ||
|
|
|
|
!message.metadata().filesize()) {
|
|
|
|
qLog(Debug) << "Failed to tag:" << url;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2012-11-28 14:43:03 +01:00
|
|
|
Song song;
|
2012-11-28 16:34:10 +01:00
|
|
|
song.InitFromProtobuf(reply->message().read_cloud_file_response().metadata());
|
2012-11-28 14:43:03 +01:00
|
|
|
song.set_directory_id(0);
|
|
|
|
song.set_etag(file["hash"].toString());
|
|
|
|
song.set_mtime(
|
|
|
|
QDateTime::fromString(file["when_changed"].toString(), Qt::ISODate).toTime_t());
|
|
|
|
song.set_ctime(
|
|
|
|
QDateTime::fromString(file["when_created"].toString(), Qt::ISODate).toTime_t());
|
|
|
|
|
|
|
|
song.set_url(url);
|
|
|
|
|
|
|
|
if (song.title().isEmpty()) {
|
|
|
|
song.set_title(file["path"].toString().mid(1));
|
2012-11-27 18:35:06 +01:00
|
|
|
}
|
2012-11-28 14:43:03 +01:00
|
|
|
|
|
|
|
qLog(Debug) << "Adding song to db:" << song.title();
|
|
|
|
library_backend_->AddOrUpdateSongs(SongList() << song);
|
2012-11-27 18:35:06 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
QUrl UbuntuOneService::GetStreamingUrlFromSongId(const QString& song_id) {
|
|
|
|
QUrl url(kContentRoot);
|
|
|
|
url.setPath(song_id);
|
|
|
|
url.setFragment(GenerateAuthorisationHeader());
|
|
|
|
return url;
|
|
|
|
}
|
2012-11-28 15:09:59 +01:00
|
|
|
|
2012-11-28 15:26:09 +01:00
|
|
|
void UbuntuOneService::ShowCoverManager() {
|
|
|
|
if (!cover_manager_) {
|
|
|
|
cover_manager_.reset(new AlbumCoverManager(app_, library_backend_));
|
|
|
|
cover_manager_->Init();
|
|
|
|
connect(cover_manager_.get(), SIGNAL(AddToPlaylist(QMimeData*)),
|
|
|
|
SLOT(AddToPlaylist(QMimeData*)));
|
|
|
|
}
|
|
|
|
cover_manager_->show();
|
|
|
|
}
|
|
|
|
|
|
|
|
void UbuntuOneService::AddToPlaylist(QMimeData* mime) {
|
|
|
|
playlist_manager_->current()->dropMimeData(
|
|
|
|
mime, Qt::CopyAction, -1, 0, QModelIndex());
|
|
|
|
}
|