File indexing completed on 2025-01-05 03:53:29
0001 /* ============================================================ 0002 * 0003 * This file is a part of digiKam project 0004 * https://www.digikam.org 0005 * 0006 * Date : 2007-16-07 0007 * Description : a tool to export items to Google web services 0008 * 0009 * SPDX-FileCopyrightText: 2007-2008 by Vardhman Jain <vardhman at gmail dot com> 0010 * SPDX-FileCopyrightText: 2008-2024 by Gilles Caulier <caulier dot gilles at gmail dot com> 0011 * SPDX-FileCopyrightText: 2009 by Luka Renko <lure at kubuntu dot org> 0012 * SPDX-FileCopyrightText: 2018 by Thanh Trung Dinh <dinhthanhtrung1996 at gmail dot com> 0013 * 0014 * SPDX-License-Identifier: GPL-2.0-or-later 0015 * 0016 * ============================================================ */ 0017 0018 #include "gptalker.h" 0019 0020 // Qt includes 0021 0022 #include <QMimeDatabase> 0023 #include <QByteArray> 0024 #include <QDomDocument> 0025 #include <QDomElement> 0026 #include <QDomNode> 0027 #include <QJsonDocument> 0028 #include <QJsonParseError> 0029 #include <QJsonObject> 0030 #include <QJsonValue> 0031 #include <QJsonArray> 0032 #include <QFile> 0033 #include <QFileInfo> 0034 #include <QImage> 0035 #include <QStringList> 0036 #include <QUrlQuery> 0037 #include <QApplication> 0038 #include <QDir> 0039 #include <QMessageBox> 0040 0041 // KDE includes 0042 0043 #include <klocalizedstring.h> 0044 0045 // Local includes 0046 0047 #include "wstoolutils.h" 0048 #include "digikam_version.h" 0049 #include "gswindow.h" 0050 #include "gpmpform.h" 0051 #include "digikam_debug.h" 0052 #include "previewloadthread.h" 0053 #include "dmetadata.h" 0054 0055 using namespace Digikam; 0056 0057 #define NB_MAX_ITEM_UPLOAD 50 0058 0059 namespace DigikamGenericGoogleServicesPlugin 0060 { 0061 0062 static bool gphotoLessThan(const GSFolder& p1, const GSFolder& p2) 0063 { 0064 return (p1.title.toLower() < p2.title.toLower()); 0065 } 0066 0067 class Q_DECL_HIDDEN GPTalker::Private 0068 { 0069 public: 0070 0071 enum State 0072 { 0073 GP_LOGOUT = -1, 0074 GP_LISTALBUMS = 0, 0075 GP_GETUSER, 0076 GP_LISTPHOTOS, 0077 GP_ADDPHOTO, 0078 GP_UPDATEPHOTO, 0079 GP_UPLOADPHOTO, 0080 GP_GETPHOTO, 0081 GP_CREATEALBUM 0082 }; 0083 0084 public: 0085 0086 explicit Private() 0087 : apiVersion(QLatin1String("v1")), 0088 userInfoUrl(QString::fromLatin1("https://www.googleapis.com/plus/%1/people/me").arg(apiVersion)), 0089 apiUrl(QString::fromLatin1("https://photoslibrary.googleapis.com/%1/%2").arg(apiVersion)), 0090 state(GP_LOGOUT), 0091 albumIdToUpload(QLatin1String("-1")), 0092 previousImageId(QLatin1String("-1")) 0093 { 0094 } 0095 0096 public: 0097 0098 QString apiVersion; 0099 0100 QString userInfoUrl; 0101 QString apiUrl; 0102 0103 State state; 0104 0105 QString albumIdToUpload; 0106 QString albumIdToImport; 0107 QString previousImageId; 0108 QString currDescription; 0109 0110 QStringList descriptionList; 0111 QStringList uploadTokenList; 0112 QList<GSFolder> albumList; 0113 QList<GSPhoto> photoList; 0114 }; 0115 0116 GPTalker::GPTalker(QWidget* const parent) 0117 : GSTalkerBase(parent, 0118 QStringList() // to get user login (temporary until gphoto supports it officially) 0119 << QLatin1String("https://www.googleapis.com/auth/plus.login") 0120 // to add and download photo in the library 0121 << QLatin1String("https://www.googleapis.com/auth/photoslibrary") 0122 // to download photo created by digiKam on GPhoto 0123 << QLatin1String("https://www.googleapis.com/auth/photoslibrary.readonly.appcreateddata") 0124 // for shared albums 0125 << QLatin1String("https://www.googleapis.com/auth/photoslibrary.sharing"), 0126 QLatin1String("GooglePhotos")), 0127 d(new Private) 0128 { 0129 connect(m_service->networkAccessManager(), SIGNAL(finished(QNetworkReply*)), 0130 this, SLOT(slotFinished(QNetworkReply*))); 0131 0132 connect(this, SIGNAL(signalError(QString)), 0133 this, SLOT(slotError(QString))); 0134 0135 connect(this, SIGNAL(signalReadyToUpload()), 0136 this, SLOT(slotUploadPhoto())); 0137 } 0138 0139 GPTalker::~GPTalker() 0140 { 0141 if (m_reply) 0142 { 0143 m_reply->abort(); 0144 m_reply = nullptr; 0145 } 0146 0147 WSToolUtils::removeTemporaryDir("google"); 0148 0149 delete d; 0150 } 0151 0152 QStringList GPTalker::getUploadTokenList() 0153 { 0154 return d->uploadTokenList; 0155 } 0156 0157 /** 0158 * (Trung): Comments below are not valid anymore with google photos api 0159 * Google Photo's Album listing request/response 0160 * First a request is sent to the url below and then we might(?) get a redirect URL 0161 * We then need to send the GET request to the Redirect url. 0162 * This uses the authenticated album list fetching to get all the albums included the unlisted-albums 0163 * which is not returned for an unauthorised request as done without the Authorization header. 0164 */ 0165 void GPTalker::listAlbums(const QString& nextPageToken) 0166 { 0167 if (m_reply) 0168 { 0169 m_reply->abort(); 0170 m_reply = nullptr; 0171 } 0172 0173 qCDebug(DIGIKAM_WEBSERVICES_LOG) << "list albums"; 0174 0175 QUrl url(d->apiUrl.arg(QLatin1String("albums"))); 0176 0177 QUrlQuery query(url); 0178 query.addQueryItem(QLatin1String("pageSize"), QLatin1String("50")); 0179 0180 if (nextPageToken.isEmpty()) 0181 { 0182 d->albumList.clear(); 0183 } 0184 else 0185 { 0186 query.addQueryItem(QLatin1String("pageToken"), nextPageToken); 0187 } 0188 0189 url.setQuery(query); 0190 0191 // qCDebug(DIGIKAM_WEBSERVICES_LOG) << "url for list albums" << url; 0192 0193 m_reply = m_service->get(url); 0194 0195 d->state = Private::GP_LISTALBUMS; 0196 0197 Q_EMIT signalBusy(true); 0198 } 0199 0200 /** 0201 * We get user profile from Google Plus API 0202 * This is a temporary solution until Google Photo support API for user profile 0203 */ 0204 void GPTalker::getLoggedInUser() 0205 { 0206 qCDebug(DIGIKAM_WEBSERVICES_LOG) << "getLoggedInUser"; 0207 0208 if (m_reply) 0209 { 0210 m_reply->abort(); 0211 m_reply = nullptr; 0212 } 0213 0214 QUrl url(d->userInfoUrl); 0215 0216 // qCDebug(DIGIKAM_WEBSERVICES_LOG) << "url for list albums" << url; 0217 0218 m_reply = m_service->get(url); 0219 0220 d->state = Private::GP_GETUSER; 0221 0222 Q_EMIT signalBusy(true); 0223 } 0224 0225 void GPTalker::listPhotos(const QString& albumId, const QString& nextPageToken) 0226 { 0227 if (m_reply) 0228 { 0229 m_reply->abort(); 0230 m_reply = nullptr; 0231 } 0232 0233 d->albumIdToImport = albumId; 0234 0235 if (nextPageToken.isEmpty()) 0236 { 0237 d->photoList.clear(); 0238 } 0239 0240 QUrl url(d->apiUrl.arg(QLatin1String("mediaItems:search"))); 0241 0242 QByteArray data; 0243 data += "{\"pageSize\": \"100\","; 0244 0245 if (!nextPageToken.isEmpty()) 0246 { 0247 data += "\"pageToken\": \""; 0248 data += nextPageToken.toLatin1(); 0249 data += "\","; 0250 } 0251 0252 data += "\"albumId\": \""; 0253 data += albumId.toLatin1(); 0254 data += "\"}"; 0255 0256 // qCDebug(DIGIKAM_WEBSERVICES_LOG) << "data to list photos:" << data; 0257 0258 m_reply = m_service->post(url, data); 0259 0260 d->state = Private::GP_LISTPHOTOS; 0261 0262 Q_EMIT signalBusy(true); 0263 } 0264 0265 void GPTalker::createAlbum(const GSFolder& album) 0266 { 0267 if (m_reply) 0268 { 0269 m_reply->abort(); 0270 m_reply = nullptr; 0271 } 0272 0273 // Create body in json 0274 QByteArray data; 0275 data += "{\"album\": "; 0276 data += "{\"title\": \""; 0277 data += album.title.toUtf8(); 0278 data += "\"}}"; 0279 // qCDebug(DIGIKAM_WEBSERVICES_LOG) << data; 0280 0281 QUrl url(d->apiUrl.arg(QLatin1String("albums"))); 0282 0283 m_reply = m_service->post(url, data); 0284 0285 d->state = Private::GP_CREATEALBUM; 0286 0287 Q_EMIT signalBusy(true); 0288 } 0289 0290 /** 0291 * First a request is sent to the url below and then we will get an upload token 0292 * Upload token then will be sent with url in GPTlaker::uploadPhoto to create real photos on user account 0293 */ 0294 bool GPTalker::addPhoto(const QString& photoPath, 0295 GSPhoto& info, 0296 const QString& albumId, 0297 bool original, 0298 bool rescale, 0299 int maxDim, 0300 int imageQuality) 0301 { 0302 if (m_reply) 0303 { 0304 m_reply->abort(); 0305 m_reply = nullptr; 0306 } 0307 0308 QUrl url(d->apiUrl.arg(QLatin1String("uploads"))); 0309 0310 // Save album ID and description to upload 0311 d->currDescription = info.description; 0312 d->albumIdToUpload = albumId; 0313 0314 QString path(photoPath); 0315 0316 QMimeDatabase mimeDB; 0317 0318 if (!original && mimeDB.mimeTypeForFile(photoPath).name().startsWith(QLatin1String("image/"))) 0319 { 0320 QImage image = PreviewLoadThread::loadHighQualitySynchronously(photoPath).copyQImage(); 0321 0322 if (image.isNull()) 0323 { 0324 image.load(photoPath); 0325 } 0326 0327 if (image.isNull()) 0328 { 0329 return false; 0330 } 0331 0332 path = WSToolUtils::makeTemporaryDir("google").filePath(QFileInfo(photoPath) 0333 .baseName().trimmed() + QLatin1String(".jpg")); 0334 0335 if (rescale && ((image.width() > maxDim) || (image.height() > maxDim))) 0336 { 0337 image = image.scaled(maxDim, maxDim, Qt::KeepAspectRatio, Qt::SmoothTransformation); 0338 } 0339 0340 image.save(path, "JPEG", imageQuality); 0341 0342 QScopedPointer<DMetadata> meta(new DMetadata); 0343 0344 if (meta->load(photoPath)) 0345 { 0346 meta->setItemDimensions(image.size()); 0347 meta->setItemOrientation(MetaEngine::ORIENTATION_NORMAL); 0348 meta->setMetadataWritingMode((int)DMetadata::WRITE_TO_FILE_ONLY); 0349 meta->save(path, true); 0350 } 0351 } 0352 0353 // Create the body for temporary upload 0354 QFile imageFile(path); 0355 0356 if (!imageFile.open(QIODevice::ReadOnly)) 0357 { 0358 return false; 0359 } 0360 0361 QByteArray data = imageFile.readAll(); 0362 imageFile.close(); 0363 0364 QString imageName = QUrl::fromLocalFile(path).fileName(); 0365 0366 QNetworkRequest netRequest(url); 0367 netRequest.setHeader(QNetworkRequest::ContentTypeHeader, QLatin1String("application/octet-stream")); 0368 netRequest.setRawHeader("Authorization", m_bearerAccessToken.toLatin1()); 0369 netRequest.setRawHeader("X-Goog-Upload-File-Name", imageName.toUtf8()); 0370 netRequest.setRawHeader("X-Goog-Upload-Protocol", "raw"); 0371 0372 // qCDebug(DIGIKAM_WEBSERVICES_LOG) << imageName; 0373 0374 m_reply = m_service->networkAccessManager()->post(netRequest, data); 0375 0376 d->state = Private::GP_ADDPHOTO; 0377 0378 Q_EMIT signalBusy(true); 0379 0380 return true; 0381 } 0382 0383 bool GPTalker::updatePhoto(const QString& /*photoPath*/, GSPhoto& /*info*/, 0384 bool /*rescale*/, int /*maxDim*/, int /*imageQuality*/) 0385 { 0386 /* 0387 if (m_reply) 0388 { 0389 m_reply->abort(); 0390 m_reply = nullptr; 0391 } 0392 0393 Q_EMIT signalBusy(true); 0394 0395 GPMPForm form; 0396 QString path = photoPath; 0397 0398 QMimeDatabase mimeDB; 0399 0400 if (mimeDB.mimeTypeForFile(photoPath).name().startsWith(QLatin1String("image/"))) 0401 { 0402 QImage image = PreviewLoadThread::loadHighQualitySynchronously(photoPath).copyQImage(); 0403 0404 if (image.isNull()) 0405 { 0406 image.load(photoPath); 0407 } 0408 0409 if (image.isNull()) 0410 { 0411 Q_EMIT signalBusy(false); 0412 return false; 0413 } 0414 0415 path = WSToolUtils::makeTemporaryDir("google").filePath(QFileInfo(photoPath) 0416 .baseName().trimmed() + QLatin1String(".jpg")); 0417 0418 if (rescale && (image.width() > maxDim || image.height() > maxDim)) 0419 { 0420 image = image.scaled(maxDim,maxDim, Qt::KeepAspectRatio, Qt::SmoothTransformation); 0421 } 0422 0423 image.save(path, "JPEG", imageQuality); 0424 0425 QScopedPointer<DMetadata> meta(new DMetadata); 0426 0427 if (meta->load(photoPath)) 0428 { 0429 meta->setItemDimensions(image.size()); 0430 meta->setItemOrientation(MetaEngine::ORIENTATION_NORMAL); 0431 meta->setMetadataWritingMode((int)DMetadata::WRITE_TO_FILE_ONLY); 0432 meta->save(path, true); 0433 } 0434 } 0435 0436 QNetworkRequest netRequest(info.editUrl); 0437 netRequest.setHeader(QNetworkRequest::ContentTypeHeader, form.contentType()); 0438 netRequest.setRawHeader("Authorization", m_bearerAccessToken.toLatin1() + "\nIf-Match: *"); 0439 0440 m_reply = m_netMngr->put(netRequest, form.formData()); 0441 0442 d->state = Private::GP_UPDATEPHOTO; 0443 */ 0444 return true; 0445 } 0446 0447 void GPTalker::getPhoto(const QString& imgPath) 0448 { 0449 if (m_reply) 0450 { 0451 m_reply->abort(); 0452 m_reply = nullptr; 0453 } 0454 0455 Q_EMIT signalBusy(true); 0456 0457 QUrl url(imgPath); 0458 0459 // qCDebug(DIGIKAM_WEBSERVICES_LOG) << "link to get photo" << url; 0460 0461 m_reply = m_service->get(url); 0462 0463 d->state = Private::GP_GETPHOTO; 0464 } 0465 0466 void GPTalker::cancel() 0467 { 0468 if (m_reply) 0469 { 0470 m_reply->abort(); 0471 m_reply = nullptr; 0472 } 0473 0474 d->descriptionList.clear(); 0475 d->uploadTokenList.clear(); 0476 0477 Q_EMIT signalBusy(false); 0478 } 0479 0480 void GPTalker::slotError(const QString& error) 0481 { 0482 QString transError; 0483 int errorNo = 0; 0484 0485 if (!error.isEmpty()) 0486 errorNo = error.toInt(); 0487 0488 switch (errorNo) 0489 { 0490 case 2: 0491 transError=i18n("No photo specified"); 0492 break; 0493 case 3: 0494 transError=i18n("General upload failure"); 0495 break; 0496 case 4: 0497 transError=i18n("File-size was zero"); 0498 break; 0499 case 5: 0500 transError=i18n("File-type was not recognized"); 0501 break; 0502 case 6: 0503 transError=i18n("User exceeded upload limit"); 0504 break; 0505 case 96: 0506 transError=i18n("Invalid signature"); 0507 break; 0508 case 97: 0509 transError=i18n("Missing signature"); 0510 break; 0511 case 98: 0512 transError=i18n("Login failed / Invalid auth token"); 0513 break; 0514 case 100: 0515 transError=i18n("Invalid API Key"); 0516 break; 0517 case 105: 0518 transError=i18n("Service currently unavailable"); 0519 break; 0520 case 108: 0521 transError=i18n("Invalid Frob"); 0522 break; 0523 case 111: 0524 transError=i18n("Format \"xxx\" not found"); 0525 break; 0526 case 112: 0527 transError=i18n("Method \"xxx\" not found"); 0528 break; 0529 case 114: 0530 transError=i18n("Invalid SOAP envelope"); 0531 break; 0532 case 115: 0533 transError=i18n("Invalid XML-RPC Method Call"); 0534 break; 0535 case 116: 0536 transError=i18n("The POST method is now required for all setters."); 0537 break; 0538 default: 0539 transError=i18n("Unknown error"); 0540 }; 0541 0542 QMessageBox::critical(QApplication::activeWindow(), i18nc("@title:window", "Error"), 0543 i18n("Error occurred: %1\nUnable to proceed further.", transError + error)); 0544 } 0545 0546 void GPTalker::slotFinished(QNetworkReply* reply) 0547 { 0548 Q_EMIT signalBusy(false); 0549 0550 if (reply != m_reply) 0551 { 0552 return; 0553 } 0554 0555 m_reply = nullptr; 0556 0557 qCDebug(DIGIKAM_WEBSERVICES_LOG) << "reply error:" << reply->error() 0558 << "-" << reply->errorString(); 0559 0560 if (reply->error() != QNetworkReply::NoError) 0561 { 0562 if (d->state == Private::GP_ADDPHOTO) 0563 { 0564 Q_EMIT signalAddPhotoDone(reply->error(), reply->errorString()); 0565 } 0566 else if (reply->error() != QNetworkReply::OperationCanceledError) 0567 { 0568 QMessageBox::critical(QApplication::activeWindow(), 0569 i18nc("@title:window", "Error"), reply->errorString()); 0570 } 0571 0572 reply->deleteLater(); 0573 return; 0574 } 0575 0576 QByteArray buffer = reply->readAll(); 0577 0578 switch (d->state) 0579 { 0580 case (Private::GP_LOGOUT): 0581 break; 0582 case (Private::GP_GETUSER): 0583 parseResponseGetLoggedInUser(buffer); 0584 break; 0585 case (Private::GP_CREATEALBUM): 0586 parseResponseCreateAlbum(buffer); 0587 break; 0588 case (Private::GP_LISTALBUMS): 0589 parseResponseListAlbums(buffer); 0590 break; 0591 case (Private::GP_LISTPHOTOS): 0592 parseResponseListPhotos(buffer); 0593 break; 0594 case (Private::GP_ADDPHOTO): 0595 parseResponseAddPhoto(buffer); 0596 break; 0597 case (Private::GP_UPDATEPHOTO): 0598 Q_EMIT signalAddPhotoDone(1, QString()); 0599 break; 0600 case (Private::GP_UPLOADPHOTO): 0601 parseResponseUploadPhoto(buffer); 0602 break; 0603 case (Private::GP_GETPHOTO): 0604 { 0605 // all we get is data of the image 0606 // qCDebug(DIGIKAM_WEBSERVICES_LOG) << buffer; 0607 0608 QString header = reply->header(QNetworkRequest::ContentDispositionHeader).toString(); 0609 QStringList headerList = header.split(QLatin1Char(';')); 0610 QString fileName; 0611 0612 if (headerList.count() > 1 && 0613 headerList.at(0) == QLatin1String("attachment") && 0614 headerList.at(1).contains(QLatin1String("filename="))) 0615 { 0616 fileName = headerList.at(1).section(QLatin1Char('"'), 1, 1); 0617 } 0618 0619 Q_EMIT signalGetPhotoDone(1, QString(), buffer, fileName); 0620 0621 break; 0622 } 0623 } 0624 0625 reply->deleteLater(); 0626 } 0627 0628 void GPTalker::slotUploadPhoto() 0629 { 0630 /* Keep track of number of items will be uploaded, because 0631 * Google Photo API upload maximum NB_MAX_ITEM_UPLOAD items in at a time 0632 */ 0633 int nbItemsUpload = 0; 0634 0635 if (m_reply) 0636 { 0637 m_reply->abort(); 0638 m_reply = nullptr; 0639 } 0640 0641 QUrl url(d->apiUrl.arg(QLatin1String("mediaItems:batchCreate"))); 0642 0643 QByteArray data; 0644 data += '{'; 0645 0646 if (d->albumIdToUpload != QLatin1String("-1")) 0647 { 0648 data += "\"albumId\": \""; 0649 data += d->albumIdToUpload.toLatin1(); 0650 data += "\","; 0651 } 0652 0653 data += "\"newMediaItems\": ["; 0654 0655 if (d->uploadTokenList.isEmpty()) 0656 { 0657 qCDebug(DIGIKAM_WEBSERVICES_LOG) << "token list is empty"; 0658 } 0659 0660 while (!d->uploadTokenList.isEmpty() && nbItemsUpload < NB_MAX_ITEM_UPLOAD) 0661 { 0662 const QString& uploadToken = d->uploadTokenList.takeFirst(); 0663 data += "{\"description\": \""; 0664 0665 if (d->descriptionList.isEmpty()) 0666 { 0667 qCDebug(DIGIKAM_WEBSERVICES_LOG) << "description list is empty"; 0668 } 0669 else 0670 { 0671 data += d->descriptionList.takeFirst().toUtf8(); 0672 } 0673 0674 data += "\","; 0675 data += "\"simpleMediaItem\": {"; 0676 data += "\"uploadToken\": \""; 0677 data += uploadToken.toLatin1(); 0678 data += "\"}}"; 0679 0680 if (d->uploadTokenList.length() > 0) 0681 { 0682 data += ','; 0683 } 0684 0685 nbItemsUpload ++; 0686 } 0687 0688 if (d->previousImageId == QLatin1String("-1")) 0689 { 0690 data += ']'; 0691 } 0692 else 0693 { 0694 data += "],\"albumPosition\": {"; 0695 data += "\"position\": \"AFTER_MEDIA_ITEM\","; 0696 data += "\"relativeMediaItemId\": \""; 0697 data += d->previousImageId.toLatin1(); 0698 data += "\"}\r\n"; 0699 } 0700 0701 data += "}\r\n"; 0702 0703 // qCDebug(DIGIKAM_WEBSERVICES_LOG) << data; 0704 0705 QNetworkRequest netRequest(url); 0706 netRequest.setHeader(QNetworkRequest::ContentTypeHeader, QLatin1String("application/json")); 0707 netRequest.setRawHeader("Authorization", m_bearerAccessToken.toLatin1()); 0708 0709 m_reply = m_service->networkAccessManager()->post(netRequest, data); 0710 0711 d->state = Private::GP_UPLOADPHOTO; 0712 0713 Q_EMIT signalBusy(true); 0714 } 0715 0716 void GPTalker::parseResponseListAlbums(const QByteArray& data) 0717 { 0718 qCDebug(DIGIKAM_WEBSERVICES_LOG) << "parseResponseListAlbums"; 0719 0720 QJsonParseError err; 0721 QJsonDocument doc = QJsonDocument::fromJson(data, &err); 0722 0723 if (err.error != QJsonParseError::NoError) 0724 { 0725 Q_EMIT signalBusy(false); 0726 Q_EMIT signalListAlbumsDone(0, QString::fromLatin1("Code: %1 - %2").arg(err.error) 0727 .arg(err.errorString()), 0728 QList<GSFolder>()); 0729 return; 0730 } 0731 0732 QJsonObject jsonObject = doc.object(); 0733 QJsonArray jsonArray = jsonObject[QLatin1String("albums")].toArray(); 0734 // qCDebug(DIGIKAM_WEBSERVICES_LOG) << "json array " << doc; 0735 0736 /** 0737 * Google-photos allows user to post photos on their main page (not in any albums) 0738 * so this folder is created for that purpose 0739 */ 0740 0741 if (d->albumList.isEmpty()) 0742 { 0743 GSFolder mainPage; 0744 d->albumList.append(mainPage); 0745 } 0746 0747 Q_FOREACH (const QJsonValue& value, jsonArray) 0748 { 0749 GSFolder album; 0750 0751 QJsonObject obj = value.toObject(); 0752 album.id = obj[QLatin1String("id")].toString(); 0753 album.title = obj[QLatin1String("title")].toString(); 0754 album.url = obj[QLatin1String("productUrl")].toString(); 0755 album.isWriteable = obj[QLatin1String("isWriteable")].toBool(); 0756 0757 d->albumList.append(album); 0758 } 0759 0760 QString nextPageToken = jsonObject[QLatin1String("nextPageToken")].toString(); 0761 0762 if (!nextPageToken.isEmpty()) 0763 { 0764 listAlbums(nextPageToken); 0765 return; 0766 } 0767 0768 std::sort(d->albumList.begin(), d->albumList.end(), gphotoLessThan); 0769 Q_EMIT signalListAlbumsDone(1, QLatin1String(""), d->albumList); 0770 } 0771 0772 void GPTalker::parseResponseListPhotos(const QByteArray& data) 0773 { 0774 qCDebug(DIGIKAM_WEBSERVICES_LOG) << "parseResponseListPhotos"; 0775 0776 QJsonParseError err; 0777 QJsonDocument doc = QJsonDocument::fromJson(data, &err); 0778 0779 if (err.error != QJsonParseError::NoError) 0780 { 0781 Q_EMIT signalBusy(false); 0782 Q_EMIT signalListPhotosDone(0, i18n("Failed to fetch photo-set list"), QList<GSPhoto>()); 0783 qCDebug(DIGIKAM_WEBSERVICES_LOG) << "error code:" << err.error 0784 << ", msg:" << err.errorString(); 0785 return; 0786 } 0787 0788 QJsonObject jsonObject = doc.object(); 0789 QJsonArray jsonArray = jsonObject[QLatin1String("mediaItems")].toArray(); 0790 0791 Q_FOREACH (const QJsonValue& value, jsonArray) 0792 { 0793 QJsonObject obj = value.toObject(); 0794 0795 GSPhoto photo; 0796 0797 photo.baseUrl = obj[QLatin1String("baseUrl")].toString(); 0798 photo.description = obj[QLatin1String("description")].toString(); 0799 photo.id = obj[QLatin1String("id")].toString(); 0800 photo.mimeType = obj[QLatin1String("mimeType")].toString(); 0801 photo.location = obj[QLatin1String("Location")].toString(); // Not yet available in v1 but will be in the future 0802 0803 QJsonObject metadata = obj[QLatin1String("mediaMetadata")].toObject(); 0804 0805 photo.creationTime = metadata[QLatin1String("creationTime")].toString(); 0806 photo.width = metadata[QLatin1String("width")].toString(); 0807 photo.height = metadata[QLatin1String("height")].toString(); 0808 0809 QString option = QLatin1String("=d"); 0810 0811 if (photo.mimeType.startsWith(QLatin1String("video/"))) 0812 { 0813 option.append(QLatin1Char('v')); 0814 } 0815 0816 photo.originalURL = QUrl(photo.baseUrl + option); 0817 // qCDebug(DIGIKAM_WEBSERVICES_LOG) << photo.originalURL; 0818 0819 d->photoList.append(photo); 0820 } 0821 0822 QString nextPageToken = jsonObject[QLatin1String("nextPageToken")].toString(); 0823 0824 if (!nextPageToken.isEmpty()) 0825 { 0826 listPhotos(d->albumIdToImport, nextPageToken); 0827 return; 0828 } 0829 0830 Q_EMIT signalListPhotosDone(1, QLatin1String(""), d->photoList); 0831 } 0832 0833 void GPTalker::parseResponseCreateAlbum(const QByteArray& data) 0834 { 0835 qCDebug(DIGIKAM_WEBSERVICES_LOG) << "parseResponseCreateAlbums"; 0836 0837 QJsonParseError err; 0838 QJsonDocument doc = QJsonDocument::fromJson(data, &err); 0839 0840 if (err.error != QJsonParseError::NoError) 0841 { 0842 Q_EMIT signalBusy(false); 0843 Q_EMIT signalCreateAlbumDone(0, QString::fromLatin1("Code: %1 - %2").arg(err.error) 0844 .arg(err.errorString()), 0845 QString()); 0846 return; 0847 } 0848 0849 QJsonObject jsonObject = doc.object(); 0850 QString albumId = jsonObject[QLatin1String("id")].toString(); 0851 qCDebug(DIGIKAM_WEBSERVICES_LOG) << "album Id" << doc; 0852 0853 Q_EMIT signalCreateAlbumDone(1, QLatin1String(""), albumId); 0854 } 0855 0856 void GPTalker::parseResponseAddPhoto(const QByteArray& data) 0857 { 0858 qCDebug(DIGIKAM_WEBSERVICES_LOG) << "parseResponseAddPhoto"; 0859 qCDebug(DIGIKAM_WEBSERVICES_LOG) << "response" << data; 0860 0861 d->uploadTokenList << QString::fromUtf8(data); 0862 d->descriptionList << d->currDescription; 0863 0864 Q_EMIT signalAddPhotoDone(1, QString()); 0865 } 0866 0867 void GPTalker::parseResponseGetLoggedInUser(const QByteArray& data) 0868 { 0869 qCDebug(DIGIKAM_WEBSERVICES_LOG) << "parseResponseGetLoggedInUser"; 0870 0871 QJsonParseError err; 0872 QJsonDocument doc = QJsonDocument::fromJson(data, &err); 0873 0874 if (err.error != QJsonParseError::NoError) 0875 { 0876 Q_EMIT signalBusy(false); 0877 return; 0878 } 0879 0880 QJsonObject jsonObject = doc.object(); 0881 QString userName = jsonObject[QLatin1String("displayName")].toString(); 0882 0883 Q_EMIT signalSetUserName(userName); 0884 0885 listAlbums(); 0886 } 0887 0888 //TODO: Parse and return photoID 0889 void GPTalker::parseResponseUploadPhoto(const QByteArray& data) 0890 { 0891 qCDebug(DIGIKAM_WEBSERVICES_LOG) << "parseResponseUploadPhoto"; 0892 0893 QJsonParseError err; 0894 QJsonDocument doc = QJsonDocument::fromJson(data, &err); 0895 0896 qCDebug(DIGIKAM_WEBSERVICES_LOG) << "doc" << doc; 0897 0898 if (err.error != QJsonParseError::NoError) 0899 { 0900 Q_EMIT signalBusy(false); 0901 Q_EMIT signalUploadPhotoDone(0, err.errorString(), QStringList()); 0902 return; 0903 } 0904 0905 QJsonObject jsonObject = doc.object(); 0906 QJsonArray jsonArray = jsonObject[QLatin1String("newMediaItemResults")].toArray(); 0907 0908 QStringList listPhotoId; 0909 0910 Q_FOREACH (const QJsonValue& value, jsonArray) 0911 { 0912 QJsonObject obj = value.toObject(); 0913 0914 QJsonObject mediaItem = obj[QLatin1String("mediaItem")].toObject(); 0915 listPhotoId << mediaItem[QLatin1String("id")].toString(); 0916 } 0917 0918 d->previousImageId = listPhotoId.last(); 0919 0920 qCDebug(DIGIKAM_WEBSERVICES_LOG) << "list photo Id" << listPhotoId.join(QLatin1String(", ")); 0921 0922 Q_EMIT signalBusy(false); 0923 Q_EMIT signalUploadPhotoDone(1, QString(), listPhotoId); 0924 } 0925 0926 } // namespace DigikamGenericGoogleServicesPlugin 0927 0928 #include "moc_gptalker.cpp"