Custom uploaders
This commit is contained in:
parent
a65a2fab58
commit
0101b61651
@ -35,7 +35,8 @@ SOURCES += main.cpp\
|
||||
io/ioutils.cpp \
|
||||
settings.cpp \
|
||||
uploaders/default/clipboarduploader.cpp \
|
||||
formatter.cpp
|
||||
formatter.cpp \
|
||||
uploaders/customuploader.cpp
|
||||
|
||||
HEADERS += mainwindow.hpp \
|
||||
cropeditor/cropeditor.hpp \
|
||||
@ -49,7 +50,8 @@ HEADERS += mainwindow.hpp \
|
||||
io/ioutils.hpp \
|
||||
settings.hpp \
|
||||
uploaders/default/clipboarduploader.hpp \
|
||||
formatter.hpp
|
||||
formatter.hpp \
|
||||
uploaders/customuploader.hpp
|
||||
|
||||
FORMS += mainwindow.ui
|
||||
|
||||
|
@ -9,7 +9,7 @@ namespace ioutils
|
||||
QNetworkAccessManager networkManager;
|
||||
}
|
||||
|
||||
void ioutils::getJson(QUrl target, QList<QPair<QString, QString>> headers, std::function<void(QJsonDocument)> callback)
|
||||
void ioutils::getJson(QUrl target, QList<QPair<QString, QString>> headers, std::function<void(QJsonDocument, QNetworkReply *)> callback)
|
||||
{
|
||||
QNetworkRequest req(target);
|
||||
for (auto header : headers)
|
||||
@ -18,7 +18,7 @@ void ioutils::getJson(QUrl target, QList<QPair<QString, QString>> headers, std::
|
||||
}
|
||||
QNetworkReply *reply = networkManager.get(req);
|
||||
QObject::connect(reply, &QNetworkReply::finished, [reply, callback] {
|
||||
callback(QJsonDocument::fromJson(reply->readAll()));
|
||||
callback(QJsonDocument::fromJson(reply->readAll()), reply);
|
||||
reply->deleteLater();
|
||||
});
|
||||
}
|
||||
@ -26,7 +26,7 @@ void ioutils::getJson(QUrl target, QList<QPair<QString, QString>> headers, std::
|
||||
void ioutils::postJson(QUrl target,
|
||||
QList<QPair<QString, QString>> headers,
|
||||
QByteArray body,
|
||||
std::function<void(QJsonDocument)> callback)
|
||||
std::function<void(QJsonDocument, QNetworkReply *)> callback)
|
||||
{
|
||||
QNetworkRequest req(target);
|
||||
for (auto header : headers)
|
||||
@ -35,7 +35,38 @@ void ioutils::postJson(QUrl target,
|
||||
}
|
||||
QNetworkReply *reply = networkManager.post(req, body);
|
||||
QObject::connect(reply, &QNetworkReply::finished, [reply, callback] {
|
||||
callback(QJsonDocument::fromJson(reply->readAll()));
|
||||
reply->deleteLater();
|
||||
callback(QJsonDocument::fromJson(reply->readAll()), reply);
|
||||
delete reply;
|
||||
});
|
||||
}
|
||||
|
||||
void ioutils::getData(QUrl target, QList<QPair<QString, QString>> headers, std::function<void(QByteArray, QNetworkReply *)> callback)
|
||||
{
|
||||
QNetworkRequest req(target);
|
||||
for (auto header : headers)
|
||||
{
|
||||
req.setRawHeader(header.first.toUtf8(), header.second.toUtf8());
|
||||
}
|
||||
QNetworkReply *reply = networkManager.get(req);
|
||||
QObject::connect(reply, &QNetworkReply::finished, [reply, callback] {
|
||||
callback(reply->readAll(), reply);
|
||||
delete reply;
|
||||
});
|
||||
}
|
||||
|
||||
void ioutils::postData(QUrl target,
|
||||
QList<QPair<QString, QString>> headers,
|
||||
QByteArray body,
|
||||
std::function<void(QByteArray, QNetworkReply *)> callback)
|
||||
{
|
||||
QNetworkRequest req(target);
|
||||
for (auto header : headers)
|
||||
{
|
||||
req.setRawHeader(header.first.toUtf8(), header.second.toUtf8());
|
||||
}
|
||||
QNetworkReply *reply = networkManager.post(req, body);
|
||||
QObject::connect(reply, &QNetworkReply::finished, [reply, callback] {
|
||||
callback(reply->readAll(), reply);
|
||||
delete reply;
|
||||
});
|
||||
}
|
||||
|
@ -10,14 +10,10 @@
|
||||
namespace ioutils
|
||||
{
|
||||
extern QNetworkAccessManager networkManager;
|
||||
void getJson(QUrl target, QList<QPair<QString, QString>> headers, std::function<void(QJsonDocument)> callback);
|
||||
void postJson(QUrl target,
|
||||
QList<QPair<QString, QString>> headers,
|
||||
QByteArray body,
|
||||
std::function<void(QJsonDocument)> callback);
|
||||
// If I need more I will add
|
||||
// Maybe when people start with plugins and custom uploaders
|
||||
// Wait, that's a secret
|
||||
void getJson(QUrl target, QList<QPair<QString, QString>> headers, std::function<void(QJsonDocument, QNetworkReply *)> callback);
|
||||
void postJson(QUrl target, QList<QPair<QString, QString>> headers, QByteArray body, std::function<void(QJsonDocument, QNetworkReply *)> callback);
|
||||
void getData(QUrl target, QList<QPair<QString, QString>> headers, std::function<void(QByteArray, QNetworkReply *)> callback);
|
||||
void postData(QUrl target, QList<QPair<QString, QString>> headers, QByteArray body, std::function<void(QByteArray, QNetworkReply *)> callback);
|
||||
}
|
||||
|
||||
#endif // IOUTILS_HPP
|
||||
|
344
uploaders/customuploader.cpp
Normal file
344
uploaders/customuploader.cpp
Normal file
@ -0,0 +1,344 @@
|
||||
#include "customuploader.hpp"
|
||||
|
||||
#include <QApplication>
|
||||
#include <QBuffer>
|
||||
#include <QClipboard>
|
||||
#include <QDebug>
|
||||
#include <QFile>
|
||||
#include <QJsonDocument>
|
||||
#include <QNetworkReply>
|
||||
#include <io/ioutils.hpp>
|
||||
|
||||
using std::runtime_error;
|
||||
|
||||
CustomUploader::CustomUploader(QString absFilePath)
|
||||
{
|
||||
types.insert("PNG", "image/png"); // Is a list of supported formats, too
|
||||
types.insert("GIF", "image/gif");
|
||||
types.insert("JPG", "image/jpeg");
|
||||
types.insert("JPEG", "image/jpeg");
|
||||
types.insert("WEBM", "video/webm");
|
||||
types.insert("WEBM", "video/mp4");
|
||||
// Let's go
|
||||
QFile file(absFilePath);
|
||||
file.open(QIODevice::ReadOnly | QIODevice::Text);
|
||||
QJsonDocument doc = QJsonDocument::fromJson(file.readAll());
|
||||
if (!doc.isObject())
|
||||
{
|
||||
throw runtime_error(QString("Invalid file: ").append(absFilePath).toStdString());
|
||||
}
|
||||
QJsonObject obj = doc.object();
|
||||
if (!obj["name"].isString())
|
||||
throw runtime_error((QString("Invalid file: ").append(absFilePath) + ": name is not a string").toStdString());
|
||||
else
|
||||
uName = obj["name"].toString();
|
||||
if (!obj.contains("desc"))
|
||||
{
|
||||
if (!obj["desc"].isString())
|
||||
throw runtime_error((QString("Invalid file: ").append(absFilePath) + ": desc not a string, or nonexisting").toStdString());
|
||||
else
|
||||
desc = obj["desc"].toString();
|
||||
}
|
||||
else
|
||||
desc = absFilePath;
|
||||
QJsonValue m = obj["method"];
|
||||
if (!m.isUndefined() && !m.isNull())
|
||||
{
|
||||
if (!m.isString())
|
||||
throw runtime_error((QString("Invalid file: ").append(absFilePath) + ": method not string").toStdString());
|
||||
QString toCheck = m.toString().toLower();
|
||||
if (toCheck == "post")
|
||||
method = HttpMethod::POST;
|
||||
else
|
||||
throw runtime_error((QString("Invalid file: ").append(absFilePath) + ": Bad method").toStdString());
|
||||
}
|
||||
QJsonValue url = obj["target"];
|
||||
if (!url.isString())
|
||||
{
|
||||
throw runtime_error((QString("Invalid file: ").append(absFilePath) + ": target missing").toStdString());
|
||||
}
|
||||
QUrl target(url.toString());
|
||||
if (!target.isValid())
|
||||
throw runtime_error((QString("Invalid file: ").append(absFilePath) + ": target not URL").toStdString());
|
||||
this->target = target;
|
||||
QJsonValue formatValue = obj["format"];
|
||||
if (!formatValue.isUndefined() && !formatValue.isNull())
|
||||
{
|
||||
if (formatValue.isString())
|
||||
{
|
||||
QString formatString = formatValue.toString().toLower();
|
||||
if (formatString == "x-www-form-urlencoded")
|
||||
format = RequestFormat::X_WWW_FORM_URLENCODED;
|
||||
else if (formatString == "json")
|
||||
format = RequestFormat::JSON;
|
||||
else if (formatString == "plain")
|
||||
format = RequestFormat::PLAIN;
|
||||
else
|
||||
throw runtime_error((QString("Invalid file: ").append(absFilePath) + ": format invalid").toStdString());
|
||||
}
|
||||
}
|
||||
else
|
||||
throw runtime_error((QString("Invalid file: ").append(absFilePath) + ": format provided but not string").toStdString());
|
||||
QJsonValue imageValue = obj["imageformat"];
|
||||
if (!imageValue.isString())
|
||||
{
|
||||
throw runtime_error((QString("Invalid file: ").append(absFilePath) + ": imageformat invalid/missing").toStdString());
|
||||
}
|
||||
QString imageFormat = imageValue.toString();
|
||||
if (imageFormat == "base64" || QRegExp("base64\\([^+]+\\+[^+]+)").exactMatch(imageFormat)
|
||||
|| QRegExp("[^+]+\\+[^+]+").exactMatch(imageFormat))
|
||||
{
|
||||
this->iFormat = imageFormat;
|
||||
}
|
||||
else
|
||||
throw runtime_error((QString("Invalid file: ").append(absFilePath) + ": imageformat bad").toStdString());
|
||||
QJsonValue bodyValue = obj["body"];
|
||||
if (format != RequestFormat::PLAIN)
|
||||
{
|
||||
if (bodyValue.isUndefined())
|
||||
throw runtime_error((QString("Invalid file: ").append(absFilePath) + ": body unset").toStdString());
|
||||
if (bodyValue.isObject())
|
||||
body = bodyValue;
|
||||
else
|
||||
throw runtime_error((QString("Invalid file: ").append(absFilePath) + ": body must be object").toStdString());
|
||||
}
|
||||
else
|
||||
{
|
||||
if (bodyValue.isString())
|
||||
{
|
||||
body = bodyValue;
|
||||
}
|
||||
else
|
||||
throw runtime_error((QString("Invalid file: ").append(absFilePath) + ": body must be string (due to PLAIN)").toStdString());
|
||||
}
|
||||
QJsonValue headerVal = obj["headers"];
|
||||
if (!(headerVal.isUndefined() || headerVal.isNull()))
|
||||
{
|
||||
if (!headerVal.isObject())
|
||||
throw runtime_error((QString("Invalid file: ").append(absFilePath) + ": headers must be object").toStdString());
|
||||
headers = headerVal.toObject();
|
||||
}
|
||||
else
|
||||
headers = QJsonObject();
|
||||
QJsonValue returnPsVal = obj["return"];
|
||||
if (returnPsVal.isString())
|
||||
{
|
||||
returnPathspec = returnPsVal.toString();
|
||||
}
|
||||
else
|
||||
throw runtime_error((QString("Invalid file: ").append(absFilePath) + ": return invalid").toStdString());
|
||||
}
|
||||
|
||||
QString CustomUploader::name()
|
||||
{
|
||||
return uName;
|
||||
}
|
||||
|
||||
QString CustomUploader::description()
|
||||
{
|
||||
return desc;
|
||||
}
|
||||
|
||||
QString getCType(RequestFormat format, QString plainType)
|
||||
{
|
||||
switch (format)
|
||||
{
|
||||
case RequestFormat::X_WWW_FORM_URLENCODED:
|
||||
return "application/x-www-form-urlencoded";
|
||||
case RequestFormat::JSON:
|
||||
return "application/json";
|
||||
case RequestFormat::PLAIN:
|
||||
return plainType;
|
||||
}
|
||||
return plainType;
|
||||
}
|
||||
|
||||
QList<QPair<QString, QString>> getHeaders(QJsonObject h, QString imageFormat, QMap<QString, QString> types, RequestFormat format)
|
||||
{
|
||||
QList<QPair<QString, QString>> headers;
|
||||
for (QString s : h.keys())
|
||||
{
|
||||
if (s.toLower() == "content-type") continue;
|
||||
QJsonValue v = h[s];
|
||||
if (!v.isString())
|
||||
headers << QPair<QString, QString>(s, QJsonDocument::fromVariant(v.toVariant()).toJson());
|
||||
else
|
||||
headers << QPair<QString, QString>(s, v.toString());
|
||||
}
|
||||
headers << QPair<QString, QString>("Content-Type", getCType(format, types.value(imageFormat)));
|
||||
return headers;
|
||||
}
|
||||
|
||||
QByteArray imageBytes(QPixmap *pixmap, QString format)
|
||||
{
|
||||
QByteArray returnVal;
|
||||
QBuffer buff(&returnVal);
|
||||
buff.open(QIODevice::WriteOnly);
|
||||
pixmap->save(&buff, format.toUpper().toLocal8Bit().constData());
|
||||
return returnVal;
|
||||
}
|
||||
|
||||
QString CustomUploader::getFormatString(bool animated)
|
||||
{
|
||||
if (iFormat == "base64")
|
||||
return animated ? "GIF" : "PNG";
|
||||
else if (QRegExp("[^+]+\\+[^+]+").exactMatch(iFormat))
|
||||
return iFormat.split('+')[(int)animated];
|
||||
else if (QRegExp("base64\\([^+]+\\+[^+]+)").exactMatch(iFormat))
|
||||
return iFormat.mid(7, iFormat.length() - 8).split('+')[(int)animated];
|
||||
return "";
|
||||
}
|
||||
|
||||
QJsonObject recurseAndReplace(QJsonObject &body, QByteArray &data, QString contentType)
|
||||
{
|
||||
QJsonObject o;
|
||||
for (QString s : body.keys())
|
||||
{
|
||||
QJsonValue v = body[s];
|
||||
if (v.isObject())
|
||||
{
|
||||
QJsonObject vo = v.toObject();
|
||||
o.insert(s, recurseAndReplace(vo, data, contentType));
|
||||
}
|
||||
else if (v.isString())
|
||||
{
|
||||
QString str = v.toString();
|
||||
if (str.startsWith("/") && str.endsWith("/"))
|
||||
{
|
||||
o.insert(s, str.replace("%image", data).replace("%contenttype", contentType));
|
||||
}
|
||||
}
|
||||
}
|
||||
return o;
|
||||
}
|
||||
|
||||
QString parsePathspec(QJsonDocument &response, QString &pathspec)
|
||||
{
|
||||
if (!pathspec.startsWith("."))
|
||||
{
|
||||
// Does not point to anything
|
||||
return "";
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!response.isObject()) return "";
|
||||
QStringList fields = pathspec.right(pathspec.length() - 1).split('.', QString::SkipEmptyParts);
|
||||
QJsonObject o = response.object();
|
||||
if (pathspec == ".")
|
||||
{
|
||||
return QString::fromUtf8(response.toJson());
|
||||
}
|
||||
QJsonValue val = o[fields.at(0)];
|
||||
if (val.isUndefined() || val.isNull())
|
||||
return "";
|
||||
else if (val.isString())
|
||||
return val.toString();
|
||||
else if (!val.isObject())
|
||||
return QString::fromUtf8(QJsonDocument::fromVariant(val.toVariant()).toJson());
|
||||
for (int i = 1; i < fields.size(); i++)
|
||||
{
|
||||
if (val.isUndefined() || val.isNull())
|
||||
return "";
|
||||
else if (val.isString())
|
||||
return val.toString();
|
||||
else if (!val.isObject())
|
||||
return QString::fromUtf8(QJsonDocument::fromVariant(val.toVariant()).toJson());
|
||||
else
|
||||
val = val.toObject()[fields.at(i)];
|
||||
}
|
||||
if (val.isUndefined() || val.isNull())
|
||||
return "";
|
||||
else if (val.isString())
|
||||
return val.toString();
|
||||
else if (!val.isObject())
|
||||
return QString::fromUtf8(QJsonDocument::fromVariant(val.toVariant()).toJson());
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
void CustomUploader::doUpload(QPixmap *pixmap)
|
||||
{
|
||||
auto h = getHeaders(headers, getFormatString(false), types, this->format);
|
||||
QString format = getFormatString(false); // Soon:tm:
|
||||
QByteArray data;
|
||||
QByteArray imgData = imageBytes(pixmap, format);
|
||||
if (iFormat == "base64" || QRegExp("base64\\([^+]\\+[^+]\\)").exactMatch(iFormat)) imgData = imgData.toBase64();
|
||||
switch (this->format)
|
||||
{
|
||||
case RequestFormat::PLAIN:
|
||||
{
|
||||
data = imgData;
|
||||
}
|
||||
break;
|
||||
case RequestFormat::JSON:
|
||||
{
|
||||
if (body.isString())
|
||||
{
|
||||
QStringList split = body.toString().replace("%contenttype", types.value(format)).split("%imagedata");
|
||||
for (int i = 0; i < split.size(); i++)
|
||||
{
|
||||
data.append(split[i]);
|
||||
if (i < split.size() - 1) data.append(imgData);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
QJsonObject vo = body.toObject();
|
||||
data = QJsonDocument::fromVariant(recurseAndReplace(vo, imgData, types.value(format)).toVariantMap()).toJson();
|
||||
}
|
||||
}
|
||||
break;
|
||||
case RequestFormat::X_WWW_FORM_URLENCODED:
|
||||
{
|
||||
QJsonObject body = this->body.toObject();
|
||||
for (QString key : body.keys())
|
||||
{
|
||||
QJsonValue val = body[key];
|
||||
if (val.isString())
|
||||
{
|
||||
QString str = val.toString();
|
||||
QByteArray strB;
|
||||
if (str.startsWith("/") && str.endsWith("/"))
|
||||
{
|
||||
str = str.mid(1, str.length() - 2);
|
||||
QStringList split = str.replace("%contenttype", types.value(format)).split("%imagedata");
|
||||
for (int i = 0; i < split.size(); i++)
|
||||
{
|
||||
strB.append(split[i]);
|
||||
if (i < split.size() - 1) strB.append(imgData);
|
||||
}
|
||||
}
|
||||
data.append(QUrl::toPercentEncoding(key)).append('=').append(strB);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!data.isEmpty()) data.append('&');
|
||||
data.append(QUrl::toPercentEncoding(key))
|
||||
.append('=')
|
||||
.append(QUrl::toPercentEncoding(QJsonDocument::fromVariant(body[key].toVariant()).toJson()));
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
switch (method)
|
||||
{
|
||||
case HttpMethod::POST:
|
||||
if (returnPathspec == "|")
|
||||
{
|
||||
ioutils::postData(target, h, data, [&](QByteArray result, QNetworkReply *) {
|
||||
QApplication::clipboard()->setText(QString::fromUtf8(result));
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
ioutils::postJson(target, h, data, [&](QJsonDocument result, QNetworkReply *) {
|
||||
if (result.isObject())
|
||||
{
|
||||
QApplication::clipboard()->setText(parsePathspec(result, returnPathspec));
|
||||
}
|
||||
});
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
43
uploaders/customuploader.hpp
Normal file
43
uploaders/customuploader.hpp
Normal file
@ -0,0 +1,43 @@
|
||||
#ifndef CUSTOMUPLOADER_HPP
|
||||
#define CUSTOMUPLOADER_HPP
|
||||
|
||||
#include "uploader.hpp"
|
||||
#include <QJsonObject>
|
||||
#include <QMap>
|
||||
#include <QUrl>
|
||||
|
||||
enum class HttpMethod
|
||||
{
|
||||
POST
|
||||
};
|
||||
|
||||
enum class RequestFormat
|
||||
{
|
||||
X_WWW_FORM_URLENCODED,
|
||||
JSON,
|
||||
PLAIN
|
||||
};
|
||||
|
||||
class CustomUploader : public Uploader
|
||||
{
|
||||
public:
|
||||
CustomUploader(QString absFilePath);
|
||||
QString name();
|
||||
QString description();
|
||||
void doUpload(QPixmap *pixmap);
|
||||
QString getFormatString(bool animated);
|
||||
QMap<QString, QString> types;
|
||||
|
||||
private:
|
||||
QString desc;
|
||||
QString uName;
|
||||
RequestFormat format = RequestFormat::JSON;
|
||||
HttpMethod method = HttpMethod::POST;
|
||||
QUrl target;
|
||||
QJsonValue body;
|
||||
QJsonObject headers;
|
||||
QString returnPathspec;
|
||||
QString iFormat;
|
||||
};
|
||||
|
||||
#endif // CUSTOMUPLOADER_HPP
|
@ -13,10 +13,9 @@ void ImgurUploader::doUpload(QPixmap *pixmap)
|
||||
pixmap->save(&buffer, "PNG");
|
||||
ioutils::postJson(QUrl("https://api.imgur.com/3/image"),
|
||||
QList<QPair<QString, QString>>()
|
||||
<< QPair<QString, QString>("Content-Type",
|
||||
"application/x-www-form-urlencoded")
|
||||
<< QPair<QString, QString>("Content-Type", "application/x-www-form-urlencoded")
|
||||
<< QPair<QString, QString>("Authorization", "Client-ID 8a98f183fc895da"),
|
||||
byteArray, [](QJsonDocument res) {
|
||||
byteArray, [](QJsonDocument res, QNetworkReply *) {
|
||||
screenshotutil::toClipboard(res.object()["data"].toObject()["link"].toString());
|
||||
});
|
||||
}
|
||||
|
@ -1,6 +1,8 @@
|
||||
#include "uploadersingleton.hpp"
|
||||
#include "customuploader.hpp"
|
||||
#include "default/clipboarduploader.hpp"
|
||||
#include "default/imguruploader.hpp"
|
||||
#include <QDebug>
|
||||
#include <QDir>
|
||||
#include <QStandardPaths>
|
||||
#include <formatter.hpp>
|
||||
@ -8,15 +10,21 @@
|
||||
|
||||
UploaderSingleton::UploaderSingleton()
|
||||
{
|
||||
// QDir
|
||||
// configDir(QStandardPaths::writableLocation(QStandardPaths::GenericConfigLocation));
|
||||
// configDir.mkpath("KShare/uploaders");
|
||||
// configDir.cd("KShare/uploaders");
|
||||
// configDir.setNameFilters({"*.uploader"});
|
||||
// for (QString file : configDir.entryList()) {
|
||||
// registerUploader(new CustomUploader(file));
|
||||
// }
|
||||
// TODO
|
||||
QDir configDir(QStandardPaths::writableLocation(QStandardPaths::GenericConfigLocation));
|
||||
configDir.mkpath("KShare/uploaders");
|
||||
configDir.cd("KShare/uploaders");
|
||||
configDir.setNameFilters({ "*.uploader" });
|
||||
for (QString file : configDir.entryList())
|
||||
{
|
||||
try
|
||||
{
|
||||
registerUploader(new CustomUploader(configDir.absoluteFilePath(file)));
|
||||
}
|
||||
catch (std::runtime_error e)
|
||||
{
|
||||
qWarning() << e.what(); // u wot m8
|
||||
}
|
||||
}
|
||||
|
||||
// UPLOADERS
|
||||
registerUploader(new ImgurUploader);
|
||||
|
Loading…
Reference in New Issue
Block a user