Skip to content

Commit

Permalink
UI: Rewrite profile system to enable user-provided storage location
Browse files Browse the repository at this point in the history
This change enables loading profiles from locations different than
OBS' own configuration directory.

It also rewrites profile management in the app to work off an in-memory
collection of profiles found on disk and does not require iterating
over directory contents for most profile interactions by the app.
  • Loading branch information
PatTheMav committed Sep 10, 2024
1 parent aa928ad commit c4467ae
Show file tree
Hide file tree
Showing 9 changed files with 1,571 additions and 1,400 deletions.
580 changes: 272 additions & 308 deletions UI/api-interface.cpp

Large diffs are not rendered by default.

209 changes: 81 additions & 128 deletions UI/obs-app.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -609,109 +609,41 @@ static bool MakeUserDirs()
return true;
}

static bool MakeUserProfileDirs()
{
char path[512];

if (GetConfigPath(path, sizeof(path), "obs-studio/basic/profiles") <= 0)
return false;
if (!do_mkdir(path))
return false;
constexpr std::string_view OBSProfileSubDirectory = "obs-studio/basic/profiles";
constexpr std::string_view OBSScenesSubDirectory = "obs-studio/basic/scenes";

if (GetConfigPath(path, sizeof(path), "obs-studio/basic/scenes") <= 0)
return false;
if (!do_mkdir(path))
return false;

return true;
}

static string GetProfileDirFromName(const char *name)
static bool MakeUserProfileDirs()
{
string outputPath;
os_glob_t *glob;
char path[512];

if (GetConfigPath(path, sizeof(path), "obs-studio/basic/profiles") <= 0)
return outputPath;

strcat(path, "/*");

if (os_glob(path, 0, &glob) != 0)
return outputPath;

for (size_t i = 0; i < glob->gl_pathc; i++) {
struct os_globent ent = glob->gl_pathv[i];
if (!ent.directory)
continue;

strcpy(path, ent.path);
strcat(path, "/basic.ini");

ConfigFile config;
if (config.Open(path, CONFIG_OPEN_EXISTING) != 0)
continue;

const char *curName =
config_get_string(config, "General", "Name");
if (astrcmpi(curName, name) == 0) {
outputPath = ent.path;
break;
const std::filesystem::path userProfilePath =
App()->userProfilesLocation /
std::filesystem::u8path(OBSProfileSubDirectory);
const std::filesystem::path userScenesPath =
App()->userScenesLocation /
std::filesystem::u8path(OBSScenesSubDirectory);

if (!std::filesystem::exists(userProfilePath)) {
try {
std::filesystem::create_directories(userProfilePath);
} catch (const std::filesystem::filesystem_error &error) {
blog(LOG_ERROR,
"Failed to create user profile directory '%s'\n%s",
userProfilePath.u8string().c_str(), error.what());
return false;
}
}

os_globfree(glob);

if (!outputPath.empty()) {
replace(outputPath.begin(), outputPath.end(), '\\', '/');
const char *start = strrchr(outputPath.c_str(), '/');
if (start)
outputPath.erase(0, start - outputPath.c_str() + 1);
}

return outputPath;
}

static string GetSceneCollectionFileFromName(const char *name)
{
string outputPath;
os_glob_t *glob;
char path[512];

if (GetConfigPath(path, sizeof(path), "obs-studio/basic/scenes") <= 0)
return outputPath;

strcat(path, "/*.json");

if (os_glob(path, 0, &glob) != 0)
return outputPath;

for (size_t i = 0; i < glob->gl_pathc; i++) {
struct os_globent ent = glob->gl_pathv[i];
if (ent.directory)
continue;

OBSDataAutoRelease data =
obs_data_create_from_json_file_safe(ent.path, "bak");
const char *curName = obs_data_get_string(data, "name");

if (astrcmpi(name, curName) == 0) {
outputPath = ent.path;
break;
if (!std::filesystem::exists(userScenesPath)) {
try {
std::filesystem::create_directories(userScenesPath);
} catch (const std::filesystem::filesystem_error &error) {
blog(LOG_ERROR,
"Failed to create user scene collection directory '%s'\n%s",
userScenesPath.u8string().c_str(), error.what());
return false;
}
}

os_globfree(glob);

if (!outputPath.empty()) {
outputPath.resize(outputPath.size() - 5);
replace(outputPath.begin(), outputPath.end(), '\\', '/');
const char *start = strrchr(outputPath.c_str(), '/');
if (start)
outputPath.erase(0, start - outputPath.c_str() + 1);
}

return outputPath;
return true;
}

bool OBSApp::UpdatePre22MultiviewLayout(const char *layout)
Expand Down Expand Up @@ -1212,56 +1144,77 @@ OBSApp::~OBSApp()
static void move_basic_to_profiles(void)
{
char path[512];
char new_path[512];
os_glob_t *glob;

/* if not first time use */
if (GetConfigPath(path, 512, "obs-studio/basic") <= 0)
return;
if (!os_file_exists(path))
if (GetAppConfigPath(path, 512, "obs-studio/basic") <= 0) {
return;
}

/* if the profiles directory doesn't already exist */
if (GetConfigPath(new_path, 512, "obs-studio/basic/profiles") <= 0)
return;
if (os_file_exists(new_path))
return;
const std::filesystem::path basicPath = std::filesystem::u8path(path);

if (os_mkdir(new_path) == MKDIR_ERROR)
if (!std::filesystem::exists(basicPath)) {
return;
}

strcat(new_path, "/");
strcat(new_path, Str("Untitled"));
if (os_mkdir(new_path) == MKDIR_ERROR)
return;
const std::filesystem::path profilesPath =
App()->userProfilesLocation /
std::filesystem::u8path("obs-studio/basic/profiles");

strcat(path, "/*.*");
if (os_glob(path, 0, &glob) != 0)
if (std::filesystem::exists(profilesPath)) {
return;
}

strcpy(path, new_path);
try {
std::filesystem::create_directories(profilesPath);
} catch (const std::filesystem::filesystem_error &error) {
blog(LOG_ERROR,
"Failed to create profiles directory for migration from basic profile\n%s",
error.what());
return;
}

for (size_t i = 0; i < glob->gl_pathc; i++) {
struct os_globent ent = glob->gl_pathv[i];
char *file;
const std::filesystem::path newProfilePath =
profilesPath / std::filesystem::u8path(Str("Untitled"));

if (ent.directory)
for (auto &entry : std::filesystem::directory_iterator(basicPath)) {
if (entry.is_directory()) {
continue;
}

file = strrchr(ent.path, '/');
if (!file++)
if (entry.path().filename().u8string() == "scenes.json") {
continue;
}

if (astrcmpi(file, "scenes.json") == 0)
continue;
if (!std::filesystem::exists(newProfilePath)) {
try {
std::filesystem::create_directory(
newProfilePath);
} catch (
const std::filesystem::filesystem_error &error) {
blog(LOG_ERROR,
"Failed to create profile directory for 'Untitled'\n%s",
error.what());
return;
}
}

strcpy(new_path, path);
strcat(new_path, "/");
strcat(new_path, file);
os_rename(ent.path, new_path);
}
const filesystem::path destinationFile =
newProfilePath / entry.path().filename();

os_globfree(glob);
const auto copyOptions =
std::filesystem::copy_options::overwrite_existing;

try {
std::filesystem::copy(entry.path(), destinationFile,
copyOptions);
} catch (const std::filesystem::filesystem_error &error) {
blog(LOG_ERROR,
"Failed to copy basic profile file '%s' to new profile 'Untitled'\n%s",
entry.path().filename().u8string().c_str(),
error.what());

return;
}
}
}

static void move_basic_to_scene_collections(void)
Expand Down
20 changes: 13 additions & 7 deletions UI/window-basic-auto-config.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -39,18 +39,24 @@ extern QCefCookieManager *panel_cookies;

/* ------------------------------------------------------------------------- */

#define SERVICE_PATH "service.json"
constexpr std::string_view OBSServiceFileName = "service.json";

static OBSData OpenServiceSettings(std::string &type)
{
char serviceJsonPath[512];
int ret = GetProfilePath(serviceJsonPath, sizeof(serviceJsonPath),
SERVICE_PATH);
if (ret <= 0)
const OBSBasic *basic =
reinterpret_cast<OBSBasic *>(App()->GetMainWindow());
const OBSProfile &currentProfile = basic->GetCurrentProfile();

const std::filesystem::path jsonFilePath =
currentProfile.path /
std::filesystem::u8path(OBSServiceFileName);

if (!std::filesystem::exists(jsonFilePath)) {
return OBSData();
}

OBSDataAutoRelease data =
obs_data_create_from_json_file_safe(serviceJsonPath, "bak");
OBSDataAutoRelease data = obs_data_create_from_json_file_safe(
jsonFilePath.u8string().c_str(), "bak");

obs_data_set_default_string(data, "type", "rtmp_common");
type = obs_data_get_string(data, "type");
Expand Down
19 changes: 14 additions & 5 deletions UI/window-basic-main-outputs.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1616,19 +1616,28 @@ struct AdvancedOutput : BasicOutputHandler {

static OBSData GetDataFromJsonFile(const char *jsonFile)
{
char fullPath[512];
const OBSBasic *basic =
reinterpret_cast<OBSBasic *>(App()->GetMainWindow());

const OBSProfile &currentProfile = basic->GetCurrentProfile();

const std::filesystem::path jsonFilePath =
currentProfile.path / std::filesystem::u8path(jsonFile);

OBSDataAutoRelease data = nullptr;

int ret = GetProfilePath(fullPath, sizeof(fullPath), jsonFile);
if (ret > 0) {
BPtr<char> jsonData = os_quick_read_utf8_file(fullPath);
if (!jsonFilePath.empty()) {
BPtr<char> jsonData = os_quick_read_utf8_file(
jsonFilePath.u8string().c_str());

if (!!jsonData) {
data = obs_data_create_from_json(jsonData);
}
}

if (!data)
if (!data) {
data = obs_data_create();
}

return data.Get();
}
Expand Down
Loading

0 comments on commit c4467ae

Please sign in to comment.