浏览代码

UI: Rework YouTube broadcast setup flow

derrod 4 年之前
父节点
当前提交
ca3f244584

+ 4 - 0
UI/auth-base.cpp

@@ -61,7 +61,11 @@ void Auth::Load()
 	if (main->auth) {
 		if (main->auth->LoadInternal()) {
 			main->auth->LoadUI();
+			main->SetBroadcastFlowEnabled(
+				main->auth->broadcastFlow());
 		}
+	} else {
+		main->SetBroadcastFlowEnabled(false);
 	}
 }
 

+ 2 - 0
UI/auth-base.hpp

@@ -34,6 +34,7 @@ public:
 		std::string service;
 		Type type;
 		bool externalOAuth;
+		bool usesBroadcastFlow;
 	};
 
 	typedef std::function<std::shared_ptr<Auth>()> create_cb;
@@ -44,6 +45,7 @@ public:
 	inline Type type() const { return def.type; }
 	inline const char *service() const { return def.service.c_str(); }
 	inline bool external() const { return def.externalOAuth; }
+	inline bool broadcastFlow() const { return def.usesBroadcastFlow; }
 
 	virtual void LoadUI() {}
 

+ 3 - 3
UI/auth-youtube.hpp

@@ -12,9 +12,9 @@ class BrowserDock;
 #endif
 
 inline const std::vector<Auth::Def> youtubeServices = {
-	{"YouTube - RTMP", Auth::Type::OAuth_LinkedAccount, true},
-	{"YouTube - RTMPS", Auth::Type::OAuth_LinkedAccount, true},
-	{"YouTube - HLS", Auth::Type::OAuth_LinkedAccount, true}};
+	{"YouTube - RTMP", Auth::Type::OAuth_LinkedAccount, true, true},
+	{"YouTube - RTMPS", Auth::Type::OAuth_LinkedAccount, true, true},
+	{"YouTube - HLS", Auth::Type::OAuth_LinkedAccount, true, true}};
 
 class YoutubeAuth : public OAuthStreamKey {
 	Q_OBJECT

+ 15 - 6
UI/data/locale/en-US.ini

@@ -391,6 +391,10 @@ Output.RecordError.EncodeErrorMsg="An encoder error occurred while recording."
 Output.BadPath.Title="Bad File Path"
 Output.BadPath.Text="The configured file output path is invalid. Please check your settings to confirm that a valid file path has been set."
 
+# broadcast setup messages
+Output.NoBroadcast.Title="No Broadcast Configured"
+Output.NoBroadcast.Text="You need to set up a broadcast before you can start streaming."
+
 # log upload dialog text and messages
 LogReturnDialog="Log Upload Successful"
 LogReturnDialog.Description="Your log file has been uploaded. You can now share the URL for debugging or support purposes."
@@ -642,8 +646,10 @@ Basic.Main.UnpauseRecording="Unpause Recording"
 Basic.Main.StoppingRecording="Stopping Recording..."
 Basic.Main.StopReplayBuffer="Stop Replay Buffer"
 Basic.Main.StoppingReplayBuffer="Stopping Replay Buffer..."
+Basic.Main.SetupBroadcast="Manage Broadcast"
 Basic.Main.StopStreaming="Stop Streaming"
 Basic.Main.StopBroadcast="End Broadcast"
+Basic.Main.AutoStopEnabled="(Auto Stop)"
 Basic.Main.StoppingStreaming="Stopping Stream..."
 Basic.Main.ForceStopStreaming="Stop Streaming (discard delay)"
 Basic.Main.ShowContextBar="Show Source Toolbar"
@@ -1187,7 +1193,8 @@ YouTube.Auth.WaitingAuth.Title="YouTube User Authorization"
 YouTube.Auth.WaitingAuth.Text="Please complete the authorization in your external browser.<br>If the external browser does not open, follow this link and complete the authorization:<br>%1"
 YouTube.AuthError.Text="Failed to get channel information: %1."
 
-YouTube.Actions.CreateNewEvent="Create New Event"
+YouTube.Actions.CreateNewEvent="Create New Broadcast"
+YouTube.Actions.ChooseEvent="Select Existing Broadcast"
 YouTube.Actions.Title="Title*"
 YouTube.Actions.MyBroadcast="My Broadcast"
 YouTube.Actions.Description="Description"
@@ -1213,10 +1220,13 @@ YouTube.Actions.EnableDVR="Enable DVR"
 YouTube.Actions.360Video="360 video"
 YouTube.Actions.360Video.Help="<a href='https://vr.youtube.com/create/360/'>(?)</a>"
 YouTube.Actions.ScheduleForLater="Schedule for later"
-YouTube.Actions.Create_GoLive="Create event and go live"
-YouTube.Actions.Choose_GoLive="Go live with selected event"
-YouTube.Actions.Create_Save="Schedule event with above settings"
-YouTube.Actions.Dashboard="YouTube Studio..."
+YouTube.Actions.Create_Ready="Create broadcast"
+YouTube.Actions.Create_GoLive="Create broadcast and start streaming"
+YouTube.Actions.Choose_Ready="Select broadcast"
+YouTube.Actions.Choose_GoLive="Select broadcast and start streaming"
+YouTube.Actions.Create_Schedule="Schedule broadcast"
+YouTube.Actions.Create_Schedule_Ready="Schedule and select broadcast"
+YouTube.Actions.Dashboard="Open YouTube Studio"
 
 YouTube.Actions.Error.Title="Live broadcast creation error"
 YouTube.Actions.Error.Text="YouTube access error '%1'.<br/>A detailed error description can be found at <a href='https://developers.google.com/youtube/v3/live/docs/errors'>https://developers.google.com/youtube/v3/live/docs/errors</a>"
@@ -1230,7 +1240,6 @@ YouTube.Actions.EventsLoading="Loading list of events..."
 YouTube.Actions.EventCreated.Title="Event Created"
 YouTube.Actions.EventCreated.Text="Event created successfully."
 
-YouTube.Actions.ChooseEvent="Select Existing Event"
 YouTube.Actions.Stream="Stream"
 YouTube.Actions.Stream.ScheduledFor="Scheduled for %1"
 YouTube.Actions.Stream.Resume="Resume interrupted stream"

+ 10 - 3
UI/forms/OBSYoutubeActions.ui

@@ -6,7 +6,7 @@
    <rect>
     <x>0</x>
     <y>0</y>
-    <width>583</width>
+    <width>584</width>
     <height>510</height>
    </rect>
   </property>
@@ -480,7 +480,7 @@
            <rect>
             <x>0</x>
             <y>0</y>
-            <width>522</width>
+            <width>179</width>
             <height>192</height>
            </rect>
           </property>
@@ -576,7 +576,7 @@
     </widget>
    </item>
    <item>
-    <layout class="QHBoxLayout" name="horizontalLayout_2" stretch="1,2,6">
+    <layout class="QHBoxLayout" name="horizontalLayout_2" stretch="1,2,0,6">
      <item>
       <widget class="QPushButton" name="cancelButton">
        <property name="text">
@@ -591,6 +591,13 @@
        </property>
       </widget>
      </item>
+     <item>
+      <widget class="QPushButton" name="saveButton">
+       <property name="text">
+        <string>YouTube.Actions.Create_Ready</string>
+       </property>
+      </widget>
+     </item>
      <item>
       <widget class="QPushButton" name="okButton">
        <property name="text">

+ 5 - 1
UI/window-basic-auto-config.cpp

@@ -1083,8 +1083,12 @@ void AutoConfig::SaveStreamSettings()
 	main->SetService(newService);
 	main->SaveService();
 	main->auth = streamPage->auth;
-	if (!!main->auth)
+	if (!!main->auth) {
 		main->auth->LoadUI();
+		main->SetBroadcastFlowEnabled(main->auth->broadcastFlow());
+	} else {
+		main->SetBroadcastFlowEnabled(false);
+	}
 
 	/* ---------------------------------- */
 	/* save stream settings               */

+ 94 - 42
UI/window-basic-main.cpp

@@ -6155,7 +6155,8 @@ void OBSBasic::DisplayStreamStartError()
 
 #if YOUTUBE_ENABLED
 void OBSBasic::YouTubeActionDialogOk(const QString &id, const QString &key,
-				     bool autostart, bool autostop)
+				     bool autostart, bool autostop,
+				     bool start_now)
 {
 	//blog(LOG_DEBUG, "Stream key: %s", QT_TO_UTF8(key));
 	obs_service_t *service_obj = GetService();
@@ -6170,8 +6171,12 @@ void OBSBasic::YouTubeActionDialogOk(const QString &id, const QString &key,
 	obs_service_update(service_obj, settings);
 	autoStartBroadcast = autostart;
 	autoStopBroadcast = autostop;
+	broadcastReady = true;
 
 	obs_data_release(settings);
+
+	if (start_now)
+		QMetaObject::invokeMethod(this, "StartStreaming");
 }
 
 void OBSBasic::YoutubeStreamCheck(const std::string &key)
@@ -6264,33 +6269,28 @@ void OBSBasic::StartStreaming()
 	if (disableOutputsRef)
 		return;
 
-	Auth *auth = GetAuth();
-	if (auth) {
-		auth->OnStreamConfig();
-#if YOUTUBE_ENABLED
-		if (!broadcastActive && autoStartBroadcast &&
-		    IsYouTubeService(auth->service())) {
-			OBSYoutubeActions *dialog;
-			dialog = new OBSYoutubeActions(this, auth);
-			connect(dialog, &OBSYoutubeActions::ok, this,
-				&OBSBasic::YouTubeActionDialogOk);
-			int result = dialog->Valid() ? dialog->exec()
-						     : QDialog::Rejected;
-			if (result != QDialog::Accepted) {
-				ui->streamButton->setText(
-					QTStr("Basic.Main.StartStreaming"));
-				ui->streamButton->setEnabled(true);
-				ui->streamButton->setChecked(false);
+	if (auth && auth->broadcastFlow()) {
+		if (!broadcastActive && !broadcastReady) {
+			ui->streamButton->setChecked(false);
 
-				if (sysTrayStream) {
-					sysTrayStream->setText(
-						ui->streamButton->text());
-					sysTrayStream->setEnabled(true);
-				}
-				return;
-			}
+			QMessageBox no_broadcast(this);
+			no_broadcast.setText(QTStr("Output.NoBroadcast.Text"));
+			QPushButton *SetupBroadcast = no_broadcast.addButton(
+				QTStr("Basic.Main.SetupBroadcast"),
+				QMessageBox::YesRole);
+			no_broadcast.setDefaultButton(SetupBroadcast);
+			no_broadcast.addButton(QTStr("Close"),
+					       QMessageBox::NoRole);
+			no_broadcast.setIcon(QMessageBox::Information);
+			no_broadcast.setWindowTitle(
+				QTStr("Output.NoBroadcast.Title"));
+			no_broadcast.exec();
+
+			if (no_broadcast.clickedButton() == SetupBroadcast)
+				QMetaObject::invokeMethod(this,
+							  "SetupBroadcast");
+			return;
 		}
-#endif
 	}
 
 	if (!outputHandler->SetupStreaming(service)) {
@@ -6306,6 +6306,7 @@ void OBSBasic::StartStreaming()
 	ui->streamButton->setEnabled(false);
 	ui->streamButton->setChecked(false);
 	ui->streamButton->setText(QTStr("Basic.Main.Connecting"));
+	ui->broadcastButton->setChecked(false);
 
 	if (sysTrayStream) {
 		sysTrayStream->setEnabled(false);
@@ -6318,7 +6319,6 @@ void OBSBasic::StartStreaming()
 	}
 
 	if (!autoStartBroadcast) {
-		ui->broadcastButton->setVisible(true);
 		ui->broadcastButton->setText(
 			QTStr("Basic.Main.StartBroadcast"));
 		ui->broadcastButton->setProperty("broadcastState", "ready");
@@ -6328,11 +6328,12 @@ void OBSBasic::StartStreaming()
 		ui->broadcastButton->setEnabled(false);
 	} else if (!autoStopBroadcast) {
 		broadcastActive = true;
-		ui->broadcastButton->setVisible(true);
 		ui->broadcastButton->setText(QTStr("Basic.Main.StopBroadcast"));
 		ui->broadcastButton->setProperty("broadcastState", "active");
 		ui->broadcastButton->style()->unpolish(ui->broadcastButton);
 		ui->broadcastButton->style()->polish(ui->broadcastButton);
+	} else {
+		ui->broadcastButton->setEnabled(false);
 	}
 
 	bool recordWhenStreaming = config_get_bool(
@@ -6353,6 +6354,14 @@ void OBSBasic::StartStreaming()
 
 void OBSBasic::BroadcastButtonClicked()
 {
+	if (!broadcastReady ||
+	    !broadcastActive && !outputHandler->StreamingActive()) {
+		SetupBroadcast();
+		if (broadcastReady)
+			ui->broadcastButton->setChecked(true);
+		return;
+	}
+
 	if (!autoStartBroadcast) {
 #if YOUTUBE_ENABLED
 		std::shared_ptr<YoutubeApiWrappers> ytAuth =
@@ -6362,20 +6371,20 @@ void OBSBasic::BroadcastButtonClicked()
 		}
 #endif
 		broadcastActive = true;
-
 		autoStartBroadcast = true; // and clear the flag
+
 		if (!autoStopBroadcast) {
 			ui->broadcastButton->setText(
 				QTStr("Basic.Main.StopBroadcast"));
-			ui->broadcastButton->setProperty("broadcastState",
-							 "active");
-			ui->broadcastButton->style()->unpolish(
-				ui->broadcastButton);
-			ui->broadcastButton->style()->polish(
-				ui->broadcastButton);
 		} else {
-			ui->broadcastButton->setVisible(false);
+			ui->broadcastButton->setText(
+				QTStr("Basic.Main.AutoStopEnabled"));
+			ui->broadcastButton->setEnabled(false);
 		}
+
+		ui->broadcastButton->setProperty("broadcastState", "active");
+		ui->broadcastButton->style()->unpolish(ui->broadcastButton);
+		ui->broadcastButton->style()->polish(ui->broadcastButton);
 	} else if (!autoStopBroadcast) {
 #if YOUTUBE_ENABLED
 		bool confirm = config_get_bool(GetGlobalConfig(), "BasicWindow",
@@ -6388,6 +6397,7 @@ void OBSBasic::BroadcastButtonClicked()
 				QMessageBox::No);
 
 			if (button == QMessageBox::No) {
+				ui->broadcastButton->setChecked(true);
 				return;
 			}
 		}
@@ -6399,12 +6409,42 @@ void OBSBasic::BroadcastButtonClicked()
 		}
 #endif
 		broadcastActive = false;
+		broadcastReady = false;
 
 		autoStopBroadcast = true;
-		ui->broadcastButton->setVisible(false);
-
 		QMetaObject::invokeMethod(this, "StopStreaming");
+		SetBroadcastFlowEnabled(true);
+	}
+}
+
+void OBSBasic::SetBroadcastFlowEnabled(bool enabled)
+{
+	ui->broadcastButton->setEnabled(enabled);
+	ui->broadcastButton->setVisible(enabled);
+	ui->broadcastButton->setChecked(broadcastReady);
+	ui->broadcastButton->setProperty("broadcastState", "idle");
+	ui->broadcastButton->style()->unpolish(ui->broadcastButton);
+	ui->broadcastButton->style()->polish(ui->broadcastButton);
+	ui->broadcastButton->setText(QTStr("Basic.Main.SetupBroadcast"));
+}
+
+void OBSBasic::SetupBroadcast()
+{
+	Auth *auth = GetAuth();
+#if YOUTUBE_ENABLED
+	if (IsYouTubeService(auth->service())) {
+		OBSYoutubeActions *dialog;
+		dialog = new OBSYoutubeActions(this, auth, broadcastReady);
+		connect(dialog, &OBSYoutubeActions::ok, this,
+			&OBSBasic::YouTubeActionDialogOk);
+		int result = dialog->Valid() ? dialog->exec()
+					     : QDialog::Rejected;
+		if (result != QDialog::Accepted) {
+			if (!broadcastReady)
+				ui->broadcastButton->setChecked(false);
+		}
 	}
+#endif
 }
 
 #ifdef _WIN32
@@ -6519,11 +6559,13 @@ void OBSBasic::StopStreaming()
 		broadcastActive = false;
 		autoStartBroadcast = true;
 		autoStopBroadcast = true;
-		ui->broadcastButton->setVisible(false);
+		broadcastReady = false;
 	}
 
-	if (autoStopBroadcast)
+	if (autoStopBroadcast) {
 		broadcastActive = false;
+		broadcastReady = false;
+	}
 
 	OnDeactivate();
 
@@ -6557,11 +6599,13 @@ void OBSBasic::ForceStopStreaming()
 		broadcastActive = false;
 		autoStartBroadcast = true;
 		autoStopBroadcast = true;
-		ui->broadcastButton->setVisible(false);
+		broadcastReady = false;
 	}
 
-	if (autoStopBroadcast)
+	if (autoStopBroadcast) {
 		broadcastActive = false;
+		broadcastReady = false;
+	}
 
 	OnDeactivate();
 
@@ -6766,6 +6810,10 @@ void OBSBasic::StreamingStop(int code, QString last_error)
 		startStreamMenu->deleteLater();
 		startStreamMenu = nullptr;
 	}
+
+	// Reset broadcast button state/text
+	if (!broadcastActive)
+		SetBroadcastFlowEnabled(auth && auth->broadcastFlow());
 }
 
 void OBSBasic::AutoRemux(QString input)
@@ -7281,6 +7329,10 @@ void OBSBasic::on_streamButton_clicked()
 				obs_service_get_settings(service);
 			bwtest = obs_data_get_bool(settings, "bwtest");
 			obs_data_release(settings);
+			// Disable confirmation if this is going to open broadcast setup
+			if (auth && auth->broadcastFlow() && !broadcastReady &&
+			    !broadcastActive)
+				confirm = false;
 		}
 
 		if (bwtest && isVisible()) {

+ 6 - 1
UI/window-basic-main.hpp

@@ -567,14 +567,17 @@ private:
 	bool autoStartBroadcast = true;
 	bool autoStopBroadcast = true;
 	bool broadcastActive = false;
+	bool broadcastReady = false;
 	QPointer<QThread> youtubeStreamCheckThread;
 #if YOUTUBE_ENABLED
 	void YoutubeStreamCheck(const std::string &key);
 	void ShowYouTubeAutoStartWarning();
 	void YouTubeActionDialogOk(const QString &id, const QString &key,
-				   bool autostart, bool autostop);
+				   bool autostart, bool autostop,
+				   bool start_now);
 #endif
 	void BroadcastButtonClicked();
+	void SetBroadcastFlowEnabled(bool enabled);
 
 	void UpdatePreviewSafeAreas();
 	bool drawSafeAreas = false;
@@ -585,6 +588,8 @@ public slots:
 
 	void DisplayStreamStartError();
 
+	void SetupBroadcast();
+
 	void StartStreaming();
 	void StopStreaming();
 	void ForceStopStreaming();

+ 6 - 1
UI/window-basic-settings-stream.cpp

@@ -243,8 +243,12 @@ void OBSBasicSettings::SaveStream1Settings()
 	main->SetService(newService);
 	main->SaveService();
 	main->auth = auth;
-	if (!!main->auth)
+	if (!!main->auth) {
 		main->auth->LoadUI();
+		main->SetBroadcastFlowEnabled(main->auth->broadcastFlow());
+	} else {
+		main->SetBroadcastFlowEnabled(false);
+	}
 
 	SaveCheckBox(ui->ignoreRecommended, "Stream1", "IgnoreRecommended");
 }
@@ -667,6 +671,7 @@ void OBSBasicSettings::on_disconnectAccount_clicked()
 
 	main->auth.reset();
 	auth.reset();
+	main->SetBroadcastFlowEnabled(false);
 
 	std::string service = QT_TO_UTF8(ui->service->currentText());
 

+ 116 - 78
UI/window-youtube-actions.cpp

@@ -13,11 +13,13 @@ const QString SchedulDateAndTimeFormat = "yyyy-MM-dd'T'hh:mm:ss'Z'";
 const QString RepresentSchedulDateAndTimeFormat = "dddd, MMMM d, yyyy h:m";
 const QString IndexOfGamingCategory = "20";
 
-OBSYoutubeActions::OBSYoutubeActions(QWidget *parent, Auth *auth)
+OBSYoutubeActions::OBSYoutubeActions(QWidget *parent, Auth *auth,
+				     bool broadcastReady)
 	: QDialog(parent),
 	  ui(new Ui::OBSYoutubeActions),
 	  apiYouTube(dynamic_cast<YoutubeApiWrappers *>(auth)),
-	  workerThread(new WorkerThread(apiYouTube))
+	  workerThread(new WorkerThread(apiYouTube)),
+	  broadcastReady(broadcastReady)
 {
 	setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
 	ui->setupUi(this);
@@ -128,6 +130,8 @@ OBSYoutubeActions::OBSYoutubeActions(QWidget *parent, Auth *auth)
 
 	connect(ui->okButton, &QPushButton::clicked, this,
 		&OBSYoutubeActions::InitBroadcast);
+	connect(ui->saveButton, &QPushButton::clicked, this,
+		&OBSYoutubeActions::ReadyBroadcast);
 	connect(ui->cancelButton, &QPushButton::clicked, this, [&]() {
 		blog(LOG_DEBUG, "YouTube live broadcast creation cancelled.");
 		// Close the dialog.
@@ -249,9 +253,9 @@ void WorkerThread::run()
 		return;
 	json11::Json broadcasts;
 
-	for (QString broacastStatus : {"active", "upcoming"}) {
+	for (QString broadcastStatus : {"active", "upcoming"}) {
 		if (!apiYouTube->GetBroadcastsList(broadcasts, "",
-						   broacastStatus)) {
+						   broadcastStatus)) {
 			emit failed();
 			return;
 		}
@@ -290,11 +294,9 @@ void WorkerThread::run()
 
 				// Treat already started streams as autostart for UI purposes
 				bool astart =
-					status == "live"
-						? true
-						: item["contentDetails"]
-						      ["enableAutoStart"]
-							      .bool_value();
+					status == "live" ||
+					item["contentDetails"]["enableAutoStart"]
+						.bool_value();
 				bool astop =
 					item["contentDetails"]["enableAutoStop"]
 						.bool_value();
@@ -329,7 +331,7 @@ void WorkerThread::run()
 					    broadcasts,
 					    QString::fromStdString(
 						    nextPageToken),
-					    broacastStatus)) {
+					    broadcastStatus)) {
 					emit failed();
 					return;
 				}
@@ -342,80 +344,64 @@ void WorkerThread::run()
 
 void OBSYoutubeActions::UpdateOkButtonStatus()
 {
+	bool enable = false;
+
 	if (ui->tabWidget->currentIndex() == 0) {
-		ui->okButton->setEnabled(
-			!ui->title->text().isEmpty() &&
-			!ui->privacyBox->currentText().isEmpty() &&
-			(ui->yesMakeForKids->isChecked() ||
-			 ui->notMakeForKids->isChecked()));
+		enable = !ui->title->text().isEmpty() &&
+			 !ui->privacyBox->currentText().isEmpty() &&
+			 (ui->yesMakeForKids->isChecked() ||
+			  ui->notMakeForKids->isChecked());
+		ui->okButton->setEnabled(enable);
+		ui->saveButton->setEnabled(enable);
+
 		if (ui->checkScheduledLater->checkState() == Qt::Checked) {
 			ui->okButton->setText(
-				QTStr("YouTube.Actions.Create_Save"));
+				QTStr("YouTube.Actions.Create_Schedule"));
+			ui->saveButton->setText(
+				QTStr("YouTube.Actions.Create_Schedule_Ready"));
 		} else {
 			ui->okButton->setText(
 				QTStr("YouTube.Actions.Create_GoLive"));
+			ui->saveButton->setText(
+				QTStr("YouTube.Actions.Create_Ready"));
 		}
-
 		ui->pushButton->setVisible(false);
 	} else {
-		ui->okButton->setEnabled(!selectedBroadcast.isEmpty());
+		enable = !selectedBroadcast.isEmpty();
+		ui->okButton->setEnabled(enable);
+		ui->saveButton->setEnabled(enable);
 		ui->okButton->setText(QTStr("YouTube.Actions.Choose_GoLive"));
+		ui->saveButton->setText(QTStr("YouTube.Actions.Choose_Ready"));
 
 		ui->pushButton->setVisible(true);
 	}
 }
-
-bool OBSYoutubeActions::StreamNowAction(YoutubeApiWrappers *api,
-					StreamDescription &stream)
+bool OBSYoutubeActions::CreateEventAction(YoutubeApiWrappers *api,
+					  StreamDescription &stream,
+					  bool stream_later,
+					  bool ready_broadcast)
 {
 	YoutubeApiWrappers *apiYouTube = api;
 	BroadcastDescription broadcast = {};
 	UiToBroadcast(broadcast);
-	// stream now is always autostart/autostop
-	broadcast.auto_start = true;
-	broadcast.auto_stop = true;
 
-	blog(LOG_DEBUG, "Scheduled date and time: %s",
-	     broadcast.schedul_date_time.toStdString().c_str());
-	if (!apiYouTube->InsertBroadcast(broadcast)) {
-		blog(LOG_DEBUG, "No broadcast created.");
-		return false;
-	}
-	stream = {"", "", "OBS Studio Video Stream"};
-	if (!apiYouTube->InsertStream(stream)) {
-		blog(LOG_DEBUG, "No stream created.");
-		return false;
-	}
-	if (!apiYouTube->BindStream(broadcast.id, stream.id)) {
-		blog(LOG_DEBUG, "No stream binded.");
-		return false;
-	}
-	if (!apiYouTube->SetVideoCategory(broadcast.id, broadcast.title,
-					  broadcast.description,
-					  broadcast.category.id)) {
-		blog(LOG_DEBUG, "No category set.");
-		return false;
+	if (stream_later) {
+		// DateTime parser means that input datetime is a local, so we need to move it
+		auto dateTime = ui->scheduledTime->dateTime();
+		auto utcDTime = dateTime.addSecs(-dateTime.offsetFromUtc());
+		broadcast.schedul_date_time =
+			utcDTime.toString(SchedulDateAndTimeFormat);
+	} else {
+		// stream now is always autostart/autostop
+		broadcast.auto_start = true;
+		broadcast.auto_stop = true;
+		broadcast.schedul_date_time =
+			QDateTime::currentDateTimeUtc().toString(
+				SchedulDateAndTimeFormat);
 	}
 
-	if (broadcast.privacy != "private")
-		apiYouTube->SetChatId(broadcast.id);
-	else
-		apiYouTube->ResetChat();
-
-	return true;
-}
-
-bool OBSYoutubeActions::StreamLaterAction(YoutubeApiWrappers *api)
-{
-	YoutubeApiWrappers *apiYouTube = api;
-	BroadcastDescription broadcast = {};
-	UiToBroadcast(broadcast);
-
-	// DateTime parser means that input datetime is a local, so we need to move it
-	auto dateTime = ui->scheduledTime->dateTime();
-	auto utcDTime = dateTime.addSecs(-dateTime.offsetFromUtc());
-	broadcast.schedul_date_time =
-		utcDTime.toString(SchedulDateAndTimeFormat);
+	autostart = broadcast.auto_start;
+	autostop = broadcast.auto_stop;
 
 	blog(LOG_DEBUG, "Scheduled date and time: %s",
 	     broadcast.schedul_date_time.toStdString().c_str());
@@ -430,10 +416,22 @@ bool OBSYoutubeActions::StreamLaterAction(YoutubeApiWrappers *api)
 		return false;
 	}
 
-	if (broadcast.privacy != "private")
-		apiYouTube->SetChatId(broadcast.id);
-	else
-		apiYouTube->ResetChat();
+	if (!stream_later || ready_broadcast) {
+		stream = {"", "", "OBS Studio Video Stream"};
+		if (!apiYouTube->InsertStream(stream)) {
+			blog(LOG_DEBUG, "No stream created.");
+			return false;
+		}
+		if (!apiYouTube->BindStream(broadcast.id, stream.id)) {
+			blog(LOG_DEBUG, "No stream binded.");
+			return false;
+		}
+
+		if (broadcast.privacy != "private")
+			apiYouTube->SetChatId(broadcast.id);
+		else
+			apiYouTube->ResetChat();
+	}
 
 	return true;
 }
@@ -513,12 +511,9 @@ void OBSYoutubeActions::InitBroadcast()
 	bool success = false;
 	auto action = [&]() {
 		if (ui->tabWidget->currentIndex() == 0) {
-			if (ui->checkScheduledLater->isChecked()) {
-				success = this->StreamLaterAction(apiYouTube);
-			} else {
-				success = this->StreamNowAction(apiYouTube,
-								stream);
-			}
+			success = this->CreateEventAction(
+				apiYouTube, stream,
+				ui->checkScheduledLater->isChecked());
 		} else {
 			success = this->ChooseAnEventAction(apiYouTube, stream);
 		};
@@ -548,13 +543,14 @@ void OBSYoutubeActions::InitBroadcast()
 				blog(LOG_DEBUG, "New valid stream: %s",
 				     QT_TO_UTF8(stream.name));
 				emit ok(QT_TO_UTF8(stream.id),
-					QT_TO_UTF8(stream.name), true, true);
+					QT_TO_UTF8(stream.name), true, true,
+					true);
 				Accept();
 			}
 		} else {
 			// Stream to precreated broadcast usecase.
 			emit ok(QT_TO_UTF8(stream.id), QT_TO_UTF8(stream.name),
-				autostart, autostop);
+				autostart, autostop, true);
 			Accept();
 		}
 	} else {
@@ -571,6 +567,51 @@ void OBSYoutubeActions::InitBroadcast()
 	}
 }
 
+void OBSYoutubeActions::ReadyBroadcast()
+{
+	StreamDescription stream;
+	QMessageBox msgBox(this);
+	msgBox.setWindowFlags(msgBox.windowFlags() &
+			      ~Qt::WindowCloseButtonHint);
+	msgBox.setWindowTitle(QTStr("YouTube.Actions.Notify.Title"));
+	msgBox.setText(QTStr("YouTube.Actions.Notify.CreatingBroadcast"));
+	msgBox.setStandardButtons(QMessageBox::StandardButtons());
+
+	bool success = false;
+	auto action = [&]() {
+		if (ui->tabWidget->currentIndex() == 0) {
+			success = this->CreateEventAction(
+				apiYouTube, stream,
+				ui->checkScheduledLater->isChecked(), true);
+		} else {
+			success = this->ChooseAnEventAction(apiYouTube, stream);
+		};
+		QMetaObject::invokeMethod(&msgBox, "accept",
+					  Qt::QueuedConnection);
+	};
+	QScopedPointer<QThread> thread(CreateQThread(action));
+	thread->start();
+	msgBox.exec();
+	thread->wait();
+
+	if (success) {
+		emit ok(QT_TO_UTF8(stream.id), QT_TO_UTF8(stream.name),
+			autostart, autostop, false);
+		Accept();
+	} else {
+		// Fail.
+		auto last_error = apiYouTube->GetLastError();
+		if (last_error.isEmpty())
+			last_error = QTStr("YouTube.Actions.Error.YouTubeApi");
+		if (!apiYouTube->GetTranslatedError(last_error))
+			last_error =
+				QTStr("YouTube.Actions.Error.NoBroadcastCreated")
+					.arg(last_error);
+
+		ShowErrorDialog(this, last_error);
+	}
+}
+
 void OBSYoutubeActions::UiToBroadcast(BroadcastDescription &broadcast)
 {
 	broadcast.title = ui->title->text();
@@ -587,9 +628,6 @@ void OBSYoutubeActions::UiToBroadcast(BroadcastDescription &broadcast)
 	broadcast.schedul_for_later = ui->checkScheduledLater->isChecked();
 	broadcast.projection = ui->check360Video->isChecked() ? "360"
 							      : "rectangular";
-	// Current time by default.
-	broadcast.schedul_date_time = QDateTime::currentDateTimeUtc().toString(
-		SchedulDateAndTimeFormat);
 }
 
 void OBSYoutubeActions::OpenYouTubeDashboard()

+ 8 - 5
UI/window-youtube-actions.hpp

@@ -35,27 +35,29 @@ class OBSYoutubeActions : public QDialog {
 
 signals:
 	void ok(const QString &id, const QString &key, bool autostart,
-		bool autostop);
+		bool autostop, bool start_now);
 
 protected:
 	void UpdateOkButtonStatus();
 
-	bool StreamNowAction(YoutubeApiWrappers *api,
-			     StreamDescription &stream);
-	bool StreamLaterAction(YoutubeApiWrappers *api);
+	bool CreateEventAction(YoutubeApiWrappers *api,
+			       StreamDescription &stream, bool stream_later,
+			       bool ready_broadcast = false);
 	bool ChooseAnEventAction(YoutubeApiWrappers *api,
 				 StreamDescription &stream);
 
 	void ShowErrorDialog(QWidget *parent, QString text);
 
 public:
-	explicit OBSYoutubeActions(QWidget *parent, Auth *auth);
+	explicit OBSYoutubeActions(QWidget *parent, Auth *auth,
+				   bool broadcastReady);
 	virtual ~OBSYoutubeActions() override;
 
 	bool Valid() { return valid; };
 
 private:
 	void InitBroadcast();
+	void ReadyBroadcast();
 	void UiToBroadcast(BroadcastDescription &broadcast);
 	void OpenYouTubeDashboard();
 	void Cancel();
@@ -64,6 +66,7 @@ private:
 	QString selectedBroadcast;
 	bool autostart, autostop;
 	bool valid = false;
+	bool broadcastReady = false;
 	YoutubeApiWrappers *apiYouTube;
 	WorkerThread *workerThread;
 };