123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311 |
- /******************************************************************************
- Copyright (C) 2025 by FiniteSingularity <[email protected]>
- 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 "PluginManager.hpp"
- #include "PluginManagerWindow.hpp"
- #include <OBSApp.hpp>
- #include <qt-wrappers.hpp>
- #include <widgets/OBSBasic.hpp>
- #include <QMessageBox>
- #include <nlohmann/json.hpp>
- #include <algorithm>
- #include <fstream>
- extern bool restart;
- namespace OBS {
- void addModuleToPluginManagerImpl(void *param, obs_module_t *newModule)
- {
- auto &instance = *static_cast<OBS::PluginManager *>(param);
- std::string moduleName = obs_get_module_file_name(newModule);
- moduleName = moduleName.substr(0, moduleName.rfind("."));
- if (!obs_get_module_allow_disable(moduleName.c_str()))
- return;
- const char *display_name = obs_get_module_name(newModule);
- std::string module_name = moduleName;
- const char *id = obs_get_module_id(newModule);
- const char *version = obs_get_module_version(newModule);
- auto it = std::find_if(instance.modules_.begin(), instance.modules_.end(),
- [&](OBS::ModuleInfo module) { return module.module_name == moduleName; });
- if (it == instance.modules_.end()) {
- instance.modules_.push_back({display_name ? display_name : "", module_name, id ? id : "",
- version ? version : "", true, true});
- } else {
- it->display_name = display_name ? display_name : "";
- it->module_name = module_name;
- it->id = id ? id : "";
- it->version = version ? version : "";
- }
- }
- constexpr std::string_view OBSPluginManagerPath = "obs-studio/plugin_manager";
- constexpr std::string_view OBSPluginManagerModulesFile = "modules.json";
- void PluginManager::preLoad()
- {
- loadModules_();
- disableModules_();
- }
- void PluginManager::postLoad()
- {
- // Find any new modules and add to Plugin Manager.
- obs_enum_modules(addModuleToPluginManager, this);
- // Get list of valid module types.
- addModuleTypes_();
- saveModules_();
- // Add provided features from any unloaded modules
- linkUnloadedModules_();
- }
- std::filesystem::path PluginManager::getConfigFilePath_()
- {
- std::filesystem::path path = App()->userPluginManagerSettingsLocation /
- std::filesystem::u8path(OBSPluginManagerPath) /
- std::filesystem::u8path(OBSPluginManagerModulesFile);
- return path;
- }
- void PluginManager::loadModules_()
- {
- auto modulesFile = getConfigFilePath_();
- if (std::filesystem::exists(modulesFile)) {
- std::ifstream jsonFile(modulesFile);
- nlohmann::json data;
- try {
- data = nlohmann::json::parse(jsonFile);
- } catch (const nlohmann::json::parse_error &error) {
- modules_.clear();
- blog(LOG_ERROR, "Error loading modules config file: %s", error.what());
- blog(LOG_ERROR, "Generating new config file.");
- return;
- }
- modules_.clear();
- for (auto it : data) {
- ModuleInfo obsModule;
- try {
- obsModule = {it.at("display_name"),
- it.at("module_name"),
- it.at("id"),
- it.at("version"),
- it.at("enabled"),
- it.at("enabled"),
- it.at("sources"),
- it.at("outputs"),
- it.at("encoders"),
- it.at("services"),
- {},
- {},
- {},
- {}};
- } catch (const nlohmann::json::out_of_range &error) {
- blog(LOG_WARNING, "Error loading module info: %s", error.what());
- continue;
- }
- modules_.push_back(obsModule);
- }
- }
- }
- void PluginManager::linkUnloadedModules_()
- {
- for (const auto &moduleInfo : modules_) {
- if (!moduleInfo.enabled) {
- auto obsModule = obs_get_disabled_module(moduleInfo.module_name.c_str());
- if (!obsModule) {
- continue;
- }
- for (const auto &source : moduleInfo.sources) {
- obs_module_add_source(obsModule, source.c_str());
- }
- for (const auto &output : moduleInfo.outputs) {
- obs_module_add_output(obsModule, output.c_str());
- }
- for (const auto &encoder : moduleInfo.encoders) {
- obs_module_add_encoder(obsModule, encoder.c_str());
- }
- for (const auto &service : moduleInfo.services) {
- obs_module_add_service(obsModule, service.c_str());
- }
- }
- }
- }
- void PluginManager::saveModules_()
- {
- auto modulesFile = getConfigFilePath_();
- std::ofstream outFile(modulesFile);
- nlohmann::json data = nlohmann::json::array();
- for (auto const &moduleInfo : modules_) {
- nlohmann::json modData;
- modData["display_name"] = moduleInfo.display_name;
- modData["module_name"] = moduleInfo.module_name;
- modData["id"] = moduleInfo.id;
- modData["version"] = moduleInfo.version;
- modData["enabled"] = moduleInfo.enabled;
- modData["sources"] = moduleInfo.sources;
- modData["outputs"] = moduleInfo.outputs;
- modData["encoders"] = moduleInfo.encoders;
- modData["services"] = moduleInfo.services;
- data.push_back(modData);
- }
- outFile << std::setw(4) << data << std::endl;
- }
- void PluginManager::addModuleTypes_()
- {
- const char *source_id;
- int i = 0;
- while (obs_enum_source_types(i, &source_id)) {
- i += 1;
- obs_module_t *obsModule = obs_source_get_module(source_id);
- if (!obsModule) {
- continue;
- }
- std::string moduleName = obs_get_module_file_name(obsModule);
- moduleName = moduleName.substr(0, moduleName.rfind("."));
- auto it = std::find_if(modules_.begin(), modules_.end(),
- [moduleName](ModuleInfo const &m) { return m.module_name == moduleName; });
- if (it != modules_.end()) {
- it->sourcesLoaded.push_back(source_id);
- }
- }
- const char *output_id;
- i = 0;
- while (obs_enum_output_types(i, &output_id)) {
- i += 1;
- obs_module_t *obsModule = obs_output_get_module(output_id);
- if (!obsModule) {
- continue;
- }
- std::string moduleName = obs_get_module_file_name(obsModule);
- moduleName = moduleName.substr(0, moduleName.rfind("."));
- auto it = std::find_if(modules_.begin(), modules_.end(),
- [moduleName](ModuleInfo const &m) { return m.module_name == moduleName; });
- if (it != modules_.end()) {
- it->outputsLoaded.push_back(output_id);
- }
- }
- const char *encoder_id;
- i = 0;
- while (obs_enum_encoder_types(i, &encoder_id)) {
- i += 1;
- obs_module_t *obsModule = obs_encoder_get_module(encoder_id);
- if (!obsModule) {
- continue;
- }
- std::string moduleName = obs_get_module_file_name(obsModule);
- moduleName = moduleName.substr(0, moduleName.rfind("."));
- auto it = std::find_if(modules_.begin(), modules_.end(),
- [moduleName](ModuleInfo const &m) { return m.module_name == moduleName; });
- if (it != modules_.end()) {
- it->encodersLoaded.push_back(encoder_id);
- }
- }
- const char *service_id;
- i = 0;
- while (obs_enum_service_types(i, &service_id)) {
- i += 1;
- obs_module_t *obsModule = obs_service_get_module(service_id);
- if (!obsModule) {
- continue;
- }
- std::string moduleName = obs_get_module_file_name(obsModule);
- moduleName = moduleName.substr(0, moduleName.rfind("."));
- auto it = std::find_if(modules_.begin(), modules_.end(),
- [moduleName](ModuleInfo const &m) { return m.module_name == moduleName; });
- if (it != modules_.end()) {
- it->servicesLoaded.push_back(service_id);
- }
- }
- for (auto &moduleInfo : modules_) {
- if (moduleInfo.enabledAtLaunch) {
- moduleInfo.sources = moduleInfo.sourcesLoaded;
- moduleInfo.encoders = moduleInfo.encodersLoaded;
- moduleInfo.outputs = moduleInfo.outputsLoaded;
- moduleInfo.services = moduleInfo.servicesLoaded;
- } else {
- for (auto const &source : moduleInfo.sources) {
- disabledSources_.push_back(source);
- }
- for (auto const &output : moduleInfo.outputs) {
- disabledOutputs_.push_back(output);
- }
- for (auto const &encoder : moduleInfo.encoders) {
- disabledEncoders_.push_back(encoder);
- }
- for (auto const &service : moduleInfo.services) {
- disabledServices_.push_back(service);
- }
- }
- }
- }
- void PluginManager::disableModules_()
- {
- for (const auto &moduleInfo : modules_) {
- if (!moduleInfo.enabled) {
- obs_add_disabled_module(moduleInfo.module_name.c_str());
- }
- }
- }
- void PluginManager::open()
- {
- auto main = OBSBasic::Get();
- PluginManagerWindow pluginManagerWindow(modules_, main);
- auto result = pluginManagerWindow.exec();
- if (result == QDialog::Accepted) {
- modules_ = pluginManagerWindow.result();
- saveModules_();
- bool changed = false;
- for (auto const &moduleInfo : modules_) {
- if (moduleInfo.enabled != moduleInfo.enabledAtLaunch) {
- changed = true;
- break;
- }
- }
- if (changed) {
- QMessageBox::StandardButton button =
- OBSMessageBox::question(main, QTStr("Restart"), QTStr("NeedsRestart"));
- if (button == QMessageBox::Yes) {
- restart = true;
- main->close();
- }
- }
- }
- }
- }; // namespace OBS
|