|
29 | 29 | #include <QJsonObject> |
30 | 30 | #include <QRandomGenerator> |
31 | 31 |
|
32 | | -#include "async/async.h" |
33 | | - |
34 | 32 | #include "clouderrors.h" |
35 | 33 |
|
36 | 34 | #include "log.h" |
@@ -158,6 +156,76 @@ static RetVal<ScoreInfo> parseScoreInfo(const QByteArray& data) |
158 | 156 | return RetVal<ScoreInfo>::make_ok(result); |
159 | 157 | } |
160 | 158 |
|
| 159 | +static RetVal<Val> parseScoreUploadResponse(const QByteArray& data) |
| 160 | +{ |
| 161 | + QJsonParseError err; |
| 162 | + QJsonDocument doc = QJsonDocument::fromJson(data, &err); |
| 163 | + if (err.error != QJsonParseError::NoError || !doc.isObject()) { |
| 164 | + return muse::make_ret(Ret::Code::InternalError, err.errorString().toStdString()); |
| 165 | + } |
| 166 | + |
| 167 | + QJsonObject scoreInfo = doc.object(); |
| 168 | + QUrl newSourceUrl = QUrl(scoreInfo.value("permalink").toString()); |
| 169 | + QUrl editUrl = QUrl(scoreInfo.value("edit_url").toString()); |
| 170 | + int newRevisionId = scoreInfo.value("revision_id").toInt(); |
| 171 | + |
| 172 | + if (!newSourceUrl.isValid()) { |
| 173 | + return make_ret(cloud::Err::CouldNotReceiveSourceUrl); |
| 174 | + } |
| 175 | + |
| 176 | + ValMap map; |
| 177 | + map["sourceUrl"] = Val(newSourceUrl.toString()); |
| 178 | + map["editUrl"] = Val(editUrl.toString()); |
| 179 | + map["revisionId"] = Val(newRevisionId); |
| 180 | + |
| 181 | + return RetVal<Val>::make_ok(Val(map)); |
| 182 | +} |
| 183 | + |
| 184 | +static QHttpMultiPartPtr makeMultiPartForScoreUpload(QIODevice* scoreData, int scoreId, const QString& title, |
| 185 | + Visibility visibility, int revisionId, |
| 186 | + const QString& licence, bool isScoreAlreadyUploaded) |
| 187 | +{ |
| 188 | + auto multiPart = std::make_shared<QHttpMultiPart>(QHttpMultiPart::FormDataType); |
| 189 | + |
| 190 | + QHttpPart filePart; |
| 191 | + filePart.setHeader(QNetworkRequest::ContentTypeHeader, QVariant("application/octet-stream")); |
| 192 | + QString contentDisposition = QString("form-data; name=\"score_data\"; filename=\"temp_%1.mscz\"").arg(generateFileNameNumber()); |
| 193 | + filePart.setHeader(QNetworkRequest::ContentDispositionHeader, QVariant(contentDisposition)); |
| 194 | + |
| 195 | + filePart.setBodyDevice(scoreData); |
| 196 | + multiPart->append(filePart); |
| 197 | + |
| 198 | + if (isScoreAlreadyUploaded) { |
| 199 | + QHttpPart scoreIdPart; |
| 200 | + scoreIdPart.setHeader(QNetworkRequest::ContentDispositionHeader, QVariant("form-data; name=\"score_id\"")); |
| 201 | + scoreIdPart.setBody(QString::number(scoreId).toLatin1()); |
| 202 | + multiPart->append(scoreIdPart); |
| 203 | + |
| 204 | + if (revisionId) { |
| 205 | + scoreIdPart.setHeader(QNetworkRequest::ContentDispositionHeader, QVariant("form-data; name=\"last_revision_id\"")); |
| 206 | + scoreIdPart.setBody(QByteArray::number(revisionId)); |
| 207 | + multiPart->append(scoreIdPart); |
| 208 | + } |
| 209 | + } |
| 210 | + |
| 211 | + QHttpPart titlePart; |
| 212 | + titlePart.setHeader(QNetworkRequest::ContentDispositionHeader, QVariant("form-data; name=\"title\"")); |
| 213 | + titlePart.setBody(title.toUtf8()); |
| 214 | + multiPart->append(titlePart); |
| 215 | + |
| 216 | + QHttpPart privacyPart; |
| 217 | + privacyPart.setHeader(QNetworkRequest::ContentDispositionHeader, QVariant("form-data; name=\"privacy\"")); |
| 218 | + privacyPart.setBody(QByteArray::number(int(visibility))); |
| 219 | + multiPart->append(privacyPart); |
| 220 | + |
| 221 | + QHttpPart licensePart; |
| 222 | + licensePart.setHeader(QNetworkRequest::ContentDispositionHeader, QVariant("form-data; name=\"license\"")); |
| 223 | + licensePart.setBody(licence.toUtf8()); |
| 224 | + multiPart->append(licensePart); |
| 225 | + |
| 226 | + return multiPart; |
| 227 | +} |
| 228 | + |
161 | 229 | static QHttpMultiPartPtr makeMultiPartForAudioUpload(QIODevice* audioData, const QString& audioFormat, const QUrl& sourceUrl) |
162 | 230 | { |
163 | 231 | auto multiPart = std::make_shared<QHttpMultiPart>(QHttpMultiPart::FormDataType); |
@@ -467,152 +535,97 @@ Promise<Ret> MuseScoreComService::doDownloadScore(int scoreId, DevicePtr scoreDa |
467 | 535 | }); |
468 | 536 | } |
469 | 537 |
|
470 | | -ProgressPtr MuseScoreComService::uploadScore(QIODevice& scoreData, const QString& title, Visibility visibility, const QUrl& sourceUrl, |
| 538 | +ProgressPtr MuseScoreComService::uploadScore(DevicePtr scoreData, const QString& title, Visibility visibility, const QUrl& sourceUrl, |
471 | 539 | int revisionId) |
472 | 540 | { |
473 | 541 | ProgressPtr progress = std::make_shared<Progress>(); |
| 542 | + progress->start(); |
474 | 543 |
|
475 | | - deprecated::INetworkManagerPtr manager = networkManagerCreator()->makeDeprecatedNetworkManager(); |
476 | | - manager->progress().progressChanged().onReceive(this, [progress](int64_t current, int64_t total, const std::string& message) { |
477 | | - progress->progress(current, total, message); |
478 | | - }); |
479 | | - |
480 | | - std::shared_ptr<ValMap> scoreUrlMap = std::make_shared<ValMap>(); |
481 | | - |
482 | | - auto uploadCallback = [this, manager, &scoreData, title, visibility, sourceUrl, revisionId, scoreUrlMap]() { |
483 | | - RetVal<ValMap> urlMap = doUploadScore(manager, scoreData, title, visibility, sourceUrl, revisionId); |
484 | | - *scoreUrlMap = urlMap.val; |
485 | | - |
486 | | - return urlMap.ret; |
487 | | - }; |
488 | | - |
489 | | - async::Async::call(this, [this, progress, uploadCallback, scoreUrlMap]() { |
490 | | - progress->start(); |
491 | | - |
492 | | - ProgressResult result; |
493 | | - result.ret = executeRequest(uploadCallback); |
494 | | - result.val = Val(*scoreUrlMap); |
495 | | - |
496 | | - progress->finish(result); |
| 544 | + executeAsyncRequest([this, scoreData, title, visibility, sourceUrl, revisionId, progress]() { |
| 545 | + return doUploadScore(scoreData, title, visibility, sourceUrl, revisionId, progress); |
| 546 | + }).onResolve(this, [progress](const Ret& ret) { |
| 547 | + if (progress->isStarted()) { |
| 548 | + progress->finish(ret); |
| 549 | + } |
497 | 550 | }); |
498 | 551 |
|
499 | 552 | return progress; |
500 | 553 | } |
501 | 554 |
|
502 | | -RetVal<ValMap> MuseScoreComService::doUploadScore(deprecated::INetworkManagerPtr uploadManager, QIODevice& scoreData, const QString& title, |
503 | | - Visibility visibility, const QUrl& sourceUrl, int revisionId) |
| 555 | +async::Promise<RetVal<bool> > MuseScoreComService::checkScoreAlreadyUploaded(const ID& scoreId) |
504 | 556 | { |
505 | | - TRACEFUNC; |
506 | | - |
507 | | - RetVal<ValMap> result = RetVal<ValMap>::make_ok(ValMap()); |
508 | | - |
509 | | - RetVal<QUrl> uploadUrl = prepareUrlForRequest(MUSESCORECOM_UPLOAD_SCORE_API_URL); |
510 | | - if (!uploadUrl.ret) { |
511 | | - result.ret = uploadUrl.ret; |
512 | | - return result; |
| 557 | + if (scoreId == INVALID_ID) { |
| 558 | + return Promise<RetVal<bool> >([](auto resolve, auto) { |
| 559 | + return resolve(RetVal<bool>::make_ok(false)); |
| 560 | + }); |
513 | 561 | } |
514 | 562 |
|
515 | | - ID scoreId = idFromCloudUrl(sourceUrl); |
516 | | - bool isScoreAlreadyUploaded = scoreId != INVALID_ID; |
517 | | - |
518 | | - if (isScoreAlreadyUploaded) { |
519 | | - RetVal<ScoreInfo> scoreInfo = downloadScoreInfo(scoreId.toUint64()); |
520 | | - |
521 | | - if (!scoreInfo.ret) { |
522 | | - if (statusCode(scoreInfo.ret) == NOT_FOUND_STATUS_CODE) { |
523 | | - isScoreAlreadyUploaded = false; |
524 | | - } else { |
525 | | - result.ret = scoreInfo.ret; |
526 | | - return result; |
| 563 | + return doDownloadScoreInfo(scoreId.toUint64()).then<RetVal<bool> >(this, [this](const RetVal<ScoreInfo>& info, auto resolve) { |
| 564 | + if (!info.ret) { |
| 565 | + if (statusCode(info.ret) == NOT_FOUND_STATUS_CODE) { |
| 566 | + return resolve(RetVal<bool>::make_ok(false)); |
527 | 567 | } |
| 568 | + return resolve(info.ret); |
528 | 569 | } |
529 | 570 |
|
530 | | - if (scoreInfo.val.owner.id != accountInfo().id.toInt()) { |
531 | | - isScoreAlreadyUploaded = false; |
532 | | - } |
533 | | - } |
534 | | - |
535 | | - scoreData.seek(0); |
536 | | - |
537 | | - QHttpMultiPart multiPart(QHttpMultiPart::FormDataType); |
538 | | - |
539 | | - QHttpPart filePart; |
540 | | - filePart.setHeader(QNetworkRequest::ContentTypeHeader, QVariant("application/octet-stream")); |
541 | | - QString contentDisposition = QString("form-data; name=\"score_data\"; filename=\"temp_%1.mscz\"").arg(generateFileNameNumber()); |
542 | | - filePart.setHeader(QNetworkRequest::ContentDispositionHeader, QVariant(contentDisposition)); |
| 571 | + const bool accountOwnsScore = info.val.owner.id == accountInfo().id.toInt(); |
| 572 | + return resolve(RetVal<bool>::make_ok(accountOwnsScore)); |
| 573 | + }); |
| 574 | +} |
543 | 575 |
|
544 | | - filePart.setBodyDevice(&scoreData); |
545 | | - multiPart.append(filePart); |
| 576 | +Promise<Ret> MuseScoreComService::doUploadScore(DevicePtr scoreData, const QString& title, |
| 577 | + Visibility visibility, const QUrl& sourceUrl, int revisionId, |
| 578 | + ProgressPtr progress) |
| 579 | +{ |
| 580 | + TRACEFUNC; |
546 | 581 |
|
547 | | - if (isScoreAlreadyUploaded) { |
548 | | - QHttpPart scoreIdPart; |
549 | | - scoreIdPart.setHeader(QNetworkRequest::ContentDispositionHeader, QVariant("form-data; name=\"score_id\"")); |
550 | | - scoreIdPart.setBody(QString::number(scoreId.toUint64()).toLatin1()); |
551 | | - multiPart.append(scoreIdPart); |
| 582 | + const ID scoreId = idFromCloudUrl(sourceUrl); |
552 | 583 |
|
553 | | - if (revisionId) { |
554 | | - scoreIdPart.setHeader(QNetworkRequest::ContentDispositionHeader, QVariant("form-data; name=\"last_revision_id\"")); |
555 | | - scoreIdPart.setBody(QByteArray::number(revisionId)); |
556 | | - multiPart.append(scoreIdPart); |
| 584 | + return checkScoreAlreadyUploaded(scoreId).then<Ret>(this, [=](const RetVal<bool>& alreadyUploaded, auto resolve) { |
| 585 | + if (!alreadyUploaded.ret) { |
| 586 | + return resolve(alreadyUploaded.ret); |
557 | 587 | } |
558 | | - } |
559 | | - |
560 | | - QHttpPart titlePart; |
561 | | - titlePart.setHeader(QNetworkRequest::ContentDispositionHeader, QVariant("form-data; name=\"title\"")); |
562 | | - titlePart.setBody(title.toUtf8()); |
563 | | - multiPart.append(titlePart); |
564 | 588 |
|
565 | | - QHttpPart privacyPart; |
566 | | - privacyPart.setHeader(QNetworkRequest::ContentDispositionHeader, QVariant("form-data; name=\"privacy\"")); |
567 | | - privacyPart.setBody(QByteArray::number(int(visibility))); |
568 | | - multiPart.append(privacyPart); |
569 | | - |
570 | | - QHttpPart licensePart; |
571 | | - licensePart.setHeader(QNetworkRequest::ContentDispositionHeader, QVariant("form-data; name=\"license\"")); |
572 | | - licensePart.setBody(configuration()->uploadingLicense()); |
573 | | - multiPart.append(licensePart); |
574 | | - |
575 | | - Ret ret(true); |
576 | | - QBuffer receivedData; |
577 | | - OutgoingDevice device(&multiPart); |
| 589 | + RetVal<QUrl> uploadUrl = prepareUrlForRequest(MUSESCORECOM_UPLOAD_SCORE_API_URL); |
| 590 | + if (!uploadUrl.ret) { |
| 591 | + return resolve(uploadUrl.ret); |
| 592 | + } |
578 | 593 |
|
579 | | - if (isScoreAlreadyUploaded) { // score exists, update |
580 | | - ret = uploadManager->put(uploadUrl.val, &device, &receivedData, headers()); |
581 | | - } else { // score doesn't exist, post a new score |
582 | | - ret = uploadManager->post(uploadUrl.val, &device, &receivedData, headers()); |
583 | | - } |
| 594 | + scoreData->seek(0); |
584 | 595 |
|
585 | | - if (!ret) { |
586 | | - printServerReply(receivedData); |
| 596 | + auto multiPart = makeMultiPartForScoreUpload(scoreData.get(), scoreId.toUint64(), title, visibility, revisionId, |
| 597 | + configuration()->uploadingLicense(), alreadyUploaded.val); |
| 598 | + auto receivedData = std::make_shared<QBuffer>(); |
587 | 599 |
|
588 | | - result.ret = uploadingDownloadingRetFromRawRet(ret, isScoreAlreadyUploaded); |
| 600 | + RetVal<Progress> uploadProgress; |
589 | 601 |
|
590 | | - return result; |
591 | | - } |
| 602 | + if (alreadyUploaded.val) { // score exists, update |
| 603 | + uploadProgress = m_networkManager->put(uploadUrl.val, multiPart, receivedData, headers()); |
| 604 | + } else { // score doesn't exist, post a new score |
| 605 | + uploadProgress = m_networkManager->post(uploadUrl.val, multiPart, receivedData, headers()); |
| 606 | + } |
592 | 607 |
|
593 | | - QJsonParseError err; |
594 | | - QJsonDocument doc = QJsonDocument::fromJson(receivedData.data(), &err); |
595 | | - if (err.error != QJsonParseError::NoError || !doc.isObject()) { |
596 | | - LOGE() << err.errorString(); |
597 | | - result.ret = muse::make_ret(Ret::Code::InternalError, err.errorString().toStdString()); |
598 | | - return result; |
599 | | - } |
| 608 | + if (!uploadProgress.ret) { |
| 609 | + return resolve(uploadProgress.ret); |
| 610 | + } |
600 | 611 |
|
601 | | - QJsonObject scoreInfo = doc.object(); |
602 | | - QUrl newSourceUrl = QUrl(scoreInfo.value("permalink").toString()); |
603 | | - QUrl editUrl = QUrl(scoreInfo.value("edit_url").toString()); |
604 | | - int newRevisionId = scoreInfo.value("revision_id").toInt(); |
| 612 | + uploadProgress.val.progressChanged().onReceive(this, [progress](int64_t current, int64_t total, const std::string& msg) { |
| 613 | + progress->progress(current, total, msg); |
| 614 | + }); |
605 | 615 |
|
606 | | - if (!newSourceUrl.isValid()) { |
607 | | - result.ret = make_ret(cloud::Err::CouldNotReceiveSourceUrl); |
608 | | - return result; |
609 | | - } |
| 616 | + uploadProgress.val.finished().onReceive(this, [this, alreadyUploaded, receivedData, resolve, progress](const ProgressResult& res) { |
| 617 | + if (!res.ret) { |
| 618 | + (void)resolve(uploadingDownloadingRetFromRawRet(res.ret, alreadyUploaded.val)); |
| 619 | + return; |
| 620 | + } |
610 | 621 |
|
611 | | - result.val["sourceUrl"] = Val(newSourceUrl.toString()); |
612 | | - result.val["editUrl"] = Val(editUrl.toString()); |
613 | | - result.val["revisionId"] = Val(newRevisionId); |
| 622 | + ProgressResult result = parseScoreUploadResponse(receivedData->data()); |
| 623 | + progress->finish(result); |
| 624 | + (void)resolve(make_ok()); |
| 625 | + }); |
614 | 626 |
|
615 | | - return result; |
| 627 | + return Promise<Ret>::dummy_result(); |
| 628 | + }); |
616 | 629 | } |
617 | 630 |
|
618 | 631 | ProgressPtr MuseScoreComService::uploadAudio(DevicePtr audioData, const QString& audioFormat, const QUrl& sourceUrl) |
|
0 commit comments