diff --git a/config-dialog.cpp b/config-dialog.cpp index ee39e93..ae7e771 100644 --- a/config-dialog.cpp +++ b/config-dialog.cpp @@ -19,6 +19,9 @@ #include #include #include +#include +#include +#include #include "hotkey-edit.hpp" #include "obs-module.h" @@ -114,6 +117,86 @@ OBSBasicSettings::OBSBasicSettings(CanvasDock *canvas_dock, QMainWindow *parent) settingsPages->addWidget(supportPage); + // Vulcast Account page + listwidgetitem = new QListWidgetItem(listWidget); + listwidgetitem->setIcon(QIcon(QString::fromUtf8(":/vulcast/media/logo.png"))); + listwidgetitem->setText(QString::fromUtf8("Vulcast")); + + QWidget *accountPage = new QWidget; + scrollArea = new QScrollArea; + scrollArea->setWidget(accountPage); + scrollArea->setWidgetResizable(true); + scrollArea->setLineWidth(0); + scrollArea->setFrameShape(QFrame::NoFrame); + settingsPages->addWidget(scrollArea); + + networkManager = new QNetworkAccessManager(this); + + auto accountGroup = new QGroupBox; + accountGroup->setStyleSheet(QString("QGroupBox{ padding-top: 4px;}")); + auto accountLayout = new QFormLayout; + accountLayout->setContentsMargins(9, 2, 9, 9); + accountLayout->setFieldGrowthPolicy(QFormLayout::AllNonFixedFieldsGrow); + accountLayout->setLabelAlignment(Qt::AlignRight | Qt::AlignVCenter); + accountGroup->setLayout(accountLayout); + + auto account_title_layout = new QHBoxLayout; + auto account_title = new QLabel(QString::fromUtf8("Vulcast Account")); + account_title->setStyleSheet(QString::fromUtf8("font-weight: bold;")); + account_title_layout->addWidget(account_title, 0, Qt::AlignLeft); + auto dashboard_link = new QLabel(QString::fromUtf8("") + + QString::fromUtf8("Get API Key") + QString::fromUtf8("")); + dashboard_link->setOpenExternalLinks(true); + account_title_layout->addWidget(dashboard_link, 0, Qt::AlignRight); + accountLayout->addRow(account_title_layout); + + auto apiInfoLabel = new QLabel(QString::fromUtf8("Enter your Vulcast API key to see linked platforms and stream settings. Generate one at vulcast.xyz/dashboard/settings.")); + apiInfoLabel->setWordWrap(true); + apiInfoLabel->setStyleSheet("color: #999; font-size: 12px;"); + accountLayout->addRow(apiInfoLabel); + + apiKeyInput = new QLineEdit; + apiKeyInput->setEchoMode(QLineEdit::Password); + apiKeyInput->setPlaceholderText(QString::fromUtf8("vcast_...")); + + QPushButton *showApiKey = new QPushButton(QString::fromUtf8("Show")); + showApiKey->setCheckable(true); + connect(showApiKey, &QAbstractButton::toggled, [showApiKey, this](bool checked) { + showApiKey->setText(checked ? QString::fromUtf8("Hide") : QString::fromUtf8("Show")); + apiKeyInput->setEchoMode(checked ? QLineEdit::Normal : QLineEdit::Password); + }); + + auto apiKeyLayout = new QHBoxLayout; + apiKeyLayout->addWidget(apiKeyInput); + apiKeyLayout->addWidget(showApiKey); + accountLayout->addRow(QString::fromUtf8("API Key"), apiKeyLayout); + + auto connectButton = new QPushButton(QString::fromUtf8("Connect")); + connect(connectButton, &QPushButton::clicked, [this] { FetchPlatformStatus(); }); + accountLayout->addRow(connectButton); + + apiStatus = new QLabel; + apiStatus->setStyleSheet("font-size: 12px;"); + apiStatus->setVisible(false); + accountLayout->addRow(apiStatus); + + auto platformGroup = new QGroupBox(QString::fromUtf8("Linked Platforms")); + platformGroup->setStyleSheet(QString("QGroupBox{ padding-top: 4px;}")); + auto platformLayout = new QVBoxLayout; + platformLayout->setContentsMargins(9, 2, 9, 9); + platformGroup->setLayout(platformLayout); + + platformList = new QListWidget; + platformList->setMinimumHeight(150); + platformList->setStyleSheet("QListWidget { border: 1px solid #333; }"); + platformLayout->addWidget(platformList); + + auto accountPageLayout = new QVBoxLayout; + accountPageLayout->addWidget(accountGroup); + accountPageLayout->addWidget(platformGroup); + accountPageLayout->addStretch(); + accountPage->setLayout(accountPageLayout); + connect(listWidget, &QListWidget::currentRowChanged, settingsPages, &QStackedWidget::setCurrentIndex); auto generalGroup = new QGroupBox; @@ -1223,6 +1306,13 @@ void OBSBasicSettings::LoadSettings() for (const auto &kv : record_encoder_property_widgets) { LoadProperty(kv.first, canvasDock->record_encoder_settings, kv.second); } + + // Load API key + auto profile_config = obs_frontend_get_profile_config(); + auto saved_key = config_get_string(profile_config, "VulcastVertical", "ApiKey"); + if (saved_key && strlen(saved_key) > 0) { + apiKeyInput->setText(QString::fromUtf8(saved_key)); + } } void OBSBasicSettings::SaveSettings() @@ -1451,6 +1541,107 @@ void OBSBasicSettings::SaveSettings() } obs_data_apply(canvasDock->record_encoder_settings, record_encoder_settings); + + // Save API key + auto profile_config = obs_frontend_get_profile_config(); + auto apiKey = apiKeyInput->text().toUtf8(); + config_set_string(profile_config, "VulcastVertical", "ApiKey", apiKey.constData()); + config_save(profile_config); +} + +void OBSBasicSettings::FetchPlatformStatus() +{ + auto apiKey = apiKeyInput->text().trimmed(); + if (apiKey.isEmpty()) { + apiStatus->setText(QString::fromUtf8("Please enter an API key.")); + apiStatus->setStyleSheet("color: #f66; font-size: 12px;"); + apiStatus->setVisible(true); + return; + } + + apiStatus->setText(QString::fromUtf8("Connecting...")); + apiStatus->setStyleSheet("color: #ff0; font-size: 12px;"); + apiStatus->setVisible(true); + platformList->clear(); + + QNetworkRequest request(QUrl(QString::fromUtf8("https://vulcast.xyz/api/plugin/status"))); + request.setRawHeader("Authorization", QString("Bearer %1").arg(apiKey).toUtf8()); + request.setRawHeader("Content-Type", "application/json"); + + QNetworkReply *reply = networkManager->get(request); + connect(reply, &QNetworkReply::finished, [this, reply] { + reply->deleteLater(); + + if (reply->error() != QNetworkReply::NoError) { + int statusCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); + if (statusCode == 401) { + apiStatus->setText(QString::fromUtf8("Invalid API key. Generate one at vulcast.xyz/dashboard/settings")); + } else { + apiStatus->setText(QString::fromUtf8("Connection failed: %1").arg(reply->errorString())); + } + apiStatus->setStyleSheet("color: #f66; font-size: 12px;"); + return; + } + + auto doc = QJsonDocument::fromJson(reply->readAll()); + auto root = doc.object(); + + // Show user info + auto user = root["user"].toObject(); + apiStatus->setText(QString::fromUtf8("Connected as %1").arg(user["email"].toString())); + apiStatus->setStyleSheet("color: #6f6; font-size: 12px;"); + + // Populate platform list + auto platforms = root["platforms"].toArray(); + platformList->clear(); + + for (auto p : platforms) { + auto platform = p.toObject(); + auto name = platform["platform"].toString(); + auto label = platform["label"].toString(); + auto connected = platform["connected"].toBool(); + auto hasKey = platform["hasStreamKey"].toBool(); + auto destinations = platform["destinations"].toArray(); + + QString icon = connected ? (hasKey ? QString::fromUtf8("✅") : QString::fromUtf8("⚠️")) : QString::fromUtf8("🔗"); + QString statusText = connected ? (hasKey ? "Ready" : "No stream key") : "Manual"; + auto item = new QListWidgetItem(QString("%1 %2 — %3 (%4)").arg(icon, name, label, statusText)); + platformList->addItem(item); + + // Show destinations (horizontal/vertical) + for (auto d : destinations) { + auto dest = d.toObject(); + auto orientation = dest["orientation"].toString(); + auto destLabel = dest["label"].toString(); + auto enabled = dest["enabled"].toBool(); + QString orientIcon = orientation == "vertical" ? QString::fromUtf8("📱") : QString::fromUtf8("🖥️"); + QString enIcon = enabled ? QString::fromUtf8(" ✓") : QString::fromUtf8(" ✗"); + auto subItem = new QListWidgetItem(QString(" %1 %2 %3").arg(orientIcon, destLabel, enIcon)); + subItem->setForeground(QColor("#999")); + platformList->addItem(subItem); + } + } + + if (platforms.isEmpty()) { + auto item = new QListWidgetItem(QString::fromUtf8("No platforms linked yet. Go to vulcast.xyz/dashboard/destinations")); + item->setForeground(QColor("#999")); + platformList->addItem(item); + } + + // Show streams + auto streams = root["streams"].toArray(); + if (!streams.isEmpty()) { + platformList->addItem(new QListWidgetItem(QString::fromUtf8(""))); + platformList->addItem(new QListWidgetItem(QString::fromUtf8("📹 Streams:"))); + for (auto s : streams) { + auto stream = s.toObject(); + auto status = stream["status"].toString(); + QString statusIcon = status == "live" ? QString::fromUtf8("🔴") : QString::fromUtf8("⚪"); + auto item = new QListWidgetItem(QString(" %1 %2 (%3)").arg(statusIcon, stream["name"].toString(), status)); + platformList->addItem(item); + } + } + }); } void OBSBasicSettings::SetEncoderBitrate(obs_encoder_t *encoder, bool record) diff --git a/config-dialog.hpp b/config-dialog.hpp index 97f8678..8ac0dbf 100644 --- a/config-dialog.hpp +++ b/config-dialog.hpp @@ -11,6 +11,8 @@ #include #include #include +#include +#include #include "hotkey-edit.hpp" @@ -78,6 +80,12 @@ private: std::vector hotkeys; + // Vulcast Account + QLineEdit *apiKeyInput; + QLabel *apiStatus; + QListWidget *platformList; + QNetworkAccessManager *networkManager; + QIcon GetGeneralIcon() const; QIcon GetAppearanceIcon() const; QIcon GetStreamIcon() const; @@ -117,5 +125,6 @@ public: void LoadSettings(); void SaveSettings(); + void FetchPlatformStatus(); public slots: };