mirror of
https://github.com/Chatterino/chatterino2.git
synced 2024-11-13 19:49:51 +01:00
Refactored the Image Uploader feature. (#4971)
Co-authored-by: Rasmus Karlsson <rasmus.karlsson@pajlada.com>
This commit is contained in:
parent
7898b97fc2
commit
fbc8aacabe
|
@ -39,6 +39,8 @@
|
|||
- Bugfix: Fixed a freeze from a bad regex in _Ignores_. (#4965)
|
||||
- Bugfix: Fixed some emotes not appearing when using _Ignores_. (#4965)
|
||||
- Bugfix: Fixed lookahead/-behind not working in _Ignores_. (#4965)
|
||||
- Bugfix: Fixed Image Uploader accidentally deleting images with some hosts when link resolver was enabled. (#4971)
|
||||
- Bugfix: Fixed rare crash with Image Uploader when closing a split right after starting an upload. (#4971)
|
||||
- Dev: Change clang-format from v14 to v16. (#4929)
|
||||
- Dev: Fixed UTF16 encoding of `modes` file for the installer. (#4791)
|
||||
- Dev: Temporarily disable High DPI scaling on Qt6 builds on Windows. (#4767)
|
||||
|
@ -66,6 +68,7 @@
|
|||
- Dev: `Details` file properties tab is now populated on Windows. (#4912)
|
||||
- Dev: Removed `Outcome` from network requests. (#4959)
|
||||
- Dev: Added Tests for Windows and MacOS in CI. (#4970)
|
||||
- Dev: Refactored the Image Uploader feature. (#4971)
|
||||
|
||||
## 2.4.6
|
||||
|
||||
|
|
|
@ -89,6 +89,11 @@ public:
|
|||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
ImageUploader *getImageUploader() override
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace chatterino::mock
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
#include "controllers/hotkeys/HotkeyController.hpp"
|
||||
#include "controllers/ignores/IgnoreController.hpp"
|
||||
#include "controllers/notifications/NotificationController.hpp"
|
||||
#include "singletons/ImageUploader.hpp"
|
||||
#ifdef CHATTERINO_HAVE_PLUGINS
|
||||
# include "controllers/plugins/PluginController.hpp"
|
||||
#endif
|
||||
|
@ -79,6 +80,7 @@ Application::Application(Settings &_settings, Paths &_paths)
|
|||
, hotkeys(&this->emplace<HotkeyController>())
|
||||
, windows(&this->emplace<WindowManager>())
|
||||
, toasts(&this->emplace<Toasts>())
|
||||
, imageUploader(&this->emplace<ImageUploader>())
|
||||
|
||||
, commands(&this->emplace<CommandController>())
|
||||
, notifications(&this->emplace<NotificationController>())
|
||||
|
|
|
@ -41,6 +41,7 @@ class Toasts;
|
|||
class ChatterinoBadges;
|
||||
class FfzBadges;
|
||||
class SeventvBadges;
|
||||
class ImageUploader;
|
||||
|
||||
class IApplication
|
||||
{
|
||||
|
@ -66,6 +67,7 @@ public:
|
|||
virtual SeventvBadges *getSeventvBadges() = 0;
|
||||
virtual IUserDataController *getUserData() = 0;
|
||||
virtual ITwitchLiveController *getTwitchLiveController() = 0;
|
||||
virtual ImageUploader *getImageUploader() = 0;
|
||||
};
|
||||
|
||||
class Application : public IApplication
|
||||
|
@ -94,6 +96,7 @@ public:
|
|||
HotkeyController *const hotkeys{};
|
||||
WindowManager *const windows{};
|
||||
Toasts *const toasts{};
|
||||
ImageUploader *const imageUploader{};
|
||||
|
||||
CommandController *const commands{};
|
||||
NotificationController *const notifications{};
|
||||
|
@ -167,6 +170,10 @@ public:
|
|||
}
|
||||
IUserDataController *getUserData() override;
|
||||
ITwitchLiveController *getTwitchLiveController() override;
|
||||
ImageUploader *getImageUploader() override
|
||||
{
|
||||
return this->imageUploader;
|
||||
}
|
||||
|
||||
pajlada::Signals::NoArgSignal streamerModeChanged;
|
||||
|
||||
|
|
|
@ -424,6 +424,8 @@ set(SOURCE_FILES
|
|||
singletons/Emotes.hpp
|
||||
singletons/Fonts.cpp
|
||||
singletons/Fonts.hpp
|
||||
singletons/ImageUploader.cpp
|
||||
singletons/ImageUploader.hpp
|
||||
singletons/Logging.cpp
|
||||
singletons/Logging.hpp
|
||||
singletons/NativeMessaging.cpp
|
||||
|
@ -475,8 +477,6 @@ set(SOURCE_FILES
|
|||
util/IpcQueue.hpp
|
||||
util/LayoutHelper.cpp
|
||||
util/LayoutHelper.hpp
|
||||
util/NuulsUploader.cpp
|
||||
util/NuulsUploader.hpp
|
||||
util/RapidjsonHelpers.cpp
|
||||
util/RapidjsonHelpers.hpp
|
||||
util/RatelimitBucket.cpp
|
||||
|
|
|
@ -32,7 +32,7 @@ Q_LOGGING_CATEGORY(chatterinoNativeMessage, "chatterino.nativemessage",
|
|||
Q_LOGGING_CATEGORY(chatterinoNetwork, "chatterino.network", logThreshold);
|
||||
Q_LOGGING_CATEGORY(chatterinoNotification, "chatterino.notification",
|
||||
logThreshold);
|
||||
Q_LOGGING_CATEGORY(chatterinoNuulsuploader, "chatterino.nuulsuploader",
|
||||
Q_LOGGING_CATEGORY(chatterinoImageuploader, "chatterino.imageuploader",
|
||||
logThreshold);
|
||||
Q_LOGGING_CATEGORY(chatterinoPubSub, "chatterino.pubsub", logThreshold);
|
||||
Q_LOGGING_CATEGORY(chatterinoRecentMessages, "chatterino.recentmessages",
|
||||
|
|
|
@ -25,7 +25,7 @@ Q_DECLARE_LOGGING_CATEGORY(chatterinoMessage);
|
|||
Q_DECLARE_LOGGING_CATEGORY(chatterinoNativeMessage);
|
||||
Q_DECLARE_LOGGING_CATEGORY(chatterinoNetwork);
|
||||
Q_DECLARE_LOGGING_CATEGORY(chatterinoNotification);
|
||||
Q_DECLARE_LOGGING_CATEGORY(chatterinoNuulsuploader);
|
||||
Q_DECLARE_LOGGING_CATEGORY(chatterinoImageuploader);
|
||||
Q_DECLARE_LOGGING_CATEGORY(chatterinoPubSub);
|
||||
Q_DECLARE_LOGGING_CATEGORY(chatterinoRecentMessages);
|
||||
Q_DECLARE_LOGGING_CATEGORY(chatterinoSettings);
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
#include "controllers/accounts/AccountController.hpp"
|
||||
#include "messages/Image.hpp"
|
||||
#include "messages/Message.hpp"
|
||||
#include "messages/MessageColor.hpp"
|
||||
#include "messages/MessageElement.hpp"
|
||||
#include "providers/LinkResolver.hpp"
|
||||
#include "providers/twitch/PubSubActions.hpp"
|
||||
|
@ -660,6 +661,58 @@ MessageBuilder::MessageBuilder(LiveUpdatesUpdateEmoteSetMessageTag /*unused*/,
|
|||
this->message().flags.set(MessageFlag::DoNotTriggerNotification);
|
||||
}
|
||||
|
||||
MessageBuilder::MessageBuilder(ImageUploaderResultTag /*unused*/,
|
||||
const QString &imageLink,
|
||||
const QString &deletionLink,
|
||||
size_t imagesStillQueued, size_t secondsLeft)
|
||||
: MessageBuilder()
|
||||
{
|
||||
this->message().flags.set(MessageFlag::System);
|
||||
this->message().flags.set(MessageFlag::DoNotTriggerNotification);
|
||||
|
||||
this->emplace<TimestampElement>();
|
||||
|
||||
using MEF = MessageElementFlag;
|
||||
auto addText = [this](QString text, MessageElementFlags mefs = MEF::Text,
|
||||
MessageColor color =
|
||||
MessageColor::System) -> TextElement * {
|
||||
this->message().searchText += text;
|
||||
this->message().messageText += text;
|
||||
return this->emplace<TextElement>(text, mefs, color);
|
||||
};
|
||||
|
||||
addText("Your image has been uploaded to");
|
||||
|
||||
// ASSUMPTION: the user gave this uploader configuration to the program
|
||||
// therefore they trust that the host is not wrong/malicious. This doesn't obey getSettings()->lowercaseDomains.
|
||||
// This also ensures that the LinkResolver doesn't get these links.
|
||||
addText(imageLink, {MEF::OriginalLink, MEF::LowercaseLink},
|
||||
MessageColor::Link)
|
||||
->setLink({Link::Url, imageLink})
|
||||
->setTrailingSpace(false);
|
||||
|
||||
if (!deletionLink.isEmpty())
|
||||
{
|
||||
addText("(Deletion link:");
|
||||
addText(deletionLink, {MEF::OriginalLink, MEF::LowercaseLink},
|
||||
MessageColor::Link)
|
||||
->setLink({Link::Url, deletionLink})
|
||||
->setTrailingSpace(false);
|
||||
addText(")")->setTrailingSpace(false);
|
||||
}
|
||||
addText(".");
|
||||
|
||||
if (imagesStillQueued == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
addText(QString("%1 left. Please wait until all of them are uploaded. "
|
||||
"About %2 seconds left.")
|
||||
.arg(imagesStillQueued)
|
||||
.arg(secondsLeft));
|
||||
}
|
||||
|
||||
Message *MessageBuilder::operator->()
|
||||
{
|
||||
return this->message_.get();
|
||||
|
|
|
@ -37,6 +37,9 @@ struct LiveUpdatesAddEmoteMessageTag {
|
|||
};
|
||||
struct LiveUpdatesUpdateEmoteSetMessageTag {
|
||||
};
|
||||
struct ImageUploaderResultTag {
|
||||
};
|
||||
|
||||
const SystemMessageTag systemMessage{};
|
||||
const TimeoutMessageTag timeoutMessage{};
|
||||
const LiveUpdatesUpdateEmoteMessageTag liveUpdatesUpdateEmoteMessage{};
|
||||
|
@ -44,6 +47,10 @@ const LiveUpdatesRemoveEmoteMessageTag liveUpdatesRemoveEmoteMessage{};
|
|||
const LiveUpdatesAddEmoteMessageTag liveUpdatesAddEmoteMessage{};
|
||||
const LiveUpdatesUpdateEmoteSetMessageTag liveUpdatesUpdateEmoteSetMessage{};
|
||||
|
||||
// This signifies that you want to construct a message containing the result of
|
||||
// a successful image upload.
|
||||
const ImageUploaderResultTag imageUploaderResultMessage{};
|
||||
|
||||
MessagePtr makeSystemMessage(const QString &text);
|
||||
MessagePtr makeSystemMessage(const QString &text, const QTime &time);
|
||||
std::pair<MessagePtr, MessagePtr> makeAutomodMessage(
|
||||
|
@ -88,6 +95,16 @@ public:
|
|||
MessageBuilder(LiveUpdatesUpdateEmoteSetMessageTag, const QString &platform,
|
||||
const QString &actor, const QString &emoteSetName);
|
||||
|
||||
/**
|
||||
* "Your image has been uploaded to %1[ (Deletion link: %2)]."
|
||||
* or "Your image has been uploaded to %1 %2. %3 left. "
|
||||
* "Please wait until all of them are uploaded. "
|
||||
* "About %4 seconds left."
|
||||
*/
|
||||
MessageBuilder(ImageUploaderResultTag, const QString &imageLink,
|
||||
const QString &deletionLink, size_t imagesStillQueued = 0,
|
||||
size_t secondsLeft = 0);
|
||||
|
||||
virtual ~MessageBuilder() = default;
|
||||
|
||||
Message *operator->();
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
#include "NuulsUploader.hpp"
|
||||
#include "singletons/ImageUploader.hpp"
|
||||
|
||||
#include "common/Env.hpp"
|
||||
#include "common/NetworkRequest.hpp"
|
||||
#include "common/NetworkResult.hpp"
|
||||
#include "common/QLogging.hpp"
|
||||
#include "messages/MessageBuilder.hpp"
|
||||
#include "providers/twitch/TwitchMessageBuilder.hpp"
|
||||
#include "singletons/Paths.hpp"
|
||||
#include "singletons/Settings.hpp"
|
||||
|
@ -16,6 +17,7 @@
|
|||
#include <QJsonDocument>
|
||||
#include <QMimeDatabase>
|
||||
#include <QMutex>
|
||||
#include <QPointer>
|
||||
#include <QSaveFile>
|
||||
|
||||
#define UPLOAD_DELAY 2000
|
||||
|
@ -41,13 +43,10 @@ std::optional<QByteArray> convertToPng(const QImage &image)
|
|||
|
||||
namespace chatterino {
|
||||
|
||||
// These variables are only used from the main thread.
|
||||
static auto uploadMutex = QMutex();
|
||||
static std::queue<RawImageData> uploadQueue;
|
||||
|
||||
// logging information on successful uploads to a json file
|
||||
void logToFile(const QString originalFilePath, QString imageLink,
|
||||
QString deletionLink, ChannelPtr channel)
|
||||
void ImageUploader::logToFile(const QString &originalFilePath,
|
||||
const QString &imageLink,
|
||||
const QString &deletionLink, ChannelPtr channel)
|
||||
{
|
||||
const QString logFileName =
|
||||
combinePath((getSettings()->logPath.getValue().isEmpty()
|
||||
|
@ -120,8 +119,13 @@ QString getLinkFromResponse(NetworkResult response, QString pattern)
|
|||
return pattern;
|
||||
}
|
||||
|
||||
void uploadImageToNuuls(RawImageData imageData, ChannelPtr channel,
|
||||
ResizingTextEdit &textEdit)
|
||||
void ImageUploader::save()
|
||||
{
|
||||
}
|
||||
|
||||
void ImageUploader::sendImageUploadRequest(RawImageData imageData,
|
||||
ChannelPtr channel,
|
||||
QPointer<ResizingTextEdit> textEdit)
|
||||
{
|
||||
const static char *const boundary = "thisistheboudaryasd";
|
||||
const static QString contentType =
|
||||
|
@ -155,91 +159,103 @@ void uploadImageToNuuls(RawImageData imageData, ChannelPtr channel,
|
|||
.header("Content-Type", contentType)
|
||||
.headerList(extraHeaders)
|
||||
.multiPart(payload)
|
||||
.onSuccess([&textEdit, channel,
|
||||
originalFilePath](NetworkResult result) {
|
||||
QString link = getSettings()->imageUploaderLink.getValue().isEmpty()
|
||||
? result.getData()
|
||||
: getLinkFromResponse(
|
||||
result, getSettings()->imageUploaderLink);
|
||||
QString deletionLink =
|
||||
getSettings()->imageUploaderDeletionLink.getValue().isEmpty()
|
||||
? ""
|
||||
: getLinkFromResponse(
|
||||
result, getSettings()->imageUploaderDeletionLink);
|
||||
qCDebug(chatterinoNuulsuploader) << link << deletionLink;
|
||||
textEdit.insertPlainText(link + " ");
|
||||
if (uploadQueue.empty())
|
||||
{
|
||||
channel->addMessage(makeSystemMessage(
|
||||
QString("Your image has been uploaded to %1 %2.")
|
||||
.arg(link)
|
||||
.arg(deletionLink.isEmpty()
|
||||
? ""
|
||||
: QString("(Deletion link: %1 )")
|
||||
.arg(deletionLink))));
|
||||
uploadMutex.unlock();
|
||||
}
|
||||
else
|
||||
{
|
||||
channel->addMessage(makeSystemMessage(
|
||||
QString("Your image has been uploaded to %1 %2. %3 left. "
|
||||
"Please wait until all of them are uploaded. "
|
||||
"About %4 seconds left.")
|
||||
.arg(link)
|
||||
.arg(deletionLink.isEmpty()
|
||||
? ""
|
||||
: QString("(Deletion link: %1 )")
|
||||
.arg(deletionLink))
|
||||
.arg(uploadQueue.size())
|
||||
.arg(uploadQueue.size() * (UPLOAD_DELAY / 1000 + 1))));
|
||||
// 2 seconds for the timer that's there not to spam the remote server
|
||||
// and 1 second of actual uploading.
|
||||
|
||||
QTimer::singleShot(UPLOAD_DELAY, [channel, &textEdit]() {
|
||||
uploadImageToNuuls(uploadQueue.front(), channel, textEdit);
|
||||
uploadQueue.pop();
|
||||
});
|
||||
}
|
||||
|
||||
logToFile(originalFilePath, link, deletionLink, channel);
|
||||
})
|
||||
.onError([channel](NetworkResult result) -> bool {
|
||||
auto errorMessage =
|
||||
QString("An error happened while uploading your image: %1")
|
||||
.arg(result.formatError());
|
||||
|
||||
// Try to read more information from the result body
|
||||
auto obj = result.parseJson();
|
||||
if (!obj.isEmpty())
|
||||
{
|
||||
auto apiCode = obj.value("code");
|
||||
if (!apiCode.isUndefined())
|
||||
{
|
||||
auto codeString = apiCode.toVariant().toString();
|
||||
codeString.truncate(20);
|
||||
errorMessage += QString(" - code: %1").arg(codeString);
|
||||
}
|
||||
|
||||
auto apiError = obj.value("error").toString();
|
||||
if (!apiError.isEmpty())
|
||||
{
|
||||
apiError.truncate(300);
|
||||
errorMessage +=
|
||||
QString(" - error: %1").arg(apiError.trimmed());
|
||||
}
|
||||
}
|
||||
|
||||
channel->addMessage(makeSystemMessage(errorMessage));
|
||||
uploadMutex.unlock();
|
||||
.onSuccess(
|
||||
[textEdit, channel, originalFilePath, this](NetworkResult result) {
|
||||
this->handleSuccessfulUpload(result, originalFilePath, channel,
|
||||
textEdit);
|
||||
})
|
||||
.onError([channel, this](NetworkResult result) -> bool {
|
||||
this->handleFailedUpload(result, channel);
|
||||
return true;
|
||||
})
|
||||
.execute();
|
||||
}
|
||||
|
||||
void upload(const QMimeData *source, ChannelPtr channel,
|
||||
ResizingTextEdit &outputTextEdit)
|
||||
void ImageUploader::handleFailedUpload(const NetworkResult &result,
|
||||
ChannelPtr channel)
|
||||
{
|
||||
if (!uploadMutex.tryLock())
|
||||
auto errorMessage =
|
||||
QString("An error happened while uploading your image: %1")
|
||||
.arg(result.formatError());
|
||||
|
||||
// Try to read more information from the result body
|
||||
auto obj = result.parseJson();
|
||||
if (!obj.isEmpty())
|
||||
{
|
||||
auto apiCode = obj.value("code");
|
||||
if (!apiCode.isUndefined())
|
||||
{
|
||||
auto codeString = apiCode.toVariant().toString();
|
||||
codeString.truncate(20);
|
||||
errorMessage += QString(" - code: %1").arg(codeString);
|
||||
}
|
||||
|
||||
auto apiError = obj.value("error").toString();
|
||||
if (!apiError.isEmpty())
|
||||
{
|
||||
apiError.truncate(300);
|
||||
errorMessage += QString(" - error: %1").arg(apiError.trimmed());
|
||||
}
|
||||
}
|
||||
|
||||
channel->addMessage(makeSystemMessage(errorMessage));
|
||||
this->uploadMutex_.unlock();
|
||||
}
|
||||
|
||||
void ImageUploader::handleSuccessfulUpload(const NetworkResult &result,
|
||||
QString originalFilePath,
|
||||
ChannelPtr channel,
|
||||
QPointer<ResizingTextEdit> textEdit)
|
||||
{
|
||||
if (textEdit == nullptr)
|
||||
{
|
||||
// Split was destroyed abort further uploads
|
||||
|
||||
while (!this->uploadQueue_.empty())
|
||||
{
|
||||
this->uploadQueue_.pop();
|
||||
}
|
||||
this->uploadMutex_.unlock();
|
||||
return;
|
||||
}
|
||||
QString link =
|
||||
getSettings()->imageUploaderLink.getValue().isEmpty()
|
||||
? result.getData()
|
||||
: getLinkFromResponse(result, getSettings()->imageUploaderLink);
|
||||
QString deletionLink =
|
||||
getSettings()->imageUploaderDeletionLink.getValue().isEmpty()
|
||||
? ""
|
||||
: getLinkFromResponse(result,
|
||||
getSettings()->imageUploaderDeletionLink);
|
||||
qCDebug(chatterinoImageuploader) << link << deletionLink;
|
||||
textEdit->insertPlainText(link + " ");
|
||||
|
||||
// 2 seconds for the timer that's there not to spam the remote server
|
||||
// and 1 second of actual uploading.
|
||||
auto timeToUpload = this->uploadQueue_.size() * (UPLOAD_DELAY / 1000 + 1);
|
||||
MessageBuilder builder(imageUploaderResultMessage, link, deletionLink,
|
||||
this->uploadQueue_.size(), timeToUpload);
|
||||
channel->addMessage(builder.release());
|
||||
if (this->uploadQueue_.empty())
|
||||
{
|
||||
this->uploadMutex_.unlock();
|
||||
}
|
||||
else
|
||||
{
|
||||
QTimer::singleShot(UPLOAD_DELAY, [channel, &textEdit, this]() {
|
||||
this->sendImageUploadRequest(this->uploadQueue_.front(), channel,
|
||||
textEdit);
|
||||
this->uploadQueue_.pop();
|
||||
});
|
||||
}
|
||||
|
||||
this->logToFile(originalFilePath, link, deletionLink, channel);
|
||||
}
|
||||
|
||||
void ImageUploader::upload(const QMimeData *source, ChannelPtr channel,
|
||||
QPointer<ResizingTextEdit> outputTextEdit)
|
||||
{
|
||||
if (!this->uploadMutex_.tryLock())
|
||||
{
|
||||
channel->addMessage(makeSystemMessage(
|
||||
QString("Please wait until the upload finishes.")));
|
||||
|
@ -265,7 +281,7 @@ void upload(const QMimeData *source, ChannelPtr channel,
|
|||
{
|
||||
channel->addMessage(
|
||||
makeSystemMessage(QString("Couldn't load image :(")));
|
||||
uploadMutex.unlock();
|
||||
this->uploadMutex_.unlock();
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -273,7 +289,7 @@ void upload(const QMimeData *source, ChannelPtr channel,
|
|||
if (imageData)
|
||||
{
|
||||
RawImageData data = {*imageData, "png", localPath};
|
||||
uploadQueue.push(data);
|
||||
this->uploadQueue_.push(data);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -281,7 +297,7 @@ void upload(const QMimeData *source, ChannelPtr channel,
|
|||
QString("Cannot upload file: %1. Couldn't convert "
|
||||
"image to png.")
|
||||
.arg(localPath)));
|
||||
uploadMutex.unlock();
|
||||
this->uploadMutex_.unlock();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -295,11 +311,11 @@ void upload(const QMimeData *source, ChannelPtr channel,
|
|||
{
|
||||
channel->addMessage(
|
||||
makeSystemMessage(QString("Failed to open file. :(")));
|
||||
uploadMutex.unlock();
|
||||
this->uploadMutex_.unlock();
|
||||
return;
|
||||
}
|
||||
RawImageData data = {file.readAll(), "gif", localPath};
|
||||
uploadQueue.push(data);
|
||||
this->uploadQueue_.push(data);
|
||||
file.close();
|
||||
// file.readAll() => might be a bit big but it /should/ work
|
||||
}
|
||||
|
@ -308,31 +324,32 @@ void upload(const QMimeData *source, ChannelPtr channel,
|
|||
channel->addMessage(makeSystemMessage(
|
||||
QString("Cannot upload file: %1. Not an image.")
|
||||
.arg(localPath)));
|
||||
uploadMutex.unlock();
|
||||
this->uploadMutex_.unlock();
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (!uploadQueue.empty())
|
||||
if (!this->uploadQueue_.empty())
|
||||
{
|
||||
uploadImageToNuuls(uploadQueue.front(), channel, outputTextEdit);
|
||||
uploadQueue.pop();
|
||||
this->sendImageUploadRequest(this->uploadQueue_.front(), channel,
|
||||
outputTextEdit);
|
||||
this->uploadQueue_.pop();
|
||||
}
|
||||
}
|
||||
else if (source->hasFormat("image/png"))
|
||||
{
|
||||
// the path to file is not present every time, thus the filePath is empty
|
||||
uploadImageToNuuls({source->data("image/png"), "png", ""}, channel,
|
||||
outputTextEdit);
|
||||
this->sendImageUploadRequest({source->data("image/png"), "png", ""},
|
||||
channel, outputTextEdit);
|
||||
}
|
||||
else if (source->hasFormat("image/jpeg"))
|
||||
{
|
||||
uploadImageToNuuls({source->data("image/jpeg"), "jpeg", ""}, channel,
|
||||
outputTextEdit);
|
||||
this->sendImageUploadRequest({source->data("image/jpeg"), "jpeg", ""},
|
||||
channel, outputTextEdit);
|
||||
}
|
||||
else if (source->hasFormat("image/gif"))
|
||||
{
|
||||
uploadImageToNuuls({source->data("image/gif"), "gif", ""}, channel,
|
||||
outputTextEdit);
|
||||
this->sendImageUploadRequest({source->data("image/gif"), "gif", ""},
|
||||
channel, outputTextEdit);
|
||||
}
|
||||
|
||||
else
|
||||
|
@ -341,14 +358,14 @@ void upload(const QMimeData *source, ChannelPtr channel,
|
|||
auto imageData = convertToPng(image);
|
||||
if (imageData)
|
||||
{
|
||||
uploadImageToNuuls({*imageData, "png", ""}, channel,
|
||||
outputTextEdit);
|
||||
sendImageUploadRequest({*imageData, "png", ""}, channel,
|
||||
outputTextEdit);
|
||||
}
|
||||
else
|
||||
{
|
||||
channel->addMessage(makeSystemMessage(
|
||||
QString("Cannot upload file, failed to convert to png.")));
|
||||
uploadMutex.unlock();
|
||||
this->uploadMutex_.unlock();
|
||||
}
|
||||
}
|
||||
}
|
49
src/singletons/ImageUploader.hpp
Normal file
49
src/singletons/ImageUploader.hpp
Normal file
|
@ -0,0 +1,49 @@
|
|||
#pragma once
|
||||
|
||||
#include "common/Singleton.hpp"
|
||||
|
||||
#include <QMimeData>
|
||||
#include <QMutex>
|
||||
#include <QString>
|
||||
|
||||
#include <memory>
|
||||
#include <queue>
|
||||
|
||||
namespace chatterino {
|
||||
|
||||
class ResizingTextEdit;
|
||||
class Channel;
|
||||
class NetworkResult;
|
||||
using ChannelPtr = std::shared_ptr<Channel>;
|
||||
|
||||
struct RawImageData {
|
||||
QByteArray data;
|
||||
QString format;
|
||||
QString filePath;
|
||||
};
|
||||
|
||||
class ImageUploader final : public Singleton
|
||||
{
|
||||
public:
|
||||
void save() override;
|
||||
void upload(const QMimeData *source, ChannelPtr channel,
|
||||
QPointer<ResizingTextEdit> outputTextEdit);
|
||||
|
||||
private:
|
||||
void sendImageUploadRequest(RawImageData imageData, ChannelPtr channel,
|
||||
QPointer<ResizingTextEdit> textEdit);
|
||||
|
||||
// This is called from the onSuccess handler of the NetworkRequest in sendImageUploadRequest
|
||||
void handleSuccessfulUpload(const NetworkResult &result,
|
||||
QString originalFilePath, ChannelPtr channel,
|
||||
QPointer<ResizingTextEdit> textEdit);
|
||||
void handleFailedUpload(const NetworkResult &result, ChannelPtr channel);
|
||||
|
||||
void logToFile(const QString &originalFilePath, const QString &imageLink,
|
||||
const QString &deletionLink, ChannelPtr channel);
|
||||
|
||||
// These variables are only used from the main thread.
|
||||
QMutex uploadMutex_;
|
||||
std::queue<RawImageData> uploadQueue_;
|
||||
};
|
||||
} // namespace chatterino
|
|
@ -1,27 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <QMimeData>
|
||||
#include <QString>
|
||||
|
||||
#include <memory>
|
||||
|
||||
namespace chatterino {
|
||||
|
||||
class ResizingTextEdit;
|
||||
class Channel;
|
||||
using ChannelPtr = std::shared_ptr<Channel>;
|
||||
|
||||
struct RawImageData {
|
||||
QByteArray data;
|
||||
QString format;
|
||||
QString filePath;
|
||||
};
|
||||
|
||||
void upload(QByteArray imageData, ChannelPtr channel,
|
||||
ResizingTextEdit &textEdit, std::string format);
|
||||
void upload(RawImageData imageData, ChannelPtr channel,
|
||||
ResizingTextEdit &textEdit);
|
||||
void upload(const QMimeData *source, ChannelPtr channel,
|
||||
ResizingTextEdit &outputTextEdit);
|
||||
|
||||
} // namespace chatterino
|
|
@ -16,12 +16,12 @@
|
|||
#include "providers/twitch/TwitchIrcServer.hpp"
|
||||
#include "providers/twitch/TwitchMessageBuilder.hpp"
|
||||
#include "singletons/Fonts.hpp"
|
||||
#include "singletons/ImageUploader.hpp"
|
||||
#include "singletons/Settings.hpp"
|
||||
#include "singletons/Theme.hpp"
|
||||
#include "singletons/WindowManager.hpp"
|
||||
#include "util/Clipboard.hpp"
|
||||
#include "util/Helpers.hpp"
|
||||
#include "util/NuulsUploader.hpp"
|
||||
#include "util/StreamLink.hpp"
|
||||
#include "widgets/dialogs/QualityPopup.hpp"
|
||||
#include "widgets/dialogs/SelectChannelDialog.hpp"
|
||||
|
@ -405,7 +405,8 @@ Split::Split(QWidget *parent)
|
|||
getSettings()->askOnImageUpload.setValue(false);
|
||||
}
|
||||
}
|
||||
upload(source, this->getChannel(), *this->input_->ui_.textEdit);
|
||||
QPointer<ResizingTextEdit> edit = this->input_->ui_.textEdit;
|
||||
getApp()->imageUploader->upload(source, this->getChannel(), edit);
|
||||
});
|
||||
|
||||
getSettings()->imageUploaderEnabled.connect(
|
||||
|
|
Loading…
Reference in a new issue