diff --git a/UI/auth-youtube.cpp b/UI/auth-youtube.cpp index 78038689c..d7ceecb51 100644 --- a/UI/auth-youtube.cpp +++ b/UI/auth-youtube.cpp @@ -141,7 +141,7 @@ void YoutubeAuth::LoadUI() QSize size = main->frameSize(); QPoint pos = main->pos(); - chat.reset(new BrowserDock()); + chat.reset(new YoutubeChatDock()); chat->setObjectName("ytChat"); chat->resize(300, 600); chat->setMinimumSize(200, 300); @@ -173,16 +173,18 @@ void YoutubeAuth::LoadUI() uiLoaded = true; } -void YoutubeAuth::SetChatId(QString &chat_id) +void YoutubeAuth::SetChatId(const QString &chat_id, + const std::string &api_chat_id) { #ifdef BROWSER_AVAILABLE QString chat_url = QString(YOUTUBE_CHAT_POPOUT_URL).arg(chat_id); if (chat && chat->cefWidget) { chat->cefWidget->setURL(chat_url.toStdString()); + chat->SetApiChatId(api_chat_id); } #else - UNUSED_PARAMETER(chat_id); + UNUSED_PARAMETER(chat_id, api_chat_id); #endif } @@ -322,3 +324,83 @@ std::shared_ptr YoutubeAuth::Login(QWidget *owner, config_save_safe(config, "tmp", nullptr); return auth; } + +#ifdef BROWSER_AVAILABLE +void YoutubeChatDock::SetWidget(QCefWidget *widget_) +{ + lineEdit = new LineEditAutoResize(); + lineEdit->setVisible(false); + lineEdit->setMaxLength(200); + lineEdit->setPlaceholderText(QTStr("YouTube.Chat.Input.Placeholder")); + sendButton = new QPushButton(QTStr("YouTube.Chat.Input.Send")); + sendButton->setVisible(false); + + chatLayout = new QHBoxLayout(); + chatLayout->setMargin(0); + chatLayout->addWidget(lineEdit, 1); + chatLayout->addWidget(sendButton); + + QVBoxLayout *layout = new QVBoxLayout(); + layout->setMargin(0); + layout->addWidget(widget_, 1); + layout->addLayout(chatLayout); + + QWidget *widget = new QWidget(); + widget->setLayout(layout); + setWidget(widget); + + QWidget::connect(lineEdit, SIGNAL(returnPressed()), this, + SLOT(SendChatMessage())); + QWidget::connect(sendButton, SIGNAL(pressed()), this, + SLOT(SendChatMessage())); + + cefWidget.reset(widget_); +} + +void YoutubeChatDock::SetApiChatId(const std::string &id) +{ + this->apiChatId = id; + QMetaObject::invokeMethod(this, "EnableChatInput", + Qt::QueuedConnection); +} + +void YoutubeChatDock::SendChatMessage() +{ + const QString message = lineEdit->text(); + if (message == "") + return; + + OBSBasic *main = OBSBasic::Get(); + YoutubeApiWrappers *apiYouTube( + dynamic_cast(main->GetAuth())); + + ExecuteFuncSafeBlock([&]() { + lineEdit->setText(""); + lineEdit->setPlaceholderText( + QTStr("YouTube.Chat.Input.Sending")); + if (apiYouTube->SendChatMessage(apiChatId, message)) { + os_sleep_ms(3000); + } else { + QString error = apiYouTube->GetLastError(); + apiYouTube->GetTranslatedError(error); + QMetaObject::invokeMethod( + this, "ShowErrorMessage", Qt::QueuedConnection, + Q_ARG(const QString &, error)); + } + lineEdit->setPlaceholderText( + QTStr("YouTube.Chat.Input.Placeholder")); + }); +} + +void YoutubeChatDock::ShowErrorMessage(const QString &error) +{ + QMessageBox::warning(this, QTStr("YouTube.Chat.Error.Title"), + QTStr("YouTube.Chat.Error.Text").arg(error)); +} + +void YoutubeChatDock::EnableChatInput() +{ + lineEdit->setVisible(true); + sendButton->setVisible(true); +} +#endif diff --git a/UI/auth-youtube.hpp b/UI/auth-youtube.hpp index 31b2ca583..c0bd7b687 100644 --- a/UI/auth-youtube.hpp +++ b/UI/auth-youtube.hpp @@ -8,7 +8,27 @@ #include "auth-oauth.hpp" #ifdef BROWSER_AVAILABLE -class BrowserDock; +#include "window-dock-browser.hpp" +#include "lineedit-autoresize.hpp" +#include +class YoutubeChatDock : public BrowserDock { + Q_OBJECT + +private: + std::string apiChatId; + LineEditAutoResize *lineEdit; + QPushButton *sendButton; + QHBoxLayout *chatLayout; + +public: + void SetWidget(QCefWidget *widget_); + void SetApiChatId(const std::string &id); + +private slots: + void SendChatMessage(); + void ShowErrorMessage(const QString &error); + void EnableChatInput(); +}; #endif inline const std::vector youtubeServices = { @@ -24,7 +44,7 @@ class YoutubeAuth : public OAuthStreamKey { std::string section; #ifdef BROWSER_AVAILABLE - QSharedPointer chat; + QSharedPointer chat; QSharedPointer chatMenu; #endif @@ -38,7 +58,7 @@ class YoutubeAuth : public OAuthStreamKey { public: YoutubeAuth(const Def &d); - void SetChatId(QString &chat_id); + void SetChatId(const QString &chat_id, const std::string &api_chat_id); void ResetChat(); static std::shared_ptr Login(QWidget *parent, diff --git a/UI/data/locale/en-US.ini b/UI/data/locale/en-US.ini index 3513f0f83..85ca51e67 100644 --- a/UI/data/locale/en-US.ini +++ b/UI/data/locale/en-US.ini @@ -1320,6 +1320,12 @@ YouTube.Actions.AutoStartStreamingWarning.Title="Manual start required" YouTube.Actions.AutoStartStreamingWarning="Auto-start is disabled for this event, click \"Go Live\" to start your broadcast." YouTube.Actions.AutoStopStreamingWarning="You will not be able to reconnect.
Your stream will stop and you will no longer be live." +YouTube.Chat.Input.Send="Send" +YouTube.Chat.Input.Placeholder="Enter message here..." +YouTube.Chat.Input.Sending="Sending..." +YouTube.Chat.Error.Title="Error while sending message" +YouTube.Chat.Error.Text="The message couldn't be sent: %1" + # YouTube API errors in format "YouTube.Errors." YouTube.Errors.liveStreamingNotEnabled="Live streaming is not enabled on the selected YouTube channel.

See youtube.com/features for more information." YouTube.Errors.livePermissionBlocked="Live streaming is unavailable on the selected YouTube Channel.
Please note that it may take up to 24 hours for live streaming to become available after enabling it in your channel settings.

See youtube.com/features for details." diff --git a/UI/window-youtube-actions.cpp b/UI/window-youtube-actions.cpp index c07119c67..26f1db83c 100644 --- a/UI/window-youtube-actions.cpp +++ b/UI/window-youtube-actions.cpp @@ -503,10 +503,13 @@ bool OBSYoutubeActions::CreateEventAction(YoutubeApiWrappers *api, return false; } - if (broadcast.privacy != "private") - apiYouTube->SetChatId(broadcast.id); - else + if (broadcast.privacy != "private") { + const std::string apiLiveChatId = + json["snippet"]["liveChatId"].string_value(); + apiYouTube->SetChatId(broadcast.id, apiLiveChatId); + } else { apiYouTube->ResetChat(); + } } return true; @@ -531,6 +534,10 @@ bool OBSYoutubeActions::ChooseAnEventAction(YoutubeApiWrappers *api, json["items"] .array_items()[0]["status"]["privacyStatus"] .string_value(); + std::string apiLiveChatId = + json["items"] + .array_items()[0]["snippet"]["liveChatId"] + .string_value(); stream.id = boundStreamId.c_str(); if (!stream.id.isEmpty() && apiYouTube->FindStream(stream.id, json)) { @@ -556,7 +563,7 @@ bool OBSYoutubeActions::ChooseAnEventAction(YoutubeApiWrappers *api, } if (broadcastPrivacy != "private") - apiYouTube->SetChatId(selectedBroadcast); + apiYouTube->SetChatId(selectedBroadcast, apiLiveChatId); else apiYouTube->ResetChat();