New upstream version 25.0.3+dfsg1
This commit is contained in:
parent
04fe0efc67
commit
8b2e5f2130
569 changed files with 62491 additions and 5875 deletions
584
UI/importers/classic.cpp
Normal file
584
UI/importers/classic.cpp
Normal 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
104
UI/importers/importers.cpp
Normal 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
173
UI/importers/importers.hpp
Normal 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
507
UI/importers/sl.cpp
Normal 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
212
UI/importers/studio.cpp
Normal 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
539
UI/importers/xsplit.cpp
Normal 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("<", "<");
|
||||
opt.replace(">", ">");
|
||||
opt.replace(""", "\"");
|
||||
|
||||
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;
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue