File indexing completed on 2025-01-19 03:53:14
0001 /* ============================================================ 0002 * 0003 * This file is a part of digiKam project 0004 * https://www.digikam.org 0005 * 0006 * Date : 2018-06-29 0007 * Description : a tool to export images to Twitter social network 0008 * 0009 * SPDX-FileCopyrightText: 2018 by Tarek Talaat <tarektalaat93 at gmail dot com> 0010 * SPDX-FileCopyrightText: 2019 by Thanh Trung Dinh <dinhthanhtrung1996 at gmail dot com> 0011 * 0012 * SPDX-License-Identifier: GPL-2.0-or-later 0013 * 0014 * ============================================================ */ 0015 0016 #include "twittertalker.h" 0017 0018 // Qt includes 0019 0020 #include <QJsonDocument> 0021 #include <QJsonParseError> 0022 #include <QJsonObject> 0023 #include <QJsonValue> 0024 #include <QJsonArray> 0025 #include <QByteArray> 0026 #include <QUrl> 0027 #include <QUrlQuery> 0028 #include <QFileInfo> 0029 #include <QWidget> 0030 #include <QScopedPointer> 0031 #include <QSettings> 0032 #include <QMessageBox> 0033 #include <QApplication> 0034 #include <QDesktopServices> 0035 0036 /* 0037 #include <QWebEngineView> 0038 #include <QWebEnginePage> 0039 #include <QWebEngineProfile> 0040 #include <QWebEngineCookieStore> 0041 */ 0042 0043 // KDE includes 0044 0045 #include <klocalizedstring.h> 0046 0047 // Local includes 0048 0049 #include "digikam_debug.h" 0050 #include "digikam_version.h" 0051 #include "wstoolutils.h" 0052 #include "twitterwindow.h" 0053 #include "twittermpform.h" 0054 #include "previewloadthread.h" 0055 #include "networkmanager.h" 0056 #include "o0settingsstore.h" 0057 #include "o1requestor.h" 0058 0059 namespace DigikamGenericTwitterPlugin 0060 { 0061 0062 QStringList imageFormat(QString::fromLatin1("jpg,png,gif,webp").split(QLatin1Char(','))); 0063 0064 class Q_DECL_HIDDEN TwTalker::Private 0065 { 0066 public: 0067 0068 enum State 0069 { 0070 TW_USERNAME = 0, 0071 TW_LISTFOLDERS, 0072 TW_CREATEFOLDER, 0073 TW_ADDPHOTO, 0074 TW_CREATETWEET, 0075 TW_UPLOADINIT, 0076 TW_UPLOADAPPEND, 0077 TW_UPLOADSTATUSCHECK, 0078 TW_UPLOADFINALIZE 0079 }; 0080 0081 public: 0082 0083 explicit Private() 0084 : clientId (QLatin1String("lkRgRsucipXsUEvKh0ECblreC")), 0085 clientSecret (QLatin1String("6EThTiPQHZTMo7F83iLHrfNO89fkDVvM9hVWaYH9D49xEOyMBe")), 0086 authUrl (QLatin1String("https://api.twitter.com/oauth/authenticate")), 0087 requestTokenUrl (QLatin1String("https://api.twitter.com/oauth/request_token")), 0088 accessTokenUrl (QLatin1String("https://api.twitter.com/oauth/access_token")), 0089 /* 0090 scope (QLatin1String("User.Read Files.ReadWrite")), 0091 */ 0092 redirectUrl (QLatin1String("http://127.0.0.1:8000")), // krazy:exclude=insecurenet 0093 uploadUrl (QLatin1String("https://upload.twitter.com/1.1/media/upload.json")), 0094 segmentIndex (0), 0095 parent (nullptr), 0096 netMngr (nullptr), 0097 reply (nullptr), 0098 state (TW_USERNAME), 0099 settings (nullptr), 0100 o1Twitter (nullptr), 0101 requestor (nullptr) 0102 { 0103 } 0104 0105 public: 0106 0107 QString clientId; 0108 QString clientSecret; 0109 QString authUrl; 0110 QString requestTokenUrl; 0111 QString accessTokenUrl; 0112 QString scope; 0113 QString redirectUrl; 0114 QString accessToken; 0115 QString uploadUrl; 0116 QString mediaUploadedPath; 0117 QString mediaId; 0118 0119 int segmentIndex; 0120 0121 QWidget* parent; 0122 0123 QNetworkAccessManager* netMngr; 0124 0125 QNetworkReply* reply; 0126 0127 State state; 0128 0129 QMap<QString, QString> urlParametersMap; 0130 /* 0131 QWebEngineView* view; 0132 */ 0133 QSettings* settings; 0134 0135 O1Twitter* o1Twitter; 0136 O1Requestor* requestor; 0137 }; 0138 0139 TwTalker::TwTalker(QWidget* const parent) 0140 : d(new Private) 0141 { 0142 d->parent = parent; 0143 d->netMngr = NetworkManager::instance()->getNetworkManager(this); 0144 0145 connect(d->netMngr, SIGNAL(finished(QNetworkReply*)), 0146 this, SLOT(slotFinished(QNetworkReply*))); 0147 0148 d->o1Twitter = new O1Twitter(this); 0149 d->o1Twitter->setClientId(d->clientId); 0150 d->o1Twitter->setClientSecret(d->clientSecret); 0151 d->o1Twitter->setLocalPort(8000); 0152 0153 d->requestor = new O1Requestor(d->netMngr, d->o1Twitter, this); 0154 0155 d->settings = WSToolUtils::getOauthSettings(this); 0156 O0SettingsStore* const store = new O0SettingsStore(d->settings, QLatin1String(O2_ENCRYPTION_KEY), this); 0157 store->setGroupKey(QLatin1String("Twitter")); 0158 d->o1Twitter->setStore(store); 0159 0160 connect(d->o1Twitter, SIGNAL(linkingFailed()), 0161 this, SLOT(slotLinkingFailed())); 0162 0163 connect(d->o1Twitter, SIGNAL(linkingSucceeded()), 0164 this, SLOT(slotLinkingSucceeded())); 0165 0166 connect(d->o1Twitter, SIGNAL(openBrowser(QUrl)), 0167 this, SLOT(slotOpenBrowser(QUrl))); 0168 } 0169 0170 TwTalker::~TwTalker() 0171 { 0172 if (d->reply) 0173 { 0174 d->reply->abort(); 0175 } 0176 0177 WSToolUtils::removeTemporaryDir("twitter"); 0178 0179 delete d; 0180 } 0181 0182 void TwTalker::link() 0183 { 0184 /* 0185 Q_EMIT signalBusy(true); 0186 QUrl url(d->requestTokenUrl); 0187 QNetworkRequest netRequest(url); 0188 netRequest.setHeader(QNetworkRequest::ContentTypeHeader, QLatin1String("application/json")); 0189 netRequest.setRawHeader("Authorization", QString::fromLatin1("OAuth oauth_callback= \"%1\"").arg(d->redirectUrl).toUtf8()); 0190 QNetworkAccessManager requestMngr; 0191 QNetworkReply* reply; 0192 reply = requestMngr.post(netRequest); 0193 0194 if (reply->error() != QNetworkReply::NoError){ 0195 0196 } 0197 0198 QByteArray buffer; 0199 buffer.append(reply->readAll()); 0200 QString response = fromLatin1(buffer); 0201 0202 QMultiMap<QString, QString> headers; 0203 0204 // Discard the first line 0205 response = response.mid(response.indexOf('\n') + 1).trimmed(); 0206 0207 Q_FOREACH (QString line, response.split('\n')) 0208 { 0209 int colon = line.indexOf(':'); 0210 QString headerName = line.left(colon).trimmed(); 0211 QString headerValue = line.mid(colon + 1).trimmed(); 0212 0213 headers.insert(headerName, headerValue); 0214 } 0215 0216 QString oauthToken = headers[oauth_token]; 0217 QSting oauthTokenSecret = headers[oauth_token_secret]; 0218 0219 QUrlQuery query(url); 0220 query.addQueryItem(QLatin1String("client_id"), d->clientId); 0221 query.addQueryItem(QLatin1String("scope"), d->scope); 0222 query.addQueryItem(QLatin1String("redirect_uri"), d->redirectUrl); 0223 query.addQueryItem(QLatin1String("response_type"), "token"); 0224 url.setQuery(query); 0225 0226 d->view = new QWebEngineView(d->parent); 0227 d->view->setWindowFlags(Qt::Dialog); 0228 d->view->load(url); 0229 d->view->show(); 0230 0231 connect(d->view, SIGNAL(urlChanged(QUrl)), 0232 this, SLOT(slotCatchUrl(QUrl))); 0233 */ 0234 0235 Q_EMIT signalBusy(true); 0236 d->o1Twitter->link(); 0237 } 0238 0239 void TwTalker::unLink() 0240 { 0241 /* 0242 d->accessToken = QString(); 0243 d->view->page()->profile()->cookieStore()->deleteAllCookies(); 0244 Q_EMIT oneDriveLinkingSucceeded(); 0245 */ 0246 0247 d->o1Twitter->unlink(); 0248 0249 d->settings->beginGroup(QLatin1String("Twitter")); 0250 d->settings->remove(QString()); 0251 d->settings->endGroup(); 0252 } 0253 0254 void TwTalker::slotOpenBrowser(const QUrl& url) 0255 { 0256 qCDebug(DIGIKAM_WEBSERVICES_LOG) << "Open Browser..."; 0257 QDesktopServices::openUrl(url); 0258 } 0259 0260 QMap<QString, QString> TwTalker::ParseUrlParameters(const QString& url) 0261 { 0262 QMap<QString, QString> urlParameters; 0263 0264 if (url.indexOf(QLatin1Char('?')) == -1) 0265 { 0266 return urlParameters; 0267 } 0268 0269 QString tmp = url.right(url.length()-url.indexOf(QLatin1Char('?'))-1); 0270 tmp = tmp.right(tmp.length() - tmp.indexOf(QLatin1Char('#'))-1); 0271 QStringList paramlist = tmp.split(QLatin1Char('&')); 0272 0273 for (int i = 0 ; i < paramlist.count() ; ++i) 0274 { 0275 QStringList paramarg = paramlist.at(i).split(QLatin1Char('=')); 0276 urlParameters.insert(paramarg.at(0),paramarg.at(1)); 0277 } 0278 0279 return urlParameters; 0280 } 0281 0282 void TwTalker::slotLinkingFailed() 0283 { 0284 qCDebug(DIGIKAM_WEBSERVICES_LOG) << "LINK to Twitter fail"; 0285 0286 Q_EMIT signalBusy(false); 0287 } 0288 0289 void TwTalker::slotLinkingSucceeded() 0290 { 0291 if (!d->o1Twitter->linked()) 0292 { 0293 qCDebug(DIGIKAM_WEBSERVICES_LOG) << "UNLINK to Twitter ok"; 0294 0295 Q_EMIT signalBusy(false); 0296 0297 return; 0298 } 0299 0300 qCDebug(DIGIKAM_WEBSERVICES_LOG) << "LINK to Twitter ok"; 0301 QVariantMap extraTokens = d->o1Twitter->extraTokens(); 0302 0303 if (!extraTokens.isEmpty()) 0304 { 0305 //Q_EMIT extraTokensReady(extraTokens); 0306 0307 qCDebug(DIGIKAM_WEBSERVICES_LOG) << "Extra tokens in response:"; 0308 0309 Q_FOREACH (const QString& key, extraTokens.keys()) 0310 { 0311 qCDebug(DIGIKAM_WEBSERVICES_LOG) << "\t" 0312 << key 0313 << ":" 0314 << (extraTokens.value(key).toString().left(3) + QLatin1String("...")); 0315 } 0316 } 0317 0318 Q_EMIT signalLinkingSucceeded(); 0319 0320 getUserName(); 0321 } 0322 0323 bool TwTalker::authenticated() 0324 { 0325 return d->o1Twitter->linked(); 0326 } 0327 0328 void TwTalker::cancel() 0329 { 0330 if (d->reply) 0331 { 0332 d->reply->abort(); 0333 d->reply = nullptr; 0334 } 0335 0336 Q_EMIT signalBusy(false); 0337 } 0338 0339 bool TwTalker::addPhoto(const QString& imgPath, 0340 const QString& /* uploadFolder */, 0341 bool rescale, 0342 int maxDim, 0343 int imageQuality) 0344 { 0345 0346 QFileInfo imgFileInfo(imgPath); 0347 QString path; 0348 bool chunked = false; 0349 0350 qCDebug(DIGIKAM_WEBSERVICES_LOG) << imgFileInfo.suffix(); 0351 0352 if ((imgFileInfo.suffix() != QLatin1String("gif")) && 0353 (imgFileInfo.suffix() != QLatin1String("mp4"))) 0354 { 0355 QImage image = PreviewLoadThread::loadHighQualitySynchronously(imgPath).copyQImage(); 0356 qint64 imageSize = QFileInfo(imgPath).size(); 0357 qCDebug(DIGIKAM_WEBSERVICES_LOG) << "SIZE of image using qfileinfo: " << imageSize; 0358 qCDebug(DIGIKAM_WEBSERVICES_LOG) << " "; 0359 0360 if (image.isNull()) 0361 { 0362 Q_EMIT signalBusy(false); 0363 return false; 0364 } 0365 0366 path = WSToolUtils::makeTemporaryDir("twitter").filePath(imgFileInfo.baseName().trimmed() + QLatin1String(".jpg")); 0367 0368 if (rescale && ((image.width() > maxDim) || (image.height() > maxDim))) 0369 { 0370 image = image.scaled(maxDim, maxDim, Qt::KeepAspectRatio, Qt::SmoothTransformation); 0371 } 0372 0373 image.save(path, "JPEG", imageQuality); 0374 0375 QScopedPointer<DMetadata> meta(new DMetadata); 0376 0377 if (meta->load(imgPath)) 0378 { 0379 meta->setItemDimensions(image.size()); 0380 meta->setItemOrientation(DMetadata::ORIENTATION_NORMAL); 0381 meta->setMetadataWritingMode((int)DMetadata::WRITE_TO_FILE_ONLY); 0382 meta->save(path, true); 0383 } 0384 } 0385 else 0386 { 0387 path = imgPath; 0388 chunked = true; 0389 } 0390 0391 if (chunked) 0392 { 0393 return addPhotoInit(path); 0394 } 0395 else 0396 { 0397 return addPhotoSingleUpload(path); 0398 } 0399 } 0400 0401 bool TwTalker::addPhotoSingleUpload(const QString& imgPath) 0402 { 0403 qCDebug(DIGIKAM_WEBSERVICES_LOG) << "addPhotoSingleUpload"; 0404 Q_EMIT signalBusy(true); 0405 0406 TwMPForm form; 0407 0408 if (!form.addFile(imgPath)) 0409 { 0410 Q_EMIT signalBusy(false); 0411 return false; 0412 } 0413 0414 form.finish(); 0415 0416 if (form.formData().isEmpty()) 0417 { 0418 qCDebug(DIGIKAM_WEBSERVICES_LOG) << "Form DATA Empty:"; 0419 } 0420 0421 if (form.formData().isNull()) 0422 { 0423 qCDebug(DIGIKAM_WEBSERVICES_LOG) << "Form DATA null:"; 0424 } 0425 0426 QUrl url = QUrl(QLatin1String("https://upload.twitter.com/1.1/media/upload.json")); 0427 0428 QList<O0RequestParameter> reqParams = QList<O0RequestParameter>(); 0429 0430 QNetworkRequest request(url); 0431 request.setHeader(QNetworkRequest::ContentTypeHeader, form.contentType()); 0432 d->reply = d->requestor->post(request, reqParams, form.formData()); 0433 0434 d->state = Private::TW_ADDPHOTO; 0435 0436 return true; 0437 } 0438 0439 bool TwTalker::addPhotoInit(const QString& imgPath) 0440 { 0441 qCDebug(DIGIKAM_WEBSERVICES_LOG) << "addPhotoInit"; 0442 Q_EMIT signalBusy(true); 0443 0444 TwMPForm form; 0445 QByteArray mediaType, mediaCategory; 0446 QFileInfo fileInfo(imgPath); 0447 QString fileFormat(fileInfo.suffix()); 0448 0449 form.addPair(form.createPair("command", "INIT")); 0450 form.addPair(form.createPair("total_bytes", QString::number(QFileInfo(imgPath).size()).toLatin1())); 0451 0452 /* (Feb 2019) 0453 * Image file must be <= 5MB 0454 * Gif must be <= 15MB 0455 * Video must be <= 512MB 0456 */ 0457 0458 if (imageFormat.indexOf(fileFormat) != -1) 0459 { 0460 mediaType = "image/jpeg"; 0461 0462 if (fileFormat == QLatin1String("gif")) 0463 { 0464 0465 if (fileInfo.size() > 15728640) 0466 { 0467 Q_EMIT signalBusy(false); 0468 Q_EMIT signalAddPhotoFailed(i18n("File too big to upload")); 0469 0470 return false; 0471 } 0472 0473 mediaCategory = "TWEET_GIF"; 0474 } 0475 else 0476 { 0477 if (fileInfo.size() > 5242880) 0478 { 0479 Q_EMIT signalBusy(false); 0480 Q_EMIT signalAddPhotoFailed(i18n("File too big to upload")); 0481 0482 return false; 0483 } 0484 0485 mediaCategory = "TWEET_IMAGE"; 0486 } 0487 } 0488 else if (fileFormat == QLatin1String("mp4")) 0489 { 0490 if (fileInfo.size() > 536870912) 0491 { 0492 Q_EMIT signalBusy(false); 0493 Q_EMIT signalAddPhotoFailed(i18n("File too big to upload")); 0494 return false; 0495 } 0496 0497 mediaType = "video/mp4"; 0498 mediaCategory = "TWEET_VIDEO"; 0499 } 0500 else 0501 { 0502 Q_EMIT signalBusy(false); 0503 Q_EMIT signalAddPhotoFailed(i18n("Media format is not supported yet")); 0504 return false; 0505 } 0506 0507 form.addPair(form.createPair("media_type", mediaType)); 0508 form.addPair(form.createPair("media_category", mediaCategory)); 0509 form.finish(); 0510 0511 qCDebug(DIGIKAM_WEBSERVICES_LOG) << form.formData(); 0512 0513 QUrl url(d->uploadUrl); 0514 0515 QList<O0RequestParameter> reqParams = QList<O0RequestParameter>(); 0516 0517 QNetworkRequest request(url); 0518 request.setHeader(QNetworkRequest::ContentTypeHeader, form.contentType()); 0519 d->reply = d->requestor->post(request, reqParams, form.formData()); 0520 0521 d->mediaUploadedPath = imgPath; 0522 0523 d->state = Private::TW_UPLOADINIT; 0524 0525 return true; 0526 } 0527 0528 bool TwTalker::addPhotoAppend(const QString& mediaId, int segmentIndex) 0529 { 0530 qCDebug(DIGIKAM_WEBSERVICES_LOG) << "addPhotoAppend: "; 0531 0532 static TwMPForm form; 0533 0534 if (segmentIndex == 0) 0535 { 0536 form.addPair(form.createPair("command", "APPEND")); 0537 form.addPair(form.createPair("media_id", mediaId.toLatin1())); 0538 form.addFile(d->mediaUploadedPath, true); 0539 d->segmentIndex = form.numberOfChunks() - 1; 0540 } 0541 QByteArray data(form.formData()); 0542 data.append(form.createPair("segment_index", QString::number(segmentIndex).toLatin1())); 0543 data.append(form.createPair("media", form.getChunk(segmentIndex))); 0544 data.append(form.border()); 0545 0546 QUrl url(d->uploadUrl); 0547 QList<O0RequestParameter> reqParams = QList<O0RequestParameter>(); 0548 0549 QNetworkRequest request(url); 0550 request.setHeader(QNetworkRequest::ContentTypeHeader, form.contentType()); 0551 d->reply = d->requestor->post(request, reqParams, data); 0552 0553 d->state = Private::TW_UPLOADAPPEND; 0554 0555 // Reset form for later uploads 0556 0557 if (segmentIndex == d->segmentIndex) 0558 { 0559 form.reset(); 0560 } 0561 0562 return true; 0563 } 0564 0565 bool TwTalker::addPhotoFinalize(const QString& mediaId) 0566 { 0567 qCDebug(DIGIKAM_WEBSERVICES_LOG) << "addPhotoFinalize: "; 0568 0569 TwMPForm form; 0570 0571 form.addPair(form.createPair("command", "FINALIZE")); 0572 form.addPair(form.createPair("media_id", mediaId.toLatin1())); 0573 form.finish(); 0574 0575 qCDebug(DIGIKAM_WEBSERVICES_LOG) << form.formData(); 0576 0577 QUrl url(d->uploadUrl); 0578 0579 QList<O0RequestParameter> reqParams = QList<O0RequestParameter>(); 0580 0581 QNetworkRequest request(url); 0582 request.setHeader(QNetworkRequest::ContentTypeHeader, form.contentType()); 0583 d->reply = d->requestor->post(request, reqParams, form.formData()); 0584 0585 d->state = Private::TW_UPLOADFINALIZE; 0586 0587 return true; 0588 } 0589 0590 void TwTalker::getUserName() 0591 { 0592 /* 0593 * The endpoint below allows to get more than just account name (e.g. profile avatar, links to tweets posted, etc.) 0594 * Look at debug message printed to console for further ideas and exploration 0595 */ 0596 QUrl url(QLatin1String("https://api.twitter.com/1.1/account/verify_credentials.json")); 0597 0598 QNetworkRequest request(url); 0599 QList<O0RequestParameter> reqParams = QList<O0RequestParameter>(); 0600 0601 d->reply = d->requestor->get(request, reqParams); 0602 d->state = Private::TW_USERNAME; 0603 0604 Q_EMIT signalBusy(true); 0605 } 0606 0607 void TwTalker::createTweet(const QString& mediaId) 0608 { 0609 QUrl url = QUrl(QLatin1String("https://api.twitter.com/1.1/statuses/update.json")); 0610 0611 QList<O0RequestParameter> reqParams = QList<O0RequestParameter>(); 0612 reqParams << O0RequestParameter(QByteArray("status"), QByteArray("")); 0613 reqParams << O0RequestParameter(QByteArray("media_ids"), mediaId.toUtf8()); 0614 QByteArray postData = O1::createQueryParameters(reqParams); 0615 0616 QNetworkRequest request(url); 0617 request.setHeader(QNetworkRequest::ContentTypeHeader, QLatin1String(O2_MIME_TYPE_XFORM)); 0618 d->reply = d->requestor->post(request, reqParams, postData); 0619 0620 d->state = Private::TW_CREATETWEET; 0621 } 0622 0623 void TwTalker::slotCheckUploadStatus() 0624 { 0625 QUrl url = QUrl(d->uploadUrl); 0626 0627 QList<O0RequestParameter> reqParams = QList<O0RequestParameter>(); 0628 reqParams << O0RequestParameter(QByteArray("command"), QByteArray("STATUS")); 0629 reqParams << O0RequestParameter(QByteArray("media_id"), d->mediaId.toUtf8()); 0630 0631 QUrlQuery query; 0632 query.addQueryItem(QLatin1String("command"), QLatin1String("STATUS")); 0633 query.addQueryItem(QLatin1String("media_id"), d->mediaId); 0634 0635 url.setQuery(query); 0636 qCDebug(DIGIKAM_WEBSERVICES_LOG) << url.toString(); 0637 0638 QNetworkRequest request(url); 0639 d->reply = d->requestor->get(request, reqParams); 0640 d->state = Private::TW_UPLOADSTATUSCHECK; 0641 } 0642 0643 void TwTalker::slotFinished(QNetworkReply* reply) 0644 { 0645 if (reply != d->reply) 0646 { 0647 return; 0648 } 0649 0650 qCDebug(DIGIKAM_WEBSERVICES_LOG) << "TwTalker::slotFinished"; 0651 0652 d->reply = nullptr; 0653 0654 if (reply->error() != QNetworkReply::NoError) 0655 { 0656 if (d->state != Private::TW_CREATEFOLDER) 0657 { 0658 qCDebug(DIGIKAM_WEBSERVICES_LOG) << reply->readAll(); 0659 qCDebug(DIGIKAM_WEBSERVICES_LOG) << "status code: " << reply->attribute( QNetworkRequest::HttpStatusCodeAttribute ).toInt(); 0660 Q_EMIT signalBusy(false); 0661 QMessageBox::critical(QApplication::activeWindow(), 0662 i18nc("@title:window", "Error"), reply->errorString()); 0663 0664 reply->deleteLater(); 0665 return; 0666 } 0667 } 0668 0669 QByteArray buffer = reply->readAll(); 0670 qCDebug(DIGIKAM_WEBSERVICES_LOG) << "status code: " << reply->attribute( QNetworkRequest::HttpStatusCodeAttribute ).toInt(); 0671 static int segmentIndex = 0; 0672 0673 switch (d->state) 0674 { 0675 case Private::TW_LISTFOLDERS: 0676 qCDebug(DIGIKAM_WEBSERVICES_LOG) << "In TW_LISTFOLDERS"; 0677 parseResponseListFolders(buffer); 0678 break; 0679 0680 case Private::TW_CREATEFOLDER: 0681 qCDebug(DIGIKAM_WEBSERVICES_LOG) << "In TW_CREATEFOLDER"; 0682 parseResponseCreateFolder(buffer); 0683 break; 0684 0685 case Private::TW_ADDPHOTO: 0686 qCDebug(DIGIKAM_WEBSERVICES_LOG) << "In TW_ADDPHOTO"; 0687 parseResponseAddPhoto(buffer); 0688 break; 0689 0690 case Private::TW_USERNAME: 0691 qCDebug(DIGIKAM_WEBSERVICES_LOG) << "In TW_USERNAME"; 0692 parseResponseUserName(buffer); 0693 break; 0694 0695 case Private::TW_CREATETWEET: 0696 qCDebug(DIGIKAM_WEBSERVICES_LOG) << "In TW_CREATETWEET"; 0697 parseResponseCreateTweet(buffer); 0698 break; 0699 0700 case Private::TW_UPLOADINIT: 0701 segmentIndex = 0; 0702 qCDebug(DIGIKAM_WEBSERVICES_LOG) << "In TW_UPLOADINIT"; 0703 parseResponseAddPhotoInit(buffer); 0704 break; 0705 0706 case Private::TW_UPLOADAPPEND: 0707 qCDebug(DIGIKAM_WEBSERVICES_LOG) << "In TW_UPLOADAPPEND (at index " << segmentIndex << ")"; 0708 segmentIndex++; 0709 parseResponseAddPhotoAppend(buffer, segmentIndex); 0710 break; 0711 0712 case Private::TW_UPLOADSTATUSCHECK: 0713 qCDebug(DIGIKAM_WEBSERVICES_LOG) << "In TW_UPLOADSTATUSCHECK"; 0714 parseCheckUploadStatus(buffer); 0715 break; 0716 0717 case Private::TW_UPLOADFINALIZE: 0718 qCDebug(DIGIKAM_WEBSERVICES_LOG) << "In TW_UPLOADFINALIZE"; 0719 parseResponseAddPhotoFinalize(buffer); 0720 break; 0721 0722 default: 0723 break; 0724 } 0725 0726 reply->deleteLater(); 0727 } 0728 0729 void TwTalker::parseResponseAddPhoto(const QByteArray& data) 0730 { 0731 QJsonParseError err; 0732 QJsonDocument doc = QJsonDocument::fromJson(data, &err); 0733 qCDebug(DIGIKAM_WEBSERVICES_LOG) << "parseResponseAddPhoto: " << doc; 0734 0735 if (err.error != QJsonParseError::NoError) 0736 { 0737 Q_EMIT signalBusy(false); 0738 Q_EMIT signalAddPhotoFailed(i18n("Failed to upload photo")); 0739 return; 0740 } 0741 0742 QJsonObject jsonObject = doc.object(); 0743 QString mediaId = jsonObject[QLatin1String("media_id_string")].toString(); 0744 qCDebug(DIGIKAM_WEBSERVICES_LOG) << "media id: " << mediaId; 0745 0746 // We haven't Q_EMIT signalAddPhotoSucceeded() here yet, since we need to update the status first 0747 0748 createTweet(mediaId); 0749 } 0750 0751 void TwTalker::parseResponseAddPhotoInit(const QByteArray& data) 0752 { 0753 QJsonParseError err; 0754 QJsonDocument doc = QJsonDocument::fromJson(data, &err); 0755 qCDebug(DIGIKAM_WEBSERVICES_LOG) << "parseResponseAddPhotoInit: " << doc; 0756 0757 if (err.error != QJsonParseError::NoError) 0758 { 0759 Q_EMIT signalBusy(false); 0760 Q_EMIT signalAddPhotoFailed(i18n("Failed to upload photo")); 0761 return; 0762 } 0763 0764 QJsonObject jsonObject = doc.object(); 0765 d->mediaId = jsonObject[QLatin1String("media_id_string")].toString(); 0766 qCDebug(DIGIKAM_WEBSERVICES_LOG) << "media id: " << d->mediaId; 0767 0768 // We haven't Q_EMIT signalAddPhotoSucceeded() here yet, since we need to update the status first 0769 0770 addPhotoAppend(d->mediaId); 0771 } 0772 0773 void TwTalker::parseResponseAddPhotoAppend(const QByteArray& /*data*/, int segmentIndex) 0774 { 0775 /* (Fev. 2019) 0776 * Currently, we don't parse data of response from addPhotoAppend, since the response is with HTTP 204 0777 * This is indeed an expected response code, because the response should be with an empty body. 0778 * However, in order to keep a compatible prototype of parseResponse methodes and reserve for future change, 0779 * we should keep argument const QByteArray& data. 0780 */ 0781 qCDebug(DIGIKAM_WEBSERVICES_LOG) << "parseResponseAddPhotoAppend: "; 0782 0783 if (segmentIndex <= d->segmentIndex) 0784 { 0785 addPhotoAppend(d->mediaId, segmentIndex); 0786 } 0787 else 0788 { 0789 addPhotoFinalize(d->mediaId); 0790 } 0791 } 0792 0793 void TwTalker::parseResponseAddPhotoFinalize(const QByteArray& data) 0794 { 0795 QJsonParseError err; 0796 QJsonDocument doc = QJsonDocument::fromJson(data, &err); 0797 qCDebug(DIGIKAM_WEBSERVICES_LOG) << "parseResponseAddPhotoFinalize: " << doc; 0798 0799 if (err.error != QJsonParseError::NoError) 0800 { 0801 Q_EMIT signalBusy(false); 0802 Q_EMIT signalAddPhotoFailed(i18n("Failed to upload photo")); 0803 0804 return; 0805 } 0806 0807 QJsonObject jsonObject = doc.object(); 0808 QJsonValue processingInfo = jsonObject[QLatin1String("processing_info")]; 0809 0810 if (processingInfo != QJsonValue::Undefined) 0811 { 0812 QString state = processingInfo.toObject()[QLatin1String("state")].toString(); 0813 qCDebug(DIGIKAM_WEBSERVICES_LOG) << "state: " << state; 0814 0815 if (state == QLatin1String("pending")) 0816 { 0817 QTimer::singleShot(processingInfo.toObject()[QLatin1String("check_after_secs")].toInt()*1000 /*msec*/, 0818 this, SLOT(slotCheckUploadStatus())); 0819 } 0820 } 0821 else 0822 { 0823 // We haven't Q_EMIT signalAddPhotoSucceeded() here yet, since we need to update the status first 0824 0825 createTweet(d->mediaId); 0826 } 0827 } 0828 0829 void TwTalker::parseCheckUploadStatus(const QByteArray& data) 0830 { 0831 QJsonParseError err; 0832 QJsonDocument doc = QJsonDocument::fromJson(data, &err); 0833 qCDebug(DIGIKAM_WEBSERVICES_LOG) << "parseCheckUploadStatus: " << doc; 0834 0835 if (err.error != QJsonParseError::NoError) 0836 { 0837 Q_EMIT signalBusy(false); 0838 Q_EMIT signalAddPhotoFailed(i18n("Failed to upload photo")); 0839 return; 0840 } 0841 0842 QJsonObject jsonObject = doc.object(); 0843 QJsonObject processingInfo = jsonObject[QLatin1String("processing_info")].toObject(); 0844 QString state = processingInfo[QLatin1String("state")].toString(); 0845 0846 if (state == QLatin1String("in_progress")) 0847 { 0848 QTimer::singleShot(processingInfo[QLatin1String("check_after_secs")].toInt()*1000 /*msec*/, this, SLOT(slotCheckUploadStatus())); 0849 } 0850 else if (state == QLatin1String("failed")) 0851 { 0852 QJsonObject error = processingInfo[QLatin1String("error")].toObject(); 0853 Q_EMIT signalBusy(false); 0854 Q_EMIT signalAddPhotoFailed(i18n("Failed to upload photo\n" 0855 "Code: %1, name: %2, message: %3", 0856 error[QLatin1String("code")].toInt(), 0857 error[QLatin1String("name")].toString(), 0858 error[QLatin1String("message")].toString())); 0859 return; 0860 } 0861 else // succeeded 0862 { 0863 // We haven't Q_EMIT signalAddPhotoSucceeded() here yet, since we need to update the status first 0864 0865 createTweet(d->mediaId); 0866 } 0867 } 0868 0869 void TwTalker::parseResponseUserName(const QByteArray& data) 0870 { 0871 QJsonParseError err; 0872 QJsonDocument doc = QJsonDocument::fromJson(data, &err); 0873 qCDebug(DIGIKAM_WEBSERVICES_LOG) << "parseResponseUserName: "<<doc; 0874 0875 if (err.error != QJsonParseError::NoError) 0876 { 0877 Q_EMIT signalBusy(false); 0878 return; 0879 } 0880 0881 QJsonObject obj = doc.object(); 0882 QString name = obj[QLatin1String("name")].toString(); 0883 QString screenName = obj[QLatin1String("screen_name")].toString(); 0884 qCDebug(DIGIKAM_WEBSERVICES_LOG) << "user full name: "<<name; 0885 qCDebug(DIGIKAM_WEBSERVICES_LOG) << "user screen name: @" << screenName; 0886 0887 Q_EMIT signalBusy(false); 0888 Q_EMIT signalSetUserName(QString::fromLatin1("%1 (@%2)").arg(name).arg(screenName)); 0889 } 0890 0891 void TwTalker::parseResponseCreateTweet(const QByteArray& data) 0892 { 0893 QJsonParseError err; 0894 QJsonDocument doc = QJsonDocument::fromJson(data, &err); 0895 qCDebug(DIGIKAM_WEBSERVICES_LOG) << "parseResponseCreateTweet: " << doc; 0896 0897 if (err.error != QJsonParseError::NoError) 0898 { 0899 Q_EMIT signalBusy(false); 0900 Q_EMIT signalAddPhotoFailed(i18n("Failed to create tweet for photo uploaded")); 0901 0902 return; 0903 } 0904 0905 qCDebug(DIGIKAM_WEBSERVICES_LOG) << "Tweet posted successfully!"; 0906 Q_EMIT signalBusy(false); 0907 Q_EMIT signalAddPhotoSucceeded(); 0908 } 0909 0910 void TwTalker::parseResponseListFolders(const QByteArray& data) 0911 { 0912 QJsonParseError err; 0913 QJsonDocument doc = QJsonDocument::fromJson(data, &err); 0914 0915 if (err.error != QJsonParseError::NoError) 0916 { 0917 Q_EMIT signalBusy(false); 0918 Q_EMIT signalListAlbumsFailed(i18n("Failed to list folders")); 0919 return; 0920 } 0921 0922 QJsonObject jsonObject = doc.object(); 0923 0924 //qCDebug(DIGIKAM_WEBSERVICES_LOG) << "Json: " << doc; 0925 0926 QJsonArray jsonArray = jsonObject[QLatin1String("value")].toArray(); 0927 0928 //qCDebug(DIGIKAM_WEBSERVICES_LOG) << "Json response: " << jsonArray; 0929 0930 QList<QPair<QString, QString> > list; 0931 list.append(qMakePair(QLatin1String(""), QLatin1String("root"))); 0932 0933 Q_FOREACH (const QJsonValue& value, jsonArray) 0934 { 0935 QString path; 0936 QString folderName; 0937 QJsonObject folder; 0938 0939 QJsonObject obj = value.toObject(); 0940 folder = obj[QLatin1String("folder")].toObject(); 0941 0942 if (!folder.isEmpty()) 0943 { 0944 folderName = obj[QLatin1String("name")].toString(); 0945 path = QLatin1Char('/') + folderName; 0946 qCDebug(DIGIKAM_WEBSERVICES_LOG) << "Folder Name is" << folderName; 0947 list.append(qMakePair(path, folderName)); 0948 } 0949 } 0950 0951 Q_EMIT signalBusy(false); 0952 Q_EMIT signalListAlbumsDone(list); 0953 } 0954 0955 void TwTalker::parseResponseCreateFolder(const QByteArray& data) 0956 { 0957 QJsonDocument doc1 = QJsonDocument::fromJson(data); 0958 QJsonObject jsonObject = doc1.object(); 0959 bool fail = jsonObject.contains(QLatin1String("error")); 0960 0961 Q_EMIT signalBusy(false); 0962 0963 if (fail) 0964 { 0965 QJsonParseError err; 0966 QJsonDocument doc2 = QJsonDocument::fromJson(data, &err); 0967 qCDebug(DIGIKAM_WEBSERVICES_LOG) << "parseResponseCreateFolder ERROR: " << doc2; 0968 0969 Q_EMIT signalCreateFolderFailed(jsonObject[QLatin1String("error_summary")].toString()); 0970 } 0971 else 0972 { 0973 Q_EMIT signalCreateFolderSucceeded(); 0974 } 0975 } 0976 0977 } // namespace DigikamGenericTwitterPlugin 0978 0979 #include "moc_twittertalker.cpp"