Skip to content

Commit 086d9ee

Browse files
Preserve inherited server URLs when switching to custom streaming
1 parent a05642d commit 086d9ee

3 files changed

Lines changed: 61 additions & 15 deletions

File tree

‎js/module.ts‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2456,6 +2456,9 @@ interface INodeObs {
24562456

24572457
/**
24582458
* Saves a complete legacy settings category, including native field metadata.
2459+
* Converting a common RTMP service to custom restores the exact native URL
2460+
* when the submitted server matches the previous form value. Inherited automatic
2461+
* selections are resolved to a URL; explicitly edited server values are preserved.
24592462
* @param category - Category name, such as `Stream` or `StreamSecond`
24602463
* @param settings - The `data` array from `OBS_settings_getSettings(category)`,
24612464
* with edited `currentValue` fields; keep the stream type field first

‎obs-studio-server/source/nodeobs_settings.cpp‎

Lines changed: 18 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -973,18 +973,23 @@ bool OBS_settings::saveStreamSettings(std::vector<SubCategory> streamSettings, S
973973
if (currentStreamType == "rtmp_common" && newserviceTypeValue == "rtmp_custom") {
974974
const std::string server = obs_data_get_string(settings, "server");
975975
const std::string currentServer = obs_data_get_string(currentSettings, "server");
976-
// Automatic selections belong to the common provider. Resolve an inherited
977-
// selection before replacing that service; an explicitly entered URL wins.
978-
if (!server.empty() && server == currentServer && server.find("://") == std::string::npos) {
979-
// Fresh profiles populate the settings object before the service has
980-
// received an update. Resolve from that snapshot without mutating it.
981-
OBSServiceAutoRelease resolver = obs_service_create("rtmp_common", "stream_settings_resolver", currentSettings, nullptr);
982-
if (!resolver)
983-
return false;
984-
const char *resolvedServer = obs_service_get_connect_info(resolver, OBS_SERVICE_CONNECT_INFO_SERVER_URL);
985-
if (!resolvedServer || std::string(resolvedServer).find("://") == std::string::npos)
986-
return false;
987-
obs_data_set_string(settings, "server", resolvedServer);
976+
// Compare against the previous form value, which omits a trailing slash.
977+
// Keep the submitted spelling intact when the user explicitly changes it.
978+
if (!server.empty() && server == getStreamSettingListValue(currentServer.c_str())) {
979+
if (currentServer.find("://") != std::string::npos) {
980+
obs_data_set_string(settings, "server", currentServer.c_str());
981+
} else {
982+
// Automatic selections belong to the common provider. Fresh profiles
983+
// populate settings before updating the service, so resolve from that
984+
// snapshot without mutating the current service.
985+
OBSServiceAutoRelease resolver = obs_service_create("rtmp_common", "stream_settings_resolver", currentSettings, nullptr);
986+
if (!resolver)
987+
return false;
988+
const char *resolvedServer = obs_service_get_connect_info(resolver, OBS_SERVICE_CONNECT_INFO_SERVER_URL);
989+
if (!resolvedServer || std::string(resolvedServer).find("://") == std::string::npos)
990+
return false;
991+
obs_data_set_string(settings, "server", resolvedServer);
992+
}
988993
}
989994
}
990995

@@ -1007,7 +1012,7 @@ bool OBS_settings::saveStreamSettings(std::vector<SubCategory> streamSettings, S
10071012
continue;
10081013

10091014
// Match the form's normalized spelling, but retain the exact native
1010-
// endpoint: providers such as Facebook require its trailing slash.
1015+
// endpoint, including any trailing slash in the catalog.
10111016
std::string value = obs_property_list_item_string(property, i);
10121017
if (value.empty())
10131018
continue;

‎tests/osn-tests/src/test_nodeobs_settings.ts‎

Lines changed: 40 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -248,8 +248,8 @@ describe(testName, function() {
248248
const selectedServer = choices[0];
249249
expect(selectedServer.endsWith('/rtmp')).to.equal(true);
250250

251-
// Facebook validates the exact native URL, including its final slash.
252-
// The generic form exposes that same choice without the final slash.
251+
// The Facebook catalog URL includes a final slash that the generic
252+
// form omits from the displayed choice.
253253
save({ server: selectedServer, key: dummyKey });
254254
expectSaved({ service: 'Facebook Live', server: selectedServer, key: dummyKey }, `${selectedServer}/`);
255255
save({ show_all: true });
@@ -273,6 +273,44 @@ describe(testName, function() {
273273
expectSaved({ streamType: 'rtmp_custom', server: youtubeServer, key: dummyKey });
274274
});
275275

276+
it('Preserves the Facebook catalog URL when switching to custom streaming', function() {
277+
save({ service: 'Facebook Live' });
278+
save({ key: dummyKey });
279+
const formServer = field('server').currentValue;
280+
const nativeServer = JSON.parse(fs.readFileSync(configPath, 'utf8')).settings.server;
281+
expect(nativeServer).to.equal(`${formServer}/`);
282+
283+
save({ streamType: 'rtmp_custom' });
284+
285+
expectSaved({ streamType: 'rtmp_custom', server: nativeServer, key: dummyKey });
286+
287+
// After conversion the custom field exposes the exact URL, so a
288+
// deliberate removal of its trailing slash must remain possible.
289+
save({ server: formServer });
290+
expectSaved({ streamType: 'rtmp_custom', server: formServer, key: dummyKey });
291+
});
292+
293+
it('Keeps an explicit replacement URL when leaving Facebook common streaming', function() {
294+
save({ service: 'Facebook Live' });
295+
save({ key: dummyKey });
296+
const server = 'rtmps://custom.example.invalid:443/application/';
297+
298+
save({ streamType: 'rtmp_custom', server });
299+
300+
expectSaved({ streamType: 'rtmp_custom', server, key: dummyKey });
301+
});
302+
303+
it('Keeps an explicitly added trailing slash when switching to custom streaming', function() {
304+
save({ service: 'YouTube - RTMPS' });
305+
save({ server: youtubeServer, key: dummyKey });
306+
expect(field('server').currentValue).to.equal(youtubeServer);
307+
const server = `${youtubeServer}/`;
308+
309+
save({ streamType: 'rtmp_custom', server });
310+
311+
expectSaved({ streamType: 'rtmp_custom', server, key: dummyKey });
312+
});
313+
276314
it('Resolves the automatic Twitch server when switching to custom streaming', function() {
277315
save({ key: dummyKey });
278316
expect(field('server').currentValue).to.equal('auto');

0 commit comments

Comments
 (0)