250 lines · 8.5 KB · C++
RawDownload
////  Snapshot.cpp//  interface/src/ui////  Created by Stojce Slavkovski on 1/26/14.//  Copyright 2014 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 <QtCore/QDateTime>#include <QtCore/QDir>#include <QtCore/QFile>#include <QtCore/QFileInfo>#include <QtCore/QTemporaryFile>#include <QtCore/QUrl>#include <QtCore/QUrlQuery>#include <QtCore/QJsonDocument>#include <QtCore/QJsonArray>#include <QtNetwork/QHttpMultiPart>#include <QtGui/QImage> #include <AccountManager.h>#include <AddressManager.h>#include <avatar/AvatarManager.h>#include <avatar/MyAvatar.h>#include <FileUtils.h>#include <NodeList.h>#include <OffscreenUi.h>#include <SharedUtil.h> #include "Application.h"#include "Snapshot.h" // filename format: hifi-snap-by-%username%-on-%date%_%time%_@-%location%.jpg// %1 <= username, %2 <= date and time, %3 <= current locationconst QString FILENAME_PATH_FORMAT = "hifi-snap-by-%1-on-%2.jpg"; const QString DATETIME_FORMAT = "yyyy-MM-dd_hh-mm-ss";const QString SNAPSHOTS_DIRECTORY = "Snapshots"; const QString URL = "highfidelity_url"; Setting::Handle<QString> Snapshot::snapshotsLocation("snapshotsLocation",    QStandardPaths::writableLocation(QStandardPaths::DesktopLocation)); SnapshotMetaData* Snapshot::parseSnapshotData(QString snapshotPath) {     if (!QFile(snapshotPath).exists()) {        return NULL;    }     QImage shot(snapshotPath);     // no location data stored    if (shot.text(URL).isEmpty()) {        return NULL;    }     // parsing URL    QUrl url = QUrl(shot.text(URL), QUrl::ParsingMode::StrictMode);     SnapshotMetaData* data = new SnapshotMetaData();    data->setURL(url);     return data;} QString Snapshot::saveSnapshot(QImage image) {     QFile* snapshotFile = savedFileForSnapshot(image, false);     // we don't need the snapshot file, so close it, grab its filename and delete it    snapshotFile->close();     QString snapshotPath = QFileInfo(*snapshotFile).absoluteFilePath();     delete snapshotFile;     return snapshotPath;} QTemporaryFile* Snapshot::saveTempSnapshot(QImage image) {    // return whatever we get back from saved file for snapshot    return static_cast<QTemporaryFile*>(savedFileForSnapshot(image, true));} QFile* Snapshot::savedFileForSnapshot(QImage & shot, bool isTemporary) {     // adding URL to snapshot    QUrl currentURL = DependencyManager::get<AddressManager>()->currentAddress();    shot.setText(URL, currentURL.toString());     QString username = DependencyManager::get<AccountManager>()->getAccountInfo().getUsername();    // normalize username, replace all non alphanumeric with '-'    username.replace(QRegExp("[^A-Za-z0-9_]"), "-");     QDateTime now = QDateTime::currentDateTime();     QString filename = FILENAME_PATH_FORMAT.arg(username, now.toString(DATETIME_FORMAT));     const int IMAGE_QUALITY = 100;     if (!isTemporary) {        QString snapshotFullPath = snapshotsLocation.get();         if (!snapshotFullPath.endsWith(QDir::separator())) {            snapshotFullPath.append(QDir::separator());        }         snapshotFullPath.append(filename);         QFile* imageFile = new QFile(snapshotFullPath);        imageFile->open(QIODevice::WriteOnly);         shot.save(imageFile, 0, IMAGE_QUALITY);        imageFile->close();         return imageFile;     } else {        QTemporaryFile* imageTempFile = new QTemporaryFile(QDir::tempPath() + "/XXXXXX-" + filename);         if (!imageTempFile->open()) {            qDebug() << "Unable to open QTemporaryFile for temp snapshot. Will not save.";            return NULL;        }         shot.save(imageTempFile, 0, IMAGE_QUALITY);        imageTempFile->close();         return imageTempFile;    }} const QString FORUM_URL = "https://alphas.highfidelity.io";const QString FORUM_UPLOADS_URL = FORUM_URL + "/uploads";const QString FORUM_POST_URL = FORUM_URL + "/posts";const QString FORUM_REPLY_TO_TOPIC = "244";const QString FORUM_POST_TEMPLATE = "<img src='%1'/><p>%2</p>";const QString SHARE_DEFAULT_ERROR = "The server isn't responding. Please try again in a few minutes.";const QString SUCCESS_LABEL_TEMPLATE = "Success!!! Go check out your image ...<br/><a style='color:#333;text-decoration:none' href='%1'>%1</a>";  QString SnapshotUploader::uploadSnapshot(const QUrl& fileUrl) {    auto accountManager = DependencyManager::get<AccountManager>();    if (accountManager->getAccountInfo().getDiscourseApiKey().isEmpty()) {        OffscreenUi::warning(nullptr, "", "Your Discourse API key is missing, you cannot share snapshots. Please try to relog.");        return QString();    }     QHttpPart apiKeyPart;    apiKeyPart.setHeader(QNetworkRequest::ContentDispositionHeader, QVariant("form-data; name=\"api_key\""));    apiKeyPart.setBody(accountManager->getAccountInfo().getDiscourseApiKey().toLatin1());     QString filename = fileUrl.toLocalFile();    qDebug() << filename;    QFile* file = new QFile(filename);    Q_ASSERT(file->exists());    file->open(QIODevice::ReadOnly);     QHttpPart imagePart;    imagePart.setHeader(QNetworkRequest::ContentTypeHeader, QVariant("image/jpeg"));    imagePart.setHeader(QNetworkRequest::ContentDispositionHeader,        QVariant("form-data; name=\"file\"; filename=\"" + file->fileName() + "\""));    imagePart.setBodyDevice(file);     QHttpMultiPart* multiPart = new QHttpMultiPart(QHttpMultiPart::FormDataType);    file->setParent(multiPart); // we cannot delete the file now, so delete it with the multiPart    multiPart->append(apiKeyPart);    multiPart->append(imagePart);     QUrl url(FORUM_UPLOADS_URL);    QNetworkRequest request(url);    request.setHeader(QNetworkRequest::UserAgentHeader, HIGH_FIDELITY_USER_AGENT);     QString result;    QEventLoop loop;     QSharedPointer<QNetworkReply> reply(NetworkAccessManager::getInstance().post(request, multiPart));    QObject::connect(reply.data(), &QNetworkReply::finished, [&] {        loop.quit();         qDebug() << reply->errorString();        for (const auto& header : reply->rawHeaderList()) {            qDebug() << "Header " << QString(header);        }        auto replyResult = reply->readAll();        qDebug() << QString(replyResult);        QJsonDocument jsonResponse = QJsonDocument::fromJson(replyResult);        const QJsonObject& responseObject = jsonResponse.object();        if (!responseObject.contains("url")) {            OffscreenUi::warning(this, "", SHARE_DEFAULT_ERROR);            return;        }        result = responseObject["url"].toString();    });    loop.exec();    return result;} QString SnapshotUploader::sendForumPost(const QString& snapshotPath, const QString& notes) {    // post to Discourse forum    QNetworkRequest request;    request.setHeader(QNetworkRequest::UserAgentHeader, HIGH_FIDELITY_USER_AGENT);    QUrl forumUrl(FORUM_POST_URL);     QUrlQuery query;    query.addQueryItem("api_key", DependencyManager::get<AccountManager>()->getAccountInfo().getDiscourseApiKey());    query.addQueryItem("topic_id", FORUM_REPLY_TO_TOPIC);    query.addQueryItem("raw", FORUM_POST_TEMPLATE.arg(snapshotPath, notes));    forumUrl.setQuery(query);     QByteArray postData = forumUrl.toEncoded(QUrl::RemoveFragment);    request.setUrl(forumUrl);    request.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded");     QNetworkReply* requestReply = NetworkAccessManager::getInstance().post(request, postData);     QEventLoop loop;    QString result;    connect(requestReply, &QNetworkReply::finished, [&] {        loop.quit();        QJsonDocument jsonResponse = QJsonDocument::fromJson(requestReply->readAll());        requestReply->deleteLater();        const QJsonObject& responseObject = jsonResponse.object();         if (!responseObject.contains("id")) {            QString errorMessage(SHARE_DEFAULT_ERROR);            if (responseObject.contains("errors")) {                QJsonArray errorArray = responseObject["errors"].toArray();                if (!errorArray.first().toString().isEmpty()) {                    errorMessage = errorArray.first().toString();                }            }            OffscreenUi::warning(this, "", errorMessage);            return;        }         const QString urlTemplate = "%1/t/%2/%3/%4";        result = urlTemplate.arg(FORUM_URL,            responseObject["topic_slug"].toString(),            QString::number(responseObject["topic_id"].toDouble()),            QString::number(responseObject["post_number"].toDouble()));    });    loop.exec();    return result;}