/****************************************************************************** Copyright (C) 2023 by Lain Bailey This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . ******************************************************************************/ #include "OBSBasic.hpp" #ifdef BROWSER_AVAILABLE #include #include #include #include #include using namespace json11; #endif #include struct QCef; struct QCefCookieManager; QCef *cef = nullptr; QCefCookieManager *panel_cookies = nullptr; bool cef_js_avail = false; #ifdef BROWSER_AVAILABLE void OBSBasic::ClearExtraBrowserDocks() { extraBrowserDockTargets.clear(); extraBrowserDockNames.clear(); extraBrowserDocks.clear(); } void OBSBasic::LoadExtraBrowserDocks() { const char *jsonStr = config_get_string(App()->GetUserConfig(), "BasicWindow", "ExtraBrowserDocks"); std::string err; Json json = Json::parse(jsonStr, err); if (!err.empty()) return; Json::array array = json.array_items(); if (!array.empty()) extraBrowserMenuDocksSeparator = ui->menuDocks->addSeparator(); for (Json &item : array) { std::string title = item["title"].string_value(); std::string url = item["url"].string_value(); std::string uuid = item["uuid"].string_value(); AddExtraBrowserDock(title.c_str(), url.c_str(), uuid.c_str(), false); } } void OBSBasic::SaveExtraBrowserDocks() { Json::array array; for (int i = 0; i < extraBrowserDocks.size(); i++) { QDockWidget *dock = extraBrowserDocks[i].get(); QString title = extraBrowserDockNames[i]; QString url = extraBrowserDockTargets[i]; QString uuid = dock->property("uuid").toString(); Json::object obj{ {"title", QT_TO_UTF8(title)}, {"url", QT_TO_UTF8(url)}, {"uuid", QT_TO_UTF8(uuid)}, }; array.push_back(obj); } std::string output = Json(array).dump(); config_set_string(App()->GetUserConfig(), "BasicWindow", "ExtraBrowserDocks", output.c_str()); } void OBSBasic::ManageExtraBrowserDocks() { if (!extraBrowsers.isNull()) { extraBrowsers->show(); extraBrowsers->raise(); return; } extraBrowsers = new OBSExtraBrowsers(this); extraBrowsers->show(); } void OBSBasic::AddExtraBrowserDock(const QString &title, const QString &url, const QString &uuid, bool firstCreate) { static int panel_version = -1; if (panel_version == -1) { panel_version = obs_browser_qcef_version(); } BrowserDock *dock = new BrowserDock(title); QString bId(uuid.isEmpty() ? QUuid::createUuid().toString() : uuid); bId.replace(QRegularExpression("[{}-]"), ""); dock->setProperty("uuid", bId); dock->setObjectName(title + OBJ_NAME_SUFFIX); dock->resize(460, 600); dock->setMinimumSize(80, 80); dock->setWindowTitle(title); dock->setAllowedAreas(Qt::AllDockWidgetAreas); QCefWidget *browser = cef->create_widget(dock, QT_TO_UTF8(url), nullptr); if (browser && panel_version >= 1) browser->allowAllPopups(true); dock->SetWidget(browser); /* Add support for Twitch Dashboard panels */ if (url.contains("twitch.tv/popout") && url.contains("dashboard/live")) { QRegularExpression re("twitch.tv\\/popout\\/([^/]+)\\/"); QRegularExpressionMatch match = re.match(url); QString username = match.captured(1); if (username.length() > 0) { std::string script; script = "Object.defineProperty(document, 'referrer', { get: () => '"; script += "https://twitch.tv/"; script += QT_TO_UTF8(username); script += "/dashboard/live"; script += "'});"; browser->setStartupScript(script); } } AddDockWidget(dock, Qt::RightDockWidgetArea, true); extraBrowserDocks.push_back(std::shared_ptr(dock)); extraBrowserDockNames.push_back(title); extraBrowserDockTargets.push_back(url); if (firstCreate) { dock->setFloating(true); QPoint curPos = pos(); QSize wSizeD2 = size() / 2; QSize dSizeD2 = dock->size() / 2; curPos.setX(curPos.x() + wSizeD2.width() - dSizeD2.width()); curPos.setY(curPos.y() + wSizeD2.height() - dSizeD2.height()); dock->move(curPos); dock->setVisible(true); } } #endif static std::string GenId() { std::random_device rd; std::mt19937_64 e2(rd()); std::uniform_int_distribution dist(0, 0xFFFFFFFFFFFFFFFF); uint64_t id = dist(e2); char id_str[20]; snprintf(id_str, sizeof(id_str), "%016llX", (unsigned long long)id); return std::string(id_str); } void CheckExistingCookieId() { OBSBasic *main = OBSBasic::Get(); if (config_has_user_value(main->Config(), "Panels", "CookieId")) return; config_set_string(main->Config(), "Panels", "CookieId", GenId().c_str()); } #ifdef BROWSER_AVAILABLE static void InitPanelCookieManager() { if (!cef) return; if (panel_cookies) return; CheckExistingCookieId(); OBSBasic *main = OBSBasic::Get(); const char *cookie_id = config_get_string(main->Config(), "Panels", "CookieId"); std::string sub_path; sub_path += "obs_profile_cookies/"; sub_path += cookie_id; panel_cookies = cef->create_cookie_manager(sub_path); } #endif void DestroyPanelCookieManager() { #ifdef BROWSER_AVAILABLE if (panel_cookies) { panel_cookies->FlushStore(); delete panel_cookies; panel_cookies = nullptr; } #endif } void DeleteCookies() { #ifdef BROWSER_AVAILABLE if (panel_cookies) { panel_cookies->DeleteCookies("", ""); } #endif } void DuplicateCurrentCookieProfile(ConfigFile &config) { #ifdef BROWSER_AVAILABLE if (cef) { OBSBasic *main = OBSBasic::Get(); std::string cookie_id = config_get_string(main->Config(), "Panels", "CookieId"); std::string src_path; src_path += "obs_profile_cookies/"; src_path += cookie_id; std::string new_id = GenId(); std::string dst_path; dst_path += "obs_profile_cookies/"; dst_path += new_id; BPtr src_path_full = cef->get_cookie_path(src_path); BPtr dst_path_full = cef->get_cookie_path(dst_path); QDir srcDir(src_path_full.Get()); QDir dstDir(dst_path_full.Get()); if (srcDir.exists()) { if (!dstDir.exists()) dstDir.mkdir(dst_path_full.Get()); QStringList files = srcDir.entryList(QDir::Files); for (const QString &file : files) { QString src = QString(src_path_full); QString dst = QString(dst_path_full); src += QDir::separator() + file; dst += QDir::separator() + file; QFile::copy(src, dst); } } config_set_string(config, "Panels", "CookieId", cookie_id.c_str()); config_set_string(main->Config(), "Panels", "CookieId", new_id.c_str()); } #else UNUSED_PARAMETER(config); #endif } void OBSBasic::InitBrowserPanelSafeBlock() { #ifdef BROWSER_AVAILABLE if (!cef) return; if (cef->init_browser()) { InitPanelCookieManager(); return; } ExecThreadedWithoutBlocking([] { cef->wait_for_browser_init(); }, QTStr("BrowserPanelInit.Title"), QTStr("BrowserPanelInit.Text")); InitPanelCookieManager(); #endif }