Skip to content

Commit

Permalink
Add a local backup of the Twitch Badges API (#4463)
Browse files Browse the repository at this point in the history
  • Loading branch information
pajlada authored Mar 19, 2023
1 parent 1ad93b7 commit 130b23e
Show file tree
Hide file tree
Showing 5 changed files with 67 additions and 46 deletions.
4 changes: 2 additions & 2 deletions .prettierignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# emoji.json should remain minified
resources/emoji.json
# JSON resources should not be prettified
resources/*.json

# Ignore submodule files
lib/*/
Expand Down
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
## Unversioned

- Minor: Added support for FrankerFaceZ animated emotes. (#4434)
- Minor: Added a local backup of the Twitch Badges API in case the request fails. (#4463)
- Bugfix: Fixed an issue where animated emotes would render on top of zero-width emotes. (#4314)
- Bugfix: Fixed an issue where it was difficult to hover a zero-width emote. (#4314)
- Bugfix: Fixed an issue where context-menu items for zero-width emotes displayed the wrong provider. (#4460)
Expand Down
1 change: 1 addition & 0 deletions resources/twitch-badges.json

Large diffs are not rendered by default.

102 changes: 58 additions & 44 deletions src/providers/twitch/TwitchBadges.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,11 @@
#include "util/DisplayBadge.hpp"

#include <QBuffer>
#include <QFile>
#include <QIcon>
#include <QImageReader>
#include <QJsonArray>
#include <QJsonObject>
#include <QJsonDocument>
#include <QJsonValue>
#include <QThread>
#include <QUrlQuery>
Expand All @@ -36,58 +37,71 @@ void TwitchBadges::loadTwitchBadges()

NetworkRequest(url)
.onSuccess([this](auto result) -> Outcome {
{
auto root = result.parseJson();
auto badgeSets = this->badgeSets_.access();

auto jsonSets = root.value("badge_sets").toObject();
for (auto sIt = jsonSets.begin(); sIt != jsonSets.end(); ++sIt)
{
auto key = sIt.key();
auto versions =
sIt.value().toObject().value("versions").toObject();

for (auto vIt = versions.begin(); vIt != versions.end();
++vIt)
{
auto versionObj = vIt.value().toObject();

auto emote = Emote{
{""},
ImageSet{
Image::fromUrl({versionObj.value("image_url_1x")
.toString()},
1),
Image::fromUrl({versionObj.value("image_url_2x")
.toString()},
.5),
Image::fromUrl({versionObj.value("image_url_4x")
.toString()},
.25),
},
Tooltip{versionObj.value("title").toString()},
Url{versionObj.value("click_url").toString()}};
// "title"
// "clickAction"

(*badgeSets)[key][vIt.key()] =
std::make_shared<Emote>(emote);
}
}
}
auto root = result.parseJson();

this->parseTwitchBadges(root);

this->loaded();
return Success;
})
.onError([this](auto res) {
qCDebug(chatterinoTwitch)
<< "Error loading Twitch Badges:" << res.status();
// Despite erroring out, we still want to reach the same point
// Loaded should still be set to true to not build up an endless queue, and the quuee should still be flushed.
qCWarning(chatterinoTwitch)
<< "Error loading Twitch Badges from the badges API:"
<< res.status() << " - falling back to backup";
QFile file(":/twitch-badges.json");
if (!file.open(QFile::ReadOnly))
{
// Despite erroring out, we still want to reach the same point
// Loaded should still be set to true to not build up an endless queue, and the quuee should still be flushed.
qCWarning(chatterinoTwitch)
<< "Error loading Twitch Badges from the local backup file";
this->loaded();
return;
}
auto bytes = file.readAll();
auto doc = QJsonDocument::fromJson(bytes);

this->parseTwitchBadges(doc.object());

this->loaded();
})
.execute();
}

void TwitchBadges::parseTwitchBadges(QJsonObject root)
{
auto badgeSets = this->badgeSets_.access();

auto jsonSets = root.value("badge_sets").toObject();
for (auto sIt = jsonSets.begin(); sIt != jsonSets.end(); ++sIt)
{
auto key = sIt.key();
auto versions = sIt.value().toObject().value("versions").toObject();

for (auto vIt = versions.begin(); vIt != versions.end(); ++vIt)
{
auto versionObj = vIt.value().toObject();

auto emote = Emote{
{""},
ImageSet{
Image::fromUrl(
{versionObj.value("image_url_1x").toString()}, 1),
Image::fromUrl(
{versionObj.value("image_url_2x").toString()}, .5),
Image::fromUrl(
{versionObj.value("image_url_4x").toString()}, .25),
},
Tooltip{versionObj.value("title").toString()},
Url{versionObj.value("click_url").toString()}};
// "title"
// "clickAction"

(*badgeSets)[key][vIt.key()] = std::make_shared<Emote>(emote);
}
}
}

void TwitchBadges::loaded()
{
std::unique_lock loadedLock(this->loadedMutex_);
Expand Down
5 changes: 5 additions & 0 deletions src/providers/twitch/TwitchBadges.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
#include <boost/optional.hpp>
#include <pajlada/signals/signal.hpp>
#include <QIcon>
#include <QJsonObject>
#include <QMap>
#include <QString>

Expand Down Expand Up @@ -49,6 +50,10 @@ class TwitchBadges

TwitchBadges();
void loadTwitchBadges();
/**
* @brief Accepts a JSON blob from https://badges.twitch.tv/v1/badges/global/display and updates our badges with it
**/
void parseTwitchBadges(QJsonObject root);
void loaded();
void loadEmoteImage(const QString &name, ImagePtr image,
BadgeIconCallback &&callback);
Expand Down

0 comments on commit 130b23e

Please sign in to comment.