Compare commits

..

2 Commits

Author SHA1 Message Date
WarmUpTill
5a69e774c2 Fix warnings 2025-12-11 18:43:49 +01:00
WarmUpTill
2389f37ed6 Add temp var for last recording save path 2025-12-11 17:22:37 +01:00
35 changed files with 140 additions and 343 deletions

View File

@@ -6,18 +6,18 @@ You have the option to ...
Both methods require [CMake](https://cmake.org/download/).
The plugin can be compiled for OBS 31 and above, although using the latest version of OBS is recommended to support all features.
The plugin can be compiled for OBS 27 and above, although using the latest version of OBS is recommended to support all features.
## Compiling in tree (recommended for development)
This section assumes that you have a working [OBS Studio development environment](https://obsproject.com/wiki/Building-OBS-Studio).
Add the "SceneSwitcher" source directory to your obs-studio source directory under obs-studio/plugins:
Add the "SceneSwitcher" source directory to your obs-studio source directory under obs-studio/UI/frontend-plugins/:
```
cd obs-studio/plugins
cd obs-studio/UI/frontend-plugins/
git clone --recursive https://github.com/WarmUpTill/SceneSwitcher.git
```
Then modify the obs-studio/plugins/CMakeLists.txt and add an entry for the scene switcher:
Then modify the obs-studio/UI/frontend-plugins/CMakeLists.txt Example and add an entry for the scene switcher:
```
add_subdirectory(SceneSwitcher)
```

View File

@@ -44,8 +44,6 @@ AdvSceneSwitcher.generalTab.generalBehavior.warnPluginLoadFailureMessage="<html>
AdvSceneSwitcher.generalTab.generalBehavior.warnCorruptedInstallMessage="The plugin installation seems to be corrupted and might crash!\nPlease make sure the plugin was installed correctly!"
AdvSceneSwitcher.generalTab.generalBehavior.hideLegacyTabs="Hide tabs which can be represented via macros"
AdvSceneSwitcher.generalTab.generalBehavior.alwaysShowMacroSearch="Always show macro search"
AdvSceneSwitcher.generalTab.generalBehavior.alwaysShowFeatureTabs="Always show all feature tabs"
AdvSceneSwitcher.generalTab.generalBehavior.alwaysShowFeatureTabs.tooltip="Show tabs that are normally hidden until first used in a macro (e.g. Action Queues, Variables, Twitch, Websockets)."
AdvSceneSwitcher.generalTab.matchBehavior="Match behavior"
AdvSceneSwitcher.generalTab.priority="Priority"
AdvSceneSwitcher.generalTab.priority.description="Switching methods priority (Highest priority is at the top)"
@@ -1258,14 +1256,12 @@ AdvSceneSwitcher.action.twitch.type.chat.emoteOnly.disable="Disable chat's emote
AdvSceneSwitcher.action.twitch.type.chat.sendMessage="Send chat message"
AdvSceneSwitcher.action.twitch.type.user.getInfo="Get user information"
AdvSceneSwitcher.action.twitch.type.reward.getInfo="Get channel points reward information"
AdvSceneSwitcher.action.twitch.type.channel.getInfo="Get channel information"
AdvSceneSwitcher.action.twitch.reward.toggleControl="Toggle reward name / variable selection control"
AdvSceneSwitcher.action.twitch.categorySelectionDisabled="Cannot select category without selecting a Twitch account first!"
AdvSceneSwitcher.action.twitch.layout.default="On{{account}}{{actions}}{{streamTitle}}{{category}}{{markerDescription}}{{clipHasDelay}}{{duration}}{{announcementColor}}{{channel}}{{pointsReward}}"
AdvSceneSwitcher.action.twitch.layout.chat="Using account{{account}}{{actions}}on{{channel}}"
AdvSceneSwitcher.action.twitch.layout.channel.getInfo="Using account{{account}}{{actions}}of{{channel}}"
AdvSceneSwitcher.action.twitch.layout.user.getInfo="Using account{{account}}{{actions}}for{{userInfoQueryType}}{{userLogin}}{{userId}}"
AdvSceneSwitcher.action.twitch.layout.reward.getInfo="Using account{{account}}{{actions}}for channel{{channel}}{{pointsReward}}{{rewardVariable}}{{toggleRewardSelection}}"
AdvSceneSwitcher.action.twitch.entry.default="On{{account}}{{actions}}{{streamTitle}}{{category}}{{markerDescription}}{{clipHasDelay}}{{duration}}{{announcementColor}}{{channel}}{{pointsReward}}"
AdvSceneSwitcher.action.twitch.entry.chat="Using account{{account}}{{actions}}on{{channel}}"
AdvSceneSwitcher.action.twitch.entry.user.getInfo="Using account{{account}}{{actions}}for{{userInfoQueryType}}{{userLogin}}{{userId}}"
AdvSceneSwitcher.action.twitch.entry.reward.getInfo="Using account{{account}}{{actions}}for channel{{channel}}{{pointsReward}}{{rewardVariable}}{{toggleRewardSelection}}"
AdvSceneSwitcher.action.twitch.title.title="Enter title"
AdvSceneSwitcher.action.twitch.marker.description="Describe marker"
AdvSceneSwitcher.action.twitch.clip.hasDelay="Add a slight delay before capturing the clip"

View File

@@ -1193,10 +1193,10 @@ AdvSceneSwitcher.action.twitch.type.user.getInfo="ユーザー情報を取得"
AdvSceneSwitcher.action.twitch.type.reward.getInfo="チャンネルポイントリワード情報を取得"
AdvSceneSwitcher.action.twitch.reward.toggleControl="リワード名/変数選択コントロールの切り替え"
AdvSceneSwitcher.action.twitch.categorySelectionDisabled="Twitchアカウントを選択しないとカテゴリを選択できません"
; AdvSceneSwitcher.action.twitch.layout.default="On{{account}}{{actions}}{{streamTitle}}{{category}}{{markerDescription}}{{clipHasDelay}}{{duration}}{{announcementColor}}{{channel}}"
AdvSceneSwitcher.action.twitch.layout.chat="{{channel}}でアカウント{{account}}{{actions}}を使用しています"
AdvSceneSwitcher.action.twitch.layout.user.getInfo="{{userInfoQueryType}}{{userLogin}}{{userId}}でアカウント{{account}}{{actions}}を使用"
AdvSceneSwitcher.action.twitch.layout.reward.getInfo="チャンネル{{channel}}{{pointsReward}}{{rewardVariable}}{{toggleRewardSelection}}でアカウント{{account}}{{actions}}を使用"
; AdvSceneSwitcher.action.twitch.entry.default="On{{account}}{{actions}}{{streamTitle}}{{category}}{{markerDescription}}{{clipHasDelay}}{{duration}}{{announcementColor}}{{channel}}"
AdvSceneSwitcher.action.twitch.entry.chat="{{channel}}でアカウント{{account}}{{actions}}を使用しています"
AdvSceneSwitcher.action.twitch.entry.user.getInfo="{{userInfoQueryType}}{{userLogin}}{{userId}}でアカウント{{account}}{{actions}}を使用"
AdvSceneSwitcher.action.twitch.entry.reward.getInfo="チャンネル{{channel}}{{pointsReward}}{{rewardVariable}}{{toggleRewardSelection}}でアカウント{{account}}{{actions}}を使用"
AdvSceneSwitcher.action.twitch.title.title="タイトルを入力"
AdvSceneSwitcher.action.twitch.marker.description="マーカーの説明"
AdvSceneSwitcher.action.twitch.clip.hasDelay="クリップをキャプチャする前にわずかな遅延を追加します"

View File

@@ -1065,8 +1065,8 @@ AdvSceneSwitcher.action.twitch.type.chat.emoteOnly.enable="Habilitar modo apenas
AdvSceneSwitcher.action.twitch.type.chat.emoteOnly.disable="Desabilitar modo apenas emotes no chat"
AdvSceneSwitcher.action.twitch.type.chat.sendMessage="Enviar mensagem de chat"
AdvSceneSwitcher.action.twitch.categorySelectionDisabled="Não é possível selecionar categoria sem selecionar uma conta Twitch primeiro!"
AdvSceneSwitcher.action.twitch.layout.default="Em{{account}}{{actions}}{{streamTitle}}{{category}}{{markerDescription}}{{clipHasDelay}}{{duration}}{{announcementColor}}{{channel}}{{pointsReward}}"
AdvSceneSwitcher.action.twitch.layout.chat="Usando conta{{account}}{{actions}}em{{channel}}"
AdvSceneSwitcher.action.twitch.entry.default="Em{{account}}{{actions}}{{streamTitle}}{{category}}{{markerDescription}}{{clipHasDelay}}{{duration}}{{announcementColor}}{{channel}}{{pointsReward}}"
AdvSceneSwitcher.action.twitch.entry.chat="Usando conta{{account}}{{actions}}em{{channel}}"
AdvSceneSwitcher.action.twitch.title.title="Digite o título"
AdvSceneSwitcher.action.twitch.marker.description="Descreva o marcador"
AdvSceneSwitcher.action.twitch.clip.hasDelay="Adicionar um leve atraso antes de capturar o clipe"

View File

@@ -1139,10 +1139,10 @@ AdvSceneSwitcher.action.twitch.type.user.getInfo="获取用户信息"
AdvSceneSwitcher.action.twitch.type.reward.getInfo="获取频道积分奖励信息"
AdvSceneSwitcher.action.twitch.reward.toggleControl="切换奖励名称/变量选择控制"
AdvSceneSwitcher.action.twitch.categorySelectionDisabled="在未选择 Twitch 帐户的情况下无法选择类别!"
AdvSceneSwitcher.action.twitch.layout.default="{{account}}{{actions}}{{streamTitle}}{{category}}{{markerDescription}}{{clipHasDelay}}{{duration}}{{announcementColor}}{{channel}}{{pointsReward}}"
AdvSceneSwitcher.action.twitch.layout.chat="使用账户{{account}}{{actions}}{{channel}}"
AdvSceneSwitcher.action.twitch.layout.user.getInfo="使用账户{{account}}{{actions}}{{userInfoQueryType}}{{userLogin}}{{userId}}"
AdvSceneSwitcher.action.twitch.layout.reward.getInfo="使用账户{{account}}{{actions}} 频道{{channel}}{{pointsReward}}{{rewardVariable}}{{toggleRewardSelection}}"
AdvSceneSwitcher.action.twitch.entry.default="{{account}}{{actions}}{{streamTitle}}{{category}}{{markerDescription}}{{clipHasDelay}}{{duration}}{{announcementColor}}{{channel}}{{pointsReward}}"
AdvSceneSwitcher.action.twitch.entry.chat="使用账户{{account}}{{actions}}{{channel}}"
AdvSceneSwitcher.action.twitch.entry.user.getInfo="使用账户{{account}}{{actions}}{{userInfoQueryType}}{{userLogin}}{{userId}}"
AdvSceneSwitcher.action.twitch.entry.reward.getInfo="使用账户{{account}}{{actions}} 频道{{channel}}{{pointsReward}}{{rewardVariable}}{{toggleRewardSelection}}"
AdvSceneSwitcher.action.twitch.title.title="输入标题"
AdvSceneSwitcher.action.twitch.marker.description="标记描述"
AdvSceneSwitcher.action.twitch.clip.hasDelay="在捕捉视频片段前稍加延迟"

View File

@@ -68,7 +68,7 @@
<x>0</x>
<y>0</y>
<width>962</width>
<height>1160</height>
<height>1132</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout_19">
@@ -268,19 +268,9 @@
</widget>
</item>
<item>
<widget class="QCheckBox" name="alwaysShowMacroSearch">
<widget class="QCheckBox" name="uiHintsDisable">
<property name="text">
<string>AdvSceneSwitcher.generalTab.generalBehavior.alwaysShowMacroSearch</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="alwaysShowFeatureTabs">
<property name="toolTip">
<string>AdvSceneSwitcher.generalTab.generalBehavior.alwaysShowFeatureTabs.tooltip</string>
</property>
<property name="text">
<string>AdvSceneSwitcher.generalTab.generalBehavior.alwaysShowFeatureTabs</string>
<string>AdvSceneSwitcher.generalTab.generalBehavior.disableUIHints</string>
</property>
</widget>
</item>
@@ -292,9 +282,9 @@
</widget>
</item>
<item>
<widget class="QCheckBox" name="uiHintsDisable">
<widget class="QCheckBox" name="alwaysShowMacroSearch">
<property name="text">
<string>AdvSceneSwitcher.generalTab.generalBehavior.disableUIHints</string>
<string>AdvSceneSwitcher.generalTab.generalBehavior.alwaysShowMacroSearch</string>
</property>
</widget>
</item>

View File

@@ -1029,8 +1029,6 @@ void AdvSceneSwitcher::SetupGeneralTab()
setTabOrder(ui->importSettings, ui->cooldownTime);
setTabOrder(ui->cooldownTime, ui->noMatchDontSwitch);
SetupShowAllTabsCheckBox(ui->alwaysShowFeatureTabs, ui->tabWidget);
MinimizeSizeOfColumn(ui->statusLayout, 0);
setWindowTitle(windowTitle() + " - " + g_GIT_TAG);
}

View File

@@ -93,46 +93,33 @@ bool MacroActionMacro::PerformAction()
return true;
}
const auto performActionForMacro = [this](Macro *macro) {
switch (_action) {
case Action::PAUSE:
macro->SetPaused();
break;
case Action::UNPAUSE:
macro->SetPaused(false);
break;
case Action::TOGGLE_PAUSE:
macro->SetPaused(!macro->Paused());
break;
case Action::RESET_COUNTER:
macro->ResetRunCount();
break;
case Action::RUN_ACTIONS:
RunActions(macro);
break;
case Action::STOP:
macro->Stop();
break;
case Action::DISABLE_ACTION:
case Action::ENABLE_ACTION:
case Action::TOGGLE_ACTION:
AdjustActionState(macro);
break;
default:
break;
}
};
if (!IsGroupMacro(macro.get())) {
performActionForMacro(macro.get());
return true;
switch (_action) {
case Action::PAUSE:
macro->SetPaused();
break;
case Action::UNPAUSE:
macro->SetPaused(false);
break;
case Action::TOGGLE_PAUSE:
macro->SetPaused(!macro->Paused());
break;
case Action::RESET_COUNTER:
macro->ResetRunCount();
break;
case Action::RUN_ACTIONS:
RunActions(macro.get());
break;
case Action::STOP:
macro->Stop();
break;
case Action::DISABLE_ACTION:
case Action::ENABLE_ACTION:
case Action::TOGGLE_ACTION:
AdjustActionState(macro.get());
break;
default:
break;
}
auto macros = GetGroupMacroEntries(macro.get());
for (const auto &macro : macros) {
performActionForMacro(macro.get());
}
return true;
}
@@ -417,7 +404,6 @@ MacroActionMacroEdit::MacroActionMacroEdit(
populateActionTypes(_actionTypes);
_conditionMacros->HideSelectedMacro();
_conditionMacros->HideGroups();
QWidget::connect(_macros, SIGNAL(currentTextChanged(const QString &)),
this, SLOT(MacroChanged(const QString &)));

View File

@@ -315,7 +315,6 @@ MacroConditionMacroEdit::MacroConditionMacroEdit(
_actionIndex(new MacroSegmentSelection(
this, MacroSegmentSelection::Type::ACTION))
{
_macros->HideGroups();
_count->setMaximum(10000000);
populateTypeSelection(_types);
populateCounterConditionSelection(_counterConditions);

View File

@@ -117,32 +117,9 @@ GetMacroConditions(Macro *macro)
return macro->Conditions();
}
bool IsGroupMacro(Macro *macro)
std::string_view GetSceneSwitchActionId()
{
return macro && macro->IsGroup();
}
std::vector<std::shared_ptr<Macro>> GetGroupMacroEntries(Macro *macro)
{
if (!macro || !macro->IsGroup()) {
return {};
}
std::vector<std::shared_ptr<Macro>> entries;
entries.reserve(macro->GroupSize());
const auto &macros = GetTopLevelMacros();
for (auto it = macros.begin(); it < macros.end(); it++) {
if ((*it)->Name() != macro->Name()) {
continue;
}
for (uint32_t i = 1; i <= macro->GroupSize(); i++) {
entries.emplace_back(*std::next(it, i));
}
break;
}
return entries;
return MacroAction::GetDefaultID();
}
std::condition_variable &GetMacroWaitCV()

View File

@@ -20,8 +20,6 @@ class Macro;
class MacroAction;
class MacroCondition;
static const int macro_func = 10;
EXPORT std::deque<std::shared_ptr<Macro>> &GetTopLevelMacros();
std::deque<std::shared_ptr<Macro>> &GetTemporaryMacros();
EXPORT std::deque<std::shared_ptr<Macro>> GetAllMacros();
@@ -37,8 +35,9 @@ GetMacroElseActions(Macro *);
EXPORT std::optional<std::deque<std::shared_ptr<MacroCondition>>>
GetMacroConditions(Macro *);
EXPORT bool IsGroupMacro(Macro *);
EXPORT std::vector<std::shared_ptr<Macro>> GetGroupMacroEntries(Macro *);
std::string_view GetSceneSwitchActionId();
constexpr auto macro_func = 10;
EXPORT std::condition_variable &GetMacroWaitCV();
EXPORT std::condition_variable &GetMacroTransitionCV();

View File

@@ -244,9 +244,22 @@ bool MacroMatchesSearchFilter(Macro *macro)
}
if (macro->IsGroup()) {
const auto groupEntries = GetGroupMacroEntries(macro);
for (const auto &entry : groupEntries) {
if (MacroMatchesSearchFilter(entry.get())) {
std::vector<std::shared_ptr<Macro>> subItems;
subItems.reserve(macro->GroupSize());
const auto &macros = GetTopLevelMacros();
for (auto it = macros.begin(); it < macros.end(); it++) {
if ((*it)->Name() != macro->Name()) {
continue;
}
for (uint32_t i = 1; i <= macro->GroupSize(); i++) {
subItems.emplace_back(*std::next(it, i));
}
break;
}
for (const auto &item : subItems) {
if (MacroMatchesSearchFilter(item.get())) {
return true;
}
}

View File

@@ -15,6 +15,9 @@ MacroSelection::MacroSelection(QWidget *parent)
obs_module_text("AdvSceneSwitcher.selectMacro"))
{
for (const auto &m : GetTopLevelMacros()) {
if (m->IsGroup()) {
continue;
}
addItem(QString::fromStdString(m->Name()));
}
@@ -63,22 +66,6 @@ void MacroSelection::HideSelectedMacro()
qobject_cast<QListView *>(view())->setRowHidden(idx, true);
}
void MacroSelection::HideGroups()
{
for (const auto &macro : GetTopLevelMacros()) {
if (!macro->IsGroup()) {
continue;
}
int idx = findText(QString::fromStdString(macro->Name()));
if (idx == -1) {
continue;
}
qobject_cast<QListView *>(view())->setRowHidden(idx, true);
}
}
void MacroSelection::ShowAllMacros()
{
auto v = qobject_cast<QListView *>(view());

View File

@@ -15,7 +15,6 @@ public:
MacroSelection(QWidget *parent);
void SetCurrentMacro(const MacroRef &);
void HideSelectedMacro(); // Macro currently being edited
void HideGroups();
void ShowAllMacros();
private slots:

View File

@@ -147,7 +147,20 @@ void AdvSceneSwitcher::on_macroAdd_clicked()
static void addGroupSubitems(std::vector<std::shared_ptr<Macro>> &macros,
const std::shared_ptr<Macro> &group)
{
auto subitems = GetGroupMacroEntries(group.get());
std::vector<std::shared_ptr<Macro>> subitems;
subitems.reserve(group->GroupSize());
// Find all subitems
auto allMacros = GetTopLevelMacros();
for (auto it = allMacros.begin(); it < allMacros.end(); it++) {
if ((*it)->Name() != group->Name()) {
continue;
}
for (uint32_t i = 1; i <= group->GroupSize(); i++) {
subitems.emplace_back(*std::next(it, i));
}
break;
}
// Remove subitems which were already selected to avoid duplicates
for (const auto &subitem : subitems) {

View File

@@ -948,12 +948,12 @@ bool Macro::PostLoad()
bool Macro::SwitchesScene() const
{
for (const auto &a : _actions) {
if (a->GetId() == MacroAction::GetDefaultID()) {
if (a->GetId() == GetSceneSwitchActionId()) {
return true;
}
}
for (const auto &a : _elseActions) {
if (a->GetId() == MacroAction::GetDefaultID()) {
if (a->GetId() == GetSceneSwitchActionId()) {
return true;
}
}

View File

@@ -65,16 +65,13 @@ ResourceTable::ResourceTable(QWidget *parent, const QString &help,
[openSettings]() { openSettings(); });
auto settingsShortcut = new QShortcut(QKeySequence("F2"), this);
settingsShortcut->setContext(Qt::WidgetWithChildrenShortcut);
QWidget::connect(settingsShortcut, &QShortcut::activated, this,
openSettings);
auto removeShortcut = new QShortcut(QKeySequence("Del"), this);
removeShortcut->setContext(Qt::WidgetWithChildrenShortcut);
QWidget::connect(removeShortcut, &QShortcut::activated, this,
[this]() { Remove(); });
auto newShortcut = new QShortcut(QKeySequence("Ctrl+N"), this);
newShortcut->setContext(Qt::WidgetWithChildrenShortcut);
QWidget::connect(newShortcut, &QShortcut::activated, this,
[this]() { Add(); });
}

View File

@@ -1,5 +1,4 @@
#include "source-helpers.hpp"
#include "canvas-helpers.hpp"
#include <obs-frontend-api.h>
@@ -29,43 +28,15 @@ std::string GetWeakSourceName(obs_weak_source_t *weak_source)
OBSWeakSource GetWeakSourceByName(const char *name)
{
OBSSourceAutoRelease source = obs_get_source_by_name(name);
OBSWeakSource weak;
obs_source_t *source = obs_get_source_by_name(name);
if (source) {
return OBSGetWeakRef(source);
weak = obs_source_get_weak_source(source);
obs_weak_source_release(weak);
obs_source_release(source);
}
#if LIBOBS_API_VER > MAKE_SEMANTIC_VERSION(31, 1, 0)
struct SearchData {
const char *name;
OBSWeakSource &scene;
};
static const auto enumCanvasScenes = [](void *dataPtr,
obs_source_t *source) -> bool {
auto data = static_cast<SearchData *>(dataPtr);
if (strcmp(data->name, obs_source_get_name(source)) == 0) {
data->scene = OBSGetWeakRef(source);
return false;
}
return true;
};
static const auto enumCanvases = [](void *dataPtr,
obs_canvas_t *canvas) -> bool {
obs_canvas_enum_scenes(canvas, enumCanvasScenes, dataPtr);
auto data = static_cast<SearchData *>(dataPtr);
return data->scene == nullptr;
};
OBSWeakSource weakScene;
SearchData searchData{name, weakScene};
obs_enum_canvases(enumCanvases, &searchData);
return weakScene;
#else
return nullptr;
#endif
return weak;
}
OBSWeakSource GetWeakSourceByQString(const QString &name)

View File

@@ -1,7 +1,6 @@
#include "tab-helpers.hpp"
#include "log-helper.hpp"
#include "obs-module-helper.hpp"
#include "plugin-state-helpers.hpp"
#include <obs.hpp>
#include <QTabBar>
@@ -34,21 +33,6 @@ struct TabCallbacks {
static std::unordered_map<const char *, TabCallbacks> createTabCallbacks;
static int lastOpenedTab = -1;
static bool alwaysShowTabs = false;
[[maybe_unused]] static bool _ = []() {
AddPluginInitStep([]() {
AddSaveStep([](obs_data_t *data) {
obs_data_set_bool(data, "alwaysShowTabs",
alwaysShowTabs);
});
AddLoadStep([](obs_data_t *data) {
alwaysShowTabs =
obs_data_get_bool(data, "alwaysShowTabs");
});
});
return true;
}();
void SetTabVisibleByName(QTabWidget *tabWidget, bool visible,
const QString &name)
@@ -213,41 +197,7 @@ void SetupOtherTabs(QTabWidget *tabWidget)
.c_str());
tabWidget->insertTab(0, widget, tabText);
callbacks.setupTab(tabWidget);
if (alwaysShowTabs) {
tabWidget->setTabVisible(0, true);
}
}
}
void SetupShowAllTabsCheckBox(QCheckBox *checkBox, QTabWidget *tabWidget)
{
const auto stateChanged = [tabWidget](int state) {
const bool newState = (state != 0);
if (newState == alwaysShowTabs) {
return;
}
alwaysShowTabs = newState;
// Only show currently hidden tabs, but don't hide tabs which
// are already visible
if (!alwaysShowTabs) {
return;
}
for (const auto &[name, _] : createTabCallbacks) {
const auto localeKey =
std::string("AdvSceneSwitcher.") + name +
".title";
auto tabText = obs_module_text(localeKey.c_str());
SetTabVisibleByName(tabWidget, true, tabText);
}
};
checkBox->setChecked(alwaysShowTabs);
QWidget::connect(checkBox, &QCheckBox::stateChanged, checkBox,
stateChanged);
}
} // namespace advss

View File

@@ -2,8 +2,6 @@
#include "export-symbol-helper.hpp"
#include <obs-data.h>
#include <QCheckBox>
#include <QTabWidget>
namespace advss {
@@ -21,6 +19,5 @@ void SetupOtherTabs(QTabWidget *tabWidget);
void SaveTabOrder(obs_data_t *obj);
void LoadTabOrder(obs_data_t *obj);
bool MacroTabIsInFocus();
void SetupShowAllTabsCheckBox(QCheckBox *checkBox, QTabWidget *tabWidget);
} // namespace advss

View File

@@ -194,7 +194,6 @@ MacroActionSequenceEdit::MacroActionSequenceEdit(
_resetIndex(new VariableSpinBox()),
_layout(new QHBoxLayout())
{
_macros->HideGroups();
populateActionSelection(_actions);
_resetIndex->setMinimum(1);

View File

@@ -31,16 +31,15 @@ bool MacroActionTimer::PerformAction()
if (!macro) {
return true;
}
const auto updateCondition = [this](MacroCondition *condition) {
auto conditions = *GetMacroConditions(macro.get());
for (auto condition : conditions) {
if (condition->GetId() != id) {
return;
continue;
}
auto timerCondition =
dynamic_cast<MacroConditionTimer *>(condition);
dynamic_cast<MacroConditionTimer *>(condition.get());
if (!timerCondition) {
return;
continue;
}
switch (_actionType) {
@@ -60,23 +59,6 @@ bool MacroActionTimer::PerformAction()
default:
break;
}
};
const auto updateMacro = [&](Macro *macro) {
auto conditions = *GetMacroConditions(macro);
for (auto condition : conditions) {
updateCondition(condition.get());
}
};
if (!IsGroupMacro(macro.get())) {
updateMacro(macro.get());
return true;
}
auto macros = GetGroupMacroEntries(macro.get());
for (const auto &macro : macros) {
updateMacro(macro.get());
}
return true;
}
@@ -149,7 +131,7 @@ void MacroActionTimer::ResolveVariablesToFixedValues()
_duration.ResolveVariables();
}
static void populateTypeSelection(QComboBox *list)
static inline void populateTypeSelection(QComboBox *list)
{
for (const auto &[_, name] : timerActions) {
list->addItem(obs_module_text(name.c_str()));
@@ -158,11 +140,12 @@ static void populateTypeSelection(QComboBox *list)
MacroActionTimerEdit::MacroActionTimerEdit(
QWidget *parent, std::shared_ptr<MacroActionTimer> entryData)
: QWidget(parent),
_macros(new MacroSelection(parent)),
_duration(new DurationSelection(this)),
_timerAction(new QComboBox(this))
: QWidget(parent)
{
_macros = new MacroSelection(parent);
_duration = new DurationSelection();
_timerAction = new QComboBox();
populateTypeSelection(_timerAction);
QWidget::connect(_macros, SIGNAL(currentTextChanged(const QString &)),
@@ -172,13 +155,15 @@ MacroActionTimerEdit::MacroActionTimerEdit(
QWidget::connect(_timerAction, SIGNAL(currentIndexChanged(int)), this,
SLOT(ActionTypeChanged(int)));
auto layout = new QHBoxLayout;
_mainLayout = new QHBoxLayout;
std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
{"{{macros}}", _macros},
{"{{duration}}", _duration},
{"{{timerAction}}", _timerAction},
};
PlaceWidgets(obs_module_text("AdvSceneSwitcher.action.timer.entry"),
layout,
{{"{{macros}}", _macros},
{"{{duration}}", _duration},
{"{{timerAction}}", _timerAction}});
setLayout(layout);
_mainLayout, widgetPlaceholders);
setLayout(_mainLayout);
_entryData = entryData;
UpdateEntryData();

View File

@@ -58,14 +58,16 @@ private slots:
signals:
void HeaderInfoChanged(const QString &);
private:
protected:
void SetWidgetVisibility();
MacroSelection *_macros;
DurationSelection *_duration;
QComboBox *_timerAction;
std::shared_ptr<MacroActionTimer> _entryData;
private:
QHBoxLayout *_mainLayout;
bool _loading = true;
};

View File

@@ -10,7 +10,7 @@ namespace advss {
enum class HotkeyType;
bool CanSimulateKeyPresses();
void PressKeys(const std::vector<HotkeyType> &keys, int duration);
void PressKeys(const std::vector<HotkeyType> keys, int duration);
class Hotkey {
public:

View File

@@ -188,7 +188,7 @@ static const std::unordered_map<HotkeyType, long> keyTable = {
{HotkeyType::Key_NumpadEnter, XK_KP_Enter},
};
void PressKeys(const std::vector<HotkeyType> &keys, int duration)
void PressKeys(const std::vector<HotkeyType> keys, int duration)
{
if (!canSimulateKeyPresses) {
return;

View File

@@ -9,7 +9,7 @@ bool CanSimulateKeyPresses()
return canSimulateKeyPresses;
}
void PressKeys(const std::vector<HotkeyType> &, int)
void PressKeys(const std::vector<HotkeyType> keys, int duration)
{
// Not supported on MacOS
return;

View File

@@ -210,7 +210,7 @@ findListPropertyById(const obs_source_t *source, const std::string &id)
}
auto property = obs_properties_first(properties);
return {findListPropertyByIdHelper(property, id),
properties_t(properties, obs_properties_destroy)};
properties_t(nullptr, obs_properties_destroy)};
}
static std::pair<obs_property_t *, properties_t>

View File

@@ -138,7 +138,7 @@ static const std::unordered_map<HotkeyType, long> keyTable = {
{HotkeyType::Key_NumpadEnter, VK_RETURN},
};
void PressKeys(const std::vector<HotkeyType> &keys, int duration)
void PressKeys(const std::vector<HotkeyType> keys, int duration)
{
const int repeatInterval = 100;

View File

@@ -513,7 +513,7 @@ void ScriptHandler::SetVariableValue(void *, calldata_t *data)
RETURN_SUCCESS();
}
void ScriptHandler::GetRunningStatus(void *, calldata_t *data)
void ScriptHandler::GetRunningStatus(void *ctx, calldata_t *data)
{
calldata_set_bool(data, "is_running", PluginIsRunning());
}

View File

@@ -58,16 +58,14 @@ std::string TwitchChannel::GetUserID(const TwitchToken &token) const
}
OBSDataArrayAutoRelease array = obs_data_get_array(res.data, "data");
size_t count = obs_data_array_count(array);
if (count == 0) {
userIDCache[_name] = "invalid";
return "invalid";
for (size_t i = 0; i < count; i++) {
OBSDataAutoRelease arrayObj = obs_data_array_item(array, i);
std::string id = obs_data_get_string(arrayObj, "id");
userIDCache[_name] = id;
return id;
}
OBSDataAutoRelease arrayObj = obs_data_array_item(array, 0);
const std::string id = obs_data_get_string(arrayObj, "id");
userIDCache[_name] = id;
return id;
userIDCache[_name] = "invalid";
return "invalid";
}
static QDate parseRFC3339Date(const std::string &rfc3339Date)

View File

@@ -72,6 +72,7 @@ void EventSub::UnregisterInstance()
void EventSub::ConnectThread()
{
_client->reset();
_connected = true;
websocketpp::lib::error_code ec;
EventSubWSClient::connection_ptr con =
_client->get_connection(_url, ec);
@@ -112,7 +113,6 @@ void EventSub::Connect()
_thread.join();
}
_disconnect = false;
_connected = true;
_thread = std::thread(&EventSub::ConnectThread, this);
}
@@ -448,7 +448,7 @@ void EventSub::StartServerMigrationClient(const std::string &url)
auto client = std::make_unique<EventSubWSClient>();
SetupClient(*client);
client->set_open_handler([this](connection_hdl) {
client->set_open_handler([this](connection_hdl hdl) {
vblog(LOG_INFO, "Twitch EventSub migration client opened");
});

View File

@@ -83,8 +83,6 @@ const static std::map<MacroActionTwitch::Action, std::string> actionTypes = {
"AdvSceneSwitcher.action.twitch.type.user.getInfo"},
{MacroActionTwitch::Action::POINTS_REWARD_GET_INFO,
"AdvSceneSwitcher.action.twitch.type.reward.getInfo"},
{MacroActionTwitch::Action::CHANNEL_GET_INFO,
"AdvSceneSwitcher.action.twitch.type.channel.getInfo"},
};
const static std::map<MacroActionTwitch::AnnouncementColor, std::string>
@@ -324,41 +322,6 @@ void MacroActionTwitch::GetUserInfo(const std::shared_ptr<TwitchToken> &token)
});
}
void MacroActionTwitch::GetChannelInfo(const std::shared_ptr<TwitchToken> &token)
{
auto info = _channel.GetInfo(*token);
if (!info) {
return;
}
SetTempVarValue("broadcaster_user_id", info->broadcaster_id);
SetTempVarValue("broadcaster_user_login", info->broadcaster_login);
SetTempVarValue("broadcaster_user_name", info->broadcaster_name);
SetTempVarValue("language", info->broadcaster_language);
SetTempVarValue("game_id", info->game_id);
SetTempVarValue("game_name", info->game_name);
SetTempVarValue("title", info->title);
SetTempVarValue("delay", std::to_string(info->delay));
std::string tags;
for (const auto &tag : info->tags) {
tags += tag + " ";
}
if (!tags.empty()) {
tags.pop_back();
}
SetTempVarValue("tags", tags);
std::string classificationLabels;
for (const auto &label : info->content_classification_labels) {
classificationLabels += label + " ";
}
if (!classificationLabels.empty()) {
classificationLabels.pop_back();
}
SetTempVarValue("content_classification_labels", classificationLabels);
SetTempVarValue("is_branded_content",
info->is_branded_content ? "true" : "false");
}
bool MacroActionTwitch::ResolveVariableSelectionToRewardId(
const std::shared_ptr<TwitchToken> &token)
{
@@ -542,16 +505,6 @@ void MacroActionTwitch::SetupTempVars()
setupTempVarHelper("offline_image_url", ".user.getInfo");
setupTempVarHelper("created_at", ".user.getInfo");
break;
case Action::CHANNEL_GET_INFO:
setupTempVarHelper("language");
setupTempVarHelper("game_id");
setupTempVarHelper("game_name");
setupTempVarHelper("title");
setupTempVarHelper("delay");
setupTempVarHelper("tags");
setupTempVarHelper("content_classification_labels");
setupTempVarHelper("is_branded_content");
break;
default:
break;
}
@@ -610,9 +563,6 @@ bool MacroActionTwitch::PerformAction()
case MacroActionTwitch::Action::POINTS_REWARD_GET_INFO:
GetRewardInfo(token);
break;
case MacroActionTwitch::Action::CHANNEL_GET_INFO:
GetChannelInfo(token);
break;
default:
break;
}
@@ -822,7 +772,6 @@ bool MacroActionTwitch::ActionIsSupportedByToken()
{Action::WHISPER_SEND, {{"user:manage:whispers"}}},
{Action::SEND_CHAT_MESSAGE, {{"chat:edit"}}},
{Action::USER_GET_INFO, {}},
{Action::CHANNEL_GET_INFO, {}},
{Action::POINTS_REWARD_GET_INFO,
{{"channel:read:redemptions"},
{"channel:manage:redemptions"}}}};
@@ -1178,8 +1127,6 @@ void MacroActionTwitchEdit::SetWidgetVisibility()
_entryData->GetAction() ==
MacroActionTwitch::Action::CHANNEL_INFO_CONTENT_LABELS_SET);
_channel->setVisible(
_entryData->GetAction() ==
MacroActionTwitch::Action::CHANNEL_GET_INFO ||
_entryData->GetAction() ==
MacroActionTwitch::Action::RAID_START ||
_entryData->GetAction() ==
@@ -1326,23 +1273,19 @@ void MacroActionTwitchEdit::SetWidgetLayout()
switch (_entryData->GetAction()) {
case MacroActionTwitch::Action::SEND_CHAT_MESSAGE:
layoutText = obs_module_text(
"AdvSceneSwitcher.action.twitch.layout.chat");
"AdvSceneSwitcher.action.twitch.entry.chat");
break;
case MacroActionTwitch::Action::USER_GET_INFO:
layoutText = obs_module_text(
"AdvSceneSwitcher.action.twitch.layout.user.getInfo");
"AdvSceneSwitcher.action.twitch.entry.user.getInfo");
break;
case MacroActionTwitch::Action::POINTS_REWARD_GET_INFO:
layoutText = obs_module_text(
"AdvSceneSwitcher.action.twitch.layout.reward.getInfo");
break;
case MacroActionTwitch::Action::CHANNEL_GET_INFO:
layoutText = obs_module_text(
"AdvSceneSwitcher.action.twitch.layout.channel.getInfo");
"AdvSceneSwitcher.action.twitch.entry.reward.getInfo");
break;
default:
layoutText = obs_module_text(
"AdvSceneSwitcher.action.twitch.layout.default");
"AdvSceneSwitcher.action.twitch.entry.default");
break;
}

View File

@@ -142,8 +142,7 @@ public:
SEND_CHAT_MESSAGE = 5000,
// Get user info
USER_GET_INFO = 6000,
CHANNEL_GET_INFO = 6100,
USER_GET_INFO = 6000
};
enum class AnnouncementColor {
@@ -204,7 +203,6 @@ private:
void SendChatMessage(const std::shared_ptr<TwitchToken> &);
void GetUserInfo(const std::shared_ptr<TwitchToken> &);
void GetRewardInfo(const std::shared_ptr<TwitchToken> &);
void GetChannelInfo(const std::shared_ptr<TwitchToken> &token);
bool ResolveVariableSelectionToRewardId(
const std::shared_ptr<TwitchToken> &);

View File

@@ -629,7 +629,7 @@ void MacroConditionTwitch::SetTempVarValues(const ChannelInfo &info)
if (!classificationLabels.empty()) {
classificationLabels.pop_back();
}
SetTempVarValue("content_classification_labels", classificationLabels);
SetTempVarValue("content_classification_labels", tags);
SetTempVarValue("is_branded_content",
info.is_branded_content ? "true" : "false");
}

View File

@@ -829,7 +829,7 @@ void TokenGrabberThread::run()
if (_serverThread.joinable()) {
_serverThread.join();
}
_stopWaiting = false;
_stopWaiting = {false};
// Generate URI to request token
auto state = generateStateString();