Skip to content

Commit

Permalink
Finish
Browse files Browse the repository at this point in the history
  • Loading branch information
pajlada committed Jul 2, 2023
1 parent 69f6a21 commit 204d18e
Show file tree
Hide file tree
Showing 15 changed files with 359 additions and 359 deletions.
5 changes: 5 additions & 0 deletions mocks/include/mocks/EmptyApplication.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,11 @@ class EmptyApplication : public IApplication
{
return nullptr;
}

ITwitchLiveController *getTwitchLiveController() override
{
return nullptr;
}
};

} // namespace chatterino::mock
6 changes: 6 additions & 0 deletions mocks/include/mocks/Helix.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,12 @@ class Helix : public IHelix
std::function<void()> finallyCallback),
(override));

MOCK_METHOD(void, fetchChannels,
(QStringList userIDs,
ResultCallback<std::vector<HelixChannel>> successCallback,
HelixFailureCallback failureCallback),
(override));

MOCK_METHOD(void, getChannel,
(QString broadcasterId,
ResultCallback<HelixChannel> successCallback,
Expand Down
2 changes: 2 additions & 0 deletions src/common/QLogging.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,8 @@ Q_LOGGING_CATEGORY(chatterinoStreamlink, "chatterino.streamlink", logThreshold);
Q_LOGGING_CATEGORY(chatterinoTheme, "chatterino.theme", logThreshold);
Q_LOGGING_CATEGORY(chatterinoTokenizer, "chatterino.tokenizer", logThreshold);
Q_LOGGING_CATEGORY(chatterinoTwitch, "chatterino.twitch", logThreshold);
Q_LOGGING_CATEGORY(chatterinoTwitchLiveController,
"chatterino.twitch.livecontroller", logThreshold);
Q_LOGGING_CATEGORY(chatterinoUpdate, "chatterino.update", logThreshold);
Q_LOGGING_CATEGORY(chatterinoWebsocket, "chatterino.websocket", logThreshold);
Q_LOGGING_CATEGORY(chatterinoWidget, "chatterino.widget", logThreshold);
Expand Down
1 change: 1 addition & 0 deletions src/common/QLogging.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ Q_DECLARE_LOGGING_CATEGORY(chatterinoStreamlink);
Q_DECLARE_LOGGING_CATEGORY(chatterinoTheme);
Q_DECLARE_LOGGING_CATEGORY(chatterinoTokenizer);
Q_DECLARE_LOGGING_CATEGORY(chatterinoTwitch);
Q_DECLARE_LOGGING_CATEGORY(chatterinoTwitchLiveController);
Q_DECLARE_LOGGING_CATEGORY(chatterinoUpdate);
Q_DECLARE_LOGGING_CATEGORY(chatterinoWebsocket);
Q_DECLARE_LOGGING_CATEGORY(chatterinoWidget);
Expand Down
142 changes: 77 additions & 65 deletions src/controllers/twitch/LiveController.cpp
Original file line number Diff line number Diff line change
@@ -1,21 +1,27 @@
#include "controllers/twitch/LiveController.hpp"

#include "common/QLogging.hpp"
#include "providers/twitch/api/Helix.hpp"
#include "providers/twitch/TwitchChannel.hpp"
#include "singletons/Paths.hpp"
#include "util/CombinePath.hpp"
#include "util/Helpers.hpp"

#include <QDebug>

namespace {

// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
const auto &LOG = chatterinoTwitchLiveController;

} // namespace

namespace chatterino {

TwitchLiveController::TwitchLiveController()
{
QObject::connect(&this->refreshTimer, &QTimer::timeout, [this] {
this->request();
});
this->refreshTimer.start(60 * 1000);
this->refreshTimer.start(TwitchLiveController::REFRESH_INTERVAL);

QObject::connect(&this->immediateRequestTimer, &QTimer::timeout, [this] {
QStringList channelIDs;
Expand All @@ -30,11 +36,15 @@ TwitchLiveController::TwitchLiveController()
this->immediateRequests.clear();
}

if (channelIDs.isEmpty())
{
return;
}

this->request(channelIDs);
});
this->immediateRequestTimer.start(1 * 1000);

qDebug() << "XD";
this->immediateRequestTimer.start(
TwitchLiveController::IMMEDIATE_REQUEST_INTERVAL);
}

void TwitchLiveController::add(std::shared_ptr<TwitchChannel> newChannel)
Expand All @@ -44,39 +54,14 @@ void TwitchLiveController::add(std::shared_ptr<TwitchChannel> newChannel)
const auto channelID = newChannel->roomId();
assert(!channelID.isEmpty());

qDebug() << "XXX: Add" << channelID;

std::unique_lock lock(this->channelsMutex);
auto &channelList = this->channels[channelID];

if (channelList.empty())
{
std::unique_lock immediateRequestsLock(this->immediateRequestsMutex);
this->immediateRequests.emplace(channelID);
std::unique_lock lock(this->channelsMutex);
this->channels[channelID] = newChannel;
}

channelList.emplace_back(newChannel);
}

void TwitchLiveController::remove(std::shared_ptr<TwitchChannel> channel)
{
const auto channelID = channel->roomId();
assert(!channelID.isEmpty());

qDebug() << "XXX: Remove" << channelID;

std::unique_lock lock(this->channelsMutex);
auto &channelList = this->channels[channelID];

channelList.erase(std::remove_if(channelList.begin(), channelList.end(),
[](const auto &c) {
return c.expired();
}),
channelList.end());

if (channelList.empty())
{
this->channels.erase(channelID);
std::unique_lock immediateRequestsLock(this->immediateRequestsMutex);
this->immediateRequests.emplace(channelID);
}
}

Expand All @@ -86,7 +71,6 @@ void TwitchLiveController::request(std::optional<QStringList> optChannelIDs)

if (optChannelIDs)
{
qDebug() << "XXX Load requests from channels map" << channelIDs;
channelIDs = *optChannelIDs;
}
else
Expand All @@ -101,22 +85,21 @@ void TwitchLiveController::request(std::optional<QStringList> optChannelIDs)

if (channelIDs.isEmpty())
{
qDebug() << "XXX: eraly out, no requests";
return;
}

auto batches = splitListIntoBatches(channelIDs, 3);
auto batches =
splitListIntoBatches(channelIDs, TwitchLiveController::BATCH_SIZE);

qCDebug(LOG) << "Make" << batches.size() << "requests";

for (const auto &batch : batches)
{
qDebug() << "XXX MAKE REQUEST" << batch;

// TODO: make concurrent
// TODO: Explore making this concurrent
getHelix()->fetchStreams(
batch, {},
[this, batch{batch}](const auto &streams) {
// on success
qDebug() << "XXX: success xd";
std::unordered_map<QString, std::optional<HelixStream>> results;

for (const auto &channelID : batch)
Expand All @@ -127,51 +110,80 @@ void TwitchLiveController::request(std::optional<QStringList> optChannelIDs)
for (const auto &stream : streams)
{
results[stream.userId] = stream;
qDebug() << "XXX: stream" << stream.userLogin;
}

QStringList deadChannels;

{
std::unique_lock lock(this->channelsMutex);
QStringList deadChannels;
std::shared_lock lock(this->channelsMutex);
for (const auto &result : results)
{
auto it = this->channels.find(result.first);
if (it != channels.end())
{
const auto &weakChannelList = it->second;
for (const auto &weakChannel : weakChannelList)
if (auto channel = it->second.lock(); channel)
{
auto channel = weakChannel.lock();
if (channel)
{
qDebug() << "XXX: channel is alive"
<< channel->getName();
channel->updateLiveStatus(result.second);
// POST LIVE STATUS
}
else
{
qDebug() << "XXX: channel is dead"
<< result.first;
// channel is dead, mark as dead
deadChannels.append(result.first);
}
channel->updateStreamStatus(result.second);
}
else
{
deadChannels.append(result.first);
}
}
}
}

if (!deadChannels.isEmpty())
{
std::unique_lock lock(this->channelsMutex);
for (const auto &deadChannel : deadChannels)
{
this->channels.erase(deadChannel);
}
}
},
[] {
qDebug() << "XXX: failed xd";
// on failure
qCWarning(LOG) << "Failed stream check request";
},
[] {});

// TODO: Explore making this concurrent
getHelix()->fetchChannels(
batch,
[this, batch{batch}](const auto &helixChannels) {
QStringList deadChannels;

{
std::shared_lock lock(this->channelsMutex);
for (const auto &helixChannel : helixChannels)
{
auto it = this->channels.find(helixChannel.userId);
if (it != this->channels.end())
{
if (auto channel = it->second.lock(); channel)
{
channel->updateStreamTitle(helixChannel.title);
channel->updateDisplayName(helixChannel.name);
}
else
{
deadChannels.append(helixChannel.userId);
}
}
}
}

if (!deadChannels.isEmpty())
{
std::unique_lock lock(this->channelsMutex);
for (const auto &deadChannel : deadChannels)
{
this->channels.erase(deadChannel);
}
}
},
[] {
// finally
qCWarning(LOG) << "Failed stream check request";
});
}
}
Expand Down
29 changes: 19 additions & 10 deletions src/controllers/twitch/LiveController.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,11 @@

#include "common/Singleton.hpp"
#include "util/QStringHash.hpp"
#include "util/RapidjsonHelpers.hpp"
#include "util/RapidJsonSerializeQString.hpp"
#include "util/serialize/Container.hpp"

#include <boost/optional.hpp>
#include <QColor>
#include <QString>
#include <QTimer>

#include <chrono>
#include <memory>
#include <mutex>
#include <optional>
Expand All @@ -28,23 +24,36 @@ class ITwitchLiveController
virtual ~ITwitchLiveController() = default;

virtual void add(std::shared_ptr<TwitchChannel> newChannel) = 0;
virtual void remove(std::shared_ptr<TwitchChannel> channel) = 0;
};

class TwitchLiveController : public ITwitchLiveController, public Singleton
{
public:
// Controls how often all channels have their stream status refreshed
static constexpr std::chrono::seconds REFRESH_INTERVAL{60};

// Controls how quickly new channels have their stream status loaded
static constexpr std::chrono::seconds IMMEDIATE_REQUEST_INTERVAL{1};

/**
* How many channels to include in a single request
*
* Should not be more than 100
**/
static constexpr int BATCH_SIZE{3};

TwitchLiveController();

// Add a Twitch channel to be queried for live status
// A request is made within a few seconds if this is the first time this channel is added
void add(std::shared_ptr<TwitchChannel> newChannel) override;
void remove(std::shared_ptr<TwitchChannel> channel) override;

private:
void request(std::optional<QStringList> optChannelIDs = std::nullopt);

// List of channel IDs pointing to Twitch Channels
std::unordered_map<QString, std::vector<std::weak_ptr<TwitchChannel>>>
channels;
// List of channel IDs pointing to their Twitch Channel
// These channels will have their stream status updated every INTERVAL seconds
std::unordered_map<QString, std::weak_ptr<TwitchChannel>> channels;
std::shared_mutex channelsMutex;

QTimer refreshTimer;
Expand Down
Loading

0 comments on commit 204d18e

Please sign in to comment.