Skip to content

Commit

Permalink
Add OBS data for Companion, add custom feedback for current scene
Browse files Browse the repository at this point in the history
  • Loading branch information
zoton2 committed Feb 16, 2024
1 parent 2a6da14 commit fcea0d8
Show file tree
Hide file tree
Showing 6 changed files with 156 additions and 6 deletions.
2 changes: 2 additions & 0 deletions companion-plugin/companion-module-esa/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,12 @@
"prettier": "@companion-module/tools/.prettierrc.json",
"dependencies": {
"@companion-module/base": "~1.7.0",
"string-similarity": "^4.0.4",
"ws": "^8.16.0"
},
"devDependencies": {
"@companion-module/tools": "^1.4.2",
"@types/string-similarity": "^4.0.2",
"@types/ws": "^8.5.10",
"@typescript-eslint/eslint-plugin": "^6.20.0",
"@typescript-eslint/parser": "^6.20.0",
Expand Down
15 changes: 15 additions & 0 deletions companion-plugin/companion-module-esa/pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

77 changes: 73 additions & 4 deletions companion-plugin/companion-module-esa/src/feedbacks.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,82 @@
import { InstanceBase } from '@companion-module/base';
import { CompanionFeedbackDefinition, InstanceBase } from '@companion-module/base';
import { findBestMatch } from 'string-similarity';
import { Config } from './config';

let instance: InstanceBase<Config>;

// Defaults, will be overwritten!
// TODO: Store in variables? Maybe not?
let sceneKeys: { [k: string]: string } = {
commercials: 'Intermission (commercials)',
gameLayout: 'Game Layout',
readerIntroduction: 'Reader Introduction',
intermission: 'Intermission',
intermissionPlayer: 'Intermission Player',
countdown: 'Countdown',
};

// TODO: Replace this with Companion's "learn" feature?
export function setObsSceneKeys(val: typeof sceneKeys) {
sceneKeys = val;
}

// COPIED FROM esa-layouts-shared FOR NOW!
/**
* Find scene based on string; at least the start of the name should be supplied.
* @param name Name of scene, at least starting of name.
*/
function findScene(name: string): string | undefined {
let match: string | undefined;
// Assumes that the obs_scene_list variable is a JSON stringified array.
const matches = (JSON.parse(
instance.getVariableValue('obs_scene_list')?.toString() || '[]',
) as string[]).filter((s) => s.startsWith(name));
if (matches.length > 1) {
const bestMatches = findBestMatch(name, matches);
match = bestMatches.bestMatch.target;
} else if (matches.length === 1) {
[match] = matches;
}
return match;
}

// COPIED FROM esa-layouts-shared FOR NOW!
/**
* Check if we are on a specified scene; at least the start of the name should be supplied.
* @param name Name of scene to check we are on, at least starting of name.
*/
function isCurrentScene(name: string): boolean {
return !!instance.getVariableValue('obs_scene')
&& instance.getVariableValue('obs_scene') === findScene(name);
}

// Defined as an additional function so we can update the choices dropdown on connection.
export const obsSceneFeedback = (): CompanionFeedbackDefinition => ({
type: 'boolean',
name: 'When OBS scene is active',
defaultStyle: {},
options: [
{
type: 'multidropdown',
label: 'Scene(s)',
id: 'scenes',
choices: Object.entries(sceneKeys).map(([k, v]) => ({ id: k, label: v })),
default: [],
},
],
callback: (feedback) => {
const scenes = feedback.options.scenes as string[];
return !!scenes.find((v) => isCurrentScene(sceneKeys[v]));
},
});

/**
* Called by module instance class when feedbacks should be set up.
* @param instance Copy of current module instance class
*/
function initFeedbacks(instance: InstanceBase<Config>) {
export function initFeedbacks(instance_: InstanceBase<Config>) {
instance = instance_;
instance.setFeedbackDefinitions({
// set up feedbacks here
obsSceneFeedback: obsSceneFeedback(),
});
}
export default initFeedbacks;
41 changes: 40 additions & 1 deletion companion-plugin/companion-module-esa/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { InstanceBase, InstanceStatus, SomeCompanionConfigField, runEntrypoint }
import { WebSocket } from 'ws';
import initActions from './actions';
import { Config, getConfigFields } from './config';
import initFeedbacks from './feedbacks';
import { initFeedbacks, obsSceneFeedback, setObsSceneKeys } from './feedbacks';
import initPresets from './presets';
import upgradeScripts from './upgrades';
import initVariables from './variables';
Expand Down Expand Up @@ -111,6 +111,45 @@ class ModuleInstance extends InstanceBase<Config> {
this.setVariableValues({
twitch_commercials_disabled: value,
});
} else if (msg.name === 'obsData') {
// TODO: Reference type from another location?
const value = msg.value as {
connected: boolean;
scene?: string;
sceneList: string[];
transitioning: boolean;
streaming: boolean;
disableTransitioning: boolean;
transitionTimestamp: number;
};
this.setVariableValues({
obs_connected: value.connected,
obs_transitioning: value.transitioning,
obs_transitioning_disabled: value.disableTransitioning,
obs_scene: value.scene,
obs_scene_list: JSON.stringify(value.sceneList), // Weird to store, change?
});
// Trigger this feedback check in case we changed scenes here.
this.checkFeedbacks('obsSceneFeedback');
} else if (msg.name === 'cfgScenes') {
// TODO: Reference type from another location?
const value = msg.value as {
commercials: string;
gameLayout: string;
readerIntroduction: string;
intermission: string;
intermissionPlayer: string;
countdown: string;
};
// Stores this for use later (maybe could be done better?)
setObsSceneKeys(value);
// Updates the multidropdown with the configuration scene names.
this.setFeedbackDefinitions({
obsSceneFeedback: obsSceneFeedback(),
});
// Trigger this feedback check, needed on connection, not sure if needed
// for anything else, but safe to have.
this.checkFeedbacks('obsSceneFeedback');
}
});
}
Expand Down
21 changes: 21 additions & 0 deletions companion-plugin/companion-module-esa/src/variables.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,27 @@ function initVariables(instance: InstanceBase<Config>) {
variableId: 'twitch_commercials_disabled',
name: 'Twitch Commercials Disabled (esa-commercials)',
},
// TODO: Use some OBS stuff from an OBS plugin instead?
{
variableId: 'obs_connected',
name: 'OBS: Connected',
},
{
variableId: 'obs_transitioning',
name: 'OBS: Transitioning',
},
{
variableId: 'obs_transitioning_disabled',
name: 'OBS: Transitioning Disabled',
},
{
variableId: 'obs_scene',
name: 'OBS: Current Scene',
},
{
variableId: 'obs_scene_list',
name: 'OBS: Scene List (JSON stringified array)',
},
]);
}
export default initVariables;
6 changes: 5 additions & 1 deletion src/extension/companion.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import companion from './util/companion';
import { get as nodecg } from './util/nodecg';
import obs, { changeScene } from './util/obs';
import { obsData, readerIntroduction, streamDeckData } from './util/replicants';
import { obsData, streamDeckData } from './util/replicants';
import { sc } from './util/speedcontrol';

const config = nodecg().bundleConfig;
Expand All @@ -18,6 +18,8 @@ sc.twitchCommercialTimer.on('change', (value) => (
companion.send({ name: 'twitchCommercialTimer', value })));
twitchCommercialsDisabled.on('change', (value) => (
companion.send({ name: 'twitchCommercialsDisabled', value })));
obsData.on('change', (value) => (
companion.send({ name: 'obsData', value: { ...value, gameLayoutScreenshot: undefined } })));

// Sending things on connection.
companion.evt.on('open', (socket) => {
Expand All @@ -26,6 +28,8 @@ companion.evt.on('open', (socket) => {
companion.send({ name: 'streamDeckData', value: streamDeckData.value });
companion.send({ name: 'twitchCommercialTimer', value: sc.twitchCommercialTimer.value });
companion.send({ name: 'twitchCommercialsDisabled', value: twitchCommercialsDisabled.value });
companion.send({ name: 'obsData', value: { ...obsData.value, gameLayoutScreenshot: undefined } });
companion.send({ name: 'cfgScenes', value: nodecg().bundleConfig.obs.names.scenes });
});

// Listening for any actions triggered from Companion.
Expand Down

0 comments on commit fcea0d8

Please sign in to comment.