Add start of marketplace uploading and project file list

This commit is contained in:
Ryan Huffman 2018-12-13 13:22:52 -08:00
parent 96673bb5b9
commit 78c4c2599e
12 changed files with 388 additions and 89 deletions

View file

@ -23,14 +23,13 @@ Windows.ScrollingWindow {
//HifiConstants { id: hifi }
Rectangle {
anchors.left: parent.left
anchors.right: parent.right
anchors.top: parent.top
anchors.bottom: parent.bottom
anchors.fill: parent
AvatarProject {
id: avatarProject
colorScheme: root.colorScheme
visible: false
anchors.fill: parent
}
Rectangle {

View file

@ -13,26 +13,23 @@ Rectangle {
property int colorScheme;
color: "blue"
visible: true
anchors.left: parent.left
anchors.right: parent.right
anchors.top: parent.top
anchors.bottom: parent.bottom
anchors.fill: parent
RalewaySemiBold {
id: avatarProjectLabel
size: 24;
anchors.left: parent.left
anchors.top: parent.top
width: parent.width
anchors.topMargin: 25
anchors.bottomMargin: 25
text: 'Avatar Project'
}
HifiControls.Button {
id: openFolderButton
anchors.left: parent.left
anchors.right: parent.right
width: parent.width
anchors.top: avatarProjectLabel.bottom
text: qsTr("Open Project Folder")
colorScheme: root.colorScheme
@ -43,15 +40,32 @@ Rectangle {
}
HifiControls.Button {
id: uploadButton
anchors.left: parent.left
anchors.right: parent.right
width: parent.width
anchors.top: openFolderButton.bottom
text: qsTr("Upload")
color: hifi.buttons.blue
colorScheme: root.colorScheme
height: 30
onClicked: function() {
}
}
Text {
id: modelText
anchors.top: uploadButton.bottom
height: 30
text: parent.height
}
Rectangle {
color: 'white'
visible: AvatarPackagerCore.currentAvatarProject !== null
width: parent.width
anchors.top: modelText.bottom
height: 1000
ListView {
anchors.fill: parent
model: AvatarPackagerCore.currentAvatarProject === null ? [] : AvatarPackagerCore.currentAvatarProject.projectFiles
delegate: Text { text: '<b>File:</b> ' + modelData }
}
}
}

View file

@ -15,6 +15,18 @@
#include <QUrl>
#include <OffscreenUi.h>
#include "ModelSelector.h"
#include <avatar/MarketplaceItemUploader.h>
#include <thread>
#include <mutex>
std::once_flag setupQMLTypesFlag;
AvatarPackager::AvatarPackager() {
std::call_once(setupQMLTypesFlag, []() {
qmlRegisterType<FST>();
});
}
bool AvatarPackager::open() {
static const QUrl url{ "hifi/AvatarPackager.qml" };
@ -32,5 +44,11 @@ QObject* AvatarPackager::openAvatarProject(QString avatarProjectFSTPath) {
//_currentAvatarProject = nullptr;
}
_currentAvatarProject = AvatarProject::openAvatarProject(avatarProjectFSTPath);
emit avatarProjectChanged();
return _currentAvatarProject;
}
QObject* AvatarPackager::uploadItem() {
std::vector<QString> filePaths;
return new MarketplaceItemUploader(QUuid(), filePaths);
}

View file

@ -21,16 +21,23 @@
class AvatarPackager : public QObject, public Dependency {
Q_OBJECT
SINGLETON_DEPENDENCY
Q_PROPERTY(QObject* currentAvatarProject READ getAvatarProject)
Q_PROPERTY(QObject* currentAvatarProject READ getAvatarProject NOTIFY avatarProjectChanged)
public:
AvatarPackager();
bool open();
Q_INVOKABLE QObject* openAvatarProject(QString avatarProjectFSTPath);
signals:
void avatarProjectChanged();
private:
Q_INVOKABLE AvatarProject* getAvatarProject() const { return _currentAvatarProject; };
AvatarProject* _currentAvatarProject { nullptr };
//Q_INVOKABLE QObject* openAvatarProject();
Q_INVOKABLE QObject* uploadItem();
AvatarProject* _currentAvatarProject{ nullptr };
};
#endif // hifi_AvatarPackager_h
#endif // hifi_AvatarPackager_h

View file

@ -11,17 +11,56 @@
#include "AvatarProject.h"
AvatarProject* AvatarProject::openAvatarProject(QString path) {
#include <FSTReader.h>
#include <QFile>
#include <QFileInfo>
#include <QUrl>
#include <QDebug>
AvatarProject* AvatarProject::openAvatarProject(const QString& path) {
const auto pathToLower = path.toLower();
if (pathToLower.endsWith(".fst")) {
// TODO: do we open FSTs from any path?
return new AvatarProject(path);
QFile file{ path };
if (!file.open(QIODevice::ReadOnly)) {
return nullptr;
}
return new AvatarProject(path, file.readAll());
}
if (pathToLower.endsWith(".fbx")) {
// TODO: Create FST here:
}
return nullptr;
}
AvatarProject::AvatarProject(const QString& fstPath, const QByteArray& data) :
_fstPath(fstPath), _fst(fstPath, FSTReader::readMapping(data)) {
_directory = QFileInfo(_fstPath).absoluteDir();
//_projectFiles = _directory.entryList();
refreshProjectFiles();
auto fileInfo = QFileInfo(_fstPath);
_projectPath = fileInfo.absoluteDir().absolutePath();
}
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;
for (auto& entry : dir.entryInfoList({}, flags)) {
if (entry.isFile()) {
_projectFiles.append(prefix + "/" + entry.fileName());
} else if (entry.isDir()) {
qDebug() << "Found dir " << entry.absoluteFilePath() << " in " << dir.absolutePath();
appendDirectory(prefix + dir.dirName() + "/", entry.absoluteFilePath());
}
}
}
void AvatarProject::refreshProjectFiles() {
_projectFiles.clear();
appendDirectory("", _directory);
}

View file

@ -13,12 +13,21 @@
#ifndef hifi_AvatarProject_h
#define hifi_AvatarProject_h
#include "FST.h"
#include <QDir>
#include <QObject>
#include <QDir>
#include <QFileInfo>
#include <QVariantHash>
#include <QUuid>
class AvatarProject : public QObject {
Q_OBJECT
Q_PROPERTY(FST* fst READ getFST)
Q_PROPERTY(QStringList projectFiles MEMBER _projectFiles)
Q_PROPERTY(QString projectFolderPath READ getProjectPath)
Q_PROPERTY(QString projectFSTPath READ getFSTPath)
Q_PROPERTY(QString projectFBXPath READ getFBXPath)
@ -37,17 +46,10 @@ public:
/**
* returns the AvatarProject or a nullptr on failure.
*/
static AvatarProject* openAvatarProject(QString path);
static AvatarProject* openAvatarProject(const QString& path);
private:
AvatarProject(QString fstPath) :
_fstPath(fstPath) {
auto fileInfo = QFileInfo(_fstPath);
_projectPath = fileInfo.absoluteDir().absolutePath();
_fstPath = _projectPath + "TemporaryFBX.fbx";
}
AvatarProject(const QString& fstPath, const QByteArray& data);
~AvatarProject() {
// TODO: cleanup FST / AvatarProjectUploader etc.
@ -55,13 +57,19 @@ private:
Q_INVOKABLE QString getProjectPath() const { return _projectPath; }
Q_INVOKABLE QString getFSTPath() const { return _fstPath; }
Q_INVOKABLE QString getFBXPath() const { return _fbxPath; }
Q_INVOKABLE QString getFBXPath() const { return _fst.getModelPath(); }
FST* getFST() { return &_fst; }
void refreshProjectFiles();
void appendDirectory(QString prefix, QDir dir);
FST _fst;
QDir _directory;
QStringList _projectFiles{};
QString _projectPath;
QString _fstPath;
// TODO: handle this in the FST Class
QString _fbxPath;
};
#endif // hifi_AvatarProject_h
#endif // hifi_AvatarProject_h

View file

@ -0,0 +1,57 @@
//
// MarketplaceItemUploader.cpp
//
//
// Created by Ryan Huffman on 12/10/2018
// Copyright 2018 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 "MarketplaceItemUploader.h"
#include <AccountManager.h>
#include <DependencyManager.h>
#include <qtimer.h>
MarketplaceItemUploader::MarketplaceItemUploader(QUuid marketplaceID, std::vector<QString> filePaths)
: _filePaths(filePaths), _marketplaceID(marketplaceID) {
}
void MarketplaceItemUploader::send() {
auto accountManager = DependencyManager::get<AccountManager>();
auto request = accountManager->createRequest("/marketplace/item", AccountManagerAuth::Required);
QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance();
QByteArray data;
/*
auto reply = networkAccessManager.post(request, data);
connect(reply, &QNetworkReply::uploadProgress, this, &MarketplaceItemUploader::uploadProgress);
connect(reply, &QNetworkReply::finished, this, [this, reply]() {
auto error = reply->error();
if (error == QNetworkReply::NoError) {
} else {
}
emit complete();
});
*/
QTimer* timer = new QTimer();
timer->setInterval(1000);
connect(timer, &QTimer::timeout, this, [this, timer]() {
if (progress <= 1.0f) {
progress += 0.1;
emit uploadProgress(progress * 100.0f, 100.0f);
} else {
emit complete();
timer->stop();
}
});
timer->start();
}

View file

@ -0,0 +1,55 @@
//
// MarketplaceItemUploader.h
//
//
// Created by Ryan Huffman on 12/10/2018
// Copyright 2018 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_MarketplaceItemUploader_h
#define hifi_MarketplaceItemUploader_h
#include <QObject>
#include <QUuid>
class QNetworkReply;
class MarketplaceItemUploader : public QObject {
Q_OBJECT
public:
enum class Error
{
None,
ItemNotUpdateable,
ItemDoesNotExist,
RequestTimedOut,
Unknown
};
enum class State
{
Ready,
Sent
};
MarketplaceItemUploader(QUuid markertplaceID, std::vector<QString> filePaths);
float progress{ 0.0f };
Q_INVOKABLE void send();
signals:
void uploadProgress(qint64 bytesSent, qint64 bytesTotal);
void complete();
private:
QNetworkReply* _reply;
QUuid _marketplaceID;
std::vector<QString> _filePaths;
};
#endif // hifi_MarketplaceItemUploader_h

45
libraries/fbx/src/FST.cpp Normal file
View file

@ -0,0 +1,45 @@
//
// FST.cpp
//
// Created by Ryan Huffman on 12/11/15.
// Copyright 2018 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 "FST.h"
#include <QDir>
#include <QFileInfo>
FST::FST(QString fstPath, QVariantHash data) : _fstPath(fstPath) {
if (data.contains("name")) {
_name = data["name"].toString();
data.remove("name");
}
if (data.contains("filename")) {
_modelPath = data["filename"].toString();
data.remove("filename");
}
_other = data;
}
QString FST::absoluteModelPath() const {
QFileInfo fileInfo{ _fstPath };
QDir dir{ fileInfo.absoluteDir() };
return dir.absoluteFilePath(_modelPath);
}
void FST::setName(const QString& name) {
_name = name;
emit nameChanged(name);
}
void FST::setModelPath(const QString& modelPath) {
_modelPath = modelPath;
emit modelPathChanged(modelPath);
}

49
libraries/fbx/src/FST.h Normal file
View file

@ -0,0 +1,49 @@
//
// FST.h
//
// Created by Ryan Huffman on 12/11/15.
// Copyright 2018 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
//
#ifndef hifi_FST_h
#define hifi_FST_h
#include <QVariantHash>
#include <QUuid>
class FST : public QObject {
Q_OBJECT
Q_PROPERTY(QString name READ getName WRITE setName NOTIFY nameChanged)
Q_PROPERTY(QString modelPath READ getModelPath WRITE setModelPath NOTIFY modelPathChanged)
Q_PROPERTY(QUuid marketplaceID READ getMarketplaceID)
public:
FST(QString fstPath, QVariantHash data);
QString absoluteModelPath() const;
QString getName() const { return _name; }
void setName(const QString& name);
QString getModelPath() const { return _modelPath; }
void setModelPath(const QString& modelPath);
QUuid getMarketplaceID() const { return _marketplaceID; }
signals:
void nameChanged(const QString& name);
void modelPathChanged(const QString& modelPath);
private:
QString _fstPath;
QString _name{};
QString _modelPath{};
QUuid _marketplaceID{};
QVariantHash _other{};
};
#endif // hifi_FST_h

View file

@ -208,6 +208,44 @@ void AccountManager::setSessionID(const QUuid& sessionID) {
}
}
QNetworkRequest AccountManager::createRequest(QString path, AccountManagerAuth::Type authType) {
QNetworkRequest networkRequest;
networkRequest.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true);
networkRequest.setHeader(QNetworkRequest::UserAgentHeader, _userAgentGetter());
networkRequest.setRawHeader(METAVERSE_SESSION_ID_HEADER,
uuidStringWithoutCurlyBraces(_sessionID).toLocal8Bit());
QUrl requestURL = _authURL;
if (requestURL.isEmpty()) { // Assignment client doesn't set _authURL.
requestURL = getMetaverseServerURL();
}
if (path.startsWith("/")) {
requestURL.setPath(path);
} else {
requestURL.setPath("/" + path);
}
if (authType != AccountManagerAuth::None ) {
if (hasValidAccessToken()) {
networkRequest.setRawHeader(ACCESS_TOKEN_AUTHORIZATION_HEADER,
_accountInfo.getAccessToken().authorizationHeaderValue());
} else {
if (authType == AccountManagerAuth::Required) {
qCDebug(networking) << "No valid access token present. Bailing on invoked request to"
<< path << "that requires authentication";
return QNetworkRequest();
}
}
}
networkRequest.setUrl(requestURL);
return networkRequest;
}
void AccountManager::sendRequest(const QString& path,
AccountManagerAuth::Type authType,
QNetworkAccessManager::Operation operation,
@ -231,46 +269,10 @@ void AccountManager::sendRequest(const QString& path,
QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance();
QNetworkRequest networkRequest;
networkRequest.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true);
networkRequest.setHeader(QNetworkRequest::UserAgentHeader, _userAgentGetter());
networkRequest.setRawHeader(METAVERSE_SESSION_ID_HEADER,
uuidStringWithoutCurlyBraces(_sessionID).toLocal8Bit());
QUrl requestURL = _authURL;
if (requestURL.isEmpty()) { // Assignment client doesn't set _authURL.
requestURL = getMetaverseServerURL();
}
if (path.startsWith("/")) {
requestURL.setPath(path);
} else {
requestURL.setPath("/" + path);
}
if (!query.isEmpty()) {
requestURL.setQuery(query);
}
if (authType != AccountManagerAuth::None ) {
if (hasValidAccessToken()) {
networkRequest.setRawHeader(ACCESS_TOKEN_AUTHORIZATION_HEADER,
_accountInfo.getAccessToken().authorizationHeaderValue());
} else {
if (authType == AccountManagerAuth::Required) {
qCDebug(networking) << "No valid access token present. Bailing on invoked request to"
<< path << "that requires authentication";
return;
}
}
}
networkRequest.setUrl(requestURL);
QNetworkRequest networkRequest = createRequest(path, authType);
if (VERBOSE_HTTP_REQUEST_DEBUGGING) {
qCDebug(networking) << "Making a request to" << qPrintable(requestURL.toString());
qCDebug(networking) << "Making a request to" << qPrintable(networkRequest.url().toString());
if (!dataByteArray.isEmpty()) {
qCDebug(networking) << "The POST/PUT body -" << QString(dataByteArray);

View file

@ -28,7 +28,8 @@
class JSONCallbackParameters {
public:
JSONCallbackParameters(QObject* callbackReceiver = nullptr, const QString& jsonCallbackMethod = QString(),
JSONCallbackParameters(QObject* callbackReceiver = nullptr,
const QString& jsonCallbackMethod = QString(),
const QString& errorCallbackMethod = QString());
bool isEmpty() const { return !callbackReceiver; }
@ -39,11 +40,12 @@ public:
};
namespace AccountManagerAuth {
enum Type {
None,
Required,
Optional
};
enum Type
{
None,
Required,
Optional
};
}
Q_DECLARE_METATYPE(AccountManagerAuth::Type);
@ -60,6 +62,7 @@ class AccountManager : public QObject, public Dependency {
public:
AccountManager(UserAgentGetter userAgentGetter = DEFAULT_USER_AGENT_GETTER);
QNetworkRequest createRequest(QString path, AccountManagerAuth::Type authType);
Q_INVOKABLE void sendRequest(const QString& path,
AccountManagerAuth::Type authType,
QNetworkAccessManager::Operation operation = QNetworkAccessManager::GetOperation,
@ -84,7 +87,7 @@ public:
void requestProfile();
DataServerAccountInfo& getAccountInfo() { return _accountInfo; }
void setAccountInfo(const DataServerAccountInfo &newAccountInfo);
void setAccountInfo(const DataServerAccountInfo& newAccountInfo);
static QJsonObject dataObjectFromResponse(QNetworkReply* requestReply);
@ -104,7 +107,10 @@ public:
public slots:
void requestAccessToken(const QString& login, const QString& password);
void requestAccessTokenWithSteam(QByteArray authSessionTicket);
void requestAccessTokenWithAuthCode(const QString& authCode, const QString& clientId, const QString& clientSecret, const QString& redirectUri);
void requestAccessTokenWithAuthCode(const QString& authCode,
const QString& clientId,
const QString& clientSecret,
const QString& redirectUri);
void refreshAccessToken();
void requestAccessTokenFinished();
@ -148,15 +154,15 @@ private:
QUrl _authURL;
DataServerAccountInfo _accountInfo;
bool _isWaitingForTokenRefresh { false };
bool _isAgent { false };
bool _isWaitingForTokenRefresh{ false };
bool _isAgent{ false };
bool _isWaitingForKeypairResponse { false };
bool _isWaitingForKeypairResponse{ false };
QByteArray _pendingPrivateKey;
QUuid _sessionID { QUuid::createUuid() };
QUuid _sessionID{ QUuid::createUuid() };
bool _limitedCommerce { false };
bool _limitedCommerce{ false };
};
#endif // hifi_AccountManager_h
#endif // hifi_AccountManager_h