auth-restream.cpp 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283
  1. #include "auth-restream.hpp"
  2. #include <QPushButton>
  3. #include <QHBoxLayout>
  4. #include <QVBoxLayout>
  5. #include <qt-wrappers.hpp>
  6. #include <json11.hpp>
  7. #include <ctime>
  8. #include <sstream>
  9. #include <obs-app.hpp>
  10. #include "window-basic-main.hpp"
  11. #include "remote-text.hpp"
  12. #include "ui-config.h"
  13. #include "obf.h"
  14. #include <browser-panel.hpp>
  15. using namespace json11;
  16. extern QCef *cef;
  17. extern QCefCookieManager *panel_cookies;
  18. /* ------------------------------------------------------------------------- */
  19. #define RESTREAM_AUTH_URL "https://obsproject.com/app-auth/restream?action=redirect"
  20. #define RESTREAM_TOKEN_URL "https://obsproject.com/app-auth/restream-token"
  21. #define RESTREAM_STREAMKEY_URL "https://api.restream.io/v2/user/streamKey"
  22. #define RESTREAM_SCOPE_VERSION 1
  23. static Auth::Def restreamDef = {
  24. "Restream",
  25. Auth::Type::OAuth_StreamKey
  26. };
  27. /* ------------------------------------------------------------------------- */
  28. RestreamAuth::RestreamAuth(const Def &d)
  29. : OAuthStreamKey(d)
  30. {
  31. }
  32. bool RestreamAuth::GetChannelInfo()
  33. try {
  34. std::string client_id = RESTREAM_CLIENTID;
  35. deobfuscate_str(&client_id[0], RESTREAM_HASH);
  36. if (!GetToken(RESTREAM_TOKEN_URL, client_id, RESTREAM_SCOPE_VERSION))
  37. return false;
  38. if (token.empty())
  39. return false;
  40. if (!key_.empty())
  41. return true;
  42. std::string auth;
  43. auth += "Authorization: Bearer ";
  44. auth += token;
  45. std::vector<std::string> headers;
  46. headers.push_back(std::string("Client-ID: ") + client_id);
  47. headers.push_back(std::move(auth));
  48. std::string output;
  49. std::string error;
  50. Json json;
  51. bool success;
  52. auto func = [&] () {
  53. success = GetRemoteFile(
  54. RESTREAM_STREAMKEY_URL,
  55. output,
  56. error,
  57. nullptr,
  58. "application/json",
  59. nullptr,
  60. headers,
  61. nullptr,
  62. 5);
  63. };
  64. ExecThreadedWithoutBlocking(
  65. func,
  66. QTStr("Auth.LoadingChannel.Title"),
  67. QTStr("Auth.LoadingChannel.Text").arg(service()));
  68. if (!success || output.empty())
  69. throw ErrorInfo("Failed to get stream key from remote", error);
  70. json = Json::parse(output, error);
  71. if (!error.empty())
  72. throw ErrorInfo("Failed to parse json", error);
  73. error = json["error"].string_value();
  74. if (!error.empty())
  75. throw ErrorInfo(error, json["error_description"].string_value());
  76. key_ = json["streamKey"].string_value();
  77. return true;
  78. } catch (ErrorInfo info) {
  79. QString title = QTStr("Auth.ChannelFailure.Title");
  80. QString text = QTStr("Auth.ChannelFailure.Text")
  81. .arg(service(), info.message.c_str(), info.error.c_str());
  82. QMessageBox::warning(OBSBasic::Get(), title, text);
  83. blog(LOG_WARNING, "%s: %s: %s",
  84. __FUNCTION__,
  85. info.message.c_str(),
  86. info.error.c_str());
  87. return false;
  88. }
  89. void RestreamAuth::SaveInternal()
  90. {
  91. OBSBasic *main = OBSBasic::Get();
  92. config_set_string(main->Config(), service(), "DockState",
  93. main->saveState().toBase64().constData());
  94. OAuthStreamKey::SaveInternal();
  95. }
  96. static inline std::string get_config_str(
  97. OBSBasic *main,
  98. const char *section,
  99. const char *name)
  100. {
  101. const char *val = config_get_string(main->Config(), section, name);
  102. return val ? val : "";
  103. }
  104. bool RestreamAuth::LoadInternal()
  105. {
  106. firstLoad = false;
  107. return OAuthStreamKey::LoadInternal();
  108. }
  109. class RestreamWidget : public QDockWidget {
  110. public:
  111. inline RestreamWidget() : QDockWidget() {}
  112. QScopedPointer<QCefWidget> widget;
  113. };
  114. void RestreamAuth::LoadUI()
  115. {
  116. if (uiLoaded)
  117. return;
  118. if (!GetChannelInfo())
  119. return;
  120. OBSBasic::InitBrowserPanelSafeBlock();
  121. OBSBasic *main = OBSBasic::Get();
  122. QCefWidget *browser;
  123. std::string url;
  124. std::string script;
  125. /* ----------------------------------- */
  126. url = "https://restream.io/chat-application";
  127. QSize size = main->frameSize();
  128. QPoint pos = main->pos();
  129. chat.reset(new RestreamWidget());
  130. chat->setObjectName("restreamChat");
  131. chat->resize(420, 600);
  132. chat->setMinimumSize(200, 300);
  133. chat->setWindowTitle(QTStr("Auth.Chat"));
  134. chat->setAllowedAreas(Qt::AllDockWidgetAreas);
  135. browser = cef->create_widget(nullptr, url, panel_cookies);
  136. chat->setWidget(browser);
  137. main->addDockWidget(Qt::RightDockWidgetArea, chat.data());
  138. chatMenu.reset(main->AddDockWidget(chat.data()));
  139. /* ----------------------------------- */
  140. url = "https://restream.io/titles/embed";
  141. info.reset(new RestreamWidget());
  142. info->setObjectName("restreamInfo");
  143. info->resize(410, 600);
  144. info->setMinimumSize(200, 150);
  145. info->setWindowTitle(QTStr("Auth.StreamInfo"));
  146. info->setAllowedAreas(Qt::AllDockWidgetAreas);
  147. browser = cef->create_widget(nullptr, url, panel_cookies);
  148. info->setWidget(browser);
  149. main->addDockWidget(Qt::RightDockWidgetArea, info.data());
  150. infoMenu.reset(main->AddDockWidget(info.data()));
  151. /* ----------------------------------- */
  152. chat->setFloating(true);
  153. info->setFloating(true);
  154. chat->move(pos.x() + size.width() - chat->width() - 50, pos.y() + 50);
  155. info->move(pos.x() + 40, pos.y() + 50);
  156. if (firstLoad) {
  157. chat->setVisible(true);
  158. info->setVisible(true);
  159. }
  160. else {
  161. const char *dockStateStr = config_get_string(main->Config(),
  162. service(), "DockState");
  163. QByteArray dockState =
  164. QByteArray::fromBase64(QByteArray(dockStateStr));
  165. main->restoreState(dockState);
  166. }
  167. uiLoaded = true;
  168. }
  169. bool RestreamAuth::RetryLogin()
  170. {
  171. OAuthLogin login(OBSBasic::Get(), RESTREAM_AUTH_URL, false);
  172. cef->add_popup_whitelist_url("about:blank", &login);
  173. if (login.exec() == QDialog::Rejected) {
  174. return false;
  175. }
  176. std::shared_ptr<RestreamAuth> auth =
  177. std::make_shared<RestreamAuth>(restreamDef);
  178. std::string client_id = RESTREAM_CLIENTID;
  179. deobfuscate_str(&client_id[0], RESTREAM_HASH);
  180. return GetToken(RESTREAM_TOKEN_URL, client_id,
  181. RESTREAM_SCOPE_VERSION,
  182. QT_TO_UTF8(login.GetCode()), true);
  183. }
  184. std::shared_ptr<Auth> RestreamAuth::Login(QWidget *parent)
  185. {
  186. OAuthLogin login(parent, RESTREAM_AUTH_URL, false);
  187. cef->add_popup_whitelist_url("about:blank", &login);
  188. if (login.exec() == QDialog::Rejected) {
  189. return nullptr;
  190. }
  191. std::shared_ptr<RestreamAuth> auth =
  192. std::make_shared<RestreamAuth>(restreamDef);
  193. std::string client_id = RESTREAM_CLIENTID;
  194. deobfuscate_str(&client_id[0], RESTREAM_HASH);
  195. if (!auth->GetToken(RESTREAM_TOKEN_URL, client_id,
  196. RESTREAM_SCOPE_VERSION,
  197. QT_TO_UTF8(login.GetCode()))) {
  198. return nullptr;
  199. }
  200. std::string error;
  201. if (auth->GetChannelInfo()) {
  202. return auth;
  203. }
  204. return nullptr;
  205. }
  206. static std::shared_ptr<Auth> CreateRestreamAuth()
  207. {
  208. return std::make_shared<RestreamAuth>(restreamDef);
  209. }
  210. static void DeleteCookies()
  211. {
  212. if (panel_cookies) {
  213. panel_cookies->DeleteCookies("restream.io", std::string());
  214. }
  215. }
  216. void RegisterRestreamAuth()
  217. {
  218. OAuth::RegisterOAuth(
  219. restreamDef,
  220. CreateRestreamAuth,
  221. RestreamAuth::Login,
  222. DeleteCookies);
  223. }