From 513cae0d401abb68e7eca391a987a3dc1138b039 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Thu, 27 Aug 2015 10:23:02 -0700 Subject: [PATCH] thread the AssetUpload so it doesn't take over main --- assignment-client/src/assets/AssetServer.cpp | 5 -- .../src/octree/OctreePacketProcessor.cpp | 5 +- interface/src/ui/AssetUploadDialogFactory.cpp | 75 ++++++++++++++----- interface/src/ui/AssetUploadDialogFactory.h | 4 + libraries/networking/src/AssetClient.cpp | 29 +++++-- libraries/networking/src/AssetClient.h | 16 ++-- .../networking/src/AssetResourceRequest.cpp | 2 +- libraries/networking/src/AssetUpload.cpp | 53 +++++++++++++ libraries/networking/src/AssetUpload.h | 47 ++++++++++++ libraries/networking/src/AssetUtils.h | 2 + 10 files changed, 201 insertions(+), 37 deletions(-) create mode 100644 libraries/networking/src/AssetUpload.cpp create mode 100644 libraries/networking/src/AssetUpload.h diff --git a/assignment-client/src/assets/AssetServer.cpp b/assignment-client/src/assets/AssetServer.cpp index 2c294e652c..281257e904 100644 --- a/assignment-client/src/assets/AssetServer.cpp +++ b/assignment-client/src/assets/AssetServer.cpp @@ -80,11 +80,6 @@ void AssetServer::run() { file.rename(_resourcesDirectory.absoluteFilePath(hash)); } } - - while (!_isFinished) { - // since we're a while loop we need to help Qt's event processing - QCoreApplication::processEvents(); - } } void AssetServer::handleAssetGetInfo(QSharedPointer packet, SharedNodePointer senderNode) { diff --git a/interface/src/octree/OctreePacketProcessor.cpp b/interface/src/octree/OctreePacketProcessor.cpp index 5b8ff78fad..4a92e7e8ac 100644 --- a/interface/src/octree/OctreePacketProcessor.cpp +++ b/interface/src/octree/OctreePacketProcessor.cpp @@ -18,9 +18,8 @@ OctreePacketProcessor::OctreePacketProcessor() { auto& packetReceiver = DependencyManager::get()->getPacketReceiver(); - - packetReceiver.registerDirectListenerForTypes({ PacketType::OctreeStats, PacketType::EntityData, - PacketType::EntityErase, PacketType::OctreeStats }, + + packetReceiver.registerDirectListenerForTypes({ PacketType::OctreeStats, PacketType::EntityData, PacketType::EntityErase }, this, "handleOctreePacket"); } diff --git a/interface/src/ui/AssetUploadDialogFactory.cpp b/interface/src/ui/AssetUploadDialogFactory.cpp index 79e3cfa2db..a948ba54ba 100644 --- a/interface/src/ui/AssetUploadDialogFactory.cpp +++ b/interface/src/ui/AssetUploadDialogFactory.cpp @@ -12,10 +12,15 @@ #include "AssetUploadDialogFactory.h" #include +#include +#include #include -#include +#include #include +#include +#include +#include AssetUploadDialogFactory& AssetUploadDialogFactory::getInstance() { static AssetUploadDialogFactory staticInstance; @@ -27,29 +32,63 @@ AssetUploadDialogFactory::AssetUploadDialogFactory() { } void AssetUploadDialogFactory::showDialog() { - auto filename = QFileDialog::getOpenFileUrl(nullptr, "Select a file to upload"); + auto filename = QFileDialog::getOpenFileUrl(_dialogParent, "Select a file to upload"); if (!filename.isEmpty()) { qDebug() << "Selected filename for upload to asset-server: " << filename; - QFile file { filename.path() }; + auto assetClient = DependencyManager::get(); + auto upload = assetClient->createUpload(filename.path()); - if (file.open(QIODevice::ReadOnly)) { + if (upload) { + // connect to the finished signal so we know when the AssetUpload is done + QObject::connect(upload, &AssetUpload::finished, this, &AssetUploadDialogFactory::handleUploadFinished); - QFileInfo fileInfo { filename.path() }; - auto extension = fileInfo.suffix(); - - auto data = file.readAll(); - - auto assetClient = DependencyManager::get(); - - assetClient->uploadAsset(data, extension, [this, extension](bool result, QString hash) mutable { - if (result) { - QMessageBox::information(_dialogParent, "Upload Successful", "URL: apt:/" + hash + "." + extension); - } else { - QMessageBox::warning(_dialogParent, "Upload Failed", "There was an error uploading the file."); - } - }); + // start the upload now + upload->start(); } } } + +void AssetUploadDialogFactory::handleUploadFinished(AssetUpload* upload, const QString& hash) { + if (true) { + // show message box for successful upload, with copiable text for ATP hash + QDialog* hashCopyDialog = new QDialog(_dialogParent); + + // delete the dialog on close + hashCopyDialog->setAttribute(Qt::WA_DeleteOnClose); + + // set the window title + hashCopyDialog->setWindowTitle(tr("Successful Asset Upload")); + + // setup a layout for the contents of the dialog + QVBoxLayout* boxLayout = new QVBoxLayout; + + // set the label text (this shows above the text box) + QLabel* lineEditLabel = new QLabel; + lineEditLabel->setText(QString("ATP URL for %1").arg(upload->getFilename())); + + // setup the line edit to hold the copiable text + QLineEdit* lineEdit = new QLineEdit; + + // set the ATP URL as the text value so it's copiable + lineEdit->insert(QString("%1://%2").arg(ATP_SCHEME).arg(hash)); + + // add the label and line edit to the dialog + boxLayout->addWidget(lineEditLabel); + boxLayout->addWidget(lineEdit); + + // setup an OK button to close the dialog + QDialogButtonBox* buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok); + connect(buttonBox, &QDialogButtonBox::accepted, hashCopyDialog, &QDialog::close); + boxLayout->addWidget(buttonBox); + + // set the new layout on the dialog + hashCopyDialog->setLayout(boxLayout); + + // show the new dialog + hashCopyDialog->show(); + } else { + // + } +} diff --git a/interface/src/ui/AssetUploadDialogFactory.h b/interface/src/ui/AssetUploadDialogFactory.h index 3d830aa4bb..0d3372cb03 100644 --- a/interface/src/ui/AssetUploadDialogFactory.h +++ b/interface/src/ui/AssetUploadDialogFactory.h @@ -16,6 +16,8 @@ #include +class AssetUpload; + class AssetUploadDialogFactory : public QObject { Q_OBJECT public: @@ -28,6 +30,8 @@ public: void setDialogParent(QWidget* dialogParent) { _dialogParent = dialogParent; } public slots: void showDialog(); +private slots: + void handleUploadFinished(AssetUpload* upload, const QString& hash); private: QWidget* _dialogParent { nullptr }; }; diff --git a/libraries/networking/src/AssetClient.cpp b/libraries/networking/src/AssetClient.cpp index 3877f05fe9..78ddd5e6e0 100644 --- a/libraries/networking/src/AssetClient.cpp +++ b/libraries/networking/src/AssetClient.cpp @@ -14,6 +14,7 @@ #include #include "AssetRequest.h" +#include "AssetUpload.h" #include "NodeList.h" #include "PacketReceiver.h" @@ -27,7 +28,7 @@ AssetClient::AssetClient() { packetReceiver.registerListener(PacketType::AssetUploadReply, this, "handleAssetUploadReply"); } -AssetRequest* AssetClient::create(QString hash) { +AssetRequest* AssetClient::createRequest(QString hash) { if (QThread::currentThread() != thread()) { AssetRequest* req; QMetaObject::invokeMethod(this, "create", @@ -46,15 +47,32 @@ AssetRequest* AssetClient::create(QString hash) { SharedNodePointer assetServer = nodeList->soloNodeOfType(NodeType::AssetServer); if (assetServer) { - auto assetClient = DependencyManager::get(); - auto request = new AssetRequest(assetClient.data(), hash); - - return request; + return new AssetRequest(this, hash); } return nullptr; } +AssetUpload* AssetClient::createUpload(QString filename) { + if (QThread::currentThread() != thread()) { + AssetUpload* upload; + QMetaObject::invokeMethod(this, "createUpload", + Qt::BlockingQueuedConnection, + Q_RETURN_ARG(AssetUpload*, upload), + Q_ARG(QString, filename)); + return upload; + } + + auto nodeList = DependencyManager::get(); + SharedNodePointer assetServer = nodeList->soloNodeOfType(NodeType::AssetServer); + + if (assetServer) { + return new AssetUpload(this, filename); + } + + return nullptr; +} + bool AssetClient::getAsset(QString hash, DataOffset start, DataOffset end, ReceivedAssetCallback callback) { if (hash.length() != HASH_HEX_LENGTH) { qDebug() << "Invalid hash size"; @@ -156,6 +174,7 @@ void AssetClient::handleAssetGetReply(QSharedPointer packetList, S bool AssetClient::uploadAsset(QByteArray data, QString extension, UploadResultCallback callback) { auto nodeList = DependencyManager::get(); SharedNodePointer assetServer = nodeList->soloNodeOfType(NodeType::AssetServer); + if (assetServer) { auto packetList = std::unique_ptr(new NLPacketList(PacketType::AssetUpload, QByteArray(), true, true)); diff --git a/libraries/networking/src/AssetClient.h b/libraries/networking/src/AssetClient.h index 5e26ff18b6..9bd3b98be6 100644 --- a/libraries/networking/src/AssetClient.h +++ b/libraries/networking/src/AssetClient.h @@ -21,6 +21,7 @@ #include "NLPacket.h" class AssetRequest; +class AssetUpload; struct AssetInfo { QString hash; @@ -36,11 +37,8 @@ class AssetClient : public QObject, public Dependency { public: AssetClient(); - Q_INVOKABLE AssetRequest* create(QString hash); - bool getAssetInfo(QString hash, GetInfoCallback callback); - bool getAsset(QString hash, DataOffset start, DataOffset end, ReceivedAssetCallback callback); - bool uploadAsset(QByteArray data, QString extension, UploadResultCallback callback); - bool abortDataRequest(MessageID messageID); + Q_INVOKABLE AssetRequest* createRequest(QString hash); + Q_INVOKABLE AssetUpload* createUpload(QString filename); private slots: void handleAssetGetInfoReply(QSharedPointer packet, SharedNodePointer senderNode); @@ -48,10 +46,18 @@ private slots: void handleAssetUploadReply(QSharedPointer packet, SharedNodePointer senderNode); private: + bool getAssetInfo(QString hash, GetInfoCallback callback); + bool getAsset(QString hash, DataOffset start, DataOffset end, ReceivedAssetCallback callback); + bool uploadAsset(QByteArray data, QString extension, UploadResultCallback callback); + bool abortDataRequest(MessageID messageID); + static MessageID _currentID; QHash _pendingRequests; QHash _pendingInfoRequests; QHash _pendingUploads; + + friend class AssetRequest; + friend class AssetUpload; }; #endif diff --git a/libraries/networking/src/AssetResourceRequest.cpp b/libraries/networking/src/AssetResourceRequest.cpp index c29a54a787..7c58a5cb9b 100644 --- a/libraries/networking/src/AssetResourceRequest.cpp +++ b/libraries/networking/src/AssetResourceRequest.cpp @@ -18,7 +18,7 @@ void ATPResourceRequest::doSend() { auto assetClient = DependencyManager::get(); auto hash = _url.path(); - auto request = assetClient->create(hash); + auto request = assetClient->createRequest(hash); if (!request) { return; diff --git a/libraries/networking/src/AssetUpload.cpp b/libraries/networking/src/AssetUpload.cpp new file mode 100644 index 0000000000..806709a6d6 --- /dev/null +++ b/libraries/networking/src/AssetUpload.cpp @@ -0,0 +1,53 @@ +// +// AssetUpload.cpp +// libraries/networking/src +// +// Created by Stephen Birarda on 2015-08-26. +// Copyright 2015 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#include "AssetUpload.h" + +#include +#include + +#include "AssetClient.h" + +AssetUpload::AssetUpload(QObject* object, const QString& filename) : + _filename(filename) +{ + +} + +void AssetUpload::start() { + if (QThread::currentThread() != thread()) { + QMetaObject::invokeMethod(this, "start", Qt::AutoConnection); + return; + } + + // try to open the file at the given filename + QFile file { _filename }; + + if (file.open(QIODevice::ReadOnly)) { + + // file opened, read the data and grab the extension + auto extension = QFileInfo(_filename).suffix(); + + auto data = file.readAll(); + + // ask the AssetClient to upload the asset and emit the proper signals from the passed callback + auto assetClient = DependencyManager::get(); + + assetClient->uploadAsset(data, extension, [this](bool result, QString hash){ + if (result) { + // successful upload - emit finished with a point to ourselves and the resulting hash + emit finished(this, hash); + } else { + + } + }); + } +} diff --git a/libraries/networking/src/AssetUpload.h b/libraries/networking/src/AssetUpload.h new file mode 100644 index 0000000000..c8921fcf84 --- /dev/null +++ b/libraries/networking/src/AssetUpload.h @@ -0,0 +1,47 @@ +// +// AssetUpload.h +// libraries/networking/src +// +// Created by Stephen Birarda on 2015-08-26. +// Copyright 2015 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#pragma once + +#ifndef hifi_AssetUpload_h +#define hifi_AssetUpload_h + +#include + +// You should be able to upload an asset from any thread, and handle the responses in a safe way +// on your own thread. Everything should happen on AssetClient's thread, the caller should +// receive events by connecting to signals on an object that lives on AssetClient's threads. + +class AssetUpload : public QObject { + Q_OBJECT +public: + + enum Result { + Success = 0, + Timeout, + TooLarge, + }; + + AssetUpload(QObject* parent, const QString& filename); + + Q_INVOKABLE void start(); + + const QString& getFilename() const { return _filename; } + +signals: + void finished(AssetUpload* upload, const QString& hash); + void progress(uint64_t totalReceived, uint64_t total); + +private: + QString _filename; +}; + +#endif // hifi_AssetUpload_h diff --git a/libraries/networking/src/AssetUtils.h b/libraries/networking/src/AssetUtils.h index c23b54de5d..55091d7a7e 100644 --- a/libraries/networking/src/AssetUtils.h +++ b/libraries/networking/src/AssetUtils.h @@ -24,4 +24,6 @@ enum AssetServerError : uint8_t { INVALID_BYTE_RANGE, }; +const QString ATP_SCHEME = "atp"; + #endif