Browse Source

Merge pull request #5411 from derrod/yt-fixes

More YouTube fixes
Jim 4 years ago
parent
commit
5a9d4d1b26
4 changed files with 91 additions and 28 deletions
  1. 7 0
      UI/data/locale/en-US.ini
  2. 44 7
      UI/window-basic-main.cpp
  3. 38 20
      UI/youtube-api-wrappers.cpp
  4. 2 1
      UI/youtube-api-wrappers.hpp

+ 7 - 0
UI/data/locale/en-US.ini

@@ -394,6 +394,8 @@ Output.BadPath.Text="The configured file output path is invalid. Please check yo
 # broadcast setup messages
 Output.NoBroadcast.Title="No Broadcast Configured"
 Output.NoBroadcast.Text="You need to set up a broadcast before you can start streaming."
+Output.BroadcastStartFailed="Failed to start broadcast"
+Output.BroadcastStopFailed="Failed to stop broadcast"
 
 # log upload dialog text and messages
 LogReturnDialog="Log Upload Successful"
@@ -1248,6 +1250,8 @@ YouTube.Actions.Error.BroadcastNotFound="The selected broadcast was not found."
 YouTube.Actions.Error.FileMissing="Selected file does not exist."
 YouTube.Actions.Error.FileOpeningFailed="Failed opening selected file."
 YouTube.Actions.Error.FileTooLarge="Selected file is too large (Limit: 2 MiB)."
+YouTube.Actions.Error.BroadcastTransitionFailed="Transitioning the broadcast failed: %1<br/><br/>If this error persists <a href='https://studio.youtube.com/video/%2/livestreaming'>open the broadcast in YouTube Studio</a> and try manually."
+YouTube.Actions.Error.BroadcastTestStarting="Broadcast is transitioning to the test stage, this can take some time. Please try again in 10-30 seconds."
 
 YouTube.Actions.EventsLoading="Loading list of events..."
 YouTube.Actions.EventCreated.Title="Event Created"
@@ -1268,3 +1272,6 @@ YouTube.Actions.AutoStopStreamingWarning="You will not be able to reconnect.<br>
 # YouTube API errors in format "YouTube.Errors.<error reason>"
 YouTube.Errors.liveStreamingNotEnabled="Live streaming is not enabled on the selected YouTube channel.<br/><br/>See <a href='https://www.youtube.com/features'>youtube.com/features</a> for more information."
 YouTube.Errors.livePermissionBlocked="Live streaming is unavailable on the selected YouTube Channel.<br/>Please note that it may take up to 24 hours for live streaming to become available after enabling it in your channel settings.<br/><br/>See <a href='https://www.youtube.com/features'>youtube.com/features</a> for details."
+YouTube.Errors.errorExecutingTransition="Transition failed due to a backend error. Please try again in a few seconds."
+YouTube.Errors.errorStreamInactive="YouTube is not receiving data for your stream. Please check your configuration and try again."
+YouTube.Errors.invalidTransition="The attempted transition was invalid. This may be due to the stream not having finished a previous transition. Please wait a few seconds and try again."

+ 44 - 7
UI/window-basic-main.cpp

@@ -6296,14 +6296,19 @@ void OBSBasic::StartStreaming()
 		ui->broadcastButton->style()->polish(ui->broadcastButton);
 		// well, we need to disable button while stream is not active
 		ui->broadcastButton->setEnabled(false);
-	} else if (!autoStopBroadcast) {
-		broadcastActive = true;
-		ui->broadcastButton->setText(QTStr("Basic.Main.StopBroadcast"));
+	} else {
+		if (!autoStopBroadcast) {
+			ui->broadcastButton->setText(
+				QTStr("Basic.Main.StopBroadcast"));
+		} else {
+			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 {
-		ui->broadcastButton->setEnabled(false);
+		broadcastActive = true;
 	}
 
 	bool recordWhenStreaming = config_get_bool(
@@ -6337,7 +6342,24 @@ void OBSBasic::BroadcastButtonClicked()
 		std::shared_ptr<YoutubeApiWrappers> ytAuth =
 			dynamic_pointer_cast<YoutubeApiWrappers>(auth);
 		if (ytAuth.get()) {
-			ytAuth->StartLatestBroadcast();
+			if (!ytAuth->StartLatestBroadcast()) {
+				auto last_error = ytAuth->GetLastError();
+				if (last_error.isEmpty())
+					last_error = QTStr(
+						"YouTube.Actions.Error.YouTubeApi");
+				if (!ytAuth->GetTranslatedError(last_error))
+					last_error =
+						QTStr("YouTube.Actions.Error.BroadcastTransitionFailed")
+							.arg(last_error,
+							     ytAuth->GetBroadcastId());
+
+				OBSMessageBox::warning(
+					this,
+					QTStr("Output.BroadcastStartFailed"),
+					last_error, true);
+				ui->broadcastButton->setChecked(false);
+				return;
+			}
 		}
 #endif
 		broadcastActive = true;
@@ -6375,7 +6397,22 @@ void OBSBasic::BroadcastButtonClicked()
 		std::shared_ptr<YoutubeApiWrappers> ytAuth =
 			dynamic_pointer_cast<YoutubeApiWrappers>(auth);
 		if (ytAuth.get()) {
-			ytAuth->StopLatestBroadcast();
+			if (!ytAuth->StopLatestBroadcast()) {
+				auto last_error = ytAuth->GetLastError();
+				if (last_error.isEmpty())
+					last_error = QTStr(
+						"YouTube.Actions.Error.YouTubeApi");
+				if (!ytAuth->GetTranslatedError(last_error))
+					last_error =
+						QTStr("YouTube.Actions.Error.BroadcastTransitionFailed")
+							.arg(last_error,
+							     ytAuth->GetBroadcastId());
+
+				OBSMessageBox::warning(
+					this,
+					QTStr("Output.BroadcastStopFailed"),
+					last_error, true);
+			}
 		}
 #endif
 		broadcastActive = false;

+ 38 - 20
UI/youtube-api-wrappers.cpp

@@ -405,7 +405,31 @@ bool YoutubeApiWrappers::StartBroadcast(const QString &broadcast_id)
 	lastErrorMessage.clear();
 	lastErrorReason.clear();
 
-	if (!ResetBroadcast(broadcast_id))
+	Json json_out;
+	if (!FindBroadcast(broadcast_id, json_out))
+		return false;
+
+	auto lifeCycleStatus =
+		json_out["items"][0]["status"]["lifeCycleStatus"].string_value();
+
+	if (lifeCycleStatus == "live" || lifeCycleStatus == "liveStarting")
+		// Broadcast is already (going to be) live
+		return true;
+	else if (lifeCycleStatus == "testStarting") {
+		// User will need to wait a few seconds before attempting to start broadcast
+		lastErrorMessage =
+			QTStr("YouTube.Actions.Error.BroadcastTestStarting");
+		lastErrorReason.clear();
+		return false;
+	}
+
+	// Only reset if broadcast has monitoring enabled and is not already in "testing" mode
+	auto monitorStreamEnabled =
+		json_out["items"][0]["contentDetails"]["monitorStream"]
+			["enableMonitorStream"]
+				.bool_value();
+	if (lifeCycleStatus != "testing" && monitorStreamEnabled &&
+	    !ResetBroadcast(broadcast_id, json_out))
 		return false;
 
 	const QString url_template = YOUTUBE_LIVE_BROADCAST_TRANSITION_URL
@@ -413,9 +437,10 @@ bool YoutubeApiWrappers::StartBroadcast(const QString &broadcast_id)
 		"&broadcastStatus=%2"
 		"&part=status";
 	const QString live = url_template.arg(broadcast_id, "live");
-	Json json_out;
-	return InsertCommand(QT_TO_UTF8(live), "application/json", "POST", "{}",
-			     json_out);
+	bool success = InsertCommand(QT_TO_UTF8(live), "application/json",
+				     "POST", "{}", json_out);
+	// Return a success if the command failed, but was redundant (broadcast already live)
+	return success || lastErrorReason == "redundantTransition";
 }
 
 bool YoutubeApiWrappers::StartLatestBroadcast()
@@ -434,8 +459,10 @@ bool YoutubeApiWrappers::StopBroadcast(const QString &broadcast_id)
 		"&part=status";
 	const QString url = url_template.arg(broadcast_id);
 	Json json_out;
-	return InsertCommand(QT_TO_UTF8(url), "application/json", "POST", "{}",
-			     json_out);
+	bool success = InsertCommand(QT_TO_UTF8(url), "application/json",
+				     "POST", "{}", json_out);
+	// Return a success if the command failed, but was redundant (broadcast already stopped)
+	return success || lastErrorReason == "redundantTransition";
 }
 
 bool YoutubeApiWrappers::StopLatestBroadcast()
@@ -453,24 +480,12 @@ QString YoutubeApiWrappers::GetBroadcastId()
 	return this->broadcast_id;
 }
 
-bool YoutubeApiWrappers::ResetBroadcast(const QString &broadcast_id)
+bool YoutubeApiWrappers::ResetBroadcast(const QString &broadcast_id,
+					json11::Json &json_out)
 {
 	lastErrorMessage.clear();
 	lastErrorReason.clear();
 
-	const QString url_template = YOUTUBE_LIVE_BROADCAST_URL
-		"?part=id,snippet,contentDetails,status"
-		"&id=%1";
-	const QString url = url_template.arg(broadcast_id);
-	Json json_out;
-
-	if (!InsertCommand(QT_TO_UTF8(url), "application/json", "", nullptr,
-			   json_out))
-		return false;
-
-	const QString put = YOUTUBE_LIVE_BROADCAST_URL
-		"?part=id,snippet,contentDetails,status";
-
 	auto snippet = json_out["items"][0]["snippet"];
 	auto status = json_out["items"][0]["status"];
 	auto contentDetails = json_out["items"][0]["contentDetails"];
@@ -514,6 +529,9 @@ bool YoutubeApiWrappers::ResetBroadcast(const QString &broadcast_id)
 			 {"startWithSlate", contentDetails["startWithSlate"]},
 		 }},
 	};
+
+	const QString put = YOUTUBE_LIVE_BROADCAST_URL
+		"?part=id,snippet,contentDetails,status";
 	return InsertCommand(QT_TO_UTF8(put), "application/json", "PUT",
 			     data.dump().c_str(), json_out);
 }

+ 2 - 1
UI/youtube-api-wrappers.hpp

@@ -70,7 +70,8 @@ public:
 			       const QString &thumbnail_file);
 	bool StartBroadcast(const QString &broadcast_id);
 	bool StopBroadcast(const QString &broadcast_id);
-	bool ResetBroadcast(const QString &broadcast_id);
+	bool ResetBroadcast(const QString &broadcast_id,
+			    json11::Json &json_out);
 	bool StartLatestBroadcast();
 	bool StopLatestBroadcast();