mirror of
https://github.com/WarmUpTill/SceneSwitcher.git
synced 2026-07-19 01:15:14 -05:00
Add Twitch clip and marker actions
* Refactor stream title variable name * Limit commerical duration to 180s * Set maximum stream title size to 140 chars
This commit is contained in:
parent
94416dd881
commit
107e1413ed
|
|
@ -780,10 +780,15 @@ AdvSceneSwitcher.action.sceneLock.entry="On{{scenes}}{{actions}}{{sources}}"
|
|||
AdvSceneSwitcher.action.twitch="Twitch"
|
||||
AdvSceneSwitcher.action.twitch.type.title="Set stream title"
|
||||
AdvSceneSwitcher.action.twitch.type.category="Set stream category"
|
||||
AdvSceneSwitcher.action.twitch.type.marker="Create stream marker"
|
||||
AdvSceneSwitcher.action.twitch.type.clip="Create stream clip"
|
||||
AdvSceneSwitcher.action.twitch.type.commercial="Start commercial with duration"
|
||||
AdvSceneSwitcher.action.twitch.categorySelectionDisabled="Cannot select category without selecting a Twitch account first!"
|
||||
AdvSceneSwitcher.action.twitch.entry="On{{account}}{{actions}}{{text}}{{category}}{{manualCategorySearch}}{{duration}}"
|
||||
AdvSceneSwitcher.action.twitch.entry="On{{account}}{{actions}}{{streamTitle}}{{category}}{{manualCategorySearch}}{{markerDescription}}{{clipHasDelay}}{{duration}}"
|
||||
AdvSceneSwitcher.action.twitch.tokenPermissionsInsufficient="Permissions of selected token are insufficient to perform selected action!"
|
||||
AdvSceneSwitcher.action.twitch.clip.hasDelay="Add a slight delay before capturing the clip"
|
||||
AdvSceneSwitcher.action.twitch.marker.description="Describe marker"
|
||||
AdvSceneSwitcher.action.twitch.title.title="Enter title"
|
||||
|
||||
; Hotkey
|
||||
AdvSceneSwitcher.hotkey.startSwitcherHotkey="Start the Advanced Scene Switcher"
|
||||
|
|
@ -936,6 +941,7 @@ AdvSceneSwitcher.twitchToken.analytics.readGames="View analytics data for the ga
|
|||
AdvSceneSwitcher.twitchToken.bits.read="View Bits information for a channel."
|
||||
AdvSceneSwitcher.twitchToken.channel.manageBroadcast="Manage a channel’s broadcast configuration, including updating channel configuration and managing stream markers and stream tags."
|
||||
AdvSceneSwitcher.twitchToken.channel.startCommercial="Run commercials on a channel."
|
||||
AdvSceneSwitcher.twitchToken.channel.createClip="Create clips from channel's broadcasts."
|
||||
|
||||
AdvSceneSwitcher.twitchCategories.fetchStart="Fetching stream categories ..."
|
||||
AdvSceneSwitcher.twitchCategories.fetchStatus="Got %1 stream categories."
|
||||
|
|
|
|||
|
|
@ -737,9 +737,15 @@ AdvSceneSwitcher.action.sceneLock.entry="Sur {{scenes}} {{actions}} {{sources}}"
|
|||
AdvSceneSwitcher.action.twitch="Twitch"
|
||||
AdvSceneSwitcher.action.twitch.type.title="Définir le titre du flux"
|
||||
AdvSceneSwitcher.action.twitch.type.category="Définir la catégorie du flux"
|
||||
AdvSceneSwitcher.action.twitch.type.marker="Create stream marker"
|
||||
AdvSceneSwitcher.action.twitch.type.clip="Create stream clip"
|
||||
AdvSceneSwitcher.action.twitch.type.commercial="Démarrer une publicité d'une durée de"
|
||||
AdvSceneSwitcher.action.twitch.categorySelectionDisabled="Impossible de sélectionner une catégorie sans avoir d'abord sélectionné un compte Twitch !"
|
||||
AdvSceneSwitcher.action.twitch.entry="Sur {{account}} {{actions}} {{text}} {{category}} {{manualCategorySearch}} {{duration}}"
|
||||
AdvSceneSwitcher.action.twitch.entry="Sur {{account}} {{actions}} {{streamTitle}} {{category}} {{manualCategorySearch}} {{markerDescription}} {{clipHasDelay}} {{duration}}"
|
||||
AdvSceneSwitcher.action.twitch.tokenPermissionsInsufficient="Permissions of selected token are insufficient to perform selected action!"
|
||||
AdvSceneSwitcher.action.twitch.clip.hasDelay="Add a slight delay before capturing the clip"
|
||||
AdvSceneSwitcher.action.twitch.marker.description="Describe marker"
|
||||
AdvSceneSwitcher.action.twitch.title.title="Enter title"
|
||||
|
||||
; Transition Tab
|
||||
AdvSceneSwitcher.transitionTab.title="Transition"
|
||||
|
|
@ -1121,6 +1127,7 @@ AdvSceneSwitcher.twitchToken.analytics.readGames="Afficher les données d'analys
|
|||
AdvSceneSwitcher.twitchToken.bits.read="Afficher les informations sur les Bits pour une chaîne."
|
||||
AdvSceneSwitcher.twitchToken.channel.manageBroadcast="Gérer la configuration de diffusion d'une chaîne, y compris la mise à jour de la configuration de la chaîne et la gestion des marqueurs de diffusion en continu et des balises de diffusion en continu."
|
||||
AdvSceneSwitcher.twitchToken.channel.startCommercial="Lancer des publicités sur une chaîne."
|
||||
AdvSceneSwitcher.twitchToken.channel.createClip="Create clips from channel's broadcasts."
|
||||
|
||||
AdvSceneSwitcher.twitchCategories.fetchStart="Récupération des catégories de streams..."
|
||||
AdvSceneSwitcher.twitchCategories.fetchStatus="Obtenu %1 catégories de streams."
|
||||
|
|
|
|||
|
|
@ -18,6 +18,10 @@ const static std::map<MacroActionTwitch::Action, std::string> actionTypes = {
|
|||
"AdvSceneSwitcher.action.twitch.type.title"},
|
||||
{MacroActionTwitch::Action::CATEGORY,
|
||||
"AdvSceneSwitcher.action.twitch.type.category"},
|
||||
{MacroActionTwitch::Action::MARKER,
|
||||
"AdvSceneSwitcher.action.twitch.type.marker"},
|
||||
{MacroActionTwitch::Action::CLIP,
|
||||
"AdvSceneSwitcher.action.twitch.type.clip"},
|
||||
{MacroActionTwitch::Action::COMMERCIAL,
|
||||
"AdvSceneSwitcher.action.twitch.type.commercial"},
|
||||
};
|
||||
|
|
@ -25,17 +29,18 @@ const static std::map<MacroActionTwitch::Action, std::string> actionTypes = {
|
|||
void MacroActionTwitch::SetStreamTitle(
|
||||
const std::shared_ptr<TwitchToken> &token) const
|
||||
{
|
||||
if (std::string(_text).empty()) {
|
||||
if (std::string(_streamTitle).empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
OBSDataAutoRelease data = obs_data_create();
|
||||
obs_data_set_string(data, "title", _text.c_str());
|
||||
obs_data_set_string(data, "title", _streamTitle.c_str());
|
||||
auto result = SendPatchRequest(
|
||||
"https://api.twitch.tv",
|
||||
std::string("/helix/channels?broadcaster_id=") +
|
||||
token->GetUserID(),
|
||||
*token, data.Get());
|
||||
|
||||
if (result.status != 204) {
|
||||
blog(LOG_INFO, "Failed to set stream title! (%d)",
|
||||
result.status);
|
||||
|
|
@ -63,6 +68,42 @@ void MacroActionTwitch::SetStreamCategory(
|
|||
}
|
||||
}
|
||||
|
||||
void MacroActionTwitch::CreateStreamMarker(
|
||||
const std::shared_ptr<TwitchToken> &token) const
|
||||
{
|
||||
OBSDataAutoRelease data = obs_data_create();
|
||||
obs_data_set_string(data, "user_id", token->GetUserID().c_str());
|
||||
|
||||
if (!std::string(_markerDescription).empty()) {
|
||||
obs_data_set_string(data, "description",
|
||||
_markerDescription.c_str());
|
||||
}
|
||||
|
||||
auto result = SendPostRequest("https://api.twitch.tv",
|
||||
"/helix/streams/markers", *token,
|
||||
data.Get());
|
||||
|
||||
if (result.status != 200) {
|
||||
blog(LOG_INFO, "Failed to create marker! (%d)", result.status);
|
||||
}
|
||||
}
|
||||
|
||||
void MacroActionTwitch::CreateStreamClip(
|
||||
const std::shared_ptr<TwitchToken> &token) const
|
||||
{
|
||||
OBSDataAutoRelease data = obs_data_create();
|
||||
auto hasDelay = _clipHasDelay ? "true" : "false";
|
||||
auto result = SendPostRequest(
|
||||
"https://api.twitch.tv",
|
||||
"/helix/clips?broadcaster_id=" + token->GetUserID() +
|
||||
"&has_delay=" + hasDelay,
|
||||
*token, data.Get());
|
||||
|
||||
if (result.status != 202) {
|
||||
blog(LOG_INFO, "Failed to create clip! (%d)", result.status);
|
||||
}
|
||||
}
|
||||
|
||||
void MacroActionTwitch::StartCommercial(
|
||||
const std::shared_ptr<TwitchToken> &token) const
|
||||
{
|
||||
|
|
@ -102,6 +143,12 @@ bool MacroActionTwitch::PerformAction()
|
|||
case MacroActionTwitch::Action::CATEGORY:
|
||||
SetStreamCategory(token);
|
||||
break;
|
||||
case MacroActionTwitch::Action::MARKER:
|
||||
CreateStreamMarker(token);
|
||||
break;
|
||||
case MacroActionTwitch::Action::CLIP:
|
||||
CreateStreamClip(token);
|
||||
break;
|
||||
case MacroActionTwitch::Action::COMMERCIAL:
|
||||
StartCommercial(token);
|
||||
break;
|
||||
|
|
@ -131,8 +178,10 @@ bool MacroActionTwitch::Save(obs_data_t *obj) const
|
|||
obs_data_set_int(obj, "action", static_cast<int>(_action));
|
||||
obs_data_set_string(obj, "token",
|
||||
GetWeakTwitchTokenName(_token).c_str());
|
||||
_text.Save(obj, "text");
|
||||
_streamTitle.Save(obj, "streamTitle");
|
||||
_category.Save(obj);
|
||||
_markerDescription.Save(obj, "markerDescription");
|
||||
obs_data_set_bool(obj, "clipHasDelay", _clipHasDelay);
|
||||
_duration.Save(obj);
|
||||
return true;
|
||||
}
|
||||
|
|
@ -142,8 +191,10 @@ bool MacroActionTwitch::Load(obs_data_t *obj)
|
|||
MacroAction::Load(obj);
|
||||
_action = static_cast<Action>(obs_data_get_int(obj, "action"));
|
||||
_token = GetWeakTwitchTokenByName(obs_data_get_string(obj, "token"));
|
||||
_text.Load(obj, "text");
|
||||
_streamTitle.Load(obj, "streamTitle");
|
||||
_category.Load(obj);
|
||||
_markerDescription.Load(obj, "markerDescription");
|
||||
_clipHasDelay = obs_data_get_bool(obj, "clipHasDelay");
|
||||
_duration.Load(obj);
|
||||
return true;
|
||||
}
|
||||
|
|
@ -158,6 +209,8 @@ bool MacroActionTwitch::ActionIsSupportedByToken()
|
|||
static const std::unordered_map<Action, TokenOption> requiredOption = {
|
||||
{Action::TITLE, {"channel:manage:broadcast"}},
|
||||
{Action::CATEGORY, {"channel:manage:broadcast"}},
|
||||
{Action::MARKER, {"channel:manage:broadcast"}},
|
||||
{Action::CLIP, {"clips:edit"}},
|
||||
{Action::COMMERCIAL, {"channel:edit:commercial"}},
|
||||
};
|
||||
auto token = _token.lock();
|
||||
|
|
@ -181,31 +234,42 @@ MacroActionTwitchEdit::MacroActionTwitchEdit(
|
|||
: QWidget(parent),
|
||||
_actions(new QComboBox()),
|
||||
_tokens(new TwitchConnectionSelection()),
|
||||
_text(new VariableLineEdit(this)),
|
||||
_streamTitle(new VariableLineEdit(this)),
|
||||
_category(new TwitchCategorySelection(this)),
|
||||
_manualCategorySearch(new TwitchCategorySearchButton()),
|
||||
_markerDescription(new VariableLineEdit(this)),
|
||||
_clipHasDelay(new QCheckBox(obs_module_text(
|
||||
"AdvSceneSwitcher.action.twitch.clip.hasDelay"))),
|
||||
_duration(new DurationSelection(this, false, 0)),
|
||||
_layout(new QHBoxLayout()),
|
||||
_tokenPermissionWarning(new QLabel(obs_module_text(
|
||||
"AdvSceneSwitcher.action.twitch.tokenPermissionsInsufficient")))
|
||||
{
|
||||
_text->setSizePolicy(QSizePolicy::MinimumExpanding,
|
||||
QSizePolicy::Preferred);
|
||||
_duration->SpinBox()->setSuffix("s");
|
||||
_streamTitle->setSizePolicy(QSizePolicy::MinimumExpanding,
|
||||
QSizePolicy::Preferred);
|
||||
_streamTitle->setMaxLength(140);
|
||||
_markerDescription->setSizePolicy(QSizePolicy::MinimumExpanding,
|
||||
QSizePolicy::Preferred);
|
||||
_markerDescription->setMaxLength(140);
|
||||
|
||||
auto spinBox = _duration->SpinBox();
|
||||
spinBox->setSuffix("s");
|
||||
spinBox->setMaximum(180);
|
||||
populateActionSelection(_actions);
|
||||
|
||||
QWidget::connect(_actions, SIGNAL(currentIndexChanged(int)), this,
|
||||
SLOT(ActionChanged(int)));
|
||||
QWidget::connect(_tokens, SIGNAL(SelectionChanged(const QString &)),
|
||||
this, SLOT(TwitchTokenChanged(const QString &)));
|
||||
QWidget::connect(_text, SIGNAL(editingFinished()), this,
|
||||
SLOT(TextChanged()));
|
||||
QWidget::connect(_streamTitle, SIGNAL(editingFinished()), this,
|
||||
SLOT(StreamTitleChanged()));
|
||||
QWidget::connect(_category,
|
||||
SIGNAL(CategoreyChanged(const TwitchCategory &)), this,
|
||||
SLOT(CategoreyChanged(const TwitchCategory &)));
|
||||
QWidget::connect(_duration,
|
||||
SIGNAL(CategoreyChanged(const TwitchCategory &)), this,
|
||||
SLOT(CategoreyChanged(const TwitchCategory &)));
|
||||
QWidget::connect(_markerDescription, SIGNAL(editingFinished()), this,
|
||||
SLOT(MarkerDescriptionChanged()));
|
||||
QObject::connect(_clipHasDelay, SIGNAL(stateChanged(int)), this,
|
||||
SLOT(HasClipDelayChanged(const Duration &)));
|
||||
QObject::connect(_duration, SIGNAL(DurationChanged(const Duration &)),
|
||||
this, SLOT(DurationChanged(const Duration &)));
|
||||
QWidget::connect(&_tokenPermissionCheckTimer, SIGNAL(timeout()), this,
|
||||
|
|
@ -215,9 +279,11 @@ MacroActionTwitchEdit::MacroActionTwitchEdit(
|
|||
_layout,
|
||||
{{"{{account}}", _tokens},
|
||||
{"{{actions}}", _actions},
|
||||
{"{{text}}", _text},
|
||||
{"{{streamTitle}}", _streamTitle},
|
||||
{"{{category}}", _category},
|
||||
{"{{manualCategorySearch}}", _manualCategorySearch},
|
||||
{"{{markerDescription}}", _markerDescription},
|
||||
{"{{clipHasDelay}}", _clipHasDelay},
|
||||
{"{{duration}}", _duration}});
|
||||
_layout->setContentsMargins(0, 0, 0, 0);
|
||||
|
||||
|
|
@ -247,14 +313,14 @@ void MacroActionTwitchEdit::TwitchTokenChanged(const QString &token)
|
|||
emit(HeaderInfoChanged(token));
|
||||
}
|
||||
|
||||
void MacroActionTwitchEdit::TextChanged()
|
||||
void MacroActionTwitchEdit::StreamTitleChanged()
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto lock = LockContext();
|
||||
_entryData->_text = _text->text().toStdString();
|
||||
_entryData->_streamTitle = _streamTitle->text().toStdString();
|
||||
}
|
||||
|
||||
void MacroActionTwitchEdit::CategoreyChanged(const TwitchCategory &category)
|
||||
|
|
@ -267,6 +333,27 @@ void MacroActionTwitchEdit::CategoreyChanged(const TwitchCategory &category)
|
|||
_entryData->_category = category;
|
||||
}
|
||||
|
||||
void MacroActionTwitchEdit::MarkerDescriptionChanged()
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto lock = LockContext();
|
||||
_entryData->_markerDescription =
|
||||
_markerDescription->text().toStdString();
|
||||
}
|
||||
|
||||
void MacroActionTwitchEdit::ClipHasDelayChanged(int state)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto lock = LockContext();
|
||||
_entryData->_clipHasDelay = state;
|
||||
}
|
||||
|
||||
void MacroActionTwitchEdit::DurationChanged(const Duration &duration)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
|
|
@ -287,15 +374,21 @@ void MacroActionTwitchEdit::CheckTokenPermissions()
|
|||
|
||||
void MacroActionTwitchEdit::SetupWidgetVisibility()
|
||||
{
|
||||
_text->setVisible(_entryData->_action ==
|
||||
MacroActionTwitch::Action::TITLE);
|
||||
_streamTitle->setVisible(_entryData->_action ==
|
||||
MacroActionTwitch::Action::TITLE);
|
||||
_category->setVisible(_entryData->_action ==
|
||||
MacroActionTwitch::Action::CATEGORY);
|
||||
_manualCategorySearch->setVisible(_entryData->_action ==
|
||||
MacroActionTwitch::Action::CATEGORY);
|
||||
_markerDescription->setVisible(_entryData->_action ==
|
||||
MacroActionTwitch::Action::MARKER);
|
||||
_clipHasDelay->setVisible(_entryData->_action ==
|
||||
MacroActionTwitch::Action::CLIP);
|
||||
_duration->setVisible(_entryData->_action ==
|
||||
MacroActionTwitch::Action::COMMERCIAL);
|
||||
if (_entryData->_action == MacroActionTwitch::Action::TITLE) {
|
||||
|
||||
if (_entryData->_action == MacroActionTwitch::Action::TITLE ||
|
||||
_entryData->_action == MacroActionTwitch::Action::MARKER) {
|
||||
RemoveStretchIfPresent(_layout);
|
||||
} else {
|
||||
AddStretchIfNecessary(_layout);
|
||||
|
|
@ -316,10 +409,12 @@ void MacroActionTwitchEdit::UpdateEntryData()
|
|||
|
||||
_actions->setCurrentIndex(static_cast<int>(_entryData->_action));
|
||||
_tokens->SetToken(_entryData->_token);
|
||||
_text->setText(_entryData->_text);
|
||||
_streamTitle->setText(_entryData->_streamTitle);
|
||||
_category->SetToken(_entryData->_token);
|
||||
_manualCategorySearch->SetToken(_entryData->_token);
|
||||
_category->SetCategory(_entryData->_category);
|
||||
_markerDescription->setText(_entryData->_markerDescription);
|
||||
_clipHasDelay->setChecked(_entryData->_clipHasDelay);
|
||||
_duration->SetDuration(_entryData->_duration);
|
||||
SetupWidgetVisibility();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,18 +26,26 @@ public:
|
|||
enum class Action {
|
||||
TITLE,
|
||||
CATEGORY,
|
||||
MARKER,
|
||||
CLIP,
|
||||
COMMERCIAL,
|
||||
};
|
||||
|
||||
Action _action = Action::TITLE;
|
||||
std::weak_ptr<TwitchToken> _token;
|
||||
StringVariable _text = obs_module_text("AdvSceneSwitcher.enterText");
|
||||
StringVariable _streamTitle =
|
||||
obs_module_text("AdvSceneSwitcher.action.twitch.title.title");
|
||||
TwitchCategory _category;
|
||||
StringVariable _markerDescription = obs_module_text(
|
||||
"AdvSceneSwitcher.action.twitch.marker.description");
|
||||
bool _clipHasDelay = false;
|
||||
Duration _duration = 60;
|
||||
|
||||
private:
|
||||
void SetStreamTitle(const std::shared_ptr<TwitchToken> &) const;
|
||||
void SetStreamCategory(const std::shared_ptr<TwitchToken> &) const;
|
||||
void CreateStreamMarker(const std::shared_ptr<TwitchToken> &) const;
|
||||
void CreateStreamClip(const std::shared_ptr<TwitchToken> &) const;
|
||||
void StartCommercial(const std::shared_ptr<TwitchToken> &) const;
|
||||
|
||||
static bool _registered;
|
||||
|
|
@ -63,8 +71,10 @@ public:
|
|||
private slots:
|
||||
void ActionChanged(int);
|
||||
void TwitchTokenChanged(const QString &);
|
||||
void TextChanged();
|
||||
void StreamTitleChanged();
|
||||
void CategoreyChanged(const TwitchCategory &);
|
||||
void MarkerDescriptionChanged();
|
||||
void ClipHasDelayChanged(int state);
|
||||
void DurationChanged(const Duration &);
|
||||
void CheckTokenPermissions();
|
||||
|
||||
|
|
@ -79,9 +89,11 @@ private:
|
|||
|
||||
QComboBox *_actions;
|
||||
TwitchConnectionSelection *_tokens;
|
||||
VariableLineEdit *_text;
|
||||
VariableLineEdit *_streamTitle;
|
||||
TwitchCategorySelection *_category;
|
||||
TwitchCategorySearchButton *_manualCategorySearch;
|
||||
VariableLineEdit *_markerDescription;
|
||||
QCheckBox *_clipHasDelay;
|
||||
DurationSelection *_duration;
|
||||
QHBoxLayout *_layout;
|
||||
QLabel *_tokenPermissionWarning;
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ const std::unordered_map<std::string, std::string> TokenOption::apiIdToLocale{
|
|||
|
||||
{"channel:manage:broadcast",
|
||||
"AdvSceneSwitcher.twitchToken.channel.manageBroadcast"},
|
||||
{"clips:edit", "AdvSceneSwitcher.twitchToken.channel.createClip"},
|
||||
{"channel:edit:commercial",
|
||||
"AdvSceneSwitcher.twitchToken.channel.startCommercial"},
|
||||
};
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user