From ea462f94e43a8490f86edd6d885b444e57f92929 Mon Sep 17 00:00:00 2001 From: Mm2PL Date: Sat, 1 Jan 2022 17:06:54 +0000 Subject: [PATCH] Check live status for all closed channels with notifications at once (#3442) Use a single api call for fetching live status of fake channels; batch by 100s Co-authored-by: Felanbird <41973452+Felanbird@users.noreply.github.com> Co-authored-by: zneix --- CHANGELOG.md | 1 + .../notifications/NotificationController.cpp | 149 +++++++++++------- .../notifications/NotificationController.hpp | 2 +- 3 files changed, 97 insertions(+), 55 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 35754983c..36a1faeaa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -76,6 +76,7 @@ - Bugfix: Fixed using special chars in Windows username breaking the storage of custom commands (#3397) - Bugfix: Fixed character counter changing fonts after going over the limit. (#3422) - Bugfix: Fixed crash that could occur if the user opens/closes ChannelViews (e.g. EmotePopup, or Splits) then modifies the showLastMessageIndicator setting. (#3444) +- Dev: Batch checking live status for channels with live notifications that aren't connected. (#3442) - Dev: Add GitHub action to test builds without precompiled headers enabled. (#3327) - Dev: Renamed CMake's build option `USE_SYSTEM_QT5KEYCHAIN` to `USE_SYSTEM_QTKEYCHAIN`. (#3103) - Dev: Add benchmarks that can be compiled with the `BUILD_BENCHMARKS` CMake flag. Off by default. (#3038) diff --git a/src/controllers/notifications/NotificationController.cpp b/src/controllers/notifications/NotificationController.cpp index ffd61fce7..9f8bb88e1 100644 --- a/src/controllers/notifications/NotificationController.cpp +++ b/src/controllers/notifications/NotificationController.cpp @@ -20,6 +20,7 @@ #include #include #include +#include namespace chatterino { @@ -127,8 +128,38 @@ NotificationModel *NotificationController::createModel(QObject *parent, return model; } +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 NotificationController::fetchFakeChannels() { + qCDebug(chatterinoNotification) << "fetching fake channels"; + QStringList channels; for (std::vector::size_type i = 0; i != channelMap[Platform::Twitch].raw().size(); i++) { @@ -136,69 +167,79 @@ void NotificationController::fetchFakeChannels() channelMap[Platform::Twitch].raw()[i]); if (chan->isEmpty()) { - getFakeTwitchChannelLiveStatus( - channelMap[Platform::Twitch].raw()[i]); + channels.push_back(channelMap[Platform::Twitch].raw()[i]); } } + for (const auto &batch : getChannelsInBatches(channels)) + { + getHelix()->fetchStreams( + QStringList(), batch, + [batch, this](std::vector streams) { + std::unordered_set liveStreams; + for (const auto &stream : streams) + { + liveStreams.insert(stream.userName); + } + + for (const auto &name : batch) + { + auto it = liveStreams.find(name); + this->checkStream(it != liveStreams.end(), name); + } + }, + [batch]() { + // we done fucked up. + qCWarning(chatterinoNotification) + << "Failed to fetch live status for " << batch; + }); + } } - -void NotificationController::getFakeTwitchChannelLiveStatus( - const QString &channelName) +void NotificationController::checkStream(bool live, QString channelName) { - getHelix()->getStreamByName( - channelName, - [channelName, this](bool live, const auto &stream) { - qCDebug(chatterinoNotification) << "[TwitchChannel" << channelName - << "] Refreshing live status"; + qCDebug(chatterinoNotification) + << "[TwitchChannel" << channelName << "] Refreshing live status"; - if (!live) - { - // Stream is offline - this->removeFakeChannel(channelName); - return; - } + if (!live) + { + // Stream is offline + this->removeFakeChannel(channelName); + return; + } - // Stream is online - auto i = std::find(fakeTwitchChannels.begin(), - fakeTwitchChannels.end(), channelName); + // Stream is online + auto i = std::find(fakeTwitchChannels.begin(), fakeTwitchChannels.end(), + channelName); - if (i != fakeTwitchChannels.end()) - { - // We have already pushed the live state of this stream - // Could not find stream in fake Twitch channels! - return; - } + if (i != fakeTwitchChannels.end()) + { + // We have already pushed the live state of this stream + // Could not find stream in fake Twitch channels! + return; + } - if (Toasts::isEnabled()) - { - getApp()->toasts->sendChannelNotification(channelName, - Platform::Twitch); - } - if (getSettings()->notificationPlaySound && - !(isInStreamerMode() && - getSettings()->streamerModeSuppressLiveNotifications)) - { - getApp()->notifications->playSound(); - } - if (getSettings()->notificationFlashTaskbar && - !(isInStreamerMode() && - getSettings()->streamerModeSuppressLiveNotifications)) - { - getApp()->windows->sendAlert(); - } - MessageBuilder builder; - TwitchMessageBuilder::liveMessage(channelName, &builder); - getApp()->twitch2->liveChannel->addMessage(builder.release()); + if (Toasts::isEnabled()) + { + getApp()->toasts->sendChannelNotification(channelName, + Platform::Twitch); + } + if (getSettings()->notificationPlaySound && + !(isInStreamerMode() && + getSettings()->streamerModeSuppressLiveNotifications)) + { + getApp()->notifications->playSound(); + } + if (getSettings()->notificationFlashTaskbar && + !(isInStreamerMode() && + getSettings()->streamerModeSuppressLiveNotifications)) + { + getApp()->windows->sendAlert(); + } + MessageBuilder builder; + TwitchMessageBuilder::liveMessage(channelName, &builder); + getApp()->twitch2->liveChannel->addMessage(builder.release()); - // Indicate that we have pushed notifications for this stream - fakeTwitchChannels.push_back(channelName); - }, - [channelName, this] { - qCDebug(chatterinoNotification) - << "[TwitchChannel" << channelName - << "] Refreshing live status (Missing ID)"; - this->removeFakeChannel(channelName); - }); + // Indicate that we have pushed notifications for this stream + fakeTwitchChannels.push_back(channelName); } void NotificationController::removeFakeChannel(const QString channelName) diff --git a/src/controllers/notifications/NotificationController.hpp b/src/controllers/notifications/NotificationController.hpp index e8d0c4ef7..abf1e3115 100644 --- a/src/controllers/notifications/NotificationController.hpp +++ b/src/controllers/notifications/NotificationController.hpp @@ -41,7 +41,7 @@ private: void fetchFakeChannels(); void removeFakeChannel(const QString channelName); - void getFakeTwitchChannelLiveStatus(const QString &channelName); + void checkStream(bool live, QString channelName); // fakeTwitchChannels is a list of streams who are live that we have already sent out a notification for std::vector fakeTwitchChannels;