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/rippleeffectlabel.cpp \
|
||||||
src/widgets/rippleeffectbutton.cpp \
|
src/widgets/rippleeffectbutton.cpp \
|
||||||
src/messages/messagecolor.cpp \
|
src/messages/messagecolor.cpp \
|
||||||
src/messages/imageloadermanager.cpp
|
src/util/networkmanager.cpp
|
||||||
|
|
||||||
HEADERS += \
|
HEADERS += \
|
||||||
src/asyncexec.hpp \
|
src/asyncexec.hpp \
|
||||||
|
@ -164,8 +164,8 @@ HEADERS += \
|
||||||
src/messages/messagecolor.hpp \
|
src/messages/messagecolor.hpp \
|
||||||
src/util/nativeeventhelper.hpp \
|
src/util/nativeeventhelper.hpp \
|
||||||
src/debug/log.hpp \
|
src/debug/log.hpp \
|
||||||
src/messages/imageloadermanager.hpp \
|
src/util/benchmark.hpp \
|
||||||
src/util/benchmark.hpp
|
src/util/networkmanager.hpp
|
||||||
|
|
||||||
PRECOMPILED_HEADER =
|
PRECOMPILED_HEADER =
|
||||||
|
|
||||||
|
|
|
@ -48,44 +48,48 @@ void EmoteManager::reloadBTTVChannelEmotes(const QString &channelName, std::weak
|
||||||
printf("[EmoteManager] Reload BTTV Channel Emotes for channel %s\n", qPrintable(channelName));
|
printf("[EmoteManager] Reload BTTV Channel Emotes for channel %s\n", qPrintable(channelName));
|
||||||
|
|
||||||
QString url("https://api.betterttv.net/2/channels/" + channelName);
|
QString url("https://api.betterttv.net/2/channels/" + channelName);
|
||||||
util::urlFetchJSON(url, [this, channelName, _map](QJsonObject &rootNode) {
|
util::urlFetchJSONTimeout(
|
||||||
auto map = _map.lock();
|
url, QThread::currentThread(),
|
||||||
|
[this, channelName, _map](QJsonObject &rootNode) {
|
||||||
|
auto map = _map.lock();
|
||||||
|
|
||||||
if (_map.expired()) {
|
if (_map.expired()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
map->clear();
|
map->clear();
|
||||||
|
|
||||||
auto emotesNode = rootNode.value("emotes").toArray();
|
auto emotesNode = rootNode.value("emotes").toArray();
|
||||||
|
|
||||||
QString linkTemplate = "https:" + rootNode.value("urlTemplate").toString();
|
QString linkTemplate = "https:" + rootNode.value("urlTemplate").toString();
|
||||||
|
|
||||||
std::vector<std::string> codes;
|
std::vector<std::string> codes;
|
||||||
for (const QJsonValue &emoteNode : emotesNode) {
|
for (const QJsonValue &emoteNode : emotesNode) {
|
||||||
QJsonObject emoteObject = emoteNode.toObject();
|
QJsonObject emoteObject = emoteNode.toObject();
|
||||||
|
|
||||||
QString id = emoteObject.value("id").toString();
|
QString id = emoteObject.value("id").toString();
|
||||||
QString code = emoteObject.value("code").toString();
|
QString code = emoteObject.value("code").toString();
|
||||||
// emoteObject.value("imageType").toString();
|
// emoteObject.value("imageType").toString();
|
||||||
|
|
||||||
QString link = linkTemplate;
|
QString link = linkTemplate;
|
||||||
link.detach();
|
link.detach();
|
||||||
|
|
||||||
link = link.replace("{{id}}", id).replace("{{image}}", "1x");
|
link = link.replace("{{id}}", id).replace("{{image}}", "1x");
|
||||||
|
|
||||||
auto emote = this->getBTTVChannelEmoteFromCaches().getOrAdd(id, [this, &code, &link] {
|
auto emote =
|
||||||
return EmoteData(new LazyLoadedImage(*this, this->windowManager, link, 1, code,
|
this->getBTTVChannelEmoteFromCaches().getOrAdd(id, [this, &code, &link] {
|
||||||
code + "\nChannel BTTV Emote"));
|
return EmoteData(new LazyLoadedImage(*this, this->windowManager, link, 1,
|
||||||
});
|
code, code + "\nChannel BTTV Emote"));
|
||||||
|
});
|
||||||
|
|
||||||
this->bttvChannelEmotes.insert(code, emote);
|
this->bttvChannelEmotes.insert(code, emote);
|
||||||
map->insert(code, emote);
|
map->insert(code, emote);
|
||||||
codes.push_back(code.toStdString());
|
codes.push_back(code.toStdString());
|
||||||
}
|
}
|
||||||
|
|
||||||
this->bttvChannelEmoteCodes[channelName.toStdString()] = codes;
|
this->bttvChannelEmoteCodes[channelName.toStdString()] = codes;
|
||||||
});
|
},
|
||||||
|
1500);
|
||||||
}
|
}
|
||||||
|
|
||||||
void EmoteManager::reloadFFZChannelEmotes(const QString &channelName, std::weak_ptr<EmoteMap> _map)
|
void EmoteManager::reloadFFZChannelEmotes(const QString &channelName, std::weak_ptr<EmoteMap> _map)
|
||||||
|
@ -94,45 +98,48 @@ void EmoteManager::reloadFFZChannelEmotes(const QString &channelName, std::weak_
|
||||||
|
|
||||||
QString url("http://api.frankerfacez.com/v1/room/" + channelName);
|
QString url("http://api.frankerfacez.com/v1/room/" + channelName);
|
||||||
|
|
||||||
util::urlFetchJSON(url, [this, channelName, _map](QJsonObject &rootNode) {
|
util::urlFetchJSONTimeout(
|
||||||
auto map = _map.lock();
|
url, QThread::currentThread(),
|
||||||
|
[this, channelName, _map](QJsonObject &rootNode) {
|
||||||
|
auto map = _map.lock();
|
||||||
|
|
||||||
if (_map.expired()) {
|
if (_map.expired()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
map->clear();
|
map->clear();
|
||||||
|
|
||||||
auto setsNode = rootNode.value("sets").toObject();
|
auto setsNode = rootNode.value("sets").toObject();
|
||||||
|
|
||||||
std::vector<std::string> codes;
|
std::vector<std::string> codes;
|
||||||
for (const QJsonValue &setNode : setsNode) {
|
for (const QJsonValue &setNode : setsNode) {
|
||||||
auto emotesNode = setNode.toObject().value("emoticons").toArray();
|
auto emotesNode = setNode.toObject().value("emoticons").toArray();
|
||||||
|
|
||||||
for (const QJsonValue &emoteNode : emotesNode) {
|
for (const QJsonValue &emoteNode : emotesNode) {
|
||||||
QJsonObject emoteObject = emoteNode.toObject();
|
QJsonObject emoteObject = emoteNode.toObject();
|
||||||
|
|
||||||
// margins
|
// margins
|
||||||
int id = emoteObject.value("id").toInt();
|
int id = emoteObject.value("id").toInt();
|
||||||
QString code = emoteObject.value("name").toString();
|
QString code = emoteObject.value("name").toString();
|
||||||
|
|
||||||
QJsonObject urls = emoteObject.value("urls").toObject();
|
QJsonObject urls = emoteObject.value("urls").toObject();
|
||||||
QString url1 = "http:" + urls.value("1").toString();
|
QString url1 = "http:" + urls.value("1").toString();
|
||||||
|
|
||||||
auto emote =
|
auto emote = this->getFFZChannelEmoteFromCaches().getOrAdd(id, [this, &code,
|
||||||
this->getFFZChannelEmoteFromCaches().getOrAdd(id, [this, &code, &url1] {
|
&url1] {
|
||||||
return EmoteData(new LazyLoadedImage(*this, this->windowManager, url1, 1,
|
return EmoteData(new LazyLoadedImage(*this, this->windowManager, url1, 1,
|
||||||
code, code + "\nGlobal FFZ Emote"));
|
code, code + "\nGlobal FFZ Emote"));
|
||||||
});
|
});
|
||||||
|
|
||||||
this->ffzChannelEmotes.insert(code, emote);
|
this->ffzChannelEmotes.insert(code, emote);
|
||||||
map->insert(code, emote);
|
map->insert(code, emote);
|
||||||
codes.push_back(code.toStdString());
|
codes.push_back(code.toStdString());
|
||||||
}
|
}
|
||||||
|
|
||||||
this->ffzChannelEmoteCodes[channelName.toStdString()] = codes;
|
this->ffzChannelEmoteCodes[channelName.toStdString()] = codes;
|
||||||
}
|
}
|
||||||
});
|
},
|
||||||
|
1500);
|
||||||
}
|
}
|
||||||
|
|
||||||
ConcurrentMap<QString, twitch::EmoteValue *> &EmoteManager::getTwitchEmotes()
|
ConcurrentMap<QString, twitch::EmoteValue *> &EmoteManager::getTwitchEmotes()
|
||||||
|
@ -377,11 +384,10 @@ void EmoteManager::refreshTwitchEmotes(const std::string &roomID)
|
||||||
qDebug() << url;
|
qDebug() << url;
|
||||||
|
|
||||||
util::urlFetchJSONTimeout(
|
util::urlFetchJSONTimeout(
|
||||||
url,
|
url, QThread::currentThread(),
|
||||||
[=, &emoteData](QJsonObject &root) {
|
[=, &emoteData](QJsonObject &root) {
|
||||||
emoteData.emoteSets.clear();
|
emoteData.emoteSets.clear();
|
||||||
emoteData.emoteCodes.clear();
|
emoteData.emoteCodes.clear();
|
||||||
|
|
||||||
auto emoticonSets = root.value("emoticon_sets").toObject();
|
auto emoticonSets = root.value("emoticon_sets").toObject();
|
||||||
for (QJsonObject::iterator it = emoticonSets.begin(); it != emoticonSets.end(); ++it) {
|
for (QJsonObject::iterator it = emoticonSets.begin(); it != emoticonSets.end(); ++it) {
|
||||||
std::string emoteSetString = it.key().toStdString();
|
std::string emoteSetString = it.key().toStdString();
|
||||||
|
|
|
@ -154,7 +154,6 @@ void IrcManager::beginConnecting()
|
||||||
this->writeConnection->sendRaw("JOIN #" + channel->name);
|
this->writeConnection->sendRaw("JOIN #" + channel->name);
|
||||||
this->readConnection->sendRaw("JOIN #" + channel->name);
|
this->readConnection->sendRaw("JOIN #" + channel->name);
|
||||||
}
|
}
|
||||||
|
|
||||||
this->writeConnection->open();
|
this->writeConnection->open();
|
||||||
this->readConnection->open();
|
this->readConnection->open();
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -7,6 +7,8 @@
|
||||||
#include <QStandardPaths>
|
#include <QStandardPaths>
|
||||||
#include <pajlada/settings/settingmanager.hpp>
|
#include <pajlada/settings/settingmanager.hpp>
|
||||||
|
|
||||||
|
#include "util/networkmanager.hpp"
|
||||||
|
|
||||||
#ifdef USEWINSDK
|
#ifdef USEWINSDK
|
||||||
#include "windows.h"
|
#include "windows.h"
|
||||||
#endif
|
#endif
|
||||||
|
@ -88,6 +90,9 @@ int main(int argc, char *argv[])
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Initialize NetworkManager
|
||||||
|
chatterino::util::NetworkManager::init();
|
||||||
|
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
|
|
||||||
{
|
{
|
||||||
|
@ -103,5 +108,8 @@ int main(int argc, char *argv[])
|
||||||
// Save settings
|
// Save settings
|
||||||
pajlada::Settings::SettingManager::save();
|
pajlada::Settings::SettingManager::save();
|
||||||
|
|
||||||
|
// Deinitialize NetworkManager (stop thread and wait for finish, should be instant)
|
||||||
|
chatterino::util::NetworkManager::deinit();
|
||||||
|
|
||||||
return ret;
|
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 "asyncexec.hpp"
|
||||||
#include "emotemanager.hpp"
|
#include "emotemanager.hpp"
|
||||||
#include "ircmanager.hpp"
|
#include "ircmanager.hpp"
|
||||||
#include "messages/imageloadermanager.hpp"
|
#include "util/networkmanager.hpp"
|
||||||
#include "util/urlfetch.hpp"
|
#include "util/urlfetch.hpp"
|
||||||
#include "windowmanager.hpp"
|
#include "windowmanager.hpp"
|
||||||
|
|
||||||
|
@ -12,9 +12,9 @@
|
||||||
#include <QNetworkReply>
|
#include <QNetworkReply>
|
||||||
#include <QNetworkRequest>
|
#include <QNetworkRequest>
|
||||||
#include <QTimer>
|
#include <QTimer>
|
||||||
#include <thread>
|
|
||||||
|
|
||||||
#include <functional>
|
#include <functional>
|
||||||
|
#include <thread>
|
||||||
|
|
||||||
namespace chatterino {
|
namespace chatterino {
|
||||||
namespace messages {
|
namespace messages {
|
||||||
|
@ -52,8 +52,43 @@ LazyLoadedImage::LazyLoadedImage(EmoteManager &_emoteManager, WindowManager &_wi
|
||||||
|
|
||||||
void LazyLoadedImage::loadImage()
|
void LazyLoadedImage::loadImage()
|
||||||
{
|
{
|
||||||
static ImageLoaderManager imageLoader;
|
util::NetworkRequest req(this->getUrl());
|
||||||
imageLoader.queue(this);
|
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->emoteManager.getGifUpdateSignal().connect([=]() {
|
||||||
this->gifUpdateTimout();
|
this->gifUpdateTimout();
|
||||||
|
|
|
@ -64,9 +64,6 @@ private:
|
||||||
|
|
||||||
void loadImage();
|
void loadImage();
|
||||||
void gifUpdateTimout();
|
void gifUpdateTimout();
|
||||||
|
|
||||||
friend class ImageLoaderWorker;
|
|
||||||
friend class ImageLoaderManager;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace messages
|
} // namespace messages
|
||||||
|
|
|
@ -40,9 +40,9 @@ Resources::Resources(EmoteManager &em, WindowManager &wm)
|
||||||
{
|
{
|
||||||
QString badgesUrl("https://badges.twitch.tv/v1/badges/global/display?language=en");
|
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();
|
QJsonObject sets = root.value("badge_sets").toObject();
|
||||||
|
qDebug() << "badges fetched";
|
||||||
for (QJsonObject::iterator it = sets.begin(); it != sets.end(); ++it) {
|
for (QJsonObject::iterator it = sets.begin(); it != sets.end(); ++it) {
|
||||||
QJsonObject versions = it.value().toObject().value("versions").toObject();
|
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) +
|
QString url = "https://badges.twitch.tv/v1/badges/channels/" + QString::fromStdString(roomID) +
|
||||||
"/display?language=en";
|
"/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();
|
QJsonObject sets = root.value("badge_sets").toObject();
|
||||||
|
|
||||||
Resources::Channel &ch = this->channels[roomID];
|
Resources::Channel &ch = this->channels[roomID];
|
||||||
|
@ -118,9 +118,9 @@ void Resources::loadChatterinoBadges()
|
||||||
|
|
||||||
QString url = "https://fourtf.com/chatterino/badges.json";
|
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();
|
QJsonArray badgeVariants = root.value("badges").toArray();
|
||||||
|
qDebug() << "chatbadges fetched";
|
||||||
for (QJsonArray::iterator it = badgeVariants.begin(); it != badgeVariants.end(); ++it) {
|
for (QJsonArray::iterator it = badgeVariants.begin(); it != badgeVariants.end(); ++it) {
|
||||||
QJsonObject badgeVariant = it->toObject();
|
QJsonObject badgeVariant = it->toObject();
|
||||||
const std::string badgeVariantTooltip =
|
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
|
#pragma once
|
||||||
|
|
||||||
#include "credentials.hpp"
|
|
||||||
#include "accountmanager.hpp"
|
#include "accountmanager.hpp"
|
||||||
|
#include "credentials.hpp"
|
||||||
|
#include "util/networkmanager.hpp"
|
||||||
|
|
||||||
#include <QEventLoop>
|
#include <QEventLoop>
|
||||||
#include <QJsonArray>
|
#include <QJsonArray>
|
||||||
|
@ -14,68 +15,128 @@
|
||||||
#include <QString>
|
#include <QString>
|
||||||
#include <QTimer>
|
#include <QTimer>
|
||||||
|
|
||||||
|
#include <QDebug>
|
||||||
|
|
||||||
#include <functional>
|
#include <functional>
|
||||||
|
|
||||||
namespace chatterino {
|
namespace chatterino {
|
||||||
namespace util {
|
namespace util {
|
||||||
|
|
||||||
namespace twitch {
|
static QJsonObject parseJSONFromReply(QNetworkReply *reply)
|
||||||
|
|
||||||
static void get(QString url, std::function<void(QJsonObject &)> successCallback)
|
|
||||||
{
|
{
|
||||||
auto manager = new QNetworkAccessManager();
|
if (reply->error() != QNetworkReply::NetworkError::NoError) {
|
||||||
|
return QJsonObject();
|
||||||
|
}
|
||||||
|
|
||||||
QUrl requestUrl(url);
|
QByteArray data = reply->readAll();
|
||||||
QNetworkRequest request(requestUrl);
|
QJsonDocument jsonDoc(QJsonDocument::fromJson(data));
|
||||||
|
|
||||||
request.setRawHeader("Client-ID", getDefaultClientID());
|
if (jsonDoc.isNull()) {
|
||||||
request.setRawHeader("Accept", "application/vnd.twitchtv.v5+json");
|
return QJsonObject();
|
||||||
|
}
|
||||||
|
|
||||||
QNetworkReply *reply = manager->get(request);
|
return jsonDoc.object();
|
||||||
|
}
|
||||||
|
|
||||||
QObject::connect(reply, &QNetworkReply::finished, [=] {
|
static void urlFetchJSON(const QString &url, const QObject *caller,
|
||||||
if (reply->error() == QNetworkReply::NetworkError::NoError) {
|
std::function<void(QJsonObject &)> successCallback)
|
||||||
QByteArray data = reply->readAll();
|
{
|
||||||
QJsonDocument jsonDoc(QJsonDocument::fromJson(data));
|
util::NetworkRequest req(url);
|
||||||
|
req.setCaller(caller);
|
||||||
if (!jsonDoc.isNull()) {
|
req.get([=](QNetworkReply *reply) {
|
||||||
QJsonObject rootNode = jsonDoc.object();
|
auto node = parseJSONFromReply(reply);
|
||||||
|
successCallback(node);
|
||||||
successCallback(rootNode);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
reply->deleteLater();
|
|
||||||
manager->deleteLater();
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
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;
|
||||||
if (!root.value("users").isArray()) {
|
timer->setSingleShot(true);
|
||||||
qDebug() << "API Error while getting user id, users is not an array";
|
|
||||||
return;
|
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto users = root.value("users").toArray();
|
reply->deleteLater();
|
||||||
if (users.size() != 1) {
|
loop->quit();
|
||||||
qDebug() << "API Error while getting user id, users array size is not 1";
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (!users[0].isObject()) {
|
|
||||||
qDebug() << "API Error while getting user id, first user is not an object";
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
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";
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
successCallback(id.toString());
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto users = root.value("users").toArray();
|
||||||
|
if (users.size() != 1) {
|
||||||
|
qDebug() << "API Error while getting user id, users array size is not 1";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!users[0].isObject()) {
|
||||||
|
qDebug() << "API Error while getting user id, first user is not an object";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
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";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
successCallback(id.toString());
|
||||||
|
});
|
||||||
}
|
}
|
||||||
static void put(QUrl url, std::function<void(QJsonObject)> successCallback)
|
static void put(QUrl url, std::function<void(QJsonObject)> successCallback)
|
||||||
{
|
{
|
||||||
|
@ -84,157 +145,24 @@ static void put(QUrl url, std::function<void(QJsonObject)> successCallback)
|
||||||
|
|
||||||
request.setRawHeader("Client-ID", getDefaultClientID());
|
request.setRawHeader("Client-ID", getDefaultClientID());
|
||||||
request.setRawHeader("Accept", "application/vnd.twitchtv.v5+json");
|
request.setRawHeader("Accept", "application/vnd.twitchtv.v5+json");
|
||||||
request.setRawHeader("Authorization", "OAuth " +
|
request.setRawHeader(
|
||||||
AccountManager::getInstance().getTwitchUser().getOAuthToken().toUtf8());
|
"Authorization",
|
||||||
QNetworkReply *reply = manager->put(request,"");
|
"OAuth " + AccountManager::getInstance().getTwitchUser().getOAuthToken().toUtf8());
|
||||||
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);
|
NetworkManager::urlPut(std::move(request), [=](QNetworkReply *reply) {
|
||||||
}
|
if (reply->error() == QNetworkReply::NetworkError::NoError) {
|
||||||
}
|
QByteArray data = reply->readAll();
|
||||||
reply->deleteLater();
|
QJsonDocument jsonDoc(QJsonDocument::fromJson(data));
|
||||||
manager->deleteLater();
|
if (!jsonDoc.isNull()) {
|
||||||
|
QJsonObject rootNode = jsonDoc.object();
|
||||||
|
|
||||||
|
successCallback(rootNode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
reply->deleteLater();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace twitch
|
} // 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 util
|
||||||
} // namespace chatterino
|
} // namespace chatterino
|
||||||
|
|
|
@ -7,7 +7,6 @@
|
||||||
#include "ui_accountpopupform.h"
|
#include "ui_accountpopupform.h"
|
||||||
|
|
||||||
#include <QClipboard>
|
#include <QClipboard>
|
||||||
#include <QDebug>
|
|
||||||
#include <QDesktopServices>
|
#include <QDesktopServices>
|
||||||
#include <QJsonArray>
|
#include <QJsonArray>
|
||||||
#include <QJsonDocument>
|
#include <QJsonDocument>
|
||||||
|
@ -80,7 +79,9 @@ AccountPopupWidget::AccountPopupWidget(std::shared_ptr<Channel> channel)
|
||||||
AccountManager::getInstance().getTwitchUser().getUserId() +
|
AccountManager::getInstance().getTwitchUser().getUserId() +
|
||||||
"/follows/channels/" + this->userID);
|
"/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, [=](){
|
QObject::connect(this->_ui->ignore, &QPushButton::clicked, this, [=](){
|
||||||
|
@ -88,7 +89,9 @@ AccountPopupWidget::AccountPopupWidget(std::shared_ptr<Channel> channel)
|
||||||
AccountManager::getInstance().getTwitchUser().getUserId() +
|
AccountManager::getInstance().getTwitchUser().getUserId() +
|
||||||
"/blocks/" + this->userID);
|
"/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](){
|
QObject::connect(this->_ui->disableHighlights, &QPushButton::clicked, this, [=, &settings](){
|
||||||
|
@ -118,7 +121,7 @@ AccountPopupWidget::AccountPopupWidget(std::shared_ptr<Channel> channel)
|
||||||
hide(); //
|
hide(); //
|
||||||
});
|
});
|
||||||
|
|
||||||
util::twitch::getUserID(AccountManager::getInstance().getTwitchUser().getNickName(),
|
util::twitch::getUserID(AccountManager::getInstance().getTwitchUser().getNickName(), this,
|
||||||
[=](const QString &id){
|
[=](const QString &id){
|
||||||
AccountManager::getInstance().getTwitchUser().setUserId(id);
|
AccountManager::getInstance().getTwitchUser().setUserId(id);
|
||||||
});
|
});
|
||||||
|
@ -137,7 +140,7 @@ void AccountPopupWidget::setChannel(std::shared_ptr<Channel> channel)
|
||||||
|
|
||||||
void AccountPopupWidget::getUserId()
|
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;
|
userID = id;
|
||||||
getUserData();
|
getUserData();
|
||||||
});
|
});
|
||||||
|
@ -145,7 +148,7 @@ void AccountPopupWidget::getUserId()
|
||||||
|
|
||||||
void AccountPopupWidget::getUserData()
|
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->lblFollowers->setText(QString::number(obj.value("followers").toInt()));
|
||||||
_ui->lblViews->setText(QString::number(obj.value("views").toInt()));
|
_ui->lblViews->setText(QString::number(obj.value("views").toInt()));
|
||||||
_ui->lblAccountAge->setText(obj.value("created_at").toString().section("T", 0, 0));
|
_ui->lblAccountAge->setText(obj.value("created_at").toString().section("T", 0, 0));
|
||||||
|
|
|
@ -239,9 +239,8 @@ QString ChannelView::getSelectedText()
|
||||||
|
|
||||||
if (first) {
|
if (first) {
|
||||||
first = false;
|
first = false;
|
||||||
bool isSingleWord =
|
bool isSingleWord = isSingleMessage && this->selection.max.charIndex - charIndex <
|
||||||
isSingleMessage &&
|
part.getCharacterLength();
|
||||||
this->selection.max.charIndex - charIndex < part.getCharacterLength();
|
|
||||||
|
|
||||||
if (isSingleWord) {
|
if (isSingleWord) {
|
||||||
// return single word
|
// return single word
|
||||||
|
@ -518,10 +517,9 @@ void ChannelView::updateMessageBuffer(messages::MessageRef *messageRef, QPixmap
|
||||||
// this->selectionMax.messageIndex >= messageIndex) {
|
// this->selectionMax.messageIndex >= messageIndex) {
|
||||||
// painter.fillRect(buffer->rect(), QColor(24, 55, 25));
|
// painter.fillRect(buffer->rect(), QColor(24, 55, 25));
|
||||||
//} else {
|
//} else {
|
||||||
painter.fillRect(buffer->rect(),
|
painter.fillRect(buffer->rect(), (messageRef->getMessage()->getCanHighlightTab())
|
||||||
(messageRef->getMessage()->getCanHighlightTab())
|
? this->colorScheme.ChatBackgroundHighlighted
|
||||||
? this->colorScheme.ChatBackgroundHighlighted
|
: this->colorScheme.ChatBackground);
|
||||||
: this->colorScheme.ChatBackground);
|
|
||||||
//}
|
//}
|
||||||
|
|
||||||
// draw selection
|
// draw selection
|
||||||
|
|
|
@ -360,17 +360,18 @@ void ChatWidget::doOpenViewerList()
|
||||||
}
|
}
|
||||||
auto loadingLabel = new QLabel("Loading...");
|
auto loadingLabel = new QLabel("Loading...");
|
||||||
|
|
||||||
util::twitch::get(
|
util::twitch::get("https://tmi.twitch.tv/group/user/" + channel->name + "/chatters", this,
|
||||||
"https://tmi.twitch.tv/group/user/" + channel->name + "/chatters", [=](QJsonObject obj) {
|
[=](QJsonObject obj) {
|
||||||
QJsonObject chattersObj = obj.value("chatters").toObject();
|
QJsonObject chattersObj = obj.value("chatters").toObject();
|
||||||
|
|
||||||
loadingLabel->hide();
|
loadingLabel->hide();
|
||||||
for (int i = 0; i < jsonLabels.size(); i++) {
|
for (int i = 0; i < jsonLabels.size(); i++) {
|
||||||
chattersList->addItem(labelList.at(i));
|
chattersList->addItem(labelList.at(i));
|
||||||
foreach (const QJsonValue &v, chattersObj.value(jsonLabels.at(i)).toArray())
|
foreach (const QJsonValue &v,
|
||||||
chattersList->addItem(v.toString());
|
chattersObj.value(jsonLabels.at(i)).toArray())
|
||||||
}
|
chattersList->addItem(v.toString());
|
||||||
});
|
}
|
||||||
|
});
|
||||||
|
|
||||||
searchBar->setPlaceholderText("Search User...");
|
searchBar->setPlaceholderText("Search User...");
|
||||||
QObject::connect(searchBar, &QLineEdit::textEdited, this, [=]() {
|
QObject::connect(searchBar, &QLineEdit::textEdited, this, [=]() {
|
||||||
|
|
|
@ -213,7 +213,7 @@ void ChatWidgetHeader::checkLive()
|
||||||
|
|
||||||
auto id = QString::fromStdString(channel->roomID);
|
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()) {
|
if (obj.value("stream").isNull()) {
|
||||||
channel->isLive = false;
|
channel->isLive = false;
|
||||||
this->updateChannelText();
|
this->updateChannelText();
|
||||||
|
|
|
@ -137,7 +137,7 @@ AdvancedLoginWidget::AdvancedLoginWidget()
|
||||||
this->ui.buttonLowerRow.layout.addWidget(&this->ui.buttonLowerRow.fillInUserIDButton);
|
this->ui.buttonLowerRow.layout.addWidget(&this->ui.buttonLowerRow.fillInUserIDButton);
|
||||||
|
|
||||||
connect(&this->ui.buttonLowerRow.fillInUserIDButton, &QPushButton::clicked, [=]() {
|
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); //
|
this->ui.userIDInput.setText(userID); //
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
Loading…
Reference in a new issue