From a36be2ffe629e5e93a670c9270f54d6b3df0110e Mon Sep 17 00:00:00 2001 From: Suman Manjunath Date: Mon, 3 Jun 2024 20:16:49 +0000 Subject: [PATCH] UI: Enable first-party YouTube Chat features in OBS Unlock the full feature set of the YouTube Chat dock in OBS by removing custom scripting/CSS logic. Enable the signed-in experience for live streaming content creators (sharing login credentials with the YouTube Control panel dock, see https://github.com/obsproject/obs-studio/pull/10747). This will allow OBS users to utilize features _already_ supported in the YouTube Chat plugin, such as * creating polls * managing Q&A sessions * a rich emoji set in the input panel * emoji fountains * moderation tools and many more. These features are available to users who are logged-in to YouTube Chat and/or the YouTube Control panel. --- UI/auth-youtube.cpp | 50 ++++++++++++++++++++++++++-------- UI/auth-youtube.hpp | 9 +++++- UI/window-dock-youtube-app.hpp | 4 ++- 3 files changed, 49 insertions(+), 14 deletions(-) diff --git a/UI/auth-youtube.cpp b/UI/auth-youtube.cpp index cd07c74cc79759..847d31b34e3dec 100644 --- a/UI/auth-youtube.cpp +++ b/UI/auth-youtube.cpp @@ -26,6 +26,7 @@ #ifdef BROWSER_AVAILABLE #include "window-dock-browser.hpp" +#include #endif using namespace json11; @@ -130,14 +131,6 @@ bool YoutubeAuth::LoadInternal() return implicit ? !token.empty() : !refresh_token.empty(); } -#ifdef BROWSER_AVAILABLE -static const char *ytchat_script = "\ -const obsCSS = document.createElement('style');\ -obsCSS.innerHTML = \"#panel-pages.yt-live-chat-renderer {display: none;}\ -yt-live-chat-viewer-engagement-message-renderer {display: none;}\";\ -document.querySelector('head').appendChild(obsCSS);"; -#endif - void YoutubeAuth::LoadUI() { if (uiLoaded) @@ -163,7 +156,6 @@ void YoutubeAuth::LoadUI() browser = cef->create_widget(chat, YOUTUBE_CHAT_PLACEHOLDER_URL, panel_cookies); - browser->setStartupScript(ytchat_script); chat->SetWidget(browser); main->AddDockWidget(chat, Qt::RightDockWidgetArea); @@ -350,6 +342,36 @@ std::shared_ptr YoutubeAuth::Login(QWidget *owner, } #ifdef BROWSER_AVAILABLE +void YoutubeChatDock::timerEvent(QTimerEvent *event) +{ + if (event->timerId() != awaitLoginTimer) + return; + + QPointer this_ = this; + auto cb = [this_](bool found) { + bool previouslyLoggedIn = this_->isLoggedIn; + this_->isLoggedIn = found; + if (found ^ previouslyLoggedIn) { + // There was a change in login state. + QMetaObject::invokeMethod(this_, "EnableChatInput", + Qt::QueuedConnection, + Q_ARG(bool, !found)); + this_->cefWidget->reloadPage(); + OBSBasic *main = OBSBasic::Get(); + if (main->GetYouTubeAppDock() != nullptr) { + QMetaObject::invokeMethod( + main->GetYouTubeAppDock(), + "SettingsUpdated", Qt::QueuedConnection, + Q_ARG(bool, !found)); + } + } + }; + if (panel_cookies) { + panel_cookies->CheckForCookie("https://www.youtube.com", "SID", + cb); + } +} + YoutubeChatDock::YoutubeChatDock(const QString &title) : BrowserDock(title) { lineEdit = new LineEditAutoResize(); @@ -368,6 +390,9 @@ YoutubeChatDock::YoutubeChatDock(const QString &title) : BrowserDock(title) &YoutubeChatDock::SendChatMessage); QWidget::connect(sendButton, &QPushButton::pressed, this, &YoutubeChatDock::SendChatMessage); + + // Check youtube.com login status periodically + awaitLoginTimer = startTimer(1000); } void YoutubeChatDock::SetWidget(QCefWidget *widget_) @@ -425,9 +450,10 @@ void YoutubeChatDock::ShowErrorMessage(const QString &error) QTStr("YouTube.Chat.Error.Text").arg(error)); } -void YoutubeChatDock::EnableChatInput() +void YoutubeChatDock::EnableChatInput(bool visible) { - lineEdit->setVisible(true); - sendButton->setVisible(true); + bool setVisible = visible && !isLoggedIn; + lineEdit->setVisible(setVisible); + sendButton->setVisible(setVisible); } #endif diff --git a/UI/auth-youtube.hpp b/UI/auth-youtube.hpp index 2e51cb48afdc59..18e797c61a514c 100644 --- a/UI/auth-youtube.hpp +++ b/UI/auth-youtube.hpp @@ -11,15 +11,22 @@ #include "window-dock-browser.hpp" #include "lineedit-autoresize.hpp" #include +#include + class YoutubeChatDock : public BrowserDock { Q_OBJECT private: std::string apiChatId; + int awaitLoginTimer; + bool isLoggedIn; LineEditAutoResize *lineEdit; QPushButton *sendButton; QHBoxLayout *chatLayout; +protected: + void timerEvent(QTimerEvent *event); + public: YoutubeChatDock(const QString &title); void SetWidget(QCefWidget *widget_); @@ -28,7 +35,7 @@ class YoutubeChatDock : public BrowserDock { private slots: void SendChatMessage(); void ShowErrorMessage(const QString &error); - void EnableChatInput(); + void EnableChatInput(bool visible = true); }; #endif diff --git a/UI/window-dock-youtube-app.hpp b/UI/window-dock-youtube-app.hpp index f09544fa1e1925..df2d8b83bc2583 100644 --- a/UI/window-dock-youtube-app.hpp +++ b/UI/window-dock-youtube-app.hpp @@ -17,7 +17,6 @@ class YouTubeAppDock : public BrowserDock { void AccountConnected(); void AccountDisconnected(); - void SettingsUpdated(bool cleanup = false); void Update(); void BroadcastCreated(const char *stream_id); @@ -29,6 +28,9 @@ class YouTubeAppDock : public BrowserDock { static YoutubeApiWrappers *GetYTApi(); static void CleanupYouTubeUrls(); +public slots: + void SettingsUpdated(bool cleanup = false); + protected: void IngestionStarted(const char *stream_id, streaming_mode_t mode); void IngestionStopped(const char *stream_id, streaming_mode_t mode);