diff --git a/plugins/twitch/channel-selection.cpp b/plugins/twitch/channel-selection.cpp index 598ce9d9..6d9f3187 100644 --- a/plugins/twitch/channel-selection.cpp +++ b/plugins/twitch/channel-selection.cpp @@ -31,6 +31,11 @@ void TwitchChannel::Save(obs_data_t *obj) const std::string TwitchChannel::GetUserID(const TwitchToken &token) const { static std::map userIDCache; + if (!token.IsValid()) { + vblog(LOG_INFO, "%s() failed: token invalid", __func__); + return "invalid"; + } + auto it = userIDCache.find(std::string(_name)); if (it != userIDCache.end()) { return it->second; @@ -47,7 +52,7 @@ std::string TwitchChannel::GetUserID(const TwitchToken &token) const } if (res.status != 200) { - blog(LOG_INFO, "failed to get Twitch user id from token! (%d)", + blog(LOG_INFO, "failed to get Twitch user id for channel! (%d)", res.status); return "invalid"; } @@ -90,7 +95,7 @@ getStringArrayHelper(const OBSDataAutoRelease &data, const std::string &value) } try { auto json = nlohmann::json::parse(jsonStr); - auto array = json[value]; + auto &array = json[value]; if (!array.is_array()) { return result; } @@ -104,7 +109,7 @@ getStringArrayHelper(const OBSDataAutoRelease &data, const std::string &value) } std::optional -TwitchChannel::GetLiveInfo(const TwitchToken &token) +TwitchChannel::GetLiveInfo(const TwitchToken &token) const { auto id = GetUserID(token); if (!IsValid(id)) { @@ -146,7 +151,8 @@ TwitchChannel::GetLiveInfo(const TwitchToken &token) return info; } -std::optional TwitchChannel::GetInfo(const TwitchToken &token) +std::optional +TwitchChannel::GetInfo(const TwitchToken &token) const { auto id = GetUserID(token); if (!IsValid(id)) { diff --git a/plugins/twitch/channel-selection.hpp b/plugins/twitch/channel-selection.hpp index 175b751d..87a46ca3 100644 --- a/plugins/twitch/channel-selection.hpp +++ b/plugins/twitch/channel-selection.hpp @@ -51,8 +51,8 @@ struct TwitchChannel { StringVariable GetName() const { return _name; } std::string GetUserID(const TwitchToken &token) const; bool IsValid(const std::string &id) const; - std::optional GetLiveInfo(const TwitchToken &); - std::optional GetInfo(const TwitchToken &); + std::optional GetLiveInfo(const TwitchToken &) const; + std::optional GetInfo(const TwitchToken &) const; void ResolveVariables(); private: diff --git a/plugins/twitch/token.cpp b/plugins/twitch/token.cpp index 1bf4bde2..6b8e5909 100644 --- a/plugins/twitch/token.cpp +++ b/plugins/twitch/token.cpp @@ -81,7 +81,7 @@ const std::unordered_map TokenOption::_apiIdToLocale{ static void saveConnections(obs_data_t *obj); static void loadConnections(obs_data_t *obj); -bool setupTwitchTokenSupport() +static bool setupTwitchTokenSupport() { AddSaveStep(saveConnections); AddLoadStep(loadConnections); @@ -153,6 +153,29 @@ bool TokenOption::operator<(const TokenOption &other) const return apiId < other.apiId; } +TwitchToken::TwitchToken(const TwitchToken &other) + : Item(other._name), + _token(other._token), + _lastValidityCheckTime(), + _lastValidityCheckValue(), + _lastValidityCheckResult(false), + _userID(other._userID), + _tokenOptions(other._tokenOptions), + _eventSub(), + _validateEventSubTimestamps(other._validateEventSubTimestamps) +{ +} + +TwitchToken &TwitchToken::operator=(const TwitchToken &other) +{ + _name = other._name; + _token = other._token; + _userID = other._userID; + _tokenOptions = other._tokenOptions; + _validateEventSubTimestamps = other._validateEventSubTimestamps; + return *this; +} + void TwitchToken::Load(obs_data_t *obj) { Item::Load(obj); @@ -273,39 +296,38 @@ std::shared_ptr TwitchToken::GetEventSub() bool TwitchToken::IsValid(bool forceUpdate) const { - static std::chrono::system_clock::time_point queryTime; - static std::string lastQueryToken; - static httplib::Result response; static httplib::Client cli("https://id.twitch.tv"); httplib::Headers headers{{"Authorization", "OAuth " + _token}}; + std::scoped_lock lock(_cacheMutex); + auto currentTime = std::chrono::system_clock::now(); - auto diff = currentTime - queryTime; + auto diff = currentTime - _lastValidityCheckTime; const bool cacheIsTooOld = diff >= std::chrono::hours(1); - const bool tokenChanged = lastQueryToken != _token; - if (tokenChanged) { - response = + const auto checkToken = [&]() -> bool { + const auto response = cli.Get("/oauth2/validate", httplib::Params{}, headers); - queryTime = std::chrono::system_clock::now(); - lastQueryToken = _token; - return response && response->status == 200; + _lastValidityCheckTime = std::chrono::system_clock::now(); + _lastValidityCheckValue = _token; + _lastValidityCheckResult = response && response->status == 200; + if (!_lastValidityCheckResult) { + blog(LOG_INFO, "Twitch token %s is not valid!", + _name.c_str()); + } + return _lastValidityCheckResult; + }; + + const bool tokenChanged = _lastValidityCheckValue != _token; + if (tokenChanged) { + return checkToken(); } - // No point in checking again as token will not become valid again - if (!forceUpdate && response && response->status != 200) { - blog(LOG_INFO, "Twitch token %s is not valid!", _name.c_str()); - return false; + if (!forceUpdate && !cacheIsTooOld) { + return _lastValidityCheckResult; } - if (!forceUpdate && !cacheIsTooOld && response) { - return response->status == 200; - } - - response = cli.Get("/oauth2/validate", httplib::Params{}, headers); - queryTime = std::chrono::system_clock::now(); - lastQueryToken = _token; - return response && response->status == 200; + return checkToken(); } TwitchToken *GetTwitchTokenByName(const QString &name) diff --git a/plugins/twitch/token.hpp b/plugins/twitch/token.hpp index 205455d6..b3763af4 100644 --- a/plugins/twitch/token.hpp +++ b/plugins/twitch/token.hpp @@ -33,6 +33,10 @@ private: class TwitchToken : public Item { public: + TwitchToken() = default; + TwitchToken(const TwitchToken &); + TwitchToken &operator=(const TwitchToken &); + static std::shared_ptr Create() { return std::make_shared(); @@ -55,6 +59,10 @@ public: private: std::string _token; + mutable std::mutex _cacheMutex; + mutable std::string _lastValidityCheckValue; + mutable bool _lastValidityCheckResult = false; + mutable std::chrono::system_clock::time_point _lastValidityCheckTime; std::string _userID; std::set _tokenOptions = TokenOption::GetAllTokenOptions(); std::shared_ptr _eventSub;