From 74ec310228698ccfa1d897beff8a58b337ed3f73 Mon Sep 17 00:00:00 2001 From: Kasia Date: Sat, 28 May 2022 20:10:10 +0200 Subject: [PATCH] Fixed channels not being set as offline (#3767) --- CHANGELOG.md | 2 +- .../notifications/NotificationController.cpp | 3 + src/providers/twitch/TwitchChannel.cpp | 3 + src/providers/twitch/TwitchIrcServer.cpp | 104 +++++++++++------- src/providers/twitch/api/Helix.cpp | 15 ++- src/providers/twitch/api/Helix.hpp | 18 ++- 6 files changed, 92 insertions(+), 53 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 824c93548..66c731ccc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -31,7 +31,7 @@ - Bugfix: Fixed viewer list not closing after pressing escape key. (#3734) - Bugfix: Fixed links with no thumbnail having previous link's thumbnail. (#3720) - Dev: Use Game Name returned by Get Streams instead of querying it from the Get Games API. (#3662) -- Dev: Batch checking live status for all channels after startup. (#3757, #3762) +- Dev: Batch checking live status for all channels after startup. (#3757, #3762, #3767) ## 2.3.5 diff --git a/src/controllers/notifications/NotificationController.cpp b/src/controllers/notifications/NotificationController.cpp index a2f2c5ae2..0d20ce6e1 100644 --- a/src/controllers/notifications/NotificationController.cpp +++ b/src/controllers/notifications/NotificationController.cpp @@ -191,6 +191,9 @@ void NotificationController::fetchFakeChannels() // we done fucked up. qCWarning(chatterinoNotification) << "Failed to fetch live status for " << batch; + }, + []() { + // finally }); } } diff --git a/src/providers/twitch/TwitchChannel.cpp b/src/providers/twitch/TwitchChannel.cpp index 0f6feba57..26e30a976 100644 --- a/src/providers/twitch/TwitchChannel.cpp +++ b/src/providers/twitch/TwitchChannel.cpp @@ -708,6 +708,9 @@ void TwitchChannel::refreshLiveStatus() }, [] { // failure + }, + [] { + // finally }); } diff --git a/src/providers/twitch/TwitchIrcServer.cpp b/src/providers/twitch/TwitchIrcServer.cpp index 08d68ddaa..d803f45cc 100644 --- a/src/providers/twitch/TwitchIrcServer.cpp +++ b/src/providers/twitch/TwitchIrcServer.cpp @@ -26,6 +26,35 @@ using namespace std::chrono_literals; namespace chatterino { +namespace { + // TODO: combine this with getEmoteSetBatches in TwitchAccount.cpp, maybe some templated thing + template + std::vector getChannelsInBatches(T channels) + { + constexpr int batchSize = 100; + + int batchCount = (channels.size() / batchSize) + 1; + + std::vector batches; + batches.reserve(batchCount); + + for (int i = 0; i < batchCount; i++) + { + T batch; + + // I hate you, msvc + int last = (std::min)(batchSize, channels.size() - batchSize * i); + for (int j = 0; j < last; j++) + { + batch.push_back(channels.at(j + (batchSize * i))); + } + batches.emplace_back(batch); + } + + return batches; + } +} // namespace + TwitchIrcServer::TwitchIrcServer() : whispersChannel(new Channel("/whispers", Channel::Type::TwitchWhispers)) , mentionsChannel(new Channel("/mentions", Channel::Type::TwitchMentions)) @@ -302,60 +331,55 @@ std::shared_ptr TwitchIrcServer::getChannelOrEmptyByID( return Channel::getEmpty(); } -namespace { - // TODO: combine this with getEmoteSetBatches in TwitchAccount.cpp, maybe some templated thing - std::vector getChannelsInBatches(QStringList channels) - { - constexpr int batchSize = 100; - - int batchCount = (channels.size() / batchSize) + 1; - - std::vector batches; - batches.reserve(batchCount); - - for (int i = 0; i < batchCount; i++) - { - QStringList batch; - - // I hate you, msvc - int last = (std::min)(batchSize, channels.size() - batchSize * i); - for (int j = 0; j < last; j++) - { - batch.push_back(channels.at(j + (batchSize * i))); - } - batches.emplace_back(batch); - } - - return batches; - } -} // namespace - void TwitchIrcServer::bulkRefreshLiveStatus() { - QStringList userIDs; - this->forEachChannel([&userIDs](ChannelPtr chan) { - auto twitchChan = dynamic_cast(chan.get()); - if (!twitchChan->roomId().isEmpty()) - userIDs.push_back(twitchChan->roomId()); + auto twitchChans = std::make_shared>(); + + this->forEachChannel([twitchChans](ChannelPtr chan) { + auto tc = dynamic_cast(chan.get()); + if (tc && !tc->roomId().isEmpty()) + { + twitchChans->insert(tc->roomId(), tc); + } }); - for (const auto &batch : getChannelsInBatches(userIDs)) + // iterate over batches of channel IDs + for (const auto &batch : getChannelsInBatches(twitchChans->keys())) { getHelix()->fetchStreams( - batch, QStringList(), - [this](std::vector streams) { + batch, {}, + [twitchChans](std::vector streams) { for (const auto &stream : streams) { - auto chan = this->getChannelOrEmpty(stream.userLogin); - if (chan->getType() != Channel::Type::Twitch) + // remaining channels will be used later to set their stream status as offline + // so we use take(id) to remove it + auto tc = twitchChans->take(stream.userId); + if (tc == nullptr) + { continue; + } - auto twitchChan = dynamic_cast(chan.get()); - twitchChan->parseLiveStatus(true, stream); + tc->parseLiveStatus(true, stream); } }, []() { // failure + }, + [batch, twitchChans] { + // All the channels that were not present in fetchStreams response should be assumed to be offline + // It is necessary to update their stream status in case they've gone live -> offline + // Otherwise some of them will be marked as live forever + for (const auto &chID : batch) + { + auto tc = twitchChans->value(chID); + // early out in case channel does not exist anymore + if (tc == nullptr) + { + continue; + } + + tc->parseLiveStatus(false, {}); + } }); } } diff --git a/src/providers/twitch/api/Helix.cpp b/src/providers/twitch/api/Helix.cpp index ee47489a0..953d7d989 100644 --- a/src/providers/twitch/api/Helix.cpp +++ b/src/providers/twitch/api/Helix.cpp @@ -145,7 +145,7 @@ void Helix::getUserFollowers( void Helix::fetchStreams( QStringList userIds, QStringList userLogins, ResultCallback> successCallback, - HelixFailureCallback failureCallback) + HelixFailureCallback failureCallback, std::function finallyCallback) { QUrlQuery urlQuery; @@ -186,19 +186,21 @@ void Helix::fetchStreams( // TODO: make better xd failureCallback(); }) + .finally(finallyCallback) .execute(); } void Helix::getStreamById(QString userId, ResultCallback successCallback, - HelixFailureCallback failureCallback) + HelixFailureCallback failureCallback, + std::function finallyCallback) { QStringList userIds{std::move(userId)}; QStringList userLogins; this->fetchStreams( userIds, userLogins, - [successCallback, failureCallback](const auto &streams) { + [successCallback](const auto &streams) { if (streams.empty()) { successCallback(false, HelixStream()); @@ -206,12 +208,13 @@ void Helix::getStreamById(QString userId, } successCallback(true, streams[0]); }, - failureCallback); + failureCallback, finallyCallback); } void Helix::getStreamByName(QString userName, ResultCallback successCallback, - HelixFailureCallback failureCallback) + HelixFailureCallback failureCallback, + std::function finallyCallback) { QStringList userIds; QStringList userLogins{std::move(userName)}; @@ -226,7 +229,7 @@ void Helix::getStreamByName(QString userName, } successCallback(true, streams[0]); }, - failureCallback); + failureCallback, finallyCallback); } /// diff --git a/src/providers/twitch/api/Helix.hpp b/src/providers/twitch/api/Helix.hpp index 3e0c3e54d..878c40eb0 100644 --- a/src/providers/twitch/api/Helix.hpp +++ b/src/providers/twitch/api/Helix.hpp @@ -352,15 +352,18 @@ public: virtual void fetchStreams( QStringList userIds, QStringList userLogins, ResultCallback> successCallback, - HelixFailureCallback failureCallback) = 0; + HelixFailureCallback failureCallback, + std::function finallyCallback) = 0; virtual void getStreamById( QString userId, ResultCallback successCallback, - HelixFailureCallback failureCallback) = 0; + HelixFailureCallback failureCallback, + std::function finallyCallback) = 0; virtual void getStreamByName( QString userName, ResultCallback successCallback, - HelixFailureCallback failureCallback) = 0; + HelixFailureCallback failureCallback, + std::function finallyCallback) = 0; // https://dev.twitch.tv/docs/api/reference#get-games virtual void fetchGames( @@ -469,15 +472,18 @@ public: // https://dev.twitch.tv/docs/api/reference#get-streams void fetchStreams(QStringList userIds, QStringList userLogins, ResultCallback> successCallback, - HelixFailureCallback failureCallback) final; + HelixFailureCallback failureCallback, + std::function finallyCallback) final; void getStreamById(QString userId, ResultCallback successCallback, - HelixFailureCallback failureCallback) final; + HelixFailureCallback failureCallback, + std::function finallyCallback) final; void getStreamByName(QString userName, ResultCallback successCallback, - HelixFailureCallback failureCallback) final; + HelixFailureCallback failureCallback, + std::function finallyCallback) final; // https://dev.twitch.tv/docs/api/reference#get-games void fetchGames(QStringList gameIds, QStringList gameNames,