mirror of
https://github.com/Chatterino/chatterino2.git
synced 2024-11-21 22:24:07 +01:00
Networkmanager (#134)
* rename ImageLoader* to Network* * static NetworkManager * NetworkManager queue arbitrary requests * modify urlfetch to use NetworkManager * urlfetchjson in terms of NetworkManager * fetchurljson fetchurltimeout fetchurljsontimeout special fetch url functions with various connects and functions to be called * operate on fetched data in the correct thread * operate on fetched resources in correct thread * networkmanager urlfetch functions * expose urlfetch functions of networkmanager through util and util::twitch * add caller to util functions * cleanup * formatting * urlPut function for NetworkManager and util::twitch * cleanup worker (no more leak) * use urlfetch for LazyLoadedImage::loadImage * Rename NetworkManager methods (#1) * Rename NetworkManager methods Remove unused NetworkManager methods Remove unused NetworkManager includes Reorder includes in lazyloadedimage.cpp and urlfetch.hpp * try to simplify code, might break everything * fixed some more stuff?
This commit is contained in:
parent
a4c0bdb926
commit
f51372102e
17 changed files with 521 additions and 427 deletions
|
@ -97,7 +97,7 @@ SOURCES += \
|
|||
src/widgets/rippleeffectlabel.cpp \
|
||||
src/widgets/rippleeffectbutton.cpp \
|
||||
src/messages/messagecolor.cpp \
|
||||
src/messages/imageloadermanager.cpp
|
||||
src/util/networkmanager.cpp
|
||||
|
||||
HEADERS += \
|
||||
src/asyncexec.hpp \
|
||||
|
@ -164,8 +164,8 @@ HEADERS += \
|
|||
src/messages/messagecolor.hpp \
|
||||
src/util/nativeeventhelper.hpp \
|
||||
src/debug/log.hpp \
|
||||
src/messages/imageloadermanager.hpp \
|
||||
src/util/benchmark.hpp
|
||||
src/util/benchmark.hpp \
|
||||
src/util/networkmanager.hpp
|
||||
|
||||
PRECOMPILED_HEADER =
|
||||
|
||||
|
|
|
@ -48,7 +48,9 @@ void EmoteManager::reloadBTTVChannelEmotes(const QString &channelName, std::weak
|
|||
printf("[EmoteManager] Reload BTTV Channel Emotes for channel %s\n", qPrintable(channelName));
|
||||
|
||||
QString url("https://api.betterttv.net/2/channels/" + channelName);
|
||||
util::urlFetchJSON(url, [this, channelName, _map](QJsonObject &rootNode) {
|
||||
util::urlFetchJSONTimeout(
|
||||
url, QThread::currentThread(),
|
||||
[this, channelName, _map](QJsonObject &rootNode) {
|
||||
auto map = _map.lock();
|
||||
|
||||
if (_map.expired()) {
|
||||
|
@ -74,9 +76,10 @@ void EmoteManager::reloadBTTVChannelEmotes(const QString &channelName, std::weak
|
|||
|
||||
link = link.replace("{{id}}", id).replace("{{image}}", "1x");
|
||||
|
||||
auto emote = this->getBTTVChannelEmoteFromCaches().getOrAdd(id, [this, &code, &link] {
|
||||
return EmoteData(new LazyLoadedImage(*this, this->windowManager, link, 1, code,
|
||||
code + "\nChannel BTTV Emote"));
|
||||
auto emote =
|
||||
this->getBTTVChannelEmoteFromCaches().getOrAdd(id, [this, &code, &link] {
|
||||
return EmoteData(new LazyLoadedImage(*this, this->windowManager, link, 1,
|
||||
code, code + "\nChannel BTTV Emote"));
|
||||
});
|
||||
|
||||
this->bttvChannelEmotes.insert(code, emote);
|
||||
|
@ -85,7 +88,8 @@ void EmoteManager::reloadBTTVChannelEmotes(const QString &channelName, std::weak
|
|||
}
|
||||
|
||||
this->bttvChannelEmoteCodes[channelName.toStdString()] = codes;
|
||||
});
|
||||
},
|
||||
1500);
|
||||
}
|
||||
|
||||
void EmoteManager::reloadFFZChannelEmotes(const QString &channelName, std::weak_ptr<EmoteMap> _map)
|
||||
|
@ -94,7 +98,9 @@ void EmoteManager::reloadFFZChannelEmotes(const QString &channelName, std::weak_
|
|||
|
||||
QString url("http://api.frankerfacez.com/v1/room/" + channelName);
|
||||
|
||||
util::urlFetchJSON(url, [this, channelName, _map](QJsonObject &rootNode) {
|
||||
util::urlFetchJSONTimeout(
|
||||
url, QThread::currentThread(),
|
||||
[this, channelName, _map](QJsonObject &rootNode) {
|
||||
auto map = _map.lock();
|
||||
|
||||
if (_map.expired()) {
|
||||
|
@ -119,8 +125,8 @@ void EmoteManager::reloadFFZChannelEmotes(const QString &channelName, std::weak_
|
|||
QJsonObject urls = emoteObject.value("urls").toObject();
|
||||
QString url1 = "http:" + urls.value("1").toString();
|
||||
|
||||
auto emote =
|
||||
this->getFFZChannelEmoteFromCaches().getOrAdd(id, [this, &code, &url1] {
|
||||
auto emote = this->getFFZChannelEmoteFromCaches().getOrAdd(id, [this, &code,
|
||||
&url1] {
|
||||
return EmoteData(new LazyLoadedImage(*this, this->windowManager, url1, 1,
|
||||
code, code + "\nGlobal FFZ Emote"));
|
||||
});
|
||||
|
@ -132,7 +138,8 @@ void EmoteManager::reloadFFZChannelEmotes(const QString &channelName, std::weak_
|
|||
|
||||
this->ffzChannelEmoteCodes[channelName.toStdString()] = codes;
|
||||
}
|
||||
});
|
||||
},
|
||||
1500);
|
||||
}
|
||||
|
||||
ConcurrentMap<QString, twitch::EmoteValue *> &EmoteManager::getTwitchEmotes()
|
||||
|
@ -377,11 +384,10 @@ void EmoteManager::refreshTwitchEmotes(const std::string &roomID)
|
|||
qDebug() << url;
|
||||
|
||||
util::urlFetchJSONTimeout(
|
||||
url,
|
||||
url, QThread::currentThread(),
|
||||
[=, &emoteData](QJsonObject &root) {
|
||||
emoteData.emoteSets.clear();
|
||||
emoteData.emoteCodes.clear();
|
||||
|
||||
auto emoticonSets = root.value("emoticon_sets").toObject();
|
||||
for (QJsonObject::iterator it = emoticonSets.begin(); it != emoticonSets.end(); ++it) {
|
||||
std::string emoteSetString = it.key().toStdString();
|
||||
|
|
|
@ -154,7 +154,6 @@ void IrcManager::beginConnecting()
|
|||
this->writeConnection->sendRaw("JOIN #" + channel->name);
|
||||
this->readConnection->sendRaw("JOIN #" + channel->name);
|
||||
}
|
||||
|
||||
this->writeConnection->open();
|
||||
this->readConnection->open();
|
||||
} else {
|
||||
|
|
|
@ -7,6 +7,8 @@
|
|||
#include <QStandardPaths>
|
||||
#include <pajlada/settings/settingmanager.hpp>
|
||||
|
||||
#include "util/networkmanager.hpp"
|
||||
|
||||
#ifdef USEWINSDK
|
||||
#include "windows.h"
|
||||
#endif
|
||||
|
@ -88,6 +90,9 @@ int main(int argc, char *argv[])
|
|||
return 1;
|
||||
}
|
||||
|
||||
// Initialize NetworkManager
|
||||
chatterino::util::NetworkManager::init();
|
||||
|
||||
int ret = 0;
|
||||
|
||||
{
|
||||
|
@ -103,5 +108,8 @@ int main(int argc, char *argv[])
|
|||
// Save settings
|
||||
pajlada::Settings::SettingManager::save();
|
||||
|
||||
// Deinitialize NetworkManager (stop thread and wait for finish, should be instant)
|
||||
chatterino::util::NetworkManager::deinit();
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
|
|
@ -1,92 +0,0 @@
|
|||
#include "messages/imageloadermanager.hpp"
|
||||
#include "emotemanager.hpp"
|
||||
#include "messages/lazyloadedimage.hpp"
|
||||
#include "windowmanager.hpp"
|
||||
|
||||
#include <QApplication>
|
||||
#include <QBuffer>
|
||||
#include <QImageReader>
|
||||
#include <QNetworkAccessManager>
|
||||
#include <QNetworkReply>
|
||||
#include <QNetworkRequest>
|
||||
|
||||
namespace chatterino {
|
||||
namespace messages {
|
||||
|
||||
ImageLoaderManager::ImageLoaderManager()
|
||||
{
|
||||
this->NaM.moveToThread(&this->workerThread);
|
||||
this->workerThread.start();
|
||||
}
|
||||
|
||||
ImageLoaderManager::~ImageLoaderManager()
|
||||
{
|
||||
this->workerThread.quit();
|
||||
this->workerThread.wait();
|
||||
}
|
||||
|
||||
void ImageLoaderWorker::handleRequest(LazyLoadedImage *lli, QNetworkAccessManager *nam)
|
||||
{
|
||||
QNetworkRequest request(QUrl(lli->getUrl()));
|
||||
QNetworkReply *reply = nam->get(request);
|
||||
|
||||
QObject::connect(reply, &QNetworkReply::finished,
|
||||
[lli, reply, this]() { this->handleLoad(lli, reply); });
|
||||
}
|
||||
|
||||
void ImageLoaderManager::queue(chatterino::messages::LazyLoadedImage *lli)
|
||||
{
|
||||
ImageLoaderRequester requester;
|
||||
ImageLoaderWorker *worker = new ImageLoaderWorker;
|
||||
|
||||
worker->moveToThread(&this->workerThread);
|
||||
|
||||
QObject::connect(&requester, &ImageLoaderRequester::request, worker,
|
||||
&ImageLoaderWorker::handleRequest);
|
||||
QObject::connect(worker, &ImageLoaderWorker::done, lli,
|
||||
[lli]() { lli->windowManager.layoutVisibleChatWidgets(); });
|
||||
|
||||
emit requester.request(lli, &this->NaM);
|
||||
}
|
||||
|
||||
void ImageLoaderWorker::handleLoad(chatterino::messages::LazyLoadedImage *lli, QNetworkReply *reply)
|
||||
{
|
||||
QByteArray array = reply->readAll();
|
||||
QBuffer buffer(&array);
|
||||
buffer.open(QIODevice::ReadOnly);
|
||||
|
||||
QImage image;
|
||||
QImageReader reader(&buffer);
|
||||
|
||||
bool first = true;
|
||||
|
||||
for (int index = 0; index < reader.imageCount(); ++index) {
|
||||
if (reader.read(&image)) {
|
||||
auto pixmap = new QPixmap(QPixmap::fromImage(image));
|
||||
|
||||
if (first) {
|
||||
first = false;
|
||||
lli->currentPixmap = pixmap;
|
||||
}
|
||||
|
||||
LazyLoadedImage::FrameData data;
|
||||
data.duration = std::max(20, reader.nextImageDelay());
|
||||
data.image = pixmap;
|
||||
|
||||
lli->allFrames.push_back(data);
|
||||
}
|
||||
}
|
||||
|
||||
if (lli->allFrames.size() > 1) {
|
||||
lli->animated = true;
|
||||
}
|
||||
|
||||
lli->emoteManager.incGeneration();
|
||||
|
||||
reply->deleteLater();
|
||||
emit this->done();
|
||||
delete this;
|
||||
}
|
||||
|
||||
} // namespace messages
|
||||
} // namespace chatterino
|
|
@ -1,46 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <QNetworkAccessManager>
|
||||
#include <QThread>
|
||||
|
||||
namespace chatterino {
|
||||
namespace messages {
|
||||
|
||||
class LazyLoadedImage;
|
||||
|
||||
class ImageLoaderWorker : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public slots:
|
||||
void handleRequest(chatterino::messages::LazyLoadedImage *lli, QNetworkAccessManager *nam);
|
||||
void handleLoad(LazyLoadedImage *lli, QNetworkReply *reply);
|
||||
|
||||
signals:
|
||||
void done();
|
||||
};
|
||||
|
||||
class ImageLoaderRequester : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
signals:
|
||||
void request(chatterino::messages::LazyLoadedImage *lli, QNetworkAccessManager *nam);
|
||||
};
|
||||
|
||||
class ImageLoaderManager : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
QThread workerThread;
|
||||
QNetworkAccessManager NaM;
|
||||
|
||||
public:
|
||||
ImageLoaderManager();
|
||||
~ImageLoaderManager();
|
||||
|
||||
void queue(chatterino::messages::LazyLoadedImage *lli);
|
||||
};
|
||||
|
||||
} // namespace messages
|
||||
} // namespace chatterino
|
|
@ -2,7 +2,7 @@
|
|||
#include "asyncexec.hpp"
|
||||
#include "emotemanager.hpp"
|
||||
#include "ircmanager.hpp"
|
||||
#include "messages/imageloadermanager.hpp"
|
||||
#include "util/networkmanager.hpp"
|
||||
#include "util/urlfetch.hpp"
|
||||
#include "windowmanager.hpp"
|
||||
|
||||
|
@ -12,9 +12,9 @@
|
|||
#include <QNetworkReply>
|
||||
#include <QNetworkRequest>
|
||||
#include <QTimer>
|
||||
#include <thread>
|
||||
|
||||
#include <functional>
|
||||
#include <thread>
|
||||
|
||||
namespace chatterino {
|
||||
namespace messages {
|
||||
|
@ -52,8 +52,43 @@ LazyLoadedImage::LazyLoadedImage(EmoteManager &_emoteManager, WindowManager &_wi
|
|||
|
||||
void LazyLoadedImage::loadImage()
|
||||
{
|
||||
static ImageLoaderManager imageLoader;
|
||||
imageLoader.queue(this);
|
||||
util::NetworkRequest req(this->getUrl());
|
||||
req.setCaller(this);
|
||||
req.get([lli = this](QNetworkReply * reply) {
|
||||
QByteArray array = reply->readAll();
|
||||
QBuffer buffer(&array);
|
||||
buffer.open(QIODevice::ReadOnly);
|
||||
|
||||
QImage image;
|
||||
QImageReader reader(&buffer);
|
||||
|
||||
bool first = true;
|
||||
|
||||
for (int index = 0; index < reader.imageCount(); ++index) {
|
||||
if (reader.read(&image)) {
|
||||
auto pixmap = new QPixmap(QPixmap::fromImage(image));
|
||||
|
||||
if (first) {
|
||||
first = false;
|
||||
lli->currentPixmap = pixmap;
|
||||
}
|
||||
|
||||
chatterino::messages::LazyLoadedImage::FrameData data;
|
||||
data.duration = std::max(20, reader.nextImageDelay());
|
||||
data.image = pixmap;
|
||||
|
||||
lli->allFrames.push_back(data);
|
||||
}
|
||||
}
|
||||
|
||||
if (lli->allFrames.size() > 1) {
|
||||
lli->animated = true;
|
||||
}
|
||||
|
||||
lli->emoteManager.incGeneration();
|
||||
|
||||
lli->windowManager.layoutVisibleChatWidgets();
|
||||
});
|
||||
|
||||
this->emoteManager.getGifUpdateSignal().connect([=]() {
|
||||
this->gifUpdateTimout();
|
||||
|
|
|
@ -64,9 +64,6 @@ private:
|
|||
|
||||
void loadImage();
|
||||
void gifUpdateTimout();
|
||||
|
||||
friend class ImageLoaderWorker;
|
||||
friend class ImageLoaderManager;
|
||||
};
|
||||
|
||||
} // namespace messages
|
||||
|
|
|
@ -40,9 +40,9 @@ Resources::Resources(EmoteManager &em, WindowManager &wm)
|
|||
{
|
||||
QString badgesUrl("https://badges.twitch.tv/v1/badges/global/display?language=en");
|
||||
|
||||
util::urlFetchJSON(badgesUrl, [this](QJsonObject &root) {
|
||||
util::urlFetchJSON(badgesUrl, QThread::currentThread(), [this](QJsonObject &root) {
|
||||
QJsonObject sets = root.value("badge_sets").toObject();
|
||||
|
||||
qDebug() << "badges fetched";
|
||||
for (QJsonObject::iterator it = sets.begin(); it != sets.end(); ++it) {
|
||||
QJsonObject versions = it.value().toObject().value("versions").toObject();
|
||||
|
||||
|
@ -88,7 +88,7 @@ void Resources::loadChannelData(const std::string &roomID, bool bypassCache)
|
|||
QString url = "https://badges.twitch.tv/v1/badges/channels/" + QString::fromStdString(roomID) +
|
||||
"/display?language=en";
|
||||
|
||||
util::urlFetchJSON(url, [this, roomID](QJsonObject &root) {
|
||||
util::urlFetchJSON(url, QThread::currentThread(), [this, roomID](QJsonObject &root) {
|
||||
QJsonObject sets = root.value("badge_sets").toObject();
|
||||
|
||||
Resources::Channel &ch = this->channels[roomID];
|
||||
|
@ -118,9 +118,9 @@ void Resources::loadChatterinoBadges()
|
|||
|
||||
QString url = "https://fourtf.com/chatterino/badges.json";
|
||||
|
||||
util::urlFetchJSON(url, [this](QJsonObject &root) {
|
||||
util::urlFetchJSON(url, QThread::currentThread(), [this](QJsonObject &root) {
|
||||
QJsonArray badgeVariants = root.value("badges").toArray();
|
||||
|
||||
qDebug() << "chatbadges fetched";
|
||||
for (QJsonArray::iterator it = badgeVariants.begin(); it != badgeVariants.end(); ++it) {
|
||||
QJsonObject badgeVariant = it->toObject();
|
||||
const std::string badgeVariantTooltip =
|
||||
|
|
24
src/util/networkmanager.cpp
Normal file
24
src/util/networkmanager.cpp
Normal file
|
@ -0,0 +1,24 @@
|
|||
#include "util/networkmanager.hpp"
|
||||
|
||||
#include <QNetworkAccessManager>
|
||||
|
||||
namespace chatterino {
|
||||
namespace util {
|
||||
|
||||
QThread NetworkManager::workerThread;
|
||||
QNetworkAccessManager NetworkManager::NaM;
|
||||
|
||||
void NetworkManager::init()
|
||||
{
|
||||
NetworkManager::NaM.moveToThread(&NetworkManager::workerThread);
|
||||
NetworkManager::workerThread.start();
|
||||
}
|
||||
|
||||
void NetworkManager::deinit()
|
||||
{
|
||||
NetworkManager::workerThread.quit();
|
||||
NetworkManager::workerThread.wait();
|
||||
}
|
||||
|
||||
} // namespace util
|
||||
} // namespace chatterino
|
233
src/util/networkmanager.hpp
Normal file
233
src/util/networkmanager.hpp
Normal file
|
@ -0,0 +1,233 @@
|
|||
#pragma once
|
||||
|
||||
#include <QNetworkAccessManager>
|
||||
#include <QThread>
|
||||
#include <QUrl>
|
||||
|
||||
namespace chatterino {
|
||||
|
||||
namespace messages {
|
||||
|
||||
class LazyLoadedImage;
|
||||
|
||||
} // namespace messages
|
||||
|
||||
namespace util {
|
||||
|
||||
class NetworkRequest
|
||||
{
|
||||
struct Data {
|
||||
QNetworkRequest request;
|
||||
const QObject *caller = nullptr;
|
||||
std::function<void(QNetworkReply *)> onReplyCreated;
|
||||
} data;
|
||||
|
||||
public:
|
||||
NetworkRequest() = delete;
|
||||
|
||||
explicit NetworkRequest(const char *url)
|
||||
{
|
||||
this->data.request.setUrl(QUrl(url));
|
||||
}
|
||||
|
||||
explicit NetworkRequest(const std::string &url)
|
||||
{
|
||||
this->data.request.setUrl(QUrl(QString::fromStdString(url)));
|
||||
}
|
||||
|
||||
explicit NetworkRequest(const QString &url)
|
||||
{
|
||||
this->data.request.setUrl(QUrl(url));
|
||||
}
|
||||
|
||||
void setCaller(const QObject *_caller)
|
||||
{
|
||||
this->data.caller = _caller;
|
||||
}
|
||||
|
||||
void setOnReplyCreated(std::function<void(QNetworkReply *)> f)
|
||||
{
|
||||
this->data.onReplyCreated = f;
|
||||
}
|
||||
|
||||
void setRawHeader(const QByteArray &headerName, const QByteArray &value)
|
||||
{
|
||||
this->data.request.setRawHeader(headerName, value);
|
||||
}
|
||||
|
||||
template <typename FinishedCallback>
|
||||
void get(FinishedCallback onFinished)
|
||||
{
|
||||
NetworkRequester requester;
|
||||
NetworkWorker *worker = new NetworkWorker;
|
||||
|
||||
worker->moveToThread(&NetworkManager::workerThread);
|
||||
|
||||
if (this->data.caller != nullptr) {
|
||||
QObject::connect(worker, &NetworkWorker::doneUrl, this->data.caller,
|
||||
[onFinished](auto reply) {
|
||||
onFinished(reply);
|
||||
reply->deleteLater();
|
||||
});
|
||||
}
|
||||
|
||||
QObject::connect(
|
||||
&requester, &NetworkRequester::requestUrl, worker,
|
||||
[ data = std::move(this->data), worker, onFinished{std::move(onFinished)} ]() {
|
||||
QNetworkReply *reply = NetworkManager::NaM.get(data.request);
|
||||
|
||||
if (data.onReplyCreated) {
|
||||
data.onReplyCreated(reply);
|
||||
}
|
||||
|
||||
QObject::connect(reply, &QNetworkReply::finished, worker,
|
||||
[ data = std::move(this->data), worker, reply, onFinished ]() {
|
||||
if (data.caller == nullptr) {
|
||||
onFinished(reply);
|
||||
|
||||
reply->deleteLater();
|
||||
} else {
|
||||
emit worker->doneUrl(reply);
|
||||
}
|
||||
|
||||
delete worker;
|
||||
});
|
||||
});
|
||||
|
||||
emit requester.requestUrl();
|
||||
}
|
||||
};
|
||||
|
||||
class NetworkWorker : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
signals:
|
||||
void doneUrl(QNetworkReply *);
|
||||
};
|
||||
|
||||
class NetworkRequester : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
signals:
|
||||
void requestUrl();
|
||||
};
|
||||
|
||||
class NetworkManager : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
static QThread workerThread;
|
||||
static QNetworkAccessManager NaM;
|
||||
|
||||
static void init();
|
||||
static void deinit();
|
||||
|
||||
template <typename FinishedCallback>
|
||||
static void urlFetch(QNetworkRequest request, FinishedCallback onFinished)
|
||||
{
|
||||
NetworkRequester requester;
|
||||
NetworkWorker *worker = new NetworkWorker;
|
||||
|
||||
worker->moveToThread(&NetworkManager::workerThread);
|
||||
QObject::connect(&requester, &NetworkRequester::requestUrl, worker,
|
||||
[ onFinished = std::move(onFinished), request = std::move(request) ]() {
|
||||
QNetworkReply *reply = NetworkManager::NaM.get(request);
|
||||
|
||||
QObject::connect(reply, &QNetworkReply::finished,
|
||||
[onFinished, reply, worker]() {
|
||||
onFinished(reply);
|
||||
delete worker;
|
||||
});
|
||||
});
|
||||
|
||||
emit requester.requestUrl();
|
||||
}
|
||||
|
||||
template <typename FinishedCallback>
|
||||
static void urlFetch(const QUrl &url, FinishedCallback onFinished)
|
||||
{
|
||||
urlFetch(QNetworkRequest(url), std::move(onFinished));
|
||||
}
|
||||
|
||||
template <typename Callback, typename ReplyCreatedCallback = void (*)(QNetworkReply *)>
|
||||
static void urlFetch(QNetworkRequest request, const QObject *caller, Callback callback,
|
||||
ReplyCreatedCallback onReplyCreated = [](QNetworkReply *) { return; })
|
||||
{
|
||||
NetworkRequester requester;
|
||||
NetworkWorker *worker = new NetworkWorker;
|
||||
|
||||
worker->moveToThread(&NetworkManager::workerThread);
|
||||
|
||||
QObject::connect(&requester, &NetworkRequester::requestUrl, worker, [=]() {
|
||||
QNetworkReply *reply = NetworkManager::NaM.get(request);
|
||||
|
||||
onReplyCreated(reply);
|
||||
|
||||
QObject::connect(reply, &QNetworkReply::finished, worker,
|
||||
[=]() { emit worker->doneUrl(reply); });
|
||||
});
|
||||
|
||||
QObject::connect(worker, &NetworkWorker::doneUrl, caller, [=](QNetworkReply *reply) {
|
||||
callback(reply);
|
||||
delete worker;
|
||||
});
|
||||
emit requester.requestUrl();
|
||||
}
|
||||
|
||||
template <typename Callback, typename ReplyCreatedCallback = void (*)(QNetworkReply *)>
|
||||
static void urlFetch(const QUrl &url, const QObject *caller, Callback callback,
|
||||
ReplyCreatedCallback onReplyCreated = [](QNetworkReply *) { return; })
|
||||
{
|
||||
urlFetch(QNetworkRequest(url), caller, callback, onReplyCreated);
|
||||
}
|
||||
|
||||
template <typename FinishedCallback>
|
||||
static void urlPut(QNetworkRequest request, FinishedCallback onFinished, QByteArray *data)
|
||||
{
|
||||
NetworkRequester requester;
|
||||
NetworkWorker *worker = new NetworkWorker;
|
||||
|
||||
worker->moveToThread(&NetworkManager::workerThread);
|
||||
QObject::connect(
|
||||
&requester, &NetworkRequester::requestUrl, worker,
|
||||
[ onFinished = std::move(onFinished), request = std::move(request), data ]() {
|
||||
QNetworkReply *reply = NetworkManager::NaM.put(request, *data);
|
||||
|
||||
QObject::connect(reply, &QNetworkReply::finished,
|
||||
[ onFinished = std::move(onFinished), reply ]() {
|
||||
onFinished(reply);
|
||||
delete worker;
|
||||
});
|
||||
});
|
||||
|
||||
emit requester.requestUrl();
|
||||
}
|
||||
|
||||
template <typename FinishedCallback>
|
||||
static void urlPut(QNetworkRequest request, FinishedCallback onFinished)
|
||||
{
|
||||
NetworkRequester requester;
|
||||
NetworkWorker *worker = new NetworkWorker;
|
||||
|
||||
worker->moveToThread(&NetworkManager::workerThread);
|
||||
QObject::connect(
|
||||
&requester, &NetworkRequester::requestUrl, worker,
|
||||
[ onFinished = std::move(onFinished), request = std::move(request), worker ]() {
|
||||
QNetworkReply *reply = NetworkManager::NaM.put(request, "");
|
||||
|
||||
QObject::connect(reply, &QNetworkReply::finished,
|
||||
[ onFinished = std::move(onFinished), reply, worker ]() {
|
||||
onFinished(reply);
|
||||
delete worker;
|
||||
});
|
||||
});
|
||||
|
||||
emit requester.requestUrl();
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace util
|
||||
} // namespace chatterino
|
|
@ -1,7 +1,8 @@
|
|||
#pragma once
|
||||
|
||||
#include "credentials.hpp"
|
||||
#include "accountmanager.hpp"
|
||||
#include "credentials.hpp"
|
||||
#include "util/networkmanager.hpp"
|
||||
|
||||
#include <QEventLoop>
|
||||
#include <QJsonArray>
|
||||
|
@ -14,45 +15,105 @@
|
|||
#include <QString>
|
||||
#include <QTimer>
|
||||
|
||||
#include <QDebug>
|
||||
|
||||
#include <functional>
|
||||
|
||||
namespace chatterino {
|
||||
namespace util {
|
||||
|
||||
namespace twitch {
|
||||
|
||||
static void get(QString url, std::function<void(QJsonObject &)> successCallback)
|
||||
static QJsonObject parseJSONFromReply(QNetworkReply *reply)
|
||||
{
|
||||
auto manager = new QNetworkAccessManager();
|
||||
if (reply->error() != QNetworkReply::NetworkError::NoError) {
|
||||
return QJsonObject();
|
||||
}
|
||||
|
||||
QUrl requestUrl(url);
|
||||
QNetworkRequest request(requestUrl);
|
||||
|
||||
request.setRawHeader("Client-ID", getDefaultClientID());
|
||||
request.setRawHeader("Accept", "application/vnd.twitchtv.v5+json");
|
||||
|
||||
QNetworkReply *reply = manager->get(request);
|
||||
|
||||
QObject::connect(reply, &QNetworkReply::finished, [=] {
|
||||
if (reply->error() == QNetworkReply::NetworkError::NoError) {
|
||||
QByteArray data = reply->readAll();
|
||||
QJsonDocument jsonDoc(QJsonDocument::fromJson(data));
|
||||
|
||||
if (!jsonDoc.isNull()) {
|
||||
QJsonObject rootNode = jsonDoc.object();
|
||||
|
||||
successCallback(rootNode);
|
||||
}
|
||||
if (jsonDoc.isNull()) {
|
||||
return QJsonObject();
|
||||
}
|
||||
|
||||
reply->deleteLater();
|
||||
manager->deleteLater();
|
||||
return jsonDoc.object();
|
||||
}
|
||||
|
||||
static void urlFetchJSON(const QString &url, const QObject *caller,
|
||||
std::function<void(QJsonObject &)> successCallback)
|
||||
{
|
||||
util::NetworkRequest req(url);
|
||||
req.setCaller(caller);
|
||||
req.get([=](QNetworkReply *reply) {
|
||||
auto node = parseJSONFromReply(reply);
|
||||
successCallback(node);
|
||||
});
|
||||
}
|
||||
|
||||
static void getUserID(QString username, std::function<void(QString)> successCallback)
|
||||
static void urlFetchTimeout(const QString &url, const QObject *caller,
|
||||
std::function<void(QNetworkReply *)> successCallback, int timeoutMs)
|
||||
{
|
||||
get("https://api.twitch.tv/kraken/users?login=" + username, [=](const QJsonObject &root) {
|
||||
QTimer *timer = new QTimer;
|
||||
timer->setSingleShot(true);
|
||||
|
||||
QEventLoop *loop = new QEventLoop;
|
||||
|
||||
util::NetworkRequest req(url);
|
||||
req.setCaller(loop);
|
||||
req.setOnReplyCreated([loop, timer](QNetworkReply *reply) {
|
||||
QObject::connect(timer, &QTimer::timeout, loop, [=]() {
|
||||
QObject::disconnect(reply, &QNetworkReply::finished, loop, &QEventLoop::quit);
|
||||
reply->abort();
|
||||
reply->deleteLater();
|
||||
});
|
||||
});
|
||||
req.get([=](QNetworkReply *reply) {
|
||||
if (reply->error() == QNetworkReply::NetworkError::NoError) {
|
||||
successCallback(reply);
|
||||
}
|
||||
|
||||
reply->deleteLater();
|
||||
loop->quit();
|
||||
});
|
||||
|
||||
QObject::connect(timer, SIGNAL(timeout()), loop, SLOT(quit()));
|
||||
|
||||
timer->start(timeoutMs);
|
||||
loop->exec();
|
||||
delete timer;
|
||||
delete loop;
|
||||
}
|
||||
|
||||
static void urlFetchJSONTimeout(const QString &url, const QObject *caller,
|
||||
std::function<void(QJsonObject &)> successCallback, int timeoutMs)
|
||||
{
|
||||
urlFetchTimeout(url, caller,
|
||||
[=](QNetworkReply *reply) {
|
||||
auto node = parseJSONFromReply(reply);
|
||||
successCallback(node);
|
||||
},
|
||||
timeoutMs);
|
||||
}
|
||||
|
||||
namespace twitch {
|
||||
|
||||
static void get(QString url, const QObject *caller,
|
||||
std::function<void(QJsonObject &)> successCallback)
|
||||
{
|
||||
util::NetworkRequest req(url);
|
||||
req.setCaller(caller);
|
||||
req.setRawHeader("Client-ID", getDefaultClientID());
|
||||
req.setRawHeader("Accept", "application/vnd.twitchtv.v5+json");
|
||||
req.get([=](QNetworkReply *reply) {
|
||||
auto node = parseJSONFromReply(reply);
|
||||
successCallback(node);
|
||||
});
|
||||
}
|
||||
|
||||
static void getUserID(QString username, const QObject *caller,
|
||||
std::function<void(QString)> successCallback)
|
||||
{
|
||||
get("https://api.twitch.tv/kraken/users?login=" + username, caller,
|
||||
[=](const QJsonObject &root) {
|
||||
if (!root.value("users").isArray()) {
|
||||
qDebug() << "API Error while getting user id, users is not an array";
|
||||
return;
|
||||
|
@ -70,8 +131,8 @@ static void getUserID(QString username, std::function<void(QString)> successCall
|
|||
auto firstUser = users[0].toObject();
|
||||
auto id = firstUser.value("_id");
|
||||
if (!id.isString()) {
|
||||
qDebug()
|
||||
<< "API Error: while getting user id, first user object `_id` key is not a string";
|
||||
qDebug() << "API Error: while getting user id, first user object `_id` key is not "
|
||||
"a string";
|
||||
return;
|
||||
}
|
||||
successCallback(id.toString());
|
||||
|
@ -84,157 +145,24 @@ static void put(QUrl url, std::function<void(QJsonObject)> successCallback)
|
|||
|
||||
request.setRawHeader("Client-ID", getDefaultClientID());
|
||||
request.setRawHeader("Accept", "application/vnd.twitchtv.v5+json");
|
||||
request.setRawHeader("Authorization", "OAuth " +
|
||||
AccountManager::getInstance().getTwitchUser().getOAuthToken().toUtf8());
|
||||
QNetworkReply *reply = manager->put(request,"");
|
||||
QObject::connect(reply, &QNetworkReply::finished, [=](){
|
||||
if(reply->error() == QNetworkReply::NetworkError::NoError)
|
||||
{
|
||||
request.setRawHeader(
|
||||
"Authorization",
|
||||
"OAuth " + AccountManager::getInstance().getTwitchUser().getOAuthToken().toUtf8());
|
||||
|
||||
NetworkManager::urlPut(std::move(request), [=](QNetworkReply *reply) {
|
||||
if (reply->error() == QNetworkReply::NetworkError::NoError) {
|
||||
QByteArray data = reply->readAll();
|
||||
QJsonDocument jsonDoc(QJsonDocument::fromJson(data));
|
||||
if(!jsonDoc.isNull())
|
||||
{
|
||||
if (!jsonDoc.isNull()) {
|
||||
QJsonObject rootNode = jsonDoc.object();
|
||||
|
||||
successCallback(rootNode);
|
||||
}
|
||||
}
|
||||
reply->deleteLater();
|
||||
manager->deleteLater();
|
||||
});
|
||||
}
|
||||
|
||||
} // namespace twitch
|
||||
|
||||
static void urlFetch(const QString &url, std::function<void(QNetworkReply &)> successCallback,
|
||||
QNetworkAccessManager *manager = nullptr)
|
||||
{
|
||||
bool customManager = true;
|
||||
|
||||
if (manager == nullptr) {
|
||||
manager = new QNetworkAccessManager();
|
||||
customManager = false;
|
||||
}
|
||||
|
||||
QUrl requestUrl(url);
|
||||
QNetworkRequest request(requestUrl);
|
||||
|
||||
QNetworkReply *reply = manager->get(request);
|
||||
|
||||
QObject::connect(reply, &QNetworkReply::finished, [=] {
|
||||
/* uncomment to follow redirects
|
||||
QVariant replyStatus = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute);
|
||||
if (replyStatus >= 300 && replyStatus <= 304) {
|
||||
QString newUrl =
|
||||
reply->attribute(QNetworkRequest::RedirectionTargetAttribute).toString();
|
||||
urlFetch(newUrl, successCallback);
|
||||
return;
|
||||
}
|
||||
*/
|
||||
|
||||
if (reply->error() == QNetworkReply::NetworkError::NoError) {
|
||||
successCallback(*reply);
|
||||
}
|
||||
|
||||
reply->deleteLater();
|
||||
if (!customManager) {
|
||||
manager->deleteLater();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
static void urlFetchJSON(const QString &url, std::function<void(QJsonObject &)> successCallback,
|
||||
QNetworkAccessManager *manager = nullptr)
|
||||
{
|
||||
urlFetch(url,
|
||||
[=](QNetworkReply &reply) {
|
||||
QByteArray data = reply.readAll();
|
||||
QJsonDocument jsonDoc(QJsonDocument::fromJson(data));
|
||||
|
||||
if (jsonDoc.isNull()) {
|
||||
return;
|
||||
}
|
||||
|
||||
QJsonObject rootNode = jsonDoc.object();
|
||||
|
||||
successCallback(rootNode);
|
||||
},
|
||||
manager);
|
||||
}
|
||||
|
||||
static void urlFetchTimeout(const QString &url,
|
||||
std::function<void(QNetworkReply &)> successCallback, int timeoutMs,
|
||||
QNetworkAccessManager *manager = nullptr)
|
||||
{
|
||||
bool customManager = true;
|
||||
|
||||
if (manager == nullptr) {
|
||||
manager = new QNetworkAccessManager();
|
||||
customManager = false;
|
||||
}
|
||||
|
||||
QUrl requestUrl(url);
|
||||
QNetworkRequest request(requestUrl);
|
||||
|
||||
QNetworkReply *reply = manager->get(request);
|
||||
|
||||
QTimer timer;
|
||||
timer.setSingleShot(true);
|
||||
|
||||
QEventLoop loop;
|
||||
QObject::connect(&timer, SIGNAL(timeout()), &loop, SLOT(quit()));
|
||||
QObject::connect(reply, SIGNAL(finished()), &loop, SLOT(quit()));
|
||||
QObject::connect(reply, &QNetworkReply::finished, [=] {
|
||||
/* uncomment to follow redirects
|
||||
QVariant replyStatus = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute);
|
||||
if (replyStatus >= 300 && replyStatus <= 304) {
|
||||
QString newUrl =
|
||||
reply->attribute(QNetworkRequest::RedirectionTargetAttribute).toString();
|
||||
urlFetch(newUrl, successCallback);
|
||||
return;
|
||||
}
|
||||
*/
|
||||
|
||||
if (reply->error() == QNetworkReply::NetworkError::NoError) {
|
||||
successCallback(*reply);
|
||||
}
|
||||
|
||||
reply->deleteLater();
|
||||
if (!customManager) {
|
||||
manager->deleteLater();
|
||||
}
|
||||
});
|
||||
timer.start(timeoutMs);
|
||||
loop.exec();
|
||||
|
||||
if (!timer.isActive()) {
|
||||
qDebug() << "TIMED OUT";
|
||||
QObject::disconnect(reply, SIGNAL(finished()), &loop, SLOT(quit()));
|
||||
reply->abort();
|
||||
} else {
|
||||
// qDebug() << "XDDD HEHEHE";
|
||||
}
|
||||
}
|
||||
|
||||
static void urlFetchJSONTimeout(const QString &url,
|
||||
std::function<void(QJsonObject &)> successCallback, int timeoutMs,
|
||||
QNetworkAccessManager *manager = nullptr)
|
||||
{
|
||||
urlFetchTimeout(url,
|
||||
[=](QNetworkReply &reply) {
|
||||
QByteArray data = reply.readAll();
|
||||
QJsonDocument jsonDoc(QJsonDocument::fromJson(data));
|
||||
|
||||
if (jsonDoc.isNull()) {
|
||||
return;
|
||||
}
|
||||
|
||||
QJsonObject rootNode = jsonDoc.object();
|
||||
|
||||
successCallback(rootNode);
|
||||
},
|
||||
timeoutMs, manager);
|
||||
}
|
||||
|
||||
} // namespace util
|
||||
} // namespace chatterino
|
||||
|
|
|
@ -7,7 +7,6 @@
|
|||
#include "ui_accountpopupform.h"
|
||||
|
||||
#include <QClipboard>
|
||||
#include <QDebug>
|
||||
#include <QDesktopServices>
|
||||
#include <QJsonArray>
|
||||
#include <QJsonDocument>
|
||||
|
@ -80,7 +79,9 @@ AccountPopupWidget::AccountPopupWidget(std::shared_ptr<Channel> channel)
|
|||
AccountManager::getInstance().getTwitchUser().getUserId() +
|
||||
"/follows/channels/" + this->userID);
|
||||
|
||||
util::twitch::put(requestUrl,[](QJsonObject obj){});
|
||||
util::twitch::put(requestUrl,[](QJsonObject obj){
|
||||
qDebug() << "follows channel: " << obj;
|
||||
});
|
||||
});
|
||||
|
||||
QObject::connect(this->_ui->ignore, &QPushButton::clicked, this, [=](){
|
||||
|
@ -88,7 +89,9 @@ AccountPopupWidget::AccountPopupWidget(std::shared_ptr<Channel> channel)
|
|||
AccountManager::getInstance().getTwitchUser().getUserId() +
|
||||
"/blocks/" + this->userID);
|
||||
|
||||
util::twitch::put(requestUrl,[](QJsonObject obj){});
|
||||
util::twitch::put(requestUrl,[](QJsonObject obj){
|
||||
qDebug() << "blocks user: " << obj;
|
||||
});
|
||||
});
|
||||
|
||||
QObject::connect(this->_ui->disableHighlights, &QPushButton::clicked, this, [=, &settings](){
|
||||
|
@ -118,7 +121,7 @@ AccountPopupWidget::AccountPopupWidget(std::shared_ptr<Channel> channel)
|
|||
hide(); //
|
||||
});
|
||||
|
||||
util::twitch::getUserID(AccountManager::getInstance().getTwitchUser().getNickName(),
|
||||
util::twitch::getUserID(AccountManager::getInstance().getTwitchUser().getNickName(), this,
|
||||
[=](const QString &id){
|
||||
AccountManager::getInstance().getTwitchUser().setUserId(id);
|
||||
});
|
||||
|
@ -137,7 +140,7 @@ void AccountPopupWidget::setChannel(std::shared_ptr<Channel> channel)
|
|||
|
||||
void AccountPopupWidget::getUserId()
|
||||
{
|
||||
util::twitch::getUserID(this->_ui->lblUsername->text(),[=](const QString &id){
|
||||
util::twitch::getUserID(this->_ui->lblUsername->text(), this, [=](const QString &id){
|
||||
userID = id;
|
||||
getUserData();
|
||||
});
|
||||
|
@ -145,7 +148,7 @@ void AccountPopupWidget::getUserId()
|
|||
|
||||
void AccountPopupWidget::getUserData()
|
||||
{
|
||||
util::twitch::get("https://api.twitch.tv/kraken/channels/" + userID,[=](const QJsonObject &obj){
|
||||
util::twitch::get("https://api.twitch.tv/kraken/channels/" + userID, this, [=](const QJsonObject &obj){
|
||||
_ui->lblFollowers->setText(QString::number(obj.value("followers").toInt()));
|
||||
_ui->lblViews->setText(QString::number(obj.value("views").toInt()));
|
||||
_ui->lblAccountAge->setText(obj.value("created_at").toString().section("T", 0, 0));
|
||||
|
|
|
@ -239,9 +239,8 @@ QString ChannelView::getSelectedText()
|
|||
|
||||
if (first) {
|
||||
first = false;
|
||||
bool isSingleWord =
|
||||
isSingleMessage &&
|
||||
this->selection.max.charIndex - charIndex < part.getCharacterLength();
|
||||
bool isSingleWord = isSingleMessage && this->selection.max.charIndex - charIndex <
|
||||
part.getCharacterLength();
|
||||
|
||||
if (isSingleWord) {
|
||||
// return single word
|
||||
|
@ -518,8 +517,7 @@ void ChannelView::updateMessageBuffer(messages::MessageRef *messageRef, QPixmap
|
|||
// this->selectionMax.messageIndex >= messageIndex) {
|
||||
// painter.fillRect(buffer->rect(), QColor(24, 55, 25));
|
||||
//} else {
|
||||
painter.fillRect(buffer->rect(),
|
||||
(messageRef->getMessage()->getCanHighlightTab())
|
||||
painter.fillRect(buffer->rect(), (messageRef->getMessage()->getCanHighlightTab())
|
||||
? this->colorScheme.ChatBackgroundHighlighted
|
||||
: this->colorScheme.ChatBackground);
|
||||
//}
|
||||
|
|
|
@ -360,14 +360,15 @@ void ChatWidget::doOpenViewerList()
|
|||
}
|
||||
auto loadingLabel = new QLabel("Loading...");
|
||||
|
||||
util::twitch::get(
|
||||
"https://tmi.twitch.tv/group/user/" + channel->name + "/chatters", [=](QJsonObject obj) {
|
||||
util::twitch::get("https://tmi.twitch.tv/group/user/" + channel->name + "/chatters", this,
|
||||
[=](QJsonObject obj) {
|
||||
QJsonObject chattersObj = obj.value("chatters").toObject();
|
||||
|
||||
loadingLabel->hide();
|
||||
for (int i = 0; i < jsonLabels.size(); i++) {
|
||||
chattersList->addItem(labelList.at(i));
|
||||
foreach (const QJsonValue &v, chattersObj.value(jsonLabels.at(i)).toArray())
|
||||
foreach (const QJsonValue &v,
|
||||
chattersObj.value(jsonLabels.at(i)).toArray())
|
||||
chattersList->addItem(v.toString());
|
||||
}
|
||||
});
|
||||
|
|
|
@ -213,7 +213,7 @@ void ChatWidgetHeader::checkLive()
|
|||
|
||||
auto id = QString::fromStdString(channel->roomID);
|
||||
|
||||
util::twitch::get("https://api.twitch.tv/kraken/streams/" + id, [=](QJsonObject obj) {
|
||||
util::twitch::get("https://api.twitch.tv/kraken/streams/" + id, this, [=](QJsonObject obj) {
|
||||
if (obj.value("stream").isNull()) {
|
||||
channel->isLive = false;
|
||||
this->updateChannelText();
|
||||
|
|
|
@ -137,7 +137,7 @@ AdvancedLoginWidget::AdvancedLoginWidget()
|
|||
this->ui.buttonLowerRow.layout.addWidget(&this->ui.buttonLowerRow.fillInUserIDButton);
|
||||
|
||||
connect(&this->ui.buttonLowerRow.fillInUserIDButton, &QPushButton::clicked, [=]() {
|
||||
util::twitch::getUserID(this->ui.usernameInput.text(), [=](const QString &userID) {
|
||||
util::twitch::getUserID(this->ui.usernameInput.text(), this, [=](const QString &userID) {
|
||||
this->ui.userIDInput.setText(userID); //
|
||||
});
|
||||
});
|
||||
|
|
Loading…
Reference in a new issue