Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Restream.io: Manage Broadcast feature #11781

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 10 additions & 1 deletion frontend/cmake/feature-restream.cmake
Original file line number Diff line number Diff line change
@@ -1,5 +1,14 @@
if(RESTREAM_CLIENTID AND RESTREAM_HASH MATCHES "^(0|[a-fA-F0-9]+)$" AND TARGET OBS::browser-panels)
target_sources(obs-studio PRIVATE oauth/RestreamAuth.cpp oauth/RestreamAuth.hpp)
target_sources(
obs-studio
PRIVATE
oauth/RestreamAuth.cpp
oauth/RestreamAuth.hpp
forms/OBSRestreamActions.ui
dialogs/OBSRestreamActions.cpp
dialogs/OBSRestreamActions.hpp
)

target_enable_feature(obs-studio "Restream API connection" RESTREAM_ENABLED)
else()
target_disable_feature(obs-studio "Restream API connection")
Expand Down
2 changes: 2 additions & 0 deletions frontend/cmake/ui-widgets.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ target_sources(
widgets/ColorSelect.hpp
widgets/OBSBasic.cpp
widgets/OBSBasic.hpp
widgets/OBSBasic_Broadcast.cpp
widgets/OBSBasic_Browser.cpp
widgets/OBSBasic_Clipboard.cpp
widgets/OBSBasic_ContextToolbar.cpp
Expand All @@ -28,6 +29,7 @@ target_sources(
widgets/OBSBasic_Projectors.cpp
widgets/OBSBasic_Recording.cpp
widgets/OBSBasic_ReplayBuffer.cpp
widgets/OBSBasic_Restream.cpp
widgets/OBSBasic_SceneCollections.cpp
widgets/OBSBasic_SceneItems.cpp
widgets/OBSBasic_Scenes.cpp
Expand Down
13 changes: 13 additions & 0 deletions frontend/data/locale/en-US.ini
Original file line number Diff line number Diff line change
Expand Up @@ -1561,6 +1561,19 @@ YouTube.Errors.invalidTransition="The attempted transition was invalid. This may
YouTube.DocksRemoval.Title="Clear Legacy YouTube Browser Docks"
YouTube.DocksRemoval.Text="These browser docks will be removed as deprecated:\n\n%1\nUse \"Docks/YouTube Live Control Room\" instead."

# Restream Actions and Auth
Restream.Actions.WindowTitle="Restream Broadcast Setup"
Restream.Actions.BroadcastSelectTitle="Select Existing Broadcast"
Restream.Actions.DashboardButton="Open Restream";
Restream.Actions.BroadcastSelectButton="Select Broadcast"
Restream.Actions.BroadcastSelectAndStartButton="Select Broadcast and Start Streaming"
Restream.Actions.BroadcastScheduled="Scheduled"
Restream.Actions.BroadcastLoadingTitle="Loading broadcast information..."
Restream.Actions.BroadcastLoadingText="Loading broadcast information for %1, please wait..."
Restream.Actions.BroadcastLoadingFailureTitle="Failed to load broadcast information"
Restream.Actions.BroadcastLoadingFailureText="Failed to load broadcast information for %1\n\n%2: %3"
Restream.Actions.BroadcastLoadingEmptyText="Failed to access Restream setup.\nPlease select a broadcast to enable streaming."

# MultitrackVideo
ConfigDownload.WarningMessageTitle="Warning"
FailedToStartStream.MissingConfigURL="No config URL available for the current service"
Expand Down
177 changes: 177 additions & 0 deletions frontend/dialogs/OBSRestreamActions.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,177 @@
#include "OBSRestreamActions.hpp"

#include <widgets/OBSBasic.hpp>
#include <utility/OBSEventFilter.hpp>

#include <qt-wrappers.hpp>
#include <QToolTip>
#include <QDateTime>
#include <QDesktopServices>
#include <QFileInfo>
#include <QStandardPaths>
#include <QImageReader>

#include "moc_OBSRestreamActions.cpp"

OBSRestreamActions::OBSRestreamActions(QWidget *parent, Auth *auth, bool broadcastReady)
: QDialog(parent),
ui(new Ui::OBSRestreamActions),
restreamAuth(dynamic_cast<RestreamAuth *>(auth)),
broadcastReady(broadcastReady)
{
setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);

eventFilter = new OBSEventFilter([this](QObject *, QEvent *event) {
if (event->type() == QEvent::ApplicationActivate) {
auto events = this->restreamAuth->GetBroadcastInfo();
this->UpdateBroadcastList(events);
}
return false;
});

App()->installEventFilter(eventFilter);

ui->setupUi(this);
ui->okButton->setEnabled(false);
ui->saveButton->setEnabled(false);

connect(ui->okButton, &QPushButton::clicked, this, &OBSRestreamActions::BroadcastSelectAndStartAction);
connect(ui->saveButton, &QPushButton::clicked, this, &OBSRestreamActions::BroadcastSelectAction);
connect(ui->dashboardButton, &QPushButton::clicked, this, &OBSRestreamActions::OpenRestreamDashboard);
connect(ui->cancelButton, &QPushButton::clicked, this, [&]() {
blog(LOG_DEBUG, "Restream live event creation cancelled.");
reject();
});

qDeleteAll(ui->scrollAreaWidgetContents->findChildren<QWidget *>(QString(), Qt::FindDirectChildrenOnly));

auto events = restreamAuth->GetBroadcastInfo();
this->UpdateBroadcastList(events);
}

OBSRestreamActions::~OBSRestreamActions()
{
if (eventFilter)
App()->removeEventFilter(eventFilter);
}

void OBSRestreamActions::UpdateBroadcastList(QVector<RestreamEventDescription> &events)
{
if (events.isEmpty()) {
RestreamEventDescription event;
event.id = "";
event.title = "Live with Restream";
event.scheduledFor = 0;
event.showId = "";
events.push_back(event);
}

auto tryToFindShowId = selectedShowId;
if (tryToFindShowId.empty())
tryToFindShowId = restreamAuth->GetShowId();

selectedEventId = "";
selectedShowId = "";

qDeleteAll(ui->scrollAreaWidgetContents->findChildren<QWidget *>(QString(), Qt::FindDirectChildrenOnly));
EnableOkButton(false);

for (auto event : events) {
if (event.showId == tryToFindShowId) {
selectedEventId = event.id;
selectedShowId = event.showId;

EnableOkButton(true);
break;
}
}

if (selectedShowId.empty()) {
if (events.size()) {
auto event = events.at(0);
selectedEventId = event.id;
selectedShowId = event.showId;

restreamAuth->SelectShow(selectedEventId, selectedShowId);

EnableOkButton(true);
emit ok(false);
} else {
restreamAuth->ResetShow();
emit ok(false);
}
}

for (auto event : events) {
ClickableLabel *label = new ClickableLabel();
label->setTextFormat(Qt::RichText);
label->setAlignment(Qt::AlignHCenter);
label->setMargin(4);

QString scheduledForString;
if (event.scheduledFor > 0) {
QDateTime dateTime = QDateTime::fromSecsSinceEpoch(event.scheduledFor);
scheduledForString = QLocale().toString(
dateTime, QString("%1 %2").arg(QLocale().dateFormat(QLocale::LongFormat),
QLocale().timeFormat(QLocale::ShortFormat)));

label->setText(QString("<big>%1</big><br>%2: %3")
.arg(QString::fromStdString(event.title),
QTStr("Restream.Actions.BroadcastScheduled"), scheduledForString));
} else {
label->setText(
QString("<big>%1</big>%2").arg(QString::fromStdString(event.title), scheduledForString));
}

connect(label, &ClickableLabel::clicked, this, [&, label, event]() {
for (QWidget *i : ui->scrollAreaWidgetContents->findChildren<QWidget *>(
QString(), Qt::FindDirectChildrenOnly)) {

i->setProperty("class", "");
i->style()->unpolish(i);
i->style()->polish(i);
}
label->setProperty("class", "row-selected");
label->style()->unpolish(label);
label->style()->polish(label);

selectedEventId = event.id;
selectedShowId = event.showId;

EnableOkButton(true);
});

ui->scrollAreaWidgetContents->layout()->addWidget(label);

if (event.showId == selectedShowId) {
label->setProperty("class", "row-selected");
label->style()->unpolish(label);
label->style()->polish(label);
}
}
}

void OBSRestreamActions::EnableOkButton(bool state)
{
ui->okButton->setEnabled(state);
ui->saveButton->setEnabled(state);
}

void OBSRestreamActions::BroadcastSelectAction()
{
restreamAuth->SelectShow(selectedEventId, selectedShowId);
emit ok(false);
accept();
}

void OBSRestreamActions::BroadcastSelectAndStartAction()
{
restreamAuth->SelectShow(selectedEventId, selectedShowId);
emit ok(true);
accept();
}

void OBSRestreamActions::OpenRestreamDashboard()
{
QDesktopServices::openUrl(QString("https://app.restream.io/"));
}
46 changes: 46 additions & 0 deletions frontend/dialogs/OBSRestreamActions.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
#pragma once

#include <QDialog>
#include <QString>
#include <QThread>

#include "ui_OBSRestreamActions.h"
#include "oauth/RestreamAuth.hpp"

class OBSEventFilter;

class OBSRestreamActions : public QDialog {
Q_OBJECT
Q_PROPERTY(QIcon thumbPlaceholder READ GetPlaceholder WRITE SetPlaceholder DESIGNABLE true)

std::unique_ptr<Ui::OBSRestreamActions> ui;

signals:
void ok(bool start_now);

protected:
void EnableOkButton(bool state);

public:
explicit OBSRestreamActions(QWidget *parent, Auth *auth, bool broadcastReady);
virtual ~OBSRestreamActions() override;

bool Valid() { return valid; };

private:
void UpdateBroadcastList(QVector<RestreamEventDescription> &newEvents);
void BroadcastSelectAction();
void BroadcastSelectAndStartAction();
void OpenRestreamDashboard();

QIcon GetPlaceholder() { return thumbPlaceholder; }
void SetPlaceholder(const QIcon &icon) { thumbPlaceholder = icon; }

RestreamAuth *restreamAuth;
OBSEventFilter *eventFilter;
std::string selectedEventId;
std::string selectedShowId;
bool broadcastReady;
bool valid = false;
QIcon thumbPlaceholder;
};
Loading
Loading