Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
63cf93a
Implemented initial TransformObsSourceScale effect definition
Oceanity Jul 27, 2024
f3d163a
Merge remote-tracking branch 'upstream/v5' into feature/obs-animate-t…
Oceanity Jul 27, 2024
29576f2
More work on Transform Source Scale
Oceanity Jul 28, 2024
ab23a45
Merge remote-tracking branch 'upstream/v5' into feature/obs-animate-t…
Oceanity Aug 1, 2024
127fb59
Merge remote-tracking branch 'upstream/v5' into feature/obs-animate-t…
Oceanity Aug 3, 2024
1556c00
Implemented initial TransformObsSourceScale effect definition
Oceanity Jul 27, 2024
5222b5b
More work on Transform Source Scale
Oceanity Jul 28, 2024
6541233
Merge branch 'feature/obs-animate-transform' of https://github.com/Oc…
Oceanity Aug 3, 2024
d2f4d18
Getting there
Oceanity Aug 3, 2024
2dc5e57
Made some changes
Oceanity Aug 3, 2024
3aaf52d
Added separate function for clear and a button to test
Oceanity Aug 3, 2024
6c4d51a
Moved OBSSource to SceneItem since it has an Id
Oceanity Aug 4, 2024
2044328
Implemented easing and optimized methods
Oceanity Aug 4, 2024
0287776
Merge remote-tracking branch 'upstream/v5' into feature/obs-animate-t…
Oceanity Aug 4, 2024
c2f821c
Cleaned up some fields
Oceanity Aug 4, 2024
f22fb77
Removed unused hook in communicator
Oceanity Aug 4, 2024
6afa608
Reverted unnecessary change from Prettier
Oceanity Aug 4, 2024
ef28862
Removed unnecessary var
Oceanity Aug 4, 2024
39f80f3
Removed unnecessary Effect Params
Oceanity Aug 4, 2024
98e92cd
Fixed error message
Oceanity Aug 4, 2024
c68abd1
Merge branch 'v5' into feature/obs-animate-transform
ebiggz Aug 6, 2024
25dc630
Merge branch 'v5' into feature/obs-animate-transform
ebiggz Aug 6, 2024
8ebd41e
Merge branch 'v5' into feature/obs-animate-transform
ebiggz Aug 6, 2024
6426247
Used `firebot-input` component and removed unnecessary `$q` hooks
Oceanity Aug 7, 2024
e685930
Merge branch 'feature/obs-animate-transform' of https://github.com/Oc…
Oceanity Aug 7, 2024
f02d442
Replaced two other checkbox type inputs with `firebot-checkbox`
Oceanity Aug 7, 2024
c866610
Few improvements
Oceanity Aug 7, 2024
8c78186
Added techdebt comment and created helper function to slightly clean …
Oceanity Aug 7, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 11 additions & 1 deletion src/backend/integrations/builtin/obs/communicator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,9 @@ import {
getImageSources,
getMediaSources,
getColorSources,
getSupportedImageFormats
getSupportedImageFormats,
getTransformableSceneItems,
OBSSceneItem
} from "./obs-remote";

export function setupFrontendListeners(
Expand All @@ -39,6 +41,14 @@ export function setupFrontendListeners(
getSourcesWithFilters
);

frontendCommunicator.onAsync<unknown[], Array<OBSSceneItem>>(
"obs-get-transformable-scene-items",
(args: [sceneName: string]) => {
const [sceneName] = args;
return getTransformableSceneItems(sceneName);
}
);

frontendCommunicator.onAsync<never, Array<OBSSource>>(
"obs-get-audio-sources",
getAudioSources
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,240 @@
import { EffectType } from "../../../../../types/effects";
import { OBSSceneItem, OBSSourceTransformKeys, transformSceneItem } from "../obs-remote";

export const TransformSourceScaleEffectType: EffectType<{
sceneName?: string;
sceneItem?: OBSSceneItem;
duration: number;
easeIn: boolean;
easeOut: boolean;
isTransformingPosition: boolean;
isTransformingScale: boolean;
isTransformingRotation: boolean;
startTransform: Record<string, string>;
endTransform: Record<string, string>;
}> = {
definition: {
id: "firebot:obs-transform-source",
name: "Transform OBS Source",
description: "Transforms the position, scale, or rotation of an OBS source either instantly or animated over time",
icon: "fad fa-arrows",
categories: ["common"]
},
optionsTemplate: `
<eos-container header="OBS Scene" pad-top="true">
<div>
<button class="btn btn-link" ng-click="getScenes()">Refresh Scene Data</button>
</div>
<ui-select ng-if="scenes != null" ng-model="effect.sceneName" on-select="selectScene($select.selected.name)">
<ui-select-match placeholder="Select a Scene...">{{$select.selected.name}}</ui-select-match>
<ui-select-choices repeat="scene.name as scene in scenes | filter: {name: $select.search}">
<div ng-bind-html="scene.name | highlight: $select.search"></div>
</ui-select-choices>
<ui-select-no-choice>
<b>No Scenes found.</b>
</ui-select-no-choice>
</ui-select>
</eos-container>
<eos-container ng-if="sceneItems != null && effect.sceneName != null" header="OBS Source" pad-top="true">
<div>
<button class="btn btn-link" ng-click="getSources(effect.sceneName)">Refresh Source Data</button>
</div>
<ui-select ng-if="sceneItems != null" ng-model="effect.sceneItem.name" on-select="selectSceneItem($select.selected)">
<ui-select-match placeholder="Select a Source...">{{$select.selected.name}}</ui-select-match>
<ui-select-choices repeat="sceneItem.name as sceneItem in sceneItems | filter: {name: $select.search}">
<div ng-bind-html="sceneItem.name | highlight: $select.search"></div>
</ui-select-choices>
<ui-select-no-choice>
<b>No transformable sources found.</b>
</ui-select-no-choice>
</ui-select>
<div ng-if="sceneItems == null" class="muted">
No transformable sources found. {{ isObsConfigured ? "Is OBS running?" : "Have you configured the OBS integration?" }}
</div>
</eos-container>
<eos-container ng-if="effect.sceneItem != null" header="Duration" pad-top="true">
<firebot-input
input-type="number"
input-title="Duration"
placeholder-text="milliseconds"
model="effect.duration"
style="margin-bottom: 20px;" />
<div style="display: flex; gap: 20px;">
<firebot-checkbox
label="Ease-In"
tooltip="Smooth the start of the animation"
model="effect.easeIn"
style="flex-basis: 50%" />
<firebot-checkbox
label="Ease-Out"
tooltip="Smooth the end of the animation"
model="effect.easeOut"
style="flex-basis: 50%" />
</div>
</eos-container>
<eos-container ng-if="effect.sceneItem != null" header="Transform" pad-top="true">
<firebot-checkbox
label="Position"
tooltip="Transform the position of the OBS source"
model="effect.isTransformingPosition" />
<div ng-if="effect.isTransformingPosition" style="margin-top: 10px">
<div style="display: flex; gap: 20px; margin-bottom: 20px;">
<firebot-input
input-title="Start X"
placeholder-text="Current X"
model="effect.startTransform.positionX"
style="flex-basis: 50%" />
<firebot-input
input-title="Start Y"
placeholder-text="Current Y"
model="effect.startTransform.positionY"
style="flex-basis: 50%" />
</div>
<div style="display: flex; gap: 20px; margin-bottom: 20px">
<firebot-input
input-title="End X"
model="effect.endTransform.positionX"
style="flex-basis: 50%" />
<firebot-input
input-title="End Y"
model="effect.endTransform.positionY"
style="flex-basis: 50%" />
</div>
</div>
<firebot-checkbox
label="Scale"
tooltip="Transform the scale of the OBS source"
model="effect.isTransformingScale" />
<div ng-if="effect.isTransformingScale" style="margin-bottom: 20px">
<div style="display: flex; gap: 20px; margin-bottom: 20px;">
<firebot-input
input-title="Start X Scale"
placeholder-text="Current X Scale"
model="effect.startTransform.scaleX"
style="flex-basis: 50%" />
<firebot-input
input-title="Start Y Scale"
placeholder-text="Current Y Scale"
model="effect.startTransform.scaleY"
style="flex-basis: 50%" />
</div>
<div style="display: flex; gap: 20px; margin-bottom: 20px;">
<firebot-input
input-title="End X Scale"
placeholder-text="0.0 - 1.0"
model="effect.endTransform.scaleX"
style="flex-basis: 50%" />
<firebot-input
input-title="End Y Scale"
placeholder-text="0.0 - 1.0"
model="effect.endTransform.scaleY"
style="flex-basis: 50%" />
</div>
</div>
<firebot-checkbox
label="Rotation"
tooltip="Transform the rotation of the OBS source"
model="effect.isTransformingRotation" />
<div ng-if="effect.isTransformingRotation" style="margin-bottom: 20px">
<div style="display: flex; gap: 20px; margin-bottom: 20px;">
<firebot-input
input-title="Start Rotation"
placeholder-text="Current Rotation"
model="effect.startTransform.rotation"
style="flex-basis: 50%" />
<firebot-input
input-title="End Rotation"
placeholder-text="0 - 360"
model="effect.endTransform.rotation"
style="flex-basis: 50%" />
</div>
</div>
</eos-container>
`,
optionsController: ($scope: any, backendCommunicator: any) => {
$scope.isObsConfigured = false;

$scope.scenes = [];
$scope.sceneItems = [];

$scope.selectScene = (sceneName: string) => {
$scope.effect.sceneItem = undefined;
$scope.getSources(sceneName);
};

$scope.selectSceneItem = (sceneItem: OBSSceneItem) => {
$scope.effect.sceneItem = sceneItem;
};

$scope.getScenes = () => {
$scope.isObsConfigured = backendCommunicator.fireEventSync("obs-is-configured");

backendCommunicator.fireEventAsync("obs-get-scene-list").then(
(scenes: string[] | undefined) => {
$scope.scenes = scenes?.map(scene => ({ name: scene, custom: false })) ?? [];
$scope.scenes.push($scope.customScene);

if ($scope.effect.sceneName != null) {
$scope.getSources($scope.effect.sceneName);
}
}
);
};
$scope.getScenes();

$scope.getSources = (sceneName: string) => {
$scope.isObsConfigured = backendCommunicator.fireEventSync("obs-is-configured");

backendCommunicator.fireEventAsync("obs-get-transformable-scene-items", [sceneName]).then(
(sceneItems: OBSSceneItem[]) => {
$scope.sceneItems = sceneItems ?? [];
}
);
};
},
optionsValidator: (effect) => {
if (effect.sceneName == null) {
return ["Please select a scene."];
}
if (effect.sceneItem == null) {
return ["Please select a source."];
}
if (effect.duration == null) {
return ["Please enter a duration."];
}
return [];
},
onTriggerEvent: async ({ effect }) => {
const parsedStart: Record<string, number> = {};
const parsedEnd: Record<string, number> = {};
const transformKeys: Array<OBSSourceTransformKeys> = [];
if (effect.isTransformingPosition) {
transformKeys.push("positionX", "positionY");
}
if (effect.isTransformingScale) {
transformKeys.push("scaleX", "scaleY");
}
if (effect.isTransformingRotation) {
transformKeys.push("rotation");
}

transformKeys.forEach((key) => {
if (effect.startTransform?.hasOwnProperty(key) && effect.startTransform[key].length) {
const value = Number(effect.startTransform[key]);
if (!isNaN(value)) {
parsedStart[key] = value;
}
}
if (effect.endTransform?.hasOwnProperty(key) && effect.endTransform[key].length) {
const value = Number(effect.endTransform[key]);
if (!isNaN(value)) {
parsedEnd[key] = value;
}
}
});

await transformSceneItem(effect.sceneName, effect.sceneItem.id, effect.duration, parsedStart, parsedEnd, effect.easeIn, effect.easeOut);

return true;
}
};
2 changes: 2 additions & 0 deletions src/backend/integrations/builtin/obs/obs-integration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import { CreateRecordChapter } from "./effects/create-recording-chapter";
import { ToggleSourceVisibilityEffectType } from "./effects/toggle-obs-source-visibility";
import { ToggleSourceFilterEffectType } from "./effects/toggle-obs-source-filter";
import { ToggleSourceMutedEffectType } from "./effects/toggle-obs-source-muted";
import { TransformSourceScaleEffectType } from "./effects/transform-obs-source-scale";
import { StartStreamEffectType } from "./effects/start-stream";
import { StopStreamEffectType } from "./effects/stop-stream";
import { StartVirtualCamEffectType } from "./effects/start-virtual-cam";
Expand Down Expand Up @@ -142,6 +143,7 @@ class ObsIntegration
effectManager.registerEffect(ToggleSourceVisibilityEffectType);
effectManager.registerEffect(ToggleSourceFilterEffectType);
effectManager.registerEffect(ToggleSourceMutedEffectType);
effectManager.registerEffect(TransformSourceScaleEffectType);
effectManager.registerEffect(StartStreamEffectType);
effectManager.registerEffect(StopStreamEffectType);
effectManager.registerEffect(StartVirtualCamEffectType);
Expand Down
Loading