mirror of
https://github.com/clementine-player/Clementine
synced 2025-01-31 03:27:40 +01:00
Use changes API instead of search API in Google Drive.
This commit is contained in:
parent
b41a2b5308
commit
951cac2ad6
@ -644,7 +644,7 @@ bool TagReaderWorker::ReadCloudFile(const QUrl& download_url,
|
||||
<< stream->cached_bytes();
|
||||
}
|
||||
|
||||
if (tag->tag()) {
|
||||
if (tag->tag() && !tag->tag()->isEmpty()) {
|
||||
song->set_title(tag->tag()->title().toCString(true));
|
||||
song->set_artist(tag->tag()->artist().toCString(true));
|
||||
song->set_album(tag->tag()->album().toCString(true));
|
||||
|
@ -29,8 +29,8 @@ using namespace google_drive;
|
||||
const char* File::kFolderMimeType = "application/vnd.google-apps.folder";
|
||||
|
||||
namespace {
|
||||
static const char* kGoogleDriveFiles = "https://www.googleapis.com/drive/v2/files";
|
||||
static const char* kGoogleDriveFile = "https://www.googleapis.com/drive/v2/files/%1";
|
||||
static const char* kGoogleDriveChanges = "https://www.googleapis.com/drive/v2/changes";
|
||||
}
|
||||
|
||||
QStringList File::parent_ids() const {
|
||||
@ -54,18 +54,18 @@ ConnectResponse::ConnectResponse(QObject* parent)
|
||||
{
|
||||
}
|
||||
|
||||
ListFilesResponse::ListFilesResponse(const QString& query, QObject* parent)
|
||||
: QObject(parent),
|
||||
query_(query)
|
||||
{
|
||||
}
|
||||
|
||||
GetFileResponse::GetFileResponse(const QString& file_id, QObject* parent)
|
||||
: QObject(parent),
|
||||
file_id_(file_id)
|
||||
{
|
||||
}
|
||||
|
||||
ListChangesResponse::ListChangesResponse(const QString& cursor, QObject* parent)
|
||||
: QObject(parent),
|
||||
cursor_(cursor)
|
||||
{
|
||||
}
|
||||
|
||||
Client::Client(QObject* parent)
|
||||
: QObject(parent),
|
||||
network_(new NetworkAccessManager(this))
|
||||
@ -104,61 +104,6 @@ void Client::AddAuthorizationHeader(QNetworkRequest* request) const {
|
||||
"Authorization", QString("Bearer %1").arg(access_token_).toUtf8());
|
||||
}
|
||||
|
||||
ListFilesResponse* Client::ListFiles(const QString& query) {
|
||||
ListFilesResponse* ret = new ListFilesResponse(query, this);
|
||||
MakeListFilesRequest(ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void Client::MakeListFilesRequest(ListFilesResponse* response, const QString& page_token) {
|
||||
QUrl url = QUrl(kGoogleDriveFiles);
|
||||
|
||||
if (!response->query_.isEmpty()) {
|
||||
url.addQueryItem("q", response->query_);
|
||||
}
|
||||
|
||||
if (!page_token.isEmpty()) {
|
||||
url.addQueryItem("pageToken", page_token);
|
||||
}
|
||||
|
||||
QNetworkRequest request = QNetworkRequest(url);
|
||||
AddAuthorizationHeader(&request);
|
||||
|
||||
QNetworkReply* reply = network_->get(request);
|
||||
NewClosure(reply, SIGNAL(finished()),
|
||||
this, SLOT(ListFilesFinished(ListFilesResponse*, QNetworkReply*)),
|
||||
response, reply);
|
||||
}
|
||||
|
||||
void Client::ListFilesFinished(ListFilesResponse* response, QNetworkReply* reply) {
|
||||
reply->deleteLater();
|
||||
|
||||
// Parse the response
|
||||
QJson::Parser parser;
|
||||
bool ok = false;
|
||||
QVariantMap result = parser.parse(reply, &ok).toMap();
|
||||
if (!ok) {
|
||||
qLog(Error) << "Failed to request files from Google Drive";
|
||||
emit response->Finished();
|
||||
return;
|
||||
}
|
||||
|
||||
// Emit the FilesFound signal for the files in the response.
|
||||
FileList files;
|
||||
foreach (const QVariant& v, result["items"].toList()) {
|
||||
files << File(v.toMap());
|
||||
}
|
||||
|
||||
emit response->FilesFound(files);
|
||||
|
||||
// Get the next page of results if there is one.
|
||||
if (result.contains("nextPageToken")) {
|
||||
MakeListFilesRequest(response, result["nextPageToken"].toString());
|
||||
} else {
|
||||
emit response->Finished();
|
||||
}
|
||||
}
|
||||
|
||||
GetFileResponse* Client::GetFile(const QString& file_id) {
|
||||
GetFileResponse* ret = new GetFileResponse(file_id, this);
|
||||
|
||||
@ -194,6 +139,75 @@ void Client::GetFileFinished(GetFileResponse* response, QNetworkReply* reply) {
|
||||
emit response->Finished();
|
||||
}
|
||||
|
||||
ListChangesResponse* Client::ListChanges(const QString& cursor) {
|
||||
ListChangesResponse* ret = new ListChangesResponse(cursor, this);
|
||||
MakeListChangesRequest(ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void Client::MakeListChangesRequest(ListChangesResponse* response, const QString& page_token) {
|
||||
QUrl url(kGoogleDriveChanges);
|
||||
if (!response->cursor().isEmpty()) {
|
||||
url.addQueryItem("startChangeId", response->cursor());
|
||||
}
|
||||
if (!page_token.isEmpty()) {
|
||||
url.addQueryItem("pageToken", page_token);
|
||||
}
|
||||
|
||||
qLog(Debug) << "Requesting changes at:" << response->cursor() << page_token;
|
||||
|
||||
QNetworkRequest request(url);
|
||||
AddAuthorizationHeader(&request);
|
||||
|
||||
QNetworkReply* reply = network_->get(request);
|
||||
NewClosure(reply, SIGNAL(finished()),
|
||||
this, SLOT(ListChangesFinished(ListChangesResponse*,QNetworkReply*)),
|
||||
response, reply);
|
||||
}
|
||||
|
||||
void Client::ListChangesFinished(ListChangesResponse* response, QNetworkReply* reply) {
|
||||
reply->deleteLater();
|
||||
|
||||
QJson::Parser parser;
|
||||
bool ok = false;
|
||||
// TODO: Put this on a separate thread as the response could be large.
|
||||
QVariantMap result = parser.parse(reply, &ok).toMap();
|
||||
if (!ok) {
|
||||
qLog(Error) << "Failed to fetch changes" << response->cursor();
|
||||
emit response->Finished();
|
||||
return;
|
||||
}
|
||||
|
||||
if (result.contains("largestChangeId")) {
|
||||
response->next_cursor_ = result["largestChangeId"].toString();
|
||||
}
|
||||
|
||||
// Emit the FilesFound signal for the files in the response.
|
||||
FileList files;
|
||||
QList<QUrl> files_deleted;
|
||||
foreach (const QVariant& v, result["items"].toList()) {
|
||||
QVariantMap change = v.toMap();
|
||||
if (!change["deleted"].toBool()) {
|
||||
files << File(change["file"].toMap());
|
||||
} else {
|
||||
QUrl url;
|
||||
url.setScheme("googledrive");
|
||||
url.setPath(change["fileId"].toString());
|
||||
files_deleted << url;
|
||||
}
|
||||
}
|
||||
|
||||
emit response->FilesFound(files);
|
||||
emit response->FilesDeleted(files_deleted);
|
||||
|
||||
// Get the next page of results if there is one.
|
||||
if (result.contains("nextPageToken")) {
|
||||
MakeListChangesRequest(response, result["nextPageToken"].toString());
|
||||
} else {
|
||||
emit response->Finished();
|
||||
}
|
||||
}
|
||||
|
||||
bool Client::is_authenticated() const {
|
||||
return !access_token_.isEmpty() &&
|
||||
QDateTime::currentDateTime().secsTo(expiry_time_) > 0;
|
||||
|
@ -1,16 +1,16 @@
|
||||
/* This file is part of Clementine.
|
||||
Copyright 2012, David Sansome <me@davidsansome.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/>.
|
||||
*/
|
||||
@ -97,23 +97,6 @@ private:
|
||||
};
|
||||
|
||||
|
||||
class ListFilesResponse : public QObject {
|
||||
Q_OBJECT
|
||||
friend class Client;
|
||||
|
||||
public:
|
||||
const QString& query() const { return query_; }
|
||||
|
||||
signals:
|
||||
void FilesFound(const QList<google_drive::File>& files);
|
||||
void Finished();
|
||||
|
||||
private:
|
||||
ListFilesResponse(const QString& query, QObject* parent);
|
||||
QString query_;
|
||||
};
|
||||
|
||||
|
||||
class GetFileResponse : public QObject {
|
||||
Q_OBJECT
|
||||
friend class Client;
|
||||
@ -132,6 +115,25 @@ private:
|
||||
};
|
||||
|
||||
|
||||
class ListChangesResponse : public QObject {
|
||||
Q_OBJECT
|
||||
friend class Client;
|
||||
public:
|
||||
const QString& cursor() const { return cursor_; }
|
||||
const QString& next_cursor() const { return next_cursor_; }
|
||||
|
||||
signals:
|
||||
void FilesFound(const QList<google_drive::File>& files);
|
||||
void FilesDeleted(const QList<QUrl>& files);
|
||||
void Finished();
|
||||
|
||||
private:
|
||||
ListChangesResponse(const QString& cursor, QObject* parent);
|
||||
QString cursor_;
|
||||
QString next_cursor_;
|
||||
};
|
||||
|
||||
|
||||
class Client : public QObject {
|
||||
Q_OBJECT
|
||||
|
||||
@ -144,21 +146,22 @@ public:
|
||||
void ForgetCredentials();
|
||||
|
||||
ConnectResponse* Connect(const QString& refresh_token = QString());
|
||||
ListFilesResponse* ListFiles(const QString& query);
|
||||
GetFileResponse* GetFile(const QString& file_id);
|
||||
ListChangesResponse* ListChanges(const QString& cursor);
|
||||
|
||||
|
||||
signals:
|
||||
void Authenticated();
|
||||
|
||||
private slots:
|
||||
void ConnectFinished(ConnectResponse* response, OAuthenticator* oauth);
|
||||
void ListFilesFinished(ListFilesResponse* response, QNetworkReply* reply);
|
||||
void GetFileFinished(GetFileResponse* response, QNetworkReply* reply);
|
||||
void ListChangesFinished(ListChangesResponse* response, QNetworkReply* reply);
|
||||
|
||||
private:
|
||||
void AddAuthorizationHeader(QNetworkRequest* request) const;
|
||||
void MakeListFilesRequest(ListFilesResponse* response,
|
||||
const QString& page_token = QString());
|
||||
void MakeListChangesRequest(ListChangesResponse* response,
|
||||
const QString& page_token = QString());
|
||||
|
||||
private:
|
||||
QNetworkAccessManager* network_;
|
||||
|
@ -74,14 +74,22 @@ void GoogleDriveService::ForgetCredentials() {
|
||||
s.remove("user_email");
|
||||
}
|
||||
|
||||
void GoogleDriveService::ListFilesForMimeType(const QString& mime_type) {
|
||||
google_drive::ListFilesResponse* list_response = client_->ListFiles(
|
||||
QString("mimeType = '%1' and trashed = false").arg(mime_type));
|
||||
connect(list_response, SIGNAL(FilesFound(QList<google_drive::File>)),
|
||||
this, SLOT(FilesFound(QList<google_drive::File>)));
|
||||
NewClosure(list_response, SIGNAL(Finished()),
|
||||
this, SLOT(ListFilesFinished(google_drive::ListFilesResponse*)),
|
||||
list_response);
|
||||
void GoogleDriveService::ListChanges(const QString& cursor) {
|
||||
google_drive::ListChangesResponse* changes_response = client_->ListChanges(cursor);
|
||||
connect(changes_response, SIGNAL(FilesFound(QList<google_drive::File>)),
|
||||
SLOT(FilesFound(QList<google_drive::File>)));
|
||||
connect(changes_response, SIGNAL(FilesDeleted(QList<QUrl>)),
|
||||
SLOT(FilesDeleted(QList<QUrl>)));
|
||||
NewClosure(changes_response, SIGNAL(Finished()),
|
||||
this, SLOT(ListChangesFinished(google_drive::ListChangesResponse*)),
|
||||
changes_response);
|
||||
}
|
||||
|
||||
void GoogleDriveService::ListChangesFinished(google_drive::ListChangesResponse* changes_response) {
|
||||
changes_response->deleteLater();
|
||||
QSettings s;
|
||||
s.beginGroup(kSettingsGroup);
|
||||
s.setValue("cursor", changes_response->next_cursor());
|
||||
}
|
||||
|
||||
void GoogleDriveService::ConnectFinished(google_drive::ConnectResponse* response) {
|
||||
@ -99,10 +107,8 @@ void GoogleDriveService::ConnectFinished(google_drive::ConnectResponse* response
|
||||
|
||||
emit Connected();
|
||||
|
||||
// Find any music files
|
||||
ListFilesForMimeType("audio/mpeg"); // MP3/AAC
|
||||
ListFilesForMimeType("application/ogg"); // OGG
|
||||
ListFilesForMimeType("application/x-flac"); // FLAC
|
||||
// Find all the changes since the last check.
|
||||
ListChanges(s.value("cursor").toString());
|
||||
}
|
||||
|
||||
void GoogleDriveService::EnsureConnected() {
|
||||
@ -122,16 +128,37 @@ void GoogleDriveService::FilesFound(const QList<google_drive::File>& files) {
|
||||
}
|
||||
}
|
||||
|
||||
void GoogleDriveService::ListFilesFinished(google_drive::ListFilesResponse* response) {
|
||||
response->deleteLater();
|
||||
void GoogleDriveService::FilesDeleted(const QList<QUrl>& files) {
|
||||
foreach (const QUrl& url, files) {
|
||||
Song song = library_backend_->GetSongByUrl(url);
|
||||
qLog(Debug) << "Deleting:" << url << song.title();
|
||||
if (song.is_valid()) {
|
||||
library_backend_->DeleteSongs(SongList() << song);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
bool IsSupportedMimeType(const QString& mime_type) {
|
||||
return mime_type == "audio/mpeg" ||
|
||||
mime_type == "application/ogg" ||
|
||||
mime_type == "application/x-flac";
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
void GoogleDriveService::MaybeAddFileToDatabase(const google_drive::File& file) {
|
||||
QString url = QString("googledrive:%1").arg(file.id());
|
||||
Song song = library_backend_->GetSongByUrl(QUrl(url));
|
||||
// Song already in index.
|
||||
// TODO: Check etag and maybe update.
|
||||
if (song.is_valid()) {
|
||||
qLog(Debug) << "Already have:" << url;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!IsSupportedMimeType(file.mime_type())) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -10,6 +10,7 @@ namespace google_drive {
|
||||
class ConnectResponse;
|
||||
class File;
|
||||
class ListFilesResponse;
|
||||
class ListChangesResponse;
|
||||
}
|
||||
|
||||
class GoogleDriveService : public CloudFileService {
|
||||
@ -38,7 +39,8 @@ class GoogleDriveService : public CloudFileService {
|
||||
private slots:
|
||||
void ConnectFinished(google_drive::ConnectResponse* response);
|
||||
void FilesFound(const QList<google_drive::File>& files);
|
||||
void ListFilesFinished(google_drive::ListFilesResponse* response);
|
||||
void FilesDeleted(const QList<QUrl>& files);
|
||||
void ListChangesFinished(google_drive::ListChangesResponse* response);
|
||||
void ReadTagsFinished(TagReaderClient::ReplyType* reply,
|
||||
const google_drive::File& metadata,
|
||||
const QString& url,
|
||||
@ -50,7 +52,7 @@ class GoogleDriveService : public CloudFileService {
|
||||
void EnsureConnected();
|
||||
void RefreshAuthorisation(const QString& refresh_token);
|
||||
void MaybeAddFileToDatabase(const google_drive::File& file);
|
||||
void ListFilesForMimeType(const QString& mime_type);
|
||||
void ListChanges(const QString& cursor);
|
||||
|
||||
google_drive::Client* client_;
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user