mirror of
https://github.com/overte-org/overte.git
synced 2025-08-09 13:28:09 +02:00
Integrate marketplace upload API
This commit is contained in:
parent
2269447741
commit
ad471387f7
12 changed files with 386 additions and 52 deletions
|
@ -28,6 +28,10 @@ Original.Button {
|
||||||
width: hifi.dimensions.buttonWidth
|
width: hifi.dimensions.buttonWidth
|
||||||
height: hifi.dimensions.controlLineHeight
|
height: hifi.dimensions.controlLineHeight
|
||||||
|
|
||||||
|
property size implicitPadding: Qt.size(20, 16)
|
||||||
|
property int implicitWidth: content.implicitWidth + implicitPadding.width
|
||||||
|
property int implicitHeight: content.implicitHeight + implicitPadding.height
|
||||||
|
|
||||||
HifiConstants { id: hifi }
|
HifiConstants { id: hifi }
|
||||||
|
|
||||||
onHoveredChanged: {
|
onHoveredChanged: {
|
||||||
|
@ -89,6 +93,9 @@ Original.Button {
|
||||||
}
|
}
|
||||||
|
|
||||||
contentItem: Item {
|
contentItem: Item {
|
||||||
|
id: content
|
||||||
|
implicitWidth: (buttonGlyph.visible ? buttonGlyph.implicitWidth : 0) + buttonText.implicitWidth
|
||||||
|
implicitHeight: buttonText.implicitHeight
|
||||||
HiFiGlyphs {
|
HiFiGlyphs {
|
||||||
id: buttonGlyph;
|
id: buttonGlyph;
|
||||||
visible: control.buttonGlyph !== "";
|
visible: control.buttonGlyph !== "";
|
||||||
|
|
|
@ -47,7 +47,7 @@ Windows.ScrollingWindow {
|
||||||
anchors.top: parent.top
|
anchors.top: parent.top
|
||||||
anchors.topMargin: 25
|
anchors.topMargin: 25
|
||||||
anchors.bottomMargin: 25
|
anchors.bottomMargin: 25
|
||||||
text: 'Avatar Packager'
|
text: 'Avatar Packager ' + parent.width + " " + parent.height
|
||||||
}
|
}
|
||||||
|
|
||||||
HifiControls.Button {
|
HifiControls.Button {
|
||||||
|
@ -72,6 +72,7 @@ Windows.ScrollingWindow {
|
||||||
height: 30
|
height: 30
|
||||||
onClicked: function() {
|
onClicked: function() {
|
||||||
var avatarProjectsPath = fileDialogHelper.standardPath(/*fileDialogHelper.StandardLocation.DocumentsLocation*/ 1) + "/High Fidelity/Avatar Projects";
|
var avatarProjectsPath = fileDialogHelper.standardPath(/*fileDialogHelper.StandardLocation.DocumentsLocation*/ 1) + "/High Fidelity/Avatar Projects";
|
||||||
|
var avatarProjectsPath = "C:/Users/ryanh/Documents/High Fidelity Avatars";
|
||||||
console.log("path = " + avatarProjectsPath);
|
console.log("path = " + avatarProjectsPath);
|
||||||
|
|
||||||
// TODO: make the dialog modal
|
// TODO: make the dialog modal
|
||||||
|
|
|
@ -3,6 +3,8 @@ import QtQuick.Controls 2.2
|
||||||
import QtQuick.Layouts 1.3
|
import QtQuick.Layouts 1.3
|
||||||
import QtGraphicalEffects 1.0
|
import QtGraphicalEffects 1.0
|
||||||
|
|
||||||
|
import QtQuick.Controls 2.2 as Original
|
||||||
|
|
||||||
import "../../controlsUit" 1.0 as HifiControls
|
import "../../controlsUit" 1.0 as HifiControls
|
||||||
import "../../stylesUit" 1.0
|
import "../../stylesUit" 1.0
|
||||||
|
|
||||||
|
@ -12,6 +14,7 @@ Item {
|
||||||
HifiConstants { id: hifi }
|
HifiConstants { id: hifi }
|
||||||
|
|
||||||
property int colorScheme;
|
property int colorScheme;
|
||||||
|
property var uploader: undefined;
|
||||||
|
|
||||||
visible: true
|
visible: true
|
||||||
|
|
||||||
|
@ -58,13 +61,118 @@ Item {
|
||||||
}
|
}
|
||||||
HifiControls.Button {
|
HifiControls.Button {
|
||||||
id: uploadButton
|
id: uploadButton
|
||||||
|
|
||||||
width: parent.width
|
width: parent.width
|
||||||
|
height: 30
|
||||||
anchors.bottom: parent.bottom
|
anchors.bottom: parent.bottom
|
||||||
|
|
||||||
text: qsTr("Upload")
|
text: qsTr("Upload")
|
||||||
color: hifi.buttons.blue
|
color: hifi.buttons.blue
|
||||||
colorScheme: root.colorScheme
|
colorScheme: root.colorScheme
|
||||||
height: 30
|
|
||||||
onClicked: function() {
|
onClicked: function() {
|
||||||
|
console.log("Uploading");
|
||||||
|
parent.uploader = AvatarPackagerCore.currentAvatarProject.upload();
|
||||||
|
console.log("uploader: "+ parent.uploader);
|
||||||
|
parent.uploader.uploadProgress.connect(function(uploaded, total) {
|
||||||
|
console.log("Uploader progress: " + uploaded + " / " + total);
|
||||||
|
});
|
||||||
|
parent.uploader.completed.connect(function() {
|
||||||
|
try {
|
||||||
|
var response = JSON.parse(parent.uploader.responseData);
|
||||||
|
console.log("Uploader complete! " + response);
|
||||||
|
uploadStatus.text = response.status;
|
||||||
|
} catch (e) {
|
||||||
|
console.log("Error parsing JSON: " + parent.uploader.reponseData);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
parent.uploader.send();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: uploadingScreen
|
||||||
|
|
||||||
|
visible: !!root.uploader
|
||||||
|
anchors.fill: parent
|
||||||
|
|
||||||
|
color: "black"
|
||||||
|
|
||||||
|
Item {
|
||||||
|
visible: !!root.uploader && !root.uploader.complete
|
||||||
|
|
||||||
|
anchors.fill: parent
|
||||||
|
|
||||||
|
AnimatedImage {
|
||||||
|
id: uploadSpinner
|
||||||
|
|
||||||
|
anchors {
|
||||||
|
horizontalCenter: parent.horizontalCenter
|
||||||
|
verticalCenter: parent.verticalCenter
|
||||||
|
}
|
||||||
|
|
||||||
|
source: "../../../icons/loader-snake-64-w.gif"
|
||||||
|
playing: true
|
||||||
|
z: 10000
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Item {
|
||||||
|
visible: !!root.uploader && root.uploader.complete
|
||||||
|
|
||||||
|
anchors.fill: parent
|
||||||
|
|
||||||
|
HiFiGlyphs {
|
||||||
|
id: successIcon
|
||||||
|
|
||||||
|
anchors {
|
||||||
|
horizontalCenter: parent.horizontalCenter
|
||||||
|
verticalCenter: parent.verticalCenter
|
||||||
|
}
|
||||||
|
|
||||||
|
size: 128
|
||||||
|
text: "\ue01a"
|
||||||
|
color: "#1FC6A6"
|
||||||
|
}
|
||||||
|
|
||||||
|
Text {
|
||||||
|
text: "Congratulations!"
|
||||||
|
|
||||||
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
anchors.top: successIcon.bottom
|
||||||
|
|
||||||
|
color: "white"
|
||||||
|
}
|
||||||
|
|
||||||
|
HifiControls.Button {
|
||||||
|
width: implicitWidth
|
||||||
|
height: implicitHeight
|
||||||
|
|
||||||
|
anchors.bottom: parent.bottom
|
||||||
|
anchors.right: parent.right
|
||||||
|
|
||||||
|
text: "View in Inventory"
|
||||||
|
|
||||||
|
color: hifi.buttons.blue
|
||||||
|
colorScheme: root.colorScheme
|
||||||
|
onClicked: function() {
|
||||||
|
console.log("Opening in inventory");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Column {
|
||||||
|
Text {
|
||||||
|
id: uploadStatus
|
||||||
|
|
||||||
|
text: "Uploading"
|
||||||
|
color: "white"
|
||||||
|
|
||||||
|
}
|
||||||
|
Text {
|
||||||
|
text: "State: " + (!!root.uploader ? root.uploader.state : " NONE")
|
||||||
|
color: "white"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -460,7 +460,7 @@ public:
|
||||||
// Don't actually crash in debug builds, in case this apparent deadlock is simply from
|
// Don't actually crash in debug builds, in case this apparent deadlock is simply from
|
||||||
// the developer actively debugging code
|
// the developer actively debugging code
|
||||||
#ifdef NDEBUG
|
#ifdef NDEBUG
|
||||||
deadlockDetectionCrash();
|
//deadlockDetectionCrash();
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -536,6 +536,7 @@ void AvatarManager::handleCollisionEvents(const CollisionEvents& collisionEvents
|
||||||
// my avatar. (Other user machines will make a similar analysis and inject sound for their collisions.)
|
// my avatar. (Other user machines will make a similar analysis and inject sound for their collisions.)
|
||||||
if (collision.idA.isNull() || collision.idB.isNull()) {
|
if (collision.idA.isNull() || collision.idB.isNull()) {
|
||||||
auto myAvatar = getMyAvatar();
|
auto myAvatar = getMyAvatar();
|
||||||
|
myAvatar->collisionWithEntity(collision);
|
||||||
auto collisionSound = myAvatar->getCollisionSound();
|
auto collisionSound = myAvatar->getCollisionSound();
|
||||||
if (collisionSound) {
|
if (collisionSound) {
|
||||||
const auto characterController = myAvatar->getCharacterController();
|
const auto characterController = myAvatar->getCharacterController();
|
||||||
|
@ -571,9 +572,8 @@ void AvatarManager::handleCollisionEvents(const CollisionEvents& collisionEvents
|
||||||
auto injector = AudioInjector::playSoundAndDelete(collisionSound, options);
|
auto injector = AudioInjector::playSoundAndDelete(collisionSound, options);
|
||||||
_collisionInjectors.emplace_back(injector);
|
_collisionInjectors.emplace_back(injector);
|
||||||
}
|
}
|
||||||
myAvatar->collisionWithEntity(collision);
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,6 +25,7 @@ std::once_flag setupQMLTypesFlag;
|
||||||
AvatarPackager::AvatarPackager() {
|
AvatarPackager::AvatarPackager() {
|
||||||
std::call_once(setupQMLTypesFlag, []() {
|
std::call_once(setupQMLTypesFlag, []() {
|
||||||
qmlRegisterType<FST>();
|
qmlRegisterType<FST>();
|
||||||
|
qmlRegisterType<MarketplaceItemUploader>();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -47,8 +48,3 @@ QObject* AvatarPackager::openAvatarProject(QString avatarProjectFSTPath) {
|
||||||
emit avatarProjectChanged();
|
emit avatarProjectChanged();
|
||||||
return _currentAvatarProject;
|
return _currentAvatarProject;
|
||||||
}
|
}
|
||||||
|
|
||||||
QObject* AvatarPackager::uploadItem() {
|
|
||||||
std::vector<QString> filePaths;
|
|
||||||
return new MarketplaceItemUploader(QUuid(), filePaths);
|
|
||||||
}
|
|
||||||
|
|
|
@ -35,7 +35,6 @@ signals:
|
||||||
private:
|
private:
|
||||||
Q_INVOKABLE AvatarProject* getAvatarProject() const { return _currentAvatarProject; };
|
Q_INVOKABLE AvatarProject* getAvatarProject() const { return _currentAvatarProject; };
|
||||||
//Q_INVOKABLE QObject* openAvatarProject();
|
//Q_INVOKABLE QObject* openAvatarProject();
|
||||||
Q_INVOKABLE QObject* uploadItem();
|
|
||||||
|
|
||||||
AvatarProject* _currentAvatarProject{ nullptr };
|
AvatarProject* _currentAvatarProject{ nullptr };
|
||||||
};
|
};
|
||||||
|
|
|
@ -41,6 +41,9 @@ AvatarProject* AvatarProject::openAvatarProject(const QString& path) {
|
||||||
AvatarProject::AvatarProject(const QString& fstPath, const QByteArray& data) :
|
AvatarProject::AvatarProject(const QString& fstPath, const QByteArray& data) :
|
||||||
_fstPath(fstPath), _fst(fstPath, FSTReader::readMapping(data)) {
|
_fstPath(fstPath), _fst(fstPath, FSTReader::readMapping(data)) {
|
||||||
|
|
||||||
|
_fstFilename = QFileInfo(_fstPath).fileName();
|
||||||
|
qDebug() << "Pointers: " << this << &_fst;
|
||||||
|
|
||||||
_directory = QFileInfo(_fstPath).absoluteDir();
|
_directory = QFileInfo(_fstPath).absoluteDir();
|
||||||
|
|
||||||
//_projectFiles = _directory.entryList();
|
//_projectFiles = _directory.entryList();
|
||||||
|
@ -51,13 +54,12 @@ AvatarProject::AvatarProject(const QString& fstPath, const QByteArray& data) :
|
||||||
}
|
}
|
||||||
|
|
||||||
void AvatarProject::appendDirectory(QString prefix, QDir dir) {
|
void AvatarProject::appendDirectory(QString prefix, QDir dir) {
|
||||||
qDebug() << "Inside of " << prefix << dir.absolutePath();
|
|
||||||
auto flags = QDir::Dirs | QDir::Files | QDir::NoSymLinks | QDir::NoDotAndDotDot | QDir::Hidden;
|
auto flags = QDir::Dirs | QDir::Files | QDir::NoSymLinks | QDir::NoDotAndDotDot | QDir::Hidden;
|
||||||
for (auto& entry : dir.entryInfoList({}, flags)) {
|
for (auto& entry : dir.entryInfoList({}, flags)) {
|
||||||
if (entry.isFile()) {
|
if (entry.isFile()) {
|
||||||
_projectFiles.append(prefix + "/" + entry.fileName());
|
//_projectFiles.append(prefix + "/" + entry.fileName());
|
||||||
|
_projectFiles.append(entry.absoluteFilePath());
|
||||||
} else if (entry.isDir()) {
|
} else if (entry.isDir()) {
|
||||||
qDebug() << "Found dir " << entry.absoluteFilePath() << " in " << dir.absolutePath();
|
|
||||||
appendDirectory(prefix + dir.dirName() + "/", entry.absoluteFilePath());
|
appendDirectory(prefix + dir.dirName() + "/", entry.absoluteFilePath());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -67,3 +69,7 @@ void AvatarProject::refreshProjectFiles() {
|
||||||
_projectFiles.clear();
|
_projectFiles.clear();
|
||||||
appendDirectory("", _directory);
|
appendDirectory("", _directory);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Q_INVOKABLE MarketplaceItemUploader* AvatarProject::upload() {
|
||||||
|
return new MarketplaceItemUploader("test_avatar", "blank description", _fstFilename, QUuid(), _projectFiles);
|
||||||
|
}
|
||||||
|
|
|
@ -13,6 +13,7 @@
|
||||||
#ifndef hifi_AvatarProject_h
|
#ifndef hifi_AvatarProject_h
|
||||||
#define hifi_AvatarProject_h
|
#define hifi_AvatarProject_h
|
||||||
|
|
||||||
|
#include "MarketplaceItemUploader.h"
|
||||||
#include "FST.h"
|
#include "FST.h"
|
||||||
|
|
||||||
#include <QDir>
|
#include <QDir>
|
||||||
|
@ -38,10 +39,7 @@ public:
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
Q_INVOKABLE QObject* upload() {
|
Q_INVOKABLE MarketplaceItemUploader* upload();
|
||||||
// TODO: create new AvatarProjectUploader here, launch it and return it for status tracking in QML
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* returns the AvatarProject or a nullptr on failure.
|
* returns the AvatarProject or a nullptr on failure.
|
||||||
|
@ -70,6 +68,7 @@ private:
|
||||||
QStringList _projectFiles{};
|
QStringList _projectFiles{};
|
||||||
QString _projectPath;
|
QString _projectPath;
|
||||||
QString _fstPath;
|
QString _fstPath;
|
||||||
|
QString _fstFilename;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // hifi_AvatarProject_h
|
#endif // hifi_AvatarProject_h
|
||||||
|
|
|
@ -14,44 +14,221 @@
|
||||||
#include <AccountManager.h>
|
#include <AccountManager.h>
|
||||||
#include <DependencyManager.h>
|
#include <DependencyManager.h>
|
||||||
|
|
||||||
#include <qtimer.h>
|
#include <QBuffer>
|
||||||
|
#include <quazip5\quazip.h>
|
||||||
MarketplaceItemUploader::MarketplaceItemUploader(QUuid marketplaceID, std::vector<QString> filePaths)
|
#include <quazip5\quazipfile.h>
|
||||||
: _filePaths(filePaths), _marketplaceID(marketplaceID) {
|
|
||||||
|
|
||||||
|
#include <qtimer.h>
|
||||||
|
#include <QFile>
|
||||||
|
#include <QFileInfo>
|
||||||
|
|
||||||
|
#include <QJsonArray>
|
||||||
|
#include <QJsonDocument>
|
||||||
|
#include <QJsonObject>
|
||||||
|
#include <QUuid>
|
||||||
|
|
||||||
|
MarketplaceItemUploader::MarketplaceItemUploader(QString title,
|
||||||
|
QString description,
|
||||||
|
QString rootFilename,
|
||||||
|
QUuid marketplaceID,
|
||||||
|
QStringList filePaths) :
|
||||||
|
_title(title),
|
||||||
|
_description(description), _rootFilename(rootFilename), _filePaths(filePaths), _marketplaceID(marketplaceID) {
|
||||||
|
qWarning() << "File paths: " << _filePaths.join(", ");
|
||||||
|
//_marketplaceID = QUuid::fromString(QLatin1String("{50dbd62f-cb6b-4be4-afb8-1ef8bd2dffa8}"));
|
||||||
|
}
|
||||||
|
|
||||||
|
void MarketplaceItemUploader::setState(State newState) {
|
||||||
|
qDebug() << "Setting uploader state to: " << newState;
|
||||||
|
|
||||||
|
_state = newState;
|
||||||
|
emit stateChanged(newState);
|
||||||
|
if (newState == State::Complete) {
|
||||||
|
emit completed();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void MarketplaceItemUploader::send() {
|
void MarketplaceItemUploader::send() {
|
||||||
|
doGetCategories();
|
||||||
|
}
|
||||||
|
|
||||||
|
void MarketplaceItemUploader::doGetCategories() {
|
||||||
|
setState(State::GettingCategories);
|
||||||
|
|
||||||
|
static const QString path = "/api/v1/marketplace/categories";
|
||||||
|
|
||||||
auto accountManager = DependencyManager::get<AccountManager>();
|
auto accountManager = DependencyManager::get<AccountManager>();
|
||||||
auto request = accountManager->createRequest("/marketplace/item", AccountManagerAuth::Required);
|
auto request = accountManager->createRequest(path, AccountManagerAuth::None);
|
||||||
|
|
||||||
|
qWarning() << "Request url is: " << request.url();
|
||||||
|
|
||||||
QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance();
|
QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance();
|
||||||
QByteArray data;
|
|
||||||
|
|
||||||
/*
|
QNetworkReply* reply = networkAccessManager.get(request);
|
||||||
auto reply = networkAccessManager.post(request, data);
|
|
||||||
|
|
||||||
connect(reply, &QNetworkReply::uploadProgress, this, &MarketplaceItemUploader::uploadProgress);
|
|
||||||
|
|
||||||
connect(reply, &QNetworkReply::finished, this, [this, reply]() {
|
connect(reply, &QNetworkReply::finished, this, [this, reply]() {
|
||||||
|
auto doc = QJsonDocument::fromJson(reply->readAll());
|
||||||
|
|
||||||
auto error = reply->error();
|
auto error = reply->error();
|
||||||
if (error == QNetworkReply::NoError) {
|
if (error == QNetworkReply::NoError) {
|
||||||
} else {
|
auto extractCategoryID = [&doc]() -> std::pair<bool, int> {
|
||||||
}
|
auto items = doc.object()["data"].toObject()["items"];
|
||||||
emit complete();
|
if (!items.isArray()) {
|
||||||
});
|
qWarning() << "Categories parse error: data.items is not an array";
|
||||||
*/
|
return { false, 0 };
|
||||||
|
}
|
||||||
|
|
||||||
QTimer* timer = new QTimer();
|
auto itemsArray = items.toArray();
|
||||||
timer->setInterval(1000);
|
for (const auto item : itemsArray) {
|
||||||
connect(timer, &QTimer::timeout, this, [this, timer]() {
|
if (!item.isObject()) {
|
||||||
if (progress <= 1.0f) {
|
qWarning() << "Categories parse error: item is not an object";
|
||||||
progress += 0.1;
|
return { false, 0 };
|
||||||
emit uploadProgress(progress * 100.0f, 100.0f);
|
}
|
||||||
|
|
||||||
|
auto itemObject = item.toObject();
|
||||||
|
if (itemObject["name"].toString() == "Avatars") {
|
||||||
|
auto idValue = itemObject["id"];
|
||||||
|
if (!idValue.isDouble()) {
|
||||||
|
qWarning() << "Categories parse error: id is not a number";
|
||||||
|
return { false, 0 };
|
||||||
|
}
|
||||||
|
return { true, (int)idValue.toDouble() };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
qWarning() << "Categories parse error: could not find a category for 'Avatar'";
|
||||||
|
return { false, 0 };
|
||||||
|
};
|
||||||
|
|
||||||
|
bool success;
|
||||||
|
int id;
|
||||||
|
std::tie(success, id) = extractCategoryID();
|
||||||
|
qDebug() << "Done " << success << id;
|
||||||
|
if (!success) {
|
||||||
|
qWarning() << "Failed to find marketplace category id";
|
||||||
|
_error = Error::Unknown;
|
||||||
|
setState(State::Complete);
|
||||||
|
} else {
|
||||||
|
doUploadAvatar();
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
emit complete();
|
_error = Error::Unknown;
|
||||||
timer->stop();
|
setState(State::Complete);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
timer->start();
|
}
|
||||||
|
|
||||||
|
void MarketplaceItemUploader::doUploadAvatar() {
|
||||||
|
QBuffer buffer{ &_fileData };
|
||||||
|
//buffer.open(QIODevice::WriteOnly);
|
||||||
|
QuaZip zip{ &buffer };
|
||||||
|
if (!zip.open(QuaZip::Mode::mdAdd)) {
|
||||||
|
qWarning() << "Failed to open zip!!";
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto& filePath : _filePaths) {
|
||||||
|
qWarning() << "Zipping: " << filePath;
|
||||||
|
QFileInfo fileInfo{ filePath };
|
||||||
|
|
||||||
|
QuaZipFile zipFile{ &zip };
|
||||||
|
if (!zipFile.open(QIODevice::WriteOnly, QuaZipNewInfo(fileInfo.fileName()))) {
|
||||||
|
qWarning() << "Could not open zip file:" << zipFile.getZipError();
|
||||||
|
_error = Error::Unknown;
|
||||||
|
setState(State::Complete);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
QFile file{ filePath };
|
||||||
|
if (file.open(QIODevice::ReadOnly)) {
|
||||||
|
zipFile.write(file.readAll());
|
||||||
|
} else {
|
||||||
|
qWarning() << "Failed to open: " << filePath;
|
||||||
|
}
|
||||||
|
file.close();
|
||||||
|
zipFile.close();
|
||||||
|
if (zipFile.getZipError() != UNZ_OK) {
|
||||||
|
qWarning() << "Could not close zip file: " << zipFile.getZipError();
|
||||||
|
setState(State::Complete);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
zip.close();
|
||||||
|
|
||||||
|
qDebug() << "Finished zipping, size: " << (buffer.size() / (1000.0f)) << "KB";
|
||||||
|
|
||||||
|
QString path = "/api/v1/marketplace/items";
|
||||||
|
bool creating = true;
|
||||||
|
if (!_marketplaceID.isNull()) {
|
||||||
|
creating = false;
|
||||||
|
auto idWithBraces = _marketplaceID.toString();
|
||||||
|
auto idWithoutBraces = idWithBraces.mid(1, idWithBraces.length() - 2);
|
||||||
|
path += "/" + idWithoutBraces;
|
||||||
|
}
|
||||||
|
auto accountManager = DependencyManager::get<AccountManager>();
|
||||||
|
auto request = accountManager->createRequest(path, AccountManagerAuth::Required);
|
||||||
|
qWarning() << "Request url is: " << request.url();
|
||||||
|
|
||||||
|
QJsonObject root{ { "marketplace_item",
|
||||||
|
QJsonObject{ { "title", _title },
|
||||||
|
{ "description", _description },
|
||||||
|
{ "root_file_key", _rootFilename },
|
||||||
|
{ "category_ids", QJsonArray({ 5 }) },
|
||||||
|
//{ "attributions", QJsonArray({ QJsonObject{ { "name", "" }, { "link", "" } } }) },
|
||||||
|
{ "license", 0 },
|
||||||
|
{ "files", QString::fromLatin1(_fileData.toBase64()) } } } };
|
||||||
|
request.setHeader(QNetworkRequest::KnownHeaders::ContentTypeHeader, "application/json");
|
||||||
|
QJsonDocument doc{ root };
|
||||||
|
|
||||||
|
qWarning() << "data: " << doc.toJson();
|
||||||
|
|
||||||
|
_fileData.toBase64();
|
||||||
|
QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance();
|
||||||
|
|
||||||
|
QNetworkReply* reply{ nullptr };
|
||||||
|
if (creating) {
|
||||||
|
reply = networkAccessManager.post(request, doc.toJson());
|
||||||
|
} else {
|
||||||
|
reply = networkAccessManager.put(request, doc.toJson());
|
||||||
|
}
|
||||||
|
|
||||||
|
connect(reply, &QNetworkReply::uploadProgress, this, &MarketplaceItemUploader::uploadProgress);
|
||||||
|
|
||||||
|
connect(reply, &QNetworkReply::finished, this, [this, reply]() {
|
||||||
|
_responseData = reply->readAll();
|
||||||
|
qWarning() << "Finished request " << _responseData;
|
||||||
|
auto error = reply->error();
|
||||||
|
if (error == QNetworkReply::NoError) {
|
||||||
|
doWaitForInventory();
|
||||||
|
} else {
|
||||||
|
_error = Error::Unknown;
|
||||||
|
setState(State::Complete);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
setState(State::UploadingAvatar);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MarketplaceItemUploader::doWaitForInventory() {
|
||||||
|
static const QString path = "/api/v1/commerce/inventory";
|
||||||
|
|
||||||
|
auto accountManager = DependencyManager::get<AccountManager>();
|
||||||
|
auto request = accountManager->createRequest(path, AccountManagerAuth::Required);
|
||||||
|
|
||||||
|
qWarning() << "Request url is: " << request.url();
|
||||||
|
|
||||||
|
QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance();
|
||||||
|
|
||||||
|
QNetworkReply* reply = networkAccessManager.post(request, "");
|
||||||
|
|
||||||
|
connect(reply, &QNetworkReply::finished, this, [this, reply]() {
|
||||||
|
auto data = reply->readAll();
|
||||||
|
qWarning() << "Finished inventory request " << data;
|
||||||
|
|
||||||
|
auto error = reply->error();
|
||||||
|
if (error == QNetworkReply::NoError) {
|
||||||
|
} else {
|
||||||
|
_error = Error::Unknown;
|
||||||
|
}
|
||||||
|
setState(State::Complete);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,36 +20,68 @@ class QNetworkReply;
|
||||||
|
|
||||||
class MarketplaceItemUploader : public QObject {
|
class MarketplaceItemUploader : public QObject {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
|
Q_PROPERTY(bool complete READ getComplete NOTIFY stateChanged)
|
||||||
|
Q_PROPERTY(State state READ getState NOTIFY stateChanged)
|
||||||
|
Q_PROPERTY(Error error READ getError)
|
||||||
|
Q_PROPERTY(QString responseData READ getResponseData)
|
||||||
public:
|
public:
|
||||||
enum class Error
|
enum class Error
|
||||||
{
|
{
|
||||||
None,
|
None,
|
||||||
ItemNotUpdateable,
|
|
||||||
ItemDoesNotExist,
|
|
||||||
RequestTimedOut,
|
|
||||||
Unknown
|
Unknown
|
||||||
};
|
};
|
||||||
|
Q_ENUM(Error);
|
||||||
|
|
||||||
enum class State
|
enum class State
|
||||||
{
|
{
|
||||||
Ready,
|
Idle,
|
||||||
Sent
|
GettingCategories,
|
||||||
|
UploadingAvatar,
|
||||||
|
WaitingForInventory,
|
||||||
|
Complete
|
||||||
};
|
};
|
||||||
|
Q_ENUM(State);
|
||||||
|
|
||||||
MarketplaceItemUploader(QUuid markertplaceID, std::vector<QString> filePaths);
|
MarketplaceItemUploader(QString title,
|
||||||
|
QString description,
|
||||||
float progress{ 0.0f };
|
QString rootFilename,
|
||||||
|
QUuid marketplaceID,
|
||||||
|
QStringList filePaths);
|
||||||
|
|
||||||
Q_INVOKABLE void send();
|
Q_INVOKABLE void send();
|
||||||
|
|
||||||
|
QString getResponseData() const { return _responseData; }
|
||||||
|
void setState(State newState);
|
||||||
|
State getState() const { return _state; }
|
||||||
|
bool getComplete() const { return _state == State::Complete; }
|
||||||
|
|
||||||
|
Error getError() const { return _error; }
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void uploadProgress(qint64 bytesSent, qint64 bytesTotal);
|
void uploadProgress(qint64 bytesSent, qint64 bytesTotal);
|
||||||
void complete();
|
void completed();
|
||||||
|
void stateChanged(State newState);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
void doGetCategories();
|
||||||
|
void doUploadAvatar();
|
||||||
|
void doWaitForInventory();
|
||||||
|
|
||||||
QNetworkReply* _reply;
|
QNetworkReply* _reply;
|
||||||
|
|
||||||
|
State _state{ State::Idle };
|
||||||
|
Error _error{ Error::None };
|
||||||
|
|
||||||
|
QString _title;
|
||||||
|
QString _description;
|
||||||
|
QString _rootFilename;
|
||||||
QUuid _marketplaceID;
|
QUuid _marketplaceID;
|
||||||
std::vector<QString> _filePaths;
|
|
||||||
|
QString _responseData;
|
||||||
|
|
||||||
|
QStringList _filePaths;
|
||||||
|
QByteArray _fileData;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // hifi_MarketplaceItemUploader_h
|
#endif // hifi_MarketplaceItemUploader_h
|
||||||
|
|
|
@ -112,6 +112,15 @@ const GROUPS = [
|
||||||
type: "color",
|
type: "color",
|
||||||
propertyID: "color",
|
propertyID: "color",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
label: "Alpha",
|
||||||
|
type: "",
|
||||||
|
type: "number",
|
||||||
|
min: 0,
|
||||||
|
max: 1,
|
||||||
|
step: 0.001,
|
||||||
|
propertyID: "alpha",
|
||||||
|
},
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
Loading…
Reference in a new issue