Config: Migrate config/persistent data to plugin_config directory

This commit moves the Config value storage from `global.ini` to a new
`config.json` file in the `plugin_config/obs-websocket` directory. This
comes after some internal discussion about plugins not using the
`plugin_config` directory, and that obs-websocket was offending.

Settings are currently stored as a JSON object, and field names have
been changed from using PascalCase to snake_case, to better align
with how JSON is stored elsewhere in OBS.
This commit is contained in:
tt2468 2024-04-22 22:30:44 -07:00
parent 4410e30684
commit af31f1adca
3 changed files with 126 additions and 54 deletions

View File

@ -17,45 +17,59 @@ You should have received a copy of the GNU General Public License along
with this program. If not, see <https://www.gnu.org/licenses/>
*/
#include <filesystem>
#include <obs-frontend-api.h>
#include "Config.h"
#include "utils/Crypto.h"
#include "utils/Platform.h"
#include "utils/Obs.h"
#define CONFIG_SECTION_NAME "OBSWebSocket"
#define CONFIG_PARAM_FIRSTLOAD "FirstLoad"
#define CONFIG_PARAM_ENABLED "ServerEnabled"
#define CONFIG_PARAM_PORT "ServerPort"
#define CONFIG_PARAM_ALERTS "AlertsEnabled"
#define CONFIG_PARAM_AUTHREQUIRED "AuthRequired"
#define CONFIG_PARAM_PASSWORD "ServerPassword"
#define PARAM_FIRSTLOAD "FirstLoad"
#define PARAM_ENABLED "ServerEnabled"
#define PARAM_PORT "ServerPort"
#define PARAM_ALERTS "AlertsEnabled"
#define PARAM_AUTHREQUIRED "AuthRequired"
#define PARAM_PASSWORD "ServerPassword"
#define CONFIG_FILE_NAME "config.json"
#define PARAM_FIRSTLOAD "first_load"
#define PARAM_ENABLED "server_enabled"
#define PARAM_PORT "server_port"
#define PARAM_ALERTS "alerts_enabled"
#define PARAM_AUTHREQUIRED "auth_required"
#define PARAM_PASSWORD "server_password"
#define CMDLINE_WEBSOCKET_PORT "websocket_port"
#define CMDLINE_WEBSOCKET_IPV4_ONLY "websocket_ipv4_only"
#define CMDLINE_WEBSOCKET_PASSWORD "websocket_password"
#define CMDLINE_WEBSOCKET_DEBUG "websocket_debug"
Config::Config()
void Config::Load(json config)
{
SetDefaultsToGlobalStore();
}
void Config::Load()
{
config_t *obsConfig = GetConfigStore();
if (!obsConfig) {
blog(LOG_ERROR, "[Config::Load] Unable to fetch OBS config!");
return;
if (config.is_null()) {
std::string configFilePath = Utils::Obs::StringHelper::GetModuleConfigPath(CONFIG_FILE_NAME);
Utils::Json::GetJsonFileContent(configFilePath, config); // Fetch the existing config, which may not exist
}
FirstLoad = config_get_bool(obsConfig, CONFIG_SECTION_NAME, PARAM_FIRSTLOAD);
ServerEnabled = config_get_bool(obsConfig, CONFIG_SECTION_NAME, PARAM_ENABLED);
AlertsEnabled = config_get_bool(obsConfig, CONFIG_SECTION_NAME, PARAM_ALERTS);
ServerPort = config_get_uint(obsConfig, CONFIG_SECTION_NAME, PARAM_PORT);
AuthRequired = config_get_bool(obsConfig, CONFIG_SECTION_NAME, PARAM_AUTHREQUIRED);
ServerPassword = config_get_string(obsConfig, CONFIG_SECTION_NAME, PARAM_PASSWORD);
// Should never happen, but just in case
if (!config.is_object())
return;
if (config.contains(PARAM_FIRSTLOAD) && config[PARAM_FIRSTLOAD].is_boolean())
FirstLoad = config[PARAM_FIRSTLOAD];
if (config.contains(PARAM_ENABLED) && config[PARAM_ENABLED].is_boolean())
ServerEnabled = config[PARAM_ENABLED];
if (config.contains(PARAM_ALERTS) && config[PARAM_ALERTS].is_boolean())
AlertsEnabled = config[PARAM_ALERTS];
if (config.contains(PARAM_PORT) && config[PARAM_PORT].is_number_unsigned())
ServerPort = config[PARAM_PORT];
if (config.contains(PARAM_AUTHREQUIRED) && config[PARAM_AUTHREQUIRED].is_boolean())
AuthRequired = config[PARAM_AUTHREQUIRED];
if (config.contains(PARAM_PASSWORD) && config[PARAM_PASSWORD].is_string())
ServerPassword = QString::fromStdString(config[PARAM_PASSWORD].get<std::string>());
// Set server password and save it to the config before processing overrides,
// so that there is always a true configured password regardless of if
@ -110,43 +124,93 @@ void Config::Load()
void Config::Save()
{
config_t *obsConfig = GetConfigStore();
if (!obsConfig) {
blog(LOG_ERROR, "[Config::Save] Unable to fetch OBS config!");
return;
}
json config;
config_set_bool(obsConfig, CONFIG_SECTION_NAME, PARAM_FIRSTLOAD, FirstLoad);
config_set_bool(obsConfig, CONFIG_SECTION_NAME, PARAM_ENABLED, ServerEnabled);
if (!PortOverridden) {
config_set_uint(obsConfig, CONFIG_SECTION_NAME, PARAM_PORT, ServerPort);
}
config_set_bool(obsConfig, CONFIG_SECTION_NAME, PARAM_ALERTS, AlertsEnabled);
std::string configFilePath = Utils::Obs::StringHelper::GetModuleConfigPath(CONFIG_FILE_NAME);
Utils::Json::GetJsonFileContent(configFilePath, config); // Fetch the existing config, which may not exist
config[PARAM_FIRSTLOAD] = FirstLoad.load();
config[PARAM_ENABLED] = ServerEnabled.load();
if (!PortOverridden)
config[PARAM_PORT] = ServerPort.load();
config[PARAM_ALERTS] = AlertsEnabled.load();
if (!PasswordOverridden) {
config_set_bool(obsConfig, CONFIG_SECTION_NAME, PARAM_AUTHREQUIRED, AuthRequired);
config_set_string(obsConfig, CONFIG_SECTION_NAME, PARAM_PASSWORD, QT_TO_UTF8(ServerPassword));
config[PARAM_AUTHREQUIRED] = AuthRequired.load();
config[PARAM_PASSWORD] = ServerPassword.toStdString();
}
config_save(obsConfig);
if (!Utils::Json::SetJsonFileContent(configFilePath, config))
blog(LOG_ERROR, "[Config::Save] Failed to write config file!");
}
void Config::SetDefaultsToGlobalStore()
// Finds any old values in global.ini and removes them, then returns the values as JSON
json MigrateGlobalConfigData()
{
config_t *obsConfig = GetConfigStore();
if (!obsConfig) {
blog(LOG_ERROR, "[Config::SetDefaultsToGlobalStore] Unable to fetch OBS config!");
return;
// Get existing global config
config_t *config = obs_frontend_get_global_config();
json ret;
// Move values to temporary JSON blob
if (config_has_user_value(config, CONFIG_SECTION_NAME, CONFIG_PARAM_FIRSTLOAD)) {
ret[PARAM_FIRSTLOAD] = config_get_bool(config, CONFIG_SECTION_NAME, CONFIG_PARAM_FIRSTLOAD);
config_remove_value(config, CONFIG_SECTION_NAME, CONFIG_PARAM_FIRSTLOAD);
}
if (config_has_user_value(config, CONFIG_SECTION_NAME, CONFIG_PARAM_ENABLED)) {
ret[PARAM_ENABLED] = config_get_bool(config, CONFIG_SECTION_NAME, CONFIG_PARAM_ENABLED);
config_remove_value(config, CONFIG_SECTION_NAME, CONFIG_PARAM_ENABLED);
}
if (config_has_user_value(config, CONFIG_SECTION_NAME, CONFIG_PARAM_PORT)) {
ret[PARAM_PORT] = config_get_uint(config, CONFIG_SECTION_NAME, CONFIG_PARAM_PORT);
config_remove_value(config, CONFIG_SECTION_NAME, CONFIG_PARAM_PORT);
}
if (config_has_user_value(config, CONFIG_SECTION_NAME, CONFIG_PARAM_ALERTS)) {
ret[PARAM_ALERTS] = config_get_bool(config, CONFIG_SECTION_NAME, CONFIG_PARAM_ALERTS);
config_remove_value(config, CONFIG_SECTION_NAME, CONFIG_PARAM_ALERTS);
}
if (config_has_user_value(config, CONFIG_SECTION_NAME, CONFIG_PARAM_AUTHREQUIRED)) {
ret[PARAM_AUTHREQUIRED] = config_get_bool(config, CONFIG_SECTION_NAME, CONFIG_PARAM_AUTHREQUIRED);
config_remove_value(config, CONFIG_SECTION_NAME, CONFIG_PARAM_AUTHREQUIRED);
}
if (config_has_user_value(config, CONFIG_SECTION_NAME, CONFIG_PARAM_PASSWORD)) {
ret[PARAM_PASSWORD] = config_get_string(config, CONFIG_SECTION_NAME, CONFIG_PARAM_PASSWORD);
config_remove_value(config, CONFIG_SECTION_NAME, CONFIG_PARAM_PASSWORD);
}
config_set_default_bool(obsConfig, CONFIG_SECTION_NAME, PARAM_FIRSTLOAD, FirstLoad);
config_set_default_bool(obsConfig, CONFIG_SECTION_NAME, PARAM_ENABLED, ServerEnabled);
config_set_default_uint(obsConfig, CONFIG_SECTION_NAME, PARAM_PORT, ServerPort);
config_set_default_bool(obsConfig, CONFIG_SECTION_NAME, PARAM_ALERTS, AlertsEnabled);
config_set_default_bool(obsConfig, CONFIG_SECTION_NAME, PARAM_AUTHREQUIRED, AuthRequired);
config_set_default_string(obsConfig, CONFIG_SECTION_NAME, PARAM_PASSWORD, QT_TO_UTF8(ServerPassword));
if (!ret.is_null()) {
blog(LOG_INFO, "[MigrateGlobalConfigData] Some configurations have been migrated from old config");
config_save(config);
}
return ret;
}
config_t *Config::GetConfigStore()
// Migration from storing persistent data in obsWebSocketPersistentData.json to the module config directory
// This will overwrite any persistent data in the destination. People doing manual OBS config modification be warned!
bool MigratePersistentData()
{
return obs_frontend_get_global_config();
std::error_code ec;
// Ensure module config directory exists
std::string moduleConfigDirectory = Utils::Obs::StringHelper::GetModuleConfigPath("");
if (!std::filesystem::exists(moduleConfigDirectory, ec))
std::filesystem::create_directories(moduleConfigDirectory, ec);
if (ec) {
blog(LOG_ERROR, "[MigratePersistentData] Failed to create directory `%s`: %s", moduleConfigDirectory.c_str(), ec.message().c_str());
return false;
}
// Move any existing persistent data to module config directory, then delete old file
std::string oldPersistentDataPath = Utils::Obs::StringHelper::GetCurrentProfilePath() + "/../../../obsWebSocketPersistentData.json";
if (std::filesystem::exists(oldPersistentDataPath, ec)) {
std::string persistentDataPath = Utils::Obs::StringHelper::GetModuleConfigPath("persistent_data.json");
std::filesystem::copy_file(oldPersistentDataPath, persistentDataPath, ec);
std::filesystem::remove(oldPersistentDataPath, ec);
blog(LOG_INFO, "[MigratePersistentData] Persistent data migrated to new path");
}
if (ec) {
blog(LOG_ERROR, "[MigratePersistentData] Failed to move persistent data: %s", ec.message().c_str());
return false;
}
return true;
}

View File

@ -23,14 +23,12 @@ with this program. If not, see <https://www.gnu.org/licenses/>
#include <QString>
#include <util/config-file.h>
#include "utils/Json.h"
#include "plugin-macros.generated.h"
struct Config {
Config();
void Load();
void Load(json config = nullptr);
void Save();
void SetDefaultsToGlobalStore();
static config_t *GetConfigStore();
std::atomic<bool> PortOverridden = false;
std::atomic<bool> PasswordOverridden = false;
@ -44,3 +42,6 @@ struct Config {
std::atomic<bool> AuthRequired = true;
QString ServerPassword;
};
json MigrateGlobalConfigData();
bool MigratePersistentData();

View File

@ -60,9 +60,16 @@ bool obs_module_load(void)
// Initialize the cpu stats
_cpuUsageInfo = os_cpu_usage_info_start();
// Handle migrations
if (!MigratePersistentData()) {
os_cpu_usage_info_destroy(_cpuUsageInfo);
return false;
}
json migratedConfig = MigrateGlobalConfigData();
// Create the config manager then load the parameters from storage
_config = ConfigPtr(new Config());
_config->Load();
_config->Load(migratedConfig);
// Initialize the event handler
_eventHandler = EventHandlerPtr(new EventHandler());