Skip to content

Commit 30eb1da

Browse files
Async uploadScore()
1 parent e191d82 commit 30eb1da

File tree

4 files changed

+144
-135
lines changed

4 files changed

+144
-135
lines changed

src/framework/cloud/musescorecom/imusescorecomservice.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ class IMuseScoreComService : MODULE_EXPORT_INTERFACE
4848

4949
virtual QUrl scoreManagerUrl() const = 0;
5050

51-
virtual ProgressPtr uploadScore(QIODevice& scoreData, const QString& title, cloud::Visibility visibility = cloud::Visibility::Private,
51+
virtual ProgressPtr uploadScore(DevicePtr scoreData, const QString& title, cloud::Visibility visibility = cloud::Visibility::Private,
5252
const QUrl& sourceUrl = QUrl(), int revisionId = 0) = 0;
5353
virtual ProgressPtr uploadAudio(DevicePtr audioData, const QString& audioFormat, const QUrl& sourceUrl) = 0;
5454

src/framework/cloud/musescorecom/musescorecomservice.cpp

Lines changed: 132 additions & 119 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,6 @@
2929
#include <QJsonObject>
3030
#include <QRandomGenerator>
3131

32-
#include "async/async.h"
33-
3432
#include "clouderrors.h"
3533

3634
#include "log.h"
@@ -158,6 +156,76 @@ static RetVal<ScoreInfo> parseScoreInfo(const QByteArray& data)
158156
return RetVal<ScoreInfo>::make_ok(result);
159157
}
160158

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+
161229
static QHttpMultiPartPtr makeMultiPartForAudioUpload(QIODevice* audioData, const QString& audioFormat, const QUrl& sourceUrl)
162230
{
163231
auto multiPart = std::make_shared<QHttpMultiPart>(QHttpMultiPart::FormDataType);
@@ -467,152 +535,97 @@ Promise<Ret> MuseScoreComService::doDownloadScore(int scoreId, DevicePtr scoreDa
467535
});
468536
}
469537

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,
471539
int revisionId)
472540
{
473541
ProgressPtr progress = std::make_shared<Progress>();
542+
progress->start();
474543

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+
}
497550
});
498551

499552
return progress;
500553
}
501554

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)
504556
{
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+
});
513561
}
514562

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));
527567
}
568+
return resolve(info.ret);
528569
}
529570

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+
}
543575

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;
546581

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);
552583

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);
557587
}
558-
}
559-
560-
QHttpPart titlePart;
561-
titlePart.setHeader(QNetworkRequest::ContentDispositionHeader, QVariant("form-data; name=\"title\""));
562-
titlePart.setBody(title.toUtf8());
563-
multiPart.append(titlePart);
564588

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+
}
578593

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);
584595

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>();
587599

588-
result.ret = uploadingDownloadingRetFromRawRet(ret, isScoreAlreadyUploaded);
600+
RetVal<Progress> uploadProgress;
589601

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+
}
592607

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+
}
600611

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+
});
605615

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+
}
610621

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+
});
614626

615-
return result;
627+
return Promise<Ret>::dummy_result();
628+
});
616629
}
617630

618631
ProgressPtr MuseScoreComService::uploadAudio(DevicePtr audioData, const QString& audioFormat, const QUrl& sourceUrl)

src/framework/cloud/musescorecom/musescorecomservice.h

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ class MuseScoreComService : public IMuseScoreComService, public AbstractCloudSer
5050

5151
QUrl scoreManagerUrl() const override;
5252

53-
ProgressPtr uploadScore(QIODevice& scoreData, const QString& title, Visibility visibility = Visibility::Private,
53+
ProgressPtr uploadScore(DevicePtr scoreData, const QString& title, Visibility visibility = Visibility::Private,
5454
const QUrl& sourceUrl = QUrl(), int revisionId = 0) override;
5555
ProgressPtr uploadAudio(DevicePtr audioData, const QString& audioFormat, const QUrl& sourceUrl) override;
5656

@@ -73,8 +73,10 @@ class MuseScoreComService : public IMuseScoreComService, public AbstractCloudSer
7373
async::Promise<RetVal<ScoreInfo> > doDownloadScoreInfo(int scoreId);
7474
async::Promise<Ret> doDownloadScore(int scoreId, DevicePtr scoreData, const QString& hash, const QString& secret, ProgressPtr progress);
7575

76-
RetVal<ValMap> doUploadScore(network::deprecated::INetworkManagerPtr uploadManager, QIODevice& scoreData, const QString& title,
77-
Visibility visibility, const QUrl& sourceUrl = QUrl(), int revisionId = 0);
76+
async::Promise<RetVal<bool> > checkScoreAlreadyUploaded(const ID& scoreId);
77+
78+
async::Promise<Ret> doUploadScore(DevicePtr scoreData, const QString& title, Visibility visibility, const QUrl& sourceUrl,
79+
int revisionId, ProgressPtr progress);
7880

7981
async::Promise<Ret> doUploadAudio(DevicePtr audioData, const QString& audioFormat, const QUrl& sourceUrl, ProgressPtr progress);
8082
};

src/project/internal/projectactionscontroller.cpp

Lines changed: 6 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1250,13 +1250,12 @@ Ret ProjectActionsController::uploadProject(const CloudProjectInfo& info, const
12501250
return false;
12511251
}
12521252

1253-
QBuffer* projectData = new QBuffer();
1253+
auto projectData = std::make_shared<QBuffer>();
12541254
projectData->open(QIODevice::WriteOnly);
12551255

1256-
Ret ret = project->writeToDevice(projectData);
1256+
Ret ret = project->writeToDevice(projectData.get());
12571257
if (!ret) {
12581258
LOGE() << ret.toString();
1259-
delete projectData;
12601259
return ret;
12611260
}
12621261

@@ -1268,28 +1267,23 @@ Ret ProjectActionsController::uploadProject(const CloudProjectInfo& info, const
12681267
// The method must not return until the saving is complete, to prevent the app from being quit prematurely
12691268
QEventLoop eventLoop;
12701269

1271-
m_uploadingProjectProgress = museScoreComService()->uploadScore(*projectData, info.name, info.visibility, info.sourceUrl,
1270+
m_uploadingProjectProgress = museScoreComService()->uploadScore(projectData, info.name, info.visibility, info.sourceUrl,
12721271
info.revisionId);
1273-
1274-
m_uploadingProjectProgress->started().onNotify(this, [this]() {
1275-
showUploadProgressDialog();
1276-
LOGD() << "Uploading project started";
1277-
});
1272+
showUploadProgressDialog();
1273+
LOGD() << "Uploading project started";
12781274

12791275
m_uploadingProjectProgress->progressChanged().onReceive(this, [](int64_t current, int64_t total, const std::string&) {
12801276
if (total > 0) {
12811277
LOGD() << "Uploading project progress: " << current << " / " << total << " bytes";
12821278
}
12831279
});
12841280

1285-
m_uploadingProjectProgress->finished().onReceive(this, [this, project, projectData, info, audio, openEditUrl, publishMode,
1281+
m_uploadingProjectProgress->finished().onReceive(this, [this, project, info, audio, openEditUrl, publishMode,
12861282
isFirstSave, &ret, &eventLoop](const ProgressResult& res) {
12871283
DEFER {
12881284
eventLoop.quit();
12891285
};
12901286

1291-
projectData->deleteLater();
1292-
12931287
ret = res.ret;
12941288

12951289
if (!res.ret) {

0 commit comments

Comments
 (0)