New upstream version 25.0.3+dfsg1

This commit is contained in:
Sebastian Ramacher 2020-03-25 09:07:22 +01:00
parent 04fe0efc67
commit 8b2e5f2130
569 changed files with 62491 additions and 5875 deletions

584
UI/importers/classic.cpp Normal file
View file

@ -0,0 +1,584 @@
/******************************************************************************
Copyright (C) 2019-2020 by Dillon Pentz <dillon@vodbox.io>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
******************************************************************************/
#include "importers.hpp"
#include <QByteArray>
using namespace std;
using namespace json11;
static bool source_name_exists(const Json::array &sources, const string &name)
{
for (size_t i = 0; i < sources.size(); i++) {
Json source = sources[i];
if (name == source["name"].string_value())
return true;
}
return false;
}
#define translate_int(in_key, in, out_key, out, off) \
out[out_key] = in[in_key].int_value() + off;
#define translate_string(in_key, in, out_key, out) out[out_key] = in[in_key];
#define translate_double(in_key, in, out_key, out) \
translate_string(in_key, in, out_key, out);
#define translate_bool(in_key, in, out_key, out) \
out[out_key] = in[in_key].int_value() == 1;
static Json::object translate_scene_item(const Json &in, const Json &source)
{
Json::object item = Json::object{};
translate_string("name", source, "name", item);
translate_int("crop.top", in, "crop_top", item, 0);
translate_int("crop.bottom", in, "crop_bottom", item, 0);
translate_int("crop.left", in, "crop_left", item, 0);
translate_int("crop.right", in, "crop_right", item, 0);
Json::object pos = Json::object{};
translate_int("x", in, "x", pos, 0);
translate_int("y", in, "y", pos, 0);
Json::object bounds = Json::object{};
translate_int("cx", in, "x", bounds, 0);
translate_int("cy", in, "y", bounds, 0);
item["pos"] = pos;
item["bounds"] = bounds;
item["bounds_type"] = 2;
item["visible"] = true;
return item;
}
static int red_blue_swap(int color)
{
int r = color / 256 / 256;
int b = color % 256;
return color - (r * 65536) - b + (b * 65536) + r;
}
static void create_string_obj(const string &data, Json::array &arr);
static Json::object translate_source(const Json &in, const Json &sources)
{
string id = in["class"].string_value();
string name = in["name"].string_value();
Json::array source_arr = sources.array_items();
if (id == "GlobalSource") {
for (size_t i = 0; i < source_arr.size(); i++) {
Json source = source_arr[i];
if (name == source["name"].string_value()) {
Json::object obj = source.object_items();
obj["preexist"] = true;
return obj;
}
}
}
Json in_settings = in["data"];
Json::object settings = Json::object{};
Json::object out = Json::object{};
int i = 0;
string new_name = name;
while (source_name_exists(source_arr, new_name)) {
new_name = name + to_string(++i);
}
out["name"] = new_name;
if (id == "TextSource") {
out["id"] = "text_gdiplus";
int color = in_settings["color"].int_value() + 16777216;
color = red_blue_swap(color) + 4278190080;
settings["color"] = color;
color = in_settings["backgroundColor"].int_value();
color = red_blue_swap(color + 16777216) + 4278190080;
settings["bk_color"] = color;
color = in_settings["outlineColor"].int_value();
color = red_blue_swap(color + 16777216) + 4278190080;
settings["outline_color"] = color;
translate_string("text", in_settings, "text", settings);
translate_int("backgroundOpacity", in_settings, "bk_opacity",
settings, 0);
translate_bool("vertical", in_settings, "vertical", settings);
translate_int("textOpacity", in_settings, "opacity", settings,
0);
translate_bool("useOutline", in_settings, "outline", settings);
translate_int("outlineOpacity", in_settings, "outline_opacity",
settings, 0);
translate_int("outlineSize", in_settings, "outline_size",
settings, 0);
translate_bool("useTextExtents", in_settings, "extents",
settings);
translate_int("extentWidth", in_settings, "extents_cx",
settings, 0);
translate_int("extentHeight", in_settings, "extents_cy",
settings, 0);
translate_bool("mode", in_settings, "read_from_file", settings);
translate_bool("wrap", in_settings, "extents_wrap", settings);
string str = in_settings["file"].string_value();
settings["file"] = StringReplace(str, "\\\\", "/");
int in_align = in_settings["align"].int_value();
string align = in_align == 0
? "left"
: (in_align == 1 ? "center" : "right");
settings["align"] = align;
bool bold = in_settings["bold"].int_value() == 1;
bool italic = in_settings["italic"].int_value() == 1;
bool underline = in_settings["underline"].int_value() == 1;
int flags = bold ? OBS_FONT_BOLD : 0;
flags |= italic ? OBS_FONT_ITALIC : 0;
flags |= underline ? OBS_FONT_UNDERLINE : 0;
Json::object font = Json::object{};
font["flags"] = flags;
translate_int("fontSize", in_settings, "size", font, 0);
translate_string("font", in_settings, "face", font);
if (bold && italic) {
font["style"] = "Bold Italic";
} else if (bold) {
font["style"] = "Bold";
} else if (italic) {
font["style"] = "Italic";
} else {
font["style"] = "Regular";
}
settings["font"] = font;
} else if (id == "MonitorCaptureSource") {
out["id"] = "monitor_capture";
translate_int("monitor", in_settings, "monitor", settings, 0);
translate_bool("captureMouse", in_settings, "capture_cursor",
settings);
} else if (id == "BitmapImageSource") {
out["id"] = "image_source";
string str = in_settings["path"].string_value();
settings["file"] = StringReplace(str, "\\\\", "/");
} else if (id == "BitmapTransitionSource") {
out["id"] = "slideshow";
Json files = in_settings["bitmap"];
if (!files.is_array()) {
files = Json::array{in_settings["bitmap"]};
}
settings["files"] = files;
} else if (id == "WindowCaptureSource") {
out["id"] = "window_capture";
string win = in_settings["window"].string_value();
string winClass = in_settings["windowClass"].string_value();
win = StringReplace(win, "/", "\\\\");
win = StringReplace(win, ":", "#3A");
winClass = StringReplace(winClass, ":", "#3A");
settings["window"] = win + ":" + winClass + ":";
settings["priority"] = 0;
} else if (id == "CLRBrowserSource") {
out["id"] = "browser_source";
string browser_dec =
QByteArray::fromBase64(in_settings["sourceSettings"]
.string_value()
.c_str())
.toStdString();
string err;
Json browser = Json::parse(browser_dec, err);
if (err != "")
return Json::object{};
Json::object obj = browser.object_items();
translate_string("CSS", obj, "css", settings);
translate_int("Height", obj, "height", settings, 0);
translate_int("Width", obj, "width", settings, 0);
translate_string("Url", obj, "url", settings);
} else if (id == "DeviceCapture") {
out["id"] = "dshow_input";
string device_id = in_settings["deviceID"].string_value();
string device_name = in_settings["deviceName"].string_value();
settings["video_device_id"] = device_name + ":" + device_id;
int w = in_settings["resolutionWidth"].int_value();
int h = in_settings["resolutionHeight"].int_value();
settings["resolution"] = to_string(w) + "x" + to_string(h);
} else if (id == "GraphicsCapture") {
bool hotkey = in_settings["useHotkey"].int_value() == 1;
if (hotkey) {
settings["capture_mode"] = "hotkey";
} else {
settings["capture_mode"] = "window";
}
string winClass = in_settings["windowClass"].string_value();
string exec = in_settings["executable"].string_value();
string window = ":" + winClass + ":" + exec;
settings["window"] = ":" + winClass + ":" + exec;
translate_bool("captureMouse", in_settings, "capture_cursor",
settings);
}
out["settings"] = settings;
return out;
}
#undef translate_int
#undef translate_string
#undef translate_double
#undef translate_bool
static void translate_sc(const Json &in, Json &out)
{
Json::object res = Json::object{};
Json::array out_sources = Json::array{};
Json::array global = in["globals"].array_items();
if (!in["globals"].is_null()) {
for (size_t i = 0; i < global.size(); i++) {
Json source = global[i];
Json out_source = translate_source(source, out_sources);
out_sources.push_back(out_source);
}
}
Json::array scenes = in["scenes"].array_items();
string first_name = "";
for (size_t i = 0; i < scenes.size(); i++) {
Json in_scene = scenes[i];
if (first_name == "")
first_name = in_scene["name"].string_value();
Json::object settings = Json::object{};
Json::array items = Json::array{};
Json::array sources = in_scene["sources"].array_items();
for (size_t x = sources.size(); x > 0; x--) {
Json source = sources[x - 1];
Json::object out_source =
translate_source(source, out_sources);
Json::object out_item =
translate_scene_item(source, out_source);
out_item["id"] = (int)x - 1;
items.push_back(out_item);
if (out_source.find("preexist") == out_source.end())
out_sources.push_back(out_source);
}
out_sources.push_back(Json::object{
{"id", "scene"},
{"name", in_scene["name"]},
{"settings",
Json::object{{"items", items},
{"id_counter", (int)items.size()}}}});
}
res["current_scene"] = first_name;
res["current_program_scene"] = first_name;
res["sources"] = out_sources;
res["name"] = in["name"];
out = res;
}
static void create_string(const string &name, Json::object &out,
const string &data)
{
string str = StringReplace(data, "\\\\", "/");
out[name] = str;
}
static void create_string_obj(const string &data, Json::array &arr)
{
Json::object obj = Json::object{};
create_string("value", obj, data);
arr.push_back(obj);
}
static void create_double(const string &name, Json::object &out,
const string &data)
{
double d = atof(data.c_str());
out[name] = d;
}
static void create_int(const string &name, Json::object &out,
const string &data)
{
int i = atoi(data.c_str());
out[name] = i;
}
static void create_data_item(Json::object &out, const string &line)
{
size_t end_pos = line.find(':') - 1;
if (end_pos == string::npos)
return;
size_t start_pos = 0;
while (line[start_pos] == ' ')
start_pos++;
string name = line.substr(start_pos, end_pos - start_pos);
const char *c_name = name.c_str();
string first = line.substr(end_pos + 3);
if ((first[0] >= 'A' && first[0] <= 'Z') ||
(first[0] >= 'a' && first[0] <= 'z') || first[0] == '\\' ||
first[0] == '/') {
if (out.find(c_name) != out.end()) {
Json::array arr = out[c_name].array_items();
if (out[c_name].is_string()) {
Json::array new_arr = Json::array{};
string str = out[c_name].string_value();
create_string_obj(str, new_arr);
arr = new_arr;
}
create_string_obj(first, arr);
out[c_name] = arr;
} else {
create_string(c_name, out, first);
}
} else if (first[0] == '"') {
string str = first.substr(1, first.size() - 2);
if (out.find(c_name) != out.end()) {
Json::array arr = out[c_name].array_items();
if (out[c_name].is_string()) {
Json::array new_arr = Json::array{};
string str1 = out[c_name].string_value();
create_string_obj(str1, new_arr);
arr = new_arr;
}
create_string_obj(str, arr);
out[c_name] = arr;
} else {
create_string(c_name, out, str);
}
} else if (first.find('.') != string::npos) {
create_double(c_name, out, first);
} else {
create_int(c_name, out, first);
}
}
static Json::object create_object(Json::object &out, string &line, string &src);
static Json::array create_sources(Json::object &out, string &line, string &src)
{
Json::array res = Json::array{};
line = ReadLine(src);
size_t l_len = line.size();
while (line != "" && line[l_len - 1] != '}') {
size_t end_pos = line.find(':');
if (end_pos == string::npos)
return Json::array{};
size_t start_pos = 0;
while (line[start_pos] == ' ')
start_pos++;
string name = line.substr(start_pos, end_pos - start_pos - 1);
Json::object nul = Json::object();
Json::object source = create_object(nul, line, src);
source["name"] = name;
res.push_back(source);
line = ReadLine(src);
l_len = line.size();
}
if (!out.empty())
out["sources"] = res;
return res;
}
static Json::object create_object(Json::object &out, string &line, string &src)
{
size_t end_pos = line.find(':');
if (end_pos == string::npos)
return Json::object{};
size_t start_pos = 0;
while (line[start_pos] == ' ')
start_pos++;
string name = line.substr(start_pos, end_pos - start_pos - 1);
Json::object res = Json::object{};
line = ReadLine(src);
size_t l_len = line.size() - 1;
while (line != "" && line[l_len] != '}') {
start_pos = 0;
while (line[start_pos] == ' ')
start_pos++;
if (line.substr(start_pos, 7) == "sources")
create_sources(res, line, src);
else if (line[l_len] == '{')
create_object(res, line, src);
else
create_data_item(res, line);
line = ReadLine(src);
l_len = line.size() - 1;
}
if (!out.empty())
out[name] = res;
return res;
}
string ClassicImporter::Name(const string &path)
{
return GetFilenameFromPath(path);
}
int ClassicImporter::ImportScenes(const string &path, string &name, Json &res)
{
BPtr<char> file_data = os_quick_read_utf8_file(path.c_str());
if (!file_data)
return IMPORTER_FILE_WONT_OPEN;
string sc_name = GetFilenameFromPath(path);
if (name == "")
name = sc_name;
Json::object data = Json::object{};
data["name"] = name;
string file = file_data.Get();
string line = ReadLine(file);
while (line != "" && line[0] != '\0') {
string key = line != "global sources : {" ? "scenes"
: "globals";
Json::array arr = create_sources(data, line, file);
data[key] = arr;
line = ReadLine(file);
}
Json sc = data;
translate_sc(sc, res);
return IMPORTER_SUCCESS;
}
bool ClassicImporter::Check(const string &path)
{
BPtr<char> file_data = os_quick_read_utf8_file(path.c_str());
if (!file_data)
return false;
bool check = false;
if (strncmp(file_data, "scenes : {\r\n", 12) == 0)
check = true;
return check;
}
OBSImporterFiles ClassicImporter::FindFiles()
{
OBSImporterFiles res;
#ifdef _WIN32
char dst[512];
int found = os_get_config_path(dst, 512, "OBS\\sceneCollection\\");
if (found == -1)
return res;
os_dir_t *dir = os_opendir(dst);
struct os_dirent *ent;
while ((ent = os_readdir(dir)) != NULL) {
if (ent->directory || *ent->d_name == '.')
continue;
string name = ent->d_name;
size_t pos = name.find(".xconfig");
if (pos != -1 && pos == name.length() - 8) {
string path = dst + name;
res.push_back(path);
}
}
os_closedir(dir);
#endif
return res;
}

104
UI/importers/importers.cpp Normal file
View file

@ -0,0 +1,104 @@
/******************************************************************************
Copyright (C) 2019-2020 by Dillon Pentz <dillon@vodbox.io>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
******************************************************************************/
#include "importers.hpp"
#include <memory>
using namespace std;
using namespace json11;
vector<unique_ptr<Importer>> importers;
void ImportersInit()
{
importers.clear();
importers.push_back(make_unique<StudioImporter>());
importers.push_back(make_unique<ClassicImporter>());
importers.push_back(make_unique<SLImporter>());
importers.push_back(make_unique<XSplitImporter>());
}
int ImportSCFromProg(const string &path, string &name, const string &program,
Json &res)
{
if (!os_file_exists(path.c_str())) {
return IMPORTER_FILE_NOT_FOUND;
}
for (size_t i = 0; i < importers.size(); i++) {
if (program == importers[i]->Prog()) {
return importers[i]->ImportScenes(path, name, res);
}
}
return IMPORTER_UNKNOWN_ERROR;
}
int ImportSC(const string &path, std::string &name, Json &res)
{
if (!os_file_exists(path.c_str())) {
return IMPORTER_FILE_NOT_FOUND;
}
string prog = DetectProgram(path);
if (prog == "Null") {
return IMPORTER_FILE_NOT_RECOGNISED;
}
return ImportSCFromProg(path, name, prog, res);
}
string DetectProgram(const string &path)
{
if (!os_file_exists(path.c_str())) {
return "Null";
}
for (size_t i = 0; i < importers.size(); i++) {
if (importers[i]->Check(path)) {
return importers[i]->Prog();
}
}
return "Null";
}
string GetSCName(const string &path, const string &prog)
{
for (size_t i = 0; i < importers.size(); i++) {
if (importers[i]->Prog() == prog) {
return importers[i]->Name(path);
}
}
return "Null";
}
OBSImporterFiles ImportersFindFiles()
{
OBSImporterFiles f;
for (size_t i = 0; i < importers.size(); i++) {
OBSImporterFiles f2 = importers[i]->FindFiles();
if (f2.size() != 0) {
f.insert(f.end(), f2.begin(), f2.end());
}
}
return f;
}

173
UI/importers/importers.hpp Normal file
View file

@ -0,0 +1,173 @@
/******************************************************************************
Copyright (C) 2019-2020 by Dillon Pentz <dillon@vodbox.io>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
******************************************************************************/
#pragma once
#include "obs.hpp"
#include "json11.hpp"
#include <util/platform.h>
#include <util/util.hpp>
#include <string>
#include <vector>
enum obs_importer_responses {
IMPORTER_SUCCESS,
IMPORTER_FILE_NOT_FOUND,
IMPORTER_FILE_NOT_RECOGNISED,
IMPORTER_FILE_WONT_OPEN,
IMPORTER_ERROR_DURING_CONVERSION,
IMPORTER_UNKNOWN_ERROR,
IMPORTER_NOT_FOUND
};
typedef std::vector<std::string> OBSImporterFiles;
class Importer {
public:
virtual ~Importer() {}
virtual std::string Prog() { return "Null"; };
virtual int ImportScenes(const std::string &path, std::string &name,
json11::Json &res) = 0;
virtual bool Check(const std::string &path) = 0;
virtual std::string Name(const std::string &path) = 0;
virtual OBSImporterFiles FindFiles()
{
OBSImporterFiles f;
return f;
};
};
class ClassicImporter : public Importer {
public:
std::string Prog() { return "OBSClassic"; };
int ImportScenes(const std::string &path, std::string &name,
json11::Json &res);
bool Check(const std::string &path);
std::string Name(const std::string &path);
OBSImporterFiles FindFiles();
};
class StudioImporter : public Importer {
public:
std::string Prog() { return "OBSStudio"; };
int ImportScenes(const std::string &path, std::string &name,
json11::Json &res);
bool Check(const std::string &path);
std::string Name(const std::string &path);
};
class SLImporter : public Importer {
public:
std::string Prog() { return "Streamlabs"; };
int ImportScenes(const std::string &path, std::string &name,
json11::Json &res);
bool Check(const std::string &path);
std::string Name(const std::string &path);
OBSImporterFiles FindFiles();
};
class XSplitImporter : public Importer {
public:
std::string Prog() { return "XSplitBroadcaster"; };
int ImportScenes(const std::string &path, std::string &name,
json11::Json &res);
bool Check(const std::string &path);
std::string Name(const std::string &path)
{
return "XSplit Import";
UNUSED_PARAMETER(path);
};
OBSImporterFiles FindFiles();
};
void ImportersInit();
std::string DetectProgram(const std::string &path);
std::string GetSCName(const std::string &path, const std::string &prog);
int ImportSCFromProg(const std::string &path, std::string &name,
const std::string &program, json11::Json &res);
int ImportSC(const std::string &path, std::string &name, json11::Json &res);
OBSImporterFiles ImportersFindFiles();
void TranslateOSStudio(json11::Json &data);
static inline std::string GetFilenameFromPath(const std::string &path)
{
#ifdef _WIN32
size_t pos = path.find_last_of('\\');
if (pos == -1 || pos < path.find_last_of('/'))
pos = path.find_last_of('/');
#else
size_t pos = path.find_last_of('/');
#endif
size_t ext = path.find_last_of('.');
if (ext < pos) {
return path.substr(pos + 1);
} else {
return path.substr(pos + 1, ext - pos - 1);
}
}
static inline std::string GetFolderFromPath(const std::string &path)
{
#ifdef _WIN32
size_t pos = path.find_last_of('\\');
if (pos == -1 || pos < path.find_last_of('/'))
pos = path.find_last_of('/');
#else
size_t pos = path.find_last_of('/');
#endif
return path.substr(0, pos + 1);
}
static inline std::string StringReplace(const std::string &in,
const std::string &search,
const std::string &rep)
{
std::string res = in;
size_t pos;
while ((pos = res.find(search)) != std::string::npos) {
res.replace(pos, search.length(), rep);
}
return res;
}
static inline std::string ReadLine(std::string &str)
{
str = StringReplace(str, "\r\n", "\n");
size_t pos = str.find('\n');
if (pos == std::string::npos)
pos = str.find(EOF);
if (pos == std::string::npos)
pos = str.find('\0');
if (pos == std::string::npos)
return "";
std::string res = str.substr(0, pos);
str = str.substr(pos + 1);
return res;
}

507
UI/importers/sl.cpp Normal file
View file

@ -0,0 +1,507 @@
/******************************************************************************
Copyright (C) 2019-2020 by Dillon Pentz <dillon@vodbox.io>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
******************************************************************************/
#include "importers.hpp"
using namespace std;
using namespace json11;
static string translate_key(const string &sl_key)
{
if (sl_key.substr(0, 6) == "Numpad" && sl_key.size() == 7) {
return "OBS_KEY_NUM" + sl_key.substr(6);
} else if (sl_key.substr(0, 3) == "Key") {
return "OBS_KEY_" + sl_key.substr(3);
} else if (sl_key.substr(0, 5) == "Digit") {
return "OBS_KEY_" + sl_key.substr(5);
} else if (sl_key[0] == 'F' && sl_key.size() < 4) {
return "OBS_KEY_" + sl_key;
}
#define add_translation(str, out) \
if (sl_key == str) { \
return out; \
}
add_translation("Backquote", "OBS_KEY_ASCIITILDE");
add_translation("Backspace", "OBS_KEY_BACKSPACE");
add_translation("Tab", "OBS_KEY_TAB");
add_translation("Space", "OBS_KEY_SPACE");
add_translation("Period", "OBS_KEY_PERIOD");
add_translation("Slash", "OBS_KEY_SLASH");
add_translation("Backslash", "OBS_KEY_BACKSLASH");
add_translation("Minus", "OBS_KEY_MINUS");
add_translation("Comma", "OBS_KEY_COMMA");
add_translation("Plus", "OBS_KEY_PLUS");
add_translation("Quote", "OBS_KEY_APOSTROPHE");
add_translation("Semicolon", "OBS_KEY_SEMICOLON");
add_translation("NumpadSubtract", "OBS_KEY_NUMMINUS");
add_translation("NumpadAdd", "OBS_KEY_NUMPLUS");
add_translation("NumpadDecimal", "OBS_KEY_NUMPERIOD");
add_translation("NumpadDivide", "OBS_KEY_NUMSLASH");
add_translation("NumpadMultiply", "OBS_KEY_NUMASTERISK");
add_translation("Enter", "OBS_KEY_RETURN");
add_translation("CapsLock", "OBS_KEY_CAPSLOCK");
add_translation("NumLock", "OBS_KEY_NUMLOCK");
add_translation("ScrollLock", "OBS_KEY_SCROLLLOCK");
add_translation("Pause", "OBS_KEY_PAUSE");
add_translation("Insert", "OBS_KEY_INSERT");
add_translation("Home", "OBS_KEY_HOME");
add_translation("End", "OBS_KEY_END");
add_translation("Escape", "OBS_KEY_ESCAPE");
add_translation("Delete", "OBS_KEY_DELETE");
add_translation("ArrowUp", "OBS_KEY_UP");
add_translation("ArrowDown", "OBS_KEY_DOWN");
add_translation("ArrowLeft", "OBS_KEY_LEFT");
add_translation("ArrowRight", "OBS_KEY_RIGHT");
add_translation("PageUp", "OBS_KEY_PAGEUP");
add_translation("PageDown", "OBS_KEY_PAGEDOWN");
add_translation("BracketLeft", "OBS_KEY_BRACKETLEFT");
add_translation("BracketRight", "OBS_KEY_BRACKETRIGHT");
#undef add_translation
return "";
}
static string translate_hotkey(const Json &hotkey, const string &source)
{
string name = hotkey["actionName"].string_value();
#define add_translation(in, str, out, source) \
if (in == str) { \
return out + source; \
}
add_translation(name, "TOGGLE_SOURCE_VISIBILITY_SHOW",
"libobs.show_scene_item.", source);
add_translation(name, "TOGGLE_SOURCE_VISIBILITY_HIDE",
"libobs.hide_scene_item.", source);
string empty = "";
add_translation(name, "SWITCH_TO_SCENE", "OBSBasic.SelectScene", empty);
add_translation(name, "TOGGLE_MUTE", "libobs.mute", empty);
add_translation(name, "TOGGLE_UNMUTE", "libobs.unmute", empty);
add_translation(name, "PUSH_TO_MUTE", "libobs.push-to-mute", empty);
add_translation(name, "PUSH_TO_TALK", "libobs.push-to-talk", empty);
add_translation(name, "GAME_CAPTURE_HOTKEY_START", "hotkey_start",
empty);
add_translation(name, "GAME_CAPTURE_HOTKEY_STOP", "hotkey_stop", empty);
return "";
#undef add_translation
}
static bool source_name_exists(const Json::array &sources, const string &name)
{
for (size_t i = 0; i < sources.size(); i++) {
Json item = sources[i];
string source_name = item["name"].string_value();
if (source_name == name)
return true;
}
return false;
}
static string get_source_name_from_id(const Json &root,
const Json::array &sources,
const string &id)
{
for (size_t i = 0; i < sources.size(); i++) {
Json item = sources[i];
string source_id = item["sl_id"].string_value();
if (source_id == id)
return item["name"].string_value();
}
Json::array scene_arr = root["scenes"]["items"].array_items();
for (size_t i = 0; i < scene_arr.size(); i++) {
Json item = scene_arr[i];
string source_id = item["id"].string_value();
if (source_id == id) {
string name = item["name"].string_value();
int copy = 1;
string out_name = name;
while (source_name_exists(sources, out_name))
out_name = name + "(" + to_string(copy++) + ")";
return out_name;
}
}
return "";
}
static void get_hotkey_bindings(Json::object &out_hotkeys,
const Json &in_hotkeys, const string &name)
{
Json::array hot_arr = in_hotkeys.array_items();
for (size_t i = 0; i < hot_arr.size(); i++) {
Json hotkey = hot_arr[i];
Json::array bindings = hotkey["bindings"].array_items();
Json::array out_hotkey = Json::array{};
string hotkey_name = translate_hotkey(hotkey, name);
for (size_t x = 0; x < bindings.size(); x++) {
Json binding = bindings[x];
Json modifiers = binding["modifiers"];
string key =
translate_key(binding["key"].string_value());
out_hotkey.push_back(
Json::object{{"control", modifiers["ctrl"]},
{"shift", modifiers["shift"]},
{"command", modifiers["meta"]},
{"alt", modifiers["alt"]},
{"key", key}});
}
out_hotkeys[hotkey_name] = out_hotkey;
}
}
static void get_scene_items(const Json &root, const Json::array &out_sources,
Json::object &scene, const Json::array &in)
{
int length = 0;
Json::object hotkeys = scene["hotkeys"].object_items();
Json::array out_items = Json::array{};
for (size_t i = 0; i < in.size(); i++) {
Json item = in[i];
Json in_crop = item["crop"];
string id = item["sourceId"].string_value();
string name = get_source_name_from_id(root, out_sources, id);
Json::array hotkey_items =
item["hotkeys"]["items"].array_items();
get_hotkey_bindings(hotkeys, hotkey_items, name);
out_items.push_back(Json::object{
{"name", name},
{"id", length++},
{"pos",
Json::object{{"x", item["x"]}, {"y", item["y"]}}},
{"scale", Json::object{{"x", item["scaleX"]},
{"y", item["scaleY"]}}},
{"rot", item["rotation"]},
{"visible", item["visible"]},
{"crop_top", in_crop["top"]},
{"crop_bottom", in_crop["bottom"]},
{"crop_left", in_crop["left"]},
{"crop_right", in_crop["right"]}});
}
scene["hotkeys"] = hotkeys;
scene["settings"] =
Json::object{{"items", out_items}, {"id_counter", length}};
}
static int attempt_import(const Json &root, const string &name, Json &res)
{
Json::array source_arr = root["sources"]["items"].array_items();
Json::array scenes_arr = root["scenes"]["items"].array_items();
Json::array t_arr = root["transitions"]["transitions"].array_items();
string t_id = root["transitions"]["defaultTransitionId"].string_value();
Json::array out_sources = Json::array{};
Json::array out_transitions = Json::array{};
for (size_t i = 0; i < source_arr.size(); i++) {
Json source = source_arr[i];
Json in_hotkeys = source["hotkeys"];
Json::array hotkey_items =
source["hotkeys"]["items"].array_items();
Json in_filters = source["filters"];
Json::array filter_items = in_filters["items"].array_items();
Json in_settings = source["settings"];
Json in_sync = source["syncOffset"];
int sync = (int)(in_sync["sec"].number_value() * 1000000000 +
in_sync["nsec"].number_value());
double vol = source["volume"].number_value();
bool muted = source["muted"].bool_value();
string name = source["name"].string_value();
int monitoring = (int)source["monitoringType"].int_value();
Json::object out_hotkeys = Json::object{};
get_hotkey_bindings(out_hotkeys, hotkey_items, "");
Json::array out_filters = Json::array{};
for (size_t x = 0; x < filter_items.size(); x++) {
Json::object filter = filter_items[x].object_items();
string type = filter["type"].string_value();
filter["id"] = type;
out_filters.push_back(filter);
}
int copy = 1;
string out_name = name;
while (source_name_exists(out_sources, out_name))
out_name = name + "(" + to_string(copy++) + ")";
string sl_id = source["id"].string_value();
out_sources.push_back(
Json::object{{"filters", out_filters},
{"hotkeys", out_hotkeys},
{"id", source["type"]},
{"sl_id", sl_id},
{"settings", in_settings},
{"sync", sync},
{"volume", vol},
{"muted", muted},
{"name", out_name},
{"monitoring_type", monitoring}});
}
string scene_name = "";
for (size_t i = 0; i < scenes_arr.size(); i++) {
Json scene = scenes_arr[i];
Json in_hotkeys = scene["hotkeys"];
Json::array hotkey_items = in_hotkeys["items"].array_items();
Json in_filters = scene["filters"];
Json::array filter_items = in_filters["items"].array_items();
Json in_settings = scene["settings"];
Json in_sync = scene["syncOffset"];
int sync = (int)(in_sync["sec"].number_value() * 1000000000 +
in_sync["nsec"].number_value());
double vol = scene["volume"].number_value();
bool muted = scene["muted"].bool_value();
string name = scene["name"].string_value();
int monitoring = scene["monitoringType"].int_value();
Json::object out_hotkeys = Json::object{};
get_hotkey_bindings(out_hotkeys, hotkey_items, "");
Json::array out_filters = Json::array{};
for (size_t x = 0; x < filter_items.size(); x++) {
Json::object filter = filter_items[x].object_items();
string type = filter["type"].string_value();
filter["id"] = type;
out_filters.push_back(filter);
}
int copy = 1;
string out_name = name;
while (source_name_exists(out_sources, out_name))
out_name = name + "(" + to_string(copy++) + ")";
if (scene_name == "")
scene_name = out_name;
string sl_id = scene["id"].string_value();
Json::object out =
Json::object{{"filters", out_filters},
{"hotkeys", out_hotkeys},
{"id", "scene"},
{"sl_id", sl_id},
{"settings", in_settings},
{"sync", sync},
{"volume", vol},
{"muted", muted},
{"name", out_name},
{"monitoring_type", monitoring},
{"private_settings", Json::object{}}};
Json in_items = scene["sceneItems"];
Json::array items_arr = in_items["items"].array_items();
get_scene_items(root, out_sources, out, items_arr);
out_sources.push_back(out);
}
string transition_name = "";
for (size_t i = 0; i < t_arr.size(); i++) {
Json transition = t_arr[i];
Json in_settings = transition["settings"];
int duration = transition["duration"].int_value();
string name = transition["name"].string_value();
string id = transition["id"].string_value();
if (id == t_id)
transition_name = name;
out_transitions.push_back(
Json::object{{"id", transition["type"]},
{"settings", in_settings},
{"name", name},
{"duration", duration}});
}
res = Json::object{{"sources", out_sources},
{"transitions", out_transitions},
{"current_scene", scene_name},
{"current_program_scene", scene_name},
{"current_transition", transition_name},
{"name", name == "" ? "Streamlabs Import" : name}};
return IMPORTER_SUCCESS;
}
string SLImporter::Name(const string &path)
{
string name;
string folder = GetFolderFromPath(path);
string manifest_file = GetFilenameFromPath(path);
string manifest_path = folder + "manifest.json";
if (os_file_exists(manifest_path.c_str())) {
BPtr<char> file_data =
os_quick_read_utf8_file(manifest_path.c_str());
string err;
Json data = Json::parse(file_data, err);
if (err == "") {
Json::array collections =
data["collections"].array_items();
bool name_set = false;
for (size_t i = 0, l = collections.size(); i < l; i++) {
Json c = collections[i];
string c_id = c["id"].string_value();
string c_name = c["name"].string_value();
if (c_id == manifest_file) {
name = c_name;
name_set = true;
break;
}
}
if (!name_set) {
name = "Unknown Streamlabs Import";
}
}
} else {
name = "Unknown Streamlabs Import";
}
return name;
}
int SLImporter::ImportScenes(const string &path, string &name, Json &res)
{
BPtr<char> file_data = os_quick_read_utf8_file(path.c_str());
std::string err;
Json data = Json::parse(file_data, err);
if (err != "")
return IMPORTER_ERROR_DURING_CONVERSION;
string node_type = data["nodeType"].string_value();
int result = IMPORTER_ERROR_DURING_CONVERSION;
if (node_type == "RootNode") {
if (name == "") {
string auto_name = Name(path);
result = attempt_import(data, auto_name, res);
} else {
result = attempt_import(data, name, res);
}
}
TranslateOSStudio(res);
return result;
}
bool SLImporter::Check(const string &path)
{
bool check = false;
BPtr<char> file_data = os_quick_read_utf8_file(path.c_str());
if (file_data) {
string err;
Json root = Json::parse(file_data, err);
if (!root.is_null()) {
string node_type = root["nodeType"].string_value();
if (node_type == "RootNode")
check = true;
}
}
return check;
}
OBSImporterFiles SLImporter::FindFiles()
{
OBSImporterFiles res;
#ifdef _WIN32
char dst[512];
int found = os_get_config_path(dst, 512,
"slobs-client\\SceneCollections\\");
if (found == -1)
return res;
os_dir_t *dir = os_opendir(dst);
struct os_dirent *ent;
while ((ent = os_readdir(dir)) != NULL) {
string name = ent->d_name;
if (ent->directory || name[0] == '.' || name == "manifest.json")
continue;
size_t pos = name.find_last_of(".json");
size_t end_pos = name.size() - 1;
if (pos != -1 && pos == end_pos) {
string str = dst + name;
res.push_back(str);
}
}
os_closedir(dir);
#endif
return res;
}

212
UI/importers/studio.cpp Normal file
View file

@ -0,0 +1,212 @@
/******************************************************************************
Copyright (C) 2019-2020 by Dillon Pentz <dillon@vodbox.io>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
******************************************************************************/
#include "importers.hpp"
using namespace std;
using namespace json11;
void TranslateOSStudio(Json &res)
{
Json::object out = res.object_items();
Json::array sources = out["sources"].array_items();
for (size_t i = 0; i < sources.size(); i++) {
Json::object source = sources[i].object_items();
Json::object settings = source["settings"].object_items();
string id = source["id"].string_value();
#define DirectTranslation(before, after) \
if (id == before) { \
source["id"] = after; \
}
#define ClearTranslation(before, after) \
if (id == before) { \
source["id"] = after; \
source["settings"] = Json::object{}; \
}
#ifdef __APPLE__
DirectTranslation("text_gdiplus", "text_ft2_source");
ClearTranslation("game_capture", "syphon-input");
ClearTranslation("wasapi_input_capture",
"coreaudio_input_capture");
ClearTranslation("wasapi_output_capture",
"coreaudio_output_capture");
ClearTranslation("pulse_input_capture",
"coreaudio_input_capture");
ClearTranslation("pulse_output_capture",
"coreaudio_output_capture");
ClearTranslation("jack_output_capture",
"coreaudio_output_capture");
ClearTranslation("alsa_input_capture",
"coreaudio_input_capture");
ClearTranslation("dshow_input", "av_capture_input");
ClearTranslation("v4l2_input", "av_capture_input");
ClearTranslation("xcomposite_input", "window_capture");
if (id == "monitor_capture") {
if (settings["show_cursor"].is_null() &&
!settings["capture_cursor"].is_null()) {
bool cursor =
settings["capture_cursor"].bool_value();
settings["show_cursor"] = cursor;
}
}
DirectTranslation("xshm_input", "monitor_capture");
#elif defined(_WIN32)
DirectTranslation("text_ft2_source", "text_gdiplus");
ClearTranslation("syphon-input", "game_capture");
ClearTranslation("coreaudio_input_capture",
"wasapi_input_capture");
ClearTranslation("coreaudio_output_capture",
"wasapi_output_capture");
ClearTranslation("pulse_input_capture", "wasapi_input_capture");
ClearTranslation("pulse_output_capture",
"wasapi_output_capture");
ClearTranslation("jack_output_capture",
"wasapi_output_capture");
ClearTranslation("alsa_input_capture", "wasapi_input_capture");
ClearTranslation("av_capture_input", "dshow_input");
ClearTranslation("v4l2_input", "dshow_input");
ClearTranslation("xcomposite_input", "window_capture");
if (id == "monitor_capture" || id == "xshm_input") {
if (!settings["show_cursor"].is_null()) {
bool cursor =
settings["show_cursor"].bool_value();
settings["capture_cursor"] = cursor;
}
source["id"] = "monitor_capture";
}
#else
DirectTranslation("text_gdiplus", "text_ft2_source");
ClearTranslation("coreaudio_input_capture",
"pulse_input_capture");
ClearTranslation("coreaudio_output_capture",
"pulse_output_capture");
ClearTranslation("wasapi_input_capture", "pulse_input_capture");
ClearTranslation("wasapi_output_capture",
"pulse_output_capture");
ClearTranslation("av_capture_input", "v4l2_input");
ClearTranslation("dshow_input", "v4l2_input");
ClearTranslation("window_capture", "xcomposite_input");
if (id == "monitor_capture") {
source["id"] = "xshm_input";
if (settings["show_cursor"].is_null() &&
!settings["capture_cursor"].is_null()) {
bool cursor =
settings["capture_cursor"].bool_value();
settings["show_cursor"] = cursor;
}
}
#endif
source["settings"] = settings;
sources[i] = source;
#undef DirectTranslation
#undef ClearTranslation
}
res = out;
}
bool StudioImporter::Check(const string &path)
{
BPtr<char> file_data = os_quick_read_utf8_file(path.c_str());
string err;
Json collection = Json::parse(file_data, err);
if (err != "")
return false;
if (collection.is_null())
return false;
if (collection["sources"].is_null())
return false;
if (collection["name"].is_null())
return false;
if (collection["current_scene"].is_null())
return false;
return true;
}
string StudioImporter::Name(const string &path)
{
BPtr<char> file_data = os_quick_read_utf8_file(path.c_str());
string err;
Json d = Json::parse(file_data, err);
string name = d["name"].string_value();
return name;
}
int StudioImporter::ImportScenes(const string &path, string &name, Json &res)
{
if (!os_file_exists(path.c_str()))
return IMPORTER_FILE_NOT_FOUND;
if (!Check(path.c_str()))
return IMPORTER_FILE_NOT_RECOGNISED;
BPtr<char> file_data = os_quick_read_utf8_file(path.c_str());
string err;
Json d = Json::parse(file_data, err);
if (err != "")
return IMPORTER_ERROR_DURING_CONVERSION;
TranslateOSStudio(d);
Json::object obj = d.object_items();
if (name != "")
obj["name"] = name;
else
obj["name"] = "OBS Studio Import";
res = obj;
return IMPORTER_SUCCESS;
}

539
UI/importers/xsplit.cpp Normal file
View file

@ -0,0 +1,539 @@
/******************************************************************************
Copyright (C) 2019-2020 by Dillon Pentz <dillon@vodbox.io>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
******************************************************************************/
#include "importers.hpp"
#include <ctype.h>
#include <QDomDocument>
using namespace std;
using namespace json11;
static int hex_string_to_int(string str)
{
int res = 0;
if (str[0] == '#')
str = str.substr(1);
for (size_t i = 0, l = str.size(); i < l; i++) {
res *= 16;
if (str[0] >= '0' && str[0] <= '9')
res += str[0] - '0';
else
res += str[0] - 'A' + 10;
str = str.substr(1);
}
return res;
}
static Json::object parse_text(QString &config)
{
int start = config.indexOf("*{");
config = config.mid(start + 1);
config.replace("\\", "/");
string err;
Json data = Json::parse(config.toStdString(), err);
if (err != "")
return Json::object{};
string outline = data["outline"].string_value();
int out = 0;
if (outline == "thick")
out = 20;
else if (outline == "thicker")
out = 40;
else if (outline == "thinner")
out = 5;
else if (outline == "thin")
out = 10;
string valign = data["vertAlign"].string_value();
if (valign == "middle")
valign = "center";
Json font = Json::object{{"face", data["fontStyle"]}, {"size", 200}};
return Json::object{
{"text", data["text"]},
{"font", font},
{"outline", out > 0},
{"outline_size", out},
{"outline_color",
hex_string_to_int(data["outlineColor"].string_value())},
{"color", hex_string_to_int(data["color"].string_value())},
{"align", data["textAlign"]},
{"valign", valign},
{"alpha", data["opacity"]}};
}
static Json::array parse_playlist(QString &playlist)
{
Json::array out = Json::array{};
while (true) {
int end = playlist.indexOf('*');
QString path = playlist.left(end);
out.push_back(Json::object{{"value", path.toStdString()}});
int next = playlist.indexOf('|');
if (next == -1)
break;
playlist = playlist.mid(next + 1);
}
return out;
}
static void parse_media_types(QDomNamedNodeMap &attr, Json::object &source,
Json::object &settings)
{
QString playlist = attr.namedItem("FilePlaylist").nodeValue();
if (playlist != "") {
source["id"] = "vlc_source";
settings["playlist"] = parse_playlist(playlist);
QString end_op = attr.namedItem("OpWhenFinished").nodeValue();
if (end_op == "2")
settings["loop"] = true;
} else {
QString url = attr.namedItem("item").nodeValue();
int sep = url.indexOf("://");
if (sep != -1) {
QString prot = url.left(sep);
if (prot == "smlndi") {
source["id"] = "ndi_source";
} else {
source["id"] = "ffmpeg_source";
int info = url.indexOf("\\");
QString input;
if (info != -1) {
input = url.left(info);
} else {
input = url;
}
settings["input"] = input.toStdString();
settings["is_local_file"] = false;
}
} else {
source["id"] = "ffmpeg_source";
settings["local_file"] =
url.replace("\\", "/").toStdString();
settings["is_local_file"] = true;
}
}
}
static Json::object parse_slideshow(QString &config)
{
int start = config.indexOf("images\":[");
if (start == -1)
return Json::object{};
config = config.mid(start + 8);
config.replace("\\\\", "/");
int end = config.indexOf(']');
if (end == -1)
return Json::object{};
string arr = config.left(end + 1).toStdString();
string err;
Json::array files = Json::parse(arr, err).array_items();
if (err != "")
return Json::object{};
Json::array files_out = Json::array{};
for (size_t i = 0; i < files.size(); i++) {
string file = files[i].string_value();
files_out.push_back(Json::object{{"value", file}});
}
QString options = config.mid(end + 1);
options[0] = '{';
Json opt = Json::parse(options.toStdString(), err);
if (err != "")
return Json::object{};
return Json::object{{"randomize", opt["random"]},
{"slide_time",
opt["delay"].number_value() * 1000 + 700},
{"files", files_out}};
}
static bool source_name_exists(const string &name, const Json::array &sources)
{
for (size_t i = 0; i < sources.size(); i++) {
if (sources.at(i)["name"].string_value() == name)
return true;
}
return false;
}
static Json get_source_with_id(const string &src_id, const Json::array &sources)
{
for (size_t i = 0; i < sources.size(); i++) {
if (sources.at(i)["src_id"].string_value() == src_id)
return sources.at(i);
}
return nullptr;
}
static void parse_items(QDomNode &item, Json::array &items,
Json::array &sources)
{
while (!item.isNull()) {
QDomNamedNodeMap attr = item.attributes();
QString srcid = attr.namedItem("srcid").nodeValue();
double vol = attr.namedItem("volume").nodeValue().toDouble();
int type = attr.namedItem("type").nodeValue().toInt();
string name;
Json::object settings;
Json::object source;
string temp_name;
int x = 0;
Json exists = get_source_with_id(srcid.toStdString(), sources);
if (!exists.is_null()) {
name = exists["name"].string_value();
goto skip;
}
name = attr.namedItem("cname").nodeValue().toStdString();
if (name == "" || name[0] == '\0')
name = attr.namedItem("name").nodeValue().toStdString();
temp_name = name;
while (source_name_exists(temp_name, sources)) {
string new_name = name + " " + to_string(x++);
temp_name = new_name;
}
name = temp_name;
settings = Json::object{};
source = Json::object{{"name", name},
{"src_id", srcid.toStdString()},
{"volume", vol}};
/** type=1 means Media of some kind (Video Playlist, RTSP,
RTMP, NDI or Media File).
type=2 means either a DShow or WASAPI source.
type=4 means an Image source.
type=5 means either a Display or Window Capture.
type=7 means a Game Capture.
type=8 means rendered with a browser, which includes:
Web Page, Image Slideshow, Text.
type=11 means another Scene. **/
if (type == 1) {
parse_media_types(attr, source, settings);
} else if (type == 2) {
QString audio = attr.namedItem("itemaudio").nodeValue();
if (audio == "") {
source["id"] = "dshow_input";
} else {
source["id"] = "wasapi_input_capture";
int dev = audio.indexOf("\\wave:") + 6;
QString res =
"{0.0.1.00000000}." + audio.mid(dev);
res = res.toLower();
settings["device_id"] = res.toStdString();
}
} else if (type == 4) {
source["id"] = "image_source";
QString path = attr.namedItem("item").nodeValue();
path.replace("\\", "/");
settings["file"] = path.toStdString();
} else if (type == 5) {
QString opt = attr.namedItem("item").nodeValue();
QDomDocument options;
options.setContent(opt);
QDomNode el = options.documentElement();
QDomNamedNodeMap o_attr = el.attributes();
QString display =
o_attr.namedItem("desktop").nodeValue();
if (display != "") {
source["id"] = "monitor_capture";
int cursor = attr.namedItem("ScrCapShowMouse")
.nodeValue()
.toInt();
settings["capture_cursor"] = cursor == 1;
} else {
source["id"] = "window_capture";
QString exec =
o_attr.namedItem("module").nodeValue();
QString window =
o_attr.namedItem("window").nodeValue();
QString _class =
o_attr.namedItem("class").nodeValue();
int pos = exec.lastIndexOf('\\');
if (_class == "") {
_class = "class";
}
QString res = window + ":" + _class + ":" +
exec.mid(pos + 1);
settings["window"] = res.toStdString();
settings["priority"] = 2;
}
} else if (type == 7) {
QString opt = attr.namedItem("item").nodeValue();
opt.replace("&lt;", "<");
opt.replace("&gt;", ">");
opt.replace("&quot;", "\"");
QDomDocument doc;
doc.setContent(opt);
QDomNode el = doc.documentElement();
QDomNamedNodeMap o_attr = el.attributes();
QString name = o_attr.namedItem("wndname").nodeValue();
QString exec =
o_attr.namedItem("imagename").nodeValue();
QString res = name = "::" + exec;
source["id"] = "game_capture";
settings["window"] = res.toStdString();
settings["capture_mode"] = "window";
} else if (type == 8) {
QString plugin = attr.namedItem("item").nodeValue();
if (plugin.startsWith(
"html:plugin:imageslideshowplg*")) {
source["id"] = "slideshow";
settings = parse_slideshow(plugin);
} else if (plugin.startsWith("html:plugin:titleplg")) {
source["id"] = "text_gdiplus";
settings = parse_text(plugin);
} else if (plugin.startsWith("http")) {
source["id"] = "browser_source";
int end = plugin.indexOf('*');
settings["url"] =
plugin.left(end).toStdString();
}
} else if (type == 11) {
QString id = attr.namedItem("item").nodeValue();
Json source =
get_source_with_id(id.toStdString(), sources);
name = source["name"].string_value();
goto skip;
}
source["settings"] = settings;
sources.push_back(source);
skip:
struct obs_video_info ovi;
obs_get_video_info(&ovi);
int width = ovi.base_width;
int height = ovi.base_height;
double pos_left =
attr.namedItem("pos_left").nodeValue().toDouble();
double pos_right =
attr.namedItem("pos_right").nodeValue().toDouble();
double pos_top =
attr.namedItem("pos_top").nodeValue().toDouble();
double pos_bottom =
attr.namedItem("pos_bottom").nodeValue().toDouble();
bool visible = attr.namedItem("visible").nodeValue() == "1";
Json out_item = Json::object{
{"bounds_type", 2},
{"pos", Json::object{{"x", pos_left * width},
{"y", pos_top * height}}},
{"bounds",
Json::object{{"x", (pos_right - pos_left) * width},
{"y", (pos_bottom - pos_top) * height}}},
{"name", name},
{"visible", visible}};
items.push_back(out_item);
item = item.nextSibling();
}
}
static Json::object parse_scenes(QDomElement &scenes)
{
Json::array sources = Json::array{};
QString first = "";
QDomNode in_scene = scenes.firstChild();
while (!in_scene.isNull()) {
QString type = in_scene.nodeName();
if (type == "placement") {
QDomNamedNodeMap attr = in_scene.attributes();
QString name = attr.namedItem("name").nodeValue();
QString id = attr.namedItem("id").nodeValue();
if (first == "")
first = name;
Json out = Json::object{
{"id", "scene"},
{"name", name.toStdString().c_str()},
{"src_id", id.toStdString().c_str()}};
sources.push_back(out);
}
in_scene = in_scene.nextSibling();
}
in_scene = scenes.firstChild();
for (size_t i = 0, l = sources.size(); i < l; i++) {
Json::object source = sources[i].object_items();
Json::array items = Json::array{};
QDomNode firstChild = in_scene.firstChild();
parse_items(firstChild, items, sources);
Json settings = Json::object{{"items", items},
{"id_counter", (int)items.size()}};
source["settings"] = settings;
sources[i] = source;
in_scene = in_scene.nextSibling();
}
return Json::object{{"sources", sources},
{"current_scene", first.toStdString()},
{"current_program_scene", first.toStdString()}};
}
int XSplitImporter::ImportScenes(const string &path, string &name,
json11::Json &res)
{
if (name == "")
name = "XSplit Import";
BPtr<char> file_data = os_quick_read_utf8_file(path.c_str());
if (!file_data)
return IMPORTER_FILE_WONT_OPEN;
QDomDocument doc;
doc.setContent(QString(file_data));
QDomElement docElem = doc.documentElement();
Json::object r = parse_scenes(docElem);
r["name"] = name;
res = r;
return IMPORTER_SUCCESS;
}
bool XSplitImporter::Check(const string &path)
{
bool check = false;
BPtr<char> file_data = os_quick_read_utf8_file(path.c_str());
if (!file_data)
return false;
string pos = file_data.Get();
string line = ReadLine(pos);
while (line != "") {
if (line.substr(0, 5) == "<?xml") {
line = ReadLine(pos);
} else {
if (line.substr(0, 14) == "<configuration") {
check = true;
}
break;
}
}
return check;
}
OBSImporterFiles XSplitImporter::FindFiles()
{
OBSImporterFiles res;
#ifdef _WIN32
char dst[512];
int found = os_get_program_data_path(
dst, 512, "SplitMediaLabs\\XSplit\\Presentation2.0\\");
if (found == -1)
return res;
os_dir_t *dir = os_opendir(dst);
struct os_dirent *ent;
while ((ent = os_readdir(dir)) != NULL) {
string name = ent->d_name;
if (ent->directory || name[0] == '.')
continue;
if (name == "Placements.bpres") {
string str = dst + name;
res.push_back(str);
break;
}
}
os_closedir(dir);
#endif
return res;
}